]> git.proxmox.com Git - libgit2.git/commitdiff
New upstream version 0.27.0+dfsg.1
authorPirate Praveen <praveen@debian.org>
Thu, 26 Apr 2018 12:36:07 +0000 (18:06 +0530)
committerPirate Praveen <praveen@debian.org>
Thu, 26 Apr 2018 12:36:07 +0000 (18:06 +0530)
543 files changed:
.travis.yml
AUTHORS
CHANGELOG.md
CMakeLists.txt
CONTRIBUTING.md
PROJECTS.md
README.md
TROUBLESHOOTING.md [new file with mode: 0644]
appveyor.yml
cmake/Modules/EnableWarnings.cmake [new file with mode: 0644]
cmake/Modules/FindCoreFoundation.cmake
cmake/Modules/FindSecurity.cmake
cmake/Modules/FindStatNsec.cmake [new file with mode: 0644]
cmake/Modules/IdeSplitSources.cmake [new file with mode: 0644]
docs/release.md [new file with mode: 0644]
examples/.gitignore [deleted file]
examples/CMakeLists.txt
examples/Makefile [deleted file]
examples/common.c
examples/common.h
examples/describe.c
examples/general.c
examples/log.c
examples/merge.c [new file with mode: 0644]
examples/network/common.c
examples/network/fetch.c
examples/network/git2.c
git.git-authors
include/git2.h
include/git2/config.h
include/git2/describe.h
include/git2/diff.h
include/git2/graph.h
include/git2/merge.h
include/git2/message.h
include/git2/notes.h
include/git2/odb.h
include/git2/patch.h
include/git2/pathspec.h
include/git2/refs.h
include/git2/remote.h
include/git2/repository.h
include/git2/reset.h
include/git2/status.h
include/git2/sys/config.h
include/git2/sys/mempack.h
include/git2/sys/odb_backend.h
include/git2/sys/remote.h [deleted file]
include/git2/tree.h
include/git2/types.h
include/git2/version.h
include/git2/worktree.h
script/appveyor-mingw.sh
script/cibuild.sh
src/CMakeLists.txt [new file with mode: 0644]
src/annotated_commit.c
src/annotated_commit.h
src/apply.c
src/apply.h
src/attr.c
src/attr.h
src/attr_file.c
src/attr_file.h
src/attrcache.c
src/attrcache.h
src/blame.c
src/blame.h
src/blame_git.c
src/blame_git.h
src/blob.c
src/blob.h
src/branch.c
src/branch.h
src/buf_text.c
src/buf_text.h
src/buffer.c
src/buffer.h
src/cache.c
src/cache.h
src/cc-compat.h
src/checkout.c
src/checkout.h
src/cherrypick.c
src/clone.c
src/clone.h
src/commit.c
src/commit.h
src/commit_list.c
src/commit_list.h
src/common.h
src/config.c
src/config.h
src/config_cache.c
src/config_file.c
src/config_file.h
src/config_parse.c [new file with mode: 0644]
src/config_parse.h [new file with mode: 0644]
src/crlf.c
src/curl_stream.c [deleted file]
src/curl_stream.h [deleted file]
src/delta.h
src/describe.c
src/diff.c
src/diff.h
src/diff_driver.c
src/diff_driver.h
src/diff_file.c
src/diff_file.h
src/diff_generate.c
src/diff_generate.h
src/diff_parse.c
src/diff_parse.h
src/diff_print.c
src/diff_stats.c
src/diff_tform.c
src/diff_tform.h
src/diff_xdiff.c
src/diff_xdiff.h
src/errors.c
src/features.h.in [new file with mode: 0644]
src/fetch.c
src/fetch.h
src/fetchhead.c
src/fetchhead.h
src/filebuf.c
src/filebuf.h
src/fileops.c
src/fileops.h
src/filter.c
src/filter.h
src/fnmatch.c
src/fnmatch.h
src/global.c
src/global.h
src/graph.c
src/hash.c
src/hash.h
src/hash/hash_collisiondetect.h
src/hash/hash_common_crypto.h
src/hash/hash_generic.c
src/hash/hash_generic.h
src/hash/hash_openssl.h
src/hash/hash_win32.c
src/hash/hash_win32.h
src/hashsig.c
src/ident.c
src/idxmap.h
src/ignore.c
src/ignore.h
src/index.c
src/index.h
src/indexer.c
src/indexer.h
src/integer.h
src/iterator.c
src/iterator.h
src/map.h
src/merge.c
src/merge.h
src/merge_driver.c
src/merge_driver.h
src/merge_file.c
src/message.h
src/mwindow.c
src/mwindow.h
src/netops.c
src/netops.h
src/notes.c
src/notes.h
src/object.c
src/object.h
src/object_api.c
src/odb.c
src/odb.h
src/odb_loose.c
src/odb_mempack.c
src/odb_pack.c
src/offmap.h
src/oid.c
src/oid.h
src/oidarray.c
src/oidarray.h
src/oidmap.h
src/openssl_stream.c [deleted file]
src/openssl_stream.h [deleted file]
src/pack-objects.c
src/pack-objects.h
src/pack.c
src/pack.h
src/parse.c [new file with mode: 0644]
src/parse.h [new file with mode: 0644]
src/patch.c
src/patch.h
src/patch_generate.c
src/patch_generate.h
src/patch_parse.c
src/patch_parse.h
src/path.c
src/path.h
src/pathspec.c
src/pathspec.h
src/pool.c
src/pool.h
src/posix.c
src/posix.h
src/pqueue.c
src/pqueue.h
src/proxy.c
src/proxy.h
src/push.c
src/push.h
src/rebase.c
src/refdb.c
src/refdb.h
src/refdb_fs.c
src/refdb_fs.h
src/reflog.c
src/reflog.h
src/refs.c
src/refs.h
src/refspec.c
src/refspec.h
src/remote.c
src/remote.h
src/repository.c
src/repository.h
src/reset.c
src/revert.c
src/revparse.c
src/revwalk.c
src/revwalk.h
src/settings.c
src/sha1_lookup.c
src/sha1_lookup.h
src/signature.c
src/signature.h
src/socket_stream.c [deleted file]
src/socket_stream.h [deleted file]
src/sortedcache.c
src/sortedcache.h
src/stash.c
src/status.c
src/status.h
src/stransport_stream.c [deleted file]
src/stransport_stream.h [deleted file]
src/streams/curl.c [new file with mode: 0644]
src/streams/curl.h [new file with mode: 0644]
src/streams/openssl.c [new file with mode: 0644]
src/streams/openssl.h [new file with mode: 0644]
src/streams/socket.c [new file with mode: 0644]
src/streams/socket.h [new file with mode: 0644]
src/streams/stransport.c [new file with mode: 0644]
src/streams/stransport.h [new file with mode: 0644]
src/streams/tls.c [new file with mode: 0644]
src/streams/tls.h [new file with mode: 0644]
src/submodule.c
src/submodule.h
src/sysdir.c
src/sysdir.h
src/tag.c
src/tag.h
src/thread-utils.c
src/thread-utils.h
src/tls_stream.c [deleted file]
src/tls_stream.h [deleted file]
src/trace.c
src/trace.h
src/trailer.c [new file with mode: 0644]
src/transaction.c
src/transport.c
src/transports/auth.c
src/transports/auth.h
src/transports/auth_negotiate.c
src/transports/auth_negotiate.h
src/transports/cred.c
src/transports/cred.h
src/transports/cred_helpers.c
src/transports/git.c
src/transports/http.c
src/transports/http.h [new file with mode: 0644]
src/transports/local.c
src/transports/smart.c
src/transports/smart.h
src/transports/smart_protocol.c
src/transports/ssh.c
src/transports/ssh.h
src/transports/winhttp.c
src/tree-cache.c
src/tree-cache.h
src/tree.c
src/tree.h
src/tsort.c
src/unix/map.c
src/unix/posix.h
src/unix/pthread.h
src/unix/realpath.c
src/util.c
src/util.h
src/varint.c
src/varint.h
src/vector.c
src/win32/dir.c
src/win32/dir.h
src/win32/error.c
src/win32/error.h
src/win32/findfile.c
src/win32/findfile.h
src/win32/map.c
src/win32/mingw-compat.h
src/win32/msvc-compat.h
src/win32/path_w32.c
src/win32/path_w32.h
src/win32/posix.h
src/win32/posix_w32.c
src/win32/precompiled.h
src/win32/reparse.h
src/win32/thread.c
src/win32/thread.h
src/win32/utf-conv.c
src/win32/utf-conv.h
src/win32/w32_buffer.c
src/win32/w32_buffer.h
src/win32/w32_crtdbg_stacktrace.c
src/win32/w32_crtdbg_stacktrace.h
src/win32/w32_stack.c
src/win32/w32_stack.h
src/win32/w32_util.h
src/win32/win32-compat.h
src/worktree.c
src/worktree.h
src/xdiff/xdiff.h
src/xdiff/xdiffi.c
src/xdiff/xdiffi.h
src/xdiff/xemit.c
src/xdiff/xemit.h
src/xdiff/xhistogram.c
src/xdiff/xinclude.h
src/xdiff/xmacros.h
src/xdiff/xmerge.c
src/xdiff/xpatience.c
src/xdiff/xprepare.c
src/xdiff/xprepare.h
src/xdiff/xtypes.h
src/xdiff/xutils.c
src/xdiff/xutils.h
src/zstream.c
src/zstream.h
tests/CMakeLists.txt [new file with mode: 0644]
tests/attr/ignore.c
tests/buf/percent.c [new file with mode: 0644]
tests/checkout/head.c
tests/checkout/tree.c
tests/clar.c
tests/clar/print.h
tests/commit/signature.c
tests/config/backend.c
tests/config/conditionals.c [new file with mode: 0644]
tests/config/configlevel.c
tests/config/include.c
tests/config/multivar.c
tests/config/read.c
tests/config/readonly.c
tests/config/write.c
tests/core/stream.c
tests/core/string.c
tests/core/structinit.c
tests/diff/blob.c
tests/diff/parse.c
tests/diff/patchid.c [new file with mode: 0644]
tests/diff/rename.c
tests/fetchhead/nonetwork.c
tests/generate.py
tests/index/filemodes.c
tests/index/tests.c
tests/iterator/iterator_helpers.c
tests/iterator/workdir.c
tests/main.c
tests/merge/conflict_data.h
tests/merge/files.c
tests/merge/trees/recursive.c
tests/merge/workdir/recursive.c
tests/merge/workdir/submodules.c
tests/message/trailer.c [new file with mode: 0644]
tests/network/fetchlocal.c
tests/notes/notes.c
tests/object/tree/write.c
tests/odb/backend/mempack.c [new file with mode: 0644]
tests/odb/backend/simple.c
tests/odb/largefiles.c [new file with mode: 0644]
tests/odb/loose.c
tests/online/clone.c
tests/online/remotes.c
tests/pack/indexer.c
tests/patch/parse.c
tests/patch/patch_common.h
tests/perf/merge.c
tests/precompiled.c [new file with mode: 0644]
tests/precompiled.h [new file with mode: 0644]
tests/rebase/submodule.c
tests/refs/branches/create.c
tests/refs/branches/move.c
tests/refs/crashes.c
tests/refs/createwithlog.c [deleted file]
tests/refs/iterator.c
tests/refs/list.c
tests/refs/peel.c
tests/refs/reflog/messages.c [new file with mode: 0644]
tests/refs/reflog/reflog.c
tests/refs/reflog/reflog_helpers.c [new file with mode: 0644]
tests/refs/reflog/reflog_helpers.h [new file with mode: 0644]
tests/refs/rename.c
tests/refs/settargetwithlog.c [deleted file]
tests/repo/head.c
tests/repo/init.c
tests/repo/open.c
tests/resources/merge-recursive/.gitted/objects/03/9d0da126f24b819a5a38186249c7f96be3824c [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/05/63b7706dcdcf94bc0c02cd96c137940278eca9 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/08/f01e1bff7e442d574eb221913515b4bd27ccd6 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/0b/beee1982b493330e375a85bbfddaba3d561556 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/0c/e202f64fa8356c1a32835fce4058ca76b0c7ed [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/0e/c39d71c1b074905350ce20ce3f0629f737a2a9 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/17/946ad3088f931102e5d81f94cf2825fc188953 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/18/2d0d250d1d7adcc60c178be5be98358b3a2fd1 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/1b/c7bcccf4bbdc8bfba2331a37ad5e9cf1dd321c [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/1b/de1883de4977ea3e664b315da951d1f614c3b1 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/23/b427bf6278724433e64ef4cf6dc166c4f2e246 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/23/cf2687a9327d55abbbd788ff04fa932072aebc [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/26/d3c94459b4faa08f009b15867993ca34153592 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/2c/ba583804a4a6fad1baf97c959be447238d1489 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/2e/7ae0d42fb7b6126f6a08ac6314ac07833a52f6 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/30/39c07db695c8c99d0a7c7e32f0afe40eae0be0 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/35/8efd6f589384fa8baf92234db9c7899a53916e [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/35/dda4f3f9b3794d92a46d908790e550ed100eae [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/36/71e42c8c8302d1a71c0ed7bf2b0a938e9e20f9 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/39/78944e4cd53edcc10a170ab2ff142f7295b958 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/3a/0dc89a8bd20e74fae69d2e038b47360fafb02e [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/3a/8c70144d0334721154b1e0529716b368483d6f [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/3e/eff81b57a0ac15a5ab6bb3a8e92511a01a429c [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/40/9f5d072decec684331672f2d6c0a9bc3640adb [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/44/faf5fba1af850dae54f8b2345938d3c7ae479f [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/4a/06b258fed8a4d15967ec4253ae7366b70f727d [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/4c/62e9482ed42c1a6d08891906b26126daa4a8f5 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/50/4dd93fb5b9c2a28c094c6e84ef0606de1e9b5c [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/50/dfa64a56b488fe8082371b182c8a3e3c942332 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/51/135c5884d7dd132fef3b432cca5826bab98f37 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/51/60ab78c1973dcd7cdebe2345dc8fcfc755e76d [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/56/fcbad344aafe519bafcc33c87b8e64849d82ab [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/5a/47615db824433f816ba62217dda6d46c5a7640 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/5c/27b5f7c6f6dd4e5b4d64976741d56c2df8f48a [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/5d/998d5f278aff5693711bc48f6852aac4b603ad [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/61/6d1209afac499b005f68309e1593b44899b054 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/71/c50785d8d512293bd3af838b131f3da5829ebc [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/75/afa96db00c26c6ebf3b377615b4e2a20563ee4 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/76/6afbfd7d42f757f1fac9ea550c9fcbc8041b89 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/7c/61830f8b8632665bb44ae5d219f520f5aa5bb4 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/7c/9a30d8dcee320a3b1f9ed10b582479faa9d3a1 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/7e/2d2bad4fc21f2832ca2afd48b1f95ab37ffb92 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/7e/70a7872576bba7e299cde45abb7da1e4d7ba81 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/7f/9c1d78d760cbfa99273bc1ef642d994c6baa5c [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/81/60cb53660b86c954144b8dbbb0b6e4db4ba6ba [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/8f/1b918542a5fe9b3bb7a8770a7525ad5b3b5864 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/97/5dd228fd1b0cacf2988167088fd1190c9ac0f5 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/98/5b725cf91c6861b5e7a419415d03cbcf5f16ca [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/98/cacbdd1fac7bbab54a6c7c97aa2103219e08b8 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/99/754e36599906b81b917447280c4918269e14ff [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/9a/228c1ee87f286202ec9a25de837a18550013b5 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/9c/dde216049c6a5ccddac0ad81f604419d8990ed [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/a0/ce8909834f389b4f8be6a6ec420868422d40a1 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/a2/817ed0e8ca6fe52bf0a20b2f50eb94b9ea5415 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/a3/5aa65d86215fce909fc0bcce8949d12becba44 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/a3/ca4c462e93fee824c8ad500917ae34b800dea4 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/a6/64873b1c0b9a1ed300f8644dde536fdaa3a34f [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/a9/9bf55117ab1958171fccfeb19885f707bd08fd [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/b0/1de62cf11945685c98ec671edabdff3e90ddc5 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/b0/4823b75c8220b89c2f8da54709cda262304cd3 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/b1/71224a4f604b6091072007765419b14c232c1d [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/b2/908343e3c16249d0036dd444fc0d4662cd8c0e [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/b6/bd0f9952f396e757d3f91e08c59a7e91707201 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/b7/de2b52ba055688061355fad1599a5d214ce8f8 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/b8/a3b657edcf31e6365a2f1c45d45e6c9ebe8f02 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/ba/9dcfe079848e8e5c1b53bc3b6e47ff57f6e481 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/bb/4e0014fb09d24312f0af37c7a45e5488f19510 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/c0/dcb4bfcd86e65a822090aa7a0455413828886b [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/c4/44758b02d4af6e3145ac2fc0e3ed02199cf7ec [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/c7/f3257db72e885d6612080c003e0f2ef480e0c4 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/ce/0d744cd2e18eacf883d43471636f231c0995e3 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/d0/97bcf99adb1022a6b7d2e94fed2031ebd9d89c [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/d0/c9bd6e2a3e327d81a32de51201d3bd58909f7c [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/d3/482dbdca5bb83aaf3e3768359855d55aef84d7 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/d5/015f9436b2d8c842bf6616e7cf5bc54eb79ced [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/db/51adf2b699eed93e883d6425f5e6c50165a9c2 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/db/7e2af8ca83b8943adce7ba37d85f8fe7d7d2a9 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/de/de92a05a0841faa8e4ad6700285cd208184458 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/e0/15ebd79a72a88b9291df11771caf56f463e8f9 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/e5/20e6aaf8d1e68a433e29d4360c1e74aa4b24d1 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/e6/269ce9017816d67c7189a58b6d0d22bf4b8a1a [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/e9/30c8c67848df4aa66319c5752fab6b8fdec765 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/ea/3521485adfa0b0373deaaa06db9218a22edae8 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/f0/856993e005c0d8ed2dc7cdc222cc1d89fb3c77 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/f2/9ccca75754d8476e5dad8cf250e03d43fe9e6c [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/f3/2c284f537ff1a55d3cbfe9a37d431b6edfadc2 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/f4/c149e7d0983e90e9ee802ff57ae3c905ba63da [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/f6/5de1834f57708e76d8dc25502b7f1ecbcce162 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/objects/f9/c04e4e9d4aaf1e6fe7478a7cc0756554974c2b [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/refs/heads/branchJ-1 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/refs/heads/branchJ-2 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/refs/heads/branchK-1 [new file with mode: 0644]
tests/resources/merge-recursive/.gitted/refs/heads/branchK-2 [new file with mode: 0644]
tests/resources/merge-resolve/.gitted/objects/50/c5dc8cdfe40c688eb0a0e23be54dd57cae2e78 [new file with mode: 0644]
tests/resources/merge-resolve/.gitted/objects/7a/a825857f87aea74ddf13d954568aa30dfcdeb4 [new file with mode: 0644]
tests/resources/merge-resolve/.gitted/refs/heads/delete-submodule [new file with mode: 0644]
tests/resources/rebase-submodule/.gitmodules [deleted file]
tests/resources/rebase-submodule/gitmodules [new file with mode: 0644]
tests/resources/rebase-submodule/my-submodule [deleted submodule]
tests/resources/renames/.gitted/objects/2c/136d294960f7d939f1ed1903f1ced78b874c87 [new file with mode: 0644]
tests/resources/renames/.gitted/objects/84/d8efa38af7ace2b302de0adbda16b1f1cd2e1b [new file with mode: 0644]
tests/resources/renames/.gitted/objects/89/7dda8ecb7fa2e092bc3f9e2a179d7c1b0364db [new file with mode: 0644]
tests/resources/renames/.gitted/objects/95/ceb126bf79e76020d8879a8b0d50a73307a97f [new file with mode: 0644]
tests/resources/renames/.gitted/objects/be/053a189b0bbde545e0a3f59ce00b46ad29ce0d [new file with mode: 0644]
tests/resources/renames/.gitted/refs/heads/delete-and-rename [new file with mode: 0644]
tests/resources/renames/.gitted/refs/heads/rewrite-and-delete [new file with mode: 0644]
tests/resources/submodules.git/HEAD [new file with mode: 0644]
tests/resources/submodules.git/config [new file with mode: 0644]
tests/resources/submodules.git/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e [new file with mode: 0644]
tests/resources/submodules.git/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357 [new file with mode: 0644]
tests/resources/submodules.git/objects/97/896810b3210244a62a82458b8e0819ecfc6850 [new file with mode: 0644]
tests/resources/submodules.git/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888 [new file with mode: 0644]
tests/resources/submodules.git/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818 [new file with mode: 0644]
tests/resources/submodules.git/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae [new file with mode: 0644]
tests/resources/submodules.git/objects/info/packs [new file with mode: 0644]
tests/resources/submodules.git/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idx [new file with mode: 0644]
tests/resources/submodules.git/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack [new file with mode: 0644]
tests/resources/submodules.git/refs/heads/master [new file with mode: 0644]
tests/resources/testrepo/.gitted/objects/57/43a3ef145d3638a0fa28233ca92897117ad74f [new file with mode: 0644]
tests/resources/testrepo/.gitted/objects/f9/ed4af42472941da45a3ce44458455ed227a6be [new file with mode: 0644]
tests/resources/testrepo/.gitted/refs/heads/executable [new file with mode: 0644]
tests/revwalk/basic.c
tests/status/ignore.c
tests/status/renames.c
tests/status/worktree.c
tests/submodule/lookup.c
tests/transport/register.c

index af38252ce511437878c593bca1801decc7987062..a4c8e91dfb57791f1fe10af4e4ce0504a7733d00 100644 (file)
@@ -17,20 +17,28 @@ env:
   - secure: "YnhS+8n6B+uoyaYfaJ3Lei7cSJqHDPiKJCKFIF2c87YDfmCvAJke8QtE7IzjYDs7UFkTCM4ox+ph2bERUrxZbSCyEkHdjIZpKuMJfYWja/jgMqTMxdyOH9y8JLFbZsSXDIXDwqBlC6vVyl1fP90M35wuWcNTs6tctfVWVofEFbs="
   - GITTEST_INVASIVE_FS_SIZE=1
  matrix:
-  - OPTIONS="-DTHREADSAFE=ON -DCMAKE_BUILD_TYPE=Release"
-  - OPTIONS="-DTHREADSAFE=OFF -DBUILD_EXAMPLES=ON"
-
-addons:
-  apt:
-    packages:
-    - cmake
-    - libssh2-1-dev
-    - openssh-client
-    - openssh-server
-    - valgrind
+  - OPTIONS="-DTHREADSAFE=ON -DENABLE_TRACE=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_WERROR=ON"
+  - OPTIONS="-DTHREADSAFE=OFF -DBUILD_EXAMPLES=ON -DENABLE_WERROR=ON"
 
+dist: trusty
 sudo: false
 
+addons:
+ apt:
+  sources:
+   - sourceline: 'deb https://dl.bintray.com/libgit2/ci-dependencies trusty libgit2deps'
+     key_url: 'https://bintray.com/user/downloadSubjectPublicKey?username=bintray'
+  packages:
+   cmake
+   curl
+   libcurl3
+   libcurl3-gnutls
+   libcurl4-gnutls-dev
+   libssh2-1-dev
+   openssh-client
+   openssh-server
+   valgrind
+
 matrix:
  fast_finish: true
  exclude:
@@ -40,16 +48,18 @@ matrix:
    - compiler: gcc
      env: COVERITY=1
      os: linux
+     dist: trusty
    - compiler: gcc
      env:
        - VALGRIND=1
          OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=OFF -DDEBUG_POOL=ON -DCMAKE_BUILD_TYPE=Debug"
      os: linux
+     dist: trusty
  allow_failures:
    - env: COVERITY=1
 
 install:
-  - if [ "$TRAVIS_OS_NAME" = "osx" ]; then ./script/install-deps-${TRAVIS_OS_NAME}.sh; fi
+  - if [ -f ./script/install-deps-${TRAVIS_OS_NAME}.sh ]; then ./script/install-deps-${TRAVIS_OS_NAME}.sh; fi
 
 # Run the Build script and tests
 script:
diff --git a/AUTHORS b/AUTHORS
index 61e2113ec925790c75009e6425781a42796e24e7..458ff06c361e26bb06747195dfdfbc2a2556c5f3 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -22,6 +22,7 @@ Dmitry Kakurin
 Dmitry Kovega
 Emeric Fermas
 Emmanuel Rodriguez
+Eric Myhre
 Florian Forster
 Holger Weiss
 Ingmar Vanhassel
index 24ecba4267c3293e1b8433052ff708c090964048..ba0cb4ea89e869a1dbc7438578b2d05021a3b007 100644 (file)
@@ -1,4 +1,4 @@
-v0.26 + 1
+v0.27 + 1
 ---------
 
 ### Changes or improvements
@@ -9,6 +9,122 @@ v0.26 + 1
 
 ### Breaking API changes
 
+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
 -----
 
index 4783e3ef98fb74d581193c2d5bb4108232f4eeab..2ca5354a7f999ff9cacafb07f590c016e9aca40d 100644 (file)
 # > cmake --build . --target install
 
 PROJECT(libgit2 C)
-CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
+CMAKE_MINIMUM_REQUIRED(VERSION 2.8.11)
 CMAKE_POLICY(SET CMP0015 NEW)
+IF (POLICY CMP0051)
+       CMAKE_POLICY(SET CMP0051 NEW)
+ENDIF()
+IF (POLICY CMP0042)
+       CMAKE_POLICY(SET CMP0042 NEW)
+ENDIF()
 
 # Add find modules to the path
-SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/")
+SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${libgit2_SOURCE_DIR}/cmake/Modules/")
 
 INCLUDE(CheckLibraryExists)
 INCLUDE(CheckFunctionExists)
@@ -24,6 +30,11 @@ INCLUDE(CheckSymbolExists)
 INCLUDE(CheckStructHasMember)
 INCLUDE(AddCFlagIfSupported)
 INCLUDE(FindPkgConfig)
+INCLUDE(FindThreads)
+INCLUDE(FindStatNsec)
+INCLUDE(IdeSplitSources)
+INCLUDE(FeatureSummary)
+INCLUDE(EnableWarnings)
 
 # Build options
 #
@@ -37,22 +48,23 @@ OPTION( PROFILE                             "Generate profiling information"                OFF )
 OPTION( ENABLE_TRACE           "Enables tracing support"                               OFF )
 OPTION( LIBGIT2_FILENAME       "Name of the produced binary"                   OFF )
 
-OPTION( USE_SHA1DC                     "Use SHA-1 with collision detection"    OFF )
-OPTION( USE_ICONV                      "Link with and use iconv library"               OFF )
+SET(SHA1_BACKEND "CollisionDetection" CACHE STRING "Backend to use for SHA1. One of Generic, OpenSSL, Win32, CommonCrypto, CollisionDetection. ")
 OPTION( USE_SSH                                "Link with libssh to enable SSH support" ON )
+OPTION( USE_HTTPS                      "Enable HTTPS support. Can be set to a specific backend"        ON )
 OPTION( USE_GSSAPI                     "Link with libgssapi for SPNEGO auth"   OFF )
 OPTION( VALGRIND                       "Configure build for valgrind"                  OFF )
 OPTION( CURL                   "Use curl for HTTP if available" ON)
+OPTION( USE_EXT_HTTP_PARSER            "Use system HTTP_Parser if available" ON)
 OPTION( DEBUG_POOL                     "Enable debug pool allocator"                   OFF )
+OPTION( ENABLE_WERROR                  "Enable compilation with -Werror"               OFF )
+OPTION( USE_BUNDLED_ZLIB    "Use the bundled version of zlib"       OFF )
 
-IF(DEBUG_POOL)
-       ADD_DEFINITIONS(-DGIT_DEBUG_POOL)
+IF (UNIX AND NOT APPLE)
+       OPTION( ENABLE_REPRODUCIBLE_BUILDS      "Enable reproducible builds"                    OFF )
 ENDIF()
 
-IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
-       SET( USE_ICONV ON )
-       FIND_PACKAGE(Security)
-       FIND_PACKAGE(CoreFoundation REQUIRED)
+IF (APPLE)
+       OPTION( USE_ICONV                       "Link with and use iconv library"                ON )
 ENDIF()
 
 IF(MSVC)
@@ -71,10 +83,6 @@ IF(MSVC)
        # 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 )
-
-       ADD_DEFINITIONS(-D_SCL_SECURE_NO_WARNINGS)
-       ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE)
-       ADD_DEFINITIONS(-D_CRT_NONSTDC_NO_DEPRECATE)
 ENDIF()
 
 
@@ -89,313 +97,21 @@ IF(MSVC)
        OPTION(MSVC_CRTDBG "Enable CRTDBG memory leak reporting" OFF)
 ENDIF()
 
-IF (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
-       OPTION( USE_OPENSSL                     "Link with and use openssl library"             ON )
-ENDIF()
-
-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()
-
-IF (HAVE_STRUCT_STAT_NSEC OR WIN32)
-       OPTION( USE_NSEC                "Care about sub-second file mtimes and ctimes"  ON  )
-ENDIF()
-
-# This variable will contain the libraries we need to put into
-# libgit2.pc's Requires.private. That is, what we're linking to or
-# what someone who's statically linking us needs to link to.
-SET(LIBGIT2_PC_REQUIRES "")
-# This will be set later if we use the system's http-parser library or
-# use iconv (OSX) and will be written to the Libs.private field in the
-# pc file.
-SET(LIBGIT2_PC_LIBS "")
-
-# Installation paths
-#
-SET(BIN_INSTALL_DIR bin CACHE PATH "Where to install binaries to.")
-SET(LIB_INSTALL_DIR lib CACHE PATH "Where to install libraries to.")
-SET(INCLUDE_INSTALL_DIR include CACHE PATH "Where to install headers to.")
-
-# Set a couple variables to be substituted inside the .pc file.
-# We can't just use LIB_INSTALL_DIR in the .pc file, as passing them as absolue
-# or relative paths is both valid and supported by cmake.
-SET (PKGCONFIG_PREFIX ${CMAKE_INSTALL_PREFIX})
-
-IF(IS_ABSOLUTE ${LIB_INSTALL_DIR})
-  SET (PKGCONFIG_LIBDIR ${LIB_INSTALL_DIR})
-ELSE(IS_ABSOLUTE ${LIB_INSTALL_DIR})
-  SET (PKGCONFIG_LIBDIR "\${prefix}/${LIB_INSTALL_DIR}")
-ENDIF (IS_ABSOLUTE ${LIB_INSTALL_DIR})
-
-IF(IS_ABSOLUTE ${INCLUDE_INSTALL_DIR})
-  SET (PKGCONFIG_INCLUDEDIR ${INCLUDE_INSTALL_DIR})
-ELSE(IS_ABSOLUTE ${INCLUDE_INSTALL_DIR})
-  SET (PKGCONFIG_INCLUDEDIR "\${prefix}/${INCLUDE_INSTALL_DIR}")
-ENDIF(IS_ABSOLUTE ${INCLUDE_INSTALL_DIR})
-
-FUNCTION(TARGET_OS_LIBRARIES target)
-       IF(WIN32)
-               TARGET_LINK_LIBRARIES(${target} ws2_32)
-       ELSEIF(CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
-               TARGET_LINK_LIBRARIES(${target} socket nsl)
-               LIST(APPEND LIBGIT2_PC_LIBS "-lsocket" "-lnsl")
-               SET(LIBGIT2_PC_LIBS ${LIBGIT2_PC_LIBS} PARENT_SCOPE)
-       ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Haiku")
-               TARGET_LINK_LIBRARIES(${target} network)
-               LIST(APPEND LIBGIT2_PC_LIBS "-lnetwork")
-               SET(LIBGIT2_PC_LIBS ${LIBGIT2_PC_LIBS} PARENT_SCOPE)
-       ENDIF()
-       CHECK_LIBRARY_EXISTS(rt clock_gettime "time.h" NEED_LIBRT)
-       IF(NEED_LIBRT)
-               TARGET_LINK_LIBRARIES(${target} rt)
-               LIST(APPEND LIBGIT2_PC_LIBS "-lrt")
-               SET(LIBGIT2_PC_LIBS ${LIBGIT2_PC_LIBS} PARENT_SCOPE)
-       ENDIF()
-
-       IF(THREADSAFE)
-               TARGET_LINK_LIBRARIES(${target} ${CMAKE_THREAD_LIBS_INIT})
-               LIST(APPEND LIBGIT2_PC_LIBS ${CMAKE_THREAD_LIBS_INIT})
-               SET(LIBGIT2_PC_LIBS ${LIBGIT2_PC_LIBS} PARENT_SCOPE)
-       ENDIF()
-ENDFUNCTION()
-
-# 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_clar 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 ${CMAKE_CURRENT_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()
-
-FILE(STRINGS "include/git2/version.h" GIT2_HEADER REGEX "^#define LIBGIT2_VERSION \"[^\"]*\"$")
+FILE(STRINGS "${libgit2_SOURCE_DIR}/include/git2/version.h" GIT2_HEADER REGEX "^#define LIBGIT2_VERSION \"[^\"]*\"$")
 
 STRING(REGEX REPLACE "^.*LIBGIT2_VERSION \"([0-9]+).*$" "\\1" LIBGIT2_VERSION_MAJOR "${GIT2_HEADER}")
 STRING(REGEX REPLACE "^.*LIBGIT2_VERSION \"[0-9]+\\.([0-9]+).*$" "\\1" LIBGIT2_VERSION_MINOR  "${GIT2_HEADER}")
 STRING(REGEX REPLACE "^.*LIBGIT2_VERSION \"[0-9]+\\.[0-9]+\\.([0-9]+).*$" "\\1" LIBGIT2_VERSION_REV "${GIT2_HEADER}")
 SET(LIBGIT2_VERSION_STRING "${LIBGIT2_VERSION_MAJOR}.${LIBGIT2_VERSION_MINOR}.${LIBGIT2_VERSION_REV}")
 
-FILE(STRINGS "include/git2/version.h" GIT2_HEADER_SOVERSION REGEX "^#define LIBGIT2_SOVERSION [0-9]+$")
+FILE(STRINGS "${libgit2_SOURCE_DIR}/include/git2/version.h" GIT2_HEADER_SOVERSION REGEX "^#define LIBGIT2_SOVERSION [0-9]+$")
 STRING(REGEX REPLACE "^.*LIBGIT2_SOVERSION ([0-9]+)$" "\\1" LIBGIT2_SOVERSION "${GIT2_HEADER_SOVERSION}")
 
-# Find required dependencies
-INCLUDE_DIRECTORIES(src include)
-
-IF (SECURITY_FOUND)
-  # OS X 10.7 and older do not have some functions we use, fall back to OpenSSL there
-  CHECK_LIBRARY_EXISTS("${SECURITY_DIRS}" SSLCreateContext "Security/SecureTransport.h" HAVE_NEWER_SECURITY)
-  IF (HAVE_NEWER_SECURITY)
-    MESSAGE("-- Found Security ${SECURITY_DIRS}")
-    LIST(APPEND LIBGIT2_PC_LIBS "-framework Security")
-  ELSE()
-    MESSAGE("-- Security framework is too old, falling back to OpenSSL")
-    SET(SECURITY_FOUND "NO")
-    SET(SECURITY_DIRS "")
-    SET(SECURITY_DIR "")
-    SET(USE_OPENSSL "ON")
-  ENDIF()
-ENDIF()
-
-IF (COREFOUNDATION_FOUND)
-  MESSAGE("-- Found CoreFoundation ${COREFOUNDATION_DIRS}")
-  LIST(APPEND LIBGIT2_PC_LIBS "-framework CoreFoundation")
-ENDIF()
-
-
-IF (WIN32 AND EMBED_SSH_PATH)
-       FILE(GLOB SRC_SSH "${EMBED_SSH_PATH}/src/*.c")
-       INCLUDE_DIRECTORIES("${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\"")
-       ADD_DEFINITIONS(-DGIT_SSH)
-ENDIF()
-
-IF (WIN32 AND WINHTTP)
-       ADD_DEFINITIONS(-DGIT_WINHTTP)
-       ADD_DEFINITIONS(-DGIT_HTTPS)
-
-       # 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)
-               FIND_PROGRAM(DLLTOOL dlltool CMAKE_FIND_ROOT_PATH_BOTH)
-               IF (NOT DLLTOOL)
-                       MESSAGE(FATAL_ERROR "Could not find dlltool command")
-               ENDIF ()
-
-               SET(LIBWINHTTP_PATH "${CMAKE_CURRENT_BINARY_DIR}/deps/winhttp")
-               FILE(MAKE_DIRECTORY ${LIBWINHTTP_PATH})
-
-               IF (CMAKE_SIZEOF_VOID_P EQUAL 8)
-                       set(WINHTTP_DEF "${CMAKE_CURRENT_SOURCE_DIR}/deps/winhttp/winhttp64.def")
-               ELSE()
-                       set(WINHTTP_DEF "${CMAKE_CURRENT_SOURCE_DIR}/deps/winhttp/winhttp.def")
-               ENDIF()
-
-               ADD_CUSTOM_COMMAND(
-                       OUTPUT ${LIBWINHTTP_PATH}/libwinhttp.a
-                       COMMAND ${DLLTOOL} -d ${WINHTTP_DEF} -k -D winhttp.dll -l libwinhttp.a
-                       DEPENDS ${WINHTTP_DEF}
-                       WORKING_DIRECTORY ${LIBWINHTTP_PATH}
-               )
-
-               SET_SOURCE_FILES_PROPERTIES(
-                       ${CMAKE_CURRENT_SOURCE_DIR}/src/transports/winhttp.c
-                       PROPERTIES OBJECT_DEPENDS ${LIBWINHTTP_PATH}/libwinhttp.a
-               )
-
-               INCLUDE_DIRECTORIES(deps/winhttp)
-               LINK_DIRECTORIES(${LIBWINHTTP_PATH})
-       ENDIF ()
-
-       LINK_LIBRARIES(winhttp rpcrt4 crypt32 ole32)
-       LIST(APPEND LIBGIT2_PC_LIBS "-lwinhttp" "-lrpcrt4" "-lcrypt32" "-lole32")
-ELSE ()
-       IF (CURL)
-               PKG_CHECK_MODULES(CURL libcurl)
-       ENDIF ()
-
-       IF (NOT AMIGA AND USE_OPENSSL)
-               FIND_PACKAGE(OpenSSL)
-       ENDIF ()
-
-       IF (CURL_FOUND)
-               ADD_DEFINITIONS(-DGIT_CURL)
-               INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIRS})
-               LINK_DIRECTORIES(${CURL_LIBRARY_DIRS})
-               LINK_LIBRARIES(${CURL_LIBRARIES})
-               LIST(APPEND LIBGIT2_PC_LIBS ${CURL_LDFLAGS})
-       ENDIF()
-ENDIF()
-
-# Specify sha1 implementation
-IF (USE_SHA1DC)
-       ADD_DEFINITIONS(-DGIT_SHA1_COLLISIONDETECT)
-       ADD_DEFINITIONS(-DSHA1DC_NO_STANDARD_INCLUDES=1)
-       ADD_DEFINITIONS(-DSHA1DC_CUSTOM_INCLUDE_SHA1_C=\"common.h\")
-       ADD_DEFINITIONS(-DSHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C=\"common.h\")
-       FILE(GLOB SRC_SHA1 src/hash/hash_collisiondetect.c src/hash/sha1dc/*)
-ELSEIF (WIN32 AND NOT MINGW)
-       ADD_DEFINITIONS(-DGIT_SHA1_WIN32)
-       FILE(GLOB SRC_SHA1 src/hash/hash_win32.c)
-ELSEIF (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
-       ADD_DEFINITIONS(-DGIT_SHA1_COMMON_CRYPTO)
-ELSEIF (OPENSSL_FOUND)
-       ADD_DEFINITIONS(-DGIT_SHA1_OPENSSL)
-       IF (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
-               LIST(APPEND LIBGIT2_PC_LIBS "-lssl")
-       ELSE()
-               SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} openssl")
-       ENDIF ()
-ELSE()
-       FILE(GLOB SRC_SHA1 src/hash/hash_generic.c)
-ENDIF()
-
-# Enable tracing
-IF (ENABLE_TRACE STREQUAL "ON")
-       ADD_DEFINITIONS(-DGIT_TRACE)
-ENDIF()
-
-# Include POSIX regex when it is required
-IF(WIN32 OR AMIGA OR CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
-       INCLUDE_DIRECTORIES(deps/regex)
-       SET(SRC_REGEX deps/regex/regex.c)
-ENDIF()
-
-# Optional external dependency: http-parser
-FIND_PACKAGE(HTTP_Parser)
-IF (HTTP_PARSER_FOUND AND HTTP_PARSER_VERSION_MAJOR EQUAL 2)
-       INCLUDE_DIRECTORIES(${HTTP_PARSER_INCLUDE_DIRS})
-       LINK_LIBRARIES(${HTTP_PARSER_LIBRARIES})
-       LIST(APPEND LIBGIT2_PC_LIBS "-lhttp_parser")
-ELSE()
-       MESSAGE(STATUS "http-parser version 2 was not found; using bundled 3rd-party sources.")
-       INCLUDE_DIRECTORIES(deps/http-parser)
-       FILE(GLOB SRC_HTTP deps/http-parser/*.c deps/http-parser/*.h)
-ENDIF()
-
-# Optional external dependency: zlib
-FIND_PACKAGE(ZLIB)
-IF (ZLIB_FOUND)
-       INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIRS})
-       LINK_LIBRARIES(${ZLIB_LIBRARIES})
-       IF(APPLE OR CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
-               LIST(APPEND LIBGIT2_PC_LIBS "-lz")
-       ELSE()
-               SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} zlib")
-       ENDIF()
-ELSE()
-       MESSAGE(STATUS "zlib was not found; using bundled 3rd-party sources." )
-       INCLUDE_DIRECTORIES(deps/zlib)
-       ADD_DEFINITIONS(-DNO_VIZ -DSTDC -DNO_GZIP)
-       FILE(GLOB SRC_ZLIB deps/zlib/*.c deps/zlib/*.h)
-ENDIF()
-
-# Optional external dependency: libssh2
-IF (USE_SSH)
-       PKG_CHECK_MODULES(LIBSSH2 libssh2)
-ENDIF()
-IF (LIBSSH2_FOUND)
-       ADD_DEFINITIONS(-DGIT_SSH)
-       INCLUDE_DIRECTORIES(${LIBSSH2_INCLUDE_DIRS})
-       LINK_DIRECTORIES(${LIBSSH2_LIBRARY_DIRS})
-       LIST(APPEND LIBGIT2_PC_LIBS ${LIBSSH2_LDFLAGS})
-       #SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} ${LIBSSH2_LDFLAGS}")
-       SET(SSH_LIBRARIES ${LIBSSH2_LIBRARIES})
-
-       CHECK_LIBRARY_EXISTS("${LIBSSH2_LIBRARIES}" libssh2_userauth_publickey_frommemory "${LIBSSH2_LIBRARY_DIRS}" HAVE_LIBSSH2_MEMORY_CREDENTIALS)
-       IF (HAVE_LIBSSH2_MEMORY_CREDENTIALS)
-               ADD_DEFINITIONS(-DGIT_SSH_MEMORY_CREDENTIALS)
-       ENDIF()
-ELSE()
-       MESSAGE(STATUS "LIBSSH2 not found. Set CMAKE_PREFIX_PATH if it is installed outside of the default search path.")
-ENDIF()
-
-# Optional external dependency: libgssapi
-IF (USE_GSSAPI)
-       FIND_PACKAGE(GSSAPI)
-ENDIF()
-IF (GSSAPI_FOUND)
-       ADD_DEFINITIONS(-DGIT_GSSAPI)
-ENDIF()
-
-# Optional external dependency: iconv
-IF (USE_ICONV)
-       FIND_PACKAGE(Iconv)
-ENDIF()
-IF (ICONV_FOUND)
-       ADD_DEFINITIONS(-DGIT_USE_ICONV)
-       INCLUDE_DIRECTORIES(${ICONV_INCLUDE_DIR})
-       LIST(APPEND LIBGIT2_PC_LIBS ${ICONV_LIBRARIES})
-ENDIF()
-
 # 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}")
 
@@ -417,8 +133,9 @@ IF (MSVC)
        ENDIF()
 
        IF (MSVC_CRTDBG)
-               SET(CRT_FLAG_DEBUG "${CRT_FLAG_DEBUG} /DGIT_MSVC_CRTDBG")
-               SET(CMAKE_C_STANDARD_LIBRARIES "${CMAKE_C_STANDARD_LIBRARIES}" "Dbghelp.lib")
+               SET(GIT_MSVC_CRTDBG 1)
+               SET(CRT_FLAG_DEBUG "${CRT_FLAG_DEBUG}")
+               SET(CMAKE_C_STANDARD_LIBRARIES "${CMAKE_C_STANDARD_LIBRARIES} Dbghelp.lib")
        ENDIF()
 
        # /Zi - Create debugging information
@@ -466,13 +183,17 @@ IF (MSVC)
        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}")
-
-       SET(WIN_RC "src/win32/git2.rc")
 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()
+
        SET(CMAKE_C_FLAGS "-D_GNU_SOURCE ${CMAKE_C_FLAGS}")
 
-       ADD_C_FLAG_IF_SUPPORTED(-Wall)
-       ADD_C_FLAG_IF_SUPPORTED(-Wextra)
+       ENABLE_WARNINGS(all)
+       ENABLE_WARNINGS(extra)
 
        IF (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
                SET(CMAKE_C_FLAGS "-std=c99 -D_POSIX_C_SOURCE=200112L -D__EXTENSIONS__ -D_POSIX_PTHREAD_SEMANTICS ${CMAKE_C_FLAGS}")
@@ -495,16 +216,17 @@ ELSE ()
                ADD_DEFINITIONS(-D__USE_MINGW_ANSI_STDIO=1)
        ENDIF ()
 
-       ADD_C_FLAG_IF_SUPPORTED(-Wdocumentation)
-       ADD_C_FLAG_IF_SUPPORTED(-Wno-missing-field-initializers)
-       ADD_C_FLAG_IF_SUPPORTED(-Wstrict-aliasing=2)
-       ADD_C_FLAG_IF_SUPPORTED(-Wstrict-prototypes)
-       ADD_C_FLAG_IF_SUPPORTED(-Wdeclaration-after-statement)
-       ADD_C_FLAG_IF_SUPPORTED(-Wno-unused-const-variable)
-       ADD_C_FLAG_IF_SUPPORTED(-Wno-unused-function)
+       ENABLE_WARNINGS(documentation)
+       DISABLE_WARNINGS(missing-field-initializers)
+       ENABLE_WARNINGS(strict-aliasing)
+       ENABLE_WARNINGS(strict-prototypes)
+       ENABLE_WARNINGS(declaration-after-statement)
+       ENABLE_WARNINGS(shift-count-overflow)
+       DISABLE_WARNINGS(unused-const-variable)
+       DISABLE_WARNINGS(unused-function)
 
        IF (APPLE) # Apple deprecated OpenSSL
-               ADD_C_FLAG_IF_SUPPORTED(-Wno-deprecated-declarations)
+           DISABLE_WARNINGS(deprecated-declarations)
        ENDIF()
 
        IF (PROFILE)
@@ -513,26 +235,6 @@ ELSE ()
        ENDIF ()
 ENDIF()
 
-CHECK_SYMBOL_EXISTS(regcomp_l "regex.h;xlocale.h" HAVE_REGCOMP_L)
-IF (HAVE_REGCOMP_L)
-       ADD_DEFINITIONS(-DHAVE_REGCOMP_L)
-ENDIF ()
-
-CHECK_FUNCTION_EXISTS(futimens HAVE_FUTIMENS)
-IF (HAVE_FUTIMENS)
-       ADD_DEFINITIONS(-DHAVE_FUTIMENS)
-ENDIF ()
-
-CHECK_FUNCTION_EXISTS(qsort_r HAVE_QSORT_R)
-IF (HAVE_QSORT_R)
-       ADD_DEFINITIONS(-DHAVE_QSORT_R)
-ENDIF ()
-
-CHECK_FUNCTION_EXISTS(qsort_s HAVE_QSORT_S)
-IF (HAVE_QSORT_S)
-       ADD_DEFINITIONS(-DHAVE_QSORT_S)
-ENDIF ()
-
 IF( NOT CMAKE_CONFIGURATION_TYPES )
        # Build Debug by default
        IF (NOT CMAKE_BUILD_TYPE)
@@ -543,177 +245,18 @@ ELSE()
        # that uses CMAKE_CONFIGURATION_TYPES and not CMAKE_BUILD_TYPE
 ENDIF()
 
-IF (SECURITY_FOUND)
-  ADD_DEFINITIONS(-DGIT_SECURE_TRANSPORT)
-  ADD_DEFINITIONS(-DGIT_HTTPS)
-  INCLUDE_DIRECTORIES(${SECURITY_INCLUDE_DIR})
-ENDIF ()
-
-IF (OPENSSL_FOUND)
-  ADD_DEFINITIONS(-DGIT_OPENSSL)
-  ADD_DEFINITIONS(-DGIT_HTTPS)
-  INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
-  SET(SSL_LIBRARIES ${OPENSSL_LIBRARIES})
-ENDIF()
-
-
-
-IF (THREADSAFE)
-       IF (NOT WIN32)
-               FIND_PACKAGE(Threads REQUIRED)
-       ENDIF()
-
-       ADD_DEFINITIONS(-DGIT_THREADS)
-ENDIF()
-
-IF (USE_NSEC)
-       ADD_DEFINITIONS(-DGIT_USE_NSEC)
-ENDIF()
-
-IF (HAVE_STRUCT_STAT_ST_MTIM)
-       ADD_DEFINITIONS(-DGIT_USE_STAT_MTIM)
-ELSEIF (HAVE_STRUCT_STAT_ST_MTIMESPEC)
-       ADD_DEFINITIONS(-DGIT_USE_STAT_MTIMESPEC)
-ELSEIF (HAVE_STRUCT_STAT_ST_MTIME_NSEC)
-       ADD_DEFINITIONS(-DGIT_USE_STAT_MTIME_NSEC)
-ENDIF()
-
-ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64)
+ADD_SUBDIRECTORY(src)
 
-# Collect sourcefiles
-FILE(GLOB SRC_H include/git2.h include/git2/*.h include/git2/sys/*.h)
-
-# On Windows use specific platform sources
-IF (WIN32 AND NOT CYGWIN)
-       ADD_DEFINITIONS(-DWIN32 -D_WIN32_WINNT=0x0501)
-       FILE(GLOB SRC_OS src/win32/*.c src/win32/*.h)
-ELSEIF (AMIGA)
-       ADD_DEFINITIONS(-DNO_ADDRINFO -DNO_READDIR_R -DNO_MMAP)
-ELSE()
-       IF (VALGRIND)
-               ADD_DEFINITIONS(-DNO_MMAP)
-       ENDIF()
-       FILE(GLOB SRC_OS src/unix/*.c src/unix/*.h)
-ENDIF()
-FILE(GLOB SRC_GIT2 src/*.c src/*.h src/transports/*.c src/transports/*.h src/xdiff/*.c src/xdiff/*.h)
-
-# Determine architecture of the machine
-IF (CMAKE_SIZEOF_VOID_P EQUAL 8)
-       ADD_DEFINITIONS(-DGIT_ARCH_64)
-ELSEIF (CMAKE_SIZEOF_VOID_P EQUAL 4)
-       ADD_DEFINITIONS(-DGIT_ARCH_32)
-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()
-
-# Compile and link libgit2
-ADD_LIBRARY(git2 ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SSH} ${SRC_SHA1} ${WIN_RC})
-TARGET_LINK_LIBRARIES(git2 ${SECURITY_DIRS})
-TARGET_LINK_LIBRARIES(git2 ${COREFOUNDATION_DIRS})
-TARGET_LINK_LIBRARIES(git2 ${SSL_LIBRARIES})
-TARGET_LINK_LIBRARIES(git2 ${SSH_LIBRARIES})
-TARGET_LINK_LIBRARIES(git2 ${GSSAPI_LIBRARIES})
-TARGET_LINK_LIBRARIES(git2 ${ICONV_LIBRARIES})
-TARGET_OS_LIBRARIES(git2)
-
-# 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(git2 PROPERTIES STATIC_LIBRARY_FLAGS "/MACHINE:x64")
-ENDIF()
-
-IDE_SPLIT_SOURCES(git2)
-
-IF (SONAME)
-       SET_TARGET_PROPERTIES(git2 PROPERTIES VERSION ${LIBGIT2_VERSION_STRING})
-       SET_TARGET_PROPERTIES(git2 PROPERTIES SOVERSION ${LIBGIT2_SOVERSION})
-       IF (LIBGIT2_FILENAME)
-               ADD_DEFINITIONS(-DLIBGIT2_FILENAME=\"${LIBGIT2_FILENAME}\")
-               SET_TARGET_PROPERTIES(git2 PROPERTIES OUTPUT_NAME ${LIBGIT2_FILENAME})
-       ELSEIF (DEFINED LIBGIT2_PREFIX)
-               SET_TARGET_PROPERTIES(git2 PROPERTIES PREFIX "${LIBGIT2_PREFIX}")
-       ENDIF()
-ENDIF()
-STRING(REPLACE ";" " " LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS}")
-CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/libgit2.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libgit2.pc @ONLY)
-
-IF (MSVC_IDE)
-   # Precompiled headers
-   SET_TARGET_PROPERTIES(git2 PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h")
-   SET_SOURCE_FILES_PROPERTIES(src/win32/precompiled.c COMPILE_FLAGS "/Ycprecompiled.h")
+# Tests
+IF (NOT MSVC)
+   IF (NOT BUILD_SHARED_LIBS)
+       SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
+   ENDIF()
 ENDIF ()
 
-# Install
-INSTALL(TARGETS git2
-       RUNTIME DESTINATION ${BIN_INSTALL_DIR}
-       LIBRARY DESTINATION ${LIB_INSTALL_DIR}
-       ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
-)
-INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/libgit2.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig )
-INSTALL(DIRECTORY include/git2 DESTINATION ${INCLUDE_INSTALL_DIR} )
-INSTALL(FILES include/git2.h DESTINATION ${INCLUDE_INSTALL_DIR} )
-
-# Tests
 IF (BUILD_CLAR)
-       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_CLAR=OFF to skip building the tests")
-       ENDIF()
-
-       SET(CLAR_FIXTURES "${CMAKE_CURRENT_SOURCE_DIR}/tests/resources/")
-       SET(CLAR_PATH "${CMAKE_CURRENT_SOURCE_DIR}/tests")
-       SET(CLAR_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/tests/resources" CACHE PATH "Path to test resources.")
-       ADD_DEFINITIONS(-DCLAR_FIXTURE_PATH=\"${CLAR_FIXTURES}\")
-       ADD_DEFINITIONS(-DCLAR_RESOURCES=\"${TEST_RESOURCES}\")
-       ADD_DEFINITIONS(-DCLAR_TMPDIR=\"libgit2_tests\")
-
-       INCLUDE_DIRECTORIES(${CLAR_PATH})
-       FILE(GLOB_RECURSE SRC_TEST ${CLAR_PATH}/*/*.c ${CLAR_PATH}/*/*.h)
-       SET(SRC_CLAR "${CLAR_PATH}/main.c" "${CLAR_PATH}/clar_libgit2.c" "${CLAR_PATH}/clar_libgit2_trace.c" "${CLAR_PATH}/clar_libgit2_timer.c" "${CLAR_PATH}/clar.c")
-
-       ADD_CUSTOM_COMMAND(
-               OUTPUT ${CLAR_PATH}/clar.suite
-               COMMAND ${PYTHON_EXECUTABLE} generate.py -f -xonline -xstress .
-               DEPENDS ${SRC_TEST}
-               WORKING_DIRECTORY ${CLAR_PATH}
-       )
-
-       SET_SOURCE_FILES_PROPERTIES(
-               ${CLAR_PATH}/clar.c
-               PROPERTIES OBJECT_DEPENDS ${CLAR_PATH}/clar.suite)
-
-       ADD_EXECUTABLE(libgit2_clar ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_CLAR} ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SSH} ${SRC_SHA1})
-
-       TARGET_LINK_LIBRARIES(libgit2_clar ${COREFOUNDATION_DIRS})
-       TARGET_LINK_LIBRARIES(libgit2_clar ${SECURITY_DIRS})
-       TARGET_LINK_LIBRARIES(libgit2_clar ${SSL_LIBRARIES})
-       TARGET_LINK_LIBRARIES(libgit2_clar ${SSH_LIBRARIES})
-       TARGET_LINK_LIBRARIES(libgit2_clar ${GSSAPI_LIBRARIES})
-       TARGET_LINK_LIBRARIES(libgit2_clar ${ICONV_LIBRARIES})
-       TARGET_OS_LIBRARIES(libgit2_clar)
-       IDE_SPLIT_SOURCES(libgit2_clar)
-
-       IF (MSVC_IDE)
-               # Precompiled headers
-               SET_TARGET_PROPERTIES(libgit2_clar PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h")
-       ENDIF ()
-
        ENABLE_TESTING()
-       IF (WINHTTP OR OPENSSL_FOUND OR SECURITY_FOUND)
-               ADD_TEST(libgit2_clar libgit2_clar -ionline -xclone::local::git_style_unc_paths -xclone::local::standard_unc_paths_are_written_git_style)
-       ELSE ()
-               ADD_TEST(libgit2_clar libgit2_clar -v -xclone::local::git_style_unc_paths -xclone::local::standard_unc_paths_are_written_git_style)
-       ENDIF ()
-
-       # Add a test target which runs the cred callback tests, to be
-       # called after setting the url and user
-       ADD_TEST(libgit2_clar-cred_callback libgit2_clar -v -sonline::clone::cred_callback)
-       ADD_TEST(libgit2_clar-proxy_credentials_in_url libgit2_clar -v -sonline::clone::proxy_credentials_in_url)
-       ADD_TEST(libgit2_clar-proxy_credentials_request libgit2_clar -v -sonline::clone::proxy_credentials_request)
+       ADD_SUBDIRECTORY(tests)
 ENDIF ()
 
 IF (TAGS)
@@ -738,3 +281,11 @@ ENDIF ()
 IF (BUILD_EXAMPLES)
        ADD_SUBDIRECTORY(examples)
 ENDIF ()
+
+IF(CMAKE_VERSION VERSION_GREATER 3)
+       FEATURE_SUMMARY(WHAT ENABLED_FEATURES DESCRIPTION "Enabled features:")
+       FEATURE_SUMMARY(WHAT DISABLED_FEATURES DESCRIPTION "Disabled features:")
+ELSE()
+       PRINT_ENABLED_FEATURES()
+       PRINT_DISABLED_FEATURES()
+ENDIF()
index 28a143570efbd3055dba492dd35b428ba5977ecc..7f372233ac59059c15eb3e747f74e737a3afa4f7 100644 (file)
@@ -71,6 +71,22 @@ 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
index f53a2e120722ac15a63517ef1003fc233d48431a..419fdcdfc1ca58359dc868fe8d1be857bbf65814 100644 (file)
@@ -36,10 +36,6 @@ These are good small projects to get started with libgit2.
       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).
-    * Implement the `--log-size` option for `examples/log.c`. I think all
-      the data is available, you would just need to add the code into the
-      `print_commit()` routine (along with a way of passing the option
-      into that function).
     * 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
index 1bbd37134bccd35dd00391e16613a4b80604c4c0..9fe99d8d56810427a1dc74237f7714d9832d2980 100644 (file)
--- a/README.md
+++ b/README.md
@@ -6,8 +6,20 @@ libgit2 - the Git linkable library
 [![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 re-entrant linkable library with a solid API, allowing you to
-write native speed custom Git applications in any language with bindings.
+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)
@@ -15,6 +27,30 @@ 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
 ============
 
@@ -40,6 +76,12 @@ 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
 ==============
 
@@ -118,6 +160,9 @@ 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
@@ -133,19 +178,49 @@ On most systems you can build the library using the following commands
 
 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
 
-       $ make test
+       $ ctest -V
 
 Alternatively you can run the test suite directly using,
 
        $ ./libgit2_clar
 
+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_clar -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_clar -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:
@@ -177,16 +252,6 @@ 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.
 
-Windows
--------
-
-You need to run the CMake commands from the Visual Studio command
-prompt, not the regular or Windows SDK one. Select the right generator
-for your version with the `-G "Visual Studio X" option.
-
-See [the website](http://libgit2.github.com/docs/guides/build-and-link/)
-for more detailed instructions.
-
 Android
 -------
 
@@ -228,6 +293,8 @@ Here are the bindings to libgit2 that are currently available:
     * 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
diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md
new file mode 100644 (file)
index 0000000..085fff8
--- /dev/null
@@ -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.
index fb3fff7ddcc87374af3a55583f7c6434a8e866a5..9b14a9c81c4fc03c36770315c117f654e4f5aff6 100644 (file)
@@ -9,13 +9,23 @@ environment:
   GITTEST_INVASIVE_FS_SIZE: 1
 
   matrix:
-  - GENERATOR: "Visual Studio 11"
+  - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013
+    GENERATOR: "Visual Studio 10 2010"
     ARCH: 32
-  - GENERATOR: "Visual Studio 11 Win64"
+  - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013
+    GENERATOR: "Visual Studio 10 2010 Win64"
     ARCH: 64
-  - GENERATOR: "MSYS Makefiles"
+  - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
+    GENERATOR: "Visual Studio 14 2015"
+    ARCH: 32
+  - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
+    GENERATOR: "Visual Studio 14 2015 Win64"
+    ARCH: 64
+  - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
+    GENERATOR: "MSYS Makefiles"
     ARCH: i686 # this is for 32-bit MinGW-w64
-  - GENERATOR: "MSYS Makefiles"
+  - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
+    GENERATOR: "MSYS Makefiles"
     ARCH: 64
 cache:
 - i686-4.9.2-release-win32-sjlj-rt_v3-rev1.7z
@@ -26,7 +36,7 @@ build_script:
     mkdir build
     cd build
     if ($env:GENERATOR -ne "MSYS Makefiles") {
-      cmake -D ENABLE_TRACE=ON -D BUILD_CLAR=ON -D MSVC_CRTDBG=ON .. -G"$env:GENERATOR"
+      cmake -D ENABLE_TRACE=ON -D BUILD_CLAR=ON -D BUILD_EXAMPLES=ON -D MSVC_CRTDBG=ON .. -G"$env:GENERATOR"
       cmake --build . --config Debug
     }
 - cmd: |
@@ -38,13 +48,8 @@ test_script:
     # Run this early so we know it's ready by the time we need it
     $proxyJob = Start-Job { java -jar $Env:APPVEYOR_BUILD_FOLDER\build\poxyproxy.jar -d --port 8080 --credentials foo:bar }
     ctest -V -R libgit2_clar
-    $env:GITTEST_REMOTE_URL="https://github.com/libgit2/non-existent"
-    $env:GITTEST_REMOTE_USER="libgit2test"
-    ctest -V -R libgit2_clar-cred_callback
     Receive-Job -Job $proxyJob
-    $env:GITTEST_REMOTE_PROXY_URL = "http://foo:bar@localhost:8080"
-    ctest -V -R libgit2_clar-proxy_credentials_in_url
-    $env:GITTEST_REMOTE_PROXY_URL = "http://localhost:8080"
+    $env:GITTEST_REMOTE_PROXY_URL = "localhost:8080"
     $env:GITTEST_REMOTE_PROXY_USER = "foo"
     $env:GITTEST_REMOTE_PROXY_PASS = "bar"
-    ctest -V -R libgit2_clar-proxy_credentials_request
+    ctest -V -R libgit2_clar-proxy_credentials
diff --git a/cmake/Modules/EnableWarnings.cmake b/cmake/Modules/EnableWarnings.cmake
new file mode 100644 (file)
index 0000000..e7d7d39
--- /dev/null
@@ -0,0 +1,14 @@
+MACRO(ENABLE_WARNINGS flag)
+       IF(ENABLE_WERROR)
+               ADD_C_FLAG_IF_SUPPORTED(-Werror=${flag})
+       ELSE()
+               ADD_C_FLAG_IF_SUPPORTED(-W${flag})
+       ENDIF()
+ENDMACRO()
+
+MACRO(DISABLE_WARNINGS flag)
+       ADD_C_FLAG_IF_SUPPORTED(-Wno-${flag})
+       IF(ENABLE_WERROR)
+               ADD_C_FLAG_IF_SUPPORTED(-Wno-error=${flag})
+       ENDIF()
+ENDMACRO()
index ebd619a5339f1f34dc924a249974aba547e6743d..e86ccbf03b991d18e9b78d5add34261a20f7447e 100644 (file)
@@ -1,9 +1,26 @@
-IF (COREFOUNDATION_INCLUDE_DIR AND COREFOUNDATION_DIRS)
-  SET(COREFOUNDATION_FOUND TRUE)
-ELSE ()
-  FIND_PATH(COREFOUNDATION_INCLUDE_DIR NAMES CoreFoundation.h)
-  FIND_LIBRARY(COREFOUNDATION_DIRS NAMES CoreFoundation)
-  IF (COREFOUNDATION_INCLUDE_DIR AND COREFOUNDATION_DIRS)
-    SET(COREFOUNDATION_FOUND TRUE)
-  ENDIF ()
+# 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("-- 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 "-- CoreFoundation not found")
+ENDIF()
+
+MARK_AS_ADVANCED(
+       COREFOUNDATION_INCLUDE_DIR
+       COREFOUNDATION_LIBRARIES
+)
index 0decdde925642b4cd1563b82fd7e6463aeb294ef..487f7e500b50f1f461655d17128a68764a16fd6b 100644 (file)
@@ -1,9 +1,28 @@
-IF (SECURITY_INCLUDE_DIR AND SECURITY_DIRS)
-  SET(SECURITY_FOUND TRUE)
-ELSE ()
-  FIND_PATH(SECURITY_INCLUDE_DIR NAMES Security/Security.h)
-  FIND_LIBRARY(SECURITY_DIRS NAMES Security)
-  IF (SECURITY_INCLUDE_DIR AND SECURITY_DIRS)
-    SET(SECURITY_FOUND TRUE)
-  ENDIF ()
+# 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("-- 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 "-- Security not found")
+ENDIF()
+
+MARK_AS_ADVANCED(
+       SECURITY_INCLUDE_DIR
+       SECURITY_LIBRARIES
+)
diff --git a/cmake/Modules/FindStatNsec.cmake b/cmake/Modules/FindStatNsec.cmake
new file mode 100644 (file)
index 0000000..fa550a2
--- /dev/null
@@ -0,0 +1,20 @@
+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()
+
+IF (HAVE_STRUCT_STAT_NSEC OR WIN32)
+       OPTION( USE_NSEC                "Care about sub-second file mtimes and ctimes"  ON  )
+ENDIF()
diff --git a/cmake/Modules/IdeSplitSources.cmake b/cmake/Modules/IdeSplitSources.cmake
new file mode 100644 (file)
index 0000000..e2e09b4
--- /dev/null
@@ -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_clar 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 ${libgit2_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/docs/release.md b/docs/release.md
new file mode 100644 (file)
index 0000000..42f032b
--- /dev/null
@@ -0,0 +1,81 @@
+# Releasing the library
+
+We have three kinds of releases: "full" releases, maintenance releases and security releases. Full ones release the state of the `master` 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
+    - [ ] Make a release candidate
+    - [ ] Plug any final leaks
+    - [ ] Fix any last-minute issues
+    - [ ] Make sure CHANGELOG reflects everything worth discussing
+    - [ ] Update the version in CHANGELOG 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:
+
+- CHANGELOG.md
+- include/git2/version.h
+
+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 `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 `master` 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.
+
+Everything else remains the same. Occasionally we might opt to backport a security fix to the previous series, based on how recently we started the new series and how serious the issue is.
+
+## 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 master checked out.
+
+    cm doc api.docurium
+
+It will start up a few proceses 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/examples/.gitignore b/examples/.gitignore
deleted file mode 100644 (file)
index 0e49159..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-general
-showindex
-diff
-rev-list
-blame
-cat-file
-init
-log
-rev-parse
-remote
-status
-tag
-for-each-ref
-describe
-*.dSYM
index 596be45ed3bfa838e16db2f2a34021d6e6bbe66a..99e2ba9c15c096d6a3e646c561dfc086f580e316 100644 (file)
@@ -1,4 +1,7 @@
-FILE(GLOB_RECURSE SRC_EXAMPLE_GIT2 network/*.c network/*.h)
+LINK_DIRECTORIES(${LIBGIT2_LIBDIRS})
+INCLUDE_DIRECTORIES(${LIBGIT2_INCLUDES})
+
+FILE(GLOB_RECURSE SRC_EXAMPLE_GIT2 network/*.c network/*.h common.?)
 ADD_EXECUTABLE(cgit2 ${SRC_EXAMPLE_GIT2})
 IF(WIN32 OR ANDROID)
        TARGET_LINK_LIBRARIES(cgit2 git2)
diff --git a/examples/Makefile b/examples/Makefile
deleted file mode 100644 (file)
index bd7e92d..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-.PHONY: all
-
-CC = gcc
-CFLAGS = -g -I../include -I../src -Wall -Wextra -Wmissing-prototypes -Wno-missing-field-initializers
-LFLAGS = -L../build -lgit2 -lz
-APPS = general showindex diff rev-list cat-file status log rev-parse init blame tag remote
-APPS += for-each-ref
-APPS += describe
-
-all: $(APPS)
-
-% : %.c
-       $(CC) -o $@ common.c $(CFLAGS) $< $(LFLAGS)
-
-clean:
-       $(RM) $(APPS)
-       $(RM) -r *.dSYM
index 96f5eaa8eacc3f0175e1995e2833f38e7902a472..118072044cd0690b22dbcf48a104d60972e86da6 100644 (file)
@@ -235,3 +235,13 @@ void treeish_to_tree(
        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;
+}
+
index adea0d318ae42d938778d6d32f2a7f8f7d87498c..af42324c23c6e4ab3495984bedeaa2dabcc1a712 100644 (file)
@@ -103,3 +103,8 @@ extern int diff_output(
  */
 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);
index 4cdf61f75cac2fc99265de92d908c6513ca1e5e1..2005de4aecb361ce25aa110cf5e230a9aeb058ca 100644 (file)
@@ -47,16 +47,6 @@ typedef struct {
 
 typedef struct args_info args_info;
 
-static 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;
-}
-
 static void opts_add_commit(describe_options *opts, const char *commit)
 {
        size_t sz;
index ff984a36ceff4c7933981a6eeed562b373ce36d6..0780d3d495a09001854f4db249e13f32d282d2bd 100644 (file)
@@ -724,6 +724,7 @@ static void config_files(const char *repo_path, git_repository* repo)
        int32_t autocorrect;
        git_config *cfg;
        git_config *snap_cfg;
+       int error_code;
 
        printf("\n*Config Listing*\n");
 
@@ -740,9 +741,33 @@ static void config_files(const char *repo_path, git_repository* repo)
        git_config_get_string(&email, snap_cfg, "user.email");
        printf("Email: %s\n", email);
 
-       /**
-        * Remember to free the configurations after usage.
-        */
+       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);
 }
index e54eed3cef098ab1d8b6e9d218f0ff8420041cb7..a107470ee7aa0031be3f2f90e7a6b9e33b46cd14 100644 (file)
@@ -50,6 +50,7 @@ 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;
@@ -63,7 +64,7 @@ struct log_options {
 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);
+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 */
@@ -148,7 +149,7 @@ int main(int argc, char *argv[])
                        break;
                }
 
-               print_commit(commit);
+               print_commit(commit, &opt);
 
                if (opt.show_diff) {
                        git_tree *a = NULL, *b = NULL;
@@ -337,7 +338,7 @@ static void print_time(const git_time *intime, const char *prefix)
 }
 
 /** Helper to print a commit object. */
-static void print_commit(git_commit *commit)
+static void print_commit(git_commit *commit, struct log_options *opts)
 {
        char buf[GIT_OID_HEXSZ + 1];
        int i, count;
@@ -347,6 +348,10 @@ static void print_commit(git_commit *commit)
        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) {
@@ -470,6 +475,8 @@ static int parse_options(
                        /** 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);
        }
diff --git a/examples/merge.c b/examples/merge.c
new file mode 100644 (file)
index 0000000..59982cb
--- /dev/null
@@ -0,0 +1,396 @@
+/*
+ * 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"
+#include <assert.h>
+
+#ifdef _MSC_VER
+#define snprintf sprintf_s
+#endif
+
+/** 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.
+ *
+ */
+
+typedef struct {
+       const char **heads;
+       size_t heads_count;
+
+       git_annotated_commit **annotated;
+       size_t annotated_count;
+
+       int no_commit : 1;
+} merge_options;
+
+static void print_usage(void)
+{
+       fprintf(stderr, "usage: merge [--no-commit] <commit...>\n");
+       exit(1);
+}
+
+static void merge_options_init(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(merge_options *opts, const char *refish)
+{
+       size_t sz;
+
+       assert(opts != NULL);
+
+       sz = ++opts->heads_count * sizeof(opts->heads[0]);
+       opts->heads = xrealloc(opts->heads, sz);
+       opts->heads[opts->heads_count - 1] = refish;
+}
+
+static void parse_options(const char **repo_path, 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_refish(git_annotated_commit **commit, git_repository *repo, const char *refish)
+{
+       git_reference *ref;
+       int err = 0;
+       git_oid oid;
+
+       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_oid_fromstr(&oid, refish);
+       if (err == GIT_OK) {
+               err = git_annotated_commit_lookup(commit, repo, &oid);
+       }
+
+       return err;
+}
+
+static int resolve_heads(git_repository *repo, 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], giterr_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_OBJ_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, 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]);
+               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", giterr_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_OBJ_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 main(int argc, char **argv)
+{
+       git_repository *repo = NULL;
+       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);
+
+       git_libgit2_init();
+
+       check_lg2(git_repository_open_ext(&repo, path, 0, NULL),
+                 "Could not open repository", NULL);
+
+       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(opts.heads);
+       free(opts.annotated);
+       git_repository_free(repo);
+       git_libgit2_shutdown();
+
+       return 0;
+}
index 1a81a10f8f33a20b29b13e0b770bc8b72a0ad69f..b0afb02389a753d925441ad01fa736cef451fdfb 100644 (file)
 # define UNUSED(x) x
 #endif
 
+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;
+}
+
 int cred_acquire_cb(git_cred **out,
                const char * UNUSED(url),
                const char * UNUSED(username_from_url),
@@ -26,14 +63,14 @@ int cred_acquire_cb(git_cred **out,
        int error;
 
        printf("Username: ");
-       if (getline(&username, NULL, stdin) < 0) {
+       if (readline(&username) < 0) {
                fprintf(stderr, "Unable to read username: %s", strerror(errno));
                return -1;
        }
 
        /* Yup. Right there on your terminal. Careful where you copy/paste output. */
        printf("Password: ");
-       if (getline(&password, NULL, stdin) < 0) {
+       if (readline(&password) < 0) {
                fprintf(stderr, "Unable to read password: %s", strerror(errno));
                free(username);
                return -1;
index 10974a9f190600cb521bd5f5a0b55731d09098c2..76b30119325ca4a942de5eb883066ff139be4f01 100644 (file)
@@ -8,13 +8,6 @@
 # include <unistd.h>
 #endif
 
-struct dl_data {
-       git_remote *remote;
-       git_fetch_options *fetch_opts;
-       int ret;
-       int finished;
-};
-
 static int progress_cb(const char *str, int len, void *data)
 {
        (void)data;
index 448103c463f074b1f86a986a350135b59f26b052..1d3d27b1b2d46cb03ad1b2a6ea3159ec880c9323 100644 (file)
@@ -2,10 +2,11 @@
 #include <stdio.h>
 #include <string.h>
 
+#include "../common.h"
 #include "common.h"
 
-// This part is not strictly libgit2-dependent, but you can use this
-// as a starting point for a git-like tool
+/* This part is not strictly libgit2-dependent, but you can use this
+ * as a starting point for a git-like tool */
 
 struct {
        char *name;
@@ -18,20 +19,12 @@ struct {
        { NULL, NULL}
 };
 
-static int run_command(git_cb fn, int argc, char **argv)
+static int run_command(git_cb fn, git_repository *repo, struct args_info args)
 {
        int error;
-       git_repository *repo;
-
-       // Before running the actual command, create an instance of the local
-       // repository and pass it to the function.
-
-       error = git_repository_open(&repo, ".git");
-       if (error < 0)
-               repo = NULL;
 
-       // Run the command. If something goes wrong, print the error message to stderr
-       error = fn(repo, argc, argv);
+       /* 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 (giterr_last() == NULL)
                        fprintf(stderr, "Error without message");
@@ -39,9 +32,6 @@ static int run_command(git_cb fn, int argc, char **argv)
                        fprintf(stderr, "Bad news:\n %s\n", giterr_last()->message);
        }
 
-       if(repo)
-               git_repository_free(repo);
-
        return !!error;
 }
 
@@ -49,6 +39,10 @@ int main(int argc, char **argv)
 {
        int i;
        int return_code = 1;
+       int error;
+       git_repository *repo;
+       struct args_info args = ARGS_INFO_INIT;
+       const char *git_dir = NULL;
 
        if (argc < 2) {
                fprintf(stderr, "usage: %s <cmd> [repo]\n", argv[0]);
@@ -57,9 +51,30 @@ int main(int argc, char **argv)
 
        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 (!strcmp(a, "--")) {
+                       /* arg separator */
+                       break;
+               }
+       }
+
+       /* Before running the actual command, create an instance of the local
+        * repository and pass it to the function. */
+
+       error = git_repository_open(&repo, git_dir);
+       if (error < 0)
+               repo = NULL;
+
        for (i = 0; commands[i].name != NULL; ++i) {
-               if (!strcmp(argv[1], commands[i].name)) {
-                       return_code = run_command(commands[i].fn, --argc, ++argv);
+               if (!strcmp(args.argv[args.pos], commands[i].name)) {
+                       return_code = run_command(commands[i].fn, repo, args);
                        goto shutdown;
                }
        }
@@ -67,6 +82,8 @@ int main(int argc, char **argv)
        fprintf(stderr, "Command not found: %s\n", argv[1]);
 
 shutdown:
+       git_repository_free(repo);
+
        git_libgit2_shutdown();
 
        return return_code;
index 6a85224b466696d61cbdd92a95c33a65ae76977e..3bc009fcc4f47476d0f3dad691079cea08d57597 100644 (file)
@@ -53,6 +53,7 @@ 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>
index bc4333cc9bbafbb1456ea44c12d47f5c3a1fcdc1..5f6104ef78863f68f7e0f5db72f3447dafd8bcd9 100644 (file)
@@ -62,5 +62,6 @@
 #include "git2/tree.h"
 #include "git2/types.h"
 #include "git2/version.h"
+#include "git2/worktree.h"
 
 #endif
index d0f1ba1b30f1342c3c8e0d2318e5d70106a349e0..d9da65b84335ff55cc0427e4ee13d2a5cf313a1a 100644 (file)
@@ -199,6 +199,8 @@ GIT_EXTERN(int) git_config_new(git_config **out);
  * @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
@@ -207,6 +209,7 @@ 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);
 
 /**
@@ -398,6 +401,10 @@ GIT_EXTERN(int) git_config_get_string_buf(git_buf *out, const git_config *cfg, c
  *
  * 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
@@ -410,6 +417,10 @@ GIT_EXTERN(int) git_config_get_multivar_foreach(const git_config *cfg, const cha
 /**
  * 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
@@ -487,6 +498,8 @@ GIT_EXTERN(int) git_config_set_string(git_config *cfg, const char *name, const c
 /**
  * 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
@@ -506,6 +519,8 @@ 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
@@ -552,6 +567,10 @@ GIT_EXTERN(int) git_config_iterator_new(git_config_iterator **out, const git_con
  * 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
@@ -565,8 +584,12 @@ GIT_EXTERN(int) git_config_iterator_glob_new(git_config_iterator **out, const gi
  * regular expression that filters which config keys are passed to the
  * callback.
  *
- * The pointers passed to the callback are only valid as long as the
- * iteration is ongoing.
+ * 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
@@ -693,6 +716,10 @@ GIT_EXTERN(int) git_config_parse_path(git_buf *out, const char *value);
  * This behaviors like `git_config_foreach_match` except instead of all config
  * entries it just enumerates through the given backend entry.
  *
+ * 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
index 971eb350cd1970fc5af14f9d9bb047f8f104f6e1..63de7e18ce284d1957d527e60cff519a21e34ef1 100644 (file)
@@ -23,7 +23,7 @@ GIT_BEGIN_DECL
 /**
  * Reference lookup strategy
  *
- * These behave like the --tags and --all optios to git-describe,
+ * 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.
  */
index 4f0871dabe448747b43ca9ffb162ca2b1d145a54..86009f5839213962f84a3151a3ebac4c05959f0d 100644 (file)
@@ -200,12 +200,18 @@ typedef enum {
        /** Use the "patience diff" algorithm */
        GIT_DIFF_PATIENCE = (1u << 28),
        /** Take extra time to find minimal diff */
-       GIT_DIFF_MINIMAL = (1 << 29),
+       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 = (1 << 30),
+       GIT_DIFF_SHOW_BINARY = (1u << 30),
+
+       /** 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 << 31),
 } git_diff_option_t;
 
 /**
@@ -515,12 +521,12 @@ typedef int(*git_diff_binary_cb)(
  * Structure describing a hunk of a diff.
  */
 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 */
+       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;
 
 /**
@@ -1400,6 +1406,51 @@ GIT_EXTERN(int) git_diff_format_email_init_options(
        git_diff_format_email_options *opts,
        unsigned int version);
 
+/**
+ * Patch ID options structure
+ *
+ * Initialize with `GIT_DIFF_PATCHID_OPTIONS_INIT` macro to
+ * correctly set the default values and version.
+ */
+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 the structure with default values. Equivalent to
+ * creating an instance with `GIT_DIFF_PATCHID_OPTIONS_INIT`.
+ */
+GIT_EXTERN(int) git_diff_patchid_init_options(
+       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 shoul 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
 
 /** @} */
index c997d8ca9df1e49aa1cfbdc1d2413e7be98ddcbb..213ae977757e537d59ae8a622ea9c5c90b80f343 100644 (file)
@@ -40,6 +40,9 @@ GIT_EXTERN(int) git_graph_ahead_behind(size_t *ahead, size_t *behind, git_reposi
 /**
  * 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 commit a previously loaded commit.
  * @param ancestor a potential ancestor commit.
  * @return 1 if the given commit is a descendant of the potential ancestor,
index 94ac8b5c5542042200371456a9590ec04ff51f42..80ef864d1d1c35c6817bf614fab3cfc685ce92f6 100644 (file)
@@ -162,6 +162,8 @@ typedef enum {
        GIT_MERGE_FILE_DIFF_MINIMAL = (1 << 7),
 } git_merge_file_flag_t;
 
+#define GIT_MERGE_CONFLICT_MARKER_SIZE 7
+
 /**
  * Options for merging a file
  */
@@ -191,6 +193,10 @@ typedef struct {
 
        /** see `git_merge_file_flag_t` above */
        git_merge_file_flag_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
index d78b1dce55bc00f03a00a2d1c8fc6cb2f6f97882..42ca3e5c291208430cff76658d427e12a4af0fea 100644 (file)
 GIT_BEGIN_DECL
 
 /**
- * Clean up message from excess whitespace and make sure that the last line
- * ends with a '\n'.
+ * Clean up excess whitespace and make sure there is a trailing newline in the message.
  *
- * Optionally, can remove lines starting with a "#".
+ * 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.
@@ -38,6 +37,47 @@ GIT_BEGIN_DECL
  */
 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.
+ */
+GIT_EXTERN(void) git_message_trailer_array_free(git_message_trailer_array *arr);
+
 /** @} */
 GIT_END_DECL
 
index 3a626cafdc3fd52d06ed2cbeebfc5becceaa1286..853e5de596b00579d9fdcb138f17522326240b1d 100644 (file)
@@ -51,6 +51,20 @@ GIT_EXTERN(int) git_note_iterator_new(
        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
  *
@@ -94,6 +108,25 @@ GIT_EXTERN(int) git_note_read(
        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
  *
@@ -153,6 +186,36 @@ GIT_EXTERN(int) git_note_create(
        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
@@ -173,6 +236,32 @@ GIT_EXTERN(int) git_note_remove(
        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
  *
index b7dc0c5f3669ecacd29c913bc1f9b64914f7ac43..006a75b7a19e3482dd52965344c5649de786798c 100644 (file)
@@ -357,11 +357,18 @@ GIT_EXTERN(void) git_odb_stream_free(git_odb_stream *stream);
  * @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, git_odb *db, const git_oid *oid);
+GIT_EXTERN(int) git_odb_open_rstream(
+       git_odb_stream **out,
+       size_t *len,
+       git_otype *type,
+       git_odb *db,
+       const git_oid *oid);
 
 /**
  * Open a stream for writing a pack file to the ODB.
index 4eb9f0263c73651d413886a792e635041d5dcefe..b177798e6a576e148edd08525ec770524d7f9e8c 100644 (file)
@@ -96,7 +96,7 @@ GIT_EXTERN(int) git_patch_from_blob_and_buffer(
        git_patch **out,
        const git_blob *old_blob,
        const char *old_as_path,
-       const char *buffer,
+       const void *buffer,
        size_t buffer_len,
        const char *buffer_as_path,
        const git_diff_options *opts);
@@ -124,7 +124,7 @@ GIT_EXTERN(int) git_patch_from_buffers(
        const void *old_buffer,
        size_t old_len,
        const char *old_as_path,
-       const char *new_buffer,
+       const void *new_buffer,
        size_t new_len,
        const char *new_as_path,
        const git_diff_options *opts);
index de6f027c51a0837005590bfb4fcd52ff66246f21..329965789709fefc60a1bbfb58d6fcb0215fe1ab 100644 (file)
@@ -26,32 +26,49 @@ typedef struct git_pathspec_match_list git_pathspec_match_list;
 
 /**
  * Options controlling how pathspec match should be executed
- *
- * - GIT_PATHSPEC_IGNORE_CASE forces match to ignore case; otherwise
- *   match will use native case sensitivity of platform filesystem
- * - GIT_PATHSPEC_USE_CASE forces case sensitive match; otherwise
- *   match will use native case sensitivity of platform filesystem
- * - GIT_PATHSPEC_NO_GLOB disables glob patterns and just uses simple
- *   string comparison for matching
- * - 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_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_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.
  */
 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;
 
index dee28cb5bf9e8ec2c4dc248fb6235ae2fcc23449..0dd453e5567e545b98b8281e668401d5dd923a1c 100644 (file)
@@ -433,6 +433,9 @@ typedef int (*git_reference_foreach_name_cb)(const char *name, void *payload);
  * 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
index e9e4e5b6577aa64687990815edef44ffcd9217ea..22e2291d0b5a4dda8962f572d88fb6f390505570 100644 (file)
@@ -75,6 +75,24 @@ GIT_EXTERN(int) git_remote_create_anonymous(
                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
  *
@@ -367,6 +385,20 @@ typedef struct {
  */
 typedef int (*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_push_update_reference_cb)(const char *refname, const char *status, void *data);
+
 /**
  * The callback settings structure
  *
@@ -434,11 +466,9 @@ struct git_remote_callbacks {
        git_push_transfer_progress push_transfer_progress;
 
        /**
-        * 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.
+        * See documentation of git_push_update_reference_cb
         */
-       int (*push_update_reference)(const char *refname, const char *status, void *data);
+       git_push_update_reference_cb push_update_reference;
 
        /**
         * Called once between the negotiation step and the upload. It
index 8aac0b3f754d1cf2cf414a90c6e5b2c7002143f6..6e0c1f71ef663adcd1a3a686ab0e5b5fdff65f58 100644 (file)
@@ -440,7 +440,7 @@ typedef enum {
  * @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, git_repository *repo, git_repository_item_t item);
+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
@@ -451,7 +451,7 @@ GIT_EXTERN(int) git_repository_item_path(git_buf *out, git_repository *repo, git
  * @param repo A repository object
  * @return the path to the repository
  */
-GIT_EXTERN(const char *) git_repository_path(git_repository *repo);
+GIT_EXTERN(const char *) git_repository_path(const git_repository *repo);
 
 /**
  * Get the path of the working directory for this repository
@@ -462,7 +462,7 @@ GIT_EXTERN(const char *) git_repository_path(git_repository *repo);
  * @param repo A repository object
  * @return the path to the working dir, if it exists
  */
-GIT_EXTERN(const char *) git_repository_workdir(git_repository *repo);
+GIT_EXTERN(const char *) git_repository_workdir(const git_repository *repo);
 
 /**
  * Get the path of the shared common directory for this repository
@@ -473,7 +473,7 @@ GIT_EXTERN(const char *) git_repository_workdir(git_repository *repo);
  * @param repo A repository object
  * @return the path to the common dir
  */
-GIT_EXTERN(const char *) git_repository_commondir(git_repository *repo);
+GIT_EXTERN(const char *) git_repository_commondir(const git_repository *repo);
 
 /**
  * Set the path to the working directory for this repository
@@ -501,7 +501,7 @@ GIT_EXTERN(int) git_repository_set_workdir(
  * @param repo Repo to test
  * @return 1 if the repository is bare, 0 otherwise.
  */
-GIT_EXTERN(int) git_repository_is_bare(git_repository *repo);
+GIT_EXTERN(int) git_repository_is_bare(const git_repository *repo);
 
 /**
  * Check if a repository is a linked work tree
@@ -509,7 +509,7 @@ GIT_EXTERN(int) git_repository_is_bare(git_repository *repo);
  * @param repo Repo to test
  * @return 1 if the repository is a linked work tree, 0 otherwise.
  */
-GIT_EXTERN(int) git_repository_is_worktree(git_repository *repo);
+GIT_EXTERN(int) git_repository_is_worktree(const git_repository *repo);
 
 /**
  * Get the configuration file for this repository.
index bd29c69e453bbb5e963b0de64307b7e094a3c845..be25414336a3caebb776151581db3feb7d3d83f9 100644 (file)
@@ -61,7 +61,7 @@ typedef enum {
  */
 GIT_EXTERN(int) git_reset(
        git_repository *repo,
-       git_object *target,
+       const git_object *target,
        git_reset_t reset_type,
        const git_checkout_options *checkout_opts);
 
@@ -79,7 +79,7 @@ GIT_EXTERN(int) git_reset(
  */
 GIT_EXTERN(int) git_reset_from_annotated(
        git_repository *repo,
-       git_annotated_commit *commit,
+       const git_annotated_commit *commit,
        git_reset_t reset_type,
        const git_checkout_options *checkout_opts);
 
@@ -103,8 +103,8 @@ GIT_EXTERN(int) git_reset_from_annotated(
  */
 GIT_EXTERN(int) git_reset_default(
        git_repository *repo,
-       git_object *target,
-       git_strarray* pathspecs);
+       const git_object *target,
+       const git_strarray* pathspecs);
 
 /** @} */
 GIT_END_DECL
index 671113955c88434702ebf87718c3d02b89eb9233..4b86818b76d4f7e6abd0c05c2c7d109c1ae8ed65 100644 (file)
@@ -173,12 +173,16 @@ typedef enum {
  * 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.
+ *
+ * The `baseline` is the tree to be used for comparison to the working directory
+ * and index; defaults to HEAD.
  */
 typedef struct {
        unsigned int      version;
        git_status_show_t show;
        unsigned int      flags;
        git_strarray      pathspec;
+       git_tree          *baseline;
 } git_status_options;
 
 #define GIT_STATUS_OPTIONS_VERSION 1
index 4dad6da42e19cdd31982b17bbe779acf91b530de..ed203226f1dc20e1b54f7004d4638c5436cdc4af 100644 (file)
@@ -58,7 +58,7 @@ struct git_config_backend {
        struct git_config *cfg;
 
        /* Open means open the file/database and parse if necessary */
-       int (*open)(struct git_config_backend *, git_config_level_t level);
+       int (*open)(struct git_config_backend *, git_config_level_t level, const git_repository *repo);
        int (*get)(struct git_config_backend *, const char *key, git_config_entry **entry);
        int (*set)(struct git_config_backend *, const char *key, const char *value);
        int (*set_multivar)(git_config_backend *cfg, const char *name, const char *regexp, const char *value);
@@ -111,6 +111,8 @@ GIT_EXTERN(int) git_config_init_backend(
  * @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
@@ -120,6 +122,7 @@ 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);
 
 /** @} */
index 96074fb77c7ff57cd28371a849a663ec81487f0a..490636b4498975017297d6c3f69a311a76274ead 100644 (file)
@@ -11,6 +11,7 @@
 #include "git2/types.h"
 #include "git2/oid.h"
 #include "git2/odb.h"
+#include "git2/buffer.h"
 
 /**
  * @file git2/sys/mempack.h
@@ -38,10 +39,10 @@ GIT_BEGIN_DECL
  *     Subsequent reads will also be served from the in-memory store
  *     to ensure consistency, until the memory store is dumped.
  *
- *     @param out Poiter where to store the ODB backend
+ *     @param out Pointer where to store the ODB backend
  *     @return 0 on success; error code otherwise
  */
-int git_mempack_new(git_odb_backend **out);
+GIT_EXTERN(int) git_mempack_new(git_odb_backend **out);
 
 /**
  *     Dump all the queued in-memory writes to a packfile.
@@ -64,7 +65,7 @@ int git_mempack_new(git_odb_backend **out);
  *     @param backend The mempack backend
  *     @return 0 on success; error code otherwise
  */
-int git_mempack_dump(git_buf *pack, git_repository *repo, git_odb_backend *backend);
+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.
@@ -78,7 +79,7 @@ int git_mempack_dump(git_buf *pack, git_repository *repo, git_odb_backend *backe
  *
  *     @param backend The mempack backend
  */
-void git_mempack_reset(git_odb_backend *backend);
+GIT_EXTERN(void) git_mempack_reset(git_odb_backend *backend);
 
 GIT_END_DECL
 
index 9bcc50ddd9cd3059d7939c1b054f916e8e6f412a..792f103fea86f314b50304caea494d05bce994f3 100644 (file)
@@ -56,7 +56,8 @@ struct git_odb_backend {
                git_odb_stream **, git_odb_backend *, git_off_t, git_otype);
 
        int (* readstream)(
-               git_odb_stream **, git_odb_backend *, const git_oid *);
+               git_odb_stream **, size_t *, git_otype *,
+               git_odb_backend *, const git_oid *);
 
        int (* exists)(
                git_odb_backend *, const git_oid *);
diff --git a/include/git2/sys/remote.h b/include/git2/sys/remote.h
deleted file mode 100644 (file)
index 3037b41..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This 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/types.h"
-
-GIT_BEGIN_DECL
-
-GIT_END_DECL
index 4740b1ffa8886671d759ff033e6abb39f9f268e2..1a363c149ca19bdb84d3ad18e576bcb7fc32b7e0 100644 (file)
@@ -307,9 +307,10 @@ GIT_EXTERN(const git_tree_entry *) git_treebuilder_get(
  * pointer may not be valid past the next operation in this
  * builder. Duplicate the entry if you want to keep it.
  *
- * No attempt is being made to ensure that the provided oid points
- * to an existing git object in the object database, nor that the
- * attributes make sense regarding the type of the pointed at object.
+ * 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
index dfdaa2920a1ca6eefd52adfb5f47e935d582d98c..8d9a947100f20d46970ffe562485abd30860aaac 100644 (file)
@@ -159,6 +159,7 @@ typedef struct git_packbuilder git_packbuilder;
 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) */
index becf97bd57f3ab6e5e36217d9f641feb9e706af6..746bf4230492fc023d2b1e19889ca1304a609386 100644 (file)
@@ -7,12 +7,12 @@
 #ifndef INCLUDE_git_version_h__
 #define INCLUDE_git_version_h__
 
-#define LIBGIT2_VERSION "0.26.0"
+#define LIBGIT2_VERSION "0.27.0"
 #define LIBGIT2_VER_MAJOR 0
-#define LIBGIT2_VER_MINOR 26
+#define LIBGIT2_VER_MINOR 27
 #define LIBGIT2_VER_REVISION 0
 #define LIBGIT2_VER_PATCH 0
 
-#define LIBGIT2_SOVERSION 26
+#define LIBGIT2_SOVERSION 27
 
 #endif
index d3fa88e3f085e3dc0f3c1d5f8b07f115387450af..a2a5d447355a3644b29d7ab0bd2f8cca2c4a8944 100644 (file)
@@ -123,7 +123,7 @@ GIT_EXTERN(int) git_worktree_add(git_worktree **out, git_repository *repo,
  * @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, char *reason);
+GIT_EXTERN(int) git_worktree_lock(git_worktree *wt, const char *reason);
 
 /**
  * Unlock a locked worktree
index d171a72d5e1ff5c0091e8ea66968384aaa34f7dc..6b2a9425edafdb996f212d018d3296454ba18d24 100755 (executable)
@@ -19,5 +19,5 @@ fi
 cd build
 gcc --version
 cmake --version
-cmake -D ENABLE_TRACE=ON -D BUILD_CLAR=ON .. -G"$GENERATOR"
+cmake -D ENABLE_TRACE=ON -D BUILD_CLAR=ON -D BUILD_EXAMPLES=ON .. -G"$GENERATOR"
 cmake --build . --config RelWithDebInfo
index 403df223eebc97a848c2c2c70ceddb2aa9e8fb2b..5d70e7506402235f38e52d37c317b254c6d06233 100755 (executable)
@@ -10,6 +10,15 @@ fi
 
 if [ "$TRAVIS_OS_NAME" = "osx" ]; then
        export PKG_CONFIG_PATH=$(ls -d /usr/local/Cellar/{curl,zlib}/*/lib/pkgconfig | paste -s -d':' -)
+
+       # Set up a ramdisk for us to put our test data on to speed up tests on macOS
+       export CLAR_TMP="$HOME"/_clar_tmp
+       mkdir -p $CLAR_TMP
+
+       # 5*2M sectors aka ~5GB of space
+       device=$(hdiutil attach -nomount ram://$((5 * 2 * 1024 * 1024)))
+       newfs_hfs $device
+       mount -t hfs $device $CLAR_TMP
 fi
 
 # Should we ask Travis to cache this file?
@@ -43,46 +52,51 @@ ctest -V -R libgit2_clar || exit $?
 
 killall git-daemon
 
-if [ "$TRAVIS_OS_NAME" = "osx" ]; then
-    echo 'PasswordAuthentication yes' | sudo tee -a /etc/sshd_config
-fi
-
+# Set up sshd
+mkdir ~/sshd/
+cat >~/sshd/sshd_config<<-EOF
+       Port 2222
+       ListenAddress 0.0.0.0
+       Protocol 2
+       HostKey ${HOME}/sshd/id_rsa
+       PidFile ${HOME}/sshd/pid
+       RSAAuthentication yes
+       PasswordAuthentication yes
+       PubkeyAuthentication yes
+       ChallengeResponseAuthentication no
+       # Required here as sshd will simply close connection otherwise
+       UsePAM no
+EOF
+ssh-keygen -t rsa -f ~/sshd/id_rsa -N "" -q
+/usr/sbin/sshd -f ~/sshd/sshd_config
+
+# Set up keys
 ssh-keygen -t rsa -f ~/.ssh/id_rsa -N "" -q
 cat ~/.ssh/id_rsa.pub >>~/.ssh/authorized_keys
-ssh-keyscan -t rsa localhost >>~/.ssh/known_hosts
+while read algorithm key comment; do
+    echo "[localhost]:2222 $algorithm $key" >>~/.ssh/known_hosts
+done <~/sshd/id_rsa.pub
 
 # Get the fingerprint for localhost and remove the colons so we can parse it as
 # a hex number. The Mac version is newer so it has a different output format.
 if [ "$TRAVIS_OS_NAME" = "osx" ]; then
-    export GITTEST_REMOTE_SSH_FINGERPRINT=$(ssh-keygen -E md5 -F localhost -l | tail -n 1 | cut -d ' ' -f 3 | cut -d : -f2- | tr -d :)
+    export GITTEST_REMOTE_SSH_FINGERPRINT=$(ssh-keygen -E md5 -F '[localhost]:2222' -l | tail -n 1 | cut -d ' ' -f 3 | cut -d : -f2- | tr -d :)
 else
-    export GITTEST_REMOTE_SSH_FINGERPRINT=$(ssh-keygen -F localhost -l | tail -n 1 | cut -d ' ' -f 2 | tr -d ':')
+    export GITTEST_REMOTE_SSH_FINGERPRINT=$(ssh-keygen -F '[localhost]:2222' -l | tail -n 1 | cut -d ' ' -f 2 | tr -d ':')
 fi
 
-export GITTEST_REMOTE_URL="ssh://localhost/$HOME/_temp/test.git"
+# Use the SSH server
+export GITTEST_REMOTE_URL="ssh://localhost:2222/$HOME/_temp/test.git"
 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=""
+ctest -V -R libgit2_clar-ssh || exit $?
 
+# Use the proxy we started at the beginning
+export GITTEST_REMOTE_PROXY_URL="localhost:8080"
+export GITTEST_REMOTE_PROXY_USER="foo"
+export GITTEST_REMOTE_PROXY_PASS="bar"
+ctest -V -R libgit2_clar-proxy_credentials || exit $?
 
-if [ -e ./libgit2_clar ]; then
-    ./libgit2_clar -sonline::push -sonline::clone::ssh_cert &&
-    ./libgit2_clar -sonline::clone::ssh_with_paths || exit $?
-    if [ "$TRAVIS_OS_NAME" = "linux" ]; then
-        ./libgit2_clar -sonline::clone::cred_callback || exit $?
-    fi
-
-    # Use the proxy we started at the beginning
-    export GITTEST_REMOTE_PROXY_URL="http://foo:bar@localhost:8080/"
-    ./libgit2_clar -sonline::clone::proxy_credentials_in_url || exit $?
-    export GITTEST_REMOTE_PROXY_URL="http://localhost:8080/"
-    export GITTEST_REMOTE_PROXY_USER="foo"
-    export GITTEST_REMOTE_PROXY_PASS="bar"
-    ./libgit2_clar -sonline::clone::proxy_credentials_request || exit $?
-
-fi
-
-export GITTEST_REMOTE_URL="https://github.com/libgit2/non-existent"
-export GITTEST_REMOTE_USER="libgit2test"
-ctest -V -R libgit2_clar-cred_callback
+kill $(cat "$HOME/sshd/pid")
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644 (file)
index 0000000..b03b96a
--- /dev/null
@@ -0,0 +1,463 @@
+IF(DEBUG_POOL)
+       SET(GIT_DEBUG_POOL 1)
+ENDIF()
+ADD_FEATURE_INFO(debugpool GIT_DEBUG_POOL "debug pool allocator")
+
+# This variable will contain the libraries we need to put into
+# libgit2.pc's Requires.private. That is, what we're linking to or
+# what someone who's statically linking us needs to link to.
+SET(LIBGIT2_PC_REQUIRES "")
+# This will be set later if we use the system's http-parser library or
+# use iconv (OSX) and will be written to the Libs.private field in the
+# pc file.
+SET(LIBGIT2_PC_LIBS "")
+
+SET(LIBGIT2_INCLUDES
+       "${CMAKE_CURRENT_BINARY_DIR}"
+       "${libgit2_SOURCE_DIR}/src"
+       "${libgit2_SOURCE_DIR}/include")
+SET(LIBGIT2_LIBS "")
+SET(LIBGIT2_LIBDIRS "")
+
+# Installation paths
+#
+SET(BIN_INSTALL_DIR bin CACHE PATH "Where to install binaries to.")
+SET(LIB_INSTALL_DIR lib CACHE PATH "Where to install libraries to.")
+SET(INCLUDE_INSTALL_DIR include CACHE PATH "Where to install headers to.")
+
+# Set a couple variables to be substituted inside the .pc file.
+# We can't just use LIB_INSTALL_DIR in the .pc file, as passing them as absolue
+# or relative paths is both valid and supported by cmake.
+SET (PKGCONFIG_PREFIX ${CMAKE_INSTALL_PREFIX})
+
+IF(IS_ABSOLUTE ${LIB_INSTALL_DIR})
+  SET (PKGCONFIG_LIBDIR ${LIB_INSTALL_DIR})
+ELSE(IS_ABSOLUTE ${LIB_INSTALL_DIR})
+  SET (PKGCONFIG_LIBDIR "\${prefix}/${LIB_INSTALL_DIR}")
+ENDIF (IS_ABSOLUTE ${LIB_INSTALL_DIR})
+
+IF(IS_ABSOLUTE ${INCLUDE_INSTALL_DIR})
+  SET (PKGCONFIG_INCLUDEDIR ${INCLUDE_INSTALL_DIR})
+ELSE(IS_ABSOLUTE ${INCLUDE_INSTALL_DIR})
+  SET (PKGCONFIG_INCLUDEDIR "\${prefix}/${INCLUDE_INSTALL_DIR}")
+ENDIF(IS_ABSOLUTE ${INCLUDE_INSTALL_DIR})
+
+# Enable tracing
+IF (ENABLE_TRACE STREQUAL "ON")
+       SET(GIT_TRACE 1)
+ENDIF()
+ADD_FEATURE_INFO(tracing GIT_TRACE "tracing support")
+
+CHECK_SYMBOL_EXISTS(regcomp_l "regex.h;xlocale.h" HAVE_REGCOMP_L)
+IF (HAVE_REGCOMP_L)
+       SET(GIT_USE_REGCOMP_L 1)
+ENDIF ()
+
+CHECK_FUNCTION_EXISTS(futimens HAVE_FUTIMENS)
+IF (HAVE_FUTIMENS)
+       SET(GIT_USE_FUTIMENS 1)
+ENDIF ()
+
+CHECK_FUNCTION_EXISTS(qsort_r HAVE_QSORT_R)
+IF (HAVE_QSORT_R)
+       ADD_DEFINITIONS(-DHAVE_QSORT_R)
+ENDIF ()
+
+CHECK_FUNCTION_EXISTS(qsort_s HAVE_QSORT_S)
+IF (HAVE_QSORT_S)
+       ADD_DEFINITIONS(-DHAVE_QSORT_S)
+ENDIF ()
+
+# Find required dependencies
+
+IF(WIN32)
+       LIST(APPEND LIBGIT2_LIBS ws2_32)
+ELSEIF(CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
+       LIST(APPEND LIBGIT2_LIBS socket nsl)
+       LIST(APPEND LIBGIT2_PC_LIBS "-lsocket" "-lnsl")
+ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Haiku")
+       LIST(APPEND LIBGIT2_LIBS network)
+       LIST(APPEND LIBGIT2_PC_LIBS "-lnetwork")
+ENDIF()
+
+CHECK_LIBRARY_EXISTS(rt clock_gettime "time.h" NEED_LIBRT)
+IF(NEED_LIBRT)
+       LIST(APPEND LIBGIT2_LIBS rt)
+       LIST(APPEND LIBGIT2_PC_LIBS "-lrt")
+ENDIF()
+
+IF(THREADSAFE)
+       LIST(APPEND LIBGIT2_LIBS ${CMAKE_THREAD_LIBS_INIT})
+       LIST(APPEND LIBGIT2_PC_LIBS ${CMAKE_THREAD_LIBS_INIT})
+ENDIF()
+ADD_FEATURE_INFO(threadsafe THREADSAFE "threadsafe support")
+
+
+IF (WIN32 AND EMBED_SSH_PATH)
+       FILE(GLOB SRC_SSH "${EMBED_SSH_PATH}/src/*.c")
+       LIST(APPEND LIBGIT2_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()
+
+IF (WIN32 AND 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("${libgit2_SOURCE_DIR}/deps/winhttp" "${libgit2_BINARY_DIR}/deps/winhttp")
+               LIST(APPEND LIBGIT2_LIBS winhttp)
+               LIST(APPEND LIBGIT2_INCLUDES "${libgit2_SOURCE_DIR}/deps/winhttp")
+               LIST(APPEND LIBGIT2_LIBDIRS ${LIBWINHTTP_PATH})
+       ELSE()
+               LIST(APPEND LIBGIT2_LIBS "winhttp")
+               LIST(APPEND LIBGIT2_PC_LIBS "-lwinhttp")
+       ENDIF ()
+
+       LIST(APPEND LIBGIT2_LIBS "rpcrt4" "crypt32" "ole32")
+       LIST(APPEND LIBGIT2_PC_LIBS "-lrpcrt4" "-lcrypt32" "-lole32")
+ELSE ()
+       IF (CURL)
+               PKG_CHECK_MODULES(CURL libcurl)
+       ENDIF ()
+
+       IF (CURL_FOUND)
+               SET(GIT_CURL 1)
+               LIST(APPEND LIBGIT2_INCLUDES ${CURL_INCLUDE_DIRS})
+               LIST(APPEND LIBGIT2_LIBDIRS ${CURL_LIBRARY_DIRS})
+               LIST(APPEND LIBGIT2_LIBS ${CURL_LIBRARIES})
+               LIST(APPEND LIBGIT2_PC_LIBS ${CURL_LDFLAGS})
+       ENDIF()
+       ADD_FEATURE_INFO(cURL GIT_CURL "cURL for HTTP proxy support")
+ENDIF()
+
+IF (USE_HTTPS)
+       IF (CMAKE_SYSTEM_NAME MATCHES "Darwin")
+               FIND_PACKAGE(Security)
+               FIND_PACKAGE(CoreFoundation)
+       ENDIF()
+
+       # Auto-select TLS backend
+       IF (USE_HTTPS STREQUAL ON)
+               IF (SECURITY_FOUND)
+                       IF (SECURITY_HAS_SSLCREATECONTEXT)
+                               SET(HTTPS_BACKEND "SecureTransport")
+                       ELSE()
+                               MESSAGE("-- Security framework is too old, falling back to OpenSSL")
+                               SET(HTTPS_BACKEND "OpenSSL")
+                       ENDIF()
+               ELSEIF (WINHTTP)
+                       SET(HTTPS_BACKEND "WinHTTP")
+               ELSE()
+                       SET(HTTPS_BACKEND "OpenSSL")
+               ENDIF()
+       ELSE()
+               # Backend was explicitly set
+               SET(HTTPS_BACKEND ${USE_HTTPS})
+       ENDIF()
+
+       # Check that we can find what's required for the selected backend
+       IF (HTTPS_BACKEND 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_INCLUDES ${SECURITY_INCLUDE_DIR})
+               LIST(APPEND LIBGIT2_LIBS ${COREFOUNDATION_LIBRARIES} ${SECURITY_LIBRARIES})
+               LIST(APPEND LIBGIT2_PC_LIBS ${COREFOUNDATION_LDFLAGS} ${SECURITY_LDFLAGS})
+       ELSEIF (HTTPS_BACKEND STREQUAL "OpenSSL")
+               FIND_PACKAGE(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_INCLUDES ${OPENSSL_INCLUDE_DIR})
+               LIST(APPEND LIBGIT2_LIBS ${OPENSSL_LIBRARIES})
+               LIST(APPEND LIBGIT2_PC_LIBS ${OPENSSL_LDFLAGS})
+               LIST(APPEND LIBGIT2_PC_REQUIRES "openssl")
+       ELSEIF (HTTPS_BACKEND STREQUAL "WinHTTP")
+               # WinHTTP setup was handled in the WinHTTP-specific block above
+       ELSE()
+               MESSAGE(FATAL_ERROR "Asked for backend ${HTTPS_BACKEND} but it wasn't found")
+       ENDIF()
+
+       ADD_FEATURE_INFO(HTTPS ON "using ${HTTPS_BACKEND}")
+       SET(GIT_HTTPS 1)
+ELSE()
+       ADD_FEATURE_INFO(HTTPS OFF "no support")
+ENDIF()
+
+# Specify sha1 implementation
+IF(SHA1_BACKEND STREQUAL "OpenSSL")
+       IF(NOT OPENSSL_FOUND)
+               FIND_PACKAGE(OpenSSL)
+               IF(NOT OPENSSL_FOUND)
+                   MESSAGE(FATAL_ERROR "Requested OpenSSL SHA1 backend, but OpenSSL could not be found")
+               ENDIF()
+       ENDIF()
+
+       ADD_FEATURE_INFO(SHA ON "using OpenSSL")
+       SET(GIT_SHA1_OPENSSL 1)
+       IF(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
+               LIST(APPEND LIBGIT2_PC_LIBS "-lssl")
+       ELSE()
+               LIST(APPEND LIBGIT2_PC_REQUIRES "openssl")
+       ENDIF()
+ELSEIF(SHA1_BACKEND STREQUAL "CollisionDetection")
+       ADD_FEATURE_INFO(SHA ON "using CollisionDetection")
+       SET(GIT_SHA1_COLLISIONDETECT 1)
+       ADD_DEFINITIONS(-DSHA1DC_NO_STANDARD_INCLUDES=1)
+       ADD_DEFINITIONS(-DSHA1DC_CUSTOM_INCLUDE_SHA1_C=\"common.h\")
+       ADD_DEFINITIONS(-DSHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C=\"common.h\")
+       FILE(GLOB SRC_SHA1 hash/hash_collisiondetect.c hash/sha1dc/*)
+ELSEIF(SHA1_BACKEND STREQUAL "Generic")
+       ADD_FEATURE_INFO(SHA ON "using Generic")
+       FILE(GLOB SRC_SHA1 hash/hash_generic.c)
+ELSEIF(SHA1_BACKEND STREQUAL "Win32")
+       ADD_FEATURE_INFO(SHA ON "using Win32")
+       SET(GIT_SHA1_WIN32 1)
+       FILE(GLOB SRC_SHA1 hash/hash_win32.c)
+ELSEIF(SHA1_BACKEND STREQUAL "CommonCrypto")
+       ADD_FEATURE_INFO(SHA ON "using CommonCrypto")
+       SET(GIT_SHA1_COMMON_CRYPTO 1)
+ELSE()
+       MESSAGE(FATAL_ERROR "Asked for unknown SHA1 backend ${SHA1_BACKEND}")
+ENDIF()
+
+# Include POSIX regex when it is required
+IF(WIN32 OR AMIGA OR CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
+       ADD_SUBDIRECTORY("${libgit2_SOURCE_DIR}/deps/regex" "${libgit2_BINARY_DIR}/deps/regex")
+       LIST(APPEND LIBGIT2_INCLUDES "${libgit2_SOURCE_DIR}/deps/regex")
+       LIST(APPEND LIBGIT2_OBJECTS $<TARGET_OBJECTS:regex>)
+ENDIF()
+
+# Optional external dependency: http-parser
+FIND_PACKAGE(HTTP_Parser)
+IF (USE_EXT_HTTP_PARSER AND HTTP_PARSER_FOUND AND HTTP_PARSER_VERSION_MAJOR EQUAL 2)
+       LIST(APPEND LIBGIT2_INCLUDES ${HTTP_PARSER_INCLUDE_DIRS})
+       LIST(APPEND LIBGIT2_LIBS ${HTTP_PARSER_LIBRARIES})
+       LIST(APPEND LIBGIT2_PC_LIBS "-lhttp_parser")
+       ADD_FEATURE_INFO(http-parser ON "http-parser support")
+ELSE()
+       MESSAGE(STATUS "http-parser version 2 was not found or disabled; using bundled 3rd-party sources.")
+       ADD_SUBDIRECTORY("${libgit2_SOURCE_DIR}/deps/http-parser" "${libgit2_BINARY_DIR}/deps/http-parser")
+       LIST(APPEND LIBGIT2_INCLUDES "${libgit2_SOURCE_DIR}/deps/http-parser")
+       LIST(APPEND LIBGIT2_OBJECTS "$<TARGET_OBJECTS:http-parser>")
+       ADD_FEATURE_INFO(http-parser ON "http-parser support (bundled)")
+ENDIF()
+
+# Optional external dependency: zlib
+IF(NOT USE_BUNDLED_ZLIB)
+       FIND_PACKAGE(ZLIB)
+       IF(ZLIB_FOUND)
+               LIST(APPEND LIBGIT2_INCLUDES ${ZLIB_INCLUDE_DIRS})
+               LIST(APPEND LIBGIT2_LIBS ${ZLIB_LIBRARIES})
+               IF(APPLE OR CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
+                       LIST(APPEND LIBGIT2_LIBS "z")
+                       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 OR NOT ZLIB_FOUND)
+       ADD_SUBDIRECTORY("${libgit2_SOURCE_DIR}/deps/zlib" "${libgit2_BINARY_DIR}/deps/zlib")
+       LIST(APPEND LIBGIT2_INCLUDES "${libgit2_SOURCE_DIR}/deps/zlib")
+       LIST(APPEND LIBGIT2_OBJECTS $<TARGET_OBJECTS:zlib>)
+       ADD_FEATURE_INFO(zlib ON "using bundled zlib")
+ENDIF()
+
+# Optional external dependency: libssh2
+IF (USE_SSH)
+       PKG_CHECK_MODULES(LIBSSH2 libssh2)
+ENDIF()
+IF (LIBSSH2_FOUND)
+       SET(GIT_SSH 1)
+       LIST(APPEND LIBGIT2_INCLUDES ${LIBSSH2_INCLUDE_DIRS})
+       LIST(APPEND LIBGIT2_LIBS ${LIBSSH2_LIBRARIES})
+       LIST(APPEND LIBGIT2_LIBDIRS ${LIBSSH2_LIBRARY_DIRS})
+       LIST(APPEND LIBGIT2_PC_LIBS ${LIBSSH2_LDFLAGS})
+       #SET(LIBGIT2_PC_LIBS "${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()
+ADD_FEATURE_INFO(SSH GIT_SSH "SSH transport support")
+
+# Optional external dependency: libgssapi
+IF (USE_GSSAPI)
+       FIND_PACKAGE(GSSAPI)
+ENDIF()
+IF (GSSAPI_FOUND)
+       SET(GIT_GSSAPI 1)
+       LIST(APPEND LIBGIT2_LIBS ${GSSAPI_LIBRARIES})
+ENDIF()
+ADD_FEATURE_INFO(SPNEGO GIT_GSSAPI "SPNEGO authentication support")
+
+# Optional external dependency: iconv
+IF (USE_ICONV)
+       FIND_PACKAGE(Iconv)
+ENDIF()
+IF (ICONV_FOUND)
+       SET(GIT_USE_ICONV 1)
+       LIST(APPEND LIBGIT2_INCLUDES ${ICONV_INCLUDE_DIR})
+       LIST(APPEND LIBGIT2_LIBS ${ICONV_LIBRARIES})
+       LIST(APPEND LIBGIT2_PC_LIBS ${ICONV_LIBRARIES})
+ENDIF()
+ADD_FEATURE_INFO(iconv GIT_USE_ICONV "iconv encoding conversion support")
+
+
+IF (THREADSAFE)
+       IF (NOT WIN32)
+               FIND_PACKAGE(Threads REQUIRED)
+       ENDIF()
+
+       SET(GIT_THREADS 1)
+ENDIF()
+
+IF (USE_NSEC)
+       SET(GIT_USE_NSEC 1)
+ENDIF()
+
+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()
+
+ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64)
+
+# Collect sourcefiles
+FILE(GLOB SRC_H
+       "${libgit2_SOURCE_DIR}/include/git2.h"
+       "${libgit2_SOURCE_DIR}/include/git2/*.h"
+       "${libgit2_SOURCE_DIR}/include/git2/sys/*.h")
+
+# On Windows use specific platform sources
+IF (WIN32 AND NOT CYGWIN)
+       ADD_DEFINITIONS(-DWIN32 -D_WIN32_WINNT=0x0501)
+
+       IF(MSVC)
+               SET(WIN_RC "win32/git2.rc")
+       ENDIF()
+
+       FILE(GLOB SRC_OS win32/*.c win32/*.h)
+ELSEIF (AMIGA)
+       ADD_DEFINITIONS(-DNO_ADDRINFO -DNO_READDIR_R -DNO_MMAP)
+ELSE()
+       IF (VALGRIND)
+               ADD_DEFINITIONS(-DNO_MMAP)
+       ENDIF()
+       FILE(GLOB SRC_OS unix/*.c unix/*.h)
+ENDIF()
+FILE(GLOB SRC_GIT2 *.c *.h
+       streams/*.c streams/*.h
+       transports/*.c transports/*.h
+       xdiff/*.c xdiff/*.h)
+
+# 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()
+
+CONFIGURE_FILE(features.h.in git2/sys/features.h)
+
+SET(LIBGIT2_SOURCES ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_SSH} ${SRC_SHA1})
+
+ADD_LIBRARY(git2internal OBJECT ${LIBGIT2_SOURCES})
+IDE_SPLIT_SOURCES(git2internal)
+LIST(APPEND LIBGIT2_OBJECTS $<TARGET_OBJECTS:git2internal>)
+
+IF (${CMAKE_VERSION} VERSION_LESS 2.8.12)
+       INCLUDE_DIRECTORIES(${LIBGIT2_INCLUDES})
+ELSE()
+       TARGET_INCLUDE_DIRECTORIES(git2internal
+           PRIVATE ${LIBGIT2_INCLUDES}
+           PUBLIC ${libgit2_SOURCE_DIR}/include)
+ENDIF()
+
+SET(LIBGIT2_OBJECTS ${LIBGIT2_OBJECTS} PARENT_SCOPE)
+SET(LIBGIT2_INCLUDES ${LIBGIT2_INCLUDES} PARENT_SCOPE)
+SET(LIBGIT2_LIBS ${LIBGIT2_LIBS} PARENT_SCOPE)
+SET(LIBGIT2_LIBDIRS ${LIBGIT2_LIBDIRS} PARENT_SCOPE)
+
+IF(XCODE_VERSION)
+       # This is required for Xcode to actually link the libgit2 library
+       # when using only object libraries.
+       FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/dummy.c "")
+       LIST(APPEND LIBGIT2_OBJECTS ${CMAKE_CURRENT_BINARY_DIR}/dummy.c)
+ENDIF()
+
+# Compile and link libgit2
+LINK_DIRECTORIES(${LIBGIT2_LIBDIRS})
+ADD_LIBRARY(git2 ${WIN_RC} ${LIBGIT2_OBJECTS})
+TARGET_LINK_LIBRARIES(git2 ${LIBGIT2_LIBS})
+
+SET_TARGET_PROPERTIES(git2 PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${libgit2_BINARY_DIR})
+SET_TARGET_PROPERTIES(git2 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${libgit2_BINARY_DIR})
+SET_TARGET_PROPERTIES(git2 PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${libgit2_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(git2 PROPERTIES STATIC_LIBRARY_FLAGS "/MACHINE:x64")
+ENDIF()
+
+IDE_SPLIT_SOURCES(git2)
+
+IF (SONAME)
+       SET_TARGET_PROPERTIES(git2 PROPERTIES VERSION ${LIBGIT2_VERSION_STRING})
+       SET_TARGET_PROPERTIES(git2 PROPERTIES SOVERSION ${LIBGIT2_SOVERSION})
+       IF (LIBGIT2_FILENAME)
+               ADD_DEFINITIONS(-DLIBGIT2_FILENAME=\"${LIBGIT2_FILENAME}\")
+               SET_TARGET_PROPERTIES(git2 PROPERTIES OUTPUT_NAME ${LIBGIT2_FILENAME})
+       ELSEIF (DEFINED LIBGIT2_PREFIX)
+               SET_TARGET_PROPERTIES(git2 PROPERTIES PREFIX "${LIBGIT2_PREFIX}")
+       ENDIF()
+ENDIF()
+
+LIST(REMOVE_DUPLICATES LIBGIT2_PC_REQUIRES)
+STRING(REPLACE ";" " " LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES}")
+STRING(REPLACE ";" " " LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS}")
+CONFIGURE_FILE(${libgit2_SOURCE_DIR}/libgit2.pc.in ${libgit2_BINARY_DIR}/libgit2.pc @ONLY)
+
+IF (MSVC_IDE)
+   # Precompiled headers
+   SET_TARGET_PROPERTIES(git2 PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h")
+   SET_SOURCE_FILES_PROPERTIES(win32/precompiled.c COMPILE_FLAGS "/Ycprecompiled.h")
+ENDIF ()
+
+# Install
+INSTALL(TARGETS git2
+       RUNTIME DESTINATION ${BIN_INSTALL_DIR}
+       LIBRARY DESTINATION ${LIB_INSTALL_DIR}
+       ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
+)
+INSTALL(FILES ${libgit2_BINARY_DIR}/libgit2.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig )
+INSTALL(DIRECTORY ${libgit2_SOURCE_DIR}/include/git2 DESTINATION ${INCLUDE_INSTALL_DIR} )
+INSTALL(FILES ${libgit2_SOURCE_DIR}/include/git2.h DESTINATION ${INCLUDE_INSTALL_DIR} )
index c2c770cba91b672cbc1a087e46454eae8d1f023e..72ba80a225a0c2b6cef19efc62bb5c3121f463eb 100644 (file)
@@ -5,8 +5,8 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
 #include "annotated_commit.h"
+
 #include "refs.h"
 #include "cache.h"
 
index 3ac8b5f692e72236c68df43955c1008f0671e913..b390066b2c9bf99bfcef152c0a1e473abf169209 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_annotated_commit_h__
 #define INCLUDE_annotated_commit_h__
 
+#include "common.h"
+
 #include "oidarray.h"
 
 #include "git2/oid.h"
index 595f5f3002da23b5ecab5ed920ba828a2bfd69c2..7801a0a540f1823c0cbec867108d88f815658f82 100644 (file)
@@ -5,6 +5,8 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
+#include "apply.h"
+
 #include <assert.h>
 
 #include "git2/patch.h"
@@ -12,7 +14,6 @@
 #include "array.h"
 #include "patch.h"
 #include "fileops.h"
-#include "apply.h"
 #include "delta.h"
 #include "zstream.h"
 
index 96e0f55b5d359df5ce92c69436f3ab90a97f1852..b29460c0b25402e2026f72902615e566df6d2b2a 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_apply_h__
 #define INCLUDE_apply_h__
 
+#include "common.h"
+
 #include "git2/patch.h"
 #include "buffer.h"
 
index 999f413181d527b10c77dd1588eacdaddeaeb3cd..93d9551d9d299827521f6b195e097e4e3e492f24 100644 (file)
@@ -1,4 +1,12 @@
-#include "common.h"
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * 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"
@@ -48,12 +56,16 @@ int git_attr_get(
        git_attr_file *file;
        git_attr_name attr;
        git_attr_rule *rule;
+       git_dir_flag dir_flag = GIT_DIR_FLAG_UNKNOWN;
 
        assert(value && repo && name);
 
        *value = NULL;
 
-       if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), GIT_DIR_FLAG_UNKNOWN) < 0)
+       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, flags, pathname, &files)) < 0)
@@ -106,13 +118,17 @@ int git_attr_get_many_with_session(
        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;
 
        assert(values && repo && names);
 
-       if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), GIT_DIR_FLAG_UNKNOWN) < 0)
+       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, flags, pathname, &files)) < 0)
@@ -188,10 +204,14 @@ int git_attr_foreach(
        git_attr_rule *rule;
        git_attr_assignment *assign;
        git_strmap *seen = NULL;
+       git_dir_flag dir_flag = GIT_DIR_FLAG_UNKNOWN;
 
        assert(repo && callback);
 
-       if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), GIT_DIR_FLAG_UNKNOWN) < 0)
+       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, flags, pathname, &files)) < 0 ||
index f9f216d07722588e23b6d3fdf3cd67991588a37f..9775652057f64a0346ac71c7db929396de71cf09 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_attr_h__
 #define INCLUDE_attr_h__
 
+#include "common.h"
+
 #include "attr_file.h"
 #include "attrcache.h"
 
index e30ea5e0c2b6820b29b430b5e4eac35c27864a63..55d0c3865f2ba430ba8cca29393aad24864389c2 100644 (file)
@@ -1,7 +1,14 @@
-#include "common.h"
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * 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 "attr_file.h"
 #include "attrcache.h"
 #include "git2/blob.h"
 #include "git2/tree.h"
index a9af2403a9fb5b355ff906021c20845962aa7d40..fedf55af59a7d9e69e2ee04ec04654d9a335b78d 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_attr_file_h__
 #define INCLUDE_attr_file_h__
 
+#include "common.h"
+
 #include "git2/oid.h"
 #include "git2/attr.h"
 #include "vector.h"
index 54161894b477ace5331467c2950306037b6688df..65e7b465524b7f3c08c749d92e4b8c34e33c01c8 100644 (file)
@@ -1,4 +1,12 @@
-#include "common.h"
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * 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"
index b91edd3e8a8594fdb994215c9cb7bea51488c6c8..f528911ea338034c3930b9917634c0926511d727 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_attrcache_h__
 #define INCLUDE_attrcache_h__
 
+#include "common.h"
+
 #include "attr_file.h"
 #include "strmap.h"
 
index 2c8584ba561d91a2b1b4134e29904a1a443bd3ba..a923bf00375f248dce3f5647e032b2bf5c50e5d0 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include "blame.h"
+
 #include "git2/commit.h"
 #include "git2/revparse.h"
 #include "git2/revwalk.h"
index d8db8d5c1d59c9f20ccc6d33762694a85821b263..8fd3ee5b193d6f113f6e74bc9bcf38c15469d738 100644 (file)
@@ -1,8 +1,9 @@
 #ifndef INCLUDE_blame_h__
 #define INCLUDE_blame_h__
 
-#include "git2/blame.h"
 #include "common.h"
+
+#include "git2/blame.h"
 #include "vector.h"
 #include "diff.h"
 #include "array.h"
index 13f5cb47c1b4ba804adb6f83cbd6df9cb1161edf..3c221b31821ea5cfe35ab9aaba0fe82ff99a130a 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include "blame_git.h"
+
 #include "commit.h"
 #include "blob.h"
 #include "xdiff/xinclude.h"
index 1891b0e1f2735041f7a25b1e8bd0e6ac4fb7ac4e..48b85a20d8cc33caa09372fc63317297a8c58b8c 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_blame_git__
 #define INCLUDE_blame_git__
 
+#include "common.h"
+
 #include "blame.h"
 
 int git_blame__get_origin(
index 19d3039fbac5b61159942b92481a3b29dcd42e07..3396fe74fbb879c7444dfb1b7f45c67fe0fae9ff 100644 (file)
@@ -5,14 +5,14 @@
  * 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 "common.h"
 #include "filebuf.h"
-#include "blob.h"
 #include "filter.h"
 #include "buf_text.h"
 
index 4cd9f1e0c178ec7aa1b5beb9cdf8e80cd092bfc8..3f1f977198bd4d42f802200aa8eccdef6bbe911b 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_blob_h__
 #define INCLUDE_blob_h__
 
+#include "common.h"
+
 #include "git2/blob.h"
 #include "repository.h"
 #include "odb.h"
index fe4955ad6dec2d711a172f4a43080e28996af0e3..42addc2ce7fd43dec637ea957521bff8d95968b7 100644 (file)
@@ -5,7 +5,8 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
+#include "branch.h"
+
 #include "commit.h"
 #include "tag.h"
 #include "config.h"
@@ -69,6 +70,12 @@ static int create_branch(
        assert(branch_name && commit && ref_out);
        assert(git_object_owner((const git_object *)commit) == repository);
 
+       if (!git__strcmp(branch_name, "HEAD")) {
+               giterr_set(GITERR_REFERENCE, "'HEAD' is not a valid 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);
index d02f2af0de40d0df5692870f24690e29204e38c9..5ae227c05aa14f483bc229ba21ba04ee34db30f9 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_branch_h__
 #define INCLUDE_branch_h__
 
+#include "common.h"
+
 #include "buffer.h"
 
 int git_branch_upstream__name(
index 7e6779d2dc61b2bd62e4c0fc5a2672aece0f4b05..306980b5cb9105889d27f12b3ca91033ce114894 100644 (file)
@@ -188,7 +188,7 @@ bool git_buf_text_is_binary(const git_buf *buf)
        git_bom_t bom;
        int printable = 0, nonprintable = 0;
 
-       scan += git_buf_text_detect_bom(&bom, buf, 0);
+       scan += git_buf_text_detect_bom(&bom, buf);
 
        if (bom > GIT_BOM_UTF8)
                return 1;
@@ -215,18 +215,18 @@ bool git_buf_text_contains_nul(const git_buf *buf)
        return (memchr(buf->ptr, '\0', buf->size) != NULL);
 }
 
-int git_buf_text_detect_bom(git_bom_t *bom, const git_buf *buf, size_t offset)
+int git_buf_text_detect_bom(git_bom_t *bom, const git_buf *buf)
 {
        const char *ptr;
        size_t len;
 
        *bom = GIT_BOM_NONE;
-       /* need at least 2 bytes after offset to look for any BOM */
-       if (buf->size < offset + 2)
+       /* need at least 2 bytes to look for any BOM */
+       if (buf->size < 2)
                return 0;
 
-       ptr = buf->ptr + offset;
-       len = buf->size - offset;
+       ptr = buf->ptr;
+       len = buf->size;
 
        switch (*ptr++) {
        case 0:
@@ -274,7 +274,7 @@ bool git_buf_text_gather_stats(
        memset(stats, 0, sizeof(*stats));
 
        /* BOM detection */
-       skip = git_buf_text_detect_bom(&stats->bom, buf, 0);
+       skip = git_buf_text_detect_bom(&stats->bom, buf);
        if (skip_bom)
                scan += skip;
 
index c9c55af8931e035b323537d2e11af679ef0da054..726b0ae7b808fccef88425bf06867cb28b3d114c 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_buf_text_h__
 #define INCLUDE_buf_text_h__
 
+#include "common.h"
+
 #include "buffer.h"
 
 typedef enum {
@@ -97,11 +99,9 @@ extern bool git_buf_text_contains_nul(const git_buf *buf);
  *
  * @param bom Set to the type of BOM detected or GIT_BOM_NONE
  * @param buf Buffer in which to check the first bytes for a BOM
- * @param offset Offset into buffer to look for BOM
  * @return Number of bytes of BOM data (or 0 if no BOM found)
  */
-extern int git_buf_text_detect_bom(
-       git_bom_t *bom, const git_buf *buf, size_t offset);
+extern int git_buf_text_detect_bom(git_bom_t *bom, const git_buf *buf);
 
 /**
  * Gather stats for a piece of text
index 6dfcbfbe60b6b98d3af60ab6af188f1f102ac998..8a58d1afafb6ae14b8730b0d098c9817dee144e0 100644 (file)
@@ -212,7 +212,7 @@ int git_buf_put(git_buf *buf, const char *data, size_t len)
                size_t new_size;
 
                assert(data);
-               
+
                GITERR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
                GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
                ENSURE_SIZE(buf, new_size);
@@ -455,6 +455,36 @@ on_error:
        return -1;
 }
 
+#define HEX_DECODE(c) ((c | 32) % 39 - 9)
+
+int git_buf_decode_percent(
+       git_buf *buf,
+       const char *str,
+       size_t str_len)
+{
+       size_t str_pos, new_size;
+
+       GITERR_CHECK_ALLOC_ADD(&new_size, buf->size, str_len);
+       GITERR_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_buf_vprintf(git_buf *buf, const char *format, va_list ap)
 {
        size_t expected_size, new_size;
index b0aece48892dd838197b40b98b1c3bf52af40319..cc77fc0300b6c5f31e5b25bd19308c6a9017267a 100644 (file)
@@ -190,6 +190,9 @@ int git_buf_encode_base85(git_buf *buf, const char *data, size_t len);
 /* Decode the given "base85" and write the result to the buffer */
 int git_buf_decode_base85(git_buf *buf, const char *base64, size_t len, size_t output_len);
 
+/* Decode the given percent-encoded string and write the result to the buffer */
+int git_buf_decode_percent(git_buf *buf, const char *str, size_t len);
+
 /*
  * Insert, remove or replace a portion of the buffer.
  *
index c92a3a78a9791f0853a0bdaeb8738a330755f40c..cdd12979f28d9d47819da6c43b3b37cbf13d197f 100644 (file)
@@ -5,12 +5,12 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
+#include "cache.h"
+
 #include "repository.h"
 #include "commit.h"
 #include "thread-utils.h"
 #include "util.h"
-#include "cache.h"
 #include "odb.h"
 #include "object.h"
 #include "git2/oid.h"
index 0f0bfcf5d4dede78792a357420d6545ed3c50ce2..9c09954ae7d08b67fea1ddc46c295b6bfb1acf2e 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_cache_h__
 #define INCLUDE_cache_h__
 
+#include "common.h"
+
 #include "git2/common.h"
 #include "git2/oid.h"
 #include "git2/odb.h"
index cefdc928b06b49a5765f41bbe8ba177d2c7cf1b4..0f05cd2d92886f2c6158dc75c0825536e4e6d219 100644 (file)
@@ -4,8 +4,8 @@
  * This 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_compat_h__
-#define INCLUDE_compat_h__
+#ifndef INCLUDE_cc_compat_h__
+#define INCLUDE_cc_compat_h__
 
 #include <stdarg.h>
 
@@ -84,4 +84,4 @@
 #      endif
 #endif
 
-#endif /* INCLUDE_compat_h__ */
+#endif
index 25018d291fef945ef53ad117e7d2f92a31f87d31..8ff5d897bbd66ed86725de1a706f263ab23e55c0 100644 (file)
@@ -5,10 +5,10 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include <assert.h>
-
 #include "checkout.h"
 
+#include <assert.h>
+
 #include "git2/repository.h"
 #include "git2/refs.h"
 #include "git2/tree.h"
@@ -70,6 +70,7 @@ typedef struct {
        git_buf tmp;
        unsigned int strategy;
        int can_symlink;
+       int respect_filemode;
        bool reload_submodules;
        size_t total_steps;
        size_t completed_steps;
@@ -159,6 +160,22 @@ GIT_INLINE(bool) is_workdir_base_or_new(
                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,
@@ -192,16 +209,23 @@ static bool checkout_is_workdir_modified(
                return rval;
        }
 
-       /* Look at the cache to decide if the workdir is modified.  If not,
-        * we can simply compare the oid in the cache to the baseitem instead
-        * of hashing the file.  If so, we allow the checkout to proceed if the
-        * oid is identical (ie, the staged item is what we're trying to check
-        * out.)
+       /*
+        * 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.
         */
-       if ((ie = git_index_get_bypath(data->index, wditem->path, 0)) != NULL) {
-               if (git_index_time_eq(&wditem->mtime, &ie->mtime) &&
-                       wditem->file_size == ie->file_size)
-                       return !is_workdir_base_or_new(&ie->id, baseitem, newitem);
+       ie = git_index_get_bypath(data->index, wditem->path, 0);
+
+       if (ie != NULL &&
+               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
@@ -214,6 +238,9 @@ static bool checkout_is_workdir_modified(
        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;
 
@@ -2005,8 +2032,11 @@ static int checkout_write_entry(
                (error = checkout_safe_for_update_only(data, fullpath->ptr, side->mode)) <= 0)
                return error;
 
-       return checkout_write_content(data,
-               &side->id, fullpath->ptr, hint_path, side->mode, &st);
+       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(
@@ -2428,6 +2458,10 @@ static int checkout_data_init(
                         &data->can_symlink, repo, GIT_CVAR_SYMLINKS)) < 0)
                goto cleanup;
 
+       if ((error = git_repository__cvar(
+                        &data->respect_filemode, repo, GIT_CVAR_FILEMODE)) < 0)
+               goto cleanup;
+
        if (!data->opts.baseline && !data->opts.baseline_index) {
                data->opts_free_baseline = true;
                error = 0;
index 60aa29b2682a7498cb33c93aec24c3d63f7e56c1..517fbf3b15e984b5391385cf31b6517909c6244a 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_checkout_h__
 #define INCLUDE_checkout_h__
 
+#include "common.h"
+
 #include "git2/checkout.h"
 #include "iterator.h"
 
index d8b6858aece3ffd068ab655a8af583c47b1a9c67..e42b74815e88baa5e5de2a1ef0e03deab89c5f57 100644 (file)
@@ -6,6 +6,7 @@
 */
 
 #include "common.h"
+
 #include "repository.h"
 #include "filebuf.h"
 #include "merge.h"
index 16ddface2920521395503e4675a9eae3f464f241..8764bb728451bb40f407491b610219eff5dfeca0 100644 (file)
@@ -5,6 +5,8 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
+#include "clone.h"
+
 #include <assert.h>
 
 #include "git2/clone.h"
@@ -16,7 +18,6 @@
 #include "git2/commit.h"
 #include "git2/tree.h"
 
-#include "common.h"
 #include "remote.h"
 #include "fileops.h"
 #include "refs.h"
index 14ca5d44c6c42e2a4cd5964f10fad616a61f2be8..864b59029e73fbf25909635ab83b4e0dcc8889e4 100644 (file)
@@ -7,6 +7,10 @@
 #ifndef INCLUDE_clone_h__
 #define INCLUDE_clone_h__
 
+#include "common.h"
+
+#include "git2/clone.h"
+
 extern int git_clone__should_clone_local(const char *url, git_clone_local_t local);
 
 #endif
index 4a340058a31df17645af643a460782361dc829ca..838688bb80b31e549ba22f06955dd624676f6fa1 100644 (file)
@@ -5,13 +5,14 @@
  * 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/sys/commit.h"
 
-#include "common.h"
 #include "odb.h"
 #include "commit.h"
 #include "signature.h"
index d01ac2b2f7d937cfc81097a32bba20eda47c71d4..781809d70fea97f83e73d389be646f38d8b100d9 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_commit_h__
 #define INCLUDE_commit_h__
 
+#include "common.h"
+
 #include "git2/commit.h"
 #include "tree.h"
 #include "repository.h"
index 3bba58c27daeeec86a912b83f5c2e2d284736834..96bd9dc150546c8b2f7a5827702f35963dafb0f1 100644 (file)
@@ -6,7 +6,7 @@
  */
 
 #include "commit_list.h"
-#include "common.h"
+
 #include "revwalk.h"
 #include "pool.h"
 #include "odb.h"
index 9746c2801c3e225f786e4d77c892ecd7bf8209f6..a7551a2bc6ed99778fe17aa58b1188fe1fada56a 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_commit_list_h__
 #define INCLUDE_commit_list_h__
 
+#include "common.h"
+
 #include "git2/oid.h"
 
 #define PARENT1  (1 << 0)
index e566aeabdace1bb45fd1bc18c8a9f3a8552f7532..44063be12df6f215de870c04cd5c7e8e562fa4e4 100644 (file)
@@ -7,6 +7,10 @@
 #ifndef INCLUDE_common_h__
 #define INCLUDE_common_h__
 
+#ifndef LIBGIT2_NO_FEATURES_H
+# include "git2/sys/features.h"
+#endif
+
 #include "git2/common.h"
 #include "cc-compat.h"
 
 # ifdef GIT_THREADS
 #      include "win32/thread.h"
 # endif
-# if defined(GIT_MSVC_CRTDBG)
-#   include "win32/w32_stack.h"
-#   include "win32/w32_crtdbg_stacktrace.h"
-# endif
 
 #else
 
@@ -230,6 +230,12 @@ GIT_INLINE(void) git__init_structure(void *structure, size_t len, unsigned int v
                GIT_ADD_SIZET_OVERFLOW(out, *(out), three) || \
                GIT_ADD_SIZET_OVERFLOW(out, *(out), four)) { return -1; }
 
+#define GITERR_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 GITERR_CHECK_ALLOC_MULTIPLY(out, nelem, elsize) \
        if (GIT_MULTIPLY_SIZET_OVERFLOW(out, nelem, elsize)) { return -1; }
@@ -238,4 +244,4 @@ GIT_INLINE(void) git__init_structure(void *structure, size_t len, unsigned int v
 
 #include "util.h"
 
-#endif /* INCLUDE_common_h__ */
+#endif
index 169a628808bc9f01666ba383c81baab5918c0220..1bc11b99f49c4b2ba812a70a170a514272633eb3 100644 (file)
@@ -5,9 +5,9 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
-#include "sysdir.h"
 #include "config.h"
+
+#include "sysdir.h"
 #include "git2/config.h"
 #include "git2/sys/config.h"
 #include "vector.h"
@@ -99,6 +99,7 @@ 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;
@@ -116,7 +117,7 @@ int git_config_add_file_ondisk(
        if (git_config_file__ondisk(&file, path) < 0)
                return -1;
 
-       if ((res = git_config_add_backend(cfg, file, level, force)) < 0) {
+       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
@@ -138,7 +139,7 @@ int git_config_open_ondisk(git_config **out, const char *path)
        if (git_config_new(&config) < 0)
                return -1;
 
-       if ((error = git_config_add_file_ondisk(config, path, GIT_CONFIG_LEVEL_LOCAL, 0)) < 0)
+       if ((error = git_config_add_file_ondisk(config, path, GIT_CONFIG_LEVEL_LOCAL, NULL, 0)) < 0)
                git_config_free(config);
        else
                *out = config;
@@ -164,7 +165,7 @@ int git_config_snapshot(git_config **out, git_config *in)
                if ((error = internal->file->snapshot(&b, internal->file)) < 0)
                        break;
 
-               if ((error = git_config_add_backend(config, b, internal->level, 0)) < 0) {
+               if ((error = git_config_add_backend(config, b, internal->level, NULL, 0)) < 0) {
                        b->free(b);
                        break;
                }
@@ -307,6 +308,7 @@ int git_config_add_backend(
        git_config *cfg,
        git_config_backend *file,
        git_config_level_t level,
+       const git_repository *repo,
        int force)
 {
        file_internal *internal;
@@ -316,7 +318,7 @@ int git_config_add_backend(
 
        GITERR_CHECK_VERSION(file, GIT_CONFIG_BACKEND_VERSION, "git_config_backend");
 
-       if ((result = file->open(file, level)) < 0)
+       if ((result = file->open(file, level, repo)) < 0)
                return result;
 
        internal = git__malloc(sizeof(file_internal));
@@ -1147,20 +1149,20 @@ int git_config_open_default(git_config **out)
 
        if (!git_config_find_global(&buf) || !git_config__global_location(&buf)) {
                error = git_config_add_file_ondisk(cfg, buf.ptr,
-                       GIT_CONFIG_LEVEL_GLOBAL, 0);
+                       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, 0);
+                       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, 0);
+                       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, 0);
+                       GIT_CONFIG_LEVEL_PROGRAMDATA, NULL, 0);
 
        git_buf_free(&buf);
 
index 00c12b50d6a45c1d45f627898fde4e0900da8af0..a5fcf2e84a1dc2d72b9b00024cd718c09c1ab3fb 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_config_h__
 #define INCLUDE_config_h__
 
+#include "common.h"
+
 #include "git2.h"
 #include "git2/config.h"
 #include "vector.h"
index 8407222749ff5825785ea18438b8e48d69621ffa..0efb1a7899e1bb6e88a35f6402780f5a53aaca55 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include "common.h"
+
 #include "fileops.h"
 #include "repository.h"
 #include "config.h"
index e15d57bbba557405a8ba3b46a4af6cd04c8867f5..aa9e83b9cfae9443a964a2ec22c0ec0c9dd35813 100644 (file)
@@ -5,7 +5,8 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
+#include "config_file.h"
+
 #include "config.h"
 #include "filebuf.h"
 #include "sysdir.h"
@@ -16,6 +17,7 @@
 #include "git2/types.h"
 #include "strmap.h"
 #include "array.h"
+#include "config_parse.h"
 
 #include <ctype.h>
 #include <sys/types.h>
@@ -74,15 +76,6 @@ typedef struct git_config_file_iter {
                 (iter) && (((tmp) = CVAR_LIST_NEXT(iter) || 1));\
                 (iter) = (tmp))
 
-struct reader {
-       git_oid checksum;
-       char *file_path;
-       git_buf buffer;
-       char *read_ptr;
-       int line_number;
-       int eof;
-};
-
 typedef struct {
        git_atomic refcount;
        git_strmap *values;
@@ -93,20 +86,20 @@ typedef struct {
        /* mutex to coordinate accessing the values */
        git_mutex values_mutex;
        refcounted_strmap *values;
+       const git_repository *repo;
+       git_config_level_t level;
 } diskfile_header;
 
 typedef struct {
        diskfile_header header;
 
-       git_config_level_t level;
-
-       git_array_t(struct reader) readers;
+       git_array_t(git_config_parser) readers;
 
        bool locked;
        git_filebuf locked_buf;
        git_buf locked_content;
 
-       char  *file_path;
+       struct config_file file;
 } diskfile_backend;
 
 typedef struct {
@@ -115,19 +108,13 @@ typedef struct {
        diskfile_backend *snapshot_from;
 } diskfile_readonly_backend;
 
-static int config_read(git_strmap *values, diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth);
-static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value);
+static int config_read(git_strmap *values, const git_repository *repo, git_config_file *file, git_config_level_t level, int depth);
+static int config_write(diskfile_backend *cfg, const char *orig_key, const char *key, const regex_t *preg, const char *value);
 static char *escape_value(const char *ptr);
 
 int git_config_file__snapshot(git_config_backend **out, diskfile_backend *in);
 static int config_snapshot(git_config_backend **out, git_config_backend *in);
 
-static void set_parse_error(struct reader *reader, int col, const char *error_str)
-{
-       giterr_set(GITERR_CONFIG, "failed to parse config file: %s (in %s:%d, column %d)",
-               error_str, reader->file_path, reader->line_number, col);
-}
-
 static int config_error_readonly(void)
 {
        giterr_set(GITERR_CONFIG, "this backend is read-only");
@@ -261,123 +248,131 @@ static int refcounted_strmap_alloc(refcounted_strmap **out)
        return error;
 }
 
-static int config_open(git_config_backend *cfg, git_config_level_t level)
+static void config_file_clear(struct config_file *file)
+{
+       struct 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_open(git_config_backend *cfg, git_config_level_t level, const git_repository *repo)
 {
        int res;
-       struct reader *reader;
        diskfile_backend *b = (diskfile_backend *)cfg;
 
-       b->level = level;
+       b->header.level = level;
+       b->header.repo = repo;
 
        if ((res = refcounted_strmap_alloc(&b->header.values)) < 0)
                return res;
 
-       git_array_init(b->readers);
-       reader = git_array_alloc(b->readers);
-       if (!reader) {
-               refcounted_strmap_free(b->header.values);
-               return -1;
-       }
-       memset(reader, 0, sizeof(struct reader));
-
-       reader->file_path = git__strdup(b->file_path);
-       GITERR_CHECK_ALLOC(reader->file_path);
-
-       git_buf_init(&reader->buffer, 0);
-       res = git_futils_readbuffer_updated(
-               &reader->buffer, b->file_path, &reader->checksum, NULL);
-
-       /* It's fine if the file doesn't exist */
-       if (res == GIT_ENOTFOUND)
+       if (!git_path_exists(b->file.path))
                return 0;
 
-       if (res < 0 || (res = config_read(b->header.values->values, b, reader, level, 0)) < 0) {
+       if (res < 0 || (res = config_read(b->header.values->values, repo, &b->file, level, 0)) < 0) {
                refcounted_strmap_free(b->header.values);
                b->header.values = NULL;
        }
 
-       reader = git_array_get(b->readers, 0);
-       git_buf_free(&reader->buffer);
-
        return res;
 }
 
-/* The meat of the refresh, as we want to use it in different places */
-static int config__refresh(git_config_backend *cfg)
+static int config_is_modified(int *modified, struct config_file *file)
 {
-       refcounted_strmap *values = NULL, *tmp;
-       diskfile_backend *b = (diskfile_backend *)cfg;
-       struct reader *reader = NULL;
+       git_config_file *include;
+       git_buf buf = GIT_BUF_INIT;
+       git_oid hash;
+       uint32_t i;
        int error = 0;
 
-       if ((error = refcounted_strmap_alloc(&values)) < 0)
-               goto out;
+       *modified = 0;
 
-       reader = git_array_get(b->readers, git_array_size(b->readers) - 1);
-       GITERR_CHECK_ALLOC(reader);
+       if ((error = git_futils_readbuffer(&buf, file->path)) < 0)
+               goto out;
 
-       if ((error = config_read(values->values, b, reader, b->level, 0)) < 0)
+       if ((error = git_hash_buf(&hash, buf.ptr, buf.size)) < 0)
                goto out;
 
-       if ((error = git_mutex_lock(&b->header.values_mutex)) < 0) {
-               giterr_set(GITERR_OS, "failed to lock config backend");
+       if (!git_oid_equal(&hash, &file->checksum)) {
+               *modified = 1;
                goto out;
        }
 
-       tmp = b->header.values;
-       b->header.values = values;
-       values = tmp;
-
-       git_mutex_unlock(&b->header.values_mutex);
+       git_array_foreach(file->includes, i, include) {
+               if ((error = config_is_modified(modified, include)) < 0 || *modified)
+                       goto out;
+       }
 
 out:
-       refcounted_strmap_free(values);
-       if (reader)
-               git_buf_free(&reader->buffer);
+       git_buf_free(&buf);
+
        return error;
 }
 
 static int config_refresh(git_config_backend *cfg)
 {
-       int error = 0, updated = 0, any_updated = 0;
        diskfile_backend *b = (diskfile_backend *)cfg;
-       struct reader *reader = NULL;
+       refcounted_strmap *values = NULL, *tmp;
+       git_config_file *include;
+       int error, modified;
        uint32_t i;
 
-       for (i = 0; i < git_array_size(b->readers); i++) {
-               reader = git_array_get(b->readers, i);
-               error = git_futils_readbuffer_updated(
-                       &reader->buffer, reader->file_path,
-                       &reader->checksum, &updated);
+       if (b->header.parent.readonly)
+               return config_error_readonly();
 
-               if (error < 0 && error != GIT_ENOTFOUND)
-                       return error;
+       error = config_is_modified(&modified, &b->file);
+       if (error < 0 && error != GIT_ENOTFOUND)
+               goto out;
+
+       if (!modified)
+               return 0;
+
+       if ((error = refcounted_strmap_alloc(&values)) < 0)
+               goto out;
 
-               if (updated)
-                       any_updated = 1;
+       /* Reparse the current configuration */
+       git_array_foreach(b->file.includes, i, include) {
+               config_file_clear(include);
+       }
+       git_array_clear(b->file.includes);
+
+       if ((error = config_read(values->values, b->header.repo, &b->file, b->header.level, 0)) < 0)
+               goto out;
+
+       if ((error = git_mutex_lock(&b->header.values_mutex)) < 0) {
+               giterr_set(GITERR_OS, "failed to lock config backend");
+               goto out;
        }
 
-       if (!any_updated)
-               return (error == GIT_ENOTFOUND) ? 0 : error;
+       tmp = b->header.values;
+       b->header.values = values;
+       values = tmp;
 
-       return config__refresh(cfg);
+       git_mutex_unlock(&b->header.values_mutex);
+
+out:
+       refcounted_strmap_free(values);
+
+       return (error == GIT_ENOTFOUND) ? 0 : error;
 }
 
 static void backend_free(git_config_backend *_backend)
 {
        diskfile_backend *backend = (diskfile_backend *)_backend;
-       uint32_t i;
 
        if (backend == NULL)
                return;
 
-       for (i = 0; i < git_array_size(backend->readers); i++) {
-               struct reader *r = git_array_get(backend->readers, i);
-               git__free(r->file_path);
-       }
-       git_array_clear(backend->readers);
-
-       git__free(backend->file_path);
+       config_file_clear(&backend->file);
        refcounted_strmap_free(backend->header.values);
        git_mutex_free(&backend->header.values_mutex);
        git__free(backend);
@@ -424,13 +419,13 @@ static int config_iterator_new(
        diskfile_header *h;
        git_config_file_iter *it;
        git_config_backend *snapshot;
-       diskfile_backend *b = (diskfile_backend *) backend;
+       diskfile_header *bh = (diskfile_header *) backend;
        int error;
 
        if ((error = config_snapshot(&snapshot, backend)) < 0)
                return error;
 
-       if ((error = snapshot->open(snapshot, b->level)) < 0)
+       if ((error = snapshot->open(snapshot, bh->level, bh->repo)) < 0)
                return error;
 
        it = git__calloc(1, sizeof(git_config_file_iter));
@@ -482,6 +477,12 @@ static int config_set(git_config_backend *cfg, const char *name, const char *val
                        goto out;
                }
 
+               if (existing->included) {
+                       giterr_set(GITERR_CONFIG, "modifying included variable is not supported");
+                       ret = -1;
+                       goto out;
+               }
+
                /* don't update if old and new values already match */
                if ((!existing->entry->value && !value) ||
                        (existing->entry->value && value &&
@@ -498,7 +499,7 @@ static int config_set(git_config_backend *cfg, const char *name, const char *val
                GITERR_CHECK_ALLOC(esc_value);
        }
 
-       if ((ret = config_write(b, key, NULL, esc_value)) < 0)
+       if ((ret = config_write(b, name, key, NULL, esc_value)) < 0)
                goto out;
 
        ret = config_refresh(cfg);
@@ -576,7 +577,7 @@ static int config_set_multivar(
        }
 
        /* If we do have it, set call config_write() and reload */
-       if ((result = config_write(b, key, &preg, value)) < 0)
+       if ((result = config_write(b, name, key, &preg, value)) < 0)
                goto out;
 
        result = config_refresh(cfg);
@@ -616,12 +617,17 @@ static int config_delete(git_config_backend *cfg, const char *name)
        var = git_strmap_value_at(values, pos);
        refcounted_strmap_free(map);
 
+       if (var->included) {
+               giterr_set(GITERR_CONFIG, "cannot delete included variable");
+               return -1;
+       }
+
        if (var->next != NULL) {
                giterr_set(GITERR_CONFIG, "cannot delete multivar with a single delete");
                return -1;
        }
 
-       if ((result = config_write(b, var->entry->name, NULL, NULL)) < 0)
+       if ((result = config_write(b, name, var->entry->name, NULL, NULL)) < 0)
                return result;
 
        return config_refresh(cfg);
@@ -662,7 +668,7 @@ static int config_delete_multivar(git_config_backend *cfg, const char *name, con
                goto out;
        }
 
-       if ((result = config_write(b, key, &preg, NULL)) < 0)
+       if ((result = config_write(b, name, key, &preg, NULL)) < 0)
                goto out;
 
        result = config_refresh(cfg);
@@ -685,10 +691,10 @@ static int config_lock(git_config_backend *_cfg)
        diskfile_backend *cfg = (diskfile_backend *) _cfg;
        int error;
 
-       if ((error = git_filebuf_open(&cfg->locked_buf, cfg->file_path, 0, GIT_CONFIG_FILE_MODE)) < 0)
+       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);
+       error = git_futils_readbuffer(&cfg->locked_content, cfg->file.path);
        if (error < 0 && error != GIT_ENOTFOUND) {
                git_filebuf_cleanup(&cfg->locked_buf);
                return error;
@@ -726,8 +732,9 @@ int git_config_file__ondisk(git_config_backend **out, const char *path)
        backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION;
        git_mutex_init(&backend->header.values_mutex);
 
-       backend->file_path = git__strdup(path);
-       GITERR_CHECK_ALLOC(backend->file_path);
+       backend->file.path = git__strdup(path);
+       GITERR_CHECK_ALLOC(backend->file.path);
+       git_array_init(backend->file.includes);
 
        backend->header.parent.open = config_open;
        backend->header.parent.get = config_get;
@@ -810,7 +817,7 @@ static void backend_readonly_free(git_config_backend *_backend)
        git__free(backend);
 }
 
-static int config_readonly_open(git_config_backend *cfg, git_config_level_t level)
+static int config_readonly_open(git_config_backend *cfg, git_config_level_t level, const git_repository *repo)
 {
        diskfile_readonly_backend *b = (diskfile_readonly_backend *) cfg;
        diskfile_backend *src = b->snapshot_from;
@@ -821,8 +828,9 @@ static int config_readonly_open(git_config_backend *cfg, git_config_level_t leve
        if (!src_header->parent.readonly && (error = config_refresh(&src_header->parent)) < 0)
                return error;
 
-       /* We're just copying data, don't care about the level */
+       /* We're just copying data, don't care about the level or repo*/
        GIT_UNUSED(level);
+       GIT_UNUSED(repo);
 
        if ((src_map = refcounted_strmap_take(src_header)) == NULL)
                return -1;
@@ -861,397 +869,6 @@ int git_config_file__snapshot(git_config_backend **out, diskfile_backend *in)
        return 0;
 }
 
-static int reader_getchar_raw(struct reader *reader)
-{
-       int c;
-
-       c = *reader->read_ptr++;
-
-       /*
-       Win 32 line breaks: if we find a \r\n sequence,
-       return only the \n as a newline
-       */
-       if (c == '\r' && *reader->read_ptr == '\n') {
-               reader->read_ptr++;
-               c = '\n';
-       }
-
-       if (c == '\n')
-               reader->line_number++;
-
-       if (c == 0) {
-               reader->eof = 1;
-               c = '\0';
-       }
-
-       return c;
-}
-
-#define SKIP_WHITESPACE (1 << 1)
-#define SKIP_COMMENTS (1 << 2)
-
-static int reader_getchar(struct reader *reader, int flags)
-{
-       const int skip_whitespace = (flags & SKIP_WHITESPACE);
-       const int skip_comments = (flags & SKIP_COMMENTS);
-       int c;
-
-       assert(reader->read_ptr);
-
-       do {
-               c = reader_getchar_raw(reader);
-       } while (c != '\n' && c != '\0' && skip_whitespace && git__isspace(c));
-
-       if (skip_comments && (c == '#' || c == ';')) {
-               do {
-                       c = reader_getchar_raw(reader);
-               } while (c != '\n' && c != '\0');
-       }
-
-       return c;
-}
-
-/*
- * Read the next char, but don't move the reading pointer.
- */
-static int reader_peek(struct reader *reader, int flags)
-{
-       void *old_read_ptr;
-       int old_lineno, old_eof;
-       int ret;
-
-       assert(reader->read_ptr);
-
-       old_read_ptr = reader->read_ptr;
-       old_lineno = reader->line_number;
-       old_eof = reader->eof;
-
-       ret = reader_getchar(reader, flags);
-
-       reader->read_ptr = old_read_ptr;
-       reader->line_number = old_lineno;
-       reader->eof = old_eof;
-
-       return ret;
-}
-
-/*
- * Read and consume a line, returning it in newly-allocated memory.
- */
-static char *reader_readline(struct reader *reader, bool skip_whitespace)
-{
-       char *line = NULL;
-       char *line_src, *line_end;
-       size_t line_len, alloc_len;
-
-       line_src = reader->read_ptr;
-
-       if (skip_whitespace) {
-               /* Skip empty empty lines */
-               while (git__isspace(*line_src))
-                       ++line_src;
-       }
-
-       line_end = strchr(line_src, '\n');
-
-       /* no newline at EOF */
-       if (line_end == NULL)
-               line_end = strchr(line_src, 0);
-
-       line_len = line_end - line_src;
-
-       if (GIT_ADD_SIZET_OVERFLOW(&alloc_len, line_len, 1) ||
-               (line = git__malloc(alloc_len)) == NULL) {
-               return NULL;
-       }
-
-       memcpy(line, line_src, line_len);
-
-       do line[line_len] = '\0';
-       while (line_len-- > 0 && git__isspace(line[line_len]));
-
-       if (*line_end == '\n')
-               line_end++;
-
-       if (*line_end == '\0')
-               reader->eof = 1;
-
-       reader->line_number++;
-       reader->read_ptr = line_end;
-
-       return line;
-}
-
-/*
- * Consume a line, without storing it anywhere
- */
-static void reader_consume_line(struct reader *reader)
-{
-       char *line_start, *line_end;
-
-       line_start = reader->read_ptr;
-       line_end = strchr(line_start, '\n');
-       /* No newline at EOF */
-       if(line_end == NULL){
-               line_end = strchr(line_start, '\0');
-       }
-
-       if (*line_end == '\n')
-               line_end++;
-
-       if (*line_end == '\0')
-               reader->eof = 1;
-
-       reader->line_number++;
-       reader->read_ptr = line_end;
-}
-
-GIT_INLINE(int) config_keychar(int c)
-{
-       return isalnum(c) || c == '-';
-}
-
-static int parse_section_header_ext(struct reader *reader, const char *line, const char *base_name, char **section_name)
-{
-       int c, rpos;
-       char *first_quote, *last_quote;
-       git_buf buf = GIT_BUF_INIT;
-       size_t quoted_len, alloc_len, base_name_len = strlen(base_name);
-
-       /*
-        * base_name is what came before the space. We should be at the
-        * first quotation mark, except for now, line isn't being kept in
-        * sync so we only really use it to calculate the length.
-        */
-
-       first_quote = strchr(line, '"');
-       if (first_quote == NULL) {
-               set_parse_error(reader, 0, "Missing quotation marks in section header");
-               goto end_error;
-       }
-
-       last_quote = strrchr(line, '"');
-       quoted_len = last_quote - first_quote;
-
-       if (quoted_len == 0) {
-               set_parse_error(reader, 0, "Missing closing quotation mark in section header");
-               goto end_error;
-       }
-
-       GITERR_CHECK_ALLOC_ADD(&alloc_len, base_name_len, quoted_len);
-       GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2);
-
-       if (git_buf_grow(&buf, alloc_len) < 0 ||
-           git_buf_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_buf_putc(&buf, (char)c);
-               c = line[++rpos];
-       } while (line + rpos < last_quote);
-
-end_parse:
-       if (git_buf_oom(&buf))
-               goto end_error;
-
-       if (line[rpos] != '"' || line[rpos + 1] != ']') {
-               set_parse_error(reader, rpos, "Unexpected text after closing quotes");
-               git_buf_free(&buf);
-               return -1;
-       }
-
-       *section_name = git_buf_detach(&buf);
-       return 0;
-
-end_error:
-       git_buf_free(&buf);
-
-       return -1;
-}
-
-static int parse_section_header(struct reader *reader, char **section_out)
-{
-       char *name, *name_end;
-       int name_length, c, pos;
-       int result;
-       char *line;
-       size_t line_len;
-
-       line = reader_readline(reader, true);
-       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;
-       }
-
-       GITERR_CHECK_ALLOC_ADD(&line_len, (size_t)(name_end - line), 1);
-       name = git__malloc(line_len);
-       GITERR_CHECK_ALLOC(name);
-
-       name_length = 0;
-       pos = 0;
-
-       /* Make sure we were given a section header */
-       c = line[pos++];
-       assert(c == '[');
-
-       c = line[pos++];
-
-       do {
-               if (git__isspace(c)){
-                       name[name_length] = '\0';
-                       result = parse_section_header_ext(reader, line, 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 0;
-
-fail_parse:
-       git__free(line);
-       git__free(name);
-       return -1;
-}
-
-static int skip_bom(struct reader *reader)
-{
-       git_bom_t bom;
-       int bom_offset = git_buf_text_detect_bom(&bom,
-               &reader->buffer, reader->read_ptr - reader->buffer.ptr);
-
-       if (bom == GIT_BOM_UTF8)
-               reader->read_ptr += 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"
-*/
-
-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] != '\\')
-                       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 included_path(git_buf *out, const char *dir, const char *path)
 {
        /* From the user's home */
@@ -1261,9 +878,6 @@ static int included_path(git_buf *out, const char *dir, const char *path)
        return git_path_join_unrooted(out, path, dir, NULL);
 }
 
-static const char *escapes = "ntb\"\\";
-static const char *escaped = "\n\t\b\"\\";
-
 /* Escape the values to write them to the file */
 static char *escape_value(const char *ptr)
 {
@@ -1281,9 +895,9 @@ static char *escape_value(const char *ptr)
                return NULL;
 
        while (*ptr != '\0') {
-               if ((esc = strchr(escaped, *ptr)) != NULL) {
+               if ((esc = strchr(git_config_escaped, *ptr)) != NULL) {
                        git_buf_putc(&buf, '\\');
-                       git_buf_putc(&buf, escapes[esc - escaped]);
+                       git_buf_putc(&buf, git_config_escapes[esc - git_config_escaped]);
                } else {
                        git_buf_putc(&buf, *ptr);
                }
@@ -1298,264 +912,151 @@ static char *escape_value(const char *ptr)
        return git_buf_detach(&buf);
 }
 
-/* '\"' -> '"' 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(escapes, *ptr)) != NULL) {
-                               *fixed++ = escaped[esc - escapes];
-                       } else {
-                               git__free(str);
-                               giterr_set(GITERR_CONFIG, "invalid escape at %s", ptr);
-                               return -1;
-                       }
-               }
-               ptr++;
-       }
-
-done:
-       *fixed = '\0';
-       *out = str;
-
-       return 0;
-}
+struct parse_data {
+       const git_repository *repo;
+       const char *file_path;
+       git_strmap *values;
+       git_config_level_t level;
+       int depth;
+};
 
-static int parse_multiline_variable(struct reader *reader, git_buf *value, int in_quotes)
+static int parse_include(git_config_parser *reader,
+       struct parse_data *parse_data, const char *file)
 {
-       char *line = NULL, *proc_line = NULL;
-       int quote_count;
-       bool multiline;
-
-       /* Check that the next line exists */
-       line = reader_readline(reader, false);
-       if (line == NULL)
-               return -1;
-
-       /* We've reached the end of the file, there is no continuation.
-        * (this is not an error).
-        */
-       if (line[0] == '\0') {
-               git__free(line);
-               return 0;
-       }
+       struct config_file *include;
+       git_buf path = GIT_BUF_INIT;
+       char *dir;
+       int result;
 
-       quote_count = strip_comments(line, !!in_quotes);
+       if ((result = git_path_dirname_r(&path, reader->file->path)) < 0)
+               return result;
 
-       /* If it was just a comment, pretend it didn't exist */
-       if (line[0] == '\0') {
-               git__free(line);
-               return parse_multiline_variable(reader, value, quote_count);
-               /* TODO: unbounded recursion. This **could** be exploitable */
-       }
+       dir = git_buf_detach(&path);
+       result = included_path(&path, dir, file);
+       git__free(dir);
 
-       if (unescape_line(&proc_line, &multiline, line, in_quotes) < 0) {
-               git__free(line);
-               return -1;
-       }
-       /* add this line to the multiline var */
+       if (result < 0)
+               return result;
 
-       git_buf_puts(value, proc_line);
-       git__free(line);
-       git__free(proc_line);
+       include = git_array_alloc(reader->file->includes);
+       memset(include, 0, sizeof(*include));
+       git_array_init(include->includes);
+       include->path = git_buf_detach(&path);
 
-       /*
-        * If we need to continue reading the next line, let's just
-        * keep putting stuff in the buffer
-        */
-       if (multiline)
-               return parse_multiline_variable(reader, value, quote_count);
+       result = config_read(parse_data->values, parse_data->repo,
+               include, parse_data->level, parse_data->depth+1);
 
-       return 0;
-}
+       if (result == GIT_ENOTFOUND) {
+               giterr_clear();
+               result = 0;
+       }
 
-GIT_INLINE(bool) is_namechar(char c)
-{
-       return isalnum(c) || c == '-';
+       return result;
 }
 
-static int parse_name(
-       char **name, const char **value, struct reader *reader, const char *line)
+static int do_match_gitdir(
+       int *matches,
+       const git_repository *repo,
+       const char *cfg_file,
+       const char *value,
+       bool case_insensitive)
 {
-       const char *name_end = line, *value_start;
-
-       *name = NULL;
-       *value = NULL;
+       git_buf path = GIT_BUF_INIT;
+       int error, fnmatch_flags;
 
-       while (*name_end && is_namechar(*name_end))
-               name_end++;
+       if (value[0] == '.' && git_path_is_dirsep(value[1])) {
+               git_path_dirname_r(&path, cfg_file);
+               git_buf_joinpath(&path, path.ptr, value + 2);
+       } else if (value[0] == '~' && git_path_is_dirsep(value[1]))
+               git_sysdir_expand_global_file(&path, value + 1);
+       else if (!git_path_is_absolute(value))
+               git_buf_joinpath(&path, "**", value);
+       else
+               git_buf_sets(&path, value);
 
-       if (line == name_end) {
-               set_parse_error(reader, 0, "Invalid configuration key");
-               return -1;
+       if (git_buf_oom(&path)) {
+               error = -1;
+               goto out;
        }
 
-       value_start = name_end;
+       if (git_path_is_dirsep(value[strlen(value) - 1]))
+               git_buf_puts(&path, "**");
 
-       while (*value_start && git__isspace(*value_start))
-               value_start++;
+       fnmatch_flags = FNM_PATHNAME|FNM_LEADING_DIR;
+       if (case_insensitive)
+               fnmatch_flags |= FNM_IGNORECASE;
 
-       if (*value_start == '=') {
-               *value = value_start + 1;
-       } else if (*value_start) {
-               set_parse_error(reader, 0, "Invalid configuration key");
-               return -1;
-       }
+       if ((error = p_fnmatch(path.ptr, git_repository_path(repo), fnmatch_flags)) < 0)
+               goto out;
 
-       if ((*name = git__strndup(line, name_end - line)) == NULL)
-               return -1;
+       *matches = (error == 0);
 
-       return 0;
+out:
+       git_buf_free(&path);
+       return error;
 }
 
-static int parse_variable(struct reader *reader, char **var_name, char **var_value)
+static int conditional_match_gitdir(
+       int *matches,
+       const git_repository *repo,
+       const char *cfg_file,
+       const char *value)
 {
-       const char *value_start = NULL;
-       char *line;
-       int quote_count;
-       bool multiline;
-
-       line = reader_readline(reader, true);
-       if (line == NULL)
-               return -1;
-
-       quote_count = strip_comments(line, 0);
-
-       /* If there is no value, boolean true is assumed */
-       *var_value = NULL;
-
-       if (parse_name(var_name, &value_start, reader, line) < 0)
-               goto on_error;
-
-       /*
-        * Now, let's try to parse the value
-        */
-       if (value_start != NULL) {
-               while (git__isspace(value_start[0]))
-                       value_start++;
-
-               if (unescape_line(var_value, &multiline, value_start, 0) < 0)
-                       goto on_error;
-
-               if (multiline) {
-                       git_buf multi_value = GIT_BUF_INIT;
-                       git_buf_attach(&multi_value, *var_value, 0);
-
-                       if (parse_multiline_variable(reader, &multi_value, quote_count) < 0 ||
-                               git_buf_oom(&multi_value)) {
-                               git_buf_free(&multi_value);
-                               goto on_error;
-                       }
-
-                       *var_value = git_buf_detach(&multi_value);
-               }
-       }
-
-       git__free(line);
-       return 0;
-
-on_error:
-       git__free(*var_name);
-       git__free(line);
-       return -1;
+       return do_match_gitdir(matches, repo, cfg_file, value, false);
 }
 
-static int config_parse(
-       struct reader *reader,
-       int (*on_section)(struct reader **reader, const char *current_section, const char *line, size_t line_len, void *data),
-       int (*on_variable)(struct reader **reader, const char *current_section, char *var_name, char *var_value, const char *line, size_t line_len, void *data),
-       int (*on_comment)(struct reader **reader, const char *line, size_t line_len, void *data),
-       int (*on_eof)(struct reader **reader, const char *current_section, void *data),
-       void *data)
+static int conditional_match_gitdir_i(
+       int *matches,
+       const git_repository *repo,
+       const char *cfg_file,
+       const char *value)
 {
-       char *current_section = NULL, *var_name, *var_value, *line_start;
-       char c;
-       size_t line_len;
-       int result = 0;
+       return do_match_gitdir(matches, repo, cfg_file, value, true);
+}
 
-       skip_bom(reader);
+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 }
+};
 
-       while (result == 0 && !reader->eof) {
-               line_start = reader->read_ptr;
+static int parse_conditional_include(git_config_parser *reader,
+       struct parse_data *parse_data, const char *section, const char *file)
+{
+       char *condition;
+       size_t i;
+       int error = 0, matches;
 
-               c = reader_peek(reader, SKIP_WHITESPACE);
+       if (!parse_data->repo)
+               return 0;
 
-               switch (c) {
-               case '\0': /* EOF when peeking, set EOF in the reader to exit the loop */
-                       reader->eof = 1;
-                       break;
+       condition = git__substrdup(section + strlen("includeIf."),
+                                  strlen(section) - strlen("includeIf.") - strlen(".path"));
 
-               case '[': /* section header, new section begins */
-                       git__free(current_section);
-                       current_section = NULL;
+       for (i = 0; i < ARRAY_SIZE(conditions); i++) {
+               if (git__prefixcmp(condition, conditions[i].prefix))
+                       continue;
 
-                       if ((result = parse_section_header(reader, &current_section)) == 0 && on_section) {
-                               line_len = reader->read_ptr - line_start;
-                               result = on_section(&reader, current_section, line_start, line_len, data);
-                       }
+               if ((error = conditions[i].matches(&matches,
+                                                  parse_data->repo,
+                                                  parse_data->file_path,
+                                                  condition + strlen(conditions[i].prefix))) < 0)
                        break;
 
-               case '\n': /* comment or whitespace-only */
-               case ';':
-               case '#':
-                       reader_consume_line(reader);
-
-                       if (on_comment) {
-                               line_len = reader->read_ptr - line_start;
-                               result = on_comment(&reader, line_start, line_len, data);
-                       }
-                       break;
+               if (matches)
+                       error = parse_include(reader, parse_data, file);
 
-               default: /* assume variable declaration */
-                       if ((result = parse_variable(reader, &var_name, &var_value)) == 0 && on_variable) {
-                               line_len = reader->read_ptr - line_start;
-                               result = on_variable(&reader, current_section, var_name, var_value, line_start, line_len, data);
-                       }
-                       break;
-               }
+               break;
        }
 
-       if (on_eof)
-               result = on_eof(&reader, current_section, data);
-
-       git__free(current_section);
-       return result;
+       git__free(condition);
+       return error;
 }
 
-struct parse_data {
-       git_strmap *values;
-       diskfile_backend *cfg_file;
-       uint32_t reader_idx;
-       git_config_level_t level;
-       int depth;
-};
-
 static int read_on_variable(
-       struct reader **reader,
+       git_config_parser *reader,
        const char *current_section,
        char *var_name,
        char *var_value,
@@ -1596,74 +1097,61 @@ static int read_on_variable(
        result = 0;
 
        /* Add or append the new config option */
-       if (!git__strcmp(var->entry->name, "include.path")) {
-               struct reader *r;
-               git_buf path = GIT_BUF_INIT;
-               char *dir;
-               uint32_t index;
-
-               r = git_array_alloc(parse_data->cfg_file->readers);
-               /* The reader may have been reallocated */
-               *reader = git_array_get(parse_data->cfg_file->readers, parse_data->reader_idx);
-               memset(r, 0, sizeof(struct reader));
-
-               if ((result = git_path_dirname_r(&path, (*reader)->file_path)) < 0)
-                       return result;
-
-               /* We need to know our index in the array, as the next config_parse call may realloc */
-               index = git_array_size(parse_data->cfg_file->readers) - 1;
-               dir = git_buf_detach(&path);
-               result = included_path(&path, dir, var->entry->value);
-               git__free(dir);
+       if (!git__strcmp(var->entry->name, "include.path"))
+               result = parse_include(reader, parse_data, var->entry->value);
+       else if (!git__prefixcmp(var->entry->name, "includeif.") &&
+                !git__suffixcmp(var->entry->name, ".path"))
+               result = parse_conditional_include(reader, parse_data,
+                                                  var->entry->name, var->entry->value);
 
-               if (result < 0)
-                       return result;
-
-               r->file_path = git_buf_detach(&path);
-               git_buf_init(&r->buffer, 0);
-
-               result = git_futils_readbuffer_updated(
-                       &r->buffer, r->file_path, &r->checksum, NULL);
-
-               if (result == 0) {
-                       result = config_read(parse_data->values, parse_data->cfg_file, r, parse_data->level, parse_data->depth+1);
-                       r = git_array_get(parse_data->cfg_file->readers, index);
-                       *reader = git_array_get(parse_data->cfg_file->readers, parse_data->reader_idx);
-               } else if (result == GIT_ENOTFOUND) {
-                       giterr_clear();
-                       result = 0;
-               }
-
-               git_buf_free(&r->buffer);
-       }
 
        return result;
 }
 
-static int config_read(git_strmap *values, diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth)
+static int config_read(
+       git_strmap *values,
+       const git_repository *repo,
+       git_config_file *file,
+       git_config_level_t level,
+       int depth)
 {
        struct parse_data parse_data;
+       git_config_parser reader;
+       git_buf contents = GIT_BUF_INIT;
+       int error;
 
        if (depth >= MAX_INCLUDE_DEPTH) {
                giterr_set(GITERR_CONFIG, "maximum config include depth reached");
                return -1;
        }
 
+       if ((error = git_futils_readbuffer(&contents, file->path)) < 0)
+               goto out;
+
+       git_parse_ctx_init(&reader.ctx, contents.ptr, contents.size);
+
+       if ((error = git_hash_buf(&file->checksum, contents.ptr, contents.size)) < 0)
+               goto out;
+
        /* Initialize the reading position */
-       reader->read_ptr = reader->buffer.ptr;
-       reader->eof = 0;
+       reader.file = file;
+       git_parse_ctx_init(&reader.ctx, contents.ptr, contents.size);
 
        /* If the file is empty, there's nothing for us to do */
-       if (*reader->read_ptr == '\0')
-               return 0;
+       if (!reader.ctx.content || *reader.ctx.content == '\0')
+               goto out;
 
+       parse_data.repo = repo;
+       parse_data.file_path = file->path;
        parse_data.values = values;
-       parse_data.cfg_file = cfg_file;
-       parse_data.reader_idx = git_array_size(cfg_file->readers) - 1;
        parse_data.level = level;
        parse_data.depth = depth;
 
-       return config_parse(reader, NULL, read_on_variable, NULL, NULL, &parse_data);
+       error = git_config_parse(&reader, NULL, read_on_variable, NULL, NULL, &parse_data);
+
+out:
+       git_buf_free(&contents);
+       return error;
 }
 
 static int write_section(git_buf *fbuf, const char *key)
@@ -1719,7 +1207,9 @@ struct write_data {
        git_buf 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 regex_t *preg;
        const char *value;
@@ -1747,7 +1237,7 @@ static int write_value(struct write_data *write_data)
 
        q = quotes_for_value(write_data->value);
        result = git_buf_printf(write_data->buf,
-               "\t%s = %s%s%s\n", write_data->name, q, write_data->value, q);
+               "\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
@@ -1760,7 +1250,7 @@ static int write_value(struct write_data *write_data)
 }
 
 static int write_on_section(
-       struct reader **reader,
+       git_config_parser *reader,
        const char *current_section,
        const char *line,
        size_t line_len,
@@ -1796,7 +1286,7 @@ static int write_on_section(
 }
 
 static int write_on_variable(
-       struct reader **reader,
+       git_config_parser *reader,
        const char *current_section,
        char *var_name,
        char *var_value,
@@ -1846,7 +1336,7 @@ static int write_on_variable(
        return write_value(write_data);
 }
 
-static int write_on_comment(struct reader **reader, const char *line, size_t line_len, void *data)
+static int write_on_comment(git_config_parser *reader, const char *line, size_t line_len, void *data)
 {
        struct write_data *write_data;
 
@@ -1857,7 +1347,7 @@ static int write_on_comment(struct reader **reader, const char *line, size_t lin
 }
 
 static int write_on_eof(
-       struct reader **reader, const char *current_section, void *data)
+       git_config_parser *reader, const char *current_section, void *data)
 {
        struct write_data *write_data = (struct write_data *)data;
        int result = 0;
@@ -1878,7 +1368,7 @@ static int write_on_eof(
        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->section);
+                       result = write_section(write_data->buf, write_data->orig_section);
 
                if (!result)
                        result = write_value(write_data);
@@ -1890,37 +1380,35 @@ static int write_on_eof(
 /*
  * This is pretty much the parsing, except we write out anything we don't have
  */
-static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char* value)
+static int config_write(diskfile_backend *cfg, const char *orig_key, const char *key, const regex_t *preg, const char* value)
 {
        int result;
-       char *section, *name, *ldot;
+       char *orig_section, *section, *orig_name, *name, *ldot;
        git_filebuf file = GIT_FILEBUF_INIT;
-       git_buf buf = GIT_BUF_INIT;
-       struct reader *reader = git_array_get(cfg->readers, 0);
+       git_buf buf = GIT_BUF_INIT, contents = GIT_BUF_INIT;
+       git_config_parser reader;
        struct write_data write_data;
 
+       memset(&reader, 0, sizeof(reader));
+       reader.file = &cfg->file;
+
        if (cfg->locked) {
-               result = git_buf_puts(&reader->buffer, git_buf_cstr(&cfg->locked_content));
+               result = git_buf_puts(&contents, git_buf_cstr(&cfg->locked_content));
        } else {
                /* Lock the file */
                if ((result = git_filebuf_open(
-                            &file, cfg->file_path, GIT_FILEBUF_HASH_CONTENTS, GIT_CONFIG_FILE_MODE)) < 0) {
-                       git_buf_free(&reader->buffer);
+                            &file, cfg->file.path, GIT_FILEBUF_HASH_CONTENTS, GIT_CONFIG_FILE_MODE)) < 0) {
+                       git_buf_free(&contents);
                        return result;
                }
 
                /* We need to read in our own config file */
-               result = git_futils_readbuffer(&reader->buffer, cfg->file_path);
+               result = git_futils_readbuffer(&contents, cfg->file.path);
        }
 
        /* Initialise the reading position */
-       if (result == GIT_ENOTFOUND) {
-               reader->read_ptr = NULL;
-               reader->eof = 1;
-               git_buf_clear(&reader->buffer);
-       } else if (result == 0) {
-               reader->read_ptr = reader->buffer.ptr;
-               reader->eof = 0;
+       if (result == 0 || result == GIT_ENOTFOUND) {
+               git_parse_ctx_init(&reader.ctx, contents.ptr, contents.size);
        } else {
                git_filebuf_cleanup(&file);
                return -1; /* OS error when reading the file */
@@ -1929,18 +1417,32 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
        ldot = strrchr(key, '.');
        name = ldot + 1;
        section = git__strndup(key, ldot - key);
+       GITERR_CHECK_ALLOC(section);
+
+       ldot = strrchr(orig_key, '.');
+       orig_name = ldot + 1;
+       orig_section = git__strndup(orig_key, ldot - orig_key);
+       GITERR_CHECK_ALLOC(orig_section);
 
        write_data.buf = &buf;
        git_buf_init(&write_data.buffered_comment, 0);
+       write_data.orig_section = orig_section;
        write_data.section = section;
        write_data.in_section = 0;
        write_data.preg_replaced = 0;
+       write_data.orig_name = orig_name;
        write_data.name = name;
        write_data.preg = preg;
        write_data.value = value;
 
-       result = config_parse(reader, write_on_section, write_on_variable, write_on_comment, write_on_eof, &write_data);
+       result = git_config_parse(&reader,
+               write_on_section,
+               write_on_variable,
+               write_on_comment,
+               write_on_eof,
+               &write_data);
        git__free(section);
+       git__free(orig_section);
        git_buf_free(&write_data.buffered_comment);
 
        if (result < 0) {
@@ -1960,6 +1462,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
 
 done:
        git_buf_free(&buf);
-       git_buf_free(&reader->buffer);
+       git_buf_free(&contents);
+       git_parse_ctx_clear(&reader.ctx);
        return result;
 }
index 654e6cacf156da3a7ab494cac498046d4320b6cc..72818e58c0d2bb660b36289de3e800c97283c6b5 100644 (file)
@@ -7,12 +7,14 @@
 #ifndef INCLUDE_config_file_h__
 #define INCLUDE_config_file_h__
 
+#include "common.h"
+
 #include "git2/sys/config.h"
 #include "git2/config.h"
 
-GIT_INLINE(int) git_config_file_open(git_config_backend *cfg, unsigned int level)
+GIT_INLINE(int) git_config_file_open(git_config_backend *cfg, unsigned int level, const git_repository *repo)
 {
-       return cfg->open(cfg, level);
+       return cfg->open(cfg, level, repo);
 }
 
 GIT_INLINE(void) git_config_file_free(git_config_backend *cfg)
@@ -69,4 +71,3 @@ GIT_INLINE(int) git_config_file_unlock(git_config_backend *cfg, int success)
 extern int git_config_file_normalize_section(char *start, char *end);
 
 #endif
-
diff --git a/src/config_parse.c b/src/config_parse.c
new file mode 100644 (file)
index 0000000..149550d
--- /dev/null
@@ -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 "config_parse.h"
+
+#include "buf_text.h"
+
+#include <ctype.h>
+
+static void set_parse_error(git_config_parser *reader, int col, const char *error_str)
+{
+       giterr_set(GITERR_CONFIG, "failed to parse config file: %s (in %s:%"PRIuZ", column %d)",
+               error_str, reader->file->path, reader->ctx.line_num, col);
+}
+
+
+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] != '\\')
+                       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_section_header_ext(git_config_parser *reader, const char *line, const char *base_name, char **section_name)
+{
+       int c, rpos;
+       char *first_quote, *last_quote;
+       git_buf buf = GIT_BUF_INIT;
+       size_t quoted_len, alloc_len, base_name_len = strlen(base_name);
+
+       /*
+        * base_name is what came before the space. We should be at the
+        * first quotation mark, except for now, line isn't being kept in
+        * sync so we only really use it to calculate the length.
+        */
+
+       first_quote = strchr(line, '"');
+       if (first_quote == NULL) {
+               set_parse_error(reader, 0, "Missing quotation marks in section header");
+               goto end_error;
+       }
+
+       last_quote = strrchr(line, '"');
+       quoted_len = last_quote - first_quote;
+
+       if (quoted_len == 0) {
+               set_parse_error(reader, 0, "Missing closing quotation mark in section header");
+               goto end_error;
+       }
+
+       GITERR_CHECK_ALLOC_ADD(&alloc_len, base_name_len, quoted_len);
+       GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2);
+
+       if (git_buf_grow(&buf, alloc_len) < 0 ||
+           git_buf_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_buf_putc(&buf, (char)c);
+               c = line[++rpos];
+       } while (line + rpos < last_quote);
+
+end_parse:
+       if (git_buf_oom(&buf))
+               goto end_error;
+
+       if (line[rpos] != '"' || line[rpos + 1] != ']') {
+               set_parse_error(reader, rpos, "Unexpected text after closing quotes");
+               git_buf_free(&buf);
+               return -1;
+       }
+
+       *section_name = git_buf_detach(&buf);
+       return 0;
+
+end_error:
+       git_buf_free(&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;
+       }
+
+       GITERR_CHECK_ALLOC_ADD(&line_len, (size_t)(name_end - line), 1);
+       name = git__malloc(line_len);
+       GITERR_CHECK_ALLOC(name);
+
+       name_length = 0;
+       pos = 0;
+
+       /* Make sure we were given a section header */
+       c = line[pos++];
+       assert(c == '[');
+
+       c = line[pos++];
+
+       do {
+               if (git__isspace(c)){
+                       name[name_length] = '\0';
+                       result = parse_section_header_ext(reader, line, 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 0;
+
+fail_parse:
+       git__free(line);
+       git__free(name);
+       return -1;
+}
+
+static int skip_bom(git_parse_ctx *parser)
+{
+       git_buf buf = GIT_BUF_INIT_CONST(parser->content, parser->content_len);
+       git_bom_t bom;
+       int bom_offset = git_buf_text_detect_bom(&bom, &buf);
+
+       if (bom == GIT_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);
+                               giterr_set(GITERR_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_buf *value, int in_quotes)
+{
+       char *line = NULL, *proc_line = NULL;
+       int quote_count;
+       bool multiline;
+
+       /* Check that the next line exists */
+       git_parse_advance_line(&reader->ctx);
+       line = git__strndup(reader->ctx.line, reader->ctx.line_len);
+       if (line == NULL)
+               return -1;
+
+       /* We've reached the end of the file, there is no continuation.
+        * (this is not an error).
+        */
+       if (line[0] == '\0') {
+               git__free(line);
+               return 0;
+       }
+
+       quote_count = strip_comments(line, !!in_quotes);
+
+       /* If it was just a comment, pretend it didn't exist */
+       if (line[0] == '\0') {
+               git__free(line);
+               return parse_multiline_variable(reader, value, quote_count);
+               /* TODO: unbounded recursion. This **could** be exploitable */
+       }
+
+       if (unescape_line(&proc_line, &multiline, line, in_quotes) < 0) {
+               git__free(line);
+               return -1;
+       }
+       /* add this line to the multiline var */
+
+       git_buf_puts(value, proc_line);
+       git__free(line);
+       git__free(proc_line);
+
+       /*
+        * If we need to continue reading the next line, let's just
+        * keep putting stuff in the buffer
+        */
+       if (multiline)
+               return parse_multiline_variable(reader, value, quote_count);
+
+       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)
+{
+       const char *value_start = NULL;
+       char *line;
+       int quote_count;
+       bool multiline;
+
+       git_parse_advance_ws(&reader->ctx);
+       line = git__strndup(reader->ctx.line, reader->ctx.line_len);
+       if (line == NULL)
+               return -1;
+
+       quote_count = strip_comments(line, 0);
+
+       /* If there is no value, boolean true is assumed */
+       *var_value = NULL;
+
+       if (parse_name(var_name, &value_start, reader, line) < 0)
+               goto on_error;
+
+       /*
+        * Now, let's try to parse the value
+        */
+       if (value_start != NULL) {
+               while (git__isspace(value_start[0]))
+                       value_start++;
+
+               if (unescape_line(var_value, &multiline, value_start, 0) < 0)
+                       goto on_error;
+
+               if (multiline) {
+                       git_buf multi_value = GIT_BUF_INIT;
+                       git_buf_attach(&multi_value, *var_value, 0);
+
+                       if (parse_multiline_variable(reader, &multi_value, quote_count) < 0 ||
+                               git_buf_oom(&multi_value)) {
+                               git_buf_free(&multi_value);
+                               goto on_error;
+                       }
+
+                       *var_value = git_buf_detach(&multi_value);
+               }
+       }
+
+       git__free(line);
+       return 0;
+
+on_error:
+       git__free(*var_name);
+       git__free(line);
+       return -1;
+}
+
+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 *data)
+{
+       git_parse_ctx *ctx;
+       char *current_section = NULL, *var_name, *var_value;
+       int result = 0;
+
+       ctx = &parser->ctx;
+
+       skip_bom(ctx);
+
+       for (; ctx->remain_len > 0; git_parse_advance_line(ctx)) {
+               const char *line_start = parser->ctx.line;
+               size_t line_len = parser->ctx.line_len;
+               char c;
+
+               /*
+                * 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;
+
+                       if ((result = parse_section_header(parser, &current_section)) == 0 && on_section) {
+                               result = on_section(parser, current_section, line_start, line_len, data);
+                       }
+                       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, data);
+                       }
+                       break;
+
+               default: /* assume variable declaration */
+                       if ((result = parse_variable(parser, &var_name, &var_value)) == 0 && on_variable) {
+                               result = on_variable(parser, current_section, var_name, var_value, line_start, line_len, data);
+                       }
+                       break;
+               }
+
+               if (result < 0)
+                       goto out;
+       }
+
+       if (on_eof)
+               result = on_eof(parser, current_section, data);
+
+out:
+       git__free(current_section);
+       return result;
+}
diff --git a/src/config_parse.h b/src/config_parse.h
new file mode 100644 (file)
index 0000000..d14a8e6
--- /dev/null
@@ -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_config_parse_h__
+#define INCLUDE_config_parse_h__
+
+#include "common.h"
+#include "array.h"
+#include "oid.h"
+#include "parse.h"
+
+static const char *git_config_escapes = "ntb\"\\";
+static const char *git_config_escaped = "\n\t\b\"\\";
+
+typedef struct config_file {
+       git_oid checksum;
+       char *path;
+       git_array_t(struct config_file) includes;
+} git_config_file;
+
+typedef struct {
+       struct config_file *file;
+       git_parse_ctx ctx;
+} git_config_parser;
+
+typedef int (*git_config_parser_section_cb)(
+       git_config_parser *parser,
+       const char *current_section,
+       const char *line,
+       size_t line_len,
+       void *data);
+
+typedef int (*git_config_parser_variable_cb)(
+       git_config_parser *parser,
+       const char *current_section,
+       char *var_name,
+       char *var_value,
+       const char *line,
+       size_t line_len,
+       void *data);
+
+typedef int (*git_config_parser_comment_cb)(
+       git_config_parser *parser,
+       const char *line,
+       size_t line_len,
+       void *data);
+
+typedef int (*git_config_parser_eof_cb)(
+       git_config_parser *parser,
+       const char *current_section,
+       void *data);
+
+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 *data);
+
+#endif
index b8ae5cda180e1a57577095db542bad8203371399..9af60076d0625600fd374fe9175803b598d0df69 100644 (file)
@@ -5,12 +5,13 @@
  * 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 "common.h"
 #include "fileops.h"
 #include "hash.h"
 #include "filter.h"
diff --git a/src/curl_stream.c b/src/curl_stream.c
deleted file mode 100644 (file)
index 4e0455c..0000000
+++ /dev/null
@@ -1,362 +0,0 @@
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifdef GIT_CURL
-
-#include <curl/curl.h>
-
-#include "stream.h"
-#include "git2/transport.h"
-#include "buffer.h"
-#include "vector.h"
-#include "proxy.h"
-
-/* This is for backwards compatibility with curl<7.45.0. */
-#ifndef CURLINFO_ACTIVESOCKET
-# define CURLINFO_ACTIVESOCKET CURLINFO_LASTSOCKET
-# define GIT_CURL_BADSOCKET -1
-# define git_activesocket_t long
-#else
-# define GIT_CURL_BADSOCKET CURL_SOCKET_BAD
-# define git_activesocket_t curl_socket_t
-#endif
-
-typedef struct {
-       git_stream parent;
-       CURL *handle;
-       curl_socket_t socket;
-       char curl_error[CURL_ERROR_SIZE + 1];
-       git_cert_x509 cert_info;
-       git_strarray cert_info_strings;
-       git_proxy_options proxy;
-       git_cred *proxy_cred;
-} curl_stream;
-
-static int seterr_curl(curl_stream *s)
-{
-       giterr_set(GITERR_NET, "curl error: %s\n", s->curl_error);
-       return -1;
-}
-
-GIT_INLINE(int) error_no_credentials(void)
-{
-       giterr_set(GITERR_NET, "proxy authentication required, but no callback provided");
-       return GIT_EAUTH;
-}
-
-static int apply_proxy_creds(curl_stream *s)
-{
-       CURLcode res;
-       git_cred_userpass_plaintext *userpass;
-
-       if (!s->proxy_cred)
-               return GIT_ENOTFOUND;
-
-       userpass = (git_cred_userpass_plaintext *) s->proxy_cred;
-       if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYUSERNAME, userpass->username)) != CURLE_OK)
-               return seterr_curl(s);
-       if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYPASSWORD, userpass->password)) != CURLE_OK)
-               return seterr_curl(s);
-
-       return 0;
-}
-
-static int ask_and_apply_proxy_creds(curl_stream *s)
-{
-       int error;
-       git_proxy_options *opts = &s->proxy;
-
-       if (!opts->credentials)
-               return error_no_credentials();
-
-       /* TODO: see if PROXYAUTH_AVAIL helps us here */
-       git_cred_free(s->proxy_cred);
-       s->proxy_cred = NULL;
-       giterr_clear();
-       error = opts->credentials(&s->proxy_cred, opts->url, NULL, GIT_CREDTYPE_USERPASS_PLAINTEXT, opts->payload);
-       if (error == GIT_PASSTHROUGH)
-               return error_no_credentials();
-       if (error < 0) {
-               if (!giterr_last())
-                       giterr_set(GITERR_NET, "proxy authentication was aborted by the user");
-               return error;
-       }
-
-       if (s->proxy_cred->credtype != GIT_CREDTYPE_USERPASS_PLAINTEXT) {
-               giterr_set(GITERR_NET, "credentials callback returned invalid credential type");
-               return -1;
-       }
-
-       return apply_proxy_creds(s);
-}
-
-static int curls_connect(git_stream *stream)
-{
-       curl_stream *s = (curl_stream *) stream;
-       git_activesocket_t sockextr;
-       long connect_last = 0;
-       int failed_cert = 0, error;
-       bool retry_connect;
-       CURLcode res;
-
-       /* Apply any credentials we've already established */
-       error = apply_proxy_creds(s);
-       if (error < 0 && error != GIT_ENOTFOUND)
-               return seterr_curl(s);
-
-       do {
-               retry_connect = 0;
-               res = curl_easy_perform(s->handle);
-
-               curl_easy_getinfo(s->handle, CURLINFO_HTTP_CONNECTCODE, &connect_last);
-
-               /* HTTP 407 Proxy Authentication Required */
-               if (connect_last == 407) {
-                       if ((error = ask_and_apply_proxy_creds(s)) < 0)
-                               return error;
-
-                       retry_connect = true;
-               }
-       } while (retry_connect);
-
-       if (res != CURLE_OK && res != CURLE_PEER_FAILED_VERIFICATION)
-               return seterr_curl(s);
-       if (res == CURLE_PEER_FAILED_VERIFICATION)
-               failed_cert = 1;
-
-       if ((res = curl_easy_getinfo(s->handle, CURLINFO_ACTIVESOCKET, &sockextr)) != CURLE_OK) {
-               return seterr_curl(s);
-       }
-
-       if (sockextr == GIT_CURL_BADSOCKET) {
-               giterr_set(GITERR_NET, "curl socket is no longer valid");
-               return -1;
-       }
-
-       s->socket = sockextr;
-
-       if (s->parent.encrypted && failed_cert)
-               return GIT_ECERTIFICATE;
-
-       return 0;
-}
-
-static int curls_certificate(git_cert **out, git_stream *stream)
-{
-       int error;
-       CURLcode res;
-       struct curl_slist *slist;
-       struct curl_certinfo *certinfo;
-       git_vector strings = GIT_VECTOR_INIT;
-       curl_stream *s = (curl_stream *) stream;
-
-       if ((res = curl_easy_getinfo(s->handle, CURLINFO_CERTINFO, &certinfo)) != CURLE_OK)
-               return seterr_curl(s);
-
-       /* No information is available, can happen with SecureTransport */
-       if (certinfo->num_of_certs == 0) {
-               s->cert_info.parent.cert_type = GIT_CERT_NONE;
-               s->cert_info.data             = NULL;
-               s->cert_info.len              = 0;
-               return 0;
-       }
-
-       if ((error = git_vector_init(&strings, 8, NULL)) < 0)
-               return error;
-
-       for (slist = certinfo->certinfo[0]; slist; slist = slist->next) {
-               char *str = git__strdup(slist->data);
-               GITERR_CHECK_ALLOC(str);
-               git_vector_insert(&strings, str);
-       }
-
-       /* Copy the contents of the vector into a strarray so we can expose them */
-       s->cert_info_strings.strings = (char **) strings.contents;
-       s->cert_info_strings.count   = strings.length;
-
-       s->cert_info.parent.cert_type = GIT_CERT_STRARRAY;
-       s->cert_info.data             = &s->cert_info_strings;
-       s->cert_info.len              = strings.length;
-
-       *out = &s->cert_info.parent;
-
-       return 0;
-}
-
-static int curls_set_proxy(git_stream *stream, const git_proxy_options *proxy_opts)
-{
-       int error;
-       CURLcode res;
-       curl_stream *s = (curl_stream *) stream;
-
-       if ((error = git_proxy_options_dup(&s->proxy, proxy_opts)) < 0)
-               return error;
-
-       if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXY, s->proxy.url)) != CURLE_OK)
-               return seterr_curl(s);
-
-       if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY)) != CURLE_OK)
-               return seterr_curl(s);
-
-       return 0;
-}
-
-static int wait_for(curl_socket_t fd, bool reading)
-{
-       int ret;
-       fd_set infd, outfd, errfd;
-
-       FD_ZERO(&infd);
-       FD_ZERO(&outfd);
-       FD_ZERO(&errfd);
-
-       assert(fd >= 0);
-       FD_SET(fd, &errfd);
-       if (reading)
-               FD_SET(fd, &infd);
-       else
-               FD_SET(fd, &outfd);
-
-       if ((ret = select(fd + 1, &infd, &outfd, &errfd, NULL)) < 0) {
-               giterr_set(GITERR_OS, "error in select");
-               return -1;
-       }
-
-       return 0;
-}
-
-static ssize_t curls_write(git_stream *stream, const char *data, size_t len, int flags)
-{
-       int error;
-       size_t off = 0, sent;
-       CURLcode res;
-       curl_stream *s = (curl_stream *) stream;
-
-       GIT_UNUSED(flags);
-
-       do {
-               if ((error = wait_for(s->socket, false)) < 0)
-                       return error;
-
-               res = curl_easy_send(s->handle, data + off, len - off, &sent);
-               if (res == CURLE_OK)
-                       off += sent;
-       } while ((res == CURLE_OK || res == CURLE_AGAIN) && off < len);
-
-       if (res != CURLE_OK)
-               return seterr_curl(s);
-
-       return len;
-}
-
-static ssize_t curls_read(git_stream *stream, void *data, size_t len)
-{
-       int error;
-       size_t read;
-       CURLcode res;
-       curl_stream *s = (curl_stream *) stream;
-
-       do {
-               if ((error = wait_for(s->socket, true)) < 0)
-                       return error;
-
-               res = curl_easy_recv(s->handle, data, len, &read);
-       } while (res == CURLE_AGAIN);
-
-       if (res != CURLE_OK)
-               return seterr_curl(s);
-
-       return read;
-}
-
-static int curls_close(git_stream *stream)
-{
-       curl_stream *s = (curl_stream *) stream;
-
-       if (!s->handle)
-               return 0;
-
-       curl_easy_cleanup(s->handle);
-       s->handle = NULL;
-       s->socket = 0;
-
-       return 0;
-}
-
-static void curls_free(git_stream *stream)
-{
-       curl_stream *s = (curl_stream *) stream;
-
-       curls_close(stream);
-       git_strarray_free(&s->cert_info_strings);
-       git__free(s);
-}
-
-int git_curl_stream_new(git_stream **out, const char *host, const char *port)
-{
-       curl_stream *st;
-       CURL *handle;
-       int iport = 0, error;
-
-       st = git__calloc(1, sizeof(curl_stream));
-       GITERR_CHECK_ALLOC(st);
-
-       handle = curl_easy_init();
-       if (handle == NULL) {
-               giterr_set(GITERR_NET, "failed to create curl handle");
-               git__free(st);
-               return -1;
-       }
-
-       if ((error = git__strtol32(&iport, port, NULL, 10)) < 0) {
-               git__free(st);
-               return error;
-       }
-
-       curl_easy_setopt(handle, CURLOPT_URL, host);
-       curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, st->curl_error);
-       curl_easy_setopt(handle, CURLOPT_PORT, iport);
-       curl_easy_setopt(handle, CURLOPT_CONNECT_ONLY, 1);
-       curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 1);
-       curl_easy_setopt(handle, CURLOPT_CERTINFO, 1);
-       curl_easy_setopt(handle, CURLOPT_HTTPPROXYTUNNEL, 1);
-       curl_easy_setopt(handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
-
-       /* curl_easy_setopt(handle, CURLOPT_VERBOSE, 1); */
-
-       st->parent.version = GIT_STREAM_VERSION;
-       st->parent.encrypted = 0; /* we don't encrypt ourselves */
-       st->parent.proxy_support = 1;
-       st->parent.connect = curls_connect;
-       st->parent.certificate = curls_certificate;
-       st->parent.set_proxy = curls_set_proxy;
-       st->parent.read = curls_read;
-       st->parent.write = curls_write;
-       st->parent.close = curls_close;
-       st->parent.free = curls_free;
-       st->handle = handle;
-
-       *out = (git_stream *) st;
-       return 0;
-}
-
-#else
-
-#include "stream.h"
-
-int git_curl_stream_new(git_stream **out, const char *host, const char *port)
-{
-       GIT_UNUSED(out);
-       GIT_UNUSED(host);
-       GIT_UNUSED(port);
-
-       giterr_set(GITERR_NET, "curl is not supported in this version");
-       return -1;
-}
-
-
-#endif
diff --git a/src/curl_stream.h b/src/curl_stream.h
deleted file mode 100644 (file)
index 283f0fe..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This 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_curl_stream_h__
-#define INCLUDE_curl_stream_h__
-
-#include "git2/sys/stream.h"
-
-extern int git_curl_stream_new(git_stream **out, const char *host, const char *port);
-
-#endif
index cc937292236b27144a6df0f735d42059be4f0519..f61987304b3eff14477c0b6101e1119bd01369cf 100644 (file)
@@ -6,6 +6,7 @@
 #define INCLUDE_git_delta_h__
 
 #include "common.h"
+
 #include "pack.h"
 
 typedef struct git_delta_index git_delta_index;
index 4a1e253787104d4d6c46b4d81ae53ac577bb7ee4..edf8edfd12be02169ce9cc456c7782ad8de42849 100644 (file)
@@ -4,12 +4,14 @@
  * 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/strarray.h"
 #include "git2/diff.h"
 #include "git2/status.h"
 
-#include "common.h"
 #include "commit.h"
 #include "commit_list.h"
 #include "oidmap.h"
index a93bd4cd0a0670fa9a600fa83292c420753cca30..c7a6528965b9401c1f0f11d52adb14772b2f9d18 100644 (file)
@@ -4,20 +4,20 @@
  * 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/version.h"
-#include "common.h"
+
 #include "diff.h"
+
+#include "git2/version.h"
 #include "diff_generate.h"
 #include "patch.h"
 #include "commit.h"
 #include "index.h"
 
-#define DIFF_FLAG_IS_SET(DIFF,FLAG) \
-       (((DIFF)->opts.flags & (FLAG)) != 0)
-#define DIFF_FLAG_ISNT_SET(DIFF,FLAG) \
-       (((DIFF)->opts.flags & (FLAG)) == 0)
-#define DIFF_FLAG_SET(DIFF,FLAG,VAL) (DIFF)->opts.flags = \
-       (VAL) ? ((DIFF)->opts.flags | (FLAG)) : ((DIFF)->opts.flags & ~(VAL))
+struct patch_id_args {
+       git_hash_ctx ctx;
+       git_oid result;
+       int first_file;
+};
 
 GIT_INLINE(const char *) diff_delta__path(const git_diff_delta *delta)
 {
@@ -374,3 +374,142 @@ int git_diff_format_email_init_options(
        return 0;
 }
 
+static int flush_hunk(git_oid *result, git_hash_ctx *ctx)
+{
+       git_oid hash;
+       unsigned short carry = 0;
+       int error, i;
+
+       if ((error = git_hash_final(&hash, ctx)) < 0 ||
+           (error = git_hash_init(ctx)) < 0)
+               return error;
+
+       for (i = 0; i < GIT_OID_RAWSZ; i++) {
+               carry += result->id[i] + hash.id[i];
+               result->id[i] = carry;
+               carry >>= 8;
+       }
+
+       return 0;
+}
+
+static void strip_spaces(git_buf *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_buf_truncate(buf, len);
+}
+
+static int file_cb(
+       const git_diff_delta *delta,
+       float progress,
+       void *payload)
+{
+       struct patch_id_args *args = (struct patch_id_args *) payload;
+       git_buf buf = GIT_BUF_INIT;
+       int error;
+
+       GIT_UNUSED(progress);
+
+       if (!args->first_file &&
+           (error = flush_hunk(&args->result, &args->ctx)) < 0)
+               goto out;
+       args->first_file = 0;
+
+       if ((error = git_buf_printf(&buf,
+                                   "diff--gita/%sb/%s---a/%s+++b/%s",
+                                   delta->old_file.path,
+                                   delta->new_file.path,
+                                   delta->old_file.path,
+                                   delta->new_file.path)) < 0)
+               goto out;
+
+       strip_spaces(&buf);
+
+       if ((error = git_hash_update(&args->ctx, buf.ptr, buf.size)) < 0)
+               goto out;
+
+out:
+       git_buf_free(&buf);
+       return error;
+}
+
+static int line_cb(
+       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_buf buf = GIT_BUF_INIT;
+       int error;
+
+       GIT_UNUSED(delta);
+       GIT_UNUSED(hunk);
+
+       switch (line->origin) {
+           case GIT_DIFF_LINE_ADDITION:
+               git_buf_putc(&buf, '+');
+               break;
+           case GIT_DIFF_LINE_DELETION:
+               git_buf_putc(&buf, '-');
+               break;
+           case GIT_DIFF_LINE_CONTEXT:
+               break;
+           default:
+               giterr_set(GITERR_PATCH, "invalid line origin for patch");
+               return -1;
+       }
+
+       git_buf_put(&buf, line->content, line->content_len);
+       strip_spaces(&buf);
+
+       if ((error = git_hash_update(&args->ctx, buf.ptr, buf.size)) < 0)
+               goto out;
+
+out:
+       git_buf_free(&buf);
+       return error;
+}
+
+int git_diff_patchid_init_options(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;
+       int error;
+
+       GITERR_CHECK_VERSION(
+               opts, GIT_DIFF_PATCHID_OPTIONS_VERSION, "git_diff_patchid_options");
+
+       memset(&args, 0, sizeof(args));
+       args.first_file = 1;
+       if ((error = git_hash_ctx_init(&args.ctx)) < 0)
+               goto out;
+
+       if ((error = git_diff_foreach(diff, file_cb, NULL, NULL, line_cb, &args)) < 0)
+               goto out;
+
+       if ((error = (flush_hunk(&args.result, &args.ctx))) < 0)
+               goto out;
+
+       git_oid_cpy(out, &args.result);
+
+out:
+       git_hash_ctx_cleanup(&args.ctx);
+       return error;
+}
index 5750d2a0903922570d19608ae6542e81b9783f0d..93374b96eedb0e26a9c93e06211a0695c59c203a 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_diff_h__
 #define INCLUDE_diff_h__
 
+#include "common.h"
+
 #include "git2/diff.h"
 #include "git2/patch.h"
 #include "git2/sys/diff.h"
@@ -32,6 +34,7 @@ typedef enum {
 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 */
@@ -63,4 +66,3 @@ 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
-
index 9109f315509bdc4549929e5c3454381ff8c532a4..7114b06b638ee86a0101e57abed39e39e73bc24f 100644 (file)
@@ -4,12 +4,12 @@
  * 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 "diff_driver.h"
 
 #include "git2/attr.h"
 
 #include "diff.h"
-#include "diff_driver.h"
 #include "strmap.h"
 #include "map.h"
 #include "buf_text.h"
@@ -354,27 +354,30 @@ done:
 }
 
 int git_diff_driver_lookup(
-       git_diff_driver **out, git_repository *repo, const char *path)
+       git_diff_driver **out, git_repository *repo,
+       git_attr_session *attrsession, const char *path)
 {
        int error = 0;
-       const char *value;
+       const char *values[1], *attrs[] = { "diff" };
 
        assert(out);
        *out = NULL;
 
        if (!repo || !path || !strlen(path))
                /* just use the auto value */;
-       else if ((error = git_attr_get(&value, repo, 0, path, "diff")) < 0)
+       else if ((error = git_attr_get_many_with_session(values, repo,
+                       attrsession, 0, path, 1, attrs)) < 0)
                /* return error below */;
-       else if (GIT_ATTR_UNSPECIFIED(value))
+
+       else if (GIT_ATTR_UNSPECIFIED(values[0]))
                /* just use the auto value */;
-       else if (GIT_ATTR_FALSE(value))
+       else if (GIT_ATTR_FALSE(values[0]))
                *out = &global_drivers[DIFF_DRIVER_BINARY];
-       else if (GIT_ATTR_TRUE(value))
+       else if (GIT_ATTR_TRUE(values[0]))
                *out = &global_drivers[DIFF_DRIVER_TEXT];
 
        /* otherwise look for driver information in config and build driver */
-       else if ((error = git_diff_driver_load(out, repo, value)) < 0) {
+       else if ((error = git_diff_driver_load(out, repo, values[0])) < 0) {
                if (error == GIT_ENOTFOUND) {
                        error = 0;
                        giterr_clear();
index 0706dcfc5a5d34d5b2fc85c6f1f3e84a7dc6e4b4..a03a67e676e73c880e6db016268cfe58ff3cd479 100644 (file)
@@ -8,6 +8,8 @@
 #define INCLUDE_diff_driver_h__
 
 #include "common.h"
+
+#include "attr_file.h"
 #include "buffer.h"
 
 typedef struct git_diff_driver_registry git_diff_driver_registry;
@@ -17,7 +19,8 @@ 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 *, const char *);
+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 */
index d5fc5e940d95fbb4c5564efa864825fe8a606804..5bb9c372ab0bdbd97818d72463b894f454c7941a 100644 (file)
@@ -4,12 +4,13 @@
  * 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 "diff_file.h"
+
 #include "git2/blob.h"
 #include "git2/submodule.h"
 #include "diff.h"
 #include "diff_generate.h"
-#include "diff_file.h"
 #include "odb.h"
 #include "fileops.h"
 #include "filter.h"
@@ -53,7 +54,8 @@ static int diff_file_content_init_common(
                fc->src = GIT_ITERATOR_TYPE_TREE;
 
        if (!fc->driver &&
-               git_diff_driver_lookup(&fc->driver, fc->repo, fc->file->path) < 0)
+               git_diff_driver_lookup(&fc->driver, fc->repo,
+                   NULL, fc->file->path) < 0)
                return -1;
 
        /* give driver a chance to modify options */
@@ -100,7 +102,8 @@ int git_diff_file_content__init_from_diff(
        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, fc->file->path) < 0)
+       if (git_diff_driver_lookup(&fc->driver, fc->repo,
+                   &diff->attrsession, fc->file->path) < 0)
                return -1;
 
        switch (delta->status) {
@@ -138,7 +141,6 @@ int git_diff_file_content__init_from_src(
        memset(fc, 0, sizeof(*fc));
        fc->repo = repo;
        fc->file = as_file;
-       fc->blob = src->blob;
 
        if (!src->blob && !src->buf) {
                fc->flags |= GIT_DIFF_FLAG__NO_DATA;
@@ -148,12 +150,15 @@ int git_diff_file_content__init_from_src(
                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 = GIT_OID_HEXSZ;
 
                        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 {
                        fc->file->size = src->buflen;
                        git_odb_hash(&fc->file->id, src->buf, src->buflen, GIT_OBJ_BLOB);
index 0d54b6d330fbc47beda7ea91dcdc6399956980c4..5da7a7bb85738cf61d5d7bf35e52a6ebd43932d8 100644 (file)
@@ -8,6 +8,7 @@
 #define INCLUDE_diff_file_h__
 
 #include "common.h"
+
 #include "diff.h"
 #include "diff_driver.h"
 #include "map.h"
index f6cc04fed22706c5c0b14ab0aaeecb388f8dac19..e11cbe4e4c475e690e71fbfb3aec6746176dad9f 100644 (file)
@@ -4,9 +4,10 @@
  * 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 "diff.h"
+
 #include "diff_generate.h"
+
+#include "diff.h"
 #include "patch_generate.h"
 #include "fileops.h"
 #include "config.h"
@@ -23,7 +24,7 @@
        (((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 & ~(VAL))
+       ((DIFF)->base.opts.flags & ~(FLAG))
 
 typedef struct {
        struct git_diff base;
@@ -388,6 +389,7 @@ 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);
@@ -410,13 +412,14 @@ static git_diff_generated *diff_generated_alloc(
        if ((diff = git__calloc(1, sizeof(git_diff_generated))) == NULL)
                return NULL;
 
-       GIT_REFCOUNT_INC(diff);
+       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));
 
        git_pool_init(&diff->base.pool, 1);
index 63e7f05324320bfb7d75756c1232aa752c2a5e59..3f182b0ba6f010b87b24d0be6193ef5ed60d3775 100644 (file)
@@ -7,6 +7,12 @@
 #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? */
@@ -120,4 +126,3 @@ GIT_INLINE(int) git_diff_file__resolve_zero_size(
 }
 
 #endif
-
index 5e3a7a1778b79f8f5da5da87d7b844c5527db906..59fd8612a8295d0b1ccf70f628fed3a45042bed6 100644 (file)
@@ -4,9 +4,10 @@
  * 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 "diff.h"
+
 #include "diff_parse.h"
+
+#include "diff.h"
 #include "patch.h"
 #include "patch_parse.h"
 
@@ -35,7 +36,7 @@ static git_diff_parsed *diff_parsed_alloc(void)
        if ((diff = git__calloc(1, sizeof(git_diff_parsed))) == NULL)
                return NULL;
 
-       GIT_REFCOUNT_INC(diff);
+       GIT_REFCOUNT_INC(&diff->base);
        diff->base.type = GIT_DIFF_TYPE_PARSED;
        diff->base.strcomp = git__strcmp;
        diff->base.strncomp = git__strncmp;
@@ -82,7 +83,7 @@ int git_diff_from_buffer(
        ctx = git_patch_parse_ctx_init(content, content_len, NULL);
        GITERR_CHECK_ALLOC(ctx);
 
-       while (ctx->remain_len) {
+       while (ctx->parse_ctx.remain_len) {
                if ((error = git_patch_parse(&patch, ctx)) < 0)
                        break;
 
index c47d4cbc93ad24164a24dbb81feccb3c978d691b..876782128454cd837834146e850f168ecefeeedb 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_diff_parse_h__
 #define INCLUDE_diff_parse_h__
 
+#include "common.h"
+
 #include "diff.h"
 
 typedef struct {
index 5aa8a37e66916b66a2d5143552f7736e7ddfbeeb..28ae384243049845c1fe72afc1aab8c533887207 100644 (file)
@@ -4,7 +4,9 @@
  * 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 "diff.h"
 #include "diff_file.h"
 #include "patch_generate.h"
index 2005712cd55710e72040490d76ffa68d737bbe35..583c684593c80fc90775d9a5ca78a7041d1c9856 100644 (file)
@@ -4,7 +4,9 @@
  * 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 "vector.h"
 #include "diff.h"
 #include "patch_generate.h"
index b004ddd662cf99f30889472aec200f2d7a06ccab..bc664dd054c7f028afd837ad5964d8c6491fea4c 100644 (file)
@@ -4,7 +4,8 @@
  * 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 "diff_tform.h"
 
 #include "git2/config.h"
 #include "git2/blob.h"
@@ -685,8 +686,10 @@ static bool is_rename_target(
                        break;
                }
                if (FLAG_SET(opts, GIT_DIFF_FIND_RENAMES_FROM_REWRITES) &&
-                       delta->similarity < opts->rename_from_rewrite_threshold)
+                       delta->similarity < opts->rename_from_rewrite_threshold) {
+                       delta->flags |= GIT_DIFF_FLAG__TO_SPLIT;
                        break;
+               }
 
                return false;
 
index 5bd9712d9c714593e571b4c2ddea2cb38bccfaaa..7abb8b3fedd5dd690508f5f8e7c864d99b7eadcf 100644 (file)
@@ -7,6 +7,10 @@
 #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);
 
@@ -19,4 +23,3 @@ extern int git_diff_find_similar__calc_similarity(
        int *score, void *siga, void *sigb, void *payload);
 
 #endif
-
index 60c4d85cba427e4404b6f2144823b0e41f7eca4c..701eb1b5fc71cf5321fd3b393012f75c60e23228 100644 (file)
@@ -4,11 +4,12 @@
  * 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 "common.h"
 #include "diff.h"
 #include "diff_driver.h"
-#include "diff_xdiff.h"
 #include "patch_generate.h"
 
 static int git_xdiff_scan_int(const char **str, int *value)
@@ -238,6 +239,8 @@ void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts)
                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;
index 88375986b323548d204b63b33039d6af96145661..aca80b131069f7a796256f8abfea58a39471ddd3 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_diff_xdiff_h__
 #define INCLUDE_diff_xdiff_h__
 
+#include "common.h"
+
 #include "diff.h"
 #include "xdiff/xdiff.h"
 #include "patch_generate.h"
index 91acc354146df96f327ce1d11bd98a264210b838..a874163b0d9c636d44483802612c7f13a397cd9b 100644 (file)
@@ -4,7 +4,9 @@
  * 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 "global.h"
 #include "posix.h"
 #include "buffer.h"
diff --git a/src/features.h.in b/src/features.h.in
new file mode 100644 (file)
index 0000000..e03b7a2
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef INCLUDE_features_h__
+#define INCLUDE_features_h__
+
+#cmakedefine GIT_DEBUG_POOL 1
+#cmakedefine GIT_TRACE 1
+#cmakedefine GIT_THREADS 1
+#cmakedefine GIT_MSVC_CRTDBG 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_USE_REGCOMP_L 1
+
+#cmakedefine GIT_SSH 1
+#cmakedefine GIT_SSH_MEMORY_CREDENTIALS 1
+
+#cmakedefine GIT_GSSAPI 1
+#cmakedefine GIT_WINHTTP 1
+#cmakedefine GIT_CURL 1
+
+#cmakedefine GIT_HTTPS 1
+#cmakedefine GIT_OPENSSL 1
+#cmakedefine GIT_SECURE_TRANSPORT 1
+
+#cmakedefine GIT_SHA1_COLLISIONDETECT 1
+#cmakedefine GIT_SHA1_WIN32 1
+#cmakedefine GIT_SHA1_COMMON_CRYPTO 1
+#cmakedefine GIT_SHA1_OPENSSL 1
+
+#endif
index f408a5174e6c19866780cff04b0fc324ce5e97e8..0b22b3673b49618c0ca3c7933f03255e450f825e 100644 (file)
@@ -5,16 +5,16 @@
  * 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 "common.h"
 #include "remote.h"
 #include "refspec.h"
 #include "pack.h"
-#include "fetch.h"
 #include "netops.h"
 #include "repository.h"
 #include "refs.h"
index 0412d4e4418e6f9be5cae484fb99b1c774f9361a..1c75af9c30699cb88e04d2ff6c5c91e076178919 100644 (file)
@@ -7,6 +7,10 @@
 #ifndef INCLUDE_fetch_h__
 #define INCLUDE_fetch_h__
 
+#include "common.h"
+
+#include "git2/remote.h"
+
 #include "netops.h"
 
 int git_fetch_negotiate(git_remote *remote, const git_fetch_options *opts);
index 6e6f3eb5e46cb290d731840bd87adbc8f12ac0d1..e55e7c85b55039f6352b96255efc63aaae22bfc6 100644 (file)
@@ -5,11 +5,11 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
+#include "fetchhead.h"
+
 #include "git2/types.h"
 #include "git2/oid.h"
 
-#include "fetchhead.h"
-#include "common.h"
 #include "buffer.h"
 #include "fileops.h"
 #include "filebuf.h"
@@ -118,7 +118,7 @@ int git_fetchhead_write(git_repository *repo, git_vector *fetchhead_refs)
        if (git_buf_joinpath(&path, repo->gitdir, GIT_FETCH_HEAD_FILE) < 0)
                return -1;
 
-       if (git_filebuf_open(&file, path.ptr, GIT_FILEBUF_FORCE, GIT_REFS_FILE_MODE) < 0) {
+       if (git_filebuf_open(&file, path.ptr, GIT_FILEBUF_APPEND, GIT_REFS_FILE_MODE) < 0) {
                git_buf_free(&path);
                return -1;
        }
index b03bd0f7459499d5f6b5ee33417d61bebe0ac6af..9e51710108b218fb48006d1abb49f647d9de9e0e 100644 (file)
@@ -7,6 +7,9 @@
 #ifndef INCLUDE_fetchhead_h__
 #define INCLUDE_fetchhead_h__
 
+#include "common.h"
+
+#include "oid.h"
 #include "vector.h"
 
 typedef struct git_fetchhead_ref {
index 80250ccdfd4a73917ccc37b786d7b750fe0fd70f..8b7e489da4d920de2a19655f172504a23196572d 100644 (file)
@@ -4,8 +4,9 @@
  * 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 "filebuf.h"
+
 #include "fileops.h"
 
 static const size_t WRITE_BUFFER_SIZE = (4096 * 2);
index c65aea7808287a263d7d23fa6888004c123bd3ad..f51ff230ff7f0a61d6938f3c267ef2bc5582867f 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_filebuf_h__
 #define INCLUDE_filebuf_h__
 
+#include "common.h"
+
 #include "fileops.h"
 #include "hash.h"
 #include <zlib.h>
index 2f3f58d4feddc9cb5ce165027be91f8951d2e3a9..58988c2d2d416e5c13cac676920832c1c810af3c 100644 (file)
@@ -4,8 +4,9 @@
  * 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 "fileops.h"
+
 #include "global.h"
 #include "strmap.h"
 #include <ctype.h>
@@ -101,6 +102,16 @@ int git_futils_open_ro(const char *path)
        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_path_set_error(errno, path, "open");
+
+       close(fd);
+       return 0;
+}
+
 git_off_t git_futils_filesize(git_file fd)
 {
        struct stat sb;
index 46886b0d7729e5e8b32d264d924f045260020532..2844ece211bc2e12086393f1dd4fe9edeb8aef3b 100644 (file)
@@ -8,6 +8,7 @@
 #define INCLUDE_fileops_h__
 
 #include "common.h"
+
 #include "map.h"
 #include "posix.h"
 #include "path.h"
@@ -246,6 +247,11 @@ extern int git_futils_cp_r(
  */
 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
  */
@@ -381,4 +387,4 @@ extern int git_futils_fsync_dir(const char *path);
  */
 extern int git_futils_fsync_parent(const char *path);
 
-#endif /* INCLUDE_fileops_h__ */
+#endif
index 361e0852950007b870f1565f29723f72e27c276b..6ab09790bf1126ab3ec1cd7adb1746e79c475f23 100644 (file)
@@ -5,10 +5,11 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
+#include "filter.h"
+
 #include "common.h"
 #include "fileops.h"
 #include "hash.h"
-#include "filter.h"
 #include "repository.h"
 #include "global.h"
 #include "git2/sys/filter.h"
index 9bd835f94bd80b092692cc82b7f0b74ec156be7a..b1c403ba999f6932d37a53c54a80bffdb3bb6d6c 100644 (file)
@@ -8,6 +8,7 @@
 #define INCLUDE_filter_h__
 
 #include "common.h"
+
 #include "attr_file.h"
 #include "git2/filter.h"
 
index 33c8a25120b840acc3cf8fb5647c84785ac15fc5..3cc2a27ba16b26cd7f062e17409a91c6b481e7ed 100644 (file)
  * Compares a filename or pathname to a pattern.
  */
 
+#include "fnmatch.h"
+
 #include <ctype.h>
 #include <stdio.h>
 #include <string.h>
 
-#include "fnmatch.h"
-
 #define EOS            '\0'
 
 #define RANGE_MATCH            1
index 88af4593978cb45ccc5f8e42422ebe61f2aa1821..ddaae15bb6e7a51ac7a1b2ac8a5222b29a8e1eb1 100644 (file)
@@ -25,8 +25,8 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
-#ifndef INCLUDE_fnmatch__compat_h__
-#define INCLUDE_fnmatch__compat_h__
+#ifndef INCLUDE_fnmatch_h__
+#define INCLUDE_fnmatch_h__
 
 #include "common.h"
 
@@ -45,5 +45,4 @@
 
 extern int p_fnmatch(const char *pattern, const char *string, int flags);
 
-#endif /* _FNMATCH_H */
-
+#endif
index afa57e1d6a80724e7c720d5d54e3a7f3124e6a00..2f9b45bcda262357d23f22ef286d86b11984444c 100644 (file)
@@ -4,13 +4,15 @@
  * 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 "global.h"
+
 #include "hash.h"
 #include "sysdir.h"
 #include "filter.h"
 #include "merge_driver.h"
-#include "openssl_stream.h"
+#include "streams/curl.h"
+#include "streams/openssl.h"
 #include "thread-utils.h"
 #include "git2/global.h"
 #include "transports/ssh.h"
@@ -22,7 +24,7 @@
 
 git_mutex git__mwindow_mutex;
 
-#define MAX_SHUTDOWN_CB 9
+#define MAX_SHUTDOWN_CB 10
 
 static git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB];
 static git_atomic git__n_shutdown_callbacks;
@@ -62,7 +64,8 @@ static int init_common(void)
                (ret = git_filter_global_init()) == 0 &&
                (ret = git_merge_driver_global_init()) == 0 &&
                (ret = git_transport_ssh_global_init()) == 0 &&
-               (ret = git_openssl_stream_global_init()) == 0)
+               (ret = git_openssl_stream_global_init()) == 0 &&
+               (ret = git_curl_stream_global_init()) == 0)
                ret = git_mwindow_global_init();
 
        GIT_MEMORY_BARRIER;
index 88f40aad1c89c9c457de158a811cd16271c089e4..3c0559c688cf4dfba8d31d7a0d8ec2742d9e1303 100644 (file)
@@ -8,6 +8,7 @@
 #define INCLUDE_global_h__
 
 #include "common.h"
+
 #include "mwindow.h"
 #include "hash.h"
 
@@ -24,11 +25,6 @@ typedef struct {
        git_thread *current_thread;
 } git_global_st;
 
-#ifdef GIT_OPENSSL
-# include <openssl/ssl.h>
-extern SSL_CTX *git__ssl_ctx;
-#endif
-
 git_global_st *git__global_state(void);
 
 extern git_mutex git__mwindow_mutex;
index 948f7d306c99f956843cc6212edd47f85de78f0d..df82f0f713d1445fbde590ace61ab9e636dd1ddc 100644 (file)
@@ -5,6 +5,8 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
+#include "common.h"
+
 #include "revwalk.h"
 #include "merge.h"
 #include "git2/graph.h"
index f3645a91393a57a36684664aefbdea60caa12c49..cc6676d4daa16377be5a7888ece09c6f3bc58340 100644 (file)
@@ -5,7 +5,6 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
 #include "hash.h"
 
 int git_hash_buf(git_oid *out, const void *data, size_t len)
index 0db0339dcdcb36ad28f86337fc1baaad5ce828ae..31eaf888972bc075b5987ea9316b625d9ff9157c 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_hash_h__
 #define INCLUDE_hash_h__
 
+#include "common.h"
+
 #include "git2/oid.h"
 
 typedef struct git_hash_prov git_hash_prov;
@@ -40,4 +42,4 @@ int git_hash_final(git_oid *out, git_hash_ctx *c);
 int git_hash_buf(git_oid *out, const void *data, size_t len);
 int git_hash_vec(git_oid *out, git_buf_vec *vec, size_t n);
 
-#endif /* INCLUDE_hash_h__ */
+#endif
index 5fdae8df6eb9d5d5b220daa928b8af9fca0c7512..4c5e3c302d45864cf0244b9635d953cda9edf058 100644 (file)
@@ -5,8 +5,8 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#ifndef INCLUDE_hash_collisiondetect_h__
-#define INCLUDE_hash_collisiondetect_h__
+#ifndef INCLUDE_hash_hash_collisiondetect_h__
+#define INCLUDE_hash_hash_collisiondetect_h__
 
 #include "hash.h"
 #include "sha1dc/sha1.h"
@@ -44,4 +44,4 @@ GIT_INLINE(int) git_hash_final(git_oid *out, git_hash_ctx *ctx)
        return 0;
 }
 
-#endif /* INCLUDE_hash_collisiondetect_h__ */
+#endif
index eeeddd0ccc58563cf5c3dd7b3f5891692b6c249f..5c3887dbad80f301c4b80dd0ac12780089d7a7eb 100644 (file)
@@ -5,8 +5,8 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#ifndef INCLUDE_hash_common_crypto_h__
-#define INCLUDE_hash_common_crypto_h__
+#ifndef INCLUDE_hash_hash_common_crypto_h__
+#define INCLUDE_hash_hash_common_crypto_h__
 
 #include "hash.h"
 
@@ -16,6 +16,8 @@ struct git_hash_ctx {
        CC_SHA1_CTX c;
 };
 
+#define CC_LONG_MAX ((CC_LONG)-1)
+
 #define git_hash_global_init() 0
 #define git_hash_ctx_init(ctx) git_hash_init(ctx)
 #define git_hash_ctx_cleanup(ctx)
@@ -27,10 +29,21 @@ GIT_INLINE(int) git_hash_init(git_hash_ctx *ctx)
        return 0;
 }
 
-GIT_INLINE(int) git_hash_update(git_hash_ctx *ctx, const void *data, size_t len)
+GIT_INLINE(int) git_hash_update(git_hash_ctx *ctx, const void *_data, size_t len)
 {
+       const unsigned char *data = _data;
+
        assert(ctx);
-       CC_SHA1_Update(&ctx->c, data, len);
+
+       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;
 }
 
@@ -41,4 +54,4 @@ GIT_INLINE(int) git_hash_final(git_oid *out, git_hash_ctx *ctx)
        return 0;
 }
 
-#endif /* INCLUDE_hash_common_crypto_h__ */
+#endif
index 472a7a6968ac0729a4bb8c04210140b64e38b8a7..7b33b61948b75bc486ba9657e7e37752c7478edb 100644 (file)
@@ -5,9 +5,9 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
+#include "hash_generic.h"
+
 #include "hash.h"
-#include "hash/hash_generic.h"
 
 #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
 
index daeb1cda8aeb4ea337b0b77a9aef5cc24e4a9dc3..21a0428071f641b66765ed71aeef0a8cfa72c274 100644 (file)
@@ -5,8 +5,10 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#ifndef INCLUDE_hash_generic_h__
-#define INCLUDE_hash_generic_h__
+#ifndef INCLUDE_hash_hash_generic_h__
+#define INCLUDE_hash_hash_generic_h__
+
+#include "common.h"
 
 #include "hash.h"
 
@@ -20,4 +22,4 @@ struct git_hash_ctx {
 #define git_hash_ctx_init(ctx) git_hash_init(ctx)
 #define git_hash_ctx_cleanup(ctx)
 
-#endif /* INCLUDE_hash_generic_h__ */
+#endif
index 9a55d472d09b91b416cc49758bb8f59e413324ca..eb2dcb02f9ea2d6b853b90704cc4b005d20e9624 100644 (file)
@@ -5,8 +5,8 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#ifndef INCLUDE_hash_openssl_h__
-#define INCLUDE_hash_openssl_h__
+#ifndef INCLUDE_hash_hash_openssl_h__
+#define INCLUDE_hash_hash_openssl_h__
 
 #include "hash.h"
 
@@ -23,22 +23,37 @@ struct git_hash_ctx {
 GIT_INLINE(int) git_hash_init(git_hash_ctx *ctx)
 {
        assert(ctx);
-       SHA1_Init(&ctx->c);
+
+       if (SHA1_Init(&ctx->c) != 1) {
+               giterr_set(GITERR_SHA1, "hash_openssl: failed to initialize hash context");
+               return -1;
+       }
+
        return 0;
 }
 
 GIT_INLINE(int) git_hash_update(git_hash_ctx *ctx, const void *data, size_t len)
 {
        assert(ctx);
-       SHA1_Update(&ctx->c, data, len);
+
+       if (SHA1_Update(&ctx->c, data, len) != 1) {
+               giterr_set(GITERR_SHA1, "hash_openssl: failed to update hash");
+               return -1;
+       }
+
        return 0;
 }
 
 GIT_INLINE(int) git_hash_final(git_oid *out, git_hash_ctx *ctx)
 {
        assert(ctx);
-       SHA1_Final(out->id, &ctx->c);
+
+       if (SHA1_Final(out->id, &ctx->c) != 1) {
+               giterr_set(GITERR_SHA1, "hash_openssl: failed to finalize hash");
+               return -1;
+       }
+
        return 0;
 }
 
-#endif /* INCLUDE_hash_openssl_h__ */
+#endif
index 6bae53e55adb742925d1102f7214afeb4b91e4cc..779802c4b721b65cde45acb230d4c1d61bcf510e 100644 (file)
@@ -5,10 +5,10 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
+#include "hash_win32.h"
+
 #include "global.h"
 #include "hash.h"
-#include "hash/hash_win32.h"
 
 #include <wincrypt.h>
 #include <strsafe.h>
@@ -24,16 +24,20 @@ GIT_INLINE(int) hash_cng_prov_init(void)
        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))
+       if (!git_has_win32_version(6, 0, 1)) {
+               giterr_set(GITERR_SHA1, "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_prov.prov.cng.dll = LoadLibrary(dll_path)) == NULL)
+               (hash_prov.prov.cng.dll = LoadLibrary(dll_path)) == NULL) {
+               giterr_set(GITERR_SHA1, "CryptoNG library could not be loaded");
                return -1;
+       }
 
        /* Load the function addresses */
        if ((hash_prov.prov.cng.open_algorithm_provider = (hash_win32_cng_open_algorithm_provider_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptOpenAlgorithmProvider")) == NULL ||
@@ -44,12 +48,16 @@ GIT_INLINE(int) hash_cng_prov_init(void)
                (hash_prov.prov.cng.destroy_hash = (hash_win32_cng_destroy_hash_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptDestroyHash")) == NULL ||
                (hash_prov.prov.cng.close_algorithm_provider = (hash_win32_cng_close_algorithm_provider_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptCloseAlgorithmProvider")) == NULL) {
                FreeLibrary(hash_prov.prov.cng.dll);
+
+               giterr_set(GITERR_OS, "CryptoNG functions could not be loaded");
                return -1;
        }
 
        /* Load the SHA1 algorithm */
        if (hash_prov.prov.cng.open_algorithm_provider(&hash_prov.prov.cng.handle, GIT_HASH_CNG_HASH_TYPE, NULL, GIT_HASH_CNG_HASH_REUSABLE) < 0) {
                FreeLibrary(hash_prov.prov.cng.dll);
+
+               giterr_set(GITERR_OS, "algorithm provider could not be initialized");
                return -1;
        }
 
@@ -57,6 +65,8 @@ GIT_INLINE(int) hash_cng_prov_init(void)
        if (hash_prov.prov.cng.get_property(hash_prov.prov.cng.handle, GIT_HASH_CNG_HASH_OBJECT_LEN, (PBYTE)&hash_prov.prov.cng.hash_object_size, sizeof(DWORD), &size_len, 0) < 0) {
                hash_prov.prov.cng.close_algorithm_provider(hash_prov.prov.cng.handle, 0);
                FreeLibrary(hash_prov.prov.cng.dll);
+
+               giterr_set(GITERR_OS, "algorithm handle could not be found");
                return -1;
        }
 
@@ -75,8 +85,10 @@ GIT_INLINE(void) hash_cng_prov_shutdown(void)
 /* Initialize CryptoAPI */
 GIT_INLINE(int) hash_cryptoapi_prov_init()
 {
-       if (!CryptAcquireContext(&hash_prov.prov.cryptoapi.handle, NULL, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
+       if (!CryptAcquireContext(&hash_prov.prov.cryptoapi.handle, NULL, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
+               giterr_set(GITERR_OS, "legacy hash context could not be started");
                return -1;
+       }
 
        hash_prov.type = CRYPTOAPI;
        return 0;
@@ -129,6 +141,7 @@ GIT_INLINE(int) hash_cryptoapi_init(git_hash_ctx *ctx)
 
        if (!CryptCreateHash(ctx->prov->prov.cryptoapi.handle, CALG_SHA1, 0, 0, &ctx->ctx.cryptoapi.hash_handle)) {
                ctx->ctx.cryptoapi.valid = 0;
+               giterr_set(GITERR_OS, "legacy hash implementation could not be created");
                return -1;
        }
 
@@ -136,12 +149,23 @@ GIT_INLINE(int) hash_cryptoapi_init(git_hash_ctx *ctx)
        return 0;
 }
 
-GIT_INLINE(int) hash_cryptoapi_update(git_hash_ctx *ctx, const void *data, size_t len)
+GIT_INLINE(int) hash_cryptoapi_update(git_hash_ctx *ctx, const void *_data, size_t len)
 {
+       const BYTE *data = (BYTE *)_data;
+
        assert(ctx->ctx.cryptoapi.valid);
 
-       if (!CryptHashData(ctx->ctx.cryptoapi.hash_handle, (const BYTE *)data, (DWORD)len, 0))
-               return -1;
+       while (len > 0) {
+               DWORD chunk = (len > MAXDWORD) ? MAXDWORD : (DWORD)len;
+
+               if (!CryptHashData(ctx->ctx.cryptoapi.hash_handle, data, chunk, 0)) {
+                       giterr_set(GITERR_OS, "legacy hash data could not be updated");
+                       return -1;
+               }
+
+               data += chunk;
+               len -= chunk;
+       }
 
        return 0;
 }
@@ -153,8 +177,10 @@ GIT_INLINE(int) hash_cryptoapi_final(git_oid *out, git_hash_ctx *ctx)
 
        assert(ctx->ctx.cryptoapi.valid);
 
-       if (!CryptGetHashParam(ctx->ctx.cryptoapi.hash_handle, HP_HASHVAL, out->id, &len, 0))
+       if (!CryptGetHashParam(ctx->ctx.cryptoapi.hash_handle, HP_HASHVAL, out->id, &len, 0)) {
+               giterr_set(GITERR_OS, "legacy hash data could not be finished");
                error = -1;
+       }
 
        CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
        ctx->ctx.cryptoapi.valid = 0;
@@ -177,6 +203,8 @@ GIT_INLINE(int) hash_ctx_cng_init(git_hash_ctx *ctx)
 
        if (hash_prov.prov.cng.create_hash(hash_prov.prov.cng.handle, &ctx->ctx.cng.hash_handle, ctx->ctx.cng.hash_object, hash_prov.prov.cng.hash_object_size, NULL, 0, 0) < 0) {
                git__free(ctx->ctx.cng.hash_object);
+
+               giterr_set(GITERR_OS, "hash implementation could not be created");
                return -1;
        }
 
@@ -194,26 +222,41 @@ GIT_INLINE(int) hash_cng_init(git_hash_ctx *ctx)
                return 0;
 
        /* CNG needs to be finished to restart */
-       if (ctx->prov->prov.cng.finish_hash(ctx->ctx.cng.hash_handle, hash, GIT_OID_RAWSZ, 0) < 0)
+       if (ctx->prov->prov.cng.finish_hash(ctx->ctx.cng.hash_handle, hash, GIT_OID_RAWSZ, 0) < 0) {
+               giterr_set(GITERR_OS, "hash implementation could not be finished");
                return -1;
+       }
 
        ctx->ctx.cng.updated = 0;
 
        return 0;
 }
 
-GIT_INLINE(int) hash_cng_update(git_hash_ctx *ctx, const void *data, size_t len)
+GIT_INLINE(int) hash_cng_update(git_hash_ctx *ctx, const void *_data, size_t len)
 {
-       if (ctx->prov->prov.cng.hash_data(ctx->ctx.cng.hash_handle, (PBYTE)data, (ULONG)len, 0) < 0)
-               return -1;
+       PBYTE data = (PBYTE)_data;
+
+       while (len > 0) {
+               ULONG chunk = (len > ULONG_MAX) ? ULONG_MAX : (ULONG)len;
+
+               if (ctx->prov->prov.cng.hash_data(ctx->ctx.cng.hash_handle, data, chunk, 0) < 0) {
+                       giterr_set(GITERR_OS, "hash could not be updated");
+                       return -1;
+               }
+
+               data += chunk;
+               len -= chunk;
+       }
 
        return 0;
 }
 
 GIT_INLINE(int) hash_cng_final(git_oid *out, git_hash_ctx *ctx)
 {
-       if (ctx->prov->prov.cng.finish_hash(ctx->ctx.cng.hash_handle, out->id, GIT_OID_RAWSZ, 0) < 0)
+       if (ctx->prov->prov.cng.finish_hash(ctx->ctx.cng.hash_handle, out->id, GIT_OID_RAWSZ, 0) < 0) {
+               giterr_set(GITERR_OS, "hash could not be finished");
                return -1;
+       }
 
        ctx->ctx.cng.updated = 0;
 
index 2eee5ca796f777f54a686abe061bc6969d545cf0..9704204e2708821d32d6dfa90c6571a6f7e43f75 100644 (file)
@@ -5,10 +5,11 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#ifndef INCLUDE_hash_win32_h__
-#define INCLUDE_hash_win32_h__
+#ifndef INCLUDE_hash_hash_win32_h__
+#define INCLUDE_hash_hash_win32_h__
 
 #include "common.h"
+
 #include "hash.h"
 
 #include <wincrypt.h>
@@ -137,4 +138,4 @@ struct git_hash_ctx {
        } ctx;
 };
 
-#endif /* INCLUDE_hash_openssl_h__ */
+#endif
index bea5383493233747d0452f0ecfe7af04131e970a..30d0594636d77bf3383557c7172a5c0f25560e48 100644 (file)
@@ -4,6 +4,9 @@
  * 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 "fileops.h"
 #include "util.h"
index 4718ed664cc945b2bd487e8edd07a14e21a24d0d..7eccf9a4358eddf8c8e3c95e7c87006d954131d1 100644 (file)
@@ -5,6 +5,8 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
+#include "common.h"
+
 #include "git2/sys/filter.h"
 #include "filter.h"
 #include "buffer.h"
index dc702c36e98b0e4f8d944e40c3db1a0c24dcd5b1..f7e903a615752ea3e56ec4f2be70220c2a1763e1 100644 (file)
@@ -7,8 +7,9 @@
 #ifndef INCLUDE_idxmap_h__
 #define INCLUDE_idxmap_h__
 
-#include <ctype.h>
 #include "common.h"
+
+#include <ctype.h>
 #include "git2/index.h"
 
 #define kmalloc git__malloc
index c324d4dd453d060ebfe1d210a556971f3499096f..dddd7e81eb21f04aeee4bb6a80df4d2f921475f2 100644 (file)
@@ -1,6 +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.
+ */
+
+#include "ignore.h"
+
 #include "git2/ignore.h"
 #include "common.h"
-#include "ignore.h"
 #include "attrcache.h"
 #include "path.h"
 #include "config.h"
  */
 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) {
-
-               /* If lengths match we need to have an exact match */
-               if (rule->length == neg->length) {
-                       return strcmp(rule->pattern, neg->pattern) == 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 ((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;
+       }
 
-               if (p[-1] != '/')
-                       return false;
-               if (memchr(shorter->pattern, '/', shorter->length) != NULL)
-                       return false;
+       /* 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;
 
-               return memcmp(p, shorter->pattern, shorter->length) == 0;
-       }
+       if (p[-1] != '/')
+               return false;
+       if (memchr(shorter->pattern, '/', shorter->length) != NULL)
+               return false;
 
-       return false;
+       return cmp(p, shorter->pattern, shorter->length) == 0;
 }
 
 /**
@@ -89,7 +101,7 @@ static int does_negate_pattern(git_attr_fnmatch *rule, git_attr_fnmatch *neg)
  */
 static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match)
 {
-       int error = 0;
+       int error = 0, fnflags;
        size_t i;
        git_attr_fnmatch *rule;
        char *path;
@@ -97,6 +109,10 @@ static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match
 
        *out = 0;
 
+       fnflags = FNM_PATHNAME;
+       if (match->flags & GIT_ATTR_FNMATCH_ICASE)
+               fnflags |= FNM_IGNORECASE;
+
        /* path of the file relative to the workdir, so we match the rules in subdirs */
        if (match->containing_dir) {
                git_buf_puts(&buf, match->containing_dir);
@@ -117,12 +133,12 @@ static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match
                                continue;
                }
 
-       /*
-        * When dealing with a directory, we add '/<star>' so
-        * p_fnmatch() honours FNM_PATHNAME. Checking for LEADINGDIR
-        * alone isn't enough as that's also set for nagations, so we
-        * need to check that NEGATIVE is off.
-        */
+               /*
+                * When dealing with a directory, we add '/<star>' so
+                * p_fnmatch() honours FNM_PATHNAME. Checking for LEADINGDIR
+                * alone isn't enough as that's also set for nagations, so we
+                * need to check that NEGATIVE is off.
+                */
                git_buf_clear(&buf);
                if (rule->containing_dir) {
                        git_buf_puts(&buf, rule->containing_dir);
@@ -136,7 +152,7 @@ static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match
                if (error < 0)
                        goto out;
 
-               if ((error = p_fnmatch(git_buf_cstr(&buf), path, FNM_PATHNAME)) < 0) {
+               if ((error = p_fnmatch(git_buf_cstr(&buf), path, fnflags)) < 0) {
                        giterr_set(GITERR_INVALID, "error matching pattern");
                        goto out;
                }
@@ -197,10 +213,26 @@ static int parse_ignore_file(
                        if (ignore_case)
                                match->flags |= GIT_ATTR_FNMATCH_ICASE;
 
+                       while (match->length > 0) {
+                               if (match->pattern[match->length - 1] == ' ' ||
+                                   match->pattern[match->length - 1] == '\t') {
+                                       match->pattern[match->length - 1] = 0;
+                                       match->length --;
+                               } else {
+                                       break;
+                               }
+                       }
+
                        scan = git__next_line(scan);
 
-                       /* if a negative match doesn't actually do anything, throw it away */
-                       if (match->flags & GIT_ATTR_FNMATCH_NEGATIVE)
+                       /*
+                        * 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)
@@ -508,6 +540,7 @@ int git_ignore_path_is_ignored(
        git_ignores ignores;
        unsigned int i;
        git_attr_file *file;
+       git_dir_flag dir_flag = GIT_DIR_FLAG_UNKNOWN;
 
        assert(repo && ignored && pathname);
 
@@ -516,7 +549,10 @@ int git_ignore_path_is_ignored(
        memset(&path, 0, sizeof(path));
        memset(&ignores, 0, sizeof(ignores));
 
-       if ((error = git_attr_path__init(&path, pathname, workdir, GIT_DIR_FLAG_UNKNOWN)) < 0 ||
+       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;
 
index 876c8e0ea2e770fed343028eeb59b1bcd6ede8be..5895d3faac930e886fdc0671b27bd75b6241e5c9 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_ignore_h__
 #define INCLUDE_ignore_h__
 
+#include "common.h"
+
 #include "repository.h"
 #include "vector.h"
 #include "attr_file.h"
index c29e90fb09bb0a6fd2d611a4c569b66f35e9d7fd..a867547fbbadf5b836e1bab5fc81e75148ba7f69 100644 (file)
@@ -5,11 +5,11 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
+#include "index.h"
+
 #include <stddef.h>
 
-#include "common.h"
 #include "repository.h"
-#include "index.h"
 #include "tree.h"
 #include "tree-cache.h"
 #include "hash.h"
@@ -1396,12 +1396,16 @@ static int index_conflict_to_reuc(git_index *index, const char *path)
        return ret;
 }
 
-static bool valid_filemode(const int filemode)
+GIT_INLINE(bool) is_file_or_link(const int filemode)
 {
        return (filemode == GIT_FILEMODE_BLOB ||
                filemode == GIT_FILEMODE_BLOB_EXECUTABLE ||
-               filemode == GIT_FILEMODE_LINK ||
-               filemode == GIT_FILEMODE_COMMIT);
+               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_frombuffer(
@@ -1419,7 +1423,7 @@ int git_index_add_frombuffer(
                        "could not initialize index entry. "
                        "Index is not backed up by an existing repository.");
 
-       if (!valid_filemode(source_entry->mode)) {
+       if (!is_file_or_link(source_entry->mode)) {
                giterr_set(GITERR_INDEX, "invalid filemode");
                return -1;
        }
@@ -1605,7 +1609,7 @@ int git_index_add(git_index *index, const git_index_entry *source_entry)
        assert(index && source_entry && source_entry->path);
 
        if (!valid_filemode(source_entry->mode)) {
-               giterr_set(GITERR_INDEX, "invalid filemode");
+               giterr_set(GITERR_INDEX, "invalid entry mode");
                return -1;
        }
 
@@ -2295,8 +2299,9 @@ static size_t index_entry_size(size_t path_len, size_t varint_len, uint32_t flag
        }
 }
 
-static size_t read_entry(
+static int read_entry(
        git_index_entry **out,
+       size_t *out_size,
        git_index *index,
        const void *buffer,
        size_t buffer_size,
@@ -2310,7 +2315,7 @@ static size_t read_entry(
        char *tmp_path = NULL;
 
        if (INDEX_FOOTER_SIZE + minimal_entry_size > buffer_size)
-               return 0;
+               return -1;
 
        /* buffer is not guaranteed to be aligned */
        memcpy(&source, buffer, sizeof(struct entry_short));
@@ -2352,7 +2357,7 @@ static size_t read_entry(
 
                        path_end = memchr(path_ptr, '\0', buffer_size);
                        if (path_end == NULL)
-                               return 0;
+                               return -1;
 
                        path_length = path_end - path_ptr;
                }
@@ -2360,19 +2365,24 @@ static size_t read_entry(
                entry_size = index_entry_size(path_length, 0, entry.flags);
                entry.path = (char *)path_ptr;
        } else {
-               size_t varint_len;
-               size_t strip_len = git_decode_varint((const unsigned char *)path_ptr,
-                                                    &varint_len);
-               size_t last_len = strlen(last);
-               size_t prefix_len = last_len - strip_len;
-               size_t suffix_len = strlen(path_ptr + varint_len);
-               size_t path_len;
-
-               if (varint_len == 0)
+               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 - strip_len;
+               suffix_len = strlen(path_ptr + varint_len);
+
                GITERR_CHECK_ALLOC_ADD(&path_len, prefix_len, suffix_len);
                GITERR_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);
                GITERR_CHECK_ALLOC(tmp_path);
 
@@ -2382,16 +2392,20 @@ static size_t read_entry(
                entry.path = tmp_path;
        }
 
+       if (entry_size == 0)
+               return -1;
+
        if (INDEX_FOOTER_SIZE + entry_size > buffer_size)
-               return 0;
+               return -1;
 
        if (index_entry_dup(out, index, &entry) < 0) {
                git__free(tmp_path);
-               return 0;
+               return -1;
        }
 
        git__free(tmp_path);
-       return entry_size;
+       *out_size = entry_size;
+       return 0;
 }
 
 static int read_header(struct index_header *dest, const void *buffer)
@@ -2494,11 +2508,10 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
 
        /* Parse all the entries */
        for (i = 0; i < header.entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) {
-               git_index_entry *entry;
-               size_t entry_size = read_entry(&entry, index, buffer, buffer_size, last);
+               git_index_entry *entry = NULL;
+               size_t entry_size;
 
-               /* 0 bytes read means an object corruption */
-               if (entry_size == 0) {
+               if ((error = read_entry(&entry, &entry_size, index, buffer, buffer_size, last)) < 0) {
                        error = index_error_invalid("invalid entry");
                        goto done;
                }
index 9918f140d68bf9fc89a541e3b442cec461f28e5c..0f1c0956cb92cd4f249cf000876a8be4fc74ba26 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_index_h__
 #define INCLUDE_index_h__
 
+#include "common.h"
+
 #include "fileops.h"
 #include "filebuf.h"
 #include "vector.h"
index 15f6cc2c4f3ff5d953dae47f7783cb8cb49fad2d..c0976f2703392884bd099895f45afd07af93f6ad 100644 (file)
@@ -5,10 +5,11 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
+#include "indexer.h"
+
 #include "git2/indexer.h"
 #include "git2/object.h"
 
-#include "common.h"
 #include "pack.h"
 #include "mwindow.h"
 #include "posix.h"
@@ -186,13 +187,17 @@ static int store_delta(git_indexer *idx)
        return 0;
 }
 
-static void hash_header(git_hash_ctx *ctx, git_off_t len, git_otype type)
+static int hash_header(git_hash_ctx *ctx, git_off_t len, git_otype 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;
 
-       hdrlen = git_odb__format_object_header(buffer, sizeof(buffer), (size_t)len, type);
-       git_hash_update(ctx, buffer, hdrlen);
+       return git_hash_update(ctx, buffer, hdrlen);
 }
 
 static int hash_object_stream(git_indexer*idx, git_packfile_stream *stream)
@@ -620,7 +625,10 @@ int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_tran
                                idx->have_delta = 1;
                        } else {
                                idx->have_delta = 0;
-                               hash_header(&idx->hash_ctx, entry_size, type);
+
+                               error = hash_header(&idx->hash_ctx, entry_size, type);
+                               if (error < 0)
+                                       goto on_error;
                        }
 
                        idx->have_stream = 1;
@@ -843,6 +851,7 @@ static int fix_thin_pack(git_indexer *idx, git_transfer_progress *stats)
 static int resolve_deltas(git_indexer *idx, git_transfer_progress *stats)
 {
        unsigned int i;
+       int error;
        struct delta_info *delta;
        int progressed = 0, non_null = 0, progress_cb_result;
 
@@ -857,8 +866,13 @@ static int resolve_deltas(git_indexer *idx, git_transfer_progress *stats)
 
                        non_null = 1;
                        idx->off = delta->delta_off;
-                       if (git_packfile_unpack(&obj, idx->pack, &idx->off) < 0)
-                               continue;
+                       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 (hash_and_save(idx, &obj, delta->delta_off) < 0)
                                continue;
@@ -950,6 +964,10 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats)
                giterr_set(GITERR_INDEXER, "unexpected data at the end of the pack");
                return -1;
        }
+       if (idx->off + 20 > idx->pack->mwf.size) {
+               giterr_set(GITERR_INDEXER, "missing trailer at the end of the pack");
+               return -1;
+       }
 
        packfile_trailer = git_mwindow_open(&idx->pack->mwf, &w, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ, &left);
        if (packfile_trailer == NULL) {
@@ -1118,6 +1136,9 @@ void git_indexer_free(git_indexer *idx)
        if (idx == NULL)
                return;
 
+       if (idx->have_stream)
+               git_packfile_stream_free(&idx->stream);
+
        git_vector_free_deep(&idx->objects);
 
        if (idx->pack->idx_cache) {
index 702694bbf29689432e79a0eebd56aabdc6461a10..8ee6115a656b9bb64893be95592625b42ca0a83e 100644 (file)
@@ -7,6 +7,10 @@
 #ifndef INCLUDE_indexer_h__
 #define INCLUDE_indexer_h__
 
-extern int git_indexer__set_fsync(git_indexer *idx, int do_fsync);
+#include "common.h"
+
+#include "git2/indexer.h"
+
+extern void git_indexer__set_fsync(git_indexer *idx, int do_fsync);
 
 #endif
index 61712cebf366ba71d816351886103725b2a0ee87..30528db5945a567bdf2fe113f9f5efeebec20e1d 100644 (file)
@@ -93,4 +93,4 @@ GIT_INLINE(bool) git__multiply_sizet_overflow(size_t *out, size_t one, size_t tw
 
 #endif
 
-#endif /* INCLUDE_integer_h__ */
+#endif
index 8fc62c01c2cc21090a20e0e9bc2a033434ecfbc4..ff075ce8c4a38e655a8e2502165eb5c51067818e 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include "iterator.h"
+
 #include "tree.h"
 #include "index.h"
 
@@ -22,6 +23,7 @@
 #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)
@@ -1456,10 +1458,15 @@ static void filesystem_iterator_set_current(
        filesystem_iterator_entry *entry)
 {
        iter->entry.ctime.seconds = entry->st.st_ctime;
-       iter->entry.ctime.nanoseconds = entry->st.st_ctime_nsec;
-
        iter->entry.mtime.seconds = 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;
@@ -1490,10 +1497,41 @@ static int filesystem_iterator_current(
        return 0;
 }
 
+static int filesystem_iterator_is_dir(
+       bool *is_dir,
+       const filesystem_iterator *iter,
+       const filesystem_iterator_entry *entry)
+{
+       struct stat st;
+       git_buf fullpath = GIT_BUF_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_buf_joinpath(&fullpath, iter->root, entry->path)) < 0 ||
+               (error = p_stat(fullpath.ptr, &st)) < 0)
+               goto done;
+
+       *is_dir = S_ISDIR(st.st_mode);
+
+done:
+       git_buf_free(&fullpath);
+       return error;
+}
+
 static int filesystem_iterator_advance(
        const git_index_entry **out, git_iterator *i)
 {
        filesystem_iterator *iter = (filesystem_iterator *)i;
+       bool is_dir;
        int error = 0;
 
        iter->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
@@ -1518,7 +1556,10 @@ static int filesystem_iterator_advance(
                entry = frame->entries.contents[frame->next_idx];
                frame->next_idx++;
 
-               if (S_ISDIR(entry->st.st_mode)) {
+               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);
 
index 0b239a5bd1e9bec3c42d8d6638ca32257718c69e..a6497d87b83aacc36d24da3aa8dde9508fbf1bb8 100644 (file)
@@ -8,6 +8,7 @@
 #define INCLUDE_iterator_h__
 
 #include "common.h"
+
 #include "git2/index.h"
 #include "vector.h"
 #include "buffer.h"
@@ -38,6 +39,8 @@ typedef enum {
        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),
 } git_iterator_flag_t;
 
 typedef enum {
index da3d1e19ad42f7c0b461732e36df7724e0faa3b2..2009c3ab5577bef2f5fb63560ec29f49f989e13f 100644 (file)
--- a/src/map.h
+++ b/src/map.h
@@ -43,4 +43,4 @@ typedef struct { /* memory mapped buffer      */
 extern int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset);
 extern int p_munmap(git_map *map);
 
-#endif /* INCLUDE_map_h__ */
+#endif
index 6e00b5adb5fbfb941c2633f996c1156bf3a83760..6c98d13fb405803c2b1108e009a24f888ccbcd37 100644 (file)
@@ -5,13 +5,13 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
+#include "merge.h"
+
 #include "posix.h"
 #include "buffer.h"
 #include "repository.h"
 #include "revwalk.h"
 #include "commit_list.h"
-#include "merge.h"
 #include "path.h"
 #include "refs.h"
 #include "object.h"
@@ -32,6 +32,8 @@
 #include "commit.h"
 #include "oidarray.h"
 #include "merge_driver.h"
+#include "oidmap.h"
+#include "array.h"
 
 #include "git2/types.h"
 #include "git2/repository.h"
@@ -1005,27 +1007,6 @@ struct merge_diff_similarity {
        size_t other_idx;
 };
 
-static int index_entry_similarity_exact(
-       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)
-{
-       GIT_UNUSED(repo);
-       GIT_UNUSED(a_idx);
-       GIT_UNUSED(b_idx);
-       GIT_UNUSED(cache);
-       GIT_UNUSED(opts);
-
-       if (git_oid__cmp(&a->id, &b->id) == 0)
-               return 100;
-
-       return 0;
-}
-
 static int index_entry_similarity_calc(
        void **out,
        git_repository *repo,
@@ -1102,12 +1083,154 @@ static int index_entry_similarity_inexact(
        return score;
 }
 
-static int merge_diff_mark_similarity(
+/* 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) {
+       khint_t pos;
+       deletes_by_oid_queue *queue;
+       size_t *array_entry;
+       int error;
+
+       pos = git_oidmap_lookup_index(map, id);
+       if (!git_oidmap_valid_index(map, pos)) {
+               queue = git_pool_malloc(pool, sizeof(deletes_by_oid_queue));
+               GITERR_CHECK_ALLOC(queue);
+
+               git_array_init(queue->arr);
+               queue->next_pos = 0;
+               queue->first_entry = idx;
+
+               git_oidmap_insert(map, id, queue, &error);
+               if (error < 0)
+                       return -1;
+       } else {
+               queue = git_oidmap_value_at(map, pos);
+               array_entry = git_array_alloc(queue->arr);
+               GITERR_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) {
+       khint_t pos;
+       deletes_by_oid_queue *queue;
+       size_t *array_entry;
+
+       pos = git_oidmap_lookup_index(map, id);
+
+       if (!git_oidmap_valid_index(map, pos))
+               return GIT_ENOTFOUND;
+
+       queue = git_oidmap_value_at(map, pos);
+       
+       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 (!(ours_deletes_by_oid = git_oidmap_alloc()) ||
+               !(theirs_deletes_by_oid = git_oidmap_alloc())) {
+               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,
-       int (*similarity_fn)(git_repository *, git_index_entry *, size_t, git_index_entry *, size_t, void **, const git_merge_options *),
        void **cache,
        const git_merge_options *opts)
 {
@@ -1132,7 +1255,7 @@ static int merge_diff_mark_similarity(
 
                        if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->our_entry) &&
                                !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->our_entry)) {
-                               similarity = similarity_fn(repo, &conflict_src->ancestor_entry, i, &conflict_tgt->our_entry, our_idx, cache, opts);
+                               similarity = index_entry_similarity_inexact(repo, &conflict_src->ancestor_entry, i, &conflict_tgt->our_entry, our_idx, cache, opts);
 
                                if (similarity == GIT_EBUFS)
                                        continue;
@@ -1158,7 +1281,7 @@ static int merge_diff_mark_similarity(
 
                        if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->their_entry) &&
                                !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->their_entry)) {
-                               similarity = similarity_fn(repo, &conflict_src->ancestor_entry, i, &conflict_tgt->their_entry, their_idx, cache, opts);
+                               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) {
@@ -1396,11 +1519,10 @@ int git_merge_diff_list__find_renames(
        /* Calculate similarity between items that were deleted from the ancestor
         * and added in the other branch.
         */
-       if ((error = merge_diff_mark_similarity(repo, diff_list, similarity_ours,
-               similarity_theirs, index_entry_similarity_exact, NULL, opts)) < 0)
+       if ((error = merge_diff_mark_similarity_exact(diff_list, similarity_ours, similarity_theirs)) < 0)
                goto done;
 
-       if (diff_list->conflicts.length <= opts->target_limit) {
+       if (opts->rename_threshold < 100 && diff_list->conflicts.length <= opts->target_limit) {
                GITERR_CHECK_ALLOC_MULTIPLY(&cache_size, diff_list->conflicts.length, 3);
                cache = git__calloc(cache_size, sizeof(void *));
                GITERR_CHECK_ALLOC(cache);
@@ -1410,9 +1532,8 @@ int git_merge_diff_list__find_renames(
                if (src_count > opts->target_limit || tgt_count > opts->target_limit) {
                        /* TODO: report! */
                } else {
-                       if ((error = merge_diff_mark_similarity(
-                               repo, diff_list, similarity_ours, similarity_theirs,
-                               index_entry_similarity_inexact, cache, opts)) < 0)
+                       if ((error = merge_diff_mark_similarity_inexact(
+                               repo, diff_list, similarity_ours, similarity_theirs, cache, opts)) < 0)
                                goto done;
                }
        }
@@ -1954,6 +2075,7 @@ int git_merge__iterators(
                file_opts.our_label = "Temporary merge branch 1";
                file_opts.their_label = "Temporary merge branch 2";
                file_opts.flags |= GIT_MERGE_FILE_FAVOR__CONFLICTED;
+               file_opts.marker_size = GIT_MERGE_CONFLICT_MARKER_SIZE + 2;
        }
 
        diff_list = git_merge_diff_list__alloc(repo);
@@ -2141,7 +2263,7 @@ static int compute_base(
        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;
+       size_t i, base_count;
        int error;
 
        *out = NULL;
@@ -2149,17 +2271,27 @@ static int compute_base(
        if (given_opts)
                memcpy(&opts, given_opts, sizeof(git_merge_options));
 
-       if ((error = insert_head_ids(&head_ids, one)) < 0 ||
-               (error = insert_head_ids(&head_ids, two)) < 0)
+       /* 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;
 
-       if ((error = git_merge_bases_many(&bases, repo,
-                       head_ids.size, head_ids.ptr)) < 0 ||
-               (error = git_annotated_commit_lookup(&base, repo, &bases.ids[0])) < 0 ||
-               (opts.flags & GIT_MERGE_NO_RECURSIVE))
+       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 < bases.count; i++) {
+       for (i = 1; i < base_count; i++) {
                recursion_level++;
 
                if (opts.recursion_limit && recursion_level > opts.recursion_limit)
index f8cac161f9c21066c52f3a7cab3826809b8d2c23..173a1b4350b6c1e2959728c0a056210a671d1063 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_merge_h__
 #define INCLUDE_merge_h__
 
+#include "common.h"
+
 #include "vector.h"
 #include "commit_list.h"
 #include "pool.h"
index 0f35d23c26a69036cb6a8f9778cc9dc3b7c8565c..ea4bd27879f425c897594396e62219c3955a47ad 100644 (file)
@@ -5,11 +5,11 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
+#include "merge_driver.h"
+
 #include "vector.h"
 #include "global.h"
 #include "merge.h"
-#include "merge_driver.h"
 #include "git2/merge.h"
 #include "git2/sys/merge.h"
 
index bde27502c7646336be829791b38c39a44a99471f..6b7da5287bd191884cbf52e3d3df84ccb92b70fc 100644 (file)
@@ -7,6 +7,8 @@
 #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"
index 5ecd8f4d0913ada3ef9d2329746e6a6509911d88..a36c1986c92201a21fb52cc0f614eabf74356665 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include "common.h"
+
 #include "repository.h"
 #include "posix.h"
 #include "fileops.h"
@@ -125,6 +126,8 @@ static int merge_file__xdiff(
        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) {
                giterr_set(GITERR_MERGE, "failed to merge files");
index 3c4b8dc4552047ee792a6ae9370f40c3a7e5256b..251727b22770eb003bfc2ee0f8766e878795e43a 100644 (file)
@@ -7,9 +7,11 @@
 #ifndef INCLUDE_message_h__
 #define INCLUDE_message_h__
 
+#include "common.h"
+
 #include "git2/message.h"
 #include "buffer.h"
 
 int git_message__prettify(git_buf *message_out, const char *message, int strip_comments);
 
-#endif /* INCLUDE_message_h__ */
+#endif
index 7bb9dbbe2a332143ac77856794621ce9c6ac08f1..38d0352b4274a0f7b1fe333a32705a179d6f7a2d 100644 (file)
@@ -5,8 +5,8 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
 #include "mwindow.h"
+
 #include "vector.h"
 #include "fileops.h"
 #include "map.h"
index bdde9e0a4b7ccc528d4e819233f53342199acf6b..ea962d1b6faa2237b922acff6faee8c82cd6d99f 100644 (file)
@@ -8,6 +8,8 @@
 #ifndef INCLUDE_mwindow__
 #define INCLUDE_mwindow__
 
+#include "common.h"
+
 #include "map.h"
 #include "vector.h"
 
index 4b73baa0e244566d04a5d096c8b26a0b72afb0c8..fa20cbaf2dee358cdc4f5c8f986150c0061c9dbd 100644 (file)
@@ -5,11 +5,11 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
+#include "netops.h"
+
 #include <ctype.h>
 #include "git2/errors.h"
 
-#include "common.h"
-#include "netops.h"
 #include "posix.h"
 #include "buffer.h"
 #include "http_parser.h"
@@ -206,83 +206,96 @@ void gitno_connection_data_free_ptrs(gitno_connection_data *d)
        git__free(d->pass); d->pass = NULL;
 }
 
-#define hex2c(c) ((c | 32) % 39 - 9)
-static char* unescape(char *str)
-{
-       int x, y;
-       int len = (int)strlen(str);
-
-       for (x=y=0; str[y]; ++x, ++y) {
-               if ((str[x] = str[y]) == '%') {
-                       if (y < len-2 && isxdigit(str[y+1]) && isxdigit(str[y+2])) {
-                               str[x] = (hex2c(str[y+1]) << 4) + hex2c(str[y+2]);
-                               y += 2;
-                       }
-               }
-       }
-       str[x] = '\0';
-       return str;
-}
-
 int gitno_extract_url_parts(
-               char **host,
-               char **port,
-               char **path,
-               char **username,
-               char **password,
-               const char *url,
-               const char *default_port)
+       char **host_out,
+       char **port_out,
+       char **path_out,
+       char **username_out,
+       char **password_out,
+       const char *url,
+       const char *default_port)
 {
        struct http_parser_url u = {0};
-       const char *_host, *_port, *_path, *_userinfo;
+       bool has_host, has_port, has_path, has_userinfo;
+       git_buf host = GIT_BUF_INIT,
+               port = GIT_BUF_INIT,
+               path = GIT_BUF_INIT,
+               username = GIT_BUF_INIT,
+               password = GIT_BUF_INIT;
+       int error = 0;
 
        if (http_parser_parse_url(url, strlen(url), false, &u)) {
                giterr_set(GITERR_NET, "malformed URL '%s'", url);
-               return GIT_EINVALIDSPEC;
+               error = GIT_EINVALIDSPEC;
+               goto done;
        }
 
-       _host = url+u.field_data[UF_HOST].off;
-       _port = url+u.field_data[UF_PORT].off;
-       _path = url+u.field_data[UF_PATH].off;
-       _userinfo = url+u.field_data[UF_USERINFO].off;
+       has_host = !!(u.field_set & (1 << UF_HOST));
+       has_port = !!(u.field_set & (1 << UF_PORT));
+       has_path = !!(u.field_set & (1 << UF_PATH));
+       has_userinfo = !!(u.field_set & (1 << UF_USERINFO));
 
-       if (u.field_set & (1 << UF_HOST)) {
-               *host = git__substrdup(_host, u.field_data[UF_HOST].len);
-               GITERR_CHECK_ALLOC(*host);
+       if (has_host) {
+               const char *url_host = url + u.field_data[UF_HOST].off;
+               size_t url_host_len = u.field_data[UF_HOST].len;
+               git_buf_decode_percent(&host, url_host, url_host_len);
        }
 
-       if (u.field_set & (1 << UF_PORT))
-               *port = git__substrdup(_port, u.field_data[UF_PORT].len);
-       else
-               *port = git__strdup(default_port);
-       GITERR_CHECK_ALLOC(*port);
+       if (has_port) {
+               const char *url_port = url + u.field_data[UF_PORT].off;
+               size_t url_port_len = u.field_data[UF_PORT].len;
+               git_buf_put(&port, url_port, url_port_len);
+       } else {
+               git_buf_puts(&port, default_port);
+       }
 
-       if (path) {
-               if (u.field_set & (1 << UF_PATH)) {
-                       *path = git__substrdup(_path, u.field_data[UF_PATH].len);
-                       GITERR_CHECK_ALLOC(*path);
-               } else {
-                       git__free(*port);
-                       *port = NULL;
-                       git__free(*host);
-                       *host = NULL;
-                       giterr_set(GITERR_NET, "invalid url, missing path");
-                       return GIT_EINVALIDSPEC;
-               }
+       if (has_path && path_out) {
+               const char *url_path = url + u.field_data[UF_PATH].off;
+               size_t url_path_len = u.field_data[UF_PATH].len;
+               git_buf_decode_percent(&path, url_path, url_path_len);
+       } else if (path_out) {
+               giterr_set(GITERR_NET, "invalid url, missing path");
+               error = GIT_EINVALIDSPEC;
+               goto done;
        }
 
-       if (u.field_set & (1 << UF_USERINFO)) {
-               const char *colon = memchr(_userinfo, ':', u.field_data[UF_USERINFO].len);
+       if (has_userinfo) {
+               const char *url_userinfo = url + u.field_data[UF_USERINFO].off;
+               size_t url_userinfo_len = u.field_data[UF_USERINFO].len;
+               const char *colon = memchr(url_userinfo, ':', url_userinfo_len);
+
                if (colon) {
-                       *username = unescape(git__substrdup(_userinfo, colon - _userinfo));
-                       *password = unescape(git__substrdup(colon+1, u.field_data[UF_USERINFO].len - (colon+1-_userinfo)));
-                       GITERR_CHECK_ALLOC(*password);
+                       const char *url_username = url_userinfo;
+                       size_t url_username_len = colon - url_userinfo;
+                       const char *url_password = colon + 1;
+                       size_t url_password_len = url_userinfo_len - (url_username_len + 1);
+
+                       git_buf_decode_percent(&username, url_username, url_username_len);
+                       git_buf_decode_percent(&password, url_password, url_password_len);
                } else {
-                       *username = git__substrdup(_userinfo, u.field_data[UF_USERINFO].len);
+                       git_buf_decode_percent(&username, url_userinfo, url_userinfo_len);
                }
-               GITERR_CHECK_ALLOC(*username);
-
        }
 
-       return 0;
+       if (git_buf_oom(&host) ||
+               git_buf_oom(&port) ||
+               git_buf_oom(&path) ||
+               git_buf_oom(&username) ||
+               git_buf_oom(&password))
+               return -1;
+
+       *host_out = git_buf_detach(&host);
+       *port_out = git_buf_detach(&port);
+       if (path_out)
+               *path_out = git_buf_detach(&path);
+       *username_out = git_buf_detach(&username);
+       *password_out = git_buf_detach(&password);
+
+done:
+       git_buf_free(&host);
+       git_buf_free(&port);
+       git_buf_free(&path);
+       git_buf_free(&username);
+       git_buf_free(&password);
+       return error;
 }
index b7170a0f2e44c58fc854006ed861917de6d53958..75fd9a512536062b202fc0345f32712d6a31e178 100644 (file)
@@ -7,8 +7,9 @@
 #ifndef INCLUDE_netops_h__
 #define INCLUDE_netops_h__
 
-#include "posix.h"
 #include "common.h"
+
+#include "posix.h"
 #include "stream.h"
 
 #ifdef GIT_OPENSSL
index 75108b9c9a290b02e37a520df430ec000c0f7e0e..f63ef3667f4d033ff87a4b05110e0bb63713c5c7 100644 (file)
@@ -268,7 +268,9 @@ static int insert_note_in_tree_enotfound_cb(git_tree **out,
                GIT_FILEMODE_BLOB);
 }
 
-static int note_write(git_oid *out,
+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,
@@ -294,13 +296,17 @@ static int note_write(git_oid *out,
                insert_note_in_tree_enotfound_cb)) < 0)
                goto cleanup;
 
-       if (out)
-               git_oid_cpy(out, &oid);
+       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;
@@ -363,7 +369,9 @@ cleanup:
        return error;
 }
 
-static int note_remove(git_repository *repo,
+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)
@@ -383,6 +391,12 @@ static int note_remove(git_repository *repo,
          *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;
@@ -410,8 +424,7 @@ static int normalize_namespace(char **out, git_repository *repo, const char *not
        return note_get_default_ref(out, repo);
 }
 
-static int retrieve_note_tree_and_commit(
-       git_tree **tree_out,
+static int retrieve_note_commit(
        git_commit **commit_out,
        char **notes_ref_out,
        git_repository *repo,
@@ -429,34 +442,82 @@ static int retrieve_note_tree_and_commit(
        if (git_commit_lookup(commit_out, repo, &oid) < 0)
                return error;
 
-       if ((error = git_commit_tree(tree_out, *commit_out)) < 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_HEXSZ + 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;
-       char *target = NULL, *notes_ref = NULL;
-       git_tree *tree = NULL;
+       char *notes_ref = NULL;
        git_commit *commit = NULL;
 
-       target = git_oid_allocfmt(oid);
-       GITERR_CHECK_ALLOC(target);
+       error = retrieve_note_commit(&commit, &notes_ref, repo, notes_ref_in);
 
-       if (!(error = retrieve_note_tree_and_commit(
-                     &tree, &commit, &notes_ref, repo, notes_ref_in)))
-               error = note_lookup(out, repo, commit, tree, target);
+       if (error < 0)
+               goto cleanup;
 
+       error = git_note_commit_read(out, repo, commit, oid);
+
+cleanup:
        git__free(notes_ref);
-       git__free(target);
-       git_tree_free(tree);
        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_HEXSZ + 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,
@@ -468,25 +529,59 @@ int git_note_create(
        int allow_note_overwrite)
 {
        int error;
-       char *target = NULL, *notes_ref = NULL;
-       git_commit *commit = NULL;
-       git_tree *tree = NULL;
-
-       target = git_oid_allocfmt(oid);
-       GITERR_CHECK_ALLOC(target);
+       char *notes_ref = NULL;
+       git_commit *existing_notes_commit = NULL;
+       git_reference *ref = NULL;
+       git_oid notes_blob_oid, notes_commit_oid;
 
-       error = retrieve_note_tree_and_commit(&tree, &commit, &notes_ref, repo, notes_ref_in);
+       error = retrieve_note_commit(&existing_notes_commit, &notes_ref,
+                       repo, notes_ref_in);
 
        if (error < 0 && error != GIT_ENOTFOUND)
                goto cleanup;
 
-       error = note_write(out, repo, author, committer, notes_ref,
-                       note, tree, target, &commit, allow_note_overwrite);
+       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,
+                               &notes_commit_oid, 1, NULL);
+
+       if (out != NULL)
+               git_oid_cpy(out, &notes_blob_oid);
 
 cleanup:
        git__free(notes_ref);
-       git__free(target);
-       git_commit_free(commit);
+       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_HEXSZ + 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;
 }
@@ -496,22 +591,29 @@ int git_note_remove(git_repository *repo, const char *notes_ref_in,
                const git_oid *oid)
 {
        int error;
-       char *target = NULL, *notes_ref;
-       git_commit *commit = NULL;
-       git_tree *tree = NULL;
+       char *notes_ref_target = NULL;
+       git_commit *existing_notes_commit = NULL;
+       git_oid new_notes_commit;
+       git_reference *notes_ref = NULL;
 
-       target = git_oid_allocfmt(oid);
-       GITERR_CHECK_ALLOC(target);
+       error = retrieve_note_commit(&existing_notes_commit, &notes_ref_target,
+                       repo, notes_ref_in);
 
-       if (!(error = retrieve_note_tree_and_commit(
-                     &tree, &commit, &notes_ref, repo, notes_ref_in)))
-               error = note_remove(
-                       repo, author, committer, notes_ref, tree, target, &commit);
+       if (error < 0)
+               goto cleanup;
 
-       git__free(notes_ref);
-       git__free(target);
-       git_commit_free(commit);
-       git_tree_free(tree);
+       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,
+                       &new_notes_commit, 1, NULL);
+
+cleanup:
+       git__free(notes_ref_target);
+       git_reference_free(notes_ref);
+       git_commit_free(existing_notes_commit);
        return error;
 }
 
@@ -639,7 +741,6 @@ int git_note_foreach(
        return error;
 }
 
-
 void git_note_iterator_free(git_note_iterator *it)
 {
        if (it == NULL)
@@ -648,6 +749,24 @@ void git_note_iterator_free(git_note_iterator *it)
        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,
@@ -656,19 +775,16 @@ int git_note_iterator_new(
 {
        int error;
        git_commit *commit = NULL;
-       git_tree *tree = NULL;
        char *notes_ref;
 
-       error = retrieve_note_tree_and_commit(&tree, &commit, &notes_ref, repo, notes_ref_in);
+       error = retrieve_note_commit(&commit, &notes_ref, repo, notes_ref_in);
        if (error < 0)
                goto cleanup;
 
-       if ((error = git_iterator_for_tree(it, tree, NULL)) < 0)
-               git_iterator_free(*it);
+       error = git_note_commit_iterator_new(it, commit);
 
 cleanup:
        git__free(notes_ref);
-       git_tree_free(tree);
        git_commit_free(commit);
 
        return error;
index cfc0ca239fa56a9a072466d2752f69c6ed47b43b..2168e459510283bfa8422c39904a09a070ec8996 100644 (file)
@@ -29,4 +29,4 @@ struct git_note {
        char *message;
 };
 
-#endif /* INCLUDE_notes_h__ */
+#endif
index 2da36a2eef87c6169a231ad5e1f69d7d9d956027..48f561384543cb696791e25e2b68987b88f0bd9e 100644 (file)
@@ -4,9 +4,11 @@
  * 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 "common.h"
 #include "repository.h"
 
 #include "commit.h"
@@ -233,14 +235,23 @@ const char *git_object_type2string(git_otype type)
 }
 
 git_otype git_object_string2type(const char *str)
+{
+       if (!str)
+               return GIT_OBJ_BAD;
+
+       return git_object_stringn2type(str, strlen(str));
+}
+
+git_otype git_object_stringn2type(const char *str, size_t len)
 {
        size_t i;
 
-       if (!str || !*str)
+       if (!str || !len || !*str)
                return GIT_OBJ_BAD;
 
        for (i = 0; i < ARRAY_SIZE(git_objects_table); i++)
-               if (!strcmp(str, git_objects_table[i].str))
+               if (*git_objects_table[i].str &&
+                       !git__prefixncmp(str, len, git_objects_table[i].str))
                        return (git_otype)i;
 
        return GIT_OBJ_BAD;
index dd227d16d4558fb4e841cd802826ab2391e34c92..e46c9cafa5f684e5d4ac3156a889c88b325da8bc 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_object_h__
 #define INCLUDE_object_h__
 
+#include "common.h"
+
 #include "repository.h"
 
 extern bool git_object__strict_input_validation;
@@ -28,6 +30,8 @@ int git_object__from_odb_object(
 
 int git_object__resolve_to_type(git_object **obj, git_otype type);
 
+git_otype git_object_stringn2type(const char *str, size_t len);
+
 int git_oid__parse(git_oid *oid, const char **buffer_out, const char *buffer_end, const char *header);
 
 void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid);
index e0d8760e7b6d6c2146eb496e9df9086a003c80b0..75efa4d105156924b25762f2502f1ecb2e9ead72 100644 (file)
@@ -4,11 +4,12 @@
  * 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/object.h"
 
 #include "common.h"
-#include "repository.h"
 
+#include "git2/object.h"
+
+#include "repository.h"
 #include "commit.h"
 #include "tree.h"
 #include "blob.h"
index ae8f247e3f2b105bbb1e99956f39395322b985a4..ef9c87555cf0ad7943dab68ea3342d9145427b96 100644 (file)
--- a/src/odb.c
+++ b/src/odb.c
@@ -5,13 +5,13 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
+#include "odb.h"
+
 #include <zlib.h>
 #include "git2/object.h"
 #include "git2/sys/odb_backend.h"
 #include "fileops.h"
 #include "hash.h"
-#include "odb.h"
 #include "delta.h"
 #include "filter.h"
 #include "repository.h"
@@ -53,6 +53,7 @@ static git_cache *odb_cache(git_odb *odb)
 
 static int odb_otype_fast(git_otype *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_otype odb_hardcoded_type(const git_oid *id)
 {
@@ -65,50 +66,75 @@ static git_otype odb_hardcoded_type(const git_oid *id)
        return GIT_OBJ_BAD;
 }
 
-static int odb_read_hardcoded(git_rawobj *raw, const git_oid *id)
+static int odb_read_hardcoded(bool *found, git_rawobj *raw, const git_oid *id)
 {
-       git_otype type = odb_hardcoded_type(id);
-       if (type == GIT_OBJ_BAD)
-               return -1;
+       git_otype type;
+
+       *found = false;
+
+       if ((type = odb_hardcoded_type(id)) == GIT_OBJ_BAD)
+               return 0;
 
        raw->type = type;
        raw->len = 0;
        raw->data = git__calloc(1, sizeof(uint8_t));
+       GITERR_CHECK_ALLOC(raw->data);
+
+       *found = true;
        return 0;
 }
 
-int git_odb__format_object_header(char *hdr, size_t n, git_off_t obj_len, git_otype obj_type)
+int git_odb__format_object_header(
+       size_t *written,
+       char *hdr,
+       size_t hdr_size,
+       git_off_t obj_len,
+       git_otype obj_type)
 {
        const char *type_str = git_object_type2string(obj_type);
-       int len = p_snprintf(hdr, n, "%s %lld", type_str, (long long)obj_len);
-       assert(len > 0 && len <= (int)n);
-       return len+1;
+       int hdr_max = (hdr_size > INT_MAX-2) ? (INT_MAX-2) : (int)hdr_size;
+       int len;
+
+       len = p_snprintf(hdr, hdr_max, "%s %lld", type_str, (long long)obj_len);
+
+       if (len < 0 || len >= hdr_max) {
+               giterr_set(GITERR_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_buf_vec vec[2];
        char header[64];
-       int hdrlen;
+       size_t hdrlen;
+       int error;
 
        assert(id && obj);
 
-       if (!git_object_typeisloose(obj->type))
+       if (!git_object_typeisloose(obj->type)) {
+               giterr_set(GITERR_INVALID, "invalid object type");
                return -1;
+       }
 
-       if (!obj->data && obj->len != 0)
+       if (!obj->data && obj->len != 0) {
+               giterr_set(GITERR_INVALID, "invalid object");
                return -1;
+       }
 
-       hdrlen = git_odb__format_object_header(header, sizeof(header), obj->len, obj->type);
+       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;
 
-       git_hash_vec(id, vec, 2);
-
-       return 0;
+       return git_hash_vec(id, vec, 2);
 }
 
 
@@ -171,7 +197,7 @@ void git_odb_object_free(git_odb_object *object)
 
 int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type)
 {
-       int hdr_len;
+       size_t hdr_len;
        char hdr[64], buffer[FILEIO_BUFSIZE];
        git_hash_ctx ctx;
        ssize_t read_len = 0;
@@ -183,9 +209,11 @@ int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type)
        }
 
        if ((error = git_hash_ctx_init(&ctx)) < 0)
-               return -1;
+               return error;
 
-       hdr_len = git_odb__format_object_header(hdr, sizeof(hdr), size, type);
+       if ((error = git_odb__format_object_header(&hdr_len, hdr,
+               sizeof(hdr), size, type)) < 0)
+               goto done;
 
        if ((error = git_hash_update(&ctx, hdr, hdr_len)) < 0)
                goto done;
@@ -341,8 +369,7 @@ static int fake_wstream__write(git_odb_stream *_stream, const char *data, size_t
 {
        fake_wstream *stream = (fake_wstream *)_stream;
 
-       if (stream->written + len > stream->size)
-               return -1;
+       assert(stream->written + len <= stream->size);
 
        memcpy(stream->buffer + stream->written, data, len);
        stream->written += len;
@@ -735,6 +762,9 @@ int git_odb_exists(git_odb *db, const git_oid *id)
 
        assert(db && id);
 
+       if (git_oid_iszero(id))
+               return 0;
+
        if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) {
                git_odb_object_free(object);
                return 1;
@@ -796,7 +826,7 @@ int git_odb_exists_prefix(
        git_oid *out, git_odb *db, const git_oid *short_id, size_t len)
 {
        int error;
-       git_oid key = {{0}}; 
+       git_oid key = {{0}};
 
        assert(db && short_id);
 
@@ -958,6 +988,11 @@ int git_odb__read_header_or_object(
 
        assert(db && id && out && len_p && type_p);
 
+       *out = NULL;
+
+       if (git_oid_iszero(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;
@@ -965,7 +1000,6 @@ int git_odb__read_header_or_object(
                return 0;
        }
 
-       *out = NULL;
        error = odb_read_header_1(len_p, type_p, db, id, false);
 
        if (error == GIT_ENOTFOUND && !git_odb_refresh(db))
@@ -1004,8 +1038,10 @@ static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id,
        bool found = false;
        int error = 0;
 
-       if (!only_refreshed && odb_read_hardcoded(&raw, id) == 0)
-               found = true;
+       if (!only_refreshed) {
+               if ((error = odb_read_hardcoded(&found, &raw, id)) < 0)
+                       return error;
+       }
 
        for (i = 0; i < db->backends.length && !found; ++i) {
                backend_internal *internal = git_vector_get(&db->backends, i);
@@ -1040,8 +1076,10 @@ static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id,
        }
 
        giterr_clear();
-       if ((object = odb_object__alloc(id, &raw)) == NULL)
+       if ((object = odb_object__alloc(id, &raw)) == NULL) {
+               error = -1;
                goto out;
+       }
 
        *out = git_cache_store_raw(odb_cache(db), object);
 
@@ -1057,6 +1095,9 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
 
        assert(out && db && id);
 
+       if (git_oid_iszero(id))
+               return error_null_oid(GIT_ENOTFOUND, "cannot read object");
+
        *out = git_cache_get_raw(odb_cache(db), id);
        if (*out != NULL)
                return 0;
@@ -1078,11 +1119,14 @@ static int odb_otype_fast(git_otype *type_p, git_odb *db, const git_oid *id)
        size_t _unused;
        int error;
 
+       if (git_oid_iszero(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;
                return 0;
        }
-       
+
        error = odb_read_header_1(&_unused, type_p, db, id, false);
 
        if (error == GIT_PASSTHROUGH) {
@@ -1161,8 +1205,10 @@ static int read_prefix_1(git_odb_object **out, git_odb *db,
                }
        }
 
-       if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL)
+       if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL) {
+               error = -1;
                goto out;
+       }
 
        *out = git_cache_store_raw(odb_cache(db), object);
 
@@ -1231,6 +1277,10 @@ int git_odb_write(
        assert(oid && db);
 
        git_odb_hash(oid, data, len, type);
+
+       if (git_oid_iszero(oid))
+               return error_null_oid(GIT_EINVALID, "cannot write object");
+
        if (git_odb__freshen(db, oid))
                return 0;
 
@@ -1263,13 +1313,17 @@ int git_odb_write(
        return error;
 }
 
-static void hash_header(git_hash_ctx *ctx, git_off_t size, git_otype type)
+static int hash_header(git_hash_ctx *ctx, git_off_t size, git_otype type)
 {
        char header[64];
-       int hdrlen;
+       size_t hdrlen;
+       int error;
 
-       hdrlen = git_odb__format_object_header(header, sizeof(header), size, type);
-       git_hash_update(ctx, header, hdrlen);
+        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(
@@ -1310,16 +1364,17 @@ int git_odb_open_wstream(
        ctx = git__malloc(sizeof(git_hash_ctx));
        GITERR_CHECK_ALLOC(ctx);
 
-       if ((error = git_hash_ctx_init(ctx)) < 0)
+       if ((error = git_hash_ctx_init(ctx)) < 0 ||
+               (error = hash_header(ctx, size, type)) < 0)
                goto done;
 
-       hash_header(ctx, size, type);
        (*stream)->hash_ctx = ctx;
-
        (*stream)->declared_size = size;
        (*stream)->received_bytes = 0;
 
 done:
+       if (error)
+               git__free(ctx);
        return error;
 }
 
@@ -1378,7 +1433,12 @@ void git_odb_stream_free(git_odb_stream *stream)
        stream->free(stream);
 }
 
-int git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oid)
+int git_odb_open_rstream(
+       git_odb_stream **stream,
+       size_t *len,
+       git_otype *type,
+       git_odb *db,
+       const git_oid *oid)
 {
        size_t i, reads = 0;
        int error = GIT_ERROR;
@@ -1391,7 +1451,7 @@ int git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oi
 
                if (b->readstream != NULL) {
                        ++reads;
-                       error = b->readstream(stream, b, oid);
+                       error = b->readstream(stream, len, type, b, oid);
                }
        }
 
@@ -1484,6 +1544,12 @@ int git_odb__error_notfound(
        return GIT_ENOTFOUND;
 }
 
+static int error_null_oid(int error, const char *message)
+{
+       giterr_set(GITERR_ODB, "odb: %s: null OID cannot exist", message);
+       return error;
+}
+
 int git_odb__error_ambiguous(const char *message)
 {
        giterr_set(GITERR_ODB, "ambiguous SHA1 prefix - %s", message);
index 61d687abf773d14bcf0a3325b4828de99545cda9..b354108e72e6292fe9376c85b7a07a8fb0bea19c 100644 (file)
--- a/src/odb.h
+++ b/src/odb.h
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_odb_h__
 #define INCLUDE_odb_h__
 
+#include "common.h"
+
 #include "git2/odb.h"
 #include "git2/oid.h"
 #include "git2/types.h"
@@ -68,7 +70,7 @@ int git_odb__hashobj(git_oid *id, git_rawobj *obj);
 /*
  * Format the object header such as it would appear in the on-disk object
  */
-int git_odb__format_object_header(char *hdr, size_t n, git_off_t obj_len, git_otype obj_type);
+int git_odb__format_object_header(size_t *out_len, char *hdr, size_t hdr_size, git_off_t obj_len, git_otype obj_type);
 /*
  * Hash an open file descriptor.
  * This is a performance call when the contents of a fd need to be hashed,
index 99fdcb44f7d3a9045bca26a594af203744afaf7f..470421e156d4d91f77670a07e72f189171c690b0 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include "common.h"
+
 #include <zlib.h>
 #include "git2/object.h"
 #include "git2/sys/odb_backend.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_otype type; /* object type */
        size_t  size; /* object size */
@@ -29,6 +34,15 @@ typedef struct {
        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;
 
@@ -90,222 +104,117 @@ static int object_mkdir(const git_buf *name, const loose_backend *be)
                GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR, NULL);
 }
 
-static size_t get_binary_object_header(obj_hdr *hdr, git_buf *obj)
+static int parse_header_packlike(
+       obj_hdr *out, size_t *out_len, const unsigned char *data, size_t len)
 {
        unsigned long c;
-       unsigned char *data = (unsigned char *)obj->ptr;
        size_t shift, size, used = 0;
 
-       if (git_buf_len(obj) == 0)
-               return 0;
+       if (len == 0)
+               goto on_error;
 
        c = data[used++];
-       hdr->type = (c >> 4) & 7;
+       out->type = (c >> 4) & 7;
 
        size = c & 15;
        shift = 4;
        while (c & 0x80) {
-               if (git_buf_len(obj) <= used)
-                       return 0;
+               if (len <= used)
+                       goto on_error;
+
                if (sizeof(size_t) * 8 <= shift)
-                       return 0;
+                       goto on_error;
+
                c = data[used++];
                size += (c & 0x7f) << shift;
                shift += 7;
        }
-       hdr->size = size;
-
-       return used;
-}
-
-static size_t get_object_header(obj_hdr *hdr, unsigned char *data)
-{
-       char c, typename[10];
-       size_t size, used = 0;
-
-       /*
-        * type name string followed by space.
-        */
-       while ((c = data[used]) != ' ') {
-               typename[used++] = c;
-               if (used >= sizeof(typename))
-                       return 0;
-       }
-       typename[used] = 0;
-       if (used == 0)
-               return 0;
-       hdr->type = git_object_string2type(typename);
-       used++; /* consume the space */
-
-       /*
-        * length follows immediately in decimal (without
-        * leading zeros).
-        */
-       size = data[used++] - '0';
-       if (size > 9)
-               return 0;
-       if (size) {
-               while ((c = data[used]) != '\0') {
-                       size_t d = c - '0';
-                       if (d > 9)
-                               break;
-                       used++;
-                       size = size * 10 + d;
-               }
-       }
-       hdr->size = size;
-
-       /*
-        * the length must be followed by a zero byte
-        */
-       if (data[used++] != '\0')
-               return 0;
-
-       return used;
-}
-
 
+       out->size = size;
 
-/***********************************************************
- *
- * ZLIB RELATED FUNCTIONS
- *
- ***********************************************************/
-
-static void init_stream(z_stream *s, void *out, size_t len)
-{
-       memset(s, 0, sizeof(*s));
-       s->next_out = out;
-       s->avail_out = (uInt)len;
-}
+       if (out_len)
+               *out_len = used;
 
-static void set_stream_input(z_stream *s, void *in, size_t len)
-{
-       s->next_in = in;
-       s->avail_in = (uInt)len;
-}
+       return 0;
 
-static void set_stream_output(z_stream *s, void *out, size_t len)
-{
-       s->next_out = out;
-       s->avail_out = (uInt)len;
+on_error:
+       giterr_set(GITERR_OBJECT, "failed to parse loose object: invalid header");
+       return -1;
 }
 
-
-static int start_inflate(z_stream *s, git_buf *obj, void *out, size_t len)
+static int parse_header(
+       obj_hdr *out,
+               size_t *out_len,
+       const unsigned char *_data,
+       size_t data_len)
 {
-       int status;
+       const char *data = (char *)_data;
+       size_t i, typename_len, size_idx, size_len;
+       int64_t size;
 
-       init_stream(s, out, len);
-       set_stream_input(s, obj->ptr, git_buf_len(obj));
+       *out_len = 0;
 
-       if ((status = inflateInit(s)) < Z_OK)
-               return status;
+       /* find the object type name */
+       for (i = 0, typename_len = 0; i < data_len; i++, typename_len++) {
+               if (data[i] == ' ')
+                       break;
+       }
 
-       return inflate(s, 0);
-}
+       if (typename_len == data_len)
+               goto on_error;
 
-static void abort_inflate(z_stream *s)
-{
-       inflateEnd(s);
-}
+       out->type = git_object_stringn2type(data, typename_len);
 
-static int finish_inflate(z_stream *s)
-{
-       int status = Z_OK;
+       size_idx = typename_len + 1;
+       for (i = size_idx, size_len = 0; i < data_len; i++, size_len++) {
+               if (data[i] == '\0')
+                       break;
+       }
 
-       while (status == Z_OK)
-               status = inflate(s, Z_FINISH);
+       if (i == data_len)
+               goto on_error;
 
-       inflateEnd(s);
+       if (git__strntol64(&size, &data[size_idx], size_len, NULL, 10) < 0 ||
+               size < 0)
+               goto on_error;
 
-       if ((status != Z_STREAM_END) || (s->avail_in != 0)) {
-               giterr_set(GITERR_ZLIB, "failed to finish zlib inflation; stream aborted prematurely");
+       if ((uint64_t)size > SIZE_MAX) {
+               giterr_set(GITERR_OBJECT, "object is larger than available memory");
                return -1;
        }
 
+       out->size = size;
+
+       if (GIT_ADD_SIZET_OVERFLOW(out_len, i, 1))
+               goto on_error;
+
        return 0;
+
+on_error:
+       giterr_set(GITERR_OBJECT, "failed to parse loose object: invalid header");
+       return -1;
 }
 
-static int is_zlib_compressed_data(unsigned char *data)
+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);
 }
 
-static int inflate_buffer(void *in, size_t inlen, void *out, size_t outlen)
-{
-       z_stream zs;
-       int status = Z_OK;
-
-       memset(&zs, 0x0, sizeof(zs));
-
-       zs.next_out = out;
-       zs.avail_out = (uInt)outlen;
-
-       zs.next_in = in;
-       zs.avail_in = (uInt)inlen;
-
-       if (inflateInit(&zs) < Z_OK) {
-               giterr_set(GITERR_ZLIB, "failed to inflate buffer");
-               return -1;
-       }
-
-       while (status == Z_OK)
-               status = inflate(&zs, Z_FINISH);
-
-       inflateEnd(&zs);
-
-       if (status != Z_STREAM_END /* || zs.avail_in != 0 */ ||
-               zs.total_out != outlen)
-       {
-               giterr_set(GITERR_ZLIB, "failed to inflate buffer; stream aborted prematurely");
-               return -1;
-       }
-
-       return 0;
-}
-
-static void *inflate_tail(z_stream *s, void *hb, size_t used, obj_hdr *hdr)
-{
-       unsigned char *buf, *head = hb;
-       size_t tail, alloc_size;
-
-       /*
-        * allocate a buffer to hold the inflated data and copy the
-        * initial sequence of inflated data from the tail of the
-        * head buffer, if any.
-        */
-       if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, hdr->size, 1) ||
-               (buf = git__malloc(alloc_size)) == NULL) {
-               inflateEnd(s);
-               return NULL;
-       }
-       tail = s->total_out - used;
-       if (used > 0 && tail > 0) {
-               if (tail > hdr->size)
-                       tail = hdr->size;
-               memcpy(buf, head + used, tail);
-       }
-       used = tail;
-
-       /*
-        * inflate the remainder of the object data, if any
-        */
-       if (hdr->size < used)
-               inflateEnd(s);
-       else {
-               set_stream_output(s, buf + used, hdr->size - used);
-               if (finish_inflate(s)) {
-                       git__free(buf);
-                       return NULL;
-               }
-       }
+/***********************************************************
+ *
+ * ODB OBJECT READING & WRITING
+ *
+ * Backend for the public API; read headers and full objects
+ * from the ODB. Write raw data to the ODB.
+ *
+ ***********************************************************/
 
-       return buf;
-}
 
 /*
  * At one point, there was a loose object format that was intended to
@@ -313,98 +222,122 @@ static void *inflate_tail(z_stream *s, void *hb, size_t used, obj_hdr *hdr)
  * of loose object data into packs. This format is no longer used, but
  * we must still read it.
  */
-static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_buf *obj)
+static int read_loose_packlike(git_rawobj *out, git_buf *obj)
 {
-       unsigned char *in, *buf;
+       git_buf body = GIT_BUF_INIT;
+       const unsigned char *obj_data;
        obj_hdr hdr;
-       size_t len, used, alloclen;
+       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 ((used = get_binary_object_header(&hdr, obj)) == 0 ||
-               !git_object_typeisloose(hdr.type)) {
+       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) {
                giterr_set(GITERR_ODB, "failed to inflate loose object");
-               return -1;
+               error = -1;
+               goto done;
        }
 
+       obj_data += head_len;
+       obj_len -= head_len;
+
        /*
         * allocate a buffer and inflate the data into it
         */
-       GITERR_CHECK_ALLOC_ADD(&alloclen, hdr.size, 1);
-       buf = git__malloc(alloclen);
-       GITERR_CHECK_ALLOC(buf);
-
-       in = ((unsigned char *)obj->ptr) + used;
-       len = obj->size - used;
-       if (inflate_buffer(in, len, buf, hdr.size) < 0) {
-               git__free(buf);
-               return -1;
+       if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, hdr.size, 1) ||
+               git_buf_init(&body, alloc_size) < 0) {
+               error = -1;
+               goto done;
        }
-       buf[hdr.size] = '\0';
 
-       out->data = buf;
+       if ((error = git_zstream_inflatebuf(&body, obj_data, obj_len)) < 0)
+               goto done;
+
        out->len = hdr.size;
        out->type = hdr.type;
+       out->data = git_buf_detach(&body);
 
-       return 0;
+done:
+       git_buf_free(&body);
+       return error;
 }
 
-static int inflate_disk_obj(git_rawobj *out, git_buf *obj)
+static int read_loose_standard(git_rawobj *out, git_buf *obj)
 {
-       unsigned char head[64], *buf;
-       z_stream zs;
+       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;
-       size_t used;
+       int error;
 
-       /*
-        * check for a pack-like loose object
-        */
-       if (!is_zlib_compressed_data((unsigned char *)obj->ptr))
-               return inflate_packlike_loose_disk_obj(out, obj);
+       if ((error = git_zstream_init(&zstream, GIT_ZSTREAM_INFLATE)) < 0 ||
+               (error = git_zstream_set_input(&zstream, git_buf_cstr(obj), git_buf_len(obj))) < 0)
+               goto done;
+
+       decompressed = sizeof(head);
 
        /*
-        * inflate the initial part of the io buffer in order
-        * to parse the object header (type and size).
+        * 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 (start_inflate(&zs, obj, head, sizeof(head)) < Z_OK ||
-               (used = get_object_header(&hdr, head)) == 0 ||
-               !git_object_typeisloose(hdr.type))
-       {
-               abort_inflate(&zs);
+       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)) {
                giterr_set(GITERR_ODB, "failed to inflate disk object");
-               return -1;
+               error = -1;
+               goto done;
        }
 
        /*
         * allocate a buffer and inflate the object data into it
         * (including the initial sequence in the head buffer).
         */
-       if ((buf = inflate_tail(&zs, head, used, &hdr)) == NULL)
-               return -1;
-       buf[hdr.size] = '\0';
+       if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, hdr.size, 1) ||
+               (body = git__malloc(alloc_size)) == NULL) {
+               error = -1;
+               goto done;
+       }
 
-       out->data = buf;
-       out->len = hdr.size;
-       out->type = hdr.type;
+       assert(decompressed >= head_len);
+       body_len = decompressed - head_len;
 
-       return 0;
-}
+       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)) {
+               giterr_set(GITERR_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);
 
-/***********************************************************
- *
- * ODB OBJECT READING & WRITING
- *
- * Backend for the public API; read headers and full objects
- * from the ODB. Write raw data to the ODB.
- *
- ***********************************************************/
+       git_zstream_free(&zstream);
+       return error;
+}
 
 static int read_loose(git_rawobj *out, git_buf *loc)
 {
@@ -420,21 +353,62 @@ static int read_loose(git_rawobj *out, git_buf *loc)
        out->len = 0;
        out->type = GIT_OBJ_BAD;
 
-       if (!(error = git_futils_readbuffer(&obj, loc->ptr)))
-               error = inflate_disk_obj(out, &obj);
+       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_buf_free(&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;
+       unsigned char inflated[MAX_HEADER_LEN];
+       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_buf *loc)
 {
-       int error = 0, z_return = Z_ERRNO, read_bytes;
-       git_file fd;
-       z_stream zs;
-       obj_hdr header_obj;
-       unsigned char raw_buffer[16], inflated_buffer[64];
+       unsigned char obj[1024];
+       int fd, obj_len, error;
 
        assert(out && loc);
 
@@ -443,35 +417,24 @@ static int read_header_loose(git_rawobj *out, git_buf *loc)
 
        out->data = NULL;
 
-       if ((fd = git_futils_open_ro(loc->ptr)) < 0)
-               return fd;
+       if ((error = fd = git_futils_open_ro(loc->ptr)) < 0 ||
+               (error = obj_len = p_read(fd, obj, sizeof(obj))) < 0)
+               goto done;
 
-       init_stream(&zs, inflated_buffer, sizeof(inflated_buffer));
-
-       z_return = inflateInit(&zs);
-
-       while (z_return == Z_OK) {
-               if ((read_bytes = p_read(fd, raw_buffer, sizeof(raw_buffer))) > 0) {
-                       set_stream_input(&zs, raw_buffer, read_bytes);
-                       z_return = inflate(&zs, 0);
-               } else
-                       z_return = Z_STREAM_END;
-       }
+       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 ((z_return != Z_STREAM_END && z_return != Z_BUF_ERROR)
-               || get_object_header(&header_obj, inflated_buffer) == 0
-               || git_object_typeisloose(header_obj.type) == 0)
-       {
+       if (!error && !git_object_typeisloose(out->type)) {
                giterr_set(GITERR_ZLIB, "failed to read loose object header");
                error = -1;
-       } else {
-               out->len = header_obj.size;
-               out->type = header_obj.type;
+               goto done;
        }
 
-       finish_inflate(&zs);
-       p_close(fd);
-
+done:
+       if (fd >= 0)
+               p_close(fd);
        return error;
 }
 
@@ -812,7 +775,7 @@ static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb
        return error;
 }
 
-static int loose_backend__stream_fwrite(git_odb_stream *_stream, const git_oid *oid)
+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;
@@ -831,13 +794,13 @@ static int loose_backend__stream_fwrite(git_odb_stream *_stream, const git_oid *
        return error;
 }
 
-static int loose_backend__stream_write(git_odb_stream *_stream, const char *data, size_t len)
+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__stream_free(git_odb_stream *_stream)
+static void loose_backend__writestream_free(git_odb_stream *_stream)
 {
        loose_writestream *stream = (loose_writestream *)_stream;
 
@@ -856,29 +819,32 @@ static int filebuf_flags(loose_backend *backend)
        return flags;
 }
 
-static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_backend, git_off_t length, git_otype type)
+static int loose_backend__writestream(git_odb_stream **stream_out, git_odb_backend *_backend, git_off_t length, git_otype type)
 {
        loose_backend *backend;
        loose_writestream *stream = NULL;
-       char hdr[64];
+       char hdr[MAX_HEADER_LEN];
        git_buf tmp_path = GIT_BUF_INIT;
-       int hdrlen;
+       size_t hdrlen;
+       int error;
 
        assert(_backend && length >= 0);
 
        backend = (loose_backend *)_backend;
        *stream_out = NULL;
 
-       hdrlen = git_odb__format_object_header(hdr, sizeof(hdr), length, type);
+       if ((error = git_odb__format_object_header(&hdrlen,
+               hdr, sizeof(hdr), length, type)) < 0)
+               return error;
 
        stream = git__calloc(1, sizeof(loose_writestream));
        GITERR_CHECK_ALLOC(stream);
 
        stream->stream.backend = _backend;
        stream->stream.read = NULL; /* read only */
-       stream->stream.write = &loose_backend__stream_write;
-       stream->stream.finalize_write = &loose_backend__stream_fwrite;
-       stream->stream.free = &loose_backend__stream_free;
+       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_buf_joinpath(&tmp_path, backend->objects_dir, "tmp_object") < 0 ||
@@ -896,18 +862,198 @@ static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_
        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;
+
+       /*
+        * 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 += chunk;
+               buffer_len -= chunk;
+       }
+
+       if (buffer_len) {
+               size_t chunk = min(buffer_len, INT_MAX);
+
+               if ((error = git_zstream_get_output(buffer, &chunk, &stream->zstream)) < 0)
+                       return error;
+
+               total += chunk;
+       }
+
+       return 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)) {
+               giterr_set(GITERR_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)) {
+               giterr_set(GITERR_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_otype *type_out,
+       git_odb_backend *_backend,
+       const git_oid *oid)
+{
+       loose_backend *backend;
+       loose_readstream *stream = NULL;
+       git_hash_ctx *hash_ctx = NULL;
+       git_buf object_path = GIT_BUF_INIT;
+       obj_hdr hdr;
+       int error = 0;
+
+       assert(stream_out && len_out && type_out && _backend && oid);
+
+       backend = (loose_backend *)_backend;
+       *stream_out = NULL;
+       *len_out = 0;
+       *type_out = GIT_OBJ_BAD;
+
+       if (locate_object(&object_path, backend, oid) < 0) {
+               error = git_odb__error_notfound("no matching loose object",
+                       oid, GIT_OID_HEXSZ);
+               goto done;
+       }
+
+       stream = git__calloc(1, sizeof(loose_readstream));
+       GITERR_CHECK_ALLOC(stream);
+
+       hash_ctx = git__malloc(sizeof(git_hash_ctx));
+       GITERR_CHECK_ALLOC(hash_ctx);
+
+       if ((error = git_hash_ctx_init(hash_ctx)) < 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) {
+               git_futils_mmap_free(&stream->map);
+               git_zstream_free(&stream->zstream);
+               git_hash_ctx_cleanup(hash_ctx);
+               git__free(hash_ctx);
+               git__free(stream);
+       }
+
+       git_buf_free(&object_path);
+       return error;
+}
+
 static int loose_backend__write(git_odb_backend *_backend, const git_oid *oid, const void *data, size_t len, git_otype type)
 {
-       int error = 0, header_len;
+       int error = 0;
        git_buf final_path = GIT_BUF_INIT;
-       char header[64];
+       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 */
-       header_len = git_odb__format_object_header(header, sizeof(header), len, type);
+       if ((error = git_odb__format_object_header(&header_len,
+               header, sizeof(header), len, type)) < 0)
+               goto cleanup;
 
        if (git_buf_joinpath(&final_path, backend->objects_dir, "tmp_object") < 0 ||
                git_filebuf_open(&fbuf, final_path.ptr, filebuf_flags(backend),
@@ -1002,7 +1148,8 @@ int git_odb_backend_loose(
        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__stream;
+       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;
index d6f2fb4af399d0b6bf43d03ddd868d4bd6973639..a81716ee3290c725acf5bab5cd25c9e874d4302e 100644 (file)
@@ -6,8 +6,10 @@
  */
 
 #include "common.h"
+
 #include "git2/object.h"
 #include "git2/sys/odb_backend.h"
+#include "git2/sys/mempack.h"
 #include "fileops.h"
 #include "hash.h"
 #include "odb.h"
index 51770a88e216d1ce0615b8fdd603400601979ecd..20aff5386bccaef5208b0236b70b797066e68fcb 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include "common.h"
+
 #include <zlib.h>
 #include "git2/repository.h"
 #include "git2/indexer.h"
index f9d2483a66492c7f4ff6c8cdf464655d1cc45079..0b0896b8ffb44db8f14f06506e4e7fbac851e009 100644 (file)
@@ -8,6 +8,7 @@
 #define INCLUDE_offmap_h__
 
 #include "common.h"
+
 #include "git2/types.h"
 
 #define kmalloc git__malloc
index 9dc7191941ae865cdf3cab161d82b64836e0c60b..0c63abb2ee04427b0cdb970892be3076c4b4fdc2 100644 (file)
--- a/src/oid.c
+++ b/src/oid.c
@@ -5,7 +5,8 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
+#include "oid.h"
+
 #include "git2/oid.h"
 #include "repository.h"
 #include "global.h"
index 922a2a3477815f6b8577aafbc7d0ed2770ace857..84231ffca905f84f2810a3ed1ef94950c241ea69 100644 (file)
--- a/src/oid.h
+++ b/src/oid.h
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_oid_h__
 #define INCLUDE_oid_h__
 
+#include "common.h"
+
 #include "git2/oid.h"
 
 /**
@@ -22,14 +24,7 @@ char *git_oid_allocfmt(const git_oid *id);
 
 GIT_INLINE(int) git_oid__hashcmp(const unsigned char *sha1, const unsigned char *sha2)
 {
-       int i;
-
-       for (i = 0; i < GIT_OID_RAWSZ; i++, sha1++, sha2++) {
-               if (*sha1 != *sha2)
-                       return *sha1 - *sha2;
-       }
-
-       return 0;
+       return memcmp(sha1, sha2, GIT_OID_RAWSZ);
 }
 
 /*
index 1d51a29581f35f406ffb706750379ddddb10290f..e70e9dd6185890fa98bcad0be0253ab4bd7c31fc 100644 (file)
@@ -5,8 +5,9 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "git2/oidarray.h"
 #include "oidarray.h"
+
+#include "git2/oidarray.h"
 #include "array.h"
 
 void git_oidarray_free(git_oidarray *arr)
@@ -19,3 +20,15 @@ void git_oidarray__from_array(git_oidarray *arr, git_array_oid_t *array)
        arr->count = array->size;
        arr->ids = array->ptr;
 }
+
+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);
+       }
+}
index a7215ae6c665d751396170c3c64b0c63cf08e5e1..eed3a1091205e3dbd7779c1ad77057c7b4e3b8ec 100644 (file)
@@ -8,11 +8,13 @@
 #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 *arr, git_array_oid_t *array);
 
 #endif
index 5632224941bf53159b3953cba10065b3a95d9310..49f129e93208947e1196512d12b2b69af47b8071 100644 (file)
@@ -8,6 +8,7 @@
 #define INCLUDE_oidmap_h__
 
 #include "common.h"
+
 #include "git2/oid.h"
 
 #define kmalloc git__malloc
diff --git a/src/openssl_stream.c b/src/openssl_stream.c
deleted file mode 100644 (file)
index 759c501..0000000
+++ /dev/null
@@ -1,656 +0,0 @@
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifdef GIT_OPENSSL
-
-#include <ctype.h>
-
-#include "global.h"
-#include "posix.h"
-#include "stream.h"
-#include "socket_stream.h"
-#include "openssl_stream.h"
-#include "netops.h"
-#include "git2/transport.h"
-#include "git2/sys/openssl.h"
-
-#ifdef GIT_CURL
-# include "curl_stream.h"
-#endif
-
-#ifndef GIT_WIN32
-# include <sys/types.h>
-# include <sys/socket.h>
-# include <netinet/in.h>
-#endif
-
-#include <openssl/ssl.h>
-#include <openssl/err.h>
-#include <openssl/x509v3.h>
-#include <openssl/bio.h>
-
-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"
-
-#if defined(GIT_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L
-
-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) {
-               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);
-}
-
-#endif /* GIT_THREADS && OPENSSL_VERSION_NUMBER < 0x10100000L */
-
-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;
-       }
-}
-
-int git_openssl_stream_global_init(void)
-{
-#ifdef GIT_OPENSSL
-       long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
-       const char *ciphers = git_libgit2__ssl_ciphers();
-
-       /* Older OpenSSL and MacOS OpenSSL doesn't have this */
-#ifdef SSL_OP_NO_COMPRESSION
-       ssl_opts |= SSL_OP_NO_COMPRESSION;
-#endif
-
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
-       SSL_load_error_strings();
-       OpenSSL_add_ssl_algorithms();
-#else
-       OPENSSL_init_ssl(0, NULL);
-#endif
-
-       /*
-        * 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.
-        */
-       git__ssl_ctx = SSL_CTX_new(SSLv23_method());
-       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)) {
-               SSL_CTX_free(git__ssl_ctx);
-               git__ssl_ctx = NULL;
-               return -1;
-       }
-
-       if (!ciphers) {
-               ciphers = GIT_SSL_DEFAULT_CIPHERS;
-       }
-
-       if(!SSL_CTX_set_cipher_list(git__ssl_ctx, ciphers)) {
-               SSL_CTX_free(git__ssl_ctx);
-               git__ssl_ctx = NULL;
-               return -1;
-       }
-
-       if (init_bio_method() < 0) {
-               SSL_CTX_free(git__ssl_ctx);
-               git__ssl_ctx = NULL;
-               return -1;
-       }
-
-#endif
-
-       git__on_shutdown(shutdown_ssl);
-
-       return 0;
-}
-
-int git_openssl_set_locking(void)
-{
-#if defined(GIT_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L
-       int num_locks, i;
-
-       num_locks = CRYPTO_num_locks();
-       openssl_locks = git__calloc(num_locks, sizeof(git_mutex));
-       GITERR_CHECK_ALLOC(openssl_locks);
-
-       for (i = 0; i < num_locks; i++) {
-               if (git_mutex_init(&openssl_locks[i]) != 0) {
-                       giterr_set(GITERR_SSL, "failed to initialize openssl locks");
-                       return -1;
-               }
-       }
-
-       CRYPTO_set_locking_callback(openssl_locking_function);
-       git__on_shutdown(shutdown_ssl_locking);
-       return 0;
-#elif OPENSSL_VERSION_NUMBER >= 0x10100000L
-       return 0;
-#else
-       giterr_set(GITERR_THREAD, "libgit2 was not built with threads");
-       return -1;
-#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");
-       GITERR_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);
-
-       assert(err != SSL_ERROR_WANT_READ);
-       assert(err != SSL_ERROR_WANT_WRITE);
-
-       switch (err) {
-       case SSL_ERROR_WANT_CONNECT:
-       case SSL_ERROR_WANT_ACCEPT:
-               giterr_set(GITERR_NET, "SSL error: connection failure");
-               break;
-       case SSL_ERROR_WANT_X509_LOOKUP:
-               giterr_set(GITERR_NET, "SSL error: x509 error");
-               break;
-       case SSL_ERROR_SYSCALL:
-               e = ERR_get_error();
-               if (e > 0) {
-                       giterr_set(GITERR_NET, "SSL error: %s",
-                                       ERR_error_string(e, NULL));
-                       break;
-               } else if (error < 0) {
-                       giterr_set(GITERR_OS, "SSL error: syscall failure");
-                       break;
-               }
-               giterr_set(GITERR_NET, "SSL error: received early EOF");
-               return GIT_EEOF;
-               break;
-       case SSL_ERROR_SSL:
-               e = ERR_get_error();
-               giterr_set(GITERR_NET, "SSL error: %s",
-                               ERR_error_string(e, NULL));
-               break;
-       case SSL_ERROR_NONE:
-       case SSL_ERROR_ZERO_RETURN:
-       default:
-               giterr_set(GITERR_NET, "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 int check_host_name(const char *name, const char *host)
-{
-       if (!strcasecmp(name, host))
-               return 0;
-
-       if (gitno__match_host(name, host) < 0)
-               return -1;
-
-       return 0;
-}
-
-static int verify_server_cert(SSL *ssl, const char *host)
-{
-       X509 *cert;
-       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;
-       int i = -1,j;
-
-       if (SSL_get_verify_result(ssl) != X509_V_OK) {
-               giterr_set(GITERR_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) {
-               giterr_set(GITERR_SSL, "the server did not provide a certificate");
-               return -1;
-       }
-
-       /* 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;
-
-                               if (check_host_name(name, host) < 0)
-                                       matched = 0;
-                               else
-                                       matched = 1;
-                       } else if (type == GEN_IPADD) {
-                               /* Here name isn't so much a name but a binary representation of the IP */
-                               matched = !!memcmp(name, addr, namelen);
-                       }
-               }
-       }
-       GENERAL_NAMES_free(alts);
-
-       if (matched == 0)
-               goto cert_fail_name;
-
-       if (matched == 1)
-               return 0;
-
-       /* 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);
-                       GITERR_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);
-               GITERR_CHECK_ALLOC(peer_cn);
-               if (memchr(peer_cn, '\0', size))
-                       goto cert_fail_name;
-       }
-
-       if (check_host_name((char *)peer_cn, host) < 0)
-               goto cert_fail_name;
-
-       OPENSSL_free(peer_cn);
-
-       return 0;
-
-on_error:
-       OPENSSL_free(peer_cn);
-       return ssl_set_error(ssl, 0);
-
-cert_fail_name:
-       OPENSSL_free(peer_cn);
-       giterr_set(GITERR_SSL, "hostname does not match certificate");
-       return GIT_ECERTIFICATE;
-}
-
-typedef struct {
-       git_stream parent;
-       git_stream *io;
-       bool connected;
-       char *host;
-       SSL *ssl;
-       git_cert_x509 cert_info;
-} openssl_stream;
-
-int openssl_close(git_stream *stream);
-
-int openssl_connect(git_stream *stream)
-{
-       int ret;
-       BIO *bio;
-       openssl_stream *st = (openssl_stream *) stream;
-
-       if ((ret = git_stream_connect(st->io)) < 0)
-               return ret;
-
-       st->connected = true;
-
-       bio = BIO_new(git_stream_bio_method);
-       GITERR_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);
-
-       return verify_server_cert(st->ssl, st->host);
-}
-
-int openssl_certificate(git_cert **out, git_stream *stream)
-{
-       openssl_stream *st = (openssl_stream *) stream;
-       int len;
-       X509 *cert = SSL_get_peer_certificate(st->ssl);
-       unsigned char *guard, *encoded_cert;
-
-       /* Retrieve the length of the certificate first */
-       len = i2d_X509(cert, NULL);
-       if (len < 0) {
-               giterr_set(GITERR_NET, "failed to retrieve certificate information");
-               return -1;
-       }
-
-       encoded_cert = git__malloc(len);
-       GITERR_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__free(encoded_cert);
-               giterr_set(GITERR_NET, "failed to retrieve certificate information");
-               return -1;
-       }
-
-       st->cert_info.parent.cert_type = GIT_CERT_X509;
-       st->cert_info.data = encoded_cert;
-       st->cert_info.len = len;
-
-       *out = &st->cert_info.parent;
-
-       return 0;
-}
-
-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);
-}
-
-ssize_t openssl_write(git_stream *stream, const char *data, size_t len, int flags)
-{
-       openssl_stream *st = (openssl_stream *) stream;
-       int ret;
-
-       GIT_UNUSED(flags);
-
-       if ((ret = SSL_write(st->ssl, data, len)) <= 0) {
-               return ssl_set_error(st->ssl, ret);
-       }
-
-       return ret;
-}
-
-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;
-}
-
-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 git_stream_close(st->io);
-}
-
-void openssl_free(git_stream *stream)
-{
-       openssl_stream *st = (openssl_stream *) stream;
-
-       SSL_free(st->ssl);
-       git__free(st->host);
-       git__free(st->cert_info.data);
-       git_stream_free(st->io);
-       git__free(st);
-}
-
-int git_openssl_stream_new(git_stream **out, const char *host, const char *port)
-{
-       int error;
-       openssl_stream *st;
-
-       st = git__calloc(1, sizeof(openssl_stream));
-       GITERR_CHECK_ALLOC(st);
-
-       st->io = NULL;
-#ifdef GIT_CURL
-       error = git_curl_stream_new(&st->io, host, port);
-#else
-       error = git_socket_stream_new(&st->io, host, port);
-#endif
-
-       if (error < 0)
-               goto out_err;
-
-       st->ssl = SSL_new(git__ssl_ctx);
-       if (st->ssl == NULL) {
-               giterr_set(GITERR_SSL, "failed to create ssl object");
-               error = -1;
-               goto out_err;
-       }
-
-       st->host = git__strdup(host);
-       GITERR_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;
-
-out_err:
-       git_stream_free(st->io);
-       git__free(st);
-
-       return error;
-}
-
-#else
-
-#include "stream.h"
-#include "git2/sys/openssl.h"
-
-int git_openssl_stream_global_init(void)
-{
-       return 0;
-}
-
-int git_openssl_set_locking(void)
-{
-       giterr_set(GITERR_SSL, "libgit2 was not built with OpenSSL support");
-       return -1;
-}
-
-int git_openssl_stream_new(git_stream **out, const char *host, const char *port)
-{
-       GIT_UNUSED(out);
-       GIT_UNUSED(host);
-       GIT_UNUSED(port);
-
-       giterr_set(GITERR_SSL, "openssl is not supported in this version");
-       return -1;
-}
-
-#endif
diff --git a/src/openssl_stream.h b/src/openssl_stream.h
deleted file mode 100644 (file)
index f5e59da..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This 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_openssl_stream_h__
-#define INCLUDE_openssl_stream_h__
-
-#include "git2/sys/stream.h"
-
-extern int git_openssl_stream_global_init(void);
-
-extern int git_openssl_stream_new(git_stream **out, const char *host, const char *port);
-
-/*
- * 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.
- */
-#ifdef GIT_OPENSSL
-# include <openssl/ssl.h>
-# include <openssl/err.h>
-# include <openssl/x509v3.h>
-# include <openssl/bio.h>
-
-
-
-# if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
-
-GIT_INLINE(BIO_METHOD*) BIO_meth_new(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;
-}
-
-GIT_INLINE(void) BIO_meth_free(BIO_METHOD *biom)
-{
-       git__free(biom);
-}
-
-GIT_INLINE(int) BIO_meth_set_write(BIO_METHOD *biom, int (*write) (BIO *, const char *, int))
-{
-       biom->bwrite = write;
-       return 1;
-}
-
-GIT_INLINE(int) BIO_meth_set_read(BIO_METHOD *biom, int (*read) (BIO *, char *, int))
-{
-       biom->bread = read;
-       return 1;
-}
-
-GIT_INLINE(int) BIO_meth_set_puts(BIO_METHOD *biom, int (*puts) (BIO *, const char *))
-{
-       biom->bputs = puts;
-       return 1;
-}
-
-GIT_INLINE(int) BIO_meth_set_gets(BIO_METHOD *biom, int (*gets) (BIO *, char *, int))
-
-{
-       biom->bgets = gets;
-       return 1;
-}
-
-GIT_INLINE(int) BIO_meth_set_ctrl(BIO_METHOD *biom, long (*ctrl) (BIO *, int, long, void *))
-{
-       biom->ctrl = ctrl;
-       return 1;
-}
-
-GIT_INLINE(int) BIO_meth_set_create(BIO_METHOD *biom, int (*create) (BIO *))
-{
-       biom->create = create;
-       return 1;
-}
-
-GIT_INLINE(int) BIO_meth_set_destroy(BIO_METHOD *biom, int (*destroy) (BIO *))
-{
-       biom->destroy = destroy;
-       return 1;
-}
-
-GIT_INLINE(int) BIO_get_new_index(void)
-{
-       /* This exists as of 1.1 so before we'd just have 0 */
-       return 0;
-}
-
-GIT_INLINE(void) BIO_set_init(BIO *b, int init)
-{
-       b->init = init;
-}
-
-GIT_INLINE(void) BIO_set_data(BIO *a, void *ptr)
-{
-       a->ptr = ptr;
-}
-
-GIT_INLINE(void*) BIO_get_data(BIO *a)
-{
-       return a->ptr;
-}
-
-GIT_INLINE(const unsigned char *) ASN1_STRING_get0_data(const ASN1_STRING *x)
-{
-       return ASN1_STRING_data((ASN1_STRING *)x);
-}
-
-# endif // OpenSSL < 1.1
-#endif // GIT_OPENSSL
-
-#endif
index ef272e8f5b066faf03c524dabc7a0bfdb2bc40c1..e9245143ceb9e3d2b5c618bcb57594585d8284ca 100644 (file)
@@ -1642,7 +1642,7 @@ int insert_tree(git_packbuilder *pb, git_tree *tree)
        if ((error = retrieve_object(&obj, pb, git_tree_id(tree))) < 0)
                return error;
 
-       if (obj->seen)
+       if (obj->seen || obj->uninteresting)
                return 0;
 
        obj->seen = 1;
@@ -1666,6 +1666,10 @@ int insert_tree(git_packbuilder *pb, git_tree *tree)
 
                        break;
                case GIT_OBJ_BLOB:
+                       if ((error = retrieve_object(&obj, pb, git_tree_id(tree))) < 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;
index e1e0ee3c8c97509a6825d5244785af480f813f97..c9cd5777afeadc3c171f0f7bdb52e36087bedfb4 100644 (file)
@@ -104,4 +104,4 @@ struct git_packbuilder {
 
 int git_packbuilder_write_buf(git_buf *buf, git_packbuilder *pb);
 
-#endif /* INCLUDE_pack_objects_h__ */
+#endif
index f8d0dc9acf807dae72f5fa8204b9a321e0d59f30..9ed3ec1af5ace1b9433896332c32b00474b93869 100644 (file)
@@ -5,9 +5,9 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
-#include "odb.h"
 #include "pack.h"
+
+#include "odb.h"
 #include "delta.h"
 #include "sha1_lookup.h"
 #include "mwindow.h"
@@ -716,8 +716,11 @@ int git_packfile_unpack(
                error = packfile_unpack_compressed(&delta, p, &w_curs, &curpos, elem->size, elem->type);
                git_mwindow_close(&w_curs);
 
-               if (error < 0)
+               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;
@@ -934,19 +937,19 @@ git_off_t get_delta_base(
        if (type == GIT_OBJ_OFS_DELTA) {
                unsigned used = 0;
                unsigned char c = base_info[used++];
-               base_offset = c & 127;
+               size_t unsigned_base_offset = c & 127;
                while (c & 128) {
                        if (left <= used)
                                return GIT_EBUFS;
-                       base_offset += 1;
-                       if (!base_offset || MSB(base_offset, 7))
+                       unsigned_base_offset += 1;
+                       if (!unsigned_base_offset || MSB(unsigned_base_offset, 7))
                                return 0; /* overflow */
                        c = base_info[used++];
-                       base_offset = (base_offset << 7) + (c & 127);
+                       unsigned_base_offset = (unsigned_base_offset << 7) + (c & 127);
                }
-               base_offset = delta_obj_offset - base_offset;
-               if (base_offset <= 0 || base_offset >= delta_obj_offset)
+               if (unsigned_base_offset == 0 || (size_t)delta_obj_offset <= unsigned_base_offset)
                        return 0; /* out of bound */
+               base_offset = delta_obj_offset - unsigned_base_offset;
                *curpos += used;
        } else if (type == GIT_OBJ_REF_DELTA) {
                /* If we have the cooperative cache, search in it first */
@@ -1316,11 +1319,7 @@ static int pack_entry_find_offset(
                short_oid->id[0], short_oid->id[1], short_oid->id[2], lo, hi, p->num_objects);
 #endif
 
-#ifdef GIT_USE_LOOKUP
-       pos = sha1_entry_pos(index, stride, 0, lo, hi, p->num_objects, short_oid->id);
-#else
        pos = sha1_position(index, stride, lo, hi, short_oid->id);
-#endif
 
        if (pos >= 0) {
                /* An object matching exactly the oid was found */
index e2bf165f48f9c577382e033f1c5106112c012c33..c0ca74f1413d5aee5f230acdb51d6d67aa941ba0 100644 (file)
@@ -8,11 +8,12 @@
 #ifndef INCLUDE_pack_h__
 #define INCLUDE_pack_h__
 
+#include "common.h"
+
 #include <zlib.h>
 
 #include "git2/oid.h"
 
-#include "common.h"
 #include "map.h"
 #include "mwindow.h"
 #include "odb.h"
diff --git a/src/parse.c b/src/parse.c
new file mode 100644 (file)
index 0000000..6b8902c
--- /dev/null
@@ -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.
+ */
+#include "parse.h"
+
+int git_parse_ctx_init(git_parse_ctx *ctx, const char *content, size_t content_len)
+{
+       if (content_len)
+               ctx->content = content;
+       else
+               ctx->content = NULL;
+
+       ctx->content_len = content_len;
+       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));
+}
+
+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_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/parse.h b/src/parse.h
new file mode 100644 (file)
index 0000000..46897e3
--- /dev/null
@@ -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;
+
+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_err(...) \
+       ( giterr_set(GITERR_PATCH, __VA_ARGS__), -1 )
+
+#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);
+
+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
index 9b7c9c64c44affbf7c6137b8f509e34cb045a51b..5e329518d248496ea01e7cdc4e02a0568aeae27a 100644 (file)
@@ -1,7 +1,14 @@
-#include "git2/patch.h"
-#include "diff.h"
+/*
+* Copyright (C) the libgit2 contributors. All rights reserved.
+*
+* 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,
index 525ae700777f9a71385d0caa89596145411ef370..156d1310e793cfbafcfbd5584941d2cbc843b7e7 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_patch_h__
 #define INCLUDE_patch_h__
 
+#include "common.h"
+
 #include "git2/patch.h"
 #include "array.h"
 
index 804fc0e09368050075ab293f8cc08f673f1cd33f..29cda8b1c3f1eb11c5b1d34dd8ca9ff59fc9ff49 100644 (file)
@@ -4,13 +4,14 @@
  * 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 "patch_generate.h"
+
 #include "git2/blob.h"
 #include "diff.h"
 #include "diff_generate.h"
 #include "diff_file.h"
 #include "diff_driver.h"
-#include "patch_generate.h"
 #include "diff_xdiff.h"
 #include "delta.h"
 #include "zstream.h"
@@ -138,7 +139,7 @@ static int patch_generated_alloc_from_diff(
 
        if (!(error = patch_generated_init(patch, diff, delta_index))) {
                patch->flags |= GIT_PATCH_GENERATED_ALLOCATED;
-               GIT_REFCOUNT_INC(patch);
+               GIT_REFCOUNT_INC(&patch->base);
        } else {
                git__free(patch);
                patch = NULL;
@@ -641,7 +642,7 @@ int git_patch_from_blob_and_buffer(
        git_patch **out,
        const git_blob *old_blob,
        const char *old_path,
-       const char *buf,
+       const void *buf,
        size_t buflen,
        const char *buf_path,
        const git_diff_options *opts)
@@ -680,7 +681,7 @@ int git_patch_from_buffers(
        const void *old_buf,
        size_t old_len,
        const char *old_path,
-       const char *new_buf,
+       const void *new_buf,
        size_t new_len,
        const char *new_path,
        const git_diff_options *opts)
index 2e89b5c4dc576fbe9e58fac6678fb887a0caf5f8..20f78cbfab82cb9aeaf4d1c4074b400225df025b 100644 (file)
@@ -8,6 +8,7 @@
 #define INCLUDE_patch_generate_h__
 
 #include "common.h"
+
 #include "diff.h"
 #include "diff_file.h"
 #include "patch.h"
index 0a9edcd188932d97de73a0c0f5be62eb60108dd5..acdd45e8237c690a5f49f783cc44d9eb23e344e3 100644 (file)
@@ -4,15 +4,14 @@
  * 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 "patch_parse.h"
 #include "diff_parse.h"
 #include "path.h"
 
-#define parse_err(...) \
-       ( giterr_set(GITERR_PATCH, __VA_ARGS__), -1 )
-
 typedef struct {
        git_patch base;
 
@@ -34,104 +33,34 @@ typedef struct {
        char *old_prefix, *new_prefix;
 } git_patch_parsed;
 
-
-GIT_INLINE(bool) parse_ctx_contains(
-       git_patch_parse_ctx *ctx, const char *str, size_t len)
-{
-       return (ctx->line_len >= len && memcmp(ctx->line, str, len) == 0);
-}
-
-#define parse_ctx_contains_s(ctx, str) \
-       parse_ctx_contains(ctx, str, sizeof(str) - 1)
-
-static void parse_advance_line(git_patch_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++;
-}
-
-static void parse_advance_chars(git_patch_parse_ctx *ctx, size_t char_cnt)
-{
-       ctx->line += char_cnt;
-       ctx->remain_len -= char_cnt;
-       ctx->line_len -= char_cnt;
-}
-
-static int parse_advance_expected(
-       git_patch_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;
-
-       parse_advance_chars(ctx, expected_len);
-       return 0;
-}
-
-#define parse_advance_expected_str(ctx, str) \
-       parse_advance_expected(ctx, str, strlen(str))
-
-static int parse_advance_ws(git_patch_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;
-}
-
-static int parse_advance_nl(git_patch_parse_ctx *ctx)
-{
-       if (ctx->line_len != 1 || ctx->line[0] != '\n')
-               return -1;
-
-       parse_advance_line(ctx);
-       return 0;
-}
-
 static int header_path_len(git_patch_parse_ctx *ctx)
 {
        bool inquote = 0;
-       bool quoted = (ctx->line_len > 0 && ctx->line[0] == '"');
+       bool quoted = git_parse_ctx_contains_s(&ctx->parse_ctx, "\"");
        size_t len;
 
-       for (len = quoted; len < ctx->line_len; len++) {
-               if (!quoted && git__isspace(ctx->line[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->line[len] == '"') {
+               else if (quoted && !inquote && ctx->parse_ctx.line[len] == '"') {
                        len++;
                        break;
                }
 
-               inquote = (!inquote && ctx->line[len] == '\\');
+               inquote = (!inquote && ctx->parse_ctx.line[len] == '\\');
        }
 
        return len;
 }
 
-static int parse_header_path_buf(git_buf *path, git_patch_parse_ctx *ctx)
+static int parse_header_path_buf(git_buf *path, git_patch_parse_ctx *ctx, size_t path_len)
 {
-       int path_len, error = 0;
-
-       path_len = header_path_len(ctx);
+       int error;
 
-       if ((error = git_buf_put(path, ctx->line, path_len)) < 0)
+       if ((error = git_buf_put(path, ctx->parse_ctx.line, path_len)) < 0)
                goto done;
 
-       parse_advance_chars(ctx, path_len);
+       git_parse_advance_chars(&ctx->parse_ctx, path_len);
 
        git_buf_rtrim(path);
 
@@ -150,7 +79,7 @@ done:
 static int parse_header_path(char **out, git_patch_parse_ctx *ctx)
 {
        git_buf path = GIT_BUF_INIT;
-       int error = parse_header_path_buf(&path, ctx);
+       int error = parse_header_path_buf(&path, ctx, header_path_len(ctx));
 
        *out = git_buf_detach(&path);
 
@@ -160,35 +89,48 @@ static int parse_header_path(char **out, git_patch_parse_ctx *ctx)
 static int parse_header_git_oldpath(
        git_patch_parsed *patch, git_patch_parse_ctx *ctx)
 {
-       return parse_header_path(&patch->old_path, ctx);
+       git_buf old_path = GIT_BUF_INIT;
+       int error;
+
+       if ((error = parse_header_path_buf(&old_path, ctx, ctx->parse_ctx.line_len - 1)) <  0)
+               goto out;
+
+       patch->old_path = git_buf_detach(&old_path);
+
+out:
+       git_buf_free(&old_path);
+       return error;
 }
 
 static int parse_header_git_newpath(
        git_patch_parsed *patch, git_patch_parse_ctx *ctx)
 {
-       return parse_header_path(&patch->new_path, ctx);
+       git_buf new_path = GIT_BUF_INIT;
+       int error;
+
+       if ((error = parse_header_path_buf(&new_path, ctx, ctx->parse_ctx.line_len - 1)) <  0)
+               goto out;
+
+       patch->new_path = git_buf_detach(&new_path);
+
+out:
+       git_buf_free(&new_path);
+       return error;
 }
 
 static int parse_header_mode(uint16_t *mode, git_patch_parse_ctx *ctx)
 {
-       const char *end;
-       int32_t m;
-       int ret;
-
-       if (ctx->line_len < 1 || !git__isdigit(ctx->line[0]))
-               return parse_err("invalid file mode at line %"PRIuZ, ctx->line_num);
+       int64_t m;
 
-       if ((ret = git__strntol32(&m, ctx->line, ctx->line_len, &end, 8)) < 0)
-               return ret;
+       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;
 
-       parse_advance_chars(ctx, (end - ctx->line));
-
-       return ret;
+       return 0;
 }
 
 static int parse_header_oid(
@@ -198,17 +140,17 @@ static int parse_header_oid(
 {
        size_t len;
 
-       for (len = 0; len < ctx->line_len && len < GIT_OID_HEXSZ; len++) {
-               if (!git__isxdigit(ctx->line[len]))
+       for (len = 0; len < ctx->parse_ctx.line_len && len < GIT_OID_HEXSZ; len++) {
+               if (!git__isxdigit(ctx->parse_ctx.line[len]))
                        break;
        }
 
        if (len < GIT_OID_MINPREFIXLEN || len > GIT_OID_HEXSZ ||
-               git_oid_fromstrn(oid, ctx->line, len) < 0)
-               return parse_err("invalid hex formatted object id at line %"PRIuZ,
-                       ctx->line_num);
+               git_oid_fromstrn(oid, ctx->parse_ctx.line, len) < 0)
+               return git_parse_err("invalid hex formatted object id at line %"PRIuZ,
+                       ctx->parse_ctx.line_num);
 
-       parse_advance_chars(ctx, len);
+       git_parse_advance_chars(&ctx->parse_ctx, len);
 
        *oid_len = (uint16_t)len;
 
@@ -218,17 +160,19 @@ static int parse_header_oid(
 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 ||
-               parse_advance_expected_str(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 (ctx->line_len > 0 && ctx->line[0] == ' ') {
+       if (git_parse_peek(&c, &ctx->parse_ctx, 0) == 0 && c == ' ') {
                uint16_t mode;
 
-               parse_advance_chars(ctx, 1);
+               git_parse_advance_chars(&ctx->parse_ctx, 1);
 
                if (parse_header_mode(&mode, ctx) < 0)
                        return -1;
@@ -287,7 +231,7 @@ static int parse_header_rename(
 {
        git_buf path = GIT_BUF_INIT;
 
-       if (parse_header_path_buf(&path, ctx) < 0)
+       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
@@ -327,19 +271,15 @@ static int parse_header_copyto(
 
 static int parse_header_percent(uint16_t *out, git_patch_parse_ctx *ctx)
 {
-       int32_t val;
-       const char *end;
+       int64_t val;
 
-       if (ctx->line_len < 1 || !git__isdigit(ctx->line[0]) ||
-               git__strntol32(&val, ctx->line, ctx->line_len, &end, 10) < 0)
+       if (git_parse_advance_digit(&val, &ctx->parse_ctx, 10) < 0)
                return -1;
 
-       parse_advance_chars(ctx, (end - ctx->line));
-
-       if (parse_advance_expected_str(ctx, "%") < 0)
+       if (git_parse_advance_expected_str(&ctx->parse_ctx, "%") < 0)
                return -1;
 
-       if (val > 100)
+       if (val < 0 || val > 100)
                return -1;
 
        *out = val;
@@ -350,8 +290,8 @@ 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 parse_err("invalid similarity percentage at line %"PRIuZ,
-                       ctx->line_num);
+               return git_parse_err("invalid similarity percentage at line %"PRIuZ,
+                       ctx->parse_ctx.line_num);
 
        return 0;
 }
@@ -362,39 +302,98 @@ static int parse_header_dissimilarity(
        uint16_t dissimilarity;
 
        if (parse_header_percent(&dissimilarity, ctx) < 0)
-               return parse_err("invalid similarity percentage at line %"PRIuZ,
-                       ctx->line_num);
+               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
+        * proceeed 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_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 *);
-} header_git_op;
-
-static const header_git_op header_git_ops[] = {
-       { "diff --git ", NULL },
-       { "@@ -", NULL },
-       { "GIT binary patch", NULL },
-       { "Binary files ", NULL },
-       { "--- ", parse_header_git_oldpath },
-       { "+++ ", parse_header_git_newpath },
-       { "index ", parse_header_git_index },
-       { "old mode ", parse_header_git_oldmode },
-       { "new mode ", parse_header_git_newmode },
-       { "deleted file mode ", parse_header_git_deletedfilemode },
-       { "new file mode ", parse_header_git_newfilemode },
-       { "rename from ", parse_header_renamefrom },
-       { "rename to ", parse_header_renameto },
-       { "rename old ", parse_header_renamefrom },
-       { "rename new ", parse_header_renameto },
-       { "copy from ", parse_header_copyfrom },
-       { "copy to ", parse_header_copyto },
-       { "similarity index ", parse_header_similarity },
-       { "dissimilarity index ", parse_header_dissimilarity },
+} 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_INDEX,      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_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_END,        0,                NULL },
 };
 
 static int parse_header_git(
@@ -403,65 +402,58 @@ static int parse_header_git(
 {
        size_t i;
        int error = 0;
-
-       /* Parse the diff --git line */
-       if (parse_advance_expected_str(ctx, "diff --git ") < 0)
-               return parse_err("corrupt git diff header at line %"PRIuZ, ctx->line_num);
-
-       if (parse_header_path(&patch->header_old_path, ctx) < 0)
-               return parse_err("corrupt old path in git diff header at line %"PRIuZ,
-                       ctx->line_num);
-
-       if (parse_advance_ws(ctx) < 0 ||
-               parse_header_path(&patch->header_new_path, ctx) < 0)
-               return parse_err("corrupt new path in git diff header at line %"PRIuZ,
-                       ctx->line_num);
+       parse_header_state state = STATE_START;
 
        /* Parse remaining header lines */
-       for (parse_advance_line(ctx);
-               ctx->remain_len > 0;
-               parse_advance_line(ctx)) {
-
+       for (; ctx->parse_ctx.remain_len > 0; git_parse_advance_line(&ctx->parse_ctx)) {
                bool found = false;
 
-               if (ctx->line_len == 0 || ctx->line[ctx->line_len - 1] != '\n')
+               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(header_git_ops); i++) {
-                       const header_git_op *op = &header_git_ops[i];
-                       size_t op_len = strlen(op->str);
+               for (i = 0; i < ARRAY_SIZE(transitions); i++) {
+                       const parse_header_transition *transition = &transitions[i];
+                       size_t op_len = strlen(transition->str);
 
-                       if (memcmp(ctx->line, op->str, min(op_len, ctx->line_len)) != 0)
+                       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 (op->fn == NULL)
+                       if (transition->fn == NULL)
                                goto done;
 
-                       parse_advance_chars(ctx, op_len);
+                       git_parse_advance_chars(&ctx->parse_ctx, op_len);
 
-                       if ((error = op->fn(patch, ctx)) < 0)
+                       if ((error = transition->fn(patch, ctx)) < 0)
                                goto done;
 
-                       parse_advance_ws(ctx);
+                       git_parse_advance_ws(&ctx->parse_ctx);
 
-                       if (parse_advance_expected_str(ctx, "\n") < 0 ||
-                           ctx->line_len > 0) {
-                               error = parse_err("trailing data at line %"PRIuZ, ctx->line_num);
+                       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 = parse_err("invalid patch header at line %"PRIuZ,
-                               ctx->line_num);
+                       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;
 }
@@ -471,17 +463,17 @@ static int parse_number(git_off_t *out, git_patch_parse_ctx *ctx)
        const char *end;
        int64_t num;
 
-       if (!git__isdigit(ctx->line[0]))
+       if (!git__isdigit(ctx->parse_ctx.line[0]))
                return -1;
 
-       if (git__strntol64(&num, ctx->line, ctx->line_len, &end, 10) < 0)
+       if (git__strntol64(&num, ctx->parse_ctx.line, ctx->parse_ctx.line_len, &end, 10) < 0)
                return -1;
 
        if (num < 0)
                return -1;
 
        *out = num;
-       parse_advance_chars(ctx, (end - ctx->line));
+       git_parse_advance_chars(&ctx->parse_ctx, (end - ctx->parse_ctx.line));
 
        return 0;
 }
@@ -490,7 +482,7 @@ static int parse_int(int *out, git_patch_parse_ctx *ctx)
 {
        git_off_t num;
 
-       if (parse_number(&num, ctx) < 0 || !git__is_int(num))
+       if (git_parse_advance_digit(&num, &ctx->parse_ctx, 10) < 0 || !git__is_int(num))
                return -1;
 
        *out = (int)num;
@@ -501,43 +493,44 @@ static int parse_hunk_header(
        git_patch_hunk *hunk,
        git_patch_parse_ctx *ctx)
 {
-       const char *header_start = ctx->line;
+       const char *header_start = ctx->parse_ctx.line;
+       char c;
 
        hunk->hunk.old_lines = 1;
        hunk->hunk.new_lines = 1;
 
-       if (parse_advance_expected_str(ctx, "@@ -") < 0 ||
+       if (git_parse_advance_expected_str(&ctx->parse_ctx, "@@ -") < 0 ||
                parse_int(&hunk->hunk.old_start, ctx) < 0)
                goto fail;
 
-       if (ctx->line_len > 0 && ctx->line[0] == ',') {
-               if (parse_advance_expected_str(ctx, ",") < 0 ||
+       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 (parse_advance_expected_str(ctx, " +") < 0 ||
+       if (git_parse_advance_expected_str(&ctx->parse_ctx, " +") < 0 ||
                parse_int(&hunk->hunk.new_start, ctx) < 0)
                goto fail;
 
-       if (ctx->line_len > 0 && ctx->line[0] == ',') {
-               if (parse_advance_expected_str(ctx, ",") < 0 ||
+       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 (parse_advance_expected_str(ctx, " @@") < 0)
+       if (git_parse_advance_expected_str(&ctx->parse_ctx, " @@") < 0)
                goto fail;
 
-       parse_advance_line(ctx);
+       git_parse_advance_line(&ctx->parse_ctx);
 
        if (!hunk->hunk.old_lines && !hunk->hunk.new_lines)
                goto fail;
 
-       hunk->hunk.header_len = ctx->line - header_start;
+       hunk->hunk.header_len = ctx->parse_ctx.line - header_start;
        if (hunk->hunk.header_len > (GIT_DIFF_HUNK_HEADER_SIZE - 1))
-               return parse_err("oversized patch hunk header at line %"PRIuZ,
-                       ctx->line_num);
+               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';
@@ -546,7 +539,7 @@ static int parse_hunk_header(
 
 fail:
        giterr_set(GITERR_PATCH, "invalid patch hunk header at line %"PRIuZ,
-               ctx->line_num);
+               ctx->parse_ctx.line_num);
        return -1;
 }
 
@@ -562,23 +555,27 @@ static int parse_hunk_body(
        int newlines = hunk->hunk.new_lines;
 
        for (;
-               ctx->remain_len > 1 &&
+               ctx->parse_ctx.remain_len > 1 &&
                (oldlines || newlines) &&
-               (ctx->remain_len <= 4 || memcmp(ctx->line, "@@ -", 4) != 0);
-               parse_advance_line(ctx)) {
+               !git_parse_ctx_contains_s(&ctx->parse_ctx, "@@ -");
+               git_parse_advance_line(&ctx->parse_ctx)) {
 
+               char c;
                int origin;
                int prefix = 1;
 
-               if (ctx->line_len == 0 || ctx->line[ctx->line_len - 1] != '\n') {
-                       error = parse_err("invalid patch instruction at line %"PRIuZ,
-                               ctx->line_num);
+               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;
                }
 
-               switch (ctx->line[0]) {
+               git_parse_peek(&c, &ctx->parse_ctx, 0);
+
+               switch (c) {
                case '\n':
                        prefix = 0;
+                       /* fall through */
 
                case ' ':
                        origin = GIT_DIFF_LINE_CONTEXT;
@@ -597,7 +594,7 @@ static int parse_hunk_body(
                        break;
 
                default:
-                       error = parse_err("invalid patch hunk at line %"PRIuZ, ctx->line_num);
+                       error = git_parse_err("invalid patch hunk at line %"PRIuZ, ctx->parse_ctx.line_num);
                        goto done;
                }
 
@@ -606,16 +603,16 @@ static int parse_hunk_body(
 
                memset(line, 0x0, sizeof(git_diff_line));
 
-               line->content = ctx->line + prefix;
-               line->content_len = ctx->line_len - prefix;
-               line->content_offset = ctx->content_len - ctx->remain_len;
+               line->content = ctx->parse_ctx.line + prefix;
+               line->content_len = ctx->parse_ctx.line_len - prefix;
+               line->content_offset = ctx->parse_ctx.content_len - ctx->parse_ctx.remain_len;
                line->origin = origin;
 
                hunk->line_count++;
        }
 
        if (oldlines || newlines) {
-               error = parse_err(
+               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;
@@ -626,19 +623,19 @@ static int parse_hunk_body(
         * localized.  Because `diff` optimizes for the case where you
         * want to apply the patch by hand.
         */
-       if (parse_ctx_contains_s(ctx, "\\ ") &&
+       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 = parse_err("cannot trim trailing newline of empty line");
+                       error = git_parse_err("cannot trim trailing newline of empty line");
                        goto done;
                }
 
                line->content_len--;
 
-               parse_advance_line(ctx);
+               git_parse_advance_line(&ctx->parse_ctx);
        }
 
 done:
@@ -651,18 +648,15 @@ static int parse_patch_header(
 {
        int error = 0;
 
-       for (ctx->line = ctx->remain;
-               ctx->remain_len > 0;
-               parse_advance_line(ctx)) {
-
+       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->line_len < 6)
+               if (ctx->parse_ctx.line_len < 6)
                        continue;
 
                /* This might be a hunk header without a patch header, provide a
                 * sensible error message. */
-               if (parse_ctx_contains_s(ctx, "@@ -")) {
-                       size_t line_num = ctx->line_num;
+               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
@@ -673,17 +667,17 @@ static int parse_patch_header(
                                continue;
                        }
 
-                       error = parse_err("invalid hunk header outside patch at line %"PRIuZ,
+                       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->remain_len < ctx->line_len + 6)
+               if (ctx->parse_ctx.remain_len < ctx->parse_ctx.line_len + 6)
                        break;
 
                /* A proper git patch */
-               if (parse_ctx_contains_s(ctx, "diff --git ")) {
+               if (git_parse_ctx_contains_s(&ctx->parse_ctx, "diff --git ")) {
                        error = parse_header_git(patch, ctx);
                        goto done;
                }
@@ -708,27 +702,30 @@ static int parse_patch_binary_side(
        git_off_t len;
        int error = 0;
 
-       if (parse_ctx_contains_s(ctx, "literal ")) {
+       if (git_parse_ctx_contains_s(&ctx->parse_ctx, "literal ")) {
                type = GIT_DIFF_BINARY_LITERAL;
-               parse_advance_chars(ctx, 8);
-       } else if (parse_ctx_contains_s(ctx, "delta ")) {
+               git_parse_advance_chars(&ctx->parse_ctx, 8);
+       } else if (git_parse_ctx_contains_s(&ctx->parse_ctx, "delta ")) {
                type = GIT_DIFF_BINARY_DELTA;
-               parse_advance_chars(ctx, 6);
+               git_parse_advance_chars(&ctx->parse_ctx, 6);
        } else {
-               error = parse_err(
-                       "unknown binary delta type at line %"PRIuZ, ctx->line_num);
+               error = git_parse_err(
+                       "unknown binary delta type at line %"PRIuZ, ctx->parse_ctx.line_num);
                goto done;
        }
 
-       if (parse_number(&len, ctx) < 0 || parse_advance_nl(ctx) < 0 || len < 0) {
-               error = parse_err("invalid binary size at line %"PRIuZ, ctx->line_num);
+       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->line_len) {
-               char c = ctx->line[0];
+       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')
@@ -737,32 +734,32 @@ static int parse_patch_binary_side(
                        decoded_len = c - 'a' + (('z' - 'a') + 1) + 1;
 
                if (!decoded_len) {
-                       error = parse_err("invalid binary length at line %"PRIuZ, ctx->line_num);
+                       error = git_parse_err("invalid binary length at line %"PRIuZ, ctx->parse_ctx.line_num);
                        goto done;
                }
 
-               parse_advance_chars(ctx, 1);
+               git_parse_advance_chars(&ctx->parse_ctx, 1);
 
                encoded_len = ((decoded_len / 4) + !!(decoded_len % 4)) * 5;
 
-               if (encoded_len > ctx->line_len - 1) {
-                       error = parse_err("truncated binary data at line %"PRIuZ, ctx->line_num);
+               if (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_buf_decode_base85(
-                       &decoded, ctx->line, encoded_len, decoded_len)) < 0)
+                       &decoded, ctx->parse_ctx.line, encoded_len, decoded_len)) < 0)
                        goto done;
 
                if (decoded.size - decoded_orig != decoded_len) {
-                       error = parse_err("truncated binary data at line %"PRIuZ, ctx->line_num);
+                       error = git_parse_err("truncated binary data at line %"PRIuZ, ctx->parse_ctx.line_num);
                        goto done;
                }
 
-               parse_advance_chars(ctx, encoded_len);
+               git_parse_advance_chars(&ctx->parse_ctx, encoded_len);
 
-               if (parse_advance_nl(ctx) < 0) {
-                       error = parse_err("trailing data at line %"PRIuZ, ctx->line_num);
+               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;
                }
        }
@@ -784,27 +781,27 @@ static int parse_patch_binary(
 {
        int error;
 
-       if (parse_advance_expected_str(ctx, "GIT binary patch") < 0 ||
-               parse_advance_nl(ctx) < 0)
-               return parse_err("corrupt git binary header at line %"PRIuZ, ctx->line_num);
+       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 (parse_advance_nl(ctx) < 0)
-               return parse_err("corrupt git binary separator at line %"PRIuZ,
-                       ctx->line_num);
+       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 (parse_advance_nl(ctx) < 0)
-               return parse_err("corrupt git binary patch separator at line %"PRIuZ,
-                       ctx->line_num);
+       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;
@@ -815,13 +812,13 @@ static int parse_patch_binary_nodata(
        git_patch_parsed *patch,
        git_patch_parse_ctx *ctx)
 {
-       if (parse_advance_expected_str(ctx, "Binary files ") < 0 ||
-               parse_advance_expected_str(ctx, patch->header_old_path) < 0 ||
-               parse_advance_expected_str(ctx, " and ") < 0 ||
-               parse_advance_expected_str(ctx, patch->header_new_path) < 0 ||
-               parse_advance_expected_str(ctx, " differ") < 0 ||
-               parse_advance_nl(ctx) < 0)
-               return parse_err("corrupt git binary header at line %"PRIuZ, ctx->line_num);
+       if (git_parse_advance_expected_str(&ctx->parse_ctx, "Binary files ") < 0 ||
+               git_parse_advance_expected_str(&ctx->parse_ctx, patch->header_old_path) < 0 ||
+               git_parse_advance_expected_str(&ctx->parse_ctx, " and ") < 0 ||
+               git_parse_advance_expected_str(&ctx->parse_ctx, patch->header_new_path) < 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;
@@ -835,7 +832,7 @@ static int parse_patch_hunks(
        git_patch_hunk *hunk;
        int error = 0;
 
-       while (parse_ctx_contains_s(ctx, "@@ -")) {
+       while (git_parse_ctx_contains_s(&ctx->parse_ctx, "@@ -")) {
                hunk = git_array_alloc(patch->base.hunks);
                GITERR_CHECK_ALLOC(hunk);
 
@@ -858,9 +855,9 @@ done:
 static int parse_patch_body(
        git_patch_parsed *patch, git_patch_parse_ctx *ctx)
 {
-       if (parse_ctx_contains_s(ctx, "GIT binary patch"))
+       if (git_parse_ctx_contains_s(&ctx->parse_ctx, "GIT binary patch"))
                return parse_patch_binary(patch, ctx);
-       else if (parse_ctx_contains_s(ctx, "Binary files "))
+       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);
@@ -876,10 +873,10 @@ int check_header_names(
                return 0;
 
        if (two_null && strcmp(two, "/dev/null") != 0)
-               return parse_err("expected %s path of '/dev/null'", old_or_new);
+               return git_parse_err("expected %s path of '/dev/null'", old_or_new);
 
        else if (!two_null && strcmp(one, two) != 0)
-               return parse_err("mismatched %s path names", old_or_new);
+               return git_parse_err("mismatched %s path names", old_or_new);
 
        return 0;
 }
@@ -912,7 +909,7 @@ static int check_prefix(
        }
 
        if (remain_len || !*path)
-               return parse_err(
+               return git_parse_err(
                        "header filename does not contain %"PRIuZ" path components",
                        prefix_len);
 
@@ -931,10 +928,10 @@ static int check_filenames(git_patch_parsed *patch)
        bool deleted = (patch->base.delta->status == GIT_DELTA_DELETED);
 
        if (patch->old_path && !patch->new_path)
-               return parse_err("missing new path");
+               return git_parse_err("missing new path");
 
        if (!patch->old_path && patch->new_path)
-               return parse_err("missing old path");
+               return git_parse_err("missing old path");
 
        /* Ensure (non-renamed) paths match */
        if (check_header_names(
@@ -967,7 +964,7 @@ static int check_filenames(git_patch_parsed *patch)
 
        if (!patch->base.delta->old_file.path &&
                !patch->base.delta->new_file.path)
-               return parse_err("git diff header lacks old / new paths");
+               return git_parse_err("git diff header lacks old / new paths");
 
        return 0;
 }
@@ -988,7 +985,7 @@ static int check_patch(git_patch_parsed *patch)
                        !(delta->flags & GIT_DIFF_FLAG_BINARY) &&
                        delta->new_file.mode == delta->old_file.mode &&
                        git_array_size(patch->base.hunks) == 0)
-               return parse_err("patch with no hunks");
+               return git_parse_err("patch with no hunks");
 
        if (delta->status == GIT_DELTA_ADDED) {
                memset(&delta->old_file.id, 0x0, sizeof(git_oid));
@@ -1014,19 +1011,11 @@ git_patch_parse_ctx *git_patch_parse_ctx_init(
        if ((ctx = git__calloc(1, sizeof(git_patch_parse_ctx))) == NULL)
                return NULL;
 
-       if (content_len) {
-               if ((ctx->content = git__malloc(content_len)) == NULL) {
-                       git__free(ctx);
-                       return NULL;
-               }
-
-               memcpy((char *)ctx->content, content, content_len);
+       if ((git_parse_ctx_init(&ctx->parse_ctx, content, content_len)) < 0) {
+               git__free(ctx);
+               return NULL;
        }
 
-       ctx->content_len = content_len;
-       ctx->remain = ctx->content;
-       ctx->remain_len = ctx->content_len;
-
        if (opts)
                memcpy(&ctx->opts, opts, sizeof(git_patch_options));
        else
@@ -1041,7 +1030,7 @@ static void patch_parse_ctx_free(git_patch_parse_ctx *ctx)
        if (!ctx)
                return;
 
-       git__free((char *)ctx->content);
+       git_parse_ctx_clear(&ctx->parse_ctx);
        git__free(ctx);
 }
 
@@ -1116,21 +1105,21 @@ int git_patch_parse(
        patch->base.delta->status = GIT_DELTA_MODIFIED;
        patch->base.delta->nfiles = 2;
 
-       start = ctx->remain_len;
+       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->remain_len;
-       ctx->remain += used;
+       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);
+       GIT_REFCOUNT_INC(&patch->base);
        *out = &patch->base;
 
 done:
index 99eb4587be1f2e665b385b47cfc7edea9c4db078..140629da857bad48977d04718cc90d01f5e3cf32 100644 (file)
@@ -7,22 +7,17 @@
 #ifndef INCLUDE_patch_parse_h__
 #define INCLUDE_patch_parse_h__
 
+#include "common.h"
+
+#include "parse.h"
+#include "patch.h"
+
 typedef struct {
        git_refcount rc;
 
-       /* Original content buffer */
-       const char *content;
-       size_t content_len;
-
        git_patch_options opts;
 
-       /* 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 parse_ctx;
 } git_patch_parse_ctx;
 
 extern git_patch_parse_ctx *git_patch_parse_ctx_init(
index 5fc7a055bfe42bf5590c29f129e05ff747db4a49..ea0a3c3f685ff9e7cc3fba7b982d3f9507222fe5 100644 (file)
@@ -4,8 +4,9 @@
  * 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 "path.h"
+
 #include "posix.h"
 #include "repository.h"
 #ifdef GIT_WIN32
index fb45a6534bfd02b8b1292c957e5898e706ce28b6..aa24bcd2fec59c66812af28bfa02d7677c31f04e 100644 (file)
@@ -8,6 +8,7 @@
 #define INCLUDE_path_h__
 
 #include "common.h"
+
 #include "posix.h"
 #include "buffer.h"
 #include "vector.h"
@@ -104,6 +105,12 @@ GIT_INLINE(int) git_path_is_dot_or_dotdotW(const wchar_t *name)
                                (name[1] == L'.' && name[2] == L'\0')));
 }
 
+#define git_path_is_absolute(p) \
+       (git__isalpha((p)[0]) && (p)[1] == ':' && ((p)[2] == '\\' || (p)[2] == '/'))
+
+#define git_path_is_dirsep(p) \
+       ((p) == '/' || (p) == '\\')
+
 /**
  * Convert backslashes in path to forward slashes.
  */
@@ -118,6 +125,13 @@ GIT_INLINE(void) git_path_mkposix(char *path)
 }
 #else
 #      define git_path_mkposix(p) /* blank */
+
+#define git_path_is_absolute(p) \
+       ((p)[0] == '/')
+
+#define git_path_is_dirsep(p) \
+       ((p) == '/')
+
 #endif
 
 /**
index 00dba4f6b4ae2230297739db11a63551a0dc1c63..998b6fb36d23b814d10f8ec12697870804d34a9c 100644 (file)
@@ -5,9 +5,10 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
+#include "pathspec.h"
+
 #include "git2/pathspec.h"
 #include "git2/diff.h"
-#include "pathspec.h"
 #include "buf_text.h"
 #include "attr_file.h"
 #include "iterator.h"
index 40cd21c3f5ccca1c6b88b4e2558bd03eb1b6a1e8..c4d1a83d3b626e401a1915ce6b548cf3a89fe216 100644 (file)
@@ -8,7 +8,8 @@
 #define INCLUDE_pathspec_h__
 
 #include "common.h"
-#include <git2/pathspec.h>
+
+#include "git2/pathspec.h"
 #include "buffer.h"
 #include "vector.h"
 #include "pool.h"
index b4fc50fcadda750d0c21b910f6a00db3f82d8439..c0efe9c9d4f7396a58ed160d4e361c9087a1a284 100644 (file)
@@ -1,4 +1,12 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * 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>
index f61f16944a4071d14c8b4d25e2939df2053a1e41..92ddf994aeaf03762402985eee96486a92ca147e 100644 (file)
@@ -8,6 +8,7 @@
 #define INCLUDE_pool_h__
 
 #include "common.h"
+
 #include "vector.h"
 
 typedef struct git_pool_page git_pool_page;
index 94deb6ab09182edf1ed2d549239cdafba358e1ad..6e7985eceb32d9b54a11a57bbf3d9d924b377cb9 100644 (file)
@@ -4,8 +4,9 @@
  * 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 "posix.h"
+
 #include "path.h"
 #include <stdio.h>
 #include <ctype.h>
@@ -40,7 +41,7 @@ int p_getaddrinfo(
        if (ainfo->ai_servent)
                ainfo->ai_port = ainfo->ai_servent->s_port;
        else
-               ainfo->ai_port = atol(port);
+               ainfo->ai_port = htons(atol(port));
 
        memcpy(&ainfo->ai_addr_in.sin_addr,
                        ainfo->ai_hostent->h_addr_list[0],
index d26371bca50a42ad61f2655900f2afa90c8f6f75..2934f247923825e1be05eae68d1854e60da1c695 100644 (file)
@@ -8,6 +8,7 @@
 #define INCLUDE_posix_h__
 
 #include "common.h"
+
 #include <fcntl.h>
 #include <time.h>
 #include "fnmatch.h"
@@ -59,6 +60,9 @@
 #ifndef O_CLOEXEC
 #define O_CLOEXEC 0
 #endif
+#ifndef SOCK_CLOEXEC
+#define SOCK_CLOEXEC 0
+#endif
 
 /* access() mode parameter #defines    */
 #ifndef F_OK
index 9341d1af301dc09cafb9f8a34051919cedb18995..3820e999ca78b2281a2c39547af56571ceb8e6cb 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include "pqueue.h"
+
 #include "util.h"
 
 #define PQUEUE_LCHILD_OF(I) (((I)<<1)+1)
index 76b14919e19f222c7a3dbdbfb847373b9d226e8b..c0a6cd49e4788a3239468cb596cd8b13f052dc98 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_pqueue_h__
 #define INCLUDE_pqueue_h__
 
+#include "common.h"
+
 #include "vector.h"
 
 typedef git_vector git_pqueue;
index f53ac11510eeabd92aac94d60fe0af09839f0f5e..9bc2c7fb1232fa453bf120e289e7fa94292907ab 100644 (file)
@@ -5,7 +5,8 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
+#include "proxy.h"
+
 #include "git2/proxy.h"
 
 int git_proxy_init_options(git_proxy_options *opts, unsigned int version)
@@ -30,3 +31,9 @@ int git_proxy_options_dup(git_proxy_options *tgt, const git_proxy_options *src)
 
        return 0;
 }
+
+void git_proxy_options_clear(git_proxy_options *opts)
+{
+       git__free((char *) opts->url);
+       opts->url = NULL;
+}
index bf93827374410db010effe205a8f6f646c79f51a..f8b5c4b50f9d0db8c86eadbcd3fb89ca36825693 100644 (file)
@@ -7,8 +7,11 @@
 #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_clear(git_proxy_options *opts);
 
-#endif
\ No newline at end of file
+#endif
index 09c234034c084c7792c8335ab5fbbe717422027c..85b683e620cccab9178aebbcee066aedf74b052c 100644 (file)
@@ -5,14 +5,14 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
+#include "push.h"
+
 #include "git2.h"
 
-#include "common.h"
 #include "pack.h"
 #include "pack-objects.h"
 #include "remote.h"
 #include "vector.h"
-#include "push.h"
 #include "tree.h"
 
 static int push_spec_rref_cmp(const void *a, const void *b)
@@ -178,6 +178,9 @@ int git_push_update_tips(git_push *push, const git_remote_callbacks *callbacks)
                if (!fetch_spec)
                        continue;
 
+               /* Clear the buffer which can be dirty from previous iteration */
+               git_buf_clear(&remote_ref_name);
+
                if ((error = git_refspec_transform(&remote_ref_name, fetch_spec, status->ref)) < 0)
                        goto on_error;
 
@@ -260,12 +263,11 @@ static int enqueue_tag(git_object **out, git_push *push, git_oid *id)
        return error;
 }
 
-static int revwalk(git_vector *commits, git_push *push)
+static int queue_objects(git_push *push)
 {
        git_remote_head *head;
        push_spec *spec;
        git_revwalk *rw;
-       git_oid oid;
        unsigned int i;
        int error = -1;
 
@@ -350,176 +352,10 @@ static int revwalk(git_vector *commits, git_push *push)
                git_revwalk_hide(rw, &head->oid);
        }
 
-       while ((error = git_revwalk_next(&oid, rw)) == 0) {
-               git_oid *o = git__malloc(GIT_OID_RAWSZ);
-               if (!o) {
-                       error = -1;
-                       goto on_error;
-               }
-               git_oid_cpy(o, &oid);
-               if ((error = git_vector_insert(commits, o)) < 0)
-                       goto on_error;
-       }
+       error = git_packbuilder_insert_walk(push->pb, rw);
 
 on_error:
        git_revwalk_free(rw);
-       return error == GIT_ITEROVER ? 0 : error;
-}
-
-static int enqueue_object(
-       const git_tree_entry *entry,
-       git_packbuilder *pb)
-{
-       switch (git_tree_entry_type(entry)) {
-               case GIT_OBJ_COMMIT:
-                       return 0;
-               case GIT_OBJ_TREE:
-                       return git_packbuilder_insert_tree(pb, entry->oid);
-               default:
-                       return git_packbuilder_insert(pb, entry->oid, entry->filename);
-       }
-}
-
-static int queue_differences(
-       git_tree *base,
-       git_tree *delta,
-       git_packbuilder *pb)
-{
-       git_tree *b_child = NULL, *d_child = NULL;
-       size_t b_length = git_tree_entrycount(base);
-       size_t d_length = git_tree_entrycount(delta);
-       size_t i = 0, j = 0;
-       int error;
-
-       while (i < b_length && j < d_length) {
-               const git_tree_entry *b_entry = git_tree_entry_byindex(base, i);
-               const git_tree_entry *d_entry = git_tree_entry_byindex(delta, j);
-               int cmp = 0;
-
-               if (!git_oid__cmp(b_entry->oid, d_entry->oid))
-                       goto loop;
-
-               cmp = strcmp(b_entry->filename, d_entry->filename);
-
-               /* If the entries are both trees and they have the same name but are
-                * different, then we'll recurse after adding the right-hand entry */
-               if (!cmp &&
-                       git_tree_entry__is_tree(b_entry) &&
-                       git_tree_entry__is_tree(d_entry)) {
-                       /* Add the right-hand entry */
-                       if ((error = git_packbuilder_insert(pb, d_entry->oid,
-                               d_entry->filename)) < 0)
-                               goto on_error;
-
-                       /* Acquire the subtrees and recurse */
-                       if ((error = git_tree_lookup(&b_child,
-                                       git_tree_owner(base), b_entry->oid)) < 0 ||
-                               (error = git_tree_lookup(&d_child,
-                                       git_tree_owner(delta), d_entry->oid)) < 0 ||
-                               (error = queue_differences(b_child, d_child, pb)) < 0)
-                               goto on_error;
-
-                       git_tree_free(b_child); b_child = NULL;
-                       git_tree_free(d_child); d_child = NULL;
-               }
-               /* If the object is new or different in the right-hand tree,
-                * then enumerate it */
-               else if (cmp >= 0 &&
-                       (error = enqueue_object(d_entry, pb)) < 0)
-                       goto on_error;
-
-       loop:
-               if (cmp <= 0) i++;
-               if (cmp >= 0) j++;
-       }
-
-       /* Drain the right-hand tree of entries */
-       for (; j < d_length; j++)
-               if ((error = enqueue_object(git_tree_entry_byindex(delta, j), pb)) < 0)
-                       goto on_error;
-
-       error = 0;
-
-on_error:
-       if (b_child)
-               git_tree_free(b_child);
-
-       if (d_child)
-               git_tree_free(d_child);
-
-       return error;
-}
-
-static int queue_objects(git_push *push)
-{
-       git_vector commits = GIT_VECTOR_INIT;
-       git_oid *oid;
-       size_t i;
-       unsigned j;
-       int error;
-
-       if ((error = revwalk(&commits, push)) < 0)
-               goto on_error;
-
-       git_vector_foreach(&commits, i, oid) {
-               git_commit *parent = NULL, *commit;
-               git_tree *tree = NULL, *ptree = NULL;
-               size_t parentcount;
-
-               if ((error = git_commit_lookup(&commit, push->repo, oid)) < 0)
-                       goto on_error;
-
-               /* Insert the commit */
-               if ((error = git_packbuilder_insert(push->pb, oid, NULL)) < 0)
-                       goto loop_error;
-
-               parentcount = git_commit_parentcount(commit);
-
-               if (!parentcount) {
-                       if ((error = git_packbuilder_insert_tree(push->pb,
-                               git_commit_tree_id(commit))) < 0)
-                               goto loop_error;
-               } else {
-                       if ((error = git_tree_lookup(&tree, push->repo,
-                                       git_commit_tree_id(commit))) < 0 ||
-                               (error = git_packbuilder_insert(push->pb,
-                                       git_commit_tree_id(commit), NULL)) < 0)
-                               goto loop_error;
-
-                       /* For each parent, add the items which are different */
-                       for (j = 0; j < parentcount; j++) {
-                               if ((error = git_commit_parent(&parent, commit, j)) < 0 ||
-                                       (error = git_commit_tree(&ptree, parent)) < 0 ||
-                                       (error = queue_differences(ptree, tree, push->pb)) < 0)
-                                       goto loop_error;
-
-                               git_tree_free(ptree); ptree = NULL;
-                               git_commit_free(parent); parent = NULL;
-                       }
-               }
-
-               error = 0;
-
-       loop_error:
-               if (tree)
-                       git_tree_free(tree);
-
-               if (ptree)
-                       git_tree_free(ptree);
-
-               if (parent)
-                       git_commit_free(parent);
-
-               git_commit_free(commit);
-
-               if (error < 0)
-                       goto on_error;
-       }
-
-       error = 0;
-
-on_error:
-       git_vector_free_deep(&commits);
        return error;
 }
 
index e32ad2f4dad79830c8b6add7ad1174f8d9a5ef7f..31ac436090c8f2fb552e39f718b428f7fa695650 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_push_h__
 #define INCLUDE_push_h__
 
+#include "common.h"
+
 #include "git2.h"
 #include "refspec.h"
 
index f528031b35c6fd129f4f0be99cb16f18e9ad2a48..3be7512544aa0c62713b7de6dc4c2a70b88998c1 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include "common.h"
+
 #include "buffer.h"
 #include "repository.h"
 #include "posix.h"
index 1ee0efb31a88df7552b43d00a1824aa60b3d4c4c..c162a153f3b0940b2ca967b4c8b3beb3933f8ec4 100644 (file)
@@ -5,8 +5,7 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
-#include "posix.h"
+#include "refdb.h"
 
 #include "git2/object.h"
 #include "git2/refs.h"
@@ -14,9 +13,9 @@
 #include "git2/sys/refdb_backend.h"
 
 #include "hash.h"
-#include "refdb.h"
 #include "refs.h"
 #include "reflog.h"
+#include "posix.h"
 
 int git_refdb_new(git_refdb **out, git_repository *repo)
 {
index 4ee3b8065bffdb2bf05fa3a8a88903138e908403..2d4ec753a0500b55cc65e5c9d7584de1e13dc368 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_refdb_h__
 #define INCLUDE_refdb_h__
 
+#include "common.h"
+
 #include "git2/refdb.h"
 #include "repository.h"
 
index eb135dc01e92149b3213479d30fd24c65b1a4443..140879d2335ed6ad8b96fd9c40db8dfa8edea09b 100644 (file)
@@ -5,6 +5,8 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
+#include "refdb_fs.h"
+
 #include "refs.h"
 #include "hash.h"
 #include "repository.h"
@@ -13,7 +15,6 @@
 #include "pack.h"
 #include "reflog.h"
 #include "refdb.h"
-#include "refdb_fs.h"
 #include "iterator.h"
 #include "sortedcache.h"
 #include "signature.h"
@@ -2034,6 +2035,7 @@ int git_refdb_backend_fs(
        if ((!git_repository__cvar(&t, backend->repo, GIT_CVAR_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;
index 79e296833066f0a6641eb254bac25f14fc2b15d8..0c84814df881b18bfd06f499c0a8d7a21ed8683f 100644 (file)
@@ -7,6 +7,10 @@
 #ifndef INCLUDE_refdb_fs_h__
 #define INCLUDE_refdb_fs_h__
 
+#include "common.h"
+
+#include "strmap.h"
+
 typedef struct {
        git_strmap *packfile;
        time_t packfile_time;
index 98ef1b669ca440e6948cb80ed98915eabd2fa0f9..938999218ddd96bea7c24fdc65b9cc5bee381e22 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include "reflog.h"
+
 #include "repository.h"
 #include "filebuf.h"
 #include "signature.h"
index 2d31ae47d45d0c1a52a50b3c21d831a771780ddf..8c3895952801b0748687e0b9f4a92389b7e450de 100644 (file)
@@ -8,6 +8,7 @@
 #define INCLUDE_reflog_h__
 
 #include "common.h"
+
 #include "git2/reflog.h"
 #include "vector.h"
 
@@ -37,4 +38,4 @@ GIT_INLINE(size_t) reflog_inverse_index(size_t idx, size_t total)
        return (total - 1) - idx;
 }
 
-#endif /* INCLUDE_reflog_h__ */
+#endif
index f7120d9ee13da0d5f31eb009d1458e0e677923d1..550963e56f98f50ef41c17ff7134e404891aedbf 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include "refs.h"
+
 #include "hash.h"
 #include "repository.h"
 #include "fileops.h"
@@ -1359,7 +1360,13 @@ int git_reference_peel(
                        return peel_error(error, ref, "Cannot resolve reference");
        }
 
-       if (!git_oid_iszero(&resolved->peel)) {
+       /*
+        * 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_OBJ_TAG && !git_oid_iszero(&resolved->peel)) {
                error = git_object_lookup(&target,
                        git_reference_owner(ref), &resolved->peel, GIT_OBJ_ANY);
        } else {
index 0c90db3aff1910ae1b4fccddd2dafd4ab71c3e7f..5149c75477e37c07535cc40475711ab067f3f1c0 100644 (file)
@@ -8,6 +8,7 @@
 #define INCLUDE_refs_h__
 
 #include "common.h"
+
 #include "git2/oid.h"
 #include "git2/refs.h"
 #include "git2/refdb.h"
@@ -29,7 +30,7 @@ extern bool git_reference__enable_symbolic_ref_target_validation;
 
 #define GIT_SYMREF "ref: "
 #define GIT_PACKEDREFS_FILE "packed-refs"
-#define GIT_PACKEDREFS_HEADER "# pack-refs with: peeled fully-peeled "
+#define GIT_PACKEDREFS_HEADER "# pack-refs with: peeled fully-peeled sorted "
 #define GIT_PACKEDREFS_FILE_MODE 0666
 
 #define GIT_HEAD_FILE "HEAD"
index d200e5609015c4e122880034e9877f3efc171c30..01a77c97facde0f3f9faf3eb79aee2639e4c0ffd 100644 (file)
@@ -5,10 +5,10 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
+#include "refspec.h"
+
 #include "git2/errors.h"
 
-#include "common.h"
-#include "refspec.h"
 #include "util.h"
 #include "posix.h"
 #include "refs.h"
index 9a87c97a5314bdaae3e09abd53e081cf35b61713..fd2d8b312204cf992ee22473bffc1cce40e97dbe 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_refspec_h__
 #define INCLUDE_refspec_h__
 
+#include "common.h"
+
 #include "git2/refspec.h"
 #include "buffer.h"
 #include "vector.h"
index bd8b3cfbc0123da10c58ed72749b6763480c60b0..4d675af822d103240ed38e8ff722e1c29e933968 100644 (file)
@@ -5,15 +5,15 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
+#include "remote.h"
+
 #include "git2/config.h"
 #include "git2/types.h"
 #include "git2/oid.h"
 #include "git2/net.h"
 
-#include "common.h"
 #include "config.h"
 #include "repository.h"
-#include "remote.h"
 #include "fetch.h"
 #include "refs.h"
 #include "refspec.h"
@@ -197,10 +197,10 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n
        git_buf var = GIT_BUF_INIT;
        int error = -1;
 
-       /* name is optional */
-       assert(out && repo && url);
+       /* repo, name, and fetch are optional */
+       assert(out && url);
 
-       if ((error = git_repository_config_snapshot(&config_ro, repo)) < 0)
+       if (repo && (error = git_repository_config_snapshot(&config_ro, repo)) < 0)
                return error;
 
        remote = git__calloc(1, sizeof(git_remote));
@@ -212,7 +212,11 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n
                (error = canonicalize_url(&canonical_url, url)) < 0)
                goto on_error;
 
-       remote->url = apply_insteadof(config_ro, canonical_url.ptr, GIT_DIRECTION_FETCH);
+       if (repo) {
+               remote->url = apply_insteadof(config_ro, canonical_url.ptr, GIT_DIRECTION_FETCH);
+       } else {
+               remote->url = git__strdup(canonical_url.ptr);
+       }
        GITERR_CHECK_ALLOC(remote->url);
 
        if (name != NULL) {
@@ -222,8 +226,9 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n
                if ((error = git_buf_printf(&var, CONFIG_URL_FMT, name)) < 0)
                        goto on_error;
 
-               if ((error = git_repository_config__weakptr(&config_rw, repo)) < 0 ||
-                       (error = git_config_set_string(config_rw, var.ptr, canonical_url.ptr)) < 0)
+               if (repo &&
+                       ((error = git_repository_config__weakptr(&config_rw, repo)) < 0 ||
+                       (error = git_config_set_string(config_rw, var.ptr, canonical_url.ptr)) < 0))
                        goto on_error;
        }
 
@@ -235,7 +240,7 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n
                if (name && (error = write_add_refspec(repo, name, fetch, true)) < 0)
                        goto on_error;
 
-               if ((error = lookup_remote_prune_config(remote, config_ro, name)) < 0)
+               if (repo && (error = lookup_remote_prune_config(remote, config_ro, name)) < 0)
                        goto on_error;
 
                /* Move the data over to where the matching functions can find them */
@@ -330,6 +335,11 @@ int git_remote_create_anonymous(git_remote **out, git_repository *repo, const ch
        return create_internal(out, repo, NULL, url, NULL);
 }
 
+int git_remote_create_detached(git_remote **out, const char *url)
+{
+       return create_internal(out, NULL, NULL, url, NULL);
+}
+
 int git_remote_dup(git_remote **dest, git_remote *source)
 {
        size_t i;
@@ -674,7 +684,9 @@ int git_remote_connect(git_remote *remote, git_direction direction, const git_re
        url = git_remote__urlfordirection(remote, direction);
        if (url == NULL) {
                giterr_set(GITERR_INVALID,
-                       "Malformed remote '%s' - missing URL", remote->name);
+                       "Malformed remote '%s' - missing %s URL",
+                       remote->name ? remote->name : "(anonymous)",
+                       direction == GIT_DIRECTION_FETCH ? "fetch" : "push");
                return -1;
        }
 
@@ -859,6 +871,11 @@ int git_remote_download(git_remote *remote, const git_strarray *refspecs, const
 
        assert(remote);
 
+       if (!remote->repo) {
+               giterr_set(GITERR_INVALID, "cannot download detached remote");
+               return -1;
+       }
+
        if (opts) {
                GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
                cbs = &opts->callbacks;
@@ -1524,6 +1541,20 @@ cleanup:
        return error;
 }
 
+static int truncate_fetch_head(const char *gitdir)
+{
+       git_buf path = GIT_BUF_INIT;
+       int error;
+
+       if ((error = git_buf_joinpath(&path, gitdir, GIT_FETCH_HEAD_FILE)) < 0)
+               return error;
+
+       error = git_futils_truncate(path.ptr, GIT_REFS_FILE_MODE);
+       git_buf_free(&path);
+
+       return error;
+}
+
 int git_remote_update_tips(
                git_remote *remote,
                const git_remote_callbacks *callbacks,
@@ -1554,6 +1585,9 @@ int git_remote_update_tips(
        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;
@@ -2344,6 +2378,11 @@ int git_remote_upload(git_remote *remote, const git_strarray *refspecs, const gi
 
        assert(remote);
 
+       if (!remote->repo) {
+               giterr_set(GITERR_INVALID, "cannot download detached remote");
+               return -1;
+       }
+
        if (opts) {
                cbs = &opts->callbacks;
                custom_headers = &opts->custom_headers;
@@ -2403,6 +2442,13 @@ int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_
        const git_strarray *custom_headers = NULL;
        const git_proxy_options *proxy = NULL;
 
+       assert(remote);
+
+       if (!remote->repo) {
+               giterr_set(GITERR_INVALID, "cannot download detached remote");
+               return -1;
+       }
+
        if (opts) {
                GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
                cbs = &opts->callbacks;
index e696997f499dfdc419dcfa71e9ce1f8548b4176a..a94481f2559af54385a1b824c8967e04eb759438 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_remote_h__
 #define INCLUDE_remote_h__
 
+#include "common.h"
+
 #include "git2/remote.h"
 #include "git2/transport.h"
 #include "git2/sys/transport.h"
index 7ecb00ed8b51dcd4c4a5c5213a5d0c17f9a05c06..90b778e032b2de7d8fb38b8e1f4f7b1ddc15defc 100644 (file)
@@ -4,14 +4,15 @@
  * 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/refdb.h"
 #include "git2/sys/repository.h"
 
 #include "common.h"
-#include "repository.h"
 #include "commit.h"
 #include "tag.h"
 #include "blob.h"
@@ -23,6 +24,7 @@
 #include "refs.h"
 #include "filter.h"
 #include "odb.h"
+#include "refdb.h"
 #include "remote.h"
 #include "merge.h"
 #include "diff_driver.h"
@@ -944,7 +946,7 @@ static int load_config(
                return error;
 
        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, 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;
@@ -953,25 +955,25 @@ static int load_config(
 
        if (global_config_path != NULL &&
                (error = git_config_add_file_ondisk(
-                       cfg, global_config_path, GIT_CONFIG_LEVEL_GLOBAL, 0)) < 0 &&
+                       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, 0)) < 0 &&
+                       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, 0)) < 0 &&
+                       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, 0)) < 0 &&
+                       cfg, programdata_path, GIT_CONFIG_LEVEL_PROGRAMDATA, repo, 0)) < 0 &&
                error != GIT_ENOTFOUND)
                goto on_error;
 
@@ -1235,7 +1237,7 @@ static int reserved_names_add8dot3(git_repository *repo, const char *path)
 
        name_len = strlen(name);
 
-       if ((name_len == def_len && memcmp(name, def, def_len) == 0) || 
+       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;
@@ -1473,7 +1475,7 @@ static int repo_local_config(
                giterr_clear();
 
                if (!(error = git_config_add_file_ondisk(
-                               parent, cfg_path, GIT_CONFIG_LEVEL_LOCAL, false)))
+                               parent, cfg_path, GIT_CONFIG_LEVEL_LOCAL, repo, false)))
                        error = git_config_open_level(out, parent, GIT_CONFIG_LEVEL_LOCAL);
        }
 
@@ -1784,7 +1786,13 @@ static int repo_init_structure(
                        default_template = true;
                }
 
-               if (tdir) {
+               /*
+                * 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;
@@ -2248,7 +2256,7 @@ int git_repository_is_empty(git_repository *repo)
        return is_empty;
 }
 
-int git_repository_item_path(git_buf *out, git_repository *repo, git_repository_item_t item)
+int git_repository_item_path(git_buf *out, const git_repository *repo, git_repository_item_t item)
 {
        const char *parent;
 
@@ -2288,13 +2296,13 @@ int git_repository_item_path(git_buf *out, git_repository *repo, git_repository_
        return 0;
 }
 
-const char *git_repository_path(git_repository *repo)
+const char *git_repository_path(const git_repository *repo)
 {
        assert(repo);
        return repo->gitdir;
 }
 
-const char *git_repository_workdir(git_repository *repo)
+const char *git_repository_workdir(const git_repository *repo)
 {
        assert(repo);
 
@@ -2304,7 +2312,7 @@ const char *git_repository_workdir(git_repository *repo)
        return repo->workdir;
 }
 
-const char *git_repository_commondir(git_repository *repo)
+const char *git_repository_commondir(const git_repository *repo)
 {
        assert(repo);
        return repo->commondir;
@@ -2354,13 +2362,13 @@ int git_repository_set_workdir(
        return error;
 }
 
-int git_repository_is_bare(git_repository *repo)
+int git_repository_is_bare(const git_repository *repo)
 {
        assert(repo);
        return repo->is_bare;
 }
 
-int git_repository_is_worktree(git_repository *repo)
+int git_repository_is_worktree(const git_repository *repo)
 {
        assert(repo);
        return repo->is_worktree;
@@ -2762,7 +2770,7 @@ int git_repository__cleanup_files(
                        error = git_futils_rmdir_r(path, NULL,
                                GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_REMOVE_BLOCKERS);
                }
-                       
+
                git_buf_clear(&buf);
        }
 
index 52f9ec2601bcf35d8a9d187f4b8cc2089d22ad35..fd6400cc10e7ab770a1aed052bde8a29eee9feac 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_repository_h__
 #define INCLUDE_repository_h__
 
+#include "common.h"
+
 #include "git2/common.h"
 #include "git2/oid.h"
 #include "git2/odb.h"
index 066b5dbda299dfd5d2e526599d62c151211d06e2..21596812fd6e408c4189d02af596a20284e5ed57 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include "common.h"
+
 #include "commit.h"
 #include "tag.h"
 #include "merge.h"
@@ -20,8 +21,8 @@
 
 int git_reset_default(
        git_repository *repo,
-       git_object *target,
-       git_strarray* pathspecs)
+       const git_object *target,
+       const git_strarray* pathspecs)
 {
        git_object *commit = NULL;
        git_tree *tree = NULL;
@@ -100,7 +101,7 @@ cleanup:
 
 static int reset(
        git_repository *repo,
-       git_object *target,
+       const git_object *target,
        const char *to,
        git_reset_t reset_type,
        const git_checkout_options *checkout_opts)
@@ -182,7 +183,7 @@ cleanup:
 
 int git_reset(
        git_repository *repo,
-       git_object *target,
+       const git_object *target,
        git_reset_t reset_type,
        const git_checkout_options *checkout_opts)
 {
@@ -191,7 +192,7 @@ int git_reset(
 
 int git_reset_from_annotated(
        git_repository *repo,
-       git_annotated_commit *commit,
+       const git_annotated_commit *commit,
        git_reset_t reset_type,
        const git_checkout_options *checkout_opts)
 {
index 747938fb33473a17be4fd6dfdb89053a2b5df76a..54f6d48e4fd450c9d3e630c7d4f28e9cd3d44c97 100644 (file)
@@ -6,6 +6,7 @@
 */
 
 #include "common.h"
+
 #include "repository.h"
 #include "filebuf.h"
 #include "merge.h"
index fd6bd1ea69cb613d1df8a2faa32df2b4885275b0..7cb22f476cf1d4bb5785b21d501fe253eb578a79 100644 (file)
@@ -5,9 +5,10 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
+#include "common.h"
+
 #include <assert.h>
 
-#include "common.h"
 #include "buffer.h"
 #include "tree.h"
 #include "refdb.h"
@@ -769,7 +770,6 @@ int revparse__ext(
                }
 
                case '@':
-               {
                        if (spec[pos+1] == '{') {
                                git_object *temp_object = NULL;
 
@@ -785,10 +785,8 @@ int revparse__ext(
                                if (temp_object != NULL)
                                        base_rev = temp_object;
                                break;
-                       } else {
-                               /* Fall through */
                        }
-               }
+                       /* fall through */
 
                default:
                        if ((error = ensure_left_hand_identifier_is_not_known_yet(base_rev, reference)) < 0)
index 77fa9fd0c2df31f582409abdb256aae334e971ec..b1aa8f91af028eb8c632f68fc9583aca4cf93b00 100644 (file)
@@ -5,12 +5,12 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
+#include "revwalk.h"
+
 #include "commit.h"
 #include "odb.h"
 #include "pool.h"
 
-#include "revwalk.h"
 #include "git2/revparse.h"
 #include "merge.h"
 #include "vector.h"
index 6b363d40f5d345cfa921fd8d804d16bf1b7f0968..578328b72a3e2b7aca463e86221ea035288a4163 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_revwalk_h__
 #define INCLUDE_revwalk_h__
 
+#include "common.h"
+
 #include "git2/revwalk.h"
 #include "oidmap.h"
 #include "commit_list.h"
index 52b861ba0c2c9b6ccd6584fccf53f740c3b78f0c..2a52ffbf633cdea2c9ae9fbb81f8b7aef948da13 100644 (file)
@@ -5,12 +5,13 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
+#include "common.h"
+
 #ifdef GIT_OPENSSL
 # include <openssl/err.h>
 #endif
 
 #include <git2.h>
-#include "common.h"
 #include "sysdir.h"
 #include "cache.h"
 #include "global.h"
@@ -18,6 +19,7 @@
 #include "odb.h"
 #include "refs.h"
 #include "transports/smart.h"
+#include "streams/openssl.h"
 
 void git_libgit2_version(int *major, int *minor, int *rev)
 {
@@ -171,14 +173,10 @@ int git_libgit2_opts(int key, ...)
                {
                        const char *file = va_arg(ap, const char *);
                        const char *path = va_arg(ap, const char *);
-                       if (!SSL_CTX_load_verify_locations(git__ssl_ctx, file, path)) {
-                               giterr_set(GITERR_NET, "SSL error: %s",
-                                       ERR_error_string(ERR_get_error(), NULL));
-                               error = -1;
-                       }
+                       error = git_openssl__set_cert_location(file, path);
                }
 #else
-               giterr_set(GITERR_NET, "cannot set certificate locations: OpenSSL is not enabled");
+               giterr_set(GITERR_SSL, "TLS backend doesn't support certificate locations");
                error = -1;
 #endif
                break;
@@ -211,7 +209,7 @@ int git_libgit2_opts(int key, ...)
                        }
                }
 #else
-               giterr_set(GITERR_NET, "cannot set custom ciphers: OpenSSL is not enabled");
+               giterr_set(GITERR_SSL, "TLS backend doesn't support custom ciphers");
                error = -1;
 #endif
                break;
index ead26de0609f0088a3d51f576182a3ad26d72630..14fcb40e56a8cca35ad5e938664fc59912fce054 100644 (file)
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include <stdio.h>
-
 #include "sha1_lookup.h"
-#include "common.h"
-#include "oid.h"
 
-/*
- * Conventional binary search loop looks like this:
- *
- *     unsigned lo, hi;
- *             do {
- *                             unsigned mi = (lo + hi) / 2;
- *                             int cmp = "entry pointed at by mi" minus "target";
- *                             if (!cmp)
- *                                             return (mi is the wanted one)
- *                             if (cmp > 0)
- *                                             hi = mi; "mi is larger than target"
- *                             else
- *                                             lo = mi+1; "mi is smaller than target"
- *             } while (lo < hi);
- *
- * The invariants are:
- *
- * - When entering the loop, lo points at a slot that is never
- *     above the target (it could be at the target), hi points at a
- *     slot that is guaranteed to be above the target (it can never
- *     be at the target).
- *
- * - We find a point 'mi' between lo and hi (mi could be the same
- *     as lo, but never can be as same as hi), and check if it hits
- *     the target. There are three cases:
- *
- *     - if it is a hit, we are happy.
- *
- *     - if it is strictly higher than the target, we set it to hi,
- *             and repeat the search.
- *
- *     - if it is strictly lower than the target, we update lo to
- *             one slot after it, because we allow lo to be at the target.
- *
- *     If the loop exits, there is no matching entry.
- *
- * When choosing 'mi', we do not have to take the "middle" but
- * anywhere in between lo and hi, as long as lo <= mi < hi is
- * satisfied. When we somehow know that the distance between the
- * target and lo is much shorter than the target and hi, we could
- * pick mi that is much closer to lo than the midway.
- *
- * Now, we can take advantage of the fact that SHA-1 is a good hash
- * function, and as long as there are enough entries in the table, we
- * can expect uniform distribution. An entry that begins with for
- * example "deadbeef..." is much likely to appear much later than in
- * the midway of the table. It can reasonably be expected to be near
- * 87% (222/256) from the top of the table.
- *
- * However, we do not want to pick "mi" too precisely. If the entry at
- * the 87% in the above example turns out to be higher than the target
- * we are looking for, we would end up narrowing the search space down
- * only by 13%, instead of 50% we would get if we did a simple binary
- * search. So we would want to hedge our bets by being less aggressive.
- *
- * The table at "table" holds at least "nr" entries of "elem_size"
- * bytes each. Each entry has the SHA-1 key at "key_offset". The
- * table is sorted by the SHA-1 key of the entries. The caller wants
- * to find the entry with "key", and knows that the entry at "lo" is
- * not higher than the entry it is looking for, and that the entry at
- * "hi" is higher than the entry it is looking for.
- */
-int sha1_entry_pos(const void *table,
-                       size_t elem_size,
-                       size_t key_offset,
-                       unsigned lo, unsigned hi, unsigned nr,
-                       const unsigned char *key)
-{
-       const unsigned char *base = (const unsigned char*)table;
-       const unsigned char *hi_key, *lo_key;
-       unsigned ofs_0;
-
-       if (!nr || lo >= hi)
-               return -1;
-
-       if (nr == hi)
-               hi_key = NULL;
-       else
-               hi_key = base + elem_size * hi + key_offset;
-       lo_key = base + elem_size * lo + key_offset;
-
-       ofs_0 = 0;
-       do {
-               int cmp;
-               unsigned ofs, mi, range;
-               unsigned lov, hiv, kyv;
-               const unsigned char *mi_key;
-
-               range = hi - lo;
-               if (hi_key) {
-                       for (ofs = ofs_0; ofs < 20; ofs++)
-                               if (lo_key[ofs] != hi_key[ofs])
-                                       break;
-                       ofs_0 = ofs;
-                       /*
-                        * byte 0 thru (ofs-1) are the same between
-                        * lo and hi; ofs is the first byte that is
-                        * different.
-                        *
-                        * If ofs==20, then no bytes are different,
-                        * meaning we have entries with duplicate
-                        * keys. We know that we are in a solid run
-                        * of this entry (because the entries are
-                        * sorted, and our lo and hi are the same,
-                        * there can be nothing but this single key
-                        * in between). So we can stop the search.
-                        * Either one of these entries is it (and
-                        * we do not care which), or we do not have
-                        * it.
-                        *
-                        * Furthermore, we know that one of our
-                        * endpoints must be the edge of the run of
-                        * duplicates. For example, given this
-                        * sequence:
-                        *
-                        *     idx 0 1 2 3 4 5
-                        *     key A C C C C D
-                        *
-                        * If we are searching for "B", we might
-                        * hit the duplicate run at lo=1, hi=3
-                        * (e.g., by first mi=3, then mi=0). But we
-                        * can never have lo > 1, because B < C.
-                        * That is, if our key is less than the
-                        * run, we know that "lo" is the edge, but
-                        * we can say nothing of "hi". Similarly,
-                        * if our key is greater than the run, we
-                        * know that "hi" is the edge, but we can
-                        * say nothing of "lo".
-                        *
-                        * Therefore if we do not find it, we also
-                        * know where it would go if it did exist:
-                        * just on the far side of the edge that we
-                        * know about.
-                        */
-                       if (ofs == 20) {
-                               mi = lo;
-                               mi_key = base + elem_size * mi + key_offset;
-                               cmp = memcmp(mi_key, key, 20);
-                               if (!cmp)
-                                       return mi;
-                               if (cmp < 0)
-                                       return -1 - hi;
-                               else
-                                       return -1 - lo;
-                       }
-
-                       hiv = hi_key[ofs_0];
-                       if (ofs_0 < 19)
-                               hiv = (hiv << 8) | hi_key[ofs_0+1];
-               } else {
-                       hiv = 256;
-                       if (ofs_0 < 19)
-                               hiv <<= 8;
-               }
-               lov = lo_key[ofs_0];
-               kyv = key[ofs_0];
-               if (ofs_0 < 19) {
-                       lov = (lov << 8) | lo_key[ofs_0+1];
-                       kyv = (kyv << 8) | key[ofs_0+1];
-               }
-               assert(lov < hiv);
-
-               if (kyv < lov)
-                       return -1 - lo;
-               if (hiv < kyv)
-                       return -1 - hi;
-
-               /*
-                * Even if we know the target is much closer to 'hi'
-                * than 'lo', if we pick too precisely and overshoot
-                * (e.g. when we know 'mi' is closer to 'hi' than to
-                * 'lo', pick 'mi' that is higher than the target), we
-                * end up narrowing the search space by a smaller
-                * amount (i.e. the distance between 'mi' and 'hi')
-                * than what we would have (i.e. about half of 'lo'
-                * and 'hi'). Hedge our bets to pick 'mi' less
-                * aggressively, i.e. make 'mi' a bit closer to the
-                * middle than we would otherwise pick.
-                */
-               kyv = (kyv * 6 + lov + hiv) / 8;
-               if (lov < hiv - 1) {
-                       if (kyv == lov)
-                               kyv++;
-                       else if (kyv == hiv)
-                               kyv--;
-               }
-               mi = (range - 1) * (kyv - lov) / (hiv - lov) + lo;
-
-#ifdef INDEX_DEBUG_LOOKUP
-               printf("lo %u hi %u rg %u mi %u ", lo, hi, range, mi);
-               printf("ofs %u lov %x, hiv %x, kyv %x\n",
-                               ofs_0, lov, hiv, kyv);
-#endif
-
-               if (!(lo <= mi && mi < hi)) {
-                       giterr_set(GITERR_INVALID, "assertion failure: binary search invariant is false");
-                       return -1;
-               }
+#include <stdio.h>
 
-               mi_key = base + elem_size * mi + key_offset;
-               cmp = memcmp(mi_key + ofs_0, key + ofs_0, 20 - ofs_0);
-               if (!cmp)
-                       return mi;
-               if (cmp > 0) {
-                       hi = mi;
-                       hi_key = mi_key;
-               } else {
-                       lo = mi + 1;
-                       lo_key = mi_key + elem_size;
-               }
-       } while (lo < hi);
-       return -((int)lo)-1;
-}
+#include "oid.h"
 
 int sha1_position(const void *table,
                        size_t stride,
@@ -232,7 +18,7 @@ int sha1_position(const void *table,
 {
        const unsigned char *base = table;
 
-       do {
+       while (lo < hi) {
                unsigned mi = (lo + hi) / 2;
                int cmp = git_oid__hashcmp(base + mi * stride, key);
 
@@ -243,7 +29,7 @@ int sha1_position(const void *table,
                        hi = mi;
                else
                        lo = mi+1;
-       } while (lo < hi);
+       }
 
        return -((int)lo)-1;
 }
index 3799620c7cd931b6f495cfe0e2970988d4f46642..841ea5b22aa13b71e119eab24a8daea483c7e83f 100644 (file)
@@ -7,13 +7,9 @@
 #ifndef INCLUDE_sha1_lookup_h__
 #define INCLUDE_sha1_lookup_h__
 
-#include <stdlib.h>
+#include "common.h"
 
-int sha1_entry_pos(const void *table,
-                       size_t elem_size,
-                       size_t key_offset,
-                       unsigned lo, unsigned hi, unsigned nr,
-                       const unsigned char *key);
+#include <stdlib.h>
 
 int sha1_position(const void *table,
                        size_t stride,
index a56b8a299abbdf7bb76164f50b76d71289f214af..cd6852326b3aac0a317c8d23d8d201ce4edc44db 100644 (file)
@@ -5,8 +5,8 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
 #include "signature.h"
+
 #include "repository.h"
 #include "git2/common.h"
 #include "posix.h"
@@ -90,6 +90,7 @@ int git_signature_new(git_signature **sig_out, const char *name, const char *ema
 
        p->when.time = time;
        p->when.offset = offset;
+       p->when.sign = (offset < 0) ? '-' : '+';
 
        *sig_out = p;
        return 0;
@@ -113,6 +114,7 @@ int git_signature_dup(git_signature **dest, const git_signature *source)
 
        signature->when.time = source->when.time;
        signature->when.offset = source->when.offset;
+       signature->when.sign = source->when.sign;
 
        *dest = signature;
 
@@ -137,6 +139,7 @@ int git_signature__pdup(git_signature **dest, const git_signature *source, git_p
 
        signature->when.time = source->when.time;
        signature->when.offset = source->when.offset;
+       signature->when.sign = source->when.sign;
 
        *dest = signature;
 
@@ -231,6 +234,7 @@ int git_signature__parse(git_signature *sig, const char **buffer_out,
                if (git__strtol64(&sig->when.time, time_start, &time_end, 10) < 0) {
                        git__free(sig->name);
                        git__free(sig->email);
+                       sig->name = sig->email = NULL;
                        return signature_error("invalid Unix timestamp");
                }
 
@@ -256,6 +260,7 @@ int git_signature__parse(git_signature *sig, const char **buffer_out,
                         */
                        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;
                        }
@@ -298,7 +303,7 @@ void git_signature__writebuf(git_buf *buf, const char *header, const git_signatu
        assert(buf && sig);
 
        offset = sig->when.offset;
-       sign = (sig->when.offset < 0) ? '-' : '+';
+       sign = (sig->when.offset < 0 || sig->when.sign == '-') ? '-' : '+';
 
        if (offset < 0)
                offset = -offset;
@@ -319,6 +324,7 @@ bool git_signature__equal(const git_signature *one, const git_signature *two)
                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.offset == two->when.offset &&
+               one->when.sign == two->when.sign;
 }
 
index 75265df52f119d523e333878df29fd6525b678ff..40d7c54f9c229918447009aea49e9ed799dc3269 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_signature_h__
 #define INCLUDE_signature_h__
 
+#include "common.h"
+
 #include "git2/common.h"
 #include "git2/signature.h"
 #include "repository.h"
diff --git a/src/socket_stream.c b/src/socket_stream.c
deleted file mode 100644 (file)
index c0a1684..0000000
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * 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 "posix.h"
-#include "netops.h"
-#include "stream.h"
-#include "socket_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
-
-#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) {
-               giterr_set(GITERR_NET, "%s: %s", str, win32_error);
-               git__free(win32_error);
-       } else {
-               giterr_set(GITERR_NET, str);
-       }
-}
-#else
-static void net_set_error(const char *str)
-{
-       giterr_set(GITERR_NET, "%s: %s", str, strerror(errno));
-}
-#endif
-
-static int close_socket(GIT_SOCKET s)
-{
-       if (s == INVALID_SOCKET)
-               return 0;
-
-#ifdef GIT_WIN32
-       if (SOCKET_ERROR == closesocket(s))
-               return -1;
-
-       if (0 != WSACleanup()) {
-               giterr_set(GITERR_OS, "winsock cleanup failed");
-               return -1;
-       }
-
-       return 0;
-#else
-       return close(s);
-#endif
-
-}
-
-int socket_connect(git_stream *stream)
-{
-       struct addrinfo *info = NULL, *p;
-       struct addrinfo hints;
-       git_socket_stream *st = (git_socket_stream *) stream;
-       GIT_SOCKET s = INVALID_SOCKET;
-       int ret;
-
-#ifdef GIT_WIN32
-       /* on win32, the WSA context needs to be initialized
-        * before any socket calls can be performed */
-       WSADATA wsd;
-
-       if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) {
-               giterr_set(GITERR_OS, "winsock init failed");
-               return -1;
-       }
-
-       if (LOBYTE(wsd.wVersion) != 2 || HIBYTE(wsd.wVersion) != 2) {
-               WSACleanup();
-               giterr_set(GITERR_OS, "winsock init failed");
-               return -1;
-       }
-#endif
-
-       memset(&hints, 0x0, sizeof(struct addrinfo));
-       hints.ai_socktype = SOCK_STREAM;
-       hints.ai_family = AF_UNSPEC;
-
-       if ((ret = p_getaddrinfo(st->host, st->port, &hints, &info)) != 0) {
-               giterr_set(GITERR_NET,
-                          "failed to resolve address for %s: %s", st->host, p_gai_strerror(ret));
-               return -1;
-       }
-
-       for (p = info; p != NULL; p = p->ai_next) {
-               s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
-
-               if (s == INVALID_SOCKET)
-                       continue;
-
-               if (connect(s, p->ai_addr, (socklen_t)p->ai_addrlen) == 0)
-                       break;
-
-               /* If we can't connect, try the next one */
-               close_socket(s);
-               s = INVALID_SOCKET;
-       }
-
-       /* Oops, we couldn't connect to any address */
-       if (s == INVALID_SOCKET && p == NULL) {
-               giterr_set(GITERR_OS, "failed to connect to %s", st->host);
-               p_freeaddrinfo(info);
-               return -1;
-       }
-
-       st->s = s;
-       p_freeaddrinfo(info);
-       return 0;
-}
-
-ssize_t socket_write(git_stream *stream, const char *data, size_t len, int flags)
-{
-       ssize_t ret;
-       size_t off = 0;
-       git_socket_stream *st = (git_socket_stream *) stream;
-
-       while (off < len) {
-               errno = 0;
-               ret = p_send(st->s, data + off, len - off, flags);
-               if (ret < 0) {
-                       net_set_error("Error sending data");
-                       return -1;
-               }
-
-               off += ret;
-       }
-
-       return off;
-}
-
-ssize_t socket_read(git_stream *stream, void *data, size_t len)
-{
-       ssize_t ret;
-       git_socket_stream *st = (git_socket_stream *) stream;
-
-       if ((ret = p_recv(st->s, data, len, 0)) < 0)
-               net_set_error("Error receiving socket data");
-
-       return ret;
-}
-
-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;
-}
-
-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);
-}
-
-int git_socket_stream_new(git_stream **out, const char *host, const char *port)
-{
-       git_socket_stream *st;
-
-       assert(out && host);
-
-       st = git__calloc(1, sizeof(git_socket_stream));
-       GITERR_CHECK_ALLOC(st);
-
-       st->host = git__strdup(host);
-       GITERR_CHECK_ALLOC(st->host);
-
-       if (port) {
-               st->port = git__strdup(port);
-               GITERR_CHECK_ALLOC(st->port);
-       }
-
-       st->parent.version = GIT_STREAM_VERSION;
-       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;
-}
diff --git a/src/socket_stream.h b/src/socket_stream.h
deleted file mode 100644 (file)
index 8e9949f..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This 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_socket_stream_h__
-#define INCLUDE_socket_stream_h__
-
-#include "netops.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);
-
-#endif
index cc322d4784ffa8b9da73fdf09265961681609114..76672d9f702079ecb007f6016edd3392db44d9b1 100644 (file)
@@ -1,3 +1,10 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * 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(
index 4cacad62b8af0c4b251cc53049375dfed546427f..a53ff48a3d4502c2ae28d284a290627aafc2eeb7 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_sorted_cache_h__
 #define INCLUDE_sorted_cache_h__
 
+#include "common.h"
+
 #include "util.h"
 #include "fileops.h"
 #include "vector.h"
index d13220cdda8b8da299dee8c398036a1b5ed37a73..8a8c57a83c0f186ec82e96312206b16950c71e55 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include "common.h"
+
 #include "repository.h"
 #include "commit.h"
 #include "message.h"
index 6752b5625ede540e4bae27045d906e57088774b3..f547bd46683c5b1c6ecd0a0296bd221b65e74039 100644 (file)
@@ -5,13 +5,13 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
+#include "status.h"
+
 #include "git2.h"
 #include "fileops.h"
 #include "hash.h"
 #include "vector.h"
 #include "tree.h"
-#include "status.h"
 #include "git2/status.h"
 #include "repository.h"
 #include "ignore.h"
@@ -280,12 +280,16 @@ int git_status_list_new(
        if ((error = git_repository__ensure_not_bare(repo, "status")) < 0 ||
                (error = git_repository_index(&index, repo)) < 0)
                return error;
-
-       /* 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;
-               giterr_clear();
+       
+       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;
+                       giterr_clear();
+               }
        }
 
        /* refresh index from disk unless prevented */
@@ -377,7 +381,8 @@ done:
 
        *out = status;
 
-       git_tree_free(head);
+       if (opts == NULL || opts->baseline != head)
+               git_tree_free(head);
        git_index_free(index);
 
        return error;
index 33008b89ca4a1d0d710d4d6cf58a5810d1f3c311..907479a2222737672d5dcb3d52163940b2f27a05 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_status_h__
 #define INCLUDE_status_h__
 
+#include "common.h"
+
 #include "diff.h"
 #include "git2/status.h"
 #include "git2/diff.h"
diff --git a/src/stransport_stream.c b/src/stransport_stream.c
deleted file mode 100644 (file)
index 50ed945..0000000
+++ /dev/null
@@ -1,294 +0,0 @@
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifdef GIT_SECURE_TRANSPORT
-
-#include <CoreFoundation/CoreFoundation.h>
-#include <Security/SecureTransport.h>
-#include <Security/SecCertificate.h>
-
-#include "git2/transport.h"
-
-#include "socket_stream.h"
-#include "curl_stream.h"
-
-static int stransport_error(OSStatus ret)
-{
-       CFStringRef message;
-
-       if (ret == noErr || ret == errSSLClosedGraceful) {
-               giterr_clear();
-               return 0;
-       }
-
-#if !TARGET_OS_IPHONE
-       message = SecCopyErrorMessageString(ret, NULL);
-       GITERR_CHECK_ALLOC(message);
-
-       giterr_set(GITERR_NET, "SecureTransport error: %s", CFStringGetCStringPtr(message, kCFStringEncodingUTF8));
-       CFRelease(message);
-#else
-    giterr_set(GITERR_NET, "SecureTransport error: OSStatus %d", (unsigned int)ret);
-    GIT_UNUSED(message);
-#endif
-
-       return -1;
-}
-
-typedef struct {
-       git_stream parent;
-       git_stream *io;
-       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 ((error = git_stream_connect(st->io)) < 0)
-               return error;
-
-       ret = SSLHandshake(st->ctx);
-       if (ret != errSSLServerAuthCompleted) {
-               giterr_set(GITERR_SSL, "unexpected return value from ssl handshake %d", 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) {
-               giterr_set(GITERR_SSL, "internal security trust error");
-               return -1;
-       }
-
-       if (sec_res == kSecTrustResultDeny || sec_res == kSecTrustResultRecoverableTrustFailure ||
-           sec_res == kSecTrustResultFatalTrustFailure)
-               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) {
-               giterr_set(GITERR_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)
-{
-       git_stream *io = (git_stream *) conn;
-
-       if (git_stream_write(io, data, *len, 0) < 0) {
-               return -36; /* "ioErr" from MacErrors.h which is not available on iOS */
-       }
-
-       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 = len;
-       if ((ret = SSLWrite(st->ctx, data, data_len, &processed)) != noErr)
-               return stransport_error(ret);
-
-       return 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)
-{
-       git_stream *io = (git_stream *) conn;
-       OSStatus error = noErr;
-       size_t off = 0;
-       ssize_t ret;
-
-       do {
-               ret = git_stream_read(io, data + off, *len - off);
-               if (ret < 0) {
-                       error = -36; /* "ioErr" from MacErrors.h which is not available on iOS */
-                       break;
-               }
-               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)
-               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 git_stream_close(st->io);
-}
-
-static void stransport_free(git_stream *stream)
-{
-       stransport_stream *st = (stransport_stream *) stream;
-
-       git_stream_free(st->io);
-       CFRelease(st->ctx);
-       if (st->der_data)
-               CFRelease(st->der_data);
-       git__free(st);
-}
-
-int git_stransport_stream_new(git_stream **out, const char *host, const char *port)
-{
-       stransport_stream *st;
-       int error;
-       OSStatus ret;
-
-       assert(out && host);
-
-       st = git__calloc(1, sizeof(stransport_stream));
-       GITERR_CHECK_ALLOC(st);
-
-#ifdef GIT_CURL
-       error = git_curl_stream_new(&st->io, host, port);
-#else
-       error = git_socket_stream_new(&st->io, host, port);
-#endif
-
-       if (error < 0){
-               git__free(st);
-               return error;
-       }
-
-       st->ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType);
-       if (!st->ctx) {
-               giterr_set(GITERR_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->io)) != 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;
-}
-
-#endif
diff --git a/src/stransport_stream.h b/src/stransport_stream.h
deleted file mode 100644 (file)
index 714f902..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This 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_stransport_stream_h__
-#define INCLUDE_stransport_stream_h__
-
-#include "git2/sys/stream.h"
-
-extern int git_stransport_stream_new(git_stream **out, const char *host, const char *port);
-
-#endif
diff --git a/src/streams/curl.c b/src/streams/curl.c
new file mode 100644 (file)
index 0000000..ee13be1
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * 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/curl.h"
+
+#ifdef GIT_CURL
+
+#include <curl/curl.h>
+
+#include "stream.h"
+#include "git2/transport.h"
+#include "buffer.h"
+#include "global.h"
+#include "vector.h"
+#include "proxy.h"
+
+/* This is for backwards compatibility with curl<7.45.0. */
+#ifndef CURLINFO_ACTIVESOCKET
+# define CURLINFO_ACTIVESOCKET CURLINFO_LASTSOCKET
+# define GIT_CURL_BADSOCKET -1
+# define git_activesocket_t long
+#else
+# define GIT_CURL_BADSOCKET CURL_SOCKET_BAD
+# define git_activesocket_t curl_socket_t
+#endif
+
+typedef struct {
+       git_stream parent;
+       CURL *handle;
+       curl_socket_t socket;
+       char curl_error[CURL_ERROR_SIZE + 1];
+       git_cert_x509 cert_info;
+       git_strarray cert_info_strings;
+       git_proxy_options proxy;
+       git_cred *proxy_cred;
+} curl_stream;
+
+int git_curl_stream_global_init(void)
+{
+       if (curl_global_init(CURL_GLOBAL_ALL) != 0) {
+               giterr_set(GITERR_NET, "could not initialize curl");
+               return -1;
+       }
+
+       /* `curl_global_cleanup` is provided by libcurl */
+       git__on_shutdown(curl_global_cleanup);
+       return 0;
+}
+
+static int seterr_curl(curl_stream *s)
+{
+       giterr_set(GITERR_NET, "curl error: %s\n", s->curl_error);
+       return -1;
+}
+
+GIT_INLINE(int) error_no_credentials(void)
+{
+       giterr_set(GITERR_NET, "proxy authentication required, but no callback provided");
+       return GIT_EAUTH;
+}
+
+static int apply_proxy_creds(curl_stream *s)
+{
+       CURLcode res;
+       git_cred_userpass_plaintext *userpass;
+
+       if (!s->proxy_cred)
+               return GIT_ENOTFOUND;
+
+       userpass = (git_cred_userpass_plaintext *) s->proxy_cred;
+       if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYUSERNAME, userpass->username)) != CURLE_OK)
+               return seterr_curl(s);
+       if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYPASSWORD, userpass->password)) != CURLE_OK)
+               return seterr_curl(s);
+
+       return 0;
+}
+
+static int ask_and_apply_proxy_creds(curl_stream *s)
+{
+       int error;
+       git_proxy_options *opts = &s->proxy;
+
+       if (!opts->credentials)
+               return error_no_credentials();
+
+       /* TODO: see if PROXYAUTH_AVAIL helps us here */
+       git_cred_free(s->proxy_cred);
+       s->proxy_cred = NULL;
+       giterr_clear();
+       error = opts->credentials(&s->proxy_cred, opts->url, NULL, GIT_CREDTYPE_USERPASS_PLAINTEXT, opts->payload);
+       if (error == GIT_PASSTHROUGH)
+               return error_no_credentials();
+       if (error < 0) {
+               if (!giterr_last())
+                       giterr_set(GITERR_NET, "proxy authentication was aborted by the user");
+               return error;
+       }
+
+       if (s->proxy_cred->credtype != GIT_CREDTYPE_USERPASS_PLAINTEXT) {
+               giterr_set(GITERR_NET, "credentials callback returned invalid credential type");
+               return -1;
+       }
+
+       return apply_proxy_creds(s);
+}
+
+static int curls_connect(git_stream *stream)
+{
+       curl_stream *s = (curl_stream *) stream;
+       git_activesocket_t sockextr;
+       long connect_last = 0;
+       int failed_cert = 0, error;
+       bool retry_connect;
+       CURLcode res;
+
+       /* Apply any credentials we've already established */
+       error = apply_proxy_creds(s);
+       if (error < 0 && error != GIT_ENOTFOUND)
+               return seterr_curl(s);
+
+       do {
+               retry_connect = 0;
+               res = curl_easy_perform(s->handle);
+
+               curl_easy_getinfo(s->handle, CURLINFO_HTTP_CONNECTCODE, &connect_last);
+
+               /* HTTP 407 Proxy Authentication Required */
+               if (connect_last == 407) {
+                       if ((error = ask_and_apply_proxy_creds(s)) < 0)
+                               return error;
+
+                       retry_connect = true;
+               }
+       } while (retry_connect);
+
+       if (res != CURLE_OK && res != CURLE_PEER_FAILED_VERIFICATION)
+               return seterr_curl(s);
+       if (res == CURLE_PEER_FAILED_VERIFICATION)
+               failed_cert = 1;
+
+       if ((res = curl_easy_getinfo(s->handle, CURLINFO_ACTIVESOCKET, &sockextr)) != CURLE_OK) {
+               return seterr_curl(s);
+       }
+
+       if (sockextr == GIT_CURL_BADSOCKET) {
+               giterr_set(GITERR_NET, "curl socket is no longer valid");
+               return -1;
+       }
+
+       s->socket = sockextr;
+
+       if (s->parent.encrypted && failed_cert)
+               return GIT_ECERTIFICATE;
+
+       return 0;
+}
+
+static int curls_certificate(git_cert **out, git_stream *stream)
+{
+       int error;
+       CURLcode res;
+       struct curl_slist *slist;
+       struct curl_certinfo *certinfo;
+       git_vector strings = GIT_VECTOR_INIT;
+       curl_stream *s = (curl_stream *) stream;
+
+       if ((res = curl_easy_getinfo(s->handle, CURLINFO_CERTINFO, &certinfo)) != CURLE_OK)
+               return seterr_curl(s);
+
+       /* No information is available, can happen with SecureTransport */
+       if (certinfo->num_of_certs == 0) {
+               s->cert_info.parent.cert_type = GIT_CERT_NONE;
+               s->cert_info.data             = NULL;
+               s->cert_info.len              = 0;
+               return 0;
+       }
+
+       if ((error = git_vector_init(&strings, 8, NULL)) < 0)
+               return error;
+
+       for (slist = certinfo->certinfo[0]; slist; slist = slist->next) {
+               char *str = git__strdup(slist->data);
+               GITERR_CHECK_ALLOC(str);
+               git_vector_insert(&strings, str);
+       }
+
+       /* Copy the contents of the vector into a strarray so we can expose them */
+       s->cert_info_strings.strings = (char **) strings.contents;
+       s->cert_info_strings.count   = strings.length;
+
+       s->cert_info.parent.cert_type = GIT_CERT_STRARRAY;
+       s->cert_info.data             = &s->cert_info_strings;
+       s->cert_info.len              = strings.length;
+
+       *out = &s->cert_info.parent;
+
+       return 0;
+}
+
+static int curls_set_proxy(git_stream *stream, const git_proxy_options *proxy_opts)
+{
+       int error;
+       CURLcode res;
+       curl_stream *s = (curl_stream *) stream;
+
+       git_proxy_options_clear(&s->proxy);
+       if ((error = git_proxy_options_dup(&s->proxy, proxy_opts)) < 0)
+               return error;
+
+       if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXY, s->proxy.url)) != CURLE_OK)
+               return seterr_curl(s);
+
+       if ((res = curl_easy_setopt(s->handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY)) != CURLE_OK)
+               return seterr_curl(s);
+
+       return 0;
+}
+
+static int wait_for(curl_socket_t fd, bool reading)
+{
+       int ret;
+       fd_set infd, outfd, errfd;
+
+       FD_ZERO(&infd);
+       FD_ZERO(&outfd);
+       FD_ZERO(&errfd);
+
+       assert(fd >= 0);
+       FD_SET(fd, &errfd);
+       if (reading)
+               FD_SET(fd, &infd);
+       else
+               FD_SET(fd, &outfd);
+
+       if ((ret = select(fd + 1, &infd, &outfd, &errfd, NULL)) < 0) {
+               giterr_set(GITERR_OS, "error in select");
+               return -1;
+       }
+
+       return 0;
+}
+
+static ssize_t curls_write(git_stream *stream, const char *data, size_t len, int flags)
+{
+       int error;
+       size_t off = 0, sent;
+       CURLcode res;
+       curl_stream *s = (curl_stream *) stream;
+
+       GIT_UNUSED(flags);
+
+       do {
+               if ((error = wait_for(s->socket, false)) < 0)
+                       return error;
+
+               res = curl_easy_send(s->handle, data + off, len - off, &sent);
+               if (res == CURLE_OK)
+                       off += sent;
+       } while ((res == CURLE_OK || res == CURLE_AGAIN) && off < len);
+
+       if (res != CURLE_OK)
+               return seterr_curl(s);
+
+       return len;
+}
+
+static ssize_t curls_read(git_stream *stream, void *data, size_t len)
+{
+       int error;
+       size_t read;
+       CURLcode res;
+       curl_stream *s = (curl_stream *) stream;
+
+       do {
+               if ((error = wait_for(s->socket, true)) < 0)
+                       return error;
+
+               res = curl_easy_recv(s->handle, data, len, &read);
+       } while (res == CURLE_AGAIN);
+
+       if (res != CURLE_OK)
+               return seterr_curl(s);
+
+       return read;
+}
+
+static int curls_close(git_stream *stream)
+{
+       curl_stream *s = (curl_stream *) stream;
+
+       if (!s->handle)
+               return 0;
+
+       curl_easy_cleanup(s->handle);
+       s->handle = NULL;
+       s->socket = 0;
+
+       return 0;
+}
+
+static void curls_free(git_stream *stream)
+{
+       curl_stream *s = (curl_stream *) stream;
+
+       curls_close(stream);
+       git_strarray_free(&s->cert_info_strings);
+       git_proxy_options_clear(&s->proxy);
+       git_cred_free(s->proxy_cred);
+       git__free(s);
+}
+
+int git_curl_stream_new(git_stream **out, const char *host, const char *port)
+{
+       curl_stream *st;
+       CURL *handle;
+       int iport = 0, error;
+
+       st = git__calloc(1, sizeof(curl_stream));
+       GITERR_CHECK_ALLOC(st);
+
+       handle = curl_easy_init();
+       if (handle == NULL) {
+               giterr_set(GITERR_NET, "failed to create curl handle");
+               git__free(st);
+               return -1;
+       }
+
+       if ((error = git__strtol32(&iport, port, NULL, 10)) < 0) {
+               git__free(st);
+               return error;
+       }
+
+       curl_easy_setopt(handle, CURLOPT_URL, host);
+       curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, st->curl_error);
+       curl_easy_setopt(handle, CURLOPT_PORT, iport);
+       curl_easy_setopt(handle, CURLOPT_CONNECT_ONLY, 1);
+       curl_easy_setopt(handle, CURLOPT_SSL_VERIFYPEER, 1);
+       curl_easy_setopt(handle, CURLOPT_CERTINFO, 1);
+       curl_easy_setopt(handle, CURLOPT_HTTPPROXYTUNNEL, 1);
+       curl_easy_setopt(handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
+
+       /* curl_easy_setopt(handle, CURLOPT_VERBOSE, 1); */
+
+       st->parent.version = GIT_STREAM_VERSION;
+       st->parent.encrypted = 0; /* we don't encrypt ourselves */
+       st->parent.proxy_support = 1;
+       st->parent.connect = curls_connect;
+       st->parent.certificate = curls_certificate;
+       st->parent.set_proxy = curls_set_proxy;
+       st->parent.read = curls_read;
+       st->parent.write = curls_write;
+       st->parent.close = curls_close;
+       st->parent.free = curls_free;
+       st->handle = handle;
+
+       *out = (git_stream *) st;
+       return 0;
+}
+
+#else
+
+#include "stream.h"
+
+int git_curl_stream_global_init(void)
+{
+       return 0;
+}
+
+int git_curl_stream_new(git_stream **out, const char *host, const char *port)
+{
+       GIT_UNUSED(out);
+       GIT_UNUSED(host);
+       GIT_UNUSED(port);
+
+       giterr_set(GITERR_NET, "curl is not supported in this version");
+       return -1;
+}
+
+
+#endif
diff --git a/src/streams/curl.h b/src/streams/curl.h
new file mode 100644 (file)
index 0000000..511cd89
--- /dev/null
@@ -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_streams_curl_h__
+#define INCLUDE_streams_curl_h__
+
+#include "common.h"
+
+#include "git2/sys/stream.h"
+
+extern int git_curl_stream_global_init(void);
+extern int git_curl_stream_new(git_stream **out, const char *host, const char *port);
+
+#endif
diff --git a/src/streams/openssl.c b/src/streams/openssl.c
new file mode 100644 (file)
index 0000000..9cbb274
--- /dev/null
@@ -0,0 +1,698 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * 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"
+
+#ifdef GIT_OPENSSL
+
+#include <ctype.h>
+
+#include "global.h"
+#include "posix.h"
+#include "stream.h"
+#include "streams/socket.h"
+#include "netops.h"
+#include "git2/transport.h"
+#include "git2/sys/openssl.h"
+
+#ifdef GIT_CURL
+# include "streams/curl.h"
+#endif
+
+#ifndef GIT_WIN32
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+#endif
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/x509v3.h>
+#include <openssl/bio.h>
+
+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"
+
+#if defined(GIT_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L
+
+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) {
+               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);
+}
+
+#endif /* GIT_THREADS && OPENSSL_VERSION_NUMBER < 0x10100000L */
+
+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;
+       }
+}
+
+int git_openssl_stream_global_init(void)
+{
+#ifdef GIT_OPENSSL
+       long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
+       const char *ciphers = git_libgit2__ssl_ciphers();
+
+       /* Older OpenSSL and MacOS OpenSSL doesn't have this */
+#ifdef SSL_OP_NO_COMPRESSION
+       ssl_opts |= SSL_OP_NO_COMPRESSION;
+#endif
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+       SSL_load_error_strings();
+       OpenSSL_add_ssl_algorithms();
+#else
+       OPENSSL_init_ssl(0, NULL);
+#endif
+
+       /*
+        * 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.
+        */
+       git__ssl_ctx = SSL_CTX_new(SSLv23_method());
+       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)) {
+               SSL_CTX_free(git__ssl_ctx);
+               git__ssl_ctx = NULL;
+               return -1;
+       }
+
+       if (!ciphers) {
+               ciphers = GIT_SSL_DEFAULT_CIPHERS;
+       }
+
+       if(!SSL_CTX_set_cipher_list(git__ssl_ctx, ciphers)) {
+               SSL_CTX_free(git__ssl_ctx);
+               git__ssl_ctx = NULL;
+               return -1;
+       }
+
+       if (init_bio_method() < 0) {
+               SSL_CTX_free(git__ssl_ctx);
+               git__ssl_ctx = NULL;
+               return -1;
+       }
+
+#endif
+
+       git__on_shutdown(shutdown_ssl);
+
+       return 0;
+}
+
+#if defined(GIT_THREADS)
+static void threadid_cb(CRYPTO_THREADID *threadid)
+{
+    CRYPTO_THREADID_set_numeric(threadid, git_thread_currentid());
+}
+#endif
+
+int git_openssl_set_locking(void)
+{
+#if defined(GIT_THREADS) && OPENSSL_VERSION_NUMBER < 0x10100000L
+       int num_locks, i;
+
+       CRYPTO_THREADID_set_callback(threadid_cb);
+
+       num_locks = CRYPTO_num_locks();
+       openssl_locks = git__calloc(num_locks, sizeof(git_mutex));
+       GITERR_CHECK_ALLOC(openssl_locks);
+
+       for (i = 0; i < num_locks; i++) {
+               if (git_mutex_init(&openssl_locks[i]) != 0) {
+                       giterr_set(GITERR_SSL, "failed to initialize openssl locks");
+                       return -1;
+               }
+       }
+
+       CRYPTO_set_locking_callback(openssl_locking_function);
+       git__on_shutdown(shutdown_ssl_locking);
+       return 0;
+#elif OPENSSL_VERSION_NUMBER >= 0x10100000L
+       return 0;
+#else
+       giterr_set(GITERR_THREAD, "libgit2 was not built with threads");
+       return -1;
+#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");
+       GITERR_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);
+
+       assert(err != SSL_ERROR_WANT_READ);
+       assert(err != SSL_ERROR_WANT_WRITE);
+
+       switch (err) {
+       case SSL_ERROR_WANT_CONNECT:
+       case SSL_ERROR_WANT_ACCEPT:
+               giterr_set(GITERR_NET, "SSL error: connection failure");
+               break;
+       case SSL_ERROR_WANT_X509_LOOKUP:
+               giterr_set(GITERR_NET, "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));
+                       giterr_set(GITERR_NET, "SSL error: %s", errmsg);
+                       break;
+               } else if (error < 0) {
+                       giterr_set(GITERR_OS, "SSL error: syscall failure");
+                       break;
+               }
+               giterr_set(GITERR_NET, "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));
+               giterr_set(GITERR_NET, "SSL error: %s", errmsg);
+               break;
+       }
+       case SSL_ERROR_NONE:
+       case SSL_ERROR_ZERO_RETURN:
+       default:
+               giterr_set(GITERR_NET, "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 int check_host_name(const char *name, const char *host)
+{
+       if (!strcasecmp(name, host))
+               return 0;
+
+       if (gitno__match_host(name, host) < 0)
+               return -1;
+
+       return 0;
+}
+
+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) {
+               giterr_set(GITERR_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;
+               giterr_set(GITERR_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;
+
+                               if (check_host_name(name, host) < 0)
+                                       matched = 0;
+                               else
+                                       matched = 1;
+                       } 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);
+                       GITERR_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);
+               GITERR_CHECK_ALLOC(peer_cn);
+               if (memchr(peer_cn, '\0', size))
+                       goto cert_fail_name;
+       }
+
+       if (check_host_name((char *)peer_cn, host) < 0)
+               goto cert_fail_name;
+
+       goto cleanup;
+
+cert_fail_name:
+       error = GIT_ECERTIFICATE;
+       giterr_set(GITERR_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;
+       bool connected;
+       char *host;
+       SSL *ssl;
+       git_cert_x509 cert_info;
+} openssl_stream;
+
+int openssl_close(git_stream *stream);
+
+int openssl_connect(git_stream *stream)
+{
+       int ret;
+       BIO *bio;
+       openssl_stream *st = (openssl_stream *) stream;
+
+       if ((ret = git_stream_connect(st->io)) < 0)
+               return ret;
+
+       st->connected = true;
+
+       bio = BIO_new(git_stream_bio_method);
+       GITERR_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);
+
+       return verify_server_cert(st->ssl, st->host);
+}
+
+int openssl_certificate(git_cert **out, git_stream *stream)
+{
+       openssl_stream *st = (openssl_stream *) stream;
+       int len;
+       X509 *cert = SSL_get_peer_certificate(st->ssl);
+       unsigned char *guard, *encoded_cert;
+
+       /* Retrieve the length of the certificate first */
+       len = i2d_X509(cert, NULL);
+       if (len < 0) {
+               giterr_set(GITERR_NET, "failed to retrieve certificate information");
+               return -1;
+       }
+
+       encoded_cert = git__malloc(len);
+       GITERR_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__free(encoded_cert);
+               giterr_set(GITERR_NET, "failed to retrieve certificate information");
+               return -1;
+       }
+
+       st->cert_info.parent.cert_type = GIT_CERT_X509;
+       st->cert_info.data = encoded_cert;
+       st->cert_info.len = len;
+
+       *out = &st->cert_info.parent;
+
+       return 0;
+}
+
+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);
+}
+
+ssize_t openssl_write(git_stream *stream, const char *data, size_t len, int flags)
+{
+       openssl_stream *st = (openssl_stream *) stream;
+       int ret;
+
+       GIT_UNUSED(flags);
+
+       if ((ret = SSL_write(st->ssl, data, len)) <= 0) {
+               return ssl_set_error(st->ssl, ret);
+       }
+
+       return ret;
+}
+
+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;
+}
+
+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 git_stream_close(st->io);
+}
+
+void openssl_free(git_stream *stream)
+{
+       openssl_stream *st = (openssl_stream *) stream;
+
+       SSL_free(st->ssl);
+       git__free(st->host);
+       git__free(st->cert_info.data);
+       git_stream_free(st->io);
+       git__free(st);
+}
+
+int git_openssl_stream_new(git_stream **out, const char *host, const char *port)
+{
+       int error;
+       openssl_stream *st;
+
+       st = git__calloc(1, sizeof(openssl_stream));
+       GITERR_CHECK_ALLOC(st);
+
+       st->io = NULL;
+#ifdef GIT_CURL
+       error = git_curl_stream_new(&st->io, host, port);
+#else
+       error = git_socket_stream_new(&st->io, host, port);
+#endif
+
+       if (error < 0)
+               goto out_err;
+
+       st->ssl = SSL_new(git__ssl_ctx);
+       if (st->ssl == NULL) {
+               giterr_set(GITERR_SSL, "failed to create ssl object");
+               error = -1;
+               goto out_err;
+       }
+
+       st->host = git__strdup(host);
+       GITERR_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;
+
+out_err:
+       git_stream_free(st->io);
+       git__free(st);
+
+       return error;
+}
+
+int git_openssl__set_cert_location(const char *file, const char *path)
+{
+       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));
+               giterr_set(GITERR_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)
+{
+       giterr_set(GITERR_SSL, "libgit2 was not built with OpenSSL support");
+       return -1;
+}
+
+int git_openssl_stream_new(git_stream **out, const char *host, const char *port)
+{
+       GIT_UNUSED(out);
+       GIT_UNUSED(host);
+       GIT_UNUSED(port);
+
+       giterr_set(GITERR_SSL, "openssl is not supported in this version");
+       return -1;
+}
+
+int git_openssl__set_cert_location(const char *file, const char *path)
+{
+       GIT_UNUSED(file);
+       GIT_UNUSED(path);
+
+       giterr_set(GITERR_SSL, "openssl is not supported in this version");
+       return -1;
+}
+
+#endif
diff --git a/src/streams/openssl.h b/src/streams/openssl.h
new file mode 100644 (file)
index 0000000..2bbad7c
--- /dev/null
@@ -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.
+ */
+#ifndef INCLUDE_streams_openssl_h__
+#define INCLUDE_streams_openssl_h__
+
+#include "common.h"
+
+#include "git2/sys/stream.h"
+
+extern int git_openssl_stream_global_init(void);
+
+extern int git_openssl_stream_new(git_stream **out, const char *host, const char *port);
+
+extern int git_openssl__set_cert_location(const char *file, const char *path);
+
+/*
+ * 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.
+ */
+#ifdef GIT_OPENSSL
+# include <openssl/ssl.h>
+# include <openssl/err.h>
+# include <openssl/x509v3.h>
+# include <openssl/bio.h>
+
+
+
+# if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+
+GIT_INLINE(BIO_METHOD*) BIO_meth_new(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;
+}
+
+GIT_INLINE(void) BIO_meth_free(BIO_METHOD *biom)
+{
+       git__free(biom);
+}
+
+GIT_INLINE(int) BIO_meth_set_write(BIO_METHOD *biom, int (*write) (BIO *, const char *, int))
+{
+       biom->bwrite = write;
+       return 1;
+}
+
+GIT_INLINE(int) BIO_meth_set_read(BIO_METHOD *biom, int (*read) (BIO *, char *, int))
+{
+       biom->bread = read;
+       return 1;
+}
+
+GIT_INLINE(int) BIO_meth_set_puts(BIO_METHOD *biom, int (*puts) (BIO *, const char *))
+{
+       biom->bputs = puts;
+       return 1;
+}
+
+GIT_INLINE(int) BIO_meth_set_gets(BIO_METHOD *biom, int (*gets) (BIO *, char *, int))
+
+{
+       biom->bgets = gets;
+       return 1;
+}
+
+GIT_INLINE(int) BIO_meth_set_ctrl(BIO_METHOD *biom, long (*ctrl) (BIO *, int, long, void *))
+{
+       biom->ctrl = ctrl;
+       return 1;
+}
+
+GIT_INLINE(int) BIO_meth_set_create(BIO_METHOD *biom, int (*create) (BIO *))
+{
+       biom->create = create;
+       return 1;
+}
+
+GIT_INLINE(int) BIO_meth_set_destroy(BIO_METHOD *biom, int (*destroy) (BIO *))
+{
+       biom->destroy = destroy;
+       return 1;
+}
+
+GIT_INLINE(int) BIO_get_new_index(void)
+{
+       /* This exists as of 1.1 so before we'd just have 0 */
+       return 0;
+}
+
+GIT_INLINE(void) BIO_set_init(BIO *b, int init)
+{
+       b->init = init;
+}
+
+GIT_INLINE(void) BIO_set_data(BIO *a, void *ptr)
+{
+       a->ptr = ptr;
+}
+
+GIT_INLINE(void*) BIO_get_data(BIO *a)
+{
+       return a->ptr;
+}
+
+GIT_INLINE(const unsigned char *) ASN1_STRING_get0_data(const ASN1_STRING *x)
+{
+       return ASN1_STRING_data((ASN1_STRING *)x);
+}
+
+# endif // OpenSSL < 1.1
+#endif // GIT_OPENSSL
+
+#endif
diff --git a/src/streams/socket.c b/src/streams/socket.c
new file mode 100644 (file)
index 0000000..0c6073b
--- /dev/null
@@ -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 "streams/socket.h"
+
+#include "posix.h"
+#include "netops.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
+
+#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) {
+               giterr_set(GITERR_NET, "%s: %s", str, win32_error);
+               git__free(win32_error);
+       } else {
+               giterr_set(GITERR_NET, str);
+       }
+}
+#else
+static void net_set_error(const char *str)
+{
+       giterr_set(GITERR_NET, "%s: %s", str, strerror(errno));
+}
+#endif
+
+static int close_socket(GIT_SOCKET s)
+{
+       if (s == INVALID_SOCKET)
+               return 0;
+
+#ifdef GIT_WIN32
+       if (SOCKET_ERROR == closesocket(s))
+               return -1;
+
+       if (0 != WSACleanup()) {
+               giterr_set(GITERR_OS, "winsock cleanup failed");
+               return -1;
+       }
+
+       return 0;
+#else
+       return close(s);
+#endif
+
+}
+
+int socket_connect(git_stream *stream)
+{
+       struct addrinfo *info = NULL, *p;
+       struct addrinfo hints;
+       git_socket_stream *st = (git_socket_stream *) stream;
+       GIT_SOCKET s = INVALID_SOCKET;
+       int ret;
+
+#ifdef GIT_WIN32
+       /* on win32, the WSA context needs to be initialized
+        * before any socket calls can be performed */
+       WSADATA wsd;
+
+       if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) {
+               giterr_set(GITERR_OS, "winsock init failed");
+               return -1;
+       }
+
+       if (LOBYTE(wsd.wVersion) != 2 || HIBYTE(wsd.wVersion) != 2) {
+               WSACleanup();
+               giterr_set(GITERR_OS, "winsock init failed");
+               return -1;
+       }
+#endif
+
+       memset(&hints, 0x0, sizeof(struct addrinfo));
+       hints.ai_socktype = SOCK_STREAM;
+       hints.ai_family = AF_UNSPEC;
+
+       if ((ret = p_getaddrinfo(st->host, st->port, &hints, &info)) != 0) {
+               giterr_set(GITERR_NET,
+                          "failed to resolve address for %s: %s", st->host, p_gai_strerror(ret));
+               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;
+
+               if (connect(s, p->ai_addr, (socklen_t)p->ai_addrlen) == 0)
+                       break;
+
+               /* If we can't connect, try the next one */
+               close_socket(s);
+               s = INVALID_SOCKET;
+       }
+
+       /* Oops, we couldn't connect to any address */
+       if (s == INVALID_SOCKET && p == NULL) {
+               giterr_set(GITERR_OS, "failed to connect to %s", st->host);
+               p_freeaddrinfo(info);
+               return -1;
+       }
+
+       st->s = s;
+       p_freeaddrinfo(info);
+       return 0;
+}
+
+ssize_t socket_write(git_stream *stream, const char *data, size_t len, int flags)
+{
+       ssize_t ret;
+       size_t off = 0;
+       git_socket_stream *st = (git_socket_stream *) stream;
+
+       while (off < len) {
+               errno = 0;
+               ret = p_send(st->s, data + off, len - off, flags);
+               if (ret < 0) {
+                       net_set_error("Error sending data");
+                       return -1;
+               }
+
+               off += ret;
+       }
+
+       return off;
+}
+
+ssize_t socket_read(git_stream *stream, void *data, size_t len)
+{
+       ssize_t ret;
+       git_socket_stream *st = (git_socket_stream *) stream;
+
+       if ((ret = p_recv(st->s, data, len, 0)) < 0)
+               net_set_error("Error receiving socket data");
+
+       return ret;
+}
+
+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;
+}
+
+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);
+}
+
+int git_socket_stream_new(git_stream **out, const char *host, const char *port)
+{
+       git_socket_stream *st;
+
+       assert(out && host);
+
+       st = git__calloc(1, sizeof(git_socket_stream));
+       GITERR_CHECK_ALLOC(st);
+
+       st->host = git__strdup(host);
+       GITERR_CHECK_ALLOC(st->host);
+
+       if (port) {
+               st->port = git__strdup(port);
+               GITERR_CHECK_ALLOC(st->port);
+       }
+
+       st->parent.version = GIT_STREAM_VERSION;
+       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;
+}
diff --git a/src/streams/socket.h b/src/streams/socket.h
new file mode 100644 (file)
index 0000000..3235f31
--- /dev/null
@@ -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_streams_socket_h__
+#define INCLUDE_streams_socket_h__
+
+#include "common.h"
+
+#include "netops.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);
+
+#endif
diff --git a/src/streams/stransport.c b/src/streams/stransport.c
new file mode 100644 (file)
index 0000000..cca17bb
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * 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"
+#include "streams/curl.h"
+
+static int stransport_error(OSStatus ret)
+{
+       CFStringRef message;
+
+       if (ret == noErr || ret == errSSLClosedGraceful) {
+               giterr_clear();
+               return 0;
+       }
+
+#if !TARGET_OS_IPHONE
+       message = SecCopyErrorMessageString(ret, NULL);
+       GITERR_CHECK_ALLOC(message);
+
+       giterr_set(GITERR_NET, "SecureTransport error: %s", CFStringGetCStringPtr(message, kCFStringEncodingUTF8));
+       CFRelease(message);
+#else
+    giterr_set(GITERR_NET, "SecureTransport error: OSStatus %d", (unsigned int)ret);
+    GIT_UNUSED(message);
+#endif
+
+       return -1;
+}
+
+typedef struct {
+       git_stream parent;
+       git_stream *io;
+       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 ((error = git_stream_connect(st->io)) < 0)
+               return error;
+
+       ret = SSLHandshake(st->ctx);
+       if (ret != errSSLServerAuthCompleted) {
+               giterr_set(GITERR_SSL, "unexpected return value from ssl handshake %d", 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) {
+               giterr_set(GITERR_SSL, "internal security trust error");
+               return -1;
+       }
+
+       if (sec_res == kSecTrustResultDeny || sec_res == kSecTrustResultRecoverableTrustFailure ||
+           sec_res == kSecTrustResultFatalTrustFailure) {
+               giterr_set(GITERR_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) {
+               giterr_set(GITERR_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)
+{
+       git_stream *io = (git_stream *) conn;
+
+       if (git_stream_write(io, data, *len, 0) < 0) {
+               return -36; /* "ioErr" from MacErrors.h which is not available on iOS */
+       }
+
+       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 = len;
+       if ((ret = SSLWrite(st->ctx, data, data_len, &processed)) != noErr)
+               return stransport_error(ret);
+
+       return 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)
+{
+       git_stream *io = (git_stream *) conn;
+       OSStatus error = noErr;
+       size_t off = 0;
+       ssize_t ret;
+
+       do {
+               ret = git_stream_read(io, data + off, *len - off);
+               if (ret < 0) {
+                       error = -36; /* "ioErr" from MacErrors.h which is not available on iOS */
+                       break;
+               }
+               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)
+               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 git_stream_close(st->io);
+}
+
+static void stransport_free(git_stream *stream)
+{
+       stransport_stream *st = (stransport_stream *) stream;
+
+       git_stream_free(st->io);
+       CFRelease(st->ctx);
+       if (st->der_data)
+               CFRelease(st->der_data);
+       git__free(st);
+}
+
+int git_stransport_stream_new(git_stream **out, const char *host, const char *port)
+{
+       stransport_stream *st;
+       int error;
+       OSStatus ret;
+
+       assert(out && host);
+
+       st = git__calloc(1, sizeof(stransport_stream));
+       GITERR_CHECK_ALLOC(st);
+
+#ifdef GIT_CURL
+       error = git_curl_stream_new(&st->io, host, port);
+#else
+       error = git_socket_stream_new(&st->io, host, port);
+#endif
+
+       if (error < 0){
+               git__free(st);
+               return error;
+       }
+
+       st->ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType);
+       if (!st->ctx) {
+               giterr_set(GITERR_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->io)) != 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;
+}
+
+#endif
diff --git a/src/streams/stransport.h b/src/streams/stransport.h
new file mode 100644 (file)
index 0000000..4c02d07
--- /dev/null
@@ -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_streams_stransport_h__
+#define INCLUDE_streams_stransport_h__
+
+#include "common.h"
+
+#include "git2/sys/stream.h"
+
+extern int git_stransport_stream_new(git_stream **out, const char *host, const char *port);
+
+#endif
diff --git a/src/streams/tls.c b/src/streams/tls.c
new file mode 100644 (file)
index 0000000..d6ca7d4
--- /dev/null
@@ -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.
+ */
+
+#include "streams/tls.h"
+
+#include "git2/errors.h"
+
+#include "streams/openssl.h"
+#include "streams/stransport.h"
+
+static git_stream_cb tls_ctor;
+
+int git_stream_register_tls(git_stream_cb ctor)
+{
+       tls_ctor = ctor;
+
+       return 0;
+}
+
+int git_tls_stream_new(git_stream **out, const char *host, const char *port)
+{
+
+       if (tls_ctor)
+               return tls_ctor(out, host, port);
+
+#ifdef GIT_SECURE_TRANSPORT
+       return git_stransport_stream_new(out, host, port);
+#elif defined(GIT_OPENSSL)
+       return git_openssl_stream_new(out, host, port);
+#else
+       GIT_UNUSED(out);
+       GIT_UNUSED(host);
+       GIT_UNUSED(port);
+
+       giterr_set(GITERR_SSL, "there is no TLS stream available");
+       return -1;
+#endif
+}
diff --git a/src/streams/tls.h b/src/streams/tls.h
new file mode 100644 (file)
index 0000000..6d110e8
--- /dev/null
@@ -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_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.
+ *
+ * This allows us to ask for a SecureTransport or OpenSSL stream
+ * according to being on general Unix vs OS X.
+ */
+extern int git_tls_stream_new(git_stream **out, const char *host, const char *port);
+
+#endif
index ddd4b0663709afe1069cf6c90f4aae3b15a382e2..3ec0307b3428226e923c175fa58de82c212b5cf1 100644 (file)
@@ -5,7 +5,8 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
+#include "submodule.h"
+
 #include "git2/config.h"
 #include "git2/sys/config.h"
 #include "git2/types.h"
@@ -17,7 +18,6 @@
 #include "config_file.h"
 #include "config.h"
 #include "repository.h"
-#include "submodule.h"
 #include "tree.h"
 #include "iterator.h"
 #include "path.h"
@@ -209,6 +209,11 @@ int git_submodule_lookup(
 
        assert(repo && name);
 
+       if (repo->is_bare) {
+               giterr_set(GITERR_SUBMODULE, "cannot get submodules without a working tree");
+               return -1;
+       }
+
        if (repo->submodule_cache != NULL) {
                khiter_t pos = git_strmap_lookup_index(repo->submodule_cache, name);
                if (git_strmap_valid_index(repo->submodule_cache, pos)) {
@@ -549,6 +554,11 @@ int git_submodule_foreach(
        int error;
        size_t i;
 
+       if (repo->is_bare) {
+               giterr_set(GITERR_SUBMODULE, "cannot get submodules without a working tree");
+               return -1;
+       }
+
        if ((error = git_strmap_alloc(&submodules)) < 0)
                return error;
 
@@ -1950,7 +1960,7 @@ static git_config_backend *open_gitmodules(
                        if (git_config_file__ondisk(&mods, path.ptr) < 0)
                                mods = NULL;
                        /* open should only fail here if the file is malformed */
-                       else if (git_config_file_open(mods, GIT_CONFIG_LEVEL_LOCAL) < 0) {
+                       else if (git_config_file_open(mods, GIT_CONFIG_LEVEL_LOCAL, repo) < 0) {
                                git_config_file_free(mods);
                                mods = NULL;
                        }
index 456a93979de7b8af7d4aa57722433522eef6cd98..72867a32258d87eff7b80c68291de72a6c4eb5bf 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_submodule_h__
 #define INCLUDE_submodule_h__
 
+#include "common.h"
+
 #include "git2/submodule.h"
 #include "git2/repository.h"
 #include "fileops.h"
index 9312a7edb4f1041b845a48f3874419699bb71e94..509b23b8294b13600f55b6c9c86a20ed8f3dfab0 100644 (file)
@@ -5,14 +5,17 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
 #include "sysdir.h"
+
 #include "global.h"
 #include "buffer.h"
 #include "path.h"
 #include <ctype.h>
 #if GIT_WIN32
 #include "win32/findfile.h"
+#else
+#include <unistd.h>
+#include <pwd.h>
 #endif
 
 static int git_sysdir_guess_programdata_dirs(git_buf *out)
@@ -34,12 +37,63 @@ static int git_sysdir_guess_system_dirs(git_buf *out)
 #endif
 }
 
+#ifndef GIT_WIN32
+static int get_passwd_home(git_buf *out, uid_t uid)
+{
+       struct passwd pwd, *pwdptr;
+       char *buf = NULL;
+       long buflen;
+       int error;
+
+       assert(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) {
+               giterr_set(GITERR_OS, "failed to get passwd entry");
+               goto out;
+       }
+
+       if (!pwdptr) {
+               giterr_set(GITERR_OS, "no passwd entry found for user");
+               goto out;
+       }
+
+       if ((error = git_buf_puts(out, pwdptr->pw_dir)) < 0)
+               goto out;
+
+out:
+       git__free(buf);
+       return error;
+}
+#endif
+
 static int git_sysdir_guess_global_dirs(git_buf *out)
 {
 #ifdef GIT_WIN32
        return git_win32__find_global_dirs(out);
 #else
-       int error = git__getenv(out, "HOME");
+       int error;
+       uid_t uid, euid;
+
+       uid = getuid();
+       euid = geteuid();
+
+       /*
+        * In case we are running setuid, use the configuration
+        * of the effective user.
+        */
+       if (uid == euid)
+           error = git__getenv(out, "HOME");
+       else
+           error = get_passwd_home(out, euid);
 
        if (error == GIT_ENOTFOUND) {
                giterr_clear();
@@ -57,12 +111,25 @@ static int git_sysdir_guess_xdg_dirs(git_buf *out)
 #else
        git_buf env = GIT_BUF_INIT;
        int error;
-
-       if ((error = git__getenv(&env, "XDG_CONFIG_HOME")) == 0)
-               error = git_buf_joinpath(out, env.ptr, "git");
-
-       if (error == GIT_ENOTFOUND && (error = git__getenv(&env, "HOME")) == 0)
-               error = git_buf_joinpath(out, env.ptr, ".config/git");
+       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_buf_joinpath(out, env.ptr, "git");
+
+               if (error == GIT_ENOTFOUND && (error = git__getenv(&env, "HOME")) == 0)
+                       error = git_buf_joinpath(out, env.ptr, ".config/git");
+       } else {
+               if ((error = get_passwd_home(&env, euid)) == 0)
+                       error = git_buf_joinpath(out, env.ptr, ".config/git");
+       }
 
        if (error == GIT_ENOTFOUND) {
                giterr_clear();
index 79f23818ad1a857b121f4bae63260b8611649b9e..ce1b4dc71331705eac8b03241d63d98bb1c6655c 100644 (file)
@@ -8,6 +8,7 @@
 #define INCLUDE_sysdir_h__
 
 #include "common.h"
+
 #include "posix.h"
 #include "buffer.h"
 
@@ -115,4 +116,4 @@ extern int git_sysdir_get_str(char *out, size_t outlen, git_sysdir_t which);
  */
 extern int git_sysdir_set(git_sysdir_t which, const char *paths);
 
-#endif /* INCLUDE_sysdir_h__ */
+#endif
index 2bf23fc3cef8ff543d779a3c3c3f5764a8bca00e..445c3ff1d04bd319831d65532223ba0d05366bca 100644 (file)
--- a/src/tag.c
+++ b/src/tag.c
@@ -5,9 +5,9 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
-#include "commit.h"
 #include "tag.h"
+
+#include "commit.h"
 #include "signature.h"
 #include "message.h"
 #include "git2/object.h"
index d0cd393c72809ee474fd2ad2e053b117ab0cc052..8aae37840c8d2473ccf4b86ab43f699d788bb99c 100644 (file)
--- a/src/tag.h
+++ b/src/tag.h
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_tag_h__
 #define INCLUDE_tag_h__
 
+#include "common.h"
+
 #include "git2/tag.h"
 #include "repository.h"
 #include "odb.h"
index dc9b2f09e9371f710d5e0d6ecc5669de4e212dd8..e5ec6a843f89e9ac09fb100f5157f308b0b45c83 100644 (file)
@@ -4,6 +4,7 @@
  * 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 "thread-utils.h"
 
index 2df2aeb99baa4c5f16a1587714f39627ee136f80..035de699fa94b9499f253d51022282c4333cea4a 100644 (file)
@@ -243,4 +243,4 @@ extern int git_online_cpus(void);
 # define GIT_MEMORY_BARRIER /* noop */
 #endif
 
-#endif /* INCLUDE_thread_utils_h__ */
+#endif
diff --git a/src/tls_stream.c b/src/tls_stream.c
deleted file mode 100644 (file)
index 83e2d06..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * 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 "openssl_stream.h"
-#include "stransport_stream.h"
-
-static git_stream_cb tls_ctor;
-
-int git_stream_register_tls(git_stream_cb ctor)
-{
-       tls_ctor = ctor;
-
-       return 0;
-}
-
-int git_tls_stream_new(git_stream **out, const char *host, const char *port)
-{
-
-       if (tls_ctor)
-               return tls_ctor(out, host, port);
-
-#ifdef GIT_SECURE_TRANSPORT
-       return git_stransport_stream_new(out, host, port);
-#elif defined(GIT_OPENSSL)
-       return git_openssl_stream_new(out, host, port);
-#else
-       GIT_UNUSED(out);
-       GIT_UNUSED(host);
-       GIT_UNUSED(port);
-
-       giterr_set(GITERR_SSL, "there is no TLS stream available");
-       return -1;
-#endif
-}
diff --git a/src/tls_stream.h b/src/tls_stream.h
deleted file mode 100644 (file)
index 98a7041..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This 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_tls_stream_h__
-#define INCLUDE_tls_stream_h__
-
-#include "git2/sys/stream.h"
-
-/**
- * Create a TLS stream with the most appropriate backend available for
- * the current platform.
- *
- * This allows us to ask for a SecureTransport or OpenSSL stream
- * according to being on general Unix vs OS X.
- */
-extern int git_tls_stream_new(git_stream **out, const char *host, const char *port);
-
-#endif
index 0f21428619a2b445beaf7a8bec53fc14bc232a91..080d1e891a9bfcfe3597b5d7a61bfc23e7eea10f 100644 (file)
@@ -5,10 +5,10 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
+#include "trace.h"
+
 #include "buffer.h"
-#include "common.h"
 #include "global.h"
-#include "trace.h"
 #include "git2/trace.h"
 
 #ifdef GIT_TRACE
index 486084d0196962d040e3295c0ee3f1dd51c59ec7..498944035abb4e337476bb8ada2f5f6b99db27d2 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_trace_h__
 #define INCLUDE_trace_h__
 
+#include "common.h"
+
 #include <git2/trace.h>
 #include "buffer.h"
 
diff --git a/src/trailer.c b/src/trailer.c
new file mode 100644 (file)
index 0000000..24c8847
--- /dev/null
@@ -0,0 +1,416 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * 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 -1.
+ */
+static int last_line(const char *buf, size_t len)
+{
+       int i;
+       if (len == 0)
+               return -1;
+       if (len == 1)
+               return 0;
+       /*
+        * 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')
+                       return i + 1;
+       }
+       return 0;
+}
+
+/*
+ * If the given line is of the form
+ * "<token><optional whitespace><separator>..." or "<separator>...", return the
+ * location of the separator. Otherwise, return -1.  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 0) is
+ * distinguished from the non-well-formed-line case (in which this function
+ * returns -1) because some callers of this function need such a distinction.
+ */
+static int find_separator(const char *line, const char *separators)
+{
+       int whitespace_found = 0;
+       const char *c;
+       for (c = line; *c; c++) {
+               if (strchr(separators, *c))
+                       return c - line;
+               if (!whitespace_found && (isalnum(*c) || *c == '-'))
+                       continue;
+               if (c != line && (*c == ' ' || *c == '\t')) {
+                       whitespace_found = 1;
+                       continue;
+               }
+               break;
+       }
+       return -1;
+}
+
+/*
+ * 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 int ignore_non_trailer(const char *buf, size_t len)
+{
+       int boc = 0;
+       size_t 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 int 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 int find_trailer_start(const char *buf, size_t len)
+{
+       const char *s;
+       int end_of_title, l, 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.
+        */
+       for (l = last_line(buf, len);
+            l >= end_of_title;
+            l = last_line(buf, l)) {
+               const char *bol = buf + l;
+               const char *const *p;
+               int separator_pos;
+
+               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;
+                       }
+               }
+
+               separator_pos = find_separator(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 int 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);
+       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);
+
+       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);
+}
index 3d3f35a03b49ac76ad7f389ec9a8c828933de177..675023afd629cfd981dffcef82e914246e5e0927 100644 (file)
@@ -5,7 +5,8 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
+#include "transaction.h"
+
 #include "repository.h"
 #include "strmap.h"
 #include "refdb.h"
index b66165332dfdbed3a80072aa19f389b0882c7084..f1e18b3238e5a98962b0025ca979d00f7ea7e6df 100644 (file)
@@ -4,7 +4,9 @@
  * 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"
index c1154db3419dae157ab1355a831122c70e00b38b..9597cc24972ae2ef307c4456b9eaddd38b672e2a 100644 (file)
@@ -5,9 +5,10 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
+#include "auth.h"
+
 #include "git2.h"
 #include "buffer.h"
-#include "auth.h"
 
 static int basic_next_token(
        git_buf *out, git_http_auth_context *ctx, git_cred *c)
index 52138cf8fae206421d1d582c2d4e5198a8c74316..3b8b29eb9e77e83af6fc070281d29221b92079e9 100644 (file)
@@ -5,8 +5,10 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#ifndef INCLUDE_http_auth_h__
-#define INCLUDE_http_auth_h__
+#ifndef INCLUDE_transports_auth_h__
+#define INCLUDE_transports_auth_h__
+
+#include "common.h"
 
 #include "git2.h"
 #include "netops.h"
@@ -60,4 +62,3 @@ int git_http_auth_basic(
        const gitno_connection_data *connection_data);
 
 #endif
-
index 7c868c9fd158f101eca5947a35467fb788624188..c9bc3043d8c8d1be6a5726572b2f0b54dd169ba6 100644 (file)
@@ -5,10 +5,11 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
+#include "auth_negotiate.h"
+
 #ifdef GIT_GSSAPI
 
 #include "git2.h"
-#include "common.h"
 #include "buffer.h"
 #include "auth.h"
 
index d7270b7abae436a0a649828c472c8352491e5835..15a528aaf7c550fa877978f77a36d5e60b02fec9 100644 (file)
@@ -5,9 +5,10 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#ifndef INCLUDE_auth_negotiate_h__
-#define INCLUDE_auth_negotiate_h__
+#ifndef INCLUDE_transports_auth_negotiate_h__
+#define INCLUDE_transports_auth_negotiate_h__
 
+#include "common.h"
 #include "git2.h"
 #include "auth.h"
 
@@ -24,4 +25,3 @@ extern int git_http_auth_negotiate(
 #endif /* GIT_GSSAPI */
 
 #endif
-
index 8e3f64435a7768ec0eeaa4e1dfd0a38f3117ffdc..8055e2d659d80b125596e7c8e414e1f308088fa2 100644 (file)
@@ -5,6 +5,8 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
+#include "cred.h"
+
 #include "git2.h"
 #include "smart.h"
 #include "git2/cred_helpers.h"
index 2de8deee81b0cd7457ec81d63b32c34c4c67a3f8..ed5821c55b32bddf3a6500f3a4680343b8aad60f 100644 (file)
@@ -4,8 +4,10 @@
  * This 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_h__
-#define INCLUDE_git_cred_h__
+#ifndef INCLUDE_transports_cred_h__
+#define INCLUDE_transports_cred_h__
+
+#include "common.h"
 
 #include "git2/transport.h"
 
index 5cc9b086924600a0f1746a61c5601583d239665e..fdc56b17e627b9e05733bb38caf6fbd55d2c175d 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include "common.h"
+
 #include "git2/cred_helpers.h"
 
 int git_cred_userpass(
index 01edfdc49c1110cfe5bca5d7b9274abdcf5db933..94178f2c5020cfc5c72e2f4f9f06e490a53068f3 100644 (file)
@@ -5,12 +5,14 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
+#include "common.h"
+
 #include "git2.h"
 #include "buffer.h"
 #include "netops.h"
 #include "git2/sys/transport.h"
 #include "stream.h"
-#include "socket_stream.h"
+#include "streams/socket.h"
 
 #define OWNING_SUBTRANSPORT(s) ((git_subtransport *)(s)->parent.subtransport)
 
index cb4a6d0d58037cf35abaf3045e36845011aca5b5..e051c8a35bd2e32268ed73f64f6154b5ede5b6e2 100644 (file)
@@ -4,6 +4,9 @@
  * 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 "git2.h"
 #include "remote.h"
 #include "smart.h"
 #include "auth.h"
+#include "http.h"
 #include "auth_negotiate.h"
-#include "tls_stream.h"
-#include "socket_stream.h"
-#include "curl_stream.h"
+#include "streams/tls.h"
+#include "streams/socket.h"
+#include "streams/curl.h"
 
 git_http_auth_scheme auth_schemes[] = {
        { GIT_AUTHTYPE_NEGOTIATE, "Negotiate", GIT_CREDTYPE_DEFAULT, git_http_auth_negotiate },
@@ -187,16 +191,6 @@ static int apply_credentials(git_buf *buf, http_subtransport *t)
        return context->next_token(buf, context, cred);
 }
 
-static const char *user_agent(void)
-{
-       const char *custom = git_libgit2__user_agent();
-
-       if (custom)
-               return custom;
-
-       return "libgit2 " LIBGIT2_VERSION;
-}
-
 static int gen_request(
        git_buf *buf,
        http_stream *s,
@@ -208,7 +202,9 @@ static int gen_request(
 
        git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, path, s->service_url);
 
-       git_buf_printf(buf, "User-Agent: git/2.0 (%s)\r\n", user_agent());
+       git_buf_puts(buf, "User-Agent: ");
+       git_http__user_agent(buf);
+       git_buf_puts(buf, "\r\n");
        git_buf_printf(buf, "Host: %s\r\n", t->connection_data.host);
 
        if (s->chunked || content_length > 0) {
diff --git a/src/transports/http.h b/src/transports/http.h
new file mode 100644 (file)
index 0000000..6c4ecc9
--- /dev/null
@@ -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_transports_http_h__
+#define INCLUDE_transports_http_h__
+
+#include "buffer.h"
+
+GIT_INLINE(int) git_http__user_agent(git_buf *buf)
+{
+       const char *ua = git_libgit2__user_agent();
+
+       if (!ua)
+               ua = "libgit2 " LIBGIT2_VERSION;
+
+       return git_buf_printf(buf, "git/2.0 (%s)", ua);
+}
+
+#endif
index e24e99860a0e0d28ee6ddb0301f0bf9e289cbfdd..740cf36a9cc9823448f548fe6e07dc4232326d38 100644 (file)
@@ -4,7 +4,9 @@
  * 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/net.h"
 #include "git2/repository.h"
@@ -16,6 +18,7 @@
 #include "git2/pack.h"
 #include "git2/commit.h"
 #include "git2/revparse.h"
+
 #include "pack-objects.h"
 #include "refs.h"
 #include "posix.h"
@@ -504,6 +507,23 @@ static int local_counting(int stage, unsigned int current, unsigned int total, v
        return error;
 }
 
+static int foreach_reference_cb(git_reference *reference, void *payload)
+{
+       git_revwalk *walk = (git_revwalk *)payload;
+
+       int 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 == GITERR_INVALID) {
+               giterr_clear();
+               error = 0;
+       }
+
+       git_reference_free(reference);
+
+       return error;
+}
+
 static int local_download_pack(
                git_transport *transport,
                git_repository *repo,
@@ -543,11 +563,6 @@ static int local_download_pack(
                if (git_object_type(obj) == GIT_OBJ_COMMIT) {
                        /* Revwalker includes only wanted commits */
                        error = git_revwalk_push(walk, &rhead->oid);
-                       if (!error && !git_oid_iszero(&rhead->loid)) {
-                               error = git_revwalk_hide(walk, &rhead->loid);
-                               if (error == GIT_ENOTFOUND)
-                                       error = 0;
-                       }
                } else {
                        /* Tag or some other wanted object. Add it on its own */
                        error = git_packbuilder_insert_recur(pack, &rhead->oid, rhead->name);
@@ -557,6 +572,9 @@ static int local_download_pack(
                        goto cleanup;
        }
 
+       if ((error = git_reference_foreach(repo, foreach_reference_cb, walk)))
+               goto cleanup;
+
        if ((error = git_packbuilder_insert_walk(pack, walk)))
                goto cleanup;
 
index a96fdf6fb018f686547550f6af6a28dd46864e9e..6d5d95fbff5266b4e39da2de459105dd6b0a26dd 100644 (file)
@@ -4,8 +4,10 @@
  * 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 "smart.h"
+
+#include "git2.h"
 #include "refs.h"
 #include "refspec.h"
 #include "proxy.h"
index b47001fe004d0c3954085458baffe22e9700b6a6..e33a2540244bd46c2dbfbe22350bce9195e299c4 100644 (file)
@@ -4,6 +4,11 @@
  * This 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 "netops.h"
@@ -191,3 +196,5 @@ int git_pkt_buffer_done(git_buf *buf);
 int git_pkt_buffer_wants(const git_remote_head * const *refs, size_t count, transport_smart_caps *caps, git_buf *buf);
 int git_pkt_buffer_have(git_oid *oid, git_buf *buf);
 void git_pkt_free(git_pkt *pkt);
+
+#endif
index 8146fa1632a383eac265c9520f05cc203bdb30a1..aecfece0a2dc87fcb26a067468ff5067f372e127 100644 (file)
@@ -4,6 +4,9 @@
  * 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"
 
@@ -270,7 +273,7 @@ static int fetch_setup_walk(git_revwalk **out, git_repository *repo)
        git_revwalk *walk = NULL;
        git_strarray refs;
        unsigned int i;
-       git_reference *ref;
+       git_reference *ref = NULL;
        int error;
 
        if ((error = git_reference_list(&refs, repo)) < 0)
@@ -282,6 +285,9 @@ static int fetch_setup_walk(git_revwalk **out, git_repository *repo)
        git_revwalk_sorting(walk, GIT_SORT_TIME);
 
        for (i = 0; i < refs.count; ++i) {
+               git_reference_free(ref);
+               ref = NULL;
+
                /* No tags */
                if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR))
                        continue;
@@ -294,16 +300,13 @@ static int fetch_setup_walk(git_revwalk **out, git_repository *repo)
 
                if ((error = git_revwalk_push(walk, git_reference_target(ref))) < 0)
                        goto on_error;
-
-               git_reference_free(ref);
        }
 
-       git_strarray_free(&refs);
        *out = walk;
-       return 0;
 
 on_error:
-       git_revwalk_free(walk);
+       if (error)
+               git_revwalk_free(walk);
        git_reference_free(ref);
        git_strarray_free(&refs);
        return error;
index 4c55e3f2a2f0c3765effdedc1dbb4bd9cc473fe2..dcd9b5e27b81a36c2276842d7c267fa8f40afaeb 100644 (file)
@@ -5,6 +5,8 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
+#include "ssh.h"
+
 #ifdef GIT_SSH
 #include <libssh2.h>
 #endif
@@ -15,8 +17,7 @@
 #include "netops.h"
 #include "smart.h"
 #include "cred.h"
-#include "socket_stream.h"
-#include "ssh.h"
+#include "streams/socket.h"
 
 #ifdef GIT_SSH
 
@@ -63,7 +64,7 @@ static void ssh_error(LIBSSH2_SESSION *session, const char *errmsg)
  */
 static int gen_proto(git_buf *request, const char *cmd, const char *url)
 {
-       char *repo;
+       const char *repo;
        int len;
        size_t i;
 
@@ -91,8 +92,10 @@ done:
        len = strlen(cmd) + 1 /* Space */ + 1 /* Quote */ + strlen(repo) + 1 /* Quote */ + 1;
 
        git_buf_grow(request, len);
-       git_buf_printf(request, "%s '%s'", cmd, repo);
-       git_buf_putc(request, '\0');
+       git_buf_puts(request, cmd);
+       git_buf_puts(request, " '");
+       git_buf_decode_percent(request, repo, strlen(repo));
+       git_buf_puts(request, "'");
 
        if (git_buf_oom(request))
                return -1;
@@ -418,8 +421,10 @@ static int _git_ssh_authenticate_session(
                }
        } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
 
-        if (rc == LIBSSH2_ERROR_PASSWORD_EXPIRED || rc == LIBSSH2_ERROR_AUTHENTICATION_FAILED)
-                return GIT_EAUTH;
+       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 (!giterr_last())
index 2db2cc5df3c3f71315d4ed92b2a9ff4fcc597592..d3e741f1d9e6db4a712e4e02b5b62cd02f28fd9c 100644 (file)
@@ -4,8 +4,10 @@
  * This 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_ssh_h__
-#define INCLUDE_ssh_h__
+#ifndef INCLUDE_transports_ssh_h__
+#define INCLUDE_transports_ssh_h__
+
+#include "common.h"
 
 int git_transport_ssh_global_init(void);
 
index fb504c912f01e90c8a2c0c11a40da9ce1a897af9..e52d54b6d705abe12ea142662d1e1d85fa91a217 100644 (file)
@@ -5,6 +5,8 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
+#include "common.h"
+
 #ifdef GIT_WINHTTP
 
 #include "git2.h"
@@ -16,6 +18,7 @@
 #include "remote.h"
 #include "repository.h"
 #include "global.h"
+#include "http.h"
 
 #include <wincrypt.h>
 #include <winhttp.h>
 #define WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH 0
 #endif
 
+#ifndef WINHTTP_FLAG_SECURE_PROTOCOL_TLS_1_1
+# define WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 0x00000200
+#endif
+
+#ifndef WINHTTP_FLAG_SECURE_PROTOCOL_TLS_1_2
+# define WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 0x00000800
+#endif
+
 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";
@@ -170,9 +181,15 @@ static int apply_default_credentials(HINTERNET request, int mechanisms)
         * is "medium" which applies to the intranet and sounds like it would correspond
         * to Internet Explorer security zones, but in fact does not. */
        DWORD data = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW;
+       DWORD native_scheme = 0;
 
-       if ((mechanisms & GIT_WINHTTP_AUTH_NTLM) == 0 &&
-               (mechanisms & GIT_WINHTTP_AUTH_NEGOTIATE) == 0) {
+       if ((mechanisms & GIT_WINHTTP_AUTH_NTLM) != 0)
+               native_scheme |= WINHTTP_AUTH_SCHEME_NTLM;
+
+       if ((mechanisms & GIT_WINHTTP_AUTH_NEGOTIATE) != 0)
+               native_scheme |= WINHTTP_AUTH_SCHEME_NEGOTIATE;
+
+       if (!native_scheme) {
                giterr_set(GITERR_NET, "invalid authentication scheme");
                return -1;
        }
@@ -180,6 +197,9 @@ static int apply_default_credentials(HINTERNET request, int mechanisms)
        if (!WinHttpSetOption(request, WINHTTP_OPTION_AUTOLOGON_POLICY, &data, sizeof(DWORD)))
                return -1;
 
+       if (!WinHttpSetCredentials(request, WINHTTP_AUTH_TARGET_SERVER, native_scheme, NULL, NULL, NULL))
+               return -1;
+
        return 0;
 }
 
@@ -267,7 +287,7 @@ static int certificate_check(winhttp_stream *s, int valid)
        cert.parent.cert_type = GIT_CERT_X509;
        cert.data = cert_ctx->pbCertEncoded;
        cert.len = cert_ctx->cbCertEncoded;
-       error = t->owner->certificate_check_cb((git_cert *) &cert, valid, t->connection_data.host, t->owner->cred_acquire_payload);
+       error = t->owner->certificate_check_cb((git_cert *) &cert, valid, t->connection_data.host, t->owner->message_cb_payload);
        CertFreeCertificateContext(cert_ctx);
 
        if (error < 0 && !giterr_last())
@@ -604,12 +624,12 @@ static int parse_unauthorized_response(
        if (WINHTTP_AUTH_SCHEME_NTLM & supported) {
                *allowed_types |= GIT_CREDTYPE_USERPASS_PLAINTEXT;
                *allowed_types |= GIT_CREDTYPE_DEFAULT;
-               *allowed_mechanisms = GIT_WINHTTP_AUTH_NEGOTIATE;
+               *allowed_mechanisms |= GIT_WINHTTP_AUTH_NTLM;
        }
 
        if (WINHTTP_AUTH_SCHEME_NEGOTIATE & supported) {
                *allowed_types |= GIT_CREDTYPE_DEFAULT;
-               *allowed_mechanisms = GIT_WINHTTP_AUTH_NEGOTIATE;
+               *allowed_mechanisms |= GIT_WINHTTP_AUTH_NEGOTIATE;
        }
 
        if (WINHTTP_AUTH_SCHEME_BASIC & supported) {
@@ -690,21 +710,6 @@ static int winhttp_close_connection(winhttp_subtransport *t)
        return ret;
 }
 
-static int user_agent(git_buf *ua)
-{
-       const char *custom = git_libgit2__user_agent();
-
-       git_buf_clear(ua);
-       git_buf_PUTS(ua, "git/1.0 (");
-
-       if (custom)
-               git_buf_puts(ua, custom);
-       else
-               git_buf_PUTS(ua, "libgit2 " LIBGIT2_VERSION);
-
-       return git_buf_putc(ua, ')');
-}
-
 static void CALLBACK winhttp_status(
        HINTERNET connection,
        DWORD_PTR ctx,
@@ -747,6 +752,10 @@ static int winhttp_connect(
        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;
 
        t->session = NULL;
        t->connection = NULL;
@@ -761,7 +770,8 @@ static int winhttp_connect(
                return -1;
        }
 
-       if ((error = user_agent(&ua)) < 0) {
+
+       if ((error = git_http__user_agent(&ua)) < 0) {
                git__free(wide_host);
                return error;
        }
@@ -788,6 +798,16 @@ static int winhttp_connect(
                goto on_error;
        }
 
+       /*
+        * Do a best-effort attempt to enable TLS 1.2 but allow this to
+        * fail; if TLS 1.2 support is not available for some reason,
+        * ignore the failure (it will keep the default protocols).
+        */
+       WinHttpSetOption(t->session,
+               WINHTTP_OPTION_SECURE_PROTOCOLS,
+               &protocols,
+               sizeof(protocols));
+
        if (!WinHttpSetTimeouts(t->session, default_timeout, default_connect_timeout, default_timeout, default_timeout)) {
                giterr_set(GITERR_OS, "failed to set timeouts for WinHTTP");
                goto on_error;
index 548054136dc3c617ad2929dc614296dbafaa0cbb..b331d22a21210e032b9845e061beb68100775e6d 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include "tree-cache.h"
+
 #include "pool.h"
 #include "tree.h"
 
index c44ca7cf5acfd779152d12341e584b7ba3d32b90..e02300e6e1963803d1b98c227c1070fcffeed67e 100644 (file)
@@ -9,6 +9,7 @@
 #define INCLUDE_tree_cache_h__
 
 #include "common.h"
+
 #include "pool.h"
 #include "buffer.h"
 #include "git2/oid.h"
index 6b1d1b23847308f75b83d4a3085fd268e3cd4d35..fdf36f850ca6fe4b2afc13e02c3cfeae860ac90a 100644 (file)
@@ -5,9 +5,9 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
-#include "commit.h"
 #include "tree.h"
+
+#include "commit.h"
 #include "git2/repository.h"
 #include "git2/object.h"
 #include "fileops.h"
@@ -440,16 +440,16 @@ int git_tree__parse(void *_tree, git_odb_object *odb_obj)
                unsigned int attr;
 
                if (parse_mode(&attr, buffer, &buffer) < 0 || !buffer)
-                       return tree_error("Failed to parse tree. Can't parse filemode", NULL);
+                       return tree_error("failed to parse tree: can't parse filemode", NULL);
 
                if ((nul = memchr(buffer, 0, buffer_end - buffer)) == NULL)
-                       return tree_error("Failed to parse tree. Object is corrupted", NULL);
+                       return tree_error("failed to parse tree: object is corrupted", NULL);
 
                if ((filename_len = nul - buffer) == 0)
-                       return tree_error("Failed to parse tree. Can't parse filename", NULL);
+                       return tree_error("failed to parse tree: can't parse filename", NULL);
 
                if ((buffer_end - (nul + 1)) < GIT_OID_RAWSZ)
-                       return tree_error("Failed to parse tree. Can't parse OID", NULL);
+                       return tree_error("failed to parse tree: can't parse OID", NULL);
 
                /* Allocate the entry */
                {
@@ -496,7 +496,10 @@ static int append_entry(
        int error = 0;
 
        if (!valid_entry_name(bld->repo, filename))
-               return tree_error("Failed to insert entry. Invalid name for a tree entry", filename);
+               return tree_error("failed to insert entry: invalid name for a tree entry", filename);
+
+       if (git_oid_iszero(id))
+               return tree_error("failed to insert entry: invalid null OID for a tree entry", filename);
 
        entry = alloc_entry(filename, strlen(filename), id);
        GITERR_CHECK_ALLOC(entry);
@@ -735,14 +738,17 @@ int git_treebuilder_insert(
        assert(bld && id && filename);
 
        if (!valid_filemode(filemode))
-               return tree_error("Failed to insert entry. Invalid filemode for file", filename);
+               return tree_error("failed to insert entry: invalid filemode for file", filename);
 
        if (!valid_entry_name(bld->repo, filename))
-               return tree_error("Failed to insert entry. Invalid name for a tree entry", filename);
+               return tree_error("failed to insert entry: invalid name for a tree entry", filename);
+
+       if (git_oid_iszero(id))
+               return tree_error("failed to insert entry: invalid null OID", filename);
 
        if (filemode != GIT_FILEMODE_COMMIT &&
            !git_object__is_valid(bld->repo, id, otype_from_mode(filemode)))
-               return tree_error("Failed to insert entry; invalid object specified", filename);
+               return tree_error("failed to insert entry: invalid object specified", filename);
 
        pos = git_strmap_lookup_index(bld->map, filename);
        if (git_strmap_valid_index(bld->map, pos)) {
@@ -793,7 +799,7 @@ 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);
+               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);
@@ -946,12 +952,12 @@ int git_tree_entry_bypath(
                        return GIT_ENOTFOUND;
                }
 
-               /* If there's only a slash left in the path, we 
+               /* 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 */
index 5e7a66e045b3e6da6ff49b626f8ee5556cb7ff1b..00f4b06eb2e03307ed8f925de58d8e08e18de8ed 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_tree_h__
 #define INCLUDE_tree_h__
 
+#include "common.h"
+
 #include "git2/tree.h"
 #include "repository.h"
 #include "odb.h"
index e59819204289778b28054d0fc3b7a5f5bdd9751f..8d1ed9787c00a26126f71812f66cf4494af52dea 100644 (file)
@@ -310,7 +310,6 @@ static ssize_t collapse(void **dst, struct tsort_run *stack, ssize_t stack_curr,
 #define PUSH_NEXT() do {\
        len = count_run(dst, curr, size, store);\
        run = minrun;\
-       if (run < minrun) run = minrun;\
        if (run > (ssize_t)size - curr) run = size - curr;\
        if (run > len) {\
                bisort(&dst[curr], len, run, cmp, payload);\
index 9d9b1fe381377d3980bceefb26db68e5bfc251b6..8a07fcff90ebc615e895338d2b5d9a3775b8b39c 100644 (file)
@@ -4,7 +4,10 @@
  * 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/common.h>
+
+#include "common.h"
+
+#include "git2/common.h"
 
 #if !defined(GIT_WIN32) && !defined(NO_MMAP)
 
index 52985fd8a868fd9452a7af8638099d4201482b4f..f2fffd5c9184072b440695eca577ee17eadaafe1 100644 (file)
@@ -4,8 +4,12 @@
  * This 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__unix_h__
-#define INCLUDE_posix__unix_h__
+#ifndef INCLUDE_unix_posix_h__
+#define INCLUDE_unix_posix_h__
+
+#ifndef LIBGIT2_NO_FEATURES_H
+# include "git2/sys/features.h"
+#endif
 
 #include <stdio.h>
 #include <dirent.h>
@@ -71,7 +75,7 @@ GIT_INLINE(int) p_fsync(int fd)
 
 #define p_timeval timeval
 
-#ifdef HAVE_FUTIMENS
+#ifdef GIT_USE_FUTIMENS
 GIT_INLINE(int) p_futimes(int f, const struct p_timeval t[2])
 {
        struct timespec s[2];
@@ -85,7 +89,7 @@ GIT_INLINE(int) p_futimes(int f, const struct p_timeval t[2])
 # define p_futimes futimes
 #endif
 
-#ifdef HAVE_REGCOMP_L
+#ifdef GIT_USE_REGCOMP_L
 #include <xlocale.h>
 GIT_INLINE(int) p_regcomp(regex_t *preg, const char *pattern, int cflags)
 {
index 3f23d10d58951cdb7d87190ca4ceee88151ecb9d..233561b4e0cb80d68ea13279dd874c3495ca33fe 100644 (file)
@@ -53,4 +53,4 @@ typedef struct {
 #define git_rwlock_free(a)     pthread_rwlock_destroy(a)
 #define GIT_RWLOCK_STATIC_INIT PTHREAD_RWLOCK_INITIALIZER
 
-#endif /* INCLUDE_unix_pthread_h__ */
+#endif
index 2e49150c2cd8c80a1b320a96801fe1649f720fa6..893bac87b3697ec3b0e6cf19b4a316ddae930e97 100644 (file)
@@ -4,7 +4,10 @@
  * 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/common.h>
+
+#include "common.h"
+
+#include "git2/common.h"
 
 #ifndef GIT_WIN32
 
index a44f4c9acfc2667a26b83175856fb124c006eeb6..34841df4f1f73a24882600985235dc69c802fca6 100644 (file)
@@ -4,11 +4,10 @@
  * 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 "util.h"
+
 #include "common.h"
-#include <stdio.h>
-#include <ctype.h>
-#include "posix.h"
 
 #ifdef GIT_WIN32
 # include "win32/w32_buffer.h"
@@ -250,35 +249,47 @@ void git__strtolower(char *str)
        git__strntolower(str, strlen(str));
 }
 
-int git__prefixcmp(const char *str, const char *prefix)
+GIT_INLINE(int) prefixcmp(const char *str, size_t str_n, const char *prefix, bool icase)
 {
-       for (;;) {
-               unsigned char p = *(prefix++), s;
+       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 = *(str++)) != p)
+
+               if (s != p)
                        return s - p;
        }
+
+       return (0 - *prefix);
 }
 
-int git__prefixcmp_icase(const char *str, const char *prefix)
+int git__prefixcmp(const char *str, const char *prefix)
 {
-       return strncasecmp(str, prefix, strlen(prefix));
+       return prefixcmp(str, SIZE_MAX, prefix, false);
 }
 
-int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix)
+int git__prefixncmp(const char *str, size_t str_n, const char *prefix)
 {
-       int s, p;
-
-       while(str_n--) {
-               s = (unsigned char)git__tolower(*str++);
-               p = (unsigned char)git__tolower(*prefix++);
+       return prefixcmp(str, str_n, prefix, false);
+}
 
-               if (s != p)
-                       return s - p;
-       }
+int git__prefixcmp_icase(const char *str, const char *prefix)
+{
+       return prefixcmp(str, SIZE_MAX, prefix, true);
+}
 
-       return (0 - *prefix);
+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)
@@ -464,9 +475,11 @@ uint32_t git__hash(const void *key, int len, uint32_t seed)
 
        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();
+               MURMUR_BLOCK();
        }
 
        h1 ^= len;
index eb15250d8dd0807cd62b5ccf2285cb643b795bba..f6d19cfdea96de4b1fdef2ed2e318f87aa5a1ae3 100644 (file)
@@ -7,42 +7,18 @@
 #ifndef INCLUDE_util_h__
 #define INCLUDE_util_h__
 
-#include "git2/buffer.h"
-#include "buffer.h"
+#include "common.h"
 
-#if defined(GIT_MSVC_CRTDBG)
-/* Enable 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.
- */
-#include <stdlib.h>
-#include <crtdbg.h>
-#include "win32/w32_crtdbg_stacktrace.h"
+#ifndef GIT_WIN32
+# include <ctype.h>
 #endif
 
+#include "git2/buffer.h"
+
+#include "buffer.h"
 #include "common.h"
 #include "strnlen.h"
+#include "thread-utils.h"
 
 #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
 #define bitsizeof(x) (CHAR_BIT * sizeof(x))
 
 #if defined(GIT_MSVC_CRTDBG)
 
-GIT_INLINE(void *) git__crtdbg__malloc(size_t len, const char *file, int line)
-{
-       void *ptr = _malloc_dbg(len, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
-       if (!ptr) giterr_set_oom();
-       return ptr;
-}
-
-GIT_INLINE(void *) git__crtdbg__calloc(size_t nelem, size_t elsize, const char *file, int line)
-{
-       void *ptr = _calloc_dbg(nelem, elsize, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
-       if (!ptr) giterr_set_oom();
-       return ptr;
-}
-
-GIT_INLINE(char *) git__crtdbg__strdup(const char *str, const char *file, int line)
-{
-       char *ptr = _strdup_dbg(str, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
-       if (!ptr) giterr_set_oom();
-       return ptr;
-}
-
-GIT_INLINE(char *) git__crtdbg__strndup(const char *str, size_t n, const char *file, int line)
-{
-       size_t length = 0, alloclength;
-       char *ptr;
-
-       length = p_strnlen(str, n);
-
-       if (GIT_ADD_SIZET_OVERFLOW(&alloclength, length, 1) ||
-               !(ptr = git__crtdbg__malloc(alloclength, file, line)))
-               return NULL;
-
-       if (length)
-               memcpy(ptr, str, length);
-
-       ptr[length] = '\0';
-
-       return ptr;
-}
-
-GIT_INLINE(char *) git__crtdbg__substrdup(const char *start, size_t n, const char *file, int line)
-{
-       char *ptr;
-       size_t alloclen;
-
-       if (GIT_ADD_SIZET_OVERFLOW(&alloclen, n, 1) ||
-               !(ptr = git__crtdbg__malloc(alloclen, file, line)))
-               return NULL;
-
-       memcpy(ptr, start, n);
-       ptr[n] = '\0';
-       return ptr;
-}
-
-GIT_INLINE(void *) git__crtdbg__realloc(void *ptr, size_t size, const char *file, int line)
-{
-       void *new_ptr = _realloc_dbg(ptr, size, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
-       if (!new_ptr) giterr_set_oom();
-       return new_ptr;
-}
-
-GIT_INLINE(void *) git__crtdbg__reallocarray(void *ptr, size_t nelem, size_t elsize, const char *file, int line)
-{
-       size_t newsize;
-
-       return GIT_MULTIPLY_SIZET_OVERFLOW(&newsize, nelem, elsize) ?
-               NULL : _realloc_dbg(ptr, newsize, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
-}
+/* Enable 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.
+ */
 
-GIT_INLINE(void *) git__crtdbg__mallocarray(size_t nelem, size_t elsize, const char *file, int line)
-{
-       return git__crtdbg__reallocarray(NULL, nelem, elsize, file, line);
-}
+#include "win32/w32_crtdbg_stacktrace.h"
 
 #define git__malloc(len)                      git__crtdbg__malloc(len, __FILE__, __LINE__)
 #define git__calloc(nelem, elsize)            git__crtdbg__calloc(nelem, elsize, __FILE__, __LINE__)
@@ -254,6 +184,7 @@ GIT_INLINE(void) git__free(void *ptr)
 
 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);
 
@@ -363,8 +294,6 @@ extern int git__strncasecmp(const char *a, const char *b, size_t sz);
 
 extern int git__strcasesort_cmp(const char *a, const char *b);
 
-#include "thread-utils.h"
-
 typedef struct {
        git_atomic refcount;
        void *owner;
@@ -373,22 +302,22 @@ typedef struct {
 typedef void (*git_refcount_freeptr)(void *r);
 
 #define GIT_REFCOUNT_INC(r) { \
-       git_atomic_inc(&((git_refcount *)(r))->refcount);       \
+       git_atomic_inc(&(r)->rc.refcount);      \
 }
 
 #define GIT_REFCOUNT_DEC(_r, do_free) { \
-       git_refcount *r = (git_refcount *)(_r); \
+       git_refcount *r = &(_r)->rc; \
        int val = git_atomic_dec(&r->refcount); \
        if (val <= 0 && r->owner == NULL) { do_free(_r); } \
 }
 
 #define GIT_REFCOUNT_OWN(r, o) { \
-       ((git_refcount *)(r))->owner = o; \
+       (r)->rc.owner = o; \
 }
 
-#define GIT_REFCOUNT_OWNER(r) (((git_refcount *)(r))->owner)
+#define GIT_REFCOUNT_OWNER(r) ((r)->rc.owner)
 
-#define GIT_REFCOUNT_VAL(r) git_atomic_get(&((git_refcount *)(r))->refcount)
+#define GIT_REFCOUNT_VAL(r) git_atomic_get((r)->rc.refcount)
 
 
 static signed char from_hex[] = {
@@ -614,4 +543,4 @@ GIT_INLINE(double) git__timer(void)
 
 extern int git__getenv(git_buf *out, const char *name);
 
-#endif /* INCLUDE_util_h__ */
+#endif
index beac8c7098c1a0e43050760bf989960288e22247..9ffc1d7448cd9d07217643887de086021e1956e3 100644 (file)
@@ -5,7 +5,6 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
 #include "varint.h"
 
 uintmax_t git_decode_varint(const unsigned char *bufp, size_t *varint_len)
index 650ec7d2a78f54687bd745549accac9b0920d1e3..652e2248657104f8f85d13bf712499c106747991 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_varint_h__
 #define INCLUDE_varint_h__
 
+#include "common.h"
+
 #include <stdint.h>
 
 extern int git_encode_varint(unsigned char *, size_t, uintmax_t);
index 620a1f56cf29d8a596a38bc5121da1944bc6c258..b12fa942db0d03847befbe7b91c4f1b61ec6800a 100644 (file)
@@ -5,8 +5,8 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
 #include "vector.h"
+
 #include "integer.h"
 
 /* In elements, not bytes */
index 8a724a4e9b94274e3732bf52df6f1835cf4f1a0d..1d37874e472e94ca036acb4dc18369edc4e8ebe6 100644 (file)
@@ -4,6 +4,9 @@
  * 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"
 
index bef39d7740cad7976fdcfb287ca9ba678b5b6227..acd64729eb945b13d8153ce1ca1f3a43c7d1f91a 100644 (file)
@@ -4,10 +4,11 @@
  * This 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_dir_h__
-#define INCLUDE_dir_h__
+#ifndef INCLUDE_win32_dir_h__
+#define INCLUDE_win32_dir_h__
 
 #include "common.h"
+
 #include "w32_util.h"
 
 struct git__dirent {
@@ -40,4 +41,4 @@ extern int git__closedir(git__DIR *);
 #      define closedir git__closedir
 # endif
 
-#endif /* INCLUDE_dir_h__ */
+#endif
index 6b450093f0c777afc8408dc69542b817d44a3e72..3a52fb5a90affe49ddbde1b8e99a91f4014b07ea 100644 (file)
@@ -5,8 +5,8 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
 #include "error.h"
+
 #include "utf-conv.h"
 
 #ifdef GIT_WINHTTP
index 12947a2e67c53adbde0e9acfeb9e369b8e28d2f3..9e81141ce1ddac18fa5d376fe728d4c8c755aeaf 100644 (file)
@@ -5,8 +5,10 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#ifndef INCLUDE_git_win32_error_h__
-#define INCLUDE_git_win32_error_h__
+#ifndef INCLUDE_win32_error_h__
+#define INCLUDE_win32_error_h__
+
+#include "common.h"
 
 extern char *git_win32_get_error_message(DWORD error_code);
 
index 1c768f7f42eb91fe45a8a46bdd33085b74d9fa3d..d56aa1fd20455e225427e677b90ae65048bafcfa 100644 (file)
@@ -5,10 +5,11 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
+#include "findfile.h"
+
 #include "path_w32.h"
 #include "utf-conv.h"
 #include "path.h"
-#include "findfile.h"
 
 #define REG_MSYSGIT_INSTALL_LOCAL L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1"
 
index 3d5fff439c25e4d46189a18a55ce5939bab1afeb..e7bcf948a5c006691885d02fb18debe551406b03 100644 (file)
@@ -5,8 +5,10 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#ifndef INCLUDE_git_findfile_h__
-#define INCLUDE_git_findfile_h__
+#ifndef INCLUDE_win32_findfile_h__
+#define INCLUDE_win32_findfile_h__
+
+#include "common.h"
 
 extern int git_win32__find_system_dirs(git_buf *out, const wchar_t *subpath);
 extern int git_win32__find_global_dirs(git_buf *out);
index 5fcc1085b3a4d41712ab04c8b05ba4917a2e6ddb..6a17aeb64e7f58cb592d6d5cf7c4e3ffe63d0217 100644 (file)
@@ -5,6 +5,8 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
+#include "common.h"
+
 #include "map.h"
 #include <errno.h>
 
index 698ebed1a013a79ca76d950f538709bf43c59a13..aa2bef98df74ea8e082038b6c32cfa4c6048e6f1 100644 (file)
@@ -4,8 +4,8 @@
  * This 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_mingw_compat__
-#define INCLUDE_mingw_compat__
+#ifndef INCLUDE_win32_mingw_compat_h__
+#define INCLUDE_win32_mingw_compat_h__
 
 #if defined(__MINGW32__)
 
@@ -20,4 +20,4 @@ void __mingworg_MemoryBarrier(void);
 
 #endif
 
-#endif /* INCLUDE_mingw_compat__ */
+#endif
index 12b50d98166b1f9a7db2cfcac96f50b651fffa4a..ea77820a2e76a83a370a1d18a68605f944fd25a3 100644 (file)
@@ -4,8 +4,8 @@
  * This 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_msvc_compat__
-#define INCLUDE_msvc_compat__
+#ifndef INCLUDE_win32_msvc_compat_h__
+#define INCLUDE_win32_msvc_compat_h__
 
 #if defined(_MSC_VER)
 
@@ -19,4 +19,4 @@ typedef SSIZE_T ssize_t;
 
 #define GIT_STDLIB_CALL __cdecl
 
-#endif /* INCLUDE_msvc_compat__ */
+#endif
index 40b95c33ba14b77aeab12ce62ef3ad5080734f0a..5e24260f7c82acd60e09d41a977ed14f10107ea1 100644 (file)
@@ -5,9 +5,9 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
-#include "path.h"
 #include "path_w32.h"
+
+#include "path.h"
 #include "utf-conv.h"
 #include "posix.h"
 #include "reparse.h"
 
 #define PATH__ABSOLUTE_LEN     3
 
-#define path__is_dirsep(p) ((p) == '/' || (p) == '\\')
-
-#define path__is_absolute(p) \
-       (git__isalpha((p)[0]) && (p)[1] == ':' && ((p)[2] == '\\' || (p)[2] == '/'))
-
 #define path__is_nt_namespace(p) \
        (((p)[0] == '\\' && (p)[1] == '\\' && (p)[2] == '?' && (p)[3] == '\\') || \
         ((p)[0] == '/' && (p)[1] == '/' && (p)[2] == '?' && (p)[3] == '/'))
@@ -59,7 +54,7 @@ static wchar_t *path__skip_server(wchar_t *path)
        wchar_t *c;
 
        for (c = path; *c; c++) {
-               if (path__is_dirsep(*c))
+               if (git_path_is_dirsep(*c))
                        return c + 1;
        }
 
@@ -73,9 +68,9 @@ static wchar_t *path__skip_prefix(wchar_t *path)
 
                if (wcsncmp(path, L"UNC\\", 4) == 0)
                        path = path__skip_server(path + 4);
-               else if (path__is_absolute(path))
+               else if (git_path_is_absolute(path))
                        path += PATH__ABSOLUTE_LEN;
-       } else if (path__is_absolute(path)) {
+       } else if (git_path_is_absolute(path)) {
                path += PATH__ABSOLUTE_LEN;
        } else if (path__is_unc(path)) {
                path = path__skip_server(path + 2);
@@ -196,7 +191,7 @@ int git_win32_path_from_utf8(git_win32_path out, const char *src)
        dest += PATH__NT_NAMESPACE_LEN;
 
        /* See if this is an absolute path (beginning with a drive letter) */
-       if (path__is_absolute(src)) {
+       if (git_path_is_absolute(src)) {
                if (git__utf8_to_16(dest, MAX_PATH, src) < 0)
                        goto on_error;
        }
@@ -220,7 +215,7 @@ int git_win32_path_from_utf8(git_win32_path out, const char *src)
                if (path__cwd(dest, MAX_PATH) < 0)
                        goto on_error;
 
-               if (!path__is_absolute(dest)) {
+               if (!git_path_is_absolute(dest)) {
                        errno = ENOENT;
                        goto on_error;
                }
index 3d9f8286012b84093c753c05bf01ee95fb3e6052..83ffd1f6fb65b3d8ffab9bc25f902daa7e97eebc 100644 (file)
@@ -4,10 +4,11 @@
  * This 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_path_w32_h__
-#define INCLUDE_git_path_w32_h__
+#ifndef INCLUDE_win32_path_w32_h__
+#define INCLUDE_win32_path_w32_h__
 
 #include "common.h"
+
 #include "vector.h"
 
 /*
index 64769ecd32eebb4445925456c7314762cc84a9d0..d5ab2e8f538723234e44d53163c5e970e2b04d06 100644 (file)
@@ -4,8 +4,8 @@
  * This 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__w32_h__
-#define INCLUDE_posix__w32_h__
+#ifndef INCLUDE_win32_posix_h__
+#define INCLUDE_win32_posix_h__
 
 #include "common.h"
 #include "../posix.h"
index e4fe4142c733a89a1d32bcb1e4ba5db9d888e17a..f51e1e546abb0378e452dcd5a9943edf89c7da33 100644 (file)
@@ -4,6 +4,9 @@
  * 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 "../posix.h"
 #include "../fileops.h"
 #include "path.h"
@@ -162,12 +165,15 @@ GIT_INLINE(bool) last_error_retryable(void)
 
 #define do_with_retries(fn, remediation) \
        do {                                                             \
-               int __tries, __ret;                                          \
-               for (__tries = 0; __tries < git_win32__retries; __tries++) { \
-                       if (__tries && (__ret = (remediation)) != 0)             \
-                               return __ret;                                        \
+               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;                                                   \
@@ -186,7 +192,7 @@ static int ensure_writable(wchar_t *path)
        if (!SetFileAttributesW(path, (attrs & ~FILE_ATTRIBUTE_READONLY)))
                goto on_error;
 
-       return 0;
+       return GIT_RETRY;
 
 on_error:
        set_errno();
index 10ca0b80c2936bf7e10c797f624834ad5e9a2942..851a083d5ec48e219321fc4970c4dea63f2e48c1 100644 (file)
@@ -1,3 +1,5 @@
+#include "common.h"
+
 #include <assert.h>
 #include <errno.h>
 #include <limits.h>
@@ -20,4 +22,3 @@
 #endif
 
 #include "git2.h"
-#include "common.h"
index 70f9fd6520c3c1a5912e0234fc68e190b4aa0361..5f7408a1b39d86af865222e7633a4cf868d49280 100644 (file)
@@ -5,8 +5,8 @@
 * a Linking Exception. For full terms see the included COPYING file.
 */
 
-#ifndef INCLUDE_git_win32_reparse_h__
-#define INCLUDE_git_win32_reparse_h__
+#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
index 87318c9d37357084d78cdcbda3c9dc10e78bdecb..2d9600515bd48e02119a30da720dd90172413192 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include "thread.h"
+
 #include "../global.h"
 
 #define CLEAN_THREAD_EXIT 0x6F012842
index 7f4a2170f79ffdd84b24291b27fccc53ef16b9cf..41cbf015b59335db18393eb72e4350b523bd335f 100644 (file)
@@ -8,7 +8,7 @@
 #ifndef INCLUDE_win32_thread_h__
 #define INCLUDE_win32_thread_h__
 
-#include "../common.h"
+#include "common.h"
 
 #if defined (_MSC_VER)
 #      define GIT_RESTRICT __restrict
@@ -61,4 +61,4 @@ int git_rwlock_wrlock(git_rwlock *);
 int git_rwlock_wrunlock(git_rwlock *);
 int git_rwlock_free(git_rwlock *);
 
-#endif /* INCLUDE_win32_thread_h__ */
+#endif
index 96fd4606ecf9fcfa86ea01b7e5301942cef4adbb..4bde3023ab6b07905e2de6e82f15247f1c1b2a59 100644 (file)
@@ -5,7 +5,6 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
 #include "utf-conv.h"
 
 GIT_INLINE(void) git__set_errno(void)
index 33b95f59f27f802ef0e6f1411d01022e63769388..6090a4b356a624eced776f3135e482ca3ccf5aa4 100644 (file)
@@ -4,12 +4,13 @@
  * This 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_utfconv_h__
-#define INCLUDE_git_utfconv_h__
+#ifndef INCLUDE_win32_utf_conv_h__
+#define INCLUDE_win32_utf_conv_h__
 
-#include <wchar.h>
 #include "common.h"
 
+#include <wchar.h>
+
 #ifndef WC_ERR_INVALID_CHARS
 # define WC_ERR_INVALID_CHARS  0x80
 #endif
index 9122baaa6842a246f9a5d335ae9b44ad4c0f4fbb..45c024d31225e2a0386bb09c8e4392b0af6b91b0 100644 (file)
@@ -5,8 +5,8 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
 #include "w32_buffer.h"
+
 #include "../buffer.h"
 #include "utf-conv.h"
 
index 62243986faa704e8e141c4a03d59deac4844dcb8..43298e4a78a99ce72ff82b1010af596e3d132238 100644 (file)
@@ -4,8 +4,10 @@
  * This 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_win32_buffer_h__
-#define INCLUDE_git_win32_buffer_h__
+#ifndef INCLUDE_win32_w32_buffer_h__
+#define INCLUDE_win32_w32_buffer_h__
+
+#include "common.h"
 
 #include "../buffer.h"
 
index 2dbdaf45b3338d64e7f9c40348c72ff8141453b8..7b3c3fb4c10d0269015190acae7a1c3c8a69064c 100644 (file)
@@ -5,9 +5,10 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
+#include "w32_crtdbg_stacktrace.h"
+
 #if defined(GIT_MSVC_CRTDBG)
 #include "w32_stack.h"
-#include "w32_crtdbg_stacktrace.h"
 
 #define CRTDBG_STACKTRACE__UID_LEN (15)
 
index 40ca60d5370df7c5eb456f04f55b535e1175cc2f..3f580357e4c7b2b2387424bd382b79bf03ca7f79 100644 (file)
@@ -4,11 +4,19 @@
  * This 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_w32_crtdbg_stacktrace_h__
-#define INCLUDE_w32_crtdbg_stacktrace_h__
+#ifndef INCLUDE_win32_w32_crtdbg_stacktrace_h__
+#define INCLUDE_win32_w32_crtdbg_stacktrace_h__
+
+#include "common.h"
 
 #if defined(GIT_MSVC_CRTDBG)
 
+#include <stdlib.h>
+#include <crtdbg.h>
+
+#include "git2/errors.h"
+#include "strnlen.h"
+
 /**
  * Initialize our memory leak tracking and de-dup data structures.
  * This should ONLY be called by git_libgit2_init().
@@ -89,5 +97,80 @@ GIT_EXTERN(int) git_win32__crtdbg_stacktrace__dump(
  */
 const char *git_win32__crtdbg_stacktrace(int skip, const char *file);
 
+GIT_INLINE(void *) git__crtdbg__malloc(size_t len, const char *file, int line)
+{
+       void *ptr = _malloc_dbg(len, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
+       if (!ptr) giterr_set_oom();
+       return ptr;
+}
+
+GIT_INLINE(void *) git__crtdbg__calloc(size_t nelem, size_t elsize, const char *file, int line)
+{
+       void *ptr = _calloc_dbg(nelem, elsize, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
+       if (!ptr) giterr_set_oom();
+       return ptr;
+}
+
+GIT_INLINE(char *) git__crtdbg__strdup(const char *str, const char *file, int line)
+{
+       char *ptr = _strdup_dbg(str, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
+       if (!ptr) giterr_set_oom();
+       return ptr;
+}
+
+GIT_INLINE(char *) git__crtdbg__strndup(const char *str, size_t n, const char *file, int line)
+{
+       size_t length = 0, alloclength;
+       char *ptr;
+
+       length = p_strnlen(str, n);
+
+       if (GIT_ADD_SIZET_OVERFLOW(&alloclength, length, 1) ||
+               !(ptr = git__crtdbg__malloc(alloclength, file, line)))
+               return NULL;
+
+       if (length)
+               memcpy(ptr, str, length);
+
+       ptr[length] = '\0';
+
+       return ptr;
+}
+
+GIT_INLINE(char *) git__crtdbg__substrdup(const char *start, size_t n, const char *file, int line)
+{
+       char *ptr;
+       size_t alloclen;
+
+       if (GIT_ADD_SIZET_OVERFLOW(&alloclen, n, 1) ||
+               !(ptr = git__crtdbg__malloc(alloclen, file, line)))
+               return NULL;
+
+       memcpy(ptr, start, n);
+       ptr[n] = '\0';
+       return ptr;
+}
+
+GIT_INLINE(void *) git__crtdbg__realloc(void *ptr, size_t size, const char *file, int line)
+{
+       void *new_ptr = _realloc_dbg(ptr, size, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
+       if (!new_ptr) giterr_set_oom();
+       return new_ptr;
+}
+
+GIT_INLINE(void *) git__crtdbg__reallocarray(void *ptr, size_t nelem, size_t elsize, const char *file, int line)
+{
+       size_t newsize;
+
+       return GIT_MULTIPLY_SIZET_OVERFLOW(&newsize, nelem, elsize) ?
+               NULL : _realloc_dbg(ptr, newsize, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
+}
+
+GIT_INLINE(void *) git__crtdbg__mallocarray(size_t nelem, size_t elsize, const char *file, int line)
+{
+       return git__crtdbg__reallocarray(NULL, nelem, elsize, file, line);
+}
+
+
 #endif
 #endif
index 15af3dcb7de675a59212b8843544c850c5a707ee..b40f9d2b4d36030eb687c5b86b2be438ce4a8b99 100644 (file)
@@ -5,11 +5,12 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
+#include "w32_stack.h"
+
 #if defined(GIT_MSVC_CRTDBG)
 #include "Windows.h"
 #include "Dbghelp.h"
 #include "win32/posix.h"
-#include "w32_stack.h"
 #include "hash.h"
 
 /**
index 21170bd2f81f4adf26a47dd5a683970852c4b13f..5f0009e0baae671e269cc4bb46667a9677b09a10 100644 (file)
@@ -5,8 +5,10 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#ifndef INCLUDE_w32_stack_h__
-#define INCLUDE_w32_stack_h__
+#ifndef INCLUDE_win32_w32_stack_h__
+#define INCLUDE_win32_w32_stack_h__
+
+#include "common.h"
 
 #if defined(GIT_MSVC_CRTDBG)
 
@@ -135,4 +137,4 @@ int git_win32__stack(
        const char *prefix, const char *suffix);
 
 #endif /* GIT_MSVC_CRTDBG */
-#endif /* INCLUDE_w32_stack_h__ */
+#endif
index 77973b502634f991bbe8917196bcf58886fb2e24..6531f47a7372b6974dbbfd22d01be5b9ad9a9489 100644 (file)
@@ -5,8 +5,10 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#ifndef INCLUDE_w32_util_h__
-#define INCLUDE_w32_util_h__
+#ifndef INCLUDE_win32_w32_util_h__
+#define INCLUDE_win32_w32_util_h__
+
+#include "common.h"
 
 #include "utf-conv.h"
 #include "posix.h"
index f888fd69e094f35015a09db8afd867c910af6dd0..dee40a438f0d74f77213b2b86fcf1ac251f097d3 100644 (file)
@@ -4,8 +4,8 @@
  * This 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_compat__
-#define INCLUDE_win32_compat__
+#ifndef INCLUDE_win32_win32_compat_h__
+#define INCLUDE_win32_win32_compat_h__
 
 #include <stdint.h>
 #include <time.h>
@@ -49,4 +49,4 @@ struct p_stat {
 
 #define stat p_stat
 
-#endif /* INCLUDE_win32_compat__ */
+#endif
index ede155b6900680ee22b2fedebfbd3255061c4440..4b18db7d6a17cb7a04c6fe02e2a0e3a8e691ad4e 100644 (file)
@@ -5,14 +5,13 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "common.h"
+#include "worktree.h"
 
 #include "git2/branch.h"
 #include "git2/commit.h"
 #include "git2/worktree.h"
 
 #include "repository.h"
-#include "worktree.h"
 
 static bool is_worktree_dir(const char *dir)
 {
@@ -384,7 +383,7 @@ out:
        return err;
 }
 
-int git_worktree_lock(git_worktree *wt, char *creason)
+int git_worktree_lock(git_worktree *wt, const char *reason)
 {
        git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
        int err;
@@ -397,8 +396,8 @@ int git_worktree_lock(git_worktree *wt, char *creason)
        if ((err = git_buf_joinpath(&path, wt->gitdir_path, "locked")) < 0)
                goto out;
 
-       if (creason)
-               git_buf_attach_notowned(&buf, creason, strlen(creason));
+       if (reason)
+               git_buf_attach_notowned(&buf, reason, strlen(reason));
 
        if ((err = git_futils_writebuffer(&buf, path.ptr, O_CREAT|O_EXCL|O_WRONLY, 0644)) < 0)
                goto out;
index 57c2e65f00f5d903b184cd0e500a628f32282bdc..52d13cced669122f3c1debef3143e9a925f31ea0 100644 (file)
@@ -7,6 +7,8 @@
 #ifndef INCLUDE_worktree_h__
 #define INCLUDE_worktree_h__
 
+#include "common.h"
+
 #include "git2/common.h"
 #include "git2/worktree.h"
 
index f08f72e16a39aec40c8d488d020c9bffa55cb1d5..5b13e77a0d3eda8e4f7a9192d62a712f3ccf567e 100644 (file)
  *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  License along with this library; if not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  *  Davide Libenzi <davidel@xmailserver.org>
  *
  */
 
-#include "../util.h"
-
 #if !defined(XDIFF_H)
 #define XDIFF_H
 
 extern "C" {
 #endif /* #ifdef __cplusplus */
 
+/* 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_NEED_MINIMAL (1 << 1)
-#define XDF_IGNORE_WHITESPACE (1 << 2)
-#define XDF_IGNORE_WHITESPACE_CHANGE (1 << 3)
-#define XDF_IGNORE_WHITESPACE_AT_EOL (1 << 4)
-#define XDF_WHITESPACE_FLAGS (XDF_IGNORE_WHITESPACE | XDF_IGNORE_WHITESPACE_CHANGE | XDF_IGNORE_WHITESPACE_AT_EOL)
+#define XDF_IGNORE_BLANK_LINES (1 << 7)
 
-#define XDF_PATIENCE_DIFF (1 << 5)
-#define XDF_HISTOGRAM_DIFF (1 << 6)
+#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_IGNORE_BLANK_LINES (1 << 7)
+#define XDF_INDENT_HEURISTIC (1 << 23)
 
+/* xdemitconf_t.flags */
 #define XDL_EMIT_FUNCNAMES (1 << 0)
-#define XDL_EMIT_COMMON (1 << 1)
 #define XDL_EMIT_FUNCCONTEXT (1 << 2)
 
 #define XDL_MMB_READONLY (1 << 0)
@@ -81,6 +86,10 @@ typedef struct s_mmbuffer {
 
 typedef struct s_xpparam {
        unsigned long flags;
+
+       /* See Documentation/diff-options.txt. */
+       char **anchors;
+       size_t anchors_nr;
 } xpparam_t;
 
 typedef struct s_xdemitcb {
index f4d01b48c56bf94ea7af5b63fd063867ff7885c8..3a71ef678cdcafe94a2127529f880bba044c2781 100644 (file)
  *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  License along with this library; if not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  *  Davide Libenzi <davidel@xmailserver.org>
  *
  */
 
 #include "xinclude.h"
-#include "common.h"
 #include "integer.h"
 
 
 #define XDL_SNAKE_CNT 20
 #define XDL_K_HEUR 4
 
-
+/** Declare a function as always inlined. */
+#if defined(_MSC_VER)
+# define XDL_INLINE(type) static __inline type
+#else
+# define XDL_INLINE(type) static inline type
+#endif
 
 typedef struct s_xdpsplit {
        long i1, i2;
@@ -404,106 +408,544 @@ static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1,
 }
 
 
-int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {
-       long ix, ixo, ixs, ixref, grpsiz, nrec = xdf->nrec;
-       char *rchg = xdf->rchg, *rchgo = xdfo->rchg;
-       xrecord_t **recs = xdf->recs;
+static int recs_match(xrecord_t *rec1, xrecord_t *rec2, long flags)
+{
+       return (rec1->ha == rec2->ha &&
+               xdl_recmatch(rec1->ptr, rec1->size,
+                            rec2->ptr, rec2->size,
+                            flags));
+}
+
+/*
+ * 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 {
        /*
-        * This is the same of what GNU diff does. Move back and forward
-        * change groups for a consistent and pretty diff output. This also
-        * helps in finding joinable change groups and reduce the diff size.
+        * Is the split at the end of the file (aside from any blank lines)?
         */
-       for (ix = ixo = 0;;) {
-               /*
-                * Find the first changed line in the to-be-compacted file.
-                * We need to keep track of both indexes, so if we find a
-                * changed lines group on the other file, while scanning the
-                * to-be-compacted file, we need to skip it properly. Note
-                * that loops that are testing for changed lines on rchg* do
-                * not need index bounding since the array is prepared with
-                * a zero at position -1 and N.
-                */
-               for (; ix < nrec && !rchg[ix]; ix++)
-                       while (rchgo[ixo++]);
-               if (ix == nrec)
+       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
+
+/*
+ * 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 {
                /*
-                * Record the start of a changed-group in the to-be-compacted file
-                * and find the end of it, on both to-be-compacted and other file
-                * indexes (ix and ixo).
+                * 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:
                 */
-               ixs = ix;
-               for (ix++; rchg[ix]; ix++);
-               for (; rchgo[ixo]; ixo++);
+               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.
+ */
+XDL_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.
+ */
+XDL_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, long flags)
+{
+       if (g->end < xdf->nrec &&
+           recs_match(xdf->recs[g->start], xdf->recs[g->end], flags)) {
+               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, long flags)
+{
+       if (g->start > 0 &&
+           recs_match(xdf->recs[g->start - 1], xdf->recs[g->end - 1], flags)) {
+               xdf->rchg[--g->start] = 1;
+               xdf->rchg[--g->end] = 0;
+
+               while (xdf->rchg[g->start - 1])
+                       g->start--;
+
+               return 0;
+       } else {
+               return -1;
+       }
+}
+
+static void xdl_bug(const char *msg)
+{
+       fprintf(stderr, "BUG: %s\n", msg);
+       exit(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 {
-                       grpsiz = ix - ixs;
+                       groupsize = g.end - g.start;
 
                        /*
-                        * If the line before the current change group, is equal to
-                        * the last line of the current change group, shift backward
-                        * the group.
+                        * 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:
                         */
-                       while (ixs > 0 && recs[ixs - 1]->ha == recs[ix - 1]->ha &&
-                              xdl_recmatch(recs[ixs - 1]->ptr, recs[ixs - 1]->size, recs[ix - 1]->ptr, recs[ix - 1]->size, flags)) {
-                               rchg[--ixs] = 1;
-                               rchg[--ix] = 0;
-
-                               /*
-                                * This change might have joined two change groups,
-                                * so we try to take this scenario in account by moving
-                                * the start index accordingly (and so the other-file
-                                * end-of-group index).
-                                */
-                               for (; rchg[ixs - 1]; ixs--);
-                               while (rchgo[--ixo]);
-                       }
+                       end_matching_other = -1;
 
-                       /*
-                        * Record the end-of-group position in case we are matched
-                        * with a group of changes in the other file (that is, the
-                        * change record before the end-of-group index in the other
-                        * file is set).
-                        */
-                       ixref = rchgo[ixo - 1] ? ix: nrec;
+                       /* Shift the group backward as much as possible: */
+                       while (!group_slide_up(xdf, &g, flags))
+                               if (group_previous(xdfo, &go))
+                                       xdl_bug("group sync broken sliding up");
 
                        /*
-                        * If the first line of the current change group, is equal to
-                        * the line next of the current change group, shift forward
-                        * the group.
+                        * This is this highest that this group can be shifted.
+                        * Record its end index:
                         */
-                       while (ix < nrec && recs[ixs]->ha == recs[ix]->ha &&
-                              xdl_recmatch(recs[ixs]->ptr, recs[ixs]->size, recs[ix]->ptr, recs[ix]->size, flags)) {
-                               rchg[ixs++] = 0;
-                               rchg[ix++] = 1;
-
-                               /*
-                                * This change might have joined two change groups,
-                                * so we try to take this scenario in account by moving
-                                * the start index accordingly (and so the other-file
-                                * end-of-group index). Keep tracking the reference
-                                * index in case we are shifting together with a
-                                * corresponding group of changes in the other file.
-                                */
-                               for (; rchg[ix]; ix++);
-                               while (rchgo[++ixo])
-                                       ixref = ix;
+                       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, flags))
+                                       break;
+                               if (group_next(xdfo, &go))
+                                       xdl_bug("group sync broken sliding down");
+
+                               if (go.end > go.start)
+                                       end_matching_other = g.end;
                        }
-               } while (grpsiz != ix - ixs);
+               } while (groupsize != g.end - g.start);
 
                /*
-                * Try to move back the possibly merged group of changes, to match
-                * the recorded position in the other file.
+                * 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.
                 */
-               while (ixref < ix) {
-                       rchg[--ixs] = 1;
-                       rchg[--ix] = 0;
-                       while (rchgo[--ixo]);
+
+               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, flags))
+                                       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;
+
+                       for (shift = earliest_end; 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, flags))
+                                       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;
 }
 
index 8b81206c9af0767bd91c4b9e453f7c5c2bde47b1..8f1c7c8b0445f88514d0cb0ce868380d1711ccd2 100644 (file)
@@ -13,8 +13,8 @@
  *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  License along with this library; if not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  *  Davide Libenzi <davidel@xmailserver.org>
  *
index 600fd1fddb8ec37789ad78e3d6263460cbd81273..0ffa6553aa991946588c84ff8af3143ac24ec1f4 100644 (file)
@@ -13,8 +13,8 @@
  *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  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);
-static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb);
-
-
-
-
 static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec) {
 
        *rec = xdf->recs[ri]->ptr;
@@ -110,7 +101,7 @@ static long def_ff(const char *rec, long len, char *buf, long sz, void *priv)
 
        if (len > 0 &&
                        (isalpha((unsigned char)*rec) || /* identifier? */
-                        *rec == '_' || /* also identifier? */
+                        *rec == '_' || /* also identifier? */
                         *rec == '$')) { /* identifiers from VMS and other esoterico */
                if (len > sz)
                        len = sz;
@@ -122,22 +113,20 @@ static long def_ff(const char *rec, long len, char *buf, long sz, void *priv)
        return -1;
 }
 
-static int xdl_emit_common(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
-                           xdemitconf_t const *xecfg) {
-       xdfile_t *xdf = &xe->xdf2;
-       const char *rchg = xdf->rchg;
-       long ix;
-
-       (void)xscr;
-       (void)xecfg;
+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, xecfg->find_func_priv);
+       return xecfg->find_func(rec, len, buf, sz, xecfg->find_func_priv);
+}
 
-       for (ix = 0; ix < xdf->nrec; ix++) {
-               if (rchg[ix])
-                       continue;
-               if (xdl_emit_record(xdf, ix, "", ecb))
-                       return -1;
-       }
-       return 0;
+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 {
@@ -148,7 +137,6 @@ struct func_line {
 static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,
                          struct func_line *func_line, long start, long limit)
 {
-       find_func_t ff = xecfg->find_func ? xecfg->find_func : def_ff;
        long l, size, step = (start > limit) ? -1 : 1;
        char *buf, dummy[1];
 
@@ -156,9 +144,7 @@ static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,
        size = func_line ? sizeof(func_line->buf) : sizeof(dummy);
 
        for (l = start; l != limit && 0 <= l && l < xe->xdf1.nrec; l += step) {
-               const char *rec;
-               long reclen = xdl_get_rec(&xe->xdf1, l, &rec);
-               long len = ff(rec, reclen, buf, size, xecfg->find_func_priv);
+               long len = match_func_rec(&xe->xdf1, xecfg, l, buf, size);
                if (len >= 0) {
                        if (func_line)
                                func_line->len = len;
@@ -168,6 +154,18 @@ static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,
        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;
@@ -175,9 +173,6 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
        long funclineprev = -1;
        struct func_line func_line = { 0 };
 
-       if (xecfg->flags & XDL_EMIT_COMMON)
-               return xdl_emit_common(xe, xscr, ecb, xecfg);
-
        for (xch = xscr; xch; xch = xche->next) {
                xche = xdl_get_hunk(&xch, xecfg);
                if (!xch)
@@ -187,7 +182,33 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
                s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);
 
                if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) {
-                       long fs1 = get_func_line(xe, xecfg, NULL, xch->i1, -1);
+                       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) {
@@ -196,7 +217,7 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
                        }
                }
 
again:
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));
@@ -208,6 +229,8 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
                        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) {
@@ -221,11 +244,12 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
                         * its new end.
                         */
                        if (xche->next) {
-                               long l = xche->next->i1;
-                               if (l <= e1 ||
+                               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 again;
+                                       goto post_context_calculation;
                                }
                        }
                }
index d29710770ce40bafa6e9eb2b2ea7c9c8ba43c727..1b9887e670d48ddab89fd3e8724f442c35fc1adc 100644 (file)
@@ -13,8 +13,8 @@
  *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  License along with this library; if not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  *  Davide Libenzi <davidel@xmailserver.org>
  *
index 0c2edb89c590337b40ffbff9751e913d1042da44..ca8605e31654d0932de84ee313ae34037246ac6a 100644 (file)
@@ -44,7 +44,6 @@
 #include "xinclude.h"
 #include "xtypes.h"
 #include "xdiff.h"
-#include "common.h"
 
 #define MAX_PTR        UINT_MAX
 #define MAX_CNT        UINT_MAX
index 4a1cde9092bcf36046b997eb7db337a48d46b5dd..068ce42f8e72d63486295ffb1692589381bbe7d7 100644 (file)
@@ -13,8 +13,8 @@
  *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  License along with this library; if not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  *  Davide Libenzi <davidel@xmailserver.org>
  *
@@ -42,5 +42,6 @@
 #include "xdiffi.h"
 #include "xemit.h"
 
+#include "common.h"
 
 #endif /* #if !defined(XINCLUDE_H) */
index 165a895a93e04b33ca7c8f3839ee85e0eccb4a07..2809a28ca960147c285bc5a224ed377a0964663a 100644 (file)
@@ -13,8 +13,8 @@
  *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  License along with this library; if not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  *  Davide Libenzi <davidel@xmailserver.org>
  *
index 6448b554265a99928111e866ec9c94a18a0f6399..6fec95aa315280912b9947d212e9e33556b83ca1 100644 (file)
  *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  License along with this library; if not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  *  Davide Libenzi <davidel@xmailserver.org>
  *
  */
 
 #include "xinclude.h"
-#include "common.h"
 
 typedef struct s_xdmerge {
        struct s_xdmerge *next;
@@ -110,7 +109,7 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,
        return 0;
 }
 
-static int xdl_recs_copy_0(size_t *out, int use_orig, xdfenv_t *xe, int i, int count, int add_nl, char *dest)
+static int xdl_recs_copy_0(size_t *out, int use_orig, xdfenv_t *xe, int i, int count, int needs_cr, int add_nl, char *dest)
 {
        xrecord_t **recs;
        size_t size = 0;
@@ -132,6 +131,12 @@ static int xdl_recs_copy_0(size_t *out, int use_orig, xdfenv_t *xe, int i, int c
        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';
+                               GITERR_CHECK_ALLOC_ADD(&size, size, 1);
+                       }
+
                        if (dest)
                                dest[size] = '\n';
 
@@ -143,14 +148,58 @@ static int xdl_recs_copy_0(size_t *out, int use_orig, xdfenv_t *xe, int i, int c
        return 0;
 }
 
-static int xdl_recs_copy(size_t *out, xdfenv_t *xe, int i, int count, int add_nl, char *dest)
+static int xdl_recs_copy(size_t *out, xdfenv_t *xe, int i, int count, int needs_cr, int add_nl, char *dest)
 {
-       return xdl_recs_copy_0(out, 0, xe, i, count, add_nl, dest);
+       return xdl_recs_copy_0(out, 0, xe, i, count, needs_cr, add_nl, dest);
 }
 
-static int xdl_orig_copy(size_t *out, xdfenv_t *xe, int i, int count, int add_nl, char *dest)
+static int xdl_orig_copy(size_t *out, xdfenv_t *xe, int i, int count, int needs_cr, int add_nl, char *dest)
 {
-       return xdl_recs_copy_0(out, 1, xe, i, count, add_nl, dest);
+       return xdl_recs_copy_0(out, 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(size_t *out, xdfenv_t *xe1, const char *name1,
@@ -162,6 +211,7 @@ static int fill_conflict_hunk(size_t *out, xdfenv_t *xe1, const char *name1,
        int marker1_size = (name1 ? (int)strlen(name1) + 1 : 0);
        int marker2_size = (name2 ? (int)strlen(name2) + 1 : 0);
        int marker3_size = (name3 ? (int)strlen(name3) + 1 : 0);
+       int needs_cr = is_cr_needed(xe1, xe2, m);
        size_t copied;
 
        *out = 0;
@@ -170,14 +220,14 @@ static int fill_conflict_hunk(size_t *out, xdfenv_t *xe1, const char *name1,
                marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
 
        /* Before conflicting part */
-       if (xdl_recs_copy(&copied, xe1, i, m->i1 - i, 0,
+       if (xdl_recs_copy(&copied, xe1, i, m->i1 - i, 0, 0,
                              dest ? dest + size : NULL) < 0)
                return -1;
 
        GITERR_CHECK_ALLOC_ADD(&size, size, copied);
 
        if (!dest) {
-               GITERR_CHECK_ALLOC_ADD4(&size, size, marker_size, 1, marker1_size);
+               GITERR_CHECK_ALLOC_ADD5(&size, size, marker_size, 1, needs_cr,  marker1_size);
        } else {
                memset(dest + size, '<', marker_size);
                size += marker_size;
@@ -186,11 +236,13 @@ static int fill_conflict_hunk(size_t *out, xdfenv_t *xe1, const char *name1,
                        memcpy(dest + size + 1, name1, marker1_size - 1);
                        size += marker1_size;
                }
+               if (needs_cr)
+                       dest[size++] = '\r';
                dest[size++] = '\n';
        }
 
        /* Postimage from side #1 */
-       if (xdl_recs_copy(&copied, xe1, m->i1, m->chg1, 1,
+       if (xdl_recs_copy(&copied, xe1, m->i1, m->chg1, needs_cr, 1,
                              dest ? dest + size : NULL) < 0)
                return -1;
 
@@ -199,7 +251,7 @@ static int fill_conflict_hunk(size_t *out, xdfenv_t *xe1, const char *name1,
        if (style == XDL_MERGE_DIFF3) {
                /* Shared preimage */
                if (!dest) {
-                       GITERR_CHECK_ALLOC_ADD4(&size, size, marker_size, 1, marker3_size);
+                       GITERR_CHECK_ALLOC_ADD5(&size, size, marker_size, 1, needs_cr, marker3_size);
                } else {
                        memset(dest + size, '|', marker_size);
                        size += marker_size;
@@ -208,32 +260,36 @@ static int fill_conflict_hunk(size_t *out, xdfenv_t *xe1, const char *name1,
                                memcpy(dest + size + 1, name3, marker3_size - 1);
                                size += marker3_size;
                        }
+                       if (needs_cr)
+                               dest[size++] = '\r';
                        dest[size++] = '\n';
                }
 
-               if (xdl_orig_copy(&copied, xe1, m->i0, m->chg0, 1,
+               if (xdl_orig_copy(&copied, xe1, m->i0, m->chg0, needs_cr, 1,
                                      dest ? dest + size : NULL) < 0)
                        return -1;
                GITERR_CHECK_ALLOC_ADD(&size, size, copied);
        }
 
        if (!dest) {
-               GITERR_CHECK_ALLOC_ADD3(&size, size, marker_size, 1);
+               GITERR_CHECK_ALLOC_ADD4(&size, 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 */
 
-       if (xdl_recs_copy(&copied, xe2, m->i2, m->chg2, 1,
+       if (xdl_recs_copy(&copied, xe2, m->i2, m->chg2, needs_cr, 1,
                              dest ? dest + size : NULL) < 0)
                return -1;
        GITERR_CHECK_ALLOC_ADD(&size, size, copied);
 
        if (!dest) {
-               GITERR_CHECK_ALLOC_ADD4(&size, size, marker_size, 1, marker2_size);
+               GITERR_CHECK_ALLOC_ADD5(&size, size, marker_size, 1, needs_cr, marker2_size);
        } else {
                memset(dest + size, '>', marker_size);
                size += marker_size;
@@ -242,6 +298,8 @@ static int fill_conflict_hunk(size_t *out, xdfenv_t *xe1, const char *name1,
                        memcpy(dest + size + 1, name2, marker2_size - 1);
                        size += marker2_size;
                }
+               if (needs_cr)
+                       dest[size++] = '\r';
                dest[size++] = '\n';
        }
 
@@ -275,14 +333,16 @@ static int xdl_fill_merge_buffer(size_t *out,
                }
                else if (m->mode & 3) {
                        /* Before conflicting part */
-                       if (xdl_recs_copy(&copied, xe1, i, m->i1 - i, 0,
+                       if (xdl_recs_copy(&copied, xe1, i, m->i1 - i, 0, 0,
                                              dest ? dest + size : NULL) < 0)
                                return -1;
                        GITERR_CHECK_ALLOC_ADD(&size, size, copied);
 
                        /* Postimage from side #1 */
                        if (m->mode & 1) {
-                               if (xdl_recs_copy(&copied, xe1, m->i1, m->chg1, (m->mode & 2),
+                               int needs_cr = is_cr_needed(xe1, xe2, m);
+
+                               if (xdl_recs_copy(&copied, xe1, m->i1, m->chg1, needs_cr, (m->mode & 2),
                                                      dest ? dest + size : NULL) < 0)
                                        return -1;
                                GITERR_CHECK_ALLOC_ADD(&size, size, copied);
@@ -290,7 +350,7 @@ static int xdl_fill_merge_buffer(size_t *out,
 
                        /* Postimage from side #2 */
                        if (m->mode & 2) {
-                               if (xdl_recs_copy(&copied, xe2, m->i2, m->chg2, 0,
+                               if (xdl_recs_copy(&copied, xe2, m->i2, m->chg2, 0, 0,
                                                      dest ? dest + size : NULL) < 0)
                                        return -1;
                                GITERR_CHECK_ALLOC_ADD(&size, size, copied);
@@ -300,7 +360,7 @@ static int xdl_fill_merge_buffer(size_t *out,
                i = m->i1 + m->chg1;
        }
 
-       if (xdl_recs_copy(&copied, xe1, i, xe1->xdf2.nrec - i, 0,
+       if (xdl_recs_copy(&copied, xe1, i, xe1->xdf2.nrec - i, 0, 0,
                              dest ? dest + size : NULL) < 0)
                return -1;
        GITERR_CHECK_ALLOC_ADD(&size, size, copied);
index 04e1a1ab2a863814df3b9a91d4e854704d47f3f5..cedf39cc35079e4b4cfecf4639b96dd367e0ed2e 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  LibXDiff by Davide Libenzi ( File Differential Library )
- *  Copyright (C) 2003-2009 Davide Libenzi, Johannes E. Schindelin
+ *  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
@@ -13,8 +13,8 @@
  *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  License along with this library; if not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  *  Davide Libenzi <davidel@xmailserver.org>
  *
@@ -62,6 +62,12 @@ struct hashmap {
                 * 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;
@@ -70,8 +76,19 @@ struct hashmap {
        xpparam_t const *xpp;
 };
 
+static int is_anchor(xpparam_t const *xpp, const char *line)
+{
+       unsigned long 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(int line, struct hashmap *map, int pass)
+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;
@@ -110,6 +127,7 @@ static void insert_record(int line, struct hashmap *map, int pass)
                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) {
@@ -147,11 +165,11 @@ static int fill_hashmap(mmfile_t *file1, mmfile_t *file2,
 
        /* First, fill with entries from the first file */
        while (count1--)
-               insert_record(line1++, result, 1);
+               insert_record(xpp, line1++, result, 1);
 
        /* Then search for matches in the second file */
        while (count2--)
-               insert_record(line2++, result, 2);
+               insert_record(xpp, line2++, result, 2);
 
        return 0;
 }
@@ -166,7 +184,7 @@ static int binary_search(struct entry **sequence, int longest,
        int left = -1, right = longest;
 
        while (left + 1 < right) {
-               int middle = (left + right) / 2;
+               int middle = left + (right - left) / 2;
                /* by construction, no two entries can be equal */
                if (sequence[middle]->line2 > entry->line2)
                        right = middle;
@@ -192,14 +210,28 @@ static struct entry *find_longest_common_sequence(struct hashmap *map)
        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;
+
        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];
-               sequence[++i] = entry;
-               if (i == longest)
+               ++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 */
index 13b55aba7441bc84d2c5c075110e9ef798ba18f8..abeb8fb84e6d73086d612b831963a227e35743b8 100644 (file)
@@ -13,8 +13,8 @@
  *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  License along with this library; if not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  *  Davide Libenzi <davidel@xmailserver.org>
  *
index 8fb06a537451cbf3335ab4bdacb0f992e9744338..947d9fc1bb8cf95719284de6563227485907988f 100644 (file)
@@ -13,8 +13,8 @@
  *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  License along with this library; if not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  *  Davide Libenzi <davidel@xmailserver.org>
  *
index 2511aef8d89ab52be5ec6a5e46236b4b6bcd07ea..8442bd436efeab81afc25db9d89da082638fcca4 100644 (file)
@@ -13,8 +13,8 @@
  *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  License along with this library; if not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  *  Davide Libenzi <davidel@xmailserver.org>
  *
index 30f2a30ac71546dcbad7c4a7c1e279d662ee1f7b..17c9ae184b6f526069290a647cae236cb69c8ba5 100644 (file)
@@ -13,8 +13,8 @@
  *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  License along with this library; if not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  *  Davide Libenzi <davidel@xmailserver.org>
  *
@@ -62,14 +62,14 @@ int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize,
 
 void *xdl_mmfile_first(mmfile_t *mmf, long *size)
 {
-       *size = (long)mmf->size;
+       *size = mmf->size;
        return mmf->ptr;
 }
 
 
 long xdl_mmfile_size(mmfile_t *mmf)
 {
-       return (long)mmf->size;
+       return mmf->size;
 }
 
 
@@ -154,6 +154,24 @@ int xdl_blankline(const char *line, long size, long flags)
        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;
@@ -168,7 +186,8 @@ int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
 
        /*
         * -w matches everything that matches with -b, and -b in turn
-        * matches everything that matches with --ignore-space-at-eol.
+        * 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.
@@ -198,8 +217,18 @@ int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
                                return 0;
                }
        } else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL) {
-               while (i1 < s1 && i2 < s2 && l1[i1++] == l2[i2++])
-                       ; /* keep going */
+               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));
        }
 
        /*
@@ -226,9 +255,16 @@ 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 (XDL_ISSPACE(*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])
@@ -260,7 +296,6 @@ static unsigned long xdl_hash_record_with_whitespace(char const **data,
        return ha;
 }
 
-
 unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
        unsigned long ha = 5381;
        char const *ptr = *data;
@@ -277,7 +312,6 @@ unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
        return ha;
 }
 
-
 unsigned int xdl_hashbits(unsigned int size) {
        unsigned int val = 1, bits = 0;
 
@@ -305,23 +339,9 @@ int xdl_num_out(char *out, long val) {
                *str++ = '0';
        *str = '\0';
 
-       return (int)(str - out);
-}
-
-
-long xdl_atol(char const *str, char const **next) {
-       long val, base;
-       char const *top;
-
-       for (top = str; XDL_ISDIGIT(*top); top++);
-       if (next)
-               *next = top;
-       for (val = 0, base = 1, top--; top >= str; top--, base *= 10)
-               val += base * (long)(*top - '0');
-       return val;
+       return str - out;
 }
 
-
 int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
                      const char *func, long funclen, xdemitcb_t *ecb) {
        int nb = 0;
@@ -356,8 +376,8 @@ int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
        nb += 3;
        if (func && funclen) {
                buf[nb++] = ' ';
-               if (funclen > (long)sizeof(buf) - nb - 1)
-                       funclen = (long)sizeof(buf) - nb - 1;
+               if (funclen > (long)(sizeof(buf) - nb - 1))
+                       funclen = sizeof(buf) - nb - 1;
                memcpy(buf + nb, func, funclen);
                nb += funclen;
        }
index 8f952a8e627ef1b108ac384af5ee147ec192e057..fba7bae03c7855ca90aff3f238321581a91a6676 100644 (file)
@@ -13,8 +13,8 @@
  *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *  License along with this library; if not, see
+ *  <http://www.gnu.org/licenses/>.
  *
  *  Davide Libenzi <davidel@xmailserver.org>
  *
@@ -31,15 +31,12 @@ int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize,
 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);
-void *xdl_cha_first(chastore_t *cha);
-void *xdl_cha_next(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);
-long xdl_atol(char const *str, char const **next);
 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,
index 141b49b27c2d8769e50a9ee3c584154af29ef8bc..affa55653f428d056983ecb935cc224df3b436ac 100644 (file)
@@ -5,25 +5,31 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
+#include "zstream.h"
+
 #include <zlib.h>
 
-#include "zstream.h"
 #include "buffer.h"
 
 #define ZSTREAM_BUFFER_SIZE (1024 * 1024)
 #define ZSTREAM_BUFFER_MIN_EXTRA 8
 
-static int zstream_seterr(git_zstream *zs)
+GIT_INLINE(int) zstream_seterr(git_zstream *zs)
 {
-       if (zs->zerr == Z_OK || zs->zerr == Z_STREAM_END)
+       switch (zs->zerr) {
+       case Z_OK:
+       case Z_STREAM_END:
+       case Z_BUF_ERROR: /* not fatal; we retry with a larger buffer */
                return 0;
-
-       if (zs->zerr == Z_MEM_ERROR)
+       case Z_MEM_ERROR:
                giterr_set_oom();
-       else if (zs->z.msg)
-               giterr_set_str(GITERR_ZLIB, zs->z.msg);
-       else
-               giterr_set(GITERR_ZLIB, "unknown compression error");
+               break;
+       default:
+               if (zs->z.msg)
+                       giterr_set_str(GITERR_ZLIB, zs->z.msg);
+               else
+                       giterr_set(GITERR_ZLIB, "unknown compression error");
+       }
 
        return -1;
 }
@@ -81,9 +87,52 @@ size_t git_zstream_suggest_output_len(git_zstream *zstream)
                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)
 {
-       int zflush = Z_FINISH;
        size_t out_remain = *out_len;
 
        if (zstream->in_len && zstream->zerr == Z_STREAM_END) {
@@ -92,46 +141,17 @@ int git_zstream_get_output(void *out, size_t *out_len, git_zstream *zstream)
        }
 
        while (out_remain > 0 && zstream->zerr != Z_STREAM_END) {
-               size_t out_queued, in_queued, out_used, in_used;
-
-               /* set up in data */
-               zstream->z.next_in  = (Bytef *)zstream->in;
-               zstream->z.avail_in = (uInt)zstream->in_len;
-               if ((size_t)zstream->z.avail_in != zstream->in_len) {
-                       zstream->z.avail_in = INT_MAX;
-                       zflush = Z_NO_FLUSH;
-               } else {
-                       zflush = Z_FINISH;
-               }
-               in_queued = (size_t)zstream->z.avail_in;
-
-               /* set up out data */
-               zstream->z.next_out = out;
-               zstream->z.avail_out = (uInt)out_remain;
-               if ((size_t)zstream->z.avail_out != out_remain)
-                       zstream->z.avail_out = INT_MAX;
-               out_queued = (size_t)zstream->z.avail_out;
-
-               /* compress next chunk */
-               if (zstream->type == GIT_ZSTREAM_INFLATE)
-                       zstream->zerr = inflate(&zstream->z, zflush);
-               else
-                       zstream->zerr = deflate(&zstream->z, zflush);
-
-               if (zstream->zerr == Z_STREAM_ERROR)
-                       return zstream_seterr(zstream);
+               size_t out_written = out_remain;
 
-               out_used = (out_queued - zstream->z.avail_out);
-               out_remain -= out_used;
-               out = ((char *)out) + out_used;
+               if (git_zstream_get_output_chunk(out, &out_written, zstream) < 0)
+                       return -1;
 
-               in_used = (in_queued - zstream->z.avail_in);
-               zstream->in_len -= in_used;
-               zstream->in += in_used;
+               out_remain -= out_written;
+               out = ((char *)out) + out_written;
        }
 
        /* either we finished the input or we did not flush the data */
-       assert(zstream->in_len > 0 || zflush == Z_FINISH);
+       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;
index f0006d32e3658b8b57957327c6587723862735e3..47ecc1322b484d70f5571055f4c1e991bdd42800 100644 (file)
@@ -7,9 +7,10 @@
 #ifndef INCLUDE_zstream_h__
 #define INCLUDE_zstream_h__
 
+#include "common.h"
+
 #include <zlib.h>
 
-#include "common.h"
 #include "buffer.h"
 
 typedef enum {
@@ -22,6 +23,7 @@ typedef struct {
        git_zstream_t type;
        const char *in;
        size_t in_len;
+       int flush;
        int zerr;
 } git_zstream;
 
@@ -34,6 +36,11 @@ 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);
@@ -43,4 +50,4 @@ void git_zstream_reset(git_zstream *zstream);
 int git_zstream_deflatebuf(git_buf *out, const void *in, size_t in_len);
 int git_zstream_inflatebuf(git_buf *out, const void *in, size_t in_len);
 
-#endif /* INCLUDE_zstream_h__ */
+#endif
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644 (file)
index 0000000..775f33f
--- /dev/null
@@ -0,0 +1,63 @@
+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_CLAR=OFF to skip building the tests")
+ENDIF()
+
+SET(CLAR_FIXTURES "${CMAKE_CURRENT_SOURCE_DIR}/resources/")
+SET(CLAR_PATH "${CMAKE_CURRENT_SOURCE_DIR}")
+ADD_DEFINITIONS(-DCLAR_FIXTURE_PATH=\"${CLAR_FIXTURES}\")
+ADD_DEFINITIONS(-DCLAR_TMPDIR=\"libgit2_tests\")
+
+INCLUDE_DIRECTORIES(${CLAR_PATH} ${libgit2_BINARY_DIR}/src)
+FILE(GLOB_RECURSE SRC_TEST ${CLAR_PATH}/*/*.c ${CLAR_PATH}/*/*.h)
+SET(SRC_CLAR "main.c" "clar_libgit2.c" "clar_libgit2_trace.c" "clar_libgit2_timer.c" "clar.c")
+
+IF(MSVC_IDE)
+       LIST(APPEND SRC_CLAR "precompiled.c")
+ENDIF()
+
+ADD_CUSTOM_COMMAND(
+       OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/clar.suite
+       COMMAND ${PYTHON_EXECUTABLE} generate.py -o "${CMAKE_CURRENT_BINARY_DIR}" -f -xonline -xstress -xperf .
+       DEPENDS ${SRC_TEST}
+       WORKING_DIRECTORY ${CLAR_PATH}
+)
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
+
+SET_SOURCE_FILES_PROPERTIES(
+       ${CLAR_PATH}/clar.c
+       PROPERTIES OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/clar.suite)
+
+LINK_DIRECTORIES(${LIBGIT2_LIBDIRS})
+INCLUDE_DIRECTORIES(${LIBGIT2_INCLUDES})
+
+ADD_EXECUTABLE(libgit2_clar ${SRC_CLAR} ${SRC_TEST} ${LIBGIT2_OBJECTS})
+
+SET_TARGET_PROPERTIES(libgit2_clar PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${libgit2_BINARY_DIR})
+
+IF (${CMAKE_VERSION} VERSION_LESS 2.8.12)
+       # Already handled by a global INCLUDE_DIRECTORY()
+ELSE()
+       TARGET_INCLUDE_DIRECTORIES(libgit2_clar PRIVATE ../src PUBLIC ../include)
+ENDIF()
+
+TARGET_LINK_LIBRARIES(libgit2_clar ${LIBGIT2_LIBS})
+IDE_SPLIT_SOURCES(libgit2_clar)
+
+IF (MSVC_IDE)
+       # Precompiled headers
+       SET_TARGET_PROPERTIES(libgit2_clar PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h")
+       SET_SOURCE_FILES_PROPERTIES("precompiled.c" COMPILE_FLAGS "/Ycprecompiled.h")
+ENDIF ()
+
+IF (USE_HTTPS)
+       ADD_TEST(libgit2_clar "${libgit2_BINARY_DIR}/libgit2_clar" -ionline -xclone::local::git_style_unc_paths -xclone::local::standard_unc_paths_are_written_git_style)
+ELSE ()
+       ADD_TEST(libgit2_clar "${libgit2_BINARY_DIR}/libgit2_clar" -v -xclone::local::git_style_unc_paths -xclone::local::standard_unc_paths_are_written_git_style)
+ENDIF ()
+
+# Add additional test targets that require special setup
+ADD_TEST(libgit2_clar-proxy_credentials "${libgit2_BINARY_DIR}/libgit2_clar" -v -sonline::clone::proxy_credentials_in_url -sonline::clone::proxy_credentials_request)
+ADD_TEST(libgit2_clar-ssh "${libgit2_BINARY_DIR}/libgit2_clar" -v -sonline::push -sonline::clone::ssh_cert -sonline::clone::ssh_with_paths)
index a089ee4080ed96f80f4bc27489cce40d2fb43eb1..d241c03b558748d60c0e615ab7d3810b7d0c158e 100644 (file)
@@ -51,6 +51,16 @@ void test_attr_ignore__allow_root(void)
        assert_is_ignored(false, "NewFolder/NewFolder/File.txt");
 }
 
+void test_attr_ignore__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_attr_ignore__ignore_root(void)
 {
        cl_git_rewritefile("attr/.gitignore", "/\n\n/NewFolder\n/NewFolder/NewFolder");
@@ -303,3 +313,46 @@ void test_attr_ignore__test(void)
        assert_is_ignored(true, "dist/foo.o");
        assert_is_ignored(true, "bin/foo");
 }
+
+void test_attr_ignore__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_attr_ignore__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_attr_ignore__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");
+}
diff --git a/tests/buf/percent.c b/tests/buf/percent.c
new file mode 100644 (file)
index 0000000..60534a0
--- /dev/null
@@ -0,0 +1,49 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+
+static void expect_decode_pass(const char *expected, const char *encoded)
+{
+       git_buf in = GIT_BUF_INIT, out = GIT_BUF_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_buf_puts(&in, encoded));
+       cl_git_pass(git_buf_PUTS(&in, "TRAILER"));
+
+       cl_git_pass(git_buf_decode_percent(&out, in.ptr, strlen(encoded)));
+
+       cl_assert_equal_s(expected, git_buf_cstr(&out));
+       cl_assert_equal_i(strlen(expected), git_buf_len(&out));
+
+       git_buf_free(&in);
+       git_buf_free(&out);
+}
+
+void test_buf_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_buf_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%");
+}
index ded86df33fd63661a71510e6e3e166498e9fb2d5..99061466f10c87a46343a18b5df59468f238e7f6 100644 (file)
@@ -136,3 +136,131 @@ void test_checkout_head__do_remove_tracked_subdir(void)
        cl_assert(!git_path_isfile("testrepo/subdir/tracked-file"));
        cl_assert(git_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);
+}
index b3b860c63c5453a4f81aee0612ba6a6e83b66375..a7e29b3db496fb99f4339b90929f6bce46fafba8 100644 (file)
@@ -10,6 +10,16 @@ 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");
@@ -1086,6 +1096,8 @@ void test_checkout_tree__filemode_preserved_in_workdir(void)
        cl_assert(!GIT_PERMS_IS_EXEC(read_filemode("a/b.txt")));
 
        git_commit_free(commit);
+#else
+       cl_skip();
 #endif
 }
 
@@ -1480,7 +1492,6 @@ 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;
-       git_status_list *status;
        size_t conflicts = 0;
 
        assert_on_branch(g_repo, "master");
@@ -1506,14 +1517,49 @@ void test_checkout_tree__baseline_is_empty_when_no_index(void)
        opts.checkout_strategy |= GIT_CHECKOUT_FORCE;
        cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
 
-       cl_git_pass(git_status_list_new(&status, g_repo, NULL));
-       cl_assert_equal_i(0, git_status_list_entrycount(status));
-       git_status_list_free(status);
+       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_OBJ_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"));
+
+       assert_status_entrycount(g_repo, 1);
+       cl_git_pass(git_checkout_tree(g_repo, obj, &g_opts));
+       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));
index 905d67db72c3f937c2582c6c32e11f44b91daa30..d5212d1caf2c814b913299ffe9ed317959a822ba 100644 (file)
@@ -313,11 +313,18 @@ 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] != '-')
+               if (argument[0] != '-' || argument[1] == '\0'
+                   || strchr("sixvqQl", argument[1]) == NULL) {
                        clar_usage(argv[0]);
+               }
+       }
+
+       for (i = 1; i < argc; ++i) {
+               char *argument = argv[i];
 
                switch (argument[1]) {
                case 's':
@@ -391,7 +398,7 @@ clar_parse_args(int argc, char **argv)
                        break;
 
                default:
-                       clar_usage(argv[0]);
+                       assert(!"Unexpected commandline argument!");
                }
        }
 }
index 6529b6b4c8ec807eb0d4724164b6ad1aee6aafa3..916d807c1c554ad43caf361b80f98ef4d813b129 100644 (file)
@@ -3,7 +3,7 @@ static void clar_print_init(int test_count, int suite_count, const char *suite_n
 {
        (void)test_count;
        printf("Loaded %d suites: %s\n", (int)suite_count, suite_names);
-       printf("Started\n");
+       printf("Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')\n");
 }
 
 static void clar_print_shutdown(int test_count, int suite_count, int error_count)
index 30bc92967c340ef6766210a3cab2e6056a801c91..286079fa2faa9ce0159b1515c7970f837fbafd46 100644 (file)
@@ -1,4 +1,5 @@
 #include "clar_libgit2.h"
+#include "signature.h"
 
 static int try_build_signature(const char *name, const char *email, git_time_t time, int offset)
 {
@@ -99,3 +100,29 @@ void test_commit_signature__from_buf(void)
        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);
+}
index 3fd6eb1147876740732eb51fb8b96d27bc794e02..18c4ca59e3b83121cd13d18c67a79ba56fe013b2 100644 (file)
@@ -10,13 +10,13 @@ void test_config_backend__checks_version(void)
        backend.version = 1024;
 
        cl_git_pass(git_config_new(&cfg));
-       cl_git_fail(git_config_add_backend(cfg, &backend, 0, false));
+       cl_git_fail(git_config_add_backend(cfg, &backend, 0, NULL, false));
        err = giterr_last();
        cl_assert_equal_i(GITERR_INVALID, err->klass);
 
        giterr_clear();
        backend.version = 1024;
-       cl_git_fail(git_config_add_backend(cfg, &backend, 0, false));
+       cl_git_fail(git_config_add_backend(cfg, &backend, 0, NULL, false));
        err = giterr_last();
        cl_assert_equal_i(GITERR_INVALID, err->klass);
 
diff --git a/tests/config/conditionals.c b/tests/config/conditionals.c
new file mode 100644 (file)
index 0000000..3a87de2
--- /dev/null
@@ -0,0 +1,103 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "fileops.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_config *cfg;
+       git_buf buf = GIT_BUF_INIT;
+
+       git_buf_printf(&buf, "[includeIf \"%s:%s\"]\n", keyword, path);
+       git_buf_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();
+
+       cl_git_pass(git_repository_config(&cfg, _repo));
+
+       if (expected) {
+               git_buf_clear(&buf);
+               cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar"));
+               cl_assert_equal_s("baz", git_buf_cstr(&buf));
+       } else {
+               cl_git_fail_with(GIT_ENOTFOUND,
+                                git_config_get_string_buf(&buf, cfg, "foo.bar"));
+       }
+
+       git_buf_free(&buf);
+       git_config_free(cfg);
+}
+
+void test_config_conditionals__gitdir(void)
+{
+       git_buf path = GIT_BUF_INIT;
+       char *sandbox_path;
+
+       assert_condition_includes("gitdir", ROOT_PREFIX "/", true);
+       assert_condition_includes("gitdir", "empty_standard_repo", true);
+       assert_condition_includes("gitdir", "empty_standard_repo/", true);
+       assert_condition_includes("gitdir", "./", true);
+
+       assert_condition_includes("gitdir", ROOT_PREFIX "/nonexistent", false);
+       assert_condition_includes("gitdir", ROOT_PREFIX "/empty_standard_repo", false);
+       assert_condition_includes("gitdir", "empty_stand", false);
+       assert_condition_includes("gitdir", "~/empty_standard_repo", false);
+
+       sandbox_path = p_realpath(clar_sandbox_path(), NULL);
+
+       git_buf_joinpath(&path, sandbox_path, "/");
+       assert_condition_includes("gitdir", path.ptr, true);
+
+       git_buf_joinpath(&path, sandbox_path, "/*");
+       assert_condition_includes("gitdir", path.ptr, true);
+
+       git_buf_joinpath(&path, sandbox_path, "empty_standard_repo");
+       assert_condition_includes("gitdir", path.ptr, true);
+
+       git_buf_joinpath(&path, sandbox_path, "Empty_Standard_Repo");
+       assert_condition_includes("gitdir", path.ptr, false);
+
+       git__free(sandbox_path);
+       git_buf_free(&path);
+}
+
+void test_config_conditionals__gitdir_i(void)
+{
+       git_buf path = GIT_BUF_INIT;
+       char *sandbox_path;
+
+       sandbox_path = p_realpath(clar_sandbox_path(), NULL);
+
+       git_buf_joinpath(&path, sandbox_path, "empty_standard_repo");
+       assert_condition_includes("gitdir/i", path.ptr, true);
+
+       git_buf_joinpath(&path, sandbox_path, "EMPTY_STANDARD_REPO");
+       assert_condition_includes("gitdir/i", path.ptr, true);
+
+       git__free(sandbox_path);
+       git_buf_free(&path);
+}
+
+void test_config_conditionals__invalid_conditional_fails(void)
+{
+       assert_condition_includes("foobar", ".git", false);
+}
index ca478b1a5327d0c578940e0040c6d5f2701dc481..b73656cb93373488c48ca1047c016a02538f9fcc 100644 (file)
@@ -7,11 +7,11 @@ void test_config_configlevel__adding_the_same_level_twice_returns_EEXISTS(void)
 
        cl_git_pass(git_config_new(&cfg));
        cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
-               GIT_CONFIG_LEVEL_LOCAL, 0));
+               GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
        cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
-               GIT_CONFIG_LEVEL_GLOBAL, 0));
+               GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
        error = git_config_add_file_ondisk(cfg, cl_fixture("config/config16"),
-               GIT_CONFIG_LEVEL_GLOBAL, 0);
+               GIT_CONFIG_LEVEL_GLOBAL, NULL, 0);
 
        cl_git_fail(error);
        cl_assert_equal_i(GIT_EEXISTS, error);
@@ -26,9 +26,9 @@ void test_config_configlevel__can_replace_a_config_file_at_an_existing_level(voi
 
        cl_git_pass(git_config_new(&cfg));
        cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config18"),
-               GIT_CONFIG_LEVEL_LOCAL, 1));
+               GIT_CONFIG_LEVEL_LOCAL, NULL, 1));
        cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config19"),
-               GIT_CONFIG_LEVEL_LOCAL, 1));
+               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);
@@ -45,9 +45,9 @@ void test_config_configlevel__can_read_from_a_single_level_focused_file_after_pa
 
        cl_git_pass(git_config_new(&cfg));
        cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config18"),
-               GIT_CONFIG_LEVEL_GLOBAL, 0));
+               GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
        cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config19"),
-               GIT_CONFIG_LEVEL_LOCAL, 0));
+               GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
 
        cl_git_pass(git_config_open_level(&single_level_cfg, cfg, GIT_CONFIG_LEVEL_LOCAL));
 
index 0a07c9b85144151e27b291c6f532bd34ce0e7985..e440b9a78fe1ffc2cc29264ea0c008743c9347fb 100644 (file)
@@ -2,25 +2,31 @@
 #include "buffer.h"
 #include "fileops.h"
 
-void test_config_include__relative(void)
+static git_config *cfg;
+static git_buf buf;
+
+void test_config_include__initialize(void)
+{
+    cfg = NULL;
+    git_buf_init(&buf, 0);
+}
+
+void test_config_include__cleanup(void)
 {
-       git_config *cfg;
-       git_buf buf = GIT_BUF_INIT;
+    git_config_free(cfg);
+    git_buf_free(&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", git_buf_cstr(&buf));
-
-       git_buf_free(&buf);
-       git_config_free(cfg);
 }
 
 void test_config_include__absolute(void)
 {
-       git_config *cfg;
-       git_buf buf = GIT_BUF_INIT;
-
        cl_git_pass(git_buf_printf(&buf, "[include]\npath = %s/config-included", cl_fixture("config")));
 
        cl_git_mkfile("config-include-absolute", git_buf_cstr(&buf));
@@ -29,16 +35,10 @@ void test_config_include__absolute(void)
 
        cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar.baz"));
        cl_assert_equal_s("huzzah", git_buf_cstr(&buf));
-
-       git_buf_free(&buf);
-       git_config_free(cfg);
 }
 
 void test_config_include__homedir(void)
 {
-       git_config *cfg;
-       git_buf buf = GIT_BUF_INIT;
-
        cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, cl_fixture("config")));
        cl_git_mkfile("config-include-homedir",  "[include]\npath = ~/config-included");
 
@@ -47,18 +47,12 @@ void test_config_include__homedir(void)
        cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar.baz"));
        cl_assert_equal_s("huzzah", git_buf_cstr(&buf));
 
-       git_buf_free(&buf);
-       git_config_free(cfg);
-
        cl_sandbox_set_search_path_defaults();
 }
 
 /* We need to pretend that the variables were defined where the file was included */
 void test_config_include__ordering(void)
 {
-       git_config *cfg;
-       git_buf buf = GIT_BUF_INIT;
-
        cl_git_mkfile("included", "[foo \"bar\"]\nbaz = hurrah\nfrotz = hiya");
        cl_git_mkfile("including",
                      "[foo \"bar\"]\nfrotz = hello\n"
@@ -72,16 +66,11 @@ void test_config_include__ordering(void)
        git_buf_clear(&buf);
        cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar.baz"));
        cl_assert_equal_s("huzzah", git_buf_cstr(&buf));
-
-       git_buf_free(&buf);
-       git_config_free(cfg);
 }
 
 /* We need to pretend that the variables were defined where the file was included */
 void test_config_include__depth(void)
 {
-       git_config *cfg;
-
        cl_git_mkfile("a", "[include]\npath = b");
        cl_git_mkfile("b", "[include]\npath = a");
 
@@ -93,9 +82,6 @@ void test_config_include__depth(void)
 
 void test_config_include__missing(void)
 {
-       git_config *cfg;
-       git_buf buf = GIT_BUF_INIT;
-
        cl_git_mkfile("including", "[include]\npath = nonexistentfile\n[foo]\nbar = baz");
 
        giterr_clear();
@@ -103,16 +89,10 @@ void test_config_include__missing(void)
        cl_assert(giterr_last() == NULL);
        cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar"));
        cl_assert_equal_s("baz", git_buf_cstr(&buf));
-
-       git_buf_free(&buf);
-       git_config_free(cfg);
 }
 
 void test_config_include__missing_homedir(void)
 {
-       git_config *cfg;
-       git_buf buf = GIT_BUF_INIT;
-
        cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, cl_fixture("config")));
        cl_git_mkfile("including", "[include]\npath = ~/.nonexistentfile\n[foo]\nbar = baz");
 
@@ -122,17 +102,12 @@ void test_config_include__missing_homedir(void)
        cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar"));
        cl_assert_equal_s("baz", git_buf_cstr(&buf));
 
-       git_buf_free(&buf);
-       git_config_free(cfg);
-
        cl_sandbox_set_search_path_defaults();
 }
 
 #define replicate10(s) s s s s s s s s s s
 void test_config_include__depth2(void)
 {
-       git_config *cfg;
-       git_buf buf = GIT_BUF_INIT;
        const char *content = "[include]\n" replicate10(replicate10("path=bottom\n"));
 
        cl_git_mkfile("top-level", "[include]\npath = middle\n[foo]\nbar = baz");
@@ -147,7 +122,45 @@ void test_config_include__depth2(void)
        git_buf_clear(&buf);
        cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar2"));
        cl_assert_equal_s("baz2", git_buf_cstr(&buf));
+}
 
-       git_buf_free(&buf);
-       git_config_free(cfg);
+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"));
+}
+
+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");
+}
+
+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"));
+}
+
+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"));
 }
index d1b8c4cda7dea470e209fd51ef4f7df3957f1ec3..4f08a48172f355ed7f08907b73906319826b4430 100644 (file)
@@ -94,27 +94,27 @@ void test_config_multivar__get(void)
        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, 1));
+       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, 1));
+       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, 1));
+       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, 1));
+       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, 1));
+       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, 1));
+       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);
index f86b2d79e70ffff215d70418b6a8ce409fce6436..a34455a0cb6167b426dd590dbe73c40c754191fd 100644 (file)
@@ -289,9 +289,9 @@ void test_config_read__foreach(void)
 
        cl_git_pass(git_config_new(&cfg));
        cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
-               GIT_CONFIG_LEVEL_SYSTEM, 0));
+               GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
        cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
-               GIT_CONFIG_LEVEL_GLOBAL, 0));
+               GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
 
        count = 0;
        cl_git_pass(git_config_foreach(cfg, count_cfg_entries_and_compare_levels, &count));
@@ -313,9 +313,9 @@ void test_config_read__iterator(void)
 
        cl_git_pass(git_config_new(&cfg));
        cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
-               GIT_CONFIG_LEVEL_SYSTEM, 0));
+               GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
        cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
-               GIT_CONFIG_LEVEL_GLOBAL, 0));
+               GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
 
        count = 0;
        cl_git_pass(git_config_iterator_new(&iter, cfg));
@@ -445,7 +445,7 @@ void test_config_read__read_git_config_entry(void)
 
        cl_git_pass(git_config_new(&cfg));
        cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
-               GIT_CONFIG_LEVEL_SYSTEM, 0));
+               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);
@@ -469,11 +469,11 @@ void test_config_read__local_config_overrides_global_config_overrides_system_con
 
        cl_git_pass(git_config_new(&cfg));
        cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
-               GIT_CONFIG_LEVEL_SYSTEM, 0));
+               GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
        cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
-               GIT_CONFIG_LEVEL_GLOBAL, 0));
+               GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
        cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config16"),
-               GIT_CONFIG_LEVEL_LOCAL, 0));
+               GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
 
        cl_git_pass(git_config_get_int32(&i, cfg, "core.dummy2"));
        cl_assert_equal_i(28, i);
@@ -482,9 +482,9 @@ void test_config_read__local_config_overrides_global_config_overrides_system_con
 
        cl_git_pass(git_config_new(&cfg));
        cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
-               GIT_CONFIG_LEVEL_SYSTEM, 0));
+               GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
        cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
-               GIT_CONFIG_LEVEL_GLOBAL, 0));
+               GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
 
        cl_git_pass(git_config_get_int32(&i, cfg, "core.dummy2"));
        cl_assert_equal_i(7, i);
@@ -510,11 +510,11 @@ void test_config_read__fallback_from_local_to_global_and_from_global_to_system(v
 
        cl_git_pass(git_config_new(&cfg));
        cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
-               GIT_CONFIG_LEVEL_SYSTEM, 0));
+               GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
        cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
-               GIT_CONFIG_LEVEL_GLOBAL, 0));
+               GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
        cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config16"),
-               GIT_CONFIG_LEVEL_LOCAL, 0));
+               GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
 
        cl_git_pass(git_config_get_int32(&i, cfg, "core.global"));
        cl_assert_equal_i(17, i);
@@ -546,9 +546,9 @@ void test_config_read__simple_read_from_specific_level(void)
 
        cl_git_pass(git_config_new(&cfg));
        cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config18"),
-               GIT_CONFIG_LEVEL_GLOBAL, 0));
+               GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
        cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config19"),
-               GIT_CONFIG_LEVEL_SYSTEM, 0));
+               GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
 
        cl_git_pass(git_config_open_level(&cfg_specific, cfg, GIT_CONFIG_LEVEL_GLOBAL));
 
@@ -703,3 +703,48 @@ void test_config_read__path(void)
        git_buf_free(&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_free(&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_free(&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_free(&buf);
+}
index f45abdd29c9d1df7e8dd9ca4a06fdb840ad66cda..a424922c1cf114ddf7063869c558489ce4ee7d02 100644 (file)
@@ -1,6 +1,7 @@
 #include "clar_libgit2.h"
 #include "config_file.h"
 #include "config.h"
+#include "path.h"
 
 static git_config *cfg;
 
@@ -21,7 +22,7 @@ void test_config_readonly__writing_to_readonly_fails(void)
 
        cl_git_pass(git_config_file__ondisk(&backend, "global"));
        backend->readonly = 1;
-       cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, 0));
+       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_path_exists("global"));
@@ -33,10 +34,10 @@ void test_config_readonly__writing_to_cfg_with_rw_precedence_succeeds(void)
 
        cl_git_pass(git_config_file__ondisk(&backend, "global"));
        backend->readonly = 1;
-       cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, 0));
+       cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
 
        cl_git_pass(git_config_file__ondisk(&backend, "local"));
-       cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_LOCAL, 0));
+       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"));
 
@@ -51,10 +52,10 @@ void test_config_readonly__writing_to_cfg_with_ro_precedence_succeeds(void)
 
        cl_git_pass(git_config_file__ondisk(&backend, "local"));
        backend->readonly = 1;
-       cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_LOCAL, 0));
+       cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
 
        cl_git_pass(git_config_file__ondisk(&backend, "global"));
-       cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, 0));
+       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"));
 
index 56ef2e9fb4ed078c4017965320d7c05325532fde..6687ba1f782c505793f743a7f342cbe203dfacc2 100644 (file)
@@ -93,9 +93,9 @@ void test_config_write__delete_value_at_specific_level(void)
 
        cl_git_pass(git_config_new(&cfg));
        cl_git_pass(git_config_add_file_ondisk(cfg, "config9",
-               GIT_CONFIG_LEVEL_LOCAL, 0));
+               GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
        cl_git_pass(git_config_add_file_ondisk(cfg, "config15",
-               GIT_CONFIG_LEVEL_GLOBAL, 0));
+               GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
 
        cl_git_pass(git_config_open_level(&cfg_specific, cfg, GIT_CONFIG_LEVEL_GLOBAL));
 
@@ -368,9 +368,9 @@ void test_config_write__add_value_at_specific_level(void)
        // 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, 0));
+               GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
        cl_git_pass(git_config_add_file_ondisk(cfg, "config15",
-               GIT_CONFIG_LEVEL_GLOBAL, 0));
+               GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
 
        cl_git_pass(git_config_open_level(&cfg_specific, cfg, GIT_CONFIG_LEVEL_GLOBAL));
 
@@ -722,3 +722,26 @@ void test_config_write__repeated(void)
 
        git_config_free(cfg);
 }
+
+void test_config_write__preserve_case(void)
+{
+       const char *filename = "config-preserve-case";
+       git_config *cfg;
+       git_buf result = GIT_BUF_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_buf_free(&result);
+
+       git_config_free(cfg);
+}
index 0cbf442304e6fd1f70837f0e2a2e45c726c0c127..9bed4ae278277495d13d4b74812d2e8d6b5eee50 100644 (file)
@@ -1,6 +1,6 @@
 #include "clar_libgit2.h"
 #include "git2/sys/stream.h"
-#include "tls_stream.h"
+#include "streams/tls.h"
 #include "stream.h"
 
 static git_stream test_stream;
@@ -37,8 +37,7 @@ void test_core_stream__register_tls(void)
         * or when openssl support is disabled (except on OSX
         * with Security framework).
         */
-#if defined(GIT_WIN32) || \
-       (!defined(GIT_SECURE_TRANSPORT) && !defined(GIT_OPENSSL))
+#if defined(GIT_WIN32) || !defined(GIT_HTTPS)
        cl_git_fail_with(-1, error);
 #else
        cl_git_pass(error);
index 90e8fa027cf54fd720e5858c3587db842d290c38..85db0c6623008a7e92015f511c4b2a7696412bb2 100644 (file)
@@ -40,6 +40,48 @@ void test_core_string__2(void)
        cl_assert(git__strcasesort_cmp("fooBar", "foobar") < 0);
 }
 
+/* compare prefixes with len */
+void test_core_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_core_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_core_string__strcmp(void)
 {
        cl_assert(git__strcmp("", "") == 0);
index 78503fcc63931a1541cfd8606ca5807e2ae26be8..8feba864e8c44d8b83a74379db240e28e5dcb5e9 100644 (file)
@@ -176,4 +176,8 @@ void test_core_structinit__compare(void)
        CHECK_MACRO_FUNC_INIT_EQUAL( \
                git_proxy_options, GIT_PROXY_OPTIONS_VERSION, \
                GIT_PROXY_OPTIONS_INIT, git_proxy_init_options);
+
+       CHECK_MACRO_FUNC_INIT_EQUAL( \
+               git_diff_patchid_options, GIT_DIFF_PATCHID_OPTIONS_VERSION, \
+               GIT_DIFF_PATCHID_OPTIONS_INIT, git_diff_patchid_init_options);
 }
index c3933c3134de0bdbb5596396ca758cc18f260af9..05cc28218c2eef8d64d8101a8272b2f692e83a5a 100644 (file)
@@ -1,6 +1,19 @@
 #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;
@@ -65,6 +78,32 @@ static void assert_one_modified(
        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));
+       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));
+       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_free(&buf);
+}
+
 void test_diff_blob__can_compare_text_blobs(void)
 {
        git_blob *a, *b, *c;
index acb6eb8a52968c634f629a1b90a755c4315fa203..dc2ceefec722b4127c976700a6b66ff2b704ab6b 100644 (file)
@@ -57,6 +57,27 @@ static void test_parse_invalid_diff(const char *invalid_diff)
        git_buf_free(&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(git_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);
diff --git a/tests/diff/patchid.c b/tests/diff/patchid.c
new file mode 100644 (file)
index 0000000..75a2aa8
--- /dev/null
@@ -0,0 +1,60 @@
+#include "clar_libgit2.h"
+#include "patch/patch_common.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));
+       cl_git_pass(git_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__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);
+}
index c1cd252391860cd35c6d9fa589998f9f0a1ebd71..ddc1d5d78102e67bfc9835c19a11a58fc143b454 100644 (file)
@@ -16,6 +16,13 @@ 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"
+
 /*
  * Renames repo has:
  *
@@ -36,12 +43,22 @@ void test_diff_rename__cleanup(void)
  *   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 = "31e47d8c1fa36d7f8d537b96158e3f024de0a9f2";
-       const char *new_sha = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
+       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;
@@ -139,8 +156,8 @@ void test_diff_rename__match_oid(void)
 
 void test_diff_rename__checks_options_version(void)
 {
-       const char *old_sha = "31e47d8c1fa36d7f8d537b96158e3f024de0a9f2";
-       const char *new_sha = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
+       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;
@@ -171,9 +188,9 @@ void test_diff_rename__checks_options_version(void)
 
 void test_diff_rename__not_exact_match(void)
 {
-       const char *sha0 = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
-       const char *sha1 = "1c068dee5790ef1580cfc4cd670915b48d790084";
-       const char *sha2 = "19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13";
+       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;
@@ -433,7 +450,7 @@ void test_diff_rename__test_small_files(void)
 
 void test_diff_rename__working_directory_changes(void)
 {
-       const char *sha0 = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
+       const char *sha0 = COPY_RENAME_COMMIT;
        const char *blobsha = "66311f5cfbe7836c27510a3ba2f43e282e2c8bba";
        git_oid id;
        git_tree *tree;
@@ -592,8 +609,8 @@ void test_diff_rename__working_directory_changes(void)
 
 void test_diff_rename__patch(void)
 {
-       const char *sha0 = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
-       const char *sha1 = "1c068dee5790ef1580cfc4cd670915b48d790084";
+       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;
@@ -1425,9 +1442,9 @@ void test_diff_rename__can_delete_unmodified_deltas(void)
 
 void test_diff_rename__matches_config_behavior(void)
 {
-       const char *sha0 = "31e47d8c1fa36d7f8d537b96158e3f024de0a9f2";
-       const char *sha1 = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
-       const char *sha2 = "1c068dee5790ef1580cfc4cd670915b48d790084";
+       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;
@@ -1508,8 +1525,8 @@ void test_diff_rename__matches_config_behavior(void)
 
 void test_diff_rename__can_override_thresholds_when_obeying_config(void)
 {
-       const char *sha1 = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
-       const char *sha2 = "1c068dee5790ef1580cfc4cd670915b48d790084";
+       const char *sha1 = COPY_RENAME_COMMIT;
+       const char *sha2 = REWRITE_COPY_COMMIT;
 
        git_tree *tree1, *tree2;
        git_config *cfg;
@@ -1563,8 +1580,8 @@ void test_diff_rename__can_override_thresholds_when_obeying_config(void)
 
 void test_diff_rename__by_config_doesnt_mess_with_whitespace_settings(void)
 {
-       const char *sha1 = "1c068dee5790ef1580cfc4cd670915b48d790084";
-       const char *sha2 = "19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13";
+       const char *sha1 = REWRITE_COPY_COMMIT;
+       const char *sha2 = RENAME_MODIFICATION_COMMIT;
 
        git_tree *tree1, *tree2;
        git_config *cfg;
@@ -1710,8 +1727,8 @@ void test_diff_rename__blank_files_not_renamed_when_not_ignoring_whitespace(void
  */
 void test_diff_rename__identical(void)
 {
-       const char *old_sha = "31e47d8c1fa36d7f8d537b96158e3f024de0a9f2";
-       const char *new_sha = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
+       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;
@@ -1748,3 +1765,216 @@ void test_diff_rename__identical(void)
        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_free(&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_free(&diff_buf);
+       git_diff_free(diff);
+       git_tree_free(old_tree);
+       git_tree_free(new_tree);
+}
index ea4b70e4a46763c960fa1fd71f4950a4bb772179..4dabb577e3de1a6dad9767102775d4d8574ab3f6 100644 (file)
@@ -353,20 +353,25 @@ void test_fetchhead_nonetwork__quote_in_branch_name(void)
 }
 
 static bool found_master;
-static bool find_master_called;
+static bool found_haacked;
+static bool find_master_haacked_called;
 
-int find_master(const char *ref_name, const char *remote_url, const git_oid *oid, unsigned int is_merge, void *payload)
+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_called = true;
+       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;
 }
@@ -375,10 +380,12 @@ void test_fetchhead_nonetwork__create_when_refpecs_given(void)
 {
        git_remote *remote;
        git_buf path = GIT_BUF_INIT;
-       char *refspec = "refs/heads/master";
+       char *refspec1 = "refs/heads/master";
+       char *refspec2 = "refs/heads/haacked";
+       char *refspecs[] = { refspec1, refspec2 };
        git_strarray specs = {
-               &refspec,
-               1,
+               refspecs,
+               2,
        };
 
        cl_set_cleanup(&cleanup_repository, "./test1");
@@ -391,9 +398,74 @@ void test_fetchhead_nonetwork__create_when_refpecs_given(void)
        cl_git_pass(git_remote_fetch(remote, &specs, NULL, NULL));
        cl_assert(git_path_exists(path.ptr));
 
-       cl_git_pass(git_repository_fetchhead_foreach(g_repo, find_master, NULL));
-       cl_assert(find_master_called);
+       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_buf_free(&path);
+}
+
+static bool count_refs_called;
+struct prefix_count {
+       const char *prefix;
+       int count;
+       int expected;
+};
+
+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_buf path = GIT_BUF_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_buf_joinpath(&path, git_repository_path(g_repo), "FETCH_HEAD"));
+       cl_assert(!git_path_exists(path.ptr));
+       cl_git_pass(git_remote_fetch(remote, NULL, NULL, NULL));
+       cl_assert(git_path_exists(path.ptr));
+
+       {
+               int i;
+               struct prefix_count prefix_counts[] = {
+                       {"refs/notes/", 0, 1},
+                       {"refs/heads/", 0, 12},
+                       {"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_buf_free(&path);
index 587efb519c0ab60692e8d4c19f3471e0dbbecd3d..0a94d4952841075620cb5ff03b2dae05782b7fa2 100644 (file)
@@ -8,7 +8,7 @@
 
 from __future__ import with_statement
 from string import Template
-import re, fnmatch, os, codecs, pickle
+import re, fnmatch, os, sys, codecs, pickle
 
 class Module(object):
     class Template(object):
@@ -128,8 +128,9 @@ class Module(object):
 
 class TestSuite(object):
 
-    def __init__(self, path):
+    def __init__(self, path, output):
         self.path = path
+        self.output = output
 
     def should_generate(self, path):
         if not os.path.isfile(path):
@@ -157,7 +158,7 @@ class TestSuite(object):
         return modules
 
     def load_cache(self):
-        path = os.path.join(self.path, '.clarcache')
+        path = os.path.join(self.output, '.clarcache')
         cache = {}
 
         try:
@@ -170,7 +171,7 @@ class TestSuite(object):
         return cache
 
     def save_cache(self):
-        path = os.path.join(self.path, '.clarcache')
+        path = os.path.join(self.output, '.clarcache')
         with open(path, 'wb') as cache:
             pickle.dump(self.modules, cache)
 
@@ -200,22 +201,24 @@ class TestSuite(object):
         return sum(len(module.callbacks) for module in self.modules.values())
 
     def write(self):
-        output = os.path.join(self.path, 'clar.suite')
+        output = os.path.join(self.output, 'clar.suite')
 
         if not self.should_generate(output):
             return False
 
         with open(output, 'w') as data:
-            for module in self.modules.values():
+            modules = sorted(self.modules.values(), key=lambda module: module.name)
+
+            for module in modules:
                 t = Module.DeclarationTemplate(module)
                 data.write(t.render())
 
-            for module in self.modules.values():
+            for module in modules:
                 t = Module.CallbacksTemplate(module)
                 data.write(t.render())
 
             suites = "static struct clar_suite _clar_suites[] = {" + ','.join(
-                Module.InfoTemplate(module).render() for module in sorted(self.modules.values(), key=lambda module: module.name)
+                Module.InfoTemplate(module).render() for module in modules
             ) + "\n};\n"
 
             data.write(suites)
@@ -232,13 +235,18 @@ if __name__ == '__main__':
     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()
-
-    for path in args or ['.']:
-        suite = TestSuite(path)
-        suite.load(options.force)
-        suite.disable(options.excluded)
-        if suite.write():
-            print("Written `clar.suite` (%d tests in %d suites)" % (suite.callback_count(), suite.suite_count()))
+    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` (%d tests in %d suites)" % (suite.callback_count(), suite.suite_count()))
 
index 2efad5b33f54e7afea118dfd6b14e7f1c5a915f9..2693b1312500d2c3f2e124896e53a0ef9d20ae9a 100644 (file)
@@ -256,3 +256,65 @@ void test_index_filemodes__invalid(void)
 
        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_frombuffer(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_frombuffer(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_frombuffer(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_frombuffer(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_frombuffer(index,
+               &new_entry, content, strlen(content)));
+       cl_assert_equal_p(NULL, git_index_get_bypath(index, "invalid_mode.txt", 0));
+
+       git_index_free(index);
+}
index 1498196b2d8d46400c255e7ed15465455745fa9b..ea8335b48594e0839889ec37af0cf2d7d6a7e4d6 100644 (file)
@@ -856,11 +856,14 @@ void test_index_tests__change_icase_on_instance(void)
 
 void test_index_tests__can_lock_index(void)
 {
+       git_repository *repo;
        git_index *index;
        git_indexwriter one = GIT_INDEXWRITER_INIT,
                two = GIT_INDEXWRITER_INIT;
 
-       cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
+       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));
@@ -873,4 +876,5 @@ void test_index_tests__can_lock_index(void)
        git_indexwriter_cleanup(&one);
        git_indexwriter_cleanup(&two);
        git_index_free(index);
+       cl_git_sandbox_cleanup();
 }
index ae48fcd465ae49ff671413fbf0ab19520febceef..68d574126ee5c843ec049d0b3a97b5dfb5cc33a1 100644 (file)
@@ -51,8 +51,7 @@ void expect_iterator_items(
                                cl_assert(entry->mode != GIT_FILEMODE_TREE);
                }
 
-               if (++count >= expected_flat)
-                       break;
+               cl_assert(++count <= expected_flat);
        }
 
        assert_at_end(i, v);
index f33fd98f1fde3ce2c01eee13cac4f2fbee741338..81016752cbcba52e6def75d845f9b45fa0f97b36 100644 (file)
@@ -610,6 +610,7 @@ void test_iterator_workdir__filesystem2(void)
        static const char *expect_base[] = {
                "heads/br2",
                "heads/dir",
+               "heads/executable",
                "heads/ident",
                "heads/long-file-name",
                "heads/master",
@@ -630,7 +631,7 @@ void test_iterator_workdir__filesystem2(void)
 
        cl_git_pass(git_iterator_for_filesystem(
                &i, "testrepo/.git/refs", NULL));
-       expect_iterator_items(i, 15, expect_base, 15, expect_base);
+       expect_iterator_items(i, 16, expect_base, 16, expect_base);
        git_iterator_free(i);
 }
 
@@ -662,7 +663,7 @@ void test_iterator_workdir__filesystem_gunk(void)
        /* should only have 13 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, 13, NULL, 13, NULL);
+       expect_iterator_items(i, 15, NULL, 15, NULL);
        git_iterator_free(i);
        git_buf_free(&parent);
 }
@@ -741,6 +742,8 @@ void test_iterator_workdir__skips_fifos_and_special_files(void)
        cl_assert_equal_i(GIT_ITEROVER, git_iterator_advance(&e, i));
 
        git_iterator_free(i);
+#else
+       cl_skip();
 #endif
 }
 
index f67c8ffbc73dbabd7140eac18dd981f8afc1baf2..b4fccecc4461350c03d7ce9df8a6da9b8eb59f5d 100644 (file)
@@ -11,7 +11,12 @@ int main(int argc, char *argv[])
 
        clar_test_init(argc, argv);
 
-       git_libgit2_init();
+       res = git_libgit2_init();
+       if (res < 0) {
+               fprintf(stderr, "failed to init libgit2");
+               return res;
+       }
+
        cl_global_trace_register();
        cl_sandbox_set_search_path_defaults();
 
index e6394a9e8fa60b1fd20fa76c76318c36985c8a74..27f19c1b01b1c281fee998e2b3048549b525dc7c 100644 (file)
        "This is a mighty fine recipe!\n" \
        ">>>>>>> branchF-2\n"
 
-#define CONFLICTING_RECURSIVE_H1_TO_H2_WITH_DIFF3 \
+#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" \
+       "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" \
+       "<<<<<<<<< Temporary merge branch 1\n" \
        "PUT INTO A POT three quarts of water, three onions cut small, one\n" \
-       ">>>>>>> Temporary merge branch 2\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-2\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" \
index daa73fadaa438ffa9580a059a73973531fbef66e..6f5a1fd9c5f7af139db240e0062507b8e49c3efc 100644 (file)
@@ -377,3 +377,51 @@ void test_merge_files__handles_binaries_when_favored(void)
 
        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);
+}
index c5b129bf89fe7e3d69b017878c0b7a9bbba3d385..71f5af150b25e3aaaad2d67ff46759311401036f 100644 (file)
@@ -312,7 +312,7 @@ void test_merge_trees_recursive__conflicting_merge_base(void)
                { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
                { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
                { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
-               { 0100644, "3a66812fed1e03ea4a6a7ee28d8a57aec1ca6537", 1, "veal.txt" },
+               { 0100644, "cfc01b0976122eae42a82064440bbf534eddd7a0", 1, "veal.txt" },
                { 0100644, "d604c75019c282144bdbbf3fd3462ba74b240efc", 2, "veal.txt" },
                { 0100644, "37a5054a9f9b4628e3924c5cb8f2147c6e2a3efc", 3, "veal.txt" },
        };
@@ -339,14 +339,14 @@ void test_merge_trees_recursive__conflicting_merge_base_with_diff3(void)
                { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
                { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
                { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
-               { 0100644, "cd17a91513f3aee9e44114d1ede67932dd41d2fc", 1, "veal.txt" },
-               { 0100644, "d604c75019c282144bdbbf3fd3462ba74b240efc", 2, "veal.txt" },
-               { 0100644, "37a5054a9f9b4628e3924c5cb8f2147c6e2a3efc", 3, "veal.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-1", "branchH-2", &opts));
+       cl_git_pass(merge_commits_from_branches(&index, repo, "branchH-2", "branchH-1", &opts));
 
        cl_assert(merge_test_index(index, merge_index_entries, 8));
 
@@ -392,19 +392,67 @@ void test_merge_trees_recursive__recursionlimit(void)
                { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
                { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
                { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
-               { 0100644, "ce7e553c6feb6e5f3bd67e3c3be04182fe3094b4", 1, "gravy.txt" },
-               { 0100644, "d8dd349b78f19a4ebe3357bacb8138f00bf5ed41", 2, "gravy.txt" },
-               { 0100644, "e50fbbd701458757bdfe9815f58ed717c588d1b5", 3, "gravy.txt" },
+               { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
                { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
-               { 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.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, "branchE-1", "branchE-2", &opts));
+       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);
+}
index 795126255f84e6585faada227c61958e91869281..d47a0c50bf178d87917c00b26f446ff0f29944e8 100644 (file)
@@ -44,7 +44,7 @@ void test_merge_workdir_recursive__writes_conflict_with_virtual_base(void)
        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_buf_free(&conflicting_buf);
 }
@@ -62,22 +62,22 @@ void test_merge_workdir_recursive__conflicting_merge_base_with_diff3(void)
                { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
                { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
                { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
-               { 0100644, "cd17a91513f3aee9e44114d1ede67932dd41d2fc", 1, "veal.txt" },
-               { 0100644, "d604c75019c282144bdbbf3fd3462ba74b240efc", 2, "veal.txt" },
-               { 0100644, "37a5054a9f9b4628e3924c5cb8f2147c6e2a3efc", 3, "veal.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-1", GIT_REFS_HEADS_DIR "branchH-2", &opts, &checkout_opts));
+       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_H1_TO_H2_WITH_DIFF3, conflicting_buf.ptr);
+       cl_assert_equal_s(CONFLICTING_RECURSIVE_H2_TO_H1_WITH_DIFF3, conflicting_buf.ptr);
 
        git_index_free(index);
        git_buf_free(&conflicting_buf);
index 7c18c2ffb80cf284c11805e8d40e53672b9acdcc..c4cc188a892898e13927dc4a02e7f68814b874b2 100644 (file)
@@ -12,6 +12,7 @@ static git_repository *repo;
 #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"
 
@@ -93,3 +94,38 @@ void test_merge_workdir_submodules__take_changed(void)
        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/message/trailer.c b/tests/message/trailer.c
new file mode 100644 (file)
index 0000000..9cc83de
--- /dev/null
@@ -0,0 +1,165 @@
+#include "clar_libgit2.h"
+#include "message.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);
+}
index 17c8f26e3330513e41c174cf861e07a23efedb9a..91e2a05958227375691e2d160a0ffccf484a7a12 100644 (file)
@@ -314,12 +314,6 @@ void test_network_fetchlocal__prune_tag(void)
        git_repository_free(repo);
 }
 
-static void cleanup_sandbox(void *unused)
-{
-       GIT_UNUSED(unused);
-       cl_git_sandbox_cleanup();
-}
-
 void test_network_fetchlocal__partial(void)
 {
        git_repository *repo = cl_git_sandbox_init("partial-testrepo");
@@ -332,7 +326,6 @@ void test_network_fetchlocal__partial(void)
        options.callbacks.transfer_progress = transfer_cb;
        options.callbacks.payload = &callcount;
 
-       cl_set_cleanup(&cleanup_sandbox, NULL);
        cl_git_pass(git_reference_list(&refnames, repo));
        cl_assert_equal_i(1, (int)refnames.count);
 
@@ -420,7 +413,6 @@ void test_network_fetchlocal__multi_remotes(void)
        git_strarray refnames = {0};
        git_fetch_options options = GIT_FETCH_OPTIONS_INIT;
 
-       cl_set_cleanup(&cleanup_sandbox, NULL);
        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"));
index a91bf5bdfc946004b67ee6c412c7b3030e6cc422..9626f53b308558ac47a1816c0f65c0964b1ed2eb 100644 (file)
@@ -73,6 +73,128 @@ static int note_list_cb(
        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));
+
+               if (git_oid_cmp(&expected_note_oid, blob_oid) != 0)
+                       continue;
+
+               cl_git_pass(git_oid_fromstr(&expected_target_oid, notes[i].object_oid));
+
+               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;
+}
+
+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));
+
+       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"));
+
+       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_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
@@ -253,6 +375,71 @@ static char *messages[] = {
 
 #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"));
+
+       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"));
+
+       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_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"));
+
+       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
@@ -298,6 +485,50 @@ void test_notes_notes__can_read_a_note_in_an_existing_fanout(void)
        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"));
+       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"));
+
+       cl_git_pass(git_note_commit_create(&notes_commit_oid, NULL, _repo, NULL, _sig, _sig, &oid, "I decorate 4a20\n", 0));
+
+       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;
@@ -388,3 +619,46 @@ void test_notes_notes__empty_iterate(void)
 
        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"));
+       cl_git_pass(git_oid_fromstr(&(oids[1]), "c47800c7266a2be04c571c04d5a6614691ea99bd"));
+
+       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]);
+}
index a9decf9c1795f7c091b2c259ac4c4ee348d4084d..a1ee03d6d6c3a531cb97f20196964ac1cbbe0b09 100644 (file)
@@ -495,6 +495,7 @@ static void test_inserting_submodule(void)
        git_treebuilder *bld;
        git_oid sm_id;
 
+       cl_git_pass(git_oid_fromstr(&sm_id, "da39a3ee5e6b4b0d3255bfef95601890afd80709"));
        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);
@@ -512,3 +513,14 @@ void test_object_tree_write__object_validity(void)
        test_inserting_submodule();
 }
 
+void test_object_tree_write__invalid_null_oid(void)
+{
+       git_treebuilder *bld;
+       git_oid null_oid = {{0}};
+
+       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(giterr_last() && strstr(giterr_last()->message, "null OID") != NULL);
+
+       git_treebuilder_free(bld);
+}
diff --git a/tests/odb/backend/mempack.c b/tests/odb/backend/mempack.c
new file mode 100644 (file)
index 0000000..624f0e6
--- /dev/null
@@ -0,0 +1,60 @@
+#include "clar_libgit2.h"
+#include "repository.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));
+       cl_git_pass(git_odb_add_backend(_odb, backend, 10));
+       cl_git_pass(git_repository_wrap_odb(&_repo, _odb));
+}
+
+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_OBJ_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"));
+       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"));
+       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_OBJ_BLOB));
+       cl_assert(git_odb_exists(_odb, &_oid) == 1);
+}
+
+void test_odb_backend_mempack__blob_create_frombuffer_succeeds(void)
+{
+       const char *data = "data";
+
+       cl_git_pass(git_blob_create_frombuffer(&_oid, _repo, data, strlen(data) + 1));
+       cl_assert(git_odb_exists(_odb, &_oid) == 1);
+}
index c0fcd403b2f22d9eb1dea7066d3283453bf814c2..f4d29cc76359f847545aaa1468adbf75b416c8c5 100644 (file)
@@ -230,3 +230,21 @@ void test_odb_backend_simple__exists_with_highly_ambiguous_prefix(void)
        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 = {{0}};
+       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(giterr_last() && strstr(giterr_last()->message, "null OID"));
+}
diff --git a/tests/odb/largefiles.c b/tests/odb/largefiles.c
new file mode 100644 (file)
index 0000000..a5b982e
--- /dev/null
@@ -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_buf buf = GIT_BUF_INIT;
+       size_t i;
+
+       for (i = 0; i < 3041; i++)
+               cl_git_pass(git_buf_puts(&buf, "Hello, world.\n"));
+
+       cl_git_pass(git_odb_open_wstream(&stream, odb, LARGEFILE_SIZE, GIT_OBJ_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_buf_free(&buf);
+}
+
+void test_odb_largefiles__write_from_memory(void)
+{
+       git_oid expected, oid;
+       git_buf buf = GIT_BUF_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_buf_puts(&buf, "Hello, world.\n"));
+
+       git_oid_fromstr(&expected, "3fb56989cca483b21ba7cb0a6edb229d10e1c26c");
+       cl_git_pass(git_odb_write(&oid, odb, buf.ptr, buf.size, GIT_OBJ_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");
+       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_otype 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_OBJ_BLOB, type);
+
+       cl_git_pass(git_hash_ctx_init(&hash));
+       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, &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_otype 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_OBJ_BLOB, type);
+}
index 2e24d6723a1d433e412833c35fb2e75b9fa43626..83d08072921238baa22cdb5ef5ffa040dd526f65 100644 (file)
@@ -55,6 +55,63 @@ static void test_read_object(object_data *data)
        git_odb_free(odb);
 }
 
+static void test_read_header(object_data *data)
+{
+       git_oid id;
+       git_odb *odb;
+       size_t len;
+       git_otype type;
+
+       write_object_files(data);
+
+       cl_git_pass(git_odb_open(&odb, "test-objects"));
+       cl_git_pass(git_oid_fromstr(&id, data->id));
+       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;
+
+       write_object_files(data);
+
+       cl_git_pass(git_odb_open(&odb, "test-objects"));
+       cl_git_pass(git_oid_fromstr(&id, data->id));
+       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;
@@ -103,6 +160,33 @@ void test_odb_loose__simple_reads(void)
        test_read_object(&some);
 }
 
+void test_odb_loose__streaming_reads(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__read_header(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_write_object_permission(
        mode_t dir_mode, mode_t file_mode,
        mode_t expected_dir_mode, mode_t expected_file_mode)
index 04fd22d45077f0433d6505352bacdd421c5d1e64..27b7b96610d5d3fdfe728228f2e806c06e1390a5 100644 (file)
@@ -263,8 +263,8 @@ static int cred_failure_cb(
 
 void test_online_clone__cred_callback_failure_return_code_is_tunnelled(void)
 {
-       if (!_remote_url || !_remote_user)
-               clar__skip();
+       _remote_url = git__strdup("https://github.com/libgit2/non-existent");
+       _remote_user = git__strdup("libgit2test");
 
        g_options.fetch_opts.callbacks.credentials = cred_failure_cb;
 
@@ -293,8 +293,8 @@ void test_online_clone__cred_callback_called_again_on_auth_failure(void)
 {
        size_t counter = 0;
 
-       if (!_remote_url || !_remote_user)
-               clar__skip();
+       _remote_url = git__strdup("https://github.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;
@@ -348,7 +348,7 @@ void test_online_clone__credentials(void)
 void test_online_clone__bitbucket_style(void)
 {
        git_cred_userpass_payload user_pass = {
-               "libgit2", "libgit2"
+               "libgit3", "libgit3"
        };
 
        g_options.fetch_opts.callbacks.credentials = git_cred_userpass;
@@ -357,15 +357,45 @@ void test_online_clone__bitbucket_style(void)
        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_cred_userpass_payload user_pass = {
+               "libgit2", "wrong"
+       };
+
+       g_options.fetch_opts.callbacks.credentials = git_cred_userpass;
+       g_options.fetch_opts.callbacks.payload = &user_pass;
 
-       /* User and pass from URL */
-       user_pass.password = "wrong";
+       /*
+        * Correct user and pass are in the URL; the (incorrect) creds in
+        * the `git_cred_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");
+}
 
-       /* Wrong password in URL, fall back to user_pass */
-       user_pass.password = "libgit2";
+void test_online_clone__bitbucket_falls_back_to_specified_creds(void)
+{
+       git_cred_userpass_payload user_pass = {
+               "libgit2", "libgit2"
+       };
+
+       g_options.fetch_opts.callbacks.credentials = git_cred_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_cred_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");
@@ -547,7 +577,7 @@ void test_online_clone__ssh_cert(void)
        if (!_remote_ssh_fingerprint)
                cl_skip();
 
-       cl_git_fail_with(GIT_EUSER, git_clone(&g_repo, "ssh://localhost/foo", "./foo", &g_options));
+       cl_git_fail_with(GIT_EUSER, git_clone(&g_repo, _remote_url, "./foo", &g_options));
 }
 
 static char *read_key_file(const char *path)
@@ -677,25 +707,37 @@ static int proxy_creds(git_cred **out, const char *url, const char *username, un
 
 void test_online_clone__proxy_credentials_request(void)
 {
+       git_buf url = GIT_BUF_INIT;
+
        if (!_remote_proxy_url || !_remote_proxy_user || !_remote_proxy_pass)
                cl_skip();
 
+       cl_git_pass(git_buf_printf(&url, "http://%s/", _remote_proxy_url));
+
        g_options.fetch_opts.proxy_opts.type = GIT_PROXY_SPECIFIED;
-       g_options.fetch_opts.proxy_opts.url = _remote_proxy_url;
+       g_options.fetch_opts.proxy_opts.url = url.ptr;
        g_options.fetch_opts.proxy_opts.credentials = proxy_creds;
        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_buf_free(&url);
 }
 
 void test_online_clone__proxy_credentials_in_url(void)
 {
-       if (!_remote_proxy_url)
+       git_buf url = GIT_BUF_INIT;
+
+       if (!_remote_proxy_url || !_remote_proxy_user || !_remote_proxy_pass)
                cl_skip();
 
+       cl_git_pass(git_buf_printf(&url, "http://%s:%s@%s/", _remote_proxy_user, _remote_proxy_pass, _remote_proxy_url));
+
        g_options.fetch_opts.proxy_opts.type = GIT_PROXY_SPECIFIED;
-       g_options.fetch_opts.proxy_opts.url = _remote_proxy_url;
+       g_options.fetch_opts.proxy_opts.url = url.ptr;
        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_buf_free(&url);
 }
index a86f2d9ae1f3fb825413f8af9f70fb0cfe8bb0b6..d79eb1f595137e853cda61988215709dd1b80818 100644 (file)
@@ -1,12 +1,13 @@
 #include "clar_libgit2.h"
 
-static const char *refspec = "refs/heads/first-merge:refs/remotes/origin/first-merge";
+#define URL "git://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));
+       cl_git_pass(git_remote_create_with_fetchspec(out, repo, name, url, REFSPEC));
 
        return 0;
 }
@@ -22,7 +23,7 @@ void test_online_remotes__single_branch(void)
        opts.remote_cb = remote_single_branch;
        opts.checkout_branch = "first-merge";
 
-       cl_git_pass(git_clone(&repo, "git://github.com/libgit2/TestGitRepository", "./single-branch", &opts));
+       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++) {
@@ -37,7 +38,7 @@ void test_online_remotes__single_branch(void)
        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]);
+       cl_assert_equal_s(REFSPEC, refs.strings[0]);
 
        git_strarray_free(&refs);
        git_remote_free(remote);
@@ -51,5 +52,76 @@ void test_online_remotes__restricted_refspecs(void)
 
        opts.remote_cb = remote_single_branch;
 
-       cl_git_fail_with(GIT_EINVALIDSPEC, git_clone(&repo, "git://github.com/libgit2/TestGitRepository", "./restrict-refspec", &opts));
+       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);
 }
index c73d3974ea9a1f5634d458a1a8c5370103be5606..f3c2204bda153c324e16ec24a6857cf590d3ee15 100644 (file)
@@ -40,6 +40,40 @@ static const unsigned char thin_pack[] = {
 };
 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, 0x03, 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, 0x03, 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;
+
 static const unsigned char base_obj[] = { 07, 076 };
 static const unsigned int base_obj_len = 2;
 
@@ -60,6 +94,38 @@ void test_pack_indexer__out_of_order(void)
        git_indexer_free(idx);
 }
 
+void test_pack_indexer__missing_trailer(void)
+{
+       git_indexer *idx = 0;
+       git_transfer_progress stats = { 0 };
+
+       cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL));
+       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(giterr_last() != NULL);
+       cl_assert_equal_i(giterr_last()->klass, GITERR_INDEXER);
+
+       git_indexer_free(idx);
+}
+
+void test_pack_indexer__leaky(void)
+{
+       git_indexer *idx = 0;
+       git_transfer_progress stats = { 0 };
+
+       cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL));
+       cl_git_pass(git_indexer_append(
+               idx, leaky_pack, leaky_pack_len, &stats));
+       cl_git_fail(git_indexer_commit(idx, &stats));
+
+       cl_assert(giterr_last() != NULL);
+       cl_assert_equal_i(giterr_last()->klass, GITERR_INDEXER);
+
+       git_indexer_free(idx);
+}
+
 void test_pack_indexer__fix_thin(void)
 {
        git_indexer *idx = NULL;
@@ -126,6 +192,35 @@ void test_pack_indexer__fix_thin(void)
        }
 }
 
+void test_pack_indexer__corrupt_length(void)
+{
+       git_indexer *idx = NULL;
+       git_transfer_progress stats = { 0 };
+       git_repository *repo;
+       git_odb *odb;
+       git_oid id, should_id;
+
+       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_OBJ_BLOB));
+       git_oid_fromstr(&should_id, "e68fe8129b546b101aee9510c5328e7f21ca1d18");
+       cl_assert_equal_oid(&should_id, &id);
+
+       cl_git_pass(git_indexer_new(&idx, ".", 0, odb, NULL, NULL));
+       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(giterr_last() != NULL);
+       cl_assert_equal_i(giterr_last()->klass, GITERR_ZLIB);
+
+       git_indexer_free(idx);
+       git_odb_free(odb);
+       git_repository_free(repo);
+}
+
 static int find_tmp_file_recurs(void *opaque, git_buf *path)
 {
        int error = 0;
index 8350ac2ddfb87481cace6bd968348baf101b5cd1..a40ad7b2306563b856b0a414a83b0f2e8b971bdf 100644 (file)
@@ -102,3 +102,9 @@ void test_patch_parse__invalid_patches_fails(void)
                strlen(PATCH_CORRUPT_MISSING_HUNK_HEADER), NULL));
 }
 
+void test_patch_parse__files_with_whitespaces_succeeds(void)
+{
+       git_patch *patch;
+       cl_git_pass(git_patch_from_buffer(&patch, PATCH_NAME_WHITESPACE, strlen(PATCH_NAME_WHITESPACE), NULL));
+       git_patch_free(patch);
+}
index 6ec55469051a9803882fb0794d85d444df73f91d..e838e6089954663f6689b51fd7996bc308f7d2d2 100644 (file)
        "@@ -9,0 +10 @@ below it!\n" \
        "+insert at end\n"
 
-/* An insertion at the beginning and end of file (and the resultant patch) */
+#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" \
        "+added line with no nl\n" \
        "\\ No newline at end of file\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" \
index b2ef082ebcb55e9a1f2cadeb6f2fb83c458acc8b..721902d63638d3e5e37f5bc0e2c03ab776b3db3c 100644 (file)
 #define ID_BRANCH_A "d853fb9f24e0fe63b3dce9fbc04fd9cfe17a030b"
 #define ID_BRANCH_B "1ce9ea3ba9b4fa666602d52a5281d41a482cc58b"
 
-
-void test_perf_merge__initialize(void)
-{
-}
-
-void test_perf_merge__cleanup(void)
-{
-}
-
 void test_perf_merge__m1(void)
 {
-#if 1
-       cl_skip();
-#else
        perf__do_merge(SRC_REPO, "m1", ID_BRANCH_A, ID_BRANCH_B);
-#endif
 }
diff --git a/tests/precompiled.c b/tests/precompiled.c
new file mode 100644 (file)
index 0000000..5f656a4
--- /dev/null
@@ -0,0 +1 @@
+#include "precompiled.h"
diff --git a/tests/precompiled.h b/tests/precompiled.h
new file mode 100644 (file)
index 0000000..ea53a60
--- /dev/null
@@ -0,0 +1,4 @@
+#include "common.h"
+#include "git2.h"
+#include "clar.h"
+#include "clar_libgit2.h"
index 7a38ab89f167f345b94a3dcb61970fa19754df04..8ae78ce5b26708cca745eff83aa85ed846f0d49e 100644 (file)
@@ -3,6 +3,7 @@
 #include "git2/rebase.h"
 #include "posix.h"
 #include "signature.h"
+#include "../submodule/submodule_helpers.h"
 
 #include <fcntl.h>
 
@@ -12,9 +13,44 @@ 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_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_OBJ_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)
@@ -31,7 +67,6 @@ void test_rebase_submodule__init_untracked(void)
        git_buf untracked_path = GIT_BUF_INIT;
        FILE *fp;
        git_submodule *submodule;
-       git_config *config;
 
        cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/asparagus"));
        cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master"));
@@ -39,12 +74,6 @@ void test_rebase_submodule__init_untracked(void)
        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));
 
-       git_repository_config(&config, repo);
-
-       cl_git_pass(git_config_set_string(config, "submodule.my-submodule.url", git_repository_path(repo)));
-
-       git_config_free(config);
-
        cl_git_pass(git_submodule_lookup(&submodule, repo, "my-submodule"));
        cl_git_pass(git_submodule_update(submodule, 1, NULL));
 
index 69488e6c78cd6bf0fe2918968ae610a7f73ef31b..a4db574461459055223e8dcc9bf4131303f60a3f 100644 (file)
@@ -116,51 +116,6 @@ void test_refs_branches_create__creating_a_branch_with_an_invalid_name_returns_E
                git_branch_create(&branch, repo, "inv@{id", target, 0));
 }
 
-void test_refs_branches_create__default_reflog_message(void)
-{
-       git_reflog *log;
-       git_buf buf = GIT_BUF_INIT;
-       const git_reflog_entry *entry;
-       git_annotated_commit *annotated;
-       git_signature *sig;
-       git_config *cfg;
-
-       cl_git_pass(git_repository_config(&cfg, repo));
-       cl_git_pass(git_config_set_string(cfg, "user.name", "Foo Bar"));
-       cl_git_pass(git_config_set_string(cfg, "user.email", "foo@example.com"));
-       git_config_free(cfg);
-
-       cl_git_pass(git_signature_default(&sig, repo));
-
-       retrieve_known_commit(&target, repo);
-       cl_git_pass(git_branch_create(&branch, repo, NEW_BRANCH_NAME, target, false));
-       cl_git_pass(git_reflog_read(&log, repo, "refs/heads/" NEW_BRANCH_NAME));
-
-       entry = git_reflog_entry_byindex(log, 0);
-       cl_git_pass(git_buf_printf(&buf, "branch: Created from %s", git_oid_tostr_s(git_commit_id(target))));
-       cl_assert_equal_s(git_buf_cstr(&buf), git_reflog_entry_message(entry));
-       cl_assert_equal_s(sig->email, git_reflog_entry_committer(entry)->email);
-
-       cl_git_pass(git_reference_remove(repo, "refs/heads/" NEW_BRANCH_NAME));
-       git_reference_free(branch);
-       git_reflog_free(log);
-       git_buf_clear(&buf);
-
-       cl_git_pass(git_annotated_commit_from_revspec(&annotated, repo, "e90810b8df3"));
-       cl_git_pass(git_branch_create_from_annotated(&branch, repo, NEW_BRANCH_NAME, annotated, true));
-       cl_git_pass(git_reflog_read(&log, repo, "refs/heads/" NEW_BRANCH_NAME));
-
-       entry = git_reflog_entry_byindex(log, 0);
-       cl_git_pass(git_buf_printf(&buf, "branch: Created from e90810b8df3"));
-       cl_assert_equal_s(git_buf_cstr(&buf), git_reflog_entry_message(entry));
-       cl_assert_equal_s(sig->email, git_reflog_entry_committer(entry)->email);
-
-       git_annotated_commit_free(annotated);
-       git_buf_free(&buf);
-       git_reflog_free(log);
-       git_signature_free(sig);
-}
-
 static void assert_branch_matches_name(
        const char *expected, const char *lookup_as)
 {
index bec39e18ba891681dad37909881734f31785ccd1..acd8661b8de1eee42eadf73c5f17cbc058856d9e 100644 (file)
@@ -195,41 +195,6 @@ void test_refs_branches_move__moving_the_branch_pointed_at_by_HEAD_updates_HEAD(
        git_reference_free(branch);
 }
 
-void test_refs_branches_move__default_reflog_message(void)
-{
-       git_reference *branch;
-       git_reference *new_branch;
-       git_reflog *log;
-       const git_reflog_entry *entry;
-       git_signature *sig;
-       git_config *cfg;
-       git_oid id;
-
-       cl_git_pass(git_repository_config(&cfg, repo));
-       cl_git_pass(git_config_set_string(cfg, "user.name", "Foo Bar"));
-       cl_git_pass(git_config_set_string(cfg, "user.email", "foo@example.com"));
-       git_config_free(cfg);
-
-       cl_git_pass(git_signature_default(&sig, repo));
-
-       cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master"));
-       git_oid_cpy(&id, git_reference_target(branch));
-       cl_git_pass(git_branch_move(&new_branch, branch, "master2", 0));
-
-       cl_git_pass(git_reflog_read(&log, repo, git_reference_name(new_branch)));
-       entry = git_reflog_entry_byindex(log, 0);
-       cl_assert_equal_s("branch: renamed refs/heads/master to refs/heads/master2",
-                       git_reflog_entry_message(entry));
-       cl_assert_equal_s(sig->email, git_reflog_entry_committer(entry)->email);
-       cl_assert_equal_oid(&id, git_reflog_entry_id_old(entry));
-       cl_assert_equal_oid(&id, git_reflog_entry_id_new(entry));
-
-       git_reference_free(branch);
-       git_reference_free(new_branch);
-       git_reflog_free(log);
-       git_signature_free(sig);
-}
-
 void test_refs_branches_move__can_move_with_unicode(void)
 {
        git_reference *original_ref, *new_ref;
index 7a10411c86f8e22fd4eecda0468b7a51b4482958..228f479a07a2ecafaf747bda4b72c16161aa45b4 100644 (file)
@@ -6,7 +6,7 @@ void test_refs_crashes__double_free(void)
        git_reference *ref, *ref2;
        const char *REFNAME = "refs/heads/xxx";
 
-       cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+       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));
@@ -16,5 +16,5 @@ void test_refs_crashes__double_free(void)
        /* reference is gone from disk, so reloading it will fail */
        cl_git_fail(git_reference_lookup(&ref2, repo, REFNAME));
 
-       git_repository_free(repo);
+       cl_git_sandbox_cleanup();
 }
diff --git a/tests/refs/createwithlog.c b/tests/refs/createwithlog.c
deleted file mode 100644 (file)
index 4f64363..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-#include "clar_libgit2.h"
-
-#include "repository.h"
-#include "git2/reflog.h"
-#include "reflog.h"
-#include "ref_helpers.h"
-
-static const char *current_master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750";
-
-static git_repository *g_repo;
-
-void test_refs_createwithlog__initialize(void)
-{
-   g_repo = cl_git_sandbox_init("testrepo.git");
-}
-
-void test_refs_createwithlog__cleanup(void)
-{
-   cl_git_sandbox_cleanup();
-}
-
-void test_refs_createwithlog__creating_a_direct_reference_adds_a_reflog_entry(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!";
-
-       git_oid_fromstr(&id, current_master_tip);
-
-       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_HEX_ZERO) == 0);
-       cl_assert_equal_oid(&id, &entry->oid_cur);
-       cl_assert_equal_s(message, entry->msg);
-
-       git_reflog_free(reflog);
-       git_reference_free(reference);
-}
index c7745130908bae61ecec49aa41c7b62b1f8b6ec4..18e9d1d5b7f423ae4db485ff2ebb6eea6ca12e40 100644 (file)
@@ -6,12 +6,12 @@ static git_repository *repo;
 
 void test_refs_iterator__initialize(void)
 {
-       cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+       repo = cl_git_sandbox_init("testrepo.git");
 }
 
 void test_refs_iterator__cleanup(void)
 {
-       git_repository_free(repo);
+       cl_git_sandbox_cleanup();
 }
 
 static const char *refnames[] = {
@@ -36,6 +36,36 @@ static const char *refnames[] = {
        "refs/tags/taggerless",
        "refs/tags/test",
        "refs/tags/wrapped_tag",
+       NULL
+};
+
+static const char *refnames_with_symlink[] = {
+       "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/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)
@@ -46,21 +76,21 @@ static int refcmp_cb(const void *a, const void *b)
        return strcmp(refa->name, refb->name);
 }
 
-static void assert_all_refnames_match(git_vector *output)
+static void assert_all_refnames_match(const char **expected, git_vector *names)
 {
        size_t i;
        git_reference *ref;
 
-       cl_assert_equal_sz(output->length, ARRAY_SIZE(refnames));
-
-       git_vector_sort(output);
+       git_vector_sort(names);
 
-       git_vector_foreach(output, i, ref) {
-               cl_assert_equal_s(ref->name, refnames[i]);
+       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(output);
+       git_vector_free(names);
 }
 
 void test_refs_iterator__list(void)
@@ -82,7 +112,7 @@ void test_refs_iterator__list(void)
 
        git_reference_iterator_free(iter);
 
-       assert_all_refnames_match(&output);
+       assert_all_refnames_match(refnames, &output);
 }
 
 void test_refs_iterator__empty(void)
@@ -115,7 +145,29 @@ void test_refs_iterator__foreach(void)
        git_vector output;
        cl_git_pass(git_vector_init(&output, 32, &refcmp_cb));
        cl_git_pass(git_reference_foreach(repo, refs_foreach_cb, &output));
-       assert_all_refnames_match(&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)
@@ -156,11 +208,11 @@ void test_refs_iterator__foreach_name(void)
        cl_git_pass(
                git_reference_foreach_name(repo, refs_foreach_name_cb, &output));
 
-       cl_assert_equal_sz(output.length, ARRAY_SIZE(refnames));
        git_vector_sort(&output);
 
        git_vector_foreach(&output, i, name) {
-               cl_assert_equal_s(name, refnames[i]);
+               cl_assert(refnames[i] != NULL);
+               cl_assert_equal_s(refnames[i], name);
                git__free(name);
        }
 
@@ -194,7 +246,7 @@ void test_refs_iterator__concurrent_delete(void)
        const char *name;
        int error;
 
-       git_repository_free(repo);
+       cl_git_sandbox_cleanup();
        repo = cl_git_sandbox_init("testrepo");
 
        cl_git_pass(git_reference_iterator_new(&iter, repo));
@@ -215,7 +267,4 @@ void test_refs_iterator__concurrent_delete(void)
        cl_assert_equal_i(GIT_ITEROVER, error);
 
        cl_assert_equal_i(full_count, concurrent_count);
-
-       cl_git_sandbox_cleanup();
-       repo = NULL;
 }
index f7ca3f707d70a0f3d70c71ec979eb4ab2b427ddc..97461fd6945c1be1edb1ea9b09e90393526e52cf 100644 (file)
@@ -36,7 +36,7 @@ void test_refs_list__all(void)
        /* 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, 17);
+       cl_assert_equal_i((int)ref_list.count, 18);
 
        git_strarray_free(&ref_list);
 }
@@ -51,7 +51,7 @@ void test_refs_list__do_not_retrieve_references_which_name_end_with_a_lock_exten
                "144344043ba4d4a405da03de3844aa829ae8be0e\n");
 
        cl_git_pass(git_reference_list(&ref_list, g_repo));
-       cl_assert_equal_i((int)ref_list.count, 17);
+       cl_assert_equal_i((int)ref_list.count, 18);
 
        git_strarray_free(&ref_list);
 }
index 83f6109c07340a53426b606e93d86e70c2801526..09809e19e2e2648e0350416735338cf92a584f05 100644 (file)
@@ -117,3 +117,15 @@ void test_refs_peel__can_peel_fully_peeled_packed_refs(void)
                            "0df1a5865c8abfc09f1f2182e6a31be550e99f07",
                            GIT_OBJ_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_OBJ_TAG,
+                           "c2596aa0151888587ec5c0187f261e63412d9e11",
+                           GIT_OBJ_TAG);
+       assert_peel_generic(g_peel_repo,
+                           "refs/foo/tag-outside-tags", GIT_OBJ_TAG,
+                           "c2596aa0151888587ec5c0187f261e63412d9e11",
+                           GIT_OBJ_TAG);
+}
diff --git a/tests/refs/reflog/messages.c b/tests/refs/reflog/messages.c
new file mode 100644 (file)
index 0000000..a7a7766
--- /dev/null
@@ -0,0 +1,409 @@
+#include "clar_libgit2.h"
+
+#include "fileops.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_signature *sig;
+       git_annotated_commit *annotated;
+
+       cl_git_pass(git_signature_now(&sig, "me", "foo@example.com"));
+
+       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);
+       git_signature_free(sig);
+}
+
+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_signature *sig;
+       git_oid id;
+       const char *msg;
+
+       cl_git_pass(git_signature_now(&sig, "me", "foo@example.com"));
+
+       msg = "checkout: moving from master to e90810b8df3e80c413d903f631643c716887138d";
+       git_oid_fromstr(&id, "e90810b8df3e80c413d903f631643c716887138d");
+       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);
+
+       git_signature_free(sig);
+}
+
+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");
+       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_OBJ_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_OBJ_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_HEX_ZERO) == 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__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_buf buf = GIT_BUF_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_buf_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_HEX_ZERO,
+               git_oid_tostr_s(git_commit_id(target)),
+               g_email, git_buf_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_HEX_ZERO,
+               git_oid_tostr_s(git_commit_id(target)),
+               g_email, "branch: Created from e90810b8df3");
+
+       git_annotated_commit_free(annotated);
+       git_buf_free(&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);
+}
index 252242152f25eb46bcf669d97a4e55888cc5ac3e..bbcdbf91b1c986321a0a5369c7c2b79554091919 100644 (file)
@@ -4,7 +4,6 @@
 #include "git2/reflog.h"
 #include "reflog.h"
 
-static const char *merge_reflog_message = "commit (merge): Merge commit";
 static const char *new_ref = "refs/heads/test-reflog";
 static const char *current_master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750";
 #define commit_msg "commit: bla bla"
@@ -448,45 +447,3 @@ void test_refs_reflog_reflog__logallrefupdates_nonbare_set_false(void)
 
        assert_no_reflog_update();
 }
-
-void test_refs_reflog_reflog__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;
-       git_reflog *log;
-       const git_reflog_entry *entry;
-
-       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_git_pass(git_reflog_read(&log, g_repo, "HEAD"));
-       entry = git_reflog_entry_byindex(log, 0);
-       cl_assert_equal_s(merge_reflog_message, git_reflog_entry_message(entry));
-
-       git_reflog_free(log);
-       git_tree_free(tree);
-       git_commit_free(b1_commit);
-       git_commit_free(b2_commit);
-       git_signature_free(s);
-}
diff --git a/tests/refs/reflog/reflog_helpers.c b/tests/refs/reflog/reflog_helpers.c
new file mode 100644 (file)
index 0000000..5e36736
--- /dev/null
@@ -0,0 +1,118 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "reflog.h"
+
+static int reflog_entry_tostr(git_buf *out, const git_reflog_entry *entry)
+{
+       char old_oid[GIT_OID_HEXSZ], new_oid[GIT_OID_HEXSZ];
+
+       assert(out && entry);
+
+       git_oid_tostr((char *)&old_oid, GIT_OID_HEXSZ, git_reflog_entry_id_old(entry));
+       git_oid_tostr((char *)&new_oid, GIT_OID_HEXSZ, git_reflog_entry_id_new(entry));
+
+       return git_buf_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, int line)
+{
+       git_reflog *log;
+       const git_reflog_entry *entry;
+       git_buf result = GIT_BUF_INIT;
+
+       cl_git_pass(git_reflog_read(&log, repo, reflog));
+       entry = git_reflog_entry_byindex(log, idx);
+       if (entry == NULL)
+               clar__fail(file, 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_oid__writebuf(&result, "\tOld OID: \"", git_object_id(obj));
+                               git_oid__writebuf(&result, "\" != \"", git_reflog_entry_id_old(entry));
+                               git_buf_puts(&result, "\"\n");
+                       }
+                       git_object_free(obj);
+               } else {
+                       git_oid *oid = git__calloc(1, sizeof(*oid));
+                       git_oid_fromstr(oid, old_spec);
+                       if (git_oid_cmp(oid, git_reflog_entry_id_old(entry)) != 0) {
+                               git_oid__writebuf(&result, "\tOld OID: \"", oid);
+                               git_oid__writebuf(&result, "\" != \"", git_reflog_entry_id_old(entry));
+                               git_buf_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_oid__writebuf(&result, "\tNew OID: \"", git_object_id(obj));
+                               git_oid__writebuf(&result, "\" != \"", git_reflog_entry_id_new(entry));
+                               git_buf_puts(&result, "\"\n");
+                       }
+                       git_object_free(obj);
+               } else {
+                       git_oid *oid = git__calloc(1, sizeof(*oid));
+                       git_oid_fromstr(oid, new_spec);
+                       if (git_oid_cmp(oid, git_reflog_entry_id_new(entry)) != 0) {
+                               git_oid__writebuf(&result, "\tNew OID: \"", oid);
+                               git_oid__writebuf(&result, "\" != \"", git_reflog_entry_id_new(entry));
+                               git_buf_puts(&result, "\"\n");
+                       }
+                       git__free(oid);
+               }
+       }
+
+       if (email && strcmp(email, git_reflog_entry_committer(entry)->email) != 0)
+               git_buf_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_buf_printf(&result, "\tMessage: \"%s\" != \"%s\"\n", message, entry_msg);
+       }
+       if (git_buf_len(&result) != 0)
+               clar__fail(file, line, "Reflog entry mismatch", git_buf_cstr(&result), 1);
+
+       git_buf_free(&result);
+       git_reflog_free(log);
+}
+
+void reflog_print(git_repository *repo, const char *reflog_name)
+{
+       git_reflog *reflog;
+       size_t idx;
+       git_buf out = GIT_BUF_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_buf_putc(&out, '\n');
+       }
+
+       fprintf(stderr, "%s", git_buf_cstr(&out));
+       git_buf_free(&out);
+       git_reflog_free(reflog);
+}
diff --git a/tests/refs/reflog/reflog_helpers.h b/tests/refs/reflog/reflog_helpers.h
new file mode 100644 (file)
index 0000000..80814ea
--- /dev/null
@@ -0,0 +1,10 @@
+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__, __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, int line);
+
+void reflog_print(git_repository *repo, const char *reflog_name);
index 6106e6c67ccdb45c541422c6990b7d09532cdf7e..3e0be313cabe6be396e2d649cbcdcab8a7cd94e6 100644 (file)
@@ -366,22 +366,3 @@ void test_refs_rename__propagate_eexists(void)
 
        git_reference_free(ref);
 }
-
-void test_refs_rename__writes_to_reflog(void)
-{
-       git_reference *ref, *new_ref;
-       git_reflog *log;
-       const git_reflog_entry *entry;
-
-       cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
-       cl_git_pass(git_reference_rename(&new_ref, ref, ref_one_name_new, false,
-                               "message"));
-       cl_git_pass(git_reflog_read(&log, g_repo, git_reference_name(new_ref)));
-       entry = git_reflog_entry_byindex(log, 0);
-       cl_assert_equal_s("message", git_reflog_entry_message(entry));
-       cl_assert_equal_s("foo@example.com", git_reflog_entry_committer(entry)->email);
-
-       git_reflog_free(log);
-       git_reference_free(ref);
-       git_reference_free(new_ref);
-}
diff --git a/tests/refs/settargetwithlog.c b/tests/refs/settargetwithlog.c
deleted file mode 100644 (file)
index 58fbb5f..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-#include "clar_libgit2.h"
-
-#include "repository.h"
-#include "git2/reflog.h"
-#include "reflog.h"
-#include "ref_helpers.h"
-
-static const char *br2_tip = "a4a7dce85cf63874e984719f4fdd239f5145052f";
-static const char *master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750";
-static const char *br2_name = "refs/heads/br2";
-
-static git_repository *g_repo;
-
-void test_refs_settargetwithlog__initialize(void)
-{
-   g_repo = cl_git_sandbox_init("testrepo.git");
-}
-
-void test_refs_settargetwithlog__cleanup(void)
-{
-   cl_git_sandbox_cleanup();
-}
-
-void test_refs_settargetwithlog__updating_a_direct_reference_adds_a_reflog_entry(void)
-{
-       git_reference *reference, *reference_out;
-       git_oid current_id, target_id;
-       git_reflog *reflog;
-       const git_reflog_entry *entry;
-
-       const char *message = "You've been logged, mate!";
-
-       git_oid_fromstr(&current_id, br2_tip);
-       git_oid_fromstr(&target_id, master_tip);
-
-       cl_git_pass(git_reference_lookup(&reference, g_repo, br2_name));
-
-       cl_git_pass(git_reference_set_target(
-               &reference_out, reference, &target_id, message));
-
-       cl_git_pass(git_reflog_read(&reflog, g_repo, br2_name));
-
-       entry = git_reflog_entry_byindex(reflog, 0);
-       cl_assert_equal_oid(&current_id, &entry->oid_old);
-       cl_assert_equal_oid(&target_id, &entry->oid_cur);
-       cl_assert_equal_s(message, entry->msg);
-
-       git_reflog_free(reflog);
-       git_reference_free(reference_out);
-       git_reference_free(reference);
-}
index d0211608707baf0d55ebd6a8f6e4fb06f418fdd7..ed3b3dcc33bcb0a0dbd11343b9084eae48517ce9 100644 (file)
@@ -18,40 +18,6 @@ void test_repo_head__cleanup(void)
        cl_git_sandbox_cleanup();
 }
 
-static void check_last_reflog_entry(const char *email, const char *message)
-{
-       git_reflog *log;
-       const git_reflog_entry *entry;
-
-       cl_git_pass(git_reflog_read(&log, repo, GIT_HEAD_FILE));
-       cl_assert(git_reflog_entrycount(log) > 0);
-       entry = git_reflog_entry_byindex(log, 0);
-       if (email)
-               cl_assert_equal_s(email, git_reflog_entry_committer(entry)->email);
-       if (message)
-               cl_assert_equal_s(message, git_reflog_entry_message(entry));
-       git_reflog_free(log);
-}
-
-void test_repo_head__head_detached(void)
-{
-       git_reference *ref;
-
-       cl_assert_equal_i(false, git_repository_head_detached(repo));
-
-       cl_git_pass(git_repository_detach_head(repo));
-       check_last_reflog_entry(g_email, "checkout: moving from master to a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
-       cl_assert_equal_i(true, git_repository_head_detached(repo));
-
-       /* take the repo back to it's original state */
-       cl_git_pass(git_reference_symbolic_create(&ref, repo, "HEAD", "refs/heads/master",
-                               true, "REATTACH"));
-       git_reference_free(ref);
-
-       check_last_reflog_entry(g_email, "REATTACH");
-       cl_assert_equal_i(false, git_repository_head_detached(repo));
-}
-
 void test_repo_head__unborn_head(void)
 {
        git_reference *ref;
@@ -214,252 +180,3 @@ void test_repo_head__can_tell_if_an_unborn_head_is_detached(void)
 
        cl_assert_equal_i(false, git_repository_head_detached(repo));
 }
-
-static void test_reflog(git_repository *repo, size_t idx,
-               const char *old_spec, const char *new_spec,
-               const char *email, const char *message)
-{
-       git_reflog *log;
-       const git_reflog_entry *entry;
-
-       cl_git_pass(git_reflog_read(&log, repo, "HEAD"));
-       entry = git_reflog_entry_byindex(log, idx);
-
-       if (old_spec) {
-               git_object *obj;
-               cl_git_pass(git_revparse_single(&obj, repo, old_spec));
-               cl_assert_equal_oid(git_object_id(obj), git_reflog_entry_id_old(entry));
-               git_object_free(obj);
-       }
-       if (new_spec) {
-               git_object *obj;
-               cl_git_pass(git_revparse_single(&obj, repo, new_spec));
-               cl_assert_equal_oid(git_object_id(obj), git_reflog_entry_id_new(entry));
-               git_object_free(obj);
-       }
-
-       if (email) {
-               cl_assert_equal_s(email, git_reflog_entry_committer(entry)->email);
-       }
-       if (message) {
-               cl_assert_equal_s(message, git_reflog_entry_message(entry));
-       }
-
-       git_reflog_free(log);
-}
-
-void test_repo_head__setting_head_updates_reflog(void)
-{
-       git_object *tag;
-       git_signature *sig;
-       git_annotated_commit *annotated;
-
-       cl_git_pass(git_signature_now(&sig, "me", "foo@example.com"));
-
-       cl_git_pass(git_repository_set_head(repo, "refs/heads/haacked"));
-       cl_git_pass(git_repository_set_head(repo, "refs/heads/unborn"));
-       cl_git_pass(git_revparse_single(&tag, repo, "tags/test"));
-       cl_git_pass(git_repository_set_head_detached(repo, git_object_id(tag)));
-       cl_git_pass(git_repository_set_head(repo, "refs/heads/haacked"));
-       cl_git_pass(git_repository_set_head(repo, "refs/tags/test"));
-       cl_git_pass(git_repository_set_head(repo, "refs/remotes/test/master"));
-
-       test_reflog(repo, 4, NULL, "refs/heads/haacked", "foo@example.com", "checkout: moving from master to haacked");
-       test_reflog(repo, 3, NULL, "tags/test^{commit}", "foo@example.com", "checkout: moving from unborn to e90810b8df3e80c413d903f631643c716887138d");
-       test_reflog(repo, 2, "tags/test^{commit}", "refs/heads/haacked", "foo@example.com", "checkout: moving from e90810b8df3e80c413d903f631643c716887138d to haacked");
-       test_reflog(repo, 1, "refs/heads/haacked", "tags/test^{commit}", "foo@example.com", "checkout: moving from haacked to test");
-       test_reflog(repo, 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, repo, "haacked~0"));
-       cl_git_pass(git_repository_set_head_detached_from_annotated(repo, annotated));
-
-       test_reflog(repo, 0, NULL, "refs/heads/haacked", "foo@example.com", "checkout: moving from be3563ae3f795b2b4353bcce3a527ad0a4f7f644 to haacked~0");
-
-       git_annotated_commit_free(annotated);
-       git_object_free(tag);
-       git_signature_free(sig);
-}
-
-static void assert_head_reflog(git_repository *repo, size_t idx,
-                              const char *old_id, const char *new_id, const char *message)
-{
-       git_reflog *log;
-       const git_reflog_entry *entry;
-       char id_str[GIT_OID_HEXSZ + 1] = {0};
-
-       cl_git_pass(git_reflog_read(&log, repo, GIT_HEAD_FILE));
-       entry = git_reflog_entry_byindex(log, idx);
-
-       git_oid_fmt(id_str, git_reflog_entry_id_old(entry));
-       cl_assert_equal_s(old_id, id_str);
-
-       git_oid_fmt(id_str, git_reflog_entry_id_new(entry));
-       cl_assert_equal_s(new_id, id_str);
-
-       cl_assert_equal_s(message, git_reflog_entry_message(entry));
-
-       git_reflog_free(log);
-}
-
-void test_repo_head__detaching_writes_reflog(void)
-{
-       git_signature *sig;
-       git_oid id;
-       const char *msg;
-
-       cl_git_pass(git_signature_now(&sig, "me", "foo@example.com"));
-
-       msg = "checkout: moving from master to e90810b8df3e80c413d903f631643c716887138d";
-       git_oid_fromstr(&id, "e90810b8df3e80c413d903f631643c716887138d");
-       cl_git_pass(git_repository_set_head_detached(repo, &id));
-       assert_head_reflog(repo, 0, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
-                          "e90810b8df3e80c413d903f631643c716887138d", msg);
-
-       msg = "checkout: moving from e90810b8df3e80c413d903f631643c716887138d to haacked";
-       cl_git_pass(git_repository_set_head(repo, "refs/heads/haacked"));
-       assert_head_reflog(repo, 0, "e90810b8df3e80c413d903f631643c716887138d",
-                          "258f0e2a959a364e40ed6603d5d44fbb24765b10", msg);
-
-       git_signature_free(sig);
-}
-
-void test_repo_head__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");
-       cl_git_pass(git_repository_set_head_detached(repo, &id));
-       assert_head_reflog(repo, 0, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
-                          "e90810b8df3e80c413d903f631643c716887138d", msg);
-
-       /* Switching to an orphan branch does not write tot he reflog */
-       cl_git_pass(git_repository_set_head(repo, "refs/heads/orphan"));
-       assert_head_reflog(repo, 0, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
-                          "e90810b8df3e80c413d903f631643c716887138d", msg);
-
-       /* And coming back, we set the source to zero */
-       msg = "checkout: moving from orphan to haacked";
-       cl_git_pass(git_repository_set_head(repo, "refs/heads/haacked"));
-       assert_head_reflog(repo, 0, "0000000000000000000000000000000000000000",
-                          "258f0e2a959a364e40ed6603d5d44fbb24765b10", msg);
-}
-
-void test_repo_head__set_to_current_target(void)
-{
-       git_reflog *log;
-       size_t nentries, nentries_after;
-
-       cl_git_pass(git_reflog_read(&log, repo, GIT_HEAD_FILE));
-       nentries = git_reflog_entrycount(log);
-       git_reflog_free(log);
-
-       cl_git_pass(git_repository_set_head(repo, "refs/heads/haacked"));
-       cl_git_pass(git_repository_set_head(repo, "refs/heads/haacked"));
-
-       cl_git_pass(git_reflog_read(&log, repo, GIT_HEAD_FILE));
-       nentries_after = git_reflog_entrycount(log);
-       git_reflog_free(log);
-
-       cl_assert_equal_i(nentries + 1, nentries_after);
-}
-
-void test_repo_head__branch_birth(void)
-{
-       git_signature *sig;
-       git_oid id;
-       git_tree *tree;
-       git_reference *ref;
-       const char *msg;
-       git_reflog *log;
-       size_t nentries, nentries_after;
-
-       cl_git_pass(git_reflog_read(&log, repo, GIT_HEAD_FILE));
-       nentries = git_reflog_entrycount(log);
-       git_reflog_free(log);
-
-       cl_git_pass(git_signature_now(&sig, "me", "foo@example.com"));
-
-       cl_git_pass(git_repository_head(&ref, repo));
-       cl_git_pass(git_reference_peel((git_object **) &tree, ref, GIT_OBJ_TREE));
-       git_reference_free(ref);
-
-       cl_git_pass(git_repository_set_head(repo, "refs/heads/orphan"));
-
-       cl_git_pass(git_reflog_read(&log, repo, GIT_HEAD_FILE));
-       nentries_after = git_reflog_entrycount(log);
-       git_reflog_free(log);
-
-       cl_assert_equal_i(nentries, nentries_after);
-
-       msg = "message 2";
-       cl_git_pass(git_commit_create(&id, repo, "HEAD", sig, sig, NULL, msg, tree, 0, NULL));
-
-       git_tree_free(tree);
-
-       cl_git_pass(git_reflog_read(&log, repo, "refs/heads/orphan"));
-       cl_assert_equal_i(1, git_reflog_entrycount(log));
-       git_reflog_free(log);
-
-       cl_git_pass(git_reflog_read(&log, repo, GIT_HEAD_FILE));
-       nentries_after = git_reflog_entrycount(log);
-       git_reflog_free(log);
-
-       cl_assert_equal_i(nentries + 1, nentries_after);
-
-       git_signature_free(sig);
-
-}
-
-static size_t 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 test_repo_head__symref_chain(void)
-{
-       git_signature *sig;
-       git_oid id;
-       git_tree *tree;
-       git_reference *ref;
-       const char *msg;
-       size_t nentries, nentries_master;
-
-       nentries = entrycount(repo, GIT_HEAD_FILE);
-
-       cl_git_pass(git_signature_now(&sig, "me", "foo@example.com"));
-
-       cl_git_pass(git_repository_head(&ref, repo));
-       cl_git_pass(git_reference_peel((git_object **) &tree, ref, GIT_OBJ_TREE));
-       git_reference_free(ref);
-
-       nentries_master = entrycount(repo, "refs/heads/master");
-
-       msg = "message 1";
-       cl_git_pass(git_reference_symbolic_create(&ref, repo, "refs/heads/master", "refs/heads/foo", 1, msg));
-       git_reference_free(ref);
-
-       cl_assert_equal_i(0, entrycount(repo, "refs/heads/foo"));
-       cl_assert_equal_i(nentries, entrycount(repo, GIT_HEAD_FILE));
-       cl_assert_equal_i(nentries_master, entrycount(repo, "refs/heads/master"));
-
-       msg = "message 2";
-       cl_git_pass(git_commit_create(&id, repo, "HEAD", sig, sig, NULL, msg, tree, 0, NULL));
-       git_tree_free(tree);
-
-       cl_assert_equal_i(1, entrycount(repo, "refs/heads/foo"));
-       cl_assert_equal_i(nentries +1, entrycount(repo, GIT_HEAD_FILE));
-       cl_assert_equal_i(nentries_master, entrycount(repo, "refs/heads/master"));
-
-       git_signature_free(sig);
-
-}
index 04d4a5c5ec770b9117fc1dfb4d3a92089f6706a2..0f969eb18cdcc8301fe5973d39fba90aa1e96758 100644 (file)
@@ -320,6 +320,17 @@ void test_repo_init__sets_logAllRefUpdates_according_to_type_of_repository(void)
        assert_config_entry_on_init_bytype("core.logallrefupdates", true, false);
 }
 
+void test_repo_init__empty_template_path(void)
+{
+       git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+       opts.template_path = "";
+
+       cl_git_pass(git_futils_mkdir("foo", 0755, 0));
+       cl_git_pass(git_repository_init_ext(&_repo, "foo", &opts));
+
+       cleanup_repository("foo");
+}
+
 void test_repo_init__extended_0(void)
 {
        git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
@@ -744,7 +755,7 @@ void test_repo_init__init_with_initial_commit(void)
        /* Initialize the repository */
        cl_git_pass(git_repository_init(&_repo, "committed", 0));
 
-       /* Init will be automatically created when requested for a new repo */
+       /* Index will be automatically created when requested for a new repo */
        cl_git_pass(git_repository_index(&index, _repo));
 
        /* Create a file so we can commit it
index 3239b6fecf2722e3483f04e49c7d6d3a6bed249c..ab36dd587f90d981fe1b2b07c20aa945b3fb33da 100644 (file)
@@ -180,6 +180,8 @@ void test_repo_open__from_git_new_workdir(void)
        cl_assert_(git__suffixcmp(git_repository_workdir(repo2), "alternate/") == 0, git_repository_workdir(repo2));
 
        git_repository_free(repo2);
+#else
+       cl_skip();
 #endif
 }
 
diff --git a/tests/resources/merge-recursive/.gitted/objects/03/9d0da126f24b819a5a38186249c7f96be3824c b/tests/resources/merge-recursive/.gitted/objects/03/9d0da126f24b819a5a38186249c7f96be3824c
new file mode 100644 (file)
index 0000000..79ad635
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/03/9d0da126f24b819a5a38186249c7f96be3824c 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 (file)
index 0000000..c6d38a7
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/05/63b7706dcdcf94bc0c02cd96c137940278eca9 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 (file)
index 0000000..c2fbf5b
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/08/f01e1bff7e442d574eb221913515b4bd27ccd6 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 (file)
index 0000000..66b9272
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/0b/beee1982b493330e375a85bbfddaba3d561556 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 (file)
index 0000000..9a84b71
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/0c/e202f64fa8356c1a32835fce4058ca76b0c7ed 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 (file)
index 0000000..68808d2
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/0e/c39d71c1b074905350ce20ce3f0629f737a2a9 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 (file)
index 0000000..9cc133e
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/17/946ad3088f931102e5d81f94cf2825fc188953 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 (file)
index 0000000..96674c8
--- /dev/null
@@ -0,0 +1,5 @@
+x\ 1\9d\8eK
+\ 21\10\ 5\14¹\80\92\13\10ñ\10â>ééè@ÆHÌ\bÞÞñ
+î^\15\14<jË2\ fi4îFg\96\89
++\8cÁ\ 5\ e\f¤3ØL6{vX
+`ÙFÐâ\99:?\86´\11Ct\8e\1dM`y"Ò*iT)\9bR´3\ 5M\84\1c!\88´\8e{ëòòé3Ë+×\9aäqü ¾Ï·%Íõ@m9I\rÚ{ð¨\9cÜ« \94Øìvpð\1f©xµµ\13\8b\90E\17
\ 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 (file)
index 0000000..8117167
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/1b/c7bcccf4bbdc8bfba2331a37ad5e9cf1dd321c 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 (file)
index 0000000..67daf36
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/1b/de1883de4977ea3e664b315da951d1f614c3b1 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 (file)
index 0000000..03be909
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/23/b427bf6278724433e64ef4cf6dc166c4f2e246 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 (file)
index 0000000..5cc665a
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/23/cf2687a9327d55abbbd788ff04fa932072aebc 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 (file)
index 0000000..dd7b1a2
--- /dev/null
@@ -0,0 +1,2 @@
+x\ 1\9d\8eA
+\ 21\fE]÷\14¹\80\92¤vÚ\82\88+O î3mF\a¦\8e\94*x{ë\15\þ÷yðÒZÊÜ\80\897­ªB\14æ\90H5ø\89ÃÀÈ\9a:sY\83õBÁ9D²£3O©úhàûk1\87\9cT-£Ø\91¦¨\99pt\81÷>N"1[!#¯v_+\>uV¸ê²\b\1cÚo,ïӭȼìÒZ\8e@\8e\ 6æî2l1 \9aN{`Ó?Ts6_\11?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 (file)
index 0000000..c0a60a1
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/2c/ba583804a4a6fad1baf97c959be447238d1489 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 (file)
index 0000000..6a9651c
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/2e/7ae0d42fb7b6126f6a08ac6314ac07833a52f6 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 (file)
index 0000000..cc21b5c
--- /dev/null
@@ -0,0 +1,3 @@
+x\ 1\9d\8eQ
+\ 2!\10@ûö\14s\81BÇÕ\14"úé\ 6Ñÿè\8cµ m\88\ 5ݾí
+}¾\a\ f^^Z\9b\a ÁÍè"ÀÉ\19â\82ÉÇ(ÂÑJ\b\96ý\84®8ñÙiã\1dÅ\8cêI]\1e\ 3ö9\92Õ\1c8\8bXÔd\93)QØèä\ 2NûX\88"[2\8a^ã¾t¸|ú,p\95Z        \ eã\aõ}º5\9aë./í\bÆ\19\8f¸¶\b[\1d´V«]\a\87ü\91ª³ú\ 2¯¯Bü
\ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/35/8efd6f589384fa8baf92234db9c7899a53916e b/tests/resources/merge-recursive/.gitted/objects/35/8efd6f589384fa8baf92234db9c7899a53916e
new file mode 100644 (file)
index 0000000..aefc81a
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/35/8efd6f589384fa8baf92234db9c7899a53916e 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 (file)
index 0000000..d6cabb4
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/35/dda4f3f9b3794d92a46d908790e550ed100eae 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 (file)
index 0000000..4f70ad6
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/36/71e42c8c8302d1a71c0ed7bf2b0a938e9e20f9 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 (file)
index 0000000..b18fd48
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/39/78944e4cd53edcc10a170ab2ff142f7295b958 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 (file)
index 0000000..fdff502
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/3a/0dc89a8bd20e74fae69d2e038b47360fafb02e differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/3a/8c70144d0334721154b1e0529716b368483d6f b/tests/resources/merge-recursive/.gitted/objects/3a/8c70144d0334721154b1e0529716b368483d6f
new file mode 100644 (file)
index 0000000..958d17d
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/3a/8c70144d0334721154b1e0529716b368483d6f 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 (file)
index 0000000..6a6c654
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/3e/eff81b57a0ac15a5ab6bb3a8e92511a01a429c 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 (file)
index 0000000..b4c9005
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/40/9f5d072decec684331672f2d6c0a9bc3640adb differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/44/faf5fba1af850dae54f8b2345938d3c7ae479f b/tests/resources/merge-recursive/.gitted/objects/44/faf5fba1af850dae54f8b2345938d3c7ae479f
new file mode 100644 (file)
index 0000000..d0bc099
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/44/faf5fba1af850dae54f8b2345938d3c7ae479f 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 (file)
index 0000000..d3e1815
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/4a/06b258fed8a4d15967ec4253ae7366b70f727d 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 (file)
index 0000000..adf6411
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 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 (file)
index 0000000..f3cee9a
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/4c/62e9482ed42c1a6d08891906b26126daa4a8f5 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 (file)
index 0000000..214d307
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/50/4dd93fb5b9c2a28c094c6e84ef0606de1e9b5c 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 (file)
index 0000000..7c90c99
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/50/dfa64a56b488fe8082371b182c8a3e3c942332 differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/51/135c5884d7dd132fef3b432cca5826bab98f37 b/tests/resources/merge-recursive/.gitted/objects/51/135c5884d7dd132fef3b432cca5826bab98f37
new file mode 100644 (file)
index 0000000..95818f1
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/51/135c5884d7dd132fef3b432cca5826bab98f37 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 (file)
index 0000000..f3f99d7
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/51/60ab78c1973dcd7cdebe2345dc8fcfc755e76d differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/56/fcbad344aafe519bafcc33c87b8e64849d82ab b/tests/resources/merge-recursive/.gitted/objects/56/fcbad344aafe519bafcc33c87b8e64849d82ab
new file mode 100644 (file)
index 0000000..06bea32
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/56/fcbad344aafe519bafcc33c87b8e64849d82ab 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 (file)
index 0000000..c1e30ce
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/5a/47615db824433f816ba62217dda6d46c5a7640 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 (file)
index 0000000..783d085
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/5c/27b5f7c6f6dd4e5b4d64976741d56c2df8f48a 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 (file)
index 0000000..a7795f5
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/5d/998d5f278aff5693711bc48f6852aac4b603ad 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 (file)
index 0000000..6a06214
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/61/6d1209afac499b005f68309e1593b44899b054 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 (file)
index 0000000..23c4033
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/71/c50785d8d512293bd3af838b131f3da5829ebc 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 (file)
index 0000000..11d7f94
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/75/afa96db00c26c6ebf3b377615b4e2a20563ee4 differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/76/6afbfd7d42f757f1fac9ea550c9fcbc8041b89 b/tests/resources/merge-recursive/.gitted/objects/76/6afbfd7d42f757f1fac9ea550c9fcbc8041b89
new file mode 100644 (file)
index 0000000..a5af383
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/76/6afbfd7d42f757f1fac9ea550c9fcbc8041b89 differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/7c/61830f8b8632665bb44ae5d219f520f5aa5bb4 b/tests/resources/merge-recursive/.gitted/objects/7c/61830f8b8632665bb44ae5d219f520f5aa5bb4
new file mode 100644 (file)
index 0000000..04b10f7
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/7c/61830f8b8632665bb44ae5d219f520f5aa5bb4 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 (file)
index 0000000..7500a24
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/7c/9a30d8dcee320a3b1f9ed10b582479faa9d3a1 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 (file)
index 0000000..11f96c9
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/7e/2d2bad4fc21f2832ca2afd48b1f95ab37ffb92 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 (file)
index 0000000..cab4639
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/7e/70a7872576bba7e299cde45abb7da1e4d7ba81 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 (file)
index 0000000..46c8e85
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/7f/9c1d78d760cbfa99273bc1ef642d994c6baa5c differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/81/60cb53660b86c954144b8dbbb0b6e4db4ba6ba b/tests/resources/merge-recursive/.gitted/objects/81/60cb53660b86c954144b8dbbb0b6e4db4ba6ba
new file mode 100644 (file)
index 0000000..19d32c7
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/81/60cb53660b86c954144b8dbbb0b6e4db4ba6ba differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/8f/1b918542a5fe9b3bb7a8770a7525ad5b3b5864 b/tests/resources/merge-recursive/.gitted/objects/8f/1b918542a5fe9b3bb7a8770a7525ad5b3b5864
new file mode 100644 (file)
index 0000000..5ecb3e5
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/8f/1b918542a5fe9b3bb7a8770a7525ad5b3b5864 differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/97/5dd228fd1b0cacf2988167088fd1190c9ac0f5 b/tests/resources/merge-recursive/.gitted/objects/97/5dd228fd1b0cacf2988167088fd1190c9ac0f5
new file mode 100644 (file)
index 0000000..96658c4
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/97/5dd228fd1b0cacf2988167088fd1190c9ac0f5 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 (file)
index 0000000..bc95c6f
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/98/5b725cf91c6861b5e7a419415d03cbcf5f16ca 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 (file)
index 0000000..13344d8
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/98/cacbdd1fac7bbab54a6c7c97aa2103219e08b8 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 (file)
index 0000000..40455ef
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/99/754e36599906b81b917447280c4918269e14ff 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 (file)
index 0000000..d6bcd52
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/9a/228c1ee87f286202ec9a25de837a18550013b5 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 (file)
index 0000000..9f2c928
--- /dev/null
@@ -0,0 +1,2 @@
+x\ 1\9d\8eQ
+B!\10Eûv\15n Pç\8d\1aDôÑ\12¢\7f\9d\19ëÁ³\17bA»Ï¶Ðç¹\9c\v\87ÖZç®\9d\85Mo"\1a­7)\87Hv\1f\80\89\ 3±dq0!S,T( Jð¬\9e©É£\ fß\ 2\12Æ8q`¶à\8a\14È\13\84Ñù\9cò>\16\b*½ú}múòi³è«,KÒ\87þ\83å}ºÕ4/;ZëQÛ\11àÑ»\88zk¢1j¬#°Ë\1fWuvê\vüÈCN
\ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/a0/ce8909834f389b4f8be6a6ec420868422d40a1 b/tests/resources/merge-recursive/.gitted/objects/a0/ce8909834f389b4f8be6a6ec420868422d40a1
new file mode 100644 (file)
index 0000000..5f6643c
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/a0/ce8909834f389b4f8be6a6ec420868422d40a1 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 (file)
index 0000000..a043a8e
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/a2/817ed0e8ca6fe52bf0a20b2f50eb94b9ea5415 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 (file)
index 0000000..e71e9ec
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/a3/5aa65d86215fce909fc0bcce8949d12becba44 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 (file)
index 0000000..dff9e97
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/a3/ca4c462e93fee824c8ad500917ae34b800dea4 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 (file)
index 0000000..f6b66da
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/a6/64873b1c0b9a1ed300f8644dde536fdaa3a34f 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 (file)
index 0000000..9c3a3ec
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/a9/9bf55117ab1958171fccfeb19885f707bd08fd 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 (file)
index 0000000..786c9a5
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/b0/1de62cf11945685c98ec671edabdff3e90ddc5 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 (file)
index 0000000..81714b0
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/b0/4823b75c8220b89c2f8da54709cda262304cd3 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 (file)
index 0000000..987d5fe
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/b1/71224a4f604b6091072007765419b14c232c1d 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 (file)
index 0000000..7f0e0ab
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/b2/908343e3c16249d0036dd444fc0d4662cd8c0e 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 (file)
index 0000000..87cb8fa
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/b6/bd0f9952f396e757d3f91e08c59a7e91707201 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 (file)
index 0000000..6fbf581
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/b7/de2b52ba055688061355fad1599a5d214ce8f8 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 (file)
index 0000000..0b8404b
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/b8/a3b657edcf31e6365a2f1c45d45e6c9ebe8f02 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 (file)
index 0000000..c43b79d
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/ba/9dcfe079848e8e5c1b53bc3b6e47ff57f6e481 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 (file)
index 0000000..ca45760
--- /dev/null
@@ -0,0 +1,4 @@
+x\ 1\9d\8fKj\ 31\10\ 5½Ö)t\81\98VëÛ\10\8cs\a\93}«Õ²\af2f\90\ 3¹½'\v_ÀËWT-\9e¬Ë2\r\8b     \ ecSµ¹\93¸\96KË      ¤v&Âì«8í)`#
+\92*s\14sçM\7f\86õàI ·\9a(J\11¢\ 6\9c%«Ç\ eÜ5\80²BUxù\98\9a\17
+!R\r\9d\19J\a êbI\99È\vûà¢\8f\84\86\1fã¶nöò·Mj¿u\9eÙ~\8eÿ1ÿ\9e¯\vOóQÖåd]t      1\16\fö\ 3
+\80Ùé~hè\e©ù2Oä¼P#
\ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/c0/dcb4bfcd86e65a822090aa7a0455413828886b b/tests/resources/merge-recursive/.gitted/objects/c0/dcb4bfcd86e65a822090aa7a0455413828886b
new file mode 100644 (file)
index 0000000..f8fe201
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/c0/dcb4bfcd86e65a822090aa7a0455413828886b 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 (file)
index 0000000..b2f6662
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/c4/44758b02d4af6e3145ac2fc0e3ed02199cf7ec differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/c7/f3257db72e885d6612080c003e0f2ef480e0c4 b/tests/resources/merge-recursive/.gitted/objects/c7/f3257db72e885d6612080c003e0f2ef480e0c4
new file mode 100644 (file)
index 0000000..255624e
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/c7/f3257db72e885d6612080c003e0f2ef480e0c4 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 (file)
index 0000000..63c457a
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/ce/0d744cd2e18eacf883d43471636f231c0995e3 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 (file)
index 0000000..51ffec0
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/d0/97bcf99adb1022a6b7d2e94fed2031ebd9d89c 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 (file)
index 0000000..5a0508c
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/d0/c9bd6e2a3e327d81a32de51201d3bd58909f7c differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/d3/482dbdca5bb83aaf3e3768359855d55aef84d7 b/tests/resources/merge-recursive/.gitted/objects/d3/482dbdca5bb83aaf3e3768359855d55aef84d7
new file mode 100644 (file)
index 0000000..8442948
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/d3/482dbdca5bb83aaf3e3768359855d55aef84d7 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 (file)
index 0000000..6d4446e
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/d5/015f9436b2d8c842bf6616e7cf5bc54eb79ced differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/db/51adf2b699eed93e883d6425f5e6c50165a9c2 b/tests/resources/merge-recursive/.gitted/objects/db/51adf2b699eed93e883d6425f5e6c50165a9c2
new file mode 100644 (file)
index 0000000..41b481e
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/db/51adf2b699eed93e883d6425f5e6c50165a9c2 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 (file)
index 0000000..0a0ad65
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/db/7e2af8ca83b8943adce7ba37d85f8fe7d7d2a9 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 (file)
index 0000000..c275071
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/de/de92a05a0841faa8e4ad6700285cd208184458 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 (file)
index 0000000..a75a6b6
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/e0/15ebd79a72a88b9291df11771caf56f463e8f9 differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/e5/20e6aaf8d1e68a433e29d4360c1e74aa4b24d1 b/tests/resources/merge-recursive/.gitted/objects/e5/20e6aaf8d1e68a433e29d4360c1e74aa4b24d1
new file mode 100644 (file)
index 0000000..bcf2dcf
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/e5/20e6aaf8d1e68a433e29d4360c1e74aa4b24d1 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 (file)
index 0000000..f9a0a27
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/e6/269ce9017816d67c7189a58b6d0d22bf4b8a1a 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 (file)
index 0000000..c0ba76d
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/e9/30c8c67848df4aa66319c5752fab6b8fdec765 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 (file)
index 0000000..40d8984
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/ea/3521485adfa0b0373deaaa06db9218a22edae8 differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/f0/856993e005c0d8ed2dc7cdc222cc1d89fb3c77 b/tests/resources/merge-recursive/.gitted/objects/f0/856993e005c0d8ed2dc7cdc222cc1d89fb3c77
new file mode 100644 (file)
index 0000000..22b1ad9
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/f0/856993e005c0d8ed2dc7cdc222cc1d89fb3c77 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 (file)
index 0000000..b918729
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/f2/9ccca75754d8476e5dad8cf250e03d43fe9e6c 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 (file)
index 0000000..be3cecc
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/f3/2c284f537ff1a55d3cbfe9a37d431b6edfadc2 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 (file)
index 0000000..e9c675b
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/f4/c149e7d0983e90e9ee802ff57ae3c905ba63da differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/f6/5de1834f57708e76d8dc25502b7f1ecbcce162 b/tests/resources/merge-recursive/.gitted/objects/f6/5de1834f57708e76d8dc25502b7f1ecbcce162
new file mode 100644 (file)
index 0000000..3d238fd
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/f6/5de1834f57708e76d8dc25502b7f1ecbcce162 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 (file)
index 0000000..bc44fa7
Binary files /dev/null and b/tests/resources/merge-recursive/.gitted/objects/f9/c04e4e9d4aaf1e6fe7478a7cc0756554974c2b differ
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 (file)
index 0000000..64612d4
--- /dev/null
@@ -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 (file)
index 0000000..bea6748
--- /dev/null
@@ -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 (file)
index 0000000..309b388
--- /dev/null
@@ -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 (file)
index 0000000..f958f13
--- /dev/null
@@ -0,0 +1 @@
+0ce202f64fa8356c1a32835fce4058ca76b0c7ed
diff --git a/tests/resources/merge-resolve/.gitted/objects/50/c5dc8cdfe40c688eb0a0e23be54dd57cae2e78 b/tests/resources/merge-resolve/.gitted/objects/50/c5dc8cdfe40c688eb0a0e23be54dd57cae2e78
new file mode 100644 (file)
index 0000000..c04baa1
--- /dev/null
@@ -0,0 +1,2 @@
+x\ 1\9d\8e]
+Â0\10\84}Î)ö\ 2J\92\ 3">x\ 4/°I6Zh\e\9eß*\9eÀ§a\86ï\83\89e\9a\86\ 6ZûC«Ì`\89\9cF\876;KL¶O)«.yìÑ8¢N¦\1c\13\87^¼¨òÜ \e\83¡Ç}óa'¤ôÑ°      öSØù\98Ù*\9b²\13´¶g©p£mHp_ëÌ\15ÎsÙh\1c\96ë/O¥>. Pi\85\ e\8e\12¥\14ñû±íÂ?¶¨\13,k\98JZG\16o»¾JT
\ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/7a/a825857f87aea74ddf13d954568aa30dfcdeb4 b/tests/resources/merge-resolve/.gitted/objects/7a/a825857f87aea74ddf13d954568aa30dfcdeb4
new file mode 100644 (file)
index 0000000..b9c0630
Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/7a/a825857f87aea74ddf13d954568aa30dfcdeb4 differ
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 (file)
index 0000000..1951316
--- /dev/null
@@ -0,0 +1 @@
+50c5dc8cdfe40c688eb0a0e23be54dd57cae2e78
diff --git a/tests/resources/rebase-submodule/.gitmodules b/tests/resources/rebase-submodule/.gitmodules
deleted file mode 100644 (file)
index f36de77..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "my-submodule"]
-       path = my-submodule
-       url = bogus
diff --git a/tests/resources/rebase-submodule/gitmodules b/tests/resources/rebase-submodule/gitmodules
new file mode 100644 (file)
index 0000000..f36de77
--- /dev/null
@@ -0,0 +1,3 @@
+[submodule "my-submodule"]
+       path = my-submodule
+       url = bogus
diff --git a/tests/resources/rebase-submodule/my-submodule b/tests/resources/rebase-submodule/my-submodule
deleted file mode 160000 (submodule)
index efad0b1..0000000
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit efad0b11c47cb2f0220cbd6f5b0f93bb99064b00
diff --git a/tests/resources/renames/.gitted/objects/2c/136d294960f7d939f1ed1903f1ced78b874c87 b/tests/resources/renames/.gitted/objects/2c/136d294960f7d939f1ed1903f1ced78b874c87
new file mode 100644 (file)
index 0000000..51b7cea
Binary files /dev/null and b/tests/resources/renames/.gitted/objects/2c/136d294960f7d939f1ed1903f1ced78b874c87 differ
diff --git a/tests/resources/renames/.gitted/objects/84/d8efa38af7ace2b302de0adbda16b1f1cd2e1b b/tests/resources/renames/.gitted/objects/84/d8efa38af7ace2b302de0adbda16b1f1cd2e1b
new file mode 100644 (file)
index 0000000..56f98fe
--- /dev/null
@@ -0,0 +1 @@
+x\ 1\95\8eQjÃ0\10\ 5ó­Sì\7f¡X+Û² \94\1e!4'Xi\9f\88\18eC¯ß\90\etþg\98r_×fÄ\1c\ fÖ\ 1ZRT\95\ 5%Ç*\8c!q.¡&°ø\984\16\9f\870\8f\9aÝ.\1d\9b\91\81µf?ñ ó\88)g\11,Z0£\8e\1cô\95©>8yÚåÞé$Ö[¹ÒÙж\8bt5:î\8fïýúølë\17ùÉ'Ï<óB\1fÃ\v\7f\86ÿ\9bî\a¿½\19H6%Å\r\ 6÷\auJI§
\ 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 (file)
index 0000000..d104e66
Binary files /dev/null and b/tests/resources/renames/.gitted/objects/89/7dda8ecb7fa2e092bc3f9e2a179d7c1b0364db differ
diff --git a/tests/resources/renames/.gitted/objects/95/ceb126bf79e76020d8879a8b0d50a73307a97f b/tests/resources/renames/.gitted/objects/95/ceb126bf79e76020d8879a8b0d50a73307a97f
new file mode 100644 (file)
index 0000000..0486ba5
Binary files /dev/null and b/tests/resources/renames/.gitted/objects/95/ceb126bf79e76020d8879a8b0d50a73307a97f differ
diff --git a/tests/resources/renames/.gitted/objects/be/053a189b0bbde545e0a3f59ce00b46ad29ce0d b/tests/resources/renames/.gitted/objects/be/053a189b0bbde545e0a3f59ce00b46ad29ce0d
new file mode 100644 (file)
index 0000000..de7aceb
Binary files /dev/null and b/tests/resources/renames/.gitted/objects/be/053a189b0bbde545e0a3f59ce00b46ad29ce0d differ
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 (file)
index 0000000..f27fc21
--- /dev/null
@@ -0,0 +1 @@
+be053a189b0bbde545e0a3f59ce00b46ad29ce0d
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 (file)
index 0000000..0c0ecad
--- /dev/null
@@ -0,0 +1 @@
+84d8efa38af7ace2b302de0adbda16b1f1cd2e1b
diff --git a/tests/resources/submodules.git/HEAD b/tests/resources/submodules.git/HEAD
new file mode 100644 (file)
index 0000000..cb089cd
--- /dev/null
@@ -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 (file)
index 0000000..07d359d
--- /dev/null
@@ -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 (file)
index 0000000..2c3c2cb
--- /dev/null
@@ -0,0 +1,2 @@
+x\ 1%\89=
+\800\fF]í)\8a\a0à"\ e\1eÃI\1c\8c\ 6\15*\95|Éý-t{?\9c2ÇilV8¿ùô$±«Øm¡ýv»ã\1ck\b­k*F\vDAÊ\ 4=(=|=6\90   ¬DAv=\ 4\ 4ÛÍA}\99&'\85\1fOò$=
\ 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 (file)
index 0000000..c85fb55
Binary files /dev/null and b/tests/resources/submodules.git/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357 differ
diff --git a/tests/resources/submodules.git/objects/97/896810b3210244a62a82458b8e0819ecfc6850 b/tests/resources/submodules.git/objects/97/896810b3210244a62a82458b8e0819ecfc6850
new file mode 100644 (file)
index 0000000..1c8dbdf
--- /dev/null
@@ -0,0 +1,3 @@
+x\ 1\9d\8e[
+Â0\10EýÎ*f\ 3ʤS\93  \88\88\15¤é\8d\16ú\904Ý¿\ 5wà×\85Ã9pÓ2MC¥FôP\v@\9dãÜ\au.\84\bpÚ!²OYá\83di\ 4YUÍ'\16Ì\958XïbPn¼ôÊ6
+ħÔ\9e\93¶1[qîÌ\16}0q«ï¥Ðc[W\8c#Ý1f\14ºÄR:àö\9bSZ¦+Y\91Æ+{µtdÏlvº\7f¬»þOm\9e¨u\98_´}è5Ôié·\11«ù\ 2`    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 (file)
index 0000000..3d78bd6
Binary files /dev/null and b/tests/resources/submodules.git/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888 differ
diff --git a/tests/resources/submodules.git/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818 b/tests/resources/submodules.git/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818
new file mode 100644 (file)
index 0000000..6e0b49e
Binary files /dev/null and b/tests/resources/submodules.git/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818 differ
diff --git a/tests/resources/submodules.git/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae b/tests/resources/submodules.git/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae
new file mode 100644 (file)
index 0000000..082a589
Binary files /dev/null and b/tests/resources/submodules.git/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae differ
diff --git a/tests/resources/submodules.git/objects/info/packs b/tests/resources/submodules.git/objects/info/packs
new file mode 100644 (file)
index 0000000..0785ef6
--- /dev/null
@@ -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 (file)
index 0000000..810fc31
Binary files /dev/null and b/tests/resources/submodules.git/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idx 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 (file)
index 0000000..c25c4a7
Binary files /dev/null and b/tests/resources/submodules.git/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack differ
diff --git a/tests/resources/submodules.git/refs/heads/master b/tests/resources/submodules.git/refs/heads/master
new file mode 100644 (file)
index 0000000..32b9358
--- /dev/null
@@ -0,0 +1 @@
+97896810b3210244a62a82458b8e0819ecfc6850
diff --git a/tests/resources/testrepo/.gitted/objects/57/43a3ef145d3638a0fa28233ca92897117ad74f b/tests/resources/testrepo/.gitted/objects/57/43a3ef145d3638a0fa28233ca92897117ad74f
new file mode 100644 (file)
index 0000000..85abb27
Binary files /dev/null and b/tests/resources/testrepo/.gitted/objects/57/43a3ef145d3638a0fa28233ca92897117ad74f differ
diff --git a/tests/resources/testrepo/.gitted/objects/f9/ed4af42472941da45a3ce44458455ed227a6be b/tests/resources/testrepo/.gitted/objects/f9/ed4af42472941da45a3ce44458455ed227a6be
new file mode 100644 (file)
index 0000000..69c52fc
--- /dev/null
@@ -0,0 +1,2 @@
+x\ 1¥N[
+B!\14ìÛUø\1f\84zôê\81\88~ÚA\e8W\8fÜ 3ÌK-?\8bvÐ|Í\ 3f&ÖR.]\1aë6½1Kç-\10pÖÖ%\98 \90Êd\82\ 1\88\84& ×ÚSò6\8b;5¾u©\10\14\81\90      Á9\fÎ\84h|\ eÓÄ`UÀh8gAk_j\93§ô¤\96äy©åQorÏÃý°#\7f\83\9fÚÅZ\ eR;\8d*¸1*·j@\fw\9cíüg\8dà\17ǵÓ|eñ\ 6\19§OÏ
\ No newline at end of file
diff --git a/tests/resources/testrepo/.gitted/refs/heads/executable b/tests/resources/testrepo/.gitted/refs/heads/executable
new file mode 100644 (file)
index 0000000..2bdccea
--- /dev/null
@@ -0,0 +1 @@
+f9ed4af42472941da45a3ce44458455ed227a6be
index 547050c6801014eb3e0df05c41221127fe39d1ea..6a23701f3b1199ba85cbc206b2dc90cd3602853e 100644 (file)
@@ -177,7 +177,7 @@ void test_revwalk_basic__glob_heads_with_invalid(void)
                /* walking */;
 
        /* git log --branches --oneline | wc -l => 16 */
-       cl_assert_equal_i(19, i);
+       cl_assert_equal_i(20, i);
 }
 
 void test_revwalk_basic__push_head(void)
index 23384fb1d452edb3e28d78c4c1bfe7639e5f77e8..dc58e8b45f41d6df483a4c1c2c411e7af0e40b04 100644 (file)
@@ -1155,3 +1155,30 @@ void test_status_ignore__subdir_ignore_everything_except_certain_files(void)
        refute_is_ignored("project/src/foo.c");
        refute_is_ignored("project/src/foo/foo.c");
 }
+
+void test_status_ignore__deeper(void)
+{
+   int ignored;
+
+    g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+    cl_git_mkfile("empty_standard_repo/.gitignore",
+          "*.data\n"
+          "!dont_ignore/*.data\n");
+
+    cl_git_pass(p_mkdir("empty_standard_repo/dont_ignore", 0777));
+    cl_git_mkfile("empty_standard_repo/foo.data", "");
+    cl_git_mkfile("empty_standard_repo/bar.data", "");
+    cl_git_mkfile("empty_standard_repo/dont_ignore/foo.data", "");
+    cl_git_mkfile("empty_standard_repo/dont_ignore/bar.data", "");
+
+    cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "foo.data"));
+    cl_assert_equal_i(1, ignored);
+    cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "bar.data"));
+    cl_assert_equal_i(1, ignored);
+
+    cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "dont_ignore/foo.data"));
+    cl_assert_equal_i(0, ignored);
+    cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "dont_ignore/bar.data"));
+    cl_assert_equal_i(0, ignored);
+}
index f482d693aaaaee21ee5bc2dadb9c1e8400b47ba4..ae32d2ee0e72b028691ad60a4b66b41832d98cef 100644 (file)
@@ -590,6 +590,12 @@ 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
@@ -610,7 +616,7 @@ void test_status_renames__precomposed_unicode_rename(void)
                { GIT_STATUS_WT_RENAMED, "sixserving.txt", nfc },
        };
 
-       rename_file(g_repo, "sixserving.txt", nfc);
+       rename_file(g_repo, "sixserving.txt", nfd);
 
        opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
 
index 1345dbfd26e02179a43a21598cf3a6fb2b87704b..c6b18c166b0499c47fbfd4629ee1dbb528a58d24 100644 (file)
@@ -1072,6 +1072,8 @@ void test_status_worktree__unreadable(void)
        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
 }
 
@@ -1106,6 +1108,8 @@ void test_status_worktree__unreadable_not_included(void)
        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
 }
 
@@ -1280,3 +1284,34 @@ void test_status_worktree__with_directory_in_pathlist(void)
        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);
+}
index e36fc44e0654834342792187804c16cfae763597..f84f07c60b47c0bc6be706137b4af495ed4a4158 100644 (file)
@@ -11,6 +11,11 @@ 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");
@@ -389,7 +394,8 @@ void test_submodule_lookup__renamed(void)
        cl_assert_equal_i(8, data.count);
 }
 
-void test_submodule_lookup_cached(void) {
+void test_submodule_lookup__cached(void)
+{
        git_submodule *sm;
        git_submodule *sm2;
        /* See that the simple tests still pass. */
@@ -413,3 +419,29 @@ void test_submodule_lookup_cached(void) {
        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"));
+}
+
+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__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));
+}
index 97aae6b20b460de598ef3594731b73fb3be4a2b3..88ba247ded5463bccc68e9b8b8ed02babb34250b 100644 (file)
@@ -45,6 +45,8 @@ void test_transport_register__custom_transport_ssh(void)
                "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;