summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVicent Marti <tanoku@gmail.com>2013-11-20 12:54:24 +0100
committerVicent Marti <tanoku@gmail.com>2013-11-20 12:54:24 +0100
commit4b0a36e881506a02b43a4ae3c19c93c919b36eeb (patch)
tree026182fa30273a4c1649928b6db3fc5335bd1ea4
parent29d7242b1dcd1f09a63417abd648a6217b85d301 (diff)
parent43cb8b32428b1b29994874349ec22eb5372e152c (diff)
downloadlibgit2-4b0a36e881506a02b43a4ae3c19c93c919b36eeb.tar.gz
Merge branch 'development'
-rw-r--r--.gitignore6
-rw-r--r--.mailmap3
-rw-r--r--.travis.yml21
-rw-r--r--AUTHORS1
-rw-r--r--CMakeLists.txt119
-rw-r--r--CONTRIBUTING.md19
-rw-r--r--COPYING63
-rw-r--r--Makefile.embed32
-rw-r--r--README.md87
-rw-r--r--docs/checkout-internals.md189
-rw-r--r--docs/diff-internals.md66
-rw-r--r--docs/error-handling.md283
-rw-r--r--examples/.gitignore6
-rw-r--r--examples/CMakeLists.txt16
-rw-r--r--examples/COPYING121
-rw-r--r--examples/Makefile4
-rw-r--r--examples/README.md23
-rw-r--r--examples/add.c159
-rw-r--r--examples/blame.c199
-rw-r--r--examples/cat-file.c171
-rw-r--r--examples/common.c191
-rw-r--r--examples/common.h87
-rw-r--r--examples/diff.c414
-rw-r--r--examples/general.c16
-rw-r--r--examples/init.c253
-rw-r--r--examples/log.c434
-rw-r--r--examples/network/Makefile3
-rw-r--r--examples/network/clone.c45
-rw-r--r--examples/network/common.c34
-rw-r--r--examples/network/common.h6
-rw-r--r--examples/network/fetch.c30
-rw-r--r--examples/network/index-pack.c12
-rw-r--r--examples/network/ls-remote.c76
-rw-r--r--examples/rev-list.c72
-rw-r--r--examples/rev-parse.c115
-rw-r--r--examples/showindex.c49
-rw-r--r--examples/status.c301
-rwxr-xr-xexamples/test/test-rev-list.sh2
-rw-r--r--git.git-authors1
-rw-r--r--include/git2.h79
-rw-r--r--include/git2/blame.h198
-rw-r--r--include/git2/blob.h32
-rw-r--r--include/git2/branch.h40
-rw-r--r--include/git2/buffer.h112
-rw-r--r--include/git2/checkout.h27
-rw-r--r--include/git2/clone.h62
-rw-r--r--include/git2/commit.h35
-rw-r--r--include/git2/common.h19
-rw-r--r--include/git2/config.h85
-rw-r--r--include/git2/cred_helpers.h2
-rw-r--r--include/git2/diff.h794
-rw-r--r--include/git2/errors.h19
-rw-r--r--include/git2/filter.h142
-rw-r--r--include/git2/index.h38
-rw-r--r--include/git2/indexer.h22
-rw-r--r--include/git2/merge.h76
-rw-r--r--include/git2/notes.h4
-rw-r--r--include/git2/object.h19
-rw-r--r--include/git2/odb.h74
-rw-r--r--include/git2/odb_backend.h56
-rw-r--r--include/git2/oid.h7
-rw-r--r--include/git2/pack.h48
-rw-r--r--include/git2/patch.h250
-rw-r--r--include/git2/pathspec.h260
-rw-r--r--include/git2/push.h31
-rw-r--r--include/git2/reflog.h36
-rw-r--r--include/git2/refs.h31
-rw-r--r--include/git2/remote.h175
-rw-r--r--include/git2/repository.h22
-rw-r--r--include/git2/revparse.h43
-rw-r--r--include/git2/revwalk.h8
-rw-r--r--include/git2/signature.h13
-rw-r--r--include/git2/stash.h2
-rw-r--r--include/git2/status.h32
-rw-r--r--include/git2/submodule.h110
-rw-r--r--include/git2/sys/config.h31
-rw-r--r--include/git2/sys/filter.h292
-rw-r--r--include/git2/sys/index.h2
-rw-r--r--include/git2/sys/odb_backend.h20
-rw-r--r--include/git2/sys/refdb_backend.h26
-rw-r--r--include/git2/sys/reflog.h21
-rw-r--r--include/git2/sys/refs.h4
-rw-r--r--include/git2/sys/repository.h20
-rw-r--r--include/git2/transport.h230
-rw-r--r--include/git2/tree.h13
-rw-r--r--include/git2/types.h94
-rw-r--r--include/git2/version.h4
-rw-r--r--libgit2.pc.in5
-rw-r--r--packaging/rpm/README6
-rw-r--r--packaging/rpm/libgit2.spec106
-rwxr-xr-xscript/cibuild.sh32
-rw-r--r--src/array.h22
-rw-r--r--src/attr.c9
-rw-r--r--src/attr_file.c20
-rw-r--r--src/attr_file.h6
-rw-r--r--src/bitvec.h75
-rw-r--r--src/blame.c476
-rw-r--r--src/blame.h93
-rw-r--r--src/blame_git.c622
-rw-r--r--src/blame_git.h20
-rw-r--r--src/blob.c175
-rw-r--r--src/blob.h9
-rw-r--r--src/branch.c84
-rw-r--r--src/buf_text.c29
-rw-r--r--src/buf_text.h8
-rw-r--r--src/buffer.c36
-rw-r--r--src/buffer.h41
-rw-r--r--src/cc-compat.h8
-rw-r--r--src/checkout.c869
-rw-r--r--src/checkout.h2
-rw-r--r--src/clone.c223
-rw-r--r--src/commit.c96
-rw-r--r--src/commit.h7
-rw-r--r--src/commit_list.c2
-rw-r--r--src/common.h24
-rw-r--r--src/config.c404
-rw-r--r--src/config.h5
-rw-r--r--src/config_cache.c1
-rw-r--r--src/config_file.c695
-rw-r--r--src/config_file.h4
-rw-r--r--src/crlf.c230
-rw-r--r--src/date.c14
-rw-r--r--src/diff.c388
-rw-r--r--src/diff.h50
-rw-r--r--src/diff_driver.c11
-rw-r--r--src/diff_file.c64
-rw-r--r--src/diff_file.h2
-rw-r--r--src/diff_patch.c335
-rw-r--r--src/diff_patch.h19
-rw-r--r--src/diff_print.c346
-rw-r--r--src/diff_tform.c478
-rw-r--r--src/diff_xdiff.c135
-rw-r--r--src/errors.c19
-rw-r--r--src/fetch.c67
-rw-r--r--src/fetch.h5
-rw-r--r--src/fetchhead.c10
-rw-r--r--src/filebuf.c33
-rw-r--r--src/filebuf.h6
-rw-r--r--src/fileops.c246
-rw-r--r--src/fileops.h62
-rw-r--r--src/filter.c666
-rw-r--r--src/filter.h71
-rw-r--r--src/global.c128
-rw-r--r--src/global.h4
-rw-r--r--src/hash.h2
-rw-r--r--src/hash/hash_generic.h1
-rw-r--r--src/hash/hash_openssl.h1
-rw-r--r--src/hash/hash_win32.c43
-rw-r--r--src/hashsig.c214
-rw-r--r--src/ident.c125
-rw-r--r--src/ignore.c43
-rw-r--r--src/ignore.h5
-rw-r--r--src/index.c219
-rw-r--r--src/index.h6
-rw-r--r--src/indexer.c461
-rw-r--r--src/iterator.c33
-rw-r--r--src/iterator.h10
-rw-r--r--src/merge.c570
-rw-r--r--src/merge.h12
-rw-r--r--src/merge_file.c2
-rw-r--r--src/netops.c173
-rw-r--r--src/netops.h24
-rw-r--r--src/object.c35
-rw-r--r--src/odb.c156
-rw-r--r--src/odb.h3
-rw-r--r--src/odb_loose.c74
-rw-r--r--src/odb_pack.c143
-rw-r--r--src/oid.c17
-rw-r--r--src/oid.h23
-rw-r--r--src/pack-objects.c143
-rw-r--r--src/pack-objects.h5
-rw-r--r--src/pack.c46
-rw-r--r--src/pack.h2
-rw-r--r--src/path.c339
-rw-r--r--src/path.h114
-rw-r--r--src/pathspec.c633
-rw-r--r--src/pathspec.h51
-rw-r--r--src/posix.h10
-rw-r--r--src/pqueue.h6
-rw-r--r--src/push.c90
-rw-r--r--src/push.h5
-rw-r--r--src/refdb.c18
-rw-r--r--src/refdb.h4
-rw-r--r--src/refdb_fs.c1244
-rw-r--r--src/reflog.c380
-rw-r--r--src/reflog.h7
-rw-r--r--src/refs.c99
-rw-r--r--src/refs.h5
-rw-r--r--src/refspec.c102
-rw-r--r--src/refspec.h8
-rw-r--r--src/remote.c461
-rw-r--r--src/remote.h7
-rw-r--r--src/repository.c365
-rw-r--r--src/repository.h3
-rw-r--r--src/reset.c13
-rw-r--r--src/revparse.c54
-rw-r--r--src/revwalk.c107
-rw-r--r--src/revwalk.h3
-rw-r--r--src/sha1_lookup.c71
-rw-r--r--src/sha1_lookup.h5
-rw-r--r--src/signature.c25
-rw-r--r--src/sortedcache.c380
-rw-r--r--src/sortedcache.h178
-rw-r--r--src/stash.c170
-rw-r--r--src/status.c65
-rw-r--r--src/status.h4
-rw-r--r--src/strmap.c32
-rw-r--r--src/strmap.h11
-rw-r--r--src/submodule.c701
-rw-r--r--src/submodule.h74
-rw-r--r--src/tag.c6
-rw-r--r--src/thread-utils.h94
-rw-r--r--src/transport.c70
-rw-r--r--src/transports/cred.c182
-rw-r--r--src/transports/git.c72
-rw-r--r--src/transports/http.c113
-rw-r--r--src/transports/local.c57
-rw-r--r--src/transports/smart.c53
-rw-r--r--src/transports/smart.h13
-rw-r--r--src/transports/smart_pkt.c33
-rw-r--r--src/transports/smart_protocol.c232
-rw-r--r--src/transports/ssh.c394
-rw-r--r--src/transports/winhttp.c303
-rw-r--r--src/tree-cache.c17
-rw-r--r--src/tree.c28
-rw-r--r--src/util.c24
-rw-r--r--src/util.h77
-rw-r--r--src/vector.c9
-rw-r--r--src/vector.h7
-rw-r--r--src/win32/dir.c38
-rw-r--r--src/win32/dir.h4
-rw-r--r--src/win32/error.c2
-rw-r--r--src/win32/findfile.c46
-rw-r--r--src/win32/findfile.h2
-rw-r--r--src/win32/mingw-compat.h5
-rw-r--r--src/win32/posix.h13
-rw-r--r--src/win32/posix_w32.c191
-rw-r--r--src/win32/precompiled.h3
-rw-r--r--src/win32/pthread.c116
-rw-r--r--src/win32/pthread.h33
-rw-r--r--src/win32/utf-conv.c8
-rw-r--r--src/win32/utf-conv.h31
-rw-r--r--src/win32/version.h25
-rw-r--r--tests-clar/attr/file.c226
-rw-r--r--tests-clar/attr/repo.c294
-rw-r--r--tests-clar/checkout/checkout_helpers.c188
-rw-r--r--tests-clar/checkout/checkout_helpers.h38
-rw-r--r--tests-clar/checkout/crlf.c147
-rw-r--r--tests-clar/checkout/head.c63
-rw-r--r--tests-clar/checkout/index.c612
-rw-r--r--tests-clar/checkout/tree.c679
-rw-r--r--tests-clar/clar.c459
-rw-r--r--tests-clar/clar.h88
-rw-r--r--tests-clar/clar/sandbox.h131
-rw-r--r--tests-clar/clar_libgit2.c345
-rw-r--r--tests-clar/clar_libgit2.h75
-rw-r--r--tests-clar/clone/empty.c83
-rw-r--r--tests-clar/clone/nonetwork.c260
-rw-r--r--tests-clar/commit/parse.c392
-rw-r--r--tests-clar/config/global.c67
-rw-r--r--tests-clar/config/multivar.c165
-rw-r--r--tests-clar/config/read.c451
-rw-r--r--tests-clar/config/stress.c92
-rw-r--r--tests-clar/config/validkeyname.c68
-rw-r--r--tests-clar/config/write.c244
-rw-r--r--tests-clar/core/buffer.c988
-rw-r--r--tests-clar/core/dirent.c235
-rw-r--r--tests-clar/core/filebuf.c92
-rw-r--r--tests-clar/core/mkdir.c182
-rw-r--r--tests-clar/core/path.c480
-rw-r--r--tests-clar/core/vector.c275
-rw-r--r--tests-clar/diff/blob.c1068
-rw-r--r--tests-clar/diff/diff_helpers.c220
-rw-r--r--tests-clar/diff/diff_helpers.h68
-rw-r--r--tests-clar/diff/diffiter.c465
-rw-r--r--tests-clar/diff/drivers.c125
-rw-r--r--tests-clar/diff/index.c167
-rw-r--r--tests-clar/diff/notify.c228
-rw-r--r--tests-clar/diff/patch.c559
-rw-r--r--tests-clar/diff/rename.c1124
-rw-r--r--tests-clar/diff/submodules.c169
-rw-r--r--tests-clar/diff/tree.c530
-rw-r--r--tests-clar/diff/workdir.c1267
-rw-r--r--tests-clar/index/addall.c274
-rw-r--r--tests-clar/index/filemodes.c153
-rw-r--r--tests-clar/index/names.c84
-rw-r--r--tests-clar/index/reuc.c374
-rw-r--r--tests-clar/index/tests.c461
-rw-r--r--tests-clar/merge/merge_helpers.c310
-rw-r--r--tests-clar/merge/merge_helpers.h59
-rw-r--r--tests-clar/merge/trees/automerge.c217
-rw-r--r--tests-clar/merge/workdir/setup.c966
-rw-r--r--tests-clar/network/fetchlocal.c78
-rw-r--r--tests-clar/network/remote/local.c160
-rw-r--r--tests-clar/network/remote/remotes.c457
-rw-r--r--tests-clar/network/urlparse.c82
-rw-r--r--tests-clar/object/blob/filter.c132
-rw-r--r--tests-clar/object/blob/fromchunks.c87
-rw-r--r--tests-clar/object/raw/write.c462
-rw-r--r--tests-clar/object/tree/attributes.c114
-rw-r--r--tests-clar/object/tree/walk.c103
-rw-r--r--tests-clar/odb/alternates.c80
-rw-r--r--tests-clar/odb/foreach.c80
-rw-r--r--tests-clar/odb/loose.c89
-rw-r--r--tests-clar/odb/mixed.c25
-rw-r--r--tests-clar/online/clone.c204
-rw-r--r--tests-clar/online/fetch.c163
-rw-r--r--tests-clar/online/fetchhead.c89
-rw-r--r--tests-clar/online/push.c711
-rw-r--r--tests-clar/online/push_util.c126
-rw-r--r--tests-clar/online/push_util.h69
-rw-r--r--tests-clar/pack/packbuilder.c148
-rw-r--r--tests-clar/refs/branches/delete.c117
-rw-r--r--tests-clar/refs/branches/foreach.c173
-rw-r--r--tests-clar/refs/branches/ishead.c116
-rw-r--r--tests-clar/refs/list.c57
-rw-r--r--tests-clar/refs/lookup.c48
-rw-r--r--tests-clar/refs/pack.c79
-rw-r--r--tests-clar/refs/read.c268
-rw-r--r--tests-clar/refs/reflog/drop.c124
-rw-r--r--tests-clar/refs/reflog/reflog.c186
-rw-r--r--tests-clar/refs/revparse.c741
-rw-r--r--tests-clar/refs/unicode.c44
-rw-r--r--tests-clar/repo/config.c200
-rw-r--r--tests-clar/repo/head.c196
-rw-r--r--tests-clar/repo/headtree.c53
-rw-r--r--tests-clar/repo/init.c532
-rw-r--r--tests-clar/repo/iterator.c927
-rw-r--r--tests-clar/repo/open.c317
-rw-r--r--tests-clar/repo/repo_helpers.c22
-rw-r--r--tests-clar/repo/repo_helpers.h6
-rw-r--r--tests-clar/reset/soft.c157
-rw-r--r--tests-clar/resources/config/config115
-rw-r--r--tests-clar/resources/duplicate.git/config5
-rw-r--r--tests-clar/resources/duplicate.git/objects/info/packs2
-rw-r--r--tests-clar/resources/submodules/gitmodules3
-rw-r--r--tests-clar/resources/testrepo.git/config36
-rw-r--r--tests-clar/revwalk/basic.c206
-rw-r--r--tests-clar/revwalk/mergebase.c380
-rw-r--r--tests-clar/stash/drop.c172
-rw-r--r--tests-clar/stash/save.c373
-rw-r--r--tests-clar/stash/stash_helpers.c68
-rw-r--r--tests-clar/stash/stash_helpers.h8
-rw-r--r--tests-clar/status/ignore.c461
-rw-r--r--tests-clar/status/renames.c385
-rw-r--r--tests-clar/status/status_data.h252
-rw-r--r--tests-clar/status/submodules.c221
-rw-r--r--tests-clar/status/worktree.c825
-rw-r--r--tests-clar/submodule/lookup.c114
-rw-r--r--tests-clar/submodule/modify.c266
-rw-r--r--tests-clar/submodule/status.c414
-rw-r--r--tests-clar/submodule/submodule_helpers.c84
-rw-r--r--tests-clar/submodule/submodule_helpers.h2
-rw-r--r--tests-clar/threads/basic.c23
-rw-r--r--tests-clar/valgrind-supp-mac.txt156
-rw-r--r--tests/README.md (renamed from tests-clar/README.md)0
-rw-r--r--tests/attr/attr_expect.h (renamed from tests-clar/attr/attr_expect.h)0
-rw-r--r--tests/attr/file.c224
-rw-r--r--tests/attr/flags.c (renamed from tests-clar/attr/flags.c)0
-rw-r--r--tests/attr/ignore.c (renamed from tests-clar/attr/ignore.c)0
-rw-r--r--tests/attr/lookup.c (renamed from tests-clar/attr/lookup.c)0
-rw-r--r--tests/attr/repo.c310
-rw-r--r--tests/blame/blame_helpers.c64
-rw-r--r--tests/blame/blame_helpers.h16
-rw-r--r--tests/blame/buffer.c166
-rw-r--r--tests/blame/getters.c56
-rw-r--r--tests/blame/harder.c71
-rw-r--r--tests/blame/simple.c305
-rw-r--r--tests/buf/basic.c (renamed from tests-clar/buf/basic.c)0
-rw-r--r--tests/buf/splice.c (renamed from tests-clar/buf/splice.c)0
-rw-r--r--tests/checkout/binaryunicode.c (renamed from tests-clar/checkout/binaryunicode.c)0
-rw-r--r--tests/checkout/checkout_helpers.c130
-rw-r--r--tests/checkout/checkout_helpers.h29
-rw-r--r--tests/checkout/conflict.c1127
-rw-r--r--tests/checkout/crlf.c231
-rw-r--r--tests/checkout/head.c62
-rw-r--r--tests/checkout/index.c620
-rw-r--r--tests/checkout/tree.c742
-rw-r--r--tests/checkout/typechange.c (renamed from tests-clar/checkout/typechange.c)0
-rw-r--r--tests/clar.c511
-rw-r--r--tests/clar.h95
-rw-r--r--tests/clar/fixtures.h (renamed from tests-clar/clar/fixtures.h)0
-rw-r--r--tests/clar/fs.h (renamed from tests-clar/clar/fs.h)0
-rw-r--r--tests/clar/print.h (renamed from tests-clar/clar/print.h)0
-rw-r--r--tests/clar/sandbox.h129
-rw-r--r--tests/clar_libgit2.c483
-rw-r--r--tests/clar_libgit2.h119
-rw-r--r--tests/clone/empty.c85
-rw-r--r--tests/clone/nonetwork.c179
-rw-r--r--tests/commit/commit.c (renamed from tests-clar/commit/commit.c)0
-rw-r--r--tests/commit/parent.c (renamed from tests-clar/commit/parent.c)0
-rw-r--r--tests/commit/parse.c413
-rw-r--r--tests/commit/signature.c (renamed from tests-clar/commit/signature.c)0
-rw-r--r--tests/commit/write.c (renamed from tests-clar/commit/write.c)0
-rw-r--r--tests/config/add.c (renamed from tests-clar/config/add.c)0
-rw-r--r--tests/config/backend.c (renamed from tests-clar/config/backend.c)0
-rw-r--r--tests/config/config_helpers.c (renamed from tests-clar/config/config_helpers.c)0
-rw-r--r--tests/config/config_helpers.h (renamed from tests-clar/config/config_helpers.h)0
-rw-r--r--tests/config/configlevel.c (renamed from tests-clar/config/configlevel.c)0
-rw-r--r--tests/config/global.c72
-rw-r--r--tests/config/include.c109
-rw-r--r--tests/config/multivar.c288
-rw-r--r--tests/config/new.c (renamed from tests-clar/config/new.c)0
-rw-r--r--tests/config/read.c569
-rw-r--r--tests/config/refresh.c (renamed from tests-clar/config/refresh.c)0
-rw-r--r--tests/config/stress.c92
-rw-r--r--tests/config/validkeyname.c68
-rw-r--r--tests/config/write.c305
-rw-r--r--tests/core/bitvec.c64
-rw-r--r--tests/core/buffer.c1039
-rw-r--r--tests/core/caps.c31
-rw-r--r--tests/core/copy.c (renamed from tests-clar/core/copy.c)0
-rw-r--r--tests/core/dirent.c236
-rw-r--r--tests/core/env.c (renamed from tests-clar/core/env.c)0
-rw-r--r--tests/core/errors.c (renamed from tests-clar/core/errors.c)0
-rw-r--r--tests/core/filebuf.c126
-rw-r--r--tests/core/hex.c (renamed from tests-clar/core/hex.c)0
-rw-r--r--tests/core/iconv.c68
-rw-r--r--tests/core/mkdir.c188
-rw-r--r--tests/core/oid.c (renamed from tests-clar/core/oid.c)0
-rw-r--r--tests/core/oidmap.c (renamed from tests-clar/core/oidmap.c)0
-rw-r--r--tests/core/opts.c (renamed from tests-clar/core/opts.c)0
-rw-r--r--tests/core/path.c583
-rw-r--r--tests/core/pool.c (renamed from tests-clar/core/pool.c)0
-rw-r--r--tests/core/posix.c99
-rw-r--r--tests/core/rmdir.c (renamed from tests-clar/core/rmdir.c)0
-rw-r--r--tests/core/sortedcache.c363
-rw-r--r--tests/core/stat.c (renamed from tests-clar/core/stat.c)0
-rw-r--r--tests/core/string.c (renamed from tests-clar/core/string.c)0
-rw-r--r--tests/core/strmap.c (renamed from tests-clar/core/strmap.c)0
-rw-r--r--tests/core/strtol.c (renamed from tests-clar/core/strtol.c)0
-rw-r--r--tests/core/vector.c275
-rw-r--r--tests/date/date.c (renamed from tests-clar/date/date.c)0
-rw-r--r--tests/diff/blob.c1067
-rw-r--r--tests/diff/diff_helpers.c246
-rw-r--r--tests/diff/diff_helpers.h64
-rw-r--r--tests/diff/diffiter.c453
-rw-r--r--tests/diff/drivers.c163
-rw-r--r--tests/diff/index.c167
-rw-r--r--tests/diff/iterator.c (renamed from tests-clar/diff/iterator.c)0
-rw-r--r--tests/diff/notify.c228
-rw-r--r--tests/diff/patch.c577
-rw-r--r--tests/diff/pathspec.c93
-rw-r--r--tests/diff/rename.c1286
-rw-r--r--tests/diff/submodules.c423
-rw-r--r--tests/diff/tree.c526
-rw-r--r--tests/diff/workdir.c1490
-rw-r--r--tests/fetchhead/fetchhead_data.h (renamed from tests-clar/fetchhead/fetchhead_data.h)0
-rw-r--r--tests/fetchhead/nonetwork.c (renamed from tests-clar/fetchhead/nonetwork.c)0
-rw-r--r--tests/filter/blob.c84
-rw-r--r--tests/filter/crlf.c71
-rw-r--r--tests/filter/crlf.h25
-rw-r--r--tests/filter/custom.c337
-rw-r--r--tests/filter/ident.c131
-rw-r--r--tests/generate.py (renamed from tests-clar/generate.py)0
-rw-r--r--tests/index/addall.c258
-rw-r--r--tests/index/conflicts.c (renamed from tests-clar/index/conflicts.c)0
-rw-r--r--tests/index/filemodes.c154
-rw-r--r--tests/index/inmemory.c (renamed from tests-clar/index/inmemory.c)0
-rw-r--r--tests/index/names.c148
-rw-r--r--tests/index/read_tree.c (renamed from tests-clar/index/read_tree.c)0
-rw-r--r--tests/index/rename.c (renamed from tests-clar/index/rename.c)0
-rw-r--r--tests/index/reuc.c372
-rw-r--r--tests/index/stage.c (renamed from tests-clar/index/stage.c)0
-rw-r--r--tests/index/tests.c545
-rw-r--r--tests/main.c (renamed from tests-clar/main.c)0
-rw-r--r--tests/merge/merge_helpers.c333
-rw-r--r--tests/merge/merge_helpers.h62
-rw-r--r--tests/merge/trees/automerge.c217
-rw-r--r--tests/merge/trees/modeconflict.c (renamed from tests-clar/merge/trees/modeconflict.c)0
-rw-r--r--tests/merge/trees/renames.c (renamed from tests-clar/merge/trees/renames.c)0
-rw-r--r--tests/merge/trees/treediff.c (renamed from tests-clar/merge/trees/treediff.c)0
-rw-r--r--tests/merge/trees/trivial.c (renamed from tests-clar/merge/trees/trivial.c)0
-rw-r--r--tests/merge/workdir/fastforward.c148
-rw-r--r--tests/merge/workdir/renames.c156
-rw-r--r--tests/merge/workdir/setup.c1057
-rw-r--r--tests/merge/workdir/simple.c491
-rw-r--r--tests/merge/workdir/trivial.c341
-rw-r--r--tests/network/cred.c (renamed from tests-clar/network/cred.c)0
-rw-r--r--tests/network/fetchlocal.c88
-rw-r--r--tests/network/refspecs.c (renamed from tests-clar/network/refspecs.c)0
-rw-r--r--tests/network/remote/createthenload.c (renamed from tests-clar/network/remote/createthenload.c)0
-rw-r--r--tests/network/remote/isvalidname.c (renamed from tests-clar/network/remote/isvalidname.c)0
-rw-r--r--tests/network/remote/local.c234
-rw-r--r--tests/network/remote/remotes.c510
-rw-r--r--tests/network/remote/rename.c (renamed from tests-clar/network/remote/rename.c)0
-rw-r--r--tests/network/urlparse.c193
-rw-r--r--tests/notes/notes.c (renamed from tests-clar/notes/notes.c)0
-rw-r--r--tests/notes/notesref.c (renamed from tests-clar/notes/notesref.c)0
-rw-r--r--tests/object/blob/filter.c143
-rw-r--r--tests/object/blob/fromchunks.c119
-rw-r--r--tests/object/blob/write.c (renamed from tests-clar/object/blob/write.c)0
-rw-r--r--tests/object/cache.c (renamed from tests-clar/object/cache.c)0
-rw-r--r--tests/object/commit/commitstagedfile.c (renamed from tests-clar/object/commit/commitstagedfile.c)0
-rw-r--r--tests/object/lookup.c (renamed from tests-clar/object/lookup.c)0
-rw-r--r--tests/object/lookupbypath.c83
-rw-r--r--tests/object/message.c (renamed from tests-clar/object/message.c)0
-rw-r--r--tests/object/peel.c (renamed from tests-clar/object/peel.c)0
-rw-r--r--tests/object/raw/chars.c (renamed from tests-clar/object/raw/chars.c)0
-rw-r--r--tests/object/raw/compare.c (renamed from tests-clar/object/raw/compare.c)0
-rw-r--r--tests/object/raw/convert.c (renamed from tests-clar/object/raw/convert.c)0
-rw-r--r--tests/object/raw/data.h (renamed from tests-clar/object/raw/data.h)0
-rw-r--r--tests/object/raw/fromstr.c (renamed from tests-clar/object/raw/fromstr.c)0
-rw-r--r--tests/object/raw/hash.c (renamed from tests-clar/object/raw/hash.c)0
-rw-r--r--tests/object/raw/short.c (renamed from tests-clar/object/raw/short.c)0
-rw-r--r--tests/object/raw/size.c (renamed from tests-clar/object/raw/size.c)0
-rw-r--r--tests/object/raw/type2string.c (renamed from tests-clar/object/raw/type2string.c)0
-rw-r--r--tests/object/raw/write.c462
-rw-r--r--tests/object/tag/list.c (renamed from tests-clar/object/tag/list.c)0
-rw-r--r--tests/object/tag/peel.c (renamed from tests-clar/object/tag/peel.c)0
-rw-r--r--tests/object/tag/read.c (renamed from tests-clar/object/tag/read.c)0
-rw-r--r--tests/object/tag/write.c (renamed from tests-clar/object/tag/write.c)0
-rw-r--r--tests/object/tree/attributes.c115
-rw-r--r--tests/object/tree/duplicateentries.c (renamed from tests-clar/object/tree/duplicateentries.c)0
-rw-r--r--tests/object/tree/frompath.c (renamed from tests-clar/object/tree/frompath.c)0
-rw-r--r--tests/object/tree/read.c (renamed from tests-clar/object/tree/read.c)0
-rw-r--r--tests/object/tree/walk.c177
-rw-r--r--tests/object/tree/write.c (renamed from tests-clar/object/tree/write.c)0
-rw-r--r--tests/odb/alternates.c80
-rw-r--r--tests/odb/backend/nonrefreshing.c274
-rw-r--r--tests/odb/foreach.c80
-rw-r--r--tests/odb/loose.c146
-rw-r--r--tests/odb/loose_data.h (renamed from tests-clar/odb/loose_data.h)0
-rw-r--r--tests/odb/mixed.c93
-rw-r--r--tests/odb/pack_data.h (renamed from tests-clar/odb/pack_data.h)0
-rw-r--r--tests/odb/pack_data_one.h (renamed from tests-clar/odb/pack_data_one.h)0
-rw-r--r--tests/odb/packed.c (renamed from tests-clar/odb/packed.c)0
-rw-r--r--tests/odb/packed_one.c (renamed from tests-clar/odb/packed_one.c)0
-rw-r--r--tests/odb/sorting.c (renamed from tests-clar/odb/sorting.c)0
-rw-r--r--tests/odb/streamwrite.c56
-rw-r--r--tests/online/clone.c291
-rw-r--r--tests/online/fetch.c171
-rw-r--r--tests/online/fetchhead.c91
-rw-r--r--tests/online/push.c784
-rw-r--r--tests/online/push_util.c132
-rw-r--r--tests/online/push_util.h71
-rw-r--r--tests/pack/indexer.c126
-rw-r--r--tests/pack/packbuilder.c204
-rw-r--r--tests/refs/branches/create.c (renamed from tests-clar/refs/branches/create.c)0
-rw-r--r--tests/refs/branches/delete.c117
-rw-r--r--tests/refs/branches/ishead.c116
-rw-r--r--tests/refs/branches/iterator.c151
-rw-r--r--tests/refs/branches/lookup.c (renamed from tests-clar/refs/branches/lookup.c)0
-rw-r--r--tests/refs/branches/move.c (renamed from tests-clar/refs/branches/move.c)0
-rw-r--r--tests/refs/branches/name.c (renamed from tests-clar/refs/branches/name.c)0
-rw-r--r--tests/refs/branches/remote.c (renamed from tests-clar/refs/branches/remote.c)0
-rw-r--r--tests/refs/branches/upstream.c (renamed from tests-clar/refs/branches/upstream.c)0
-rw-r--r--tests/refs/branches/upstreamname.c (renamed from tests-clar/refs/branches/upstreamname.c)0
-rw-r--r--tests/refs/crashes.c (renamed from tests-clar/refs/crashes.c)0
-rw-r--r--tests/refs/create.c (renamed from tests-clar/refs/create.c)0
-rw-r--r--tests/refs/delete.c (renamed from tests-clar/refs/delete.c)0
-rw-r--r--tests/refs/foreachglob.c (renamed from tests-clar/refs/foreachglob.c)0
-rw-r--r--tests/refs/isvalidname.c (renamed from tests-clar/refs/isvalidname.c)0
-rw-r--r--tests/refs/iterator.c (renamed from tests-clar/refs/iterator.c)0
-rw-r--r--tests/refs/list.c57
-rw-r--r--tests/refs/listall.c (renamed from tests-clar/refs/listall.c)0
-rw-r--r--tests/refs/lookup.c60
-rw-r--r--tests/refs/normalize.c (renamed from tests-clar/refs/normalize.c)0
-rw-r--r--tests/refs/overwrite.c (renamed from tests-clar/refs/overwrite.c)0
-rw-r--r--tests/refs/pack.c105
-rw-r--r--tests/refs/peel.c (renamed from tests-clar/refs/peel.c)0
-rw-r--r--tests/refs/read.c284
-rw-r--r--tests/refs/ref_helpers.c (renamed from tests-clar/refs/ref_helpers.c)0
-rw-r--r--tests/refs/ref_helpers.h (renamed from tests-clar/refs/ref_helpers.h)0
-rw-r--r--tests/refs/reflog/drop.c115
-rw-r--r--tests/refs/reflog/reflog.c209
-rw-r--r--tests/refs/rename.c (renamed from tests-clar/refs/rename.c)0
-rw-r--r--tests/refs/revparse.c813
-rw-r--r--tests/refs/setter.c (renamed from tests-clar/refs/setter.c)0
-rw-r--r--tests/refs/shorthand.c (renamed from tests-clar/refs/shorthand.c)0
-rw-r--r--tests/refs/unicode.c56
-rw-r--r--tests/refs/update.c (renamed from tests-clar/refs/update.c)0
-rw-r--r--tests/repo/config.c208
-rw-r--r--tests/repo/discover.c (renamed from tests-clar/repo/discover.c)0
-rw-r--r--tests/repo/getters.c (renamed from tests-clar/repo/getters.c)0
-rw-r--r--tests/repo/hashfile.c (renamed from tests-clar/repo/hashfile.c)0
-rw-r--r--tests/repo/head.c196
-rw-r--r--tests/repo/headtree.c53
-rw-r--r--tests/repo/init.c606
-rw-r--r--tests/repo/iterator.c962
-rw-r--r--tests/repo/message.c (renamed from tests-clar/repo/message.c)0
-rw-r--r--tests/repo/open.c376
-rw-r--r--tests/repo/pathspec.c385
-rw-r--r--tests/repo/repo_helpers.c22
-rw-r--r--tests/repo/repo_helpers.h6
-rw-r--r--tests/repo/setters.c (renamed from tests-clar/repo/setters.c)0
-rw-r--r--tests/repo/shallow.c (renamed from tests-clar/repo/shallow.c)0
-rw-r--r--tests/repo/state.c (renamed from tests-clar/repo/state.c)0
-rw-r--r--tests/reset/default.c (renamed from tests-clar/reset/default.c)0
-rw-r--r--tests/reset/hard.c (renamed from tests-clar/reset/hard.c)0
-rw-r--r--tests/reset/mixed.c (renamed from tests-clar/reset/mixed.c)0
-rw-r--r--tests/reset/reset_helpers.c (renamed from tests-clar/reset/reset_helpers.c)0
-rw-r--r--tests/reset/reset_helpers.h (renamed from tests-clar/reset/reset_helpers.h)0
-rw-r--r--tests/reset/soft.c157
-rw-r--r--tests/resources/.gitattributes (renamed from tests-clar/resources/.gitattributes)0
-rw-r--r--tests/resources/.gitignore (renamed from tests-clar/resources/.gitignore)0
-rw-r--r--tests/resources/attr/.gitted/HEAD (renamed from tests-clar/resources/attr/.gitted/HEAD)0
-rw-r--r--tests/resources/attr/.gitted/config (renamed from tests-clar/resources/attr/.gitted/config)0
-rw-r--r--tests/resources/attr/.gitted/description (renamed from tests-clar/resources/attr/.gitted/description)0
-rw-r--r--tests/resources/attr/.gitted/index (renamed from tests-clar/resources/attr/.gitted/index)bin1856 -> 1856 bytes
-rw-r--r--tests/resources/attr/.gitted/info/attributes (renamed from tests-clar/resources/attr/.gitted/info/attributes)0
-rw-r--r--tests/resources/attr/.gitted/info/exclude (renamed from tests-clar/resources/attr/.gitted/info/exclude)0
-rw-r--r--tests/resources/attr/.gitted/logs/HEAD (renamed from tests-clar/resources/attr/.gitted/logs/HEAD)0
-rw-r--r--tests/resources/attr/.gitted/logs/refs/heads/master (renamed from tests-clar/resources/attr/.gitted/logs/refs/heads/master)0
-rw-r--r--tests/resources/attr/.gitted/objects/10/8bb4e7fd7b16490dc33ff7d972151e73d7166e (renamed from tests-clar/resources/attr/.gitted/objects/10/8bb4e7fd7b16490dc33ff7d972151e73d7166e)bin130 -> 130 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/16/983da6643656bb44c43965ecb6855c6d574512 (renamed from tests-clar/resources/attr/.gitted/objects/16/983da6643656bb44c43965ecb6855c6d574512)bin446 -> 446 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/21/7878ab49e1314388ea2e32dc6fdb58a1b969e0 (renamed from tests-clar/resources/attr/.gitted/objects/21/7878ab49e1314388ea2e32dc6fdb58a1b969e0)0
-rw-r--r--tests/resources/attr/.gitted/objects/24/fa9a9fc4e202313e24b648087495441dab432b (renamed from tests-clar/resources/attr/.gitted/objects/24/fa9a9fc4e202313e24b648087495441dab432b)bin180 -> 180 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/29/29de282ce999e95183aedac6451d3384559c4b (renamed from tests-clar/resources/attr/.gitted/objects/29/29de282ce999e95183aedac6451d3384559c4b)bin58 -> 58 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/2b/40c5aca159b04ea8d20ffe36cdf8b09369b14a (renamed from tests-clar/resources/attr/.gitted/objects/2b/40c5aca159b04ea8d20ffe36cdf8b09369b14a)0
-rw-r--r--tests/resources/attr/.gitted/objects/2c/66e14f77196ea763fb1e41612c1aa2bc2d8ed2 (renamed from tests-clar/resources/attr/.gitted/objects/2c/66e14f77196ea763fb1e41612c1aa2bc2d8ed2)bin316 -> 316 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/2d/e7dfe3588f3c7e9ad59e7d50ba90e3329df9d9 (renamed from tests-clar/resources/attr/.gitted/objects/2d/e7dfe3588f3c7e9ad59e7d50ba90e3329df9d9)bin124 -> 124 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/37/0fe9ec224ce33e71f9e5ec2bd1142ce9937a6a (renamed from tests-clar/resources/attr/.gitted/objects/37/0fe9ec224ce33e71f9e5ec2bd1142ce9937a6a)bin177 -> 177 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/3a/6df026462ebafe455af9867d27eda20a9e0974 (renamed from tests-clar/resources/attr/.gitted/objects/3a/6df026462ebafe455af9867d27eda20a9e0974)bin84 -> 84 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/3b/74db7ab381105dc0d28f8295a77f6a82989292 (renamed from tests-clar/resources/attr/.gitted/objects/3b/74db7ab381105dc0d28f8295a77f6a82989292)bin276 -> 276 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/3e/42ffc54a663f9401cc25843d6c0e71a33e4249 (renamed from tests-clar/resources/attr/.gitted/objects/3e/42ffc54a663f9401cc25843d6c0e71a33e4249)bin596 -> 596 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/45/141a79a77842c59a63229403220a4e4be74e3d (renamed from tests-clar/resources/attr/.gitted/objects/45/141a79a77842c59a63229403220a4e4be74e3d)bin36 -> 36 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/45/5a314fa848d52ae1f11d254da4f60858fc97f4 (renamed from tests-clar/resources/attr/.gitted/objects/45/5a314fa848d52ae1f11d254da4f60858fc97f4)bin446 -> 446 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 (renamed from tests-clar/resources/attr/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057)bin18 -> 18 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/4d/713dc48e6b1bd75b0d61ad078ba9ca3a56745d (renamed from tests-clar/resources/attr/.gitted/objects/4d/713dc48e6b1bd75b0d61ad078ba9ca3a56745d)0
-rw-r--r--tests/resources/attr/.gitted/objects/4e/49ba8c5b6c32ff28cd9dcb60be34df50fcc485 (renamed from tests-clar/resources/attr/.gitted/objects/4e/49ba8c5b6c32ff28cd9dcb60be34df50fcc485)bin81 -> 81 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/55/6f8c827b8e4a02ad5cab77dca2bcb3e226b0b3 (renamed from tests-clar/resources/attr/.gitted/objects/55/6f8c827b8e4a02ad5cab77dca2bcb3e226b0b3)bin24 -> 24 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/58/19a185d77b03325aaf87cafc771db36f6ddca7 (renamed from tests-clar/resources/attr/.gitted/objects/58/19a185d77b03325aaf87cafc771db36f6ddca7)bin19 -> 19 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/60/5812ab7fe421fdd325a935d35cb06a9234a7d7 (renamed from tests-clar/resources/attr/.gitted/objects/60/5812ab7fe421fdd325a935d35cb06a9234a7d7)0
-rw-r--r--tests/resources/attr/.gitted/objects/6b/ab5c79cd5140d0f800917f550eb2a3dc32b0da (renamed from tests-clar/resources/attr/.gitted/objects/6b/ab5c79cd5140d0f800917f550eb2a3dc32b0da)0
-rw-r--r--tests/resources/attr/.gitted/objects/6d/968d62c89c7d9ea23a4c9a7b665d017c3d8ffd (renamed from tests-clar/resources/attr/.gitted/objects/6d/968d62c89c7d9ea23a4c9a7b665d017c3d8ffd)bin422 -> 422 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/71/7fc31f6b84f9d6fc3a4edbca259d7fc92beee2 (renamed from tests-clar/resources/attr/.gitted/objects/71/7fc31f6b84f9d6fc3a4edbca259d7fc92beee2)bin422 -> 422 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/8d/0b9df9bd30be7910ddda60548d485bc302b911 (renamed from tests-clar/resources/attr/.gitted/objects/8d/0b9df9bd30be7910ddda60548d485bc302b911)0
-rw-r--r--tests/resources/attr/.gitted/objects/93/61f40bb97239cf55811892e14de2e344168ba1 (renamed from tests-clar/resources/attr/.gitted/objects/93/61f40bb97239cf55811892e14de2e344168ba1)bin45 -> 45 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/94/da4faa0a6bfb8ee6ccf7153801a69202b31857 (renamed from tests-clar/resources/attr/.gitted/objects/94/da4faa0a6bfb8ee6ccf7153801a69202b31857)bin124 -> 124 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/96/089fd31ce1d3ee2afb0ba09ba063066932f027 (renamed from tests-clar/resources/attr/.gitted/objects/96/089fd31ce1d3ee2afb0ba09ba063066932f027)bin422 -> 422 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9 (renamed from tests-clar/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9)bin95 -> 95 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/9e/5bdc47d6a80f2be0ea3049ad74231b94609242 (renamed from tests-clar/resources/attr/.gitted/objects/9e/5bdc47d6a80f2be0ea3049ad74231b94609242)bin20 -> 20 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9 (renamed from tests-clar/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9)bin151 -> 151 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/a0/f7217ae99f5ac3e88534f5cea267febc5fa85b (renamed from tests-clar/resources/attr/.gitted/objects/a0/f7217ae99f5ac3e88534f5cea267febc5fa85b)0
-rw-r--r--tests/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320 (renamed from tests-clar/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320)bin351 -> 351 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/a5/d76cad53f66f1312bd995909a5bab3c0820770 (renamed from tests-clar/resources/attr/.gitted/objects/a5/d76cad53f66f1312bd995909a5bab3c0820770)0
-rw-r--r--tests/resources/attr/.gitted/objects/a9/7cc019851d401a4f1d091cb91a15890a0dd1ba (renamed from tests-clar/resources/attr/.gitted/objects/a9/7cc019851d401a4f1d091cb91a15890a0dd1ba)0
-rw-r--r--tests/resources/attr/.gitted/objects/b4/35cd5689a0fb54afbeda4ac20368aa480e8f04 (renamed from tests-clar/resources/attr/.gitted/objects/b4/35cd5689a0fb54afbeda4ac20368aa480e8f04)bin40 -> 40 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/c0/091889c0c77142b87a1fa5123a6398a61d33e7 (renamed from tests-clar/resources/attr/.gitted/objects/c0/091889c0c77142b87a1fa5123a6398a61d33e7)bin290 -> 290 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/c4/85abe35abd4aa6fd83b076a78bbea9e2e7e06c (renamed from tests-clar/resources/attr/.gitted/objects/c4/85abe35abd4aa6fd83b076a78bbea9e2e7e06c)bin129 -> 129 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/c7/aadd770d5907a8475c29e9ee21a27b88bf675d (renamed from tests-clar/resources/attr/.gitted/objects/c7/aadd770d5907a8475c29e9ee21a27b88bf675d)bin60 -> 60 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/c9/6bbb2c2557a8325ae1559e3ba79cdcecb23076 (renamed from tests-clar/resources/attr/.gitted/objects/c9/6bbb2c2557a8325ae1559e3ba79cdcecb23076)0
-rw-r--r--tests/resources/attr/.gitted/objects/ce/39a97a7fb1fa90bcf5e711249c1e507476ae0e (renamed from tests-clar/resources/attr/.gitted/objects/ce/39a97a7fb1fa90bcf5e711249c1e507476ae0e)bin446 -> 446 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/d5/7da33c16b14326ecb05d19bbea908f5e4c47d9 (renamed from tests-clar/resources/attr/.gitted/objects/d5/7da33c16b14326ecb05d19bbea908f5e4c47d9)bin379 -> 379 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2 (renamed from tests-clar/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2)bin18 -> 18 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/dc/cada462d3df8ac6de596fb8c896aba9344f941 (renamed from tests-clar/resources/attr/.gitted/objects/dc/cada462d3df8ac6de596fb8c896aba9344f941)bin35 -> 35 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/de/863bff4976c9ed7e17a4da0fd524908dc84049 (renamed from tests-clar/resources/attr/.gitted/objects/de/863bff4976c9ed7e17a4da0fd524908dc84049)bin4115 -> 4115 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/e5/63cf4758f0d646f1b14b76016aa17fa9e549a4 (renamed from tests-clar/resources/attr/.gitted/objects/e5/63cf4758f0d646f1b14b76016aa17fa9e549a4)bin39 -> 39 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/ec/b97df2a174987475ac816e3847fc8e9f6c596b (renamed from tests-clar/resources/attr/.gitted/objects/ec/b97df2a174987475ac816e3847fc8e9f6c596b)bin171 -> 171 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/ed/f3dcee4003d71f139777898882ccd097e34c53 (renamed from tests-clar/resources/attr/.gitted/objects/ed/f3dcee4003d71f139777898882ccd097e34c53)bin6289 -> 6289 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/f2/c6d717cf4a5a3e6b02684155ab07b766982165 (renamed from tests-clar/resources/attr/.gitted/objects/f2/c6d717cf4a5a3e6b02684155ab07b766982165)bin44 -> 44 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/f5/b0af1fb4f5c0cd7aad880711d368a07333c307 (renamed from tests-clar/resources/attr/.gitted/objects/f5/b0af1fb4f5c0cd7aad880711d368a07333c307)0
-rw-r--r--tests/resources/attr/.gitted/objects/fb/5067b1aef3ac1ada4b379dbcb7d17255df7d78 (renamed from tests-clar/resources/attr/.gitted/objects/fb/5067b1aef3ac1ada4b379dbcb7d17255df7d78)bin28 -> 28 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/fe/773770c5a6cc7185580c9204b1ff18a33ff3fc (renamed from tests-clar/resources/attr/.gitted/objects/fe/773770c5a6cc7185580c9204b1ff18a33ff3fc)0
-rw-r--r--tests/resources/attr/.gitted/objects/ff/69f8639ce2e6010b3f33a74160aad98b48da2b (renamed from tests-clar/resources/attr/.gitted/objects/ff/69f8639ce2e6010b3f33a74160aad98b48da2b)bin18 -> 18 bytes
-rw-r--r--tests/resources/attr/.gitted/refs/heads/master (renamed from tests-clar/resources/attr/.gitted/refs/heads/master)0
-rw-r--r--tests/resources/attr/attr0 (renamed from tests-clar/resources/attr/attr0)0
-rw-r--r--tests/resources/attr/attr1 (renamed from tests-clar/resources/attr/attr1)0
-rw-r--r--tests/resources/attr/attr2 (renamed from tests-clar/resources/attr/attr2)0
-rw-r--r--tests/resources/attr/attr3 (renamed from tests-clar/resources/attr/attr3)0
-rw-r--r--tests/resources/attr/binfile (renamed from tests-clar/resources/attr/binfile)0
-rw-r--r--tests/resources/attr/dir/file (renamed from tests-clar/resources/attr/dir/file)0
-rw-r--r--tests/resources/attr/file (renamed from tests-clar/resources/attr/file)0
-rw-r--r--tests/resources/attr/gitattributes (renamed from tests-clar/resources/attr/gitattributes)0
-rw-r--r--tests/resources/attr/gitignore (renamed from tests-clar/resources/attr/gitignore)0
-rw-r--r--tests/resources/attr/ign (renamed from tests-clar/resources/attr/ign)0
-rw-r--r--tests/resources/attr/macro_bad (renamed from tests-clar/resources/attr/macro_bad)0
-rw-r--r--tests/resources/attr/macro_test (renamed from tests-clar/resources/attr/macro_test)0
-rw-r--r--tests/resources/attr/root_test1 (renamed from tests-clar/resources/attr/root_test1)0
-rw-r--r--tests/resources/attr/root_test2 (renamed from tests-clar/resources/attr/root_test2)0
-rw-r--r--tests/resources/attr/root_test3 (renamed from tests-clar/resources/attr/root_test3)0
-rw-r--r--tests/resources/attr/root_test4.txt (renamed from tests-clar/resources/attr/root_test4.txt)0
-rw-r--r--tests/resources/attr/sub/.gitattributes (renamed from tests-clar/resources/attr/sub/.gitattributes)0
-rw-r--r--tests/resources/attr/sub/abc (renamed from tests-clar/resources/attr/sub/abc)0
-rw-r--r--tests/resources/attr/sub/dir/file (renamed from tests-clar/resources/attr/sub/dir/file)0
-rw-r--r--tests/resources/attr/sub/file (renamed from tests-clar/resources/attr/sub/file)0
-rw-r--r--tests/resources/attr/sub/ign/file (renamed from tests-clar/resources/attr/sub/ign/file)0
-rw-r--r--tests/resources/attr/sub/ign/sub/file (renamed from tests-clar/resources/attr/sub/ign/sub/file)0
-rw-r--r--tests/resources/attr/sub/sub/.gitattributes (renamed from tests-clar/resources/attr/sub/sub/.gitattributes)0
-rw-r--r--tests/resources/attr/sub/sub/dir (renamed from tests-clar/resources/attr/sub/sub/dir)0
-rw-r--r--tests/resources/attr/sub/sub/file (renamed from tests-clar/resources/attr/sub/sub/file)0
-rw-r--r--tests/resources/attr/sub/sub/subsub.txt (renamed from tests-clar/resources/attr/sub/sub/subsub.txt)0
-rw-r--r--tests/resources/attr/sub/subdir_test1 (renamed from tests-clar/resources/attr/sub/subdir_test1)0
-rw-r--r--tests/resources/attr/sub/subdir_test2.txt (renamed from tests-clar/resources/attr/sub/subdir_test2.txt)0
-rw-r--r--tests/resources/attr_index/.gitted/HEAD (renamed from tests-clar/resources/attr_index/.gitted/HEAD)0
-rw-r--r--tests/resources/attr_index/.gitted/config (renamed from tests-clar/resources/attr_index/.gitted/config)0
-rw-r--r--tests/resources/attr_index/.gitted/description (renamed from tests-clar/resources/attr_index/.gitted/description)0
-rw-r--r--tests/resources/attr_index/.gitted/index (renamed from tests-clar/resources/attr_index/.gitted/index)bin520 -> 520 bytes
-rw-r--r--tests/resources/attr_index/.gitted/info/exclude (renamed from tests-clar/resources/attr_index/.gitted/info/exclude)0
-rw-r--r--tests/resources/attr_index/.gitted/info/refs (renamed from tests-clar/resources/attr_index/.gitted/info/refs)0
-rw-r--r--tests/resources/attr_index/.gitted/logs/HEAD (renamed from tests-clar/resources/attr_index/.gitted/logs/HEAD)0
-rw-r--r--tests/resources/attr_index/.gitted/logs/refs/heads/master (renamed from tests-clar/resources/attr_index/.gitted/logs/refs/heads/master)0
-rw-r--r--tests/resources/attr_index/.gitted/objects/38/12cfef36615db1788d4e63f90028007e17a348 (renamed from tests-clar/resources/attr_index/.gitted/objects/38/12cfef36615db1788d4e63f90028007e17a348)0
-rw-r--r--tests/resources/attr_index/.gitted/objects/59/d942b8be2784bc96db9b22202c10815c9a077b (renamed from tests-clar/resources/attr_index/.gitted/objects/59/d942b8be2784bc96db9b22202c10815c9a077b)0
-rw-r--r--tests/resources/attr_index/.gitted/objects/cd/f17ea3fe625ef812f4dce7f423f4f299287505 (renamed from tests-clar/resources/attr_index/.gitted/objects/cd/f17ea3fe625ef812f4dce7f423f4f299287505)bin61 -> 61 bytes
-rw-r--r--tests/resources/attr_index/.gitted/objects/f7/2502ddd01412bb20796ff812af56fd53b82b52 (renamed from tests-clar/resources/attr_index/.gitted/objects/f7/2502ddd01412bb20796ff812af56fd53b82b52)bin149 -> 149 bytes
-rw-r--r--tests/resources/attr_index/.gitted/objects/info/packs (renamed from tests-clar/resources/attr_index/.gitted/objects/info/packs)0
-rw-r--r--tests/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.idx (renamed from tests-clar/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.idx)bin1492 -> 1492 bytes
-rw-r--r--tests/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.pack (renamed from tests-clar/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.pack)bin1106 -> 1106 bytes
-rw-r--r--tests/resources/attr_index/.gitted/packed-refs (renamed from tests-clar/resources/attr_index/.gitted/packed-refs)0
-rw-r--r--tests/resources/attr_index/.gitted/refs/heads/master (renamed from tests-clar/resources/attr_index/.gitted/refs/heads/master)0
-rw-r--r--tests/resources/attr_index/README.md (renamed from tests-clar/resources/attr_index/README.md)0
-rw-r--r--tests/resources/attr_index/README.txt (renamed from tests-clar/resources/attr_index/README.txt)0
-rw-r--r--tests/resources/attr_index/gitattributes (renamed from tests-clar/resources/attr_index/gitattributes)0
-rw-r--r--tests/resources/attr_index/sub/sub/.gitattributes (renamed from tests-clar/resources/attr_index/sub/sub/.gitattributes)0
-rw-r--r--tests/resources/attr_index/sub/sub/README.md (renamed from tests-clar/resources/attr_index/sub/sub/README.md)0
-rw-r--r--tests/resources/attr_index/sub/sub/README.txt (renamed from tests-clar/resources/attr_index/sub/sub/README.txt)0
-rw-r--r--tests/resources/bad.indexbin0 -> 412 bytes
-rw-r--r--tests/resources/bad_tag.git/HEAD (renamed from tests-clar/resources/bad_tag.git/HEAD)0
-rw-r--r--tests/resources/bad_tag.git/config (renamed from tests-clar/resources/bad_tag.git/config)0
-rw-r--r--tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.idx (renamed from tests-clar/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.idx)bin1268 -> 1268 bytes
-rw-r--r--tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.pack (renamed from tests-clar/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.pack)bin596 -> 596 bytes
-rw-r--r--tests/resources/bad_tag.git/packed-refs (renamed from tests-clar/resources/bad_tag.git/packed-refs)0
-rw-r--r--tests/resources/bad_tag.git/refs/dummy-marker.txt (renamed from tests-clar/resources/bad_tag.git/refs/dummy-marker.txt)0
-rw-r--r--tests/resources/big.index (renamed from tests-clar/resources/big.index)bin335272 -> 335272 bytes
-rw-r--r--tests/resources/binaryunicode/.gitted/HEAD (renamed from tests-clar/resources/binaryunicode/.gitted/HEAD)0
-rw-r--r--tests/resources/binaryunicode/.gitted/config (renamed from tests-clar/resources/binaryunicode/.gitted/config)0
-rw-r--r--tests/resources/binaryunicode/.gitted/description (renamed from tests-clar/resources/binaryunicode/.gitted/description)0
-rw-r--r--tests/resources/binaryunicode/.gitted/index (renamed from tests-clar/resources/binaryunicode/.gitted/index)bin104 -> 104 bytes
-rw-r--r--tests/resources/binaryunicode/.gitted/info/exclude (renamed from tests-clar/resources/binaryunicode/.gitted/info/exclude)0
-rw-r--r--tests/resources/binaryunicode/.gitted/info/refs (renamed from tests-clar/resources/binaryunicode/.gitted/info/refs)0
-rw-r--r--tests/resources/binaryunicode/.gitted/objects/info/packs (renamed from tests-clar/resources/binaryunicode/.gitted/objects/info/packs)0
-rw-r--r--tests/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.idx (renamed from tests-clar/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.idx)bin1380 -> 1380 bytes
-rw-r--r--tests/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.pack (renamed from tests-clar/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.pack)bin20879 -> 20879 bytes
-rw-r--r--tests/resources/binaryunicode/.gitted/refs/heads/branch1 (renamed from tests-clar/resources/binaryunicode/.gitted/refs/heads/branch1)0
-rw-r--r--tests/resources/binaryunicode/.gitted/refs/heads/branch2 (renamed from tests-clar/resources/binaryunicode/.gitted/refs/heads/branch2)0
-rw-r--r--tests/resources/binaryunicode/.gitted/refs/heads/master (renamed from tests-clar/resources/binaryunicode/.gitted/refs/heads/master)0
-rw-r--r--tests/resources/binaryunicode/file.txt (renamed from tests-clar/resources/binaryunicode/file.txt)0
-rw-r--r--tests/resources/blametest.git/HEAD (renamed from tests-clar/resources/crlf/.gitted/HEAD)0
-rw-r--r--tests/resources/blametest.git/config (renamed from tests-clar/resources/twowaymerge.git/config)0
-rw-r--r--tests/resources/blametest.git/description (renamed from tests-clar/resources/deprecated-mode.git/description)0
-rw-r--r--tests/resources/blametest.git/objects/0c/bab4d45fd61e55a1c9697f9f9cb07a12e15448bin0 -> 46 bytes
-rw-r--r--tests/resources/blametest.git/objects/1a/ac69ae5d96461afc4d81d0066cb12f5b05a35bbin0 -> 28 bytes
-rw-r--r--tests/resources/blametest.git/objects/1b/5f0775af166331c854bd8d1bca3450eaf2532abin0 -> 35 bytes
-rw-r--r--tests/resources/blametest.git/objects/48/2f2c370e35c2c314fc1f96db2beb33f955a26abin0 -> 35 bytes
-rw-r--r--tests/resources/blametest.git/objects/63/d671eb32d250e4a83766ebbc60e818c1e1e93a3
-rw-r--r--tests/resources/blametest.git/objects/63/eb57322e363e18d460da5ea8284f3cd2340b36bin0 -> 76 bytes
-rw-r--r--tests/resources/blametest.git/objects/8b/137891791fe96927ad78e64b0aad7bded08bdcbin0 -> 16 bytes
-rw-r--r--tests/resources/blametest.git/objects/96/679d59cf9f74d69b3c920f258559b5e8c9a18abin0 -> 47 bytes
-rw-r--r--tests/resources/blametest.git/objects/98/89d6e5557761aa8e3607e80c874a6dc51ada7cbin0 -> 43 bytes
-rw-r--r--tests/resources/blametest.git/objects/aa/06ecca6c4ad6432ab9313e556ca92ba4bcf9e91
-rw-r--r--tests/resources/blametest.git/objects/b1/76dfc3a4dc8734e4c579f77236a9c8d0a965d2bin0 -> 76 bytes
-rw-r--r--tests/resources/blametest.git/objects/b9/0bb887b7c03750ae6b352ffe76ab9d2e86ee7dbin0 -> 56 bytes
-rw-r--r--tests/resources/blametest.git/objects/b9/9f7ac0b88909253d829554c14af488c3b0f3a52
-rw-r--r--tests/resources/blametest.git/objects/bc/7c5ac2bafe828a68e9d1d460343718d6fbe1363
-rw-r--r--tests/resources/blametest.git/objects/cf/e0e1e1e3ba18f149fd47f5e1aef6016b2260c3bin0 -> 76 bytes
-rw-r--r--tests/resources/blametest.git/objects/d0/67729932057cdb7527a833d6799c4ddc5206401
-rw-r--r--tests/resources/blametest.git/objects/da/237394e6132d20d30f175b9b73c8638fddddda4
-rw-r--r--tests/resources/blametest.git/objects/e5/b41c1ea533f87388ab69b13baf0b5a562d6243bin0 -> 76 bytes
-rw-r--r--tests/resources/blametest.git/objects/ef/32df4d259143933715c74951f932d9892364d1bin0 -> 42 bytes
-rw-r--r--tests/resources/blametest.git/refs/heads/master1
-rw-r--r--tests/resources/config/.gitconfig (renamed from tests-clar/resources/config/.gitconfig)0
-rw-r--r--tests/resources/config/config-include2
-rw-r--r--tests/resources/config/config-included2
-rw-r--r--tests/resources/config/config0 (renamed from tests-clar/resources/config/config0)0
-rw-r--r--tests/resources/config/config1 (renamed from tests-clar/resources/config/config1)0
-rw-r--r--tests/resources/config/config10 (renamed from tests-clar/resources/config/config10)0
-rw-r--r--tests/resources/config/config115
-rw-r--r--tests/resources/config/config12 (renamed from tests-clar/resources/config/config12)0
-rw-r--r--tests/resources/config/config13 (renamed from tests-clar/resources/config/config13)0
-rw-r--r--tests/resources/config/config14 (renamed from tests-clar/resources/config/config14)0
-rw-r--r--tests/resources/config/config15 (renamed from tests-clar/resources/config/config15)0
-rw-r--r--tests/resources/config/config16 (renamed from tests-clar/resources/config/config16)0
-rw-r--r--tests/resources/config/config17 (renamed from tests-clar/resources/config/config17)0
-rw-r--r--tests/resources/config/config18 (renamed from tests-clar/resources/config/config18)0
-rw-r--r--tests/resources/config/config19 (renamed from tests-clar/resources/config/config19)0
-rw-r--r--tests/resources/config/config2 (renamed from tests-clar/resources/config/config2)0
-rw-r--r--tests/resources/config/config2011
-rw-r--r--tests/resources/config/config3 (renamed from tests-clar/resources/config/config3)0
-rw-r--r--tests/resources/config/config4 (renamed from tests-clar/resources/config/config4)0
-rw-r--r--tests/resources/config/config5 (renamed from tests-clar/resources/config/config5)0
-rw-r--r--tests/resources/config/config6 (renamed from tests-clar/resources/config/config6)0
-rw-r--r--tests/resources/config/config7 (renamed from tests-clar/resources/config/config7)0
-rw-r--r--tests/resources/config/config8 (renamed from tests-clar/resources/config/config8)0
-rw-r--r--tests/resources/config/config9 (renamed from tests-clar/resources/config/config9)0
-rw-r--r--tests/resources/crlf/.gitted/HEAD (renamed from tests-clar/resources/deprecated-mode.git/HEAD)0
-rw-r--r--tests/resources/crlf/.gitted/config (renamed from tests-clar/resources/crlf/.gitted/config)0
-rw-r--r--tests/resources/crlf/.gitted/objects/04/de00b358f13389948756732158eaaaefa1448c (renamed from tests-clar/resources/crlf/.gitted/objects/04/de00b358f13389948756732158eaaaefa1448c)bin28 -> 28 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/0a/a76e474d259bd7c13eb726a1396c381db55c88 (renamed from tests-clar/resources/crlf/.gitted/objects/0a/a76e474d259bd7c13eb726a1396c381db55c88)bin27 -> 27 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/0d/06894e14df22e066763ae906e0ed3eb79c205f (renamed from tests-clar/resources/crlf/.gitted/objects/0d/06894e14df22e066763ae906e0ed3eb79c205f)bin134 -> 134 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/0f/f5a53f19bfd2b5eea1ba550295c47515678987 (renamed from tests-clar/resources/crlf/.gitted/objects/0f/f5a53f19bfd2b5eea1ba550295c47515678987)bin29 -> 29 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/12/faf3c1ea55f572473cec9052fca468c3584ccb (renamed from tests-clar/resources/crlf/.gitted/objects/12/faf3c1ea55f572473cec9052fca468c3584ccb)0
-rw-r--r--tests/resources/crlf/.gitted/objects/38/1cfe630df902bc29271a202d3277981180e4a6 (renamed from tests-clar/resources/crlf/.gitted/objects/38/1cfe630df902bc29271a202d3277981180e4a6)bin25 -> 25 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/79/9770d1cff46753a57db7a066159b5610da6e3a (renamed from tests-clar/resources/crlf/.gitted/objects/79/9770d1cff46753a57db7a066159b5610da6e3a)bin20 -> 20 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/7c/ce67e58173e2b01f7db124ceaabe3183d19c49 (renamed from tests-clar/resources/crlf/.gitted/objects/7c/ce67e58173e2b01f7db124ceaabe3183d19c49)bin24 -> 24 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/a9/a2e8913c1dbe2812fac5e6b4e0a4bd5d0d5966 (renamed from tests-clar/resources/crlf/.gitted/objects/a9/a2e8913c1dbe2812fac5e6b4e0a4bd5d0d5966)0
-rw-r--r--tests/resources/crlf/.gitted/objects/ba/aa042ab2976f8264e467988e6112ee518ec62e (renamed from tests-clar/resources/crlf/.gitted/objects/ba/aa042ab2976f8264e467988e6112ee518ec62e)bin159 -> 159 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/dc/88e3b917de821e25962bea7ec1f55c4ce2112c (renamed from tests-clar/resources/crlf/.gitted/objects/dc/88e3b917de821e25962bea7ec1f55c4ce2112c)bin32 -> 32 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/ea/030d3c6cec212069eca698cabaa5b4550f1511 (renamed from tests-clar/resources/crlf/.gitted/objects/ea/030d3c6cec212069eca698cabaa5b4550f1511)bin32 -> 32 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/fe/085d9ace90cc675b87df15e1aeed0c3a31407f (renamed from tests-clar/resources/crlf/.gitted/objects/fe/085d9ace90cc675b87df15e1aeed0c3a31407f)bin139 -> 139 bytes
-rw-r--r--tests/resources/crlf/.gitted/refs/heads/master (renamed from tests-clar/resources/crlf/.gitted/refs/heads/master)0
-rw-r--r--tests/resources/crlf/.gitted/refs/heads/utf8 (renamed from tests-clar/resources/crlf/.gitted/refs/heads/utf8)0
-rw-r--r--tests/resources/deprecated-mode.git/HEAD (renamed from tests-clar/resources/diff/.gitted/HEAD)0
-rw-r--r--tests/resources/deprecated-mode.git/config (renamed from tests-clar/resources/deprecated-mode.git/config)0
-rw-r--r--tests/resources/deprecated-mode.git/description (renamed from tests-clar/resources/diff/.gitted/description)0
-rw-r--r--tests/resources/deprecated-mode.git/index (renamed from tests-clar/resources/deprecated-mode.git/index)bin112 -> 112 bytes
-rw-r--r--tests/resources/deprecated-mode.git/info/exclude (renamed from tests-clar/resources/deprecated-mode.git/info/exclude)0
-rw-r--r--tests/resources/deprecated-mode.git/objects/06/262edc257418e9987caf999f9a7a3e1547adff (renamed from tests-clar/resources/deprecated-mode.git/objects/06/262edc257418e9987caf999f9a7a3e1547adff)bin124 -> 124 bytes
-rw-r--r--tests/resources/deprecated-mode.git/objects/08/10fb7818088ff5ac41ee49199b51473b1bd6c7 (renamed from tests-clar/resources/deprecated-mode.git/objects/08/10fb7818088ff5ac41ee49199b51473b1bd6c7)bin350 -> 350 bytes
-rw-r--r--tests/resources/deprecated-mode.git/objects/1b/05fdaa881ee45b48cbaa5e9b037d667a47745e (renamed from tests-clar/resources/deprecated-mode.git/objects/1b/05fdaa881ee45b48cbaa5e9b037d667a47745e)bin57 -> 57 bytes
-rw-r--r--tests/resources/deprecated-mode.git/objects/3d/0970ec547fc41ef8a5882dde99c6adce65b021 (renamed from tests-clar/resources/deprecated-mode.git/objects/3d/0970ec547fc41ef8a5882dde99c6adce65b021)bin29 -> 29 bytes
-rw-r--r--tests/resources/deprecated-mode.git/refs/heads/master (renamed from tests-clar/resources/deprecated-mode.git/refs/heads/master)0
-rw-r--r--tests/resources/diff/.gitted/HEAD (renamed from tests-clar/resources/duplicate.git/HEAD)0
-rw-r--r--tests/resources/diff/.gitted/config (renamed from tests-clar/resources/diff/.gitted/config)0
-rw-r--r--tests/resources/diff/.gitted/description (renamed from tests-clar/resources/duplicate.git/description)0
-rw-r--r--tests/resources/diff/.gitted/index (renamed from tests-clar/resources/diff/.gitted/index)bin225 -> 225 bytes
-rw-r--r--tests/resources/diff/.gitted/info/exclude (renamed from tests-clar/resources/diff/.gitted/info/exclude)0
-rw-r--r--tests/resources/diff/.gitted/logs/HEAD (renamed from tests-clar/resources/diff/.gitted/logs/HEAD)0
-rw-r--r--tests/resources/diff/.gitted/logs/refs/heads/master (renamed from tests-clar/resources/diff/.gitted/logs/refs/heads/master)0
-rw-r--r--tests/resources/diff/.gitted/objects/29/ab7053bb4dde0298e03e2c179e890b7dd465a7 (renamed from tests-clar/resources/diff/.gitted/objects/29/ab7053bb4dde0298e03e2c179e890b7dd465a7)bin730 -> 730 bytes
-rw-r--r--tests/resources/diff/.gitted/objects/3e/5bcbad2a68e5bc60a53b8388eea53a1a7ab847 (renamed from tests-clar/resources/diff/.gitted/objects/3e/5bcbad2a68e5bc60a53b8388eea53a1a7ab847)bin1108 -> 1108 bytes
-rw-r--r--tests/resources/diff/.gitted/objects/54/6c735f16a3b44d9784075c2c0dab2ac9bf1989 (renamed from tests-clar/resources/diff/.gitted/objects/54/6c735f16a3b44d9784075c2c0dab2ac9bf1989)bin1110 -> 1110 bytes
-rw-r--r--tests/resources/diff/.gitted/objects/7a/9e0b02e63179929fed24f0a3e0f19168114d10 (renamed from tests-clar/resources/diff/.gitted/objects/7a/9e0b02e63179929fed24f0a3e0f19168114d10)bin160 -> 160 bytes
-rw-r--r--tests/resources/diff/.gitted/objects/7b/808f723a8ca90df319682c221187235af76693 (renamed from tests-clar/resources/diff/.gitted/objects/7b/808f723a8ca90df319682c221187235af76693)bin922 -> 922 bytes
-rw-r--r--tests/resources/diff/.gitted/objects/88/789109439c1e1c3cd45224001edee5304ed53c (renamed from tests-clar/resources/diff/.gitted/objects/88/789109439c1e1c3cd45224001edee5304ed53c)0
-rw-r--r--tests/resources/diff/.gitted/objects/cb/8294e696339863df760b2ff5d1e275bee72455 (renamed from tests-clar/resources/diff/.gitted/objects/cb/8294e696339863df760b2ff5d1e275bee72455)bin86 -> 86 bytes
-rw-r--r--tests/resources/diff/.gitted/objects/d7/0d245ed97ed2aa596dd1af6536e4bfdb047b69 (renamed from tests-clar/resources/diff/.gitted/objects/d7/0d245ed97ed2aa596dd1af6536e4bfdb047b69)0
-rw-r--r--tests/resources/diff/.gitted/refs/heads/master (renamed from tests-clar/resources/diff/.gitted/refs/heads/master)0
-rw-r--r--tests/resources/diff/another.txt (renamed from tests-clar/resources/diff/another.txt)0
-rw-r--r--tests/resources/diff/readme.txt (renamed from tests-clar/resources/diff/readme.txt)0
-rw-r--r--tests/resources/duplicate.git/COMMIT_EDITMSG (renamed from tests-clar/resources/duplicate.git/COMMIT_EDITMSG)0
-rw-r--r--tests/resources/duplicate.git/HEAD (renamed from tests-clar/resources/empty_bare.git/HEAD)0
-rw-r--r--tests/resources/duplicate.git/config (renamed from tests-clar/resources/short_tag.git/config)0
-rw-r--r--tests/resources/duplicate.git/description (renamed from tests-clar/resources/empty_bare.git/description)0
-rw-r--r--tests/resources/duplicate.git/index (renamed from tests-clar/resources/duplicate.git/index)bin104 -> 104 bytes
-rw-r--r--tests/resources/duplicate.git/info/exclude (renamed from tests-clar/resources/duplicate.git/info/exclude)0
-rw-r--r--tests/resources/duplicate.git/info/refs (renamed from tests-clar/resources/duplicate.git/info/refs)0
-rw-r--r--tests/resources/duplicate.git/logs/HEAD (renamed from tests-clar/resources/duplicate.git/logs/HEAD)0
-rw-r--r--tests/resources/duplicate.git/logs/refs/heads/master (renamed from tests-clar/resources/duplicate.git/logs/refs/heads/master)0
-rw-r--r--tests/resources/duplicate.git/objects/0d/deadede9e6d6ccddce0ee1e5749eed0485e5eabin0 -> 22 bytes
-rw-r--r--tests/resources/duplicate.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a (renamed from tests-clar/resources/duplicate.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a)bin21 -> 21 bytes
-rw-r--r--tests/resources/duplicate.git/objects/info/packs3
-rw-r--r--tests/resources/duplicate.git/objects/pack/pack-29a4896f0a0b9c9947b0927c57a5c03dcae052e3.idxbin0 -> 1184 bytes
-rw-r--r--tests/resources/duplicate.git/objects/pack/pack-29a4896f0a0b9c9947b0927c57a5c03dcae052e3.packbin0 -> 249 bytes
-rw-r--r--tests/resources/duplicate.git/objects/pack/pack-b18eeacbd65cbd30a365d7564b45a468e8bd43d6.idxbin0 -> 1268 bytes
-rw-r--r--tests/resources/duplicate.git/objects/pack/pack-b18eeacbd65cbd30a365d7564b45a468e8bd43d6.packbin0 -> 369 bytes
-rw-r--r--tests/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.idx (renamed from tests-clar/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.idx)bin1156 -> 1156 bytes
-rw-r--r--tests/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.pack (renamed from tests-clar/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.pack)bin213 -> 213 bytes
-rw-r--r--tests/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.idxbin0 -> 1100 bytes
-rw-r--r--tests/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.packbin0 -> 47 bytes
-rw-r--r--tests/resources/duplicate.git/packed-refs (renamed from tests-clar/resources/duplicate.git/packed-refs)0
-rw-r--r--tests/resources/duplicate.git/refs/heads/dummy-marker.txt1
-rw-r--r--tests/resources/empty_bare.git/HEAD (renamed from tests-clar/resources/empty_standard_repo/.gitted/HEAD)0
-rw-r--r--tests/resources/empty_bare.git/config (renamed from tests-clar/resources/empty_bare.git/config)0
-rw-r--r--tests/resources/empty_bare.git/description (renamed from tests-clar/resources/empty_standard_repo/.gitted/description)0
-rw-r--r--tests/resources/empty_bare.git/info/exclude (renamed from tests-clar/resources/empty_bare.git/info/exclude)0
-rw-r--r--tests/resources/empty_bare.git/objects/info/dummy-marker.txt (renamed from tests-clar/resources/empty_bare.git/objects/info/dummy-marker.txt)0
-rw-r--r--tests/resources/empty_bare.git/objects/pack/dummy-marker.txt (renamed from tests-clar/resources/empty_bare.git/objects/pack/dummy-marker.txt)0
-rw-r--r--tests/resources/empty_bare.git/refs/heads/dummy-marker.txt (renamed from tests-clar/resources/empty_bare.git/refs/heads/dummy-marker.txt)0
-rw-r--r--tests/resources/empty_bare.git/refs/tags/dummy-marker.txt (renamed from tests-clar/resources/empty_bare.git/refs/tags/dummy-marker.txt)0
-rw-r--r--tests/resources/empty_standard_repo/.gitted/HEAD (renamed from tests-clar/resources/filemodes/.gitted/HEAD)0
-rw-r--r--tests/resources/empty_standard_repo/.gitted/config (renamed from tests-clar/resources/empty_standard_repo/.gitted/config)0
-rw-r--r--tests/resources/empty_standard_repo/.gitted/description (renamed from tests-clar/resources/filemodes/.gitted/description)0
-rw-r--r--tests/resources/empty_standard_repo/.gitted/info/exclude (renamed from tests-clar/resources/empty_standard_repo/.gitted/info/exclude)0
-rw-r--r--tests/resources/empty_standard_repo/.gitted/objects/info/dummy-marker.txt (renamed from tests-clar/resources/empty_standard_repo/.gitted/objects/info/dummy-marker.txt)0
-rw-r--r--tests/resources/empty_standard_repo/.gitted/objects/pack/dummy-marker.txt (renamed from tests-clar/resources/empty_standard_repo/.gitted/objects/pack/dummy-marker.txt)0
-rw-r--r--tests/resources/empty_standard_repo/.gitted/refs/heads/dummy-marker.txt (renamed from tests-clar/resources/empty_standard_repo/.gitted/refs/heads/dummy-marker.txt)0
-rw-r--r--tests/resources/empty_standard_repo/.gitted/refs/tags/dummy-marker.txt (renamed from tests-clar/resources/empty_standard_repo/.gitted/refs/tags/dummy-marker.txt)0
-rw-r--r--tests/resources/filemodes/.gitted/HEAD (renamed from tests-clar/resources/icase/.gitted/HEAD)0
-rw-r--r--tests/resources/filemodes/.gitted/config (renamed from tests-clar/resources/filemodes/.gitted/config)0
-rw-r--r--tests/resources/filemodes/.gitted/description (renamed from tests-clar/resources/icase/.gitted/description)0
-rw-r--r--tests/resources/filemodes/.gitted/index (renamed from tests-clar/resources/filemodes/.gitted/index)bin528 -> 528 bytes
-rw-r--r--tests/resources/filemodes/.gitted/info/exclude (renamed from tests-clar/resources/filemodes/.gitted/info/exclude)0
-rw-r--r--tests/resources/filemodes/.gitted/logs/HEAD (renamed from tests-clar/resources/filemodes/.gitted/logs/HEAD)0
-rw-r--r--tests/resources/filemodes/.gitted/logs/refs/heads/master (renamed from tests-clar/resources/filemodes/.gitted/logs/refs/heads/master)0
-rw-r--r--tests/resources/filemodes/.gitted/objects/99/62c8453ba6f0cf8dac7c5dcc2fa2897fa9964a (renamed from tests-clar/resources/filemodes/.gitted/objects/99/62c8453ba6f0cf8dac7c5dcc2fa2897fa9964a)bin139 -> 139 bytes
-rw-r--r--tests/resources/filemodes/.gitted/objects/a5/c5dd0fc6c313159a69b1d19d7f61a9f978e8f1 (renamed from tests-clar/resources/filemodes/.gitted/objects/a5/c5dd0fc6c313159a69b1d19d7f61a9f978e8f1)bin21 -> 21 bytes
-rw-r--r--tests/resources/filemodes/.gitted/objects/e7/48d196331bcb20267eaaee4ff3326cb73b8182 (renamed from tests-clar/resources/filemodes/.gitted/objects/e7/48d196331bcb20267eaaee4ff3326cb73b8182)bin99 -> 99 bytes
-rw-r--r--tests/resources/filemodes/.gitted/refs/heads/master (renamed from tests-clar/resources/filemodes/.gitted/refs/heads/master)0
-rw-r--r--tests/resources/filemodes/exec_off (renamed from tests-clar/resources/filemodes/exec_off)0
-rwxr-xr-xtests/resources/filemodes/exec_off2on_staged (renamed from tests-clar/resources/filemodes/exec_off2on_staged)0
-rwxr-xr-xtests/resources/filemodes/exec_off2on_workdir (renamed from tests-clar/resources/filemodes/exec_off2on_workdir)0
-rw-r--r--tests/resources/filemodes/exec_off_untracked (renamed from tests-clar/resources/filemodes/exec_off_untracked)0
-rwxr-xr-xtests/resources/filemodes/exec_on (renamed from tests-clar/resources/filemodes/exec_on)0
-rw-r--r--tests/resources/filemodes/exec_on2off_staged (renamed from tests-clar/resources/filemodes/exec_on2off_staged)0
-rw-r--r--tests/resources/filemodes/exec_on2off_workdir (renamed from tests-clar/resources/filemodes/exec_on2off_workdir)0
-rwxr-xr-xtests/resources/filemodes/exec_on_untracked (renamed from tests-clar/resources/filemodes/exec_on_untracked)0
-rw-r--r--tests/resources/gitgit.index (renamed from tests-clar/resources/gitgit.index)bin134799 -> 134799 bytes
-rw-r--r--tests/resources/icase/.gitted/HEAD (renamed from tests-clar/resources/issue_1397/.gitted/HEAD)0
-rw-r--r--tests/resources/icase/.gitted/config (renamed from tests-clar/resources/icase/.gitted/config)0
-rw-r--r--tests/resources/icase/.gitted/description (renamed from tests-clar/resources/issue_592b/.gitted/description)0
-rw-r--r--tests/resources/icase/.gitted/index (renamed from tests-clar/resources/icase/.gitted/index)bin1392 -> 1392 bytes
-rw-r--r--tests/resources/icase/.gitted/info/exclude (renamed from tests-clar/resources/icase/.gitted/info/exclude)0
-rw-r--r--tests/resources/icase/.gitted/logs/HEAD (renamed from tests-clar/resources/icase/.gitted/logs/HEAD)0
-rw-r--r--tests/resources/icase/.gitted/logs/refs/heads/master (renamed from tests-clar/resources/icase/.gitted/logs/refs/heads/master)0
-rw-r--r--tests/resources/icase/.gitted/objects/3e/257c57f136a1cb8f2b8e9a2e5bc8ec0258bdce (renamed from tests-clar/resources/icase/.gitted/objects/3e/257c57f136a1cb8f2b8e9a2e5bc8ec0258bdce)bin114 -> 114 bytes
-rw-r--r--tests/resources/icase/.gitted/objects/4d/d6027d083575c7431396dc2a3174afeb393c93 (renamed from tests-clar/resources/icase/.gitted/objects/4d/d6027d083575c7431396dc2a3174afeb393c93)bin61 -> 61 bytes
-rw-r--r--tests/resources/icase/.gitted/objects/62/e0af52c199ec731fe4ad230041cd3286192d49 (renamed from tests-clar/resources/icase/.gitted/objects/62/e0af52c199ec731fe4ad230041cd3286192d49)bin19 -> 19 bytes
-rw-r--r--tests/resources/icase/.gitted/objects/76/d6e1d231b1085fcce151427e9899335de74be6 (renamed from tests-clar/resources/icase/.gitted/objects/76/d6e1d231b1085fcce151427e9899335de74be6)0
-rw-r--r--tests/resources/icase/.gitted/objects/d4/4e18fb93b7107b5cd1b95d601591d77869a1b6 (renamed from tests-clar/resources/icase/.gitted/objects/d4/4e18fb93b7107b5cd1b95d601591d77869a1b6)bin21 -> 21 bytes
-rw-r--r--tests/resources/icase/.gitted/refs/heads/master (renamed from tests-clar/resources/icase/.gitted/refs/heads/master)0
-rw-r--r--tests/resources/icase/B (renamed from tests-clar/resources/icase/B)0
-rw-r--r--tests/resources/icase/D (renamed from tests-clar/resources/icase/D)0
-rw-r--r--tests/resources/icase/F (renamed from tests-clar/resources/icase/F)0
-rw-r--r--tests/resources/icase/H (renamed from tests-clar/resources/icase/H)0
-rw-r--r--tests/resources/icase/J (renamed from tests-clar/resources/icase/J)0
-rw-r--r--tests/resources/icase/L/1 (renamed from tests-clar/resources/icase/L/1)0
-rw-r--r--tests/resources/icase/L/B (renamed from tests-clar/resources/icase/L/B)0
-rw-r--r--tests/resources/icase/L/D (renamed from tests-clar/resources/icase/L/D)0
-rw-r--r--tests/resources/icase/L/a (renamed from tests-clar/resources/icase/L/a)0
-rw-r--r--tests/resources/icase/L/c (renamed from tests-clar/resources/icase/L/c)0
-rw-r--r--tests/resources/icase/a (renamed from tests-clar/resources/icase/a)0
-rw-r--r--tests/resources/icase/c (renamed from tests-clar/resources/icase/c)0
-rw-r--r--tests/resources/icase/e (renamed from tests-clar/resources/icase/e)0
-rw-r--r--tests/resources/icase/g (renamed from tests-clar/resources/icase/g)0
-rw-r--r--tests/resources/icase/i (renamed from tests-clar/resources/icase/i)0
-rw-r--r--tests/resources/icase/k/1 (renamed from tests-clar/resources/icase/k/1)0
-rw-r--r--tests/resources/icase/k/B (renamed from tests-clar/resources/icase/k/B)0
-rw-r--r--tests/resources/icase/k/D (renamed from tests-clar/resources/icase/k/D)0
-rw-r--r--tests/resources/icase/k/a (renamed from tests-clar/resources/icase/k/a)0
-rw-r--r--tests/resources/icase/k/c (renamed from tests-clar/resources/icase/k/c)0
-rw-r--r--tests/resources/issue_1397/.gitted/HEAD (renamed from tests-clar/resources/issue_592/.gitted/HEAD)0
-rw-r--r--tests/resources/issue_1397/.gitted/config (renamed from tests-clar/resources/issue_1397/.gitted/config)0
-rw-r--r--tests/resources/issue_1397/.gitted/index (renamed from tests-clar/resources/issue_1397/.gitted/index)bin233 -> 233 bytes
-rw-r--r--tests/resources/issue_1397/.gitted/objects/7f/483a738f867e5b21c8f377d70311f011eb48b5 (renamed from tests-clar/resources/issue_1397/.gitted/objects/7f/483a738f867e5b21c8f377d70311f011eb48b5)0
-rw-r--r--tests/resources/issue_1397/.gitted/objects/83/12e0889a9cbab77c732b6bc39b51a683e3a318 (renamed from tests-clar/resources/issue_1397/.gitted/objects/83/12e0889a9cbab77c732b6bc39b51a683e3a318)bin48 -> 48 bytes
-rw-r--r--tests/resources/issue_1397/.gitted/objects/8a/7ef047fc933edb62e84e7977b0612ec3f6f283 (renamed from tests-clar/resources/issue_1397/.gitted/objects/8a/7ef047fc933edb62e84e7977b0612ec3f6f283)bin141 -> 141 bytes
-rw-r--r--tests/resources/issue_1397/.gitted/objects/8e/8f80088a9274fd23584992f587083ca1bcbbac (renamed from tests-clar/resources/issue_1397/.gitted/objects/8e/8f80088a9274fd23584992f587083ca1bcbbac)bin63 -> 63 bytes
-rw-r--r--tests/resources/issue_1397/.gitted/objects/f2/c62dea0372a0578e053697d5c1ba1ac05e774a (renamed from tests-clar/resources/issue_1397/.gitted/objects/f2/c62dea0372a0578e053697d5c1ba1ac05e774a)bin94 -> 94 bytes
-rw-r--r--tests/resources/issue_1397/.gitted/objects/ff/3578d64d199d5b48d92bbb569e0a273e411741 (renamed from tests-clar/resources/issue_1397/.gitted/objects/ff/3578d64d199d5b48d92bbb569e0a273e411741)bin73 -> 73 bytes
-rw-r--r--tests/resources/issue_1397/.gitted/refs/heads/master (renamed from tests-clar/resources/issue_1397/.gitted/refs/heads/master)0
-rw-r--r--tests/resources/issue_1397/crlf_file.txt (renamed from tests-clar/resources/issue_1397/crlf_file.txt)0
-rw-r--r--tests/resources/issue_1397/some_other_crlf_file.txt (renamed from tests-clar/resources/issue_1397/some_other_crlf_file.txt)0
-rw-r--r--tests/resources/issue_592/.gitted/COMMIT_EDITMSG (renamed from tests-clar/resources/issue_592/.gitted/COMMIT_EDITMSG)0
-rw-r--r--tests/resources/issue_592/.gitted/HEAD (renamed from tests-clar/resources/issue_592b/.gitted/HEAD)0
-rw-r--r--tests/resources/issue_592/.gitted/config (renamed from tests-clar/resources/issue_592/.gitted/config)0
-rw-r--r--tests/resources/issue_592/.gitted/index (renamed from tests-clar/resources/issue_592/.gitted/index)bin392 -> 392 bytes
-rw-r--r--tests/resources/issue_592/.gitted/info/exclude (renamed from tests-clar/resources/issue_592/.gitted/info/exclude)0
-rw-r--r--tests/resources/issue_592/.gitted/logs/HEAD (renamed from tests-clar/resources/issue_592/.gitted/logs/HEAD)0
-rw-r--r--tests/resources/issue_592/.gitted/logs/refs/heads/master (renamed from tests-clar/resources/issue_592/.gitted/logs/refs/heads/master)0
-rw-r--r--tests/resources/issue_592/.gitted/objects/06/07ee9d4ccce8e4c4fa13c2c7d727e7faba4e0e (renamed from tests-clar/resources/issue_592/.gitted/objects/06/07ee9d4ccce8e4c4fa13c2c7d727e7faba4e0e)bin87 -> 87 bytes
-rw-r--r--tests/resources/issue_592/.gitted/objects/49/363a72a90d9424240258cd3759f23788ecf1d8 (renamed from tests-clar/resources/issue_592/.gitted/objects/49/363a72a90d9424240258cd3759f23788ecf1d8)bin55 -> 55 bytes
-rw-r--r--tests/resources/issue_592/.gitted/objects/4d/383e87f0371ba8fa353f3912db6862b2625e85 (renamed from tests-clar/resources/issue_592/.gitted/objects/4d/383e87f0371ba8fa353f3912db6862b2625e85)0
-rw-r--r--tests/resources/issue_592/.gitted/objects/71/44be264b61825fbff68046fe999bdfe96a1792 (renamed from tests-clar/resources/issue_592/.gitted/objects/71/44be264b61825fbff68046fe999bdfe96a1792)bin50 -> 50 bytes
-rw-r--r--tests/resources/issue_592/.gitted/objects/be/de83ee10b5b3f00239660b00acec2d55fd0b84 (renamed from tests-clar/resources/issue_592/.gitted/objects/be/de83ee10b5b3f00239660b00acec2d55fd0b84)bin107 -> 107 bytes
-rw-r--r--tests/resources/issue_592/.gitted/objects/e3/8fcc7a6060f5eb5b876e836b52ae4769363f21 (renamed from tests-clar/resources/issue_592/.gitted/objects/e3/8fcc7a6060f5eb5b876e836b52ae4769363f21)bin137 -> 137 bytes
-rw-r--r--tests/resources/issue_592/.gitted/objects/f1/adef63cb08891a0942b76fc4b9c50c6c494bc7 (renamed from tests-clar/resources/issue_592/.gitted/objects/f1/adef63cb08891a0942b76fc4b9c50c6c494bc7)bin29 -> 29 bytes
-rw-r--r--tests/resources/issue_592/.gitted/refs/heads/master (renamed from tests-clar/resources/issue_592/.gitted/refs/heads/master)0
-rw-r--r--tests/resources/issue_592/a.txt (renamed from tests-clar/resources/issue_592/a.txt)0
-rw-r--r--tests/resources/issue_592/c/a.txt (renamed from tests-clar/resources/issue_592/c/a.txt)0
-rw-r--r--tests/resources/issue_592/l.txt (renamed from tests-clar/resources/issue_592/l.txt)0
-rw-r--r--tests/resources/issue_592/t/a.txt (renamed from tests-clar/resources/issue_592/t/a.txt)0
-rw-r--r--tests/resources/issue_592/t/b.txt (renamed from tests-clar/resources/issue_592/t/b.txt)0
-rw-r--r--tests/resources/issue_592b/.gitted/HEAD (renamed from tests-clar/resources/merge-resolve/.gitted/HEAD)0
-rw-r--r--tests/resources/issue_592b/.gitted/config (renamed from tests-clar/resources/issue_592b/.gitted/config)0
-rw-r--r--tests/resources/issue_592b/.gitted/description (renamed from tests-clar/resources/merge-resolve/.gitted/description)0
-rw-r--r--tests/resources/issue_592b/.gitted/index (renamed from tests-clar/resources/issue_592b/.gitted/index)bin376 -> 376 bytes
-rw-r--r--tests/resources/issue_592b/.gitted/info/exclude (renamed from tests-clar/resources/issue_592b/.gitted/info/exclude)0
-rw-r--r--tests/resources/issue_592b/.gitted/logs/HEAD (renamed from tests-clar/resources/issue_592b/.gitted/logs/HEAD)0
-rw-r--r--tests/resources/issue_592b/.gitted/logs/refs/heads/master (renamed from tests-clar/resources/issue_592b/.gitted/logs/refs/heads/master)0
-rw-r--r--tests/resources/issue_592b/.gitted/objects/3f/bf1852f72fd268e36457b13a18cdd9a4c9ea35 (renamed from tests-clar/resources/issue_592b/.gitted/objects/3f/bf1852f72fd268e36457b13a18cdd9a4c9ea35)0
-rw-r--r--tests/resources/issue_592b/.gitted/objects/6f/a891d3e578c83e1c03bdb9e0fdd8e6e934157f (renamed from tests-clar/resources/issue_592b/.gitted/objects/6f/a891d3e578c83e1c03bdb9e0fdd8e6e934157f)bin28 -> 28 bytes
-rw-r--r--tests/resources/issue_592b/.gitted/objects/80/07d41d5794e6ce4d4d2c97e370d5a9aa6d5213 (renamed from tests-clar/resources/issue_592b/.gitted/objects/80/07d41d5794e6ce4d4d2c97e370d5a9aa6d5213)bin24 -> 24 bytes
-rw-r--r--tests/resources/issue_592b/.gitted/objects/a6/5fb6583a7c425284142f285bc359a2d6565513 (renamed from tests-clar/resources/issue_592b/.gitted/objects/a6/5fb6583a7c425284142f285bc359a2d6565513)bin93 -> 93 bytes
-rw-r--r--tests/resources/issue_592b/.gitted/objects/ae/be7a55922c7097ef91ca3a7bc327a901d87c2c (renamed from tests-clar/resources/issue_592b/.gitted/objects/ae/be7a55922c7097ef91ca3a7bc327a901d87c2c)bin122 -> 122 bytes
-rw-r--r--tests/resources/issue_592b/.gitted/objects/b3/44b055867fcdc1f01eaa75056a43e868eb4fbc (renamed from tests-clar/resources/issue_592b/.gitted/objects/b3/44b055867fcdc1f01eaa75056a43e868eb4fbc)bin36 -> 36 bytes
-rw-r--r--tests/resources/issue_592b/.gitted/objects/f7/d75fbfad8b1d2e307ced287ea78aad403cdce3 (renamed from tests-clar/resources/issue_592b/.gitted/objects/f7/d75fbfad8b1d2e307ced287ea78aad403cdce3)bin57 -> 57 bytes
-rw-r--r--tests/resources/issue_592b/.gitted/refs/heads/master (renamed from tests-clar/resources/issue_592b/.gitted/refs/heads/master)0
-rw-r--r--tests/resources/issue_592b/gitignore (renamed from tests-clar/resources/issue_592b/gitignore)0
-rw-r--r--tests/resources/issue_592b/ignored/contained/ignored3.txt (renamed from tests-clar/resources/issue_592b/ignored/contained/ignored3.txt)0
-rw-r--r--tests/resources/issue_592b/ignored/contained/tracked3.txt (renamed from tests-clar/resources/issue_592b/ignored/contained/tracked3.txt)0
-rw-r--r--tests/resources/issue_592b/ignored/ignored2.txt (renamed from tests-clar/resources/issue_592b/ignored/ignored2.txt)0
-rw-r--r--tests/resources/issue_592b/ignored/tracked2.txt (renamed from tests-clar/resources/issue_592b/ignored/tracked2.txt)0
-rw-r--r--tests/resources/issue_592b/ignored1.txt (renamed from tests-clar/resources/issue_592b/ignored1.txt)0
-rw-r--r--tests/resources/issue_592b/tracked1.txt (renamed from tests-clar/resources/issue_592b/tracked1.txt)0
-rw-r--r--tests/resources/merge-resolve/.gitted/COMMIT_EDITMSG (renamed from tests-clar/resources/merge-resolve/.gitted/COMMIT_EDITMSG)0
-rw-r--r--tests/resources/merge-resolve/.gitted/HEAD (renamed from tests-clar/resources/mergedrepo/.gitted/HEAD)0
-rw-r--r--tests/resources/merge-resolve/.gitted/ORIG_HEAD (renamed from tests-clar/resources/merge-resolve/.gitted/ORIG_HEAD)0
-rw-r--r--tests/resources/merge-resolve/.gitted/config (renamed from tests-clar/resources/merge-resolve/.gitted/config)0
-rw-r--r--tests/resources/merge-resolve/.gitted/description (renamed from tests-clar/resources/mergedrepo/.gitted/description)0
-rw-r--r--tests/resources/merge-resolve/.gitted/index (renamed from tests-clar/resources/merge-resolve/.gitted/index)bin624 -> 624 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/HEAD (renamed from tests-clar/resources/merge-resolve/.gitted/logs/HEAD)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/branch (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/branch)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/df_ancestor (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/df_ancestor)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/df_side1 (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/df_side1)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/df_side2 (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/df_side2)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/ff_branch (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/ff_branch)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/master (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/master)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/octo1 (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo1)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/octo2 (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo2)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/octo3 (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo3)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/octo4 (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo4)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/octo5 (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo5)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/octo6 (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo6)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/renames1 (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/renames1)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/renames2 (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/renames2)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10 (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10-branch (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10-branch)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11 (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11-branch (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11-branch)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13 (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13-branch (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13-branch)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14 (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14-branch (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14-branch)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt-branch (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt-branch)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt-branch (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt-branch)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4 (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4-branch (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4-branch)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1 (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1-branch (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1-branch)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2 (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2-branch (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2-branch)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6 (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6-branch (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6-branch)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7 (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7-branch (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7-branch)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8 (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8-branch (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8-branch)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9 (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9-branch (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9-branch)0
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/unrelated (renamed from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/unrelated)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/00/5b6fcc8fec71d2550bef8462d169b3c26aa14b (renamed from tests-clar/resources/merge-resolve/.gitted/objects/00/5b6fcc8fec71d2550bef8462d169b3c26aa14b)bin168 -> 168 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/00/9b9cab6fdac02915a88ecd078b7a792ed802d8 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/00/9b9cab6fdac02915a88ecd078b7a792ed802d8)bin164 -> 164 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/00/c7d33f1ffa79d19c2272b370fcaeaadba49c08 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/00/c7d33f1ffa79d19c2272b370fcaeaadba49c08)bin147 -> 147 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/01/f149e1b8f84bd8896aaff6d6b22af88459ded0 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/01/f149e1b8f84bd8896aaff6d6b22af88459ded0)bin166 -> 166 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/02/04a84f822acbf6386b36d33f1f6bc68bbbf858 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/02/04a84f822acbf6386b36d33f1f6bc68bbbf858)bin168 -> 168 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/02/251f990ca8e92e7ae61d3426163fa821c64001 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/02/251f990ca8e92e7ae61d3426163fa821c64001)bin264 -> 264 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/03/21415405cb906c46869919af56d51dbbe5e85c (renamed from tests-clar/resources/merge-resolve/.gitted/objects/03/21415405cb906c46869919af56d51dbbe5e85c)bin271 -> 271 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/03/2ebc5ab85d9553bb187d3cd40875ff23a63ed0 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/03/2ebc5ab85d9553bb187d3cd40875ff23a63ed0)bin29 -> 29 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/03/b87706555accbf874ccd410dbda01e8e70a67f (renamed from tests-clar/resources/merge-resolve/.gitted/objects/03/b87706555accbf874ccd410dbda01e8e70a67f)bin353 -> 353 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/03/dad1005e5d06d418f50b12e0bcd48ff2306a03 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/03/dad1005e5d06d418f50b12e0bcd48ff2306a03)bin264 -> 264 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/05/1ffd7901a442faf56b226161649074f15c7c47 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/05/1ffd7901a442faf56b226161649074f15c7c47)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/05/8541fc37114bfc1dddf6bd6bffc7fae5c2e6fe (renamed from tests-clar/resources/merge-resolve/.gitted/objects/05/8541fc37114bfc1dddf6bd6bffc7fae5c2e6fe)bin63 -> 63 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/05/f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c (renamed from tests-clar/resources/merge-resolve/.gitted/objects/05/f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c)bin170 -> 170 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/07/a759da919f737221791d542f176ab49c88837f (renamed from tests-clar/resources/merge-resolve/.gitted/objects/07/a759da919f737221791d542f176ab49c88837f)bin165 -> 165 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/07/c514b04698e068892b31c8d352b85813b99c6e (renamed from tests-clar/resources/merge-resolve/.gitted/objects/07/c514b04698e068892b31c8d352b85813b99c6e)bin32 -> 32 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/09/055301463b7f2f8ee5d368f8ed5c0a40ad8515 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/09/055301463b7f2f8ee5d368f8ed5c0a40ad8515)bin41 -> 41 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/09/17bb159596aea4d295f4857da77e8f96b3c7dc (renamed from tests-clar/resources/merge-resolve/.gitted/objects/09/17bb159596aea4d295f4857da77e8f96b3c7dc)bin36 -> 36 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/09/2ce8682d7f3a2a3a769a6daca58950168ba5c4 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/09/2ce8682d7f3a2a3a769a6daca58950168ba5c4)bin163 -> 163 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/09/3bebf072dd4bbba88833667d6ffe454df199e1 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/09/3bebf072dd4bbba88833667d6ffe454df199e1)bin266 -> 266 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/09/768bed22680cdb0859683fa9677ccc8d5a25c1 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/09/768bed22680cdb0859683fa9677ccc8d5a25c1)bin275 -> 275 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/0a/75d9aac1dc84fb5aa51f7325c0ab53242ddef7 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/0a/75d9aac1dc84fb5aa51f7325c0ab53242ddef7)bin275 -> 275 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/0c/fd6c54ef6532d862408f562309dc9c74a401e8 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/0c/fd6c54ef6532d862408f562309dc9c74a401e8)bin28 -> 28 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/0d/52e3a556e189ba0948ae56780918011c1b167d (renamed from tests-clar/resources/merge-resolve/.gitted/objects/0d/52e3a556e189ba0948ae56780918011c1b167d)bin235 -> 235 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/0d/872f8e871a30208305978ecbf9e66d864f1638 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/0d/872f8e871a30208305978ecbf9e66d864f1638)bin89 -> 89 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/0e/c5f433959cd46177f745903353efb5be08d151 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/0e/c5f433959cd46177f745903353efb5be08d151)bin165 -> 165 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/11/deab00b2d3a6f5a3073988ac050c2d7b6655e2 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/11/deab00b2d3a6f5a3073988ac050c2d7b6655e2)bin34 -> 34 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/11/f4f3c08b737f5fd896cbefa1425ee63b21b2fa (renamed from tests-clar/resources/merge-resolve/.gitted/objects/11/f4f3c08b737f5fd896cbefa1425ee63b21b2fa)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/13/d1be4ea52a6ced1d7a1d832f0ee3c399348e5e (renamed from tests-clar/resources/merge-resolve/.gitted/objects/13/d1be4ea52a6ced1d7a1d832f0ee3c399348e5e)bin168 -> 168 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/14/39088f509b79b1535b64193137d3ce4b240734 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/14/39088f509b79b1535b64193137d3ce4b240734)bin58 -> 58 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/15/8dc7bedb202f5b26502bf3574faa7f4238d56c (renamed from tests-clar/resources/merge-resolve/.gitted/objects/15/8dc7bedb202f5b26502bf3574faa7f4238d56c)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/16/f825815cfd20a07a75c71554e82d8eede0b061 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/16/f825815cfd20a07a75c71554e82d8eede0b061)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/17/8940b450f238a56c0d75b7955cb57b38191982 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/17/8940b450f238a56c0d75b7955cb57b38191982)bin65 -> 65 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/18/3310e30fb1499af8c619108ffea4d300b5e778 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/18/3310e30fb1499af8c619108ffea4d300b5e778)bin170 -> 170 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/18/cb316b1cefa0f8a6946f0e201a8e1a6f845ab9 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/18/cb316b1cefa0f8a6946f0e201a8e1a6f845ab9)bin68 -> 68 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/19/b7ac485269b672a101060894de3ba9c2a24dd1 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/19/b7ac485269b672a101060894de3ba9c2a24dd1)bin53 -> 53 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/1a/010b1c0f081b2e8901d55307a15c29ff30af0ebin0 -> 19 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/1c/ff9ec6a47a537380dedfdd17c9e76d74259a2b (renamed from tests-clar/resources/merge-resolve/.gitted/objects/1c/ff9ec6a47a537380dedfdd17c9e76d74259a2b)bin33 -> 33 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/1e/4ff029aee68d0d69ef9eb6efa6cbf1ec732f99 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/1e/4ff029aee68d0d69ef9eb6efa6cbf1ec732f99)bin29 -> 29 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/1f/81433e3161efbf250576c58fede7f6b836f3d3 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/1f/81433e3161efbf250576c58fede7f6b836f3d3)bin262 -> 262 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/20/91d94c8bd3eb0835dc5220de5e8bb310fa1513 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/20/91d94c8bd3eb0835dc5220de5e8bb310fa1513)bin271 -> 271 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/21/671e290278286fb2ce4c63d01699b67adce331 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/21/671e290278286fb2ce4c63d01699b67adce331)bin79 -> 79 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/22/7792b52aaa0b238bea00ec7e509b02623f168c (renamed from tests-clar/resources/merge-resolve/.gitted/objects/22/7792b52aaa0b238bea00ec7e509b02623f168c)bin102 -> 102 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/23/3c0919c998ed110a4b6ff36f353aec8b713487 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/23/3c0919c998ed110a4b6ff36f353aec8b713487)bin43 -> 43 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/23/92a2dacc9efb562b8635d6579fb458751c7c5b (renamed from tests-clar/resources/merge-resolve/.gitted/objects/23/92a2dacc9efb562b8635d6579fb458751c7c5b)bin142 -> 142 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/24/1a1005cd9b980732741b74385b891142bcba28 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/24/1a1005cd9b980732741b74385b891142bcba28)bin67 -> 67 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/24/2591eb280ee9eeb2ce63524b9a8b9bc4cb515d (renamed from tests-clar/resources/merge-resolve/.gitted/objects/24/2591eb280ee9eeb2ce63524b9a8b9bc4cb515d)bin30 -> 30 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/24/90b9f1a079420870027deefb49f51d6656cf74 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/24/90b9f1a079420870027deefb49f51d6656cf74)bin268 -> 268 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/25/9d08ca43af9200e9ea9a098e44a5a350ebd9b3 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/25/9d08ca43af9200e9ea9a098e44a5a350ebd9b3)bin381 -> 381 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/25/c40b7660c08c8fb581f770312f41b9b03119d1 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/25/c40b7660c08c8fb581f770312f41b9b03119d1)bin31 -> 31 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/26/153a3ff3649b6c2bb652d3f06878c6e0a172f9 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/26/153a3ff3649b6c2bb652d3f06878c6e0a172f9)bin48 -> 48 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/27/133da702ba3c60af2a01e96c2555ff4045d692 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/27/133da702ba3c60af2a01e96c2555ff4045d692)bin32 -> 32 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/2b/0de5dc27505dcdd83a75c8bf1fcd9462cd7add (renamed from tests-clar/resources/merge-resolve/.gitted/objects/2b/0de5dc27505dcdd83a75c8bf1fcd9462cd7add)bin147 -> 147 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/2b/5f1f181ee3b58ea751f5dd5d8f9b445520a136 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/2b/5f1f181ee3b58ea751f5dd5d8f9b445520a136)bin53 -> 53 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863)bin56 -> 56 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/2d/a538570bc1e5b2c3e855bf702f35248ad0735f (renamed from tests-clar/resources/merge-resolve/.gitted/objects/2d/a538570bc1e5b2c3e855bf702f35248ad0735f)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/2f/2e37b7ebbae467978610896ca3aafcdad2ee67 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/2f/2e37b7ebbae467978610896ca3aafcdad2ee67)bin52 -> 52 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/2f/4024ce528d36d8670c289cce5a7963e625bb0c (renamed from tests-clar/resources/merge-resolve/.gitted/objects/2f/4024ce528d36d8670c289cce5a7963e625bb0c)bin179 -> 179 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/2f/56120107d680129a5d9791b521cb1e73a2ed31 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/2f/56120107d680129a5d9791b521cb1e73a2ed31)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/2f/598248eeccfc27e5ca44d9d96383f6dfea7b16 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/2f/598248eeccfc27e5ca44d9d96383f6dfea7b16)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/31/68dca1a561889b045a6441909f4c56145e666d (renamed from tests-clar/resources/merge-resolve/.gitted/objects/31/68dca1a561889b045a6441909f4c56145e666d)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/31/d5472536041a83d986829240bbbdc897c6f8a6 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/31/d5472536041a83d986829240bbbdc897c6f8a6)bin41 -> 41 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/32/21dd512b7e2dc4b5bd03046df6c81b2ab2070b (renamed from tests-clar/resources/merge-resolve/.gitted/objects/32/21dd512b7e2dc4b5bd03046df6c81b2ab2070b)bin47 -> 47 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/33/46d64325b39e5323733492cd55f808994a2475 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/33/46d64325b39e5323733492cd55f808994a2475)bin33 -> 33 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/33/d500f588fbbe65901d82b4e6b008e549064be0 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/33/d500f588fbbe65901d82b4e6b008e549064be0)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/34/bfafff88eaf118402b44e6f3e2dbbf1a582b05 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/34/bfafff88eaf118402b44e6f3e2dbbf1a582b05)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/35/0c6eb3010efc403a6bed682332635314e9ed58 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/35/0c6eb3010efc403a6bed682332635314e9ed58)bin92 -> 92 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/35/411bfb77cd2cc431f3a03a2b4976ed94b5d241 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/35/411bfb77cd2cc431f3a03a2b4976ed94b5d241)bin31 -> 31 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/35/4704d3613ad4228e4786fc76656b11e98236c4 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/35/4704d3613ad4228e4786fc76656b11e98236c4)bin41 -> 41 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/35/632e43612c06a3ea924bfbacd48333da874c29 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/35/632e43612c06a3ea924bfbacd48333da874c29)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/35/75826c96a975031d2c14368529cc5c4353a8fd (renamed from tests-clar/resources/merge-resolve/.gitted/objects/35/75826c96a975031d2c14368529cc5c4353a8fd)bin163 -> 163 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/36/219b49367146cb2e6a1555b5a9ebd4d0328495 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/36/219b49367146cb2e6a1555b5a9ebd4d0328495)bin68 -> 68 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/36/4bbe4ce80c7bd31e6307dce77d46e3e1759fb3 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/36/4bbe4ce80c7bd31e6307dce77d46e3e1759fb3)bin35 -> 35 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/37/48859b001c6e627e712a07951aee40afd19b41 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/37/48859b001c6e627e712a07951aee40afd19b41)bin41 -> 41 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/38/5c8a0f26ddf79e9041e15e17dc352ed2c4cced (renamed from tests-clar/resources/merge-resolve/.gitted/objects/38/5c8a0f26ddf79e9041e15e17dc352ed2c4cced)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/3b/47b031b3e55ae11e14a05260b1c3ffd6838d55 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/3b/47b031b3e55ae11e14a05260b1c3ffd6838d55)bin161 -> 161 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/3b/bf0bf59b20df5d5fc58b9fc1dc07be637c301f (renamed from tests-clar/resources/merge-resolve/.gitted/objects/3b/bf0bf59b20df5d5fc58b9fc1dc07be637c301f)bin269 -> 269 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/3e/f4d30382ca33fdeba9fda895a99e0891ba37aa (renamed from tests-clar/resources/merge-resolve/.gitted/objects/3e/f4d30382ca33fdeba9fda895a99e0891ba37aa)bin36 -> 36 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/3e/f9bfe82f9635518ae89152322f3b46fd4ba25b (renamed from tests-clar/resources/merge-resolve/.gitted/objects/3e/f9bfe82f9635518ae89152322f3b46fd4ba25b)bin172 -> 172 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/40/2784a46a4a3982294231594cbeb431f506d22c (renamed from tests-clar/resources/merge-resolve/.gitted/objects/40/2784a46a4a3982294231594cbeb431f506d22c)bin83 -> 83 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/41/2b32fb66137366147f1801ecc962452757d48a (renamed from tests-clar/resources/merge-resolve/.gitted/objects/41/2b32fb66137366147f1801ecc962452757d48a)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/43/aafd43bea779ec74317dc361f45ae3f532a505 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/43/aafd43bea779ec74317dc361f45ae3f532a505)bin37 -> 37 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/43/c338656342227a3a3cd3aa85cbf784061f5425 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/43/c338656342227a3a3cd3aa85cbf784061f5425)bin266 -> 266 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/45/299c1ca5e07bba1fd90843056fb559f96b1f5a (renamed from tests-clar/resources/merge-resolve/.gitted/objects/45/299c1ca5e07bba1fd90843056fb559f96b1f5a)bin58 -> 58 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/46/6daf8552b891e5c22bc58c9d7fc1a2eb8f0289 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/46/6daf8552b891e5c22bc58c9d7fc1a2eb8f0289)bin382 -> 382 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/47/6dbb3e207313d1d8aaa120c6ad204bf1295e53 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/47/6dbb3e207313d1d8aaa120c6ad204bf1295e53)bin522 -> 522 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/47/8172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/47/8172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2)bin165 -> 165 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/49/130a28ef567af9a6a6104c38773fedfa5f9742 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/49/130a28ef567af9a6a6104c38773fedfa5f9742)bin37 -> 37 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/49/9df817155e4bdd3c6ee192a72c52f481818230 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/49/9df817155e4bdd3c6ee192a72c52f481818230)bin35 -> 35 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4a/9550ebcc97ce22b22f45af7b829bb030d003f5 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/4a/9550ebcc97ce22b22f45af7b829bb030d003f5)bin53 -> 53 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4b/253da36a0ae8bfce63aeabd8c5b58429925594 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/4b/253da36a0ae8bfce63aeabd8c5b58429925594)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4b/48deed3a433909bfd6b6ab3d4b91348b6af464 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/4b/48deed3a433909bfd6b6ab3d4b91348b6af464)bin24 -> 24 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904)bin15 -> 15 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4c/9fac0707f8d4195037ae5a681aa48626491541 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/4c/9fac0707f8d4195037ae5a681aa48626491541)bin167 -> 167 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4c/a408a8c88655f7586a1b580be6fad138121e98 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/4c/a408a8c88655f7586a1b580be6fad138121e98)bin159 -> 159 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4e/0d9401aee78eb345a8685a859d37c8c3c0bbed (renamed from tests-clar/resources/merge-resolve/.gitted/objects/4e/0d9401aee78eb345a8685a859d37c8c3c0bbed)bin262 -> 262 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10)bin56 -> 56 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4e/b04c9e79e88f6640d01ff5b25ca2a60764f216 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/4e/b04c9e79e88f6640d01ff5b25ca2a60764f216)bin34 -> 34 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4f/e93c0ec83eb6305cbace3dace88ecee1b63cb6 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/4f/e93c0ec83eb6305cbace3dace88ecee1b63cb6)bin161 -> 161 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/50/12fd565b1393bdfda1805d4ec38ce6619e1fd1 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/50/12fd565b1393bdfda1805d4ec38ce6619e1fd1)bin29 -> 29 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/50/4f75ac95a71ef98051817618576a68505b92f9 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/50/4f75ac95a71ef98051817618576a68505b92f9)bin93 -> 93 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/50/84fc2a88b6bdba8db93bd3953a8f4fdb470238 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/50/84fc2a88b6bdba8db93bd3953a8f4fdb470238)bin53 -> 53 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/50/ce7d7d01217679e26c55939eef119e0c93e272 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/50/ce7d7d01217679e26c55939eef119e0c93e272)bin159 -> 159 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/51/95a1b480f66691b667f10a9e41e70115a78351 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/51/95a1b480f66691b667f10a9e41e70115a78351)bin170 -> 170 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/52/d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/52/d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/53/825f41ac8d640612f9423a2f03a69f3d96809a (renamed from tests-clar/resources/merge-resolve/.gitted/objects/53/825f41ac8d640612f9423a2f03a69f3d96809a)bin164 -> 164 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/54/269b3f6ec3d7d4ede24dd350dd5d605495c3ae (renamed from tests-clar/resources/merge-resolve/.gitted/objects/54/269b3f6ec3d7d4ede24dd350dd5d605495c3ae)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/54/59c89aa0026d543ce8343bd89871bce543f9c2 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/54/59c89aa0026d543ce8343bd89871bce543f9c2)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/54/7607c690372fe81fab8e3bb44c530e129118fd (renamed from tests-clar/resources/merge-resolve/.gitted/objects/54/7607c690372fe81fab8e3bb44c530e129118fd)bin58 -> 58 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/55/b4e4687e7a0d9ca367016ed930f385d4022e6f (renamed from tests-clar/resources/merge-resolve/.gitted/objects/55/b4e4687e7a0d9ca367016ed930f385d4022e6f)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/56/6ab53c220a2eafc1212af1a024513230280ab9 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/56/6ab53c220a2eafc1212af1a024513230280ab9)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/56/a638b76b75e068590ac999c2f8621e7f3e264c (renamed from tests-clar/resources/merge-resolve/.gitted/objects/56/a638b76b75e068590ac999c2f8621e7f3e264c)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/57/079a46233ae2b6df62e9ade71c4948512abefb (renamed from tests-clar/resources/merge-resolve/.gitted/objects/57/079a46233ae2b6df62e9ade71c4948512abefb)bin168 -> 168 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/58/43febcb23480df0b5edb22a21c59c772bb8e29 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/58/43febcb23480df0b5edb22a21c59c772bb8e29)bin71 -> 71 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/58/e853f66699fd02629fd50bde08082bc005933a (renamed from tests-clar/resources/merge-resolve/.gitted/objects/58/e853f66699fd02629fd50bde08082bc005933a)bin160 -> 160 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/59/6803b523203a4851c824c07366906f8353f4ad (renamed from tests-clar/resources/merge-resolve/.gitted/objects/59/6803b523203a4851c824c07366906f8353f4ad)bin163 -> 163 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/5c/2411f8075f48a6b2fdb85ebc0d371747c4df15 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/5c/2411f8075f48a6b2fdb85ebc0d371747c4df15)bin37 -> 37 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/5c/341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d (renamed from tests-clar/resources/merge-resolve/.gitted/objects/5c/341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d)bin37 -> 37 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/5c/3b68a71fc4fa5d362fd3875e53137c6a5ab7a5 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/5c/3b68a71fc4fa5d362fd3875e53137c6a5ab7a5)bin40 -> 40 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/5d/c1018e90b19654bee986b7a0c268804d39659d (renamed from tests-clar/resources/merge-resolve/.gitted/objects/5d/c1018e90b19654bee986b7a0c268804d39659d)bin168 -> 168 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/5d/dd0fe66f990dc0e5cf9fec6d9b465240e9537f (renamed from tests-clar/resources/merge-resolve/.gitted/objects/5d/dd0fe66f990dc0e5cf9fec6d9b465240e9537f)bin43 -> 43 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/5e/b7bb6a146eb3c7fd3990b240a2308eceb1cf8d (renamed from tests-clar/resources/merge-resolve/.gitted/objects/5e/b7bb6a146eb3c7fd3990b240a2308eceb1cf8d)bin268 -> 268 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/5f/bfbdc04b4eca46f54f4853a3c5a1dce28f5165 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/5f/bfbdc04b4eca46f54f4853a3c5a1dce28f5165)bin283 -> 283 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/60/91fc2c036a382a69489e3f518ee5aae9a4e567 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/60/91fc2c036a382a69489e3f518ee5aae9a4e567)bin258 -> 258 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/61/340eeed7340fa6a8792def9a5938bb5d4434bb (renamed from tests-clar/resources/merge-resolve/.gitted/objects/61/340eeed7340fa6a8792def9a5938bb5d4434bb)bin92 -> 92 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/61/78885b38fe96e825ac0f492c0a941f288b37f6 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/61/78885b38fe96e825ac0f492c0a941f288b37f6)bin289 -> 289 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/62/12c31dab5e482247d7977e4f0dd3601decf13b (renamed from tests-clar/resources/merge-resolve/.gitted/objects/62/12c31dab5e482247d7977e4f0dd3601decf13b)bin45 -> 45 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/62/269111c3b02a9355badcb9da8678b1bf41787b (renamed from tests-clar/resources/merge-resolve/.gitted/objects/62/269111c3b02a9355badcb9da8678b1bf41787b)bin269 -> 269 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/62/c4f6533c9a3894191fdcb96a3be935ade63f1a (renamed from tests-clar/resources/merge-resolve/.gitted/objects/62/c4f6533c9a3894191fdcb96a3be935ade63f1a)bin53 -> 53 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/63/247125386de9ec90a27ad36169307bf8a11a38 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/63/247125386de9ec90a27ad36169307bf8a11a38)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/67/18a45909532d1fcf5600d0877f7fe7e78f0b86 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/67/18a45909532d1fcf5600d0877f7fe7e78f0b86)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/68/c6c84b091926c7d90aa6a79b2bc3bb6adccd8e (renamed from tests-clar/resources/merge-resolve/.gitted/objects/68/c6c84b091926c7d90aa6a79b2bc3bb6adccd8e)bin55 -> 55 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/69/f570c57b24ea7c086e94c5e574964798321435 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/69/f570c57b24ea7c086e94c5e574964798321435)bin266 -> 266 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/6a/e1a3967031a42cf955d9d5c2395211ac82f6cf (renamed from tests-clar/resources/merge-resolve/.gitted/objects/6a/e1a3967031a42cf955d9d5c2395211ac82f6cf)bin272 -> 272 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/6b/7e37be8ce0b897093f2878a9dcd8f396beda2c (renamed from tests-clar/resources/merge-resolve/.gitted/objects/6b/7e37be8ce0b897093f2878a9dcd8f396beda2c)bin53 -> 53 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/6c/06dcd163587c2cc18be44857e0b71116382aeb (renamed from tests-clar/resources/merge-resolve/.gitted/objects/6c/06dcd163587c2cc18be44857e0b71116382aeb)bin30 -> 30 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/6f/32739c3724d1d5f855299309f388606f407468 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/6f/32739c3724d1d5f855299309f388606f407468)bin630 -> 630 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/6f/a33014764bf1120a454eb8437ae098238e409b (renamed from tests-clar/resources/merge-resolve/.gitted/objects/6f/a33014764bf1120a454eb8437ae098238e409b)bin168 -> 168 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/6f/be9fb85c86d7d1435f728da418bdff52c640a9 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/6f/be9fb85c86d7d1435f728da418bdff52c640a9)bin83 -> 83 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/71/17467b18605a660ebe5586df69e2311ed5609f (renamed from tests-clar/resources/merge-resolve/.gitted/objects/71/17467b18605a660ebe5586df69e2311ed5609f)bin265 -> 265 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/71/2ebba6669ea847d9829e4f1059d6c830c8b531 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/71/2ebba6669ea847d9829e4f1059d6c830c8b531)bin152 -> 152 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/71/add2d7b93d55bf3600f8a1582beceebbd050c8 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/71/add2d7b93d55bf3600f8a1582beceebbd050c8)bin264 -> 264 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/72/ea499e108df5ff0a4a913e7655bbeeb1fb69f2bin0 -> 26 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/74/df13f0793afdaa972150bba976f7de8284914e (renamed from tests-clar/resources/merge-resolve/.gitted/objects/74/df13f0793afdaa972150bba976f7de8284914e)bin26 -> 26 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/75/a811bf6bc57694adb3fe604786f3a4efd1cd1b (renamed from tests-clar/resources/merge-resolve/.gitted/objects/75/a811bf6bc57694adb3fe604786f3a4efd1cd1b)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/76/63fce0130db092936b137cabd693ec234eb060 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/76/63fce0130db092936b137cabd693ec234eb060)bin49 -> 49 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/76/ab0e2868197ec158ddd6c78d8a0d2fd73d38f9 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/76/ab0e2868197ec158ddd6c78d8a0d2fd73d38f9)bin37 -> 37 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/7a/a3edf2bcfee22398e6b55295aa56366b7aaf76 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/7a/a3edf2bcfee22398e6b55295aa56366b7aaf76)bin271 -> 271 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/7c/2c5228c9e90170d4a35e6558e47163daf092e5 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/7c/2c5228c9e90170d4a35e6558e47163daf092e5)bin172 -> 172 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/7c/b63eed597130ba4abb87b3e544b85021905520 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/7c/b63eed597130ba4abb87b3e544b85021905520)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/7e/2d058d5fedf8329db44db4fac610d6b1a89159 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/7e/2d058d5fedf8329db44db4fac610d6b1a89159)bin165 -> 165 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/7f/7a2da58126226986d71c6ddfab4afba693280d (renamed from tests-clar/resources/merge-resolve/.gitted/objects/7f/7a2da58126226986d71c6ddfab4afba693280d)bin199 -> 199 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/80/a8fbb3abb1ba423d554e9630b8fc2e5698f86b (renamed from tests-clar/resources/merge-resolve/.gitted/objects/80/a8fbb3abb1ba423d554e9630b8fc2e5698f86b)bin168 -> 168 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/81/87117062b750eed4f93fd7e899f17b52ce554d (renamed from tests-clar/resources/merge-resolve/.gitted/objects/81/87117062b750eed4f93fd7e899f17b52ce554d)bin170 -> 170 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/83/07d93a155903a5c49576583f0ce1f6ff897c0e (renamed from tests-clar/resources/merge-resolve/.gitted/objects/83/07d93a155903a5c49576583f0ce1f6ff897c0e)bin30 -> 30 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/83/824a8c6658768e2013905219cc8c64cc3d9a2e (renamed from tests-clar/resources/merge-resolve/.gitted/objects/83/824a8c6658768e2013905219cc8c64cc3d9a2e)bin382 -> 382 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/84/9619b03ae540acee4d1edec96b86993da6b497 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/84/9619b03ae540acee4d1edec96b86993da6b497)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/84/de84f8f3a6d63e636ee9ad81f4b80512fa9bbe (renamed from tests-clar/resources/merge-resolve/.gitted/objects/84/de84f8f3a6d63e636ee9ad81f4b80512fa9bbe)bin41 -> 41 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/86/088dae8bade454995b21a1c88107b0e1accdab (renamed from tests-clar/resources/merge-resolve/.gitted/objects/86/088dae8bade454995b21a1c88107b0e1accdab)bin47 -> 47 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/87/b4926260d77a3b851e71ecce06839bd650b231 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/87/b4926260d77a3b851e71ecce06839bd650b231)bin43 -> 43 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/88/e185910a15cd13bdf44854ad037f4842b03b29 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/88/e185910a15cd13bdf44854ad037f4842b03b29)bin177 -> 177 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/8a/ad9d0ea334951da47b621a475b39cc6ed759bf (renamed from tests-clar/resources/merge-resolve/.gitted/objects/8a/ad9d0ea334951da47b621a475b39cc6ed759bf)bin51 -> 51 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/8a/ae714f7d939309d7f132b30646d96743134a9f (renamed from tests-clar/resources/merge-resolve/.gitted/objects/8a/ae714f7d939309d7f132b30646d96743134a9f)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/8b/095d8fd01594f4d14454d073e3ac57b9ce485f (renamed from tests-clar/resources/merge-resolve/.gitted/objects/8b/095d8fd01594f4d14454d073e3ac57b9ce485f)bin201 -> 201 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/8b/5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a (renamed from tests-clar/resources/merge-resolve/.gitted/objects/8b/5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/8b/fb012a6d809e499bd8d3e194a3929bc8995b93bin0 -> 34 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/8c/749d9968d4b10dcfb06c9f97d0e5d92d337071 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/8c/749d9968d4b10dcfb06c9f97d0e5d92d337071)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/8f/4433f8593ddd65b7dd43dd4564d841f4d9c8aa (renamed from tests-clar/resources/merge-resolve/.gitted/objects/8f/4433f8593ddd65b7dd43dd4564d841f4d9c8aa)bin164 -> 164 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/90/a336c7dacbe295159413559b0043b8bdc60d57 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/90/a336c7dacbe295159413559b0043b8bdc60d57)bin271 -> 271 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/91/2b2d7819cf9c1029e414883857ed61d597a1a5 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/91/2b2d7819cf9c1029e414883857ed61d597a1a5)bin295 -> 295 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/91/8bb3e09090a9995d48af9a2a6296d7e6088d1c (renamed from tests-clar/resources/merge-resolve/.gitted/objects/91/8bb3e09090a9995d48af9a2a6296d7e6088d1c)bin38 -> 38 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/92/7d4943cdbdc9a667db8e62cfd0a41870235c51 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/92/7d4943cdbdc9a667db8e62cfd0a41870235c51)bin535 -> 535 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/93/77fccdb210540b8c0520cc6e80eb632c20bd25 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/93/77fccdb210540b8c0520cc6e80eb632c20bd25)bin53 -> 53 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/94/4f5dd1a867cab4c2bbcb896493435cae1dcc1a (renamed from tests-clar/resources/merge-resolve/.gitted/objects/94/4f5dd1a867cab4c2bbcb896493435cae1dcc1a)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/94/8ba6e701c1edab0c2d394fb7c5538334129793 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/94/8ba6e701c1edab0c2d394fb7c5538334129793)bin71 -> 71 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/95/646149ab6b6ba6edc83cff678582538b457b2b (renamed from tests-clar/resources/merge-resolve/.gitted/objects/95/646149ab6b6ba6edc83cff678582538b457b2b)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/95/9de65e568274120fdf9e3af9f77b1550122149 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/95/9de65e568274120fdf9e3af9f77b1550122149)bin40 -> 40 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/96/8ca794a4597f7f6abbb2b8d940b4078a0f3fd4 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/96/8ca794a4597f7f6abbb2b8d940b4078a0f3fd4)bin53 -> 53 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/97/7c696519c5a3004c5f1d15d60c89dbeb8f235f (renamed from tests-clar/resources/merge-resolve/.gitted/objects/97/7c696519c5a3004c5f1d15d60c89dbeb8f235f)bin160 -> 160 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/98/ba4205fcf31f5dd93c916d35fe3f3b3d0e6714 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/98/ba4205fcf31f5dd93c916d35fe3f3b3d0e6714)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/98/d52d07c0b0bbf2b46548f6aa521295c2cb55db (renamed from tests-clar/resources/merge-resolve/.gitted/objects/98/d52d07c0b0bbf2b46548f6aa521295c2cb55db)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/99/b4f7e4f24470fa06b980bc21f1095c2a9425c0 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/99/b4f7e4f24470fa06b980bc21f1095c2a9425c0)bin164 -> 164 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/9a/301fbe6fada7dcb74fcd7c20269b5c743459a7 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/9a/301fbe6fada7dcb74fcd7c20269b5c743459a7)bin163 -> 163 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/9a/f731fa116d1eb9a6c0109562472cfee6f5a979 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/9a/f731fa116d1eb9a6c0109562472cfee6f5a979)bin48 -> 48 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/9c/0b6c34ef379a42d858f03fef38630f476b9102 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/9c/0b6c34ef379a42d858f03fef38630f476b9102)bin38 -> 38 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/9e/7f4359c469f309b6057febf4c6e80742cbed5b (renamed from tests-clar/resources/merge-resolve/.gitted/objects/9e/7f4359c469f309b6057febf4c6e80742cbed5b)bin539 -> 539 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/9e/fe7723802d4305142eee177e018fee1572c4f4 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/9e/fe7723802d4305142eee177e018fee1572c4f4)bin36 -> 36 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/9f/74397a3397b3585faf09e9926b110d7f654254 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/9f/74397a3397b3585faf09e9926b110d7f654254)bin621 -> 621 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a0/31a28ae70e33a641ce4b8a8f6317f1ab79dee4 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/a0/31a28ae70e33a641ce4b8a8f6317f1ab79dee4)bin37 -> 37 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a3/9a620dae5bc8b4e771cd4d251b7d080401a21e (renamed from tests-clar/resources/merge-resolve/.gitted/objects/a3/9a620dae5bc8b4e771cd4d251b7d080401a21e)bin29 -> 29 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a3/fabece9eb8748da810e1e08266fef9b7136ad4 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/a3/fabece9eb8748da810e1e08266fef9b7136ad4)bin164 -> 164 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a4/1b1bb6d0be3c22fb654234c33b428e15c8cc27 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/a4/1b1bb6d0be3c22fb654234c33b428e15c8cc27)bin92 -> 92 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a4/3150a738849c59376cf30bb2a68348a83c8f48 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/a4/3150a738849c59376cf30bb2a68348a83c8f48)bin162 -> 162 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a5/563304ddf6caba25cb50323a2ea6f7dbfcadca (renamed from tests-clar/resources/merge-resolve/.gitted/objects/a5/563304ddf6caba25cb50323a2ea6f7dbfcadca)bin48 -> 48 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a7/08b253bd507417ec42d1467a7fd2d7519c4956 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/a7/08b253bd507417ec42d1467a7fd2d7519c4956)bin40 -> 40 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a7/65fb87eb2f7a1920b73b2d5a057f8f8476a42b (renamed from tests-clar/resources/merge-resolve/.gitted/objects/a7/65fb87eb2f7a1920b73b2d5a057f8f8476a42b)bin170 -> 170 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a7/7a56a49f8f3ae242e02717f18ebbc60c5cc543 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/a7/7a56a49f8f3ae242e02717f18ebbc60c5cc543)bin65 -> 65 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a7/dbfcbfc1a60709cb80b5ca24539008456531d0 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/a7/dbfcbfc1a60709cb80b5ca24539008456531d0)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a8/02e06f1782a9645b9851bc7202cee74a8a4972 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/a8/02e06f1782a9645b9851bc7202cee74a8a4972)bin172 -> 172 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a8/87dd39ad3edd610fc9083dcb61e40ab50673d1 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/a8/87dd39ad3edd610fc9083dcb61e40ab50673d1)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a9/0bc3fb6f15181972a2959a921429efbd81a473 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/a9/0bc3fb6f15181972a2959a921429efbd81a473)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ab/40af3cb8a3ed2e2843e96d9aa7871336b94573 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/ab/40af3cb8a3ed2e2843e96d9aa7871336b94573)bin161 -> 161 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ab/6c44a2e84492ad4b41bb6bac87353e9d02ac8b (renamed from tests-clar/resources/merge-resolve/.gitted/objects/ab/6c44a2e84492ad4b41bb6bac87353e9d02ac8b)bin33 -> 33 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ab/929391ac42572f92110f3deeb4f0844a951e22 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/ab/929391ac42572f92110f3deeb4f0844a951e22)bin40 -> 40 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ac/4045f965119e6998f4340ed0f411decfb3ec05 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/ac/4045f965119e6998f4340ed0f411decfb3ec05)bin29 -> 29 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ad/a14492498136771f69dd451866cabcb0e9ef9a (renamed from tests-clar/resources/merge-resolve/.gitted/objects/ad/a14492498136771f69dd451866cabcb0e9ef9a)bin39 -> 39 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ad/a55a45d14527dc3dfc714ea1c65d2e1e6fbe87 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/ad/a55a45d14527dc3dfc714ea1c65d2e1e6fbe87)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/b2/d399ae15224e1d58066e3c8df70ce37de7a656 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/b2/d399ae15224e1d58066e3c8df70ce37de7a656)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/b4/2712cfe99a1a500b2a51fe984e0b8a7702ba11 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/b4/2712cfe99a1a500b2a51fe984e0b8a7702ba11)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/b6/9fe837e4cecfd4c9a40cdca7c138468687df07 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/b6/9fe837e4cecfd4c9a40cdca7c138468687df07)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/b6/f610aef53bd343e6c96227de874c66f00ee8e8 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/b6/f610aef53bd343e6c96227de874c66f00ee8e8)bin162 -> 162 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/b7/a2576f9fc20024ac9ef17cb134acbd1ac73127 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/b7/a2576f9fc20024ac9ef17cb134acbd1ac73127)bin320 -> 320 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/b8/a3a806d3950e8c0a03a34f234a92eff0e2c68d (renamed from tests-clar/resources/merge-resolve/.gitted/objects/b8/a3a806d3950e8c0a03a34f234a92eff0e2c68d)bin286 -> 286 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ba/cac9b3493509aa15e1730e1545fc0919d1dae0 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/ba/cac9b3493509aa15e1730e1545fc0919d1dae0)bin29 -> 29 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/bc/744705e1d8a019993cf88f62bc4020f1b80919 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/bc/744705e1d8a019993cf88f62bc4020f1b80919)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/bc/95c75d59386147d1e79a87c33068d8dbfd71f2 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/bc/95c75d59386147d1e79a87c33068d8dbfd71f2)bin348 -> 348 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/bd/593285fc7fe4ca18ccdbabf027f5d689101452 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/bd/593285fc7fe4ca18ccdbabf027f5d689101452)bin159 -> 159 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/bd/867fbae2faa80b920b002b80b1c91bcade7784 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/bd/867fbae2faa80b920b002b80b1c91bcade7784)bin48 -> 48 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/bd/9cb4cd0a770cb9adcb5fce212142ef40ea1c35 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/bd/9cb4cd0a770cb9adcb5fce212142ef40ea1c35)bin51 -> 51 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/be/f6e37b3ee632ba74159168836f382fed21d77d (renamed from tests-clar/resources/merge-resolve/.gitted/objects/be/f6e37b3ee632ba74159168836f382fed21d77d)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c0/6a9be584ac49aa02c5551312d9e2982c91df10 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/c0/6a9be584ac49aa02c5551312d9e2982c91df10)bin348 -> 348 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c1/b17981db0840109a820dae8674ee29684134ff (renamed from tests-clar/resources/merge-resolve/.gitted/objects/c1/b17981db0840109a820dae8674ee29684134ff)bin348 -> 348 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c1/b6a51bbb87c2f82b161412c3d20b59fc69b090 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/c1/b6a51bbb87c2f82b161412c3d20b59fc69b090)bin47 -> 47 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c3/5dee9bcc0e989f3b0c40f68372a9a51b6c4e6a (renamed from tests-clar/resources/merge-resolve/.gitted/objects/c3/5dee9bcc0e989f3b0c40f68372a9a51b6c4e6a)bin162 -> 162 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c3/d02eeef75183df7584d8d13ac03053910c1301 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/c3/d02eeef75183df7584d8d13ac03053910c1301)bin67 -> 67 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c4/efe31e9decccc8b2b4d3df9aac2cdfe2995618 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/c4/efe31e9decccc8b2b4d3df9aac2cdfe2995618)bin538 -> 538 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c5/0d0f1cb60b8b0fe1615ad20ace557e9d68d7bd (renamed from tests-clar/resources/merge-resolve/.gitted/objects/c5/0d0f1cb60b8b0fe1615ad20ace557e9d68d7bd)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c5/bbe550b9f09444bdddd3ecf3d97c0b42aa786c (renamed from tests-clar/resources/merge-resolve/.gitted/objects/c5/bbe550b9f09444bdddd3ecf3d97c0b42aa786c)bin269 -> 269 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c6/07fc30883e335def28cd686b51f6cfa02b06ec (renamed from tests-clar/resources/merge-resolve/.gitted/objects/c6/07fc30883e335def28cd686b51f6cfa02b06ec)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c6/92ecf62007c0ac9fb26e2aa884de2933de15ed (renamed from tests-clar/resources/merge-resolve/.gitted/objects/c6/92ecf62007c0ac9fb26e2aa884de2933de15ed)bin40 -> 40 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c8/f06f2e3bb2964174677e91f0abead0e43c9e5d (renamed from tests-clar/resources/merge-resolve/.gitted/objects/c8/f06f2e3bb2964174677e91f0abead0e43c9e5d)bin45 -> 45 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c9/174cef549ec94ecbc43ef03cdc775b4950becb (renamed from tests-clar/resources/merge-resolve/.gitted/objects/c9/174cef549ec94ecbc43ef03cdc775b4950becb)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c9/4b27e41064c521120627e07e2035cca1d24ffa (renamed from tests-clar/resources/merge-resolve/.gitted/objects/c9/4b27e41064c521120627e07e2035cca1d24ffa)bin162 -> 162 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ca/b2cf23998b40f1af2d9d9a756dc9e285a8df4b (renamed from tests-clar/resources/merge-resolve/.gitted/objects/ca/b2cf23998b40f1af2d9d9a756dc9e285a8df4b)bin40 -> 40 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/cb/491780d82e46dc88a065b965ab307a038f2bc2 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/cb/491780d82e46dc88a065b965ab307a038f2bc2)bin163 -> 163 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/cb/6693a788715b82440a54e0eacd19ba9f6ec559 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/cb/6693a788715b82440a54e0eacd19ba9f6ec559)bin41 -> 41 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/cc/3e3009134cb88014129fc8858d1101359e5e2f (renamed from tests-clar/resources/merge-resolve/.gitted/objects/cc/3e3009134cb88014129fc8858d1101359e5e2f)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ce/8860d49e3bea6fd745874a01b7c3e46da8cbc3 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/ce/8860d49e3bea6fd745874a01b7c3e46da8cbc3)bin48 -> 48 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ce/e656c392ad0557b3aae0fb411475c206e2926f (renamed from tests-clar/resources/merge-resolve/.gitted/objects/ce/e656c392ad0557b3aae0fb411475c206e2926f)bin32 -> 32 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/cf/8c5cc8a85a1ff5a4ba51e0bc7cf5665669924d (renamed from tests-clar/resources/merge-resolve/.gitted/objects/cf/8c5cc8a85a1ff5a4ba51e0bc7cf5665669924d)bin29 -> 29 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d0/7ec190c306ec690bac349e87d01c4358e49bb2 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/d0/7ec190c306ec690bac349e87d01c4358e49bb2)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d0/d4594e16f2e19107e3fa7ea63e7aaaff305ffb (renamed from tests-clar/resources/merge-resolve/.gitted/objects/d0/d4594e16f2e19107e3fa7ea63e7aaaff305ffb)bin51 -> 51 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d2/f8637f2eab2507a1e13cbc9df4729ec386627e (renamed from tests-clar/resources/merge-resolve/.gitted/objects/d2/f8637f2eab2507a1e13cbc9df4729ec386627e)bin268 -> 268 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d3/719a5ae8e4d92276b5313ce976f6ee5af2b436 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/d3/719a5ae8e4d92276b5313ce976f6ee5af2b436)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d3/7aa3bbfe1c0c49b909781251b956dbabe85f96 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/d3/7aa3bbfe1c0c49b909781251b956dbabe85f96)bin80 -> 80 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d3/7ad72a2052685fc6201c2af90103ad42d2079b (renamed from tests-clar/resources/merge-resolve/.gitted/objects/d3/7ad72a2052685fc6201c2af90103ad42d2079b)bin233 -> 233 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d4/207f77243500bec335ab477f9227fcdb1e271a (renamed from tests-clar/resources/merge-resolve/.gitted/objects/d4/207f77243500bec335ab477f9227fcdb1e271a)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46)bin38 -> 38 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d5/093787ef302b941b6aab081b99fb4880038bd8 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/d5/093787ef302b941b6aab081b99fb4880038bd8)bin30 -> 30 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d5/a61b0b4992a4f0caa887fa08b52431e727bb6f (renamed from tests-clar/resources/merge-resolve/.gitted/objects/d5/a61b0b4992a4f0caa887fa08b52431e727bb6f)bin81 -> 81 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d5/b6fc965c926a1bfc9ee456042b94088b5c5d21 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/d5/b6fc965c926a1bfc9ee456042b94088b5c5d21)bin319 -> 319 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d5/ec1152fe25e9fec00189eb00b3db71db24c218 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/d5/ec1152fe25e9fec00189eb00b3db71db24c218)bin24 -> 24 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d6/42b9770c66bba94a08df09b5efb095001f76d7 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/d6/42b9770c66bba94a08df09b5efb095001f76d7)bin539 -> 539 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d6/462fa3f5292857db599c54aea2bf91616230c5 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/d6/462fa3f5292857db599c54aea2bf91616230c5)bin48 -> 48 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d6/cf6c7741b3316826af1314042550c97ded1d50 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/d6/cf6c7741b3316826af1314042550c97ded1d50)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d8/74671ef5b20184836cb983bb273e5280384d0b (renamed from tests-clar/resources/merge-resolve/.gitted/objects/d8/74671ef5b20184836cb983bb273e5280384d0b)bin162 -> 162 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d8/fa77b6833082c1ea36b7828a582d4c43882450 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/d8/fa77b6833082c1ea36b7828a582d4c43882450)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d9/63979c237d08b6ba39062ee7bf64c7d34a27f8 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/d9/63979c237d08b6ba39062ee7bf64c7d34a27f8)bin48 -> 48 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/da/178208145ef585a1bd5ca5f4c9785d738df2cf (renamed from tests-clar/resources/merge-resolve/.gitted/objects/da/178208145ef585a1bd5ca5f4c9785d738df2cf)bin41 -> 41 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/db/6261a7c65c7fd678520c9bb6f2c47582ab9ed5 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/db/6261a7c65c7fd678520c9bb6f2c47582ab9ed5)bin624 -> 624 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/dd/9a570c3400e6e07bc4d7651d6e20b08926b3d9 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/dd/9a570c3400e6e07bc4d7651d6e20b08926b3d9)bin36 -> 36 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/de/872ee3618b894992e9d1e18ba2ebe256a112f9 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/de/872ee3618b894992e9d1e18ba2ebe256a112f9)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/df/e3f22baa1f6fce5447901c3086bae368de6bdd (renamed from tests-clar/resources/merge-resolve/.gitted/objects/df/e3f22baa1f6fce5447901c3086bae368de6bdd)bin40 -> 40 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e0/67f9361140f19391472df8a82d6610813c73b7 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/e0/67f9361140f19391472df8a82d6610813c73b7)bin53 -> 53 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e1/129b3cfb5898e0fbd606e0cb80b2755e50d161 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/e1/129b3cfb5898e0fbd606e0cb80b2755e50d161)bin92 -> 92 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e1/7ace1492648c9dc5701bad5c47af9d1b60c4e9 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/e1/7ace1492648c9dc5701bad5c47af9d1b60c4e9)bin264 -> 264 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e2/c6abbd55fed5ac71a5f2751e29b4a34726a595 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/e2/c6abbd55fed5ac71a5f2751e29b4a34726a595)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e3/1e7ad3ed298f24e383c4950f4671993ec078e4 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/e3/1e7ad3ed298f24e383c4950f4671993ec078e4)bin210 -> 210 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e3/76fbdd06ebf021c92724da9f26f44212734e3e (renamed from tests-clar/resources/merge-resolve/.gitted/objects/e3/76fbdd06ebf021c92724da9f26f44212734e3e)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e4/9f917b448d1340b31d76e54ba388268fd4c922 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/e4/9f917b448d1340b31d76e54ba388268fd4c922)bin36 -> 36 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e4/f618a2c3ed0669308735727df5ebf2447f022f (renamed from tests-clar/resources/merge-resolve/.gitted/objects/e4/f618a2c3ed0669308735727df5ebf2447f022f)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e6/5a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/e6/5a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812)bin160 -> 160 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e8/107f24196736b870a318a0e28f048e29f6feff (renamed from tests-clar/resources/merge-resolve/.gitted/objects/e8/107f24196736b870a318a0e28f048e29f6feff)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e9/2cdb7017dc6c5aed25cb4202c5b0104b872246 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/e9/2cdb7017dc6c5aed25cb4202c5b0104b872246)bin48 -> 48 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e9/ad6ec3e38364a3d07feda7c4197d4d845c53b5 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/e9/ad6ec3e38364a3d07feda7c4197d4d845c53b5)bin36 -> 36 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e9/f48beccc62d535739bfbdebe0a55ed716d8366 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/e9/f48beccc62d535739bfbdebe0a55ed716d8366)bin382 -> 382 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/eb/c09d0137cfb0c26697aed0109fb943ad906f3f (renamed from tests-clar/resources/merge-resolve/.gitted/objects/eb/c09d0137cfb0c26697aed0109fb943ad906f3f)bin166 -> 166 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ec/67e5a86adff465359f1c8f995e12dbdfa08d8a (renamed from tests-clar/resources/merge-resolve/.gitted/objects/ec/67e5a86adff465359f1c8f995e12dbdfa08d8a)bin166 -> 166 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ed/9523e62e453e50dd9be1606af19399b96e397a (renamed from tests-clar/resources/merge-resolve/.gitted/objects/ed/9523e62e453e50dd9be1606af19399b96e397a)bin87 -> 87 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ee/1d6f164893c1866a323f072eeed36b855656be (renamed from tests-clar/resources/merge-resolve/.gitted/objects/ee/1d6f164893c1866a323f072eeed36b855656be)bin291 -> 291 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf (renamed from tests-clar/resources/merge-resolve/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf)bin64 -> 64 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ee/a9286df54245fea72c5b557291470eb825f38f (renamed from tests-clar/resources/merge-resolve/.gitted/objects/ee/a9286df54245fea72c5b557291470eb825f38f)bin235 -> 235 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ef/58fdd8086c243bdc81f99e379acacfd21d32d6 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/ef/58fdd8086c243bdc81f99e379acacfd21d32d6)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ef/c499524cf105d5264ac7fc54e07e95764e8075 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/ef/c499524cf105d5264ac7fc54e07e95764e8075)bin32 -> 32 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ef/c9121fdedaf08ba180b53ebfbcf71bd488ed09 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/ef/c9121fdedaf08ba180b53ebfbcf71bd488ed09)bin160 -> 160 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f0/053b8060bb3f0be5cbcc3147a07ece26bf097e (renamed from tests-clar/resources/merge-resolve/.gitted/objects/f0/053b8060bb3f0be5cbcc3147a07ece26bf097e)bin163 -> 163 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f0/ce2b8e4986084d9b308fb72709e414c23eb5e6 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/f0/ce2b8e4986084d9b308fb72709e414c23eb5e6)bin125 -> 125 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f2/0c9063fa0bda9a397c96947a7b687305c49753 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/f2/0c9063fa0bda9a397c96947a7b687305c49753)bin29 -> 29 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f2/9e7fb590551095230c6149cbe72f2e9104a796 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/f2/9e7fb590551095230c6149cbe72f2e9104a796)bin41 -> 41 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f3/293571dcd708b6a3faf03818cd2844d000e198 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/f3/293571dcd708b6a3faf03818cd2844d000e198)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f3/f1164b68b57b1995b658a828320e6df3081fae (renamed from tests-clar/resources/merge-resolve/.gitted/objects/f3/f1164b68b57b1995b658a828320e6df3081fae)bin310 -> 310 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f4/15caf3fcad16304cb424b67f0ee6b12dc03aae (renamed from tests-clar/resources/merge-resolve/.gitted/objects/f4/15caf3fcad16304cb424b67f0ee6b12dc03aae)bin320 -> 320 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f4/8097eb340dc5a7cae55aabcf1faf4548aa821f (renamed from tests-clar/resources/merge-resolve/.gitted/objects/f4/8097eb340dc5a7cae55aabcf1faf4548aa821f)bin165 -> 165 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f5/504f36e6f4eb797a56fc5bac6c6c7f32969bf2 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/f5/504f36e6f4eb797a56fc5bac6c6c7f32969bf2)bin42 -> 42 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f5/b50c85a87cac64d7eb3254cdd1aec9564c0293 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/f5/b50c85a87cac64d7eb3254cdd1aec9564c0293)bin35 -> 35 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f5/f9dd5886a6ee20272be0aafc790cba43b31931 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/f5/f9dd5886a6ee20272be0aafc790cba43b31931)bin244 -> 244 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f6/be049e284c0f9dcbbc745543885be3502ea521 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/f6/be049e284c0f9dcbbc745543885be3502ea521)bin265 -> 265 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f7/c332bd4d4d4b777366cae4d24d1687477576bf (renamed from tests-clar/resources/merge-resolve/.gitted/objects/f7/c332bd4d4d4b777366cae4d24d1687477576bf)bin156 -> 156 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f8/958bdf4d365a84a9a178b1f5f35ff1dacbd884 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/f8/958bdf4d365a84a9a178b1f5f35ff1dacbd884)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/fa/c03f2c5139618d87d53614c153823bf1f31396 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/fa/c03f2c5139618d87d53614c153823bf1f31396)bin76 -> 76 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/fa/da9356aa3f74622327a3038ae9c6f92e1c5c1d (renamed from tests-clar/resources/merge-resolve/.gitted/objects/fa/da9356aa3f74622327a3038ae9c6f92e1c5c1d)bin168 -> 168 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/fb/738a106cfd097a4acb96ce132ecb1ad6c46b03 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/fb/738a106cfd097a4acb96ce132ecb1ad6c46b03)bin264 -> 264 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/fc/4c636d6515e9e261f9260dbcf3cc6eca97ea08 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/fc/4c636d6515e9e261f9260dbcf3cc6eca97ea08)bin29 -> 29 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/fc/7d7b805f7a9428574f4f802b2e34cd20ab9d99 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/fc/7d7b805f7a9428574f4f802b2e34cd20ab9d99)bin575 -> 575 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/fc/90237dc4891fa6c69827fc465632225e391618 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/fc/90237dc4891fa6c69827fc465632225e391618)bin163 -> 163 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/fd/57d2d6770fad8e9959124793a17f441b571e66 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/fd/57d2d6770fad8e9959124793a17f441b571e66)bin279 -> 279 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/fd/89f8cffb663ac89095a0f9764902e93ceaca6a (renamed from tests-clar/resources/merge-resolve/.gitted/objects/fd/89f8cffb663ac89095a0f9764902e93ceaca6a)0
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/fe/5407fc50a53aecb41d1a6e9ea7b612e581af87 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/fe/5407fc50a53aecb41d1a6e9ea7b612e581af87)bin48 -> 48 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ff/49d07869831ad761bbdaea026086f8789bcb00 (renamed from tests-clar/resources/merge-resolve/.gitted/objects/ff/49d07869831ad761bbdaea026086f8789bcb00)bin24 -> 24 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ff/b312248d607284c290023f9502eea010d34efd (renamed from tests-clar/resources/merge-resolve/.gitted/objects/ff/b312248d607284c290023f9502eea010d34efd)bin68 -> 68 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/branch (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/branch)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/df_ancestor (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/df_ancestor)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/df_side1 (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/df_side1)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/df_side2 (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/df_side2)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/ff_branch (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/ff_branch)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/master (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/master)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/octo1 (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/octo1)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/octo2 (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/octo2)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/octo3 (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/octo3)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/octo4 (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/octo4)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/octo5 (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/octo5)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/octo6 (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/octo6)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ancestor (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ancestor)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ours (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ours)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_theirs (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/rename_conflict_theirs)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/renames1 (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/renames1)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/renames2 (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/renames2)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-10 (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-10)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-10-branch (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-10-branch)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-11 (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-11)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-11-branch (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-11-branch)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-13 (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-13)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-13-branch (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-13-branch)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-14 (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-14)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-14-branch (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-14-branch)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-2alt (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-2alt)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-2alt-branch (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-2alt-branch)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-3alt (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-3alt)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-3alt-branch (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-3alt-branch)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-4 (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-4)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-4-branch (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-4-branch)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1 (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1-branch (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1-branch)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2 (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2-branch (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2-branch)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-6 (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-6)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-6-branch (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-6-branch)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-7 (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-7)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-7-branch (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-7-branch)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-8 (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-8)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-8-branch (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-8-branch)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-9 (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-9)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-9-branch (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-9-branch)0
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/unrelated (renamed from tests-clar/resources/merge-resolve/.gitted/refs/heads/unrelated)0
-rw-r--r--tests/resources/merge-resolve/added-in-master.txt (renamed from tests-clar/resources/merge-resolve/added-in-master.txt)0
-rw-r--r--tests/resources/merge-resolve/automergeable.txt (renamed from tests-clar/resources/merge-resolve/automergeable.txt)0
-rw-r--r--tests/resources/merge-resolve/changed-in-branch.txt (renamed from tests-clar/resources/merge-resolve/changed-in-branch.txt)0
-rw-r--r--tests/resources/merge-resolve/changed-in-master.txt (renamed from tests-clar/resources/merge-resolve/changed-in-master.txt)0
-rw-r--r--tests/resources/merge-resolve/conflicting.txt (renamed from tests-clar/resources/merge-resolve/conflicting.txt)0
-rw-r--r--tests/resources/merge-resolve/removed-in-branch.txt (renamed from tests-clar/resources/merge-resolve/removed-in-branch.txt)0
-rw-r--r--tests/resources/merge-resolve/unchanged.txt (renamed from tests-clar/resources/merge-resolve/unchanged.txt)0
-rw-r--r--tests/resources/mergedrepo/.gitted/COMMIT_EDITMSG (renamed from tests-clar/resources/mergedrepo/.gitted/COMMIT_EDITMSG)0
-rw-r--r--tests/resources/mergedrepo/.gitted/HEAD (renamed from tests-clar/resources/peeled.git/HEAD)0
-rw-r--r--tests/resources/mergedrepo/.gitted/MERGE_HEAD (renamed from tests-clar/resources/mergedrepo/.gitted/MERGE_HEAD)0
-rw-r--r--tests/resources/mergedrepo/.gitted/MERGE_MODE (renamed from tests-clar/resources/mergedrepo/.gitted/MERGE_MODE)0
-rw-r--r--tests/resources/mergedrepo/.gitted/MERGE_MSG (renamed from tests-clar/resources/mergedrepo/.gitted/MERGE_MSG)0
-rw-r--r--tests/resources/mergedrepo/.gitted/ORIG_HEAD (renamed from tests-clar/resources/mergedrepo/.gitted/ORIG_HEAD)0
-rw-r--r--tests/resources/mergedrepo/.gitted/config (renamed from tests-clar/resources/mergedrepo/.gitted/config)0
-rw-r--r--tests/resources/mergedrepo/.gitted/description (renamed from tests-clar/resources/push_src/.gitted/description)0
-rw-r--r--tests/resources/mergedrepo/.gitted/index (renamed from tests-clar/resources/mergedrepo/.gitted/index)bin842 -> 842 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/info/exclude (renamed from tests-clar/resources/mergedrepo/.gitted/info/exclude)0
-rw-r--r--tests/resources/mergedrepo/.gitted/logs/HEAD (renamed from tests-clar/resources/mergedrepo/.gitted/logs/HEAD)0
-rw-r--r--tests/resources/mergedrepo/.gitted/logs/refs/heads/branch (renamed from tests-clar/resources/mergedrepo/.gitted/logs/refs/heads/branch)0
-rw-r--r--tests/resources/mergedrepo/.gitted/logs/refs/heads/master (renamed from tests-clar/resources/mergedrepo/.gitted/logs/refs/heads/master)0
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/03/db1d37504ca0c4f7c26d7776b0e28bdea08712 (renamed from tests-clar/resources/mergedrepo/.gitted/objects/03/db1d37504ca0c4f7c26d7776b0e28bdea08712)bin141 -> 141 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/17/0efc1023e0ed2390150bb4469c8456b63e8f91 (renamed from tests-clar/resources/mergedrepo/.gitted/objects/17/0efc1023e0ed2390150bb4469c8456b63e8f91)bin141 -> 141 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/1f/85ca51b8e0aac893a621b61a9c2661d6aa6d81 (renamed from tests-clar/resources/mergedrepo/.gitted/objects/1f/85ca51b8e0aac893a621b61a9c2661d6aa6d81)bin34 -> 34 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/22/0bd62631c8cf7a83ef39c6b94595f00517211e (renamed from tests-clar/resources/mergedrepo/.gitted/objects/22/0bd62631c8cf7a83ef39c6b94595f00517211e)bin42 -> 42 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/32/d55d59265db86dd690f0a7fc563db43e2bc6a6 (renamed from tests-clar/resources/mergedrepo/.gitted/objects/32/d55d59265db86dd690f0a7fc563db43e2bc6a6)bin159 -> 159 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/38/e2d82b9065a237904af4b780b4d68da6950534 (renamed from tests-clar/resources/mergedrepo/.gitted/objects/38/e2d82b9065a237904af4b780b4d68da6950534)bin74 -> 74 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/3a/34580a35add43a4cf361e8e9a30060a905c876 (renamed from tests-clar/resources/mergedrepo/.gitted/objects/3a/34580a35add43a4cf361e8e9a30060a905c876)0
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/44/58b8bc9e72b6c8755ae456f60e9844d0538d8c (renamed from tests-clar/resources/mergedrepo/.gitted/objects/44/58b8bc9e72b6c8755ae456f60e9844d0538d8c)bin39 -> 39 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/47/8871385b9cd03908c5383acfd568bef023c6b3 (renamed from tests-clar/resources/mergedrepo/.gitted/objects/47/8871385b9cd03908c5383acfd568bef023c6b3)bin36 -> 36 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/51/6bd85f78061e09ccc714561d7b504672cb52da (renamed from tests-clar/resources/mergedrepo/.gitted/objects/51/6bd85f78061e09ccc714561d7b504672cb52da)bin36 -> 36 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/53/c1d95a01f4514b162066fc98564500c96c46ad (renamed from tests-clar/resources/mergedrepo/.gitted/objects/53/c1d95a01f4514b162066fc98564500c96c46ad)bin45 -> 45 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/6a/ea5f295304c36144ad6e9247a291b7f8112399 (renamed from tests-clar/resources/mergedrepo/.gitted/objects/6a/ea5f295304c36144ad6e9247a291b7f8112399)bin49 -> 49 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/70/68e30a7f0090ae32db35dfa1e4189d8780fcb8 (renamed from tests-clar/resources/mergedrepo/.gitted/objects/70/68e30a7f0090ae32db35dfa1e4189d8780fcb8)bin85 -> 85 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/75/938de1e367098b3e9a7b1ec3c4ac4548afffe4 (renamed from tests-clar/resources/mergedrepo/.gitted/objects/75/938de1e367098b3e9a7b1ec3c4ac4548afffe4)bin41 -> 41 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/7b/26923aaf452b1977eb08617c59475fb3f74b71 (renamed from tests-clar/resources/mergedrepo/.gitted/objects/7b/26923aaf452b1977eb08617c59475fb3f74b71)bin41 -> 41 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/84/af62840be1b1c47b778a8a249f3ff45155038c (renamed from tests-clar/resources/mergedrepo/.gitted/objects/84/af62840be1b1c47b778a8a249f3ff45155038c)bin40 -> 40 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/88/71f7a2ee3addfc4ba39fbd0783c8e738d04cda (renamed from tests-clar/resources/mergedrepo/.gitted/objects/88/71f7a2ee3addfc4ba39fbd0783c8e738d04cda)bin66 -> 66 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/88/7b153b165d32409c70163e0f734c090f12f673 (renamed from tests-clar/resources/mergedrepo/.gitted/objects/88/7b153b165d32409c70163e0f734c090f12f673)bin38 -> 38 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/8a/ad34cc83733590e74b93d0f7cf00375e2a735a (renamed from tests-clar/resources/mergedrepo/.gitted/objects/8a/ad34cc83733590e74b93d0f7cf00375e2a735a)bin78 -> 78 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/8b/3f43d2402825c200f835ca1762413e386fd0b2 (renamed from tests-clar/resources/mergedrepo/.gitted/objects/8b/3f43d2402825c200f835ca1762413e386fd0b2)bin57 -> 57 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/8b/72416545c7e761b64cecad4f1686eae4078aa8 (renamed from tests-clar/resources/mergedrepo/.gitted/objects/8b/72416545c7e761b64cecad4f1686eae4078aa8)bin38 -> 38 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/8f/3c06cff9a83757cec40c80bc9bf31a2582bde9 (renamed from tests-clar/resources/mergedrepo/.gitted/objects/8f/3c06cff9a83757cec40c80bc9bf31a2582bde9)bin39 -> 39 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/8f/fcc405925511824a2240a6d3686aa7f8c7ac50 (renamed from tests-clar/resources/mergedrepo/.gitted/objects/8f/fcc405925511824a2240a6d3686aa7f8c7ac50)bin140 -> 140 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/9a/05ccb4e0f948de03128e095f39dae6976751c5 (renamed from tests-clar/resources/mergedrepo/.gitted/objects/9a/05ccb4e0f948de03128e095f39dae6976751c5)0
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/9d/81f82fccc7dcd7de7a1ffead1815294c2e092c (renamed from tests-clar/resources/mergedrepo/.gitted/objects/9d/81f82fccc7dcd7de7a1ffead1815294c2e092c)bin36 -> 36 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/b7/cedb8ad4cbb22b6363f9578cbd749797f7ef0d (renamed from tests-clar/resources/mergedrepo/.gitted/objects/b7/cedb8ad4cbb22b6363f9578cbd749797f7ef0d)bin66 -> 66 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/d0/1885ea594926eae9ba5b54ad76692af5969f51 (renamed from tests-clar/resources/mergedrepo/.gitted/objects/d0/1885ea594926eae9ba5b54ad76692af5969f51)bin55 -> 55 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/e2/809157a7766f272e4cfe26e61ef2678a5357ff (renamed from tests-clar/resources/mergedrepo/.gitted/objects/e2/809157a7766f272e4cfe26e61ef2678a5357ff)0
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/e6/2cac5c88b9928f2695b934c70efa4285324478 (renamed from tests-clar/resources/mergedrepo/.gitted/objects/e6/2cac5c88b9928f2695b934c70efa4285324478)bin87 -> 87 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/f7/2784290c151092abf04ce6b875068547f70406 (renamed from tests-clar/resources/mergedrepo/.gitted/objects/f7/2784290c151092abf04ce6b875068547f70406)bin141 -> 141 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/refs/heads/branch (renamed from tests-clar/resources/mergedrepo/.gitted/refs/heads/branch)0
-rw-r--r--tests/resources/mergedrepo/.gitted/refs/heads/master (renamed from tests-clar/resources/mergedrepo/.gitted/refs/heads/master)0
-rw-r--r--tests/resources/mergedrepo/conflicts-one.txt (renamed from tests-clar/resources/mergedrepo/conflicts-one.txt)0
-rw-r--r--tests/resources/mergedrepo/conflicts-two.txt (renamed from tests-clar/resources/mergedrepo/conflicts-two.txt)0
-rw-r--r--tests/resources/mergedrepo/one.txt (renamed from tests-clar/resources/mergedrepo/one.txt)0
-rw-r--r--tests/resources/mergedrepo/two.txt (renamed from tests-clar/resources/mergedrepo/two.txt)0
-rw-r--r--tests/resources/partial-testrepo/.gitted/HEAD (renamed from tests-clar/resources/partial-testrepo/.gitted/HEAD)0
-rw-r--r--tests/resources/partial-testrepo/.gitted/config (renamed from tests-clar/resources/partial-testrepo/.gitted/config)0
-rw-r--r--tests/resources/partial-testrepo/.gitted/index (renamed from tests-clar/resources/partial-testrepo/.gitted/index)bin328 -> 328 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 (renamed from tests-clar/resources/partial-testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08)bin19 -> 19 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e (renamed from tests-clar/resources/partial-testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e)bin163 -> 163 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925 (renamed from tests-clar/resources/partial-testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925)bin147 -> 147 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 (renamed from tests-clar/resources/partial-testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7)bin51 -> 51 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd (renamed from tests-clar/resources/partial-testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd)bin119 -> 119 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 (renamed from tests-clar/resources/partial-testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057)bin18 -> 18 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 (renamed from tests-clar/resources/partial-testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045)0
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63 (renamed from tests-clar/resources/partial-testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63)bin50 -> 50 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 (renamed from tests-clar/resources/partial-testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644)0
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc (renamed from tests-clar/resources/partial-testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc)bin50 -> 50 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735 (renamed from tests-clar/resources/partial-testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735)bin19 -> 19 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a (renamed from tests-clar/resources/partial-testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a)bin119 -> 119 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d (renamed from tests-clar/resources/partial-testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d)bin82 -> 82 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479 (renamed from tests-clar/resources/partial-testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479)bin126 -> 126 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a (renamed from tests-clar/resources/partial-testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a)0
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f (renamed from tests-clar/resources/partial-testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f)0
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd (renamed from tests-clar/resources/partial-testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd)bin28 -> 28 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 (renamed from tests-clar/resources/partial-testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6)bin26 -> 26 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd (renamed from tests-clar/resources/partial-testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd)0
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9 (renamed from tests-clar/resources/partial-testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9)bin162 -> 162 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83 (renamed from tests-clar/resources/partial-testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83)bin147 -> 147 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 (renamed from tests-clar/resources/partial-testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1)bin82 -> 82 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92 (renamed from tests-clar/resources/partial-testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92)bin24 -> 24 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765 (renamed from tests-clar/resources/partial-testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765)bin82 -> 82 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/pack/.gitkeep (renamed from tests-clar/resources/partial-testrepo/.gitted/objects/pack/.gitkeep)0
-rw-r--r--tests/resources/partial-testrepo/.gitted/refs/heads/dir (renamed from tests-clar/resources/partial-testrepo/.gitted/refs/heads/dir)0
-rw-r--r--tests/resources/peeled.git/HEAD (renamed from tests-clar/resources/push_src/.gitted/HEAD)0
-rw-r--r--tests/resources/peeled.git/config (renamed from tests-clar/resources/peeled.git/config)0
-rw-r--r--tests/resources/peeled.git/objects/info/packs (renamed from tests-clar/resources/peeled.git/objects/info/packs)0
-rw-r--r--tests/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.idx (renamed from tests-clar/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.idx)bin1156 -> 1156 bytes
-rw-r--r--tests/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.pack (renamed from tests-clar/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.pack)bin274 -> 274 bytes
-rw-r--r--tests/resources/peeled.git/packed-refs (renamed from tests-clar/resources/peeled.git/packed-refs)0
-rw-r--r--tests/resources/peeled.git/refs/heads/master (renamed from tests-clar/resources/peeled.git/refs/heads/master)0
-rw-r--r--tests/resources/push.sh (renamed from tests-clar/resources/push.sh)0
-rw-r--r--tests/resources/push_src/.gitted/COMMIT_EDITMSG (renamed from tests-clar/resources/push_src/.gitted/COMMIT_EDITMSG)0
-rw-r--r--tests/resources/push_src/.gitted/HEAD (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/HEAD)0
-rw-r--r--tests/resources/push_src/.gitted/ORIG_HEAD (renamed from tests-clar/resources/push_src/.gitted/ORIG_HEAD)0
-rw-r--r--tests/resources/push_src/.gitted/config (renamed from tests-clar/resources/push_src/.gitted/config)0
-rw-r--r--tests/resources/push_src/.gitted/description (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/description)0
-rw-r--r--tests/resources/push_src/.gitted/index (renamed from tests-clar/resources/push_src/.gitted/index)bin470 -> 470 bytes
-rw-r--r--tests/resources/push_src/.gitted/info/exclude (renamed from tests-clar/resources/push_src/.gitted/info/exclude)0
-rw-r--r--tests/resources/push_src/.gitted/logs/HEAD (renamed from tests-clar/resources/push_src/.gitted/logs/HEAD)0
-rw-r--r--tests/resources/push_src/.gitted/logs/refs/heads/b1 (renamed from tests-clar/resources/push_src/.gitted/logs/refs/heads/b1)0
-rw-r--r--tests/resources/push_src/.gitted/logs/refs/heads/b2 (renamed from tests-clar/resources/push_src/.gitted/logs/refs/heads/b2)0
-rw-r--r--tests/resources/push_src/.gitted/logs/refs/heads/b3 (renamed from tests-clar/resources/push_src/.gitted/logs/refs/heads/b3)0
-rw-r--r--tests/resources/push_src/.gitted/logs/refs/heads/b4 (renamed from tests-clar/resources/push_src/.gitted/logs/refs/heads/b4)0
-rw-r--r--tests/resources/push_src/.gitted/logs/refs/heads/b5 (renamed from tests-clar/resources/push_src/.gitted/logs/refs/heads/b5)0
-rw-r--r--tests/resources/push_src/.gitted/logs/refs/heads/master (renamed from tests-clar/resources/push_src/.gitted/logs/refs/heads/master)0
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/HEAD (renamed from tests-clar/resources/renames/.gitted/HEAD)0
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/config (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/config)0
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/description (renamed from tests-clar/resources/renames/.gitted/description)0
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/index (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/index)bin256 -> 256 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/info/exclude (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/info/exclude)0
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/logs/HEAD (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/logs/HEAD)0
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/logs/refs/heads/master (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/logs/refs/heads/master)0
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/logs/refs/remotes/origin/HEAD (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/logs/refs/remotes/origin/HEAD)0
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/08/b041783f40edfe12bb406c9c9a8a040177c125 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/08/b041783f40edfe12bb406c9c9a8a040177c125)bin54 -> 54 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08)bin19 -> 19 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/18/1037049a54a1eb5fab404658a3a250b44335d7)bin51 -> 51 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/18/10dff58d8a660512d4832e740f692884338ccd (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/18/10dff58d8a660512d4832e740f692884338ccd)bin119 -> 119 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd)bin122 -> 122 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/1b/8cbad43e867676df601306689fe7c3def5e689 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/1b/8cbad43e867676df601306689fe7c3def5e689)bin51 -> 51 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b)bin21 -> 21 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10)bin168 -> 168 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d)bin21 -> 21 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7)bin44 -> 44 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54)bin50 -> 50 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc)bin23 -> 23 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057)bin18 -> 18 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045)0
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea)bin44 -> 44 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91)bin152 -> 152 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644)0
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a)bin119 -> 119 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af)0
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/7b/4384978d2493e851f9cca7858815fac9b10980 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/7b/4384978d2493e851f9cca7858815fac9b10980)bin145 -> 145 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/81/4889a078c031f61ed08ab5fa863aea9314344d (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/81/4889a078c031f61ed08ab5fa863aea9314344d)bin82 -> 82 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/84/96071c1b46c854b31185ea97743be6a8774479 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/84/96071c1b46c854b31185ea97743be6a8774479)bin126 -> 126 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe)0
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162)0
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4)bin50 -> 50 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2)0
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a)0
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f)0
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750)0
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd)bin28 -> 28 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6)bin26 -> 26 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/ae/90f12eea699729ed24555e40b9fd669da12a12 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/ae/90f12eea699729ed24555e40b9fd669da12a12)bin148 -> 148 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1)0
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593)bin80 -> 80 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644)0
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd)0
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659)bin149 -> 149 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f)bin21 -> 21 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759)bin79 -> 79 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391)bin15 -> 15 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0)bin21 -> 21 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3)bin103 -> 103 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1)bin82 -> 82 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/fa/49b077972391ad58037050f2a75f74e3671e92 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/fa/49b077972391ad58037050f2a75f74e3671e92)bin24 -> 24 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/fd/093bff70906175335656e6ce6ae05783708765 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/fd/093bff70906175335656e6ce6ae05783708765)bin82 -> 82 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09 (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09)bin152 -> 152 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx)bin46656 -> 46656 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack)bin386089 -> 386089 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx)bin1240 -> 1240 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack)bin491 -> 491 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx)bin1240 -> 1240 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack)bin498 -> 498 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/packed-refs (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/packed-refs)0
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/refs/heads/master (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/refs/heads/master)0
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/refs/remotes/origin/HEAD (renamed from tests-clar/resources/push_src/.gitted/modules/submodule/refs/remotes/origin/HEAD)0
-rw-r--r--tests/resources/push_src/.gitted/objects/08/585692ce06452da6f82ae66b90d98b55536fca (renamed from tests-clar/resources/push_src/.gitted/objects/08/585692ce06452da6f82ae66b90d98b55536fca)0
-rw-r--r--tests/resources/push_src/.gitted/objects/27/b7ce66243eb1403862d05f958c002312df173d (renamed from tests-clar/resources/push_src/.gitted/objects/27/b7ce66243eb1403862d05f958c002312df173d)0
-rw-r--r--tests/resources/push_src/.gitted/objects/28/905c54ea45a4bed8d7b90f51bd8bd81eec8840 (renamed from tests-clar/resources/push_src/.gitted/objects/28/905c54ea45a4bed8d7b90f51bd8bd81eec8840)bin109 -> 109 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/36/6226fb970ac0caa9d3f55967ab01334a548f60 (renamed from tests-clar/resources/push_src/.gitted/objects/36/6226fb970ac0caa9d3f55967ab01334a548f60)bin20 -> 20 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/36/f79b2846017d3761e0a02d0bccd573e0f90c572
-rw-r--r--tests/resources/push_src/.gitted/objects/5c/0bb3d1b9449d1cc69d7519fd05166f01840915 (renamed from tests-clar/resources/push_src/.gitted/objects/5c/0bb3d1b9449d1cc69d7519fd05166f01840915)bin128 -> 128 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/61/780798228d17af2d34fce4cfbdf35556832472 (renamed from tests-clar/resources/push_src/.gitted/objects/61/780798228d17af2d34fce4cfbdf35556832472)bin17 -> 17 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/64/fd55f9b6390202db5e5666fd1fb339089fba4d (renamed from tests-clar/resources/push_src/.gitted/objects/64/fd55f9b6390202db5e5666fd1fb339089fba4d)bin176 -> 176 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/78/981922613b2afb6025042ff6bd878ac1994e85 (renamed from tests-clar/resources/push_src/.gitted/objects/78/981922613b2afb6025042ff6bd878ac1994e85)bin17 -> 17 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/80/5c54522e614f29f70d2413a0470247d8b424ac (renamed from tests-clar/resources/push_src/.gitted/objects/80/5c54522e614f29f70d2413a0470247d8b424ac)bin131 -> 131 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/95/1bbbb90e2259a4c8950db78946784fb53fcbce (renamed from tests-clar/resources/push_src/.gitted/objects/95/1bbbb90e2259a4c8950db78946784fb53fcbce)0
-rw-r--r--tests/resources/push_src/.gitted/objects/a7/8705c3b2725f931d3ee05348d83cc26700f247 (renamed from tests-clar/resources/push_src/.gitted/objects/a7/8705c3b2725f931d3ee05348d83cc26700f247)bin166 -> 166 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/b4/83ae7ba66decee9aee971f501221dea84b1498 (renamed from tests-clar/resources/push_src/.gitted/objects/b4/83ae7ba66decee9aee971f501221dea84b1498)0
-rw-r--r--tests/resources/push_src/.gitted/objects/b4/e1f2b375a64c1ccd40c5ff6aa8bc96839ba4fd (renamed from tests-clar/resources/push_src/.gitted/objects/b4/e1f2b375a64c1ccd40c5ff6aa8bc96839ba4fd)bin148 -> 148 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/c1/0409136a7a75e025fa502a1b2fd7b62b77d279 (renamed from tests-clar/resources/push_src/.gitted/objects/c1/0409136a7a75e025fa502a1b2fd7b62b77d279)bin22 -> 22 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/cd/881f90f2933db2e4cc26b8c71fe6037ac7fe4c (renamed from tests-clar/resources/push_src/.gitted/objects/cd/881f90f2933db2e4cc26b8c71fe6037ac7fe4c)bin80 -> 80 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/d9/b63a88223d8367516f50bd131a5f7349b7f3e4 (renamed from tests-clar/resources/push_src/.gitted/objects/d9/b63a88223d8367516f50bd131a5f7349b7f3e4)0
-rw-r--r--tests/resources/push_src/.gitted/objects/dc/ab83249f6f9d1ed735d651352a80519339b591 (renamed from tests-clar/resources/push_src/.gitted/objects/dc/ab83249f6f9d1ed735d651352a80519339b591)bin80 -> 80 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/ee/a4f2705eeec2db3813f2430829afce99cd00b5bin0 -> 141 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/f7/8a3106c85fb549c65198b2a2086276c6174928 (renamed from tests-clar/resources/push_src/.gitted/objects/f7/8a3106c85fb549c65198b2a2086276c6174928)bin65 -> 65 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/f8/f7aefc2900a3d737cea9eee45729fd55761e1a (renamed from tests-clar/resources/push_src/.gitted/objects/f8/f7aefc2900a3d737cea9eee45729fd55761e1a)bin50 -> 50 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/fa/38b91f199934685819bea316186d8b008c52a2 (renamed from tests-clar/resources/push_src/.gitted/objects/fa/38b91f199934685819bea316186d8b008c52a2)0
-rw-r--r--tests/resources/push_src/.gitted/objects/ff/83aa4c5e5d28e3bcba2f5c6e2adc61286a4e5e (renamed from tests-clar/resources/push_src/.gitted/objects/ff/83aa4c5e5d28e3bcba2f5c6e2adc61286a4e5e)0
-rw-r--r--tests/resources/push_src/.gitted/objects/ff/fe95c7fd0a37fa2ed702f8f93b56b2196b3925 (renamed from tests-clar/resources/push_src/.gitted/objects/ff/fe95c7fd0a37fa2ed702f8f93b56b2196b3925)bin109 -> 109 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/pack/dummy (renamed from tests-clar/resources/push_src/.gitted/objects/pack/dummy)0
-rw-r--r--tests/resources/push_src/.gitted/refs/heads/b1 (renamed from tests-clar/resources/push_src/.gitted/refs/heads/b1)0
-rw-r--r--tests/resources/push_src/.gitted/refs/heads/b2 (renamed from tests-clar/resources/push_src/.gitted/refs/heads/b2)0
-rw-r--r--tests/resources/push_src/.gitted/refs/heads/b3 (renamed from tests-clar/resources/push_src/.gitted/refs/heads/b3)0
-rw-r--r--tests/resources/push_src/.gitted/refs/heads/b4 (renamed from tests-clar/resources/push_src/.gitted/refs/heads/b4)0
-rw-r--r--tests/resources/push_src/.gitted/refs/heads/b5 (renamed from tests-clar/resources/push_src/.gitted/refs/heads/b5)0
-rw-r--r--tests/resources/push_src/.gitted/refs/heads/b6 (renamed from tests-clar/resources/push_src/.gitted/refs/heads/b6)0
-rw-r--r--tests/resources/push_src/.gitted/refs/tags/tag-blob (renamed from tests-clar/resources/push_src/.gitted/refs/tags/tag-blob)0
-rw-r--r--tests/resources/push_src/.gitted/refs/tags/tag-commit (renamed from tests-clar/resources/push_src/.gitted/refs/tags/tag-commit)0
-rw-r--r--tests/resources/push_src/.gitted/refs/tags/tag-commit-two1
-rw-r--r--tests/resources/push_src/.gitted/refs/tags/tag-lightweight (renamed from tests-clar/resources/push_src/.gitted/refs/tags/tag-lightweight)0
-rw-r--r--tests/resources/push_src/.gitted/refs/tags/tag-tag1
-rw-r--r--tests/resources/push_src/.gitted/refs/tags/tag-tree (renamed from tests-clar/resources/push_src/.gitted/refs/tags/tag-tree)0
-rw-r--r--tests/resources/push_src/a.txt (renamed from tests-clar/resources/push_src/a.txt)0
-rw-r--r--tests/resources/push_src/fold/b.txt (renamed from tests-clar/resources/push_src/fold/b.txt)0
-rw-r--r--tests/resources/push_src/foldb.txt (renamed from tests-clar/resources/push_src/foldb.txt)0
-rw-r--r--tests/resources/push_src/gitmodules (renamed from tests-clar/resources/push_src/gitmodules)0
-rw-r--r--tests/resources/push_src/submodule/.gitted (renamed from tests-clar/resources/push_src/submodule/.gitted)0
-rw-r--r--tests/resources/push_src/submodule/README (renamed from tests-clar/resources/push_src/submodule/README)0
-rw-r--r--tests/resources/push_src/submodule/branch_file.txt (renamed from tests-clar/resources/push_src/submodule/branch_file.txt)0
-rw-r--r--tests/resources/push_src/submodule/new.txt (renamed from tests-clar/resources/push_src/submodule/new.txt)0
-rw-r--r--tests/resources/renames/.gitted/HEAD (renamed from tests-clar/resources/shallow.git/HEAD)0
-rw-r--r--tests/resources/renames/.gitted/config (renamed from tests-clar/resources/renames/.gitted/config)0
-rw-r--r--tests/resources/renames/.gitted/description (renamed from tests-clar/resources/status/.gitted/description)0
-rw-r--r--tests/resources/renames/.gitted/index (renamed from tests-clar/resources/renames/.gitted/index)bin352 -> 352 bytes
-rw-r--r--tests/resources/renames/.gitted/info/exclude (renamed from tests-clar/resources/renames/.gitted/info/exclude)0
-rw-r--r--tests/resources/renames/.gitted/logs/HEAD (renamed from tests-clar/resources/renames/.gitted/logs/HEAD)0
-rw-r--r--tests/resources/renames/.gitted/logs/refs/heads/master (renamed from tests-clar/resources/renames/.gitted/logs/refs/heads/master)0
-rw-r--r--tests/resources/renames/.gitted/objects/03/da7ad872536bd448da8d88eb7165338bf923a7 (renamed from tests-clar/resources/renames/.gitted/objects/03/da7ad872536bd448da8d88eb7165338bf923a7)bin90 -> 90 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/17/58bdd7c16a72ff7c17d8de0c957ced3ccad645 (renamed from tests-clar/resources/renames/.gitted/objects/17/58bdd7c16a72ff7c17d8de0c957ced3ccad645)0
-rw-r--r--tests/resources/renames/.gitted/objects/19/dd32dfb1520a64e5bbaae8dce6ef423dfa2f13 (renamed from tests-clar/resources/renames/.gitted/objects/19/dd32dfb1520a64e5bbaae8dce6ef423dfa2f13)0
-rw-r--r--tests/resources/renames/.gitted/objects/1c/068dee5790ef1580cfc4cd670915b48d790084 (renamed from tests-clar/resources/renames/.gitted/objects/1c/068dee5790ef1580cfc4cd670915b48d790084)bin176 -> 176 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/2b/c7f351d20b53f1c72c16c4b036e491c478c49a (renamed from tests-clar/resources/renames/.gitted/objects/2b/c7f351d20b53f1c72c16c4b036e491c478c49a)bin173 -> 173 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/31/e47d8c1fa36d7f8d537b96158e3f024de0a9f2 (renamed from tests-clar/resources/renames/.gitted/objects/31/e47d8c1fa36d7f8d537b96158e3f024de0a9f2)bin131 -> 131 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/35/92953ff3ea5e8ba700c429f3aefe33c8806754 (renamed from tests-clar/resources/renames/.gitted/objects/35/92953ff3ea5e8ba700c429f3aefe33c8806754)bin80 -> 80 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/36/020db6cdacaa93497f31edcd8f242ff9bc366d (renamed from tests-clar/resources/renames/.gitted/objects/36/020db6cdacaa93497f31edcd8f242ff9bc366d)bin431 -> 431 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/3c/04741dd4b96c4ae4b00ec0f6e10c816a30aad2 (renamed from tests-clar/resources/renames/.gitted/objects/3c/04741dd4b96c4ae4b00ec0f6e10c816a30aad2)bin159 -> 159 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/42/10ffd5c390b21dd5483375e75288dea9ede512 (renamed from tests-clar/resources/renames/.gitted/objects/42/10ffd5c390b21dd5483375e75288dea9ede512)bin1145 -> 1145 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/44/4a76ed3e45b183753f49376af30da8c3fe276a (renamed from tests-clar/resources/renames/.gitted/objects/44/4a76ed3e45b183753f49376af30da8c3fe276a)bin135 -> 135 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/47/184c1e7eb22abcbed2bf4ee87d4e38096f7951 (renamed from tests-clar/resources/renames/.gitted/objects/47/184c1e7eb22abcbed2bf4ee87d4e38096f7951)bin229 -> 229 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/4e/4cae3e7dd56ed74bff39526d0469e554432953 (renamed from tests-clar/resources/renames/.gitted/objects/4e/4cae3e7dd56ed74bff39526d0469e554432953)bin452 -> 452 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/50/e90273af7d826ff0a95865bcd3ba8412c447d9 (renamed from tests-clar/resources/renames/.gitted/objects/50/e90273af7d826ff0a95865bcd3ba8412c447d9)0
-rw-r--r--tests/resources/renames/.gitted/objects/5e/26abc56a5a84d89790f45416648899cbe13109 (renamed from tests-clar/resources/renames/.gitted/objects/5e/26abc56a5a84d89790f45416648899cbe13109)bin163 -> 163 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/61/8c6f2f8740bd6049b2fb9eb93fc15726462745 (renamed from tests-clar/resources/renames/.gitted/objects/61/8c6f2f8740bd6049b2fb9eb93fc15726462745)bin106 -> 106 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/66/311f5cfbe7836c27510a3ba2f43e282e2c8bba (renamed from tests-clar/resources/renames/.gitted/objects/66/311f5cfbe7836c27510a3ba2f43e282e2c8bba)bin1155 -> 1155 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/93/f538c45a57a87eb4c1e86f91c6ee41d66c7ba7 (renamed from tests-clar/resources/renames/.gitted/objects/93/f538c45a57a87eb4c1e86f91c6ee41d66c7ba7)bin229 -> 229 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/9a/69d960ae94b060f56c2a8702545e2bb1abb935 (renamed from tests-clar/resources/renames/.gitted/objects/9a/69d960ae94b060f56c2a8702545e2bb1abb935)bin464 -> 464 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/ad/0a8e55a104ac54a8a29ed4b84b49e76837a113 (renamed from tests-clar/resources/renames/.gitted/objects/ad/0a8e55a104ac54a8a29ed4b84b49e76837a113)bin415 -> 415 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/b9/25b224cc91f897001a9993fbce169fdaa8858f (renamed from tests-clar/resources/renames/.gitted/objects/b9/25b224cc91f897001a9993fbce169fdaa8858f)bin76 -> 76 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/d7/9b202de198fa61b02424b9e25e840dc75e1323 (renamed from tests-clar/resources/renames/.gitted/objects/d7/9b202de198fa61b02424b9e25e840dc75e1323)bin421 -> 421 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/ea/c43f5195a2cee53b7458d8dad16aedde10711b (renamed from tests-clar/resources/renames/.gitted/objects/ea/c43f5195a2cee53b7458d8dad16aedde10711b)bin118 -> 118 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/ea/f4a3e3bfe68585e90cada20736ace491cd100b (renamed from tests-clar/resources/renames/.gitted/objects/ea/f4a3e3bfe68585e90cada20736ace491cd100b)0
-rw-r--r--tests/resources/renames/.gitted/objects/f9/0d4fc20ecddf21eebe6a37e9225d244339d2b5 (renamed from tests-clar/resources/renames/.gitted/objects/f9/0d4fc20ecddf21eebe6a37e9225d244339d2b5)bin441 -> 441 bytes
-rw-r--r--tests/resources/renames/.gitted/refs/heads/master (renamed from tests-clar/resources/renames/.gitted/refs/heads/master)0
-rw-r--r--tests/resources/renames/.gitted/refs/heads/renames_similar (renamed from tests-clar/resources/renames/.gitted/refs/heads/renames_similar)0
-rw-r--r--tests/resources/renames/.gitted/refs/heads/renames_similar_two (renamed from tests-clar/resources/renames/.gitted/refs/heads/renames_similar_two)0
-rw-r--r--tests/resources/renames/ikeepsix.txt (renamed from tests-clar/resources/renames/ikeepsix.txt)0
-rw-r--r--tests/resources/renames/sixserving.txt (renamed from tests-clar/resources/renames/sixserving.txt)0
-rw-r--r--tests/resources/renames/songof7cities.txt (renamed from tests-clar/resources/renames/songof7cities.txt)0
-rw-r--r--tests/resources/renames/untimely.txt (renamed from tests-clar/resources/renames/untimely.txt)0
-rw-r--r--tests/resources/shallow.git/HEAD (renamed from tests-clar/resources/short_tag.git/HEAD)0
-rw-r--r--tests/resources/shallow.git/config (renamed from tests-clar/resources/shallow.git/config)0
-rw-r--r--tests/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.idx (renamed from tests-clar/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.idx)bin1324 -> 1324 bytes
-rw-r--r--tests/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.pack (renamed from tests-clar/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.pack)bin791 -> 791 bytes
-rw-r--r--tests/resources/shallow.git/packed-refs (renamed from tests-clar/resources/shallow.git/packed-refs)0
-rw-r--r--tests/resources/shallow.git/refs/.gitkeep (renamed from tests-clar/resources/shallow.git/refs/.gitkeep)0
-rw-r--r--tests/resources/shallow.git/shallow (renamed from tests-clar/resources/shallow.git/shallow)0
-rw-r--r--tests/resources/short_tag.git/HEAD (renamed from tests-clar/resources/status/.gitted/HEAD)0
-rw-r--r--tests/resources/short_tag.git/config5
-rw-r--r--tests/resources/short_tag.git/index (renamed from tests-clar/resources/short_tag.git/index)bin104 -> 104 bytes
-rw-r--r--tests/resources/short_tag.git/objects/4a/5ed60bafcf4638b7c8356bd4ce1916bfede93c (renamed from tests-clar/resources/short_tag.git/objects/4a/5ed60bafcf4638b7c8356bd4ce1916bfede93c)bin169 -> 169 bytes
-rw-r--r--tests/resources/short_tag.git/objects/4d/5fcadc293a348e88f777dc0920f11e7d71441c (renamed from tests-clar/resources/short_tag.git/objects/4d/5fcadc293a348e88f777dc0920f11e7d71441c)bin48 -> 48 bytes
-rw-r--r--tests/resources/short_tag.git/objects/5d/a7760512a953e3c7c4e47e4392c7a4338fb729 (renamed from tests-clar/resources/short_tag.git/objects/5d/a7760512a953e3c7c4e47e4392c7a4338fb729)0
-rw-r--r--tests/resources/short_tag.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 (renamed from tests-clar/resources/short_tag.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391)bin15 -> 15 bytes
-rw-r--r--tests/resources/short_tag.git/packed-refs (renamed from tests-clar/resources/short_tag.git/packed-refs)0
-rw-r--r--tests/resources/short_tag.git/refs/heads/master (renamed from tests-clar/resources/short_tag.git/refs/heads/master)0
-rw-r--r--tests/resources/status/.gitted/COMMIT_EDITMSG (renamed from tests-clar/resources/status/.gitted/COMMIT_EDITMSG)0
-rw-r--r--tests/resources/status/.gitted/HEAD (renamed from tests-clar/resources/submod2/.gitted/HEAD)0
-rw-r--r--tests/resources/status/.gitted/ORIG_HEAD (renamed from tests-clar/resources/status/.gitted/ORIG_HEAD)0
-rw-r--r--tests/resources/status/.gitted/config (renamed from tests-clar/resources/status/.gitted/config)0
-rw-r--r--tests/resources/status/.gitted/description (renamed from tests-clar/resources/submod2/.gitted/description)0
-rw-r--r--tests/resources/status/.gitted/index (renamed from tests-clar/resources/status/.gitted/index)bin1160 -> 1160 bytes
-rw-r--r--tests/resources/status/.gitted/info/exclude (renamed from tests-clar/resources/status/.gitted/info/exclude)0
-rw-r--r--tests/resources/status/.gitted/logs/HEAD (renamed from tests-clar/resources/status/.gitted/logs/HEAD)0
-rw-r--r--tests/resources/status/.gitted/logs/refs/heads/master (renamed from tests-clar/resources/status/.gitted/logs/refs/heads/master)0
-rw-r--r--tests/resources/status/.gitted/objects/00/17bd4ab1ec30440b17bae1680cff124ab5f1f6 (renamed from tests-clar/resources/status/.gitted/objects/00/17bd4ab1ec30440b17bae1680cff124ab5f1f6)0
-rw-r--r--tests/resources/status/.gitted/objects/06/1d42a44cacde5726057b67558821d95db96f19 (renamed from tests-clar/resources/status/.gitted/objects/06/1d42a44cacde5726057b67558821d95db96f19)bin44 -> 44 bytes
-rw-r--r--tests/resources/status/.gitted/objects/18/88c805345ba265b0ee9449b8877b6064592058 (renamed from tests-clar/resources/status/.gitted/objects/18/88c805345ba265b0ee9449b8877b6064592058)bin36 -> 36 bytes
-rw-r--r--tests/resources/status/.gitted/objects/19/d9cc8584ac2c7dcf57d2680375e80f099dc481 (renamed from tests-clar/resources/status/.gitted/objects/19/d9cc8584ac2c7dcf57d2680375e80f099dc481)bin22 -> 22 bytes
-rw-r--r--tests/resources/status/.gitted/objects/26/a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f (renamed from tests-clar/resources/status/.gitted/objects/26/a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f)0
-rw-r--r--tests/resources/status/.gitted/objects/32/504b727382542f9f089e24fddac5e78533e96c (renamed from tests-clar/resources/status/.gitted/objects/32/504b727382542f9f089e24fddac5e78533e96c)bin31 -> 31 bytes
-rw-r--r--tests/resources/status/.gitted/objects/37/fcb02ccc1a85d1941e7f106d52dc3702dcf0d0 (renamed from tests-clar/resources/status/.gitted/objects/37/fcb02ccc1a85d1941e7f106d52dc3702dcf0d0)bin331 -> 331 bytes
-rw-r--r--tests/resources/status/.gitted/objects/45/2e4244b5d083ddf0460acf1ecc74db9dcfa11a (renamed from tests-clar/resources/status/.gitted/objects/45/2e4244b5d083ddf0460acf1ecc74db9dcfa11a)bin30 -> 30 bytes
-rw-r--r--tests/resources/status/.gitted/objects/52/9a16e8e762d4acb7b9636ff540a00831f9155a (renamed from tests-clar/resources/status/.gitted/objects/52/9a16e8e762d4acb7b9636ff540a00831f9155a)bin32 -> 32 bytes
-rw-r--r--tests/resources/status/.gitted/objects/53/ace0d1cc1145a5f4fe4f78a186a60263190733 (renamed from tests-clar/resources/status/.gitted/objects/53/ace0d1cc1145a5f4fe4f78a186a60263190733)bin36 -> 36 bytes
-rw-r--r--tests/resources/status/.gitted/objects/54/52d32f1dd538eb0405e8a83cc185f79e25e80f (renamed from tests-clar/resources/status/.gitted/objects/54/52d32f1dd538eb0405e8a83cc185f79e25e80f)bin29 -> 29 bytes
-rw-r--r--tests/resources/status/.gitted/objects/55/d316c9ba708999f1918e9677d01dfcae69c6b9 (renamed from tests-clar/resources/status/.gitted/objects/55/d316c9ba708999f1918e9677d01dfcae69c6b9)bin33 -> 33 bytes
-rw-r--r--tests/resources/status/.gitted/objects/70/bd9443ada07063e7fbf0b3ff5c13f7494d89c2 (renamed from tests-clar/resources/status/.gitted/objects/70/bd9443ada07063e7fbf0b3ff5c13f7494d89c2)bin44 -> 44 bytes
-rw-r--r--tests/resources/status/.gitted/objects/73/5b6a258cd196a8f7c9428419b02c1dca93fd75 (renamed from tests-clar/resources/status/.gitted/objects/73/5b6a258cd196a8f7c9428419b02c1dca93fd75)bin160 -> 160 bytes
-rw-r--r--tests/resources/status/.gitted/objects/75/6e27627e67bfbc048d01ece5819c6de733d7ea (renamed from tests-clar/resources/status/.gitted/objects/75/6e27627e67bfbc048d01ece5819c6de733d7ea)bin301 -> 301 bytes
-rw-r--r--tests/resources/status/.gitted/objects/90/6ee7711f4f4928ddcb2a5f8fbc500deba0d2a8 (renamed from tests-clar/resources/status/.gitted/objects/90/6ee7711f4f4928ddcb2a5f8fbc500deba0d2a8)bin46 -> 46 bytes
-rw-r--r--tests/resources/status/.gitted/objects/90/b8c29d8ba39434d1c63e1b093daaa26e5bd972 (renamed from tests-clar/resources/status/.gitted/objects/90/b8c29d8ba39434d1c63e1b093daaa26e5bd972)bin41 -> 41 bytes
-rw-r--r--tests/resources/status/.gitted/objects/9c/2e02cdffa8d73e6c189074594477a6baf87960 (renamed from tests-clar/resources/status/.gitted/objects/9c/2e02cdffa8d73e6c189074594477a6baf87960)bin268 -> 268 bytes
-rw-r--r--tests/resources/status/.gitted/objects/a0/de7e0ac200c489c41c59dfa910154a70264e6e (renamed from tests-clar/resources/status/.gitted/objects/a0/de7e0ac200c489c41c59dfa910154a70264e6e)bin29 -> 29 bytes
-rw-r--r--tests/resources/status/.gitted/objects/a6/191982709b746d5650e93c2acf34ef74e11504 (renamed from tests-clar/resources/status/.gitted/objects/a6/191982709b746d5650e93c2acf34ef74e11504)bin37 -> 37 bytes
-rw-r--r--tests/resources/status/.gitted/objects/a6/be623522ce87a1d862128ac42672604f7b468b (renamed from tests-clar/resources/status/.gitted/objects/a6/be623522ce87a1d862128ac42672604f7b468b)bin46 -> 46 bytes
-rw-r--r--tests/resources/status/.gitted/objects/aa/27a641456848200fdb7f7c99ba36f8a0952877 (renamed from tests-clar/resources/status/.gitted/objects/aa/27a641456848200fdb7f7c99ba36f8a0952877)bin120 -> 120 bytes
-rw-r--r--tests/resources/status/.gitted/objects/da/bc8af9bd6e9f5bbe96a176f1a24baf3d1f8916 (renamed from tests-clar/resources/status/.gitted/objects/da/bc8af9bd6e9f5bbe96a176f1a24baf3d1f8916)bin42 -> 42 bytes
-rw-r--r--tests/resources/status/.gitted/objects/e8/ee89e15bbe9b20137715232387b3de5b28972e (renamed from tests-clar/resources/status/.gitted/objects/e8/ee89e15bbe9b20137715232387b3de5b28972e)bin38 -> 38 bytes
-rw-r--r--tests/resources/status/.gitted/objects/e9/b9107f290627c04d097733a10055af941f6bca (renamed from tests-clar/resources/status/.gitted/objects/e9/b9107f290627c04d097733a10055af941f6bca)bin37 -> 37 bytes
-rw-r--r--tests/resources/status/.gitted/objects/ed/062903b8f6f3dccb2fa81117ba6590944ef9bd (renamed from tests-clar/resources/status/.gitted/objects/ed/062903b8f6f3dccb2fa81117ba6590944ef9bd)bin42 -> 42 bytes
-rw-r--r--tests/resources/status/.gitted/refs/heads/master (renamed from tests-clar/resources/status/.gitted/refs/heads/master)0
-rw-r--r--tests/resources/status/current_file (renamed from tests-clar/resources/status/current_file)0
-rw-r--r--tests/resources/status/ignored_file (renamed from tests-clar/resources/status/ignored_file)0
-rw-r--r--tests/resources/status/modified_file (renamed from tests-clar/resources/status/modified_file)0
-rw-r--r--tests/resources/status/new_file (renamed from tests-clar/resources/status/new_file)0
-rw-r--r--tests/resources/status/staged_changes (renamed from tests-clar/resources/status/staged_changes)0
-rw-r--r--tests/resources/status/staged_changes_modified_file (renamed from tests-clar/resources/status/staged_changes_modified_file)0
-rw-r--r--tests/resources/status/staged_delete_modified_file (renamed from tests-clar/resources/status/staged_delete_modified_file)0
-rw-r--r--tests/resources/status/staged_new_file (renamed from tests-clar/resources/status/staged_new_file)0
-rw-r--r--tests/resources/status/staged_new_file_modified_file (renamed from tests-clar/resources/status/staged_new_file_modified_file)0
-rw-r--r--tests/resources/status/subdir.txt (renamed from tests-clar/resources/status/subdir.txt)0
-rw-r--r--tests/resources/status/subdir/current_file (renamed from tests-clar/resources/status/subdir/current_file)0
-rw-r--r--tests/resources/status/subdir/modified_file (renamed from tests-clar/resources/status/subdir/modified_file)0
-rw-r--r--tests/resources/status/subdir/new_file (renamed from tests-clar/resources/status/subdir/new_file)0
-rw-r--r--tests/resources/status/è¿™ (renamed from tests-clar/resources/status/è¿™)0
-rw-r--r--tests/resources/submod2/.gitted/HEAD (renamed from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/HEAD)0
-rw-r--r--tests/resources/submod2/.gitted/config (renamed from tests-clar/resources/submod2/.gitted/config)0
-rw-r--r--tests/resources/submod2/.gitted/description (renamed from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/description)0
-rw-r--r--tests/resources/submod2/.gitted/index (renamed from tests-clar/resources/submod2/.gitted/index)bin944 -> 944 bytes
-rw-r--r--tests/resources/submod2/.gitted/info/exclude (renamed from tests-clar/resources/submod2/.gitted/info/exclude)0
-rw-r--r--tests/resources/submod2/.gitted/logs/HEAD (renamed from tests-clar/resources/submod2/.gitted/logs/HEAD)0
-rw-r--r--tests/resources/submod2/.gitted/logs/refs/heads/master (renamed from tests-clar/resources/submod2/.gitted/logs/refs/heads/master)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/HEAD (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/HEAD)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/config (renamed from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/config)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/description (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/description)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/index (renamed from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/index)bin192 -> 192 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/info/exclude (renamed from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/info/exclude)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/HEAD (renamed from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/HEAD)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/heads/master (renamed from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/heads/master)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/remotes/origin/HEAD (renamed from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/remotes/origin/HEAD)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1)bin55 -> 55 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484)bin53 -> 53 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5)bin93 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/41/bd4bc3df978de695f67ace64c560913da11653 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/41/bd4bc3df978de695f67ace64c560913da11653)bin163 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/48/0095882d281ed676fe5b863569520e54a7d5c0)bin163 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/5e/4963595a9774b90524d35a807169049de8ccad (renamed from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/5e/4963595a9774b90524d35a807169049de8ccad)bin167 -> 167 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/6b/31c659545507c381e9cd34ec508f16c04e149e (renamed from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/6b/31c659545507c381e9cd34ec508f16c04e149e)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/73/ba924a80437097795ae839e66e187c55d3babf (renamed from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/73/ba924a80437097795ae839e66e187c55d3babf)bin93 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a (renamed from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/9efbdadaa4a582778d4584385495559ea0994b (renamed from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/9efbdadaa4a582778d4584385495559ea0994b)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e (renamed from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e)bin81 -> 81 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5)bin93 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/packed-refs (renamed from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/packed-refs)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/heads/master (renamed from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/heads/master)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/remotes/origin/HEAD (renamed from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/remotes/origin/HEAD)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/HEAD (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/HEAD)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/config (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/config)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/description (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/description)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/index (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/index)bin192 -> 192 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/info/exclude (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/info/exclude)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/logs/HEAD (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/logs/HEAD)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/heads/master (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/heads/master)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/remotes/origin/HEAD (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/remotes/origin/HEAD)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1)bin55 -> 55 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484)bin53 -> 53 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5)bin93 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/41/bd4bc3df978de695f67ace64c560913da11653 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/41/bd4bc3df978de695f67ace64c560913da11653)bin163 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0)bin163 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/5e/4963595a9774b90524d35a807169049de8ccad (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/5e/4963595a9774b90524d35a807169049de8ccad)bin167 -> 167 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/73/ba924a80437097795ae839e66e187c55d3babf (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/73/ba924a80437097795ae839e66e187c55d3babf)bin93 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e)bin81 -> 81 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5)bin93 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/packed-refs (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/packed-refs)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/refs/heads/master (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/refs/heads/master)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/refs/remotes/origin/HEAD (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/refs/remotes/origin/HEAD)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/COMMIT_EDITMSG (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/COMMIT_EDITMSG)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/HEAD (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/HEAD)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/config (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/config)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/description (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/description)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/index (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/index)bin192 -> 192 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/info/exclude (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/info/exclude)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/logs/HEAD (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/logs/HEAD)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/heads/master (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/heads/master)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/remotes/origin/HEAD (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/remotes/origin/HEAD)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1)bin55 -> 55 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484)bin53 -> 53 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5)bin93 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/3d/9386c507f6b093471a3e324085657a3c2b4247 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/3d/9386c507f6b093471a3e324085657a3c2b4247)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/41/bd4bc3df978de695f67ace64c560913da11653 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/41/bd4bc3df978de695f67ace64c560913da11653)bin163 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/48/0095882d281ed676fe5b863569520e54a7d5c0)bin163 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/5e/4963595a9774b90524d35a807169049de8ccad (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/5e/4963595a9774b90524d35a807169049de8ccad)bin167 -> 167 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/6b/31c659545507c381e9cd34ec508f16c04e149e (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/6b/31c659545507c381e9cd34ec508f16c04e149e)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/73/ba924a80437097795ae839e66e187c55d3babf (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/73/ba924a80437097795ae839e66e187c55d3babf)bin93 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/77/fb0ed3e58568d6ad362c78de08ab8649d76e29 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/77/fb0ed3e58568d6ad362c78de08ab8649d76e29)bin93 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/78/9efbdadaa4a582778d4584385495559ea0994b (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/78/9efbdadaa4a582778d4584385495559ea0994b)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e)bin81 -> 81 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/8e/b1e637ed9fc8e5454fa20d38f809091f9395f4 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/8e/b1e637ed9fc8e5454fa20d38f809091f9395f4)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5)bin93 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/packed-refs (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/packed-refs)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/refs/heads/master (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/refs/heads/master)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/refs/remotes/origin/HEAD (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/refs/remotes/origin/HEAD)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/HEAD (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/HEAD)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/config (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/config)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/description (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/description)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/index (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/index)bin192 -> 192 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/info/exclude (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/info/exclude)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/logs/HEAD (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/logs/HEAD)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/heads/master (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/heads/master)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/remotes/origin/HEAD (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/remotes/origin/HEAD)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1)bin55 -> 55 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484)bin53 -> 53 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5)bin93 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/41/bd4bc3df978de695f67ace64c560913da11653 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/41/bd4bc3df978de695f67ace64c560913da11653)bin163 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/48/0095882d281ed676fe5b863569520e54a7d5c0)bin163 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/5e/4963595a9774b90524d35a807169049de8ccad (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/5e/4963595a9774b90524d35a807169049de8ccad)bin167 -> 167 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/6b/31c659545507c381e9cd34ec508f16c04e149e (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/6b/31c659545507c381e9cd34ec508f16c04e149e)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/73/ba924a80437097795ae839e66e187c55d3babf (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/73/ba924a80437097795ae839e66e187c55d3babf)bin93 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/78/9efbdadaa4a582778d4584385495559ea0994b (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/78/9efbdadaa4a582778d4584385495559ea0994b)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e)bin81 -> 81 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/a0/2d31770687965547ab7a04cee199b29ee458d6 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/a0/2d31770687965547ab7a04cee199b29ee458d6)bin134 -> 134 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5)bin93 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/packed-refs (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/packed-refs)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/refs/heads/master (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/refs/heads/master)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/refs/remotes/origin/HEAD (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/refs/remotes/origin/HEAD)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/HEAD (renamed from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/HEAD)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/config (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/config)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/description (renamed from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/description)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/index (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/index)bin192 -> 192 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/info/exclude (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/info/exclude)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/HEAD (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/HEAD)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/heads/master (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/heads/master)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/remotes/origin/HEAD (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/remotes/origin/HEAD)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1)bin55 -> 55 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484)bin53 -> 53 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5)bin93 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/41/bd4bc3df978de695f67ace64c560913da11653 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/41/bd4bc3df978de695f67ace64c560913da11653)bin163 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0)bin163 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/5e/4963595a9774b90524d35a807169049de8ccad (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/5e/4963595a9774b90524d35a807169049de8ccad)bin167 -> 167 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/73/ba924a80437097795ae839e66e187c55d3babf (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/73/ba924a80437097795ae839e66e187c55d3babf)bin93 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e)bin81 -> 81 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5)bin93 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/packed-refs (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/packed-refs)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/heads/master (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/heads/master)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/remotes/origin/HEAD (renamed from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/remotes/origin/HEAD)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/HEAD (renamed from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/HEAD)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/config (renamed from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/config)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/description (renamed from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/description)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/index (renamed from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/index)bin192 -> 192 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/info/exclude (renamed from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/info/exclude)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/HEAD (renamed from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/logs/HEAD)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/heads/master (renamed from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/heads/master)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/remotes/origin/HEAD (renamed from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/remotes/origin/HEAD)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1)bin55 -> 55 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484)bin53 -> 53 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5)bin93 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/41/bd4bc3df978de695f67ace64c560913da11653 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/41/bd4bc3df978de695f67ace64c560913da11653)bin163 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/5e/4963595a9774b90524d35a807169049de8ccad (renamed from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/5e/4963595a9774b90524d35a807169049de8ccad)bin167 -> 167 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/6b/31c659545507c381e9cd34ec508f16c04e149e (renamed from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/6b/31c659545507c381e9cd34ec508f16c04e149e)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a (renamed from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e (renamed from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e)bin81 -> 81 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5)bin93 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/packed-refs (renamed from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/packed-refs)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/refs/heads/master (renamed from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/refs/heads/master)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/refs/remotes/origin/HEAD (renamed from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/refs/remotes/origin/HEAD)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/HEAD (renamed from tests-clar/resources/submod2/not-submodule/.gitted/HEAD)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/config (renamed from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/config)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/description (renamed from tests-clar/resources/submod2/not-submodule/.gitted/description)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/index (renamed from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/index)bin192 -> 192 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/info/exclude (renamed from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/info/exclude)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/logs/HEAD (renamed from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/logs/HEAD)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/heads/master (renamed from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/heads/master)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/remotes/origin/HEAD (renamed from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/remotes/origin/HEAD)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1)bin55 -> 55 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484)bin53 -> 53 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5)bin93 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/41/bd4bc3df978de695f67ace64c560913da11653 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/41/bd4bc3df978de695f67ace64c560913da11653)bin163 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/48/0095882d281ed676fe5b863569520e54a7d5c0)bin163 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/5e/4963595a9774b90524d35a807169049de8ccad (renamed from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/5e/4963595a9774b90524d35a807169049de8ccad)bin167 -> 167 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/6b/31c659545507c381e9cd34ec508f16c04e149e (renamed from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/6b/31c659545507c381e9cd34ec508f16c04e149e)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/73/ba924a80437097795ae839e66e187c55d3babf (renamed from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/73/ba924a80437097795ae839e66e187c55d3babf)bin93 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a (renamed from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/78/9efbdadaa4a582778d4584385495559ea0994b (renamed from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/78/9efbdadaa4a582778d4584385495559ea0994b)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e (renamed from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e)bin81 -> 81 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 (renamed from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5)bin93 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/packed-refs (renamed from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/packed-refs)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/refs/heads/master (renamed from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/refs/heads/master)0
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/refs/remotes/origin/HEAD (renamed from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/refs/remotes/origin/HEAD)0
-rw-r--r--tests/resources/submod2/.gitted/objects/09/460e5b6cbcb05a3e404593c32a3aa7221eca0e (renamed from tests-clar/resources/submod2/.gitted/objects/09/460e5b6cbcb05a3e404593c32a3aa7221eca0e)bin197 -> 197 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/14/fe9ccf104058df25e0a08361c4494e167ef243 (renamed from tests-clar/resources/submod2/.gitted/objects/14/fe9ccf104058df25e0a08361c4494e167ef243)0
-rw-r--r--tests/resources/submod2/.gitted/objects/22/ce3e0311dda73a5992d54a4a595518d3876ea7 (renamed from tests-clar/resources/submod2/.gitted/objects/22/ce3e0311dda73a5992d54a4a595518d3876ea7)0
-rw-r--r--tests/resources/submod2/.gitted/objects/25/5546424b0efb847b1bfc91dbf7348b277f8970 (renamed from tests-clar/resources/submod2/.gitted/objects/25/5546424b0efb847b1bfc91dbf7348b277f8970)bin157 -> 157 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/2a/30f1e6f94b20917005a21273f65b406d0f8bad (renamed from tests-clar/resources/submod2/.gitted/objects/2a/30f1e6f94b20917005a21273f65b406d0f8bad)bin144 -> 144 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/42/cfb95cd01bf9225b659b5ee3edcc78e8eeb478 (renamed from tests-clar/resources/submod2/.gitted/objects/42/cfb95cd01bf9225b659b5ee3edcc78e8eeb478)bin40 -> 40 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/57/958699c2dc394f81cfc76950e9c3ac3025c398 (renamed from tests-clar/resources/submod2/.gitted/objects/57/958699c2dc394f81cfc76950e9c3ac3025c398)bin136 -> 136 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/59/01da4f1c67756eeadc5121d206bec2431f253b (renamed from tests-clar/resources/submod2/.gitted/objects/59/01da4f1c67756eeadc5121d206bec2431f253b)0
-rw-r--r--tests/resources/submod2/.gitted/objects/60/7d96653d4d0a4f733107f7890c2e67b55b620d (renamed from tests-clar/resources/submod2/.gitted/objects/60/7d96653d4d0a4f733107f7890c2e67b55b620d)bin53 -> 53 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/74/84482eb8db738cafa696993664607500a3f2b9 (renamed from tests-clar/resources/submod2/.gitted/objects/74/84482eb8db738cafa696993664607500a3f2b9)bin173 -> 173 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/7b/a4c5c3561daa5ab1a86215cfb0587e96d404d6 (renamed from tests-clar/resources/submod2/.gitted/objects/7b/a4c5c3561daa5ab1a86215cfb0587e96d404d6)bin48 -> 48 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/87/3585b94bdeabccea991ea5e3ec1a277895b698 (renamed from tests-clar/resources/submod2/.gitted/objects/87/3585b94bdeabccea991ea5e3ec1a277895b698)bin137 -> 137 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/97/4cf7c73de336b0c4e019f918f3cee367d72e84 (renamed from tests-clar/resources/submod2/.gitted/objects/97/4cf7c73de336b0c4e019f918f3cee367d72e84)0
-rw-r--r--tests/resources/submod2/.gitted/objects/9d/bc299bc013ea253583b40bf327b5a6e4037b89 (renamed from tests-clar/resources/submod2/.gitted/objects/9d/bc299bc013ea253583b40bf327b5a6e4037b89)bin80 -> 80 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/a9/104bf89e911387244ef499413960ba472066d9 (renamed from tests-clar/resources/submod2/.gitted/objects/a9/104bf89e911387244ef499413960ba472066d9)bin165 -> 165 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/b6/14088620bbdc1d29549d223ceba0f4419fd4cb (renamed from tests-clar/resources/submod2/.gitted/objects/b6/14088620bbdc1d29549d223ceba0f4419fd4cb)bin110 -> 110 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/d4/07f19e50c1da1ff584beafe0d6dac7237c5d06 (renamed from tests-clar/resources/submod2/.gitted/objects/d4/07f19e50c1da1ff584beafe0d6dac7237c5d06)bin55 -> 55 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/d9/3e95571d92cceb5de28c205f1d5f3cc8b88bc8 (renamed from tests-clar/resources/submod2/.gitted/objects/d9/3e95571d92cceb5de28c205f1d5f3cc8b88bc8)0
-rw-r--r--tests/resources/submod2/.gitted/objects/e3/b83bf274ee065eee48734cf8c6dfaf5e81471c (renamed from tests-clar/resources/submod2/.gitted/objects/e3/b83bf274ee065eee48734cf8c6dfaf5e81471c)bin246 -> 246 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/f5/4414c25e6d24fe39f5c3f128d7c8a17bc23833 (renamed from tests-clar/resources/submod2/.gitted/objects/f5/4414c25e6d24fe39f5c3f128d7c8a17bc23833)0
-rw-r--r--tests/resources/submod2/.gitted/objects/f9/90a25a74d1a8281ce2ab018ea8df66795cd60b (renamed from tests-clar/resources/submod2/.gitted/objects/f9/90a25a74d1a8281ce2ab018ea8df66795cd60b)0
-rw-r--r--tests/resources/submod2/.gitted/refs/heads/master (renamed from tests-clar/resources/submod2/.gitted/refs/heads/master)0
-rw-r--r--tests/resources/submod2/README.txt (renamed from tests-clar/resources/submod2/README.txt)0
-rw-r--r--tests/resources/submod2/gitmodules (renamed from tests-clar/resources/submod2/gitmodules)0
-rw-r--r--tests/resources/submod2/just_a_dir/contents (renamed from tests-clar/resources/submod2/just_a_dir/contents)0
-rw-r--r--tests/resources/submod2/just_a_file (renamed from tests-clar/resources/submod2/just_a_file)0
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/HEAD (renamed from tests-clar/resources/submod2_target/.gitted/HEAD)0
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/config (renamed from tests-clar/resources/submod2/not-submodule/.gitted/config)0
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/description (renamed from tests-clar/resources/submod2_target/.gitted/description)0
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/index (renamed from tests-clar/resources/submod2/not-submodule/.gitted/index)bin112 -> 112 bytes
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/info/exclude (renamed from tests-clar/resources/submod2/not-submodule/.gitted/info/exclude)0
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/logs/HEAD (renamed from tests-clar/resources/submod2/not-submodule/.gitted/logs/HEAD)0
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/logs/refs/heads/master (renamed from tests-clar/resources/submod2/not-submodule/.gitted/logs/refs/heads/master)0
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/objects/68/e92c611b80ee1ed8f38314ff9577f0d15b2444 (renamed from tests-clar/resources/submod2/not-submodule/.gitted/objects/68/e92c611b80ee1ed8f38314ff9577f0d15b2444)bin132 -> 132 bytes
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/objects/71/ff9927d7c8a5639e062c38a7d35c433c424627 (renamed from tests-clar/resources/submod2/not-submodule/.gitted/objects/71/ff9927d7c8a5639e062c38a7d35c433c424627)bin52 -> 52 bytes
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/objects/f0/1d56b18efd353ef2bb93a4585d590a0847195e (renamed from tests-clar/resources/submod2/not-submodule/.gitted/objects/f0/1d56b18efd353ef2bb93a4585d590a0847195e)bin55 -> 55 bytes
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/refs/heads/master (renamed from tests-clar/resources/submod2/not-submodule/.gitted/refs/heads/master)0
-rw-r--r--tests/resources/submod2/not-submodule/README.txt (renamed from tests-clar/resources/submod2/not-submodule/README.txt)0
-rw-r--r--tests/resources/submod2/not/.gitted/notempty (renamed from tests-clar/resources/submod2/not/.gitted/notempty)0
-rw-r--r--tests/resources/submod2/not/README.txt (renamed from tests-clar/resources/submod2/not/README.txt)0
-rw-r--r--tests/resources/submod2/sm_added_and_uncommited/.gitted (renamed from tests-clar/resources/submod2/sm_added_and_uncommited/.gitted)0
-rw-r--r--tests/resources/submod2/sm_added_and_uncommited/README.txt (renamed from tests-clar/resources/submod2/sm_added_and_uncommited/README.txt)0
-rw-r--r--tests/resources/submod2/sm_added_and_uncommited/file_to_modify (renamed from tests-clar/resources/submod2/sm_added_and_uncommited/file_to_modify)0
-rw-r--r--tests/resources/submod2/sm_changed_file/.gitted (renamed from tests-clar/resources/submod2/sm_changed_file/.gitted)0
-rw-r--r--tests/resources/submod2/sm_changed_file/README.txt (renamed from tests-clar/resources/submod2/sm_changed_file/README.txt)0
-rw-r--r--tests/resources/submod2/sm_changed_file/file_to_modify (renamed from tests-clar/resources/submod2/sm_changed_file/file_to_modify)0
-rw-r--r--tests/resources/submod2/sm_changed_head/.gitted (renamed from tests-clar/resources/submod2/sm_changed_head/.gitted)0
-rw-r--r--tests/resources/submod2/sm_changed_head/README.txt (renamed from tests-clar/resources/submod2/sm_changed_head/README.txt)0
-rw-r--r--tests/resources/submod2/sm_changed_head/file_to_modify (renamed from tests-clar/resources/submod2/sm_changed_head/file_to_modify)0
-rw-r--r--tests/resources/submod2/sm_changed_index/.gitted (renamed from tests-clar/resources/submod2/sm_changed_index/.gitted)0
-rw-r--r--tests/resources/submod2/sm_changed_index/README.txt (renamed from tests-clar/resources/submod2/sm_changed_index/README.txt)0
-rw-r--r--tests/resources/submod2/sm_changed_index/file_to_modify (renamed from tests-clar/resources/submod2/sm_changed_index/file_to_modify)0
-rw-r--r--tests/resources/submod2/sm_changed_untracked_file/.gitted (renamed from tests-clar/resources/submod2/sm_changed_untracked_file/.gitted)0
-rw-r--r--tests/resources/submod2/sm_changed_untracked_file/README.txt (renamed from tests-clar/resources/submod2/sm_changed_untracked_file/README.txt)0
-rw-r--r--tests/resources/submod2/sm_changed_untracked_file/file_to_modify (renamed from tests-clar/resources/submod2/sm_changed_untracked_file/file_to_modify)0
-rw-r--r--tests/resources/submod2/sm_changed_untracked_file/i_am_untracked (renamed from tests-clar/resources/submod2/sm_changed_untracked_file/i_am_untracked)0
-rw-r--r--tests/resources/submod2/sm_missing_commits/.gitted (renamed from tests-clar/resources/submod2/sm_missing_commits/.gitted)0
-rw-r--r--tests/resources/submod2/sm_missing_commits/README.txt (renamed from tests-clar/resources/submod2/sm_missing_commits/README.txt)0
-rw-r--r--tests/resources/submod2/sm_missing_commits/file_to_modify (renamed from tests-clar/resources/submod2/sm_missing_commits/file_to_modify)0
-rw-r--r--tests/resources/submod2/sm_unchanged/.gitted (renamed from tests-clar/resources/submod2/sm_unchanged/.gitted)0
-rw-r--r--tests/resources/submod2/sm_unchanged/README.txt (renamed from tests-clar/resources/submod2/sm_unchanged/README.txt)0
-rw-r--r--tests/resources/submod2/sm_unchanged/file_to_modify (renamed from tests-clar/resources/submod2/sm_unchanged/file_to_modify)0
-rw-r--r--tests/resources/submod2_target/.gitted/HEAD (renamed from tests-clar/resources/submodules/.gitted/HEAD)0
-rw-r--r--tests/resources/submod2_target/.gitted/config (renamed from tests-clar/resources/submod2_target/.gitted/config)0
-rw-r--r--tests/resources/submod2_target/.gitted/description (renamed from tests-clar/resources/submodules/.gitted/description)0
-rw-r--r--tests/resources/submod2_target/.gitted/index (renamed from tests-clar/resources/submod2_target/.gitted/index)bin192 -> 192 bytes
-rw-r--r--tests/resources/submod2_target/.gitted/info/exclude (renamed from tests-clar/resources/submod2_target/.gitted/info/exclude)0
-rw-r--r--tests/resources/submod2_target/.gitted/logs/HEAD (renamed from tests-clar/resources/submod2_target/.gitted/logs/HEAD)0
-rw-r--r--tests/resources/submod2_target/.gitted/logs/refs/heads/master (renamed from tests-clar/resources/submod2_target/.gitted/logs/refs/heads/master)0
-rw-r--r--tests/resources/submod2_target/.gitted/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 (renamed from tests-clar/resources/submod2_target/.gitted/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1)bin55 -> 55 bytes
-rw-r--r--tests/resources/submod2_target/.gitted/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 (renamed from tests-clar/resources/submod2_target/.gitted/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484)bin53 -> 53 bytes
-rw-r--r--tests/resources/submod2_target/.gitted/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 (renamed from tests-clar/resources/submod2_target/.gitted/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5)bin93 -> 93 bytes
-rw-r--r--tests/resources/submod2_target/.gitted/objects/41/bd4bc3df978de695f67ace64c560913da11653 (renamed from tests-clar/resources/submod2_target/.gitted/objects/41/bd4bc3df978de695f67ace64c560913da11653)bin163 -> 163 bytes
-rw-r--r--tests/resources/submod2_target/.gitted/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 (renamed from tests-clar/resources/submod2_target/.gitted/objects/48/0095882d281ed676fe5b863569520e54a7d5c0)bin163 -> 163 bytes
-rw-r--r--tests/resources/submod2_target/.gitted/objects/5e/4963595a9774b90524d35a807169049de8ccad (renamed from tests-clar/resources/submod2_target/.gitted/objects/5e/4963595a9774b90524d35a807169049de8ccad)bin167 -> 167 bytes
-rw-r--r--tests/resources/submod2_target/.gitted/objects/6b/31c659545507c381e9cd34ec508f16c04e149e (renamed from tests-clar/resources/submod2_target/.gitted/objects/6b/31c659545507c381e9cd34ec508f16c04e149e)0
-rw-r--r--tests/resources/submod2_target/.gitted/objects/73/ba924a80437097795ae839e66e187c55d3babf (renamed from tests-clar/resources/submod2_target/.gitted/objects/73/ba924a80437097795ae839e66e187c55d3babf)bin93 -> 93 bytes
-rw-r--r--tests/resources/submod2_target/.gitted/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a (renamed from tests-clar/resources/submod2_target/.gitted/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a)0
-rw-r--r--tests/resources/submod2_target/.gitted/objects/78/9efbdadaa4a582778d4584385495559ea0994b (renamed from tests-clar/resources/submod2_target/.gitted/objects/78/9efbdadaa4a582778d4584385495559ea0994b)0
-rw-r--r--tests/resources/submod2_target/.gitted/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e (renamed from tests-clar/resources/submod2_target/.gitted/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e)bin81 -> 81 bytes
-rw-r--r--tests/resources/submod2_target/.gitted/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 (renamed from tests-clar/resources/submod2_target/.gitted/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5)bin93 -> 93 bytes
-rw-r--r--tests/resources/submod2_target/.gitted/refs/heads/master (renamed from tests-clar/resources/submod2_target/.gitted/refs/heads/master)0
-rw-r--r--tests/resources/submod2_target/README.txt (renamed from tests-clar/resources/submod2_target/README.txt)0
-rw-r--r--tests/resources/submod2_target/file_to_modify (renamed from tests-clar/resources/submod2_target/file_to_modify)0
-rw-r--r--tests/resources/submodules/.gitted/HEAD (renamed from tests-clar/resources/submodules/testrepo/.gitted/HEAD)0
-rw-r--r--tests/resources/submodules/.gitted/config (renamed from tests-clar/resources/submodules/.gitted/config)0
-rw-r--r--tests/resources/submodules/.gitted/description (renamed from tests-clar/resources/submodules/testrepo/.gitted/description)0
-rw-r--r--tests/resources/submodules/.gitted/index (renamed from tests-clar/resources/submodules/.gitted/index)bin408 -> 408 bytes
-rw-r--r--tests/resources/submodules/.gitted/info/exclude (renamed from tests-clar/resources/submodules/.gitted/info/exclude)0
-rw-r--r--tests/resources/submodules/.gitted/info/refs (renamed from tests-clar/resources/submodules/.gitted/info/refs)0
-rw-r--r--tests/resources/submodules/.gitted/logs/HEAD (renamed from tests-clar/resources/submodules/.gitted/logs/HEAD)0
-rw-r--r--tests/resources/submodules/.gitted/logs/refs/heads/master (renamed from tests-clar/resources/submodules/.gitted/logs/refs/heads/master)0
-rw-r--r--tests/resources/submodules/.gitted/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e (renamed from tests-clar/resources/submodules/.gitted/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e)0
-rw-r--r--tests/resources/submodules/.gitted/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357 (renamed from tests-clar/resources/submodules/.gitted/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357)bin97 -> 97 bytes
-rw-r--r--tests/resources/submodules/.gitted/objects/97/896810b3210244a62a82458b8e0819ecfc6850 (renamed from tests-clar/resources/submodules/.gitted/objects/97/896810b3210244a62a82458b8e0819ecfc6850)0
-rw-r--r--tests/resources/submodules/.gitted/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888 (renamed from tests-clar/resources/submodules/.gitted/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888)bin138 -> 138 bytes
-rw-r--r--tests/resources/submodules/.gitted/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818 (renamed from tests-clar/resources/submodules/.gitted/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818)bin21 -> 21 bytes
-rw-r--r--tests/resources/submodules/.gitted/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae (renamed from tests-clar/resources/submodules/.gitted/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae)bin120 -> 120 bytes
-rw-r--r--tests/resources/submodules/.gitted/objects/info/packs (renamed from tests-clar/resources/submodules/.gitted/objects/info/packs)0
-rw-r--r--tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idx (renamed from tests-clar/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idx)bin1156 -> 1156 bytes
-rw-r--r--tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack (renamed from tests-clar/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack)bin228 -> 228 bytes
-rw-r--r--tests/resources/submodules/.gitted/packed-refs (renamed from tests-clar/resources/submodules/.gitted/packed-refs)0
-rw-r--r--tests/resources/submodules/.gitted/refs/heads/master (renamed from tests-clar/resources/submodules/.gitted/refs/heads/master)0
-rw-r--r--tests/resources/submodules/added (renamed from tests-clar/resources/submodules/added)0
-rw-r--r--tests/resources/submodules/gitmodules6
-rw-r--r--tests/resources/submodules/ignored (renamed from tests-clar/resources/submodules/ignored)0
-rw-r--r--tests/resources/submodules/modified (renamed from tests-clar/resources/submodules/modified)0
-rw-r--r--tests/resources/submodules/testrepo/.gitted/HEAD (renamed from tests-clar/resources/testrepo.git/HEAD)0
-rw-r--r--tests/resources/submodules/testrepo/.gitted/config (renamed from tests-clar/resources/submodules/testrepo/.gitted/config)0
-rw-r--r--tests/resources/submodules/testrepo/.gitted/description (renamed from tests-clar/resources/testrepo2/.gitted/description)0
-rw-r--r--tests/resources/submodules/testrepo/.gitted/index (renamed from tests-clar/resources/submodules/testrepo/.gitted/index)bin256 -> 256 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/info/exclude (renamed from tests-clar/resources/submodules/testrepo/.gitted/info/exclude)0
-rw-r--r--tests/resources/submodules/testrepo/.gitted/logs/HEAD (renamed from tests-clar/resources/submodules/testrepo/.gitted/logs/HEAD)0
-rw-r--r--tests/resources/submodules/testrepo/.gitted/logs/refs/heads/master (renamed from tests-clar/resources/submodules/testrepo/.gitted/logs/refs/heads/master)0
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08)bin19 -> 19 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7)bin51 -> 51 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd)bin119 -> 119 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b)bin21 -> 21 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d)bin21 -> 21 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54 (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54)bin50 -> 50 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc)bin23 -> 23 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057)bin18 -> 18 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045)0
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644)0
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a)bin119 -> 119 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af)0
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980 (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980)bin145 -> 145 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d)bin82 -> 82 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479 (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479)bin126 -> 126 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162 (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162)0
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4 (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4)bin50 -> 50 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a)0
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f)0
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750 (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750)0
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd)bin28 -> 28 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6)bin26 -> 26 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12 (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12)bin148 -> 148 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1)0
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593 (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593)bin80 -> 80 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644)0
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd)0
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f)bin21 -> 21 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391)bin15 -> 15 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0 (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0)bin21 -> 21 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3 (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3)bin103 -> 103 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1)bin82 -> 82 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92 (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92)bin24 -> 24 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765 (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765)bin82 -> 82 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx)bin46656 -> 46656 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack)bin386089 -> 386089 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx)bin1240 -> 1240 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack)bin491 -> 491 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx)bin1240 -> 1240 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack (renamed from tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack)bin498 -> 498 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/packed-refs (renamed from tests-clar/resources/submodules/testrepo/.gitted/packed-refs)0
-rw-r--r--tests/resources/submodules/testrepo/.gitted/refs/heads/master (renamed from tests-clar/resources/submodules/testrepo/.gitted/refs/heads/master)0
-rw-r--r--tests/resources/submodules/testrepo/.gitted/refs/remotes/origin/HEAD (renamed from tests-clar/resources/submodules/testrepo/.gitted/refs/remotes/origin/HEAD)0
-rw-r--r--tests/resources/submodules/testrepo/README (renamed from tests-clar/resources/submodules/testrepo/README)0
-rw-r--r--tests/resources/submodules/testrepo/branch_file.txt (renamed from tests-clar/resources/submodules/testrepo/branch_file.txt)0
-rw-r--r--tests/resources/submodules/testrepo/new.txt (renamed from tests-clar/resources/submodules/testrepo/new.txt)0
-rw-r--r--tests/resources/submodules/unmodified (renamed from tests-clar/resources/submodules/unmodified)0
-rw-r--r--tests/resources/submodules/untracked (renamed from tests-clar/resources/submodules/untracked)0
-rw-r--r--tests/resources/template/branches/.gitignore (renamed from tests-clar/resources/template/branches/.gitignore)0
-rw-r--r--tests/resources/template/description (renamed from tests-clar/resources/template/description)0
l---------tests/resources/template/hooks/link.sample (renamed from tests-clar/resources/template/hooks/link.sample)0
-rwxr-xr-xtests/resources/template/hooks/update.sample (renamed from tests-clar/resources/template/hooks/update.sample)0
-rw-r--r--tests/resources/template/info/exclude (renamed from tests-clar/resources/template/info/exclude)0
-rw-r--r--tests/resources/testrepo.git/FETCH_HEAD (renamed from tests-clar/resources/testrepo.git/FETCH_HEAD)0
-rw-r--r--tests/resources/testrepo.git/HEAD (renamed from tests-clar/resources/testrepo/.gitted/HEAD)0
-rw-r--r--tests/resources/testrepo.git/HEAD_TRACKER (renamed from tests-clar/resources/testrepo.git/HEAD_TRACKER)0
-rw-r--r--tests/resources/testrepo.git/config40
-rw-r--r--tests/resources/testrepo.git/index (renamed from tests-clar/resources/testrepo.git/index)bin10041 -> 10041 bytes
-rw-r--r--tests/resources/testrepo.git/logs/HEAD (renamed from tests-clar/resources/testrepo.git/logs/HEAD)0
-rw-r--r--tests/resources/testrepo.git/logs/refs/heads/br2 (renamed from tests-clar/resources/testrepo.git/logs/refs/heads/br2)0
-rw-r--r--tests/resources/testrepo.git/logs/refs/heads/master (renamed from tests-clar/resources/testrepo.git/logs/refs/heads/master)0
-rw-r--r--tests/resources/testrepo.git/logs/refs/heads/not-good (renamed from tests-clar/resources/testrepo.git/logs/refs/heads/not-good)0
-rw-r--r--tests/resources/testrepo.git/logs/refs/remotes/origin/HEAD (renamed from tests-clar/resources/testrepo.git/logs/refs/remotes/origin/HEAD)0
-rw-r--r--tests/resources/testrepo.git/logs/refs/remotes/test/master (renamed from tests-clar/resources/testrepo.git/logs/refs/remotes/test/master)0
-rw-r--r--tests/resources/testrepo.git/objects/08/b041783f40edfe12bb406c9c9a8a040177c125 (renamed from tests-clar/resources/testrepo.git/objects/08/b041783f40edfe12bb406c9c9a8a040177c125)bin54 -> 54 bytes
-rw-r--r--tests/resources/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 (renamed from tests-clar/resources/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08)bin19 -> 19 bytes
-rw-r--r--tests/resources/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 (renamed from tests-clar/resources/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7)bin51 -> 51 bytes
-rw-r--r--tests/resources/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccd (renamed from tests-clar/resources/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccd)bin119 -> 119 bytes
-rw-r--r--tests/resources/testrepo.git/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd (renamed from tests-clar/resources/testrepo.git/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd)bin122 -> 122 bytes
-rw-r--r--tests/resources/testrepo.git/objects/1b/8cbad43e867676df601306689fe7c3def5e689 (renamed from tests-clar/resources/testrepo.git/objects/1b/8cbad43e867676df601306689fe7c3def5e689)bin51 -> 51 bytes
-rw-r--r--tests/resources/testrepo.git/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b (renamed from tests-clar/resources/testrepo.git/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b)bin21 -> 21 bytes
-rw-r--r--tests/resources/testrepo.git/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10 (renamed from tests-clar/resources/testrepo.git/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10)bin168 -> 168 bytes
-rw-r--r--tests/resources/testrepo.git/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d (renamed from tests-clar/resources/testrepo.git/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d)bin21 -> 21 bytes
-rw-r--r--tests/resources/testrepo.git/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7 (renamed from tests-clar/resources/testrepo.git/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7)bin44 -> 44 bytes
-rw-r--r--tests/resources/testrepo.git/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54 (renamed from tests-clar/resources/testrepo.git/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54)bin50 -> 50 bytes
-rw-r--r--tests/resources/testrepo.git/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc (renamed from tests-clar/resources/testrepo.git/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc)bin23 -> 23 bytes
-rw-r--r--tests/resources/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 (renamed from tests-clar/resources/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057)bin18 -> 18 bytes
-rw-r--r--tests/resources/testrepo.git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 (renamed from tests-clar/resources/testrepo.git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045)0
-rw-r--r--tests/resources/testrepo.git/objects/4a/23e2e65ad4e31c4c9db7dc746650bfad082679 (renamed from tests-clar/resources/testrepo.git/objects/4a/23e2e65ad4e31c4c9db7dc746650bfad082679)bin83 -> 83 bytes
-rw-r--r--tests/resources/testrepo.git/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea (renamed from tests-clar/resources/testrepo.git/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea)bin44 -> 44 bytes
-rw-r--r--tests/resources/testrepo.git/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91 (renamed from tests-clar/resources/testrepo.git/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91)bin152 -> 152 bytes
-rw-r--r--tests/resources/testrepo.git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 (renamed from tests-clar/resources/testrepo.git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644)0
-rw-r--r--tests/resources/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a (renamed from tests-clar/resources/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a)bin119 -> 119 bytes
-rw-r--r--tests/resources/testrepo.git/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af (renamed from tests-clar/resources/testrepo.git/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af)0
-rw-r--r--tests/resources/testrepo.git/objects/7b/4384978d2493e851f9cca7858815fac9b10980 (renamed from tests-clar/resources/testrepo.git/objects/7b/4384978d2493e851f9cca7858815fac9b10980)bin145 -> 145 bytes
-rw-r--r--tests/resources/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d (renamed from tests-clar/resources/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d)bin82 -> 82 bytes
-rw-r--r--tests/resources/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479 (renamed from tests-clar/resources/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479)bin126 -> 126 bytes
-rw-r--r--tests/resources/testrepo.git/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe (renamed from tests-clar/resources/testrepo.git/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe)0
-rw-r--r--tests/resources/testrepo.git/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162 (renamed from tests-clar/resources/testrepo.git/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162)0
-rw-r--r--tests/resources/testrepo.git/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4 (renamed from tests-clar/resources/testrepo.git/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4)bin50 -> 50 bytes
-rw-r--r--tests/resources/testrepo.git/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2 (renamed from tests-clar/resources/testrepo.git/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2)0
-rw-r--r--tests/resources/testrepo.git/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a (renamed from tests-clar/resources/testrepo.git/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a)0
-rw-r--r--tests/resources/testrepo.git/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f (renamed from tests-clar/resources/testrepo.git/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f)0
-rw-r--r--tests/resources/testrepo.git/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750 (renamed from tests-clar/resources/testrepo.git/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750)0
-rw-r--r--tests/resources/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd (renamed from tests-clar/resources/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd)bin28 -> 28 bytes
-rw-r--r--tests/resources/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 (renamed from tests-clar/resources/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6)bin26 -> 26 bytes
-rw-r--r--tests/resources/testrepo.git/objects/ae/90f12eea699729ed24555e40b9fd669da12a12 (renamed from tests-clar/resources/testrepo.git/objects/ae/90f12eea699729ed24555e40b9fd669da12a12)bin148 -> 148 bytes
-rw-r--r--tests/resources/testrepo.git/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 (renamed from tests-clar/resources/testrepo.git/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1)0
-rw-r--r--tests/resources/testrepo.git/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593 (renamed from tests-clar/resources/testrepo.git/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593)bin80 -> 80 bytes
-rw-r--r--tests/resources/testrepo.git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 (renamed from tests-clar/resources/testrepo.git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644)0
-rw-r--r--tests/resources/testrepo.git/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd (renamed from tests-clar/resources/testrepo.git/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd)0
-rw-r--r--tests/resources/testrepo.git/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659 (renamed from tests-clar/resources/testrepo.git/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659)bin149 -> 149 bytes
-rw-r--r--tests/resources/testrepo.git/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f (renamed from tests-clar/resources/testrepo.git/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f)bin21 -> 21 bytes
-rw-r--r--tests/resources/testrepo.git/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759 (renamed from tests-clar/resources/testrepo.git/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759)bin79 -> 79 bytes
-rw-r--r--tests/resources/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 (renamed from tests-clar/resources/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391)bin15 -> 15 bytes
-rw-r--r--tests/resources/testrepo.git/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0 (renamed from tests-clar/resources/testrepo.git/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0)bin21 -> 21 bytes
-rw-r--r--tests/resources/testrepo.git/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3 (renamed from tests-clar/resources/testrepo.git/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3)bin103 -> 103 bytes
-rw-r--r--tests/resources/testrepo.git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 (renamed from tests-clar/resources/testrepo.git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1)bin82 -> 82 bytes
-rw-r--r--tests/resources/testrepo.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92 (renamed from tests-clar/resources/testrepo.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92)bin24 -> 24 bytes
-rw-r--r--tests/resources/testrepo.git/objects/fd/093bff70906175335656e6ce6ae05783708765 (renamed from tests-clar/resources/testrepo.git/objects/fd/093bff70906175335656e6ce6ae05783708765)bin82 -> 82 bytes
-rw-r--r--tests/resources/testrepo.git/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09 (renamed from tests-clar/resources/testrepo.git/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09)bin152 -> 152 bytes
-rw-r--r--tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx (renamed from tests-clar/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx)bin46656 -> 46656 bytes
-rw-r--r--tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack (renamed from tests-clar/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack)bin386089 -> 386089 bytes
-rw-r--r--tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx (renamed from tests-clar/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx)bin1240 -> 1240 bytes
-rw-r--r--tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack (renamed from tests-clar/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack)bin491 -> 491 bytes
-rw-r--r--tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx (renamed from tests-clar/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx)bin1240 -> 1240 bytes
-rw-r--r--tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack (renamed from tests-clar/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack)bin498 -> 498 bytes
-rw-r--r--tests/resources/testrepo.git/packed-refs (renamed from tests-clar/resources/testrepo.git/packed-refs)0
-rw-r--r--tests/resources/testrepo.git/refs/heads/br2 (renamed from tests-clar/resources/testrepo.git/refs/heads/br2)0
-rw-r--r--tests/resources/testrepo.git/refs/heads/cannot-fetch (renamed from tests-clar/resources/testrepo.git/refs/heads/cannot-fetch)0
-rw-r--r--tests/resources/testrepo.git/refs/heads/chomped (renamed from tests-clar/resources/testrepo.git/refs/heads/chomped)0
-rw-r--r--tests/resources/testrepo.git/refs/heads/haacked (renamed from tests-clar/resources/testrepo.git/refs/heads/haacked)0
-rw-r--r--tests/resources/testrepo.git/refs/heads/master (renamed from tests-clar/resources/testrepo.git/refs/heads/master)0
-rw-r--r--tests/resources/testrepo.git/refs/heads/not-good (renamed from tests-clar/resources/testrepo.git/refs/heads/not-good)0
-rw-r--r--tests/resources/testrepo.git/refs/heads/packed-test (renamed from tests-clar/resources/testrepo.git/refs/heads/packed-test)0
-rw-r--r--tests/resources/testrepo.git/refs/heads/subtrees (renamed from tests-clar/resources/testrepo.git/refs/heads/subtrees)0
-rw-r--r--tests/resources/testrepo.git/refs/heads/test (renamed from tests-clar/resources/testrepo.git/refs/heads/test)0
-rw-r--r--tests/resources/testrepo.git/refs/heads/track-local (renamed from tests-clar/resources/testrepo.git/refs/heads/track-local)0
-rw-r--r--tests/resources/testrepo.git/refs/heads/trailing (renamed from tests-clar/resources/testrepo.git/refs/heads/trailing)0
-rw-r--r--tests/resources/testrepo.git/refs/notes/fanout (renamed from tests-clar/resources/testrepo.git/refs/notes/fanout)0
-rw-r--r--tests/resources/testrepo.git/refs/remotes/test/master (renamed from tests-clar/resources/testrepo.git/refs/remotes/test/master)0
-rw-r--r--tests/resources/testrepo.git/refs/tags/annotated_tag_to_blob (renamed from tests-clar/resources/testrepo.git/refs/tags/annotated_tag_to_blob)0
-rw-r--r--tests/resources/testrepo.git/refs/tags/e90810b (renamed from tests-clar/resources/testrepo.git/refs/tags/e90810b)0
-rw-r--r--tests/resources/testrepo.git/refs/tags/hard_tag (renamed from tests-clar/resources/testrepo.git/refs/tags/hard_tag)0
-rw-r--r--tests/resources/testrepo.git/refs/tags/point_to_blob (renamed from tests-clar/resources/testrepo.git/refs/tags/point_to_blob)0
-rw-r--r--tests/resources/testrepo.git/refs/tags/taggerless (renamed from tests-clar/resources/testrepo.git/refs/tags/taggerless)0
-rw-r--r--tests/resources/testrepo.git/refs/tags/test (renamed from tests-clar/resources/testrepo.git/refs/tags/test)0
-rw-r--r--tests/resources/testrepo.git/refs/tags/wrapped_tag (renamed from tests-clar/resources/testrepo.git/refs/tags/wrapped_tag)0
-rw-r--r--tests/resources/testrepo/.gitted/HEAD (renamed from tests-clar/resources/testrepo2/.gitted/HEAD)0
-rw-r--r--tests/resources/testrepo/.gitted/HEAD_TRACKER (renamed from tests-clar/resources/testrepo/.gitted/HEAD_TRACKER)0
-rw-r--r--tests/resources/testrepo/.gitted/config (renamed from tests-clar/resources/testrepo/.gitted/config)0
-rw-r--r--tests/resources/testrepo/.gitted/index (renamed from tests-clar/resources/testrepo/.gitted/index)bin10041 -> 10041 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/09/9fabac3a9ea935598528c27f866e34089c2eff (renamed from tests-clar/resources/testrepo/.gitted/objects/09/9fabac3a9ea935598528c27f866e34089c2eff)0
-rw-r--r--tests/resources/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 (renamed from tests-clar/resources/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08)bin19 -> 19 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e (renamed from tests-clar/resources/testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e)bin163 -> 163 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925 (renamed from tests-clar/resources/testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925)bin147 -> 147 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 (renamed from tests-clar/resources/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7)bin51 -> 51 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd (renamed from tests-clar/resources/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd)bin119 -> 119 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b (renamed from tests-clar/resources/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b)bin21 -> 21 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d (renamed from tests-clar/resources/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d)bin21 -> 21 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54 (renamed from tests-clar/resources/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54)bin50 -> 50 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc (renamed from tests-clar/resources/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc)bin23 -> 23 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 (renamed from tests-clar/resources/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057)bin18 -> 18 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/45/dd856fdd4d89b884c340ba0e047752d9b085d6 (renamed from tests-clar/resources/testrepo/.gitted/objects/45/dd856fdd4d89b884c340ba0e047752d9b085d6)bin156 -> 156 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 (renamed from tests-clar/resources/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045)0
-rw-r--r--tests/resources/testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63 (renamed from tests-clar/resources/testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63)bin50 -> 50 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 (renamed from tests-clar/resources/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644)0
-rw-r--r--tests/resources/testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc (renamed from tests-clar/resources/testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc)bin50 -> 50 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735 (renamed from tests-clar/resources/testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735)bin19 -> 19 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/6b/377958d8c6a4906e8573b53672a1a23a4e8ce6bin0 -> 167 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/6b/9b767af9992b4abad5e24ffb1ba2d688ca602ebin0 -> 41 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a (renamed from tests-clar/resources/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a)bin119 -> 119 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af (renamed from tests-clar/resources/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af)0
-rw-r--r--tests/resources/testrepo/.gitted/objects/7b/2417a23b63e1fdde88c80e14b33247c6e5785abin0 -> 187 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980 (renamed from tests-clar/resources/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980)bin145 -> 145 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d (renamed from tests-clar/resources/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d)bin82 -> 82 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479 (renamed from tests-clar/resources/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479)bin126 -> 126 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/87/380ae84009e9c503506c2f6143a4fc6c60bf80 (renamed from tests-clar/resources/testrepo/.gitted/objects/87/380ae84009e9c503506c2f6143a4fc6c60bf80)bin161 -> 161 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162 (renamed from tests-clar/resources/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162)0
-rw-r--r--tests/resources/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4 (renamed from tests-clar/resources/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4)bin50 -> 50 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a (renamed from tests-clar/resources/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a)0
-rw-r--r--tests/resources/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f (renamed from tests-clar/resources/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f)0
-rw-r--r--tests/resources/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750 (renamed from tests-clar/resources/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750)0
-rw-r--r--tests/resources/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd (renamed from tests-clar/resources/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd)bin28 -> 28 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 (renamed from tests-clar/resources/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6)bin26 -> 26 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12 (renamed from tests-clar/resources/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12)bin148 -> 148 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 (renamed from tests-clar/resources/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1)0
-rw-r--r--tests/resources/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593 (renamed from tests-clar/resources/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593)bin80 -> 80 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 (renamed from tests-clar/resources/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644)0
-rw-r--r--tests/resources/testrepo/.gitted/objects/c0/528fd6cc988c0a40ce0be11bc192fc8dc5346e (renamed from tests-clar/resources/testrepo/.gitted/objects/c0/528fd6cc988c0a40ce0be11bc192fc8dc5346e)bin22 -> 22 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd (renamed from tests-clar/resources/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd)0
-rw-r--r--tests/resources/testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9 (renamed from tests-clar/resources/testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9)bin162 -> 162 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83 (renamed from tests-clar/resources/testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83)bin147 -> 147 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f (renamed from tests-clar/resources/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f)bin21 -> 21 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 (renamed from tests-clar/resources/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391)bin15 -> 15 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0 (renamed from tests-clar/resources/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0)bin21 -> 21 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3 (renamed from tests-clar/resources/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3)bin103 -> 103 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 (renamed from tests-clar/resources/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1)bin82 -> 82 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92 (renamed from tests-clar/resources/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92)bin24 -> 24 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765 (renamed from tests-clar/resources/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765)bin82 -> 82 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx (renamed from tests-clar/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx)bin46656 -> 46656 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack (renamed from tests-clar/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack)bin386089 -> 386089 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx (renamed from tests-clar/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx)bin1240 -> 1240 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack (renamed from tests-clar/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack)bin491 -> 491 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx (renamed from tests-clar/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx)bin1240 -> 1240 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack (renamed from tests-clar/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack)bin498 -> 498 bytes
-rw-r--r--tests/resources/testrepo/.gitted/packed-refs (renamed from tests-clar/resources/testrepo/.gitted/packed-refs)0
-rw-r--r--tests/resources/testrepo/.gitted/refs/heads/br2 (renamed from tests-clar/resources/testrepo/.gitted/refs/heads/br2)0
-rw-r--r--tests/resources/testrepo/.gitted/refs/heads/dir (renamed from tests-clar/resources/testrepo/.gitted/refs/heads/dir)0
-rw-r--r--tests/resources/testrepo/.gitted/refs/heads/long-file-name1
-rw-r--r--tests/resources/testrepo/.gitted/refs/heads/master (renamed from tests-clar/resources/testrepo/.gitted/refs/heads/master)0
-rw-r--r--tests/resources/testrepo/.gitted/refs/heads/packed-test (renamed from tests-clar/resources/testrepo/.gitted/refs/heads/packed-test)0
-rw-r--r--tests/resources/testrepo/.gitted/refs/heads/subtrees (renamed from tests-clar/resources/testrepo/.gitted/refs/heads/subtrees)0
-rw-r--r--tests/resources/testrepo/.gitted/refs/heads/test (renamed from tests-clar/resources/testrepo/.gitted/refs/heads/test)0
-rw-r--r--tests/resources/testrepo/.gitted/refs/tags/e90810b (renamed from tests-clar/resources/testrepo/.gitted/refs/tags/e90810b)0
-rw-r--r--tests/resources/testrepo/.gitted/refs/tags/foo/bar (renamed from tests-clar/resources/testrepo/.gitted/refs/tags/foo/bar)0
-rw-r--r--tests/resources/testrepo/.gitted/refs/tags/foo/foo/bar (renamed from tests-clar/resources/testrepo/.gitted/refs/tags/foo/foo/bar)0
-rw-r--r--tests/resources/testrepo/.gitted/refs/tags/point_to_blob (renamed from tests-clar/resources/testrepo/.gitted/refs/tags/point_to_blob)0
-rw-r--r--tests/resources/testrepo/.gitted/refs/tags/test (renamed from tests-clar/resources/testrepo/.gitted/refs/tags/test)0
-rw-r--r--tests/resources/testrepo2/.gitted/HEAD (renamed from tests-clar/resources/twowaymerge.git/HEAD)0
-rw-r--r--tests/resources/testrepo2/.gitted/config (renamed from tests-clar/resources/testrepo2/.gitted/config)0
-rw-r--r--tests/resources/testrepo2/.gitted/description (renamed from tests-clar/resources/twowaymerge.git/description)0
-rw-r--r--tests/resources/testrepo2/.gitted/index (renamed from tests-clar/resources/testrepo2/.gitted/index)bin512 -> 512 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/info/exclude (renamed from tests-clar/resources/testrepo2/.gitted/info/exclude)0
-rw-r--r--tests/resources/testrepo2/.gitted/logs/HEAD (renamed from tests-clar/resources/testrepo2/.gitted/logs/HEAD)0
-rw-r--r--tests/resources/testrepo2/.gitted/logs/refs/heads/master (renamed from tests-clar/resources/testrepo2/.gitted/logs/refs/heads/master)0
-rw-r--r--tests/resources/testrepo2/.gitted/logs/refs/remotes/origin/HEAD (renamed from tests-clar/resources/testrepo2/.gitted/logs/refs/remotes/origin/HEAD)0
-rw-r--r--tests/resources/testrepo2/.gitted/objects/0c/37a5391bbff43c37f0d0371823a5509eed5b1d (renamed from tests-clar/resources/testrepo2/.gitted/objects/0c/37a5391bbff43c37f0d0371823a5509eed5b1d)bin134 -> 134 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 (renamed from tests-clar/resources/testrepo2/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08)bin19 -> 19 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 (renamed from tests-clar/resources/testrepo2/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7)bin51 -> 51 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd (renamed from tests-clar/resources/testrepo2/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd)bin119 -> 119 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/2d/2eff63372b08adf0a9eb84109ccf7d19e2f3a2 (renamed from tests-clar/resources/testrepo2/.gitted/objects/2d/2eff63372b08adf0a9eb84109ccf7d19e2f3a2)bin125 -> 125 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/36/060c58702ed4c2a40832c51758d5344201d89a (renamed from tests-clar/resources/testrepo2/.gitted/objects/36/060c58702ed4c2a40832c51758d5344201d89a)0
-rw-r--r--tests/resources/testrepo2/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 (renamed from tests-clar/resources/testrepo2/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057)bin18 -> 18 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 (renamed from tests-clar/resources/testrepo2/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045)0
-rw-r--r--tests/resources/testrepo2/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 (renamed from tests-clar/resources/testrepo2/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644)0
-rw-r--r--tests/resources/testrepo2/.gitted/objects/61/9f9935957e010c419cb9d15621916ddfcc0b96 (renamed from tests-clar/resources/testrepo2/.gitted/objects/61/9f9935957e010c419cb9d15621916ddfcc0b96)bin116 -> 116 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a (renamed from tests-clar/resources/testrepo2/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a)bin119 -> 119 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/7f/043268ea43ce18e3540acaabf9e090c91965b0 (renamed from tests-clar/resources/testrepo2/.gitted/objects/7f/043268ea43ce18e3540acaabf9e090c91965b0)bin55 -> 55 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d (renamed from tests-clar/resources/testrepo2/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d)bin82 -> 82 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479 (renamed from tests-clar/resources/testrepo2/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479)bin126 -> 126 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a (renamed from tests-clar/resources/testrepo2/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a)0
-rw-r--r--tests/resources/testrepo2/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f (renamed from tests-clar/resources/testrepo2/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f)0
-rw-r--r--tests/resources/testrepo2/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd (renamed from tests-clar/resources/testrepo2/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd)bin28 -> 28 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 (renamed from tests-clar/resources/testrepo2/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6)bin26 -> 26 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 (renamed from tests-clar/resources/testrepo2/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644)0
-rw-r--r--tests/resources/testrepo2/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd (renamed from tests-clar/resources/testrepo2/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd)0
-rw-r--r--tests/resources/testrepo2/.gitted/objects/c4/dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b (renamed from tests-clar/resources/testrepo2/.gitted/objects/c4/dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b)bin116 -> 116 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 (renamed from tests-clar/resources/testrepo2/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391)bin15 -> 15 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 (renamed from tests-clar/resources/testrepo2/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1)bin82 -> 82 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92 (renamed from tests-clar/resources/testrepo2/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92)bin24 -> 24 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765 (renamed from tests-clar/resources/testrepo2/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765)bin82 -> 82 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx (renamed from tests-clar/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx)bin1240 -> 1240 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack (renamed from tests-clar/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack)bin491 -> 491 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/packed-refs (renamed from tests-clar/resources/testrepo2/.gitted/packed-refs)0
-rw-r--r--tests/resources/testrepo2/.gitted/refs/heads/master (renamed from tests-clar/resources/testrepo2/.gitted/refs/heads/master)0
-rw-r--r--tests/resources/testrepo2/.gitted/refs/remotes/origin/HEAD (renamed from tests-clar/resources/testrepo2/.gitted/refs/remotes/origin/HEAD)0
-rw-r--r--tests/resources/testrepo2/README (renamed from tests-clar/resources/testrepo2/README)0
-rw-r--r--tests/resources/testrepo2/new.txt (renamed from tests-clar/resources/testrepo2/new.txt)0
-rw-r--r--tests/resources/testrepo2/subdir/README (renamed from tests-clar/resources/testrepo2/subdir/README)0
-rw-r--r--tests/resources/testrepo2/subdir/new.txt (renamed from tests-clar/resources/testrepo2/subdir/new.txt)0
-rw-r--r--tests/resources/testrepo2/subdir/subdir2/README (renamed from tests-clar/resources/testrepo2/subdir/subdir2/README)0
-rw-r--r--tests/resources/testrepo2/subdir/subdir2/new.txt (renamed from tests-clar/resources/testrepo2/subdir/subdir2/new.txt)0
-rw-r--r--tests/resources/twowaymerge.git/HEAD (renamed from tests-clar/resources/typechanges/.gitted/HEAD)0
-rw-r--r--tests/resources/twowaymerge.git/config5
-rw-r--r--tests/resources/twowaymerge.git/description (renamed from tests-clar/resources/typechanges/.gitted/description)0
-rw-r--r--tests/resources/twowaymerge.git/info/exclude (renamed from tests-clar/resources/twowaymerge.git/info/exclude)0
-rw-r--r--tests/resources/twowaymerge.git/objects/0c/8a3f1f3d5f421cf83048c7c73ee3b55a5e0f29 (renamed from tests-clar/resources/twowaymerge.git/objects/0c/8a3f1f3d5f421cf83048c7c73ee3b55a5e0f29)bin157 -> 157 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/10/2dce8e3081f398e4bdd9fd894dc85ac3ca6a67 (renamed from tests-clar/resources/twowaymerge.git/objects/10/2dce8e3081f398e4bdd9fd894dc85ac3ca6a67)bin54 -> 54 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/17/7d8634a28e26ec7819284752757ebe01a479d5 (renamed from tests-clar/resources/twowaymerge.git/objects/17/7d8634a28e26ec7819284752757ebe01a479d5)bin80 -> 80 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/1c/30b88f5f3ee66d78df6520a7de9e89b890818b (renamed from tests-clar/resources/twowaymerge.git/objects/1c/30b88f5f3ee66d78df6520a7de9e89b890818b)0
-rw-r--r--tests/resources/twowaymerge.git/objects/1f/4c0311a24b63f6fc209a59a1e404942d4a5006 (renamed from tests-clar/resources/twowaymerge.git/objects/1f/4c0311a24b63f6fc209a59a1e404942d4a5006)0
-rw-r--r--tests/resources/twowaymerge.git/objects/22/24e191514cb4bd8c566d80dac22dfcb1e9bb83 (renamed from tests-clar/resources/twowaymerge.git/objects/22/24e191514cb4bd8c566d80dac22dfcb1e9bb83)0
-rw-r--r--tests/resources/twowaymerge.git/objects/29/6e56023cdc034d2735fee8c0d85a659d1b07f4 (renamed from tests-clar/resources/twowaymerge.git/objects/29/6e56023cdc034d2735fee8c0d85a659d1b07f4)bin51 -> 51 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/31/51880ae2b363f1c262cf98b750c1f169a0d432 (renamed from tests-clar/resources/twowaymerge.git/objects/31/51880ae2b363f1c262cf98b750c1f169a0d432)bin68 -> 68 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/3b/287f8730c81d0b763c2d294618a5e32b67b4f8 (renamed from tests-clar/resources/twowaymerge.git/objects/3b/287f8730c81d0b763c2d294618a5e32b67b4f8)bin54 -> 54 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/42/b7311aa626e712891940c1ec5d5cba201946a4 (renamed from tests-clar/resources/twowaymerge.git/objects/42/b7311aa626e712891940c1ec5d5cba201946a4)0
-rw-r--r--tests/resources/twowaymerge.git/objects/49/6d6428b9cf92981dc9495211e6e1120fb6f2ba (renamed from tests-clar/resources/twowaymerge.git/objects/49/6d6428b9cf92981dc9495211e6e1120fb6f2ba)bin46 -> 46 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/59/b0cf7d74659e1cdb13305319d6d4ce2733c118 (renamed from tests-clar/resources/twowaymerge.git/objects/59/b0cf7d74659e1cdb13305319d6d4ce2733c118)bin65 -> 65 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/6a/b5d28acbf3c3bdff276f7ccfdf29c1520e542f (renamed from tests-clar/resources/twowaymerge.git/objects/6a/b5d28acbf3c3bdff276f7ccfdf29c1520e542f)0
-rw-r--r--tests/resources/twowaymerge.git/objects/6c/fca542b55b8b37017e6125a4b8f59a6eae6f11 (renamed from tests-clar/resources/twowaymerge.git/objects/6c/fca542b55b8b37017e6125a4b8f59a6eae6f11)bin68 -> 68 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/76/5b32c65d38f04c4f287abda055818ec0f26912 (renamed from tests-clar/resources/twowaymerge.git/objects/76/5b32c65d38f04c4f287abda055818ec0f26912)bin54 -> 54 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/7b/8c336c45fc6895c1c60827260fe5d798e5d247 (renamed from tests-clar/resources/twowaymerge.git/objects/7b/8c336c45fc6895c1c60827260fe5d798e5d247)0
-rw-r--r--tests/resources/twowaymerge.git/objects/82/bf9a1a10a4b25c1f14c9607b60970705e92545 (renamed from tests-clar/resources/twowaymerge.git/objects/82/bf9a1a10a4b25c1f14c9607b60970705e92545)0
-rw-r--r--tests/resources/twowaymerge.git/objects/8b/82fb1794cb1c8c7f172ec730a4c2db0ae3e650 (renamed from tests-clar/resources/twowaymerge.git/objects/8b/82fb1794cb1c8c7f172ec730a4c2db0ae3e650)0
-rw-r--r--tests/resources/twowaymerge.git/objects/9a/40a2f11c191f180c47e54b11567cb3c1e89b30 (renamed from tests-clar/resources/twowaymerge.git/objects/9a/40a2f11c191f180c47e54b11567cb3c1e89b30)bin62 -> 62 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/9b/219343610c88a1187c996d0dc58330b55cee28 (renamed from tests-clar/resources/twowaymerge.git/objects/9b/219343610c88a1187c996d0dc58330b55cee28)0
-rw-r--r--tests/resources/twowaymerge.git/objects/9f/e06a50f4d1634d6c6879854d01d80857388706 (renamed from tests-clar/resources/twowaymerge.git/objects/9f/e06a50f4d1634d6c6879854d01d80857388706)bin65 -> 65 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/a4/1a49f8f5cd9b6cb14a076bf8394881ed0b4d19 (renamed from tests-clar/resources/twowaymerge.git/objects/a4/1a49f8f5cd9b6cb14a076bf8394881ed0b4d19)0
-rw-r--r--tests/resources/twowaymerge.git/objects/a9/53a018c5b10b20c86e69fef55ebc8ad4c5a417 (renamed from tests-clar/resources/twowaymerge.git/objects/a9/53a018c5b10b20c86e69fef55ebc8ad4c5a417)0
-rw-r--r--tests/resources/twowaymerge.git/objects/a9/cce3cd1b3efbda5b1f4a6dcc3f1570b2d3d74c (renamed from tests-clar/resources/twowaymerge.git/objects/a9/cce3cd1b3efbda5b1f4a6dcc3f1570b2d3d74c)0
-rw-r--r--tests/resources/twowaymerge.git/objects/bd/1732c43c68d712ad09e1d872b9be6d4b9efdc4 (renamed from tests-clar/resources/twowaymerge.git/objects/bd/1732c43c68d712ad09e1d872b9be6d4b9efdc4)bin158 -> 158 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/c3/7a783c20d92ac92362a78a32860f7eebf938ef (renamed from tests-clar/resources/twowaymerge.git/objects/c3/7a783c20d92ac92362a78a32860f7eebf938ef)bin158 -> 158 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/cb/dd40facab1682754eb67f7a43f29e672903cf6 (renamed from tests-clar/resources/twowaymerge.git/objects/cb/dd40facab1682754eb67f7a43f29e672903cf6)bin51 -> 51 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/cd/f97fd3bb48eb3827638bb33d208f5fd32d0aa6 (renamed from tests-clar/resources/twowaymerge.git/objects/cd/f97fd3bb48eb3827638bb33d208f5fd32d0aa6)bin158 -> 158 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/d6/f10d549cb335b9e6d38afc1f0088be69b50494 (renamed from tests-clar/resources/twowaymerge.git/objects/d6/f10d549cb335b9e6d38afc1f0088be69b50494)bin62 -> 62 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/d9/acdc7ae7632adfeec67fa73c1e343cf4d1f47e (renamed from tests-clar/resources/twowaymerge.git/objects/d9/acdc7ae7632adfeec67fa73c1e343cf4d1f47e)0
-rw-r--r--tests/resources/twowaymerge.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 (renamed from tests-clar/resources/twowaymerge.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391)bin15 -> 15 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/ef/0488f0b722f0be8bcb90a7730ac7efafd1d694 (renamed from tests-clar/resources/twowaymerge.git/objects/ef/0488f0b722f0be8bcb90a7730ac7efafd1d694)0
-rw-r--r--tests/resources/twowaymerge.git/objects/fc/f7e3f51c11d199ab7a78403ee4f9ccd028da25 (renamed from tests-clar/resources/twowaymerge.git/objects/fc/f7e3f51c11d199ab7a78403ee4f9ccd028da25)bin62 -> 62 bytes
-rw-r--r--tests/resources/twowaymerge.git/refs/heads/first-branch (renamed from tests-clar/resources/twowaymerge.git/refs/heads/first-branch)0
-rw-r--r--tests/resources/twowaymerge.git/refs/heads/master (renamed from tests-clar/resources/twowaymerge.git/refs/heads/master)0
-rw-r--r--tests/resources/twowaymerge.git/refs/heads/second-branch (renamed from tests-clar/resources/twowaymerge.git/refs/heads/second-branch)0
-rw-r--r--tests/resources/typechanges/.gitted/HEAD (renamed from tests-clar/resources/typechanges/.gitted/modules/b/HEAD)0
-rw-r--r--tests/resources/typechanges/.gitted/config (renamed from tests-clar/resources/typechanges/.gitted/config)0
-rw-r--r--tests/resources/typechanges/.gitted/description (renamed from tests-clar/resources/typechanges/.gitted/modules/b/description)0
-rw-r--r--tests/resources/typechanges/.gitted/index (renamed from tests-clar/resources/typechanges/.gitted/index)bin184 -> 184 bytes
-rw-r--r--tests/resources/typechanges/.gitted/info/exclude (renamed from tests-clar/resources/typechanges/.gitted/info/exclude)0
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/HEAD (renamed from tests-clar/resources/typechanges/.gitted/modules/d/HEAD)0
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/config (renamed from tests-clar/resources/typechanges/.gitted/modules/b/config)0
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/description (renamed from tests-clar/resources/typechanges/.gitted/modules/d/description)0
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/index (renamed from tests-clar/resources/typechanges/.gitted/modules/b/index)bin192 -> 192 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/info/exclude (renamed from tests-clar/resources/typechanges/.gitted/modules/b/info/exclude)0
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 (renamed from tests-clar/resources/typechanges/.gitted/modules/b/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1)bin55 -> 55 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 (renamed from tests-clar/resources/typechanges/.gitted/modules/b/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484)bin53 -> 53 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 (renamed from tests-clar/resources/typechanges/.gitted/modules/b/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5)bin93 -> 93 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/41/bd4bc3df978de695f67ace64c560913da11653 (renamed from tests-clar/resources/typechanges/.gitted/modules/b/objects/41/bd4bc3df978de695f67ace64c560913da11653)bin163 -> 163 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 (renamed from tests-clar/resources/typechanges/.gitted/modules/b/objects/48/0095882d281ed676fe5b863569520e54a7d5c0)bin163 -> 163 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/5e/4963595a9774b90524d35a807169049de8ccad (renamed from tests-clar/resources/typechanges/.gitted/modules/b/objects/5e/4963595a9774b90524d35a807169049de8ccad)bin167 -> 167 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/6b/31c659545507c381e9cd34ec508f16c04e149e (renamed from tests-clar/resources/typechanges/.gitted/modules/b/objects/6b/31c659545507c381e9cd34ec508f16c04e149e)0
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/73/ba924a80437097795ae839e66e187c55d3babf (renamed from tests-clar/resources/typechanges/.gitted/modules/b/objects/73/ba924a80437097795ae839e66e187c55d3babf)bin93 -> 93 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a (renamed from tests-clar/resources/typechanges/.gitted/modules/b/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a)0
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/78/9efbdadaa4a582778d4584385495559ea0994b (renamed from tests-clar/resources/typechanges/.gitted/modules/b/objects/78/9efbdadaa4a582778d4584385495559ea0994b)0
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e (renamed from tests-clar/resources/typechanges/.gitted/modules/b/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e)bin81 -> 81 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 (renamed from tests-clar/resources/typechanges/.gitted/modules/b/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5)bin93 -> 93 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/packed-refs (renamed from tests-clar/resources/typechanges/.gitted/modules/b/packed-refs)0
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/refs/heads/master (renamed from tests-clar/resources/typechanges/.gitted/modules/b/refs/heads/master)0
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/refs/remotes/origin/HEAD (renamed from tests-clar/resources/typechanges/.gitted/modules/b/refs/remotes/origin/HEAD)0
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/HEAD (renamed from tests-clar/resources/typechanges/.gitted/modules/e/HEAD)0
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/config (renamed from tests-clar/resources/typechanges/.gitted/modules/d/config)0
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/description (renamed from tests-clar/resources/typechanges/.gitted/modules/e/description)0
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/index (renamed from tests-clar/resources/typechanges/.gitted/modules/d/index)bin192 -> 192 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/info/exclude (renamed from tests-clar/resources/typechanges/.gitted/modules/d/info/exclude)0
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 (renamed from tests-clar/resources/typechanges/.gitted/modules/d/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1)bin55 -> 55 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 (renamed from tests-clar/resources/typechanges/.gitted/modules/d/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484)bin53 -> 53 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 (renamed from tests-clar/resources/typechanges/.gitted/modules/d/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5)bin93 -> 93 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/41/bd4bc3df978de695f67ace64c560913da11653 (renamed from tests-clar/resources/typechanges/.gitted/modules/d/objects/41/bd4bc3df978de695f67ace64c560913da11653)bin163 -> 163 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 (renamed from tests-clar/resources/typechanges/.gitted/modules/d/objects/48/0095882d281ed676fe5b863569520e54a7d5c0)bin163 -> 163 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/5e/4963595a9774b90524d35a807169049de8ccad (renamed from tests-clar/resources/typechanges/.gitted/modules/d/objects/5e/4963595a9774b90524d35a807169049de8ccad)bin167 -> 167 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/6b/31c659545507c381e9cd34ec508f16c04e149e (renamed from tests-clar/resources/typechanges/.gitted/modules/d/objects/6b/31c659545507c381e9cd34ec508f16c04e149e)0
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/73/ba924a80437097795ae839e66e187c55d3babf (renamed from tests-clar/resources/typechanges/.gitted/modules/d/objects/73/ba924a80437097795ae839e66e187c55d3babf)bin93 -> 93 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a (renamed from tests-clar/resources/typechanges/.gitted/modules/d/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a)0
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/78/9efbdadaa4a582778d4584385495559ea0994b (renamed from tests-clar/resources/typechanges/.gitted/modules/d/objects/78/9efbdadaa4a582778d4584385495559ea0994b)0
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e (renamed from tests-clar/resources/typechanges/.gitted/modules/d/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e)bin81 -> 81 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 (renamed from tests-clar/resources/typechanges/.gitted/modules/d/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5)bin93 -> 93 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/packed-refs (renamed from tests-clar/resources/typechanges/.gitted/modules/d/packed-refs)0
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/refs/heads/master (renamed from tests-clar/resources/typechanges/.gitted/modules/d/refs/heads/master)0
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/refs/remotes/origin/HEAD (renamed from tests-clar/resources/typechanges/.gitted/modules/d/refs/remotes/origin/HEAD)0
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/HEAD (renamed from tests-clar/resources/unsymlinked.git/HEAD)0
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/config (renamed from tests-clar/resources/typechanges/.gitted/modules/e/config)0
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/description (renamed from tests-clar/resources/unsymlinked.git/description)0
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/index (renamed from tests-clar/resources/typechanges/.gitted/modules/e/index)bin192 -> 192 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/info/exclude (renamed from tests-clar/resources/typechanges/.gitted/modules/e/info/exclude)0
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 (renamed from tests-clar/resources/typechanges/.gitted/modules/e/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1)bin55 -> 55 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 (renamed from tests-clar/resources/typechanges/.gitted/modules/e/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484)bin53 -> 53 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 (renamed from tests-clar/resources/typechanges/.gitted/modules/e/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5)bin93 -> 93 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/41/bd4bc3df978de695f67ace64c560913da11653 (renamed from tests-clar/resources/typechanges/.gitted/modules/e/objects/41/bd4bc3df978de695f67ace64c560913da11653)bin163 -> 163 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 (renamed from tests-clar/resources/typechanges/.gitted/modules/e/objects/48/0095882d281ed676fe5b863569520e54a7d5c0)bin163 -> 163 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/5e/4963595a9774b90524d35a807169049de8ccad (renamed from tests-clar/resources/typechanges/.gitted/modules/e/objects/5e/4963595a9774b90524d35a807169049de8ccad)bin167 -> 167 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/6b/31c659545507c381e9cd34ec508f16c04e149e (renamed from tests-clar/resources/typechanges/.gitted/modules/e/objects/6b/31c659545507c381e9cd34ec508f16c04e149e)0
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/73/ba924a80437097795ae839e66e187c55d3babf (renamed from tests-clar/resources/typechanges/.gitted/modules/e/objects/73/ba924a80437097795ae839e66e187c55d3babf)bin93 -> 93 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a (renamed from tests-clar/resources/typechanges/.gitted/modules/e/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a)0
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/78/9efbdadaa4a582778d4584385495559ea0994b (renamed from tests-clar/resources/typechanges/.gitted/modules/e/objects/78/9efbdadaa4a582778d4584385495559ea0994b)0
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e (renamed from tests-clar/resources/typechanges/.gitted/modules/e/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e)bin81 -> 81 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 (renamed from tests-clar/resources/typechanges/.gitted/modules/e/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5)bin93 -> 93 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/packed-refs (renamed from tests-clar/resources/typechanges/.gitted/modules/e/packed-refs)0
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/refs/heads/master (renamed from tests-clar/resources/typechanges/.gitted/modules/e/refs/heads/master)0
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/refs/remotes/origin/HEAD (renamed from tests-clar/resources/typechanges/.gitted/modules/e/refs/remotes/origin/HEAD)0
-rw-r--r--tests/resources/typechanges/.gitted/objects/0d/78578795b7ca49fd8df6c4b6d27c5c02d991d8 (renamed from tests-clar/resources/typechanges/.gitted/objects/0d/78578795b7ca49fd8df6c4b6d27c5c02d991d8)bin76 -> 76 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/0e/7ed140b514b8cae23254cb8656fe1674403aff (renamed from tests-clar/resources/typechanges/.gitted/objects/0e/7ed140b514b8cae23254cb8656fe1674403aff)bin162 -> 162 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/0f/f461da9689266f482d8f6654a4400b4e33c586 (renamed from tests-clar/resources/typechanges/.gitted/objects/0f/f461da9689266f482d8f6654a4400b4e33c586)bin486 -> 486 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/18/aa7e45bbe4c3cc24a0b079696c59d36675af97 (renamed from tests-clar/resources/typechanges/.gitted/objects/18/aa7e45bbe4c3cc24a0b079696c59d36675af97)bin89 -> 89 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/1b/63caae4a5ca96f78e8dfefc376c6a39a142475 (renamed from tests-clar/resources/typechanges/.gitted/objects/1b/63caae4a5ca96f78e8dfefc376c6a39a142475)bin161 -> 161 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/1e/abe82aa3b2365a394f6108f24435df6e193d02 (renamed from tests-clar/resources/typechanges/.gitted/objects/1e/abe82aa3b2365a394f6108f24435df6e193d02)bin549 -> 549 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/42/061c01a1c70097d1e4579f29a5adf40abdec95 (renamed from tests-clar/resources/typechanges/.gitted/objects/42/061c01a1c70097d1e4579f29a5adf40abdec95)bin24 -> 24 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/46/2838cee476a87e7cff32196b66fa18ed756592 (renamed from tests-clar/resources/typechanges/.gitted/objects/46/2838cee476a87e7cff32196b66fa18ed756592)bin76 -> 76 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/63/499e4ea8e096b831515ceb1d5a7593e4d87ae5 (renamed from tests-clar/resources/typechanges/.gitted/objects/63/499e4ea8e096b831515ceb1d5a7593e4d87ae5)bin18 -> 18 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/68/1af94e10eaf262f3ab7cb9b8fd5f4158ba4d3e (renamed from tests-clar/resources/typechanges/.gitted/objects/68/1af94e10eaf262f3ab7cb9b8fd5f4158ba4d3e)bin24 -> 24 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/6a/9008602b811e69a9b7a2d83496f39a794fdeeb (renamed from tests-clar/resources/typechanges/.gitted/objects/6a/9008602b811e69a9b7a2d83496f39a794fdeeb)bin602 -> 602 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/6e/ae26c90e8ccc4d16208972119c40635489c6f0 (renamed from tests-clar/resources/typechanges/.gitted/objects/6e/ae26c90e8ccc4d16208972119c40635489c6f0)bin160 -> 160 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/6f/39eabbb8a7541515e0d35971078bccb502e7e0 (renamed from tests-clar/resources/typechanges/.gitted/objects/6f/39eabbb8a7541515e0d35971078bccb502e7e0)bin66 -> 66 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/71/54d3083461536dfc71ad5542f3e65e723a06c4 (renamed from tests-clar/resources/typechanges/.gitted/objects/71/54d3083461536dfc71ad5542f3e65e723a06c4)bin657 -> 657 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/75/56c1d893a4c0ca85ac8ac51de47ff399758729 (renamed from tests-clar/resources/typechanges/.gitted/objects/75/56c1d893a4c0ca85ac8ac51de47ff399758729)bin226 -> 226 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/76/fef844064c26d5e06c2508240dae661e7231b2 (renamed from tests-clar/resources/typechanges/.gitted/objects/76/fef844064c26d5e06c2508240dae661e7231b2)bin66 -> 66 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/79/b9f23e85f55ea36a472a902e875bc1121a94cb (renamed from tests-clar/resources/typechanges/.gitted/objects/79/b9f23e85f55ea36a472a902e875bc1121a94cb)0
-rw-r--r--tests/resources/typechanges/.gitted/objects/85/28da0ea65eacf1f74f9ed6696adbac547963ad (renamed from tests-clar/resources/typechanges/.gitted/objects/85/28da0ea65eacf1f74f9ed6696adbac547963ad)bin451 -> 451 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/8b/3726b365824ad5a07c537247f4bc73ed7d37ea (renamed from tests-clar/resources/typechanges/.gitted/objects/8b/3726b365824ad5a07c537247f4bc73ed7d37ea)bin76 -> 76 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/93/3e28c1c8a68838a763d250bdf0b2c6068289c3 (renamed from tests-clar/resources/typechanges/.gitted/objects/93/3e28c1c8a68838a763d250bdf0b2c6068289c3)bin226 -> 226 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/96/2710fe5b4e453e9e827945b3487c525968ec4a (renamed from tests-clar/resources/typechanges/.gitted/objects/96/2710fe5b4e453e9e827945b3487c525968ec4a)bin76 -> 76 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/96/6cf1b3598e195b31b2cde3784f9a19f0728a6f (renamed from tests-clar/resources/typechanges/.gitted/objects/96/6cf1b3598e195b31b2cde3784f9a19f0728a6f)bin226 -> 226 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/99/e8bab9ece009f0fba7eb41f850f4c12bedb9b7 (renamed from tests-clar/resources/typechanges/.gitted/objects/99/e8bab9ece009f0fba7eb41f850f4c12bedb9b7)bin701 -> 701 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/9b/19edf33a03a0c59cdfc113bfa5c06179bf9b1a (renamed from tests-clar/resources/typechanges/.gitted/objects/9b/19edf33a03a0c59cdfc113bfa5c06179bf9b1a)0
-rw-r--r--tests/resources/typechanges/.gitted/objects/9b/db75b73836a99e3dbeea640a81de81031fdc29 (renamed from tests-clar/resources/typechanges/.gitted/objects/9b/db75b73836a99e3dbeea640a81de81031fdc29)bin162 -> 162 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/9d/0235c7a7edc0889a18f97a42ee6db9fe688447 (renamed from tests-clar/resources/typechanges/.gitted/objects/9d/0235c7a7edc0889a18f97a42ee6db9fe688447)bin160 -> 160 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/9e/ffc457877f109b2a4319e14bee613a15f2a00d (renamed from tests-clar/resources/typechanges/.gitted/objects/9e/ffc457877f109b2a4319e14bee613a15f2a00d)bin226 -> 226 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/a0/a9bad6f6f40325198f938a0e3ae981622d7707 (renamed from tests-clar/resources/typechanges/.gitted/objects/a0/a9bad6f6f40325198f938a0e3ae981622d7707)bin54 -> 54 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/b1/977dc4e573b812d4619754c98138c56999dc0d (renamed from tests-clar/resources/typechanges/.gitted/objects/b1/977dc4e573b812d4619754c98138c56999dc0d)bin518 -> 518 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/d7/5992dd02391e128dac332dcc78d649dd9ab095 (renamed from tests-clar/resources/typechanges/.gitted/objects/d7/5992dd02391e128dac332dcc78d649dd9ab095)bin577 -> 577 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/da/e2709d638df52212b1f43ff61797ebfedfcc7c (renamed from tests-clar/resources/typechanges/.gitted/objects/da/e2709d638df52212b1f43ff61797ebfedfcc7c)bin78 -> 78 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/e1/152adcb9adf37ec551ada9ba377ab53aec3bad (renamed from tests-clar/resources/typechanges/.gitted/objects/e1/152adcb9adf37ec551ada9ba377ab53aec3bad)bin19 -> 19 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/e4/ed436a9eb0f198cda722886a5f8d6d6c836b7b (renamed from tests-clar/resources/typechanges/.gitted/objects/e4/ed436a9eb0f198cda722886a5f8d6d6c836b7b)bin225 -> 225 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 (renamed from tests-clar/resources/typechanges/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391)bin15 -> 15 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/f2/0b79342712e0b2315647cd8227a573fd3bc46e (renamed from tests-clar/resources/typechanges/.gitted/objects/f2/0b79342712e0b2315647cd8227a573fd3bc46e)bin66 -> 66 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/fd/e0147e3b59f381635a3b016e3fe6dacb70779d (renamed from tests-clar/resources/typechanges/.gitted/objects/fd/e0147e3b59f381635a3b016e3fe6dacb70779d)bin53 -> 53 bytes
-rw-r--r--tests/resources/typechanges/.gitted/refs/heads/master (renamed from tests-clar/resources/typechanges/.gitted/refs/heads/master)0
-rw-r--r--tests/resources/typechanges/README.md (renamed from tests-clar/resources/typechanges/README.md)0
-rw-r--r--tests/resources/typechanges/gitmodules (renamed from tests-clar/resources/typechanges/gitmodules)0
-rw-r--r--tests/resources/unsymlinked.git/HEAD1
-rw-r--r--tests/resources/unsymlinked.git/config (renamed from tests-clar/resources/unsymlinked.git/config)0
-rw-r--r--tests/resources/unsymlinked.git/description1
-rw-r--r--tests/resources/unsymlinked.git/info/exclude (renamed from tests-clar/resources/unsymlinked.git/info/exclude)0
-rw-r--r--tests/resources/unsymlinked.git/objects/08/8b64704e0d6b8bd061dea879418cb5442a3fbf (renamed from tests-clar/resources/unsymlinked.git/objects/08/8b64704e0d6b8bd061dea879418cb5442a3fbf)bin49 -> 49 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/13/a5e939bca25940c069fd2169d993dba328e30b (renamed from tests-clar/resources/unsymlinked.git/objects/13/a5e939bca25940c069fd2169d993dba328e30b)bin44 -> 44 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/19/bf568e59e3a0b363cafb4106226e62d4a4c41c (renamed from tests-clar/resources/unsymlinked.git/objects/19/bf568e59e3a0b363cafb4106226e62d4a4c41c)bin29 -> 29 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/58/1fadd35b4cf320d102a152f918729011604773 (renamed from tests-clar/resources/unsymlinked.git/objects/58/1fadd35b4cf320d102a152f918729011604773)bin47 -> 47 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/5c/87b6791e8b13da658a14d1ef7e09b5dc3bac8c (renamed from tests-clar/resources/unsymlinked.git/objects/5c/87b6791e8b13da658a14d1ef7e09b5dc3bac8c)bin78 -> 78 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/6f/e5f5398af85fb3de8a6aba0339b6d3bfa26a27 (renamed from tests-clar/resources/unsymlinked.git/objects/6f/e5f5398af85fb3de8a6aba0339b6d3bfa26a27)bin49 -> 49 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/7f/ccd75616ec188b8f1b23d67506a334cc34a49d (renamed from tests-clar/resources/unsymlinked.git/objects/7f/ccd75616ec188b8f1b23d67506a334cc34a49d)bin132 -> 132 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/80/6999882bf91d24241e4077906b9017605eb1f3 (renamed from tests-clar/resources/unsymlinked.git/objects/80/6999882bf91d24241e4077906b9017605eb1f3)bin170 -> 170 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/83/7d176303c5005505ec1e4a30231c40930c0230 (renamed from tests-clar/resources/unsymlinked.git/objects/83/7d176303c5005505ec1e4a30231c40930c0230)bin44 -> 44 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/a8/595ccca04f40818ae0155c8f9c77a230e597b6 (renamed from tests-clar/resources/unsymlinked.git/objects/a8/595ccca04f40818ae0155c8f9c77a230e597b6)0
-rw-r--r--tests/resources/unsymlinked.git/objects/cf/8f1cf5cce859c438d6cc067284cb5e161206e7 (renamed from tests-clar/resources/unsymlinked.git/objects/cf/8f1cf5cce859c438d6cc067284cb5e161206e7)bin49 -> 49 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/d5/278d05c8607ec420bfee4cf219fbc0eeebfd6a (renamed from tests-clar/resources/unsymlinked.git/objects/d5/278d05c8607ec420bfee4cf219fbc0eeebfd6a)bin49 -> 49 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/f4/e16fb76536591a41454194058d048d8e4dd2e9 (renamed from tests-clar/resources/unsymlinked.git/objects/f4/e16fb76536591a41454194058d048d8e4dd2e9)bin44 -> 44 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/f9/e65619d93fdf2673882e0a261c5e93b1a84006 (renamed from tests-clar/resources/unsymlinked.git/objects/f9/e65619d93fdf2673882e0a261c5e93b1a84006)bin32 -> 32 bytes
-rw-r--r--tests/resources/unsymlinked.git/refs/heads/exe-file (renamed from tests-clar/resources/unsymlinked.git/refs/heads/exe-file)0
-rw-r--r--tests/resources/unsymlinked.git/refs/heads/master (renamed from tests-clar/resources/unsymlinked.git/refs/heads/master)0
-rw-r--r--tests/resources/unsymlinked.git/refs/heads/reg-file (renamed from tests-clar/resources/unsymlinked.git/refs/heads/reg-file)0
-rw-r--r--tests/revwalk/basic.c254
-rw-r--r--tests/revwalk/mergebase.c392
-rw-r--r--tests/revwalk/signatureparsing.c (renamed from tests-clar/revwalk/signatureparsing.c)0
-rw-r--r--tests/revwalk/simplify.c55
-rw-r--r--tests/stash/drop.c174
-rw-r--r--tests/stash/foreach.c (renamed from tests-clar/stash/foreach.c)0
-rw-r--r--tests/stash/save.c354
-rw-r--r--tests/stash/stash_helpers.c56
-rw-r--r--tests/stash/stash_helpers.h8
-rw-r--r--tests/stash/submodules.c80
-rw-r--r--tests/status/ignore.c582
-rw-r--r--tests/status/renames.c557
-rw-r--r--tests/status/single.c (renamed from tests-clar/status/single.c)0
-rw-r--r--tests/status/status_data.h326
-rw-r--r--tests/status/status_helpers.c (renamed from tests-clar/status/status_helpers.c)0
-rw-r--r--tests/status/status_helpers.h (renamed from tests-clar/status/status_helpers.h)0
-rw-r--r--tests/status/submodules.c223
-rw-r--r--tests/status/worktree.c875
-rw-r--r--tests/status/worktree_init.c (renamed from tests-clar/status/worktree_init.c)0
-rw-r--r--tests/stress/diff.c146
-rw-r--r--tests/submodule/lookup.c172
-rw-r--r--tests/submodule/modify.c253
-rw-r--r--tests/submodule/status.c425
-rw-r--r--tests/submodule/submodule_helpers.c127
-rw-r--r--tests/submodule/submodule_helpers.h5
-rw-r--r--tests/threads/basic.c36
-rw-r--r--tests/threads/refdb.c213
-rw-r--r--tests/trace/trace.c (renamed from tests-clar/trace/trace.c)0
-rw-r--r--tests/valgrind-supp-mac.txt184
2418 files changed, 56412 insertions, 35837 deletions
diff --git a/.gitignore b/.gitignore
index bba9d5d5c..d4e0454fa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,6 @@
-/tests-clar/clar.suite
-/tests-clar/clar.suite.rule
-/tests-clar/.clarcache
+/tests/clar.suite
+/tests/clar.suite.rule
+/tests/.clarcache
/apidocs
/trash-*.exe
/libgit2.pc
diff --git a/.mailmap b/.mailmap
index 3f5a087ea..c656f64c7 100644
--- a/.mailmap
+++ b/.mailmap
@@ -16,3 +16,6 @@ Xavier L. <xavier.l@afrosoft.tk> <xavier.l@afrosoft.tk>
Sascha Cunz <sascha@babbelbox.org> <Sascha@BabbelBox.org>
Authmillenon <authmillenon@googlemail.com> <martin@ucsmail.de>
Authmillenon <authmillenon@googlemail.com> <authmillenon@googlemail.com>
+Edward Thomson <ethomson@microsoft.com> <ethomson@edwardthomson.com>
+J. David Ibáñez <jdavid.ibp@gmail.com> <jdavid@itaapy.com>
+Russell Belfer <rb@github.com> <arrbee@arrbee.com>
diff --git a/.travis.yml b/.travis.yml
index 0d5746f2e..151060fb4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,6 @@
# Travis-CI Build for libgit2
# see travis-ci.org for details
-# As CMake is not officially supported we use erlang VMs
language: c
compiler:
@@ -18,26 +17,18 @@ matrix:
- compiler: i586-mingw32msvc-gcc
env: OPTIONS="-DBUILD_CLAR=OFF -DWIN32=ON -DMINGW=ON"
-# Make sure CMake is installed
install:
- - sudo apt-get update >/dev/null
- - sudo apt-get -q install cmake valgrind
+ - sudo apt-get -qq update
+ - sudo apt-get -qq install cmake libssh2-1-dev openssh-client openssh-server
-# Run the Build script
+# Run the Build script and tests
script:
- - mkdir _temp
- - git init --bare _temp/test.git
- - git daemon --listen=localhost --export-all --enable=receive-pack --base-path=_temp _temp 2>/dev/null &
- - export GITTEST_REMOTE_URL="git://localhost/test.git"
- - mkdir _build
- - cd _build
- - cmake .. -DCMAKE_INSTALL_PREFIX=../_install $OPTIONS
- - cmake --build . --target install
- - ctest -V .
+ - script/cibuild.sh
# Run Tests
after_success:
- - valgrind --leak-check=full --show-reachable=yes --suppressions=../libgit2_clar.supp ./libgit2_clar -ionline
+ - sudo apt-get -qq install valgrind
+ - valgrind --leak-check=full --show-reachable=yes --suppressions=./libgit2_clar.supp _build/libgit2_clar -ionline
# Only watch the development branch
branches:
diff --git a/AUTHORS b/AUTHORS
index 587da249d..f3a03ee74 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -68,5 +68,6 @@ Sven Strickroth
Tim Branyen
Tim Clem
Tim Harder
+Torsten Bögershausen
Trent Mick
Vicent Marti
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 34d56b3fa..ac1032acf 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -27,25 +27,44 @@ OPTION( BUILD_EXAMPLES "Build library usage example apps" OFF )
OPTION( TAGS "Generate tags" OFF )
OPTION( PROFILE "Generate profiling information" OFF )
OPTION( ENABLE_TRACE "Enables tracing support" OFF )
-OPTION( LIBGIT2_FILENAME "Name of the produced binary" OFF )
+OPTION( LIBGIT2_FILENAME "Name of the produced binary" OFF )
+
+OPTION( ANDROID "Build for android NDK" OFF )
+
+OPTION( USE_ICONV "Link with and use iconv library" OFF )
+OPTION( USE_SSH "Link with libssh to enable SSH support" ON )
+
+IF(APPLE)
+ SET( USE_ICONV ON )
+ENDIF()
IF(MSVC)
- # This option is only availalbe when building with MSVC. By default,
- # libgit2 is build using the stdcall calling convention, as that's what
- # the CLR expects by default and how the Windows API is built.
- #
- # If you are writing a C or C++ program and want to link to libgit2, you
- # have to either:
- # - Add /Gz to the compiler options of _your_ program / library.
- # - Turn this off by invoking CMake with the "-DSTDCALL=Off" argument.
+ # This option is only available when building with MSVC. By default, libgit2
+ # is build using the cdecl calling convention, which is useful if you're
+ # writing C. However, the CLR and Win32 API both expect stdcall.
#
- OPTION( STDCALL "Build libgit2 with the __stdcall convention" ON )
+ # If you are writing a CLR program and want to link to libgit2, you'll want
+ # to turn this on by invoking CMake with the "-DSTDCALL=ON" argument.
+ OPTION( STDCALL "Build libgit2 with the __stdcall convention" OFF )
# This option must match the settings used in your program, in particular if you
# are linking statically
OPTION( STATIC_CRT "Link the static CRT libraries" ON )
+
+ # By default, libgit2 is built with WinHTTP. To use the built-in
+ # HTTP transport, invoke CMake with the "-DWINHTTP=OFF" argument.
+ OPTION( WINHTTP "Use Win32 WinHTTP routines" 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.")
@@ -57,7 +76,18 @@ FUNCTION(TARGET_OS_LIBRARIES target)
TARGET_LINK_LIBRARIES(${target} ws2_32)
ELSEIF(CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
TARGET_LINK_LIBRARIES(${target} socket nsl)
- ENDIF ()
+ SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} -lsocket -lnsl" PARENT_SCOPE)
+ ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Linux")
+ TARGET_LINK_LIBRARIES(${target} rt)
+ SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} -lrt" PARENT_SCOPE)
+ ENDIF()
+
+ IF(USE_ICONV)
+ TARGET_LINK_LIBRARIES(${target} iconv)
+ ADD_DEFINITIONS(-DGIT_USE_ICONV)
+ SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} -liconv" PARENT_SCOPE)
+ ENDIF()
+
IF(THREADSAFE)
TARGET_LINK_LIBRARIES(${target} ${CMAKE_THREAD_LIBS_INIT})
ENDIF()
@@ -67,7 +97,7 @@ ENDFUNCTION()
# explorer does. This is esp. useful with the libgit2_clar project, were
# usually 2 or more files share the same name. Sadly, this file grouping
# is a per-directory option in cmake and not per-target, resulting in
-# empty virtual folders "tests-clar" for the git2.dll
+# empty virtual folders "tests" for the git2.dll
FUNCTION(MSVC_SPLIT_SOURCES target)
IF(MSVC_IDE)
GET_TARGET_PROPERTY(sources ${target} SOURCES)
@@ -96,8 +126,10 @@ SET(LIBGIT2_VERSION_STRING "${LIBGIT2_VERSION_MAJOR}.${LIBGIT2_VERSION_MINOR}.${
# Find required dependencies
INCLUDE_DIRECTORIES(src include)
-IF (WIN32 AND NOT MINGW)
+IF (WIN32 AND WINHTTP AND NOT MINGW)
ADD_DEFINITIONS(-DGIT_WINHTTP)
+ INCLUDE_DIRECTORIES(deps/http-parser)
+ FILE(GLOB SRC_HTTP deps/http-parser/*.c deps/http-parser/*.h)
ELSE ()
IF (NOT AMIGA)
FIND_PACKAGE(OpenSSL)
@@ -107,10 +139,11 @@ ELSE ()
IF (HTTP_PARSER_FOUND AND HTTP_PARSER_VERSION_MAJOR EQUAL 2)
INCLUDE_DIRECTORIES(${HTTP_PARSER_INCLUDE_DIRS})
LINK_LIBRARIES(${HTTP_PARSER_LIBRARIES})
+ SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} -lhttp_parser")
ELSE()
MESSAGE("http-parser was not found or is too old; using bundled 3rd-party sources.")
INCLUDE_DIRECTORIES(deps/http-parser)
- FILE(GLOB SRC_HTTP deps/http-parser/*.c)
+ FILE(GLOB SRC_HTTP deps/http-parser/*.c deps/http-parser/*.h)
ENDIF()
ENDIF()
@@ -120,6 +153,7 @@ IF (WIN32 AND NOT MINGW AND NOT SHA1_TYPE STREQUAL "builtin")
FILE(GLOB SRC_SHA1 src/hash/hash_win32.c)
ELSEIF (OPENSSL_FOUND AND NOT SHA1_TYPE STREQUAL "builtin")
ADD_DEFINITIONS(-DOPENSSL_SHA1)
+ SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} openssl")
ELSE()
FILE(GLOB SRC_SHA1 src/hash/hash_generic.c)
ENDIF()
@@ -130,7 +164,7 @@ IF (ENABLE_TRACE STREQUAL "ON")
ENDIF()
# Include POSIX regex when it is required
-IF(WIN32 OR AMIGA)
+IF(WIN32 OR AMIGA OR ANDROID)
INCLUDE_DIRECTORIES(deps/regex)
SET(SRC_REGEX deps/regex/regex.c)
ENDIF()
@@ -142,24 +176,31 @@ FIND_PACKAGE(ZLIB QUIET)
IF (ZLIB_FOUND)
INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIRS})
LINK_LIBRARIES(${ZLIB_LIBRARIES})
+ IF(APPLE)
+ SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} -lz")
+ ELSE()
+ SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} zlib")
+ ENDIF()
# Fake the message CMake would have shown
MESSAGE("-- Found zlib: ${ZLIB_LIBRARY}")
ELSE()
MESSAGE( "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)
+ FILE(GLOB SRC_ZLIB deps/zlib/*.c deps/zlib/*.h)
ENDIF()
-IF(NOT LIBSSH2_LIBRARY)
+IF (USE_SSH AND NOT MINGW)
FIND_PACKAGE(LIBSSH2 QUIET)
ENDIF()
IF (LIBSSH2_FOUND)
ADD_DEFINITIONS(-DGIT_SSH)
INCLUDE_DIRECTORIES(${LIBSSH2_INCLUDE_DIR})
+ SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} libssh2")
SET(SSH_LIBRARIES ${LIBSSH2_LIBRARIES})
ENDIF()
+
# Platform specific compilation flags
IF (MSVC)
@@ -285,19 +326,19 @@ ENDIF()
ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64)
# Collect sourcefiles
-FILE(GLOB SRC_H include/git2/*.h)
+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)
+ FILE(GLOB SRC_OS src/win32/*.c src/win32/*.h)
ELSEIF (AMIGA)
ADD_DEFINITIONS(-DNO_ADDRINFO -DNO_READDIR_R)
- FILE(GLOB SRC_OS src/amiga/*.c)
+ FILE(GLOB SRC_OS src/amiga/*.c src/amiga/*.h)
ELSE()
- FILE(GLOB SRC_OS src/unix/*.c)
+ FILE(GLOB SRC_OS src/unix/*.c src/unix/*.h)
ENDIF()
-FILE(GLOB SRC_GIT2 src/*.c src/transports/*.c src/xdiff/*.c)
+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)
@@ -309,7 +350,7 @@ ELSE()
ENDIF()
# Compile and link libgit2
-ADD_LIBRARY(git2 ${SRC_GIT2} ${SRC_OS} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1} ${WIN_RC})
+ADD_LIBRARY(git2 ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1} ${WIN_RC})
TARGET_LINK_LIBRARIES(git2 ${SSL_LIBRARIES})
TARGET_LINK_LIBRARIES(git2 ${SSH_LIBRARIES})
TARGET_OS_LIBRARIES(git2)
@@ -352,19 +393,19 @@ INSTALL(FILES include/git2.h DESTINATION ${INCLUDE_INSTALL_DIR} )
IF (BUILD_CLAR)
FIND_PACKAGE(PythonInterp REQUIRED)
- SET(CLAR_FIXTURES "${CMAKE_CURRENT_SOURCE_DIR}/tests-clar/resources/")
- SET(CLAR_PATH "${CMAKE_CURRENT_SOURCE_DIR}/tests-clar")
- SET(CLAR_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/tests-clar/resources" CACHE PATH "Path to test resources.")
+ 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}\")
INCLUDE_DIRECTORIES(${CLAR_PATH})
- FILE(GLOB_RECURSE SRC_TEST ${CLAR_PATH}/*/*.c)
+ 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.c")
ADD_CUSTOM_COMMAND(
OUTPUT ${CLAR_PATH}/clar.suite
- COMMAND ${PYTHON_EXECUTABLE} generate.py -f -xonline .
+ COMMAND ${PYTHON_EXECUTABLE} generate.py -f -xonline -xstress .
DEPENDS ${SRC_TEST}
WORKING_DIRECTORY ${CLAR_PATH}
)
@@ -373,7 +414,7 @@ IF (BUILD_CLAR)
${CLAR_PATH}/clar.c
PROPERTIES OBJECT_DEPENDS ${CLAR_PATH}/clar.suite)
- ADD_EXECUTABLE(libgit2_clar ${SRC_GIT2} ${SRC_OS} ${SRC_CLAR} ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1})
+ ADD_EXECUTABLE(libgit2_clar ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_CLAR} ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1})
TARGET_LINK_LIBRARIES(libgit2_clar ${SSL_LIBRARIES})
TARGET_LINK_LIBRARIES(libgit2_clar ${SSH_LIBRARIES})
@@ -409,23 +450,5 @@ IF (TAGS)
ENDIF ()
IF (BUILD_EXAMPLES)
- FILE(GLOB_RECURSE EXAMPLE_SRC examples/network/*.c)
- ADD_EXECUTABLE(cgit2 ${EXAMPLE_SRC})
- IF(WIN32)
- TARGET_LINK_LIBRARIES(cgit2 git2)
- ELSE()
- TARGET_LINK_LIBRARIES(cgit2 git2 pthread)
- ENDIF()
-
- ADD_EXECUTABLE(git-diff examples/diff.c)
- TARGET_LINK_LIBRARIES(git-diff git2)
-
- ADD_EXECUTABLE(git-general examples/general.c)
- TARGET_LINK_LIBRARIES(git-general git2)
-
- ADD_EXECUTABLE(git-showindex examples/showindex.c)
- TARGET_LINK_LIBRARIES(git-showindex git2)
-
- ADD_EXECUTABLE(git-rev-list examples/rev-list.c)
- TARGET_LINK_LIBRARIES(git-rev-list git2)
+ ADD_SUBDIRECTORY(examples)
ENDIF ()
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 28ef27f42..807cd5320 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -3,6 +3,12 @@
We're making it easy to do interesting things with git, and we'd love to have
your help.
+## Licensing
+
+By contributing to libgit2, you agree to release your contribution under the terms of the license.
+For code under `examples`, this is governed by the [CC0 Public Domain Dedication](examples/COPYING).
+All other code is released under the [GPL v2 with linking exception](COPYING).
+
## Discussion & Chat
We hang out in the #libgit2 channel on irc.freenode.net.
@@ -48,6 +54,12 @@ Please include a nice description of your changes with your PR; if we have
to read the whole diff to figure out why you're contributing in the first
place, you're less likely to get feedback and have your change merged in.
+If you are working on a particular area then feel free to submit a PR that
+highlights your work in progress (and flag in the PR title that it's not
+ready to merge). This will help in getting visibility for your fix, allow
+others to comment early on the changes and also let others know that you
+are currently working on something.
+
## Porting Code From Other Open-Source Projects
`libgit2` is licensed under the terms of the GPL v2 with a linking
@@ -57,14 +69,17 @@ The most common case is porting code from core Git. Git is a pure GPL
project, which means that in order to port code to this project, we need the
explicit permission of the author. Check the
[`git.git-authors`](https://github.com/libgit2/libgit2/blob/development/git.git-authors)
-file for authors who have already consented; feel free to add someone if
-you've obtained their consent.
+file for authors who have already consented.
Other licenses have other requirements; check the license of the library
you're porting code *from* to see what you need to do. As a general rule,
MIT and BSD (3-clause) licenses are typically no problem. Apache 2.0
license typically doesn't work due to GPL incompatibility.
+If you are pulling in code from core Git, another project or code you've pulled from
+a forum / Stack Overflow then please flag this in your PR and also make sure you've
+given proper credit to the original author in the code snippet.
+
## Style Guide
`libgit2` is written in [ANSI C](http://en.wikipedia.org/wiki/ANSI_C)
diff --git a/COPYING b/COPYING
index d1ca4d401..f7e9f3af7 100644
--- a/COPYING
+++ b/COPYING
@@ -928,3 +928,66 @@ necessary. Here is a sample; alter the names:
Ty Coon, President of Vice
That's all there is to it!
+
+----------------------------------------------------------------------
+
+Portions of src/win32/posix_w32.c are derrived from link_win32.c in PHP:
+
+--------------------------------------------------------------------
+ The PHP License, version 3.01
+Copyright (c) 1999 - 2012 The PHP Group. All rights reserved.
+--------------------------------------------------------------------
+
+Redistribution and use in source and binary forms, with or without
+modification, is permitted provided that the following conditions
+are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ 3. The name "PHP" must not be used to endorse or promote products
+ derived from this software without prior written permission. For
+ written permission, please contact group@php.net.
+
+ 4. Products derived from this software may not be called "PHP", nor
+ may "PHP" appear in their name, without prior written permission
+ from group@php.net. You may indicate that your software works in
+ conjunction with PHP by saying "Foo for PHP" instead of calling
+ it "PHP Foo" or "phpfoo"
+
+ 5. The PHP Group may publish revised and/or new versions of the
+ license from time to time. Each version will be given a
+ distinguishing version number.
+ Once covered code has been published under a particular version
+ of the license, you may always continue to use it under the terms
+ of that version. You may also choose to use such covered code
+ under the terms of any subsequent version of the license
+ published by the PHP Group. No one other than the PHP Group has
+ the right to modify the terms applicable to covered code created
+ under this License.
+
+ 6. Redistributions of any form whatsoever must retain the following
+ acknowledgment:
+ "This product includes PHP software, freely available from
+ <http://www.php.net/software/>".
+
+THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND
+ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP
+DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+
+--------------------------------------------------------------------
+
diff --git a/Makefile.embed b/Makefile.embed
index 76b4d3cda..eb8a78ebf 100644
--- a/Makefile.embed
+++ b/Makefile.embed
@@ -1,26 +1,44 @@
-PLATFORM=$(shell uname -o)
+PLATFORM=$(shell uname -s)
+
+ifneq (,$(CROSS_COMPILE))
+ PREFIX=$(CROSS_COMPILE)-
+else
+ PREFIX=
+endif
+
+MINGW=0
+ifneq (,$(findstring MINGW32,$(PLATFORM)))
+ MINGW=1
+endif
+ifneq (,$(findstring mingw,$(CROSS_COMPILE)))
+ MINGW=1
+endif
rm=rm -f
-AR=ar cq
-RANLIB=ranlib
+AR=$(PREFIX)ar cq
+RANLIB=$(PREFIX)ranlib
+
LIBNAME=libgit2.a
-ifeq ($(PLATFORM),Msys)
+
+ifeq ($(MINGW),1)
CC=gcc
else
CC=cc
endif
+CC:=$(PREFIX)$(CC)
+
INCLUDES= -I. -Isrc -Iinclude -Ideps/http-parser -Ideps/zlib
DEFINES= $(INCLUDES) -DNO_VIZ -DSTDC -DNO_GZIP -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE $(EXTRA_DEFINES)
-CFLAGS= -g $(DEFINES) -Wall -Wextra -O2 $(EXTRA_CFLAGS)
+CFLAGS= -g $(DEFINES) -Wall -Wextra -Wno-missing-field-initializers -O2 $(EXTRA_CFLAGS)
SRCS = $(wildcard src/*.c) $(wildcard src/transports/*.c) $(wildcard src/xdiff/*.c) $(wildcard deps/http-parser/*.c) $(wildcard deps/zlib/*.c) src/hash/hash_generic.c
-ifeq ($(PLATFORM),Msys)
+ifeq ($(MINGW),1)
SRCS += $(wildcard src/win32/*.c) $(wildcard src/compat/*.c) deps/regex/regex.c
INCLUDES += -Ideps/regex
- DEFINES += -DWIN32 -D_WIN32_WINNT=0x0501
+ DEFINES += -DWIN32 -D_WIN32_WINNT=0x0501 -D__USE_MINGW_ANSI_STDIO=1
else
SRCS += $(wildcard src/unix/*.c)
CFLAGS += -fPIC
diff --git a/README.md b/README.md
index a2a18765a..8ce60af62 100644
--- a/README.md
+++ b/README.md
@@ -1,30 +1,36 @@
libgit2 - the Git linkable library
-======================
+==================================
[![Build Status](https://secure.travis-ci.org/libgit2/libgit2.png?branch=development)](http://travis-ci.org/libgit2/libgit2)
-libgit2 is a portable, pure C implementation of the Git core methods provided as a
+`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.
-libgit2 is licensed under a **very permissive license** (GPLv2 with a special Linking Exception).
-This basically means that you can link it (unmodified) with any kind of software without having to
-release its source code.
-
-* Mailing list: ~~<libgit2@librelist.org>~~
- The libgit2 mailing list has
- traditionally been hosted in Librelist, but Librelist is and has always
- been a shitshow. We encourage you to [open an issue](https://github.com/libgit2/libgit2/issues)
- on GitHub instead for any questions regarding the library.
- * Archives: <http://librelist.com/browser/libgit2/>
-* Website: <http://libgit2.github.com>
+`libgit2` is licensed under a **very permissive license** (GPLv2 with a special
+Linking Exception). This basically means that you can link it (unmodified)
+with any kind of software without having to release its source code.
+Additionally, the example code has been released to the public domain (see the
+[separate license](examples/COPYING) for more information).
+
+* Website: [libgit2.github.com](http://libgit2.github.com)
+* StackOverflow Tag: [libgit2](http://stackoverflow.com/questions/tagged/libgit2)
+* Issues: [GitHub Issues](https://github.com/libgit2/libgit2/issues) (Right here!)
* API documentation: <http://libgit2.github.com/libgit2>
-* IRC: #libgit2 on irc.freenode.net.
+* IRC: [#libgit2](irc://irc.freenode.net/libgit2) on irc.freenode.net.
+* Mailing list: The libgit2 mailing list was
+ traditionally hosted in Librelist but has been deprecated. We encourage you to
+ [use StackOverflow](http://stackoverflow.com/questions/tagged/libgit2) instead for any questions regarding
+ the library, or [open an issue](https://github.com/libgit2/libgit2/issues)
+ on GitHub for bug reports. The mailing list archives are still available at
+ <http://librelist.com/browser/libgit2/>.
+
What It Can Do
-==================================
+==============
-libgit2 is already very usable.
+`libgit2` is already very usable and is being used in production for many applications including the GitHub.com site, in Plastic SCM
+and also powering Microsoft's Visual Studio tools for Git. The library provides:
* SHA conversions, formatting and shortening
* abstracted ODB backend system
@@ -39,15 +45,26 @@ libgit2 is already very usable.
* descriptive and detailed error messages
* ...and more (over 175 different API calls)
+Optional dependencies
+=====================
+
+While the library provides git functionality without the need for
+dependencies, it can make use of a few libraries to add to it:
+
+- pthreads (non-Windows) to enable threadsafe access as well as multi-threaded pack generation
+- OpenSSL (non-Windows) to talk over HTTPS and provide the SHA-1 functions
+- LibSSH2 to enable the ssh transport
+- iconv (OSX) to handle the HFS+ path encoding peculiarities
+
Building libgit2 - Using CMake
==============================
-libgit2 builds cleanly on most platforms without any external dependencies.
+`libgit2` builds cleanly on most platforms without any external dependencies.
Under Unix-like systems, like Linux, \*BSD and Mac OS X, libgit2 expects `pthreads` to be available;
they should be installed by default on all systems. Under Windows, libgit2 uses the native Windows API
for threading.
-The libgit2 library is built using CMake 2.6+ (<http://www.cmake.org>) on all platforms.
+The `libgit2` library is built using `CMake 2.6+` (<http://www.cmake.org>) on all platforms.
On most systems you can build the library using the following commands
@@ -104,6 +121,28 @@ See [the wiki]
(https://github.com/libgit2/libgit2/wiki/Building-libgit2-on-Windows)
for more detailed instructions.
+Android
+-------
+
+Extract toolchain from NDK using, `make-standalone-toolchain.sh` script.
+Optionally, crosscompile and install OpenSSL inside of it. Then create CMake
+toolchain file that configures paths to your crosscompiler (substitute `{PATH}`
+with full path to the toolchain):
+
+ SET(CMAKE_SYSTEM_NAME Linux)
+ SET(CMAKE_SYSTEM_VERSION Android)
+
+ SET(CMAKE_C_COMPILER {PATH}/bin/arm-linux-androideabi-gcc)
+ SET(CMAKE_CXX_COMPILER {PATH}/bin/arm-linux-androideabi-g++)
+ SET(CMAKE_FIND_ROOT_PATH {PATH}/sysroot/)
+
+ SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+ SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+ SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+
+Add `-DCMAKE_TOOLCHAIN_FILE={pathToToolchainFile} -DANDROID=1` to cmake command
+when configuring.
+
Language Bindings
==================================
@@ -118,9 +157,9 @@ Here are the bindings to libgit2 that are currently available:
* Delphi
* GitForDelphi <https://github.com/libgit2/GitForDelphi>
* Erlang
- * Geef <https://github.com/schacon/geef>
+ * Geef <https://github.com/carlosmn/geef>
* Go
- * go-git <https://github.com/str1ngs/go-git>
+ * git2go <https://github.com/libgit2/git2go>
* GObject
* libgit2-glib <https://live.gnome.org/Libgit2-glib>
* Haskell
@@ -128,8 +167,8 @@ Here are the bindings to libgit2 that are currently available:
* Lua
* luagit2 <https://github.com/libgit2/luagit2>
* .NET
- * libgit2net, low level bindings <https://github.com/txdv/libgit2net>
* libgit2sharp <https://github.com/libgit2/libgit2sharp>
+ * libgit2net, low level bindings superseded by libgit2sharp <https://github.com/txdv/libgit2net>
* Node.js
* node-gitteh <https://github.com/libgit2/node-gitteh>
* nodegit <https://github.com/tbranyen/nodegit>
@@ -161,9 +200,9 @@ Check the [contribution guidelines](CONTRIBUTING.md).
License
==================================
-libgit2 is under GPL2 **with linking exemption**. This means you
-can link to the library with any program, commercial, open source or
-other. However, you cannot modify libgit2 and distribute it without
+`libgit2` is under GPL2 **with linking exemption**. This means you
+can link to and use the library from any program, proprietary or open source; paid
+or gratis. However, you cannot modify libgit2 and distribute it without
supplying the source.
See the COPYING file for the full license text.
diff --git a/docs/checkout-internals.md b/docs/checkout-internals.md
index cb646da5d..6147ffdd8 100644
--- a/docs/checkout-internals.md
+++ b/docs/checkout-internals.md
@@ -71,19 +71,19 @@ Key
Diff with 2 non-workdir iterators
---------------------------------
- Old New
- --- ---
- 0 x x - nothing
- 1 x B1 - added blob
- 2 x T1 - added tree
- 3 B1 x - removed blob
- 4 B1 B1 - unmodified blob
- 5 B1 B2 - modified blob
- 6 B1 T1 - typechange blob -> tree
- 7 T1 x - removed tree
- 8 T1 B1 - typechange tree -> blob
- 9 T1 T1 - unmodified tree
- 10 T1 T2 - modified tree (implies modified/added/removed blob inside)
+| | Old | New | |
+|----|-----|-----|------------------------------------------------------------|
+| 0 | x | x | nothing |
+| 1 | x | B1 | added blob |
+| 2 | x | T1 | added tree |
+| 3 | B1 | x | removed blob |
+| 4 | B1 | B1 | unmodified blob |
+| 5 | B1 | B2 | modified blob |
+| 6 | B1 | T1 | typechange blob -> tree |
+| 7 | T1 | x | removed tree |
+| 8 | T1 | B1 | typechange tree -> blob |
+| 9 | T1 | T1 | unmodified tree |
+| 10 | T1 | T2 | modified tree (implies modified/added/removed blob inside) |
Now, let's make the "New" iterator into a working directory iterator, so
@@ -92,23 +92,23 @@ we replace "added" items with either untracked or ignored, like this:
Diff with non-work & workdir iterators
--------------------------------------
- Old New-WD
- --- ------
- 0 x x - nothing
- 1 x B1 - untracked blob
- 2 x Bi - ignored file
- 3 x T1 - untracked tree
- 4 x Ti - ignored tree
- 5 B1 x - removed blob
- 6 B1 B1 - unmodified blob
- 7 B1 B2 - modified blob
- 8 B1 T1 - typechange blob -> tree
- 9 B1 Ti - removed blob AND ignored tree as separate items
- 10 T1 x - removed tree
- 11 T1 B1 - typechange tree -> blob
- 12 T1 Bi - removed tree AND ignored blob as separate items
- 13 T1 T1 - unmodified tree
- 14 T1 T2 - modified tree (implies modified/added/removed blob inside)
+| | Old | New | |
+|----|-----|-----|------------------------------------------------------------|
+| 0 | x | x | nothing |
+| 1 | x | B1 | untracked blob |
+| 2 | x | Bi | ignored file |
+| 3 | x | T1 | untracked tree |
+| 4 | x | Ti | ignored tree |
+| 5 | B1 | x | removed blob |
+| 6 | B1 | B1 | unmodified blob |
+| 7 | B1 | B2 | modified blob |
+| 8 | B1 | T1 | typechange blob -> tree |
+| 9 | B1 | Ti | removed blob AND ignored tree as separate items |
+| 10 | T1 | x | removed tree |
+| 11 | T1 | B1 | typechange tree -> blob |
+| 12 | T1 | Bi | removed tree AND ignored blob as separate items |
+| 13 | T1 | T1 | unmodified tree |
+| 14 | T1 | T2 | modified tree (implies modified/added/removed blob inside) |
Note: if there is a corresponding entry in the old tree, then a working
directory item won't be ignored (i.e. no Bi or Ti for tracked items).
@@ -122,46 +122,47 @@ Checkout From 3 Iterators (2 not workdir, 1 workdir)
(base == old HEAD; target == what to checkout; actual == working dir)
- base target actual/workdir
- ---- ------ ------
- 0 x x x - nothing
- 1 x x B1/Bi/T1/Ti - untracked/ignored blob/tree (SAFE)
- 2+ x B1 x - add blob (SAFE)
- 3 x B1 B1 - independently added blob (FORCEABLE-2)
- 4* x B1 B2/Bi/T1/Ti - add blob with content conflict (FORCEABLE-2)
- 5+ x T1 x - add tree (SAFE)
- 6* x T1 B1/Bi - add tree with blob conflict (FORCEABLE-2)
- 7 x T1 T1/i - independently added tree (SAFE+MISSING)
- 8 B1 x x - independently deleted blob (SAFE+MISSING)
- 9- B1 x B1 - delete blob (SAFE)
- 10- B1 x B2 - delete of modified blob (FORCEABLE-1)
- 11 B1 x T1/Ti - independently deleted blob AND untrack/ign tree (SAFE+MISSING !!!)
- 12 B1 B1 x - locally deleted blob (DIRTY || SAFE+CREATE)
- 13+ B1 B2 x - update to deleted blob (SAFE+MISSING)
- 14 B1 B1 B1 - unmodified file (SAFE)
- 15 B1 B1 B2 - locally modified file (DIRTY)
- 16+ B1 B2 B1 - update unmodified blob (SAFE)
- 17 B1 B2 B2 - independently updated blob (FORCEABLE-1)
- 18+ B1 B2 B3 - update to modified blob (FORCEABLE-1)
- 19 B1 B1 T1/Ti - locally deleted blob AND untrack/ign tree (DIRTY)
- 20* B1 B2 T1/Ti - update to deleted blob AND untrack/ign tree (F-1)
- 21+ B1 T1 x - add tree with locally deleted blob (SAFE+MISSING)
- 22* B1 T1 B1 - add tree AND deleted blob (SAFE)
- 23* B1 T1 B2 - add tree with delete of modified blob (F-1)
- 24 B1 T1 T1 - add tree with deleted blob (F-1)
- 25 T1 x x - independently deleted tree (SAFE+MISSING)
- 26 T1 x B1/Bi - independently deleted tree AND untrack/ign blob (F-1)
- 27- T1 x T1 - deleted tree (MAYBE SAFE)
- 28+ T1 B1 x - deleted tree AND added blob (SAFE+MISSING)
- 29 T1 B1 B1 - independently typechanged tree -> blob (F-1)
- 30+ T1 B1 B2 - typechange tree->blob with conflicting blob (F-1)
- 31* T1 B1 T1/T2 - typechange tree->blob (MAYBE SAFE)
- 32+ T1 T1 x - restore locally deleted tree (SAFE+MISSING)
- 33 T1 T1 B1/Bi - locally typechange tree->untrack/ign blob (DIRTY)
- 34 T1 T1 T1/T2 - unmodified tree (MAYBE SAFE)
- 35+ T1 T2 x - update locally deleted tree (SAFE+MISSING)
- 36* T1 T2 B1/Bi - update to tree with typechanged tree->blob conflict (F-1)
- 37 T1 T2 T1/T2/T3 - update to existing tree (MAYBE SAFE)
+| |base | target | actual/workdir | |
+|-----|-----|------- |----------------|--------------------------------------------------------------------|
+| 0 | x | x | x | nothing |
+| 1 | x | x | B1/Bi/T1/Ti | untracked/ignored blob/tree (SAFE) |
+| 2+ | x | B1 | x | add blob (SAFE) |
+| 3 | x | B1 | B1 | independently added blob (FORCEABLE-2) |
+| 4* | x | B1 | B2/Bi/T1/Ti | add blob with content conflict (FORCEABLE-2) |
+| 5+ | x | T1 | x | add tree (SAFE) |
+| 6* | x | T1 | B1/Bi | add tree with blob conflict (FORCEABLE-2) |
+| 7 | x | T1 | T1/i | independently added tree (SAFE+MISSING) |
+| 8 | B1 | x | x | independently deleted blob (SAFE+MISSING) |
+| 9- | B1 | x | B1 | delete blob (SAFE) |
+| 10- | B1 | x | B2 | delete of modified blob (FORCEABLE-1) |
+| 11 | B1 | x | T1/Ti | independently deleted blob AND untrack/ign tree (SAFE+MISSING !!!) |
+| 12 | B1 | B1 | x | locally deleted blob (DIRTY || SAFE+CREATE) |
+| 13+ | B1 | B2 | x | update to deleted blob (SAFE+MISSING) |
+| 14 | B1 | B1 | B1 | unmodified file (SAFE) |
+| 15 | B1 | B1 | B2 | locally modified file (DIRTY) |
+| 16+ | B1 | B2 | B1 | update unmodified blob (SAFE) |
+| 17 | B1 | B2 | B2 | independently updated blob (FORCEABLE-1) |
+| 18+ | B1 | B2 | B3 | update to modified blob (FORCEABLE-1) |
+| 19 | B1 | B1 | T1/Ti | locally deleted blob AND untrack/ign tree (DIRTY) |
+| 20* | B1 | B2 | T1/Ti | update to deleted blob AND untrack/ign tree (F-1) |
+| 21+ | B1 | T1 | x | add tree with locally deleted blob (SAFE+MISSING) |
+| 22* | B1 | T1 | B1 | add tree AND deleted blob (SAFE) |
+| 23* | B1 | T1 | B2 | add tree with delete of modified blob (F-1) |
+| 24 | B1 | T1 | T1 | add tree with deleted blob (F-1) |
+| 25 | T1 | x | x | independently deleted tree (SAFE+MISSING) |
+| 26 | T1 | x | B1/Bi | independently deleted tree AND untrack/ign blob (F-1) |
+| 27- | T1 | x | T1 | deleted tree (MAYBE SAFE) |
+| 28+ | T1 | B1 | x | deleted tree AND added blob (SAFE+MISSING) |
+| 29 | T1 | B1 | B1 | independently typechanged tree -> blob (F-1) |
+| 30+ | T1 | B1 | B2 | typechange tree->blob with conflicting blob (F-1) |
+| 31* | T1 | B1 | T1/T2 | typechange tree->blob (MAYBE SAFE) |
+| 32+ | T1 | T1 | x | restore locally deleted tree (SAFE+MISSING) |
+| 33 | T1 | T1 | B1/Bi | locally typechange tree->untrack/ign blob (DIRTY) |
+| 34 | T1 | T1 | T1/T2 | unmodified tree (MAYBE SAFE) |
+| 35+ | T1 | T2 | x | update locally deleted tree (SAFE+MISSING) |
+| 36* | T1 | T2 | B1/Bi | update to tree with typechanged tree->blob conflict (F-1) |
+| 37 | T1 | T2 | T1/T2/T3 | update to existing tree (MAYBE SAFE) |
+
The number is followed by ' ' if no change is needed or '+' if the case
needs to write to disk or '-' if something must be deleted and '*' if
@@ -169,34 +170,34 @@ there should be a delete followed by an write.
There are four tiers of safe cases:
-- SAFE == completely safe to update
-- SAFE+MISSING == safe except the workdir is missing the expect content
-- MAYBE SAFE == safe if workdir tree matches (or is missing) baseline
+* SAFE == completely safe to update
+* SAFE+MISSING == safe except the workdir is missing the expect content
+* MAYBE SAFE == safe if workdir tree matches (or is missing) baseline
content, which is unknown at this point
-- FORCEABLE == conflict unless FORCE is given
-- DIRTY == no conflict but change is not applied unless FORCE
+* FORCEABLE == conflict unless FORCE is given
+* DIRTY == no conflict but change is not applied unless FORCE
Some slightly unusual circumstances:
- 8 - parent dir is only deleted when file is, so parent will be left if
- empty even though it would be deleted if the file were present
- 11 - core git does not consider this a conflict but attempts to delete T1
- and gives "unable to unlink file" error yet does not skip the rest
- of the operation
- 12 - without FORCE file is left deleted (i.e. not restored) so new wd is
- dirty (and warning message "D file" is printed), with FORCE, file is
- restored.
- 24 - This should be considered MAYBE SAFE since effectively it is 7 and 8
- combined, but core git considers this a conflict unless forced.
- 26 - This combines two cases (1 & 25) (and also implied 8 for tree content)
- which are ok on their own, but core git treat this as a conflict.
- If not forced, this is a conflict. If forced, this actually doesn't
- have to write anything and leaves the new blob as an untracked file.
- 32 - This is the only case where the baseline and target values match
- and yet we will still write to the working directory. In all other
- cases, if baseline == target, we don't touch the workdir (it is
- either already right or is "dirty"). However, since this case also
- implies that a ?/B1/x case will exist as well, it can be skipped.
+* 8 - parent dir is only deleted when file is, so parent will be left if
+ empty even though it would be deleted if the file were present
+* 11 - core git does not consider this a conflict but attempts to delete T1
+ and gives "unable to unlink file" error yet does not skip the rest
+ of the operation
+* 12 - without FORCE file is left deleted (i.e. not restored) so new wd is
+ dirty (and warning message "D file" is printed), with FORCE, file is
+ restored.
+* 24 - This should be considered MAYBE SAFE since effectively it is 7 and 8
+ combined, but core git considers this a conflict unless forced.
+* 26 - This combines two cases (1 & 25) (and also implied 8 for tree content)
+ which are ok on their own, but core git treat this as a conflict.
+ If not forced, this is a conflict. If forced, this actually doesn't
+ have to write anything and leaves the new blob as an untracked file.
+* 32 - This is the only case where the baseline and target values match
+ and yet we will still write to the working directory. In all other
+ cases, if baseline == target, we don't touch the workdir (it is
+ either already right or is "dirty"). However, since this case also
+ implies that a ?/B1/x case will exist as well, it can be skipped.
Cases 3, 17, 24, 26, and 29 are all considered conflicts even though
none of them will require making any updates to the working directory.
diff --git a/docs/diff-internals.md b/docs/diff-internals.md
index 53e71f5b5..cf8ad5383 100644
--- a/docs/diff-internals.md
+++ b/docs/diff-internals.md
@@ -45,44 +45,48 @@ Internal Objects
* `git_diff_file_content` is an internal structure that represents the
data on one side of an item to be diffed; it is an augmented
`git_diff_file` with more flags and the actual file data.
-** it is created from a repository plus a) a git_diff_file, b) a git_blob,
+
+ * it is created from a repository plus a) a git_diff_file, b) a git_blob,
or c) raw data and size
-** there are three main operations on git_diff_file_content:
-*** _initialization_ sets up the data structure and does what it can up to,
- but not including loading and looking at the actual data
-*** _loading_ loads the data, preprocesses it (i.e. applies filters) and
- potentially analyzes it (to decide if binary)
-*** _free_ releases loaded data and frees any allocated memory
+ * there are three main operations on git_diff_file_content:
+
+ * _initialization_ sets up the data structure and does what it can up to,
+ but not including loading and looking at the actual data
+ * _loading_ loads the data, preprocesses it (i.e. applies filters) and
+ potentially analyzes it (to decide if binary)
+ * _free_ releases loaded data and frees any allocated memory
* The internal structure of a `git_diff_patch` stores the actual diff
between a pair of `git_diff_file_content` items
-** it may be "unset" if the items are not diffable
-** "empty" if the items are the same
-** otherwise it will consist of a set of hunks each of which covers some
- number of lines of context, additions and deletions
-** a patch is created from two git_diff_file_content items
-** a patch is fully instantiated in three phases:
-*** initial creation and initialization
-*** loading of data and preliminary data examination
-*** diffing of data and optional storage of diffs
-** (TBD) if a patch is asked to store the diffs and the size of the diff
- is significantly smaller than the raw data of the two sides, then the
- patch may be flattened using a pool of string data
+
+ * it may be "unset" if the items are not diffable
+ * "empty" if the items are the same
+ * otherwise it will consist of a set of hunks each of which covers some
+ number of lines of context, additions and deletions
+ * a patch is created from two git_diff_file_content items
+ * a patch is fully instantiated in three phases:
+
+ * initial creation and initialization
+ * loading of data and preliminary data examination
+ * diffing of data and optional storage of diffs
+ * (TBD) if a patch is asked to store the diffs and the size of the diff
+ is significantly smaller than the raw data of the two sides, then the
+ patch may be flattened using a pool of string data
* `git_diff_output` is an internal structure that represents an output
target for a `git_diff_patch`
-** It consists of file, hunk, and line callbacks, plus a payload
-** There is a standard flattened output that can be used for plain text output
-** Typically we use a `git_xdiff_output` which drives the callbacks via the
- xdiff code taken from core Git.
+ * It consists of file, hunk, and line callbacks, plus a payload
+ * There is a standard flattened output that can be used for plain text output
+ * Typically we use a `git_xdiff_output` which drives the callbacks via the
+ xdiff code taken from core Git.
* `git_diff_driver` is an internal structure that encapsulates the logic
for a given type of file
-** a driver is looked up based on the name and mode of a file.
-** the driver can then be used to:
-*** determine if a file is binary (by attributes, by git_diff_options
- settings, or by examining the content)
-*** give you a function pointer that is used to evaluate function context
- for hunk headers
-** At some point, the logic for getting a filtered version of file content
- or calculating the OID of a file may be moved into the driver.
+ * a driver is looked up based on the name and mode of a file.
+ * the driver can then be used to:
+ * determine if a file is binary (by attributes, by git_diff_options
+ settings, or by examining the content)
+ * give you a function pointer that is used to evaluate function context
+ for hunk headers
+ * At some point, the logic for getting a filtered version of file content
+ or calculating the OID of a file may be moved into the driver.
diff --git a/docs/error-handling.md b/docs/error-handling.md
index 655afeba8..2dbe64a71 100644
--- a/docs/error-handling.md
+++ b/docs/error-handling.md
@@ -1,111 +1,270 @@
Error reporting in libgit2
==========================
-Error reporting is performed on an explicit `git_error **` argument, which appears at the end of all API calls that can return an error. Yes, this does clutter the API.
-
-When a function fails, an error is set on the error variable **and** returns one of the generic error codes.
+Libgit2 tries to follow the POSIX style: functions return an `int` value
+with 0 (zero) indicating success and negative values indicating an error.
+There are specific negative error codes for each "expected failure"
+(e.g. `GIT_ENOTFOUND` for files that take a path which might be missing)
+and a generic error code (-1) for all critical or non-specific failures
+(e.g. running out of memory or system corruption).
+
+When a negative value is returned, an error message is also set. The
+message can be accessed via the `giterr_last` function which will return a
+pointer to a `git_error` structure containing the error message text and
+the class of error (i.e. what part of the library generated the error).
+
+For instance: An object lookup by SHA prefix (`git_object_lookup_prefix`)
+has two expected failure cases: the SHA is not found at all which returns
+`GIT_ENOTFOUND` or the SHA prefix is ambiguous (i.e. two or more objects
+share the prefix) which returns `GIT_EAMBIGUOUS`. There are any number of
+critical failures (such as a packfile being corrupted, a loose object
+having the wrong access permissions, etc.) all of which will return -1.
+When the object lookup is successful, it will return 0.
+
+If libgit2 was compiled with threads enabled (`-DTHREADSAFE=ON` when using
+CMake), then the error message will be kept in thread-local storage, so it
+will not be modified by other threads. If threads are not enabled, then
+the error message is in global data.
+
+All of the error return codes, the `git_error` type, the error access
+functions, and the error classes are defined in `include/git2/errors.h`.
+See the documentation there for details on the APIs for accessing,
+clearing, and even setting error codes.
+
+When writing libgit2 code, please be smart and conservative when returning
+error codes. Functions usually have a maximum of two or three "expected
+errors" and in most cases only one. If you feel there are more possible
+expected error scenarios, then the API you are writing may be at too high
+a level for core libgit2.
+
+Example usage
+-------------
+
+When using libgit2, you will typically capture the return value from
+functions using an `int` variable and check to see if it is negative.
+When that happens, you can, if you wish, look at the specific value or
+look at the error message that was generated.
~~~c
-int git_repository_open(git_repository **repository, const char *path, git_error **error)
{
- // perform some opening
- if (p_exists(path) < 0) {
- giterr_set(error, GITERR_REPOSITORY, "The path '%s' doesn't exist", path);
- return GIT_ENOTFOUND;
- }
+ git_repository *repo;
+ int error = git_repository_open(&repo, "path/to/repo");
- ...
+ if (error < 0) {
+ fprintf(stderr, "Could not open repository: %s\n", giterr_last()->message);
+ exit(1);
+ }
- if (try_to_parse(path, error) < 0)
- return GIT_ERROR;
+ ... use `repo` here ...
- ...
+ git_repository_free(repo); /* void function - no error return code */
}
~~~
-The simple error API
---------------------
+Some of the error return values do have meaning. Optionally, you can look
+at the specific error values to decide what to do.
+
+~~~c
+{
+ git_repository *repo;
+ const char *path = "path/to/repo";
+ int error = git_repository_open(&repo, path);
+
+ if (error < 0) {
+ if (error == GIT_ENOTFOUND)
+ fprintf(stderr, "Could not find repository at path '%s'\n", path);
+ else
+ fprintf(stderr, "Unable to open repository: %s\n",
+ giterr_last()->message);
+ exit(1);
+ }
-- `void giterr_set(git_error **, int, const char *, ...)`: the main function used to set an error. It allocates a new error object and stores it in the passed error pointer. It has no return value. The arguments for `giterr_set` are as follows:
+ ... happy ...
+}
+~~~
- - `git_error **error_ptr`: the pointer where the error will be created.
- - `int error_class`: the class for the error. This is **not** an error code: this is an specific enum that specifies the error family. The point is to map these families 1-1 with Exception types on higher level languages (e.g. GitRepositoryException)
- - `const char *error_str, ...`: the error string, with optional formatting arguments
+Some of the higher-level language bindings may use a range of information
+from libgit2 to convert error return codes into exceptions, including the
+specific error return codes and even the class of error and the error
+message returned by `giterr_last`, but the full range of that logic is
+beyond the scope of this document.
-- `void giterr_free(git_error *)`: takes an error and frees it. This function is available in the external API.
+Example internal implementation
+-------------------------------
-- `void giterr_clear(git_error **)`: clears an error previously set in an error pointer, setting it to NULL and calling `giterr_free` on it.
+Internally, libgit2 detects error scenarios, records error messages, and
+returns error values. Errors from low-level functions are generally
+passed upwards (unless the higher level can either handle the error or
+wants to translate the error into something more meaningful).
-- `void giterr_propagate(git_error **, git_error *)`: moves an error to a given error pointer, handling the case when the error pointer is NULL (in that case the error gets freed, because it cannot be propagated).
+~~~c
+int git_repository_open(git_repository **repository, const char *path)
+{
+ /* perform some logic to open the repository */
+ if (p_exists(path) < 0) {
+ giterr_set(GITERR_REPOSITORY, "The path '%s' doesn't exist", path);
+ return GIT_ENOTFOUND;
+ }
-The new error code return values
---------------------------------
+ ...
+}
+~~~
-We are doing this the POSIX way: one error code for each "expected failure", and a generic error code for all the critical failures.
+The public error API
+--------------------
-For instance: A reference lookup can have an expected failure (which is when the reference cannot be found), and a critical failure (which could be any of a long list of things that could go wrong, such as the refs packfile being corrupted, a loose ref being written with the wrong permissions, etc). We cannot have distinct error codes for every single error in the library, hence `git_reference_lookup` would return GIT_SUCCESS if the operation was successful, GIT_ENOTFOUND when the reference doesn't exist, and GIT_ERROR when an error happens -- **the error is then detailed in the `git_error` parameter**.
+- `const git_error *giterr_last(void)`: The main function used to look up
+ the last error. This may return NULL if no error has occurred.
+ Otherwise this should return a `git_error` object indicating the class
+ of error and the error message that was generated by the library.
+
+ The last error is stored in thread-local storage when libgit2 is
+ compiled with thread support, so you do not have to worry about another
+ thread overwriting the value. When thread support is off, the last
+ error is a global value.
+
+ _Note_ There are some known bugs in the library where this may return
+ NULL even when an error code was generated. Please report these as
+ bugs, but in the meantime, please code defensively and check for NULL
+ when calling this function.
+
+- `void geterr_clear(void)`: This function clears the last error. The
+ library will call this when an error is generated by low level function
+ and the higher level function handles the error.
+
+ _Note_ There are some known bugs in the library where a low level
+ function's error message is not cleared by higher level code that
+ handles the error and returns zero. Please report these as bugs, but in
+ the meantime, a zero return value from a libgit2 API does not guarantee
+ that `giterr_last()` will return NULL.
+
+- `void giterr_set_str(int error_class, const char *message)`: This
+ function can be used when writing a custom backend module to set the
+ libgit2 error message. See the documentation on this function for its
+ use. Normal usage of libgit2 will probably never need to call this API.
+
+- `void giterr_set_oom(void)`: This is a standard function for reporting
+ an out-of-memory error. It is written in a manner that it doesn't have
+ to allocate any extra memory in order to record the error, so this is
+ the best way to report that scenario.
+
+Deviations from the standard
+----------------------------
+
+There are some public functions that do not return `int` values. There
+are two primary cases:
+
+* `void` return values: If a function has a `void` return, then it will
+ never fail. This primary will be used for object destructors.
+
+* `git_xyz *` return values: These are simple accessor functions where the
+ only meaningful error would typically be looking something up by index
+ and having the index be out of bounds. In those cases, the function
+ will typically return NULL.
+
+* Boolean return values: There are some cases where a function cannot fail
+ and wants to return a boolean value. In those cases, we try to return 1
+ for true and 0 for false. These cases are rare and the return value for
+ the function should probably be an `unsigned int` to denote these cases.
+ If you find an exception, please open an issue and let's fix it.
+
+There are a few other exceptions to these rules here and there in the
+library, but those are extremely rare and should probably be converted
+over to other to more standard patterns for usage. Feel free to open
+issues pointing these out.
+
+There are some known bugs in the library where some functions may return a
+negative value but not set an error message and some other functions may
+return zero (no error) and yet leave an error message set. Please report
+these cases as issues and they will be fixed. In the meanwhile, please
+code defensively, checking that the return value of `giterr_last` is not
+NULL before using it, and not relying on `giterr_last` to return NULL when
+a function returns 0 for success.
+
+The internal error API
+----------------------
-Please be smart when returning error codes. Functions have max two "expected errors", and in most cases only one.
+- `void giterr_set(int error_class, const char *fmt, ...)`: This is the
+ main internal function for setting an error. It works like `printf` to
+ format the error message. See the notes of `giterr_set_str` for a
+ general description of how error messages are stored (and also about
+ special handling for `error_class` of `GITERR_OS`).
Writing error messages
----------------------
Here are some guidelines when writing error messages:
-- Use proper English, and an impersonal or past tenses: *The given path does not exist*, *Failed to lookup object in ODB*
+- Use proper English, and an impersonal or past tenses: *The given path
+ does not exist*, *Failed to lookup object in ODB*
-- Use short, direct and objective messages. **One line, max**. libgit2 is a low level library: think that all the messages reported will be thrown as Ruby or Python exceptions. Think how long are common exception messages in those languages.
+- Use short, direct and objective messages. **One line, max**. libgit2 is
+ a low level library: think that all the messages reported will be thrown
+ as Ruby or Python exceptions. Think how long are common exception
+ messages in those languages.
-- **Do not add redundant information to the error message**, specially information that can be inferred from the context.
+- **Do not add redundant information to the error message**, specially
+ information that can be inferred from the context.
- E.g. in `git_repository_open`, do not report a message like "Failed to open repository: path not found". Somebody is
- calling that function. If it fails, he already knows that the repository failed to open!
+ E.g. in `git_repository_open`, do not report a message like "Failed to
+ open repository: path not found". Somebody is calling that
+ function. If it fails, they already know that the repository failed to
+ open!
General guidelines for error reporting
--------------------------------------
-- We never handle programming errors with these functions. Programming errors are `assert`ed, and when their source is internal, fixed as soon as possible. This is C, people.
-
- Example of programming errors that would **not** be handled: passing NULL to a function that expects a valid pointer; passing a `git_tree` to a function that expects a `git_commit`. All these cases need to be identified with `assert` and fixed asap.
+- Libgit2 does not handle programming errors with these
+ functions. Programming errors are `assert`ed, and when their source is
+ internal, fixed as soon as possible. This is C, people.
- Example of a runtime error: failing to parse a `git_tree` because it contains invalid data. Failing to open a file because it doesn't exist on disk. These errors would be handled, and a `git_error` would be set.
+ Example of programming errors that would **not** be handled: passing
+ NULL to a function that expects a valid pointer; passing a `git_tree`
+ to a function that expects a `git_commit`. All these cases need to be
+ identified with `assert` and fixed asap.
-- The `git_error **` argument is always the last in the signature of all API calls. No exceptions.
+ Example of a runtime error: failing to parse a `git_tree` because it
+ contains invalid data. Failing to open a file because it doesn't exist
+ on disk. These errors are handled, a meaningful error message is set,
+ and an error code is returned.
-- When the programmer (or us, internally) doesn't need error handling, he can pass `NULL` to the `git_error **` param. This means that the errors won't be *reported*, but obviously they still will be handled (i.e. the failing function will interrupt and return cleanly). This is transparently handled by `giterr_set`
+- In general, *do not* try to overwrite errors internally and *do*
+ propagate error codes from lower level functions to the higher level.
+ There are some cases where propagating an error code will be more
+ confusing rather than less, so there are some exceptions to this rule,
+ but the default behavior should be to simply clean up and pass the error
+ on up to the caller.
-- `git_error *` **must be initialized to `NULL` before passing its value to a function!!**
+ **WRONG**
~~~c
- git_error *err;
- git_error *good_error = NULL;
-
- git_foo_func(arg1, arg2, &error); // invalid: `error` is not initialized
- git_foo_func2(arg1, arg2, &good_error); // OK!
- git_foo_func3(arg1, arg2, NULL); // OK! But no error reporting!
- ~~~
-
-- Piling up errors is an error! Don't do this! Errors must always be free'd when a function returns.
+ int git_commit_parent(...)
+ {
+ ...
- ~~~c
- git_error *error = NULL;
+ if (git_commit_lookup(parent, repo, parent_id) < 0) {
+ giterr_set(GITERR_COMMIT, "Overwrite lookup error message");
+ return -1; /* mask error code */
+ }
- git_foo_func1(arg1, &error);
- git_foo_func2(arg2, &error); // WRONG! What if func1 failed? `error` would leak!
+ ...
+ }
~~~
-- Likewise: do not rethrow errors internally!
+ **RIGHT**
~~~c
- int git_commit_create(..., git_error **error)
+ int git_commit_parent(...)
{
- if (git_reference_exists("HEAD", error) < 0) {
- /* HEAD does not exist; create it so we can commit... */
- if (git_reference_create("HEAD", error) < 0) {
- /* error could be rethrown */
- }
- }
+ ...
-- Remember that errors are now allocated, and hence they need to be free'd after they've been used. Failure to do so internally (e.g. in the already seen examples of error piling) will be reported by Valgrind, so we can easily find where are we rethrowing errors.
+ error = git_commit_lookup(parent, repo, parent_id);
+ if (error < 0) {
+ /* cleanup intermediate objects if necessary */
+ /* leave error message and propagate error code */
+ return error;
+ }
-- Remember that any function that fails **will set an error object**, and that object will be freed.
+ ...
+ }
+ ~~~
diff --git a/examples/.gitignore b/examples/.gitignore
index e8e0820a5..711493994 100644
--- a/examples/.gitignore
+++ b/examples/.gitignore
@@ -2,4 +2,10 @@ general
showindex
diff
rev-list
+blame
+cat-file
+init
+log
+rev-parse
+status
*.dSYM
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
new file mode 100644
index 000000000..596be45ed
--- /dev/null
+++ b/examples/CMakeLists.txt
@@ -0,0 +1,16 @@
+FILE(GLOB_RECURSE SRC_EXAMPLE_GIT2 network/*.c network/*.h)
+ADD_EXECUTABLE(cgit2 ${SRC_EXAMPLE_GIT2})
+IF(WIN32 OR ANDROID)
+ TARGET_LINK_LIBRARIES(cgit2 git2)
+ELSE()
+ TARGET_LINK_LIBRARIES(cgit2 git2 pthread)
+ENDIF()
+
+FILE(GLOB SRC_EXAMPLE_APPS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.c)
+FOREACH(src_app ${SRC_EXAMPLE_APPS})
+ STRING(REPLACE ".c" "" app_name ${src_app})
+ IF(NOT ${app_name} STREQUAL "common")
+ ADD_EXECUTABLE(${app_name} ${src_app} "common.c")
+ TARGET_LINK_LIBRARIES(${app_name} git2)
+ ENDIF()
+ENDFOREACH()
diff --git a/examples/COPYING b/examples/COPYING
new file mode 100644
index 000000000..0e259d42c
--- /dev/null
+++ b/examples/COPYING
@@ -0,0 +1,121 @@
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+ LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+ REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+ PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+ THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+ HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+ i. the right to reproduce, adapt, distribute, perform, display,
+ communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+ likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+ subject to the limitations in paragraph 4(a), below;
+ v. rights protecting the extraction, dissemination, use and reuse of data
+ in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+ European Parliament and of the Council of 11 March 1996 on the legal
+ protection of databases, and under any national implementation
+ thereof, including any amended or successor version of such
+ directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+ world based on applicable law or treaty, and any national
+ implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+ surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+ warranties of any kind concerning the Work, express, implied,
+ statutory or otherwise, including without limitation warranties of
+ title, merchantability, fitness for a particular purpose, non
+ infringement, or the absence of latent or other defects, accuracy, or
+ the present or absence of errors, whether or not discoverable, all to
+ the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+ that may apply to the Work or any use thereof, including without
+ limitation any person's Copyright and Related Rights in the Work.
+ Further, Affirmer disclaims responsibility for obtaining any necessary
+ consents, permissions or other rights required for any use of the
+ Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+ party to this document and has no duty or obligation with respect to
+ this CC0 or use of the Work.
diff --git a/examples/Makefile b/examples/Makefile
index 140cc4da9..2e7f68f82 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -3,12 +3,12 @@
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
+APPS = general showindex diff rev-list cat-file status log rev-parse init blame
all: $(APPS)
% : %.c
- $(CC) -o $@ $(CFLAGS) $< $(LFLAGS)
+ $(CC) -o $@ common.c $(CFLAGS) $< $(LFLAGS)
clean:
$(RM) $(APPS)
diff --git a/examples/README.md b/examples/README.md
index f2b6d7d23..769c4b267 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -1,11 +1,22 @@
libgit2 examples
================
-These examples are meant as thin, easy-to-read snippets for Docurium
-(https://github.com/github/docurium) rather than full-blown
-implementations of Git commands. They are not vetted as carefully
-for bugs, error handling, or cross-platform compatibility as the
-rest of the code in libgit2, so copy with some caution.
+These examples are a mixture of basic emulation of core Git command line
+functions and simple snippets demonstrating libgit2 API usage (for use
+with Docurium). As a whole, they are not vetted carefully for bugs, error
+handling, and cross-platform compatibility in the same manner as the rest
+of the code in libgit2, so copy with caution.
-For HTML versions, check "Examples" at http://libgit2.github.com/libgit2
+That being said, you are welcome to copy code from these examples as
+desired when using libgit2. They have been [released to the public domain][cc0],
+so there are no restrictions on their use.
+[cc0]: COPYING
+
+For annotated HTML versions, see the "Examples" section of:
+
+ http://libgit2.github.com/libgit2
+
+such as:
+
+ http://libgit2.github.com/libgit2/ex/HEAD/general.html
diff --git a/examples/add.c b/examples/add.c
new file mode 100644
index 000000000..336596bde
--- /dev/null
+++ b/examples/add.c
@@ -0,0 +1,159 @@
+/*
+ * libgit2 "add" example - shows how to modify the index
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+#include <assert.h>
+
+enum print_options {
+ SKIP = 1,
+ VERBOSE = 2,
+ UPDATE = 4,
+};
+
+struct print_payload {
+ enum print_options options;
+ git_repository *repo;
+};
+
+/* Forward declarations for helpers */
+static void parse_opts(int *options, int *count, int argc, char *argv[]);
+void init_array(git_strarray *array, int argc, char **argv);
+int print_matched_cb(const char *path, const char *matched_pathspec, void *payload);
+
+int main (int argc, char** argv)
+{
+ git_index_matched_path_cb matched_cb = NULL;
+ git_repository *repo = NULL;
+ git_index *index;
+ git_strarray array = {0};
+ int options = 0, count = 0;
+ struct print_payload payload = {0};
+
+ git_threads_init();
+
+ parse_opts(&options, &count, argc, argv);
+
+ init_array(&array, argc-count, argv+count);
+
+ check_lg2(git_repository_open(&repo, "."), "No git repository", NULL);
+ check_lg2(git_repository_index(&index, repo), "Could not open repository index", NULL);
+
+ if (options&VERBOSE || options&SKIP) {
+ matched_cb = &print_matched_cb;
+ }
+
+ payload.options = options;
+ payload.repo = repo;
+
+ if (options&UPDATE) {
+ git_index_update_all(index, &array, matched_cb, &payload);
+ } else {
+ git_index_add_all(index, &array, 0, matched_cb, &payload);
+ }
+
+ git_index_write(index);
+ git_index_free(index);
+ git_repository_free(repo);
+
+ git_threads_shutdown();
+
+ return 0;
+}
+
+int print_matched_cb(const char *path, const char *matched_pathspec, void *payload)
+{
+ struct print_payload p = *(struct print_payload*)(payload);
+ int ret;
+ git_status_t status;
+ (void)matched_pathspec;
+
+ if (git_status_file(&status, p.repo, path)) {
+ return -1; //abort
+ }
+
+ if (status & GIT_STATUS_WT_MODIFIED ||
+ status & GIT_STATUS_WT_NEW) {
+ printf("add '%s'\n", path);
+ ret = 0;
+ } else {
+ ret = 1;
+ }
+
+ if(p.options & SKIP) {
+ ret = 1;
+ }
+
+ return ret;
+}
+
+void init_array(git_strarray *array, int argc, char **argv)
+{
+ unsigned int i;
+
+ array->count = argc;
+ array->strings = malloc(sizeof(char*) * array->count);
+ assert(array->strings!=NULL);
+
+ for(i=0; i<array->count; i++) {
+ array->strings[i]=argv[i];
+ }
+
+ return;
+}
+
+void print_usage(void)
+{
+ fprintf(stderr, "usage: add [options] [--] file-spec [file-spec] [...]\n\n");
+ fprintf(stderr, "\t-n, --dry-run dry run\n");
+ fprintf(stderr, "\t-v, --verbose be verbose\n");
+ fprintf(stderr, "\t-u, --update update tracked files\n");
+ exit(1);
+}
+
+static void parse_opts(int *options, int *count, int argc, char *argv[])
+{
+ int i;
+
+ for (i = 1; i < argc; ++i) {
+ if (argv[i][0] != '-') {
+ break;
+ }
+ else if(!strcmp(argv[i], "--verbose") || !strcmp(argv[i], "-v")) {
+ *options |= VERBOSE;
+ }
+ else if(!strcmp(argv[i], "--dry-run") || !strcmp(argv[i], "-n")) {
+ *options |= SKIP;
+ }
+ else if(!strcmp(argv[i], "--update") || !strcmp(argv[i], "-u")) {
+ *options |= UPDATE;
+ }
+ else if(!strcmp(argv[i], "-h")) {
+ print_usage();
+ break;
+ }
+ else if(!strcmp(argv[i], "--")) {
+ i++;
+ break;
+ }
+ else {
+ fprintf(stderr, "Unsupported option %s.\n", argv[i]);
+ print_usage();
+ }
+ }
+
+ if (argc<=i)
+ print_usage();
+
+ *count = i;
+}
diff --git a/examples/blame.c b/examples/blame.c
new file mode 100644
index 000000000..06310d540
--- /dev/null
+++ b/examples/blame.c
@@ -0,0 +1,199 @@
+/*
+ * libgit2 "blame" example - shows how to use the blame API
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+/**
+ * This example demonstrates how to invoke the libgit2 blame API to roughly
+ * simulate the output of `git blame` and a few of its command line arguments.
+ */
+
+struct opts {
+ char *path;
+ char *commitspec;
+ int C;
+ int M;
+ int start_line;
+ int end_line;
+};
+static void parse_opts(struct opts *o, int argc, char *argv[]);
+
+int main(int argc, char *argv[])
+{
+ int i, line, break_on_null_hunk;
+ char spec[1024] = {0};
+ struct opts o = {0};
+ const char *rawdata;
+ git_repository *repo = NULL;
+ git_revspec revspec = {0};
+ git_blame_options blameopts = GIT_BLAME_OPTIONS_INIT;
+ git_blame *blame = NULL;
+ git_blob *blob;
+ git_object *obj;
+
+ git_threads_init();
+
+ parse_opts(&o, argc, argv);
+ if (o.M) blameopts.flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES;
+ if (o.C) blameopts.flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES;
+
+ /** Open the repository. */
+ check_lg2(git_repository_open_ext(&repo, ".", 0, NULL), "Couldn't open repository", NULL);
+
+ /**
+ * The commit range comes in "commitish" form. Use the rev-parse API to
+ * nail down the end points.
+ */
+ if (o.commitspec) {
+ check_lg2(git_revparse(&revspec, repo, o.commitspec), "Couldn't parse commit spec", NULL);
+ if (revspec.flags & GIT_REVPARSE_SINGLE) {
+ git_oid_cpy(&blameopts.newest_commit, git_object_id(revspec.from));
+ git_object_free(revspec.from);
+ } else {
+ git_oid_cpy(&blameopts.oldest_commit, git_object_id(revspec.from));
+ git_oid_cpy(&blameopts.newest_commit, git_object_id(revspec.to));
+ git_object_free(revspec.from);
+ git_object_free(revspec.to);
+ }
+ }
+
+ /** Run the blame. */
+ check_lg2(git_blame_file(&blame, repo, o.path, &blameopts), "Blame error", NULL);
+
+ /**
+ * Get the raw data inside the blob for output. We use the
+ * `commitish:path/to/file.txt` format to find it.
+ */
+ if (git_oid_iszero(&blameopts.newest_commit))
+ strcpy(spec, "HEAD");
+ else
+ git_oid_tostr(spec, sizeof(spec), &blameopts.newest_commit);
+ strcat(spec, ":");
+ strcat(spec, o.path);
+
+ check_lg2(git_revparse_single(&obj, repo, spec), "Object lookup error", NULL);
+ check_lg2(git_blob_lookup(&blob, repo, git_object_id(obj)), "Blob lookup error", NULL);
+ git_object_free(obj);
+
+ rawdata = git_blob_rawcontent(blob);
+
+ /** Produce the output. */
+ line = 1;
+ i = 0;
+ break_on_null_hunk = 0;
+ while (i < git_blob_rawsize(blob)) {
+ const char *eol = strchr(rawdata+i, '\n');
+ char oid[10] = {0};
+ const git_blame_hunk *hunk = git_blame_get_hunk_byline(blame, line);
+
+ if (break_on_null_hunk && !hunk) break;
+
+ if (hunk) {
+ break_on_null_hunk = 1;
+ char sig[128] = {0};
+
+ git_oid_tostr(oid, 10, &hunk->final_commit_id);
+ snprintf(sig, 30, "%s <%s>", hunk->final_signature->name, hunk->final_signature->email);
+
+ printf("%s ( %-30s %3d) %.*s\n",
+ oid,
+ sig,
+ line,
+ (int)(eol-rawdata-i),
+ rawdata+i);
+ }
+
+ i = (int)(eol - rawdata + 1);
+ line++;
+ }
+
+ /** Cleanup. */
+ git_blob_free(blob);
+ git_blame_free(blame);
+ git_repository_free(repo);
+
+ git_threads_shutdown();
+
+ return 0;
+}
+
+/** Tell the user how to make this thing work. */
+static void usage(const char *msg, const char *arg)
+{
+ if (msg && arg)
+ fprintf(stderr, "%s: %s\n", msg, arg);
+ else if (msg)
+ fprintf(stderr, "%s\n", msg);
+ fprintf(stderr, "usage: blame [options] [<commit range>] <path>\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, " <commit range> example: `HEAD~10..HEAD`, or `1234abcd`\n");
+ fprintf(stderr, " -L <n,m> process only line range n-m, counting from 1\n");
+ fprintf(stderr, " -M find line moves within and across files\n");
+ fprintf(stderr, " -C find line copies within and across files\n");
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+/** Parse the arguments. */
+static void parse_opts(struct opts *o, int argc, char *argv[])
+{
+ int i;
+ char *bare_args[3] = {0};
+
+ if (argc < 2) usage(NULL, NULL);
+
+ for (i=1; i<argc; i++) {
+ char *a = argv[i];
+
+ if (a[0] != '-') {
+ int i=0;
+ while (bare_args[i] && i < 3) ++i;
+ if (i >= 3)
+ usage("Invalid argument set", NULL);
+ bare_args[i] = a;
+ }
+ else if (!strcmp(a, "--"))
+ continue;
+ else if (!strcasecmp(a, "-M"))
+ o->M = 1;
+ else if (!strcasecmp(a, "-C"))
+ o->C = 1;
+ else if (!strcasecmp(a, "-L")) {
+ i++; a = argv[i];
+ if (i >= argc) fatal("Not enough arguments to -L", NULL);
+ check_lg2(sscanf(a, "%d,%d", &o->start_line, &o->end_line)-2, "-L format error", NULL);
+ }
+ else {
+ /* commit range */
+ if (o->commitspec) fatal("Only one commit spec allowed", NULL);
+ o->commitspec = a;
+ }
+ }
+
+ /* Handle the bare arguments */
+ if (!bare_args[0]) usage("Please specify a path", NULL);
+ o->path = bare_args[0];
+ if (bare_args[1]) {
+ /* <commitspec> <path> */
+ o->path = bare_args[1];
+ o->commitspec = bare_args[0];
+ }
+ if (bare_args[2]) {
+ /* <oldcommit> <newcommit> <path> */
+ char spec[128] = {0};
+ o->path = bare_args[2];
+ sprintf(spec, "%s..%s", bare_args[0], bare_args[1]);
+ o->commitspec = spec;
+ }
+}
diff --git a/examples/cat-file.c b/examples/cat-file.c
index ebb6cb0ca..fa6add07b 100644
--- a/examples/cat-file.c
+++ b/examples/cat-file.c
@@ -1,37 +1,18 @@
-#include <stdio.h>
-#include <git2.h>
-#include <stdlib.h>
-#include <string.h>
-
-static git_repository *g_repo;
-
-static void check(int error, const char *message)
-{
- if (error) {
- fprintf(stderr, "%s (%d)\n", message, error);
- exit(1);
- }
-}
-
-static void usage(const char *message, const char *arg)
-{
- if (message && arg)
- fprintf(stderr, "%s: %s\n", message, arg);
- else if (message)
- fprintf(stderr, "%s\n", message);
- fprintf(stderr, "usage: cat-file (-t | -s | -e | -p) [<options>] <object>\n");
- exit(1);
-}
-
-static int check_str_param(
- const char *arg, const char *pattern, const char **val)
-{
- size_t len = strlen(pattern);
- if (strncmp(arg, pattern, len))
- return 0;
- *val = (const char *)(arg + len);
- return 1;
-}
+/*
+ * libgit2 "cat-file" example - shows how to print data from the ODB
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
static void print_signature(const char *header, const git_signature *sig)
{
@@ -57,12 +38,14 @@ static void print_signature(const char *header, const git_signature *sig)
sign, hours, minutes);
}
+/** Printing out a blob is simple, get the contents and print */
static void show_blob(const git_blob *blob)
{
/* ? Does this need crlf filtering? */
fwrite(git_blob_rawcontent(blob), git_blob_rawsize(blob), 1, stdout);
}
+/** Show each entry with its type, id and attributes */
static void show_tree(const git_tree *tree)
{
size_t i, max_i = (int)git_tree_entrycount(tree);
@@ -81,6 +64,9 @@ static void show_tree(const git_tree *tree)
}
}
+/**
+ * Commits and tags have a few interesting fields in their header.
+ */
static void show_commit(const git_commit *commit)
{
unsigned int i, max_i;
@@ -123,53 +109,34 @@ enum {
SHOW_PRETTY = 4
};
+/* Forward declarations for option-parsing helper */
+struct opts {
+ const char *dir;
+ const char *rev;
+ int action;
+ int verbose;
+};
+static void parse_opts(struct opts *o, int argc, char *argv[]);
+
+
+/** Entry point for this command */
int main(int argc, char *argv[])
{
- const char *dir = ".", *rev = NULL;
- int i, action = 0, verbose = 0;
+ git_repository *repo;
+ struct opts o = { ".", NULL, 0, 0 };
git_object *obj = NULL;
char oidstr[GIT_OID_HEXSZ + 1];
git_threads_init();
- for (i = 1; i < argc; ++i) {
- char *a = argv[i];
+ parse_opts(&o, argc, argv);
- if (a[0] != '-') {
- if (rev != NULL)
- usage("Only one rev should be provided", NULL);
- else
- rev = a;
- }
- else if (!strcmp(a, "-t"))
- action = SHOW_TYPE;
- else if (!strcmp(a, "-s"))
- action = SHOW_SIZE;
- else if (!strcmp(a, "-e"))
- action = SHOW_NONE;
- else if (!strcmp(a, "-p"))
- action = SHOW_PRETTY;
- else if (!strcmp(a, "-q"))
- verbose = 0;
- else if (!strcmp(a, "-v"))
- verbose = 1;
- else if (!strcmp(a, "--help") || !strcmp(a, "-h"))
- usage(NULL, NULL);
- else if (!check_str_param(a, "--git-dir=", &dir))
- usage("Unknown option", a);
- }
+ check_lg2(git_repository_open_ext(&repo, o.dir, 0, NULL),
+ "Could not open repository", NULL);
+ check_lg2(git_revparse_single(&obj, repo, o.rev),
+ "Could not resolve", o.rev);
- if (!action || !rev)
- usage(NULL, NULL);
-
- check(git_repository_open_ext(&g_repo, dir, 0, NULL),
- "Could not open repository");
-
- if (git_revparse_single(&obj, g_repo, rev) < 0) {
- fprintf(stderr, "Could not resolve '%s'\n", rev);
- exit(1);
- }
- if (verbose) {
+ if (o.verbose) {
char oidstr[GIT_OID_HEXSZ + 1];
git_oid_tostr(oidstr, sizeof(oidstr), git_object_id(obj));
@@ -177,7 +144,7 @@ int main(int argc, char *argv[])
git_object_type2string(git_object_type(obj)), oidstr);
}
- switch (action) {
+ switch (o.action) {
case SHOW_TYPE:
printf("%s\n", git_object_type2string(git_object_type(obj)));
break;
@@ -185,9 +152,9 @@ int main(int argc, char *argv[])
git_odb *odb;
git_odb_object *odbobj;
- check(git_repository_odb(&odb, g_repo), "Could not open ODB");
- check(git_odb_read(&odbobj, odb, git_object_id(obj)),
- "Could not find obj");
+ check_lg2(git_repository_odb(&odb, repo), "Could not open ODB", NULL);
+ check_lg2(git_odb_read(&odbobj, odb, git_object_id(obj)),
+ "Could not find obj", NULL);
printf("%ld\n", (long)git_odb_object_size(odbobj));
@@ -221,9 +188,59 @@ int main(int argc, char *argv[])
}
git_object_free(obj);
- git_repository_free(g_repo);
+ git_repository_free(repo);
git_threads_shutdown();
return 0;
}
+
+/** Print out usage information */
+static void usage(const char *message, const char *arg)
+{
+ if (message && arg)
+ fprintf(stderr, "%s: %s\n", message, arg);
+ else if (message)
+ fprintf(stderr, "%s\n", message);
+ fprintf(stderr,
+ "usage: cat-file (-t | -s | -e | -p) [-v] [-q] "
+ "[-h|--help] [--git-dir=<dir>] <object>\n");
+ exit(1);
+}
+
+/** Parse the command-line options taken from git */
+static void parse_opts(struct opts *o, int argc, char *argv[])
+{
+ struct args_info args = ARGS_INFO_INIT;
+
+ for (args.pos = 1; args.pos < argc; ++args.pos) {
+ char *a = argv[args.pos];
+
+ if (a[0] != '-') {
+ if (o->rev != NULL)
+ usage("Only one rev should be provided", NULL);
+ else
+ o->rev = a;
+ }
+ else if (!strcmp(a, "-t"))
+ o->action = SHOW_TYPE;
+ else if (!strcmp(a, "-s"))
+ o->action = SHOW_SIZE;
+ else if (!strcmp(a, "-e"))
+ o->action = SHOW_NONE;
+ else if (!strcmp(a, "-p"))
+ o->action = SHOW_PRETTY;
+ else if (!strcmp(a, "-q"))
+ o->verbose = 0;
+ else if (!strcmp(a, "-v"))
+ o->verbose = 1;
+ else if (!strcmp(a, "--help") || !strcmp(a, "-h"))
+ usage(NULL, NULL);
+ else if (!match_str_arg(&o->dir, &args, "--git-dir"))
+ usage("Unknown option", a);
+ }
+
+ if (!o->action || !o->rev)
+ usage(NULL, NULL);
+
+}
diff --git a/examples/common.c b/examples/common.c
new file mode 100644
index 000000000..12dbccf59
--- /dev/null
+++ b/examples/common.c
@@ -0,0 +1,191 @@
+/*
+ * Utilities library for libgit2 examples
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+void check_lg2(int error, const char *message, const char *extra)
+{
+ const git_error *lg2err;
+ const char *lg2msg = "", *lg2spacer = "";
+
+ if (!error)
+ return;
+
+ if ((lg2err = giterr_last()) != NULL && lg2err->message != NULL) {
+ lg2msg = lg2err->message;
+ lg2spacer = " - ";
+ }
+
+ if (extra)
+ fprintf(stderr, "%s '%s' [%d]%s%s\n",
+ message, extra, error, lg2spacer, lg2msg);
+ else
+ fprintf(stderr, "%s [%d]%s%s\n",
+ message, error, lg2spacer, lg2msg);
+
+ exit(1);
+}
+
+void fatal(const char *message, const char *extra)
+{
+ if (extra)
+ fprintf(stderr, "%s %s\n", message, extra);
+ else
+ fprintf(stderr, "%s\n", message);
+
+ exit(1);
+}
+
+size_t is_prefixed(const char *str, const char *pfx)
+{
+ size_t len = strlen(pfx);
+ return strncmp(str, pfx, len) ? 0 : len;
+}
+
+int match_str_arg(
+ const char **out, struct args_info *args, const char *opt)
+{
+ const char *found = args->argv[args->pos];
+ size_t len = is_prefixed(found, opt);
+
+ if (!len)
+ return 0;
+
+ if (!found[len]) {
+ if (args->pos + 1 == args->argc)
+ fatal("expected value following argument", opt);
+ args->pos += 1;
+ *out = args->argv[args->pos];
+ return 1;
+ }
+
+ if (found[len] == '=') {
+ *out = found + len + 1;
+ return 1;
+ }
+
+ return 0;
+}
+
+static const char *match_numeric_arg(struct args_info *args, const char *opt)
+{
+ const char *found = args->argv[args->pos];
+ size_t len = is_prefixed(found, opt);
+
+ if (!len)
+ return NULL;
+
+ if (!found[len]) {
+ if (args->pos + 1 == args->argc)
+ fatal("expected numeric value following argument", opt);
+ args->pos += 1;
+ found = args->argv[args->pos];
+ } else {
+ found = found + len;
+ if (*found == '=')
+ found++;
+ }
+
+ return found;
+}
+
+int match_uint16_arg(
+ uint16_t *out, struct args_info *args, const char *opt)
+{
+ const char *found = match_numeric_arg(args, opt);
+ uint16_t val;
+ char *endptr = NULL;
+
+ if (!found)
+ return 0;
+
+ val = (uint16_t)strtoul(found, &endptr, 0);
+ if (!endptr || *endptr != '\0')
+ fatal("expected number after argument", opt);
+
+ if (out)
+ *out = val;
+ return 1;
+}
+
+static int match_int_internal(
+ int *out, const char *str, int allow_negative, const char *opt)
+{
+ char *endptr = NULL;
+ int val = (int)strtol(str, &endptr, 10);
+
+ if (!endptr || *endptr != '\0')
+ fatal("expected number", opt);
+ else if (val < 0 && !allow_negative)
+ fatal("negative values are not allowed", opt);
+
+ if (out)
+ *out = val;
+
+ return 1;
+}
+
+int is_integer(int *out, const char *str, int allow_negative)
+{
+ return match_int_internal(out, str, allow_negative, NULL);
+}
+
+int match_int_arg(
+ int *out, struct args_info *args, const char *opt, int allow_negative)
+{
+ const char *found = match_numeric_arg(args, opt);
+ if (!found)
+ return 0;
+ return match_int_internal(out, found, allow_negative, opt);
+}
+
+int diff_output(
+ const git_diff_delta *d,
+ const git_diff_hunk *h,
+ const git_diff_line *l,
+ void *p)
+{
+ FILE *fp = p;
+
+ (void)d; (void)h;
+
+ if (!fp)
+ fp = stdout;
+
+ if (l->origin == GIT_DIFF_LINE_CONTEXT ||
+ l->origin == GIT_DIFF_LINE_ADDITION ||
+ l->origin == GIT_DIFF_LINE_DELETION)
+ fputc(l->origin, fp);
+
+ fwrite(l->content, 1, l->content_len, fp);
+
+ return 0;
+}
+
+void treeish_to_tree(
+ git_tree **out, git_repository *repo, const char *treeish)
+{
+ git_object *obj = NULL;
+
+ check_lg2(
+ git_revparse_single(&obj, repo, treeish),
+ "looking up object", treeish);
+
+ check_lg2(
+ git_object_peel((git_object **)out, obj, GIT_OBJ_TREE),
+ "resolving object to tree", treeish);
+
+ git_object_free(obj);
+}
+
diff --git a/examples/common.h b/examples/common.h
new file mode 100644
index 000000000..2fd7d579f
--- /dev/null
+++ b/examples/common.h
@@ -0,0 +1,87 @@
+/*
+ * Utilities library for libgit2 examples
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <git2.h>
+
+/**
+ * Check libgit2 error code, printing error to stderr on failure and
+ * exiting the program.
+ */
+extern void check_lg2(int error, const char *message, const char *extra);
+
+/**
+ * Exit the program, printing error to stderr
+ */
+extern void fatal(const char *message, const char *extra);
+
+/**
+ * Check if a string has the given prefix. Returns 0 if not prefixed
+ * or the length of the prefix if it is.
+ */
+extern size_t is_prefixed(const char *str, const char *pfx);
+
+/**
+ * Match an integer string, returning 1 if matched, 0 if not.
+ */
+extern int is_integer(int *out, const char *str, int allow_negative);
+
+struct args_info {
+ int argc;
+ char **argv;
+ int pos;
+};
+#define ARGS_INFO_INIT { argc, argv, 0 }
+
+/**
+ * Check current `args` entry against `opt` string. If it matches
+ * exactly, take the next arg as a string; if it matches as a prefix with
+ * an equal sign, take the remainder as a string; otherwise return 0.
+ */
+extern int match_str_arg(
+ const char **out, struct args_info *args, const char *opt);
+
+/**
+ * Check current `args` entry against `opt` string parsing as uint16. If
+ * `opt` matches exactly, take the next arg as a uint16_t value; if `opt`
+ * is a prefix (equal sign optional), take the remainder of the arg as a
+ * uint16_t value; otherwise return 0.
+ */
+extern int match_uint16_arg(
+ uint16_t *out, struct args_info *args, const char *opt);
+
+/**
+ * Check current `args` entry against `opt` string parsing as int. If
+ * `opt` matches exactly, take the next arg as an int value; if it matches
+ * as a prefix (equal sign optional), take the remainder of the arg as a
+ * int value; otherwise return 0.
+ */
+extern int match_int_arg(
+ int *out, struct args_info *args, const char *opt, int allow_negative);
+
+/**
+ * Basic output function for plain text diff output
+ * Pass `FILE*` such as `stdout` or `stderr` as payload (or NULL == `stdout`)
+ */
+extern int diff_output(
+ const git_diff_delta*, const git_diff_hunk*, const git_diff_line*, void*);
+
+/**
+ * Convert a treeish argument to an actual tree; this will call check_lg2
+ * and exit the program if `treeish` cannot be resolved to a tree
+ */
+extern void treeish_to_tree(
+ git_tree **out, git_repository *repo, const char *treeish);
diff --git a/examples/diff.c b/examples/diff.c
index 11efa21ec..daf5d7030 100644
--- a/examples/diff.c
+++ b/examples/diff.c
@@ -1,70 +1,176 @@
-#include <stdio.h>
-#include <git2.h>
-#include <stdlib.h>
-#include <string.h>
+/*
+ * libgit2 "diff" example - shows how to use the diff API
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+/**
+ * This example demonstrates the use of the libgit2 diff APIs to
+ * create `git_diff` objects and display them, emulating a number of
+ * core Git `diff` command line options.
+ *
+ * This covers on a portion of the core Git diff options and doesn't
+ * have particularly good error handling, but it should show most of
+ * the core libgit2 diff APIs, including various types of diffs and
+ * how to do renaming detection and patch formatting.
+ */
+
+static const char *colors[] = {
+ "\033[m", /* reset */
+ "\033[1m", /* bold */
+ "\033[31m", /* red */
+ "\033[32m", /* green */
+ "\033[36m" /* cyan */
+};
-static void check(int error, const char *message)
-{
- if (error) {
- fprintf(stderr, "%s (%d)\n", message, error);
- exit(1);
- }
-}
+/** The 'opts' struct captures all the various parsed command line options. */
+struct opts {
+ git_diff_options diffopts;
+ git_diff_find_options findopts;
+ int color;
+ int cached;
+ git_diff_format_t format;
+ const char *treeish1;
+ const char *treeish2;
+ const char *dir;
+};
-static int resolve_to_tree(
- git_repository *repo, const char *identifier, git_tree **tree)
+/** These functions are implemented at the end */
+static void parse_opts(struct opts *o, int argc, char *argv[]);
+static int color_printer(
+ const git_diff_delta*, const git_diff_hunk*, const git_diff_line*, void*);
+
+int main(int argc, char *argv[])
{
- int err = 0;
- git_object *obj = NULL;
-
- if ((err = git_revparse_single(&obj, repo, identifier)) < 0)
- return err;
-
- switch (git_object_type(obj)) {
- case GIT_OBJ_TREE:
- *tree = (git_tree *)obj;
- break;
- case GIT_OBJ_COMMIT:
- err = git_commit_tree(tree, (git_commit *)obj);
- git_object_free(obj);
- break;
- default:
- err = GIT_ENOTFOUND;
+ git_repository *repo = NULL;
+ git_tree *t1 = NULL, *t2 = NULL;
+ git_diff *diff;
+ struct opts o = {
+ GIT_DIFF_OPTIONS_INIT, GIT_DIFF_FIND_OPTIONS_INIT,
+ -1, 0, GIT_DIFF_FORMAT_PATCH, NULL, NULL, "."
+ };
+
+ git_threads_init();
+
+ parse_opts(&o, argc, argv);
+
+ check_lg2(git_repository_open_ext(&repo, o.dir, 0, NULL),
+ "Could not open repository", o.dir);
+
+ /**
+ * Possible argument patterns:
+ *
+ * * &lt;sha1&gt; &lt;sha2&gt;
+ * * &lt;sha1&gt; --cached
+ * * &lt;sha1&gt;
+ * * --cached
+ * * nothing
+ *
+ * Currently ranged arguments like &lt;sha1&gt;..&lt;sha2&gt; and &lt;sha1&gt;...&lt;sha2&gt;
+ * are not supported in this example
+ */
+
+ if (o.treeish1)
+ treeish_to_tree(&t1, repo, o.treeish1);
+ if (o.treeish2)
+ treeish_to_tree(&t2, repo, o.treeish2);
+
+ if (t1 && t2)
+ check_lg2(
+ git_diff_tree_to_tree(&diff, repo, t1, t2, &o.diffopts),
+ "diff trees", NULL);
+ else if (t1 && o.cached)
+ check_lg2(
+ git_diff_tree_to_index(&diff, repo, t1, NULL, &o.diffopts),
+ "diff tree to index", NULL);
+ else if (t1)
+ check_lg2(
+ git_diff_tree_to_workdir_with_index(&diff, repo, t1, &o.diffopts),
+ "diff tree to working directory", NULL);
+ else if (o.cached) {
+ treeish_to_tree(&t1, repo, "HEAD");
+ check_lg2(
+ git_diff_tree_to_index(&diff, repo, t1, NULL, &o.diffopts),
+ "diff tree to index", NULL);
}
+ else
+ check_lg2(
+ git_diff_index_to_workdir(&diff, repo, NULL, &o.diffopts),
+ "diff index to working directory", NULL);
+
+ /** Apply rename and copy detection if requested. */
+
+ if ((o.findopts.flags & GIT_DIFF_FIND_ALL) != 0)
+ check_lg2(
+ git_diff_find_similar(diff, &o.findopts),
+ "finding renames and copies", NULL);
+
+ /** Generate simple output using libgit2 display helper. */
+
+ if (o.color >= 0)
+ fputs(colors[0], stdout);
+
+ check_lg2(
+ git_diff_print(diff, o.format, color_printer, &o.color),
+ "displaying diff", NULL);
- return err;
+ if (o.color >= 0)
+ fputs(colors[0], stdout);
+
+ /** Cleanup before exiting. */
+
+ git_diff_free(diff);
+ git_tree_free(t1);
+ git_tree_free(t2);
+ git_repository_free(repo);
+
+ git_threads_shutdown();
+
+ return 0;
}
-char *colors[] = {
- "\033[m", /* reset */
- "\033[1m", /* bold */
- "\033[31m", /* red */
- "\033[32m", /* green */
- "\033[36m" /* cyan */
-};
+static void usage(const char *message, const char *arg)
+{
+ if (message && arg)
+ fprintf(stderr, "%s: %s\n", message, arg);
+ else if (message)
+ fprintf(stderr, "%s\n", message);
+ fprintf(stderr, "usage: diff [<tree-oid> [<tree-oid>]]\n");
+ exit(1);
+}
-static int printer(
+/** This implements very rudimentary colorized output. */
+static int color_printer(
const git_diff_delta *delta,
- const git_diff_range *range,
- char usage,
- const char *line,
- size_t line_len,
+ const git_diff_hunk *hunk,
+ const git_diff_line *line,
void *data)
{
int *last_color = data, color = 0;
- (void)delta; (void)range; (void)line_len;
+ (void)delta; (void)hunk;
if (*last_color >= 0) {
- switch (usage) {
- case GIT_DIFF_LINE_ADDITION: color = 3; break;
- case GIT_DIFF_LINE_DELETION: color = 2; break;
+ switch (line->origin) {
+ case GIT_DIFF_LINE_ADDITION: color = 3; break;
+ case GIT_DIFF_LINE_DELETION: color = 2; break;
case GIT_DIFF_LINE_ADD_EOFNL: color = 3; break;
case GIT_DIFF_LINE_DEL_EOFNL: color = 2; break;
- case GIT_DIFF_LINE_FILE_HDR: color = 1; break;
- case GIT_DIFF_LINE_HUNK_HDR: color = 4; break;
- default: color = 0;
+ case GIT_DIFF_LINE_FILE_HDR: color = 1; break;
+ case GIT_DIFF_LINE_HUNK_HDR: color = 4; break;
+ default: break;
}
+
if (color != *last_color) {
if (*last_color == 1 || color == 1)
fputs(colors[0], stdout);
@@ -73,193 +179,79 @@ static int printer(
}
}
- fputs(line, stdout);
- return 0;
-}
-
-static int check_uint16_param(const char *arg, const char *pattern, uint16_t *val)
-{
- size_t len = strlen(pattern);
- uint16_t strval;
- char *endptr = NULL;
- if (strncmp(arg, pattern, len))
- return 0;
- if (arg[len] == '\0' && pattern[len - 1] != '=')
- return 1;
- if (arg[len] == '=')
- len++;
- strval = strtoul(arg + len, &endptr, 0);
- if (endptr == arg)
- return 0;
- *val = strval;
- return 1;
-}
-
-static int check_str_param(const char *arg, const char *pattern, const char **val)
-{
- size_t len = strlen(pattern);
- if (strncmp(arg, pattern, len))
- return 0;
- *val = (const char *)(arg + len);
- return 1;
-}
-
-static void usage(const char *message, const char *arg)
-{
- if (message && arg)
- fprintf(stderr, "%s: %s\n", message, arg);
- else if (message)
- fprintf(stderr, "%s\n", message);
- fprintf(stderr, "usage: diff [<tree-oid> [<tree-oid>]]\n");
- exit(1);
+ return diff_output(delta, hunk, line, stdout);
}
-enum {
- FORMAT_PATCH = 0,
- FORMAT_COMPACT = 1,
- FORMAT_RAW = 2
-};
-
-int main(int argc, char *argv[])
+/** Parse arguments as copied from git-diff. */
+static void parse_opts(struct opts *o, int argc, char *argv[])
{
- git_repository *repo = NULL;
- git_tree *t1 = NULL, *t2 = NULL;
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
- git_diff_list *diff;
- int i, color = -1, format = FORMAT_PATCH, cached = 0;
- char *a, *treeish1 = NULL, *treeish2 = NULL;
- const char *dir = ".";
+ struct args_info args = ARGS_INFO_INIT;
- git_threads_init();
-
- /* parse arguments as copied from git-diff */
- for (i = 1; i < argc; ++i) {
- a = argv[i];
+ for (args.pos = 1; args.pos < argc; ++args.pos) {
+ const char *a = argv[args.pos];
if (a[0] != '-') {
- if (treeish1 == NULL)
- treeish1 = a;
- else if (treeish2 == NULL)
- treeish2 = a;
+ if (o->treeish1 == NULL)
+ o->treeish1 = a;
+ else if (o->treeish2 == NULL)
+ o->treeish2 = a;
else
usage("Only one or two tree identifiers can be provided", NULL);
}
else if (!strcmp(a, "-p") || !strcmp(a, "-u") ||
!strcmp(a, "--patch"))
- format = FORMAT_PATCH;
+ o->format = GIT_DIFF_FORMAT_PATCH;
else if (!strcmp(a, "--cached"))
- cached = 1;
+ o->cached = 1;
+ else if (!strcmp(a, "--name-only"))
+ o->format = GIT_DIFF_FORMAT_NAME_ONLY;
else if (!strcmp(a, "--name-status"))
- format = FORMAT_COMPACT;
+ o->format = GIT_DIFF_FORMAT_NAME_STATUS;
else if (!strcmp(a, "--raw"))
- format = FORMAT_RAW;
+ o->format = GIT_DIFF_FORMAT_RAW;
else if (!strcmp(a, "--color"))
- color = 0;
+ o->color = 0;
else if (!strcmp(a, "--no-color"))
- color = -1;
+ o->color = -1;
else if (!strcmp(a, "-R"))
- opts.flags |= GIT_DIFF_REVERSE;
+ o->diffopts.flags |= GIT_DIFF_REVERSE;
else if (!strcmp(a, "-a") || !strcmp(a, "--text"))
- opts.flags |= GIT_DIFF_FORCE_TEXT;
+ o->diffopts.flags |= GIT_DIFF_FORCE_TEXT;
else if (!strcmp(a, "--ignore-space-at-eol"))
- opts.flags |= GIT_DIFF_IGNORE_WHITESPACE_EOL;
+ o->diffopts.flags |= GIT_DIFF_IGNORE_WHITESPACE_EOL;
else if (!strcmp(a, "-b") || !strcmp(a, "--ignore-space-change"))
- opts.flags |= GIT_DIFF_IGNORE_WHITESPACE_CHANGE;
+ o->diffopts.flags |= GIT_DIFF_IGNORE_WHITESPACE_CHANGE;
else if (!strcmp(a, "-w") || !strcmp(a, "--ignore-all-space"))
- opts.flags |= GIT_DIFF_IGNORE_WHITESPACE;
+ o->diffopts.flags |= GIT_DIFF_IGNORE_WHITESPACE;
else if (!strcmp(a, "--ignored"))
- opts.flags |= GIT_DIFF_INCLUDE_IGNORED;
+ o->diffopts.flags |= GIT_DIFF_INCLUDE_IGNORED;
else if (!strcmp(a, "--untracked"))
- opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
- else if (check_uint16_param(a, "-M", &findopts.rename_threshold) ||
- check_uint16_param(a, "--find-renames",
- &findopts.rename_threshold))
- findopts.flags |= GIT_DIFF_FIND_RENAMES;
- else if (check_uint16_param(a, "-C", &findopts.copy_threshold) ||
- check_uint16_param(a, "--find-copies",
- &findopts.copy_threshold))
- findopts.flags |= GIT_DIFF_FIND_COPIES;
+ o->diffopts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
+ else if (match_uint16_arg(
+ &o->findopts.rename_threshold, &args, "-M") ||
+ match_uint16_arg(
+ &o->findopts.rename_threshold, &args, "--find-renames"))
+ o->findopts.flags |= GIT_DIFF_FIND_RENAMES;
+ else if (match_uint16_arg(
+ &o->findopts.copy_threshold, &args, "-C") ||
+ match_uint16_arg(
+ &o->findopts.copy_threshold, &args, "--find-copies"))
+ o->findopts.flags |= GIT_DIFF_FIND_COPIES;
else if (!strcmp(a, "--find-copies-harder"))
- findopts.flags |= GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED;
- else if (!strncmp(a, "-B", 2) || !strncmp(a, "--break-rewrites", 16)) {
+ o->findopts.flags |= GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED;
+ else if (is_prefixed(a, "-B") || is_prefixed(a, "--break-rewrites"))
/* TODO: parse thresholds */
- findopts.flags |= GIT_DIFF_FIND_REWRITES;
- }
- else if (!check_uint16_param(a, "-U", &opts.context_lines) &&
- !check_uint16_param(a, "--unified=", &opts.context_lines) &&
- !check_uint16_param(a, "--inter-hunk-context=",
- &opts.interhunk_lines) &&
- !check_str_param(a, "--src-prefix=", &opts.old_prefix) &&
- !check_str_param(a, "--dst-prefix=", &opts.new_prefix) &&
- !check_str_param(a, "--git-dir=", &dir))
- usage("Unknown arg", a);
+ o->findopts.flags |= GIT_DIFF_FIND_REWRITES;
+ else if (!match_uint16_arg(
+ &o->diffopts.context_lines, &args, "-U") &&
+ !match_uint16_arg(
+ &o->diffopts.context_lines, &args, "--unified") &&
+ !match_uint16_arg(
+ &o->diffopts.interhunk_lines, &args, "--inter-hunk-context") &&
+ !match_str_arg(&o->diffopts.old_prefix, &args, "--src-prefix") &&
+ !match_str_arg(&o->diffopts.new_prefix, &args, "--dst-prefix") &&
+ !match_str_arg(&o->dir, &args, "--git-dir"))
+ usage("Unknown command line argument", a);
}
-
- /* open repo */
-
- check(git_repository_open_ext(&repo, dir, 0, NULL),
- "Could not open repository");
-
- if (treeish1)
- check(resolve_to_tree(repo, treeish1, &t1), "Looking up first tree");
- if (treeish2)
- check(resolve_to_tree(repo, treeish2, &t2), "Looking up second tree");
-
- /* <sha1> <sha2> */
- /* <sha1> --cached */
- /* <sha1> */
- /* --cached */
- /* nothing */
-
- if (t1 && t2)
- check(git_diff_tree_to_tree(&diff, repo, t1, t2, &opts), "Diff");
- else if (t1 && cached)
- check(git_diff_tree_to_index(&diff, repo, t1, NULL, &opts), "Diff");
- else if (t1) {
- git_diff_list *diff2;
- check(git_diff_tree_to_index(&diff, repo, t1, NULL, &opts), "Diff");
- check(git_diff_index_to_workdir(&diff2, repo, NULL, &opts), "Diff");
- check(git_diff_merge(diff, diff2), "Merge diffs");
- git_diff_list_free(diff2);
- }
- else if (cached) {
- check(resolve_to_tree(repo, "HEAD", &t1), "looking up HEAD");
- check(git_diff_tree_to_index(&diff, repo, t1, NULL, &opts), "Diff");
- }
- else
- check(git_diff_index_to_workdir(&diff, repo, NULL, &opts), "Diff");
-
- if ((findopts.flags & GIT_DIFF_FIND_ALL) != 0)
- check(git_diff_find_similar(diff, &findopts),
- "finding renames and copies ");
-
- if (color >= 0)
- fputs(colors[0], stdout);
-
- switch (format) {
- case FORMAT_PATCH:
- check(git_diff_print_patch(diff, printer, &color), "Displaying diff");
- break;
- case FORMAT_COMPACT:
- check(git_diff_print_compact(diff, printer, &color), "Displaying diff");
- break;
- case FORMAT_RAW:
- check(git_diff_print_raw(diff, printer, &color), "Displaying diff");
- break;
- }
-
- if (color >= 0)
- fputs(colors[0], stdout);
-
- git_diff_list_free(diff);
- git_tree_free(t1);
- git_tree_free(t2);
- git_repository_free(repo);
-
- git_threads_shutdown();
-
- return 0;
}
-
diff --git a/examples/general.c b/examples/general.c
index d7a58479c..58e141b35 100644
--- a/examples/general.c
+++ b/examples/general.c
@@ -1,3 +1,17 @@
+/*
+ * libgit2 "general" example - shows basic libgit2 concepts
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
// [**libgit2**][lg] is a portable, pure C implementation of the Git core
// methods provided as a re-entrant linkable library with a solid API,
// allowing you to write native speed custom Git applications in any
@@ -52,7 +66,7 @@ int main (int argc, char** argv)
// simplest. There are also [methods][me] for specifying the index file
// and work tree locations, here we assume they are in the normal places.
//
- // (Try running this program against tests-clar/resources/testrepo.git.)
+ // (Try running this program against tests/resources/testrepo.git.)
//
// [me]: http://libgit2.github.com/libgit2/#HEAD/group/repository
int error;
diff --git a/examples/init.c b/examples/init.c
new file mode 100644
index 000000000..0e823ab81
--- /dev/null
+++ b/examples/init.c
@@ -0,0 +1,253 @@
+/*
+ * libgit2 "init" example - shows how to initialize a new repo
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+/**
+ * This is a sample program that is similar to "git init". See the
+ * documentation for that (try "git help init") to understand what this
+ * program is emulating.
+ *
+ * This demonstrates using the libgit2 APIs to initialize a new repository.
+ *
+ * This also contains a special additional option that regular "git init"
+ * does not support which is "--initial-commit" to make a first empty commit.
+ * That is demonstrated in the "create_initial_commit" helper function.
+ */
+
+/** Forward declarations of helpers */
+struct opts {
+ int no_options;
+ int quiet;
+ int bare;
+ int initial_commit;
+ uint32_t shared;
+ const char *template;
+ const char *gitdir;
+ const char *dir;
+};
+static void create_initial_commit(git_repository *repo);
+static void parse_opts(struct opts *o, int argc, char *argv[]);
+
+
+int main(int argc, char *argv[])
+{
+ git_repository *repo = NULL;
+ struct opts o = { 1, 0, 0, 0, GIT_REPOSITORY_INIT_SHARED_UMASK, 0, 0, 0 };
+
+ git_threads_init();
+
+ parse_opts(&o, argc, argv);
+
+ /* Initialize repository. */
+
+ if (o.no_options) {
+ /**
+ * No options were specified, so let's demonstrate the default
+ * simple case of git_repository_init() API usage...
+ */
+ check_lg2(git_repository_init(&repo, o.dir, 0),
+ "Could not initialize repository", NULL);
+ }
+ else {
+ /**
+ * Some command line options were specified, so we'll use the
+ * extended init API to handle them
+ */
+ git_repository_init_options initopts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+ initopts.flags = GIT_REPOSITORY_INIT_MKPATH;
+
+ if (o.bare)
+ initopts.flags |= GIT_REPOSITORY_INIT_BARE;
+
+ if (o.template) {
+ initopts.flags |= GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
+ initopts.template_path = o.template;
+ }
+
+ if (o.gitdir) {
+ /**
+ * If you specified a separate git directory, then initialize
+ * the repository at that path and use the second path as the
+ * working directory of the repository (with a git-link file)
+ */
+ initopts.workdir_path = o.dir;
+ o.dir = o.gitdir;
+ }
+
+ if (o.shared != 0)
+ initopts.mode = o.shared;
+
+ check_lg2(git_repository_init_ext(&repo, o.dir, &initopts),
+ "Could not initialize repository", NULL);
+ }
+
+ /** Print a message to stdout like "git init" does. */
+
+ if (!o.quiet) {
+ if (o.bare || o.gitdir)
+ o.dir = git_repository_path(repo);
+ else
+ o.dir = git_repository_workdir(repo);
+
+ printf("Initialized empty Git repository in %s\n", o.dir);
+ }
+
+ /**
+ * As an extension to the basic "git init" command, this example
+ * gives the option to create an empty initial commit. This is
+ * mostly to demonstrate what it takes to do that, but also some
+ * people like to have that empty base commit in their repo.
+ */
+ if (o.initial_commit) {
+ create_initial_commit(repo);
+ printf("Created empty initial commit\n");
+ }
+
+ git_repository_free(repo);
+ git_threads_shutdown();
+
+ return 0;
+}
+
+/**
+ * Unlike regular "git init", this example shows how to create an initial
+ * empty commit in the repository. This is the helper function that does
+ * that.
+ */
+static void create_initial_commit(git_repository *repo)
+{
+ git_signature *sig;
+ git_index *index;
+ git_oid tree_id, commit_id;
+ git_tree *tree;
+
+ /** First use the config to initialize a commit signature for the user. */
+
+ if (git_signature_default(&sig, repo) < 0)
+ fatal("Unable to create a commit signature.",
+ "Perhaps 'user.name' and 'user.email' are not set");
+
+ /* Now let's create an empty tree for this commit */
+
+ if (git_repository_index(&index, repo) < 0)
+ fatal("Could not open repository index", NULL);
+
+ /**
+ * Outside of this example, you could call git_index_add_bypath()
+ * here to put actual files into the index. For our purposes, we'll
+ * leave it empty for now.
+ */
+
+ if (git_index_write_tree(&tree_id, index) < 0)
+ fatal("Unable to write initial tree from index", NULL);
+
+ git_index_free(index);
+
+ if (git_tree_lookup(&tree, repo, &tree_id) < 0)
+ fatal("Could not look up initial tree", NULL);
+
+ /**
+ * Ready to create the initial commit.
+ *
+ * Normally creating a commit would involve looking up the current
+ * HEAD commit and making that be the parent of the initial commit,
+ * but here this is the first commit so there will be no parent.
+ */
+
+ if (git_commit_create_v(
+ &commit_id, repo, "HEAD", sig, sig,
+ NULL, "Initial commit", tree, 0) < 0)
+ fatal("Could not create the initial commit", NULL);
+
+ /** Clean up so we don't leak memory. */
+
+ git_tree_free(tree);
+ git_signature_free(sig);
+}
+
+static void usage(const char *error, const char *arg)
+{
+ fprintf(stderr, "error: %s '%s'\n", error, arg);
+ fprintf(stderr,
+ "usage: init [-q | --quiet] [--bare] [--template=<dir>]\n"
+ " [--shared[=perms]] [--initial-commit]\n"
+ " [--separate-git-dir] <directory>\n");
+ exit(1);
+}
+
+/** Parse the tail of the --shared= argument. */
+static uint32_t parse_shared(const char *shared)
+{
+ if (!strcmp(shared, "false") || !strcmp(shared, "umask"))
+ return GIT_REPOSITORY_INIT_SHARED_UMASK;
+
+ else if (!strcmp(shared, "true") || !strcmp(shared, "group"))
+ return GIT_REPOSITORY_INIT_SHARED_GROUP;
+
+ else if (!strcmp(shared, "all") || !strcmp(shared, "world") ||
+ !strcmp(shared, "everybody"))
+ return GIT_REPOSITORY_INIT_SHARED_ALL;
+
+ else if (shared[0] == '0') {
+ long val;
+ char *end = NULL;
+ val = strtol(shared + 1, &end, 8);
+ if (end == shared + 1 || *end != 0)
+ usage("invalid octal value for --shared", shared);
+ return (uint32_t)val;
+ }
+
+ else
+ usage("unknown value for --shared", shared);
+
+ return 0;
+}
+
+static void parse_opts(struct opts *o, int argc, char *argv[])
+{
+ struct args_info args = ARGS_INFO_INIT;
+ const char *sharedarg;
+
+ /** Process arguments. */
+
+ for (args.pos = 1; args.pos < argc; ++args.pos) {
+ char *a = argv[args.pos];
+
+ if (a[0] == '-')
+ o->no_options = 0;
+
+ if (a[0] != '-') {
+ if (o->dir != NULL)
+ usage("extra argument", a);
+ o->dir = a;
+ }
+ else if (!strcmp(a, "-q") || !strcmp(a, "--quiet"))
+ o->quiet = 1;
+ else if (!strcmp(a, "--bare"))
+ o->bare = 1;
+ else if (!strcmp(a, "--shared"))
+ o->shared = GIT_REPOSITORY_INIT_SHARED_GROUP;
+ else if (!strcmp(a, "--initial-commit"))
+ o->initial_commit = 1;
+ else if (match_str_arg(&sharedarg, &args, "--shared"))
+ o->shared = parse_shared(sharedarg);
+ else if (!match_str_arg(&o->template, &args, "--template") ||
+ !match_str_arg(&o->gitdir, &args, "--separate-git-dir"))
+ usage("unknown option", a);
+ }
+
+ if (!o->dir)
+ usage("must specify directory to init", NULL);
+}
diff --git a/examples/log.c b/examples/log.c
new file mode 100644
index 000000000..471c5ff96
--- /dev/null
+++ b/examples/log.c
@@ -0,0 +1,434 @@
+/*
+ * libgit2 "log" example - shows how to walk history and get commit info
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+/**
+ * This example demonstrates the libgit2 rev walker APIs to roughly
+ * simulate the output of `git log` and a few of command line arguments.
+ * `git log` has many many options and this only shows a few of them.
+ *
+ * This does not have:
+ *
+ * - Robust error handling
+ * - Colorized or paginated output formatting
+ * - Most of the `git log` options
+ *
+ * This does have:
+ *
+ * - Examples of translating command line arguments to equivalent libgit2
+ * revwalker configuration calls
+ * - Simplified options to apply pathspec limits and to show basic diffs
+ */
+
+/** log_state represents walker being configured while handling options */
+struct log_state {
+ git_repository *repo;
+ const char *repodir;
+ git_revwalk *walker;
+ int hide;
+ int sorting;
+ int revisions;
+};
+
+/** utility functions that are called to configure the walker */
+static void set_sorting(struct log_state *s, unsigned int sort_mode);
+static void push_rev(struct log_state *s, git_object *obj, int hide);
+static int add_revision(struct log_state *s, const char *revstr);
+
+/** log_options holds other command line options that affect log output */
+struct log_options {
+ int show_diff;
+ int skip, limit;
+ int min_parents, max_parents;
+ git_time_t before;
+ git_time_t after;
+ char *author;
+ char *committer;
+};
+
+/** utility functions that parse options and help with log output */
+static int parse_options(
+ struct log_state *s, struct log_options *opt, int argc, char **argv);
+static void print_time(const git_time *intime, const char *prefix);
+static void print_commit(git_commit *commit);
+static int match_with_parent(git_commit *commit, int i, git_diff_options *);
+
+
+int main(int argc, char *argv[])
+{
+ int i, count = 0, printed = 0, parents, last_arg;
+ struct log_state s;
+ struct log_options opt;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_oid oid;
+ git_commit *commit = NULL;
+ git_pathspec *ps = NULL;
+
+ git_threads_init();
+
+ /** Parse arguments and set up revwalker. */
+
+ last_arg = parse_options(&s, &opt, argc, argv);
+
+ diffopts.pathspec.strings = &argv[last_arg];
+ diffopts.pathspec.count = argc - last_arg;
+ if (diffopts.pathspec.count > 0)
+ check_lg2(git_pathspec_new(&ps, &diffopts.pathspec),
+ "Building pathspec", NULL);
+
+ if (!s.revisions)
+ add_revision(&s, NULL);
+
+ /** Use the revwalker to traverse the history. */
+
+ printed = count = 0;
+
+ for (; !git_revwalk_next(&oid, s.walker); git_commit_free(commit)) {
+ check_lg2(git_commit_lookup(&commit, s.repo, &oid),
+ "Failed to look up commit", NULL);
+
+ parents = (int)git_commit_parentcount(commit);
+ if (parents < opt.min_parents)
+ continue;
+ if (opt.max_parents > 0 && parents > opt.max_parents)
+ continue;
+
+ if (diffopts.pathspec.count > 0) {
+ int unmatched = parents;
+
+ if (parents == 0) {
+ git_tree *tree;
+ check_lg2(git_commit_tree(&tree, commit), "Get tree", NULL);
+ if (git_pathspec_match_tree(
+ NULL, tree, GIT_PATHSPEC_NO_MATCH_ERROR, ps) != 0)
+ unmatched = 1;
+ git_tree_free(tree);
+ } else if (parents == 1) {
+ unmatched = match_with_parent(commit, 0, &diffopts) ? 0 : 1;
+ } else {
+ for (i = 0; i < parents; ++i) {
+ if (match_with_parent(commit, i, &diffopts))
+ unmatched--;
+ }
+ }
+
+ if (unmatched > 0)
+ continue;
+ }
+
+ if (count++ < opt.skip)
+ continue;
+ if (opt.limit != -1 && printed++ >= opt.limit) {
+ git_commit_free(commit);
+ break;
+ }
+
+ print_commit(commit);
+
+ if (opt.show_diff) {
+ git_tree *a = NULL, *b = NULL;
+ git_diff *diff = NULL;
+
+ if (parents > 1)
+ continue;
+ check_lg2(git_commit_tree(&b, commit), "Get tree", NULL);
+ if (parents == 1) {
+ git_commit *parent;
+ check_lg2(git_commit_parent(&parent, commit, 0), "Get parent", NULL);
+ check_lg2(git_commit_tree(&a, parent), "Tree for parent", NULL);
+ git_commit_free(parent);
+ }
+
+ check_lg2(git_diff_tree_to_tree(
+ &diff, git_commit_owner(commit), a, b, &diffopts),
+ "Diff commit with parent", NULL);
+ check_lg2(
+ git_diff_print(diff, GIT_DIFF_FORMAT_PATCH, diff_output, NULL),
+ "Displaying diff", NULL);
+
+ git_diff_free(diff);
+ git_tree_free(a);
+ git_tree_free(b);
+ }
+ }
+
+ git_pathspec_free(ps);
+ git_revwalk_free(s.walker);
+ git_repository_free(s.repo);
+ git_threads_shutdown();
+
+ return 0;
+}
+
+/** Push object (for hide or show) onto revwalker. */
+static void push_rev(struct log_state *s, git_object *obj, int hide)
+{
+ hide = s->hide ^ hide;
+
+ /** Create revwalker on demand if it doesn't already exist. */
+ if (!s->walker) {
+ check_lg2(git_revwalk_new(&s->walker, s->repo),
+ "Could not create revision walker", NULL);
+ git_revwalk_sorting(s->walker, s->sorting);
+ }
+
+ if (!obj)
+ check_lg2(git_revwalk_push_head(s->walker),
+ "Could not find repository HEAD", NULL);
+ else if (hide)
+ check_lg2(git_revwalk_hide(s->walker, git_object_id(obj)),
+ "Reference does not refer to a commit", NULL);
+ else
+ check_lg2(git_revwalk_push(s->walker, git_object_id(obj)),
+ "Reference does not refer to a commit", NULL);
+
+ git_object_free(obj);
+}
+
+/** Parse revision string and add revs to walker. */
+static int add_revision(struct log_state *s, const char *revstr)
+{
+ git_revspec revs;
+ int hide = 0;
+
+ /** Open repo on demand if it isn't already open. */
+ if (!s->repo) {
+ if (!s->repodir) s->repodir = ".";
+ check_lg2(git_repository_open_ext(&s->repo, s->repodir, 0, NULL),
+ "Could not open repository", s->repodir);
+ }
+
+ if (!revstr) {
+ push_rev(s, NULL, hide);
+ return 0;
+ }
+
+ if (*revstr == '^') {
+ revs.flags = GIT_REVPARSE_SINGLE;
+ hide = !hide;
+
+ if (git_revparse_single(&revs.from, s->repo, revstr + 1) < 0)
+ return -1;
+ } else if (git_revparse(&revs, s->repo, revstr) < 0)
+ return -1;
+
+ if ((revs.flags & GIT_REVPARSE_SINGLE) != 0)
+ push_rev(s, revs.from, hide);
+ else {
+ push_rev(s, revs.to, hide);
+
+ if ((revs.flags & GIT_REVPARSE_MERGE_BASE) != 0) {
+ git_oid base;
+ check_lg2(git_merge_base(&base, s->repo,
+ git_object_id(revs.from), git_object_id(revs.to)),
+ "Could not find merge base", revstr);
+ check_lg2(
+ git_object_lookup(&revs.to, s->repo, &base, GIT_OBJ_COMMIT),
+ "Could not find merge base commit", NULL);
+
+ push_rev(s, revs.to, hide);
+ }
+
+ push_rev(s, revs.from, !hide);
+ }
+
+ return 0;
+}
+
+/** Update revwalker with sorting mode. */
+static void set_sorting(struct log_state *s, unsigned int sort_mode)
+{
+ /** Open repo on demand if it isn't already open. */
+ if (!s->repo) {
+ if (!s->repodir) s->repodir = ".";
+ check_lg2(git_repository_open_ext(&s->repo, s->repodir, 0, NULL),
+ "Could not open repository", s->repodir);
+ }
+
+ /** Create revwalker on demand if it doesn't already exist. */
+ if (!s->walker)
+ check_lg2(git_revwalk_new(&s->walker, s->repo),
+ "Could not create revision walker", NULL);
+
+ if (sort_mode == GIT_SORT_REVERSE)
+ s->sorting = s->sorting ^ GIT_SORT_REVERSE;
+ else
+ s->sorting = sort_mode | (s->sorting & GIT_SORT_REVERSE);
+
+ git_revwalk_sorting(s->walker, s->sorting);
+}
+
+/** Helper to format a git_time value like Git. */
+static void print_time(const git_time *intime, const char *prefix)
+{
+ char sign, out[32];
+ struct tm *intm;
+ int offset, hours, minutes;
+ time_t t;
+
+ offset = intime->offset;
+ if (offset < 0) {
+ sign = '-';
+ offset = -offset;
+ } else {
+ sign = '+';
+ }
+
+ hours = offset / 60;
+ minutes = offset % 60;
+
+ t = (time_t)intime->time + (intime->offset * 60);
+
+ intm = gmtime(&t);
+ strftime(out, sizeof(out), "%a %b %e %T %Y", intm);
+
+ printf("%s%s %c%02d%02d\n", prefix, out, sign, hours, minutes);
+}
+
+/** Helper to print a commit object. */
+static void print_commit(git_commit *commit)
+{
+ char buf[GIT_OID_HEXSZ + 1];
+ int i, count;
+ const git_signature *sig;
+ const char *scan, *eol;
+
+ git_oid_tostr(buf, sizeof(buf), git_commit_id(commit));
+ printf("commit %s\n", buf);
+
+ if ((count = (int)git_commit_parentcount(commit)) > 1) {
+ printf("Merge:");
+ for (i = 0; i < count; ++i) {
+ git_oid_tostr(buf, 8, git_commit_parent_id(commit, i));
+ printf(" %s", buf);
+ }
+ printf("\n");
+ }
+
+ if ((sig = git_commit_author(commit)) != NULL) {
+ printf("Author: %s <%s>\n", sig->name, sig->email);
+ print_time(&sig->when, "Date: ");
+ }
+ printf("\n");
+
+ for (scan = git_commit_message(commit); scan && *scan; ) {
+ for (eol = scan; *eol && *eol != '\n'; ++eol) /* find eol */;
+
+ printf(" %.*s\n", (int)(eol - scan), scan);
+ scan = *eol ? eol + 1 : NULL;
+ }
+ printf("\n");
+}
+
+/** Helper to find how many files in a commit changed from its nth parent. */
+static int match_with_parent(git_commit *commit, int i, git_diff_options *opts)
+{
+ git_commit *parent;
+ git_tree *a, *b;
+ git_diff *diff;
+ int ndeltas;
+
+ check_lg2(
+ git_commit_parent(&parent, commit, (size_t)i), "Get parent", NULL);
+ check_lg2(git_commit_tree(&a, parent), "Tree for parent", NULL);
+ check_lg2(git_commit_tree(&b, commit), "Tree for commit", NULL);
+ check_lg2(
+ git_diff_tree_to_tree(&diff, git_commit_owner(commit), a, b, opts),
+ "Checking diff between parent and commit", NULL);
+
+ ndeltas = (int)git_diff_num_deltas(diff);
+
+ git_diff_free(diff);
+ git_tree_free(a);
+ git_tree_free(b);
+ git_commit_free(parent);
+
+ return ndeltas > 0;
+}
+
+/** Print a usage message for the program. */
+static void usage(const char *message, const char *arg)
+{
+ if (message && arg)
+ fprintf(stderr, "%s: %s\n", message, arg);
+ else if (message)
+ fprintf(stderr, "%s\n", message);
+ fprintf(stderr, "usage: log [<options>]\n");
+ exit(1);
+}
+
+/** Parse some log command line options. */
+static int parse_options(
+ struct log_state *s, struct log_options *opt, int argc, char **argv)
+{
+ struct args_info args = ARGS_INFO_INIT;
+
+ memset(s, 0, sizeof(*s));
+ s->sorting = GIT_SORT_TIME;
+
+ memset(opt, 0, sizeof(*opt));
+ opt->max_parents = -1;
+ opt->limit = -1;
+
+ for (args.pos = 1; args.pos < argc; ++args.pos) {
+ const char *a = argv[args.pos];
+
+ if (a[0] != '-') {
+ if (!add_revision(s, a))
+ s->revisions++;
+ else
+ /** Try failed revision parse as filename. */
+ break;
+ } else if (!strcmp(a, "--")) {
+ ++args.pos;
+ break;
+ }
+ else if (!strcmp(a, "--date-order"))
+ set_sorting(s, GIT_SORT_TIME);
+ else if (!strcmp(a, "--topo-order"))
+ set_sorting(s, GIT_SORT_TOPOLOGICAL);
+ else if (!strcmp(a, "--reverse"))
+ set_sorting(s, GIT_SORT_REVERSE);
+ else if (match_str_arg(&s->repodir, &args, "--git-dir"))
+ /** Found git-dir. */;
+ else if (match_int_arg(&opt->skip, &args, "--skip", 0))
+ /** Found valid --skip. */;
+ else if (match_int_arg(&opt->limit, &args, "--max-count", 0))
+ /** Found valid --max-count. */;
+ else if (a[1] >= '0' && a[1] <= '9')
+ is_integer(&opt->limit, a + 1, 0);
+ else if (match_int_arg(&opt->limit, &args, "-n", 0))
+ /** Found valid -n. */;
+ else if (!strcmp(a, "--merges"))
+ opt->min_parents = 2;
+ else if (!strcmp(a, "--no-merges"))
+ opt->max_parents = 1;
+ else if (!strcmp(a, "--no-min-parents"))
+ opt->min_parents = 0;
+ else if (!strcmp(a, "--no-max-parents"))
+ opt->max_parents = -1;
+ else if (match_int_arg(&opt->max_parents, &args, "--max-parents=", 1))
+ /** Found valid --max-parents. */;
+ else if (match_int_arg(&opt->min_parents, &args, "--min-parents=", 0))
+ /** Found valid --min_parents. */;
+ else if (!strcmp(a, "-p") || !strcmp(a, "-u") || !strcmp(a, "--patch"))
+ opt->show_diff = 1;
+ else
+ usage("Unsupported argument", a);
+ }
+
+ return args.pos;
+}
+
diff --git a/examples/network/Makefile b/examples/network/Makefile
index 810eb705b..f65c6cb26 100644
--- a/examples/network/Makefile
+++ b/examples/network/Makefile
@@ -11,7 +11,8 @@ OBJECTS = \
ls-remote.o \
fetch.o \
clone.o \
- index-pack.o
+ index-pack.o \
+ common.o
all: $(OBJECTS)
$(CC) $(CFLAGS) $(LDFLAGS) -o git2 $(OBJECTS) $(LIBRARIES)
diff --git a/examples/network/clone.c b/examples/network/clone.c
index 00c25c1ae..4df47eb7f 100644
--- a/examples/network/clone.c
+++ b/examples/network/clone.c
@@ -9,19 +9,6 @@
# include <unistd.h>
#endif
-/* Shamelessly borrowed from http://stackoverflow.com/questions/3417837/
- * with permission of the original author, Martin Pool.
- * http://sourcefrog.net/weblog/software/languages/C/unused.html
- */
-#ifdef UNUSED
-#elif defined(__GNUC__)
-# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
-#elif defined(__LCLINT__)
-# define UNUSED(x) /*@unused@*/ x
-#else
-# define UNUSED(x) x
-#endif
-
typedef struct progress_data {
git_transfer_progress fetch_progress;
size_t completed_steps;
@@ -38,13 +25,19 @@ static void print_progress(const progress_data *pd)
: 0.f;
int kbytes = pd->fetch_progress.received_bytes / 1024;
- printf("net %3d%% (%4d kb, %5d/%5d) / idx %3d%% (%5d/%5d) / chk %3d%% (%4" PRIuZ "/%4" PRIuZ ") %s\n",
+ if (pd->fetch_progress.received_objects == pd->fetch_progress.total_objects) {
+ printf("Resolving deltas %d/%d\r",
+ pd->fetch_progress.indexed_deltas,
+ pd->fetch_progress.total_deltas);
+ } else {
+ printf("net %3d%% (%4d kb, %5d/%5d) / idx %3d%% (%5d/%5d) / chk %3d%% (%4" PRIuZ "/%4" PRIuZ ") %s\n",
network_percent, kbytes,
pd->fetch_progress.received_objects, pd->fetch_progress.total_objects,
index_percent, pd->fetch_progress.indexed_objects, pd->fetch_progress.total_objects,
checkout_percent,
pd->completed_steps, pd->total_steps,
pd->path);
+ }
}
static int fetch_progress(const git_transfer_progress *stats, void *payload)
@@ -63,24 +56,6 @@ static void checkout_progress(const char *path, size_t cur, size_t tot, void *pa
print_progress(pd);
}
-static int cred_acquire(git_cred **out,
- const char * UNUSED(url),
- const char * UNUSED(username_from_url),
- unsigned int UNUSED(allowed_types),
- void * UNUSED(payload))
-{
- char username[128] = {0};
- char password[128] = {0};
-
- printf("Username: ");
- scanf("%s", username);
-
- /* Yup. Right there on your terminal. Careful where you copy/paste output. */
- printf("Password: ");
- scanf("%s", password);
-
- return git_cred_userpass_plaintext_new(out, username, password);
-}
int do_clone(git_repository *repo, int argc, char **argv)
{
@@ -105,9 +80,9 @@ int do_clone(git_repository *repo, int argc, char **argv)
checkout_opts.progress_cb = checkout_progress;
checkout_opts.progress_payload = &pd;
clone_opts.checkout_opts = checkout_opts;
- clone_opts.fetch_progress_cb = &fetch_progress;
- clone_opts.fetch_progress_payload = &pd;
- clone_opts.cred_acquire_cb = cred_acquire;
+ clone_opts.remote_callbacks.transfer_progress = &fetch_progress;
+ clone_opts.remote_callbacks.credentials = cred_acquire_cb;
+ clone_opts.remote_callbacks.payload = &pd;
// Do the clone
error = git_clone(&cloned_repo, url, path, &clone_opts);
diff --git a/examples/network/common.c b/examples/network/common.c
new file mode 100644
index 000000000..d123eedbd
--- /dev/null
+++ b/examples/network/common.c
@@ -0,0 +1,34 @@
+#include "common.h"
+#include <stdio.h>
+
+/* Shamelessly borrowed from http://stackoverflow.com/questions/3417837/
+ * with permission of the original author, Martin Pool.
+ * http://sourcefrog.net/weblog/software/languages/C/unused.html
+ */
+#ifdef UNUSED
+#elif defined(__GNUC__)
+# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
+#elif defined(__LCLINT__)
+# define UNUSED(x) /*@unused@*/ x
+#else
+# define UNUSED(x) x
+#endif
+
+int cred_acquire_cb(git_cred **out,
+ const char * UNUSED(url),
+ const char * UNUSED(username_from_url),
+ unsigned int UNUSED(allowed_types),
+ void * UNUSED(payload))
+{
+ char username[128] = {0};
+ char password[128] = {0};
+
+ printf("Username: ");
+ scanf("%s", username);
+
+ /* Yup. Right there on your terminal. Careful where you copy/paste output. */
+ printf("Password: ");
+ scanf("%s", password);
+
+ return git_cred_userpass_plaintext_new(out, username, password);
+}
diff --git a/examples/network/common.h b/examples/network/common.h
index a4cfa1a7e..1b09caad4 100644
--- a/examples/network/common.h
+++ b/examples/network/common.h
@@ -12,6 +12,12 @@ int fetch(git_repository *repo, int argc, char **argv);
int index_pack(git_repository *repo, int argc, char **argv);
int do_clone(git_repository *repo, int argc, char **argv);
+int cred_acquire_cb(git_cred **out,
+ const char * url,
+ const char * username_from_url,
+ unsigned int allowed_types,
+ void *payload);
+
#ifndef PRIuZ
/* Define the printf format specifer to use for size_t output */
#if defined(_MSC_VER) || defined(__MINGW32__)
diff --git a/examples/network/fetch.c b/examples/network/fetch.c
index 6020ec6ec..474b45bbb 100644
--- a/examples/network/fetch.c
+++ b/examples/network/fetch.c
@@ -14,11 +14,12 @@ struct dl_data {
int finished;
};
-static void progress_cb(const char *str, int len, void *data)
+static int progress_cb(const char *str, int len, void *data)
{
(void)data;
printf("remote: %.*s", len, str);
fflush(stdout); /* We don't have the \n to force the flush */
+ return 0;
}
static void *download(void *ptr)
@@ -35,7 +36,7 @@ static void *download(void *ptr)
// Download the packfile and index it. This function updates the
// amount of received data and the indexer stats which lets you
// inform the user about progress.
- if (git_remote_download(data->remote, NULL, NULL) < 0) {
+ if (git_remote_download(data->remote) < 0) {
data->ret = -1;
goto exit;
}
@@ -47,6 +48,11 @@ exit:
return &data->ret;
}
+/**
+ * This function gets called for each remote-tracking branch that gets
+ * updated. The message we output depends on whether it's a new one or
+ * an update.
+ */
static int update_cb(const char *refname, const git_oid *a, const git_oid *b, void *data)
{
char a_str[GIT_OID_HEXSZ+1], b_str[GIT_OID_HEXSZ+1];
@@ -66,6 +72,7 @@ static int update_cb(const char *refname, const git_oid *a, const git_oid *b, vo
return 0;
}
+/** Entry point for this command */
int fetch(git_repository *repo, int argc, char **argv)
{
git_remote *remote = NULL;
@@ -91,6 +98,7 @@ int fetch(git_repository *repo, int argc, char **argv)
// Set up the callbacks (only update_tips for now)
callbacks.update_tips = &update_cb;
callbacks.progress = &progress_cb;
+ callbacks.credentials = cred_acquire_cb;
git_remote_set_callbacks(remote, &callbacks);
// Set up the information for the background worker thread
@@ -112,10 +120,14 @@ int fetch(git_repository *repo, int argc, char **argv)
do {
usleep(10000);
- if (stats->total_objects > 0)
+ if (stats->received_objects == stats->total_objects) {
+ printf("Resolving deltas %d/%d\r",
+ stats->indexed_deltas, stats->total_deltas);
+ } else if (stats->total_objects > 0) {
printf("Received %d/%d objects (%d) in %" PRIuZ " bytes\r",
stats->received_objects, stats->total_objects,
stats->indexed_objects, stats->received_bytes);
+ }
} while (!data.finished);
if (data.ret < 0)
@@ -124,8 +136,18 @@ int fetch(git_repository *repo, int argc, char **argv)
pthread_join(worker, NULL);
#endif
- printf("\rReceived %d/%d objects in %zu bytes\n",
+ /**
+ * If there are local objects (we got a thin pack), then tell
+ * the user how many objects we saved from having to cross the
+ * network.
+ */
+ if (stats->local_objects > 0) {
+ printf("\rReceived %d/%d objects in %zu bytes (used %d local objects)\n",
+ stats->indexed_objects, stats->total_objects, stats->received_bytes, stats->local_objects);
+ } else{
+ printf("\rReceived %d/%d objects in %zu bytes\n",
stats->indexed_objects, stats->total_objects, stats->received_bytes);
+ }
// Disconnect the underlying connection to prevent from idling.
git_remote_disconnect(remote);
diff --git a/examples/network/index-pack.c b/examples/network/index-pack.c
index 889305da8..314f21160 100644
--- a/examples/network/index-pack.c
+++ b/examples/network/index-pack.c
@@ -31,7 +31,7 @@ static int index_cb(const git_transfer_progress *stats, void *data)
int index_pack(git_repository *repo, int argc, char **argv)
{
- git_indexer_stream *idx;
+ git_indexer *idx;
git_transfer_progress stats = {0, 0};
int error;
char hash[GIT_OID_HEXSZ + 1] = {0};
@@ -46,7 +46,7 @@ int index_pack(git_repository *repo, int argc, char **argv)
return EXIT_FAILURE;
}
- if (git_indexer_stream_new(&idx, ".", NULL, NULL) < 0) {
+ if (git_indexer_new(&idx, ".", 0, NULL, NULL, NULL) < 0) {
puts("bad idx");
return -1;
}
@@ -61,7 +61,7 @@ int index_pack(git_repository *repo, int argc, char **argv)
if (read_bytes < 0)
break;
- if ((error = git_indexer_stream_add(idx, buf, read_bytes, &stats)) < 0)
+ if ((error = git_indexer_append(idx, buf, read_bytes, &stats)) < 0)
goto cleanup;
index_cb(&stats, NULL);
@@ -73,16 +73,16 @@ int index_pack(git_repository *repo, int argc, char **argv)
goto cleanup;
}
- if ((error = git_indexer_stream_finalize(idx, &stats)) < 0)
+ if ((error = git_indexer_commit(idx, &stats)) < 0)
goto cleanup;
printf("\rIndexing %d of %d\n", stats.indexed_objects, stats.total_objects);
- git_oid_fmt(hash, git_indexer_stream_hash(idx));
+ git_oid_fmt(hash, git_indexer_hash(idx));
puts(hash);
cleanup:
close(fd);
- git_indexer_stream_free(idx);
+ git_indexer_free(idx);
return error;
}
diff --git a/examples/network/ls-remote.c b/examples/network/ls-remote.c
index 252011828..1e08b293e 100644
--- a/examples/network/ls-remote.c
+++ b/examples/network/ls-remote.c
@@ -4,65 +4,52 @@
#include <string.h>
#include "common.h"
-static int show_ref__cb(git_remote_head *head, void *payload)
-{
- char oid[GIT_OID_HEXSZ + 1] = {0};
-
- (void)payload;
- git_oid_fmt(oid, &head->oid);
- printf("%s\t%s\n", oid, head->name);
- return 0;
-}
-
-static int use_unnamed(git_repository *repo, const char *url)
-{
- git_remote *remote = NULL;
- int error;
-
- // Create an instance of a remote from the URL. The transport to use
- // is detected from the URL
- error = git_remote_create_inmemory(&remote, repo, NULL, url);
- if (error < 0)
- goto cleanup;
-
- // When connecting, the underlying code needs to know wether we
- // want to push or fetch
- error = git_remote_connect(remote, GIT_DIRECTION_FETCH);
- if (error < 0)
- goto cleanup;
-
- // With git_remote_ls we can retrieve the advertised heads
- error = git_remote_ls(remote, &show_ref__cb, NULL);
-
-cleanup:
- git_remote_free(remote);
- return error;
-}
-
static int use_remote(git_repository *repo, char *name)
{
git_remote *remote = NULL;
int error;
+ const git_remote_head **refs;
+ size_t refs_len, i;
+ git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
// Find the remote by name
error = git_remote_load(&remote, repo, name);
- if (error < 0)
- goto cleanup;
+ if (error < 0) {
+ error = git_remote_create_inmemory(&remote, repo, NULL, name);
+ if (error < 0)
+ goto cleanup;
+ }
+
+ /**
+ * Connect to the remote and call the printing function for
+ * each of the remote references.
+ */
+ callbacks.credentials = cred_acquire_cb;
+ git_remote_set_callbacks(remote, &callbacks);
error = git_remote_connect(remote, GIT_DIRECTION_FETCH);
if (error < 0)
goto cleanup;
- error = git_remote_ls(remote, &show_ref__cb, NULL);
+ /**
+ * Get the list of references on the remote and print out
+ * their name next to what they point to.
+ */
+ if (git_remote_ls(&refs, &refs_len, remote) < 0)
+ goto cleanup;
+
+ for (i = 0; i < refs_len; i++) {
+ char oid[GIT_OID_HEXSZ + 1] = {0};
+ git_oid_fmt(oid, &refs[i]->oid);
+ printf("%s\t%s\n", oid, refs[i]->name);
+ }
cleanup:
git_remote_free(remote);
return error;
}
-// This gets called to do the work. The remote can be given either as
-// the name of a configured remote or an URL.
-
+/** Entry point for this command */
int ls_remote(git_repository *repo, int argc, char **argv)
{
int error;
@@ -72,12 +59,7 @@ int ls_remote(git_repository *repo, int argc, char **argv)
return EXIT_FAILURE;
}
- /* If there's a ':' in the name, assume it's an URL */
- if (strchr(argv[1], ':') != NULL) {
- error = use_unnamed(repo, argv[1]);
- } else {
- error = use_remote(repo, argv[1]);
- }
+ error = use_remote(repo, argv[1]);
return error;
}
diff --git a/examples/rev-list.c b/examples/rev-list.c
index 1fb7ebf9f..5c0d751a3 100644
--- a/examples/rev-list.c
+++ b/examples/rev-list.c
@@ -1,17 +1,43 @@
-#include <stdio.h>
-#include <string.h>
+/*
+ * libgit2 "rev-list" example - shows how to transform a rev-spec into a list
+ * of commit ids
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+static int revwalk_parseopts(git_repository *repo, git_revwalk *walk, int nopts, char **opts);
-#include <git2.h>
-
-static void check_error(int error_code, const char *action)
+int main (int argc, char **argv)
{
- if (!error_code)
- return;
+ git_repository *repo;
+ git_revwalk *walk;
+ git_oid oid;
+ char buf[41];
+
+ git_threads_init();
- const git_error *error = giterr_last();
- fprintf(stderr, "Error %d %s: %s\n", -error_code, action,
- (error && error->message) ? error->message : "???");
- exit(1);
+ check_lg2(git_repository_open_ext(&repo, ".", 0, NULL), "opening repository", NULL);
+ check_lg2(git_revwalk_new(&walk, repo), "allocating revwalk", NULL);
+ check_lg2(revwalk_parseopts(repo, walk, argc-1, argv+1), "parsing options", NULL);
+
+ while (!git_revwalk_next(&oid, walk)) {
+ git_oid_fmt(buf, &oid);
+ buf[40] = '\0';
+ printf("%s\n", buf);
+ }
+
+ git_threads_shutdown();
+ return 0;
}
static int push_commit(git_revwalk *walk, const git_oid *oid, int hide)
@@ -93,27 +119,3 @@ static int revwalk_parseopts(git_repository *repo, git_revwalk *walk, int nopts,
return 0;
}
-int main (int argc, char **argv)
-{
- int error;
- git_repository *repo;
- git_revwalk *walk;
- git_oid oid;
- char buf[41];
-
- error = git_repository_open_ext(&repo, ".", 0, NULL);
- check_error(error, "opening repository");
-
- error = git_revwalk_new(&walk, repo);
- check_error(error, "allocating revwalk");
- error = revwalk_parseopts(repo, walk, argc-1, argv+1);
- check_error(error, "parsing options");
-
- while (!git_revwalk_next(&oid, walk)) {
- git_oid_fmt(buf, &oid);
- buf[40] = '\0';
- printf("%s\n", buf);
- }
-
- return 0;
-}
diff --git a/examples/rev-parse.c b/examples/rev-parse.c
new file mode 100644
index 000000000..a24833767
--- /dev/null
+++ b/examples/rev-parse.c
@@ -0,0 +1,115 @@
+/*
+ * libgit2 "rev-parse" example - shows how to parse revspecs
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+/** Forward declarations for helpers. */
+struct parse_state {
+ git_repository *repo;
+ const char *repodir;
+ const char *spec;
+ int not;
+};
+static void parse_opts(struct parse_state *ps, int argc, char *argv[]);
+static int parse_revision(struct parse_state *ps);
+
+
+int main(int argc, char *argv[])
+{
+ struct parse_state ps = {0};
+
+ git_threads_init();
+ parse_opts(&ps, argc, argv);
+
+ check_lg2(parse_revision(&ps), "Parsing", NULL);
+
+ git_repository_free(ps.repo);
+ git_threads_shutdown();
+
+ return 0;
+}
+
+static void usage(const char *message, const char *arg)
+{
+ if (message && arg)
+ fprintf(stderr, "%s: %s\n", message, arg);
+ else if (message)
+ fprintf(stderr, "%s\n", message);
+ fprintf(stderr, "usage: rev-parse [ --option ] <args>...\n");
+ exit(1);
+}
+
+static void parse_opts(struct parse_state *ps, int argc, char *argv[])
+{
+ struct args_info args = ARGS_INFO_INIT;
+
+ for (args.pos=1; args.pos < argc; ++args.pos) {
+ const char *a = argv[args.pos];
+
+ if (a[0] != '-') {
+ if (ps->spec)
+ usage("Too many specs", a);
+ ps->spec = a;
+ } else if (!strcmp(a, "--not"))
+ ps->not = !ps->not;
+ else if (!match_str_arg(&ps->repodir, &args, "--git-dir"))
+ usage("Cannot handle argument", a);
+ }
+}
+
+static int parse_revision(struct parse_state *ps)
+{
+ git_revspec rs;
+ char str[GIT_OID_HEXSZ + 1];
+
+ if (!ps->repo) {
+ if (!ps->repodir)
+ ps->repodir = ".";
+ check_lg2(git_repository_open_ext(&ps->repo, ps->repodir, 0, NULL),
+ "Could not open repository from", ps->repodir);
+ }
+
+ check_lg2(git_revparse(&rs, ps->repo, ps->spec), "Could not parse", ps->spec);
+
+ if ((rs.flags & GIT_REVPARSE_SINGLE) != 0) {
+ git_oid_tostr(str, sizeof(str), git_object_id(rs.from));
+ printf("%s\n", str);
+ git_object_free(rs.from);
+ }
+ else if ((rs.flags & GIT_REVPARSE_RANGE) != 0) {
+ git_oid_tostr(str, sizeof(str), git_object_id(rs.to));
+ printf("%s\n", str);
+ git_object_free(rs.to);
+
+ if ((rs.flags & GIT_REVPARSE_MERGE_BASE) != 0) {
+ git_oid base;
+ check_lg2(git_merge_base(&base, ps->repo,
+ git_object_id(rs.from), git_object_id(rs.to)),
+ "Could not find merge base", ps->spec);
+
+ git_oid_tostr(str, sizeof(str), &base);
+ printf("%s\n", str);
+ }
+
+ git_oid_tostr(str, sizeof(str), git_object_id(rs.from));
+ printf("^%s\n", str);
+ git_object_free(rs.from);
+ }
+ else {
+ fatal("Invalid results from git_revparse", ps->spec);
+ }
+
+ return 0;
+}
+
diff --git a/examples/showindex.c b/examples/showindex.c
index e92a9c8de..7f7c38379 100644
--- a/examples/showindex.c
+++ b/examples/showindex.c
@@ -1,10 +1,21 @@
-#include <git2.h>
-#include <stdio.h>
-#include <string.h>
+/*
+ * libgit2 "showindex" example - shows how to extract data from the index
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
int main (int argc, char** argv)
{
- git_repository *repo = NULL;
git_index *index;
unsigned int i, ecount;
char *dir = ".";
@@ -12,31 +23,24 @@ int main (int argc, char** argv)
char out[41];
out[40] = '\0';
+ git_threads_init();
+
+ if (argc > 2)
+ fatal("usage: showindex [<repo-dir>]", NULL);
if (argc > 1)
dir = argv[1];
- if (!dir || argc > 2) {
- fprintf(stderr, "usage: showindex [<repo-dir>]\n");
- return 1;
- }
dirlen = strlen(dir);
if (dirlen > 5 && strcmp(dir + dirlen - 5, "index") == 0) {
- if (git_index_open(&index, dir) < 0) {
- fprintf(stderr, "could not open index: %s\n", dir);
- return 1;
- }
+ check_lg2(git_index_open(&index, dir), "could not open index", dir);
} else {
- if (git_repository_open_ext(&repo, dir, 0, NULL) < 0) {
- fprintf(stderr, "could not open repository: %s\n", dir);
- return 1;
- }
- if (git_repository_index(&index, repo) < 0) {
- fprintf(stderr, "could not open repository index\n");
- return 1;
- }
+ git_repository *repo;
+ check_lg2(git_repository_open_ext(&repo, dir, 0, NULL), "could not open repository", dir);
+ check_lg2(git_repository_index(&index, repo), "could not open repository index", NULL);
+ git_repository_free(repo);
}
- git_index_read(index);
+ git_index_read(index, 0);
ecount = git_index_entrycount(index);
if (!ecount)
@@ -60,8 +64,7 @@ int main (int argc, char** argv)
}
git_index_free(index);
- git_repository_free(repo);
+ git_threads_shutdown();
return 0;
}
-
diff --git a/examples/status.c b/examples/status.c
index 689098415..3adfe0d5d 100644
--- a/examples/status.c
+++ b/examples/status.c
@@ -1,33 +1,32 @@
/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
+ * libgit2 "status" example - shows how to use the status APIs
*
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
+ * 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 <git2.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-enum {
- FORMAT_DEFAULT = 0,
- FORMAT_LONG = 1,
- FORMAT_SHORT = 2,
- FORMAT_PORCELAIN = 3,
-};
-#define MAX_PATHSPEC 8
+#include "common.h"
-/*
+/**
* This example demonstrates the use of the libgit2 status APIs,
* particularly the `git_status_list` object, to roughly simulate the
* output of running `git status`. It serves as a simple example of
* using those APIs to get basic status information.
*
* This does not have:
+ *
* - Robust error handling
* - Colorized or paginated output formatting
*
* This does have:
+ *
* - Examples of translating command line arguments to the status
* options settings to mimic `git status` results.
* - A sample status formatter that matches the default "long" format
@@ -35,34 +34,91 @@ enum {
* - A sample status formatter that matches the "short" format
*/
-static void check(int error, const char *message, const char *extra)
+enum {
+ FORMAT_DEFAULT = 0,
+ FORMAT_LONG = 1,
+ FORMAT_SHORT = 2,
+ FORMAT_PORCELAIN = 3,
+};
+
+#define MAX_PATHSPEC 8
+
+struct opts {
+ git_status_options statusopt;
+ char *repodir;
+ char *pathspec[MAX_PATHSPEC];
+ int npaths;
+ int format;
+ int zterm;
+ int showbranch;
+};
+
+static void parse_opts(struct opts *o, int argc, char *argv[]);
+static void show_branch(git_repository *repo, int format);
+static void print_long(git_status_list *status);
+static void print_short(git_repository *repo, git_status_list *status);
+
+int main(int argc, char *argv[])
{
- const git_error *lg2err;
- const char *lg2msg = "", *lg2spacer = "";
+ git_repository *repo = NULL;
+ git_status_list *status;
+ struct opts o = { GIT_STATUS_OPTIONS_INIT, "." };
- if (!error)
- return;
+ git_threads_init();
- if ((lg2err = giterr_last()) != NULL && lg2err->message != NULL) {
- lg2msg = lg2err->message;
- lg2spacer = " - ";
- }
+ o.statusopt.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+ o.statusopt.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX |
+ GIT_STATUS_OPT_SORT_CASE_SENSITIVELY;
+
+ parse_opts(&o, argc, argv);
+
+ /**
+ * Try to open the repository at the given path (or at the current
+ * directory if none was given).
+ */
+ check_lg2(git_repository_open_ext(&repo, o.repodir, 0, NULL),
+ "Could not open repository", o.repodir);
+
+ if (git_repository_is_bare(repo))
+ fatal("Cannot report status on bare repository",
+ git_repository_path(repo));
+
+ /**
+ * Run status on the repository
+ *
+ * We use `git_status_list_new()` to generate a list of status
+ * information which lets us iterate over it at our
+ * convenience and extract the data we want to show out of
+ * each entry.
+ *
+ * You can use `git_status_foreach()` or
+ * `git_status_foreach_ext()` if you'd prefer to execute a
+ * callback for each entry. The latter gives you more control
+ * about what results are presented.
+ */
+ check_lg2(git_status_list_new(&status, repo, &o.statusopt),
+ "Could not get status", NULL);
- if (extra)
- fprintf(stderr, "%s '%s' [%d]%s%s\n",
- message, extra, error, lg2spacer, lg2msg);
+ if (o.showbranch)
+ show_branch(repo, o.format);
+
+ if (o.format == FORMAT_LONG)
+ print_long(status);
else
- fprintf(stderr, "%s [%d]%s%s\n",
- message, error, lg2spacer, lg2msg);
+ print_short(repo, status);
- exit(1);
-}
+ git_status_list_free(status);
+ git_repository_free(repo);
+ git_threads_shutdown();
-static void fail(const char *message)
-{
- check(-1, message, NULL);
+ return 0;
}
+/**
+ * If the user asked for the branch, let's show the short name of the
+ * branch.
+ */
static void show_branch(git_repository *repo, int format)
{
int error = 0;
@@ -71,14 +127,12 @@ static void show_branch(git_repository *repo, int format)
error = git_repository_head(&head, repo);
- if (error == GIT_EORPHANEDHEAD || error == GIT_ENOTFOUND)
+ if (error == GIT_EUNBORNBRANCH || error == GIT_ENOTFOUND)
branch = NULL;
else if (!error) {
- branch = git_reference_name(head);
- if (!strncmp(branch, "refs/heads/", strlen("refs/heads/")))
- branch += strlen("refs/heads/");
+ branch = git_reference_shorthand(head);
} else
- check(error, "failed to get current branch", NULL);
+ check_lg2(error, "failed to get current branch", NULL);
if (format == FORMAT_LONG)
printf("# On branch %s\n",
@@ -89,7 +143,11 @@ static void show_branch(git_repository *repo, int format)
git_reference_free(head);
}
-static void print_long(git_repository *repo, git_status_list *status)
+/**
+ * This function print out an output similar to git's status command
+ * in long form, including the command-line hints.
+ */
+static void print_long(git_status_list *status)
{
size_t i, maxi = git_status_list_entrycount(status);
const git_status_entry *s;
@@ -97,9 +155,7 @@ static void print_long(git_repository *repo, git_status_list *status)
int changed_in_workdir = 0, rm_in_workdir = 0;
const char *old_path, *new_path;
- (void)repo;
-
- /* print index changes */
+ /** Print index changes. */
for (i = 0; i < maxi; ++i) {
char *istatus = NULL;
@@ -148,16 +204,22 @@ static void print_long(git_repository *repo, git_status_list *status)
}
header = 0;
- /* print workdir changes to tracked files */
+ /** Print workdir changes to tracked files. */
for (i = 0; i < maxi; ++i) {
char *wstatus = NULL;
s = git_status_byindex(status, i);
+ /**
+ * With `GIT_STATUS_OPT_INCLUDE_UNMODIFIED` (not used in this example)
+ * `index_to_workdir` may not be `NULL` even if there are
+ * no differences, in which case it will be a `GIT_DELTA_UNMODIFIED`.
+ */
if (s->status == GIT_STATUS_CURRENT || s->index_to_workdir == NULL)
continue;
+ /** Print out the output since we know the file has some changes */
if (s->status & GIT_STATUS_WT_MODIFIED)
wstatus = "modified: ";
if (s->status & GIT_STATUS_WT_DELETED)
@@ -191,9 +253,8 @@ static void print_long(git_repository *repo, git_status_list *status)
changed_in_workdir = 1;
printf("#\n");
}
- header = 0;
- /* print untracked files */
+ /** Print untracked files. */
header = 0;
@@ -215,7 +276,7 @@ static void print_long(git_repository *repo, git_status_list *status)
header = 0;
- /* print ignored files */
+ /** Print ignored files. */
for (i = 0; i < maxi; ++i) {
s = git_status_byindex(status, i);
@@ -237,6 +298,10 @@ static void print_long(git_repository *repo, git_status_list *status)
printf("no changes added to commit (use \"git add\" and/or \"git commit -a\")\n");
}
+/**
+ * This version of the output prefixes each path with two status
+ * columns and shows submodule status information.
+ */
static void print_short(git_repository *repo, git_status_list *status)
{
size_t i, maxi = git_status_list_entrycount(status);
@@ -287,6 +352,10 @@ static void print_short(git_repository *repo, git_status_list *status)
if (istatus == '?' && wstatus == '?')
continue;
+ /**
+ * A commit in a tree is how submodules are stored, so
+ * let's go take a look at its status.
+ */
if (s->index_to_workdir &&
s->index_to_workdir->new_file.mode == GIT_FILEMODE_COMMIT)
{
@@ -308,6 +377,10 @@ static void print_short(git_repository *repo, git_status_list *status)
}
}
+ /**
+ * Now that we have all the information, it's time to format the output.
+ */
+
if (s->head_to_index) {
a = s->head_to_index->old_file.path;
b = s->head_to_index->new_file.path;
@@ -341,103 +414,61 @@ static void print_short(git_repository *repo, git_status_list *status)
}
}
-int main(int argc, char *argv[])
+/**
+ * Parse options that git's status command supports.
+ */
+static void parse_opts(struct opts *o, int argc, char *argv[])
{
- git_repository *repo = NULL;
- int i, npaths = 0, format = FORMAT_DEFAULT, zterm = 0, showbranch = 0;
- git_status_options opt = GIT_STATUS_OPTIONS_INIT;
- git_status_list *status;
- char *repodir = ".", *pathspec[MAX_PATHSPEC];
+ struct args_info args = ARGS_INFO_INIT;
- opt.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
- opt.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
- GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX |
- GIT_STATUS_OPT_SORT_CASE_SENSITIVELY;
+ for (args.pos = 1; args.pos < argc; ++args.pos) {
+ char *a = argv[args.pos];
- for (i = 1; i < argc; ++i) {
- if (argv[i][0] != '-') {
- if (npaths < MAX_PATHSPEC)
- pathspec[npaths++] = argv[i];
+ if (a[0] != '-') {
+ if (o->npaths < MAX_PATHSPEC)
+ o->pathspec[o->npaths++] = a;
else
- fail("Example only supports a limited pathspec");
+ fatal("Example only supports a limited pathspec", NULL);
}
- else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--short"))
- format = FORMAT_SHORT;
- else if (!strcmp(argv[i], "--long"))
- format = FORMAT_LONG;
- else if (!strcmp(argv[i], "--porcelain"))
- format = FORMAT_PORCELAIN;
- else if (!strcmp(argv[i], "-b") || !strcmp(argv[i], "--branch"))
- showbranch = 1;
- else if (!strcmp(argv[i], "-z")) {
- zterm = 1;
- if (format == FORMAT_DEFAULT)
- format = FORMAT_PORCELAIN;
+ else if (!strcmp(a, "-s") || !strcmp(a, "--short"))
+ o->format = FORMAT_SHORT;
+ else if (!strcmp(a, "--long"))
+ o->format = FORMAT_LONG;
+ else if (!strcmp(a, "--porcelain"))
+ o->format = FORMAT_PORCELAIN;
+ else if (!strcmp(a, "-b") || !strcmp(a, "--branch"))
+ o->showbranch = 1;
+ else if (!strcmp(a, "-z")) {
+ o->zterm = 1;
+ if (o->format == FORMAT_DEFAULT)
+ o->format = FORMAT_PORCELAIN;
}
- else if (!strcmp(argv[i], "--ignored"))
- opt.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED;
- else if (!strcmp(argv[i], "-uno") ||
- !strcmp(argv[i], "--untracked-files=no"))
- opt.flags &= ~GIT_STATUS_OPT_INCLUDE_UNTRACKED;
- else if (!strcmp(argv[i], "-unormal") ||
- !strcmp(argv[i], "--untracked-files=normal"))
- opt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
- else if (!strcmp(argv[i], "-uall") ||
- !strcmp(argv[i], "--untracked-files=all"))
- opt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ else if (!strcmp(a, "--ignored"))
+ o->statusopt.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED;
+ else if (!strcmp(a, "-uno") ||
+ !strcmp(a, "--untracked-files=no"))
+ o->statusopt.flags &= ~GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+ else if (!strcmp(a, "-unormal") ||
+ !strcmp(a, "--untracked-files=normal"))
+ o->statusopt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+ else if (!strcmp(a, "-uall") ||
+ !strcmp(a, "--untracked-files=all"))
+ o->statusopt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
- else if (!strcmp(argv[i], "--ignore-submodules=all"))
- opt.flags |= GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
- else if (!strncmp(argv[i], "--git-dir=", strlen("--git-dir=")))
- repodir = argv[i] + strlen("--git-dir=");
+ else if (!strcmp(a, "--ignore-submodules=all"))
+ o->statusopt.flags |= GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
+ else if (!strncmp(a, "--git-dir=", strlen("--git-dir=")))
+ o->repodir = a + strlen("--git-dir=");
else
- check(-1, "Unsupported option", argv[i]);
+ check_lg2(-1, "Unsupported option", a);
}
- if (format == FORMAT_DEFAULT)
- format = FORMAT_LONG;
- if (format == FORMAT_LONG)
- showbranch = 1;
- if (npaths > 0) {
- opt.pathspec.strings = pathspec;
- opt.pathspec.count = npaths;
+ if (o->format == FORMAT_DEFAULT)
+ o->format = FORMAT_LONG;
+ if (o->format == FORMAT_LONG)
+ o->showbranch = 1;
+ if (o->npaths > 0) {
+ o->statusopt.pathspec.strings = o->pathspec;
+ o->statusopt.pathspec.count = o->npaths;
}
-
- /*
- * Try to open the repository at the given path (or at the current
- * directory if none was given).
- */
- check(git_repository_open_ext(&repo, repodir, 0, NULL),
- "Could not open repository", repodir);
-
- if (git_repository_is_bare(repo))
- fail("Cannot report status on bare repository");
-
- /*
- * Run status on the repository
- *
- * Because we want to simluate a full "git status" run and want to
- * support some command line options, we use `git_status_foreach_ext()`
- * instead of just the plain status call. This allows (a) iterating
- * over the index and then the workdir and (b) extra flags that control
- * which files are included. If you just want simple status (e.g. to
- * enumerate files that are modified) then you probably don't need the
- * extended API.
- */
- check(git_status_list_new(&status, repo, &opt),
- "Could not get status", NULL);
-
- if (showbranch)
- show_branch(repo, format);
-
- if (format == FORMAT_LONG)
- print_long(repo, status);
- else
- print_short(repo, status);
-
- git_status_list_free(status);
- git_repository_free(repo);
-
- return 0;
}
-
diff --git a/examples/test/test-rev-list.sh b/examples/test/test-rev-list.sh
index bc0eea7cf..aa645be5e 100755
--- a/examples/test/test-rev-list.sh
+++ b/examples/test/test-rev-list.sh
@@ -4,7 +4,7 @@ THIS_FILE="$(readlink -f "$0")"
ROOT="$(dirname "$(dirname "$(dirname "$THIS_FILE")")")"
PROGRAM="$ROOT"/examples/rev-list
LIBDIR="$ROOT"/build
-REPO="$ROOT"/tests-clar/resources/testrepo.git
+REPO="$ROOT"/tests/resources/testrepo.git
cd "$REPO"
diff --git a/git.git-authors b/git.git-authors
index 3fcf27ffc..7d4c95bb3 100644
--- a/git.git-authors
+++ b/git.git-authors
@@ -68,3 +68,4 @@ ok Sebastian Schuberth <sschuberth@gmail.com>
ok Shawn O. Pearce <spearce@spearce.org>
ok Steffen Prohaska <prohaska@zib.de>
ok Sven Verdoolaege <skimo@kotnet.org>
+ok Torsten Bögershausen <tboegi@web.de>
diff --git a/include/git2.h b/include/git2.h
index 5f9fc4824..d00a20a2e 100644
--- a/include/git2.h
+++ b/include/git2.h
@@ -8,53 +8,52 @@
#ifndef INCLUDE_git_git_h__
#define INCLUDE_git_git_h__
-#include "git2/version.h"
-
-#include "git2/common.h"
-#include "git2/threads.h"
-#include "git2/errors.h"
-
-#include "git2/types.h"
-
-#include "git2/oid.h"
-#include "git2/signature.h"
-#include "git2/odb.h"
-
-#include "git2/repository.h"
-#include "git2/revwalk.h"
-#include "git2/merge.h"
-#include "git2/graph.h"
-#include "git2/refs.h"
-#include "git2/reflog.h"
-#include "git2/revparse.h"
-
-#include "git2/object.h"
+#include "git2/attr.h"
#include "git2/blob.h"
+#include "git2/blame.h"
+#include "git2/branch.h"
+#include "git2/buffer.h"
+#include "git2/checkout.h"
+#include "git2/clone.h"
#include "git2/commit.h"
-#include "git2/tag.h"
-#include "git2/tree.h"
-#include "git2/diff.h"
-
-#include "git2/index.h"
+#include "git2/common.h"
#include "git2/config.h"
-#include "git2/transport.h"
-#include "git2/remote.h"
-#include "git2/clone.h"
-#include "git2/checkout.h"
-#include "git2/push.h"
-
-#include "git2/attr.h"
+#include "git2/diff.h"
+#include "git2/errors.h"
+#include "git2/filter.h"
+#include "git2/graph.h"
#include "git2/ignore.h"
-#include "git2/branch.h"
-#include "git2/refspec.h"
-#include "git2/net.h"
-#include "git2/status.h"
+#include "git2/index.h"
#include "git2/indexer.h"
-#include "git2/submodule.h"
-#include "git2/notes.h"
-#include "git2/reset.h"
+#include "git2/merge.h"
#include "git2/message.h"
+#include "git2/net.h"
+#include "git2/notes.h"
+#include "git2/object.h"
+#include "git2/odb.h"
+#include "git2/oid.h"
#include "git2/pack.h"
+#include "git2/patch.h"
+#include "git2/pathspec.h"
+#include "git2/push.h"
+#include "git2/refdb.h"
+#include "git2/reflog.h"
+#include "git2/refs.h"
+#include "git2/refspec.h"
+#include "git2/remote.h"
+#include "git2/repository.h"
+#include "git2/reset.h"
+#include "git2/revparse.h"
+#include "git2/revwalk.h"
+#include "git2/signature.h"
#include "git2/stash.h"
+#include "git2/status.h"
+#include "git2/submodule.h"
+#include "git2/tag.h"
+#include "git2/threads.h"
+#include "git2/transport.h"
+#include "git2/tree.h"
+#include "git2/types.h"
+#include "git2/version.h"
#endif
diff --git a/include/git2/blame.h b/include/git2/blame.h
new file mode 100644
index 000000000..73bcc5bc6
--- /dev/null
+++ b/include/git2/blame.h
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_git_blame_h__
+#define INCLUDE_git_blame_h__
+
+#include "common.h"
+#include "oid.h"
+
+/**
+ * @file git2/blame.h
+ * @brief Git blame routines
+ * @defgroup git_blame Git blame routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Flags for indicating option behavior for git_blame APIs.
+ */
+typedef enum {
+ /** Normal blame, the default */
+ GIT_BLAME_NORMAL = 0,
+ /** Track lines that have moved within a file (like `git blame -M`).
+ * NOT IMPLEMENTED. */
+ GIT_BLAME_TRACK_COPIES_SAME_FILE = (1<<0),
+ /** Track lines that have moved across files in the same commit (like `git blame -C`).
+ * NOT IMPLEMENTED. */
+ GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES = (1<<1),
+ /** Track lines that have been copied from another file that exists in the
+ * same commit (like `git blame -CC`). Implies SAME_FILE.
+ * NOT IMPLEMENTED. */
+ GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES = (1<<2),
+ /** Track lines that have been copied from another file that exists in *any*
+ * commit (like `git blame -CCC`). Implies SAME_COMMIT_COPIES.
+ * NOT IMPLEMENTED. */
+ GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES = (1<<3),
+} git_blame_flag_t;
+
+/**
+ * Blame options structure
+ *
+ * Use zeros to indicate default settings. It's easiest to use the
+ * `GIT_BLAME_OPTIONS_INIT` macro:
+ * git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+ *
+ * - `flags` is a combination of the `git_blame_flag_t` values above.
+ * - `min_match_characters` is the lower bound on the number of alphanumeric
+ * characters that must be detected as moving/copying within a file for it to
+ * associate those lines with the parent commit. The default value is 20.
+ * This value only takes effect if any of the `GIT_BLAME_TRACK_COPIES_*`
+ * flags are specified.
+ * - `newest_commit` is the id of the newest commit to consider. The default
+ * is HEAD.
+ * - `oldest_commit` is the id of the oldest commit to consider. The default
+ * is the first commit encountered with a NULL parent.
+ * - `min_line` is the first line in the file to blame. The default is 1 (line
+ * numbers start with 1).
+ * - `max_line` is the last line in the file to blame. The default is the last
+ * line of the file.
+ */
+
+typedef struct git_blame_options {
+ unsigned int version;
+
+ uint32_t flags;
+ uint16_t min_match_characters;
+ git_oid newest_commit;
+ git_oid oldest_commit;
+ uint32_t min_line;
+ uint32_t max_line;
+} git_blame_options;
+
+#define GIT_BLAME_OPTIONS_VERSION 1
+#define GIT_BLAME_OPTIONS_INIT {GIT_BLAME_OPTIONS_VERSION}
+
+/**
+ * Structure that represents a blame hunk.
+ *
+ * - `lines_in_hunk` is the number of lines in this hunk
+ * - `final_commit_id` is the OID of the commit where this line was last
+ * changed.
+ * - `final_start_line_number` is the 1-based line number where this hunk
+ * begins, in the final version of the file
+ * - `orig_commit_id` is the OID of the commit where this hunk was found. This
+ * will usually be the same as `final_commit_id`, except when
+ * `GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES` has been specified.
+ * - `orig_path` is the path to the file where this hunk originated, as of the
+ * commit specified by `orig_commit_id`.
+ * - `orig_start_line_number` is the 1-based line number where this hunk begins
+ * in the file named by `orig_path` in the commit specified by
+ * `orig_commit_id`.
+ * - `boundary` is 1 iff the hunk has been tracked to a boundary commit (the
+ * root, or the commit specified in git_blame_options.oldest_commit)
+ */
+typedef struct git_blame_hunk {
+ uint16_t lines_in_hunk;
+
+ git_oid final_commit_id;
+ uint16_t final_start_line_number;
+ git_signature *final_signature;
+
+ git_oid orig_commit_id;
+ const char *orig_path;
+ uint16_t orig_start_line_number;
+ git_signature *orig_signature;
+
+ char boundary;
+} git_blame_hunk;
+
+
+/* Opaque structure to hold blame results */
+typedef struct git_blame git_blame;
+
+/**
+ * Gets the number of hunks that exist in the blame structure.
+ */
+GIT_EXTERN(uint32_t) git_blame_get_hunk_count(git_blame *blame);
+
+/**
+ * Gets the blame hunk at the given index.
+ *
+ * @param blame the blame structure to query
+ * @param index index of the hunk to retrieve
+ * @return the hunk at the given index, or NULL on error
+ */
+GIT_EXTERN(const git_blame_hunk*) git_blame_get_hunk_byindex(
+ git_blame *blame,
+ uint32_t index);
+
+/**
+ * Gets the hunk that relates to the given line number in the newest commit.
+ *
+ * @param blame the blame structure to query
+ * @param lineno the (1-based) line number to find a hunk for
+ * @return the hunk that contains the given line, or NULL on error
+ */
+GIT_EXTERN(const git_blame_hunk*) git_blame_get_hunk_byline(
+ git_blame *blame,
+ uint32_t lineno);
+
+/**
+ * Get the blame for a single file.
+ *
+ * @param out pointer that will receive the blame object
+ * @param repo repository whose history is to be walked
+ * @param path path to file to consider
+ * @param options options for the blame operation. If NULL, this is treated as
+ * though GIT_BLAME_OPTIONS_INIT were passed.
+ * @return 0 on success, or an error code. (use giterr_last for information
+ * about the error.)
+ */
+GIT_EXTERN(int) git_blame_file(
+ git_blame **out,
+ git_repository *repo,
+ const char *path,
+ git_blame_options *options);
+
+
+/**
+ * Get blame data for a file that has been modified in memory. The `reference`
+ * parameter is a pre-calculated blame for the in-odb history of the file. This
+ * means that once a file blame is completed (which can be expensive), updating
+ * the buffer blame is very fast.
+ *
+ * Lines that differ between the buffer and the committed version are marked as
+ * having a zero OID for their final_commit_id.
+ *
+ * @param out pointer that will receive the resulting blame data
+ * @param reference cached blame from the history of the file (usually the output
+ * from git_blame_file)
+ * @param buffer the (possibly) modified contents of the file
+ * @param buffer_len number of valid bytes in the buffer
+ * @return 0 on success, or an error code. (use giterr_last for information
+ * about the error)
+ */
+GIT_EXTERN(int) git_blame_buffer(
+ git_blame **out,
+ git_blame *reference,
+ const char *buffer,
+ uint32_t buffer_len);
+
+/**
+ * Free memory allocated by git_blame_file or git_blame_buffer.
+ *
+ * @param blame the blame structure to free
+ */
+GIT_EXTERN(void) git_blame_free(git_blame *blame);
+
+/** @} */
+GIT_END_DECL
+#endif
+
diff --git a/include/git2/blob.h b/include/git2/blob.h
index 8fca48966..dcab4fbe0 100644
--- a/include/git2/blob.h
+++ b/include/git2/blob.h
@@ -11,6 +11,7 @@
#include "types.h"
#include "oid.h"
#include "object.h"
+#include "buffer.h"
/**
* @file git2/blob.h
@@ -96,6 +97,37 @@ GIT_EXTERN(const void *) git_blob_rawcontent(const git_blob *blob);
GIT_EXTERN(git_off_t) git_blob_rawsize(const git_blob *blob);
/**
+ * Get a buffer with the filtered content of a blob.
+ *
+ * This applies filters as if the blob was being checked out to the
+ * working directory under the specified filename. This may apply
+ * CRLF filtering or other types of changes depending on the file
+ * attributes set for the blob and the content detected in it.
+ *
+ * The output is written into a `git_buf` which the caller must free
+ * when done (via `git_buf_free`).
+ *
+ * If no filters need to be applied, then the `out` buffer will just be
+ * populated with a pointer to the raw content of the blob. In that case,
+ * be careful to *not* free the blob until done with the buffer. To keep
+ * the data detached from the blob, call `git_buf_grow` on the buffer
+ * with a `want_size` of 0 and the buffer will be reallocated to be
+ * detached from the blob.
+ *
+ * @param out The git_buf to be filled in
+ * @param blob Pointer to the blob
+ * @param as_path Path used for file attribute lookups, etc.
+ * @param check_for_binary_data Should this test if blob content contains
+ * NUL bytes / looks like binary data before applying filters?
+ * @return 0 on success or an error code
+ */
+GIT_EXTERN(int) git_blob_filtered_content(
+ git_buf *out,
+ git_blob *blob,
+ const char *as_path,
+ int check_for_binary_data);
+
+/**
* Read a file from the working folder of a repository
* and write it to the Object Database as a loose blob
*
diff --git a/include/git2/branch.h b/include/git2/branch.h
index de414e9b0..44d6fd9c3 100644
--- a/include/git2/branch.h
+++ b/include/git2/branch.h
@@ -66,33 +66,41 @@ GIT_EXTERN(int) git_branch_create(
*/
GIT_EXTERN(int) git_branch_delete(git_reference *branch);
-typedef int (*git_branch_foreach_cb)(
- const char *branch_name,
- git_branch_t branch_type,
- void *payload);
+/** Iterator type for branches */
+typedef struct git_branch_iterator git_branch_iterator;
/**
- * Loop over all the branches and issue a callback for each one.
- *
- * If the callback returns a non-zero value, this will stop looping.
+ * Create an iterator which loops over the requested branches.
*
+ * @param out the iterator
* @param repo Repository where to find the branches.
- *
* @param list_flags Filtering flags for the branch
* listing. Valid values are GIT_BRANCH_LOCAL, GIT_BRANCH_REMOTE
* or a combination of the two.
*
- * @param branch_cb Callback to invoke per found branch.
+ * @return 0 on success or an error code
+ */
+GIT_EXTERN(int) git_branch_iterator_new(
+ git_branch_iterator **out,
+ git_repository *repo,
+ git_branch_t list_flags);
+
+/**
+ * Retrieve the next branch from the iterator
*
- * @param payload Extra parameter to callback function.
+ * @param out the reference
+ * @param out_type the type of branch (local or remote-tracking)
+ * @param iter the branch iterator
+ * @return 0 on success, GIT_ITEROVER if there are no more branches or an error code.
+ */
+GIT_EXTERN(int) git_branch_next(git_reference **out, git_branch_t *out_type, git_branch_iterator *iter);
+
+/**
+ * Free a branch iterator
*
- * @return 0 on success, GIT_EUSER on non-zero callback, or error code
+ * @param iter the iterator to free
*/
-GIT_EXTERN(int) git_branch_foreach(
- git_repository *repo,
- unsigned int list_flags,
- git_branch_foreach_cb branch_cb,
- void *payload);
+GIT_EXTERN(void) git_branch_iterator_free(git_branch_iterator *iter);
/**
* Move/rename an existing local branch reference.
diff --git a/include/git2/buffer.h b/include/git2/buffer.h
new file mode 100644
index 000000000..36a61e6c9
--- /dev/null
+++ b/include/git2/buffer.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_buf_h__
+#define INCLUDE_git_buf_h__
+
+#include "common.h"
+
+/**
+ * @file git2/buffer.h
+ * @brief Buffer export structure
+ *
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * A data buffer for exporting data from libgit2
+ *
+ * Sometimes libgit2 wants to return an allocated data buffer to the
+ * caller and have the caller take responsibility for freeing that memory.
+ * This can be awkward if the caller does not have easy access to the same
+ * allocation functions that libgit2 is using. In those cases, libgit2
+ * will fill in a `git_buf` and the caller can use `git_buf_free()` to
+ * release it when they are done.
+ *
+ * A `git_buf` may also be used for the caller to pass in a reference to
+ * a block of memory they hold. In this case, libgit2 will not resize or
+ * free the memory, but will read from it as needed.
+ *
+ * A `git_buf` is a public structure with three fields:
+ *
+ * - `ptr` points to the start of the allocated memory. If it is NULL,
+ * then the `git_buf` is considered empty and libgit2 will feel free
+ * to overwrite it with new data.
+ *
+ * - `size` holds the size (in bytes) of the data that is actually used.
+ *
+ * - `asize` holds the known total amount of allocated memory if the `ptr`
+ * was allocated by libgit2. It may be larger than `size`. If `ptr`
+ * was not allocated by libgit2 and should not be resized and/or freed,
+ * then `asize` will be set to zero.
+ *
+ * Some APIs may occasionally do something slightly unusual with a buffer,
+ * such as setting `ptr` to a value that was passed in by the user. In
+ * those cases, the behavior will be clearly documented by the API.
+ */
+typedef struct {
+ char *ptr;
+ size_t asize, size;
+} git_buf;
+
+/**
+ * Static initializer for git_buf from static buffer
+ */
+#define GIT_BUF_INIT_CONST(STR,LEN) { (char *)(STR), 0, (size_t)(LEN) }
+
+/**
+ * Free the memory referred to by the git_buf.
+ *
+ * Note that this does not free the `git_buf` itself, just the memory
+ * pointed to by `buffer->ptr`. This will not free the memory if it looks
+ * like it was not allocated internally, but it will clear the buffer back
+ * to the empty state.
+ *
+ * @param buffer The buffer to deallocate
+ */
+GIT_EXTERN(void) git_buf_free(git_buf *buffer);
+
+/**
+ * Resize the buffer allocation to make more space.
+ *
+ * This will attempt to grow the buffer to accomodate the target size.
+ *
+ * If the buffer refers to memory that was not allocated by libgit2 (i.e.
+ * the `asize` field is zero), then `ptr` will be replaced with a newly
+ * allocated block of data. Be careful so that memory allocated by the
+ * caller is not lost. As a special variant, if you pass `target_size` as
+ * 0 and the memory is not allocated by libgit2, this will allocate a new
+ * buffer of size `size` and copy the external data into it.
+ *
+ * Currently, this will never shrink a buffer, only expand it.
+ *
+ * If the allocation fails, this will return an error and the buffer will be
+ * marked as invalid for future operations, invaliding the contents.
+ *
+ * @param buffer The buffer to be resized; may or may not be allocated yet
+ * @param target_size The desired available size
+ * @return 0 on success, -1 on allocation failure
+ */
+GIT_EXTERN(int) git_buf_grow(git_buf *buffer, size_t target_size);
+
+/**
+ * Set buffer to a copy of some raw data.
+ *
+ * @param buffer The buffer to set
+ * @param data The data to copy into the buffer
+ * @param datalen The length of the data to copy into the buffer
+ * @return 0 on success, -1 on allocation failure
+ */
+GIT_EXTERN(int) git_buf_set(
+ git_buf *buffer, const void *data, size_t datalen);
+
+GIT_END_DECL
+
+/** @} */
+
+#endif
diff --git a/include/git2/checkout.h b/include/git2/checkout.h
index a086408c7..efafdc3e4 100644
--- a/include/git2/checkout.h
+++ b/include/git2/checkout.h
@@ -131,6 +131,13 @@ typedef enum {
/** Don't refresh index/config/etc before doing checkout */
GIT_CHECKOUT_NO_REFRESH = (1u << 9),
+ /** Allow checkout to skip unmerged files */
+ GIT_CHECKOUT_SKIP_UNMERGED = (1u << 10),
+ /** For unmerged files, checkout stage 2 from index */
+ GIT_CHECKOUT_USE_OURS = (1u << 11),
+ /** For unmerged files, checkout stage 3 from index */
+ GIT_CHECKOUT_USE_THEIRS = (1u << 12),
+
/** Treat pathspec as simple list of exact match file paths */
GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH = (1u << 13),
@@ -141,13 +148,6 @@ typedef enum {
* THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED
*/
- /** Allow checkout to skip unmerged files (NOT IMPLEMENTED) */
- GIT_CHECKOUT_SKIP_UNMERGED = (1u << 10),
- /** For unmerged files, checkout stage 2 from index (NOT IMPLEMENTED) */
- GIT_CHECKOUT_USE_OURS = (1u << 11),
- /** For unmerged files, checkout stage 3 from index (NOT IMPLEMENTED) */
- GIT_CHECKOUT_USE_THEIRS = (1u << 12),
-
/** Recursively checkout submodules with same options (NOT IMPLEMENTED) */
GIT_CHECKOUT_UPDATE_SUBMODULES = (1u << 16),
/** Recursively checkout submodules if HEAD moved in super repo (NOT IMPLEMENTED) */
@@ -238,6 +238,9 @@ typedef struct git_checkout_opts {
git_tree *baseline; /** expected content of workdir, defaults to HEAD */
const char *target_directory; /** alternative checkout path to workdir */
+
+ const char *our_label; /** the name of the "our" side of conflicts */
+ const char *their_label; /** the name of the "their" side of conflicts */
} git_checkout_opts;
#define GIT_CHECKOUT_OPTS_VERSION 1
@@ -249,13 +252,13 @@ typedef struct git_checkout_opts {
*
* @param repo repository to check out (must be non-bare)
* @param opts specifies checkout options (may be NULL)
- * @return 0 on success, GIT_EORPHANEDHEAD when HEAD points to a non existing
+ * @return 0 on success, GIT_EUNBORNBRANCH when HEAD points to a non existing
* branch, GIT_ERROR otherwise (use giterr_last for information
* about the error)
*/
GIT_EXTERN(int) git_checkout_head(
git_repository *repo,
- git_checkout_opts *opts);
+ const git_checkout_opts *opts);
/**
* Updates files in the working tree to match the content of the index.
@@ -269,7 +272,7 @@ GIT_EXTERN(int) git_checkout_head(
GIT_EXTERN(int) git_checkout_index(
git_repository *repo,
git_index *index,
- git_checkout_opts *opts);
+ const git_checkout_opts *opts);
/**
* Updates files in the index and working tree to match the content of the
@@ -277,7 +280,7 @@ GIT_EXTERN(int) git_checkout_index(
*
* @param repo repository to check out (must be non-bare)
* @param treeish a commit, tag or tree which content will be used to update
- * the working directory
+ * the working directory (or NULL to use HEAD)
* @param opts specifies checkout options (may be NULL)
* @return 0 on success, GIT_ERROR otherwise (use giterr_last for information
* about the error)
@@ -285,7 +288,7 @@ GIT_EXTERN(int) git_checkout_index(
GIT_EXTERN(int) git_checkout_tree(
git_repository *repo,
const git_object *treeish,
- git_checkout_opts *opts);
+ const git_checkout_opts *opts);
/** @} */
GIT_END_DECL
diff --git a/include/git2/clone.h b/include/git2/clone.h
index 5858b4e32..331cf92e7 100644
--- a/include/git2/clone.h
+++ b/include/git2/clone.h
@@ -35,30 +35,12 @@ GIT_BEGIN_DECL
* set the `checkout_strategy` to GIT_CHECKOUT_DEFAULT.
* - `bare` should be set to zero to create a standard repo, non-zero for
* a bare repo
- * - `fetch_progress_cb` is optional callback for fetch progress. Be aware that
- * this is called inline with network and indexing operations, so performance
- * may be affected.
- * - `fetch_progress_payload` is payload for fetch_progress_cb
+ * - `ignore_cert_errors` should be set to 1 if errors validating the remote host's
+ * certificate should be ignored.
*
* ** "origin" remote options: **
* - `remote_name` is the name given to the "origin" remote. The default is
* "origin".
- * - `pushurl` is a URL to be used for pushing. NULL means use the fetch url.
- * - `fetch_spec` is the fetch specification to be used for fetching. NULL
- * results in the same behavior as GIT_REMOTE_DEFAULT_FETCH.
- * - `push_spec` is the fetch specification to be used for pushing. NULL means
- * use the same spec as for fetching.
- * - `cred_acquire_cb` is a callback to be used if credentials are required
- * during the initial fetch.
- * - `cred_acquire_payload` is the payload for the above callback.
- * - `transport_flags` is flags used to create transport if no transport is
- * provided.
- * - `transport` is a custom transport to be used for the initial fetch. NULL
- * means use the transport autodetected from the URL.
- * - `remote_callbacks` may be used to specify custom progress callbacks for
- * the origin remote before the fetch is initiated.
- * - `remote_autotag` may be used to specify the autotag setting before the
- * initial fetch. The default is GIT_REMOTE_DOWNLOAD_TAGS_ALL.
* - `checkout_branch` gives the name of the branch to checkout. NULL means
* use the remote's HEAD.
*/
@@ -67,29 +49,23 @@ typedef struct git_clone_options {
unsigned int version;
git_checkout_opts checkout_opts;
- int bare;
- git_transfer_progress_callback fetch_progress_cb;
- void *fetch_progress_payload;
+ git_remote_callbacks remote_callbacks;
+ int bare;
+ int ignore_cert_errors;
const char *remote_name;
- const char *pushurl;
- const char *fetch_spec;
- const char *push_spec;
- git_cred_acquire_cb cred_acquire_cb;
- void *cred_acquire_payload;
- git_transport_flags_t transport_flags;
- git_transport *transport;
- git_remote_callbacks *remote_callbacks;
- git_remote_autotag_option_t remote_autotag;
const char* checkout_branch;
} git_clone_options;
#define GIT_CLONE_OPTIONS_VERSION 1
-#define GIT_CLONE_OPTIONS_INIT {GIT_CLONE_OPTIONS_VERSION, {GIT_CHECKOUT_OPTS_VERSION, GIT_CHECKOUT_SAFE_CREATE}}
+#define GIT_CLONE_OPTIONS_INIT {GIT_CLONE_OPTIONS_VERSION, {GIT_CHECKOUT_OPTS_VERSION, GIT_CHECKOUT_SAFE_CREATE}, GIT_REMOTE_CALLBACKS_INIT}
/**
- * Clone a remote repository, and checkout the branch pointed to by the remote
- * HEAD.
+ * Clone a remote repository.
+ *
+ * This version handles the simple case. If you'd like to create the
+ * repository or remote with non-default settings, you can create and
+ * configure them and then use `git_clone_into()`.
*
* @param out pointer that will receive the resulting repository object
* @param url the remote repository to clone
@@ -105,6 +81,22 @@ GIT_EXTERN(int) git_clone(
const char *local_path,
const git_clone_options *options);
+/**
+ * Clone into a repository
+ *
+ * After creating the repository and remote and configuring them for
+ * paths and callbacks respectively, you can call this function to
+ * perform the clone operation and optionally checkout files.
+ *
+ * @param repo the repository to use
+ * @param remote the remote repository to clone from
+ * @param co_opts options to use during checkout
+ * @param branch the branch to checkout after the clone, pass NULL for the remote's
+ * default branch
+ * @return 0 on success or an error code
+ */
+GIT_EXTERN(int) git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_opts *co_opts, const char *branch);
+
/** @} */
GIT_END_DECL
#endif
diff --git a/include/git2/commit.h b/include/git2/commit.h
index 544d21d87..a08cf1c6c 100644
--- a/include/git2/commit.h
+++ b/include/git2/commit.h
@@ -24,17 +24,24 @@ GIT_BEGIN_DECL
/**
* Lookup a commit object from a repository.
*
+ * The returned object should be released with `git_commit_free` when no
+ * longer needed.
+ *
* @param commit pointer to the looked up commit
* @param repo the repo to use when locating the commit.
* @param id identity of the commit to locate. If the object is
* an annotated tag it will be peeled back to the commit.
* @return 0 or an error code
*/
-GIT_EXTERN(int) git_commit_lookup(git_commit **commit, git_repository *repo, const git_oid *id);
+GIT_EXTERN(int) git_commit_lookup(
+ git_commit **commit, git_repository *repo, const git_oid *id);
/**
- * Lookup a commit object from a repository,
- * given a prefix of its identifier (short id).
+ * Lookup a commit object from a repository, given a prefix of its
+ * identifier (short id).
+ *
+ * The returned object should be released with `git_commit_free` when no
+ * longer needed.
*
* @see git_object_lookup_prefix
*
@@ -45,7 +52,8 @@ GIT_EXTERN(int) git_commit_lookup(git_commit **commit, git_repository *repo, con
* @param len the length of the short identifier
* @return 0 or an error code
*/
-GIT_EXTERN(int) git_commit_lookup_prefix(git_commit **commit, git_repository *repo, const git_oid *id, size_t len);
+GIT_EXTERN(int) git_commit_lookup_prefix(
+ git_commit **commit, git_repository *repo, const git_oid *id, size_t len);
/**
* Close an open commit
@@ -92,12 +100,23 @@ GIT_EXTERN(const char *) git_commit_message_encoding(const git_commit *commit);
/**
* Get the full message of a commit.
*
+ * The returned message will be slightly prettified by removing any
+ * potential leading newlines.
+ *
* @param commit a previously loaded commit.
* @return the message of a commit
*/
GIT_EXTERN(const char *) git_commit_message(const git_commit *commit);
/**
+ * Get the full raw message of a commit.
+ *
+ * @param commit a previously loaded commit.
+ * @return the raw message of a commit
+ */
+GIT_EXTERN(const char *) git_commit_message_raw(const git_commit *commit);
+
+/**
* Get the commit time (i.e. committer time) of a commit.
*
* @param commit a previously loaded commit.
@@ -130,6 +149,14 @@ GIT_EXTERN(const git_signature *) git_commit_committer(const git_commit *commit)
GIT_EXTERN(const git_signature *) git_commit_author(const git_commit *commit);
/**
+ * Get the full raw text of the commit header.
+ *
+ * @param commit a previously loaded commit
+ * @return the header text of the commit
+ */
+GIT_EXTERN(const char *) git_commit_raw_header(const git_commit *commit);
+
+/**
* Get the tree pointed to by a commit.
*
* @param tree_out pointer where to store the tree object
diff --git a/include/git2/common.h b/include/git2/common.h
index b52e13918..dca0c9c21 100644
--- a/include/git2/common.h
+++ b/include/git2/common.h
@@ -105,7 +105,8 @@ GIT_EXTERN(void) git_libgit2_version(int *major, int *minor, int *rev);
*/
typedef enum {
GIT_CAP_THREADS = ( 1 << 0 ),
- GIT_CAP_HTTPS = ( 1 << 1 )
+ GIT_CAP_HTTPS = ( 1 << 1 ),
+ GIT_CAP_SSH = ( 1 << 2 ),
} git_cap_t;
/**
@@ -135,7 +136,9 @@ typedef enum {
GIT_OPT_SET_CACHE_OBJECT_LIMIT,
GIT_OPT_SET_CACHE_MAX_SIZE,
GIT_OPT_ENABLE_CACHING,
- GIT_OPT_GET_CACHED_MEMORY
+ GIT_OPT_GET_CACHED_MEMORY,
+ GIT_OPT_GET_TEMPLATE_PATH,
+ GIT_OPT_SET_TEMPLATE_PATH
} git_libgit2_opt_t;
/**
@@ -209,6 +212,18 @@ typedef enum {
* > Get the current bytes in cache and the maximum that would be
* > allowed in the cache.
*
+ * * opts(GIT_OPT_GET_TEMPLATE_PATH, char *out, size_t len)
+ *
+ * > Get the default template path.
+ * > The path is written to the `out`
+ * > buffer up to size `len`. Returns GIT_EBUFS if buffer is too small.
+ *
+ * * opts(GIT_OPT_SET_TEMPLATE_PATH, const char *path)
+ *
+ * > Set the default template path.
+ * >
+ * > - `path` directory of template.
+ *
* @param option Option key
* @param ... value to set the option
* @return 0 on success, <0 on failure
diff --git a/include/git2/config.h b/include/git2/config.h
index 827d43544..95da4bc03 100644
--- a/include/git2/config.h
+++ b/include/git2/config.h
@@ -61,6 +61,7 @@ typedef struct {
} git_config_entry;
typedef int (*git_config_foreach_cb)(const git_config_entry *, void *);
+typedef struct git_config_iterator git_config_iterator;
typedef enum {
GIT_CVAR_FALSE = 0,
@@ -327,7 +328,7 @@ GIT_EXTERN(int) git_config_get_bool(int *out, const git_config *cfg, const char
GIT_EXTERN(int) git_config_get_string(const char **out, const git_config *cfg, const char *name);
/**
- * Get each value of a multivar.
+ * Get each value of a multivar in a foreach callback
*
* The callback will be called on each variable found
*
@@ -338,7 +339,34 @@ GIT_EXTERN(int) git_config_get_string(const char **out, const git_config *cfg, c
* @param callback the function to be called on each value of the variable
* @param payload opaque pointer to pass to the callback
*/
-GIT_EXTERN(int) git_config_get_multivar(const git_config *cfg, const char *name, const char *regexp, git_config_foreach_cb callback, void *payload);
+GIT_EXTERN(int) git_config_get_multivar_foreach(const git_config *cfg, const char *name, const char *regexp, git_config_foreach_cb callback, void *payload);
+
+/**
+ * Get each value of a multivar
+ *
+ * @param out pointer to store the iterator
+ * @param cfg where to look for the variable
+ * @param name the variable's name
+ * @param regexp regular expression to filter which variables we're
+ * interested in. Use NULL to indicate all
+ */
+GIT_EXTERN(int) git_config_multivar_iterator_new(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp);
+
+/**
+ * Return the current entry and advance the iterator
+ *
+ * @param entry pointer to store the entry
+ * @param iter the iterator
+ * @return 0 or an error code. GIT_ITEROVER if the iteration has completed
+ */
+GIT_EXTERN(int) git_config_next(git_config_entry **entry, git_config_iterator *iter);
+
+/**
+ * Free a config iterator
+ *
+ * @param iter the iterator to free
+ */
+GIT_EXTERN(void) git_config_iterator_free(git_config_iterator *iter);
/**
* Set the value of an integer config variable in the config file
@@ -407,6 +435,17 @@ GIT_EXTERN(int) git_config_set_multivar(git_config *cfg, const char *name, const
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.
+ *
+ * @param cfg where to look for the variables
+ * @param name the variable's name
+ * @param regexp a regular expression to indicate which values to delete
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_config_delete_multivar(git_config *cfg, const char *name, const char *regexp);
+
+/**
* Perform an operation on each config variable.
*
* The callback receives the normalized name and value of each variable
@@ -425,6 +464,29 @@ GIT_EXTERN(int) git_config_foreach(
void *payload);
/**
+ * Iterate over all the config variables
+ *
+ * Use `git_config_next` to advance the iteration and
+ * `git_config_iterator_free` when done.
+ *
+ * @param out pointer to store the iterator
+ * @param cfg where to ge the variables from
+ */
+GIT_EXTERN(int) git_config_iterator_new(git_config_iterator **out, const git_config *cfg);
+
+/**
+ * Iterate over all the config variables whose name matches a pattern
+ *
+ * Use `git_config_next` to advance the iteration and
+ * `git_config_iterator_free` when done.
+ *
+ * @param out pointer to store the iterator
+ * @param cfg where to ge the variables from
+ * @param regexp regular expression to match the names
+ */
+GIT_EXTERN(int) git_config_iterator_glob_new(git_config_iterator **out, const git_config *cfg, const char *regexp);
+
+/**
* Perform an operation on each config variable matching a regular expression.
*
* This behaviors like `git_config_foreach` with an additional filter of a
@@ -535,6 +597,25 @@ GIT_EXTERN(int) git_config_parse_int32(int32_t *out, const char *value);
GIT_EXTERN(int) git_config_parse_int64(int64_t *out, const char *value);
+/**
+ * Perform an operation on each config variable in given config backend
+ * matching a regular expression.
+ *
+ * This behaviors like `git_config_foreach_match` except instead of all config
+ * entries it just enumerates through the given backend entry.
+ *
+ * @param backend where to get the variables from
+ * @param regexp regular expression to match against config names (can be NULL)
+ * @param callback the function to call on each variable
+ * @param payload the data to pass to the callback
+ */
+GIT_EXTERN(int) git_config_backend_foreach_match(
+ git_config_backend *backend,
+ const char *regexp,
+ int (*fn)(const git_config_entry *, void *),
+ void *data);
+
+
/** @} */
GIT_END_DECL
#endif
diff --git a/include/git2/cred_helpers.h b/include/git2/cred_helpers.h
index 5d93cf4dd..1d8809211 100644
--- a/include/git2/cred_helpers.h
+++ b/include/git2/cred_helpers.h
@@ -7,7 +7,7 @@
#ifndef INCLUDE_git_cred_helpers_h__
#define INCLUDE_git_cred_helpers_h__
-#include "git2/transport.h"
+#include "transport.h"
/**
* @file git2/cred_helpers.h
diff --git a/include/git2/diff.h b/include/git2/diff.h
index 43029c49c..61d288380 100644
--- a/include/git2/diff.h
+++ b/include/git2/diff.h
@@ -20,34 +20,38 @@
* Overview
* --------
*
- * Calculating diffs is generally done in two phases: building a diff list
- * then traversing the diff list. This makes is easier to share logic
- * across the various types of diffs (tree vs tree, workdir vs index, etc.),
- * and also allows you to insert optional diff list post-processing phases,
- * such as rename detected, in between the steps. When you are done with a
- * diff list object, it must be freed.
+ * Calculating diffs is generally done in two phases: building a list of
+ * diffs then traversing it. This makes is easier to share logic across
+ * the various types of diffs (tree vs tree, workdir vs index, etc.), and
+ * also allows you to insert optional diff post-processing phases,
+ * such as rename detection, in between the steps. When you are done with
+ * a diff object, it must be freed.
*
* Terminology
* -----------
*
* To understand the diff APIs, you should know the following terms:
*
- * - A `diff` or `diff list` represents the cumulative list of differences
- * between two snapshots of a repository (possibly filtered by a set of
- * file name patterns). This is the `git_diff_list` object.
+ * - A `diff` represents the cumulative list of differences between two
+ * snapshots of a repository (possibly filtered by a set of file name
+ * patterns). This is the `git_diff` object.
+ *
* - A `delta` is a file pair with an old and new revision. The old version
* may be absent if the file was just created and the new version may be
* absent if the file was deleted. A diff is mostly just a list of deltas.
+ *
* - A `binary` file / delta is a file (or pair) for which no text diffs
- * should be generated. A diff list can contain delta entries that are
+ * should be generated. A diff can contain delta entries that are
* binary, but no diff content will be output for those files. There is
* a base heuristic for binary detection and you can further tune the
* behavior with git attributes or diff flags and option settings.
+ *
* - A `hunk` is a span of modified lines in a delta along with some stable
* surrounding context. You can configure the amount of context and other
* properties of how hunks are generated. Each hunk also comes with a
* header that described where it starts and ends in both the old and new
* versions in the delta.
+ *
* - A `line` is a range of characters inside a hunk. It could be a context
* line (i.e. in both old and new versions), an added line (i.e. only in
* the new version), or a removed line (i.e. only in the old version).
@@ -68,100 +72,125 @@ GIT_BEGIN_DECL
typedef enum {
/** Normal diff, the default */
GIT_DIFF_NORMAL = 0,
- /** Reverse the sides of the diff */
- GIT_DIFF_REVERSE = (1 << 0),
- /** Treat all files as text, disabling binary attributes & detection */
- GIT_DIFF_FORCE_TEXT = (1 << 1),
- /** Ignore all whitespace */
- GIT_DIFF_IGNORE_WHITESPACE = (1 << 2),
- /** Ignore changes in amount of whitespace */
- GIT_DIFF_IGNORE_WHITESPACE_CHANGE = (1 << 3),
- /** Ignore whitespace at end of line */
- GIT_DIFF_IGNORE_WHITESPACE_EOL = (1 << 4),
- /** Exclude submodules from the diff completely */
- GIT_DIFF_IGNORE_SUBMODULES = (1 << 5),
- /** Use the "patience diff" algorithm (currently unimplemented) */
- GIT_DIFF_PATIENCE = (1 << 6),
- /** Include ignored files in the diff list */
- GIT_DIFF_INCLUDE_IGNORED = (1 << 7),
- /** Include untracked files in the diff list */
- GIT_DIFF_INCLUDE_UNTRACKED = (1 << 8),
- /** Include unmodified files in the diff list */
- GIT_DIFF_INCLUDE_UNMODIFIED = (1 << 9),
- /** Even with GIT_DIFF_INCLUDE_UNTRACKED, an entire untracked
- * directory will be marked with only a single entry in the diff list
- * (a la what core Git does in `git status`); this flag adds *all*
- * files under untracked directories as UNTRACKED entries, too.
+ /*
+ * Options controlling which files will be in the diff
*/
- GIT_DIFF_RECURSE_UNTRACKED_DIRS = (1 << 10),
- /** If the pathspec is set in the diff options, this flags means to
- * apply it as an exact match instead of as an fnmatch pattern.
- */
- GIT_DIFF_DISABLE_PATHSPEC_MATCH = (1 << 11),
+ /** Reverse the sides of the diff */
+ GIT_DIFF_REVERSE = (1u << 0),
- /** Use case insensitive filename comparisons */
- GIT_DIFF_DELTAS_ARE_ICASE = (1 << 12),
+ /** Include ignored files in the diff */
+ GIT_DIFF_INCLUDE_IGNORED = (1u << 1),
- /** When generating patch text, include the content of untracked
- * files. This automatically turns on GIT_DIFF_INCLUDE_UNTRACKED but
- * it does not turn on GIT_DIFF_RECURSE_UNTRACKED_DIRS. Add that
- * flag if you want the content of every single UNTRACKED file.
+ /** Even with GIT_DIFF_INCLUDE_IGNORED, an entire ignored directory
+ * will be marked with only a single entry in the diff; this flag
+ * adds all files under the directory as IGNORED entries, too.
*/
- GIT_DIFF_INCLUDE_UNTRACKED_CONTENT = (1 << 13),
+ GIT_DIFF_RECURSE_IGNORED_DIRS = (1u << 2),
- /** Disable updating of the `binary` flag in delta records. This is
- * useful when iterating over a diff if you don't need hunk and data
- * callbacks and want to avoid having to load file completely.
+ /** Include untracked files in the diff */
+ GIT_DIFF_INCLUDE_UNTRACKED = (1u << 3),
+
+ /** Even with GIT_DIFF_INCLUDE_UNTRACKED, an entire untracked
+ * directory will be marked with only a single entry in the diff
+ * (a la what core Git does in `git status`); this flag adds *all*
+ * files under untracked directories as UNTRACKED entries, too.
*/
- GIT_DIFF_SKIP_BINARY_CHECK = (1 << 14),
+ GIT_DIFF_RECURSE_UNTRACKED_DIRS = (1u << 4),
+
+ /** Include unmodified files in the diff */
+ GIT_DIFF_INCLUDE_UNMODIFIED = (1u << 5),
/** Normally, a type change between files will be converted into a
* DELETED record for the old and an ADDED record for the new; this
* options enabled the generation of TYPECHANGE delta records.
*/
- GIT_DIFF_INCLUDE_TYPECHANGE = (1 << 15),
+ GIT_DIFF_INCLUDE_TYPECHANGE = (1u << 6),
/** Even with GIT_DIFF_INCLUDE_TYPECHANGE, blob->tree changes still
* generally show as a DELETED blob. This flag tries to correctly
* label blob->tree transitions as TYPECHANGE records with new_file's
* mode set to tree. Note: the tree SHA will not be available.
*/
- GIT_DIFF_INCLUDE_TYPECHANGE_TREES = (1 << 16),
+ GIT_DIFF_INCLUDE_TYPECHANGE_TREES = (1u << 7),
/** Ignore file mode changes */
- GIT_DIFF_IGNORE_FILEMODE = (1 << 17),
+ GIT_DIFF_IGNORE_FILEMODE = (1u << 8),
- /** Even with GIT_DIFF_INCLUDE_IGNORED, an entire ignored directory
- * will be marked with only a single entry in the diff list; this flag
- * adds all files under the directory as IGNORED entries, too.
+ /** Treat all submodules as unmodified */
+ GIT_DIFF_IGNORE_SUBMODULES = (1u << 9),
+
+ /** Use case insensitive filename comparisons */
+ GIT_DIFF_IGNORE_CASE = (1u << 10),
+
+ /** If the pathspec is set in the diff options, this flags means to
+ * apply it as an exact match instead of as an fnmatch pattern.
*/
- GIT_DIFF_RECURSE_IGNORED_DIRS = (1 << 18),
-
- /** Core Git scans inside untracked directories, labeling them IGNORED
- * if they are empty or only contain ignored files; a directory is
- * consider UNTRACKED only if it has an actual untracked file in it.
- * This scan is extra work for a case you often don't care about. This
- * flag makes libgit2 immediately label an untracked directory as
- * UNTRACKED without looking inside it (which differs from core Git).
- * Of course, ignore rules are still checked for the directory itself.
+ GIT_DIFF_DISABLE_PATHSPEC_MATCH = (1u << 12),
+
+ /** Disable updating of the `binary` flag in delta records. This is
+ * useful when iterating over a diff if you don't need hunk and data
+ * callbacks and want to avoid having to load file completely.
+ */
+ GIT_DIFF_SKIP_BINARY_CHECK = (1u << 13),
+
+ /** When diff finds an untracked directory, to match the behavior of
+ * core Git, it scans the contents for IGNORED and UNTRACKED files.
+ * If *all* contents are IGNORED, then the directory is IGNORED; if
+ * any contents are not IGNORED, then the directory is UNTRACKED.
+ * This is extra work that may not matter in many cases. This flag
+ * turns off that scan and immediately labels an untracked directory
+ * as UNTRACKED (changing the behavior to not match core Git).
+ */
+ GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS = (1u << 14),
+
+ /*
+ * Options controlling how output will be generated
*/
- GIT_DIFF_FAST_UNTRACKED_DIRS = (1 << 19),
+ /** Treat all files as text, disabling binary attributes & detection */
+ GIT_DIFF_FORCE_TEXT = (1u << 20),
/** Treat all files as binary, disabling text diffs */
- GIT_DIFF_FORCE_BINARY = (1 << 20),
+ GIT_DIFF_FORCE_BINARY = (1u << 21),
+
+ /** Ignore all whitespace */
+ GIT_DIFF_IGNORE_WHITESPACE = (1u << 22),
+ /** Ignore changes in amount of whitespace */
+ GIT_DIFF_IGNORE_WHITESPACE_CHANGE = (1u << 23),
+ /** Ignore whitespace at end of line */
+ GIT_DIFF_IGNORE_WHITESPACE_EOL = (1u << 24),
+
+ /** When generating patch text, include the content of untracked
+ * files. This automatically turns on GIT_DIFF_INCLUDE_UNTRACKED but
+ * it does not turn on GIT_DIFF_RECURSE_UNTRACKED_DIRS. Add that
+ * flag if you want the content of every single UNTRACKED file.
+ */
+ GIT_DIFF_SHOW_UNTRACKED_CONTENT = (1u << 25),
+
+ /** When generating output, include the names of unmodified files if
+ * they are included in the git_diff. Normally these are skipped in
+ * the formats that list files (e.g. name-only, name-status, raw).
+ * Even with this, these will not be included in patch format.
+ */
+ GIT_DIFF_SHOW_UNMODIFIED = (1u << 26),
+
+ /** Use the "patience diff" algorithm */
+ GIT_DIFF_PATIENCE = (1u << 28),
+ /** Take extra time to find minimal diff */
+ GIT_DIFF_MINIMAL = (1 << 29),
+
} git_diff_option_t;
/**
- * The diff list object that contains all individual file deltas.
+ * The diff object that contains all individual file deltas.
*
* This is an opaque structure which will be allocated by one of the diff
* generator functions below (such as `git_diff_tree_to_tree`). You are
* responsible for releasing the object memory when done, using the
- * `git_diff_list_free()` function.
+ * `git_diff_free()` function.
*/
-typedef struct git_diff_list git_diff_list;
+typedef struct git_diff git_diff;
/**
* Flags for the delta object and the file objects on each side.
@@ -172,16 +201,16 @@ typedef struct git_diff_list git_diff_list;
* considered reserved for internal or future use.
*/
typedef enum {
- GIT_DIFF_FLAG_BINARY = (1 << 0), /** file(s) treated as binary data */
- GIT_DIFF_FLAG_NOT_BINARY = (1 << 1), /** file(s) treated as text data */
- GIT_DIFF_FLAG_VALID_OID = (1 << 2), /** `oid` value is known correct */
+ GIT_DIFF_FLAG_BINARY = (1u << 0), /** file(s) treated as binary data */
+ GIT_DIFF_FLAG_NOT_BINARY = (1u << 1), /** file(s) treated as text data */
+ GIT_DIFF_FLAG_VALID_OID = (1u << 2), /** `oid` value is known correct */
} git_diff_flag_t;
/**
* What type of change is described by a git_diff_delta?
*
* `GIT_DELTA_RENAMED` and `GIT_DELTA_COPIED` will only show up if you run
- * `git_diff_find_similar()` on the diff list object.
+ * `git_diff_find_similar()` on the diff object.
*
* `GIT_DELTA_TYPECHANGE` only shows up given `GIT_DIFF_INCLUDE_TYPECHANGE`
* in the option flags (otherwise type changes will be split into ADDED /
@@ -200,11 +229,11 @@ typedef enum {
} git_delta_t;
/**
- * Description of one side of a diff entry.
+ * Description of one side of a delta.
*
- * Although this is called a "file", it may actually represent a file, a
- * symbolic link, a submodule commit id, or even a tree (although that only
- * if you are tracking type changes or ignored/untracked directories).
+ * Although this is called a "file", it could represent a file, a symbolic
+ * link, a submodule commit id, or even a tree (although that only if you
+ * are tracking type changes or ignored/untracked directories).
*
* The `oid` is the `git_oid` of the item. If the entry represents an
* absent side of a diff (e.g. the `old_file` of a `GIT_DELTA_ADDED` delta),
@@ -231,9 +260,8 @@ typedef struct {
/**
* Description of changes to one entry.
*
- * When iterating over a diff list object, this will be passed to most
- * callback functions and you can use the contents to understand exactly
- * what has changed.
+ * When iterating over a diff, this will be passed to most callbacks and
+ * you can use the contents to understand exactly what has changed.
*
* The `old_file` represents the "from" side of the diff and the `new_file`
* represents to "to" side of the diff. What those means depend on the
@@ -266,28 +294,29 @@ typedef struct {
* the score (a la `printf("M%03d", 100 - delta->similarity)`).
*/
typedef struct {
+ git_delta_t status;
+ uint32_t flags; /**< git_diff_flag_t values */
+ uint16_t similarity; /**< for RENAMED and COPIED, value 0-100 */
+ uint16_t nfiles; /**< number of files in this delta */
git_diff_file old_file;
git_diff_file new_file;
- git_delta_t status;
- uint32_t similarity; /**< for RENAMED and COPIED, value 0-100 */
- uint32_t flags;
} git_diff_delta;
/**
* Diff notification callback function.
*
* The callback will be called for each file, just before the `git_delta_t`
- * gets inserted into the diff list.
+ * gets inserted into the diff.
*
* When the callback:
* - returns < 0, the diff process will be aborted.
- * - returns > 0, the delta will not be inserted into the diff list, but the
+ * - returns > 0, the delta will not be inserted into the diff, but the
* diff process continues.
- * - returns 0, the delta is inserted into the diff list, and the diff process
+ * - returns 0, the delta is inserted into the diff, and the diff process
* continues.
*/
typedef int (*git_diff_notify_cb)(
- const git_diff_list *diff_so_far,
+ const git_diff *diff_so_far,
const git_diff_delta *delta_to_add,
const char *matched_pathspec,
void *payload);
@@ -314,28 +343,44 @@ typedef int (*git_diff_notify_cb)(
* - `notify_cb` is an optional callback function, notifying the consumer of
* which files are being examined as the diff is generated
* - `notify_payload` is the payload data to pass to the `notify_cb` function
+ * - `ignore_submodules` overrides the submodule ignore setting for all
+ * submodules in the diff.
*/
typedef struct {
unsigned int version; /**< version for the struct */
uint32_t flags; /**< defaults to GIT_DIFF_NORMAL */
- uint16_t context_lines; /**< defaults to 3 */
- uint16_t interhunk_lines; /**< defaults to 0 */
- const char *old_prefix; /**< defaults to "a" */
- const char *new_prefix; /**< defaults to "b" */
- git_strarray pathspec; /**< defaults to include all paths */
- git_off_t max_size; /**< defaults to 512MB */
+
+ /* options controlling which files are in the diff */
+
+ git_submodule_ignore_t ignore_submodules; /**< submodule ignore rule */
+ git_strarray pathspec; /**< defaults to include all paths */
git_diff_notify_cb notify_cb;
- void *notify_payload;
+ void *notify_payload;
+
+ /* options controlling how to diff text is generated */
+
+ uint16_t context_lines; /**< defaults to 3 */
+ uint16_t interhunk_lines; /**< defaults to 0 */
+ uint16_t oid_abbrev; /**< default 'core.abbrev' or 7 if unset */
+ git_off_t max_size; /**< defaults to 512MB */
+ const char *old_prefix; /**< defaults to "a" */
+ const char *new_prefix; /**< defaults to "b" */
} git_diff_options;
+/* The current version of the diff options structure */
#define GIT_DIFF_OPTIONS_VERSION 1
-#define GIT_DIFF_OPTIONS_INIT {GIT_DIFF_OPTIONS_VERSION, GIT_DIFF_NORMAL, 3}
+
+/* Stack initializer for diff options. Alternatively use
+ * `git_diff_options_init` programmatic initialization.
+ */
+#define GIT_DIFF_OPTIONS_INIT \
+ {GIT_DIFF_OPTIONS_VERSION, 0, GIT_SUBMODULE_IGNORE_DEFAULT, {NULL,0}, NULL, NULL, 3}
/**
* When iterating over a diff, callback that will be made per file.
*
* @param delta A pointer to the delta data for the file
- * @param progress Goes from 0 to 1 over the diff list
+ * @param progress Goes from 0 to 1 over the diff
* @param payload User-specified pointer from foreach function
*/
typedef int (*git_diff_file_cb)(
@@ -346,34 +391,35 @@ typedef int (*git_diff_file_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 */
-} git_diff_range;
+typedef struct git_diff_hunk git_diff_hunk;
+struct git_diff_hunk {
+ 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[128]; /** Header text, NUL-byte terminated */
+};
/**
* When iterating over a diff, callback that will be made per hunk.
*/
typedef int (*git_diff_hunk_cb)(
const git_diff_delta *delta,
- const git_diff_range *range,
- const char *header,
- size_t header_len,
+ const git_diff_hunk *hunk,
void *payload);
/**
* Line origin constants.
*
* These values describe where a line came from and will be passed to
- * the git_diff_data_cb when iterating over a diff. There are some
+ * the git_diff_line_cb when iterating over a diff. There are some
* special origin constants at the end that are used for the text
* output callbacks to demarcate lines that are actually part of
* the file or hunk headers.
*/
typedef enum {
- /* These values will be sent to `git_diff_data_cb` along with the line */
+ /* These values will be sent to `git_diff_line_cb` along with the line */
GIT_DIFF_LINE_CONTEXT = ' ',
GIT_DIFF_LINE_ADDITION = '+',
GIT_DIFF_LINE_DELETION = '-',
@@ -382,16 +428,29 @@ typedef enum {
GIT_DIFF_LINE_ADD_EOFNL = '>', /**< Old has no LF at end, new does */
GIT_DIFF_LINE_DEL_EOFNL = '<', /**< Old has LF at end, new does not */
- /* The following values will only be sent to a `git_diff_data_cb` when
- * the content of a diff is being formatted (eg. through
- * git_diff_print_patch() or git_diff_print_compact(), for instance).
+ /* The following values will only be sent to a `git_diff_line_cb` when
+ * the content of a diff is being formatted through `git_diff_print`.
*/
GIT_DIFF_LINE_FILE_HDR = 'F',
GIT_DIFF_LINE_HUNK_HDR = 'H',
- GIT_DIFF_LINE_BINARY = 'B'
+ GIT_DIFF_LINE_BINARY = 'B' /**< For "Binary files x and y differ" */
} git_diff_line_t;
/**
+ * Structure describing a line (or data span) of a diff.
+ */
+typedef struct git_diff_line git_diff_line;
+struct git_diff_line {
+ char origin; /** A git_diff_line_t value */
+ int old_lineno; /** Line number in old file or -1 for added line */
+ int new_lineno; /** Line number in new file or -1 for deleted line */
+ int num_lines; /** Number of newline characters in content */
+ size_t content_len; /** Number of bytes of data */
+ git_off_t content_offset; /** Offset in the original file to the content */
+ const char *content; /** Pointer to diff text, not NUL-byte terminated */
+};
+
+/**
* When iterating over a diff, callback that will be made per text diff
* line. In this context, the provided range will be NULL.
*
@@ -399,46 +458,36 @@ typedef enum {
* of text. This uses some extra GIT_DIFF_LINE_... constants for output
* of lines of file and hunk headers.
*/
-typedef int (*git_diff_data_cb)(
+typedef int (*git_diff_line_cb)(
const git_diff_delta *delta, /** delta that contains this data */
- const git_diff_range *range, /** range of lines containing this data */
- char line_origin, /** git_diff_list_t value from above */
- const char *content, /** diff data - not NUL terminated */
- size_t content_len, /** number of bytes of diff data */
+ const git_diff_hunk *hunk, /** hunk containing this data */
+ const git_diff_line *line, /** line data */
void *payload); /** user reference data */
/**
- * The diff patch is used to store all the text diffs for a delta.
- *
- * You can easily loop over the content of patches and get information about
- * them.
- */
-typedef struct git_diff_patch git_diff_patch;
-
-/**
* Flags to control the behavior of diff rename/copy detection.
*/
typedef enum {
/** look for renames? (`--find-renames`) */
- GIT_DIFF_FIND_RENAMES = (1 << 0),
+ GIT_DIFF_FIND_RENAMES = (1u << 0),
/** consider old side of modified for renames? (`--break-rewrites=N`) */
- GIT_DIFF_FIND_RENAMES_FROM_REWRITES = (1 << 1),
+ GIT_DIFF_FIND_RENAMES_FROM_REWRITES = (1u << 1),
/** look for copies? (a la `--find-copies`) */
- GIT_DIFF_FIND_COPIES = (1 << 2),
+ GIT_DIFF_FIND_COPIES = (1u << 2),
/** consider unmodified as copy sources? (`--find-copies-harder`) */
- GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED = (1 << 3),
+ GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED = (1u << 3),
/** mark large rewrites for split (`--break-rewrites=/M`) */
- GIT_DIFF_FIND_REWRITES = (1 << 4),
+ GIT_DIFF_FIND_REWRITES = (1u << 4),
/** actually split large rewrites into delete/add pairs */
- GIT_DIFF_BREAK_REWRITES = (1 << 5),
+ GIT_DIFF_BREAK_REWRITES = (1u << 5),
/** mark rewrites for split and break into delete/add pairs */
GIT_DIFF_FIND_AND_BREAK_REWRITES =
(GIT_DIFF_FIND_REWRITES | GIT_DIFF_BREAK_REWRITES),
/** find renames/copies for untracked items in working directory */
- GIT_DIFF_FIND_FOR_UNTRACKED = (1 << 6),
+ GIT_DIFF_FIND_FOR_UNTRACKED = (1u << 6),
/** turn on all finding features */
GIT_DIFF_FIND_ALL = (0x0ff),
@@ -446,11 +495,14 @@ typedef enum {
/** measure similarity ignoring leading whitespace (default) */
GIT_DIFF_FIND_IGNORE_LEADING_WHITESPACE = 0,
/** measure similarity ignoring all whitespace */
- GIT_DIFF_FIND_IGNORE_WHITESPACE = (1 << 12),
+ GIT_DIFF_FIND_IGNORE_WHITESPACE = (1u << 12),
/** measure similarity including all data */
- GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE = (1 << 13),
+ GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE = (1u << 13),
/** measure similarity only by comparing SHAs (fast and cheap) */
- GIT_DIFF_FIND_EXACT_MATCH_ONLY = (1 << 14),
+ GIT_DIFF_FIND_EXACT_MATCH_ONLY = (1u << 14),
+
+ /** do not break rewrites unless they contribute to a rename */
+ GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY = (1u << 15),
} git_diff_find_t;
/**
@@ -515,22 +567,22 @@ typedef struct {
#define GIT_DIFF_FIND_OPTIONS_VERSION 1
#define GIT_DIFF_FIND_OPTIONS_INIT {GIT_DIFF_FIND_OPTIONS_VERSION}
-/** @name Diff List Generator Functions
+/** @name Diff Generator Functions
*
* These are the functions you would use to create (or destroy) a
- * git_diff_list from various objects in a repository.
+ * git_diff from various objects in a repository.
*/
/**@{*/
/**
- * Deallocate a diff list.
+ * Deallocate a diff.
*
- * @param diff The previously created diff list; cannot be used after free.
+ * @param diff The previously created diff; cannot be used after free.
*/
-GIT_EXTERN(void) git_diff_list_free(git_diff_list *diff);
+GIT_EXTERN(void) git_diff_free(git_diff *diff);
/**
- * Create a diff list with the difference between two tree objects.
+ * Create a diff with the difference between two tree objects.
*
* This is equivalent to `git diff <old-tree> <new-tree>`
*
@@ -539,21 +591,21 @@ GIT_EXTERN(void) git_diff_list_free(git_diff_list *diff);
* pass NULL to indicate an empty tree, although it is an error to pass
* NULL for both the `old_tree` and `new_tree`.
*
- * @param diff Output pointer to a git_diff_list pointer to be allocated.
+ * @param diff Output pointer to a git_diff pointer to be allocated.
* @param repo The repository containing the trees.
* @param old_tree A git_tree object to diff from, or NULL for empty tree.
* @param new_tree A git_tree object to diff to, or NULL for empty tree.
* @param opts Structure with options to influence diff or NULL for defaults.
*/
GIT_EXTERN(int) git_diff_tree_to_tree(
- git_diff_list **diff,
+ git_diff **diff,
git_repository *repo,
git_tree *old_tree,
git_tree *new_tree,
const git_diff_options *opts); /**< can be NULL for defaults */
/**
- * Create a diff list between a tree and repository index.
+ * Create a diff between a tree and repository index.
*
* This is equivalent to `git diff --cached <treeish>` or if you pass
* the HEAD tree, then like `git diff --cached`.
@@ -561,21 +613,25 @@ GIT_EXTERN(int) git_diff_tree_to_tree(
* The tree you pass will be used for the "old_file" side of the delta, and
* the index will be used for the "new_file" side of the delta.
*
- * @param diff Output pointer to a git_diff_list pointer to be allocated.
+ * If you pass NULL for the index, then the existing index of the `repo`
+ * will be used. In this case, the index will be refreshed from disk
+ * (if it has changed) before the diff is generated.
+ *
+ * @param diff Output pointer to a git_diff pointer to be allocated.
* @param repo The repository containing the tree and index.
* @param old_tree A git_tree object to diff from, or NULL for empty tree.
* @param index The index to diff with; repo index used if NULL.
* @param opts Structure with options to influence diff or NULL for defaults.
*/
GIT_EXTERN(int) git_diff_tree_to_index(
- git_diff_list **diff,
+ git_diff **diff,
git_repository *repo,
git_tree *old_tree,
git_index *index,
const git_diff_options *opts); /**< can be NULL for defaults */
/**
- * Create a diff list between the repository index and the workdir directory.
+ * Create a diff between the repository index and the workdir directory.
*
* This matches the `git diff` command. See the note below on
* `git_diff_tree_to_workdir` for a discussion of the difference between
@@ -585,19 +641,23 @@ GIT_EXTERN(int) git_diff_tree_to_index(
* The index will be used for the "old_file" side of the delta, and the
* working directory will be used for the "new_file" side of the delta.
*
- * @param diff Output pointer to a git_diff_list pointer to be allocated.
+ * If you pass NULL for the index, then the existing index of the `repo`
+ * will be used. In this case, the index will be refreshed from disk
+ * (if it has changed) before the diff is generated.
+ *
+ * @param diff Output pointer to a git_diff pointer to be allocated.
* @param repo The repository.
* @param index The index to diff from; repo index used if NULL.
* @param opts Structure with options to influence diff or NULL for defaults.
*/
GIT_EXTERN(int) git_diff_index_to_workdir(
- git_diff_list **diff,
+ git_diff **diff,
git_repository *repo,
git_index *index,
const git_diff_options *opts); /**< can be NULL for defaults */
/**
- * Create a diff list between a tree and the working directory.
+ * Create a diff between a tree and the working directory.
*
* The tree you provide will be used for the "old_file" side of the delta,
* and the working directory will be used for the "new_file" side.
@@ -611,31 +671,51 @@ GIT_EXTERN(int) git_diff_index_to_workdir(
* files in the index. It may come as a surprise, but there is no direct
* equivalent in core git.
*
- * To emulate `git diff <treeish>`, call both `git_diff_tree_to_index` and
- * `git_diff_index_to_workdir`, then call `git_diff_merge` on the results.
- * That will yield a `git_diff_list` that matches the git output.
+ * To emulate `git diff <tree>`, use `git_diff_tree_to_workdir_with_index`
+ * (or `git_diff_tree_to_index` and `git_diff_index_to_workdir`, then call
+ * `git_diff_merge` on the results). That will yield a `git_diff` that
+ * matches the git output.
*
* If this seems confusing, take the case of a file with a staged deletion
* where the file has then been put back into the working dir and modified.
* The tree-to-workdir diff for that file is 'modified', but core git would
* show status 'deleted' since there is a pending deletion in the index.
*
- * @param diff A pointer to a git_diff_list pointer that will be allocated.
+ * @param diff A pointer to a git_diff pointer that will be allocated.
* @param repo The repository containing the tree.
* @param old_tree A git_tree object to diff from, or NULL for empty tree.
* @param opts Structure with options to influence diff or NULL for defaults.
*/
GIT_EXTERN(int) git_diff_tree_to_workdir(
- git_diff_list **diff,
+ git_diff **diff,
+ git_repository *repo,
+ git_tree *old_tree,
+ const git_diff_options *opts); /**< can be NULL for defaults */
+
+/**
+ * Create a diff between a tree and the working directory using index data
+ * to account for staged deletes, tracked files, etc.
+ *
+ * This emulates `git diff <tree>` by diffing the tree to the index and
+ * the index to the working directory and blending the results into a
+ * single diff that includes staged deleted, etc.
+ *
+ * @param diff A pointer to a git_diff pointer that will be allocated.
+ * @param repo The repository containing the tree.
+ * @param old_tree A git_tree object to diff from, or NULL for empty tree.
+ * @param opts Structure with options to influence diff or NULL for defaults.
+ */
+GIT_EXTERN(int) git_diff_tree_to_workdir_with_index(
+ git_diff **diff,
git_repository *repo,
git_tree *old_tree,
const git_diff_options *opts); /**< can be NULL for defaults */
/**
- * Merge one diff list into another.
+ * Merge one diff into another.
*
* This merges items from the "from" list into the "onto" list. The
- * resulting diff list will have all items that appear in either list.
+ * resulting diff will have all items that appear in either list.
* If an item appears in both lists, then it will be "merged" to appear
* as if the old version was from the "onto" list and the new version
* is from the "from" list (with the exception that if the item has a
@@ -645,320 +725,176 @@ GIT_EXTERN(int) git_diff_tree_to_workdir(
* @param from Diff to merge.
*/
GIT_EXTERN(int) git_diff_merge(
- git_diff_list *onto,
- const git_diff_list *from);
+ git_diff *onto,
+ const git_diff *from);
/**
- * Transform a diff list marking file renames, copies, etc.
+ * Transform a diff marking file renames, copies, etc.
*
- * This modifies a diff list in place, replacing old entries that look
+ * This modifies a diff in place, replacing old entries that look
* like renames or copies with new entries reflecting those changes.
* This also will, if requested, break modified files into add/remove
* pairs if the amount of change is above a threshold.
*
- * @param diff Diff list to run detection algorithms on
+ * @param diff diff to run detection algorithms on
* @param options Control how detection should be run, NULL for defaults
* @return 0 on success, -1 on failure
*/
GIT_EXTERN(int) git_diff_find_similar(
- git_diff_list *diff,
- git_diff_find_options *options);
-
-/**@}*/
-
-
-/** @name Diff List Processor Functions
- *
- * These are the functions you apply to a diff list to process it
- * or read it in some way.
- */
-/**@{*/
+ git_diff *diff,
+ const git_diff_find_options *options);
/**
- * Loop over all deltas in a diff list issuing callbacks.
+ * Initialize diff options structure
*
- * This will iterate through all of the files described in a diff. You
- * should provide a file callback to learn about each file.
+ * In most cases, you can probably just use `GIT_DIFF_OPTIONS_INIT` to
+ * initialize the diff options structure, but in some cases that is not
+ * going to work. You can call this function instead. Note that you
+ * must pass both a pointer to the structure to be initialized and the
+ * `GIT_DIFF_OPTIONS_VERSION` value from the header you compiled with.
*
- * The "hunk" and "line" callbacks are optional, and the text diff of the
- * files will only be calculated if they are not NULL. Of course, these
- * callbacks will not be invoked for binary files on the diff list or for
- * files whose only changed is a file mode change.
- *
- * Returning a non-zero value from any of the callbacks will terminate
- * the iteration and cause this return `GIT_EUSER`.
- *
- * @param diff A git_diff_list generated by one of the above functions.
- * @param file_cb Callback function to make per file in the diff.
- * @param hunk_cb Optional callback to make per hunk of text diff. This
- * callback is called to describe a range of lines in the
- * diff. It will not be issued for binary files.
- * @param line_cb Optional callback to make per line of diff text. This
- * same callback will be made for context lines, added, and
- * removed lines, and even for a deleted trailing newline.
- * @param payload Reference pointer that will be passed to your callbacks.
- * @return 0 on success, GIT_EUSER on non-zero callback, or error code
+ * @param options Pointer to git_diff_options memory to be initialized
+ * @param version Should be `GIT_DIFF_OPTIONS_VERSION`
+ * @return 0 on success, negative on failure (such as unsupported version)
*/
-GIT_EXTERN(int) git_diff_foreach(
- git_diff_list *diff,
- git_diff_file_cb file_cb,
- git_diff_hunk_cb hunk_cb,
- git_diff_data_cb line_cb,
- void *payload);
+GIT_EXTERN(int) git_diff_options_init(
+ git_diff_options *options,
+ unsigned int version);
-/**
- * Iterate over a diff generating text output like "git diff --name-status".
- *
- * Returning a non-zero value from the callbacks will terminate the
- * iteration and cause this return `GIT_EUSER`.
- *
- * @param diff A git_diff_list generated by one of the above functions.
- * @param print_cb Callback to make per line of diff text.
- * @param payload Reference pointer that will be passed to your callback.
- * @return 0 on success, GIT_EUSER on non-zero callback, or error code
- */
-GIT_EXTERN(int) git_diff_print_compact(
- git_diff_list *diff,
- git_diff_data_cb print_cb,
- void *payload);
+/**@}*/
-/**
- * Iterate over a diff generating text output like "git diff --raw".
- *
- * Returning a non-zero value from the callbacks will terminate the
- * iteration and cause this return `GIT_EUSER`.
- *
- * @param diff A git_diff_list generated by one of the above functions.
- * @param print_cb Callback to make per line of diff text.
- * @param payload Reference pointer that will be passed to your callback.
- * @return 0 on success, GIT_EUSER on non-zero callback, or error code
- */
-GIT_EXTERN(int) git_diff_print_raw(
- git_diff_list *diff,
- git_diff_data_cb print_cb,
- void *payload);
-/**
- * Look up the single character abbreviation for a delta status code.
- *
- * When you call `git_diff_print_compact` it prints single letter codes into
- * the output such as 'A' for added, 'D' for deleted, 'M' for modified, etc.
- * It is sometimes convenient to convert a git_delta_t value into these
- * letters for your own purposes. This function does just that. By the
- * way, unmodified will return a space (i.e. ' ').
+/** @name Diff Processor Functions
*
- * @param status The git_delta_t value to look up
- * @return The single character label for that code
- */
-GIT_EXTERN(char) git_diff_status_char(git_delta_t status);
-
-/**
- * Iterate over a diff generating text output like "git diff".
- *
- * This is a super easy way to generate a patch from a diff.
- *
- * Returning a non-zero value from the callbacks will terminate the
- * iteration and cause this return `GIT_EUSER`.
- *
- * @param diff A git_diff_list generated by one of the above functions.
- * @param payload Reference pointer that will be passed to your callbacks.
- * @param print_cb Callback function to output lines of the diff. This
- * same function will be called for file headers, hunk
- * headers, and diff lines. Fortunately, you can probably
- * use various GIT_DIFF_LINE constants to determine what
- * text you are given.
- * @return 0 on success, GIT_EUSER on non-zero callback, or error code
+ * These are the functions you apply to a diff to process it
+ * or read it in some way.
*/
-GIT_EXTERN(int) git_diff_print_patch(
- git_diff_list *diff,
- git_diff_data_cb print_cb,
- void *payload);
+/**@{*/
/**
- * Query how many diff records are there in a diff list.
+ * Query how many diff records are there in a diff.
*
- * @param diff A git_diff_list generated by one of the above functions
+ * @param diff A git_diff generated by one of the above functions
* @return Count of number of deltas in the list
*/
-GIT_EXTERN(size_t) git_diff_num_deltas(git_diff_list *diff);
+GIT_EXTERN(size_t) git_diff_num_deltas(const git_diff *diff);
/**
- * Query how many diff deltas are there in a diff list filtered by type.
+ * Query how many diff deltas are there in a diff filtered by type.
*
* This works just like `git_diff_entrycount()` with an extra parameter
* that is a `git_delta_t` and returns just the count of how many deltas
* match that particular type.
*
- * @param diff A git_diff_list generated by one of the above functions
+ * @param diff A git_diff generated by one of the above functions
* @param type A git_delta_t value to filter the count
* @return Count of number of deltas matching delta_t type
*/
GIT_EXTERN(size_t) git_diff_num_deltas_of_type(
- git_diff_list *diff,
- git_delta_t type);
+ const git_diff *diff, git_delta_t type);
/**
- * Return the diff delta and patch for an entry in the diff list.
- *
- * The `git_diff_patch` is a newly created object contains the text diffs
- * for the delta. You have to call `git_diff_patch_free()` when you are
- * done with it. You can use the patch object to loop over all the hunks
- * and lines in the diff of the one delta.
+ * Return the diff delta for an entry in the diff list.
*
- * For an unchanged file or a binary file, no `git_diff_patch` will be
- * created, the output will be set to NULL, and the `binary` flag will be
- * set true in the `git_diff_delta` structure.
- *
- * The `git_diff_delta` pointer points to internal data and you do not have
+ * The `git_delta` pointer points to internal data and you do not have
* to release it when you are done with it. It will go away when the
- * `git_diff_list` and `git_diff_patch` go away.
+ * `git_diff` (or any associated `git_patch`) goes away.
*
- * It is okay to pass NULL for either of the output parameters; if you pass
- * NULL for the `git_diff_patch`, then the text diff will not be calculated.
+ * Note that the flags on the delta related to whether it has binary
+ * content or not may not be set if there are no attributes set for the
+ * file and there has been no reason to load the file data at this point.
+ * For now, if you need those flags to be up to date, your only option is
+ * to either use `git_diff_foreach` or create a `git_patch`.
*
- * @param patch_out Output parameter for the delta patch object
- * @param delta_out Output parameter for the delta object
* @param diff Diff list object
* @param idx Index into diff list
- * @return 0 on success, other value < 0 on error
+ * @return Pointer to git_diff_delta (or NULL if `idx` out of range)
*/
-GIT_EXTERN(int) git_diff_get_patch(
- git_diff_patch **patch_out,
- const git_diff_delta **delta_out,
- git_diff_list *diff,
- size_t idx);
+GIT_EXTERN(const git_diff_delta *) git_diff_get_delta(
+ const git_diff *diff, size_t idx);
/**
- * Free a git_diff_patch object.
- */
-GIT_EXTERN(void) git_diff_patch_free(
- git_diff_patch *patch);
-
-/**
- * Get the delta associated with a patch
- */
-GIT_EXTERN(const git_diff_delta *) git_diff_patch_delta(
- git_diff_patch *patch);
-
-/**
- * Get the number of hunks in a patch
+ * Check if deltas are sorted case sensitively or insensitively.
+ *
+ * @param diff diff to check
+ * @return 0 if case sensitive, 1 if case is ignored
*/
-GIT_EXTERN(size_t) git_diff_patch_num_hunks(
- git_diff_patch *patch);
+GIT_EXTERN(int) git_diff_is_sorted_icase(const git_diff *diff);
/**
- * Get line counts of each type in a patch.
+ * Loop over all deltas in a diff issuing callbacks.
*
- * This helps imitate a diff --numstat type of output. For that purpose,
- * you only need the `total_additions` and `total_deletions` values, but we
- * include the `total_context` line count in case you want the total number
- * of lines of diff output that will be generated.
+ * This will iterate through all of the files described in a diff. You
+ * should provide a file callback to learn about each file.
*
- * All outputs are optional. Pass NULL if you don't need a particular count.
+ * The "hunk" and "line" callbacks are optional, and the text diff of the
+ * files will only be calculated if they are not NULL. Of course, these
+ * callbacks will not be invoked for binary files on the diff or for
+ * files whose only changed is a file mode change.
*
- * @param total_context Count of context lines in output, can be NULL.
- * @param total_additions Count of addition lines in output, can be NULL.
- * @param total_deletions Count of deletion lines in output, can be NULL.
- * @param patch The git_diff_patch object
- * @return 0 on success, <0 on error
- */
-GIT_EXTERN(int) git_diff_patch_line_stats(
- size_t *total_context,
- size_t *total_additions,
- size_t *total_deletions,
- const git_diff_patch *patch);
-
-/**
- * Get the information about a hunk in a patch
- *
- * Given a patch and a hunk index into the patch, this returns detailed
- * information about that hunk. Any of the output pointers can be passed
- * as NULL if you don't care about that particular piece of information.
- *
- * @param range Output pointer to git_diff_range of hunk
- * @param header Output pointer to header string for hunk. Unlike the
- * content pointer for each line, this will be NUL-terminated
- * @param header_len Output value of characters in header string
- * @param lines_in_hunk Output count of total lines in this hunk
- * @param patch Input pointer to patch object
- * @param hunk_idx Input index of hunk to get information about
- * @return 0 on success, GIT_ENOTFOUND if hunk_idx out of range, <0 on error
+ * Returning a non-zero value from any of the callbacks will terminate
+ * the iteration and cause this return `GIT_EUSER`.
+ *
+ * @param diff A git_diff generated by one of the above functions.
+ * @param file_cb Callback function to make per file in the diff.
+ * @param hunk_cb Optional callback to make per hunk of text diff. This
+ * callback is called to describe a range of lines in the
+ * diff. It will not be issued for binary files.
+ * @param line_cb Optional callback to make per line of diff text. This
+ * same callback will be made for context lines, added, and
+ * removed lines, and even for a deleted trailing newline.
+ * @param payload Reference pointer that will be passed to your callbacks.
+ * @return 0 on success, GIT_EUSER on non-zero callback, or error code
*/
-GIT_EXTERN(int) git_diff_patch_get_hunk(
- const git_diff_range **range,
- const char **header,
- size_t *header_len,
- size_t *lines_in_hunk,
- git_diff_patch *patch,
- size_t hunk_idx);
+GIT_EXTERN(int) git_diff_foreach(
+ git_diff *diff,
+ git_diff_file_cb file_cb,
+ git_diff_hunk_cb hunk_cb,
+ git_diff_line_cb line_cb,
+ void *payload);
/**
- * Get the number of lines in a hunk.
+ * Look up the single character abbreviation for a delta status code.
+ *
+ * When you run `git diff --name-status` it uses single letter codes in
+ * the output such as 'A' for added, 'D' for deleted, 'M' for modified,
+ * etc. This function converts a git_delta_t value into these letters for
+ * your own purposes. GIT_DELTA_UNTRACKED will return a space (i.e. ' ').
*
- * @param patch The git_diff_patch object
- * @param hunk_idx Index of the hunk
- * @return Number of lines in hunk or -1 if invalid hunk index
+ * @param status The git_delta_t value to look up
+ * @return The single character label for that code
*/
-GIT_EXTERN(int) git_diff_patch_num_lines_in_hunk(
- git_diff_patch *patch,
- size_t hunk_idx);
+GIT_EXTERN(char) git_diff_status_char(git_delta_t status);
/**
- * Get data about a line in a hunk of a patch.
- *
- * Given a patch, a hunk index, and a line index in the hunk, this
- * will return a lot of details about that line. If you pass a hunk
- * index larger than the number of hunks or a line index larger than
- * the number of lines in the hunk, this will return -1.
- *
- * @param line_origin A GIT_DIFF_LINE constant from above
- * @param content Pointer to content of diff line, not NUL-terminated
- * @param content_len Number of characters in content
- * @param old_lineno Line number in old file or -1 if line is added
- * @param new_lineno Line number in new file or -1 if line is deleted
- * @param patch The patch to look in
- * @param hunk_idx The index of the hunk
- * @param line_of_hunk The index of the line in the hunk
- * @return 0 on success, <0 on failure
+ * Possible output formats for diff data
*/
-GIT_EXTERN(int) git_diff_patch_get_line_in_hunk(
- char *line_origin,
- const char **content,
- size_t *content_len,
- int *old_lineno,
- int *new_lineno,
- git_diff_patch *patch,
- size_t hunk_idx,
- size_t line_of_hunk);
+typedef enum {
+ GIT_DIFF_FORMAT_PATCH = 1u, /**< full git diff */
+ GIT_DIFF_FORMAT_PATCH_HEADER = 2u, /**< just the file headers of patch */
+ GIT_DIFF_FORMAT_RAW = 3u, /**< like git diff --raw */
+ GIT_DIFF_FORMAT_NAME_ONLY = 4u, /**< like git diff --name-only */
+ GIT_DIFF_FORMAT_NAME_STATUS = 5u, /**< like git diff --name-status */
+} git_diff_format_t;
/**
- * Serialize the patch to text via callback.
+ * Iterate over a diff generating formatted text output.
*
- * Returning a non-zero value from the callback will terminate the iteration
- * and cause this return `GIT_EUSER`.
+ * Returning a non-zero value from the callbacks will terminate the
+ * iteration and cause this return `GIT_EUSER`.
*
- * @param patch A git_diff_patch representing changes to one file
- * @param print_cb Callback function to output lines of the patch. Will be
- * called for file headers, hunk headers, and diff lines.
- * @param payload Reference pointer that will be passed to your callbacks.
+ * @param diff A git_diff generated by one of the above functions.
+ * @param format A git_diff_format_t value to pick the text format.
+ * @param print_cb Callback to make per line of diff text.
+ * @param payload Reference pointer that will be passed to your callback.
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
*/
-GIT_EXTERN(int) git_diff_patch_print(
- git_diff_patch *patch,
- git_diff_data_cb print_cb,
+GIT_EXTERN(int) git_diff_print(
+ git_diff *diff,
+ git_diff_format_t format,
+ git_diff_line_cb print_cb,
void *payload);
-/**
- * Get the content of a patch as a single diff text.
- *
- * @param string Allocated string; caller must free.
- * @param patch A git_diff_patch representing changes to one file
- * @return 0 on success, <0 on failure.
- */
-GIT_EXTERN(int) git_diff_patch_to_str(
- char **string,
- git_diff_patch *patch);
-
/**@}*/
@@ -1001,34 +937,10 @@ GIT_EXTERN(int) git_diff_blobs(
const git_diff_options *options,
git_diff_file_cb file_cb,
git_diff_hunk_cb hunk_cb,
- git_diff_data_cb line_cb,
+ git_diff_line_cb line_cb,
void *payload);
/**
- * Directly generate a patch from the difference between two blobs.
- *
- * This is just like `git_diff_blobs()` except it generates a patch object
- * for the difference instead of directly making callbacks. You can use the
- * standard `git_diff_patch` accessor functions to read the patch data, and
- * you must call `git_diff_patch_free()` on the patch when done.
- *
- * @param out The generated patch; NULL on error
- * @param old_blob Blob for old side of diff, or NULL for empty blob
- * @param old_as_path Treat old blob as if it had this filename; can be NULL
- * @param new_blob Blob for new side of diff, or NULL for empty blob
- * @param new_as_path Treat new blob as if it had this filename; can be NULL
- * @param opts Options for diff, or NULL for default options
- * @return 0 on success or error code < 0
- */
-GIT_EXTERN(int) git_diff_patch_from_blobs(
- git_diff_patch **out,
- const git_blob *old_blob,
- const char *old_as_path,
- const git_blob *new_blob,
- const char *new_as_path,
- const git_diff_options *opts);
-
-/**
* Directly run a diff between a blob and a buffer.
*
* As with `git_diff_blobs`, comparing a blob and buffer lacks some context,
@@ -1048,7 +960,7 @@ GIT_EXTERN(int) git_diff_patch_from_blobs(
* @param options Options for diff, or NULL for default options
* @param file_cb Callback for "file"; made once if there is a diff; can be NULL
* @param hunk_cb Callback for each hunk in diff; can be NULL
- * @param data_cb Callback for each line in diff; can be NULL
+ * @param line_cb Callback for each line in diff; can be NULL
* @param payload Payload passed to each callback function
* @return 0 on success, GIT_EUSER on non-zero callback return, or error code
*/
@@ -1061,35 +973,9 @@ GIT_EXTERN(int) git_diff_blob_to_buffer(
const git_diff_options *options,
git_diff_file_cb file_cb,
git_diff_hunk_cb hunk_cb,
- git_diff_data_cb data_cb,
+ git_diff_line_cb line_cb,
void *payload);
-/**
- * Directly generate a patch from the difference between a blob and a buffer.
- *
- * This is just like `git_diff_blob_to_buffer()` except it generates a patch
- * object for the difference instead of directly making callbacks. You can
- * use the standard `git_diff_patch` accessor functions to read the patch
- * data, and you must call `git_diff_patch_free()` on the patch when done.
- *
- * @param out The generated patch; NULL on error
- * @param old_blob Blob for old side of diff, or NULL for empty blob
- * @param old_as_path Treat old blob as if it had this filename; can be NULL
- * @param buffer Raw data for new side of diff, or NULL for empty
- * @param buffer_len Length of raw data for new side of diff
- * @param buffer_as_path Treat buffer as if it had this filename; can be NULL
- * @param opts Options for diff, or NULL for default options
- * @return 0 on success or error code < 0
- */
-GIT_EXTERN(int) git_diff_patch_from_blob_and_buffer(
- git_diff_patch **out,
- const git_blob *old_blob,
- const char *old_as_path,
- const char *buffer,
- size_t buffer_len,
- const char *buffer_as_path,
- const git_diff_options *opts);
-
GIT_END_DECL
diff --git a/include/git2/errors.h b/include/git2/errors.h
index 2032a436a..be7a31d8e 100644
--- a/include/git2/errors.h
+++ b/include/git2/errors.h
@@ -8,6 +8,7 @@
#define INCLUDE_git_errors_h__
#include "common.h"
+#include "buffer.h"
/**
* @file git2/errors.h
@@ -27,11 +28,12 @@ typedef enum {
GIT_EBUFS = -6,
GIT_EUSER = -7,
GIT_EBAREREPO = -8,
- GIT_EORPHANEDHEAD = -9,
+ GIT_EUNBORNBRANCH = -9,
GIT_EUNMERGED = -10,
GIT_ENONFASTFORWARD = -11,
GIT_EINVALIDSPEC = -12,
GIT_EMERGECONFLICT = -13,
+ GIT_ELOCKED = -14,
GIT_PASSTHROUGH = -30,
GIT_ITEROVER = -31,
@@ -44,6 +46,7 @@ typedef struct {
/** Error classes */
typedef enum {
+ GITERR_NONE = 0,
GITERR_NOMEMORY,
GITERR_OS,
GITERR_INVALID,
@@ -66,6 +69,8 @@ typedef enum {
GITERR_CHECKOUT,
GITERR_FETCHHEAD,
GITERR_MERGE,
+ GITERR_SSH,
+ GITERR_FILTER,
} git_error_t;
/**
@@ -82,6 +87,18 @@ GIT_EXTERN(const git_error *) giterr_last(void);
GIT_EXTERN(void) giterr_clear(void);
/**
+ * Get the last error data and clear it.
+ *
+ * This copies the last error into the given `git_error` struct
+ * and returns 0 if the copy was successful, leaving the error
+ * cleared as if `giterr_clear` had been called.
+ *
+ * If there was no existing error in the library, -1 will be returned
+ * and the contents of `cpy` will be left unmodified.
+ */
+GIT_EXTERN(int) giterr_detach(git_error *cpy);
+
+/**
* Set the error message string for this thread.
*
* This function is public so that custom ODB backends and the like can
diff --git a/include/git2/filter.h b/include/git2/filter.h
new file mode 100644
index 000000000..f96b6766b
--- /dev/null
+++ b/include/git2/filter.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_filter_h__
+#define INCLUDE_git_filter_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+#include "buffer.h"
+
+/**
+ * @file git2/filter.h
+ * @brief Git filter APIs
+ *
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Filters are applied in one of two directions: smudging - which is
+ * exporting a file from the Git object database to the working directory,
+ * and cleaning - which is importing a file from the working directory to
+ * the Git object database. These values control which direction of
+ * change is being applied.
+ */
+typedef enum {
+ GIT_FILTER_TO_WORKTREE = 0,
+ GIT_FILTER_SMUDGE = GIT_FILTER_TO_WORKTREE,
+ GIT_FILTER_TO_ODB = 1,
+ GIT_FILTER_CLEAN = GIT_FILTER_TO_ODB,
+} git_filter_mode_t;
+
+/**
+ * A filter that can transform file data
+ *
+ * This represents a filter that can be used to transform or even replace
+ * file data. Libgit2 includes one built in filter and it is possible to
+ * write your own (see git2/sys/filter.h for information on that).
+ *
+ * The two builtin filters are:
+ *
+ * * "crlf" which uses the complex rules with the "text", "eol", and
+ * "crlf" file attributes to decide how to convert between LF and CRLF
+ * line endings
+ * * "ident" which replaces "$Id$" in a blob with "$Id: <blob OID>$" upon
+ * checkout and replaced "$Id: <anything>$" with "$Id$" on checkin.
+ */
+typedef struct git_filter git_filter;
+
+/**
+ * List of filters to be applied
+ *
+ * This represents a list of filters to be applied to a file / blob. You
+ * can build the list with one call, apply it with another, and dispose it
+ * with a third. In typical usage, there are not many occasions where a
+ * git_filter_list is needed directly since the library will generally
+ * handle conversions for you, but it can be convenient to be able to
+ * build and apply the list sometimes.
+ */
+typedef struct git_filter_list git_filter_list;
+
+/**
+ * Load the filter list for a given path.
+ *
+ * This will return 0 (success) but set the output git_filter_list to NULL
+ * if no filters are requested for the given file.
+ *
+ * @param filters Output newly created git_filter_list (or NULL)
+ * @param repo Repository object that contains `path`
+ * @param blob The blob to which the filter will be applied (if known)
+ * @param path Relative path of the file to be filtered
+ * @param mode Filtering direction (WT->ODB or ODB->WT)
+ * @return 0 on success (which could still return NULL if no filters are
+ * needed for the requested file), <0 on error
+ */
+GIT_EXTERN(int) git_filter_list_load(
+ git_filter_list **filters,
+ git_repository *repo,
+ git_blob *blob, /* can be NULL */
+ const char *path,
+ git_filter_mode_t mode);
+
+/**
+ * Apply filter list to a data buffer.
+ *
+ * See `git2/buffer.h` for background on `git_buf` objects.
+ *
+ * If the `in` buffer holds data allocated by libgit2 (i.e. `in->asize` is
+ * not zero), then it will be overwritten when applying the filters. If
+ * not, then it will be left untouched.
+ *
+ * If there are no filters to apply (or `filters` is NULL), then the `out`
+ * buffer will reference the `in` buffer data (with `asize` set to zero)
+ * instead of allocating data. This keeps allocations to a minimum, but
+ * it means you have to be careful about freeing the `in` data since `out`
+ * may be pointing to it!
+ *
+ * @param out Buffer to store the result of the filtering
+ * @param filters A loaded git_filter_list (or NULL)
+ * @param in Buffer containing the data to filter
+ * @return 0 on success, an error code otherwise
+ */
+GIT_EXTERN(int) git_filter_list_apply_to_data(
+ git_buf *out,
+ git_filter_list *filters,
+ git_buf *in);
+
+/**
+ * Apply filter list to the contents of a file on disk
+ */
+GIT_EXTERN(int) git_filter_list_apply_to_file(
+ git_buf *out,
+ git_filter_list *filters,
+ git_repository *repo,
+ const char *path);
+
+/**
+ * Apply filter list to the contents of a blob
+ */
+GIT_EXTERN(int) git_filter_list_apply_to_blob(
+ git_buf *out,
+ git_filter_list *filters,
+ git_blob *blob);
+
+/**
+ * Free a git_filter_list
+ *
+ * @param filters A git_filter_list created by `git_filter_list_load`
+ */
+GIT_EXTERN(void) git_filter_list_free(git_filter_list *filters);
+
+
+GIT_END_DECL
+
+/** @} */
+
+#endif
diff --git a/include/git2/index.h b/include/git2/index.h
index 51694aded..a60db370a 100644
--- a/include/git2/index.h
+++ b/include/git2/index.h
@@ -120,9 +120,9 @@ typedef struct git_index_entry {
/** Capabilities of system that affect index actions. */
typedef enum {
- GIT_INDEXCAP_IGNORE_CASE = 1,
- GIT_INDEXCAP_NO_FILEMODE = 2,
- GIT_INDEXCAP_NO_SYMLINKS = 4,
+ GIT_INDEXCAP_IGNORE_CASE = 1u,
+ GIT_INDEXCAP_NO_FILEMODE = 2u,
+ GIT_INDEXCAP_NO_SYMLINKS = 4u,
GIT_INDEXCAP_FROM_OWNER = ~0u
} git_indexcap_t;
@@ -138,6 +138,14 @@ typedef enum {
GIT_INDEX_ADD_CHECK_PATHSPEC = (1u << 2),
} git_index_add_option_t;
+/**
+ * Match any index stage.
+ *
+ * Some index APIs take a stage to match; pass this value to match
+ * any entry matching the path regardless of stage.
+ */
+#define GIT_INDEX_STAGE_ANY -1
+
/** @name Index File Functions
*
* These functions work on the index file itself.
@@ -214,13 +222,23 @@ GIT_EXTERN(unsigned int) git_index_caps(const git_index *index);
GIT_EXTERN(int) git_index_set_caps(git_index *index, unsigned int caps);
/**
- * Update the contents of an existing index object in memory
- * by reading from the hard disk.
+ * Update the contents of an existing index object in memory by reading
+ * from the hard disk.
+ *
+ * If `force` is true, this performs a "hard" read that discards in-memory
+ * changes and always reloads the on-disk index data. If there is no
+ * on-disk version, the index will be cleared.
+ *
+ * If `force` is false, this does a "soft" read that reloads the index
+ * data from disk only if it has changed since the last time it was
+ * loaded. Purely in-memory index data will be untouched. Be aware: if
+ * there are changes on disk, unwritten in-memory changes are discarded.
*
* @param index an existing index object
+ * @param force if true, always reload, vs. only read if file has changed
* @return 0 or an error code
*/
-GIT_EXTERN(int) git_index_read(git_index *index);
+GIT_EXTERN(int) git_index_read(git_index *index, int force);
/**
* Write an existing index object from memory back to disk
@@ -232,6 +250,14 @@ GIT_EXTERN(int) git_index_read(git_index *index);
GIT_EXTERN(int) git_index_write(git_index *index);
/**
+ * Get the full path to the index file on disk.
+ *
+ * @param index an existing index object
+ * @return path to index file or NULL for in-memory index
+ */
+GIT_EXTERN(const char *) git_index_path(git_index *index);
+
+/**
* Read a tree into the index file with stats
*
* The current index contents will be replaced by the specified tree.
diff --git a/include/git2/indexer.h b/include/git2/indexer.h
index 4db072c9b..e4c03ad06 100644
--- a/include/git2/indexer.h
+++ b/include/git2/indexer.h
@@ -13,19 +13,25 @@
GIT_BEGIN_DECL
-typedef struct git_indexer_stream git_indexer_stream;
+typedef struct git_indexer git_indexer;
/**
- * Create a new streaming indexer instance
+ * Create a new indexer instance
*
* @param out where to store the indexer instance
* @param path to the directory where the packfile should be stored
+ * @param mode permissions to use creating packfile or 0 for defaults
+ * @param odb object database from which to read base objects when
+ * fixing thin packs. Pass NULL if no thin pack is expected (an error
+ * will be returned if there are bases missing)
* @param progress_cb function to call with progress information
* @param progress_cb_payload payload for the progress callback
*/
-GIT_EXTERN(int) git_indexer_stream_new(
- git_indexer_stream **out,
+GIT_EXTERN(int) git_indexer_new(
+ git_indexer **out,
const char *path,
+ unsigned int mode,
+ git_odb *odb,
git_transfer_progress_callback progress_cb,
void *progress_cb_payload);
@@ -37,7 +43,7 @@ GIT_EXTERN(int) git_indexer_stream_new(
* @param size the size of the data in bytes
* @param stats stat storage
*/
-GIT_EXTERN(int) git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t size, git_transfer_progress *stats);
+GIT_EXTERN(int) git_indexer_append(git_indexer *idx, const void *data, size_t size, git_transfer_progress *stats);
/**
* Finalize the pack and index
@@ -46,7 +52,7 @@ GIT_EXTERN(int) git_indexer_stream_add(git_indexer_stream *idx, const void *data
*
* @param idx the indexer
*/
-GIT_EXTERN(int) git_indexer_stream_finalize(git_indexer_stream *idx, git_transfer_progress *stats);
+GIT_EXTERN(int) git_indexer_commit(git_indexer *idx, git_transfer_progress *stats);
/**
* Get the packfile's hash
@@ -56,14 +62,14 @@ GIT_EXTERN(int) git_indexer_stream_finalize(git_indexer_stream *idx, git_transfe
*
* @param idx the indexer instance
*/
-GIT_EXTERN(const git_oid *) git_indexer_stream_hash(const git_indexer_stream *idx);
+GIT_EXTERN(const git_oid *) git_indexer_hash(const git_indexer *idx);
/**
* Free the indexer and its resources
*
* @param idx the indexer to free
*/
-GIT_EXTERN(void) git_indexer_stream_free(git_indexer_stream *idx);
+GIT_EXTERN(void) git_indexer_free(git_indexer *idx);
GIT_END_DECL
diff --git a/include/git2/merge.h b/include/git2/merge.h
index cef6f775b..3354fbeab 100644
--- a/include/git2/merge.h
+++ b/include/git2/merge.h
@@ -7,11 +7,11 @@
#ifndef INCLUDE_git_merge_h__
#define INCLUDE_git_merge_h__
-#include "git2/common.h"
-#include "git2/types.h"
-#include "git2/oid.h"
-#include "git2/checkout.h"
-#include "git2/index.h"
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+#include "checkout.h"
+#include "index.h"
/**
* @file git2/merge.h
@@ -66,6 +66,29 @@ typedef struct {
/**
+ * Option flags for `git_merge`.
+ *
+ * GIT_MERGE_NO_FASTFORWARD - Do not fast-forward.
+ */
+typedef enum {
+ GIT_MERGE_NO_FASTFORWARD = 1,
+ GIT_MERGE_FASTFORWARD_ONLY = 2,
+} git_merge_flags_t;
+
+typedef struct {
+ unsigned int version;
+
+ git_merge_flags_t merge_flags;
+ git_merge_tree_opts merge_tree_opts;
+
+ git_checkout_opts checkout_opts;
+} git_merge_opts;
+
+#define GIT_MERGE_OPTS_VERSION 1
+#define GIT_MERGE_OPTS_INIT {GIT_MERGE_OPTS_VERSION, 0, GIT_MERGE_TREE_OPTS_INIT, GIT_CHECKOUT_OPTS_INIT}
+
+
+/**
* Find a merge base between two commits
*
* @param out the OID of a merge base between 'one' and 'two'
@@ -85,15 +108,15 @@ GIT_EXTERN(int) git_merge_base(
*
* @param out the OID of a merge base considering all the commits
* @param repo the repository where the commits exist
- * @param input_array oids of the commits
* @param length The number of commits in the provided `input_array`
+ * @param input_array oids of the commits
* @return Zero on success; GIT_ENOTFOUND or -1 on failure.
*/
GIT_EXTERN(int) git_merge_base_many(
git_oid *out,
git_repository *repo,
- const git_oid input_array[],
- size_t length);
+ size_t length,
+ const git_oid input_array[]);
/**
* Creates a `git_merge_head` from the given reference
@@ -168,6 +191,43 @@ GIT_EXTERN(int) git_merge_trees(
const git_tree *their_tree,
const git_merge_tree_opts *opts);
+/**
+ * Merges the given commits into HEAD, producing a new commit.
+ *
+ * @param out the results of the merge
+ * @param repo the repository to merge
+ * @param merge_heads the heads to merge into
+ * @param merge_heads_len the number of heads to merge
+ * @param flags merge flags
+ */
+GIT_EXTERN(int) git_merge(
+ git_merge_result **out,
+ git_repository *repo,
+ const git_merge_head **their_heads,
+ size_t their_heads_len,
+ const git_merge_opts *opts);
+
+/**
+ * Returns true if a merge is up-to-date (we were asked to merge the target
+ * into itself.)
+ */
+GIT_EXTERN(int) git_merge_result_is_uptodate(git_merge_result *merge_result);
+
+/**
+ * Returns true if a merge is eligible for fastforward
+ */
+GIT_EXTERN(int) git_merge_result_is_fastforward(git_merge_result *merge_result);
+
+/**
+ * Gets the fast-forward OID if the merge was a fastforward.
+ *
+ * @param out the OID of the fast-forward
+ * @param merge_result the results of the merge
+ */
+GIT_EXTERN(int) git_merge_result_fastforward_oid(git_oid *out, git_merge_result *merge_result);
+
+GIT_EXTERN(void) git_merge_result_free(git_merge_result *merge_result);
+
/** @} */
GIT_END_DECL
#endif
diff --git a/include/git2/notes.h b/include/git2/notes.h
index 7382904ad..76361633b 100644
--- a/include/git2/notes.h
+++ b/include/git2/notes.h
@@ -99,7 +99,7 @@ GIT_EXTERN(int) git_note_read(
/**
* Get the note message
*
- * @param note
+ * @param note the note
* @return the note message
*/
GIT_EXTERN(const char *) git_note_message(const git_note *note);
@@ -108,7 +108,7 @@ GIT_EXTERN(const char *) git_note_message(const git_note *note);
/**
* Get the note object OID
*
- * @param note
+ * @param note the note
* @return the note object OID
*/
GIT_EXTERN(const git_oid *) git_note_oid(const git_note *note);
diff --git a/include/git2/object.h b/include/git2/object.h
index b91b04dba..c40631fa6 100644
--- a/include/git2/object.h
+++ b/include/git2/object.h
@@ -36,7 +36,7 @@ GIT_BEGIN_DECL
* @param repo the repository to look up the object
* @param id the unique identifier for the object
* @param type the type of the object
- * @return a reference to the object
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_object_lookup(
git_object **object,
@@ -78,6 +78,23 @@ GIT_EXTERN(int) git_object_lookup_prefix(
size_t len,
git_otype type);
+
+/**
+ * Lookup an object that represents a tree entry.
+ *
+ * @param out buffer that receives a pointer to the object (which must be freed
+ * by the caller)
+ * @param treeish root object that can be peeled to a tree
+ * @param path relative path from the root object to the desired object
+ * @param type type of object desired
+ * @return 0 on success, or an error code
+ */
+GIT_EXTERN(int) git_object_lookup_bypath(
+ git_object **out,
+ const git_object *treeish,
+ const char *path,
+ git_otype type);
+
/**
* Get the id (SHA1) of a repository object
*
diff --git a/include/git2/odb.h b/include/git2/odb.h
index b64436c4d..ad56384f0 100644
--- a/include/git2/odb.h
+++ b/include/git2/odb.h
@@ -120,7 +120,7 @@ GIT_EXTERN(int) git_odb_read(git_odb_object **out, git_odb *db, const git_oid *i
* @param db database to search for the object in.
* @param short_id a prefix of the id of the object to read.
* @param len the length of the prefix
- * @return
+ * @return
* - 0 if the object was read;
* - GIT_ENOTFOUND if the object is not in the database.
* - GIT_EAMBIGUOUS if the prefix is ambiguous (several objects match the prefix)
@@ -219,18 +219,12 @@ GIT_EXTERN(int) git_odb_write(git_oid *out, git_odb *odb, const void *data, size
* The type and final length of the object must be specified
* when opening the stream.
*
- * The returned stream will be of type `GIT_STREAM_WRONLY` and
- * will have the following methods:
- *
- * - stream->write: write `n` bytes into the stream
- * - stream->finalize_write: close the stream and store the object in
- * the odb
- * - stream->free: free the stream
- *
- * The streaming write won't be effective until `stream->finalize_write`
- * is called and returns without an error
+ * The returned stream will be of type `GIT_STREAM_WRONLY`, and it
+ * won't be effective until `git_odb_stream_finalize_write` is called
+ * and returns without an error
*
- * The stream must always be free'd or will leak memory.
+ * The stream must always be freed when done with `git_odb_stream_free` or
+ * will leak memory.
*
* @see git_odb_stream
*
@@ -243,6 +237,48 @@ GIT_EXTERN(int) git_odb_write(git_oid *out, git_odb *odb, const void *data, size
GIT_EXTERN(int) git_odb_open_wstream(git_odb_stream **out, git_odb *db, size_t size, git_otype type);
/**
+ * Write to an odb stream
+ *
+ * This method will fail if the total number of received bytes exceeds the
+ * size declared with `git_odb_open_wstream()`
+ *
+ * @param stream the stream
+ * @param buffer the data to write
+ * @param len the buffer's length
+ * @return 0 if the write succeeded; error code otherwise
+ */
+GIT_EXTERN(int) git_odb_stream_write(git_odb_stream *stream, const char *buffer, size_t len);
+
+/**
+ * Finish writing to an odb stream
+ *
+ * The object will take its final name and will be available to the
+ * odb.
+ *
+ * This method will fail if the total number of received bytes
+ * differs from the size declared with `git_odb_open_wstream()`
+ *
+ * @param out pointer to store the resulting object's id
+ * @param stream the stream
+ * @return 0 on success; an error code otherwise
+ */
+GIT_EXTERN(int) git_odb_stream_finalize_write(git_oid *out, git_odb_stream *stream);
+
+/**
+ * Read from an odb stream
+ *
+ * Most backends don't implement streaming reads
+ */
+GIT_EXTERN(int) git_odb_stream_read(git_odb_stream *stream, char *buffer, size_t len);
+
+/**
+ * Free an odb stream
+ *
+ * @param stream the stream to free
+ */
+GIT_EXTERN(void) git_odb_stream_free(git_odb_stream *stream);
+
+/**
* Open a stream to read an object from the ODB
*
* Note that most backends do *not* support streaming reads
@@ -322,6 +358,20 @@ GIT_EXTERN(int) git_odb_hash(git_oid *out, const void *data, size_t len, git_oty
GIT_EXTERN(int) git_odb_hashfile(git_oid *out, const char *path, git_otype type);
/**
+ * Create a copy of an odb_object
+ *
+ * The returned copy must be manually freed with `git_odb_object_free`.
+ * Note that because of an implementation detail, the returned copy will be
+ * the same pointer as `source`: the object is internally refcounted, so the
+ * copy still needs to be freed twice.
+ *
+ * @param dest pointer where to store the copy
+ * @param source object to copy
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_odb_object_dup(git_odb_object **dest, git_odb_object *source);
+
+/**
* Close an ODB object
*
* This method must always be called once a `git_odb_object` is no
diff --git a/include/git2/odb_backend.h b/include/git2/odb_backend.h
index af1e3e5b9..4d772cab9 100644
--- a/include/git2/odb_backend.h
+++ b/include/git2/odb_backend.h
@@ -7,8 +7,8 @@
#ifndef INCLUDE_git_odb_backend_h__
#define INCLUDE_git_odb_backend_h__
-#include "git2/common.h"
-#include "git2/types.h"
+#include "common.h"
+#include "types.h"
/**
* @file git2/backend.h
@@ -40,10 +40,18 @@ GIT_EXTERN(int) git_odb_backend_pack(git_odb_backend **out, const char *objects_
* @param objects_dir the Git repository's objects directory
* @param compression_level zlib compression level to use
* @param do_fsync whether to do an fsync() after writing (currently ignored)
+ * @param dir_mode permissions to use creating a directory or 0 for defaults
+ * @param file_mode permissions to use creating a file or 0 for defaults
*
* @return 0 or an error code
*/
-GIT_EXTERN(int) git_odb_backend_loose(git_odb_backend **out, const char *objects_dir, int compression_level, int do_fsync);
+GIT_EXTERN(int) git_odb_backend_loose(
+ git_odb_backend **out,
+ const char *objects_dir,
+ int compression_level,
+ int do_fsync,
+ unsigned int dir_mode,
+ unsigned int file_mode);
/**
* Create a backend out of a single packfile
@@ -65,14 +73,50 @@ typedef enum {
GIT_STREAM_RW = (GIT_STREAM_RDONLY | GIT_STREAM_WRONLY),
} git_odb_stream_t;
-/** A stream to read/write from a backend */
+/**
+ * A stream to read/write from a backend.
+ *
+ * This represents a stream of data being written to or read from a
+ * backend. When writing, the frontend functions take care of
+ * calculating the object's id and all `finalize_write` needs to do is
+ * store the object with the id it is passed.
+ */
struct git_odb_stream {
git_odb_backend *backend;
unsigned int mode;
+ void *hash_ctx;
+
+ size_t declared_size;
+ size_t received_bytes;
+ /**
+ * Write at most `len` bytes into `buffer` and advance the stream.
+ */
int (*read)(git_odb_stream *stream, char *buffer, size_t len);
+
+ /**
+ * Write `len` bytes from `buffer` into the stream.
+ */
int (*write)(git_odb_stream *stream, const char *buffer, size_t len);
- int (*finalize_write)(git_oid *oid_p, git_odb_stream *stream);
+
+ /**
+ * Store the contents of the stream as an object with the id
+ * specified in `oid`.
+ *
+ * This method might not be invoked if:
+ * - an error occurs earlier with the `write` callback,
+ * - the object referred to by `oid` already exists in any backend, or
+ * - the final number of received bytes differs from the size declared
+ * with `git_odb_open_wstream()`
+ */
+ int (*finalize_write)(git_odb_stream *stream, const git_oid *oid);
+
+ /**
+ * Free the stream's memory.
+ *
+ * This method might be called without a call to `finalize_write` if
+ * an error occurs or if the object is already present in the ODB.
+ */
void (*free)(git_odb_stream *stream);
};
@@ -80,7 +124,7 @@ struct git_odb_stream {
struct git_odb_writepack {
git_odb_backend *backend;
- int (*add)(git_odb_writepack *writepack, const void *data, size_t size, git_transfer_progress *stats);
+ int (*append)(git_odb_writepack *writepack, const void *data, size_t size, git_transfer_progress *stats);
int (*commit)(git_odb_writepack *writepack, git_transfer_progress *stats);
void (*free)(git_odb_writepack *writepack);
};
diff --git a/include/git2/oid.h b/include/git2/oid.h
index 662338d93..384b656d7 100644
--- a/include/git2/oid.h
+++ b/include/git2/oid.h
@@ -188,8 +188,7 @@ GIT_EXTERN(int) git_oid_ncmp(const git_oid *a, const git_oid *b, size_t len);
*
* @param id oid structure.
* @param str input hex string of an object id.
- * @return GIT_ENOTOID if str is not a valid hex string,
- * 0 in case of a match, GIT_ERROR otherwise.
+ * @return 0 in case of a match, -1 otherwise.
*/
GIT_EXTERN(int) git_oid_streq(const git_oid *id, const char *str);
@@ -241,13 +240,13 @@ GIT_EXTERN(git_oid_shorten *) git_oid_shorten_new(size_t min_length);
* or freed.
*
* For performance reasons, there is a hard-limit of how many
- * OIDs can be added to a single set (around ~22000, assuming
+ * OIDs can be added to a single set (around ~32000, assuming
* a mostly randomized distribution), which should be enough
* for any kind of program, and keeps the algorithm fast and
* memory-efficient.
*
* Attempting to add more than those OIDs will result in a
- * GIT_ENOMEM error
+ * GITERR_INVALID error
*
* @param os a `git_oid_shorten` instance
* @param text_id an OID in text form
diff --git a/include/git2/pack.h b/include/git2/pack.h
index cc1f48add..88a2716bb 100644
--- a/include/git2/pack.h
+++ b/include/git2/pack.h
@@ -38,7 +38,7 @@
* `git_packbuilder_set_threads` can be used to adjust the number of
* threads used for the process.
*
- * See tests-clar/pack/packbuilder.c for an example.
+ * See tests/pack/packbuilder.c for an example.
*
* @ingroup Git
* @{
@@ -46,6 +46,14 @@
GIT_BEGIN_DECL
/**
+ * Stages that are reported by the packbuilder progress callback.
+ */
+typedef enum {
+ GIT_PACKBUILDER_ADDING_OBJECTS = 0,
+ GIT_PACKBUILDER_DELTAFICATION = 1,
+} git_packbuilder_stage_t;
+
+/**
* Initialize a new packbuilder
*
* @param out The new packbuilder object
@@ -111,6 +119,7 @@ GIT_EXTERN(int) git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid
*
* @param pb The packbuilder
* @param path to the directory where the packfile and index should be stored
+ * @param mode permissions to use creating a packfile or 0 for defaults
* @param progress_cb function to call with progress information from the indexer (optional)
* @param progress_cb_payload payload for the progress callback (optional)
*
@@ -119,9 +128,20 @@ GIT_EXTERN(int) git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid
GIT_EXTERN(int) git_packbuilder_write(
git_packbuilder *pb,
const char *path,
+ unsigned int mode,
git_transfer_progress_callback progress_cb,
void *progress_cb_payload);
+/**
+* Get the packfile's hash
+*
+* A packfile's name is derived from the sorted hashing of all object
+* names. This is only correct after the packfile has been written.
+*
+* @param pb The packbuilder object
+*/
+GIT_EXTERN(const git_oid *) git_packbuilder_hash(git_packbuilder *pb);
+
typedef int (*git_packbuilder_foreach_cb)(void *buf, size_t size, void *payload);
/**
* Create the new pack and pass each object to the callback
@@ -137,7 +157,7 @@ GIT_EXTERN(int) git_packbuilder_foreach(git_packbuilder *pb, git_packbuilder_for
* Get the total number of objects the packbuilder will write out
*
* @param pb the packbuilder
- * @return
+ * @return the number of objects in the packfile
*/
GIT_EXTERN(uint32_t) git_packbuilder_object_count(git_packbuilder *pb);
@@ -145,10 +165,32 @@ GIT_EXTERN(uint32_t) git_packbuilder_object_count(git_packbuilder *pb);
* Get the number of objects the packbuilder has already written out
*
* @param pb the packbuilder
- * @return
+ * @return the number of objects which have already been written
*/
GIT_EXTERN(uint32_t) git_packbuilder_written(git_packbuilder *pb);
+/** Packbuilder progress notification function */
+typedef int (*git_packbuilder_progress)(
+ int stage,
+ unsigned int current,
+ unsigned int total,
+ void *payload);
+
+/**
+ * Set the callbacks for a packbuilder
+ *
+ * @param pb The packbuilder object
+ * @param progress_cb Function to call with progress information during
+ * pack building. Be aware that this is called inline with pack building
+ * operations, so performance may be affected.
+ * @param progress_cb_payload Payload for progress callback.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_packbuilder_set_callbacks(
+ git_packbuilder *pb,
+ git_packbuilder_progress progress_cb,
+ void *progress_cb_payload);
+
/**
* Free the packbuilder and all associated data
*
diff --git a/include/git2/patch.h b/include/git2/patch.h
new file mode 100644
index 000000000..6a6ad92d7
--- /dev/null
+++ b/include/git2/patch.h
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_patch_h__
+#define INCLUDE_git_patch_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+#include "diff.h"
+
+/**
+ * @file git2/patch.h
+ * @brief Patch handling routines.
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * The diff patch is used to store all the text diffs for a delta.
+ *
+ * You can easily loop over the content of patches and get information about
+ * them.
+ */
+typedef struct git_patch git_patch;
+
+/**
+ * Return the diff delta and patch for an entry in the diff list.
+ *
+ * The `git_patch` is a newly created object contains the text diffs
+ * for the delta. You have to call `git_patch_free()` when you are
+ * done with it. You can use the patch object to loop over all the hunks
+ * and lines in the diff of the one delta.
+ *
+ * For an unchanged file or a binary file, no `git_patch` will be
+ * created, the output will be set to NULL, and the `binary` flag will be
+ * set true in the `git_diff_delta` structure.
+ *
+ * The `git_diff_delta` pointer points to internal data and you do not have
+ * to release it when you are done with it. It will go away when the
+ * `git_diff` and `git_patch` go away.
+ *
+ * It is okay to pass NULL for either of the output parameters; if you pass
+ * NULL for the `git_patch`, then the text diff will not be calculated.
+ *
+ * @param out Output parameter for the delta patch object
+ * @param diff Diff list object
+ * @param idx Index into diff list
+ * @return 0 on success, other value < 0 on error
+ */
+GIT_EXTERN(int) git_patch_from_diff(
+ git_patch **out, git_diff *diff, size_t idx);
+
+/**
+ * Directly generate a patch from the difference between two blobs.
+ *
+ * This is just like `git_diff_blobs()` except it generates a patch object
+ * for the difference instead of directly making callbacks. You can use the
+ * standard `git_patch` accessor functions to read the patch data, and
+ * you must call `git_patch_free()` on the patch when done.
+ *
+ * @param out The generated patch; NULL on error
+ * @param old_blob Blob for old side of diff, or NULL for empty blob
+ * @param old_as_path Treat old blob as if it had this filename; can be NULL
+ * @param new_blob Blob for new side of diff, or NULL for empty blob
+ * @param new_as_path Treat new blob as if it had this filename; can be NULL
+ * @param opts Options for diff, or NULL for default options
+ * @return 0 on success or error code < 0
+ */
+GIT_EXTERN(int) git_patch_from_blobs(
+ git_patch **out,
+ const git_blob *old_blob,
+ const char *old_as_path,
+ const git_blob *new_blob,
+ const char *new_as_path,
+ const git_diff_options *opts);
+
+/**
+ * Directly generate a patch from the difference between a blob and a buffer.
+ *
+ * This is just like `git_diff_blob_to_buffer()` except it generates a patch
+ * object for the difference instead of directly making callbacks. You can
+ * use the standard `git_patch` accessor functions to read the patch
+ * data, and you must call `git_patch_free()` on the patch when done.
+ *
+ * @param out The generated patch; NULL on error
+ * @param old_blob Blob for old side of diff, or NULL for empty blob
+ * @param old_as_path Treat old blob as if it had this filename; can be NULL
+ * @param buffer Raw data for new side of diff, or NULL for empty
+ * @param buffer_len Length of raw data for new side of diff
+ * @param buffer_as_path Treat buffer as if it had this filename; can be NULL
+ * @param opts Options for diff, or NULL for default options
+ * @return 0 on success or error code < 0
+ */
+GIT_EXTERN(int) git_patch_from_blob_and_buffer(
+ git_patch **out,
+ const git_blob *old_blob,
+ const char *old_as_path,
+ const char *buffer,
+ size_t buffer_len,
+ const char *buffer_as_path,
+ const git_diff_options *opts);
+
+/**
+ * Free a git_patch object.
+ */
+GIT_EXTERN(void) git_patch_free(git_patch *patch);
+
+/**
+ * Get the delta associated with a patch
+ */
+GIT_EXTERN(const git_diff_delta *) git_patch_get_delta(git_patch *patch);
+
+/**
+ * Get the number of hunks in a patch
+ */
+GIT_EXTERN(size_t) git_patch_num_hunks(git_patch *patch);
+
+/**
+ * Get line counts of each type in a patch.
+ *
+ * This helps imitate a diff --numstat type of output. For that purpose,
+ * you only need the `total_additions` and `total_deletions` values, but we
+ * include the `total_context` line count in case you want the total number
+ * of lines of diff output that will be generated.
+ *
+ * All outputs are optional. Pass NULL if you don't need a particular count.
+ *
+ * @param total_context Count of context lines in output, can be NULL.
+ * @param total_additions Count of addition lines in output, can be NULL.
+ * @param total_deletions Count of deletion lines in output, can be NULL.
+ * @param patch The git_patch object
+ * @return 0 on success, <0 on error
+ */
+GIT_EXTERN(int) git_patch_line_stats(
+ size_t *total_context,
+ size_t *total_additions,
+ size_t *total_deletions,
+ const git_patch *patch);
+
+/**
+ * Get the information about a hunk in a patch
+ *
+ * Given a patch and a hunk index into the patch, this returns detailed
+ * information about that hunk. Any of the output pointers can be passed
+ * as NULL if you don't care about that particular piece of information.
+ *
+ * @param out Output pointer to git_diff_hunk of hunk
+ * @param lines_in_hunk Output count of total lines in this hunk
+ * @param patch Input pointer to patch object
+ * @param hunk_idx Input index of hunk to get information about
+ * @return 0 on success, GIT_ENOTFOUND if hunk_idx out of range, <0 on error
+ */
+GIT_EXTERN(int) git_patch_get_hunk(
+ const git_diff_hunk **out,
+ size_t *lines_in_hunk,
+ git_patch *patch,
+ size_t hunk_idx);
+
+/**
+ * Get the number of lines in a hunk.
+ *
+ * @param patch The git_patch object
+ * @param hunk_idx Index of the hunk
+ * @return Number of lines in hunk or -1 if invalid hunk index
+ */
+GIT_EXTERN(int) git_patch_num_lines_in_hunk(
+ git_patch *patch,
+ size_t hunk_idx);
+
+/**
+ * Get data about a line in a hunk of a patch.
+ *
+ * Given a patch, a hunk index, and a line index in the hunk, this
+ * will return a lot of details about that line. If you pass a hunk
+ * index larger than the number of hunks or a line index larger than
+ * the number of lines in the hunk, this will return -1.
+ *
+ * @param out The git_diff_line data for this line
+ * @param patch The patch to look in
+ * @param hunk_idx The index of the hunk
+ * @param line_of_hunk The index of the line in the hunk
+ * @return 0 on success, <0 on failure
+ */
+GIT_EXTERN(int) git_patch_get_line_in_hunk(
+ const git_diff_line **out,
+ git_patch *patch,
+ size_t hunk_idx,
+ size_t line_of_hunk);
+
+/**
+ * Look up size of patch diff data in bytes
+ *
+ * This returns the raw size of the patch data. This only includes the
+ * actual data from the lines of the diff, not the file or hunk headers.
+ *
+ * If you pass `include_context` as true (non-zero), this will be the size
+ * of all of the diff output; if you pass it as false (zero), this will
+ * only include the actual changed lines (as if `context_lines` was 0).
+ *
+ * @param patch A git_patch representing changes to one file
+ * @param include_context Include context lines in size if non-zero
+ * @param include_hunk_headers Include hunk header lines if non-zero
+ * @param include_file_headers Include file header lines if non-zero
+ * @return The number of bytes of data
+ */
+GIT_EXTERN(size_t) git_patch_size(
+ git_patch *patch,
+ int include_context,
+ int include_hunk_headers,
+ int include_file_headers);
+
+/**
+ * Serialize the patch to text via callback.
+ *
+ * Returning a non-zero value from the callback will terminate the iteration
+ * and cause this return `GIT_EUSER`.
+ *
+ * @param patch A git_patch representing changes to one file
+ * @param print_cb Callback function to output lines of the patch. Will be
+ * called for file headers, hunk headers, and diff lines.
+ * @param payload Reference pointer that will be passed to your callbacks.
+ * @return 0 on success, GIT_EUSER on non-zero callback, or error code
+ */
+GIT_EXTERN(int) git_patch_print(
+ git_patch *patch,
+ git_diff_line_cb print_cb,
+ void *payload);
+
+/**
+ * Get the content of a patch as a single diff text.
+ *
+ * @param string Allocated string; caller must free.
+ * @param patch A git_patch representing changes to one file
+ * @return 0 on success, <0 on failure.
+ */
+GIT_EXTERN(int) git_patch_to_str(
+ char **string,
+ git_patch *patch);
+
+
+GIT_END_DECL
+
+/**@}*/
+
+#endif
diff --git a/include/git2/pathspec.h b/include/git2/pathspec.h
new file mode 100644
index 000000000..2fb0bb716
--- /dev/null
+++ b/include/git2/pathspec.h
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_pathspec_h__
+#define INCLUDE_git_pathspec_h__
+
+#include "common.h"
+#include "types.h"
+#include "strarray.h"
+#include "diff.h"
+
+/**
+ * Compiled pathspec
+ */
+typedef struct git_pathspec git_pathspec;
+
+/**
+ * List of filenames matching a pathspec
+ */
+typedef struct git_pathspec_match_list git_pathspec_match_list;
+
+/**
+ * Options controlling how pathspec match should be executed
+ *
+ * - 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 = (1u << 0),
+ GIT_PATHSPEC_USE_CASE = (1u << 1),
+ GIT_PATHSPEC_NO_GLOB = (1u << 2),
+ GIT_PATHSPEC_NO_MATCH_ERROR = (1u << 3),
+ GIT_PATHSPEC_FIND_FAILURES = (1u << 4),
+ GIT_PATHSPEC_FAILURES_ONLY = (1u << 5),
+} git_pathspec_flag_t;
+
+/**
+ * Compile a pathspec
+ *
+ * @param out Output of the compiled pathspec
+ * @param pathspec A git_strarray of the paths to match
+ * @return 0 on success, <0 on failure
+ */
+GIT_EXTERN(int) git_pathspec_new(
+ git_pathspec **out, const git_strarray *pathspec);
+
+/**
+ * Free a pathspec
+ *
+ * @param ps The compiled pathspec
+ */
+GIT_EXTERN(void) git_pathspec_free(git_pathspec *ps);
+
+/**
+ * Try to match a path against a pathspec
+ *
+ * Unlike most of the other pathspec matching functions, this will not
+ * fall back on the native case-sensitivity for your platform. You must
+ * explicitly pass flags to control case sensitivity or else this will
+ * fall back on being case sensitive.
+ *
+ * @param ps The compiled pathspec
+ * @param flags Combination of git_pathspec_flag_t options to control match
+ * @param path The pathname to attempt to match
+ * @return 1 is path matches spec, 0 if it does not
+ */
+GIT_EXTERN(int) git_pathspec_matches_path(
+ const git_pathspec *ps, uint32_t flags, const char *path);
+
+/**
+ * Match a pathspec against the working directory of a repository.
+ *
+ * This matches the pathspec against the current files in the working
+ * directory of the repository. It is an error to invoke this on a bare
+ * repo. This handles git ignores (i.e. ignored files will not be
+ * considered to match the `pathspec` unless the file is tracked in the
+ * index).
+ *
+ * If `out` is not NULL, this returns a `git_patchspec_match_list`. That
+ * contains the list of all matched filenames (unless you pass the
+ * `GIT_PATHSPEC_FAILURES_ONLY` flag) and may also contain the list of
+ * pathspecs with no match (if you used the `GIT_PATHSPEC_FIND_FAILURES`
+ * flag). You must call `git_pathspec_match_list_free()` on this object.
+ *
+ * @param out Output list of matches; pass NULL to just get return value
+ * @param repo The repository in which to match; bare repo is an error
+ * @param flags Combination of git_pathspec_flag_t options to control match
+ * @param ps Pathspec to be matched
+ * @return 0 on success, -1 on error, GIT_ENOTFOUND if no matches and
+ * the GIT_PATHSPEC_NO_MATCH_ERROR flag was given
+ */
+GIT_EXTERN(int) git_pathspec_match_workdir(
+ git_pathspec_match_list **out,
+ git_repository *repo,
+ uint32_t flags,
+ git_pathspec *ps);
+
+/**
+ * Match a pathspec against entries in an index.
+ *
+ * This matches the pathspec against the files in the repository index.
+ *
+ * NOTE: At the moment, the case sensitivity of this match is controlled
+ * by the current case-sensitivity of the index object itself and the
+ * USE_CASE and IGNORE_CASE flags will have no effect. This behavior will
+ * be corrected in a future release.
+ *
+ * If `out` is not NULL, this returns a `git_patchspec_match_list`. That
+ * contains the list of all matched filenames (unless you pass the
+ * `GIT_PATHSPEC_FAILURES_ONLY` flag) and may also contain the list of
+ * pathspecs with no match (if you used the `GIT_PATHSPEC_FIND_FAILURES`
+ * flag). You must call `git_pathspec_match_list_free()` on this object.
+ *
+ * @param out Output list of matches; pass NULL to just get return value
+ * @param index The index to match against
+ * @param flags Combination of git_pathspec_flag_t options to control match
+ * @param ps Pathspec to be matched
+ * @return 0 on success, -1 on error, GIT_ENOTFOUND if no matches and
+ * the GIT_PATHSPEC_NO_MATCH_ERROR flag is used
+ */
+GIT_EXTERN(int) git_pathspec_match_index(
+ git_pathspec_match_list **out,
+ git_index *index,
+ uint32_t flags,
+ git_pathspec *ps);
+
+/**
+ * Match a pathspec against files in a tree.
+ *
+ * This matches the pathspec against the files in the given tree.
+ *
+ * If `out` is not NULL, this returns a `git_patchspec_match_list`. That
+ * contains the list of all matched filenames (unless you pass the
+ * `GIT_PATHSPEC_FAILURES_ONLY` flag) and may also contain the list of
+ * pathspecs with no match (if you used the `GIT_PATHSPEC_FIND_FAILURES`
+ * flag). You must call `git_pathspec_match_list_free()` on this object.
+ *
+ * @param out Output list of matches; pass NULL to just get return value
+ * @param tree The root-level tree to match against
+ * @param flags Combination of git_pathspec_flag_t options to control match
+ * @param ps Pathspec to be matched
+ * @return 0 on success, -1 on error, GIT_ENOTFOUND if no matches and
+ * the GIT_PATHSPEC_NO_MATCH_ERROR flag is used
+ */
+GIT_EXTERN(int) git_pathspec_match_tree(
+ git_pathspec_match_list **out,
+ git_tree *tree,
+ uint32_t flags,
+ git_pathspec *ps);
+
+/**
+ * Match a pathspec against files in a diff list.
+ *
+ * This matches the pathspec against the files in the given diff list.
+ *
+ * If `out` is not NULL, this returns a `git_patchspec_match_list`. That
+ * contains the list of all matched filenames (unless you pass the
+ * `GIT_PATHSPEC_FAILURES_ONLY` flag) and may also contain the list of
+ * pathspecs with no match (if you used the `GIT_PATHSPEC_FIND_FAILURES`
+ * flag). You must call `git_pathspec_match_list_free()` on this object.
+ *
+ * @param out Output list of matches; pass NULL to just get return value
+ * @param diff A generated diff list
+ * @param flags Combination of git_pathspec_flag_t options to control match
+ * @param ps Pathspec to be matched
+ * @return 0 on success, -1 on error, GIT_ENOTFOUND if no matches and
+ * the GIT_PATHSPEC_NO_MATCH_ERROR flag is used
+ */
+GIT_EXTERN(int) git_pathspec_match_diff(
+ git_pathspec_match_list **out,
+ git_diff *diff,
+ uint32_t flags,
+ git_pathspec *ps);
+
+/**
+ * Free memory associates with a git_pathspec_match_list
+ *
+ * @param m The git_pathspec_match_list to be freed
+ */
+GIT_EXTERN(void) git_pathspec_match_list_free(git_pathspec_match_list *m);
+
+/**
+ * Get the number of items in a match list.
+ *
+ * @param m The git_pathspec_match_list object
+ * @return Number of items in match list
+ */
+GIT_EXTERN(size_t) git_pathspec_match_list_entrycount(
+ const git_pathspec_match_list *m);
+
+/**
+ * Get a matching filename by position.
+ *
+ * This routine cannot be used if the match list was generated by
+ * `git_pathspec_match_diff`. If so, it will always return NULL.
+ *
+ * @param m The git_pathspec_match_list object
+ * @param pos The index into the list
+ * @return The filename of the match
+ */
+GIT_EXTERN(const char *) git_pathspec_match_list_entry(
+ const git_pathspec_match_list *m, size_t pos);
+
+/**
+ * Get a matching diff delta by position.
+ *
+ * This routine can only be used if the match list was generated by
+ * `git_pathspec_match_diff`. Otherwise it will always return NULL.
+ *
+ * @param m The git_pathspec_match_list object
+ * @param pos The index into the list
+ * @return The filename of the match
+ */
+GIT_EXTERN(const git_diff_delta *) git_pathspec_match_list_diff_entry(
+ const git_pathspec_match_list *m, size_t pos);
+
+/**
+ * Get the number of pathspec items that did not match.
+ *
+ * This will be zero unless you passed GIT_PATHSPEC_FIND_FAILURES when
+ * generating the git_pathspec_match_list.
+ *
+ * @param m The git_pathspec_match_list object
+ * @return Number of items in original pathspec that had no matches
+ */
+GIT_EXTERN(size_t) git_pathspec_match_list_failed_entrycount(
+ const git_pathspec_match_list *m);
+
+/**
+ * Get an original pathspec string that had no matches.
+ *
+ * This will be return NULL for positions out of range.
+ *
+ * @param m The git_pathspec_match_list object
+ * @param pos The index into the failed items
+ * @return The pathspec pattern that didn't match anything
+ */
+GIT_EXTERN(const char *) git_pathspec_match_list_failed_entry(
+ const git_pathspec_match_list *m, size_t pos);
+
+#endif
diff --git a/include/git2/push.h b/include/git2/push.h
index f92308144..77ef74039 100644
--- a/include/git2/push.h
+++ b/include/git2/push.h
@@ -8,6 +8,7 @@
#define INCLUDE_git_push_h__
#include "common.h"
+#include "pack.h"
/**
* @file git2/push.h
@@ -38,6 +39,13 @@ typedef struct {
#define GIT_PUSH_OPTIONS_VERSION 1
#define GIT_PUSH_OPTIONS_INIT { GIT_PUSH_OPTIONS_VERSION }
+/** Push network progress notification function */
+typedef int (*git_push_transfer_progress)(
+ unsigned int current,
+ unsigned int total,
+ size_t bytes,
+ void* payload);
+
/**
* Create a new push object
*
@@ -61,6 +69,27 @@ GIT_EXTERN(int) git_push_set_options(
const git_push_options *opts);
/**
+ * Set the callbacks for a push
+ *
+ * @param push The push object
+ * @param pack_progress_cb Function to call with progress information during
+ * pack building. Be aware that this is called inline with pack building
+ * operations, so performance may be affected.
+ * @param pack_progress_cb_payload Payload for the pack progress callback.
+ * @param transfer_progress_cb Function to call with progress information during
+ * the upload portion of a push. Be aware that this is called inline with
+ * pack building operations, so performance may be affected.
+ * @param transfer_progress_cb_payload Payload for the network progress callback.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_push_set_callbacks(
+ git_push *push,
+ git_packbuilder_progress pack_progress_cb,
+ void *pack_progress_cb_payload,
+ git_push_transfer_progress transfer_progress_cb,
+ void *transfer_progress_cb_payload);
+
+/**
* Add a refspec to be pushed
*
* @param push The push object
@@ -98,7 +127,7 @@ GIT_EXTERN(int) git_push_finish(git_push *push);
*
* @param push The push object
*
- * @return true if equal, false otherwise
+ * @return true if remote side successfully unpacked, false otherwise
*/
GIT_EXTERN(int) git_push_unpack_ok(git_push *push);
diff --git a/include/git2/reflog.h b/include/git2/reflog.h
index 4944530af..2d1b6eeaa 100644
--- a/include/git2/reflog.h
+++ b/include/git2/reflog.h
@@ -31,10 +31,11 @@ GIT_BEGIN_DECL
* git_reflog_free().
*
* @param out pointer to reflog
- * @param ref reference to read the reflog for
+ * @param repo the repostiory
+ * @param name reference to look up
* @return 0 or an error code
*/
-GIT_EXTERN(int) git_reflog_read(git_reflog **out, const git_reference *ref);
+GIT_EXTERN(int) git_reflog_read(git_reflog **out, git_repository *repo, const char *name);
/**
* Write an existing in-memory reflog object back to disk
@@ -59,26 +60,45 @@ GIT_EXTERN(int) git_reflog_write(git_reflog *reflog);
GIT_EXTERN(int) git_reflog_append(git_reflog *reflog, const git_oid *id, const git_signature *committer, const char *msg);
/**
- * Rename the reflog for the given reference
+ * Add a new entry to the named reflog.
+ *
+ * This utility function loads the named reflog, appends to it and
+ * writes it back out to the backend.
+ *
+ * `msg` is optional and can be NULL.
+ *
+ * @param repo the repository to act on
+ * @param name the reflog's name
+ * @param id the OID the reference is now pointing to
+ * @param committer the signature of the committer
+ * @param msg the reflog message
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_reflog_append_to(git_repository *repo, const char *name, const git_oid *id, const git_signature *committer, const char *msg);
+
+/**
+ * Rename a reflog
*
* The reflog to be renamed is expected to already exist
*
* The new name will be checked for validity.
* See `git_reference_create_symbolic()` for rules about valid names.
*
- * @param ref the reference
- * @param name the new name of the reference
+ * @param repo the repository
+ * @param old_name the old name of the reference
+ * @param new_name the new name of the reference
* @return 0 on success, GIT_EINVALIDSPEC or an error code
*/
-GIT_EXTERN(int) git_reflog_rename(git_reference *ref, const char *name);
+GIT_EXTERN(int) git_reflog_rename(git_repository *repo, const char *old_name, const char *name);
/**
* Delete the reflog for the given reference
*
- * @param ref the reference
+ * @param repo the repository
+ * @param name the reflog to delete
* @return 0 or an error code
*/
-GIT_EXTERN(int) git_reflog_delete(git_reference *ref);
+GIT_EXTERN(int) git_reflog_delete(git_repository *repo, const char *name);
/**
* Get the number of log entries in a reflog
diff --git a/include/git2/refs.h b/include/git2/refs.h
index 795f7ab27..4041947f6 100644
--- a/include/git2/refs.h
+++ b/include/git2/refs.h
@@ -32,7 +32,7 @@ GIT_BEGIN_DECL
* @param out pointer to the looked-up reference
* @param repo the repository to look up the reference
* @param name the long name for the reference (e.g. HEAD, refs/heads/master, refs/tags/v0.1.0, ...)
- * @return 0 on success, ENOTFOUND, EINVALIDSPEC or an error code.
+ * @return 0 on success, GIT_ENOTFOUND, GIT_EINVALIDSPEC or an error code.
*/
GIT_EXTERN(int) git_reference_lookup(git_reference **out, git_repository *repo, const char *name);
@@ -49,7 +49,7 @@ GIT_EXTERN(int) git_reference_lookup(git_reference **out, git_repository *repo,
* @param out Pointer to oid to be filled in
* @param repo The repository in which to look up the reference
* @param name The long name for the reference (e.g. HEAD, refs/heads/master, refs/tags/v0.1.0, ...)
- * @return 0 on success, ENOTFOUND, EINVALIDSPEC or an error code.
+ * @return 0 on success, GIT_ENOTFOUND, GIT_EINVALIDSPEC or an error code.
*/
GIT_EXTERN(int) git_reference_name_to_id(
git_oid *out, git_repository *repo, const char *name);
@@ -94,7 +94,7 @@ GIT_EXTERN(int) git_reference_dwim(git_reference **out, git_repository *repo, co
* @param name The name of the reference
* @param target The target of the reference
* @param force Overwrite existing references
- * @return 0 on success, EEXISTS, EINVALIDSPEC or an error code
+ * @return 0 on success, GIT_EEXISTS, GIT_EINVALIDSPEC or an error code
*/
GIT_EXTERN(int) git_reference_symbolic_create(git_reference **out, git_repository *repo, const char *name, const char *target, int force);
@@ -126,7 +126,7 @@ GIT_EXTERN(int) git_reference_symbolic_create(git_reference **out, git_repositor
* @param name The name of the reference
* @param id The object id pointed to by the reference.
* @param force Overwrite existing references
- * @return 0 on success, EEXISTS, EINVALIDSPEC or an error code
+ * @return 0 on success, GIT_EEXISTS, GIT_EINVALIDSPEC or an error code
*/
GIT_EXTERN(int) git_reference_create(git_reference **out, git_repository *repo, const char *name, const git_oid *id, int force);
@@ -225,7 +225,7 @@ GIT_EXTERN(git_repository *) git_reference_owner(const git_reference *ref);
* @param out Pointer to the newly created reference
* @param ref The reference
* @param target The new target for the reference
- * @return 0 on success, EINVALIDSPEC or an error code
+ * @return 0 on success, GIT_EINVALIDSPEC or an error code
*/
GIT_EXTERN(int) git_reference_symbolic_set_target(
git_reference **out,
@@ -268,7 +268,7 @@ GIT_EXTERN(int) git_reference_set_target(
* @param ref The reference to rename
* @param new_name The new name for the reference
* @param force Overwrite an existing reference
- * @return 0 on success, EINVALIDSPEC, EEXISTS or an error code
+ * @return 0 on success, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code
*
*/
GIT_EXTERN(int) git_reference_rename(
@@ -442,9 +442,18 @@ GIT_EXTERN(int) git_reference_is_branch(git_reference *ref);
*/
GIT_EXTERN(int) git_reference_is_remote(git_reference *ref);
+/**
+ * Check if a reference is a tag
+ *
+ * @param ref A git reference
+ *
+ * @return 1 when the reference lives in the refs/tags
+ * namespace; 0 otherwise.
+ */
+GIT_EXTERN(int) git_reference_is_tag(git_reference *ref);
typedef enum {
- GIT_REF_FORMAT_NORMAL = 0,
+ GIT_REF_FORMAT_NORMAL = 0u,
/**
* Control whether one-level refnames are accepted
@@ -452,7 +461,7 @@ typedef enum {
* components). Those are expected to be written only using
* uppercase letters and underscore (FETCH_HEAD, ...)
*/
- GIT_REF_FORMAT_ALLOW_ONELEVEL = (1 << 0),
+ GIT_REF_FORMAT_ALLOW_ONELEVEL = (1u << 0),
/**
* Interpret the provided name as a reference pattern for a
@@ -461,14 +470,14 @@ typedef enum {
* in place of a one full pathname component
* (e.g., foo/<star>/bar but not foo/bar<star>).
*/
- GIT_REF_FORMAT_REFSPEC_PATTERN = (1 << 1),
+ GIT_REF_FORMAT_REFSPEC_PATTERN = (1u << 1),
/**
* Interpret the name as part of a refspec in shorthand form
* so the `ONELEVEL` naming rules aren't enforced and 'master'
* becomes a valid name.
*/
- GIT_REF_FORMAT_REFSPEC_SHORTHAND = (1 << 2),
+ GIT_REF_FORMAT_REFSPEC_SHORTHAND = (1u << 2),
} git_reference_normalize_t;
/**
@@ -488,7 +497,7 @@ typedef enum {
* @param name Reference name to be checked.
* @param flags Flags to constrain name validation rules - see the
* GIT_REF_FORMAT constants above.
- * @return 0 on success, GIT_EBUFS if buffer is too small, EINVALIDSPEC
+ * @return 0 on success, GIT_EBUFS if buffer is too small, GIT_EINVALIDSPEC
* or an error code.
*/
GIT_EXTERN(int) git_reference_normalize_name(
diff --git a/include/git2/remote.h b/include/git2/remote.h
index 45d15d0a3..7410909dc 100644
--- a/include/git2/remote.h
+++ b/include/git2/remote.h
@@ -25,13 +25,6 @@
GIT_BEGIN_DECL
typedef int (*git_remote_rename_problem_cb)(const char *problematic_refspec, void *payload);
-/*
- * TODO: This functions still need to be implemented:
- * - _listcb/_foreach
- * - _add
- * - _rename
- * - _del (needs support from config)
- */
/**
* Add a remote with the default fetch refspec to the repository's configuration. This
@@ -50,6 +43,25 @@ GIT_EXTERN(int) git_remote_create(
const char *url);
/**
+ * Add a remote with the provided fetch refspec (or default if NULL) to the repository's
+ * configuration. This
+ * calls git_remote_save before returning.
+ *
+ * @param out the resulting remote
+ * @param repo the repository in which to create the remote
+ * @param name the remote's name
+ * @param url the remote's url
+ * @param fetch the remote fetch value
+ * @return 0, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code
+ */
+GIT_EXTERN(int) git_remote_create_with_fetchspec(
+ git_remote **out,
+ git_repository *repo,
+ const char *name,
+ const char *url,
+ const char *fetch);
+
+/**
* Create a remote in memory
*
* Create a remote with the given refspec in memory. You can use
@@ -61,7 +73,7 @@ GIT_EXTERN(int) git_remote_create(
*
* @param out pointer to the new remote object
* @param repo the associated repository
- * @param fetch the fetch refspec to use for this remote. May be NULL for defaults.
+ * @param fetch the fetch refspec to use for this remote.
* @param url the remote repository's URL
* @return 0 or an error code
*/
@@ -96,6 +108,14 @@ GIT_EXTERN(int) git_remote_load(git_remote **out, git_repository *repo, const ch
GIT_EXTERN(int) git_remote_save(const git_remote *remote);
/**
+ * Get the remote's repository
+ *
+ * @param remote the remote
+ * @return a pointer to the repository
+ */
+GIT_EXTERN(git_repository *) git_remote_owner(const git_remote *remote);
+
+/**
* Get the remote's name
*
* @param remote the remote
@@ -144,8 +164,11 @@ GIT_EXTERN(int) git_remote_set_pushurl(git_remote *remote, const char* url);
/**
* Add a fetch refspec to the remote
*
+ * Convenience function for adding a single fetch refspec to the
+ * current list in the remote.
+ *
* @param remote the remote
- * @apram refspec the new fetch refspec
+ * @param refspec the new fetch refspec
* @return 0 or an error value
*/
GIT_EXTERN(int) git_remote_add_fetch(git_remote *remote, const char *refspec);
@@ -162,8 +185,21 @@ GIT_EXTERN(int) git_remote_add_fetch(git_remote *remote, const char *refspec);
GIT_EXTERN(int) git_remote_get_fetch_refspecs(git_strarray *array, git_remote *remote);
/**
+ * Set the remote's list of fetch refspecs
+ *
+ * The contents of the string array are copied.
+ *
+ * @param remote the remote to modify
+ * @param array the new list of fetch resfpecs
+ */
+GIT_EXTERN(int) git_remote_set_fetch_refspecs(git_remote *remote, git_strarray *array);
+
+/**
* Add a push refspec to the remote
*
+ * Convenience function for adding a single push refspec to the
+ * current list in the remote.
+ *
* @param remote the remote
* @param refspec the new push refspec
* @return 0 or an error value
@@ -182,6 +218,16 @@ GIT_EXTERN(int) git_remote_add_push(git_remote *remote, const char *refspec);
GIT_EXTERN(int) git_remote_get_push_refspecs(git_strarray *array, git_remote *remote);
/**
+ * Set the remote's list of push refspecs
+ *
+ * The contents of the string array are copied.
+ *
+ * @param remote the remote to modify
+ * @param array the new list of push resfpecs
+ */
+GIT_EXTERN(int) git_remote_set_push_refspecs(git_remote *remote, git_strarray *array);
+
+/**
* Clear the refspecs
*
* Remove all configured fetch and push refspecs from the remote.
@@ -208,15 +254,6 @@ GIT_EXTERN(size_t) git_remote_refspec_count(git_remote *remote);
GIT_EXTERN(const git_refspec *)git_remote_get_refspec(git_remote *remote, size_t n);
/**
- * Remove a refspec from the remote
- *
- * @param remote the remote to query
- * @param n the refspec to remove
- * @return 0 or GIT_ENOTFOUND
- */
-GIT_EXTERN(int) git_remote_remove_refspec(git_remote *remote, size_t n);
-
-/**
* Open a connection to a remote
*
* The transport is selected based on the URL. The direction argument
@@ -236,36 +273,30 @@ GIT_EXTERN(int) git_remote_connect(git_remote *remote, git_direction direction);
* The remote (or more exactly its transport) must be connected. The
* memory belongs to the remote.
*
- * If you a return a non-zero value from the callback, this will stop
- * looping over the refs.
+ * The array will stay valid as long as the remote object exists and
+ * its transport isn't changed, but a copy is recommended for usage of
+ * the data.
*
+ * @param out pointer to the array
+ * @param size the number of remote heads
* @param remote the remote
- * @param list_cb function to call with each ref discovered at the remote
- * @param payload additional data to pass to the callback
- * @return 0 on success, GIT_EUSER on non-zero callback, or error code
+ * @return 0 on success, or an error code
*/
-GIT_EXTERN(int) git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload);
+GIT_EXTERN(int) git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote);
/**
- * Download the packfile
+ * Download and index the packfile
*
- * Negotiate what objects should be downloaded and download the
- * packfile with those objects. The packfile is downloaded with a
- * temporary filename, as it's final name is not known yet. If there
- * was no packfile needed (all the objects were available locally),
- * filename will be NULL and the function will return success.
+ * Connect to the remote if it hasn't been done yet, negotiate with
+ * the remote git which objects are missing, download and index the
+ * packfile.
+ *
+ * The .idx file will be created and both it and the packfile with be
+ * renamed to their final name.
*
- * @param remote the remote to download from
- * @param progress_cb function to call with progress information. Be aware that
- * this is called inline with network and indexing operations, so performance
- * may be affected.
- * @param payload payload for the progress callback
* @return 0 or an error code
*/
-GIT_EXTERN(int) git_remote_download(
- git_remote *remote,
- git_transfer_progress_callback progress_cb,
- void *payload);
+GIT_EXTERN(int) git_remote_download(git_remote *remote);
/**
* Check whether the remote is connected
@@ -317,6 +348,17 @@ GIT_EXTERN(void) git_remote_free(git_remote *remote);
GIT_EXTERN(int) git_remote_update_tips(git_remote *remote);
/**
+ * Download new data and update tips
+ *
+ * Convenience function to connect to a remote, download the data,
+ * disconnect and update the remote-tracking branches.
+ *
+ * @param remote the remote to fetch from
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_remote_fetch(git_remote *remote);
+
+/**
* Return whether a string is a valid remote URL
*
* @param url the url to check
@@ -352,21 +394,6 @@ GIT_EXTERN(int) git_remote_list(git_strarray *out, git_repository *repo);
GIT_EXTERN(void) git_remote_check_cert(git_remote *remote, int check);
/**
- * Set a credentials acquisition callback for this remote. If the remote is
- * not available for anonymous access, then you must set this callback in order
- * to provide credentials to the transport at the time of authentication
- * failure so that retry can be performed.
- *
- * @param remote the remote to configure
- * @param cred_acquire_cb The credentials acquisition callback to use (defaults
- * to NULL)
- */
-GIT_EXTERN(void) git_remote_set_cred_acquire_cb(
- git_remote *remote,
- git_cred_acquire_cb cred_acquire_cb,
- void *payload);
-
-/**
* Sets a custom transport for the remote. The caller can use this function
* to bypass the automatic discovery of a transport by URL scheme (i.e.
* http://, https://, git://) and supply their own transport to be used
@@ -395,13 +422,47 @@ typedef enum git_remote_completion_type {
/**
* The callback settings structure
*
- * Set the calbacks to be called by the remote.
+ * Set the callbacks to be called by the remote when informing the user
+ * about the progress of the network operations.
*/
struct git_remote_callbacks {
unsigned int version;
- void (*progress)(const char *str, int len, void *data);
+ /**
+ * Textual progress from the remote. Text send over the
+ * progress side-band will be passed to this function (this is
+ * the 'counting objects' output.
+ */
+ int (*progress)(const char *str, int len, void *data);
+
+ /**
+ * Completion is called when different parts of the download
+ * process are done (currently unused).
+ */
int (*completion)(git_remote_completion_type type, void *data);
+
+ /**
+ * This will be called if the remote host requires
+ * authentication in order to connect to it.
+ */
+ int (*credentials)(git_cred **cred, const char *url, const char *username_from_url, unsigned int allowed_types, void *data);
+
+ /**
+ * During the download of new data, this will be regularly
+ * called with the current count of progress done by the
+ * indexer.
+ */
+ int (*transfer_progress)(const git_transfer_progress *stats, void *data);
+
+ /**
+ * Each time a reference is updated locally, this function
+ * will be called with information about it.
+ */
int (*update_tips)(const char *refname, const git_oid *a, const git_oid *b, void *data);
+
+ /**
+ * This will be passed to each of the callbacks in this struct
+ * as the last parameter.
+ */
void *payload;
};
@@ -418,7 +479,7 @@ struct git_remote_callbacks {
* @param callbacks a pointer to the user's callback settings
* @return 0 or an error code
*/
-GIT_EXTERN(int) git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callbacks);
+GIT_EXTERN(int) git_remote_set_callbacks(git_remote *remote, const git_remote_callbacks *callbacks);
/**
* Get the statistics structure that is filled in by the fetch operation.
diff --git a/include/git2/repository.h b/include/git2/repository.h
index 2164cfac1..b4d561992 100644
--- a/include/git2/repository.h
+++ b/include/git2/repository.h
@@ -94,10 +94,14 @@ GIT_EXTERN(int) git_repository_discover(
* changes from the `stat` system call). (E.g. Searching in a user's home
* directory "/home/user/source/" will not return "/.git/" as the found
* repo if "/" is a different filesystem than "/home".)
+ * * GIT_REPOSITORY_OPEN_BARE - Open repository as a bare repo regardless
+ * of core.bare config, and defer loading config file for faster setup.
+ * Unlike `git_repository_open_bare`, this can follow gitlinks.
*/
typedef enum {
GIT_REPOSITORY_OPEN_NO_SEARCH = (1 << 0),
GIT_REPOSITORY_OPEN_CROSS_FS = (1 << 1),
+ GIT_REPOSITORY_OPEN_BARE = (1 << 2),
} git_repository_open_flag_t;
/**
@@ -178,7 +182,7 @@ GIT_EXTERN(int) git_repository_init(
* when initializing a new repo. Details of individual values are:
*
* * BARE - Create a bare repository with no working directory.
- * * NO_REINIT - Return an EEXISTS error if the repo_path appears to
+ * * NO_REINIT - Return an GIT_EEXISTS error if the repo_path appears to
* already be an git repository.
* * NO_DOTGIT_DIR - Normally a "/.git/" will be appended to the repo
* path for non-bare repos (if it is not already there), but
@@ -293,7 +297,7 @@ GIT_EXTERN(int) git_repository_init_ext(
* @param out pointer to the reference which will be retrieved
* @param repo a repository object
*
- * @return 0 on success, GIT_EORPHANEDHEAD when HEAD points to a non existing
+ * @return 0 on success, GIT_EUNBORNBRANCH when HEAD points to a non existing
* branch, GIT_ENOTFOUND when HEAD is missing; an error code otherwise
*/
GIT_EXTERN(int) git_repository_head(git_reference **out, git_repository *repo);
@@ -311,16 +315,16 @@ GIT_EXTERN(int) git_repository_head(git_reference **out, git_repository *repo);
GIT_EXTERN(int) git_repository_head_detached(git_repository *repo);
/**
- * Check if the current branch is an orphan
+ * Check if the current branch is unborn
*
- * An orphan branch is one named from HEAD but which doesn't exist in
+ * An unborn branch is one named from HEAD but which doesn't exist in
* the refs namespace, because it doesn't have any commit to point to.
*
* @param repo Repo to test
- * @return 1 if the current branch is an orphan, 0 if it's not; error
+ * @return 1 if the current branch is unborn, 0 if it's not; error
* code if there was an error
*/
-GIT_EXTERN(int) git_repository_head_orphan(git_repository *repo);
+GIT_EXTERN(int) git_repository_head_unborn(git_repository *repo);
/**
* Check if a repository is empty
@@ -471,7 +475,7 @@ GIT_EXTERN(int) git_repository_index(git_index **out, git_repository *repo);
* @param out Buffer to write data into or NULL to just read required size
* @param len Length of `out` buffer in bytes
* @param repo Repository to read prepared message from
- * @return GIT_ENOUTFOUND if no message exists, other value < 0 for other
+ * @return GIT_ENOTFOUND if no message exists, other value < 0 for other
* errors, or total bytes in message (may be > `len`) on success
*/
GIT_EXTERN(int) git_repository_message(char *out, size_t len, git_repository *repo);
@@ -601,13 +605,13 @@ GIT_EXTERN(int) git_repository_set_head_detached(
* If the HEAD is already detached and points to a Tag, the HEAD is
* updated into making it point to the peeled Commit, and 0 is returned.
*
- * If the HEAD is already detached and points to a non commitish, the HEAD is
+ * If the HEAD is already detached and points to a non commitish, the HEAD is
* unaltered, and -1 is returned.
*
* Otherwise, the HEAD will be detached and point to the peeled Commit.
*
* @param repo Repository pointer
- * @return 0 on success, GIT_EORPHANEDHEAD when HEAD points to a non existing
+ * @return 0 on success, GIT_EUNBORNBRANCH when HEAD points to a non existing
* branch or an error code
*/
GIT_EXTERN(int) git_repository_detach_head(
diff --git a/include/git2/revparse.h b/include/git2/revparse.h
index 786a9da57..d170e1621 100644
--- a/include/git2/revparse.h
+++ b/include/git2/revparse.h
@@ -10,7 +10,6 @@
#include "common.h"
#include "types.h"
-
/**
* @file git2/revparse.h
* @brief Git revision parsing routines
@@ -21,27 +20,37 @@
GIT_BEGIN_DECL
/**
- * Find a single object, as specified by a revision string. See `man gitrevisions`,
- * or http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for
+ * Find a single object, as specified by a revision string.
+ *
+ * See `man gitrevisions`, or
+ * http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for
* information on the syntax accepted.
*
+ * The returned object should be released with `git_object_free` when no
+ * longer needed.
+ *
* @param out pointer to output object
* @param repo the repository to search in
* @param spec the textual specification for an object
* @return 0 on success, GIT_ENOTFOUND, GIT_EAMBIGUOUS, GIT_EINVALIDSPEC or an error code
*/
-GIT_EXTERN(int) git_revparse_single(git_object **out, git_repository *repo, const char *spec);
+GIT_EXTERN(int) git_revparse_single(
+ git_object **out, git_repository *repo, const char *spec);
/**
- * Find a single object, as specified by a revision string.
- * See `man gitrevisions`,
- * or http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for
+ * Find a single object and intermediate reference by a revision string.
+ *
+ * See `man gitrevisions`, or
+ * http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for
* information on the syntax accepted.
*
* In some cases (`@{<-n>}` or `<branchname>@{upstream}`), the expression may
* point to an intermediate reference. When such expressions are being passed
* in, `reference_out` will be valued as well.
*
+ * The returned object should be released with `git_object_free` and the
+ * returned reference with `git_reference_free` when no longer needed.
+ *
* @param object_out pointer to output object
* @param reference_out pointer to output reference or NULL
* @param repo the repository to search in
@@ -76,25 +85,27 @@ typedef struct {
git_object *from;
/** The right element of the revspec; must be freed by the user */
git_object *to;
- /** The intent of the revspec */
+ /** The intent of the revspec (i.e. `git_revparse_mode_t` flags) */
unsigned int flags;
} git_revspec;
/**
- * Parse a revision string for `from`, `to`, and intent. See `man gitrevisions` or
- * http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for information
- * on the syntax accepted.
+ * Parse a revision string for `from`, `to`, and intent.
+ *
+ * See `man gitrevisions` or
+ * http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for
+ * information on the syntax accepted.
*
- * @param revspec Pointer to an user-allocated git_revspec struct where the result
- * of the rev-parse will be stored
+ * @param revspec Pointer to an user-allocated git_revspec struct where
+ * the result of the rev-parse will be stored
* @param repo the repository to search in
* @param spec the rev-parse spec to parse
* @return 0 on success, GIT_INVALIDSPEC, GIT_ENOTFOUND, GIT_EAMBIGUOUS or an error code
*/
GIT_EXTERN(int) git_revparse(
- git_revspec *revspec,
- git_repository *repo,
- const char *spec);
+ git_revspec *revspec,
+ git_repository *repo,
+ const char *spec);
/** @} */
diff --git a/include/git2/revwalk.h b/include/git2/revwalk.h
index 8bfe0b502..c59b79938 100644
--- a/include/git2/revwalk.h
+++ b/include/git2/revwalk.h
@@ -232,6 +232,14 @@ GIT_EXTERN(void) git_revwalk_sorting(git_revwalk *walk, unsigned int sort_mode);
GIT_EXTERN(int) git_revwalk_push_range(git_revwalk *walk, const char *range);
/**
+ * Simplify the history by first-parent
+ *
+ * No parents other than the first for each commit will be enqueued.
+ */
+GIT_EXTERN(void) git_revwalk_simplify_first_parent(git_revwalk *walk);
+
+
+/**
* Free a revision walker previously allocated.
*
* @param walk traversal handle to close. If NULL nothing occurs.
diff --git a/include/git2/signature.h b/include/git2/signature.h
index 00d19de66..2fa46d032 100644
--- a/include/git2/signature.h
+++ b/include/git2/signature.h
@@ -48,6 +48,19 @@ GIT_EXTERN(int) git_signature_new(git_signature **out, const char *name, const c
*/
GIT_EXTERN(int) git_signature_now(git_signature **out, const char *name, const char *email);
+/**
+ * Create a new action signature with default user and now timestamp.
+ *
+ * This looks up the user.name and user.email from the configuration and
+ * uses the current time as the timestamp, and creates a new signature
+ * based on that information. It will return GIT_ENOTFOUND if either the
+ * user.name or user.email are not set.
+ *
+ * @param out new signature
+ * @param repo repository pointer
+ * @return 0 on success, GIT_ENOTFOUND if config is missing, or error code
+ */
+GIT_EXTERN(int) git_signature_default(git_signature **out, git_repository *repo);
/**
* Create a copy of an existing signature. All internal strings are also
diff --git a/include/git2/stash.h b/include/git2/stash.h
index 68d1b5413..b48d33f5d 100644
--- a/include/git2/stash.h
+++ b/include/git2/stash.h
@@ -57,7 +57,7 @@ typedef enum {
GIT_EXTERN(int) git_stash_save(
git_oid *out,
git_repository *repo,
- git_signature *stasher,
+ const git_signature *stasher,
const char *message,
unsigned int flags);
diff --git a/include/git2/status.h b/include/git2/status.h
index 63aea2f3b..4ec3432df 100644
--- a/include/git2/status.h
+++ b/include/git2/status.h
@@ -60,25 +60,24 @@ typedef int (*git_status_cb)(
const char *path, unsigned int status_flags, void *payload);
/**
- * For extended status, select the files on which to report status.
+ * Select the files on which to report status.
+ *
+ * With `git_status_foreach_ext`, this will control which changes get
+ * callbacks. With `git_status_list_new`, these will control which
+ * changes are included in the list.
*
* - GIT_STATUS_SHOW_INDEX_AND_WORKDIR is the default. This roughly
- * matches `git status --porcelain` where each file gets a callback
- * indicating its status in the index and in the working directory.
+ * matches `git status --porcelain` regarding which files are
+ * included and in what order.
* - GIT_STATUS_SHOW_INDEX_ONLY only gives status based on HEAD to index
* comparison, not looking at working directory changes.
* - GIT_STATUS_SHOW_WORKDIR_ONLY only gives status based on index to
* working directory comparison, not comparing the index to the HEAD.
- * - GIT_STATUS_SHOW_INDEX_THEN_WORKDIR runs index-only then workdir-only,
- * issuing (up to) two callbacks per file (first index, then workdir).
- * This is slightly more efficient than separate calls and can make it
- * easier to emulate plain `git status` text output.
*/
typedef enum {
GIT_STATUS_SHOW_INDEX_AND_WORKDIR = 0,
GIT_STATUS_SHOW_INDEX_ONLY = 1,
GIT_STATUS_SHOW_WORKDIR_ONLY = 2,
- GIT_STATUS_SHOW_INDEX_THEN_WORKDIR = 3,
} git_status_show_t;
/**
@@ -108,7 +107,7 @@ typedef enum {
* - GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX indicates that rename detection
* should be processed between the head and the index and enables
* the GIT_STATUS_INDEX_RENAMED as a possible status flag.
- * - GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR indicates tha rename
+ * - GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR indicates that rename
* detection should be run between the index and the working directory
* and enabled GIT_STATUS_WT_RENAMED as a possible status flag.
* - GIT_STATUS_OPT_SORT_CASE_SENSITIVELY overrides the native case
@@ -117,6 +116,11 @@ typedef enum {
* - GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY overrides the native case
* sensitivity for the file system and forces the output to be in
* case-insensitive order
+ * - GIT_STATUS_OPT_RENAMES_FROM_REWRITES indicates that rename detection
+ * should include rewritten files
+ * - GIT_STATUS_OPT_NO_REFRESH bypasses the default status behavior of
+ * doing a "soft" index reload (i.e. reloading the index data if the
+ * file on disk has been modified outside libgit2).
*
* Calling `git_status_foreach()` is like calling the extended version
* with: GIT_STATUS_OPT_INCLUDE_IGNORED, GIT_STATUS_OPT_INCLUDE_UNTRACKED,
@@ -135,6 +139,8 @@ typedef enum {
GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR = (1u << 8),
GIT_STATUS_OPT_SORT_CASE_SENSITIVELY = (1u << 9),
GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY = (1u << 10),
+ GIT_STATUS_OPT_RENAMES_FROM_REWRITES = (1u << 11),
+ GIT_STATUS_OPT_NO_REFRESH = (1u << 12),
} git_status_opt_t;
#define GIT_STATUS_OPT_DEFAULTS \
@@ -235,12 +241,12 @@ GIT_EXTERN(int) git_status_foreach_ext(
* This is not quite the same as calling `git_status_foreach_ext()` with
* the pathspec set to the specified path.
*
- * @param status_flags The status value for the file
+ * @param status_flags Output combination of git_status_t values for file
* @param repo A repository object
- * @param path The file to retrieve status for, rooted at the repo's workdir
+ * @param path The file to retrieve status for relative to the repo workdir
* @return 0 on success, GIT_ENOTFOUND if the file is not found in the HEAD,
- * index, and work tree, GIT_EINVALIDPATH if `path` points at a folder,
- * GIT_EAMBIGUOUS if "path" matches multiple files, -1 on other error.
+ * index, and work tree, GIT_EAMBIGUOUS if `path` matches multiple files
+ * or if it refers to a folder, and -1 on other errors.
*/
GIT_EXTERN(int) git_status_file(
unsigned int *status_flags,
diff --git a/include/git2/submodule.h b/include/git2/submodule.h
index 91b5300ae..186f263f5 100644
--- a/include/git2/submodule.h
+++ b/include/git2/submodule.h
@@ -14,51 +14,18 @@
/**
* @file git2/submodule.h
* @brief Git submodule management utilities
- * @defgroup git_submodule Git submodule management routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Opaque structure representing a submodule.
*
* Submodule support in libgit2 builds a list of known submodules and keeps
* it in the repository. The list is built from the .gitmodules file, the
* .git/config file, the index, and the HEAD tree. Items in the working
* directory that look like submodules (i.e. a git repo) but are not
* mentioned in those places won't be tracked.
- */
-typedef struct git_submodule git_submodule;
-
-/**
- * Values that could be specified for the update rule of a submodule.
*
- * Use the DEFAULT value if you have altered the update value via
- * `git_submodule_set_update()` and wish to reset to the original default.
- */
-typedef enum {
- GIT_SUBMODULE_UPDATE_DEFAULT = -1,
- GIT_SUBMODULE_UPDATE_CHECKOUT = 0,
- GIT_SUBMODULE_UPDATE_REBASE = 1,
- GIT_SUBMODULE_UPDATE_MERGE = 2,
- GIT_SUBMODULE_UPDATE_NONE = 3
-} git_submodule_update_t;
-
-/**
- * Values that could be specified for how closely to examine the
- * working directory when getting submodule status.
- *
- * Use the DEFUALT value if you have altered the ignore value via
- * `git_submodule_set_ignore()` and wish to reset to the original value.
+ * @defgroup git_submodule Git submodule management routines
+ * @ingroup Git
+ * @{
*/
-typedef enum {
- GIT_SUBMODULE_IGNORE_DEFAULT = -1, /* reset to default */
- GIT_SUBMODULE_IGNORE_NONE = 0, /* any change or untracked == dirty */
- GIT_SUBMODULE_IGNORE_UNTRACKED = 1, /* dirty if tracked files change */
- GIT_SUBMODULE_IGNORE_DIRTY = 2, /* only dirty if HEAD moved */
- GIT_SUBMODULE_IGNORE_ALL = 3 /* never dirty */
-} git_submodule_ignore_t;
+GIT_BEGIN_DECL
/**
* Return codes for submodule status.
@@ -119,19 +86,9 @@ typedef enum {
GIT_SUBMODULE_STATUS_WD_UNTRACKED = (1u << 13),
} git_submodule_status_t;
-#define GIT_SUBMODULE_STATUS__IN_FLAGS \
- (GIT_SUBMODULE_STATUS_IN_HEAD | \
- GIT_SUBMODULE_STATUS_IN_INDEX | \
- GIT_SUBMODULE_STATUS_IN_CONFIG | \
- GIT_SUBMODULE_STATUS_IN_WD)
-
-#define GIT_SUBMODULE_STATUS__INDEX_FLAGS \
- (GIT_SUBMODULE_STATUS_INDEX_ADDED | \
- GIT_SUBMODULE_STATUS_INDEX_DELETED | \
- GIT_SUBMODULE_STATUS_INDEX_MODIFIED)
-
-#define GIT_SUBMODULE_STATUS__WD_FLAGS \
- ~(GIT_SUBMODULE_STATUS__IN_FLAGS | GIT_SUBMODULE_STATUS__INDEX_FLAGS)
+#define GIT_SUBMODULE_STATUS__IN_FLAGS 0x000Fu
+#define GIT_SUBMODULE_STATUS__INDEX_FLAGS 0x0070u
+#define GIT_SUBMODULE_STATUS__WD_FLAGS 0x3F80u
#define GIT_SUBMODULE_STATUS_IS_UNMODIFIED(S) \
(((S) & ~GIT_SUBMODULE_STATUS__IN_FLAGS) == 0)
@@ -359,9 +316,10 @@ GIT_EXTERN(const git_oid *) git_submodule_head_id(git_submodule *submodule);
GIT_EXTERN(const git_oid *) git_submodule_wd_id(git_submodule *submodule);
/**
- * Get the ignore rule for the submodule.
+ * Get the ignore rule that will be used for the submodule.
*
- * There are four ignore values:
+ * These values control the behavior of `git_submodule_status()` for this
+ * submodule. There are four ignore values:
*
* - **GIT_SUBMODULE_IGNORE_NONE** will consider any change to the contents
* of the submodule from a clean checkout to be dirty, including the
@@ -375,6 +333,13 @@ GIT_EXTERN(const git_oid *) git_submodule_wd_id(git_submodule *submodule);
* - **GIT_SUBMODULE_IGNORE_ALL** means not to open the submodule repo.
* The working directory will be consider clean so long as there is a
* checked out version present.
+ *
+ * plus the special **GIT_SUBMODULE_IGNORE_RESET** which can be used with
+ * `git_submodule_set_ignore()` to revert to the on-disk setting.
+ *
+ * @param submodule The submodule to check
+ * @return The current git_submodule_ignore_t valyue what will be used for
+ * this submodule.
*/
GIT_EXTERN(git_submodule_ignore_t) git_submodule_ignore(
git_submodule *submodule);
@@ -382,15 +347,17 @@ GIT_EXTERN(git_submodule_ignore_t) git_submodule_ignore(
/**
* Set the ignore rule for the submodule.
*
- * This sets the ignore rule in memory for the submodule. This will be used
- * for any following actions (such as `git_submodule_status()`) while the
- * submodule is in memory. You should call `git_submodule_save()` if you
- * want to persist the new ignore role.
+ * This sets the in-memory ignore rule for the submodule which will
+ * control the behavior of `git_submodule_status()`.
*
- * Calling this again with GIT_SUBMODULE_IGNORE_DEFAULT or calling
- * `git_submodule_reload()` will revert the rule to the value that was in the
- * original config.
+ * To make changes persistent, call `git_submodule_save()` to write the
+ * value to disk (in the ".gitmodules" and ".git/config" files).
*
+ * Call with `GIT_SUBMODULE_IGNORE_RESET` or call `git_submodule_reload()`
+ * to revert the in-memory rule to the value that is on disk.
+ *
+ * @param submodule The submodule to update
+ * @param ignore The new value for the ignore rule
* @return old value for ignore
*/
GIT_EXTERN(git_submodule_ignore_t) git_submodule_set_ignore(
@@ -398,7 +365,16 @@ GIT_EXTERN(git_submodule_ignore_t) git_submodule_set_ignore(
git_submodule_ignore_t ignore);
/**
- * Get the update rule for the submodule.
+ * Get the update rule that will be used for the submodule.
+ *
+ * This value controls the behavior of the `git submodule update` command.
+ * There are four useful values documented with `git_submodule_update_t`
+ * plus the `GIT_SUBMODULE_UPDATE_RESET` which can be used to revert to
+ * the on-disk setting.
+ *
+ * @param submodule The submodule to check
+ * @return The current git_submodule_update_t value that will be used
+ * for this submodule.
*/
GIT_EXTERN(git_submodule_update_t) git_submodule_update(
git_submodule *submodule);
@@ -406,13 +382,17 @@ GIT_EXTERN(git_submodule_update_t) git_submodule_update(
/**
* Set the update rule for the submodule.
*
- * This sets the update rule in memory for the submodule. You should call
- * `git_submodule_save()` if you want to persist the new update rule.
+ * The initial value comes from the ".git/config" setting of
+ * `submodule.$name.update` for this submodule (which is initialized from
+ * the ".gitmodules" file). Using this function sets the update rule in
+ * memory for the submodule. Call `git_submodule_save()` to write out the
+ * new update rule.
*
- * Calling this again with GIT_SUBMODULE_UPDATE_DEFAULT or calling
- * `git_submodule_reload()` will revert the rule to the value that was in the
- * original config.
+ * Calling this again with GIT_SUBMODULE_UPDATE_RESET or calling
+ * `git_submodule_reload()` will revert the rule to the on disk value.
*
+ * @param submodule The submodule to update
+ * @param update The new value to use
* @return old value for update
*/
GIT_EXTERN(git_submodule_update_t) git_submodule_set_update(
diff --git a/include/git2/sys/config.h b/include/git2/sys/config.h
index 11e59cf03..419ad7ea7 100644
--- a/include/git2/sys/config.h
+++ b/include/git2/sys/config.h
@@ -21,6 +21,33 @@
GIT_BEGIN_DECL
/**
+ * Every iterator must have this struct as its first element, so the
+ * API can talk to it. You'd define your iterator as
+ *
+ * struct my_iterator {
+ * git_config_iterator parent;
+ * ...
+ * }
+ *
+ * and assign `iter->parent.backend` to your `git_config_backend`.
+ */
+struct git_config_iterator {
+ git_config_backend *backend;
+ unsigned int flags;
+
+ /**
+ * Return the current entry and advance the iterator. The
+ * memory belongs to the library.
+ */
+ int (*next)(git_config_entry **entry, git_config_iterator *iter);
+
+ /**
+ * Free the iterator
+ */
+ void (*free)(git_config_iterator *iter);
+};
+
+/**
* Generic backend that implements the interface to
* access a configuration file
*/
@@ -31,11 +58,11 @@ struct git_config_backend {
/* Open means open the file/database and parse if necessary */
int (*open)(struct git_config_backend *, git_config_level_t level);
int (*get)(const struct git_config_backend *, const char *key, const git_config_entry **entry);
- int (*get_multivar)(struct git_config_backend *, const char *key, const char *regexp, git_config_foreach_cb callback, void *payload);
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);
int (*del)(struct git_config_backend *, const char *key);
- int (*foreach)(struct git_config_backend *, const char *, git_config_foreach_cb callback, void *payload);
+ int (*del_multivar)(struct git_config_backend *, const char *key, const char *regexp);
+ int (*iterator)(git_config_iterator **, struct git_config_backend *);
int (*refresh)(struct git_config_backend *);
void (*free)(struct git_config_backend *);
};
diff --git a/include/git2/sys/filter.h b/include/git2/sys/filter.h
new file mode 100644
index 000000000..94ad3aed4
--- /dev/null
+++ b/include/git2/sys/filter.h
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_sys_git_filter_h__
+#define INCLUDE_sys_git_filter_h__
+
+#include "git2/filter.h"
+
+/**
+ * @file git2/sys/filter.h
+ * @brief Git filter backend and plugin routines
+ * @defgroup git_backend Git custom backend APIs
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Look up a filter by name
+ *
+ * @param name The name of the filter
+ * @return Pointer to the filter object or NULL if not found
+ */
+GIT_EXTERN(git_filter *) git_filter_lookup(const char *name);
+
+#define GIT_FILTER_CRLF "crlf"
+#define GIT_FILTER_IDENT "ident"
+
+/**
+ * This is priority that the internal CRLF filter will be registered with
+ */
+#define GIT_FILTER_CRLF_PRIORITY 0
+
+/**
+ * This is priority that the internal ident filter will be registered with
+ */
+#define GIT_FILTER_IDENT_PRIORITY 100
+
+/**
+ * This is priority to use with a custom filter to imitate a core Git
+ * filter driver, so that it will be run last on checkout and first on
+ * checkin. You do not have to use this, but it helps compatibility.
+ */
+#define GIT_FILTER_DRIVER_PRIORITY 200
+
+/**
+ * Create a new empty filter list
+ *
+ * Normally you won't use this because `git_filter_list_load` will create
+ * the filter list for you, but you can use this in combination with the
+ * `git_filter_lookup` and `git_filter_list_push` functions to assemble
+ * your own chains of filters.
+ */
+GIT_EXTERN(int) git_filter_list_new(
+ git_filter_list **out, git_repository *repo, git_filter_mode_t mode);
+
+/**
+ * Add a filter to a filter list with the given payload.
+ *
+ * Normally you won't have to do this because the filter list is created
+ * by calling the "check" function on registered filters when the filter
+ * attributes are set, but this does allow more direct manipulation of
+ * filter lists when desired.
+ *
+ * Note that normally the "check" function can set up a payload for the
+ * filter. Using this function, you can either pass in a payload if you
+ * know the expected payload format, or you can pass NULL. Some filters
+ * may fail with a NULL payload. Good luck!
+ */
+GIT_EXTERN(int) git_filter_list_push(
+ git_filter_list *fl, git_filter *filter, void *payload);
+
+/**
+ * Look up how many filters are in the list
+ *
+ * We will attempt to apply all of these filters to any data passed in,
+ * but note that the filter apply action still has the option of skipping
+ * data that is passed in (for example, the CRLF filter will skip data
+ * that appears to be binary).
+ *
+ * @param fl A filter list
+ * @return The number of filters in the list
+ */
+GIT_EXTERN(size_t) git_filter_list_length(const git_filter_list *fl);
+
+/**
+ * A filter source represents a file/blob to be processed
+ */
+typedef struct git_filter_source git_filter_source;
+
+/**
+ * Get the repository that the source data is coming from.
+ */
+GIT_EXTERN(git_repository *) git_filter_source_repo(const git_filter_source *src);
+
+/**
+ * Get the path that the source data is coming from.
+ */
+GIT_EXTERN(const char *) git_filter_source_path(const git_filter_source *src);
+
+/**
+ * Get the file mode of the source file
+ * If the mode is unknown, this will return 0
+ */
+GIT_EXTERN(uint16_t) git_filter_source_filemode(const git_filter_source *src);
+
+/**
+ * Get the OID of the source
+ * If the OID is unknown (often the case with GIT_FILTER_CLEAN) then
+ * this will return NULL.
+ */
+GIT_EXTERN(const git_oid *) git_filter_source_id(const git_filter_source *src);
+
+/**
+ * Get the git_filter_mode_t to be applied
+ */
+GIT_EXTERN(git_filter_mode_t) git_filter_source_mode(const git_filter_source *src);
+
+/*
+ * struct git_filter
+ *
+ * The filter lifecycle:
+ * - initialize - first use of filter
+ * - shutdown - filter removed/unregistered from system
+ * - check - considering filter for file
+ * - apply - apply filter to file contents
+ * - cleanup - done with file
+ */
+
+/**
+ * Initialize callback on filter
+ *
+ * Specified as `filter.initialize`, this is an optional callback invoked
+ * before a filter is first used. It will be called once at most.
+ *
+ * If non-NULL, the filter's `initialize` callback will be invoked right
+ * before the first use of the filter, so you can defer expensive
+ * initialization operations (in case libgit2 is being used in a way that
+ * doesn't need the filter).
+ */
+typedef int (*git_filter_init_fn)(git_filter *self);
+
+/**
+ * Shutdown callback on filter
+ *
+ * Specified as `filter.shutdown`, this is an optional callback invoked
+ * when the filter is unregistered or when libgit2 is shutting down. It
+ * will be called once at most and should release resources as needed.
+ *
+ * Typically this function will free the `git_filter` object itself.
+ */
+typedef void (*git_filter_shutdown_fn)(git_filter *self);
+
+/**
+ * Callback to decide if a given source needs this filter
+ *
+ * Specified as `filter.check`, this is an optional callback that checks
+ * if filtering is needed for a given source.
+ *
+ * It should return 0 if the filter should be applied (i.e. success),
+ * GIT_PASSTHROUGH if the filter should not be applied, or an error code
+ * to fail out of the filter processing pipeline and return to the caller.
+ *
+ * The `attr_values` will be set to the values of any attributes given in
+ * the filter definition. See `git_filter` below for more detail.
+ *
+ * The `payload` will be a pointer to a reference payload for the filter.
+ * This will start as NULL, but `check` can assign to this pointer for
+ * later use by the `apply` callback. Note that the value should be heap
+ * allocated (not stack), so that it doesn't go away before the `apply`
+ * callback can use it. If a filter allocates and assigns a value to the
+ * `payload`, it will need a `cleanup` callback to free the payload.
+ */
+typedef int (*git_filter_check_fn)(
+ git_filter *self,
+ void **payload, /* points to NULL ptr on entry, may be set */
+ const git_filter_source *src,
+ const char **attr_values);
+
+/**
+ * Callback to actually perform the data filtering
+ *
+ * Specified as `filter.apply`, this is the callback that actually filters
+ * data. If it successfully writes the output, it should return 0. Like
+ * `check`, it can return GIT_PASSTHROUGH to indicate that the filter
+ * doesn't want to run. Other error codes will stop filter processing and
+ * return to the caller.
+ *
+ * The `payload` value will refer to any payload that was set by the
+ * `check` callback. It may be read from or written to as needed.
+ */
+typedef int (*git_filter_apply_fn)(
+ git_filter *self,
+ void **payload, /* may be read and/or set */
+ git_buf *to,
+ const git_buf *from,
+ const git_filter_source *src);
+
+/**
+ * Callback to clean up after filtering has been applied
+ *
+ * Specified as `filter.cleanup`, this is an optional callback invoked
+ * after the filter has been applied. If the `check` or `apply` callbacks
+ * allocated a `payload` to keep per-source filter state, use this
+ * callback to free that payload and release resources as required.
+ */
+typedef void (*git_filter_cleanup_fn)(
+ git_filter *self,
+ void *payload);
+
+/**
+ * Filter structure used to register custom filters.
+ *
+ * To associate extra data with a filter, allocate extra data and put the
+ * `git_filter` struct at the start of your data buffer, then cast the
+ * `self` pointer to your larger structure when your callback is invoked.
+ *
+ * `version` should be set to GIT_FILTER_VERSION
+ *
+ * `attributes` is a whitespace-separated list of attribute names to check
+ * for this filter (e.g. "eol crlf text"). If the attribute name is bare,
+ * it will be simply loaded and passed to the `check` callback. If it has
+ * a value (i.e. "name=value"), the attribute must match that value for
+ * the filter to be applied.
+ *
+ * The `initialize`, `shutdown`, `check`, `apply`, and `cleanup` callbacks
+ * are all documented above with the respective function pointer typedefs.
+ */
+struct git_filter {
+ unsigned int version;
+
+ const char *attributes;
+
+ git_filter_init_fn initialize;
+ git_filter_shutdown_fn shutdown;
+ git_filter_check_fn check;
+ git_filter_apply_fn apply;
+ git_filter_cleanup_fn cleanup;
+};
+
+#define GIT_FILTER_VERSION 1
+
+/**
+ * Register a filter under a given name with a given priority.
+ *
+ * As mentioned elsewhere, the initialize callback will not be invoked
+ * immediately. It is deferred until the filter is used in some way.
+ *
+ * A filter's attribute checks and `check` and `apply` callbacks will be
+ * issued in order of `priority` on smudge (to workdir), and in reverse
+ * order of `priority` on clean (to odb).
+ *
+ * Two filters are preregistered with libgit2:
+ * - GIT_FILTER_CRLF with priority 0
+ * - GIT_FILTER_IDENT with priority 100
+ *
+ * Currently the filter registry is not thread safe, so any registering or
+ * deregistering of filters must be done outside of any possible usage of
+ * the filters (i.e. during application setup or shutdown).
+ *
+ * @param name A name by which the filter can be referenced. Attempting
+ * to register with an in-use name will return GIT_EEXISTS.
+ * @param filter The filter definition. This pointer will be stored as is
+ * by libgit2 so it must be a durable allocation (either static
+ * or on the heap).
+ * @param priority The priority for filter application
+ * @return 0 on successful registry, error code <0 on failure
+ */
+GIT_EXTERN(int) git_filter_register(
+ const char *name, git_filter *filter, int priority);
+
+/**
+ * Remove the filter with the given name
+ *
+ * Attempting to remove the builtin libgit2 filters is not permitted and
+ * will return an error.
+ *
+ * Currently the filter registry is not thread safe, so any registering or
+ * deregistering of filters must be done outside of any possible usage of
+ * the filters (i.e. during application setup or shutdown).
+ *
+ * @param name The name under which the filter was registered
+ * @return 0 on success, error code <0 on failure
+ */
+GIT_EXTERN(int) git_filter_unregister(const char *name);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/sys/index.h b/include/git2/sys/index.h
index a32e07036..1a06a4df1 100644
--- a/include/git2/sys/index.h
+++ b/include/git2/sys/index.h
@@ -72,7 +72,6 @@ GIT_EXTERN(int) git_index_name_add(git_index *index,
* Remove all filename conflict entries.
*
* @param index an existing index object
- * @return 0 or an error code
*/
GIT_EXTERN(void) git_index_name_clear(git_index *index);
@@ -168,7 +167,6 @@ GIT_EXTERN(int) git_index_reuc_remove(git_index *index, size_t n);
* Remove all resolve undo entries from the index
*
* @param index an existing index object
- * @return 0 or an error code
*/
GIT_EXTERN(void) git_index_reuc_clear(git_index *index);
diff --git a/include/git2/sys/odb_backend.h b/include/git2/sys/odb_backend.h
index 3cd2734c0..8039a5b82 100644
--- a/include/git2/sys/odb_backend.h
+++ b/include/git2/sys/odb_backend.h
@@ -48,12 +48,12 @@ struct git_odb_backend {
int (* read_header)(
size_t *, git_otype *, git_odb_backend *, const git_oid *);
- /* The writer may assume that the object
- * has already been hashed and is passed
- * in the first parameter.
+ /**
+ * Write an object into the backend. The id of the object has
+ * already been calculated and is passed in.
*/
int (* write)(
- git_oid *, git_odb_backend *, const void *, size_t, git_otype);
+ git_odb_backend *, const git_oid *, const void *, size_t, git_otype);
int (* writestream)(
git_odb_stream **, git_odb_backend *, size_t, git_otype);
@@ -64,13 +64,23 @@ struct git_odb_backend {
int (* exists)(
git_odb_backend *, const git_oid *);
+ /**
+ * If the backend implements a refreshing mechanism, it should be exposed
+ * through this endpoint. Each call to `git_odb_refresh()` will invoke it.
+ *
+ * However, the backend implementation should try to stay up-to-date as much
+ * as possible by itself as libgit2 will not automatically invoke
+ * `git_odb_refresh()`. For instance, a potential strategy for the backend
+ * implementation to achieve this could be to internally invoke this
+ * endpoint on failed lookups (ie. `exists()`, `read()`, `read_header()`).
+ */
int (* refresh)(git_odb_backend *);
int (* foreach)(
git_odb_backend *, git_odb_foreach_cb cb, void *payload);
int (* writepack)(
- git_odb_writepack **, git_odb_backend *,
+ git_odb_writepack **, git_odb_backend *, git_odb *odb,
git_transfer_progress_callback progress_cb, void *progress_payload);
void (* free)(git_odb_backend *);
diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h
index 9b457b074..9cf5073fb 100644
--- a/include/git2/sys/refdb_backend.h
+++ b/include/git2/sys/refdb_backend.h
@@ -103,7 +103,7 @@ struct git_refdb_backend {
* Deletes the given reference from the refdb. A refdb implementation
* must provide this function.
*/
- int (*delete)(git_refdb_backend *backend, const char *ref_name);
+ int (*del)(git_refdb_backend *backend, const char *ref_name);
/**
* Suggests that the given refdb compress or optimize its references.
@@ -119,10 +119,30 @@ struct git_refdb_backend {
* provide this function; if it is not provided, nothing will be done.
*/
void (*free)(git_refdb_backend *backend);
+
+ /**
+ * Read the reflog for the given reference name.
+ */
+ int (*reflog_read)(git_reflog **out, git_refdb_backend *backend, const char *name);
+
+ /**
+ * Write a reflog to disk.
+ */
+ int (*reflog_write)(git_refdb_backend *backend, git_reflog *reflog);
+
+ /**
+ * Rename a reflog
+ */
+ int (*reflog_rename)(git_refdb_backend *_backend, const char *old_name, const char *new_name);
+
+ /**
+ * Remove a reflog.
+ */
+ int (*reflog_delete)(git_refdb_backend *backend, const char *name);
};
-#define GIT_ODB_BACKEND_VERSION 1
-#define GIT_ODB_BACKEND_INIT {GIT_ODB_BACKEND_VERSION}
+#define GIT_REFDB_BACKEND_VERSION 1
+#define GIT_REFDB_BACKEND_INIT {GIT_REFDB_BACKEND_VERSION}
/**
* Constructors for default filesystem-based refdb backend
diff --git a/include/git2/sys/reflog.h b/include/git2/sys/reflog.h
new file mode 100644
index 000000000..c9d0041b9
--- /dev/null
+++ b/include/git2/sys/reflog.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_sys_git_reflog_h__
+#define INCLUDE_sys_git_reflog_h__
+
+#include "git2/common.h"
+#include "git2/types.h"
+#include "git2/oid.h"
+
+GIT_BEGIN_DECL
+
+GIT_EXTERN(git_reflog_entry *) git_reflog_entry__alloc(void);
+GIT_EXTERN(void) git_reflog_entry__free(git_reflog_entry *entry);
+
+GIT_END_DECL
+
+#endif
diff --git a/include/git2/sys/refs.h b/include/git2/sys/refs.h
index 85963258c..dd95ca12c 100644
--- a/include/git2/sys/refs.h
+++ b/include/git2/sys/refs.h
@@ -16,7 +16,7 @@
*
* @param name the reference name
* @param oid the object id for a direct reference
- * @param symbolic the target for a symbolic reference
+ * @param peel the first non-tag object's OID, or NULL
* @return the created git_reference or NULL on error
*/
GIT_EXTERN(git_reference *) git_reference__alloc(
@@ -28,7 +28,7 @@ GIT_EXTERN(git_reference *) git_reference__alloc(
* Create a new symbolic reference.
*
* @param name the reference name
- * @param symbolic the target for a symbolic reference
+ * @param target the target for a symbolic reference
* @return the created git_reference or NULL on error
*/
GIT_EXTERN(git_reference *) git_reference__alloc_symbolic(
diff --git a/include/git2/sys/repository.h b/include/git2/sys/repository.h
index ba3d65ae5..36f8b5836 100644
--- a/include/git2/sys/repository.h
+++ b/include/git2/sys/repository.h
@@ -27,7 +27,6 @@ GIT_BEGIN_DECL
*/
GIT_EXTERN(int) git_repository_new(git_repository **out);
-
/**
* Reset all the internal state in a repository.
*
@@ -42,6 +41,25 @@ GIT_EXTERN(int) git_repository_new(git_repository **out);
GIT_EXTERN(void) git_repository__cleanup(git_repository *repo);
/**
+ * Update the filesystem config settings for an open repository
+ *
+ * When a repository is initialized, config values are set based on the
+ * properties of the filesystem that the repository is on, such as
+ * "core.ignorecase", "core.filemode", "core.symlinks", etc. If the
+ * repository is moved to a new filesystem, these properties may no
+ * longer be correct and API calls may not behave as expected. This
+ * call reruns the phase of repository initialization that sets those
+ * properties to compensate for the current filesystem of the repo.
+ *
+ * @param repo A repository object
+ * @param recurse_submodules Should submodules be updated recursively
+ * @returrn 0 on success, < 0 on error
+ */
+GIT_EXTERN(int) git_repository_reinit_filesystem(
+ git_repository *repo,
+ int recurse_submodules);
+
+/**
* Set the configuration file for this repository
*
* This configuration file will be used for all configuration
diff --git a/include/git2/transport.h b/include/git2/transport.h
index 81bb3abe1..caabd0465 100644
--- a/include/git2/transport.h
+++ b/include/git2/transport.h
@@ -28,22 +28,31 @@ GIT_BEGIN_DECL
*** Begin interface for credentials acquisition ***
*/
+/** Authentication type requested */
typedef enum {
/* git_cred_userpass_plaintext */
- GIT_CREDTYPE_USERPASS_PLAINTEXT = 1,
- GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE = 2,
- GIT_CREDTYPE_SSH_PUBLICKEY = 3,
+ GIT_CREDTYPE_USERPASS_PLAINTEXT = (1u << 0),
+
+ /* git_cred_ssh_key */
+ GIT_CREDTYPE_SSH_KEY = (1u << 1),
+
+ /* git_cred_ssh_custom */
+ GIT_CREDTYPE_SSH_CUSTOM = (1u << 2),
+
+ /* git_cred_default */
+ GIT_CREDTYPE_DEFAULT = (1u << 3),
} git_credtype_t;
/* The base structure for all credential types */
-typedef struct git_cred {
+typedef struct git_cred git_cred;
+
+struct git_cred {
git_credtype_t credtype;
- void (*free)(
- struct git_cred *cred);
-} git_cred;
+ void (*free)(git_cred *cred);
+};
-/* A plaintext username and password */
-typedef struct git_cred_userpass_plaintext {
+/** A plaintext username and password */
+typedef struct {
git_cred parent;
char *username;
char *password;
@@ -51,27 +60,46 @@ typedef struct git_cred_userpass_plaintext {
#ifdef GIT_SSH
typedef LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC((*git_cred_sign_callback));
+#else
+typedef int (*git_cred_sign_callback)(void *, ...);
+#endif
-/* A ssh key file and passphrase */
-typedef struct git_cred_ssh_keyfile_passphrase {
+/**
+ * A ssh key from disk
+ */
+typedef struct git_cred_ssh_key {
git_cred parent;
+ char *username;
char *publickey;
char *privatekey;
char *passphrase;
-} git_cred_ssh_keyfile_passphrase;
+} git_cred_ssh_key;
-/* A ssh public key and authentication callback */
-typedef struct git_cred_ssh_publickey {
+/**
+ * A key with a custom signature function
+ */
+typedef struct git_cred_ssh_custom {
git_cred parent;
+ char *username;
char *publickey;
- size_t publickey_len;
+ size_t publickey_len;
void *sign_callback;
void *sign_data;
-} git_cred_ssh_publickey;
-#endif
+} git_cred_ssh_custom;
+
+/** A key for NTLM/Kerberos "default" credentials */
+typedef struct git_cred git_cred_default;
+
+/**
+ * Check whether a credential object contains username information.
+ *
+ * @param cred object to check
+ * @return 1 if the credential object has non-NULL username, 0 otherwise
+ */
+GIT_EXTERN(int) git_cred_has_username(git_cred *cred);
/**
- * Creates a new plain-text username and password credential object.
+ * Create a new plain-text username and password credential object.
* The supplied credential parameter will be internally duplicated.
*
* @param out The newly created credential object.
@@ -84,52 +112,68 @@ GIT_EXTERN(int) git_cred_userpass_plaintext_new(
const char *username,
const char *password);
-#ifdef GIT_SSH
/**
- * Creates a new ssh key file and passphrase credential object.
+ * Create a new passphrase-protected ssh key credential object.
* The supplied credential parameter will be internally duplicated.
*
* @param out The newly created credential object.
+ * @param username username to use to authenticate
* @param publickey The path to the public key of the credential.
* @param privatekey The path to the private key of the credential.
* @param passphrase The passphrase of the credential.
* @return 0 for success or an error code for failure
*/
-GIT_EXTERN(int) git_cred_ssh_keyfile_passphrase_new(
+GIT_EXTERN(int) git_cred_ssh_key_new(
git_cred **out,
+ const char *username,
const char *publickey,
const char *privatekey,
- const char *passphrase);
+ const char *passphrase);
/**
- * Creates a new ssh public key credential object.
+ * Create an ssh key credential with a custom signing function.
+ *
+ * This lets you use your own function to sign the challenge.
+ *
+ * This function and its credential type is provided for completeness
+ * and wraps `libssh2_userauth_publickey()`, which is undocumented.
+ *
* The supplied credential parameter will be internally duplicated.
*
* @param out The newly created credential object.
+ * @param username username to use to authenticate
* @param publickey The bytes of the public key.
* @param publickey_len The length of the public key in bytes.
- * @param sign_callback The callback method for authenticating.
- * @param sign_data The abstract data sent to the sign_callback method.
+ * @param sign_fn The callback method to sign the data during the challenge.
+ * @param sign_data The data to pass to the sign function.
* @return 0 for success or an error code for failure
*/
-GIT_EXTERN(int) git_cred_ssh_publickey_new(
+GIT_EXTERN(int) git_cred_ssh_custom_new(
git_cred **out,
+ const char *username,
const char *publickey,
- size_t publickey_len,
- git_cred_sign_callback,
- void *sign_data);
-#endif
+ size_t publickey_len,
+ git_cred_sign_callback sign_fn,
+ void *sign_data);
+
+/**
+ * Create a "default" credential usable for Negotiate mechanisms like NTLM
+ * or Kerberos authentication.
+ *
+ * @return 0 for success or an error code for failure
+ */
+GIT_EXTERN(int) git_cred_default_new(git_cred **out);
/**
* Signature of a function which acquires a credential object.
*
- * @param cred The newly created credential object.
- * @param url The resource for which we are demanding a credential.
- * @param username_from_url The username that was embedded in a "user@host"
+ * - cred: The newly created credential object.
+ * - url: The resource for which we are demanding a credential.
+ * - username_from_url: The username that was embedded in a "user@host"
* remote url, or NULL if not included.
- * @param allowed_types A bitmask stating which cred types are OK to return.
- * @param payload The payload provided when specifying this callback.
- * @return 0 for success or an error code for failure
+ * - allowed_types: A bitmask stating which cred types are OK to return.
+ * - payload: The payload provided when specifying this callback.
+ * - returns 0 for success or non-zero to indicate an error
*/
typedef int (*git_cred_acquire_cb)(
git_cred **cred,
@@ -150,39 +194,45 @@ typedef enum {
GIT_TRANSPORTFLAGS_NO_CHECK_CERT = 1
} git_transport_flags_t;
-typedef void (*git_transport_message_cb)(const char *str, int len, void *data);
+typedef int (*git_transport_message_cb)(const char *str, int len, void *data);
+
+typedef struct git_transport git_transport;
-typedef struct git_transport {
+struct git_transport {
unsigned int version;
/* Set progress and error callbacks */
- int (*set_callbacks)(struct git_transport *transport,
+ int (*set_callbacks)(
+ git_transport *transport,
git_transport_message_cb progress_cb,
git_transport_message_cb error_cb,
void *payload);
/* Connect the transport to the remote repository, using the given
* direction. */
- int (*connect)(struct git_transport *transport,
+ int (*connect)(
+ git_transport *transport,
const char *url,
git_cred_acquire_cb cred_acquire_cb,
void *cred_acquire_payload,
int direction,
int flags);
- /* This function may be called after a successful call to connect(). The
- * provided callback is invoked for each ref discovered on the remote
- * end. */
- int (*ls)(struct git_transport *transport,
- git_headlist_cb list_cb,
- void *payload);
+ /* This function may be called after a successful call to
+ * connect(). The array returned is owned by the transport and
+ * is guranteed until the next call of a transport function. */
+ int (*ls)(
+ const git_remote_head ***out,
+ size_t *size,
+ git_transport *transport);
/* Executes the push whose context is in the git_push object. */
- int (*push)(struct git_transport *transport, git_push *push);
+ int (*push)(git_transport *transport, git_push *push);
/* This function may be called after a successful call to connect(), when
* the direction is FETCH. The function performs a negotiation to calculate
* the wants list for the fetch. */
- int (*negotiate_fetch)(struct git_transport *transport,
+ int (*negotiate_fetch)(
+ git_transport *transport,
git_repository *repo,
const git_remote_head * const *refs,
size_t count);
@@ -190,28 +240,29 @@ typedef struct git_transport {
/* This function may be called after a successful call to negotiate_fetch(),
* when the direction is FETCH. This function retrieves the pack file for
* the fetch from the remote end. */
- int (*download_pack)(struct git_transport *transport,
+ int (*download_pack)(
+ git_transport *transport,
git_repository *repo,
git_transfer_progress *stats,
git_transfer_progress_callback progress_cb,
void *progress_payload);
/* Checks to see if the transport is connected */
- int (*is_connected)(struct git_transport *transport);
+ int (*is_connected)(git_transport *transport);
/* Reads the flags value previously passed into connect() */
- int (*read_flags)(struct git_transport *transport, int *flags);
+ int (*read_flags)(git_transport *transport, int *flags);
/* Cancels any outstanding transport operation */
- void (*cancel)(struct git_transport *transport);
+ void (*cancel)(git_transport *transport);
/* This function is the reverse of connect() -- it terminates the
* connection to the remote end. */
- int (*close)(struct git_transport *transport);
+ int (*close)(git_transport *transport);
/* Frees/destructs the git_transport object. */
- void (*free)(struct git_transport *transport);
-} git_transport;
+ void (*free)(git_transport *transport);
+};
#define GIT_TRANSPORT_VERSION 1
#define GIT_TRANSPORT_INIT {GIT_TRANSPORT_VERSION}
@@ -231,6 +282,40 @@ GIT_EXTERN(int) git_transport_new(git_transport **out, git_remote *owner, const
/* Signature of a function which creates a transport */
typedef int (*git_transport_cb)(git_transport **out, git_remote *owner, void *param);
+/**
+ * Add a custom transport definition, to be used in addition to the built-in
+ * set of transports that come with libgit2.
+ *
+ * The caller is responsible for synchronizing calls to git_transport_register
+ * and git_transport_unregister with other calls to the library that
+ * instantiate transports.
+ *
+ * @param prefix The scheme (ending in "://") to match, i.e. "git://"
+ * @param priority The priority of this transport relative to others with
+ * the same prefix. Built-in transports have a priority of 1.
+ * @param cb The callback used to create an instance of the transport
+ * @param param A fixed parameter to pass to cb at creation time
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_transport_register(
+ const char *prefix,
+ unsigned priority,
+ git_transport_cb cb,
+ void *param);
+
+/**
+ *
+ * Unregister a custom transport definition which was previously registered
+ * with git_transport_register.
+ *
+ * @param prefix From the previous call to git_transport_register
+ * @param priority From the previous call to git_transport_register
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_transport_unregister(
+ const char *prefix,
+ unsigned priority);
+
/* Transports which come with libgit2 (match git_transport_cb). The expected
* value for "param" is listed in-line below. */
@@ -299,35 +384,36 @@ typedef enum {
GIT_SERVICE_RECEIVEPACK = 4,
} git_smart_service_t;
-struct git_smart_subtransport;
+typedef struct git_smart_subtransport git_smart_subtransport;
+typedef struct git_smart_subtransport_stream git_smart_subtransport_stream;
/* A stream used by the smart transport to read and write data
* from a subtransport */
-typedef struct git_smart_subtransport_stream {
+struct git_smart_subtransport_stream {
/* The owning subtransport */
- struct git_smart_subtransport *subtransport;
+ git_smart_subtransport *subtransport;
int (*read)(
- struct git_smart_subtransport_stream *stream,
- char *buffer,
- size_t buf_size,
- size_t *bytes_read);
+ git_smart_subtransport_stream *stream,
+ char *buffer,
+ size_t buf_size,
+ size_t *bytes_read);
int (*write)(
- struct git_smart_subtransport_stream *stream,
- const char *buffer,
- size_t len);
+ git_smart_subtransport_stream *stream,
+ const char *buffer,
+ size_t len);
void (*free)(
- struct git_smart_subtransport_stream *stream);
-} git_smart_subtransport_stream;
+ git_smart_subtransport_stream *stream);
+};
/* An implementation of a subtransport which carries data for the
* smart transport */
-typedef struct git_smart_subtransport {
+struct git_smart_subtransport {
int (* action)(
git_smart_subtransport_stream **out,
- struct git_smart_subtransport *transport,
+ git_smart_subtransport *transport,
const char *url,
git_smart_service_t action);
@@ -337,10 +423,10 @@ typedef struct git_smart_subtransport {
*
* 1. UPLOADPACK_LS -> UPLOADPACK
* 2. RECEIVEPACK_LS -> RECEIVEPACK */
- int (* close)(struct git_smart_subtransport *transport);
+ int (*close)(git_smart_subtransport *transport);
- void (* free)(struct git_smart_subtransport *transport);
-} git_smart_subtransport;
+ void (*free)(git_smart_subtransport *transport);
+};
/* A function which creates a new subtransport for the smart transport */
typedef int (*git_smart_subtransport_cb)(
diff --git a/include/git2/tree.h b/include/git2/tree.h
index 65d8cc16e..d94b446c2 100644
--- a/include/git2/tree.h
+++ b/include/git2/tree.h
@@ -199,6 +199,17 @@ GIT_EXTERN(git_otype) git_tree_entry_type(const git_tree_entry *entry);
GIT_EXTERN(git_filemode_t) git_tree_entry_filemode(const git_tree_entry *entry);
/**
+ * Get the raw UNIX file attributes of a tree entry
+ *
+ * This function does not perform any normalization and is only useful
+ * if you need to be able to recreate the original tree object.
+ *
+ * @param entry a tree entry
+ * @return filemode as an integer
+ */
+
+GIT_EXTERN(git_filemode_t) git_tree_entry_filemode_raw(const git_tree_entry *entry);
+/**
* Compare two tree entries
*
* @param e1 first tree entry
@@ -208,7 +219,7 @@ GIT_EXTERN(git_filemode_t) git_tree_entry_filemode(const git_tree_entry *entry);
GIT_EXTERN(int) git_tree_entry_cmp(const git_tree_entry *e1, const git_tree_entry *e2);
/**
- * Convert a tree entry to the git_object it points too.
+ * Convert a tree entry to the git_object it points to.
*
* You must call `git_object_free()` on the object when you are done with it.
*
diff --git a/include/git2/types.h b/include/git2/types.h
index dc344075c..55505b110 100644
--- a/include/git2/types.h
+++ b/include/git2/types.h
@@ -174,6 +174,9 @@ typedef struct git_reference_iterator git_reference_iterator;
/** Merge heads, the input to merge */
typedef struct git_merge_head git_merge_head;
+/** Merge result */
+typedef struct git_merge_result git_merge_result;
+
/** Representation of a status collection */
typedef struct git_status_list git_status_list;
@@ -212,11 +215,21 @@ typedef struct git_remote_callbacks git_remote_callbacks;
/**
* This is passed as the first argument to the callback to allow the
* user to see the progress.
+ *
+ * - total_objects: number of objects in the packfile being downloaded
+ * - indexed_objects: received objects that have been hashed
+ * - received_objects: objects which have been downloaded
+ * - local_objects: locally-available objects that have been injected
+ * in order to fix a thin pack.
+ * - received-bytes: size of the packfile received up to now
*/
typedef struct git_transfer_progress {
unsigned int total_objects;
unsigned int indexed_objects;
unsigned int received_objects;
+ unsigned int local_objects;
+ unsigned int total_deltas;
+ unsigned int indexed_deltas;
size_t received_bytes;
} git_transfer_progress;
@@ -229,6 +242,87 @@ typedef struct git_transfer_progress {
*/
typedef int (*git_transfer_progress_callback)(const git_transfer_progress *stats, void *payload);
+/**
+ * Opaque structure representing a submodule.
+ */
+typedef struct git_submodule git_submodule;
+
+/**
+ * Submodule update values
+ *
+ * These values represent settings for the `submodule.$name.update`
+ * configuration value which says how to handle `git submodule update` for
+ * this submodule. The value is usually set in the ".gitmodules" file and
+ * copied to ".git/config" when the submodule is initialized.
+ *
+ * You can override this setting on a per-submodule basis with
+ * `git_submodule_set_update()` and write the changed value to disk using
+ * `git_submodule_save()`. If you have overwritten the value, you can
+ * revert it by passing `GIT_SUBMODULE_UPDATE_RESET` to the set function.
+ *
+ * The values are:
+ *
+ * - GIT_SUBMODULE_UPDATE_RESET: reset to the on-disk value.
+ * - GIT_SUBMODULE_UPDATE_CHECKOUT: the default; when a submodule is
+ * updated, checkout the new detached HEAD to the submodule directory.
+ * - GIT_SUBMODULE_UPDATE_REBASE: update by rebasing the current checked
+ * out branch onto the commit from the superproject.
+ * - GIT_SUBMODULE_UPDATE_MERGE: update by merging the commit in the
+ * superproject into the current checkout out branch of the submodule.
+ * - GIT_SUBMODULE_UPDATE_NONE: do not update this submodule even when
+ * the commit in the superproject is updated.
+ * - GIT_SUBMODULE_UPDATE_DEFAULT: not used except as static initializer
+ * when we don't want any particular update rule to be specified.
+ */
+typedef enum {
+ GIT_SUBMODULE_UPDATE_RESET = -1,
+
+ GIT_SUBMODULE_UPDATE_CHECKOUT = 1,
+ GIT_SUBMODULE_UPDATE_REBASE = 2,
+ GIT_SUBMODULE_UPDATE_MERGE = 3,
+ GIT_SUBMODULE_UPDATE_NONE = 4,
+
+ GIT_SUBMODULE_UPDATE_DEFAULT = 0
+} git_submodule_update_t;
+
+/**
+ * Submodule ignore values
+ *
+ * These values represent settings for the `submodule.$name.ignore`
+ * configuration value which says how deeply to look at the working
+ * directory when getting submodule status.
+ *
+ * You can override this value in memory on a per-submodule basis with
+ * `git_submodule_set_ignore()` and can write the changed value to disk
+ * with `git_submodule_save()`. If you have overwritten the value, you
+ * can revert to the on disk value by using `GIT_SUBMODULE_IGNORE_RESET`.
+ *
+ * The values are:
+ *
+ * - GIT_SUBMODULE_IGNORE_RESET: reset to the on-disk value.
+ * - GIT_SUBMODULE_IGNORE_NONE: don't ignore any change - i.e. even an
+ * untracked file, will mark the submodule as dirty. Ignored files are
+ * still ignored, of course.
+ * - GIT_SUBMODULE_IGNORE_UNTRACKED: ignore untracked files; only changes
+ * to tracked files, or the index or the HEAD commit will matter.
+ * - GIT_SUBMODULE_IGNORE_DIRTY: ignore changes in the working directory,
+ * only considering changes if the HEAD of submodule has moved from the
+ * value in the superproject.
+ * - GIT_SUBMODULE_IGNORE_ALL: never check if the submodule is dirty
+ * - GIT_SUBMODULE_IGNORE_DEFAULT: not used except as static initializer
+ * when we don't want any particular ignore rule to be specified.
+ */
+typedef enum {
+ GIT_SUBMODULE_IGNORE_RESET = -1, /* reset to on-disk value */
+
+ GIT_SUBMODULE_IGNORE_NONE = 1, /* any change or untracked == dirty */
+ GIT_SUBMODULE_IGNORE_UNTRACKED = 2, /* dirty if tracked files change */
+ GIT_SUBMODULE_IGNORE_DIRTY = 3, /* only dirty if HEAD moved */
+ GIT_SUBMODULE_IGNORE_ALL = 4, /* never dirty */
+
+ GIT_SUBMODULE_IGNORE_DEFAULT = 0
+} git_submodule_ignore_t;
+
/** @} */
GIT_END_DECL
diff --git a/include/git2/version.h b/include/git2/version.h
index d8a915fac..c4c5e8eb1 100644
--- a/include/git2/version.h
+++ b/include/git2/version.h
@@ -7,9 +7,9 @@
#ifndef INCLUDE_git_version_h__
#define INCLUDE_git_version_h__
-#define LIBGIT2_VERSION "0.19.0"
+#define LIBGIT2_VERSION "0.20.0"
#define LIBGIT2_VER_MAJOR 0
-#define LIBGIT2_VER_MINOR 19
+#define LIBGIT2_VER_MINOR 20
#define LIBGIT2_VER_REVISION 0
#endif
diff --git a/libgit2.pc.in b/libgit2.pc.in
index 52ad901f7..8f5279234 100644
--- a/libgit2.pc.in
+++ b/libgit2.pc.in
@@ -4,6 +4,7 @@ includedir=@CMAKE_INSTALL_PREFIX@/@INCLUDE_INSTALL_DIR@
Name: libgit2
Description: The git library, take 2
Version: @LIBGIT2_VERSION_STRING@
-Requires: libcrypto
-Libs: -L${libdir} -lgit2 -lz -lcrypto
+Requires.private: @LIBGIT2_PC_REQUIRES@
+Libs.private: @LIBGIT2_PC_LIBS@
+Libs: -L${libdir} -lgit2
Cflags: -I${includedir}
diff --git a/packaging/rpm/README b/packaging/rpm/README
deleted file mode 100644
index 1a6410b16..000000000
--- a/packaging/rpm/README
+++ /dev/null
@@ -1,6 +0,0 @@
-To build RPM pakcages for Fedora, follow these steps:
- cp packaging/rpm/libgit2.spec ~/rpmbuild/SPECS
- cd ~/rpmbuild/SOURCES
- wget https://github.com/downloads/libgit2/libgit2/libgit2-0.16.0.tar.gz
- cd ~/rpmbuild/SPECS
- rpmbuild -ba libgit2.spec
diff --git a/packaging/rpm/libgit2.spec b/packaging/rpm/libgit2.spec
deleted file mode 100644
index 80e70c164..000000000
--- a/packaging/rpm/libgit2.spec
+++ /dev/null
@@ -1,106 +0,0 @@
-#
-# spec file for package libgit2
-#
-# Copyright (c) 2012 Saleem Ansari <tuxdna@gmail.com>
-# Copyright (c) 2012 SUSE LINUX Products GmbH, Nuernberg, Germany.
-# Copyright (c) 2011, Sascha Peilicke <saschpe@gmx.de>
-#
-# All modifications and additions to the file contributed by third parties
-# remain the property of their copyright owners, unless otherwise agreed
-# upon. The license for this file, and modifications and additions to the
-# file, is the same license as for the pristine package itself (unless the
-# license for the pristine package is not an Open Source License, in which
-# case the license is the MIT License). An "Open Source License" is a
-# license that conforms to the Open Source Definition (Version 1.9)
-# published by the Open Source Initiative.
-
-# Please submit bugfixes or comments via http://bugs.opensuse.org/
-#
-Name: libgit2
-Version: 0.16.0
-Release: 1
-Summary: C git library
-License: GPL-2.0 with linking
-Group: Development/Libraries/C and C++
-Url: http://libgit2.github.com/
-Source0: https://github.com/downloads/libgit2/libgit2/libgit2-0.16.0.tar.gz
-BuildRequires: cmake
-BuildRequires: pkgconfig
-BuildRoot: %{_tmppath}/%{name}-%{version}-build
-%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos_version}
-BuildRequires: openssl-devel
-%else
-BuildRequires: libopenssl-devel
-%endif
-
-%description
-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.
-
-%package -n %{name}-0
-Summary: C git library
-Group: System/Libraries
-
-%description -n %{name}-0
-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.
-
-%package devel
-Summary: C git library
-Group: Development/Libraries/C and C++
-Requires: %{name}-0 >= %{version}
-
-%description devel
-This package contains all necessary include files and libraries needed
-to compile and develop applications that use libgit2.
-
-%prep
-%setup -q
-
-%build
-cmake . \
- -DCMAKE_C_FLAGS:STRING="%{optflags}" \
- -DCMAKE_INSTALL_PREFIX:PATH=%{_prefix} \
- -DLIB_INSTALL_DIR:PATH=%{_libdir}S
-make %{?_smp_mflags}
-
-%install
-%make_install
-
-%post -n %{name}-0 -p /sbin/ldconfig
-%postun -n %{name}-0 -p /sbin/ldconfig
-
-%files -n %{name}-0
-%defattr (-,root,root)
-%doc AUTHORS COPYING README.md
-%{_libdir}/%{name}.so.*
-
-%files devel
-%defattr (-,root,root)
-%doc CONVENTIONS examples
-%{_libdir}/%{name}.so
-%{_includedir}/git2*
-%{_libdir}/pkgconfig/libgit2.pc
-
-%changelog
-* Tue Mar 04 2012 tuxdna@gmail.com
-- Update to version 0.16.0
-* Tue Jan 31 2012 jengelh@medozas.de
-- Provide pkgconfig symbols
-* Thu Oct 27 2011 saschpe@suse.de
-- Change license to 'GPL-2.0 with linking', fixes bnc#726789
-* Wed Oct 26 2011 saschpe@suse.de
-- Update to version 0.15.0:
- * Upstream doesn't provide changes
-- Removed outdated %%clean section
-* Tue Jan 18 2011 saschpe@gmx.de
-- Proper Requires for devel package
-* Tue Jan 18 2011 saschpe@gmx.de
-- Set BuildRequires to "openssl-devel" also for RHEL and CentOS
-* Tue Jan 18 2011 saschpe@gmx.de
-- Initial commit (0.0.1)
-- Added patch to fix shared library soname
diff --git a/script/cibuild.sh b/script/cibuild.sh
new file mode 100755
index 000000000..aa4fa47aa
--- /dev/null
+++ b/script/cibuild.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+# Create a test repo which we can use for the online::push tests
+mkdir $HOME/_temp
+git init --bare $HOME/_temp/test.git
+git daemon --listen=localhost --export-all --enable=receive-pack --base-path=$HOME/_temp $HOME/_temp 2>/dev/null &
+export GITTEST_REMOTE_URL="git://localhost/test.git"
+
+mkdir _build
+cd _build
+cmake .. -DCMAKE_INSTALL_PREFIX=../_install $OPTIONS || exit $?
+cmake --build . --target install || exit $?
+ctest -V . || exit $?
+
+# Now that we've tested the raw git protocol, let's set up ssh to we
+# can do the push tests over it
+
+killall git-daemon
+sudo start ssh
+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
+
+export GITTEST_REMOTE_URL="ssh://localhost/$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=""
+
+if [ -e ./libgit2_clar ]; then
+ ./libgit2_clar -sonline::push
+fi
diff --git a/src/array.h b/src/array.h
index 2d77c71a0..1d4e1c224 100644
--- a/src/array.h
+++ b/src/array.h
@@ -30,37 +30,45 @@
#define git_array_init(a) \
do { (a).size = (a).asize = 0; (a).ptr = NULL; } while (0)
+#define git_array_init_to_size(a, desired) \
+ do { (a).size = 0; (a).asize = desired; (a).ptr = git__calloc(desired, sizeof(*(a).ptr)); } while (0)
+
#define git_array_clear(a) \
do { git__free((a).ptr); git_array_init(a); } while (0)
#define GITERR_CHECK_ARRAY(a) GITERR_CHECK_ALLOC((a).ptr)
-typedef git_array_t(void) git_array_generic_t;
+typedef git_array_t(char) git_array_generic_t;
/* use a generic array for growth so this can return the new item */
-GIT_INLINE(void *) git_array_grow(git_array_generic_t *a, size_t item_size)
+GIT_INLINE(void *) git_array_grow(void *_a, size_t item_size)
{
+ git_array_generic_t *a = _a;
uint32_t new_size = (a->size < 8) ? 8 : a->asize * 3 / 2;
- void *new_array = git__realloc(a->ptr, new_size * item_size);
+ char *new_array = git__realloc(a->ptr, new_size * item_size);
if (!new_array) {
git_array_clear(*a);
return NULL;
} else {
a->ptr = new_array; a->asize = new_size; a->size++;
- return (((char *)a->ptr) + (a->size - 1) * item_size);
+ return a->ptr + (a->size - 1) * item_size;
}
}
#define git_array_alloc(a) \
- ((a).size >= (a).asize) ? \
- git_array_grow((git_array_generic_t *)&(a), sizeof(*(a).ptr)) : \
- (a).ptr ? &(a).ptr[(a).size++] : NULL
+ (((a).size >= (a).asize) ? \
+ git_array_grow(&(a), sizeof(*(a).ptr)) : \
+ ((a).ptr ? &(a).ptr[(a).size++] : NULL))
#define git_array_last(a) ((a).size ? &(a).ptr[(a).size - 1] : NULL)
+#define git_array_pop(a) ((a).size ? &(a).ptr[--(a).size] : NULL)
+
#define git_array_get(a, i) (((i) < (a).size) ? &(a).ptr[(i)] : NULL)
#define git_array_size(a) (a).size
+#define git_array_valid_index(a, i) ((i) < (a).size)
+
#endif
diff --git a/src/attr.c b/src/attr.c
index 6cdff29f9..98a328a55 100644
--- a/src/attr.c
+++ b/src/attr.c
@@ -1,3 +1,4 @@
+#include "common.h"
#include "repository.h"
#include "fileops.h"
#include "config.h"
@@ -26,7 +27,6 @@ git_attr_t git_attr_value(const char *attr)
return GIT_ATTR_VALUE_T;
}
-
static int collect_attr_files(
git_repository *repo,
uint32_t flags,
@@ -103,8 +103,6 @@ int git_attr_get_many(
attr_get_many_info *info = NULL;
size_t num_found = 0;
- memset((void *)values, 0, sizeof(const char *) * num_attr);
-
if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0)
return -1;
@@ -141,6 +139,11 @@ int git_attr_get_many(
}
}
+ for (k = 0; k < num_attr; k++) {
+ if (!info[k].found)
+ values[k] = NULL;
+ }
+
cleanup:
git_vector_free(&files);
git_attr_path__free(&path);
diff --git a/src/attr_file.c b/src/attr_file.c
index d880398e8..4eb732436 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -39,7 +39,7 @@ int git_attr_file__new(
attrs->key = git_pool_malloc(attrs->pool, (uint32_t)len + 3);
GITERR_CHECK_ALLOC(attrs->key);
- attrs->key[0] = '0' + from;
+ attrs->key[0] = '0' + (char)from;
attrs->key[1] = '#';
memcpy(&attrs->key[2], path, len);
attrs->key[len + 2] = '\0';
@@ -79,9 +79,13 @@ int git_attr_file__parse_buffer(
while (!error && *scan) {
/* allocate rule if needed */
- if (!rule && !(rule = git__calloc(1, sizeof(git_attr_rule)))) {
- error = -1;
- break;
+ if (!rule) {
+ if (!(rule = git__calloc(1, sizeof(git_attr_rule)))) {
+ error = -1;
+ break;
+ }
+ rule->match.flags = GIT_ATTR_FNMATCH_ALLOWNEG |
+ GIT_ATTR_FNMATCH_ALLOWMACRO;
}
/* parse the next "pattern attr attr attr" line */
@@ -351,8 +355,8 @@ int git_attr_fnmatch__parse(
if (parse_optimized_patterns(spec, pool, *base))
return 0;
- spec->flags = (spec->flags & GIT_ATTR_FNMATCH_ALLOWSPACE);
- allow_space = (spec->flags != 0);
+ spec->flags = (spec->flags & GIT_ATTR_FNMATCH__INCOMING);
+ allow_space = ((spec->flags & GIT_ATTR_FNMATCH_ALLOWSPACE) != 0);
pattern = *base;
@@ -362,7 +366,7 @@ int git_attr_fnmatch__parse(
return GIT_ENOTFOUND;
}
- if (*pattern == '[') {
+ if (*pattern == '[' && (spec->flags & GIT_ATTR_FNMATCH_ALLOWMACRO) != 0) {
if (strncmp(pattern, "[attr]", 6) == 0) {
spec->flags = spec->flags | GIT_ATTR_FNMATCH_MACRO;
pattern += 6;
@@ -370,7 +374,7 @@ int git_attr_fnmatch__parse(
/* else a character range like [a-e]* which is accepted */
}
- if (*pattern == '!') {
+ if (*pattern == '!' && (spec->flags & GIT_ATTR_FNMATCH_ALLOWNEG) != 0) {
spec->flags = spec->flags | GIT_ATTR_FNMATCH_NEGATIVE;
pattern++;
}
diff --git a/src/attr_file.h b/src/attr_file.h
index 15bba1c6a..3bc7c6cb8 100644
--- a/src/attr_file.h
+++ b/src/attr_file.h
@@ -28,6 +28,12 @@
#define GIT_ATTR_FNMATCH_ALLOWSPACE (1U << 6)
#define GIT_ATTR_FNMATCH_ICASE (1U << 7)
#define GIT_ATTR_FNMATCH_MATCH_ALL (1U << 8)
+#define GIT_ATTR_FNMATCH_ALLOWNEG (1U << 9)
+#define GIT_ATTR_FNMATCH_ALLOWMACRO (1U << 10)
+
+#define GIT_ATTR_FNMATCH__INCOMING \
+ (GIT_ATTR_FNMATCH_ALLOWSPACE | \
+ GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_ALLOWMACRO)
extern const char *git_attr__true;
extern const char *git_attr__false;
diff --git a/src/bitvec.h b/src/bitvec.h
new file mode 100644
index 000000000..fd6f0ccf8
--- /dev/null
+++ b/src/bitvec.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_bitvec_h__
+#define INCLUDE_bitvec_h__
+
+#include "util.h"
+
+/*
+ * This is a silly little fixed length bit vector type that will store
+ * vectors of 64 bits or less directly in the structure and allocate
+ * memory for vectors longer than 64 bits. You can use the two versions
+ * transparently through the API and avoid heap allocation completely when
+ * using a short bit vector as a result.
+ */
+typedef struct {
+ size_t length;
+ union {
+ uint64_t *words;
+ uint64_t bits;
+ } u;
+} git_bitvec;
+
+GIT_INLINE(int) git_bitvec_init(git_bitvec *bv, size_t capacity)
+{
+ memset(bv, 0x0, sizeof(*bv));
+
+ if (capacity >= 64) {
+ bv->length = (capacity / 64) + 1;
+ bv->u.words = git__calloc(bv->length, sizeof(uint64_t));
+ if (!bv->u.words)
+ return -1;
+ }
+
+ return 0;
+}
+
+#define GIT_BITVEC_MASK(BIT) ((uint64_t)1 << (BIT % 64))
+#define GIT_BITVEC_WORD(BV, BIT) (BV->length ? &BV->u.words[BIT / 64] : &BV->u.bits)
+
+GIT_INLINE(void) git_bitvec_set(git_bitvec *bv, size_t bit, bool on)
+{
+ uint64_t *word = GIT_BITVEC_WORD(bv, bit);
+ uint64_t mask = GIT_BITVEC_MASK(bit);
+
+ if (on)
+ *word |= mask;
+ else
+ *word &= ~mask;
+}
+
+GIT_INLINE(bool) git_bitvec_get(git_bitvec *bv, size_t bit)
+{
+ uint64_t *word = GIT_BITVEC_WORD(bv, bit);
+ return (*word & GIT_BITVEC_MASK(bit)) != 0;
+}
+
+GIT_INLINE(void) git_bitvec_clear(git_bitvec *bv)
+{
+ if (!bv->length)
+ bv->u.bits = 0;
+ else
+ memset(bv->u.words, 0x0, bv->length * sizeof(uint64_t));
+}
+
+GIT_INLINE(void) git_bitvec_free(git_bitvec *bv)
+{
+ if (bv->length)
+ git__free(bv->u.words);
+}
+
+#endif
diff --git a/src/blame.c b/src/blame.c
new file mode 100644
index 000000000..219a6bfe4
--- /dev/null
+++ b/src/blame.c
@@ -0,0 +1,476 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "blame.h"
+#include "git2/commit.h"
+#include "git2/revparse.h"
+#include "git2/revwalk.h"
+#include "git2/tree.h"
+#include "git2/diff.h"
+#include "git2/blob.h"
+#include "git2/signature.h"
+#include "util.h"
+#include "repository.h"
+#include "blame_git.h"
+
+
+static int hunk_byfinalline_search_cmp(const void *key, const void *entry)
+{
+ uint16_t lineno = (uint16_t)*(size_t*)key;
+ git_blame_hunk *hunk = (git_blame_hunk*)entry;
+
+ if (lineno < hunk->final_start_line_number)
+ return -1;
+ if (lineno >= hunk->final_start_line_number + hunk->lines_in_hunk)
+ return 1;
+ return 0;
+}
+
+static int paths_cmp(const void *a, const void *b) { return git__strcmp((char*)a, (char*)b); }
+static int hunk_cmp(const void *_a, const void *_b)
+{
+ git_blame_hunk *a = (git_blame_hunk*)_a,
+ *b = (git_blame_hunk*)_b;
+
+ return a->final_start_line_number - b->final_start_line_number;
+}
+
+static bool hunk_ends_at_or_before_line(git_blame_hunk *hunk, size_t line)
+{
+ return line >= (size_t)(hunk->final_start_line_number + hunk->lines_in_hunk - 1);
+}
+
+static bool hunk_starts_at_or_after_line(git_blame_hunk *hunk, size_t line)
+{
+ return line <= hunk->final_start_line_number;
+}
+
+static git_blame_hunk* new_hunk(
+ uint16_t start,
+ uint16_t lines,
+ uint16_t orig_start,
+ const char *path)
+{
+ git_blame_hunk *hunk = git__calloc(1, sizeof(git_blame_hunk));
+ if (!hunk) return NULL;
+
+ hunk->lines_in_hunk = lines;
+ hunk->final_start_line_number = start;
+ hunk->orig_start_line_number = orig_start;
+ hunk->orig_path = path ? git__strdup(path) : NULL;
+
+ return hunk;
+}
+
+static git_blame_hunk* dup_hunk(git_blame_hunk *hunk)
+{
+ git_blame_hunk *newhunk = new_hunk(
+ hunk->final_start_line_number,
+ hunk->lines_in_hunk,
+ hunk->orig_start_line_number,
+ hunk->orig_path);
+ git_oid_cpy(&newhunk->orig_commit_id, &hunk->orig_commit_id);
+ git_oid_cpy(&newhunk->final_commit_id, &hunk->final_commit_id);
+ newhunk->boundary = hunk->boundary;
+ newhunk->final_signature = git_signature_dup(hunk->final_signature);
+ newhunk->orig_signature = git_signature_dup(hunk->orig_signature);
+ return newhunk;
+}
+
+static void free_hunk(git_blame_hunk *hunk)
+{
+ git__free((void*)hunk->orig_path);
+ git_signature_free(hunk->final_signature);
+ git_signature_free(hunk->orig_signature);
+ git__free(hunk);
+}
+
+/* Starting with the hunk that includes start_line, shift all following hunks'
+ * final_start_line by shift_by lines */
+static void shift_hunks_by(git_vector *v, size_t start_line, int shift_by)
+{
+ size_t i;
+
+ if (!git_vector_bsearch2( &i, v, hunk_byfinalline_search_cmp, &start_line)) {
+ for (; i < v->length; i++) {
+ git_blame_hunk *hunk = (git_blame_hunk*)v->contents[i];
+ hunk->final_start_line_number += shift_by;
+ }
+ }
+}
+
+git_blame* git_blame__alloc(
+ git_repository *repo,
+ git_blame_options opts,
+ const char *path)
+{
+ git_blame *gbr = (git_blame*)git__calloc(1, sizeof(git_blame));
+ if (!gbr) {
+ giterr_set_oom();
+ return NULL;
+ }
+ git_vector_init(&gbr->hunks, 8, hunk_cmp);
+ git_vector_init(&gbr->paths, 8, paths_cmp);
+ gbr->repository = repo;
+ gbr->options = opts;
+ gbr->path = git__strdup(path);
+ git_vector_insert(&gbr->paths, git__strdup(path));
+ return gbr;
+}
+
+void git_blame_free(git_blame *blame)
+{
+ size_t i;
+ git_blame_hunk *hunk;
+ char *path;
+
+ if (!blame) return;
+
+ git_vector_foreach(&blame->hunks, i, hunk)
+ free_hunk(hunk);
+ git_vector_free(&blame->hunks);
+
+ git_vector_foreach(&blame->paths, i, path)
+ git__free(path);
+ git_vector_free(&blame->paths);
+
+ git_array_clear(blame->line_index);
+
+ git__free((void*)blame->path);
+ git_blob_free(blame->final_blob);
+ git__free(blame);
+}
+
+uint32_t git_blame_get_hunk_count(git_blame *blame)
+{
+ assert(blame);
+ return (uint32_t)blame->hunks.length;
+}
+
+const git_blame_hunk *git_blame_get_hunk_byindex(git_blame *blame, uint32_t index)
+{
+ assert(blame);
+ return (git_blame_hunk*)git_vector_get(&blame->hunks, index);
+}
+
+const git_blame_hunk *git_blame_get_hunk_byline(git_blame *blame, uint32_t lineno)
+{
+ size_t i;
+ assert(blame);
+
+ if (!git_vector_bsearch2( &i, &blame->hunks, hunk_byfinalline_search_cmp, &lineno)) {
+ return git_blame_get_hunk_byindex(blame, (uint32_t)i);
+ }
+
+ return NULL;
+}
+
+static void normalize_options(
+ git_blame_options *out,
+ const git_blame_options *in,
+ git_repository *repo)
+{
+ git_blame_options dummy = GIT_BLAME_OPTIONS_INIT;
+ if (!in) in = &dummy;
+
+ memcpy(out, in, sizeof(git_blame_options));
+
+ /* No newest_commit => HEAD */
+ if (git_oid_iszero(&out->newest_commit)) {
+ git_reference_name_to_id(&out->newest_commit, repo, "HEAD");
+ }
+
+ /* min_line 0 really means 1 */
+ if (!out->min_line) out->min_line = 1;
+ /* max_line 0 really means N, but we don't know N yet */
+
+ /* Fix up option implications */
+ if (out->flags & GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES)
+ out->flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES;
+ if (out->flags & GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES)
+ out->flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES;
+ if (out->flags & GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES)
+ out->flags |= GIT_BLAME_TRACK_COPIES_SAME_FILE;
+}
+
+static git_blame_hunk *split_hunk_in_vector(
+ git_vector *vec,
+ git_blame_hunk *hunk,
+ size_t rel_line,
+ bool return_new)
+{
+ size_t new_line_count;
+ git_blame_hunk *nh;
+
+ /* Don't split if already at a boundary */
+ if (rel_line <= 0 ||
+ rel_line >= hunk->lines_in_hunk)
+ {
+ return hunk;
+ }
+
+ new_line_count = hunk->lines_in_hunk - rel_line;
+ nh = new_hunk((uint16_t)(hunk->final_start_line_number+rel_line), (uint16_t)new_line_count,
+ (uint16_t)(hunk->orig_start_line_number+rel_line), hunk->orig_path);
+ git_oid_cpy(&nh->final_commit_id, &hunk->final_commit_id);
+ git_oid_cpy(&nh->orig_commit_id, &hunk->orig_commit_id);
+
+ /* Adjust hunk that was split */
+ hunk->lines_in_hunk -= (uint16_t)new_line_count;
+ git_vector_insert_sorted(vec, nh, NULL);
+ {
+ git_blame_hunk *ret = return_new ? nh : hunk;
+ return ret;
+ }
+}
+
+/*
+ * Construct a list of char indices for where lines begin
+ * Adapted from core git:
+ * https://github.com/gitster/git/blob/be5c9fb9049ed470e7005f159bb923a5f4de1309/builtin/blame.c#L1760-L1789
+ */
+static int index_blob_lines(git_blame *blame)
+{
+ const char *buf = blame->final_buf;
+ git_off_t len = blame->final_buf_size;
+ int num = 0, incomplete = 0, bol = 1;
+ size_t *i;
+
+ if (len && buf[len-1] != '\n')
+ incomplete++; /* incomplete line at the end */
+ while (len--) {
+ if (bol) {
+ i = git_array_alloc(blame->line_index);
+ GITERR_CHECK_ALLOC(i);
+ *i = buf - blame->final_buf;
+ bol = 0;
+ }
+ if (*buf++ == '\n') {
+ num++;
+ bol = 1;
+ }
+ }
+ i = git_array_alloc(blame->line_index);
+ GITERR_CHECK_ALLOC(i);
+ *i = buf - blame->final_buf;
+ blame->num_lines = num + incomplete;
+ return blame->num_lines;
+}
+
+static git_blame_hunk* hunk_from_entry(git_blame__entry *e)
+{
+ git_blame_hunk *h = new_hunk(
+ e->lno+1, e->num_lines, e->s_lno+1, e->suspect->path);
+ git_oid_cpy(&h->final_commit_id, git_commit_id(e->suspect->commit));
+ h->final_signature = git_signature_dup(git_commit_author(e->suspect->commit));
+ h->boundary = e->is_boundary ? 1 : 0;
+ return h;
+}
+
+static int load_blob(git_blame *blame)
+{
+ int error;
+
+ if (blame->final_blob) return 0;
+
+ error = git_commit_lookup(&blame->final, blame->repository, &blame->options.newest_commit);
+ if (error < 0)
+ goto cleanup;
+ error = git_object_lookup_bypath((git_object**)&blame->final_blob,
+ (git_object*)blame->final, blame->path, GIT_OBJ_BLOB);
+ if (error < 0)
+ goto cleanup;
+
+cleanup:
+ return error;
+}
+
+static int blame_internal(git_blame *blame)
+{
+ int error;
+ git_blame__entry *ent = NULL;
+ git_blame__origin *o;
+
+ if ((error = load_blob(blame)) < 0 ||
+ (error = git_blame__get_origin(&o, blame, blame->final, blame->path)) < 0)
+ goto cleanup;
+ blame->final_buf = git_blob_rawcontent(blame->final_blob);
+ blame->final_buf_size = git_blob_rawsize(blame->final_blob);
+
+ ent = git__calloc(1, sizeof(git_blame__entry));
+ ent->num_lines = index_blob_lines(blame);
+ ent->lno = blame->options.min_line - 1;
+ ent->num_lines = ent->num_lines - blame->options.min_line + 1;
+ if (blame->options.max_line > 0)
+ ent->num_lines = blame->options.max_line - blame->options.min_line + 1;
+ ent->s_lno = ent->lno;
+ ent->suspect = o;
+
+ blame->ent = ent;
+ blame->path = blame->path;
+
+ git_blame__like_git(blame, blame->options.flags);
+
+cleanup:
+ for (ent = blame->ent; ent; ) {
+ git_blame__entry *e = ent->next;
+
+ git_vector_insert(&blame->hunks, hunk_from_entry(ent));
+
+ git_blame__free_entry(ent);
+ ent = e;
+ }
+
+ return error;
+}
+
+/*******************************************************************************
+ * File blaming
+ ******************************************************************************/
+
+int git_blame_file(
+ git_blame **out,
+ git_repository *repo,
+ const char *path,
+ git_blame_options *options)
+{
+ int error = -1;
+ git_blame_options normOptions = GIT_BLAME_OPTIONS_INIT;
+ git_blame *blame = NULL;
+
+ assert(out && repo && path);
+ normalize_options(&normOptions, options, repo);
+
+ blame = git_blame__alloc(repo, normOptions, path);
+ GITERR_CHECK_ALLOC(blame);
+
+ if ((error = load_blob(blame)) < 0)
+ goto on_error;
+
+ if ((error = blame_internal(blame)) < 0)
+ goto on_error;
+
+ *out = blame;
+ return 0;
+
+on_error:
+ git_blame_free(blame);
+ return error;
+}
+
+/*******************************************************************************
+ * Buffer blaming
+ *******************************************************************************/
+
+static bool hunk_is_bufferblame(git_blame_hunk *hunk)
+{
+ return git_oid_iszero(&hunk->final_commit_id);
+}
+
+static int buffer_hunk_cb(
+ const git_diff_delta *delta,
+ const git_diff_hunk *hunk,
+ void *payload)
+{
+ git_blame *blame = (git_blame*)payload;
+ uint32_t wedge_line;
+
+ GIT_UNUSED(delta);
+
+ wedge_line = (hunk->old_lines == 0) ? hunk->new_start : hunk->old_start;
+ blame->current_diff_line = wedge_line;
+
+ blame->current_hunk = (git_blame_hunk*)git_blame_get_hunk_byline(blame, wedge_line);
+ if (!blame->current_hunk) {
+ /* Line added at the end of the file */
+ blame->current_hunk = new_hunk(wedge_line, 0, wedge_line, blame->path);
+ git_vector_insert(&blame->hunks, blame->current_hunk);
+ } else if (!hunk_starts_at_or_after_line(blame->current_hunk, wedge_line)){
+ /* If this hunk doesn't start between existing hunks, split a hunk up so it does */
+ blame->current_hunk = split_hunk_in_vector(&blame->hunks, blame->current_hunk,
+ wedge_line - blame->current_hunk->orig_start_line_number, true);
+ }
+
+ return 0;
+}
+
+static int ptrs_equal_cmp(const void *a, const void *b) { return a<b ? -1 : a>b ? 1 : 0; }
+static int buffer_line_cb(
+ const git_diff_delta *delta,
+ const git_diff_hunk *hunk,
+ const git_diff_line *line,
+ void *payload)
+{
+ git_blame *blame = (git_blame*)payload;
+
+ GIT_UNUSED(delta);
+ GIT_UNUSED(hunk);
+ GIT_UNUSED(line);
+
+ if (line->origin == GIT_DIFF_LINE_ADDITION) {
+ if (hunk_is_bufferblame(blame->current_hunk) &&
+ hunk_ends_at_or_before_line(blame->current_hunk, blame->current_diff_line)) {
+ /* Append to the current buffer-blame hunk */
+ blame->current_hunk->lines_in_hunk++;
+ shift_hunks_by(&blame->hunks, blame->current_diff_line+1, 1);
+ } else {
+ /* Create a new buffer-blame hunk with this line */
+ shift_hunks_by(&blame->hunks, blame->current_diff_line, 1);
+ blame->current_hunk = new_hunk((uint16_t)blame->current_diff_line, 1, 0, blame->path);
+ git_vector_insert_sorted(&blame->hunks, blame->current_hunk, NULL);
+ }
+ blame->current_diff_line++;
+ }
+
+ if (line->origin == GIT_DIFF_LINE_DELETION) {
+ /* Trim the line from the current hunk; remove it if it's now empty */
+ size_t shift_base = blame->current_diff_line + blame->current_hunk->lines_in_hunk+1;
+
+ if (--(blame->current_hunk->lines_in_hunk) == 0) {
+ size_t i;
+ shift_base--;
+ if (!git_vector_search2(&i, &blame->hunks, ptrs_equal_cmp, blame->current_hunk)) {
+ git_vector_remove(&blame->hunks, i);
+ free_hunk(blame->current_hunk);
+ blame->current_hunk = (git_blame_hunk*)git_blame_get_hunk_byindex(blame, (uint32_t)i);
+ }
+ }
+ shift_hunks_by(&blame->hunks, shift_base, -1);
+ }
+ return 0;
+}
+
+int git_blame_buffer(
+ git_blame **out,
+ git_blame *reference,
+ const char *buffer,
+ uint32_t buffer_len)
+{
+ git_blame *blame;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ size_t i;
+ git_blame_hunk *hunk;
+
+ diffopts.context_lines = 0;
+
+ assert(out && reference && buffer && buffer_len);
+
+ blame = git_blame__alloc(reference->repository, reference->options, reference->path);
+
+ /* Duplicate all of the hunk structures in the reference blame */
+ git_vector_foreach(&reference->hunks, i, hunk) {
+ git_vector_insert(&blame->hunks, dup_hunk(hunk));
+ }
+
+ /* Diff to the reference blob */
+ git_diff_blob_to_buffer(reference->final_blob, blame->path,
+ buffer, buffer_len, blame->path,
+ &diffopts, NULL, buffer_hunk_cb, buffer_line_cb, blame);
+
+ *out = blame;
+ return 0;
+}
diff --git a/src/blame.h b/src/blame.h
new file mode 100644
index 000000000..637e43985
--- /dev/null
+++ b/src/blame.h
@@ -0,0 +1,93 @@
+#ifndef INCLUDE_blame_h__
+#define INCLUDE_blame_h__
+
+#include "git2/blame.h"
+#include "common.h"
+#include "vector.h"
+#include "diff.h"
+#include "array.h"
+#include "git2/oid.h"
+
+/*
+ * One blob in a commit that is being suspected
+ */
+typedef struct git_blame__origin {
+ int refcnt;
+ struct git_blame__origin *previous;
+ git_commit *commit;
+ git_blob *blob;
+ char path[GIT_FLEX_ARRAY];
+} git_blame__origin;
+
+/*
+ * Each group of lines is described by a git_blame__entry; it can be split
+ * as we pass blame to the parents. They form a linked list in the
+ * scoreboard structure, sorted by the target line number.
+ */
+typedef struct git_blame__entry {
+ struct git_blame__entry *prev;
+ struct git_blame__entry *next;
+
+ /* the first line of this group in the final image;
+ * internally all line numbers are 0 based.
+ */
+ int lno;
+
+ /* how many lines this group has */
+ int num_lines;
+
+ /* the commit that introduced this group into the final image */
+ git_blame__origin *suspect;
+
+ /* true if the suspect is truly guilty; false while we have not
+ * checked if the group came from one of its parents.
+ */
+ bool guilty;
+
+ /* true if the entry has been scanned for copies in the current parent
+ */
+ bool scanned;
+
+ /* the line number of the first line of this group in the
+ * suspect's file; internally all line numbers are 0 based.
+ */
+ int s_lno;
+
+ /* how significant this entry is -- cached to avoid
+ * scanning the lines over and over.
+ */
+ unsigned score;
+
+ /* Whether this entry has been tracked to a boundary commit.
+ */
+ bool is_boundary;
+} git_blame__entry;
+
+struct git_blame {
+ const char *path;
+ git_repository *repository;
+ git_blame_options options;
+
+ git_vector hunks;
+ git_vector paths;
+
+ git_blob *final_blob;
+ git_array_t(size_t) line_index;
+
+ size_t current_diff_line;
+ git_blame_hunk *current_hunk;
+
+ /* Scoreboard fields */
+ git_commit *final;
+ git_blame__entry *ent;
+ int num_lines;
+ const char *final_buf;
+ git_off_t final_buf_size;
+};
+
+git_blame *git_blame__alloc(
+ git_repository *repo,
+ git_blame_options opts,
+ const char *path);
+
+#endif
diff --git a/src/blame_git.c b/src/blame_git.c
new file mode 100644
index 000000000..800f1f039
--- /dev/null
+++ b/src/blame_git.c
@@ -0,0 +1,622 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "blame_git.h"
+#include "commit.h"
+#include "blob.h"
+#include "xdiff/xinclude.h"
+
+/*
+ * Origin is refcounted and usually we keep the blob contents to be
+ * reused.
+ */
+static git_blame__origin *origin_incref(git_blame__origin *o)
+{
+ if (o)
+ o->refcnt++;
+ return o;
+}
+
+static void origin_decref(git_blame__origin *o)
+{
+ if (o && --o->refcnt <= 0) {
+ if (o->previous)
+ origin_decref(o->previous);
+ git_blob_free(o->blob);
+ git_commit_free(o->commit);
+ git__free(o);
+ }
+}
+
+/* Given a commit and a path in it, create a new origin structure. */
+static int make_origin(git_blame__origin **out, git_commit *commit, const char *path)
+{
+ int error = 0;
+ git_blame__origin *o;
+
+ o = git__calloc(1, sizeof(*o) + strlen(path) + 1);
+ GITERR_CHECK_ALLOC(o);
+ o->commit = commit;
+ o->refcnt = 1;
+ strcpy(o->path, path);
+
+ if (!(error = git_object_lookup_bypath((git_object**)&o->blob, (git_object*)commit,
+ path, GIT_OBJ_BLOB))) {
+ *out = o;
+ } else {
+ origin_decref(o);
+ }
+ return error;
+}
+
+/* Locate an existing origin or create a new one. */
+int git_blame__get_origin(
+ git_blame__origin **out,
+ git_blame *blame,
+ git_commit *commit,
+ const char *path)
+{
+ git_blame__entry *e;
+
+ for (e = blame->ent; e; e = e->next) {
+ if (e->suspect->commit == commit && !strcmp(e->suspect->path, path)) {
+ *out = origin_incref(e->suspect);
+ }
+ }
+ return make_origin(out, commit, path);
+}
+
+typedef struct blame_chunk_cb_data {
+ git_blame *blame;
+ git_blame__origin *target;
+ git_blame__origin *parent;
+ long tlno;
+ long plno;
+}blame_chunk_cb_data;
+
+static bool same_suspect(git_blame__origin *a, git_blame__origin *b)
+{
+ if (a == b)
+ return true;
+ if (git_oid_cmp(git_commit_id(a->commit), git_commit_id(b->commit)))
+ return false;
+ return 0 == strcmp(a->path, b->path);
+}
+
+/* find the line number of the last line the target is suspected for */
+static int find_last_in_target(git_blame *blame, git_blame__origin *target)
+{
+ git_blame__entry *e;
+ int last_in_target = -1;
+
+ for (e=blame->ent; e; e=e->next) {
+ if (e->guilty || !same_suspect(e->suspect, target))
+ continue;
+ if (last_in_target < e->s_lno + e->num_lines)
+ last_in_target = e->s_lno + e->num_lines;
+ }
+ return last_in_target;
+}
+
+/*
+ * It is known that lines between tlno to same came from parent, and e
+ * has an overlap with that range. it also is known that parent's
+ * line plno corresponds to e's line tlno.
+ *
+ * <---- e ----->
+ * <------> (entirely within)
+ * <------------> (extends past)
+ * <------------> (starts before)
+ * <------------------> (entirely encloses)
+ *
+ * Split e into potentially three parts; before this chunk, the chunk
+ * to be blamed for the parent, and after that portion.
+ */
+static void split_overlap(git_blame__entry *split, git_blame__entry *e,
+ int tlno, int plno, int same, git_blame__origin *parent)
+{
+ int chunk_end_lno;
+
+ if (e->s_lno < tlno) {
+ /* there is a pre-chunk part not blamed on the parent */
+ split[0].suspect = origin_incref(e->suspect);
+ split[0].lno = e->lno;
+ split[0].s_lno = e->s_lno;
+ split[0].num_lines = tlno - e->s_lno;
+ split[1].lno = e->lno + tlno - e->s_lno;
+ split[1].s_lno = plno;
+ } else {
+ split[1].lno = e->lno;
+ split[1].s_lno = plno + (e->s_lno - tlno);
+ }
+
+ if (same < e->s_lno + e->num_lines) {
+ /* there is a post-chunk part not blamed on parent */
+ split[2].suspect = origin_incref(e->suspect);
+ split[2].lno = e->lno + (same - e->s_lno);
+ split[2].s_lno = e->s_lno + (same - e->s_lno);
+ split[2].num_lines = e->s_lno + e->num_lines - same;
+ chunk_end_lno = split[2].lno;
+ } else {
+ chunk_end_lno = e->lno + e->num_lines;
+ }
+ split[1].num_lines = chunk_end_lno - split[1].lno;
+
+ /*
+ * if it turns out there is nothing to blame the parent for, forget about
+ * the splitting. !split[1].suspect signals this.
+ */
+ if (split[1].num_lines < 1)
+ return;
+ split[1].suspect = origin_incref(parent);
+}
+
+/*
+ * Link in a new blame entry to the scoreboard. Entries that cover the same
+ * line range have been removed from the scoreboard previously.
+ */
+static void add_blame_entry(git_blame *blame, git_blame__entry *e)
+{
+ git_blame__entry *ent, *prev = NULL;
+
+ origin_incref(e->suspect);
+
+ for (ent = blame->ent; ent && ent->lno < e->lno; ent = ent->next)
+ prev = ent;
+
+ /* prev, if not NULL, is the last one that is below e */
+ e->prev = prev;
+ if (prev) {
+ e->next = prev->next;
+ prev->next = e;
+ } else {
+ e->next = blame->ent;
+ blame->ent = e;
+ }
+ if (e->next)
+ e->next->prev = e;
+}
+
+/*
+ * src typically is on-stack; we want to copy the information in it to
+ * a malloced blame_entry that is already on the linked list of the scoreboard.
+ * The origin of dst loses a refcnt while the origin of src gains one.
+ */
+static void dup_entry(git_blame__entry *dst, git_blame__entry *src)
+{
+ git_blame__entry *p, *n;
+
+ p = dst->prev;
+ n = dst->next;
+ origin_incref(src->suspect);
+ origin_decref(dst->suspect);
+ memcpy(dst, src, sizeof(*src));
+ dst->prev = p;
+ dst->next = n;
+ dst->score = 0;
+}
+
+/*
+ * split_overlap() divided an existing blame e into up to three parts in split.
+ * Adjust the linked list of blames in the scoreboard to reflect the split.
+ */
+static void split_blame(git_blame *blame, git_blame__entry *split, git_blame__entry *e)
+{
+ git_blame__entry *new_entry;
+
+ if (split[0].suspect && split[2].suspect) {
+ /* The first part (reuse storage for the existing entry e */
+ dup_entry(e, &split[0]);
+
+ /* The last part -- me */
+ new_entry = git__malloc(sizeof(*new_entry));
+ memcpy(new_entry, &(split[2]), sizeof(git_blame__entry));
+ add_blame_entry(blame, new_entry);
+
+ /* ... and the middle part -- parent */
+ new_entry = git__malloc(sizeof(*new_entry));
+ memcpy(new_entry, &(split[1]), sizeof(git_blame__entry));
+ add_blame_entry(blame, new_entry);
+ } else if (!split[0].suspect && !split[2].suspect) {
+ /*
+ * The parent covers the entire area; reuse storage for e and replace it
+ * with the parent
+ */
+ dup_entry(e, &split[1]);
+ } else if (split[0].suspect) {
+ /* me and then parent */
+ dup_entry(e, &split[0]);
+ new_entry = git__malloc(sizeof(*new_entry));
+ memcpy(new_entry, &(split[1]), sizeof(git_blame__entry));
+ add_blame_entry(blame, new_entry);
+ } else {
+ /* parent and then me */
+ dup_entry(e, &split[1]);
+ new_entry = git__malloc(sizeof(*new_entry));
+ memcpy(new_entry, &(split[2]), sizeof(git_blame__entry));
+ add_blame_entry(blame, new_entry);
+ }
+}
+
+/*
+ * After splitting the blame, the origins used by the on-stack blame_entry
+ * should lose one refcnt each.
+ */
+static void decref_split(git_blame__entry *split)
+{
+ int i;
+ for (i=0; i<3; i++)
+ origin_decref(split[i].suspect);
+}
+
+/*
+ * Helper for blame_chunk(). blame_entry e is known to overlap with the patch
+ * hunk; split it and pass blame to the parent.
+ */
+static void blame_overlap(
+ git_blame *blame,
+ git_blame__entry *e,
+ int tlno,
+ int plno,
+ int same,
+ git_blame__origin *parent)
+{
+ git_blame__entry split[3] = {{0}};
+
+ split_overlap(split, e, tlno, plno, same, parent);
+ if (split[1].suspect)
+ split_blame(blame, split, e);
+ decref_split(split);
+}
+
+/*
+ * Process one hunk from the patch between the current suspect for blame_entry
+ * e and its parent. Find and split the overlap, and pass blame to the
+ * overlapping part to the parent.
+ */
+static void blame_chunk(
+ git_blame *blame,
+ int tlno,
+ int plno,
+ int same,
+ git_blame__origin *target,
+ git_blame__origin *parent)
+{
+ git_blame__entry *e;
+
+ for (e = blame->ent; e; e = e->next) {
+ if (e->guilty || !same_suspect(e->suspect, target))
+ continue;
+ if (same <= e->s_lno)
+ continue;
+ if (tlno < e->s_lno + e->num_lines) {
+ blame_overlap(blame, e, tlno, plno, same, parent);
+ }
+ }
+}
+
+static int my_emit(
+ xdfenv_t *xe,
+ xdchange_t *xscr,
+ xdemitcb_t *ecb,
+ xdemitconf_t const *xecfg)
+{
+ xdchange_t *xch = xscr;
+ GIT_UNUSED(xe);
+ GIT_UNUSED(xecfg);
+ while (xch) {
+ blame_chunk_cb_data *d = ecb->priv;
+ blame_chunk(d->blame, d->tlno, d->plno, xch->i2, d->target, d->parent);
+ d->plno = xch->i1 + xch->chg1;
+ d->tlno = xch->i2 + xch->chg2;
+ xch = xch->next;
+ }
+ return 0;
+}
+
+static void trim_common_tail(mmfile_t *a, mmfile_t *b, long ctx)
+{
+ const int blk = 1024;
+ long trimmed = 0, recovered = 0;
+ char *ap = a->ptr + a->size;
+ char *bp = b->ptr + b->size;
+ long smaller = (long)((a->size < b->size) ? a->size : b->size);
+
+ if (ctx)
+ return;
+
+ while (blk + trimmed <= smaller && !memcmp(ap - blk, bp - blk, blk)) {
+ trimmed += blk;
+ ap -= blk;
+ bp -= blk;
+ }
+
+ while (recovered < trimmed)
+ if (ap[recovered++] == '\n')
+ break;
+ a->size -= trimmed - recovered;
+ b->size -= trimmed - recovered;
+}
+
+static int diff_hunks(mmfile_t file_a, mmfile_t file_b, void *cb_data)
+{
+ xpparam_t xpp = {0};
+ xdemitconf_t xecfg = {0};
+ xdemitcb_t ecb = {0};
+
+ xecfg.emit_func = (void(*)(void))my_emit;
+ ecb.priv = cb_data;
+
+ trim_common_tail(&file_a, &file_b, 0);
+ return xdl_diff(&file_a, &file_b, &xpp, &xecfg, &ecb);
+}
+
+static void fill_origin_blob(git_blame__origin *o, mmfile_t *file)
+{
+ memset(file, 0, sizeof(*file));
+ if (o->blob) {
+ file->ptr = (char*)git_blob_rawcontent(o->blob);
+ file->size = (size_t)git_blob_rawsize(o->blob);
+ }
+}
+
+static int pass_blame_to_parent(
+ git_blame *blame,
+ git_blame__origin *target,
+ git_blame__origin *parent)
+{
+ int last_in_target;
+ mmfile_t file_p, file_o;
+ blame_chunk_cb_data d = { blame, target, parent, 0, 0 };
+
+ last_in_target = find_last_in_target(blame, target);
+ if (last_in_target < 0)
+ return 1; /* nothing remains for this target */
+
+ fill_origin_blob(parent, &file_p);
+ fill_origin_blob(target, &file_o);
+
+ diff_hunks(file_p, file_o, &d);
+ /* The reset (i.e. anything after tlno) are the same as the parent */
+ blame_chunk(blame, d.tlno, d.plno, last_in_target, target, parent);
+
+ return 0;
+}
+
+static int paths_on_dup(void **old, void *new)
+{
+ GIT_UNUSED(old);
+ git__free(new);
+ return -1;
+}
+
+static git_blame__origin* find_origin(
+ git_blame *blame,
+ git_commit *parent,
+ git_blame__origin *origin)
+{
+ git_blame__origin *porigin = NULL;
+ git_diff *difflist = NULL;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_tree *otree=NULL, *ptree=NULL;
+
+ /* Get the trees from this commit and its parent */
+ if (0 != git_commit_tree(&otree, origin->commit) ||
+ 0 != git_commit_tree(&ptree, parent))
+ goto cleanup;
+
+ /* Configure the diff */
+ diffopts.context_lines = 0;
+ diffopts.flags = GIT_DIFF_SKIP_BINARY_CHECK;
+
+ /* Check to see if files we're interested have changed */
+ diffopts.pathspec.count = blame->paths.length;
+ diffopts.pathspec.strings = (char**)blame->paths.contents;
+ if (0 != git_diff_tree_to_tree(&difflist, blame->repository, ptree, otree, &diffopts))
+ goto cleanup;
+
+ if (!git_diff_num_deltas(difflist)) {
+ /* No changes; copy data */
+ git_blame__get_origin(&porigin, blame, parent, origin->path);
+ } else {
+ git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
+ int i;
+
+ /* Generate a full diff between the two trees */
+ git_diff_free(difflist);
+ diffopts.pathspec.count = 0;
+ if (0 != git_diff_tree_to_tree(&difflist, blame->repository, ptree, otree, &diffopts))
+ goto cleanup;
+
+ /* Let diff find renames */
+ findopts.flags = GIT_DIFF_FIND_RENAMES;
+ if (0 != git_diff_find_similar(difflist, &findopts))
+ goto cleanup;
+
+ /* Find one that matches */
+ for (i=0; i<(int)git_diff_num_deltas(difflist); i++) {
+ const git_diff_delta *delta = git_diff_get_delta(difflist, i);
+
+ if (!git_vector_bsearch(NULL, &blame->paths, delta->new_file.path))
+ {
+ git_vector_insert_sorted(&blame->paths, (void*)git__strdup(delta->old_file.path),
+ paths_on_dup);
+ make_origin(&porigin, parent, delta->old_file.path);
+ }
+ }
+ }
+
+cleanup:
+ git_diff_free(difflist);
+ git_tree_free(otree);
+ git_tree_free(ptree);
+ return porigin;
+}
+
+/*
+ * The blobs of origin and porigin exactly match, so everything origin is
+ * suspected for can be blamed on the parent.
+ */
+static void pass_whole_blame(git_blame *blame,
+ git_blame__origin *origin, git_blame__origin *porigin)
+{
+ git_blame__entry *e;
+
+ if (!porigin->blob)
+ git_object_lookup((git_object**)&porigin->blob, blame->repository,
+ git_blob_id(origin->blob), GIT_OBJ_BLOB);
+ for (e=blame->ent; e; e=e->next) {
+ if (!same_suspect(e->suspect, origin))
+ continue;
+ origin_incref(porigin);
+ origin_decref(e->suspect);
+ e->suspect = porigin;
+ }
+}
+
+static void pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt)
+{
+ git_commit *commit = origin->commit;
+ int i, num_parents;
+ git_blame__origin *sg_buf[16];
+ git_blame__origin *porigin, **sg_origin = sg_buf;
+
+ GIT_UNUSED(opt);
+
+ num_parents = git_commit_parentcount(commit);
+ if (!git_oid_cmp(git_commit_id(commit), &blame->options.oldest_commit))
+ /* Stop at oldest specified commit */
+ num_parents = 0;
+ if (!num_parents) {
+ git_oid_cpy(&blame->options.oldest_commit, git_commit_id(commit));
+ goto finish;
+ }
+ else if (num_parents < (int)ARRAY_SIZE(sg_buf))
+ memset(sg_buf, 0, sizeof(sg_buf));
+ else
+ sg_origin = git__calloc(num_parents, sizeof(*sg_origin));
+
+ for (i=0; i<num_parents; i++) {
+ git_commit *p;
+ int j, same;
+
+ if (sg_origin[i])
+ continue;
+
+ git_commit_parent(&p, origin->commit, i);
+ porigin = find_origin(blame, p, origin);
+
+ if (!porigin)
+ continue;
+ if (porigin->blob && origin->blob &&
+ !git_oid_cmp(git_blob_id(porigin->blob), git_blob_id(origin->blob))) {
+ pass_whole_blame(blame, origin, porigin);
+ origin_decref(porigin);
+ goto finish;
+ }
+ for (j = same = 0; j<i; j++)
+ if (sg_origin[j] &&
+ !git_oid_cmp(git_blob_id(sg_origin[j]->blob), git_blob_id(porigin->blob))) {
+ same = 1;
+ break;
+ }
+ if (!same)
+ sg_origin[i] = porigin;
+ else
+ origin_decref(porigin);
+ }
+
+ /* Standard blame */
+ for (i=0; i<num_parents; i++) {
+ git_blame__origin *porigin = sg_origin[i];
+ if (!porigin)
+ continue;
+ if (!origin->previous) {
+ origin_incref(porigin);
+ origin->previous = porigin;
+ }
+ if (pass_blame_to_parent(blame, origin, porigin))
+ goto finish;
+ }
+
+ /* TODO: optionally find moves in parents' files */
+
+ /* TODO: optionally find copies in parents' files */
+
+finish:
+ for (i=0; i<num_parents; i++)
+ if (sg_origin[i])
+ origin_decref(sg_origin[i]);
+ if (sg_origin != sg_buf)
+ git__free(sg_origin);
+ return;
+}
+
+/*
+ * If two blame entries that are next to each other came from
+ * contiguous lines in the same origin (i.e. <commit, path> pair),
+ * merge them together.
+ */
+static void coalesce(git_blame *blame)
+{
+ git_blame__entry *ent, *next;
+
+ for (ent=blame->ent; ent && (next = ent->next); ent = next) {
+ if (same_suspect(ent->suspect, next->suspect) &&
+ ent->guilty == next->guilty &&
+ ent->s_lno + ent->num_lines == next->s_lno)
+ {
+ ent->num_lines += next->num_lines;
+ ent->next = next->next;
+ if (ent->next)
+ ent->next->prev = ent;
+ origin_decref(next->suspect);
+ git__free(next);
+ ent->score = 0;
+ next = ent; /* again */
+ }
+ }
+}
+
+void git_blame__like_git(git_blame *blame, uint32_t opt)
+{
+ while (true) {
+ git_blame__entry *ent;
+ git_blame__origin *suspect = NULL;
+
+ /* Find a suspect to break down */
+ for (ent = blame->ent; !suspect && ent; ent = ent->next)
+ if (!ent->guilty)
+ suspect = ent->suspect;
+ if (!suspect)
+ return; /* all done */
+
+ /* We'll use this suspect later in the loop, so hold on to it for now. */
+ origin_incref(suspect);
+ pass_blame(blame, suspect, opt);
+
+ /* Take responsibility for the remaining entries */
+ for (ent = blame->ent; ent; ent = ent->next) {
+ if (same_suspect(ent->suspect, suspect)) {
+ ent->guilty = true;
+ ent->is_boundary = !git_oid_cmp(
+ git_commit_id(suspect->commit),
+ &blame->options.oldest_commit);
+ }
+ }
+ origin_decref(suspect);
+ }
+
+ coalesce(blame);
+}
+
+void git_blame__free_entry(git_blame__entry *ent)
+{
+ if (!ent) return;
+ origin_decref(ent->suspect);
+ git__free(ent);
+}
diff --git a/src/blame_git.h b/src/blame_git.h
new file mode 100644
index 000000000..3ec2710b8
--- /dev/null
+++ b/src/blame_git.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_blame_git__
+#define INCLUDE_blame_git__
+
+#include "blame.h"
+
+int git_blame__get_origin(
+ git_blame__origin **out,
+ git_blame *sb,
+ git_commit *commit,
+ const char *path);
+void git_blame__free_entry(git_blame__entry *ent);
+void git_blame__like_git(git_blame *sb, uint32_t flags);
+
+#endif
diff --git a/src/blob.c b/src/blob.c
index 2e4d5f479..2c6d52800 100644
--- a/src/blob.c
+++ b/src/blob.c
@@ -60,10 +60,10 @@ int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *b
(error = git_odb_open_wstream(&stream, odb, len, GIT_OBJ_BLOB)) < 0)
return error;
- if ((error = stream->write(stream, buffer, len)) == 0)
- error = stream->finalize_write(oid, stream);
+ if ((error = git_odb_stream_write(stream, buffer, len)) == 0)
+ error = git_odb_stream_finalize_write(oid, stream);
- stream->free(stream);
+ git_odb_stream_free(stream);
return error;
}
@@ -80,12 +80,12 @@ static int write_file_stream(
return error;
if ((fd = git_futils_open_ro(path)) < 0) {
- stream->free(stream);
+ git_odb_stream_free(stream);
return -1;
}
while (!error && (read_len = p_read(fd, buffer, sizeof(buffer))) > 0) {
- error = stream->write(stream, buffer, read_len);
+ error = git_odb_stream_write(stream, buffer, read_len);
written += read_len;
}
@@ -97,36 +97,32 @@ static int write_file_stream(
}
if (!error)
- error = stream->finalize_write(oid, stream);
+ error = git_odb_stream_finalize_write(oid, stream);
- stream->free(stream);
+ git_odb_stream_free(stream);
return error;
}
static int write_file_filtered(
git_oid *oid,
+ git_off_t *size,
git_odb *odb,
const char *full_path,
- git_vector *filters)
+ git_filter_list *fl)
{
int error;
- git_buf source = GIT_BUF_INIT;
- git_buf dest = GIT_BUF_INIT;
+ git_buf tgt = GIT_BUF_INIT;
- if ((error = git_futils_readbuffer(&source, full_path)) < 0)
- return error;
-
- error = git_filters_apply(&dest, &source, filters);
-
- /* Free the source as soon as possible. This can be big in memory,
- * and we don't want to ODB write to choke */
- git_buf_free(&source);
+ error = git_filter_list_apply_to_file(&tgt, fl, NULL, full_path);
/* Write the file to disk if it was properly filtered */
- if (!error)
- error = git_odb_write(oid, odb, dest.ptr, dest.size, GIT_OBJ_BLOB);
+ if (!error) {
+ *size = tgt.size;
+
+ error = git_odb_write(oid, odb, tgt.ptr, tgt.size, GIT_OBJ_BLOB);
+ }
- git_buf_free(&dest);
+ git_buf_free(&tgt);
return error;
}
@@ -152,45 +148,67 @@ static int write_symlink(
return error;
}
-static int blob_create_internal(git_oid *oid, git_repository *repo, const char *content_path, const char *hint_path, bool try_load_filters)
+int git_blob__create_from_paths(
+ git_oid *oid,
+ struct stat *out_st,
+ git_repository *repo,
+ const char *content_path,
+ const char *hint_path,
+ mode_t hint_mode,
+ bool try_load_filters)
{
int error;
struct stat st;
git_odb *odb = NULL;
git_off_t size;
+ mode_t mode;
+ git_buf path = GIT_BUF_INIT;
assert(hint_path || !try_load_filters);
- if ((error = git_path_lstat(content_path, &st)) < 0 || (error = git_repository_odb__weakptr(&odb, repo)) < 0)
- return error;
+ if (!content_path) {
+ if (git_repository__ensure_not_bare(repo, "create blob from file") < 0)
+ return GIT_EBAREREPO;
+
+ if (git_buf_joinpath(
+ &path, git_repository_workdir(repo), hint_path) < 0)
+ return -1;
+
+ content_path = path.ptr;
+ }
+
+ if ((error = git_path_lstat(content_path, &st)) < 0 ||
+ (error = git_repository_odb(&odb, repo)) < 0)
+ goto done;
+
+ if (out_st)
+ memcpy(out_st, &st, sizeof(st));
size = st.st_size;
+ mode = hint_mode ? hint_mode : st.st_mode;
- if (S_ISLNK(st.st_mode)) {
+ if (S_ISLNK(mode)) {
error = write_symlink(oid, odb, content_path, (size_t)size);
} else {
- git_vector write_filters = GIT_VECTOR_INIT;
- int filter_count = 0;
+ git_filter_list *fl = NULL;
- if (try_load_filters) {
+ if (try_load_filters)
/* Load the filters for writing this file to the ODB */
- filter_count = git_filters_load(
- &write_filters, repo, hint_path, GIT_FILTER_TO_ODB);
- }
+ error = git_filter_list_load(
+ &fl, repo, NULL, hint_path, GIT_FILTER_TO_ODB);
- if (filter_count < 0) {
- /* Negative value means there was a critical error */
- error = filter_count;
- } else if (filter_count == 0) {
+ if (error < 0)
+ /* well, that didn't work */;
+ else if (fl == NULL)
/* No filters need to be applied to the document: we can stream
* directly from disk */
error = write_file_stream(oid, odb, content_path, size);
- } else {
+ else {
/* We need to apply one or more filters */
- error = write_file_filtered(oid, odb, content_path, &write_filters);
- }
+ error = write_file_filtered(oid, &size, odb, content_path, fl);
- git_filters_free(&write_filters);
+ git_filter_list_free(fl);
+ }
/*
* TODO: eventually support streaming filtered files, for files
@@ -207,34 +225,21 @@ static int blob_create_internal(git_oid *oid, git_repository *repo, const char *
*/
}
+done:
+ git_odb_free(odb);
+ git_buf_free(&path);
+
return error;
}
-int git_blob_create_fromworkdir(git_oid *oid, git_repository *repo, const char *path)
+int git_blob_create_fromworkdir(
+ git_oid *oid, git_repository *repo, const char *path)
{
- git_buf full_path = GIT_BUF_INIT;
- const char *workdir;
- int error;
-
- if ((error = git_repository__ensure_not_bare(repo, "create blob from file")) < 0)
- return error;
-
- workdir = git_repository_workdir(repo);
-
- if (git_buf_joinpath(&full_path, workdir, path) < 0) {
- git_buf_free(&full_path);
- return -1;
- }
-
- error = blob_create_internal(
- oid, repo, git_buf_cstr(&full_path),
- git_buf_cstr(&full_path) + strlen(workdir), true);
-
- git_buf_free(&full_path);
- return error;
+ return git_blob__create_from_paths(oid, NULL, repo, NULL, path, 0, true);
}
-int git_blob_create_fromdisk(git_oid *oid, git_repository *repo, const char *path)
+int git_blob_create_fromdisk(
+ git_oid *oid, git_repository *repo, const char *path)
{
int error;
git_buf full_path = GIT_BUF_INIT;
@@ -251,8 +256,8 @@ int git_blob_create_fromdisk(git_oid *oid, git_repository *repo, const char *pat
if (workdir && !git__prefixcmp(hintpath, workdir))
hintpath += strlen(workdir);
- error = blob_create_internal(
- oid, repo, git_buf_cstr(&full_path), hintpath, true);
+ error = git_blob__create_from_paths(
+ oid, NULL, repo, git_buf_cstr(&full_path), hintpath, 0, true);
git_buf_free(&full_path);
return error;
@@ -272,17 +277,14 @@ int git_blob_create_fromchunks(
git_filebuf file = GIT_FILEBUF_INIT;
git_buf path = GIT_BUF_INIT;
- if (git_buf_join_n(
- &path, '/', 3,
- git_repository_path(repo),
- GIT_OBJECTS_DIR,
- "streamed") < 0)
- goto cleanup;
+ if (git_buf_joinpath(
+ &path, git_repository_path(repo), GIT_OBJECTS_DIR "streamed") < 0)
+ goto cleanup;
content = git__malloc(BUFFER_SIZE);
GITERR_CHECK_ALLOC(content);
- if (git_filebuf_open(&file, git_buf_cstr(&path), GIT_FILEBUF_TEMPORARY) < 0)
+ if (git_filebuf_open(&file, git_buf_cstr(&path), GIT_FILEBUF_TEMPORARY, 0666) < 0)
goto cleanup;
while (1) {
@@ -303,7 +305,8 @@ int git_blob_create_fromchunks(
if (git_filebuf_flush(&file) < 0)
goto cleanup;
- error = blob_create_internal(oid, repo, file.path_lock, hintpath, hintpath != NULL);
+ error = git_blob__create_from_paths(
+ oid, NULL, repo, file.path_lock, hintpath, 0, hintpath != NULL);
cleanup:
git_buf_free(&path);
@@ -318,8 +321,34 @@ int git_blob_is_binary(git_blob *blob)
assert(blob);
- content.ptr = blob->odb_object->buffer;
- content.size = min(blob->odb_object->cached.size, 4000);
+ content.ptr = blob->odb_object->buffer;
+ content.size = min(blob->odb_object->cached.size, 4000);
+ content.asize = 0;
return git_buf_text_is_binary(&content);
}
+
+int git_blob_filtered_content(
+ git_buf *out,
+ git_blob *blob,
+ const char *path,
+ int check_for_binary_data)
+{
+ int error = 0;
+ git_filter_list *fl = NULL;
+
+ assert(blob && path && out);
+
+ if (check_for_binary_data && git_blob_is_binary(blob))
+ return 0;
+
+ if (!(error = git_filter_list_load(
+ &fl, git_blob_owner(blob), blob, path, GIT_FILTER_TO_WORKTREE))) {
+
+ error = git_filter_list_apply_to_blob(out, fl, blob);
+
+ git_filter_list_free(fl);
+ }
+
+ return error;
+}
diff --git a/src/blob.h b/src/blob.h
index 22e37cc3a..4cd9f1e0c 100644
--- a/src/blob.h
+++ b/src/blob.h
@@ -21,4 +21,13 @@ void git_blob__free(void *blob);
int git_blob__parse(void *blob, git_odb_object *obj);
int git_blob__getbuf(git_buf *buffer, git_blob *blob);
+extern int git_blob__create_from_paths(
+ git_oid *out_oid,
+ struct stat *out_st,
+ git_repository *repo,
+ const char *full_path,
+ const char *hint_path,
+ mode_t hint_mode,
+ bool apply_filters);
+
#endif
diff --git a/src/branch.c b/src/branch.c
index 7064fa7fc..95b3fd980 100644
--- a/src/branch.c
+++ b/src/branch.c
@@ -124,48 +124,66 @@ on_error:
return error;
}
-int git_branch_foreach(
- git_repository *repo,
- unsigned int list_flags,
- git_branch_foreach_cb callback,
- void *payload)
-{
+typedef struct {
git_reference_iterator *iter;
- git_reference *ref;
- int error = 0;
+ unsigned int flags;
+} branch_iter;
- if (git_reference_iterator_new(&iter, repo) < 0)
- return -1;
+int git_branch_next(git_reference **out, git_branch_t *out_type, git_branch_iterator *_iter)
+{
+ branch_iter *iter = (branch_iter *) _iter;
+ git_reference *ref;
+ int error;
- while ((error = git_reference_next(&ref, iter)) == 0) {
- if (list_flags & GIT_BRANCH_LOCAL &&
- git__prefixcmp(ref->name, GIT_REFS_HEADS_DIR) == 0) {
- if (callback(ref->name + strlen(GIT_REFS_HEADS_DIR),
- GIT_BRANCH_LOCAL, payload)) {
- error = GIT_EUSER;
- }
+ while ((error = git_reference_next(&ref, iter->iter)) == 0) {
+ if ((iter->flags & GIT_BRANCH_LOCAL) &&
+ !git__prefixcmp(ref->name, GIT_REFS_HEADS_DIR)) {
+ *out = ref;
+ *out_type = GIT_BRANCH_LOCAL;
+
+ return 0;
+ } else if ((iter->flags & GIT_BRANCH_REMOTE) &&
+ !git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR)) {
+ *out = ref;
+ *out_type = GIT_BRANCH_REMOTE;
+
+ return 0;
+ } else {
+ git_reference_free(ref);
}
+ }
- if (list_flags & GIT_BRANCH_REMOTE &&
- git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR) == 0) {
- if (callback(ref->name + strlen(GIT_REFS_REMOTES_DIR),
- GIT_BRANCH_REMOTE, payload)) {
- error = GIT_EUSER;
- }
- }
+ return error;
+}
+
+int git_branch_iterator_new(
+ git_branch_iterator **out,
+ git_repository *repo,
+ git_branch_t list_flags)
+{
+ branch_iter *iter;
+
+ iter = git__calloc(1, sizeof(branch_iter));
+ GITERR_CHECK_ALLOC(iter);
- git_reference_free(ref);
+ iter->flags = list_flags;
- /* check if the callback has cancelled iteration */
- if (error == GIT_EUSER)
- break;
+ if (git_reference_iterator_new(&iter->iter, repo) < 0) {
+ git__free(iter);
+ return -1;
}
- if (error == GIT_ITEROVER)
- error = 0;
+ *out = (git_branch_iterator *) iter;
- git_reference_iterator_free(iter);
- return error;
+ return 0;
+}
+
+void git_branch_iterator_free(git_branch_iterator *_iter)
+{
+ branch_iter *iter = (branch_iter *) _iter;
+
+ git_reference_iterator_free(iter->iter);
+ git__free(iter);
}
int git_branch_move(
@@ -585,7 +603,7 @@ int git_branch_is_head(
error = git_repository_head(&head, git_reference_owner(branch));
- if (error == GIT_EORPHANEDHEAD || error == GIT_ENOTFOUND)
+ if (error == GIT_EUNBORNBRANCH || error == GIT_ENOTFOUND)
return false;
if (error < 0)
diff --git a/src/buf_text.c b/src/buf_text.c
index 443454b5f..631feb3f8 100644
--- a/src/buf_text.c
+++ b/src/buf_text.c
@@ -70,10 +70,10 @@ int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src)
assert(tgt != src);
if (!next)
- return GIT_ENOTFOUND;
+ return git_buf_set(tgt, src->ptr, src->size);
/* reduce reallocs while in the loop */
- if (git_buf_grow(tgt, src->size) < 0)
+ if (git_buf_grow(tgt, src->size + 1) < 0)
return -1;
out = tgt->ptr;
tgt->size = 0;
@@ -81,20 +81,25 @@ int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src)
/* Find the next \r and copy whole chunk up to there to tgt */
for (; next; scan = next + 1, next = memchr(scan, '\r', scan_end - scan)) {
if (next > scan) {
- size_t copylen = next - scan;
+ size_t copylen = (size_t)(next - scan);
memcpy(out, scan, copylen);
out += copylen;
}
/* Do not drop \r unless it is followed by \n */
- if (next[1] != '\n')
+ if (next + 1 == scan_end || next[1] != '\n')
*out++ = '\r';
}
/* Copy remaining input into dest */
- memcpy(out, scan, scan_end - scan + 1); /* +1 for NUL byte */
- out += (scan_end - scan);
- tgt->size = out - tgt->ptr;
+ if (scan < scan_end) {
+ size_t remaining = (size_t)(scan_end - scan);
+ memcpy(out, scan, remaining);
+ out += remaining;
+ }
+
+ tgt->size = (size_t)(out - tgt->ptr);
+ tgt->ptr[tgt->size] = '\0';
return 0;
}
@@ -109,7 +114,7 @@ int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src)
assert(tgt != src);
if (!next)
- return GIT_ENOTFOUND;
+ return git_buf_set(tgt, src->ptr, src->size);
/* attempt to reduce reallocs while in the loop */
if (git_buf_grow(tgt, src->size + (src->size >> 4) + 1) < 0)
@@ -170,8 +175,14 @@ int git_buf_text_common_prefix(git_buf *buf, const git_strarray *strings)
bool git_buf_text_is_binary(const git_buf *buf)
{
const char *scan = buf->ptr, *end = buf->ptr + buf->size;
+ git_bom_t bom;
int printable = 0, nonprintable = 0;
+ scan += git_buf_text_detect_bom(&bom, buf, 0);
+
+ if (bom > GIT_BOM_UTF8)
+ return 1;
+
while (scan < end) {
unsigned char c = *scan++;
@@ -262,7 +273,7 @@ bool git_buf_text_gather_stats(
while (scan < end) {
unsigned char c = *scan++;
- if ((c > 0x1F && c < 0x7F) || c > 0x9f)
+ if (c > 0x1F && c != 0x7F)
stats->printable++;
else switch (c) {
case '\0':
diff --git a/src/buf_text.h b/src/buf_text.h
index 58e4e26a7..3ac9d1443 100644
--- a/src/buf_text.h
+++ b/src/buf_text.h
@@ -56,16 +56,16 @@ GIT_INLINE(int) git_buf_text_puts_escape_regex(git_buf *buf, const char *string)
extern void git_buf_text_unescape(git_buf *buf);
/**
- * Replace all \r\n with \n (or do nothing if no \r\n are found)
+ * Replace all \r\n with \n. Does not modify \r without trailing \n.
*
- * @return 0 on success, GIT_ENOTFOUND if no \r\n, -1 on memory error
+ * @return 0 on success, -1 on memory error
*/
extern int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src);
/**
- * Replace all \n with \r\n (or do nothing if no \n are found)
+ * Replace all \n with \r\n. Does not modify existing \r\n.
*
- * @return 0 on success, GIT_ENOTFOUND if no \n, -1 on memory error
+ * @return 0 on success, -1 on memory error
*/
extern int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src);
diff --git a/src/buffer.c b/src/buffer.c
index 6e3ffe560..20682322e 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -6,6 +6,7 @@
*/
#include "buffer.h"
#include "posix.h"
+#include "git2/buffer.h"
#include <stdarg.h>
#include <ctype.h>
@@ -31,7 +32,8 @@ void git_buf_init(git_buf *buf, size_t initial_size)
git_buf_grow(buf, initial_size);
}
-int git_buf_try_grow(git_buf *buf, size_t target_size, bool mark_oom)
+int git_buf_try_grow(
+ git_buf *buf, size_t target_size, bool mark_oom, bool preserve_external)
{
char *new_ptr;
size_t new_size;
@@ -39,6 +41,9 @@ int git_buf_try_grow(git_buf *buf, size_t target_size, bool mark_oom)
if (buf->ptr == git_buf__oom)
return -1;
+ if (!target_size)
+ target_size = buf->size;
+
if (target_size <= buf->asize)
return 0;
@@ -66,6 +71,9 @@ int git_buf_try_grow(git_buf *buf, size_t target_size, bool mark_oom)
return -1;
}
+ if (preserve_external && !buf->asize && buf->ptr != NULL && buf->size > 0)
+ memcpy(new_ptr, buf->ptr, min(buf->size, new_size));
+
buf->asize = new_size;
buf->ptr = new_ptr;
@@ -77,11 +85,16 @@ int git_buf_try_grow(git_buf *buf, size_t target_size, bool mark_oom)
return 0;
}
+int git_buf_grow(git_buf *buffer, size_t target_size)
+{
+ return git_buf_try_grow(buffer, target_size, true, true);
+}
+
void git_buf_free(git_buf *buf)
{
if (!buf) return;
- if (buf->ptr != git_buf__initbuf && buf->ptr != git_buf__oom)
+ if (buf->asize > 0 && buf->ptr != NULL && buf->ptr != git_buf__oom)
git__free(buf->ptr);
git_buf_init(buf, 0);
@@ -90,11 +103,15 @@ void git_buf_free(git_buf *buf)
void git_buf_clear(git_buf *buf)
{
buf->size = 0;
+
+ if (!buf->ptr)
+ buf->ptr = git_buf__initbuf;
+
if (buf->asize > 0)
buf->ptr[0] = '\0';
}
-int git_buf_set(git_buf *buf, const char *data, size_t len)
+int git_buf_set(git_buf *buf, const void *data, size_t len)
{
if (len == 0 || data == NULL) {
git_buf_clear(buf);
@@ -137,7 +154,7 @@ int git_buf_puts(git_buf *buf, const char *string)
return git_buf_put(buf, string, strlen(string));
}
-static const char b64str[64] =
+static const char b64str[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int git_buf_put_base64(git_buf *buf, const char *data, size_t len)
@@ -194,6 +211,8 @@ int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
format, args
);
+ va_end(args);
+
if (len < 0) {
git__free(buf->ptr);
buf->ptr = git_buf__oom;
@@ -259,6 +278,15 @@ void git_buf_truncate(git_buf *buf, size_t len)
}
}
+void git_buf_shorten(git_buf *buf, size_t amount)
+{
+ if (amount > buf->size)
+ amount = buf->size;
+
+ buf->size = buf->size - amount;
+ buf->ptr[buf->size] = '\0';
+}
+
void git_buf_rtruncate_at_char(git_buf *buf, char separator)
{
ssize_t idx = git_buf_rfind_next(buf, separator);
diff --git a/src/buffer.h b/src/buffer.h
index 5402f3827..4ca9d4d94 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -9,18 +9,26 @@
#include "common.h"
#include "git2/strarray.h"
+#include "git2/buffer.h"
#include <stdarg.h>
-typedef struct {
- char *ptr;
- size_t asize, size;
-} git_buf;
+/* typedef struct {
+ * char *ptr;
+ * size_t asize, size;
+ * } git_buf;
+ */
extern char git_buf__initbuf[];
extern char git_buf__oom[];
+/* Use to initialize buffer structure when git_buf is on stack */
#define GIT_BUF_INIT { git_buf__initbuf, 0, 0 }
+GIT_INLINE(bool) git_buf_is_allocated(const git_buf *buf)
+{
+ return (buf->ptr != NULL && buf->asize > 0);
+}
+
/**
* Initialize a git_buf structure.
*
@@ -32,27 +40,16 @@ extern void git_buf_init(git_buf *buf, size_t initial_size);
/**
* Attempt to grow the buffer to hold at least `target_size` bytes.
*
- * If the allocation fails, this will return an error. If mark_oom is true,
+ * If the allocation fails, this will return an error. If `mark_oom` is true,
* this will mark the buffer as invalid for future operations; if false,
* existing buffer content will be preserved, but calling code must handle
- * that buffer was not expanded.
+ * that buffer was not expanded. If `preserve_external` is true, then any
+ * existing data pointed to be `ptr` even if `asize` is zero will be copied
+ * into the newly allocated buffer.
*/
-extern int git_buf_try_grow(git_buf *buf, size_t target_size, bool mark_oom);
-
-/**
- * Grow the buffer to hold at least `target_size` bytes.
- *
- * If the allocation fails, this will return an error and the buffer will be
- * marked as invalid for future operations, invaliding contents.
- *
- * @return 0 on success or -1 on failure
- */
-GIT_INLINE(int) git_buf_grow(git_buf *buf, size_t target_size)
-{
- return git_buf_try_grow(buf, target_size, true);
-}
+extern int git_buf_try_grow(
+ git_buf *buf, size_t target_size, bool mark_oom, bool preserve_external);
-extern void git_buf_free(git_buf *buf);
extern void git_buf_swap(git_buf *buf_a, git_buf *buf_b);
extern char *git_buf_detach(git_buf *buf);
extern void git_buf_attach(git_buf *buf, char *ptr, size_t asize);
@@ -81,7 +78,6 @@ GIT_INLINE(bool) git_buf_oom(const git_buf *buf)
* return code of these functions and call them in a series then just call
* git_buf_oom at the end.
*/
-int git_buf_set(git_buf *buf, const char *data, size_t len);
int git_buf_sets(git_buf *buf, const char *string);
int git_buf_putc(git_buf *buf, char c);
int git_buf_put(git_buf *buf, const char *data, size_t len);
@@ -91,6 +87,7 @@ int git_buf_vprintf(git_buf *buf, const char *format, va_list ap);
void git_buf_clear(git_buf *buf);
void git_buf_consume(git_buf *buf, const char *end);
void git_buf_truncate(git_buf *buf, size_t len);
+void git_buf_shorten(git_buf *buf, size_t amount);
void git_buf_rtruncate_at_char(git_buf *path, char separator);
int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...);
diff --git a/src/cc-compat.h b/src/cc-compat.h
index a5e4ce17e..37f1ea81e 100644
--- a/src/cc-compat.h
+++ b/src/cc-compat.h
@@ -54,8 +54,12 @@
#if defined (_MSC_VER)
typedef unsigned char bool;
-# define true 1
-# define false 0
+# ifndef true
+# define true 1
+# endif
+# ifndef false
+# define false 0
+# endif
#else
# include <stdbool.h>
#endif
diff --git a/src/checkout.c b/src/checkout.c
index 8f9ec64e4..6d7e3cfd4 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -26,6 +26,8 @@
#include "diff.h"
#include "pathspec.h"
#include "buf_text.h"
+#include "merge_file.h"
+#include "path.h"
/* See docs/checkout-internals.md for more information */
@@ -35,21 +37,23 @@ enum {
CHECKOUT_ACTION__UPDATE_BLOB = 2,
CHECKOUT_ACTION__UPDATE_SUBMODULE = 4,
CHECKOUT_ACTION__CONFLICT = 8,
- CHECKOUT_ACTION__MAX = 8,
- CHECKOUT_ACTION__DEFER_REMOVE = 16,
+ CHECKOUT_ACTION__UPDATE_CONFLICT = 16,
+ CHECKOUT_ACTION__MAX = 16,
+ CHECKOUT_ACTION__DEFER_REMOVE = 32,
CHECKOUT_ACTION__REMOVE_AND_UPDATE =
(CHECKOUT_ACTION__UPDATE_BLOB | CHECKOUT_ACTION__REMOVE),
};
typedef struct {
git_repository *repo;
- git_diff_list *diff;
+ git_diff *diff;
git_checkout_opts opts;
bool opts_free_baseline;
char *pfx;
git_index *index;
git_pool pool;
git_vector removes;
+ git_vector conflicts;
git_buf path;
size_t workdir_len;
unsigned int strategy;
@@ -59,6 +63,16 @@ typedef struct {
size_t completed_steps;
} checkout_data;
+typedef struct {
+ const git_index_entry *ancestor;
+ const git_index_entry *ours;
+ const git_index_entry *theirs;
+
+ int name_collision:1,
+ directoryfile:1,
+ one_to_two:1;
+} checkout_conflictdata;
+
static int checkout_notify(
checkout_data *data,
git_checkout_notify_t why,
@@ -246,10 +260,10 @@ static int checkout_action_wd_only(
bool remove = false;
git_checkout_notify_t notify = GIT_CHECKOUT_NOTIFY_NONE;
- if (!git_pathspec_match_path(
+ if (!git_pathspec__match(
pathspec, wd->path,
(data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0,
- git_iterator_ignore_case(workdir), NULL))
+ git_iterator_ignore_case(workdir), NULL, NULL))
return 0;
/* check if item is tracked in the index but not in the checkout diff */
@@ -592,6 +606,359 @@ static int checkout_remaining_wd_items(
return error;
}
+GIT_INLINE(int) checkout_idxentry_cmp(
+ const git_index_entry *a,
+ const git_index_entry *b)
+{
+ if (!a && !b)
+ return 0;
+ else if (!a && b)
+ return -1;
+ else if(a && !b)
+ return 1;
+ else
+ return strcmp(a->path, b->path);
+}
+
+static int checkout_conflictdata_cmp(const void *a, const void *b)
+{
+ const checkout_conflictdata *ca = a;
+ const checkout_conflictdata *cb = b;
+ int diff;
+
+ if ((diff = checkout_idxentry_cmp(ca->ancestor, cb->ancestor)) == 0 &&
+ (diff = checkout_idxentry_cmp(ca->ours, cb->theirs)) == 0)
+ diff = checkout_idxentry_cmp(ca->theirs, cb->theirs);
+
+ return diff;
+}
+
+int checkout_conflictdata_empty(const git_vector *conflicts, size_t idx)
+{
+ checkout_conflictdata *conflict;
+
+ if ((conflict = git_vector_get(conflicts, idx)) == NULL)
+ return -1;
+
+ if (conflict->ancestor || conflict->ours || conflict->theirs)
+ return 0;
+
+ git__free(conflict);
+ return 1;
+}
+
+GIT_INLINE(bool) conflict_pathspec_match(
+ checkout_data *data,
+ git_iterator *workdir,
+ git_vector *pathspec,
+ const git_index_entry *ancestor,
+ const git_index_entry *ours,
+ const git_index_entry *theirs)
+{
+ /* if the pathspec matches ours *or* theirs, proceed */
+ if (ours && git_pathspec__match(pathspec, ours->path,
+ (data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0,
+ git_iterator_ignore_case(workdir), NULL, NULL))
+ return true;
+
+ if (theirs && git_pathspec__match(pathspec, theirs->path,
+ (data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0,
+ git_iterator_ignore_case(workdir), NULL, NULL))
+ return true;
+
+ if (ancestor && git_pathspec__match(pathspec, ancestor->path,
+ (data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0,
+ git_iterator_ignore_case(workdir), NULL, NULL))
+ return true;
+
+ return false;
+}
+
+static int checkout_conflicts_load(checkout_data *data, git_iterator *workdir, git_vector *pathspec)
+{
+ git_index_conflict_iterator *iterator = NULL;
+ const git_index_entry *ancestor, *ours, *theirs;
+ checkout_conflictdata *conflict;
+ int error = 0;
+
+ if ((error = git_index_conflict_iterator_new(&iterator, data->index)) < 0)
+ goto done;
+
+ data->conflicts._cmp = checkout_conflictdata_cmp;
+
+ /* Collect the conflicts */
+ while ((error = git_index_conflict_next(&ancestor, &ours, &theirs, iterator)) == 0) {
+ if (!conflict_pathspec_match(data, workdir, pathspec, ancestor, ours, theirs))
+ continue;
+
+ conflict = git__calloc(1, sizeof(checkout_conflictdata));
+ GITERR_CHECK_ALLOC(conflict);
+
+ conflict->ancestor = ancestor;
+ conflict->ours = ours;
+ conflict->theirs = theirs;
+
+ git_vector_insert(&data->conflicts, conflict);
+ }
+
+ if (error == GIT_ITEROVER)
+ error = 0;
+
+done:
+ git_index_conflict_iterator_free(iterator);
+
+ return error;
+}
+
+GIT_INLINE(int) checkout_conflicts_cmp_entry(
+ const char *path,
+ const git_index_entry *entry)
+{
+ return strcmp((const char *)path, entry->path);
+}
+
+static int checkout_conflicts_cmp_ancestor(const void *p, const void *c)
+{
+ const char *path = p;
+ const checkout_conflictdata *conflict = c;
+
+ if (!conflict->ancestor)
+ return 1;
+
+ return checkout_conflicts_cmp_entry(path, conflict->ancestor);
+}
+
+static checkout_conflictdata *checkout_conflicts_search_ancestor(
+ checkout_data *data,
+ const char *path)
+{
+ size_t pos;
+
+ if (git_vector_bsearch2(&pos, &data->conflicts, checkout_conflicts_cmp_ancestor, path) < 0)
+ return NULL;
+
+ return git_vector_get(&data->conflicts, pos);
+}
+
+static checkout_conflictdata *checkout_conflicts_search_branch(
+ checkout_data *data,
+ const char *path)
+{
+ checkout_conflictdata *conflict;
+ size_t i;
+
+ git_vector_foreach(&data->conflicts, i, conflict) {
+ int cmp = -1;
+
+ if (conflict->ancestor)
+ break;
+
+ if (conflict->ours)
+ cmp = checkout_conflicts_cmp_entry(path, conflict->ours);
+ else if (conflict->theirs)
+ cmp = checkout_conflicts_cmp_entry(path, conflict->theirs);
+
+ if (cmp == 0)
+ return conflict;
+ }
+
+ return NULL;
+}
+
+static int checkout_conflicts_load_byname_entry(
+ checkout_conflictdata **ancestor_out,
+ checkout_conflictdata **ours_out,
+ checkout_conflictdata **theirs_out,
+ checkout_data *data,
+ const git_index_name_entry *name_entry)
+{
+ checkout_conflictdata *ancestor, *ours = NULL, *theirs = NULL;
+ int error = 0;
+
+ *ancestor_out = NULL;
+ *ours_out = NULL;
+ *theirs_out = NULL;
+
+ if (!name_entry->ancestor) {
+ giterr_set(GITERR_INDEX, "A NAME entry exists without an ancestor");
+ error = -1;
+ goto done;
+ }
+
+ if (!name_entry->ours && !name_entry->theirs) {
+ giterr_set(GITERR_INDEX, "A NAME entry exists without an ours or theirs");
+ error = -1;
+ goto done;
+ }
+
+ if ((ancestor = checkout_conflicts_search_ancestor(data,
+ name_entry->ancestor)) == NULL) {
+ giterr_set(GITERR_INDEX,
+ "A NAME entry referenced ancestor entry '%s' which does not exist in the main index",
+ name_entry->ancestor);
+ error = -1;
+ goto done;
+ }
+
+ if (name_entry->ours) {
+ if (strcmp(name_entry->ancestor, name_entry->ours) == 0)
+ ours = ancestor;
+ else if ((ours = checkout_conflicts_search_branch(data, name_entry->ours)) == NULL ||
+ ours->ours == NULL) {
+ giterr_set(GITERR_INDEX,
+ "A NAME entry referenced our entry '%s' which does not exist in the main index",
+ name_entry->ours);
+ error = -1;
+ goto done;
+ }
+ }
+
+ if (name_entry->theirs) {
+ if (strcmp(name_entry->ancestor, name_entry->theirs) == 0)
+ theirs = ancestor;
+ else if (name_entry->ours && strcmp(name_entry->ours, name_entry->theirs) == 0)
+ theirs = ours;
+ else if ((theirs = checkout_conflicts_search_branch(data, name_entry->theirs)) == NULL ||
+ theirs->theirs == NULL) {
+ giterr_set(GITERR_INDEX,
+ "A NAME entry referenced their entry '%s' which does not exist in the main index",
+ name_entry->theirs);
+ error = -1;
+ goto done;
+ }
+ }
+
+ *ancestor_out = ancestor;
+ *ours_out = ours;
+ *theirs_out = theirs;
+
+done:
+ return error;
+}
+
+static int checkout_conflicts_coalesce_renames(
+ checkout_data *data)
+{
+ const git_index_name_entry *name_entry;
+ checkout_conflictdata *ancestor_conflict, *our_conflict, *their_conflict;
+ size_t i, names;
+ int error = 0;
+
+ /* Juggle entries based on renames */
+ names = git_index_name_entrycount(data->index);
+
+ for (i = 0; i < names; i++) {
+ name_entry = git_index_name_get_byindex(data->index, i);
+
+ if ((error = checkout_conflicts_load_byname_entry(
+ &ancestor_conflict, &our_conflict, &their_conflict,
+ data, name_entry)) < 0)
+ goto done;
+
+ if (our_conflict && our_conflict != ancestor_conflict) {
+ ancestor_conflict->ours = our_conflict->ours;
+ our_conflict->ours = NULL;
+
+ if (our_conflict->theirs)
+ our_conflict->name_collision = 1;
+
+ if (our_conflict->name_collision)
+ ancestor_conflict->name_collision = 1;
+ }
+
+ if (their_conflict && their_conflict != ancestor_conflict) {
+ ancestor_conflict->theirs = their_conflict->theirs;
+ their_conflict->theirs = NULL;
+
+ if (their_conflict->ours)
+ their_conflict->name_collision = 1;
+
+ if (their_conflict->name_collision)
+ ancestor_conflict->name_collision = 1;
+ }
+
+ if (our_conflict && our_conflict != ancestor_conflict &&
+ their_conflict && their_conflict != ancestor_conflict)
+ ancestor_conflict->one_to_two = 1;
+ }
+
+ git_vector_remove_matching(&data->conflicts, checkout_conflictdata_empty);
+
+done:
+ return error;
+}
+
+static int checkout_conflicts_mark_directoryfile(
+ checkout_data *data)
+{
+ checkout_conflictdata *conflict;
+ const git_index_entry *entry;
+ size_t i, j, len;
+ const char *path;
+ int prefixed, error = 0;
+
+ len = git_index_entrycount(data->index);
+
+ /* Find d/f conflicts */
+ git_vector_foreach(&data->conflicts, i, conflict) {
+ if ((conflict->ours && conflict->theirs) ||
+ (!conflict->ours && !conflict->theirs))
+ continue;
+
+ path = conflict->ours ?
+ conflict->ours->path : conflict->theirs->path;
+
+ if ((error = git_index_find(&j, data->index, path)) < 0) {
+ if (error == GIT_ENOTFOUND)
+ giterr_set(GITERR_INDEX,
+ "Index inconsistency, could not find entry for expected conflict '%s'", path);
+
+ goto done;
+ }
+
+ for (; j < len; j++) {
+ if ((entry = git_index_get_byindex(data->index, j)) == NULL) {
+ giterr_set(GITERR_INDEX,
+ "Index inconsistency, truncated index while loading expected conflict '%s'", path);
+ error = -1;
+ goto done;
+ }
+
+ prefixed = git_path_equal_or_prefixed(path, entry->path);
+
+ if (prefixed == GIT_PATH_EQUAL)
+ continue;
+
+ if (prefixed == GIT_PATH_PREFIX)
+ conflict->directoryfile = 1;
+
+ break;
+ }
+ }
+
+done:
+ return error;
+}
+
+static int checkout_get_conflicts(
+ checkout_data *data,
+ git_iterator *workdir,
+ git_vector *pathspec)
+{
+ int error = 0;
+
+ if (data->strategy & GIT_CHECKOUT_SKIP_UNMERGED)
+ return 0;
+
+ if ((error = checkout_conflicts_load(data, workdir, pathspec)) < 0 ||
+ (error = checkout_conflicts_coalesce_renames(data)) < 0 ||
+ (error = checkout_conflicts_mark_directoryfile(data)) < 0)
+ goto done;
+
+done:
+ return error;
+}
+
static int checkout_get_actions(
uint32_t **actions_ptr,
size_t **counts_ptr,
@@ -607,7 +974,7 @@ static int checkout_get_actions(
uint32_t *actions = NULL;
if (data->opts.paths.count > 0 &&
- git_pathspec_init(&pathspec, &data->opts.paths, &pathpool) < 0)
+ git_pathspec__vinit(&pathspec, &data->opts.paths, &pathpool) < 0)
return -1;
if ((error = git_iterator_current(&wditem, workdir)) < 0 &&
@@ -659,7 +1026,13 @@ static int checkout_get_actions(
goto fail;
}
- git_pathspec_free(&pathspec);
+
+ if ((error = checkout_get_conflicts(data, workdir, &pathspec)) < 0)
+ goto fail;
+
+ counts[CHECKOUT_ACTION__UPDATE_CONFLICT] = git_vector_length(&data->conflicts);
+
+ git_pathspec__vfree(&pathspec);
git_pool_clear(&pathpool);
return 0;
@@ -670,7 +1043,7 @@ fail:
*actions_ptr = NULL;
git__free(actions);
- git_pathspec_free(&pathspec);
+ git_pathspec__vfree(&pathspec);
git_pool_clear(&pathpool);
return error;
@@ -678,7 +1051,7 @@ fail:
static int buffer_to_file(
struct stat *st,
- git_buf *buffer,
+ git_buf *buf,
const char *path,
mode_t dir_mode,
int file_open_flags,
@@ -690,80 +1063,52 @@ static int buffer_to_file(
return error;
if ((error = git_futils_writebuffer(
- buffer, path, file_open_flags, file_mode)) < 0)
+ buf, path, file_open_flags, file_mode)) < 0)
return error;
- if (st != NULL && (error = p_stat(path, st)) < 0) {
- giterr_set(GITERR_OS, "Error while statting '%s'", path);
- return error;
- }
+ if (st != NULL && (error = p_stat(path, st)) < 0)
+ giterr_set(GITERR_OS, "Error statting '%s'", path);
- if ((file_mode & 0100) != 0 && (error = p_chmod(path, file_mode)) < 0) {
+ else if (GIT_PERMS_IS_EXEC(file_mode) &&
+ (error = p_chmod(path, file_mode)) < 0)
giterr_set(GITERR_OS, "Failed to set permissions on '%s'", path);
- return error;
- }
- return 0;
+ return error;
}
static int blob_content_to_file(
struct stat *st,
git_blob *blob,
const char *path,
+ const char * hint_path,
mode_t entry_filemode,
git_checkout_opts *opts)
{
- int error = -1, nb_filters = 0;
- mode_t file_mode = opts->file_mode;
- bool dont_free_filtered;
- git_buf unfiltered = GIT_BUF_INIT, filtered = GIT_BUF_INIT;
- git_vector filters = GIT_VECTOR_INIT;
-
- /* Create a fake git_buf from the blob raw data... */
- filtered.ptr = (void *)git_blob_rawcontent(blob);
- filtered.size = (size_t)git_blob_rawsize(blob);
- /* ... and make sure it doesn't get unexpectedly freed */
- dont_free_filtered = true;
-
- if (!opts->disable_filters &&
- !git_buf_text_is_binary(&filtered) &&
- (nb_filters = git_filters_load(
- &filters,
- git_object_owner((git_object *)blob),
- path,
- GIT_FILTER_TO_WORKTREE)) > 0)
- {
- /* reset 'filtered' so it can be a filter target */
- git_buf_init(&filtered, 0);
- dont_free_filtered = false;
- }
+ int error = 0;
+ mode_t file_mode = opts->file_mode ? opts->file_mode : entry_filemode;
+ git_buf out = GIT_BUF_INIT;
+ git_filter_list *fl = NULL;
- if (nb_filters < 0)
- return nb_filters;
+ if (hint_path == NULL)
+ hint_path = path;
- if (nb_filters > 0) {
- if ((error = git_blob__getbuf(&unfiltered, blob)) < 0)
- goto cleanup;
+ if (!opts->disable_filters)
+ error = git_filter_list_load(
+ &fl, git_blob_owner(blob), blob, hint_path, GIT_FILTER_TO_WORKTREE);
- if ((error = git_filters_apply(&filtered, &unfiltered, &filters)) < 0)
- goto cleanup;
- }
+ if (!error)
+ error = git_filter_list_apply_to_blob(&out, fl, blob);
- /* Allow overriding of file mode */
- if (!file_mode)
- file_mode = entry_filemode;
+ git_filter_list_free(fl);
- error = buffer_to_file(
- st, &filtered, path, opts->dir_mode, opts->file_open_flags, file_mode);
+ if (!error) {
+ error = buffer_to_file(
+ st, &out, path, opts->dir_mode, opts->file_open_flags, file_mode);
- if (!error)
st->st_mode = entry_filemode;
-cleanup:
- git_filters_free(&filters);
- git_buf_free(&unfiltered);
- if (!dont_free_filtered)
- git_buf_free(&filtered);
+ git_buf_free(&out);
+ }
return error;
}
@@ -815,7 +1160,8 @@ static int checkout_update_index(
memset(&entry, 0, sizeof(entry));
entry.path = (char *)file->path; /* cast to prevent warning */
- git_index_entry__init_from_stat(&entry, st);
+ git_index_entry__init_from_stat(
+ &entry, st, !(git_index_caps(data->index) & GIT_INDEXCAP_NO_FILEMODE));
git_oid_cpy(&entry.oid, &file->oid);
return git_index_add(data->index, &entry);
@@ -917,34 +1263,26 @@ static int checkout_safe_for_update_only(const char *path, mode_t expected_mode)
return 0;
}
-static int checkout_blob(
+static int checkout_write_content(
checkout_data *data,
- const git_diff_file *file)
+ const git_oid *oid,
+ const char *full_path,
+ const char *hint_path,
+ unsigned int mode,
+ struct stat *st)
{
int error = 0;
git_blob *blob;
- struct stat st;
- git_buf_truncate(&data->path, data->workdir_len);
- if (git_buf_puts(&data->path, file->path) < 0)
- return -1;
-
- if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0) {
- int rval = checkout_safe_for_update_only(
- git_buf_cstr(&data->path), file->mode);
- if (rval <= 0)
- return rval;
- }
-
- if ((error = git_blob_lookup(&blob, data->repo, &file->oid)) < 0)
+ if ((error = git_blob_lookup(&blob, data->repo, oid)) < 0)
return error;
- if (S_ISLNK(file->mode))
+ if (S_ISLNK(mode))
error = blob_content_to_link(
- &st, blob, git_buf_cstr(&data->path), data->opts.dir_mode, data->can_symlink);
+ st, blob, full_path, data->opts.dir_mode, data->can_symlink);
else
error = blob_content_to_file(
- &st, blob, git_buf_cstr(&data->path), file->mode, &data->opts);
+ st, blob, full_path, hint_path, mode, &data->opts);
git_blob_free(blob);
@@ -959,6 +1297,30 @@ static int checkout_blob(
error = 0;
}
+ return error;
+}
+
+static int checkout_blob(
+ checkout_data *data,
+ const git_diff_file *file)
+{
+ int error = 0;
+ struct stat st;
+
+ git_buf_truncate(&data->path, data->workdir_len);
+ if (git_buf_puts(&data->path, file->path) < 0)
+ return -1;
+
+ if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0) {
+ int rval = checkout_safe_for_update_only(
+ git_buf_cstr(&data->path), file->mode);
+ if (rval <= 0)
+ return rval;
+ }
+
+ error = checkout_write_content(
+ data, &file->oid, git_buf_cstr(&data->path), NULL, file->mode, &st);
+
/* update the index unless prevented */
if (!error && (data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0)
error = checkout_update_index(data, file, &st);
@@ -1129,8 +1491,278 @@ static int checkout_lookup_head_tree(git_tree **out, git_repository *repo)
return error;
}
+
+static int conflict_entry_name(
+ git_buf *out,
+ const char *side_name,
+ const char *filename)
+{
+ if (git_buf_puts(out, side_name) < 0 ||
+ git_buf_putc(out, ':') < 0 ||
+ git_buf_puts(out, filename) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int checkout_path_suffixed(git_buf *path, const char *suffix)
+{
+ size_t path_len;
+ int i = 0, error = 0;
+
+ if ((error = git_buf_putc(path, '~')) < 0 || (error = git_buf_puts(path, suffix)) < 0)
+ return -1;
+
+ path_len = git_buf_len(path);
+
+ while (git_path_exists(git_buf_cstr(path)) && i < INT_MAX) {
+ git_buf_truncate(path, path_len);
+
+ if ((error = git_buf_putc(path, '_')) < 0 ||
+ (error = git_buf_printf(path, "%d", i)) < 0)
+ return error;
+
+ i++;
+ }
+
+ if (i == INT_MAX) {
+ git_buf_truncate(path, path_len);
+
+ giterr_set(GITERR_CHECKOUT, "Could not write '%s': working directory file exists", path);
+ return GIT_EEXISTS;
+ }
+
+ return 0;
+}
+
+static int checkout_write_entry(
+ checkout_data *data,
+ checkout_conflictdata *conflict,
+ const git_index_entry *side)
+{
+ const char *hint_path = NULL, *suffix;
+ struct stat st;
+ int error;
+
+ assert (side == conflict->ours || side == conflict->theirs);
+
+ git_buf_truncate(&data->path, data->workdir_len);
+ if (git_buf_puts(&data->path, side->path) < 0)
+ return -1;
+
+ if ((conflict->name_collision || conflict->directoryfile) &&
+ (data->strategy & GIT_CHECKOUT_USE_OURS) == 0 &&
+ (data->strategy & GIT_CHECKOUT_USE_THEIRS) == 0) {
+
+ if (side == conflict->ours)
+ suffix = data->opts.our_label ? data->opts.our_label :
+ "ours";
+ else
+ suffix = data->opts.their_label ? data->opts.their_label :
+ "theirs";
+
+ if (checkout_path_suffixed(&data->path, suffix) < 0)
+ return -1;
+
+ hint_path = side->path;
+ }
+
+ if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0 &&
+ (error = checkout_safe_for_update_only(git_buf_cstr(&data->path), side->mode)) <= 0)
+ return error;
+
+ return checkout_write_content(data,
+ &side->oid, git_buf_cstr(&data->path), hint_path, side->mode, &st);
+}
+
+static int checkout_write_entries(
+ checkout_data *data,
+ checkout_conflictdata *conflict)
+{
+ int error = 0;
+
+ if ((error = checkout_write_entry(data, conflict, conflict->ours)) >= 0)
+ error = checkout_write_entry(data, conflict, conflict->theirs);
+
+ return error;
+}
+
+static int checkout_merge_path(
+ git_buf *out,
+ checkout_data *data,
+ checkout_conflictdata *conflict,
+ git_merge_file_result *result)
+{
+ const char *our_label_raw, *their_label_raw, *suffix;
+ int error = 0;
+
+ if ((error = git_buf_joinpath(out, git_repository_workdir(data->repo), result->path)) < 0)
+ return error;
+
+ /* Most conflicts simply use the filename in the index */
+ if (!conflict->name_collision)
+ return 0;
+
+ /* Rename 2->1 conflicts need the branch name appended */
+ our_label_raw = data->opts.our_label ? data->opts.our_label : "ours";
+ their_label_raw = data->opts.their_label ? data->opts.their_label : "theirs";
+ suffix = strcmp(result->path, conflict->ours->path) == 0 ? our_label_raw : their_label_raw;
+
+ if ((error = checkout_path_suffixed(out, suffix)) < 0)
+ return error;
+
+ return 0;
+}
+
+static int checkout_write_merge(
+ checkout_data *data,
+ checkout_conflictdata *conflict)
+{
+ git_buf our_label = GIT_BUF_INIT, their_label = GIT_BUF_INIT,
+ path_suffixed = GIT_BUF_INIT, path_workdir = GIT_BUF_INIT;
+ git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT,
+ ours = GIT_MERGE_FILE_INPUT_INIT,
+ theirs = GIT_MERGE_FILE_INPUT_INIT;
+ git_merge_file_result result = GIT_MERGE_FILE_RESULT_INIT;
+ git_filebuf output = GIT_FILEBUF_INIT;
+ int error = 0;
+
+ if ((conflict->ancestor &&
+ (error = git_merge_file_input_from_index_entry(
+ &ancestor, data->repo, conflict->ancestor)) < 0) ||
+ (error = git_merge_file_input_from_index_entry(
+ &ours, data->repo, conflict->ours)) < 0 ||
+ (error = git_merge_file_input_from_index_entry(
+ &theirs, data->repo, conflict->theirs)) < 0)
+ goto done;
+
+ ancestor.label = NULL;
+ ours.label = data->opts.our_label ? data->opts.our_label : "ours";
+ theirs.label = data->opts.their_label ? data->opts.their_label : "theirs";
+
+ /* If all the paths are identical, decorate the diff3 file with the branch
+ * names. Otherwise, append branch_name:path.
+ */
+ if (conflict->ours && conflict->theirs &&
+ strcmp(conflict->ours->path, conflict->theirs->path) != 0) {
+
+ if ((error = conflict_entry_name(
+ &our_label, ours.label, conflict->ours->path)) < 0 ||
+ (error = conflict_entry_name(
+ &their_label, theirs.label, conflict->theirs->path)) < 0)
+ goto done;
+
+ ours.label = git_buf_cstr(&our_label);
+ theirs.label = git_buf_cstr(&their_label);
+ }
+
+ if ((error = git_merge_files(&result, &ancestor, &ours, &theirs, 0)) < 0)
+ goto done;
+
+ if (result.path == NULL || result.mode == 0) {
+ giterr_set(GITERR_CHECKOUT, "Could not merge contents of file");
+ error = GIT_EMERGECONFLICT;
+ goto done;
+ }
+
+ if ((error = checkout_merge_path(&path_workdir, data, conflict, &result)) < 0)
+ goto done;
+
+ if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0 &&
+ (error = checkout_safe_for_update_only(git_buf_cstr(&path_workdir), result.mode)) <= 0)
+ goto done;
+
+ if ((error = git_futils_mkpath2file(path_workdir.ptr, 0755)) < 0 ||
+ (error = git_filebuf_open(&output, path_workdir.ptr, GIT_FILEBUF_DO_NOT_BUFFER, result.mode)) < 0 ||
+ (error = git_filebuf_write(&output, result.data, result.len)) < 0 ||
+ (error = git_filebuf_commit(&output)) < 0)
+ goto done;
+
+done:
+ git_buf_free(&our_label);
+ git_buf_free(&their_label);
+
+ git_merge_file_input_free(&ancestor);
+ git_merge_file_input_free(&ours);
+ git_merge_file_input_free(&theirs);
+ git_merge_file_result_free(&result);
+ git_buf_free(&path_workdir);
+ git_buf_free(&path_suffixed);
+
+ return error;
+}
+
+static int checkout_create_conflicts(checkout_data *data)
+{
+ checkout_conflictdata *conflict;
+ size_t i;
+ int error = 0;
+
+ git_vector_foreach(&data->conflicts, i, conflict) {
+ /* Both deleted: nothing to do */
+ if (conflict->ours == NULL && conflict->theirs == NULL)
+ error = 0;
+
+ else if ((data->strategy & GIT_CHECKOUT_USE_OURS) &&
+ conflict->ours)
+ error = checkout_write_entry(data, conflict, conflict->ours);
+ else if ((data->strategy & GIT_CHECKOUT_USE_THEIRS) &&
+ conflict->theirs)
+ error = checkout_write_entry(data, conflict, conflict->theirs);
+
+ /* Ignore the other side of name collisions. */
+ else if ((data->strategy & GIT_CHECKOUT_USE_OURS) &&
+ !conflict->ours && conflict->name_collision)
+ error = 0;
+ else if ((data->strategy & GIT_CHECKOUT_USE_THEIRS) &&
+ !conflict->theirs && conflict->name_collision)
+ error = 0;
+
+ /* For modify/delete, name collisions and d/f conflicts, write
+ * the file (potentially with the name mangled.
+ */
+ else if (conflict->ours != NULL && conflict->theirs == NULL)
+ error = checkout_write_entry(data, conflict, conflict->ours);
+ else if (conflict->ours == NULL && conflict->theirs != NULL)
+ error = checkout_write_entry(data, conflict, conflict->theirs);
+
+ /* Add/add conflicts and rename 1->2 conflicts, write the
+ * ours/theirs sides (potentially name mangled).
+ */
+ else if (conflict->one_to_two)
+ error = checkout_write_entries(data, conflict);
+
+ /* If all sides are links, write the ours side */
+ else if (S_ISLNK(conflict->ours->mode) &&
+ S_ISLNK(conflict->theirs->mode))
+ error = checkout_write_entry(data, conflict, conflict->ours);
+ /* Link/file conflicts, write the file side */
+ else if (S_ISLNK(conflict->ours->mode))
+ error = checkout_write_entry(data, conflict, conflict->theirs);
+ else if (S_ISLNK(conflict->theirs->mode))
+ error = checkout_write_entry(data, conflict, conflict->ours);
+
+ else
+ error = checkout_write_merge(data, conflict);
+
+ if (error)
+ break;
+
+ data->completed_steps++;
+ report_progress(data,
+ conflict->ours ? conflict->ours->path :
+ (conflict->theirs ? conflict->theirs->path : conflict->ancestor->path));
+ }
+
+ return error;
+}
+
+
static void checkout_data_clear(checkout_data *data)
{
+ checkout_conflictdata *conflict;
+ size_t i;
+
if (data->opts_free_baseline) {
git_tree_free(data->opts.baseline);
data->opts.baseline = NULL;
@@ -1139,6 +1771,11 @@ static void checkout_data_clear(checkout_data *data)
git_vector_free(&data->removes);
git_pool_clear(&data->pool);
+ git_vector_foreach(&data->conflicts, i, conflict)
+ git__free(conflict);
+
+ git_vector_free(&data->conflicts);
+
git__free(data->pfx);
data->pfx = NULL;
@@ -1151,7 +1788,7 @@ static void checkout_data_clear(checkout_data *data)
static int checkout_data_init(
checkout_data *data,
git_iterator *target,
- git_checkout_opts *proposed)
+ const git_checkout_opts *proposed)
{
int error = 0;
git_repository *repo = git_iterator_owner(target);
@@ -1200,10 +1837,20 @@ static int checkout_data_init(
} else {
/* otherwise, grab and reload the index */
if ((error = git_repository_index(&data->index, data->repo)) < 0 ||
- (error = git_index_read(data->index)) < 0)
+ (error = git_index_read(data->index, true)) < 0)
+ goto cleanup;
+
+ /* cannot checkout if unresolved conflicts exist */
+ if ((data->opts.checkout_strategy & GIT_CHECKOUT_FORCE) == 0 &&
+ git_index_has_conflicts(data->index)) {
+ error = GIT_EMERGECONFLICT;
+ giterr_set(GITERR_CHECKOUT,
+ "unresolved conflicts exist in the index");
goto cleanup;
+ }
- /* clear the REUC when doing a tree or commit checkout */
+ /* clean conflict data when doing a tree or commit checkout */
+ git_index_name_clear(data->index);
git_index_reuc_clear(data->index);
}
}
@@ -1235,7 +1882,7 @@ static int checkout_data_init(
error = checkout_lookup_head_tree(&data->opts.baseline, repo);
- if (error == GIT_EORPHANEDHEAD) {
+ if (error == GIT_EUNBORNBRANCH) {
error = 0;
giterr_clear();
}
@@ -1245,6 +1892,7 @@ static int checkout_data_init(
}
if ((error = git_vector_init(&data->removes, 0, git__strcmp_cb)) < 0 ||
+ (error = git_vector_init(&data->conflicts, 0, NULL)) < 0 ||
(error = git_pool_init(&data->pool, 1, 0)) < 0 ||
(error = git_buf_puts(&data->path, data->opts.target_directory)) < 0 ||
(error = git_path_to_dir(&data->path)) < 0)
@@ -1261,7 +1909,7 @@ cleanup:
int git_checkout_iterator(
git_iterator *target,
- git_checkout_opts *opts)
+ const git_checkout_opts *opts)
{
int error = 0;
git_iterator *baseline = NULL, *workdir = NULL;
@@ -1315,14 +1963,16 @@ int git_checkout_iterator(
goto cleanup;
/* Loop through diff (and working directory iterator) building a list of
- * actions to be taken, plus look for conflicts and send notifications.
+ * actions to be taken, plus look for conflicts and send notifications,
+ * then loop through conflicts.
*/
if ((error = checkout_get_actions(&actions, &counts, &data, workdir)) < 0)
goto cleanup;
data.total_steps = counts[CHECKOUT_ACTION__REMOVE] +
counts[CHECKOUT_ACTION__UPDATE_BLOB] +
- counts[CHECKOUT_ACTION__UPDATE_SUBMODULE];
+ counts[CHECKOUT_ACTION__UPDATE_SUBMODULE] +
+ counts[CHECKOUT_ACTION__UPDATE_CONFLICT];
report_progress(&data, NULL); /* establish 0 baseline */
@@ -1341,6 +1991,10 @@ int git_checkout_iterator(
(error = checkout_create_submodules(actions, &data)) < 0)
goto cleanup;
+ if (counts[CHECKOUT_ACTION__UPDATE_CONFLICT] > 0 &&
+ (error = checkout_create_conflicts(&data)) < 0)
+ goto cleanup;
+
assert(data.completed_steps == data.total_steps);
cleanup:
@@ -1351,7 +2005,7 @@ cleanup:
(data.strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0)
error = git_index_write(data.index);
- git_diff_list_free(data.diff);
+ git_diff_free(data.diff);
git_iterator_free(workdir);
git_iterator_free(baseline);
git__free(actions);
@@ -1364,7 +2018,7 @@ cleanup:
int git_checkout_index(
git_repository *repo,
git_index *index,
- git_checkout_opts *opts)
+ const git_checkout_opts *opts)
{
int error;
git_iterator *index_i;
@@ -1399,7 +2053,7 @@ int git_checkout_index(
int git_checkout_tree(
git_repository *repo,
const git_object *treeish,
- git_checkout_opts *opts)
+ const git_checkout_opts *opts)
{
int error;
git_tree *tree = NULL;
@@ -1419,10 +2073,21 @@ int git_checkout_tree(
if (!repo)
repo = git_object_owner(treeish);
- if (git_object_peel((git_object **)&tree, treeish, GIT_OBJ_TREE) < 0) {
- giterr_set(
- GITERR_CHECKOUT, "Provided object cannot be peeled to a tree");
- return -1;
+ if (treeish) {
+ if (git_object_peel((git_object **)&tree, treeish, GIT_OBJ_TREE) < 0) {
+ giterr_set(
+ GITERR_CHECKOUT, "Provided object cannot be peeled to a tree");
+ return -1;
+ }
+ }
+ else {
+ if ((error = checkout_lookup_head_tree(&tree, repo)) < 0) {
+ if (error != GIT_EUNBORNBRANCH)
+ giterr_set(
+ GITERR_CHECKOUT,
+ "HEAD could not be peeled to a tree and no treeish given");
+ return error;
+ }
}
if (!(error = git_iterator_for_tree(&tree_i, tree, 0, NULL, NULL)))
@@ -1436,20 +2101,8 @@ int git_checkout_tree(
int git_checkout_head(
git_repository *repo,
- git_checkout_opts *opts)
+ const git_checkout_opts *opts)
{
- int error;
- git_tree *head = NULL;
- git_iterator *head_i = NULL;
-
assert(repo);
-
- if (!(error = checkout_lookup_head_tree(&head, repo)) &&
- !(error = git_iterator_for_tree(&head_i, head, 0, NULL, NULL)))
- error = git_checkout_iterator(head_i, opts);
-
- git_iterator_free(head_i);
- git_tree_free(head);
-
- return error;
+ return git_checkout_tree(repo, NULL, opts);
}
diff --git a/src/checkout.h b/src/checkout.h
index b1dc80c38..6d7186860 100644
--- a/src/checkout.h
+++ b/src/checkout.h
@@ -19,6 +19,6 @@
*/
extern int git_checkout_iterator(
git_iterator *target,
- git_checkout_opts *opts);
+ const git_checkout_opts *opts);
#endif
diff --git a/src/clone.c b/src/clone.c
index 5b6c6f77d..23aacd478 100644
--- a/src/clone.c
+++ b/src/clone.c
@@ -176,25 +176,20 @@ static int update_head_to_new_branch(
return error;
}
-static int get_head_callback(git_remote_head *head, void *payload)
-{
- git_remote_head **destination = (git_remote_head **)payload;
-
- /* Save the first entry, and terminate the enumeration */
- *destination = head;
- return 1;
-}
-
static int update_head_to_remote(git_repository *repo, git_remote *remote)
{
int retcode = -1;
+ size_t refs_len;
git_refspec dummy_spec;
- git_remote_head *remote_head;
+ const git_remote_head *remote_head, **refs;
struct head_info head_info;
git_buf remote_master_name = GIT_BUF_INIT;
+ if (git_remote_ls(&refs, &refs_len, remote) < 0)
+ return -1;
+
/* Did we just clone an empty repository? */
- if (remote->refs.length == 0) {
+ if (refs_len == 0) {
return setup_tracking_config(
repo,
"master",
@@ -202,12 +197,8 @@ static int update_head_to_remote(git_repository *repo, git_remote *remote)
GIT_REFS_HEADS_MASTER_FILE);
}
- /* Get the remote's HEAD. This is always the first ref in remote->refs. */
- remote_head = NULL;
-
- if (!remote->transport->ls(remote->transport, get_head_callback, &remote_head))
- return -1;
-
+ /* Get the remote's HEAD. This is always the first ref in the list. */
+ remote_head = refs[0];
assert(remote_head);
git_oid_cpy(&head_info.remote_head_oid, &remote_head->oid);
@@ -220,7 +211,7 @@ static int update_head_to_remote(git_repository *repo, git_remote *remote)
memset(&dummy_spec, 0, sizeof(git_refspec));
head_info.refspec = &dummy_spec;
}
-
+
/* Determine the remote tracking reference name from the local master */
if (git_refspec_transform_r(
&remote_master_name,
@@ -270,23 +261,23 @@ cleanup:
static int update_head_to_branch(
git_repository *repo,
- const git_clone_options *options)
+ const char *remote_name,
+ const char *branch)
{
int retcode;
git_buf remote_branch_name = GIT_BUF_INIT;
git_reference* remote_ref = NULL;
- assert(options->checkout_branch);
+ assert(remote_name && branch);
if ((retcode = git_buf_printf(&remote_branch_name, GIT_REFS_REMOTES_DIR "%s/%s",
- options->remote_name, options->checkout_branch)) < 0 )
+ remote_name, branch)) < 0 )
goto cleanup;
if ((retcode = git_reference_lookup(&remote_ref, repo, git_buf_cstr(&remote_branch_name))) < 0)
goto cleanup;
- retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref),
- options->checkout_branch);
+ retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref), branch);
cleanup:
git_reference_free(remote_ref);
@@ -306,41 +297,18 @@ static int create_and_configure_origin(
{
int error;
git_remote *origin = NULL;
+ const char *name;
- if ((error = git_remote_create(&origin, repo, options->remote_name, url)) < 0)
+ name = options->remote_name ? options->remote_name : "origin";
+ if ((error = git_remote_create(&origin, repo, name, url)) < 0)
goto on_error;
- git_remote_set_cred_acquire_cb(origin, options->cred_acquire_cb,
- options->cred_acquire_payload);
- git_remote_set_autotag(origin, options->remote_autotag);
- /*
- * Don't write FETCH_HEAD, we'll check out the remote tracking
- * branch ourselves based on the server's default.
- */
- git_remote_set_update_fetchhead(origin, 0);
+ if (options->ignore_cert_errors)
+ git_remote_check_cert(origin, 0);
- if (options->remote_callbacks &&
- (error = git_remote_set_callbacks(origin, options->remote_callbacks)) < 0)
+ if ((error = git_remote_set_callbacks(origin, &options->remote_callbacks)) < 0)
goto on_error;
- if (options->fetch_spec) {
- git_remote_clear_refspecs(origin);
- if ((error = git_remote_add_fetch(origin, options->fetch_spec)) < 0)
- goto on_error;
- }
-
- if (options->push_spec &&
- (error = git_remote_add_push(origin, options->push_spec)) < 0)
- goto on_error;
-
- if (options->pushurl &&
- (error = git_remote_set_pushurl(origin, options->pushurl)) < 0)
- goto on_error;
-
- if (options->transport_flags == GIT_TRANSPORTFLAGS_NO_CHECK_CERT) {
- git_remote_check_cert(origin, 0);
- }
-
if ((error = git_remote_save(origin)) < 0)
goto on_error;
@@ -352,59 +320,10 @@ on_error:
return error;
}
-
-static int setup_remotes_and_fetch(
- git_repository *repo,
- const char *url,
- const git_clone_options *options)
-{
- int retcode = GIT_ERROR;
- git_remote *origin = NULL;
-
- /* Construct an origin remote */
- if ((retcode = create_and_configure_origin(&origin, repo, url, options)) < 0)
- goto on_error;
-
- git_remote_set_update_fetchhead(origin, 0);
-
- /* If the download_tags value has not been specified, then make sure to
- * download tags as well. It is set here because we want to download tags
- * on the initial clone, but do not want to persist the value in the
- * configuration file.
- */
- if (origin->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_AUTO &&
- ((retcode = git_remote_add_fetch(origin, "refs/tags/*:refs/tags/*")) < 0))
- goto on_error;
-
- /* Connect and download everything */
- if ((retcode = git_remote_connect(origin, GIT_DIRECTION_FETCH)) < 0)
- goto on_error;
-
- if ((retcode = git_remote_download(origin, options->fetch_progress_cb,
- options->fetch_progress_payload)) < 0)
- goto on_error;
-
- /* Create "origin/foo" branches for all remote branches */
- if ((retcode = git_remote_update_tips(origin)) < 0)
- goto on_error;
-
- /* Point HEAD to the requested branch */
- if (options->checkout_branch)
- retcode = update_head_to_branch(repo, options);
- /* Point HEAD to the same ref as the remote's head */
- else
- retcode = update_head_to_remote(repo, origin);
-
-on_error:
- git_remote_free(origin);
- return retcode;
-}
-
-
static bool should_checkout(
git_repository *repo,
bool is_bare,
- git_checkout_opts *opts)
+ const git_checkout_opts *opts)
{
if (is_bare)
return false;
@@ -415,64 +334,102 @@ static bool should_checkout(
if (opts->checkout_strategy == GIT_CHECKOUT_NONE)
return false;
- return !git_repository_head_orphan(repo);
+ return !git_repository_head_unborn(repo);
}
-static void normalize_options(git_clone_options *dst, const git_clone_options *src)
+int git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_opts *co_opts, const char *branch)
{
- git_clone_options default_options = GIT_CLONE_OPTIONS_INIT;
- if (!src) src = &default_options;
+ int error = 0, old_fetchhead;
+ git_strarray refspecs;
+
+ assert(repo && remote);
+
+ if (!git_repository_is_empty(repo)) {
+ giterr_set(GITERR_INVALID, "the repository is not empty");
+ return -1;
+ }
+
+
+ if ((error = git_remote_get_fetch_refspecs(&refspecs, remote)) < 0)
+ return error;
+
+ if ((error = git_remote_add_fetch(remote, "refs/tags/*:refs/tags/*")) < 0)
+ return error;
- *dst = *src;
+ old_fetchhead = git_remote_update_fetchhead(remote);
+ git_remote_set_update_fetchhead(remote, 0);
- /* Provide defaults for null pointers */
- if (!dst->remote_name) dst->remote_name = "origin";
+ if ((error = git_remote_fetch(remote)) < 0)
+ goto cleanup;
+
+ if (branch)
+ error = update_head_to_branch(repo, git_remote_name(remote), branch);
+ /* Point HEAD to the same ref as the remote's head */
+ else
+ error = update_head_to_remote(repo, remote);
+
+ if (!error && should_checkout(repo, git_repository_is_bare(repo), co_opts))
+ error = git_checkout_head(repo, co_opts);
+
+cleanup:
+ git_remote_set_update_fetchhead(remote, old_fetchhead);
+ /* Go back to the original refspecs */
+ if (git_remote_set_fetch_refspecs(remote, &refspecs) < 0) {
+ git_strarray_free(&refspecs);
+ return -1;
+ }
+
+ git_strarray_free(&refspecs);
+
+ return error;
}
int git_clone(
git_repository **out,
const char *url,
const char *local_path,
- const git_clone_options *options)
+ const git_clone_options *_options)
{
- int retcode = GIT_ERROR;
+ int error = 0;
git_repository *repo = NULL;
- git_clone_options normOptions;
- int remove_directory_on_failure = 0;
+ git_remote *origin;
+ git_clone_options options = GIT_CLONE_OPTIONS_INIT;
+ uint32_t rmdir_flags = GIT_RMDIR_REMOVE_FILES;
assert(out && url && local_path);
- normalize_options(&normOptions, options);
- GITERR_CHECK_VERSION(&normOptions, GIT_CLONE_OPTIONS_VERSION, "git_clone_options");
+ if (_options)
+ memcpy(&options, _options, sizeof(git_clone_options));
+
+ GITERR_CHECK_VERSION(&options, GIT_CLONE_OPTIONS_VERSION, "git_clone_options");
/* Only clone to a new directory or an empty directory */
if (git_path_exists(local_path) && !git_path_is_empty_dir(local_path)) {
giterr_set(GITERR_INVALID,
"'%s' exists and is not an empty directory", local_path);
- return GIT_ERROR;
+ return GIT_EEXISTS;
}
- /* Only remove the directory on failure if we create it */
- remove_directory_on_failure = !git_path_exists(local_path);
+ /* Only remove the root directory on failure if we create it */
+ if (git_path_exists(local_path))
+ rmdir_flags |= GIT_RMDIR_SKIP_ROOT;
- if (!(retcode = git_repository_init(&repo, local_path, normOptions.bare))) {
- if ((retcode = setup_remotes_and_fetch(repo, url, &normOptions)) < 0) {
- /* Failed to fetch; clean up */
- git_repository_free(repo);
+ if ((error = git_repository_init(&repo, local_path, options.bare)) < 0)
+ return error;
- if (remove_directory_on_failure)
- git_futils_rmdir_r(local_path, NULL, GIT_RMDIR_REMOVE_FILES);
- else
- git_futils_cleanupdir_r(local_path);
+ if (!(error = create_and_configure_origin(&origin, repo, url, &options))) {
+ error = git_clone_into(
+ repo, origin, &options.checkout_opts, options.checkout_branch);
- } else {
- *out = repo;
- retcode = 0;
- }
+ git_remote_free(origin);
}
- if (!retcode && should_checkout(repo, normOptions.bare, &normOptions.checkout_opts))
- retcode = git_checkout_head(*out, &normOptions.checkout_opts);
+ if (error < 0) {
+ git_repository_free(repo);
+ repo = NULL;
+ (void)git_futils_rmdir_r(local_path, NULL, rmdir_flags);
+ }
- return retcode;
+ *out = repo;
+ return error;
}
diff --git a/src/commit.c b/src/commit.c
index 1ab9b34f7..91b60bbb2 100644
--- a/src/commit.c
+++ b/src/commit.c
@@ -19,30 +19,19 @@
#include <stdarg.h>
-static void clear_parents(git_commit *commit)
-{
- size_t i;
-
- for (i = 0; i < commit->parent_ids.length; ++i) {
- git_oid *parent = git_vector_get(&commit->parent_ids, i);
- git__free(parent);
- }
-
- git_vector_clear(&commit->parent_ids);
-}
-
void git_commit__free(void *_commit)
{
git_commit *commit = _commit;
- clear_parents(commit);
- git_vector_free(&commit->parent_ids);
+ git_array_clear(commit->parent_ids);
git_signature_free(commit->author);
git_signature_free(commit->committer);
- git__free(commit->message);
+ git__free(commit->raw_header);
+ git__free(commit->raw_message);
git__free(commit->message_encoding);
+
git__free(commit);
}
@@ -171,12 +160,35 @@ int git_commit_create(
int git_commit__parse(void *_commit, git_odb_object *odb_obj)
{
git_commit *commit = _commit;
- const char *buffer = git_odb_object_data(odb_obj);
- const char *buffer_end = buffer + git_odb_object_size(odb_obj);
+ const char *buffer_start = git_odb_object_data(odb_obj), *buffer;
+ const char *buffer_end = buffer_start + git_odb_object_size(odb_obj);
git_oid parent_id;
+ uint32_t parent_count = 0;
+ size_t header_len;
+
+ /* find end-of-header (counting parents as we go) */
+ for (buffer = buffer_start; buffer < buffer_end; ++buffer) {
+ if (!strncmp("\n\n", buffer, 2)) {
+ ++buffer;
+ break;
+ }
+ if (!strncmp("\nparent ", buffer, strlen("\nparent ")))
+ ++parent_count;
+ }
- if (git_vector_init(&commit->parent_ids, 4, NULL) < 0)
- return -1;
+ header_len = buffer - buffer_start;
+ commit->raw_header = git__strndup(buffer_start, header_len);
+ GITERR_CHECK_ALLOC(commit->raw_header);
+
+ /* point "buffer" to header data */
+ buffer = commit->raw_header;
+ buffer_end = commit->raw_header + header_len;
+
+ if (parent_count < 1)
+ parent_count = 1;
+
+ git_array_init_to_size(commit->parent_ids, parent_count);
+ GITERR_CHECK_ARRAY(commit->parent_ids);
if (git_oid__parse(&commit->tree_id, &buffer, buffer_end, "tree ") < 0)
goto bad_buffer;
@@ -186,13 +198,10 @@ int git_commit__parse(void *_commit, git_odb_object *odb_obj)
*/
while (git_oid__parse(&parent_id, &buffer, buffer_end, "parent ") == 0) {
- git_oid *new_id = git__malloc(sizeof(git_oid));
+ git_oid *new_id = git_array_alloc(commit->parent_ids);
GITERR_CHECK_ALLOC(new_id);
git_oid_cpy(new_id, &parent_id);
-
- if (git_vector_insert(&commit->parent_ids, new_id) < 0)
- return -1;
}
commit->author = git__malloc(sizeof(git_signature));
@@ -208,8 +217,8 @@ int git_commit__parse(void *_commit, git_odb_object *odb_obj)
if (git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n') < 0)
return -1;
- /* Parse add'l header entries until blank line found */
- while (buffer < buffer_end && *buffer != '\n') {
+ /* Parse add'l header entries */
+ while (buffer < buffer_end) {
const char *eoln = buffer;
while (eoln < buffer_end && *eoln != '\n')
++eoln;
@@ -223,18 +232,21 @@ int git_commit__parse(void *_commit, git_odb_object *odb_obj)
if (eoln < buffer_end && *eoln == '\n')
++eoln;
-
buffer = eoln;
}
- /* buffer is now at the end of the header, double-check and move forward into the message */
- if (buffer < buffer_end && *buffer == '\n')
- buffer++;
+ /* point "buffer" to data after header */
+ buffer = git_odb_object_data(odb_obj);
+ buffer_end = buffer + git_odb_object_size(odb_obj);
- /* parse commit message */
+ buffer += header_len;
+ if (*buffer == '\n')
+ ++buffer;
+
+ /* extract commit message */
if (buffer <= buffer_end) {
- commit->message = git__strndup(buffer, buffer_end - buffer);
- GITERR_CHECK_ALLOC(commit->message);
+ commit->raw_message = git__strndup(buffer, buffer_end - buffer);
+ GITERR_CHECK_ALLOC(commit->raw_message);
}
return 0;
@@ -253,13 +265,27 @@ bad_buffer:
GIT_COMMIT_GETTER(const git_signature *, author, commit->author)
GIT_COMMIT_GETTER(const git_signature *, committer, commit->committer)
-GIT_COMMIT_GETTER(const char *, message, commit->message)
+GIT_COMMIT_GETTER(const char *, message_raw, commit->raw_message)
GIT_COMMIT_GETTER(const char *, message_encoding, commit->message_encoding)
+GIT_COMMIT_GETTER(const char *, raw_header, commit->raw_header)
GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time)
GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset)
-GIT_COMMIT_GETTER(unsigned int, parentcount, (unsigned int)commit->parent_ids.length)
+GIT_COMMIT_GETTER(unsigned int, parentcount, (unsigned int)git_array_size(commit->parent_ids))
GIT_COMMIT_GETTER(const git_oid *, tree_id, &commit->tree_id);
+const char *git_commit_message(const git_commit *commit)
+{
+ const char *message = commit->raw_message;
+
+ assert(commit);
+
+ /* trim leading newlines from raw message */
+ while (*message && *message == '\n')
+ ++message;
+
+ return message;
+}
+
int git_commit_tree(git_tree **tree_out, const git_commit *commit)
{
assert(commit);
@@ -271,7 +297,7 @@ const git_oid *git_commit_parent_id(
{
assert(commit);
- return git_vector_get(&commit->parent_ids, n);
+ return git_array_get(commit->parent_ids, n);
}
int git_commit_parent(
diff --git a/src/commit.h b/src/commit.h
index d0981b125..d452e2975 100644
--- a/src/commit.h
+++ b/src/commit.h
@@ -10,21 +10,22 @@
#include "git2/commit.h"
#include "tree.h"
#include "repository.h"
-#include "vector.h"
+#include "array.h"
#include <time.h>
struct git_commit {
git_object object;
- git_vector parent_ids;
+ git_array_t(git_oid) parent_ids;
git_oid tree_id;
git_signature *author;
git_signature *committer;
char *message_encoding;
- char *message;
+ char *raw_message;
+ char *raw_header;
};
void git_commit__free(void *commit);
diff --git a/src/commit_list.c b/src/commit_list.c
index bd5b5201a..64416e54d 100644
--- a/src/commit_list.c
+++ b/src/commit_list.c
@@ -36,7 +36,7 @@ git_commit_list *git_commit_list_insert_by_date(git_commit_list_node *item, git_
git_commit_list *p;
while ((p = *pp) != NULL) {
- if (git_commit_list_time_cmp(p->item, item) < 0)
+ if (git_commit_list_time_cmp(p->item, item) > 0)
break;
pp = &p->next;
diff --git a/src/common.h b/src/common.h
index 02d9ce9b6..159d31b2e 100644
--- a/src/common.h
+++ b/src/common.h
@@ -74,6 +74,30 @@ void giterr_set(int error_class, const char *string, ...);
int giterr_set_regex(const regex_t *regex, int error_code);
/**
+ * Gets the system error code for this thread.
+ */
+GIT_INLINE(int) giterr_system_last(void)
+{
+#ifdef GIT_WIN32
+ return GetLastError();
+#else
+ return errno;
+#endif
+}
+
+/**
+ * Sets the system error code for this thread.
+ */
+GIT_INLINE(void) giterr_system_set(int code)
+{
+#ifdef GIT_WIN32
+ SetLastError(code);
+#else
+ errno = code;
+#endif
+}
+
+/**
* Check a versioned structure for validity
*/
GIT_INLINE(int) giterr__check_version(const void *structure, unsigned int expected_max, const char *name)
diff --git a/src/config.c b/src/config.c
index 068c40260..0d9471383 100644
--- a/src/config.c
+++ b/src/config.c
@@ -315,30 +315,241 @@ int git_config_refresh(git_config *cfg)
* Loop over all the variables
*/
+typedef struct {
+ git_config_iterator parent;
+ git_config_iterator *current;
+ const git_config *cfg;
+ regex_t regex;
+ int has_regex;
+ size_t i;
+} all_iter;
+
+static int find_next_backend(size_t *out, const git_config *cfg, size_t i)
+{
+ file_internal *internal;
+
+ for (; i > 0; --i) {
+ internal = git_vector_get(&cfg->files, i - 1);
+ if (!internal || !internal->file)
+ continue;
+
+ *out = i;
+ return 0;
+ }
+
+ return -1;
+}
+
+static int all_iter_next(git_config_entry **entry, git_config_iterator *_iter)
+{
+ all_iter *iter = (all_iter *) _iter;
+ file_internal *internal;
+ git_config_backend *backend;
+ size_t i;
+ int error = 0;
+
+ if (iter->current != NULL &&
+ (error = iter->current->next(entry, iter->current)) == 0) {
+ return 0;
+ }
+
+ if (error < 0 && error != GIT_ITEROVER)
+ return error;
+
+ do {
+ if (find_next_backend(&i, iter->cfg, iter->i) < 0)
+ return GIT_ITEROVER;
+
+ internal = git_vector_get(&iter->cfg->files, i - 1);
+ backend = internal->file;
+ iter->i = i - 1;
+
+ if (iter->current)
+ iter->current->free(iter->current);
+
+ iter->current = NULL;
+ error = backend->iterator(&iter->current, backend);
+ if (error == GIT_ENOTFOUND)
+ continue;
+
+ if (error < 0)
+ return error;
+
+ error = iter->current->next(entry, iter->current);
+ /* If this backend is empty, then keep going */
+ if (error == GIT_ITEROVER)
+ continue;
+
+ return error;
+
+ } while(1);
+
+ return GIT_ITEROVER;
+}
+
+static int all_iter_glob_next(git_config_entry **entry, git_config_iterator *_iter)
+{
+ int error;
+ all_iter *iter = (all_iter *) _iter;
+
+ /*
+ * We use the "normal" function to grab the next one across
+ * backends and then apply the regex
+ */
+ while ((error = all_iter_next(entry, _iter)) == 0) {
+ /* skip non-matching keys if regexp was provided */
+ if (regexec(&iter->regex, (*entry)->name, 0, NULL, 0) != 0)
+ continue;
+
+ /* and simply return if we like the entry's name */
+ return 0;
+ }
+
+ return error;
+}
+
+static void all_iter_free(git_config_iterator *_iter)
+{
+ all_iter *iter = (all_iter *) _iter;
+
+ if (iter->current)
+ iter->current->free(iter->current);
+
+ git__free(iter);
+}
+
+static void all_iter_glob_free(git_config_iterator *_iter)
+{
+ all_iter *iter = (all_iter *) _iter;
+
+ regfree(&iter->regex);
+ all_iter_free(_iter);
+}
+
+int git_config_iterator_new(git_config_iterator **out, const git_config *cfg)
+{
+ all_iter *iter;
+
+ iter = git__calloc(1, sizeof(all_iter));
+ GITERR_CHECK_ALLOC(iter);
+
+ iter->parent.free = all_iter_free;
+ iter->parent.next = all_iter_next;
+
+ iter->i = cfg->files.length;
+ iter->cfg = cfg;
+
+ *out = (git_config_iterator *) iter;
+
+ return 0;
+}
+
+int git_config_iterator_glob_new(git_config_iterator **out, const git_config *cfg, const char *regexp)
+{
+ all_iter *iter;
+ int result;
+
+ if (regexp == NULL)
+ return git_config_iterator_new(out, cfg);
+
+ iter = git__calloc(1, sizeof(all_iter));
+ GITERR_CHECK_ALLOC(iter);
+
+ if ((result = regcomp(&iter->regex, regexp, REG_EXTENDED)) < 0) {
+ giterr_set_regex(&iter->regex, result);
+ regfree(&iter->regex);
+ return -1;
+ }
+
+ iter->parent.next = all_iter_glob_next;
+ iter->parent.free = all_iter_glob_free;
+ iter->i = cfg->files.length;
+ iter->cfg = cfg;
+
+ *out = (git_config_iterator *) iter;
+
+ return 0;
+}
+
int git_config_foreach(
const git_config *cfg, git_config_foreach_cb cb, void *payload)
{
return git_config_foreach_match(cfg, NULL, cb, payload);
}
+int git_config_backend_foreach_match(
+ git_config_backend *backend,
+ const char *regexp,
+ int (*fn)(const git_config_entry *, void *),
+ void *data)
+{
+ git_config_entry *entry;
+ git_config_iterator* iter;
+ regex_t regex;
+ int result = 0;
+
+ if (regexp != NULL) {
+ if ((result = regcomp(&regex, regexp, REG_EXTENDED)) < 0) {
+ giterr_set_regex(&regex, result);
+ regfree(&regex);
+ return -1;
+ }
+ }
+
+ if ((result = backend->iterator(&iter, backend)) < 0) {
+ iter = NULL;
+ return -1;
+ }
+
+ while(!(iter->next(&entry, iter) < 0)) {
+ /* skip non-matching keys if regexp was provided */
+ if (regexp && regexec(&regex, entry->name, 0, NULL, 0) != 0)
+ continue;
+
+ /* abort iterator on non-zero return value */
+ if (fn(entry, data)) {
+ giterr_clear();
+ result = GIT_EUSER;
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ if (regexp != NULL)
+ regfree(&regex);
+
+ iter->free(iter);
+
+ return result;
+}
+
int git_config_foreach_match(
const git_config *cfg,
const char *regexp,
git_config_foreach_cb cb,
void *payload)
{
- int ret = 0;
- size_t i;
- file_internal *internal;
- git_config_backend *file;
+ int error;
+ git_config_iterator *iter;
+ git_config_entry *entry;
- for (i = 0; i < cfg->files.length && ret == 0; ++i) {
- internal = git_vector_get(&cfg->files, i);
- file = internal->file;
- ret = file->foreach(file, regexp, cb, payload);
+ if ((error = git_config_iterator_glob_new(&iter, cfg, regexp)) < 0)
+ return error;
+
+ while ((error = git_config_next(&entry, iter)) == 0) {
+ if(cb(entry, payload)) {
+ giterr_clear();
+ error = GIT_EUSER;
+ break;
+ }
}
- return ret;
+ git_config_iterator_free(iter);
+
+ if (error == GIT_ITEROVER)
+ error = 0;
+
+ return error;
}
/**************
@@ -528,31 +739,114 @@ int git_config_get_entry(const git_config_entry **out, const git_config *cfg, co
return config_error_notfound(name);
}
-int git_config_get_multivar(
+int git_config_get_multivar_foreach(
const git_config *cfg, const char *name, const char *regexp,
git_config_foreach_cb cb, void *payload)
{
- file_internal *internal;
- git_config_backend *file;
- int ret = GIT_ENOTFOUND;
- size_t i;
+ int err, found;
+ git_config_iterator *iter;
+ git_config_entry *entry;
+
+ if ((err = git_config_multivar_iterator_new(&iter, cfg, name, regexp)) < 0)
+ return err;
+
+ found = 0;
+ while ((err = iter->next(&entry, iter)) == 0) {
+ found = 1;
+ if(cb(entry, payload)) {
+ iter->free(iter);
+ return GIT_EUSER;
+ }
+ }
- /*
- * This loop runs the "wrong" way 'round because we need to
- * look at every value from the most general to most specific
- */
- for (i = cfg->files.length; i > 0; --i) {
- internal = git_vector_get(&cfg->files, i - 1);
- if (!internal || !internal->file)
+ iter->free(iter);
+ if (err == GIT_ITEROVER)
+ err = 0;
+
+ if (found == 0 && err == 0)
+ err = config_error_notfound(name);
+
+ return err;
+}
+
+typedef struct {
+ git_config_iterator parent;
+ git_config_iterator *iter;
+ char *name;
+ regex_t regex;
+ int have_regex;
+} multivar_iter;
+
+static int multivar_iter_next(git_config_entry **entry, git_config_iterator *_iter)
+{
+ multivar_iter *iter = (multivar_iter *) _iter;
+ int error = 0;
+
+ while ((error = iter->iter->next(entry, iter->iter)) == 0) {
+ if (git__strcmp(iter->name, (*entry)->name))
continue;
- file = internal->file;
- ret = file->get_multivar(file, name, regexp, cb, payload);
- if (ret < 0 && ret != GIT_ENOTFOUND)
- return ret;
+ if (!iter->have_regex)
+ return 0;
+
+ if (regexec(&iter->regex, (*entry)->value, 0, NULL, 0) == 0)
+ return 0;
+ }
+
+ return error;
+}
+
+void multivar_iter_free(git_config_iterator *_iter)
+{
+ multivar_iter *iter = (multivar_iter *) _iter;
+
+ iter->iter->free(iter->iter);
+
+ git__free(iter->name);
+ regfree(&iter->regex);
+ git__free(iter);
+}
+
+int git_config_multivar_iterator_new(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp)
+{
+ multivar_iter *iter = NULL;
+ git_config_iterator *inner = NULL;
+ int error;
+
+ if ((error = git_config_iterator_new(&inner, cfg)) < 0)
+ return error;
+
+ iter = git__calloc(1, sizeof(multivar_iter));
+ GITERR_CHECK_ALLOC(iter);
+
+ if ((error = git_config__normalize_name(name, &iter->name)) < 0)
+ goto on_error;
+
+ if (regexp != NULL) {
+ error = regcomp(&iter->regex, regexp, REG_EXTENDED);
+ if (error < 0) {
+ giterr_set_regex(&iter->regex, error);
+ error = -1;
+ regfree(&iter->regex);
+ goto on_error;
+ }
+
+ iter->have_regex = 1;
}
- return (ret == GIT_ENOTFOUND) ? config_error_notfound(name) : 0;
+ iter->iter = inner;
+ iter->parent.free = multivar_iter_free;
+ iter->parent.next = multivar_iter_next;
+
+ *out = (git_config_iterator *) iter;
+
+ return 0;
+
+on_error:
+
+ inner->free(inner);
+ git__free(iter);
+ return error;
}
int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value)
@@ -568,6 +862,29 @@ int git_config_set_multivar(git_config *cfg, const char *name, const char *regex
return file->set_multivar(file, name, regexp, value);
}
+int git_config_delete_multivar(git_config *cfg, const char *name, const char *regexp)
+{
+ git_config_backend *file;
+ file_internal *internal;
+
+ internal = git_vector_get(&cfg->files, 0);
+ if (!internal || !internal->file)
+ return config_error_nofiles(name);
+ file = internal->file;
+
+ return file->del_multivar(file, name, regexp);
+}
+
+int git_config_next(git_config_entry **entry, git_config_iterator *iter)
+{
+ return iter->next(entry, iter);
+}
+
+void git_config_iterator_free(git_config_iterator *iter)
+{
+ iter->free(iter);
+}
+
static int git_config__find_file_to_path(
char *out, size_t outlen, int (*find)(git_buf *buf))
{
@@ -811,6 +1128,41 @@ fail_parse:
return -1;
}
+/* Take something the user gave us and make it nice for our hash function */
+int git_config__normalize_name(const char *in, char **out)
+{
+ char *name, *fdot, *ldot;
+
+ assert(in && out);
+
+ name = git__strdup(in);
+ GITERR_CHECK_ALLOC(name);
+
+ fdot = strchr(name, '.');
+ ldot = strrchr(name, '.');
+
+ if (fdot == NULL || fdot == name || ldot == NULL || !ldot[1])
+ goto invalid;
+
+ /* Validate and downcase up to first dot and after last dot */
+ if (git_config_file_normalize_section(name, fdot) < 0 ||
+ git_config_file_normalize_section(ldot + 1, NULL) < 0)
+ goto invalid;
+
+ /* If there is a middle range, make sure it doesn't have newlines */
+ while (fdot < ldot)
+ if (*fdot++ == '\n')
+ goto invalid;
+
+ *out = name;
+ return 0;
+
+invalid:
+ git__free(name);
+ giterr_set(GITERR_CONFIG, "Invalid config item name '%s'", in);
+ return GIT_EINVALIDSPEC;
+}
+
struct rename_data {
git_config *config;
git_buf *name;
diff --git a/src/config.h b/src/config.h
index c5c11ae14..01e8465cc 100644
--- a/src/config.h
+++ b/src/config.h
@@ -47,6 +47,9 @@ extern int git_config_rename_section(
* @param out the new backend
* @param path where the config file is located
*/
-extern int git_config_file__ondisk(struct git_config_backend **out, const char *path);
+extern int git_config_file__ondisk(git_config_backend **out, const char *path);
+
+extern int git_config__normalize_name(const char *in, char **out);
+
#endif
diff --git a/src/config_cache.c b/src/config_cache.c
index 84de3a5ed..6808521a3 100644
--- a/src/config_cache.c
+++ b/src/config_cache.c
@@ -67,6 +67,7 @@ static struct map_data _cvar_maps[] = {
{"core.ignorestat", NULL, 0, GIT_IGNORESTAT_DEFAULT },
{"core.trustctime", NULL, 0, GIT_TRUSTCTIME_DEFAULT },
{"core.abbrev", _cvar_map_int, 1, GIT_ABBREV_DEFAULT },
+ {"core.precomposeunicode", NULL, 0, GIT_PRECOMPOSE_DEFAULT },
};
int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar)
diff --git a/src/config_file.c b/src/config_file.c
index dec952115..15c8de49c 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -15,6 +15,7 @@
#include "git2/sys/config.h"
#include "git2/types.h"
#include "strmap.h"
+#include "array.h"
#include <ctype.h>
#include <sys/types.h>
@@ -25,8 +26,18 @@ GIT__USE_STRMAP;
typedef struct cvar_t {
struct cvar_t *next;
git_config_entry *entry;
+ int included; /* whether this is part of [include] */
} cvar_t;
+typedef struct git_config_file_iter {
+ git_config_iterator parent;
+ git_strmap_iter iter;
+ cvar_t* next_var;
+} git_config_file_iter;
+
+/* Max depth for [include] directives */
+#define MAX_INCLUDE_DEPTH 10
+
#define CVAR_LIST_HEAD(list) ((list)->head)
#define CVAR_LIST_TAIL(list) ((list)->tail)
@@ -65,34 +76,37 @@ typedef struct cvar_t {
(iter) && (((tmp) = CVAR_LIST_NEXT(iter) || 1));\
(iter) = (tmp))
+struct reader {
+ time_t file_mtime;
+ size_t file_size;
+ char *file_path;
+ git_buf buffer;
+ char *read_ptr;
+ int line_number;
+ int eof;
+};
+
typedef struct {
git_config_backend parent;
git_strmap *values;
- struct {
- git_buf buffer;
- char *read_ptr;
- int line_number;
- int eof;
- } reader;
+ git_array_t(struct reader) readers;
char *file_path;
- time_t file_mtime;
- size_t file_size;
git_config_level_t level;
} diskfile_backend;
-static int config_parse(diskfile_backend *cfg_file, git_config_level_t level);
-static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_value);
+static int config_parse(diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth);
+static int parse_variable(struct reader *reader, char **var_name, char **var_value);
static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value);
static char *escape_value(const char *ptr);
-static void set_parse_error(diskfile_backend *backend, int col, const char *error_str)
+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, backend->file_path, backend->reader.line_number, col);
+ error_str, reader->file_path, reader->line_number, col);
}
static void cvar_free(cvar_t *var)
@@ -106,6 +120,18 @@ static void cvar_free(cvar_t *var)
git__free(var);
}
+static int cvar_length(cvar_t *var)
+{
+ int length = 0;
+
+ while (var) {
+ length++;
+ var = var->next;
+ }
+
+ return length;
+}
+
int git_config_file_normalize_section(char *start, char *end)
{
char *scan;
@@ -118,7 +144,7 @@ int git_config_file_normalize_section(char *start, char *end)
if (end && scan >= end)
break;
if (isalnum(*scan))
- *scan = tolower(*scan);
+ *scan = (char)tolower(*scan);
else if (*scan != '-' || scan == start)
return GIT_EINVALIDSPEC;
}
@@ -129,41 +155,6 @@ int git_config_file_normalize_section(char *start, char *end)
return 0;
}
-/* Take something the user gave us and make it nice for our hash function */
-static int normalize_name(const char *in, char **out)
-{
- char *name, *fdot, *ldot;
-
- assert(in && out);
-
- name = git__strdup(in);
- GITERR_CHECK_ALLOC(name);
-
- fdot = strchr(name, '.');
- ldot = strrchr(name, '.');
-
- if (fdot == NULL || fdot == name || ldot == NULL || !ldot[1])
- goto invalid;
-
- /* Validate and downcase up to first dot and after last dot */
- if (git_config_file_normalize_section(name, fdot) < 0 ||
- git_config_file_normalize_section(ldot + 1, NULL) < 0)
- goto invalid;
-
- /* If there is a middle range, make sure it doesn't have newlines */
- while (fdot < ldot)
- if (*fdot++ == '\n')
- goto invalid;
-
- *out = name;
- return 0;
-
-invalid:
- git__free(name);
- giterr_set(GITERR_CONFIG, "Invalid config item name '%s'", in);
- return GIT_EINVALIDSPEC;
-}
-
static void free_vars(git_strmap *values)
{
cvar_t *var = NULL;
@@ -184,6 +175,7 @@ static void free_vars(git_strmap *values)
static int config_open(git_config_backend *cfg, git_config_level_t level)
{
int res;
+ struct reader *reader;
diskfile_backend *b = (diskfile_backend *)cfg;
b->level = level;
@@ -191,32 +183,52 @@ static int config_open(git_config_backend *cfg, git_config_level_t level)
b->values = git_strmap_alloc();
GITERR_CHECK_ALLOC(b->values);
- git_buf_init(&b->reader.buffer, 0);
+ git_array_init(b->readers);
+ reader = git_array_alloc(b->readers);
+ 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(
- &b->reader.buffer, b->file_path, &b->file_mtime, &b->file_size, NULL);
+ &reader->buffer, b->file_path, &reader->file_mtime, &reader->file_size, NULL);
/* It's fine if the file doesn't exist */
if (res == GIT_ENOTFOUND)
return 0;
- if (res < 0 || (res = config_parse(b, level)) < 0) {
+ if (res < 0 || (res = config_parse(b, reader, level, 0)) < 0) {
free_vars(b->values);
b->values = NULL;
}
- git_buf_free(&b->reader.buffer);
+ reader = git_array_get(b->readers, 0);
+ git_buf_free(&reader->buffer);
return res;
}
static int config_refresh(git_config_backend *cfg)
{
- int res, updated = 0;
+ int res = 0, updated = 0, any_updated = 0;
diskfile_backend *b = (diskfile_backend *)cfg;
git_strmap *old_values;
+ struct reader *reader;
+ uint32_t i;
- res = git_futils_readbuffer_updated(
- &b->reader.buffer, b->file_path, &b->file_mtime, &b->file_size, &updated);
- if (res < 0 || !updated)
+ for (i = 0; i < git_array_size(b->readers); i++) {
+ reader = git_array_get(b->readers, i);
+ res = git_futils_readbuffer_updated(
+ &reader->buffer, reader->file_path, &reader->file_mtime, &reader->file_size, &updated);
+
+ if (res < 0)
+ return (res == GIT_ENOTFOUND) ? 0 : res;
+
+ if (updated)
+ any_updated = 1;
+ }
+
+ if (!any_updated)
return (res == GIT_ENOTFOUND) ? 0 : res;
/* need to reload - store old values and prep for reload */
@@ -224,74 +236,88 @@ static int config_refresh(git_config_backend *cfg)
b->values = git_strmap_alloc();
GITERR_CHECK_ALLOC(b->values);
- if ((res = config_parse(b, b->level)) < 0) {
+ if ((res = config_parse(b, reader, b->level, 0)) < 0) {
free_vars(b->values);
b->values = old_values;
} else {
free_vars(old_values);
}
- git_buf_free(&b->reader.buffer);
+ git_buf_free(&reader->buffer);
return res;
}
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);
free_vars(backend->values);
git__free(backend);
}
-static int file_foreach(
- git_config_backend *backend,
- const char *regexp,
- int (*fn)(const git_config_entry *, void *),
- void *data)
+static void config_iterator_free(
+ git_config_iterator* iter)
{
- diskfile_backend *b = (diskfile_backend *)backend;
- cvar_t *var, *next_var;
- const char *key;
- regex_t regex;
- int result = 0;
+ git__free(iter);
+}
- if (!b->values)
- return 0;
+static int config_iterator_next(
+ git_config_entry **entry,
+ git_config_iterator *iter)
+{
+ git_config_file_iter *it = (git_config_file_iter *) iter;
+ diskfile_backend *b = (diskfile_backend *) it->parent.backend;
+ int err = 0;
+ cvar_t * var;
- if (regexp != NULL) {
- if ((result = regcomp(&regex, regexp, REG_EXTENDED)) < 0) {
- giterr_set_regex(&regex, result);
- regfree(&regex);
- return -1;
- }
+ if (it->next_var == NULL) {
+ err = git_strmap_next((void**) &var, &(it->iter), b->values);
+ } else {
+ var = it->next_var;
}
- git_strmap_foreach(b->values, key, var,
- for (; var != NULL; var = next_var) {
- next_var = CVAR_LIST_NEXT(var);
+ if (err < 0) {
+ it->next_var = NULL;
+ return err;
+ }
- /* skip non-matching keys if regexp was provided */
- if (regexp && regexec(&regex, key, 0, NULL, 0) != 0)
- continue;
+ *entry = var->entry;
+ it->next_var = CVAR_LIST_NEXT(var);
- /* abort iterator on non-zero return value */
- if (fn(var->entry, data)) {
- giterr_clear();
- result = GIT_EUSER;
- goto cleanup;
- }
- }
- );
+ return 0;
+}
-cleanup:
- if (regexp != NULL)
- regfree(&regex);
+static int config_iterator_new(
+ git_config_iterator **iter,
+ struct git_config_backend* backend)
+{
+ diskfile_backend *b = (diskfile_backend *)backend;
+ git_config_file_iter *it = git__calloc(1, sizeof(git_config_file_iter));
- return result;
+ GIT_UNUSED(b);
+
+ GITERR_CHECK_ALLOC(it);
+
+ it->parent.backend = backend;
+ it->iter = git_strmap_begin(b->values);
+ it->next_var = NULL;
+
+ it->parent.next = config_iterator_next;
+ it->parent.free = config_iterator_free;
+ *iter = (git_config_iterator *) it;
+
+ return 0;
}
static int config_set(git_config_backend *cfg, const char *name, const char *value)
@@ -302,7 +328,7 @@ static int config_set(git_config_backend *cfg, const char *name, const char *val
khiter_t pos;
int rval, ret;
- if ((rval = normalize_name(name, &key)) < 0)
+ if ((rval = git_config__normalize_name(name, &key)) < 0)
return rval;
/*
@@ -359,10 +385,10 @@ static int config_set(git_config_backend *cfg, const char *name, const char *val
GITERR_CHECK_ALLOC(esc_value);
}
- if (config_write(b, key, NULL, esc_value) < 0) {
+ if ((ret = config_write(b, key, NULL, esc_value)) < 0) {
git__free(esc_value);
cvar_free(var);
- return -1;
+ return ret;
}
git__free(esc_value);
@@ -384,82 +410,23 @@ static int config_get(const git_config_backend *cfg, const char *name, const git
char *key;
khiter_t pos;
int error;
-
- if ((error = normalize_name(name, &key)) < 0)
- return error;
-
- pos = git_strmap_lookup_index(b->values, key);
- git__free(key);
-
- /* no error message; the config system will write one */
- if (!git_strmap_valid_index(b->values, pos))
- return GIT_ENOTFOUND;
-
- *out = ((cvar_t *)git_strmap_value_at(b->values, pos))->entry;
-
- return 0;
-}
-
-static int config_get_multivar(
- git_config_backend *cfg,
- const char *name,
- const char *regex_str,
- int (*fn)(const git_config_entry *, void *),
- void *data)
-{
cvar_t *var;
- diskfile_backend *b = (diskfile_backend *)cfg;
- char *key;
- khiter_t pos;
- int error;
- if ((error = normalize_name(name, &key)) < 0)
+ if ((error = git_config__normalize_name(name, &key)) < 0)
return error;
pos = git_strmap_lookup_index(b->values, key);
git__free(key);
+ /* no error message; the config system will write one */
if (!git_strmap_valid_index(b->values, pos))
return GIT_ENOTFOUND;
var = git_strmap_value_at(b->values, pos);
+ while (var->next)
+ var = var->next;
- if (regex_str != NULL) {
- regex_t regex;
- int result;
-
- /* regex matching; build the regex */
- result = regcomp(&regex, regex_str, REG_EXTENDED);
- if (result < 0) {
- giterr_set_regex(&regex, result);
- regfree(&regex);
- return -1;
- }
-
- /* and throw the callback only on the variables that
- * match the regex */
- do {
- if (regexec(&regex, var->entry->value, 0, NULL, 0) == 0) {
- /* early termination by the user is not an error;
- * just break and return successfully */
- if (fn(var->entry, data) < 0)
- break;
- }
-
- var = var->next;
- } while (var != NULL);
- regfree(&regex);
- } else {
- /* no regex; go through all the variables */
- do {
- /* early termination by the user is not an error;
- * just break and return successfully */
- if (fn(var->entry, data) < 0)
- break;
-
- var = var->next;
- } while (var != NULL);
- }
+ *out = var->entry;
return 0;
}
@@ -477,7 +444,7 @@ static int config_set_multivar(
assert(regexp);
- if ((result = normalize_name(name, &key)) < 0)
+ if ((result = git_config__normalize_name(name, &key)) < 0)
return result;
pos = git_strmap_lookup_index(b->values, key);
@@ -550,7 +517,7 @@ static int config_delete(git_config_backend *cfg, const char *name)
int result;
khiter_t pos;
- if ((result = normalize_name(name, &key)) < 0)
+ if ((result = git_config__normalize_name(name, &key)) < 0)
return result;
pos = git_strmap_lookup_index(b->values, key);
@@ -576,6 +543,80 @@ static int config_delete(git_config_backend *cfg, const char *name)
return result;
}
+static int config_delete_multivar(git_config_backend *cfg, const char *name, const char *regexp)
+{
+ cvar_t *var, *prev = NULL, *new_head = NULL;
+ cvar_t **to_delete;
+ int to_delete_idx;
+ diskfile_backend *b = (diskfile_backend *)cfg;
+ char *key;
+ regex_t preg;
+ int result;
+ khiter_t pos;
+
+ if ((result = git_config__normalize_name(name, &key)) < 0)
+ return result;
+
+ pos = git_strmap_lookup_index(b->values, key);
+
+ if (!git_strmap_valid_index(b->values, pos)) {
+ giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name);
+ git__free(key);
+ return GIT_ENOTFOUND;
+ }
+
+ var = git_strmap_value_at(b->values, pos);
+
+ result = regcomp(&preg, regexp, REG_EXTENDED);
+ if (result < 0) {
+ git__free(key);
+ giterr_set_regex(&preg, result);
+ regfree(&preg);
+ return -1;
+ }
+
+ to_delete = git__calloc(cvar_length(var), sizeof(cvar_t *));
+ GITERR_CHECK_ALLOC(to_delete);
+ to_delete_idx = 0;
+
+ while (var != NULL) {
+ cvar_t *next = var->next;
+
+ if (regexec(&preg, var->entry->value, 0, NULL, 0) == 0) {
+ // If we are past the head, reattach previous node to next one,
+ // otherwise set the new head for the strmap.
+ if (prev != NULL) {
+ prev->next = next;
+ } else {
+ new_head = next;
+ }
+
+ to_delete[to_delete_idx++] = var;
+ } else {
+ prev = var;
+ }
+
+ var = next;
+ }
+
+ if (new_head != NULL) {
+ git_strmap_set_value_at(b->values, pos, new_head);
+ } else {
+ git_strmap_delete_at(b->values, pos);
+ }
+
+ if (to_delete_idx > 0)
+ result = config_write(b, key, &preg, NULL);
+
+ while (to_delete_idx-- > 0)
+ cvar_free(to_delete[to_delete_idx]);
+
+ git__free(key);
+ git__free(to_delete);
+ regfree(&preg);
+ return result;
+}
+
int git_config_file__ondisk(git_config_backend **out, const char *path)
{
diskfile_backend *backend;
@@ -590,11 +631,11 @@ int git_config_file__ondisk(git_config_backend **out, const char *path)
backend->parent.open = config_open;
backend->parent.get = config_get;
- backend->parent.get_multivar = config_get_multivar;
backend->parent.set = config_set;
backend->parent.set_multivar = config_set_multivar;
backend->parent.del = config_delete;
- backend->parent.foreach = file_foreach;
+ backend->parent.del_multivar = config_delete_multivar;
+ backend->parent.iterator = config_iterator_new;
backend->parent.refresh = config_refresh;
backend->parent.free = backend_free;
@@ -603,26 +644,26 @@ int git_config_file__ondisk(git_config_backend **out, const char *path)
return 0;
}
-static int cfg_getchar_raw(diskfile_backend *cfg)
+static int reader_getchar_raw(struct reader *reader)
{
int c;
- c = *cfg->reader.read_ptr++;
+ 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' && *cfg->reader.read_ptr == '\n') {
- cfg->reader.read_ptr++;
+ if (c == '\r' && *reader->read_ptr == '\n') {
+ reader->read_ptr++;
c = '\n';
}
if (c == '\n')
- cfg->reader.line_number++;
+ reader->line_number++;
if (c == 0) {
- cfg->reader.eof = 1;
+ reader->eof = 1;
c = '\n';
}
@@ -632,21 +673,23 @@ static int cfg_getchar_raw(diskfile_backend *cfg)
#define SKIP_WHITESPACE (1 << 1)
#define SKIP_COMMENTS (1 << 2)
-static int cfg_getchar(diskfile_backend *cfg_file, int flags)
+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(cfg_file->reader.read_ptr);
+ assert(reader->read_ptr);
- do c = cfg_getchar_raw(cfg_file);
- while (skip_whitespace && git__isspace(c) &&
- !cfg_file->reader.eof);
+ do {
+ c = reader_getchar_raw(reader);
+ } while (skip_whitespace && git__isspace(c) &&
+ !reader->eof);
if (skip_comments && (c == '#' || c == ';')) {
- do c = cfg_getchar_raw(cfg_file);
- while (c != '\n');
+ do {
+ c = reader_getchar_raw(reader);
+ } while (c != '\n');
}
return c;
@@ -655,23 +698,23 @@ static int cfg_getchar(diskfile_backend *cfg_file, int flags)
/*
* Read the next char, but don't move the reading pointer.
*/
-static int cfg_peek(diskfile_backend *cfg, int flags)
+static int reader_peek(struct reader *reader, int flags)
{
void *old_read_ptr;
int old_lineno, old_eof;
int ret;
- assert(cfg->reader.read_ptr);
+ assert(reader->read_ptr);
- old_read_ptr = cfg->reader.read_ptr;
- old_lineno = cfg->reader.line_number;
- old_eof = cfg->reader.eof;
+ old_read_ptr = reader->read_ptr;
+ old_lineno = reader->line_number;
+ old_eof = reader->eof;
- ret = cfg_getchar(cfg, flags);
+ ret = reader_getchar(reader, flags);
- cfg->reader.read_ptr = old_read_ptr;
- cfg->reader.line_number = old_lineno;
- cfg->reader.eof = old_eof;
+ reader->read_ptr = old_read_ptr;
+ reader->line_number = old_lineno;
+ reader->eof = old_eof;
return ret;
}
@@ -679,13 +722,13 @@ static int cfg_peek(diskfile_backend *cfg, int flags)
/*
* Read and consume a line, returning it in newly-allocated memory.
*/
-static char *cfg_readline(diskfile_backend *cfg, bool skip_whitespace)
+static char *reader_readline(struct reader *reader, bool skip_whitespace)
{
char *line = NULL;
char *line_src, *line_end;
size_t line_len;
- line_src = cfg->reader.read_ptr;
+ line_src = reader->read_ptr;
if (skip_whitespace) {
/* Skip empty empty lines */
@@ -714,10 +757,10 @@ static char *cfg_readline(diskfile_backend *cfg, bool skip_whitespace)
line_end++;
if (*line_end == '\0')
- cfg->reader.eof = 1;
+ reader->eof = 1;
- cfg->reader.line_number++;
- cfg->reader.read_ptr = line_end;
+ reader->line_number++;
+ reader->read_ptr = line_end;
return line;
}
@@ -725,11 +768,11 @@ static char *cfg_readline(diskfile_backend *cfg, bool skip_whitespace)
/*
* Consume a line, without storing it anywhere
*/
-static void cfg_consume_line(diskfile_backend *cfg)
+static void reader_consume_line(struct reader *reader)
{
char *line_start, *line_end;
- line_start = cfg->reader.read_ptr;
+ line_start = reader->read_ptr;
line_end = strchr(line_start, '\n');
/* No newline at EOF */
if(line_end == NULL){
@@ -740,10 +783,10 @@ static void cfg_consume_line(diskfile_backend *cfg)
line_end++;
if (*line_end == '\0')
- cfg->reader.eof = 1;
+ reader->eof = 1;
- cfg->reader.line_number++;
- cfg->reader.read_ptr = line_end;
+ reader->line_number++;
+ reader->read_ptr = line_end;
}
GIT_INLINE(int) config_keychar(int c)
@@ -751,12 +794,11 @@ GIT_INLINE(int) config_keychar(int c)
return isalnum(c) || c == '-';
}
-static int parse_section_header_ext(diskfile_backend *cfg, const char *line, const char *base_name, char **section_name)
+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;
- int quote_marks;
/*
* 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
@@ -767,7 +809,7 @@ static int parse_section_header_ext(diskfile_backend *cfg, const char *line, con
last_quote = strrchr(line, '"');
if (last_quote - first_quote == 0) {
- set_parse_error(cfg, 0, "Missing closing quotation mark in section header");
+ set_parse_error(reader, 0, "Missing closing quotation mark in section header");
return -1;
}
@@ -775,37 +817,30 @@ static int parse_section_header_ext(diskfile_backend *cfg, const char *line, con
git_buf_printf(&buf, "%s.", base_name);
rpos = 0;
- quote_marks = 0;
line = first_quote;
- c = line[rpos++];
+ 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 {
- if (quote_marks == 2) {
- set_parse_error(cfg, rpos, "Unexpected text after closing quotes");
+
+ switch (c) {
+ case 0:
+ set_parse_error(reader, 0, "Unexpected end-of-line in section header");
git_buf_free(&buf);
return -1;
- }
- switch (c) {
case '"':
- ++quote_marks;
- continue;
+ goto end_parse;
case '\\':
- c = line[rpos++];
-
- switch (c) {
- case '"':
- case '\\':
- break;
+ c = line[++rpos];
- default:
- set_parse_error(cfg, rpos, "Unsupported escape sequence");
+ if (c == 0) {
+ set_parse_error(reader, rpos, "Unexpected end-of-line in section header");
git_buf_free(&buf);
return -1;
}
@@ -814,29 +849,37 @@ static int parse_section_header_ext(diskfile_backend *cfg, const char *line, con
break;
}
- git_buf_putc(&buf, c);
- } while ((c = line[rpos++]) != ']');
+ git_buf_putc(&buf, (char)c);
+ c = line[++rpos];
+ } while (line + rpos < last_quote);
+
+end_parse:
+ 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;
}
-static int parse_section_header(diskfile_backend *cfg, char **section_out)
+static int parse_section_header(struct reader *reader, char **section_out)
{
char *name, *name_end;
int name_length, c, pos;
int result;
char *line;
- line = cfg_readline(cfg, true);
+ line = reader_readline(reader, true);
if (line == NULL)
return -1;
/* find the end of the variable's name */
- name_end = strchr(line, ']');
+ name_end = strrchr(line, ']');
if (name_end == NULL) {
git__free(line);
- set_parse_error(cfg, 0, "Missing ']' in section header");
+ set_parse_error(reader, 0, "Missing ']' in section header");
return -1;
}
@@ -855,14 +898,14 @@ static int parse_section_header(diskfile_backend *cfg, char **section_out)
do {
if (git__isspace(c)){
name[name_length] = '\0';
- result = parse_section_header_ext(cfg, line, name, section_out);
+ 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(cfg, pos, "Unexpected character in header");
+ set_parse_error(reader, pos, "Unexpected character in header");
goto fail_parse;
}
@@ -871,7 +914,7 @@ static int parse_section_header(diskfile_backend *cfg, char **section_out)
} while ((c = line[pos++]) != ']');
if (line[pos - 1] != ']') {
- set_parse_error(cfg, pos, "Unexpected end of file");
+ set_parse_error(reader, pos, "Unexpected end of file");
goto fail_parse;
}
@@ -888,14 +931,14 @@ fail_parse:
return -1;
}
-static int skip_bom(diskfile_backend *cfg)
+static int skip_bom(struct reader *reader)
{
git_bom_t bom;
int bom_offset = git_buf_text_detect_bom(&bom,
- &cfg->reader.buffer, cfg->reader.read_ptr - cfg->reader.buffer.ptr);
+ &reader->buffer, reader->read_ptr - reader->buffer.ptr);
if (bom == GIT_BOM_UTF8)
- cfg->reader.read_ptr += bom_offset;
+ reader->read_ptr += bom_offset;
/* TODO: reference implementation is pretty stupid with BoM */
@@ -965,7 +1008,16 @@ static int strip_comments(char *line, int in_quotes)
return quote_count;
}
-static int config_parse(diskfile_backend *cfg_file, git_config_level_t level)
+static int included_path(git_buf *out, const char *dir, const char *path)
+{
+ /* From the user's home */
+ if (path[0] == '~' && path[1] == '/')
+ return git_futils_find_global_file(out, &path[1]);
+
+ return git_path_join_unrooted(out, path, dir, NULL);
+}
+
+static int config_parse(diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth)
{
int c;
char *current_section = NULL;
@@ -975,39 +1027,46 @@ static int config_parse(diskfile_backend *cfg_file, git_config_level_t level)
git_buf buf = GIT_BUF_INIT;
int result = 0;
khiter_t pos;
+ uint32_t reader_idx;
+
+ if (depth >= MAX_INCLUDE_DEPTH) {
+ giterr_set(GITERR_CONFIG, "Maximum config include depth reached");
+ return -1;
+ }
+ reader_idx = git_array_size(cfg_file->readers) - 1;
/* Initialize the reading position */
- cfg_file->reader.read_ptr = cfg_file->reader.buffer.ptr;
- cfg_file->reader.eof = 0;
+ reader->read_ptr = reader->buffer.ptr;
+ reader->eof = 0;
/* If the file is empty, there's nothing for us to do */
- if (*cfg_file->reader.read_ptr == '\0')
+ if (*reader->read_ptr == '\0')
return 0;
- skip_bom(cfg_file);
+ skip_bom(reader);
- while (result == 0 && !cfg_file->reader.eof) {
+ while (result == 0 && !reader->eof) {
- c = cfg_peek(cfg_file, SKIP_WHITESPACE);
+ c = reader_peek(reader, SKIP_WHITESPACE);
switch (c) {
case '\n': /* EOF when peeking, set EOF in the reader to exit the loop */
- cfg_file->reader.eof = 1;
+ reader->eof = 1;
break;
case '[': /* section header, new section begins */
git__free(current_section);
current_section = NULL;
- result = parse_section_header(cfg_file, &current_section);
+ result = parse_section_header(reader, &current_section);
break;
case ';':
case '#':
- cfg_consume_line(cfg_file);
+ reader_consume_line(reader);
break;
default: /* assume variable declaration */
- result = parse_variable(cfg_file, &var_name, &var_value);
+ result = parse_variable(reader, &var_name, &var_value);
if (result < 0)
break;
@@ -1028,6 +1087,7 @@ static int config_parse(diskfile_backend *cfg_file, git_config_level_t level)
var->entry->name = git_buf_detach(&buf);
var->entry->value = var_value;
var->entry->level = level;
+ var->included = !!depth;
/* Add or append the new config option */
pos = git_strmap_lookup_index(cfg_file->values, var->entry->name);
@@ -1044,6 +1104,42 @@ static int config_parse(diskfile_backend *cfg_file, git_config_level_t level)
existing->next = var;
}
+ 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(cfg_file->readers);
+ /* The reader may have been reallocated */
+ reader = git_array_get(cfg_file->readers, reader_idx);
+ memset(r, 0, sizeof(struct reader));
+ if ((result = git_path_dirname_r(&path, reader->file_path)) < 0)
+ break;
+
+ /* We need to know out index in the array, as the next config_parse call may realloc */
+ index = git_array_size(cfg_file->readers) - 1;
+ dir = git_buf_detach(&path);
+ result = included_path(&path, dir, var->entry->value);
+ git__free(dir);
+
+ if (result < 0)
+ break;
+
+ r->file_path = git_buf_detach(&path);
+ git_buf_init(&r->buffer, 0);
+ if ((result = git_futils_readbuffer_updated(&r->buffer, r->file_path, &r->file_mtime,
+ &r->file_size, NULL)) < 0)
+ break;
+
+ result = config_parse(cfg_file, r, level, depth+1);
+ r = git_array_get(cfg_file->readers, index);
+ git_buf_free(&r->buffer);
+
+ if (result < 0)
+ break;
+ }
+
break;
}
}
@@ -1082,6 +1178,24 @@ static int write_section(git_filebuf *file, const char *key)
return result;
}
+static const char *quotes_for_value(const char *value)
+{
+ const char *ptr;
+
+ if (value[0] == ' ' || value[0] == '\0')
+ return "\"";
+
+ for (ptr = value; *ptr; ++ptr) {
+ if (*ptr == ';' || *ptr == '#')
+ return "\"";
+ }
+
+ if (ptr[-1] == ' ')
+ return "\"";
+
+ return "";
+}
+
/*
* This is pretty much the parsing, except we write out anything we don't have
*/
@@ -1089,38 +1203,44 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
{
int result, c;
int section_matches = 0, last_section_matched = 0, preg_replaced = 0, write_trailer = 0;
- const char *pre_end = NULL, *post_start = NULL, *data_start;
+ const char *pre_end = NULL, *post_start = NULL, *data_start, *write_start;
char *current_section = NULL, *section, *name, *ldot;
git_filebuf file = GIT_FILEBUF_INIT;
+ struct reader *reader = git_array_get(cfg->readers, 0);
/* We need to read in our own config file */
- result = git_futils_readbuffer(&cfg->reader.buffer, cfg->file_path);
+ result = git_futils_readbuffer(&reader->buffer, cfg->file_path);
/* Initialise the reading position */
if (result == GIT_ENOTFOUND) {
- cfg->reader.read_ptr = NULL;
- cfg->reader.eof = 1;
+ reader->read_ptr = NULL;
+ reader->eof = 1;
data_start = NULL;
- git_buf_clear(&cfg->reader.buffer);
+ git_buf_clear(&reader->buffer);
} else if (result == 0) {
- cfg->reader.read_ptr = cfg->reader.buffer.ptr;
- cfg->reader.eof = 0;
- data_start = cfg->reader.read_ptr;
+ reader->read_ptr = reader->buffer.ptr;
+ reader->eof = 0;
+ data_start = reader->read_ptr;
} else {
return -1; /* OS error when reading the file */
}
+ write_start = data_start;
+
/* Lock the file */
- if (git_filebuf_open(&file, cfg->file_path, 0) < 0)
- return -1;
+ if ((result = git_filebuf_open(
+ &file, cfg->file_path, 0, GIT_CONFIG_FILE_MODE)) < 0) {
+ git_buf_free(&reader->buffer);
+ return result;
+ }
- skip_bom(cfg);
+ skip_bom(reader);
ldot = strrchr(key, '.');
name = ldot + 1;
section = git__strndup(key, ldot - key);
- while (!cfg->reader.eof) {
- c = cfg_peek(cfg, SKIP_WHITESPACE);
+ while (!reader->eof) {
+ c = reader_peek(reader, SKIP_WHITESPACE);
if (c == '\0') { /* We've arrived at the end of the file */
break;
@@ -1133,11 +1253,11 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
* new section. If we actually want to replace it, the
* default case will take care of updating them.
*/
- pre_end = post_start = cfg->reader.read_ptr;
+ pre_end = post_start = reader->read_ptr;
git__free(current_section);
current_section = NULL;
- if (parse_section_header(cfg, &current_section) < 0)
+ if (parse_section_header(reader, &current_section) < 0)
goto rewrite_fail;
/* Keep track of when it stops matching */
@@ -1146,7 +1266,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
}
else if (c == ';' || c == '#') {
- cfg_consume_line(cfg);
+ reader_consume_line(reader);
}
else {
@@ -1162,15 +1282,15 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
*/
if (!section_matches) {
if (!last_section_matched) {
- cfg_consume_line(cfg);
+ reader_consume_line(reader);
continue;
}
} else {
int has_matched = 0;
char *var_name, *var_value;
- pre_end = cfg->reader.read_ptr;
- if (parse_variable(cfg, &var_name, &var_value) < 0)
+ pre_end = reader->read_ptr;
+ if (parse_variable(reader, &var_name, &var_value) < 0)
goto rewrite_fail;
/* First try to match the name of the variable */
@@ -1189,23 +1309,28 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
if (!has_matched)
continue;
- post_start = cfg->reader.read_ptr;
+ post_start = reader->read_ptr;
}
/* We've found the variable we wanted to change, so
* write anything up to it */
- git_filebuf_write(&file, data_start, pre_end - data_start);
+ git_filebuf_write(&file, write_start, pre_end - write_start);
preg_replaced = 1;
/* Then replace the variable. If the value is NULL, it
* means we want to delete it, so don't write anything. */
if (value != NULL) {
- git_filebuf_printf(&file, "\t%s = %s\n", name, value);
+ const char *q = quotes_for_value(value);
+ git_filebuf_printf(&file, "\t%s = %s%s%s\n", name, q, value, q);
}
- /* multiline variable? we need to keep reading lines to match */
- if (preg != NULL) {
- data_start = post_start;
+ /*
+ * If we have a multivar, we should keep looking for entries,
+ * but only if we're in the right section. Otherwise we'll end up
+ * looping on the edge of a matching and a non-matching section.
+ */
+ if (section_matches && preg != NULL) {
+ write_start = post_start;
continue;
}
@@ -1232,12 +1357,14 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
*/
if (write_trailer) {
/* Write out rest of the file */
- git_filebuf_write(&file, post_start, cfg->reader.buffer.size - (post_start - data_start));
+ git_filebuf_write(&file, post_start, reader->buffer.size - (post_start - data_start));
} else {
if (preg_replaced) {
- git_filebuf_printf(&file, "\n%s", data_start);
+ git_filebuf_printf(&file, "\n%s", write_start);
} else {
- git_filebuf_write(&file, cfg->reader.buffer.ptr, cfg->reader.buffer.size);
+ const char *q;
+
+ git_filebuf_write(&file, reader->buffer.ptr, reader->buffer.size);
/* And now if we just need to add a variable */
if (!section_matches && write_section(&file, section) < 0)
@@ -1253,10 +1380,11 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
}
/* If we are here, there is at least a section line */
- if (cfg->reader.buffer.size > 0 && *(cfg->reader.buffer.ptr + cfg->reader.buffer.size - 1) != '\n')
+ if (reader->buffer.size > 0 && *(reader->buffer.ptr + reader->buffer.size - 1) != '\n')
git_filebuf_write(&file, "\n", 1);
- git_filebuf_printf(&file, "\t%s = %s\n", name, value);
+ q = quotes_for_value(value);
+ git_filebuf_printf(&file, "\t%s = %s%s%s\n", name, q, value, q);
}
}
@@ -1264,10 +1392,10 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
git__free(current_section);
/* refresh stats - if this errors, then commit will error too */
- (void)git_filebuf_stats(&cfg->file_mtime, &cfg->file_size, &file);
+ (void)git_filebuf_stats(&reader->file_mtime, &reader->file_size, &file);
- result = git_filebuf_commit(&file, GIT_CONFIG_FILE_MODE);
- git_buf_free(&cfg->reader.buffer);
+ result = git_filebuf_commit(&file);
+ git_buf_free(&reader->buffer);
return result;
@@ -1276,7 +1404,7 @@ rewrite_fail:
git__free(current_section);
git_filebuf_cleanup(&file);
- git_buf_free(&cfg->reader.buffer);
+ git_buf_free(&reader->buffer);
return -1;
}
@@ -1293,6 +1421,9 @@ static char *escape_value(const char *ptr)
assert(ptr);
len = strlen(ptr);
+ if (!len)
+ return git__calloc(1, sizeof(char));
+
git_buf_grow(&buf, len);
while (*ptr != '\0') {
@@ -1365,19 +1496,19 @@ static int is_multiline_var(const char *str)
return (end > str) && (count & 1);
}
-static int parse_multiline_variable(diskfile_backend *cfg, git_buf *value, int in_quotes)
+static int parse_multiline_variable(struct reader *reader, git_buf *value, int in_quotes)
{
char *line = NULL, *proc_line = NULL;
int quote_count;
/* Check that the next line exists */
- line = cfg_readline(cfg, false);
+ line = reader_readline(reader, false);
if (line == NULL)
return -1;
/* We've reached the end of the file, there is input missing */
if (line[0] == '\0') {
- set_parse_error(cfg, 0, "Unexpected end of file while parsing multine var");
+ set_parse_error(reader, 0, "Unexpected end of file while parsing multine var");
git__free(line);
return -1;
}
@@ -1387,7 +1518,7 @@ static int parse_multiline_variable(diskfile_backend *cfg, git_buf *value, int i
/* If it was just a comment, pretend it didn't exist */
if (line[0] == '\0') {
git__free(line);
- return parse_multiline_variable(cfg, value, quote_count);
+ return parse_multiline_variable(reader, value, quote_count);
/* TODO: unbounded recursion. This **could** be exploitable */
}
@@ -1395,7 +1526,7 @@ static int parse_multiline_variable(diskfile_backend *cfg, git_buf *value, int i
* standard, this character **has** to be last one in the buf, with
* no whitespace after it */
assert(is_multiline_var(value->ptr));
- git_buf_truncate(value, git_buf_len(value) - 1);
+ git_buf_shorten(value, 1);
proc_line = fixup_line(line, in_quotes);
if (proc_line == NULL) {
@@ -1412,19 +1543,19 @@ static int parse_multiline_variable(diskfile_backend *cfg, git_buf *value, int i
* keep putting stuff in the buffer
*/
if (is_multiline_var(value->ptr))
- return parse_multiline_variable(cfg, value, quote_count);
+ return parse_multiline_variable(reader, value, quote_count);
return 0;
}
-static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_value)
+static int parse_variable(struct reader *reader, char **var_name, char **var_value)
{
const char *var_end = NULL;
const char *value_start = NULL;
char *line;
int quote_count;
- line = cfg_readline(cfg, true);
+ line = reader_readline(reader, true);
if (line == NULL)
return -1;
@@ -1459,7 +1590,7 @@ static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_val
GITERR_CHECK_ALLOC(proc_line);
git_buf_puts(&multi_value, proc_line);
git__free(proc_line);
- if (parse_multiline_variable(cfg, &multi_value, quote_count) < 0 || git_buf_oom(&multi_value)) {
+ if (parse_multiline_variable(reader, &multi_value, quote_count) < 0 || git_buf_oom(&multi_value)) {
git__free(*var_name);
git__free(line);
git_buf_free(&multi_value);
diff --git a/src/config_file.h b/src/config_file.h
index 7445859c4..d4a1a4061 100644
--- a/src/config_file.h
+++ b/src/config_file.h
@@ -42,7 +42,7 @@ GIT_INLINE(int) git_config_file_foreach(
int (*fn)(const git_config_entry *entry, void *data),
void *data)
{
- return cfg->foreach(cfg, NULL, fn, data);
+ return git_config_backend_foreach_match(cfg, NULL, fn, data);
}
GIT_INLINE(int) git_config_file_foreach_match(
@@ -51,7 +51,7 @@ GIT_INLINE(int) git_config_file_foreach_match(
int (*fn)(const git_config_entry *entry, void *data),
void *data)
{
- return cfg->foreach(cfg, regexp, fn, data);
+ return git_config_backend_foreach_match(cfg, regexp, fn, data);
}
extern int git_config_file_normalize_section(char *start, char *end);
diff --git a/src/crlf.c b/src/crlf.c
index 65039f9cc..b25c02cce 100644
--- a/src/crlf.c
+++ b/src/crlf.c
@@ -8,6 +8,7 @@
#include "git2/attr.h"
#include "git2/blob.h"
#include "git2/index.h"
+#include "git2/sys/filter.h"
#include "common.h"
#include "fileops.h"
@@ -19,13 +20,11 @@
struct crlf_attrs {
int crlf_action;
int eol;
+ int auto_crlf;
};
struct crlf_filter {
git_filter f;
- struct crlf_attrs attrs;
- git_repository *repo;
- char path[GIT_FLEX_ARRAY];
};
static int check_crlf(const char *value)
@@ -76,41 +75,10 @@ static int crlf_input_action(struct crlf_attrs *ca)
return ca->crlf_action;
}
-static int crlf_load_attributes(struct crlf_attrs *ca, git_repository *repo, const char *path)
+static int has_cr_in_index(const git_filter_source *src)
{
-#define NUM_CONV_ATTRS 3
-
- static const char *attr_names[NUM_CONV_ATTRS] = {
- "crlf", "eol", "text",
- };
-
- const char *attr_vals[NUM_CONV_ATTRS];
- int error;
-
- error = git_attr_get_many(attr_vals,
- repo, 0, path, NUM_CONV_ATTRS, attr_names);
-
- if (error == GIT_ENOTFOUND) {
- ca->crlf_action = GIT_CRLF_GUESS;
- ca->eol = GIT_EOL_UNSET;
- return 0;
- }
-
- if (error == 0) {
- ca->crlf_action = check_crlf(attr_vals[2]); /* text */
- if (ca->crlf_action == GIT_CRLF_GUESS)
- ca->crlf_action = check_crlf(attr_vals[0]); /* clrf */
-
- ca->eol = check_eol(attr_vals[1]); /* eol */
- return 0;
- }
-
- return -1;
-}
-
-static int has_cr_in_index(git_filter *self)
-{
- struct crlf_filter *filter = (struct crlf_filter *)self;
+ git_repository *repo = git_filter_source_repo(src);
+ const char *path = git_filter_source_path(src);
git_index *index;
const git_index_entry *entry;
git_blob *blob;
@@ -118,19 +86,22 @@ static int has_cr_in_index(git_filter *self)
git_off_t blobsize;
bool found_cr;
- if (git_repository_index__weakptr(&index, filter->repo) < 0) {
+ if (!path)
+ return false;
+
+ if (git_repository_index__weakptr(&index, repo) < 0) {
giterr_clear();
return false;
}
- if (!(entry = git_index_get_bypath(index, filter->path, 0)) &&
- !(entry = git_index_get_bypath(index, filter->path, 1)))
+ if (!(entry = git_index_get_bypath(index, path, 0)) &&
+ !(entry = git_index_get_bypath(index, path, 1)))
return false;
if (!S_ISREG(entry->mode)) /* don't crlf filter non-blobs */
return true;
- if (git_blob_lookup(&blob, filter->repo, &entry->oid) < 0)
+ if (git_blob_lookup(&blob, repo, &entry->oid) < 0)
return false;
blobcontent = git_blob_rawcontent(blob);
@@ -147,27 +118,24 @@ static int has_cr_in_index(git_filter *self)
}
static int crlf_apply_to_odb(
- git_filter *self, git_buf *dest, const git_buf *source)
+ struct crlf_attrs *ca,
+ git_buf *to,
+ const git_buf *from,
+ const git_filter_source *src)
{
- struct crlf_filter *filter = (struct crlf_filter *)self;
-
- assert(self && dest && source);
-
/* Empty file? Nothing to do */
- if (git_buf_len(source) == 0)
+ if (!git_buf_len(from))
return 0;
/* Heuristics to see if we can skip the conversion.
* Straight from Core Git.
*/
- if (filter->attrs.crlf_action == GIT_CRLF_AUTO ||
- filter->attrs.crlf_action == GIT_CRLF_GUESS) {
-
+ if (ca->crlf_action == GIT_CRLF_AUTO || ca->crlf_action == GIT_CRLF_GUESS) {
git_buf_text_stats stats;
- /* Check heuristics for binary vs text... */
- if (git_buf_text_gather_stats(&stats, source, false))
- return -1;
+ /* Check heuristics for binary vs text - returns true if binary */
+ if (git_buf_text_gather_stats(&stats, from, false))
+ return GIT_PASSTHROUGH;
/*
* We're currently not going to even try to convert stuff
@@ -175,28 +143,28 @@ static int crlf_apply_to_odb(
* stuff?
*/
if (stats.cr != stats.crlf)
- return -1;
+ return GIT_PASSTHROUGH;
- if (filter->attrs.crlf_action == GIT_CRLF_GUESS) {
+ if (ca->crlf_action == GIT_CRLF_GUESS) {
/*
* If the file in the index has any CR in it, do not convert.
* This is the new safer autocrlf handling.
*/
- if (has_cr_in_index(self))
- return -1;
+ if (has_cr_in_index(src))
+ return GIT_PASSTHROUGH;
}
if (!stats.cr)
- return -1;
+ return GIT_PASSTHROUGH;
}
/* Actually drop the carriage returns */
- return git_buf_text_crlf_to_lf(dest, source);
+ return git_buf_text_crlf_to_lf(to, from);
}
-static const char *line_ending(struct crlf_filter *filter)
+static const char *line_ending(struct crlf_attrs *ca)
{
- switch (filter->attrs.crlf_action) {
+ switch (ca->crlf_action) {
case GIT_CRLF_BINARY:
case GIT_CRLF_INPUT:
return "\n";
@@ -213,11 +181,9 @@ static const char *line_ending(struct crlf_filter *filter)
goto line_ending_error;
}
- switch (filter->attrs.eol) {
+ switch (ca->eol) {
case GIT_EOL_UNSET:
- return GIT_EOL_NATIVE == GIT_EOL_CRLF
- ? "\r\n"
- : "\n";
+ return GIT_EOL_NATIVE == GIT_EOL_CRLF ? "\r\n" : "\n";
case GIT_EOL_CRLF:
return "\r\n";
@@ -235,41 +201,64 @@ line_ending_error:
}
static int crlf_apply_to_workdir(
- git_filter *self, git_buf *dest, const git_buf *source)
+ struct crlf_attrs *ca, git_buf *to, const git_buf *from)
{
- struct crlf_filter *filter = (struct crlf_filter *)self;
const char *workdir_ending = NULL;
- assert(self && dest && source);
-
/* Empty file? Nothing to do. */
- if (git_buf_len(source) == 0)
- return -1;
+ if (git_buf_len(from) == 0)
+ return 0;
+
+ /* Don't filter binary files */
+ if (git_buf_text_is_binary(from))
+ return GIT_PASSTHROUGH;
/* Determine proper line ending */
- workdir_ending = line_ending(filter);
+ workdir_ending = line_ending(ca);
if (!workdir_ending)
return -1;
- if (!strcmp("\n", workdir_ending)) /* do nothing for \n ending */
- return -1;
- /* for now, only lf->crlf conversion is supported here */
- assert(!strcmp("\r\n", workdir_ending));
- return git_buf_text_lf_to_crlf(dest, source);
+ if (!strcmp("\n", workdir_ending)) {
+ if (ca->crlf_action == GIT_CRLF_GUESS && ca->auto_crlf)
+ return GIT_PASSTHROUGH;
+
+ if (git_buf_find(from, '\r') < 0)
+ return GIT_PASSTHROUGH;
+
+ if (git_buf_text_crlf_to_lf(to, from) < 0)
+ return -1;
+ } else {
+ /* only other supported option is lf->crlf conversion */
+ assert(!strcmp("\r\n", workdir_ending));
+
+ if (git_buf_text_lf_to_crlf(to, from) < 0)
+ return -1;
+ }
+
+ return 0;
}
-static int find_and_add_filter(
- git_vector *filters, git_repository *repo, const char *path,
- int (*apply)(struct git_filter *self, git_buf *dest, const git_buf *source))
+static int crlf_check(
+ git_filter *self,
+ void **payload, /* points to NULL ptr on entry, may be set */
+ const git_filter_source *src,
+ const char **attr_values)
{
- struct crlf_attrs ca;
- struct crlf_filter *filter;
- size_t pathlen;
int error;
+ struct crlf_attrs ca;
+
+ GIT_UNUSED(self);
- /* Load gitattributes for the path */
- if ((error = crlf_load_attributes(&ca, repo, path)) < 0)
- return error;
+ if (!attr_values) {
+ ca.crlf_action = GIT_CRLF_GUESS;
+ ca.eol = GIT_EOL_UNSET;
+ } else {
+ ca.crlf_action = check_crlf(attr_values[2]); /* text */
+ if (ca.crlf_action == GIT_CRLF_GUESS)
+ ca.crlf_action = check_crlf(attr_values[0]); /* clrf */
+ ca.eol = check_eol(attr_values[1]); /* eol */
+ }
+ ca.auto_crlf = GIT_AUTO_CRLF_DEFAULT;
/*
* Use the core Git logic to see if we should perform CRLF for this file
@@ -278,41 +267,64 @@ static int find_and_add_filter(
ca.crlf_action = crlf_input_action(&ca);
if (ca.crlf_action == GIT_CRLF_BINARY)
- return 0;
+ return GIT_PASSTHROUGH;
if (ca.crlf_action == GIT_CRLF_GUESS) {
- int auto_crlf;
-
- if ((error = git_repository__cvar(&auto_crlf, repo, GIT_CVAR_AUTO_CRLF)) < 0)
+ error = git_repository__cvar(
+ &ca.auto_crlf, git_filter_source_repo(src), GIT_CVAR_AUTO_CRLF);
+ if (error < 0)
return error;
- if (auto_crlf == GIT_AUTO_CRLF_FALSE)
- return 0;
+ if (ca.auto_crlf == GIT_AUTO_CRLF_FALSE)
+ return GIT_PASSTHROUGH;
}
- /* If we're good, we create a new filter object and push it
- * into the filters array */
- pathlen = strlen(path);
- filter = git__malloc(sizeof(struct crlf_filter) + pathlen + 1);
- GITERR_CHECK_ALLOC(filter);
+ *payload = git__malloc(sizeof(ca));
+ GITERR_CHECK_ALLOC(*payload);
+ memcpy(*payload, &ca, sizeof(ca));
+
+ return 0;
+}
- filter->f.apply = apply;
- filter->f.do_free = NULL;
- memcpy(&filter->attrs, &ca, sizeof(struct crlf_attrs));
- filter->repo = repo;
- memcpy(filter->path, path, pathlen + 1);
+static int crlf_apply(
+ git_filter *self,
+ void **payload, /* may be read and/or set */
+ git_buf *to,
+ const git_buf *from,
+ const git_filter_source *src)
+{
+ /* initialize payload in case `check` was bypassed */
+ if (!*payload) {
+ int error = crlf_check(self, payload, src, NULL);
+ if (error < 0 && error != GIT_PASSTHROUGH)
+ return error;
+ }
- return git_vector_insert(filters, filter);
+ if (git_filter_source_mode(src) == GIT_FILTER_SMUDGE)
+ return crlf_apply_to_workdir(*payload, to, from);
+ else
+ return crlf_apply_to_odb(*payload, to, from, src);
}
-int git_filter_add__crlf_to_odb(
- git_vector *filters, git_repository *repo, const char *path)
+static void crlf_cleanup(
+ git_filter *self,
+ void *payload)
{
- return find_and_add_filter(filters, repo, path, &crlf_apply_to_odb);
+ GIT_UNUSED(self);
+ git__free(payload);
}
-int git_filter_add__crlf_to_workdir(
- git_vector *filters, git_repository *repo, const char *path)
+git_filter *git_crlf_filter_new(void)
{
- return find_and_add_filter(filters, repo, path, &crlf_apply_to_workdir);
+ struct crlf_filter *f = git__calloc(1, sizeof(struct crlf_filter));
+
+ f->f.version = GIT_FILTER_VERSION;
+ f->f.attributes = "crlf eol text";
+ f->f.initialize = NULL;
+ f->f.shutdown = git_filter_free;
+ f->f.check = crlf_check;
+ f->f.apply = crlf_apply;
+ f->f.cleanup = crlf_cleanup;
+
+ return (git_filter *)f;
}
diff --git a/src/date.c b/src/date.c
index 48841e4f9..7849c2f02 100644
--- a/src/date.c
+++ b/src/date.c
@@ -823,15 +823,13 @@ static void pending_number(struct tm *tm, int *num)
}
static git_time_t approxidate_str(const char *date,
- const struct timeval *tv,
- int *error_ret)
+ time_t time_sec,
+ int *error_ret)
{
int number = 0;
int touched = 0;
struct tm tm = {0}, now;
- time_t time_sec;
- time_sec = tv->tv_sec;
p_localtime_r(&time_sec, &tm);
now = tm;
@@ -861,7 +859,7 @@ static git_time_t approxidate_str(const char *date,
int git__date_parse(git_time_t *out, const char *date)
{
- struct timeval tv;
+ time_t time_sec;
git_time_t timestamp;
int offset, error_ret=0;
@@ -870,7 +868,9 @@ int git__date_parse(git_time_t *out, const char *date)
return 0;
}
- p_gettimeofday(&tv, NULL);
- *out = approxidate_str(date, &tv, &error_ret);
+ if (time(&time_sec) == -1)
+ return -1;
+
+ *out = approxidate_str(date, time_sec, &error_ret);
return error_ret;
}
diff --git a/src/diff.c b/src/diff.c
index 26e117402..4c33a0213 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -13,6 +13,7 @@
#include "pathspec.h"
#include "index.h"
#include "odb.h"
+#include "submodule.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)
@@ -20,7 +21,7 @@
(VAL) ? ((DIFF)->opts.flags | (FLAG)) : ((DIFF)->opts.flags & ~(VAL))
static git_diff_delta *diff_delta__alloc(
- git_diff_list *diff,
+ git_diff *diff,
git_delta_t status,
const char *path)
{
@@ -49,7 +50,7 @@ static git_diff_delta *diff_delta__alloc(
}
static int diff_notify(
- const git_diff_list *diff,
+ const git_diff *diff,
const git_diff_delta *delta,
const char *matched_pathspec)
{
@@ -61,14 +62,17 @@ static int diff_notify(
}
static int diff_delta__from_one(
- git_diff_list *diff,
- git_delta_t status,
+ git_diff *diff,
+ git_delta_t status,
const git_index_entry *entry)
{
git_diff_delta *delta;
const char *matched_pathspec;
int notify_res;
+ if ((entry->flags & GIT_IDXENTRY_VALID) != 0)
+ return 0;
+
if (status == GIT_DELTA_IGNORED &&
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_IGNORED))
return 0;
@@ -77,15 +81,11 @@ static int diff_delta__from_one(
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNTRACKED))
return 0;
- if (entry->mode == GIT_FILEMODE_COMMIT &&
- DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES))
- return 0;
-
- if (!git_pathspec_match_path(
+ if (!git_pathspec__match(
&diff->pathspec, entry->path,
DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH),
- DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE),
- &matched_pathspec))
+ DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE),
+ &matched_pathspec, NULL))
return 0;
delta = diff_delta__alloc(diff, status, entry->path);
@@ -93,6 +93,7 @@ static int diff_delta__from_one(
/* This fn is just for single-sided diffs */
assert(status != GIT_DELTA_MODIFIED);
+ delta->nfiles = 1;
if (delta->status == GIT_DELTA_DELETED) {
delta->old_file.mode = entry->mode;
@@ -123,7 +124,7 @@ static int diff_delta__from_one(
}
static int diff_delta__from_two(
- git_diff_list *diff,
+ git_diff *diff,
git_delta_t status,
const git_index_entry *old_entry,
uint32_t old_mode,
@@ -140,11 +141,6 @@ static int diff_delta__from_two(
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNMODIFIED))
return 0;
- if (old_entry->mode == GIT_FILEMODE_COMMIT &&
- new_entry->mode == GIT_FILEMODE_COMMIT &&
- DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES))
- return 0;
-
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
uint32_t temp_mode = old_mode;
const git_index_entry *temp_entry = old_entry;
@@ -156,6 +152,7 @@ static int diff_delta__from_two(
delta = diff_delta__alloc(diff, status, canonical_path);
GITERR_CHECK_ALLOC(delta);
+ delta->nfiles = 2;
git_oid_cpy(&delta->old_file.oid, &old_entry->oid);
delta->old_file.size = old_entry->file_size;
@@ -189,7 +186,7 @@ static int diff_delta__from_two(
}
static git_diff_delta *diff_delta__last_for_item(
- git_diff_list *diff,
+ git_diff *diff,
const git_index_entry *item)
{
git_diff_delta *delta = git_vector_last(&diff->deltas);
@@ -247,6 +244,11 @@ GIT_INLINE(const char *) diff_delta__path(const git_diff_delta *delta)
return str;
}
+const char *git_diff_delta__path(const git_diff_delta *delta)
+{
+ return diff_delta__path(delta);
+}
+
int git_diff_delta__cmp(const void *a, const void *b)
{
const git_diff_delta *da = a, *db = b;
@@ -261,6 +263,26 @@ int git_diff_delta__casecmp(const void *a, const void *b)
return val ? val : ((int)da->status - (int)db->status);
}
+GIT_INLINE(const char *) diff_delta__i2w_path(const git_diff_delta *delta)
+{
+ return delta->old_file.path ?
+ delta->old_file.path : delta->new_file.path;
+}
+
+int git_diff_delta__i2w_cmp(const void *a, const void *b)
+{
+ const git_diff_delta *da = a, *db = b;
+ int val = strcmp(diff_delta__i2w_path(da), diff_delta__i2w_path(db));
+ return val ? val : ((int)da->status - (int)db->status);
+}
+
+int git_diff_delta__i2w_casecmp(const void *a, const void *b)
+{
+ const git_diff_delta *da = a, *db = b;
+ int val = strcasecmp(diff_delta__i2w_path(da), diff_delta__i2w_path(db));
+ return val ? val : ((int)da->status - (int)db->status);
+}
+
bool git_diff_delta__should_skip(
const git_diff_options *opts, const git_diff_delta *delta)
{
@@ -323,13 +345,13 @@ static const char *diff_mnemonic_prefix(
return pfx;
}
-static git_diff_list *diff_list_alloc(
+static git_diff *diff_list_alloc(
git_repository *repo,
git_iterator *old_iter,
git_iterator *new_iter)
{
git_diff_options dflt = GIT_DIFF_OPTIONS_INIT;
- git_diff_list *diff = git__calloc(1, sizeof(git_diff_list));
+ git_diff *diff = git__calloc(1, sizeof(git_diff));
if (!diff)
return NULL;
@@ -343,7 +365,7 @@ static git_diff_list *diff_list_alloc(
if (git_vector_init(&diff->deltas, 0, git_diff_delta__cmp) < 0 ||
git_pool_init(&diff->pool, 1, 0) < 0) {
- git_diff_list_free(diff);
+ git_diff_free(diff);
return NULL;
}
@@ -351,14 +373,14 @@ static git_diff_list *diff_list_alloc(
* the ignore_case bit set */
if (!git_iterator_ignore_case(old_iter) &&
!git_iterator_ignore_case(new_iter)) {
- diff->opts.flags &= ~GIT_DIFF_DELTAS_ARE_ICASE;
+ diff->opts.flags &= ~GIT_DIFF_IGNORE_CASE;
diff->strcomp = git__strcmp;
diff->strncomp = git__strncmp;
diff->pfxcomp = git__prefixcmp;
diff->entrycomp = git_index_entry__cmp;
} else {
- diff->opts.flags |= GIT_DIFF_DELTAS_ARE_ICASE;
+ diff->opts.flags |= GIT_DIFF_IGNORE_CASE;
diff->strcomp = git__strcasecmp;
diff->strncomp = git__strncasecmp;
@@ -372,7 +394,7 @@ static git_diff_list *diff_list_alloc(
}
static int diff_list_apply_options(
- git_diff_list *diff,
+ git_diff *diff,
const git_diff_options *opts)
{
git_config *cfg;
@@ -382,12 +404,12 @@ static int diff_list_apply_options(
if (opts) {
/* copy user options (except case sensitivity info from iterators) */
- bool icase = DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE);
+ bool icase = DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE);
memcpy(&diff->opts, opts, sizeof(diff->opts));
- DIFF_FLAG_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE, icase);
+ DIFF_FLAG_SET(diff, GIT_DIFF_IGNORE_CASE, icase);
/* initialize pathspec from options */
- if (git_pathspec_init(&diff->pathspec, &opts->pathspec, pool) < 0)
+ if (git_pathspec__vinit(&diff->pathspec, &opts->pathspec, pool) < 0)
return -1;
}
@@ -396,7 +418,7 @@ static int diff_list_apply_options(
diff->opts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE;
/* flag INCLUDE_UNTRACKED_CONTENT implies INCLUDE_UNTRACKED */
- if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_UNTRACKED_CONTENT))
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_SHOW_UNTRACKED_CONTENT))
diff->opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
/* load config values that affect diff behavior */
@@ -407,7 +429,7 @@ static int diff_list_apply_options(
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_HAS_SYMLINKS;
if (!git_repository__cvar(&val, repo, GIT_CVAR_IGNORESTAT) && val)
- diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_ASSUME_UNCHANGED;
+ diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_IGNORE_STAT;
if ((diff->opts.flags & GIT_DIFF_IGNORE_FILEMODE) == 0 &&
!git_repository__cvar(&val, repo, GIT_CVAR_FILEMODE) && val)
@@ -423,10 +445,28 @@ static int diff_list_apply_options(
/* If not given explicit `opts`, check `diff.xyz` configs */
if (!opts) {
- diff->opts.context_lines = config_int(cfg, "diff.context", 3);
+ int context = config_int(cfg, "diff.context", 3);
+ diff->opts.context_lines = context >= 0 ? (uint16_t)context : 3;
+
+ /* add other defaults here */
+ }
+
+ /* Reverse src info if diff is reversed */
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
+ git_iterator_type_t tmp_src = diff->old_src;
+ diff->old_src = diff->new_src;
+ diff->new_src = tmp_src;
+ }
- if (config_bool(cfg, "diff.ignoreSubmodules", 0))
- diff->opts.flags |= GIT_DIFF_IGNORE_SUBMODULES;
+ /* if ignore_submodules not explicitly set, check diff config */
+ if (diff->opts.ignore_submodules <= 0) {
+ const char *str;
+
+ if (git_config_get_string(&str , cfg, "diff.ignoreSubmodules") < 0)
+ giterr_clear();
+ else if (str != NULL &&
+ git_submodule_parse_ignore(&diff->opts.ignore_submodules, str) < 0)
+ giterr_clear();
}
/* if either prefix is not set, figure out appropriate value */
@@ -454,15 +494,15 @@ static int diff_list_apply_options(
return -1;
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
- const char *swap = diff->opts.old_prefix;
- diff->opts.old_prefix = diff->opts.new_prefix;
- diff->opts.new_prefix = swap;
+ const char *tmp_prefix = diff->opts.old_prefix;
+ diff->opts.old_prefix = diff->opts.new_prefix;
+ diff->opts.new_prefix = tmp_prefix;
}
return 0;
}
-static void diff_list_free(git_diff_list *diff)
+static void diff_list_free(git_diff *diff)
{
git_diff_delta *delta;
unsigned int i;
@@ -473,14 +513,14 @@ static void diff_list_free(git_diff_list *diff)
}
git_vector_free(&diff->deltas);
- git_pathspec_free(&diff->pathspec);
+ git_pathspec__vfree(&diff->pathspec);
git_pool_clear(&diff->pool);
git__memzero(diff, sizeof(*diff));
git__free(diff);
}
-void git_diff_list_free(git_diff_list *diff)
+void git_diff_free(git_diff *diff)
{
if (!diff)
return;
@@ -488,7 +528,7 @@ void git_diff_list_free(git_diff_list *diff)
GIT_REFCOUNT_DEC(diff, diff_list_free);
}
-void git_diff_list_addref(git_diff_list *diff)
+void git_diff_addref(git_diff *diff)
{
GIT_REFCOUNT_INC(diff);
}
@@ -541,21 +581,21 @@ int git_diff__oid_for_file(
giterr_set(GITERR_OS, "File size overflow (for 32-bits) on '%s'", path);
result = -1;
} else {
- git_vector filters = GIT_VECTOR_INIT;
+ git_filter_list *fl = NULL;
- result = git_filters_load(&filters, repo, path, GIT_FILTER_TO_ODB);
- if (result >= 0) {
+ result = git_filter_list_load(&fl, repo, NULL, path, GIT_FILTER_TO_ODB);
+ if (!result) {
int fd = git_futils_open_ro(full_path.ptr);
if (fd < 0)
result = fd;
else {
result = git_odb__hashfd_filtered(
- oid, fd, (size_t)size, GIT_OBJ_BLOB, &filters);
+ oid, fd, (size_t)size, GIT_OBJ_BLOB, fl);
p_close(fd);
}
- }
- git_filters_free(&filters);
+ git_filter_list_free(fl);
+ }
}
cleanup:
@@ -584,45 +624,54 @@ typedef struct {
static int maybe_modified_submodule(
git_delta_t *status,
git_oid *found_oid,
- git_diff_list *diff,
+ git_diff *diff,
diff_in_progress *info)
{
int error = 0;
git_submodule *sub;
unsigned int sm_status = 0;
- const git_oid *sm_oid;
+ git_submodule_ignore_t ign = diff->opts.ignore_submodules;
*status = GIT_DELTA_UNMODIFIED;
- if (!DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES) &&
- !(error = git_submodule_lookup(
- &sub, diff->repo, info->nitem->path)) &&
- git_submodule_ignore(sub) != GIT_SUBMODULE_IGNORE_ALL &&
- !(error = git_submodule_status(&sm_status, sub)))
- {
- /* check IS_WD_UNMODIFIED because this case is only used
- * when the new side of the diff is the working directory
- */
- if (!GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(sm_status))
- *status = GIT_DELTA_MODIFIED;
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES) ||
+ ign == GIT_SUBMODULE_IGNORE_ALL)
+ return 0;
- /* grab OID while we are here */
- if (git_oid_iszero(&info->nitem->oid) &&
- (sm_oid = git_submodule_wd_id(sub)) != NULL)
- git_oid_cpy(found_oid, sm_oid);
- }
+ if ((error = git_submodule_lookup(
+ &sub, diff->repo, info->nitem->path)) < 0) {
- /* GIT_EEXISTS means a dir with .git in it was found - ignore it */
- if (error == GIT_EEXISTS) {
- giterr_clear();
- error = 0;
+ /* GIT_EEXISTS means dir with .git in it was found - ignore it */
+ if (error == GIT_EEXISTS) {
+ giterr_clear();
+ error = 0;
+ }
+ return error;
}
- return error;
+ if (ign <= 0 && git_submodule_ignore(sub) == GIT_SUBMODULE_IGNORE_ALL)
+ return 0;
+
+ if ((error = git_submodule__status(
+ &sm_status, NULL, NULL, found_oid, sub, ign)) < 0)
+ return error;
+
+ /* check IS_WD_UNMODIFIED because this case is only used
+ * when the new side of the diff is the working directory
+ */
+ if (!GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(sm_status))
+ *status = GIT_DELTA_MODIFIED;
+
+ /* now that we have a HEAD OID, check if HEAD moved */
+ if ((sm_status & GIT_SUBMODULE_STATUS_IN_WD) != 0 &&
+ !git_oid_equal(&info->oitem->oid, found_oid))
+ *status = GIT_DELTA_MODIFIED;
+
+ return 0;
}
static int maybe_modified(
- git_diff_list *diff,
+ git_diff *diff,
diff_in_progress *info)
{
git_oid noid;
@@ -634,11 +683,11 @@ static int maybe_modified(
bool new_is_workdir = (info->new_iter->type == GIT_ITERATOR_TYPE_WORKDIR);
const char *matched_pathspec;
- if (!git_pathspec_match_path(
+ if (!git_pathspec__match(
&diff->pathspec, oitem->path,
DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH),
- DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE),
- &matched_pathspec))
+ DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE),
+ &matched_pathspec, NULL))
return 0;
memset(&noid, 0, sizeof(noid));
@@ -655,9 +704,8 @@ static int maybe_modified(
nmode = (nmode & ~MODE_BITS_MASK) | (omode & MODE_BITS_MASK);
/* support "assume unchanged" (poorly, b/c we still stat everything) */
- if ((diff->diffcaps & GIT_DIFFCAPS_ASSUME_UNCHANGED) != 0)
- status = (oitem->flags_extended & GIT_IDXENTRY_INTENT_TO_ADD) ?
- GIT_DELTA_MODIFIED : GIT_DELTA_UNMODIFIED;
+ if ((oitem->flags & GIT_IDXENTRY_VALID) != 0)
+ status = GIT_DELTA_UNMODIFIED;
/* support "skip worktree" index bit */
else if ((oitem->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE) != 0)
@@ -719,7 +767,7 @@ static int maybe_modified(
/* if we got here and decided that the files are modified, but we
* haven't calculated the OID of the new item, then calculate it now
*/
- if (status != GIT_DELTA_UNMODIFIED && git_oid_iszero(&nitem->oid)) {
+ if (status == GIT_DELTA_MODIFIED && git_oid_iszero(&nitem->oid)) {
if (git_oid_iszero(&noid)) {
if (git_diff__oid_for_file(diff->repo,
nitem->path, nitem->mode, nitem->file_size, &noid) < 0)
@@ -741,7 +789,7 @@ static int maybe_modified(
}
static bool entry_is_prefixed(
- git_diff_list *diff,
+ git_diff *diff,
const git_index_entry *item,
const git_index_entry *prefix_item)
{
@@ -758,7 +806,7 @@ static bool entry_is_prefixed(
}
static int diff_scan_inside_untracked_dir(
- git_diff_list *diff, diff_in_progress *info, git_delta_t *delta_type)
+ git_diff *diff, diff_in_progress *info, git_delta_t *delta_type)
{
int error = 0;
git_buf base = GIT_BUF_INIT;
@@ -824,7 +872,7 @@ done:
}
static int handle_unmatched_new_item(
- git_diff_list *diff, diff_in_progress *info)
+ git_diff *diff, diff_in_progress *info)
{
int error = 0;
const git_index_entry *nitem = info->nitem;
@@ -842,7 +890,7 @@ static int handle_unmatched_new_item(
git_buf_clear(&info->ignore_prefix);
}
- if (S_ISDIR(nitem->mode)) {
+ if (nitem->mode == GIT_FILEMODE_TREE) {
bool recurse_into_dir = contains_oitem;
/* if not already inside an ignored dir, check if this is ignored */
@@ -873,7 +921,7 @@ static int handle_unmatched_new_item(
*/
if (!recurse_into_dir &&
delta_type == GIT_DELTA_UNTRACKED &&
- DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_FAST_UNTRACKED_DIRS))
+ DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS))
{
git_diff_delta *last;
@@ -946,6 +994,16 @@ static int handle_unmatched_new_item(
else if (info->new_iter->type != GIT_ITERATOR_TYPE_WORKDIR)
delta_type = GIT_DELTA_ADDED;
+ else if (nitem->mode == GIT_FILEMODE_COMMIT) {
+ git_submodule *sm;
+
+ /* ignore things that are not actual submodules */
+ if (git_submodule_lookup(&sm, info->repo, nitem->path) != 0) {
+ giterr_clear();
+ delta_type = GIT_DELTA_IGNORED;
+ }
+ }
+
/* Actually create the record for this item if necessary */
if ((error = diff_delta__from_one(diff, delta_type, nitem)) < 0)
return error;
@@ -969,7 +1027,7 @@ static int handle_unmatched_new_item(
}
static int handle_unmatched_old_item(
- git_diff_list *diff, diff_in_progress *info)
+ git_diff *diff, diff_in_progress *info)
{
int error = diff_delta__from_one(diff, GIT_DELTA_DELETED, info->oitem);
if (error < 0)
@@ -1001,7 +1059,7 @@ static int handle_unmatched_old_item(
}
static int handle_matched_item(
- git_diff_list *diff, diff_in_progress *info)
+ git_diff *diff, diff_in_progress *info)
{
int error = 0;
@@ -1016,7 +1074,7 @@ static int handle_matched_item(
}
int git_diff__from_iterators(
- git_diff_list **diff_ptr,
+ git_diff **diff_ptr,
git_repository *repo,
git_iterator *old_iter,
git_iterator *new_iter,
@@ -1024,7 +1082,7 @@ int git_diff__from_iterators(
{
int error = 0;
diff_in_progress info;
- git_diff_list *diff;
+ git_diff *diff;
*diff_ptr = NULL;
@@ -1037,7 +1095,7 @@ int git_diff__from_iterators(
git_buf_init(&info.ignore_prefix, 0);
/* make iterators have matching icase behavior */
- if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE)) {
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE)) {
if ((error = git_iterator_set_ignore_case(old_iter, true)) < 0 ||
(error = git_iterator_set_ignore_case(new_iter, true)) < 0)
goto cleanup;
@@ -1085,7 +1143,7 @@ cleanup:
if (!error)
*diff_ptr = diff;
else
- git_diff_list_free(diff);
+ git_diff_free(diff);
git_buf_free(&info.ignore_prefix);
@@ -1102,7 +1160,7 @@ cleanup:
} while (0)
int git_diff_tree_to_tree(
- git_diff_list **diff,
+ git_diff **diff,
git_repository *repo,
git_tree *old_tree,
git_tree *new_tree,
@@ -1117,7 +1175,7 @@ int git_diff_tree_to_tree(
* currently case insensitive, unless the user explicitly asked
* for case insensitivity
*/
- if (opts && (opts->flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0)
+ if (opts && (opts->flags & GIT_DIFF_IGNORE_CASE) != 0)
iflag = GIT_ITERATOR_IGNORE_CASE;
DIFF_FROM_ITERATORS(
@@ -1128,8 +1186,19 @@ int git_diff_tree_to_tree(
return error;
}
+static int diff_load_index(git_index **index, git_repository *repo)
+{
+ int error = git_repository_index__weakptr(index, repo);
+
+ /* reload the repository index when user did not pass one in */
+ if (!error && git_index_read(*index, false) < 0)
+ giterr_clear();
+
+ return error;
+}
+
int git_diff_tree_to_index(
- git_diff_list **diff,
+ git_diff **diff,
git_repository *repo,
git_tree *old_tree,
git_index *index,
@@ -1140,7 +1209,7 @@ int git_diff_tree_to_index(
assert(diff && repo);
- if (!index && (error = git_repository_index__weakptr(&index, repo)) < 0)
+ if (!index && (error = diff_load_index(&index, repo)) < 0)
return error;
if (index->ignore_case) {
@@ -1157,9 +1226,9 @@ int git_diff_tree_to_index(
git_index__set_ignore_case(index, true);
if (!error) {
- git_diff_list *d = *diff;
+ git_diff *d = *diff;
- d->opts.flags |= GIT_DIFF_DELTAS_ARE_ICASE;
+ d->opts.flags |= GIT_DIFF_IGNORE_CASE;
d->strcomp = git__strcasecmp;
d->strncomp = git__strncasecmp;
d->pfxcomp = git__prefixcmp_icase;
@@ -1174,7 +1243,7 @@ int git_diff_tree_to_index(
}
int git_diff_index_to_workdir(
- git_diff_list **diff,
+ git_diff **diff,
git_repository *repo,
git_index *index,
const git_diff_options *opts)
@@ -1183,7 +1252,7 @@ int git_diff_index_to_workdir(
assert(diff && repo);
- if (!index && (error = git_repository_index__weakptr(&index, repo)) < 0)
+ if (!index && (error = diff_load_index(&index, repo)) < 0)
return error;
DIFF_FROM_ITERATORS(
@@ -1195,9 +1264,8 @@ int git_diff_index_to_workdir(
return error;
}
-
int git_diff_tree_to_workdir(
- git_diff_list **diff,
+ git_diff **diff,
git_repository *repo,
git_tree *old_tree,
const git_diff_options *opts)
@@ -1215,16 +1283,60 @@ int git_diff_tree_to_workdir(
return error;
}
-size_t git_diff_num_deltas(git_diff_list *diff)
+int git_diff_tree_to_workdir_with_index(
+ git_diff **diff,
+ git_repository *repo,
+ git_tree *old_tree,
+ const git_diff_options *opts)
+{
+ int error = 0;
+ git_diff *d1 = NULL, *d2 = NULL;
+ git_index *index = NULL;
+
+ assert(diff && repo);
+
+ if ((error = diff_load_index(&index, repo)) < 0)
+ return error;
+
+ if (!(error = git_diff_tree_to_index(&d1, repo, old_tree, index, opts)) &&
+ !(error = git_diff_index_to_workdir(&d2, repo, index, opts)))
+ error = git_diff_merge(d1, d2);
+
+ git_diff_free(d2);
+
+ if (error) {
+ git_diff_free(d1);
+ d1 = NULL;
+ }
+
+ *diff = d1;
+ return error;
+}
+
+int git_diff_options_init(git_diff_options *options, unsigned int version)
+{
+ git_diff_options template = GIT_DIFF_OPTIONS_INIT;
+
+ if (version != template.version) {
+ giterr_set(GITERR_INVALID,
+ "Invalid version %d for git_diff_options", (int)version);
+ return -1;
+ }
+
+ memcpy(options, &template, sizeof(*options));
+ return 0;
+}
+
+size_t git_diff_num_deltas(const git_diff *diff)
{
assert(diff);
- return (size_t)diff->deltas.length;
+ return diff->deltas.length;
}
-size_t git_diff_num_deltas_of_type(git_diff_list *diff, git_delta_t type)
+size_t git_diff_num_deltas_of_type(const git_diff *diff, git_delta_t type)
{
size_t i, count = 0;
- git_diff_delta *delta;
+ const git_diff_delta *delta;
assert(diff);
@@ -1235,9 +1347,20 @@ size_t git_diff_num_deltas_of_type(git_diff_list *diff, git_delta_t type)
return count;
}
+const git_diff_delta *git_diff_get_delta(const git_diff *diff, size_t idx)
+{
+ assert(diff);
+ return git_vector_get(&diff->deltas, idx);
+}
+
+int git_diff_is_sorted_icase(const git_diff *diff)
+{
+ return (diff->opts.flags & GIT_DIFF_IGNORE_CASE) != 0;
+}
+
int git_diff__paired_foreach(
- git_diff_list *head2idx,
- git_diff_list *idx2wd,
+ git_diff *head2idx,
+ git_diff *idx2wd,
int (*cb)(git_diff_delta *h2i, git_diff_delta *i2w, void *payload),
void *payload)
{
@@ -1245,10 +1368,12 @@ int git_diff__paired_foreach(
git_diff_delta *h2i, *i2w;
size_t i, j, i_max, j_max;
int (*strcomp)(const char *, const char *) = git__strcmp;
- bool icase_mismatch;
+ bool h2i_icase, i2w_icase, icase_mismatch;
i_max = head2idx ? head2idx->deltas.length : 0;
j_max = idx2wd ? idx2wd->deltas.length : 0;
+ if (!i_max && !j_max)
+ return 0;
/* At some point, tree-to-index diffs will probably never ignore case,
* even if that isn't true now. Index-to-workdir diffs may or may not
@@ -1258,24 +1383,35 @@ int git_diff__paired_foreach(
* Therefore the main thing we need to do here is make sure the diffs
* are traversed in a compatible order. To do this, we temporarily
* resort a mismatched diff to get the order correct.
+ *
+ * In order to traverse renames in the index->workdir, we need to
+ * ensure that we compare the index name on both sides, so we
+ * always sort by the old name in the i2w list.
*/
+ h2i_icase = head2idx != NULL &&
+ (head2idx->opts.flags & GIT_DIFF_IGNORE_CASE) != 0;
+
+ i2w_icase = idx2wd != NULL &&
+ (idx2wd->opts.flags & GIT_DIFF_IGNORE_CASE) != 0;
+
icase_mismatch =
- (head2idx != NULL && idx2wd != NULL &&
- ((head2idx->opts.flags ^ idx2wd->opts.flags) & GIT_DIFF_DELTAS_ARE_ICASE));
-
- /* force case-sensitive delta sort */
- if (icase_mismatch) {
- if (head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) {
- git_vector_set_cmp(&head2idx->deltas, git_diff_delta__cmp);
- git_vector_sort(&head2idx->deltas);
- } else {
- git_vector_set_cmp(&idx2wd->deltas, git_diff_delta__cmp);
- git_vector_sort(&idx2wd->deltas);
- }
+ (head2idx != NULL && idx2wd != NULL && h2i_icase != i2w_icase);
+
+ if (icase_mismatch && h2i_icase) {
+ git_vector_set_cmp(&head2idx->deltas, git_diff_delta__cmp);
+ git_vector_sort(&head2idx->deltas);
}
- else if (head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE)
+
+ if (i2w_icase && !icase_mismatch) {
strcomp = git__strcasecmp;
+ git_vector_set_cmp(&idx2wd->deltas, git_diff_delta__i2w_casecmp);
+ git_vector_sort(&idx2wd->deltas);
+ } else if (idx2wd != NULL) {
+ git_vector_set_cmp(&idx2wd->deltas, git_diff_delta__i2w_cmp);
+ git_vector_sort(&idx2wd->deltas);
+ }
+
for (i = 0, j = 0; i < i_max || j < j_max; ) {
h2i = head2idx ? GIT_VECTOR_GET(&head2idx->deltas, i) : NULL;
i2w = idx2wd ? GIT_VECTOR_GET(&idx2wd->deltas, j) : NULL;
@@ -1299,14 +1435,16 @@ int git_diff__paired_foreach(
}
/* restore case-insensitive delta sort */
- if (icase_mismatch) {
- if (head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) {
- git_vector_set_cmp(&head2idx->deltas, git_diff_delta__casecmp);
- git_vector_sort(&head2idx->deltas);
- } else {
- git_vector_set_cmp(&idx2wd->deltas, git_diff_delta__casecmp);
- git_vector_sort(&idx2wd->deltas);
- }
+ if (icase_mismatch && h2i_icase) {
+ git_vector_set_cmp(&head2idx->deltas, git_diff_delta__casecmp);
+ git_vector_sort(&head2idx->deltas);
+ }
+
+ /* restore idx2wd sort by new path */
+ if (idx2wd != NULL) {
+ git_vector_set_cmp(&idx2wd->deltas,
+ i2w_icase ? git_diff_delta__casecmp : git_diff_delta__cmp);
+ git_vector_sort(&idx2wd->deltas);
}
return 0;
diff --git a/src/diff.h b/src/diff.h
index 6ef03ee7c..2c9298a5f 100644
--- a/src/diff.h
+++ b/src/diff.h
@@ -16,13 +16,14 @@
#include "iterator.h"
#include "repository.h"
#include "pool.h"
+#include "odb.h"
#define DIFF_OLD_PREFIX_DEFAULT "a/"
#define DIFF_NEW_PREFIX_DEFAULT "b/"
enum {
GIT_DIFFCAPS_HAS_SYMLINKS = (1 << 0), /* symlinks on platform? */
- GIT_DIFFCAPS_ASSUME_UNCHANGED = (1 << 1), /* use stat? */
+ GIT_DIFFCAPS_IGNORE_STAT = (1 << 1), /* use stat? */
GIT_DIFFCAPS_TRUST_MODE_BITS = (1 << 2), /* use st_mode? */
GIT_DIFFCAPS_TRUST_CTIME = (1 << 3), /* use st_ctime? */
GIT_DIFFCAPS_USE_DEV = (1 << 4), /* use st_dev? */
@@ -51,7 +52,7 @@ enum {
#define GIT_DIFF__VERBOSE (1 << 30)
-struct git_diff_list {
+struct git_diff {
git_refcount rc;
git_repository *repo;
git_diff_options opts;
@@ -71,27 +72,36 @@ struct git_diff_list {
extern void git_diff__cleanup_modes(
uint32_t diffcaps, uint32_t *omode, uint32_t *nmode);
-extern void git_diff_list_addref(git_diff_list *diff);
+extern void git_diff_addref(git_diff *diff);
extern int git_diff_delta__cmp(const void *a, const void *b);
extern int git_diff_delta__casecmp(const void *a, const void *b);
+extern const char *git_diff_delta__path(const git_diff_delta *delta);
+
extern bool git_diff_delta__should_skip(
const git_diff_options *opts, const git_diff_delta *delta);
+extern int git_diff_delta__format_file_header(
+ git_buf *out,
+ const git_diff_delta *delta,
+ const char *oldpfx,
+ const char *newpfx,
+ int oid_strlen);
+
extern int git_diff__oid_for_file(
git_repository *, const char *, uint16_t, git_off_t, git_oid *);
extern int git_diff__from_iterators(
- git_diff_list **diff_ptr,
+ git_diff **diff_ptr,
git_repository *repo,
git_iterator *old_iter,
git_iterator *new_iter,
const git_diff_options *opts);
extern int git_diff__paired_foreach(
- git_diff_list *idx2head,
- git_diff_list *wd2idx,
+ git_diff *idx2head,
+ git_diff *wd2idx,
int (*cb)(git_diff_delta *i2h, git_diff_delta *w2i, void *payload),
void *payload);
@@ -106,5 +116,33 @@ extern void git_diff_find_similar__hashsig_free(void *sig, void *payload);
extern int git_diff_find_similar__calc_similarity(
int *score, void *siga, void *sigb, void *payload);
+/*
+ * Sometimes a git_diff_file will have a zero size; this attempts to
+ * fill in the size without loading the blob if possible. If that is
+ * not possible, then it will return the git_odb_object that had to be
+ * loaded and the caller can use it or dispose of it as needed.
+ */
+GIT_INLINE(int) git_diff_file__resolve_zero_size(
+ git_diff_file *file, git_odb_object **odb_obj, git_repository *repo)
+{
+ int error;
+ git_odb *odb;
+ size_t len;
+ git_otype type;
+
+ if ((error = git_repository_odb(&odb, repo)) < 0)
+ return error;
+
+ error = git_odb__read_header_or_object(
+ odb_obj, &len, &type, odb, &file->oid);
+
+ git_odb_free(odb);
+
+ if (!error)
+ file->size = (git_off_t)len;
+
+ return error;
+}
+
#endif
diff --git a/src/diff_driver.c b/src/diff_driver.c
index 469be0d14..bd5a8fbd9 100644
--- a/src/diff_driver.c
+++ b/src/diff_driver.c
@@ -187,7 +187,7 @@ static int git_diff_driver_load(
git_buf_truncate(&name, namelen + strlen("diff.."));
git_buf_put(&name, "xfuncname", strlen("xfuncname"));
- if ((error = git_config_get_multivar(
+ if ((error = git_config_get_multivar_foreach(
cfg, name.ptr, NULL, diff_driver_xfuncname, drv)) < 0) {
if (error != GIT_ENOTFOUND)
goto done;
@@ -196,7 +196,7 @@ static int git_diff_driver_load(
git_buf_truncate(&name, namelen + strlen("diff.."));
git_buf_put(&name, "funcname", strlen("funcname"));
- if ((error = git_config_get_multivar(
+ if ((error = git_config_get_multivar_foreach(
cfg, name.ptr, NULL, diff_driver_funcname, drv)) < 0) {
if (error != GIT_ENOTFOUND)
goto done;
@@ -373,10 +373,11 @@ static long diff_context_find(
!ctxt->match_line(ctxt->driver, ctxt->line.ptr, ctxt->line.size))
return -1;
- git_buf_truncate(&ctxt->line, (size_t)out_size);
- git_buf_copy_cstr(out, (size_t)out_size, &ctxt->line);
+ if (out_size > (long)ctxt->line.size)
+ out_size = (long)ctxt->line.size;
+ memcpy(out, ctxt->line.ptr, (size_t)out_size);
- return (long)ctxt->line.size;
+ return out_size;
}
void git_diff_find_context_init(
diff --git a/src/diff_file.c b/src/diff_file.c
index 9d06daafa..a4c8641bc 100644
--- a/src/diff_file.c
+++ b/src/diff_file.c
@@ -88,7 +88,7 @@ static int diff_file_content_init_common(
int git_diff_file_content__init_from_diff(
git_diff_file_content *fc,
- git_diff_list *diff,
+ git_diff *diff,
size_t delta_index,
bool use_old)
{
@@ -110,7 +110,7 @@ int git_diff_file_content__init_from_diff(
has_data = use_old; break;
case GIT_DELTA_UNTRACKED:
has_data = !use_old &&
- (diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) != 0;
+ (diff->opts.flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) != 0;
break;
case GIT_DELTA_MODIFIED:
case GIT_DELTA_COPIED:
@@ -241,19 +241,9 @@ static int diff_file_content_load_blob(git_diff_file_content *fc)
/* if we don't know size, try to peek at object header first */
if (!fc->file->size) {
- git_odb *odb;
- size_t len;
- git_otype type;
-
- if (!(error = git_repository_odb__weakptr(&odb, fc->repo))) {
- error = git_odb__read_header_or_object(
- &odb_obj, &len, &type, odb, &fc->file->oid);
- git_odb_free(odb);
- }
- if (error)
+ if ((error = git_diff_file__resolve_zero_size(
+ fc->file, &odb_obj, fc->repo)) < 0)
return error;
-
- fc->file->size = len;
}
if (diff_file_content_binary_by_size(fc))
@@ -306,9 +296,9 @@ static int diff_file_content_load_workdir_file(
git_diff_file_content *fc, git_buf *path)
{
int error = 0;
- git_vector filters = GIT_VECTOR_INIT;
- git_buf raw = GIT_BUF_INIT, filtered = GIT_BUF_INIT;
+ git_filter_list *fl = NULL;
git_file fd = git_futils_open_ro(git_buf_cstr(path));
+ git_buf raw = GIT_BUF_INIT;
if (fd < 0)
return fd;
@@ -320,41 +310,38 @@ static int diff_file_content_load_workdir_file(
if (diff_file_content_binary_by_size(fc))
goto cleanup;
- if ((error = git_filters_load(
- &filters, fc->repo, fc->file->path, GIT_FILTER_TO_ODB)) < 0)
+ if ((error = git_filter_list_load(
+ &fl, fc->repo, NULL, fc->file->path, GIT_FILTER_TO_ODB)) < 0)
goto cleanup;
- /* error >= is a filter count */
- if (error == 0) {
+ /* if there are no filters, try to mmap the file */
+ if (fl == NULL) {
if (!(error = git_futils_mmap_ro(
- &fc->map, fd, 0, (size_t)fc->file->size)))
+ &fc->map, fd, 0, (size_t)fc->file->size))) {
fc->flags |= GIT_DIFF_FLAG__UNMAP_DATA;
- else /* fall through to try readbuffer below */
- giterr_clear();
+ goto cleanup;
+ }
+
+ /* if mmap failed, fall through to try readbuffer below */
+ giterr_clear();
}
- if (error != 0) {
- error = git_futils_readbuffer_fd(&raw, fd, (size_t)fc->file->size);
- if (error < 0)
- goto cleanup;
+ if (!(error = git_futils_readbuffer_fd(&raw, fd, (size_t)fc->file->size))) {
+ git_buf out = GIT_BUF_INIT;
- if (!filters.length)
- git_buf_swap(&filtered, &raw);
- else
- error = git_filters_apply(&filtered, &raw, &filters);
+ error = git_filter_list_apply_to_data(&out, fl, &raw);
+
+ git_buf_free(&raw);
if (!error) {
- fc->map.len = git_buf_len(&filtered);
- fc->map.data = git_buf_detach(&filtered);
+ fc->map.len = out.size;
+ fc->map.data = out.ptr;
fc->flags |= GIT_DIFF_FLAG__FREE_DATA;
}
-
- git_buf_free(&raw);
- git_buf_free(&filtered);
}
cleanup:
- git_filters_free(&filters);
+ git_filter_list_free(fl);
p_close(fd);
return error;
@@ -417,6 +404,9 @@ int git_diff_file_content__load(git_diff_file_content *fc)
void git_diff_file_content__unload(git_diff_file_content *fc)
{
+ if ((fc->flags & GIT_DIFF_FLAG__LOADED) == 0)
+ return;
+
if (fc->flags & GIT_DIFF_FLAG__FREE_DATA) {
git__free(fc->map.data);
fc->map.data = "";
diff --git a/src/diff_file.h b/src/diff_file.h
index fb08cca6a..84bf255aa 100644
--- a/src/diff_file.h
+++ b/src/diff_file.h
@@ -27,7 +27,7 @@ typedef struct {
extern int git_diff_file_content__init_from_diff(
git_diff_file_content *fc,
- git_diff_list *diff,
+ git_diff *diff,
size_t delta_index,
bool use_old);
diff --git a/src/diff_patch.c b/src/diff_patch.c
index 9060d0a24..cc49d68eb 100644
--- a/src/diff_patch.c
+++ b/src/diff_patch.c
@@ -10,38 +10,27 @@
#include "diff_driver.h"
#include "diff_patch.h"
#include "diff_xdiff.h"
-
-/* cached information about a single span in a diff */
-typedef struct diff_patch_line diff_patch_line;
-struct diff_patch_line {
- const char *ptr;
- size_t len;
- size_t lines, oldno, newno;
- char origin;
-};
+#include "fileops.h"
/* cached information about a hunk in a diff */
typedef struct diff_patch_hunk diff_patch_hunk;
struct diff_patch_hunk {
- git_diff_range range;
- char header[128];
- size_t header_len;
+ git_diff_hunk hunk;
size_t line_start;
size_t line_count;
};
-struct git_diff_patch {
+struct git_patch {
git_refcount rc;
- git_diff_list *diff; /* for refcount purposes, maybe NULL for blob diffs */
+ git_diff *diff; /* for refcount purposes, maybe NULL for blob diffs */
git_diff_delta *delta;
size_t delta_index;
git_diff_file_content ofile;
git_diff_file_content nfile;
uint32_t flags;
git_array_t(diff_patch_hunk) hunks;
- git_array_t(diff_patch_line) lines;
- size_t oldno, newno;
- size_t content_size;
+ git_array_t(git_diff_line) lines;
+ size_t content_size, context_size, header_size;
git_pool flattened;
};
@@ -54,12 +43,13 @@ enum {
GIT_DIFF_PATCH_FLATTENED = (1 << 5),
};
-static void diff_output_init(git_diff_output*, const git_diff_options*,
- git_diff_file_cb, git_diff_hunk_cb, git_diff_data_cb, void*);
+static void diff_output_init(
+ git_diff_output*, const git_diff_options*,
+ git_diff_file_cb, git_diff_hunk_cb, git_diff_line_cb, void*);
-static void diff_output_to_patch(git_diff_output *, git_diff_patch *);
+static void diff_output_to_patch(git_diff_output *, git_patch *);
-static void diff_patch_update_binary(git_diff_patch *patch)
+static void diff_patch_update_binary(git_patch *patch)
{
if ((patch->delta->flags & DIFF_FLAGS_KNOWN_BINARY) != 0)
return;
@@ -73,21 +63,21 @@ static void diff_patch_update_binary(git_diff_patch *patch)
patch->delta->flags |= GIT_DIFF_FLAG_NOT_BINARY;
}
-static void diff_patch_init_common(git_diff_patch *patch)
+static void diff_patch_init_common(git_patch *patch)
{
diff_patch_update_binary(patch);
if ((patch->delta->flags & GIT_DIFF_FLAG_BINARY) != 0)
- patch->flags |= GIT_DIFF_PATCH_LOADED; /* set LOADED but not DIFFABLE */
+ patch->flags |= GIT_DIFF_PATCH_LOADED; /* LOADED but not DIFFABLE */
patch->flags |= GIT_DIFF_PATCH_INITIALIZED;
if (patch->diff)
- git_diff_list_addref(patch->diff);
+ git_diff_addref(patch->diff);
}
static int diff_patch_init_from_diff(
- git_diff_patch *patch, git_diff_list *diff, size_t delta_index)
+ git_patch *patch, git_diff *diff, size_t delta_index)
{
int error = 0;
@@ -108,12 +98,10 @@ static int diff_patch_init_from_diff(
}
static int diff_patch_alloc_from_diff(
- git_diff_patch **out,
- git_diff_list *diff,
- size_t delta_index)
+ git_patch **out, git_diff *diff, size_t delta_index)
{
int error;
- git_diff_patch *patch = git__calloc(1, sizeof(git_diff_patch));
+ git_patch *patch = git__calloc(1, sizeof(git_patch));
GITERR_CHECK_ALLOC(patch);
if (!(error = diff_patch_init_from_diff(patch, diff, delta_index))) {
@@ -128,7 +116,7 @@ static int diff_patch_alloc_from_diff(
return error;
}
-static int diff_patch_load(git_diff_patch *patch, git_diff_output *output)
+static int diff_patch_load(git_patch *patch, git_diff_output *output)
{
int error = 0;
bool incomplete_data;
@@ -175,9 +163,12 @@ static int diff_patch_load(git_diff_patch *patch, git_diff_output *output)
goto cleanup;
}
- /* if we were previously missing an oid, update MODIFIED->UNMODIFIED */
+ /* if previously missing an oid, and now that we have it the two sides
+ * are the same (and not submodules), update MODIFIED -> UNMODIFIED
+ */
if (incomplete_data &&
patch->ofile.file->mode == patch->nfile.file->mode &&
+ patch->ofile.file->mode != GIT_FILEMODE_COMMIT &&
git_oid_equal(&patch->ofile.file->oid, &patch->nfile.file->oid) &&
patch->delta->status == GIT_DELTA_MODIFIED) /* not RENAMED/COPIED! */
patch->delta->status = GIT_DELTA_UNMODIFIED;
@@ -203,7 +194,7 @@ cleanup:
}
static int diff_patch_file_callback(
- git_diff_patch *patch, git_diff_output *output)
+ git_patch *patch, git_diff_output *output)
{
float progress;
@@ -219,13 +210,17 @@ static int diff_patch_file_callback(
return output->error;
}
-static int diff_patch_generate(git_diff_patch *patch, git_diff_output *output)
+static int diff_patch_generate(git_patch *patch, git_diff_output *output)
{
int error = 0;
if ((patch->flags & GIT_DIFF_PATCH_DIFFED) != 0)
return 0;
+ /* if we are not looking at the hunks and lines, don't do the diff */
+ if (!output->hunk_cb && !output->data_cb)
+ return 0;
+
if ((patch->flags & GIT_DIFF_PATCH_LOADED) == 0 &&
(error = diff_patch_load(patch, output)) < 0)
return error;
@@ -240,7 +235,7 @@ static int diff_patch_generate(git_diff_patch *patch, git_diff_output *output)
return error;
}
-static void diff_patch_free(git_diff_patch *patch)
+static void diff_patch_free(git_patch *patch)
{
git_diff_file_content__clear(&patch->ofile);
git_diff_file_content__clear(&patch->nfile);
@@ -248,7 +243,7 @@ static void diff_patch_free(git_diff_patch *patch)
git_array_clear(patch->lines);
git_array_clear(patch->hunks);
- git_diff_list_free(patch->diff); /* decrements refcount */
+ git_diff_free(patch->diff); /* decrements refcount */
patch->diff = NULL;
git_pool_clear(&patch->flattened);
@@ -257,7 +252,7 @@ static void diff_patch_free(git_diff_patch *patch)
git__free(patch);
}
-static int diff_required(git_diff_list *diff, const char *action)
+static int diff_required(git_diff *diff, const char *action)
{
if (diff)
return 0;
@@ -266,22 +261,22 @@ static int diff_required(git_diff_list *diff, const char *action)
}
int git_diff_foreach(
- git_diff_list *diff,
+ git_diff *diff,
git_diff_file_cb file_cb,
git_diff_hunk_cb hunk_cb,
- git_diff_data_cb data_cb,
+ git_diff_line_cb data_cb,
void *payload)
{
int error = 0;
git_xdiff_output xo;
size_t idx;
- git_diff_patch patch;
+ git_patch patch;
if (diff_required(diff, "git_diff_foreach") < 0)
return -1;
- diff_output_init((git_diff_output *)&xo,
- &diff->opts, file_cb, hunk_cb, data_cb, payload);
+ diff_output_init(
+ &xo.output, &diff->opts, file_cb, hunk_cb, data_cb, payload);
git_xdiff_init(&xo, &diff->opts);
git_vector_foreach(&diff->deltas, idx, patch.delta) {
@@ -292,12 +287,12 @@ int git_diff_foreach(
if (!(error = diff_patch_init_from_diff(&patch, diff, idx))) {
- error = diff_patch_file_callback(&patch, (git_diff_output *)&xo);
+ error = diff_patch_file_callback(&patch, &xo.output);
if (!error)
- error = diff_patch_generate(&patch, (git_diff_output *)&xo);
+ error = diff_patch_generate(&patch, &xo.output);
- git_diff_patch_free(&patch);
+ git_patch_free(&patch);
}
if (error < 0)
@@ -310,7 +305,7 @@ int git_diff_foreach(
}
typedef struct {
- git_diff_patch patch;
+ git_patch patch;
git_diff_delta delta;
char paths[GIT_FLEX_ARRAY];
} diff_patch_with_delta;
@@ -318,7 +313,7 @@ typedef struct {
static int diff_single_generate(diff_patch_with_delta *pd, git_xdiff_output *xo)
{
int error = 0;
- git_diff_patch *patch = &pd->patch;
+ git_patch *patch = &pd->patch;
bool has_old = ((patch->ofile.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
bool has_new = ((patch->nfile.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
@@ -422,7 +417,7 @@ int git_diff_blobs(
const git_diff_options *opts,
git_diff_file_cb file_cb,
git_diff_hunk_cb hunk_cb,
- git_diff_data_cb data_cb,
+ git_diff_line_cb data_cb,
void *payload)
{
int error = 0;
@@ -433,7 +428,7 @@ int git_diff_blobs(
memset(&xo, 0, sizeof(xo));
diff_output_init(
- (git_diff_output *)&xo, opts, file_cb, hunk_cb, data_cb, payload);
+ &xo.output, opts, file_cb, hunk_cb, data_cb, payload);
git_xdiff_init(&xo, opts);
if (!old_path && new_path)
@@ -444,13 +439,13 @@ int git_diff_blobs(
error = diff_patch_from_blobs(
&pd, &xo, old_blob, old_path, new_blob, new_path, opts);
- git_diff_patch_free((git_diff_patch *)&pd);
+ git_patch_free(&pd.patch);
return error;
}
-int git_diff_patch_from_blobs(
- git_diff_patch **out,
+int git_patch_from_blobs(
+ git_patch **out,
const git_blob *old_blob,
const char *old_path,
const git_blob *new_blob,
@@ -469,16 +464,16 @@ int git_diff_patch_from_blobs(
memset(&xo, 0, sizeof(xo));
- diff_output_to_patch((git_diff_output *)&xo, &pd->patch);
+ diff_output_to_patch(&xo.output, &pd->patch);
git_xdiff_init(&xo, opts);
error = diff_patch_from_blobs(
pd, &xo, old_blob, old_path, new_blob, new_path, opts);
if (!error)
- *out = (git_diff_patch *)pd;
+ *out = (git_patch *)pd;
else
- git_diff_patch_free((git_diff_patch *)pd);
+ git_patch_free((git_patch *)pd);
return error;
}
@@ -534,7 +529,7 @@ int git_diff_blob_to_buffer(
const git_diff_options *opts,
git_diff_file_cb file_cb,
git_diff_hunk_cb hunk_cb,
- git_diff_data_cb data_cb,
+ git_diff_line_cb data_cb,
void *payload)
{
int error = 0;
@@ -545,7 +540,7 @@ int git_diff_blob_to_buffer(
memset(&xo, 0, sizeof(xo));
diff_output_init(
- (git_diff_output *)&xo, opts, file_cb, hunk_cb, data_cb, payload);
+ &xo.output, opts, file_cb, hunk_cb, data_cb, payload);
git_xdiff_init(&xo, opts);
if (!old_path && buf_path)
@@ -556,13 +551,13 @@ int git_diff_blob_to_buffer(
error = diff_patch_from_blob_and_buffer(
&pd, &xo, old_blob, old_path, buf, buflen, buf_path, opts);
- git_diff_patch_free((git_diff_patch *)&pd);
+ git_patch_free(&pd.patch);
return error;
}
-int git_diff_patch_from_blob_and_buffer(
- git_diff_patch **out,
+int git_patch_from_blob_and_buffer(
+ git_patch **out,
const git_blob *old_blob,
const char *old_path,
const char *buf,
@@ -582,35 +577,31 @@ int git_diff_patch_from_blob_and_buffer(
memset(&xo, 0, sizeof(xo));
- diff_output_to_patch((git_diff_output *)&xo, &pd->patch);
+ diff_output_to_patch(&xo.output, &pd->patch);
git_xdiff_init(&xo, opts);
error = diff_patch_from_blob_and_buffer(
pd, &xo, old_blob, old_path, buf, buflen, buf_path, opts);
if (!error)
- *out = (git_diff_patch *)pd;
+ *out = (git_patch *)pd;
else
- git_diff_patch_free((git_diff_patch *)pd);
+ git_patch_free((git_patch *)pd);
return error;
}
-int git_diff_get_patch(
- git_diff_patch **patch_ptr,
- const git_diff_delta **delta_ptr,
- git_diff_list *diff,
- size_t idx)
+int git_patch_from_diff(
+ git_patch **patch_ptr, git_diff *diff, size_t idx)
{
int error = 0;
git_xdiff_output xo;
git_diff_delta *delta = NULL;
- git_diff_patch *patch = NULL;
+ git_patch *patch = NULL;
if (patch_ptr) *patch_ptr = NULL;
- if (delta_ptr) *delta_ptr = NULL;
- if (diff_required(diff, "git_diff_get_patch") < 0)
+ if (diff_required(diff, "git_patch_from_diff") < 0)
return -1;
delta = git_vector_get(&diff->deltas, idx);
@@ -619,9 +610,6 @@ int git_diff_get_patch(
return GIT_ENOTFOUND;
}
- if (delta_ptr)
- *delta_ptr = delta;
-
if (git_diff_delta__should_skip(&diff->opts, delta))
return 0;
@@ -634,13 +622,13 @@ int git_diff_get_patch(
if ((error = diff_patch_alloc_from_diff(&patch, diff, idx)) < 0)
return error;
- diff_output_to_patch((git_diff_output *)&xo, patch);
+ diff_output_to_patch(&xo.output, patch);
git_xdiff_init(&xo, &diff->opts);
- error = diff_patch_file_callback(patch, (git_diff_output *)&xo);
+ error = diff_patch_file_callback(patch, &xo.output);
if (!error)
- error = diff_patch_generate(patch, (git_diff_output *)&xo);
+ error = diff_patch_generate(patch, &xo.output);
if (!error) {
/* if cumulative diff size is < 0.5 total size, flatten the patch */
@@ -648,7 +636,7 @@ int git_diff_get_patch(
}
if (error || !patch_ptr)
- git_diff_patch_free(patch);
+ git_patch_free(patch);
else
*patch_ptr = patch;
@@ -657,36 +645,36 @@ int git_diff_get_patch(
return error;
}
-void git_diff_patch_free(git_diff_patch *patch)
+void git_patch_free(git_patch *patch)
{
if (patch)
GIT_REFCOUNT_DEC(patch, diff_patch_free);
}
-const git_diff_delta *git_diff_patch_delta(git_diff_patch *patch)
+const git_diff_delta *git_patch_get_delta(git_patch *patch)
{
assert(patch);
return patch->delta;
}
-size_t git_diff_patch_num_hunks(git_diff_patch *patch)
+size_t git_patch_num_hunks(git_patch *patch)
{
assert(patch);
return git_array_size(patch->hunks);
}
-int git_diff_patch_line_stats(
+int git_patch_line_stats(
size_t *total_ctxt,
size_t *total_adds,
size_t *total_dels,
- const git_diff_patch *patch)
+ const git_patch *patch)
{
size_t totals[3], idx;
memset(totals, 0, sizeof(totals));
for (idx = 0; idx < git_array_size(patch->lines); ++idx) {
- diff_patch_line *line = git_array_get(patch->lines, idx);
+ git_diff_line *line = git_array_get(patch->lines, idx);
if (!line)
continue;
@@ -718,12 +706,10 @@ static int diff_error_outofrange(const char *thing)
return GIT_ENOTFOUND;
}
-int git_diff_patch_get_hunk(
- const git_diff_range **range,
- const char **header,
- size_t *header_len,
+int git_patch_get_hunk(
+ const git_diff_hunk **out,
size_t *lines_in_hunk,
- git_diff_patch *patch,
+ git_patch *patch,
size_t hunk_idx)
{
diff_patch_hunk *hunk;
@@ -732,21 +718,17 @@ int git_diff_patch_get_hunk(
hunk = git_array_get(patch->hunks, hunk_idx);
if (!hunk) {
- if (range) *range = NULL;
- if (header) *header = NULL;
- if (header_len) *header_len = 0;
+ if (out) *out = NULL;
if (lines_in_hunk) *lines_in_hunk = 0;
return diff_error_outofrange("hunk");
}
- if (range) *range = &hunk->range;
- if (header) *header = hunk->header;
- if (header_len) *header_len = hunk->header_len;
+ if (out) *out = &hunk->hunk;
if (lines_in_hunk) *lines_in_hunk = hunk->line_count;
return 0;
}
-int git_diff_patch_num_lines_in_hunk(git_diff_patch *patch, size_t hunk_idx)
+int git_patch_num_lines_in_hunk(git_patch *patch, size_t hunk_idx)
{
diff_patch_hunk *hunk;
assert(patch);
@@ -756,82 +738,96 @@ int git_diff_patch_num_lines_in_hunk(git_diff_patch *patch, size_t hunk_idx)
return (int)hunk->line_count;
}
-int git_diff_patch_get_line_in_hunk(
- char *line_origin,
- const char **content,
- size_t *content_len,
- int *old_lineno,
- int *new_lineno,
- git_diff_patch *patch,
+int git_patch_get_line_in_hunk(
+ const git_diff_line **out,
+ git_patch *patch,
size_t hunk_idx,
size_t line_of_hunk)
{
diff_patch_hunk *hunk;
- diff_patch_line *line;
- const char *thing;
+ git_diff_line *line;
assert(patch);
if (!(hunk = git_array_get(patch->hunks, hunk_idx))) {
- thing = "hunk";
- goto notfound;
+ if (out) *out = NULL;
+ return diff_error_outofrange("hunk");
}
if (line_of_hunk >= hunk->line_count ||
!(line = git_array_get(
patch->lines, hunk->line_start + line_of_hunk))) {
- thing = "line";
- goto notfound;
+ if (out) *out = NULL;
+ return diff_error_outofrange("line");
}
- if (line_origin) *line_origin = line->origin;
- if (content) *content = line->ptr;
- if (content_len) *content_len = line->len;
- if (old_lineno) *old_lineno = (int)line->oldno;
- if (new_lineno) *new_lineno = (int)line->newno;
-
+ if (out) *out = line;
return 0;
+}
-notfound:
- if (line_origin) *line_origin = GIT_DIFF_LINE_CONTEXT;
- if (content) *content = NULL;
- if (content_len) *content_len = 0;
- if (old_lineno) *old_lineno = -1;
- if (new_lineno) *new_lineno = -1;
+size_t git_patch_size(
+ git_patch *patch,
+ int include_context,
+ int include_hunk_headers,
+ int include_file_headers)
+{
+ size_t out;
- return diff_error_outofrange(thing);
+ assert(patch);
+
+ out = patch->content_size;
+
+ if (!include_context)
+ out -= patch->context_size;
+
+ if (include_hunk_headers)
+ out += patch->header_size;
+
+ if (include_file_headers) {
+ git_buf file_header = GIT_BUF_INIT;
+
+ if (git_diff_delta__format_file_header(
+ &file_header, patch->delta, NULL, NULL, 0) < 0)
+ giterr_clear();
+ else
+ out += git_buf_len(&file_header);
+
+ git_buf_free(&file_header);
+ }
+
+ return out;
}
-git_diff_list *git_diff_patch__diff(git_diff_patch *patch)
+git_diff *git_patch__diff(git_patch *patch)
{
return patch->diff;
}
-git_diff_driver *git_diff_patch__driver(git_diff_patch *patch)
+git_diff_driver *git_patch__driver(git_patch *patch)
{
/* ofile driver is representative for whole patch */
return patch->ofile.driver;
}
-void git_diff_patch__old_data(
- char **ptr, size_t *len, git_diff_patch *patch)
+void git_patch__old_data(
+ char **ptr, size_t *len, git_patch *patch)
{
*ptr = patch->ofile.map.data;
*len = patch->ofile.map.len;
}
-void git_diff_patch__new_data(
- char **ptr, size_t *len, git_diff_patch *patch)
+void git_patch__new_data(
+ char **ptr, size_t *len, git_patch *patch)
{
*ptr = patch->nfile.map.data;
*len = patch->nfile.map.len;
}
-int git_diff_patch__invoke_callbacks(
- git_diff_patch *patch,
+int git_patch__invoke_callbacks(
+ git_patch *patch,
git_diff_file_cb file_cb,
git_diff_hunk_cb hunk_cb,
- git_diff_data_cb line_cb,
+ git_diff_line_cb line_cb,
void *payload)
{
int error = 0;
@@ -846,18 +842,16 @@ int git_diff_patch__invoke_callbacks(
for (i = 0; !error && i < git_array_size(patch->hunks); ++i) {
diff_patch_hunk *h = git_array_get(patch->hunks, i);
- error = hunk_cb(
- patch->delta, &h->range, h->header, h->header_len, payload);
+ error = hunk_cb(patch->delta, &h->hunk, payload);
if (!line_cb)
continue;
for (j = 0; !error && j < h->line_count; ++j) {
- diff_patch_line *l =
+ git_diff_line *l =
git_array_get(patch->lines, h->line_start + j);
- error = line_cb(
- patch->delta, &h->range, l->origin, l->ptr, l->len, payload);
+ error = line_cb(patch->delta, &h->hunk, l, payload);
}
}
@@ -876,12 +870,10 @@ static int diff_patch_file_cb(
static int diff_patch_hunk_cb(
const git_diff_delta *delta,
- const git_diff_range *range,
- const char *header,
- size_t header_len,
+ const git_diff_hunk *hunk_,
void *payload)
{
- git_diff_patch *patch = payload;
+ git_patch *patch = payload;
diff_patch_hunk *hunk;
GIT_UNUSED(delta);
@@ -889,36 +881,28 @@ static int diff_patch_hunk_cb(
hunk = git_array_alloc(patch->hunks);
GITERR_CHECK_ALLOC(hunk);
- memcpy(&hunk->range, range, sizeof(hunk->range));
+ memcpy(&hunk->hunk, hunk_, sizeof(hunk->hunk));
- assert(header_len + 1 < sizeof(hunk->header));
- memcpy(&hunk->header, header, header_len);
- hunk->header[header_len] = '\0';
- hunk->header_len = header_len;
+ patch->header_size += hunk_->header_len;
hunk->line_start = git_array_size(patch->lines);
hunk->line_count = 0;
- patch->oldno = range->old_start;
- patch->newno = range->new_start;
-
return 0;
}
static int diff_patch_line_cb(
const git_diff_delta *delta,
- const git_diff_range *range,
- char line_origin,
- const char *content,
- size_t content_len,
+ const git_diff_hunk *hunk_,
+ const git_diff_line *line_,
void *payload)
{
- git_diff_patch *patch = payload;
+ git_patch *patch = payload;
diff_patch_hunk *hunk;
- diff_patch_line *line;
+ git_diff_line *line;
GIT_UNUSED(delta);
- GIT_UNUSED(range);
+ GIT_UNUSED(hunk_);
hunk = git_array_last(patch->hunks);
GITERR_CHECK_ALLOC(hunk);
@@ -926,39 +910,20 @@ static int diff_patch_line_cb(
line = git_array_alloc(patch->lines);
GITERR_CHECK_ALLOC(line);
- line->ptr = content;
- line->len = content_len;
- line->origin = line_origin;
-
- patch->content_size += content_len;
+ memcpy(line, line_, sizeof(*line));
/* do some bookkeeping so we can provide old/new line numbers */
- for (line->lines = 0; content_len > 0; --content_len) {
- if (*content++ == '\n')
- ++line->lines;
- }
+ patch->content_size += line->content_len;
- switch (line_origin) {
- case GIT_DIFF_LINE_ADDITION:
- case GIT_DIFF_LINE_DEL_EOFNL:
- line->oldno = -1;
- line->newno = patch->newno;
- patch->newno += line->lines;
- break;
- case GIT_DIFF_LINE_DELETION:
- case GIT_DIFF_LINE_ADD_EOFNL:
- line->oldno = patch->oldno;
- line->newno = -1;
- patch->oldno += line->lines;
- break;
- default:
- line->oldno = patch->oldno;
- line->newno = patch->newno;
- patch->oldno += line->lines;
- patch->newno += line->lines;
- break;
- }
+ if (line->origin == GIT_DIFF_LINE_ADDITION ||
+ line->origin == GIT_DIFF_LINE_DELETION)
+ patch->content_size += 1;
+ else if (line->origin == GIT_DIFF_LINE_CONTEXT) {
+ patch->content_size += 1;
+ patch->context_size += line->content_len + 1;
+ } else if (line->origin == GIT_DIFF_LINE_CONTEXT_EOFNL)
+ patch->context_size += line->content_len;
hunk->line_count++;
@@ -970,7 +935,7 @@ static void diff_output_init(
const git_diff_options *opts,
git_diff_file_cb file_cb,
git_diff_hunk_cb hunk_cb,
- git_diff_data_cb data_cb,
+ git_diff_line_cb data_cb,
void *payload)
{
GIT_UNUSED(opts);
@@ -983,7 +948,7 @@ static void diff_output_init(
out->payload = payload;
}
-static void diff_output_to_patch(git_diff_output *out, git_diff_patch *patch)
+static void diff_output_to_patch(git_diff_output *out, git_patch *patch)
{
diff_output_init(
out, NULL,
diff --git a/src/diff_patch.h b/src/diff_patch.h
index 56af14600..df2ba4c31 100644
--- a/src/diff_patch.h
+++ b/src/diff_patch.h
@@ -11,19 +11,20 @@
#include "diff.h"
#include "diff_file.h"
#include "array.h"
+#include "git2/patch.h"
-extern git_diff_list *git_diff_patch__diff(git_diff_patch *);
+extern git_diff *git_patch__diff(git_patch *);
-extern git_diff_driver *git_diff_patch__driver(git_diff_patch *);
+extern git_diff_driver *git_patch__driver(git_patch *);
-extern void git_diff_patch__old_data(char **, size_t *, git_diff_patch *);
-extern void git_diff_patch__new_data(char **, size_t *, git_diff_patch *);
+extern void git_patch__old_data(char **, size_t *, git_patch *);
+extern void git_patch__new_data(char **, size_t *, git_patch *);
-extern int git_diff_patch__invoke_callbacks(
- git_diff_patch *patch,
+extern int git_patch__invoke_callbacks(
+ git_patch *patch,
git_diff_file_cb file_cb,
git_diff_hunk_cb hunk_cb,
- git_diff_data_cb line_cb,
+ git_diff_line_cb line_cb,
void *payload);
typedef struct git_diff_output git_diff_output;
@@ -31,7 +32,7 @@ struct git_diff_output {
/* these callbacks are issued with the diff data */
git_diff_file_cb file_cb;
git_diff_hunk_cb hunk_cb;
- git_diff_data_cb data_cb;
+ git_diff_line_cb data_cb;
void *payload;
/* this records the actual error in cases where it may be obscured */
@@ -40,7 +41,7 @@ struct git_diff_output {
/* this callback is used to do the diff and drive the other callbacks.
* see diff_xdiff.h for how to use this in practice for now.
*/
- int (*diff_cb)(git_diff_output *output, git_diff_patch *patch);
+ int (*diff_cb)(git_diff_output *output, git_patch *patch);
};
#endif
diff --git a/src/diff_print.c b/src/diff_print.c
index 0de548813..b04b11515 100644
--- a/src/diff_print.c
+++ b/src/diff_print.c
@@ -7,26 +7,39 @@
#include "common.h"
#include "diff.h"
#include "diff_patch.h"
-#include "buffer.h"
+#include "fileops.h"
typedef struct {
- git_diff_list *diff;
- git_diff_data_cb print_cb;
+ git_diff *diff;
+ git_diff_format_t format;
+ git_diff_line_cb print_cb;
void *payload;
git_buf *buf;
+ uint32_t flags;
int oid_strlen;
+ git_diff_line line;
} diff_print_info;
static int diff_print_info_init(
diff_print_info *pi,
- git_buf *out, git_diff_list *diff, git_diff_data_cb cb, void *payload)
+ git_buf *out,
+ git_diff *diff,
+ git_diff_format_t format,
+ git_diff_line_cb cb,
+ void *payload)
{
pi->diff = diff;
+ pi->format = format;
pi->print_cb = cb;
pi->payload = payload;
pi->buf = out;
- if (!diff || !diff->repo)
+ if (diff)
+ pi->flags = diff->opts.flags;
+
+ if (diff && diff->opts.oid_abbrev != 0)
+ pi->oid_strlen = diff->opts.oid_abbrev;
+ else if (!diff || !diff->repo)
pi->oid_strlen = GIT_ABBREV_DEFAULT;
else if (git_repository__cvar(
&pi->oid_strlen, diff->repo, GIT_CVAR_ABBREV) < 0)
@@ -39,6 +52,11 @@ static int diff_print_info_init(
else if (pi->oid_strlen > GIT_OID_HEXSZ + 1)
pi->oid_strlen = GIT_OID_HEXSZ + 1;
+ memset(&pi->line, 0, sizeof(pi->line));
+ pi->line.old_lineno = -1;
+ pi->line.new_lineno = -1;
+ pi->line.num_lines = 1;
+
return 0;
}
@@ -46,7 +64,7 @@ static char diff_pick_suffix(int mode)
{
if (S_ISDIR(mode))
return '/';
- else if (mode & 0100) /* -V536 */
+ else if (GIT_PERMS_IS_EXEC(mode)) /* -V536 */
/* in git, modes are very regular, so we must have 0100755 mode */
return '*';
else
@@ -77,7 +95,35 @@ static int callback_error(void)
return GIT_EUSER;
}
-static int diff_print_one_compact(
+static int diff_print_one_name_only(
+ const git_diff_delta *delta, float progress, void *data)
+{
+ diff_print_info *pi = data;
+ git_buf *out = pi->buf;
+
+ GIT_UNUSED(progress);
+
+ if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 &&
+ delta->status == GIT_DELTA_UNMODIFIED)
+ return 0;
+
+ git_buf_clear(out);
+
+ if (git_buf_puts(out, delta->new_file.path) < 0 ||
+ git_buf_putc(out, '\n'))
+ return -1;
+
+ pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
+ pi->line.content = git_buf_cstr(out);
+ pi->line.content_len = git_buf_len(out);
+
+ if (pi->print_cb(delta, NULL, &pi->line, pi->payload))
+ return callback_error();
+
+ return 0;
+}
+
+static int diff_print_one_name_status(
const git_diff_delta *delta, float progress, void *data)
{
diff_print_info *pi = data;
@@ -88,7 +134,7 @@ static int diff_print_one_compact(
GIT_UNUSED(progress);
- if (code == ' ')
+ if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && code == ' ')
return 0;
old_suffix = diff_pick_suffix(delta->old_file.mode);
@@ -98,12 +144,12 @@ static int diff_print_one_compact(
if (delta->old_file.path != delta->new_file.path &&
strcomp(delta->old_file.path,delta->new_file.path) != 0)
- git_buf_printf(out, "%c\t%s%c -> %s%c\n", code,
+ git_buf_printf(out, "%c\t%s%c %s%c\n", code,
delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
else if (delta->old_file.mode != delta->new_file.mode &&
delta->old_file.mode != 0 && delta->new_file.mode != 0)
- git_buf_printf(out, "%c\t%s%c (%o -> %o)\n", code,
- delta->old_file.path, new_suffix, delta->old_file.mode, delta->new_file.mode);
+ git_buf_printf(out, "%c\t%s%c %s%c\n", code,
+ delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
else if (old_suffix != ' ')
git_buf_printf(out, "%c\t%s%c\n", code, delta->old_file.path, old_suffix);
else
@@ -112,31 +158,16 @@ static int diff_print_one_compact(
if (git_buf_oom(out))
return -1;
- if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
- git_buf_cstr(out), git_buf_len(out), pi->payload))
+ pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
+ pi->line.content = git_buf_cstr(out);
+ pi->line.content_len = git_buf_len(out);
+
+ if (pi->print_cb(delta, NULL, &pi->line, pi->payload))
return callback_error();
return 0;
}
-/* print a git_diff_list to a print callback in compact format */
-int git_diff_print_compact(
- git_diff_list *diff,
- git_diff_data_cb print_cb,
- void *payload)
-{
- int error;
- git_buf buf = GIT_BUF_INIT;
- diff_print_info pi;
-
- if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
- error = git_diff_foreach(diff, diff_print_one_compact, NULL, NULL, &pi);
-
- git_buf_free(&buf);
-
- return error;
-}
-
static int diff_print_one_raw(
const git_diff_delta *delta, float progress, void *data)
{
@@ -147,7 +178,7 @@ static int diff_print_one_raw(
GIT_UNUSED(progress);
- if (code == ' ')
+ if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && code == ' ')
return 0;
git_buf_clear(out);
@@ -173,38 +204,23 @@ static int diff_print_one_raw(
if (git_buf_oom(out))
return -1;
- if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
- git_buf_cstr(out), git_buf_len(out), pi->payload))
+ pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
+ pi->line.content = git_buf_cstr(out);
+ pi->line.content_len = git_buf_len(out);
+
+ if (pi->print_cb(delta, NULL, &pi->line, pi->payload))
return callback_error();
return 0;
}
-/* print a git_diff_list to a print callback in raw output format */
-int git_diff_print_raw(
- git_diff_list *diff,
- git_diff_data_cb print_cb,
- void *payload)
+static int diff_print_oid_range(
+ git_buf *out, const git_diff_delta *delta, int oid_strlen)
{
- int error;
- git_buf buf = GIT_BUF_INIT;
- diff_print_info pi;
-
- if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
- error = git_diff_foreach(diff, diff_print_one_raw, NULL, NULL, &pi);
-
- git_buf_free(&buf);
-
- return error;
-}
-
-static int diff_print_oid_range(diff_print_info *pi, const git_diff_delta *delta)
-{
- git_buf *out = pi->buf;
char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
- git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.oid);
- git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.oid);
+ git_oid_tostr(start_oid, oid_strlen, &delta->old_file.oid);
+ git_oid_tostr(end_oid, oid_strlen, &delta->new_file.oid);
/* TODO: Match git diff more closely */
if (delta->old_file.mode == delta->new_file.mode) {
@@ -228,70 +244,102 @@ static int diff_print_oid_range(diff_print_info *pi, const git_diff_delta *delta
return 0;
}
-static int diff_print_patch_file(
- const git_diff_delta *delta, float progress, void *data)
+static int diff_delta_format_with_paths(
+ git_buf *out,
+ const git_diff_delta *delta,
+ const char *oldpfx,
+ const char *newpfx,
+ const char *template)
{
- diff_print_info *pi = data;
- const char *oldpfx = pi->diff ? pi->diff->opts.old_prefix : NULL;
const char *oldpath = delta->old_file.path;
- const char *newpfx = pi->diff ? pi->diff->opts.new_prefix : NULL;
const char *newpath = delta->new_file.path;
- uint32_t opts_flags = pi->diff ? pi->diff->opts.flags : GIT_DIFF_NORMAL;
- GIT_UNUSED(progress);
+ if (git_oid_iszero(&delta->old_file.oid)) {
+ oldpfx = "";
+ oldpath = "/dev/null";
+ }
+ if (git_oid_iszero(&delta->new_file.oid)) {
+ newpfx = "";
+ newpath = "/dev/null";
+ }
- if (S_ISDIR(delta->new_file.mode) ||
- delta->status == GIT_DELTA_UNMODIFIED ||
- delta->status == GIT_DELTA_IGNORED ||
- (delta->status == GIT_DELTA_UNTRACKED &&
- (opts_flags & GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) == 0))
- return 0;
+ return git_buf_printf(out, template, oldpfx, oldpath, newpfx, newpath);
+}
+int git_diff_delta__format_file_header(
+ git_buf *out,
+ const git_diff_delta *delta,
+ const char *oldpfx,
+ const char *newpfx,
+ int oid_strlen)
+{
if (!oldpfx)
oldpfx = DIFF_OLD_PREFIX_DEFAULT;
if (!newpfx)
newpfx = DIFF_NEW_PREFIX_DEFAULT;
+ if (!oid_strlen)
+ oid_strlen = GIT_ABBREV_DEFAULT + 1;
- git_buf_clear(pi->buf);
- git_buf_printf(pi->buf, "diff --git %s%s %s%s\n",
+ git_buf_clear(out);
+
+ git_buf_printf(out, "diff --git %s%s %s%s\n",
oldpfx, delta->old_file.path, newpfx, delta->new_file.path);
- if (diff_print_oid_range(pi, delta) < 0)
+ if (diff_print_oid_range(out, delta, oid_strlen) < 0)
return -1;
- if (git_oid_iszero(&delta->old_file.oid)) {
- oldpfx = "";
- oldpath = "/dev/null";
- }
- if (git_oid_iszero(&delta->new_file.oid)) {
- newpfx = "";
- newpath = "/dev/null";
- }
+ if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
+ diff_delta_format_with_paths(
+ out, delta, oldpfx, newpfx, "--- %s%s\n+++ %s%s\n");
- if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0) {
- git_buf_printf(pi->buf, "--- %s%s\n", oldpfx, oldpath);
- git_buf_printf(pi->buf, "+++ %s%s\n", newpfx, newpath);
- }
+ return git_buf_oom(out) ? -1 : 0;
+}
+
+static int diff_print_patch_file(
+ const git_diff_delta *delta, float progress, void *data)
+{
+ diff_print_info *pi = data;
+ const char *oldpfx =
+ pi->diff ? pi->diff->opts.old_prefix : DIFF_OLD_PREFIX_DEFAULT;
+ const char *newpfx =
+ pi->diff ? pi->diff->opts.new_prefix : DIFF_NEW_PREFIX_DEFAULT;
- if (git_buf_oom(pi->buf))
+ GIT_UNUSED(progress);
+
+ if (S_ISDIR(delta->new_file.mode) ||
+ delta->status == GIT_DELTA_UNMODIFIED ||
+ delta->status == GIT_DELTA_IGNORED ||
+ (delta->status == GIT_DELTA_UNTRACKED &&
+ (pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0))
+ return 0;
+
+ if (git_diff_delta__format_file_header(
+ pi->buf, delta, oldpfx, newpfx, pi->oid_strlen) < 0)
return -1;
- if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
- git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
+ pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
+ pi->line.content = git_buf_cstr(pi->buf);
+ pi->line.content_len = git_buf_len(pi->buf);
+
+ if (pi->print_cb(delta, NULL, &pi->line, pi->payload))
return callback_error();
if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
return 0;
git_buf_clear(pi->buf);
- git_buf_printf(
- pi->buf, "Binary files %s%s and %s%s differ\n",
- oldpfx, oldpath, newpfx, newpath);
- if (git_buf_oom(pi->buf))
+
+ if (diff_delta_format_with_paths(
+ pi->buf, delta, oldpfx, newpfx,
+ "Binary files %s%s and %s%s differ\n") < 0)
return -1;
- if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_BINARY,
- git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
+ pi->line.origin = GIT_DIFF_LINE_BINARY;
+ pi->line.content = git_buf_cstr(pi->buf);
+ pi->line.content_len = git_buf_len(pi->buf);
+ pi->line.num_lines = 1;
+
+ if (pi->print_cb(delta, NULL, &pi->line, pi->payload))
return callback_error();
return 0;
@@ -299,9 +347,7 @@ static int diff_print_patch_file(
static int diff_print_patch_hunk(
const git_diff_delta *d,
- const git_diff_range *r,
- const char *header,
- size_t header_len,
+ const git_diff_hunk *h,
void *data)
{
diff_print_info *pi = data;
@@ -309,12 +355,11 @@ static int diff_print_patch_hunk(
if (S_ISDIR(d->new_file.mode))
return 0;
- git_buf_clear(pi->buf);
- if (git_buf_printf(pi->buf, "%.*s", (int)header_len, header) < 0)
- return -1;
+ pi->line.origin = GIT_DIFF_LINE_HUNK_HDR;
+ pi->line.content = h->header;
+ pi->line.content_len = h->header_len;
- if (pi->print_cb(d, r, GIT_DIFF_LINE_HUNK_HDR,
- git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
+ if (pi->print_cb(d, h, &pi->line, pi->payload))
return callback_error();
return 0;
@@ -322,10 +367,8 @@ static int diff_print_patch_hunk(
static int diff_print_patch_line(
const git_diff_delta *delta,
- const git_diff_range *range,
- char line_origin, /* GIT_DIFF_LINE value from above */
- const char *content,
- size_t content_len,
+ const git_diff_hunk *hunk,
+ const git_diff_line *line,
void *data)
{
diff_print_info *pi = data;
@@ -333,49 +376,63 @@ static int diff_print_patch_line(
if (S_ISDIR(delta->new_file.mode))
return 0;
- git_buf_clear(pi->buf);
-
- if (line_origin == GIT_DIFF_LINE_ADDITION ||
- line_origin == GIT_DIFF_LINE_DELETION ||
- line_origin == GIT_DIFF_LINE_CONTEXT)
- git_buf_printf(pi->buf, "%c%.*s", line_origin, (int)content_len, content);
- else if (content_len > 0)
- git_buf_printf(pi->buf, "%.*s", (int)content_len, content);
-
- if (git_buf_oom(pi->buf))
- return -1;
-
- if (pi->print_cb(delta, range, line_origin,
- git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
+ if (pi->print_cb(delta, hunk, line, pi->payload))
return callback_error();
return 0;
}
-/* print a git_diff_list to an output callback in patch format */
-int git_diff_print_patch(
- git_diff_list *diff,
- git_diff_data_cb print_cb,
+/* print a git_diff to an output callback */
+int git_diff_print(
+ git_diff *diff,
+ git_diff_format_t format,
+ git_diff_line_cb print_cb,
void *payload)
{
int error;
git_buf buf = GIT_BUF_INIT;
diff_print_info pi;
+ git_diff_file_cb print_file = NULL;
+ git_diff_hunk_cb print_hunk = NULL;
+ git_diff_line_cb print_line = NULL;
+
+ switch (format) {
+ case GIT_DIFF_FORMAT_PATCH:
+ print_file = diff_print_patch_file;
+ print_hunk = diff_print_patch_hunk;
+ print_line = diff_print_patch_line;
+ break;
+ case GIT_DIFF_FORMAT_PATCH_HEADER:
+ print_file = diff_print_patch_file;
+ break;
+ case GIT_DIFF_FORMAT_RAW:
+ print_file = diff_print_one_raw;
+ break;
+ case GIT_DIFF_FORMAT_NAME_ONLY:
+ print_file = diff_print_one_name_only;
+ break;
+ case GIT_DIFF_FORMAT_NAME_STATUS:
+ print_file = diff_print_one_name_status;
+ break;
+ default:
+ giterr_set(GITERR_INVALID, "Unknown diff output format (%d)", format);
+ return -1;
+ }
- if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
+ if (!(error = diff_print_info_init(
+ &pi, &buf, diff, format, print_cb, payload)))
error = git_diff_foreach(
- diff, diff_print_patch_file, diff_print_patch_hunk,
- diff_print_patch_line, &pi);
+ diff, print_file, print_hunk, print_line, &pi);
git_buf_free(&buf);
return error;
}
-/* print a git_diff_patch to an output callback */
-int git_diff_patch_print(
- git_diff_patch *patch,
- git_diff_data_cb print_cb,
+/* print a git_patch to an output callback */
+int git_patch_print(
+ git_patch *patch,
+ git_diff_line_cb print_cb,
void *payload)
{
int error;
@@ -385,8 +442,9 @@ int git_diff_patch_print(
assert(patch && print_cb);
if (!(error = diff_print_info_init(
- &pi, &temp, git_diff_patch__diff(patch), print_cb, payload)))
- error = git_diff_patch__invoke_callbacks(
+ &pi, &temp, git_patch__diff(patch),
+ GIT_DIFF_FORMAT_PATCH, print_cb, payload)))
+ error = git_patch__invoke_callbacks(
patch, diff_print_patch_file, diff_print_patch_hunk,
diff_print_patch_line, &pi);
@@ -397,26 +455,30 @@ int git_diff_patch_print(
static int diff_print_to_buffer_cb(
const git_diff_delta *delta,
- const git_diff_range *range,
- char line_origin,
- const char *content,
- size_t content_len,
+ const git_diff_hunk *hunk,
+ const git_diff_line *line,
void *payload)
{
git_buf *output = payload;
- GIT_UNUSED(delta); GIT_UNUSED(range); GIT_UNUSED(line_origin);
- return git_buf_put(output, content, content_len);
+ GIT_UNUSED(delta); GIT_UNUSED(hunk);
+
+ if (line->origin == GIT_DIFF_LINE_ADDITION ||
+ line->origin == GIT_DIFF_LINE_DELETION ||
+ line->origin == GIT_DIFF_LINE_CONTEXT)
+ git_buf_putc(output, line->origin);
+
+ return git_buf_put(output, line->content, line->content_len);
}
-/* print a git_diff_patch to a string buffer */
-int git_diff_patch_to_str(
+/* print a git_patch to a string buffer */
+int git_patch_to_str(
char **string,
- git_diff_patch *patch)
+ git_patch *patch)
{
int error;
git_buf output = GIT_BUF_INIT;
- error = git_diff_patch_print(patch, diff_print_to_buffer_cb, &output);
+ error = git_patch_print(patch, diff_print_to_buffer_cb, &output);
/* GIT_EUSER means git_buf_put in print_to_buffer_cb returned -1,
* meaning a memory allocation failure, so just map to -1...
diff --git a/src/diff_tform.c b/src/diff_tform.c
index 8c4e96ecf..28a9cc70d 100644
--- a/src/diff_tform.c
+++ b/src/diff_tform.c
@@ -46,7 +46,9 @@ fail:
}
static git_diff_delta *diff_delta__merge_like_cgit(
- const git_diff_delta *a, const git_diff_delta *b, git_pool *pool)
+ const git_diff_delta *a,
+ const git_diff_delta *b,
+ git_pool *pool)
{
git_diff_delta *dup;
@@ -96,15 +98,46 @@ static git_diff_delta *diff_delta__merge_like_cgit(
return dup;
}
-int git_diff_merge(
- git_diff_list *onto,
- const git_diff_list *from)
+static git_diff_delta *diff_delta__merge_like_cgit_reversed(
+ const git_diff_delta *a,
+ const git_diff_delta *b,
+ git_pool *pool)
+{
+ git_diff_delta *dup;
+
+ /* reversed version of above logic */
+
+ if (a->status == GIT_DELTA_UNMODIFIED)
+ return diff_delta__dup(b, pool);
+
+ if ((dup = diff_delta__dup(a, pool)) == NULL)
+ return NULL;
+
+ if (b->status == GIT_DELTA_UNMODIFIED || b->status == GIT_DELTA_UNTRACKED)
+ return dup;
+
+ if (dup->status == GIT_DELTA_DELETED) {
+ if (b->status == GIT_DELTA_ADDED)
+ dup->status = GIT_DELTA_UNMODIFIED;
+ } else {
+ dup->status = b->status;
+ }
+
+ git_oid_cpy(&dup->old_file.oid, &b->old_file.oid);
+ dup->old_file.mode = b->old_file.mode;
+ dup->old_file.size = b->old_file.size;
+ dup->old_file.flags = b->old_file.flags;
+
+ return dup;
+}
+
+int git_diff_merge(git_diff *onto, const git_diff *from)
{
int error = 0;
git_pool onto_pool;
git_vector onto_new;
git_diff_delta *delta;
- bool ignore_case = false;
+ bool ignore_case, reversed;
unsigned int i, j;
assert(onto && from);
@@ -112,26 +145,26 @@ int git_diff_merge(
if (!from->deltas.length)
return 0;
+ ignore_case = ((onto->opts.flags & GIT_DIFF_IGNORE_CASE) != 0);
+ reversed = ((onto->opts.flags & GIT_DIFF_REVERSE) != 0);
+
+ if (ignore_case != ((from->opts.flags & GIT_DIFF_IGNORE_CASE) != 0) ||
+ reversed != ((from->opts.flags & GIT_DIFF_REVERSE) != 0)) {
+ giterr_set(GITERR_INVALID,
+ "Attempt to merge diffs created with conflicting options");
+ return -1;
+ }
+
if (git_vector_init(
&onto_new, onto->deltas.length, git_diff_delta__cmp) < 0 ||
git_pool_init(&onto_pool, 1, 0) < 0)
return -1;
- if ((onto->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0 ||
- (from->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0)
- {
- ignore_case = true;
-
- /* This function currently only supports merging diff lists that
- * are sorted identically. */
- assert((onto->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0 &&
- (from->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0);
- }
-
for (i = 0, j = 0; i < onto->deltas.length || j < from->deltas.length; ) {
git_diff_delta *o = GIT_VECTOR_GET(&onto->deltas, i);
const git_diff_delta *f = GIT_VECTOR_GET(&from->deltas, j);
- int cmp = !f ? -1 : !o ? 1 : STRCMP_CASESELECT(ignore_case, o->old_file.path, f->old_file.path);
+ int cmp = !f ? -1 : !o ? 1 :
+ STRCMP_CASESELECT(ignore_case, o->old_file.path, f->old_file.path);
if (cmp < 0) {
delta = diff_delta__dup(o, &onto_pool);
@@ -140,7 +173,9 @@ int git_diff_merge(
delta = diff_delta__dup(f, &onto_pool);
j++;
} else {
- delta = diff_delta__merge_like_cgit(o, f, &onto_pool);
+ delta = reversed ?
+ diff_delta__merge_like_cgit_reversed(o, f, &onto_pool) :
+ diff_delta__merge_like_cgit(o, f, &onto_pool);
i++;
j++;
}
@@ -160,7 +195,11 @@ int git_diff_merge(
if (!error) {
git_vector_swap(&onto->deltas, &onto_new);
git_pool_swap(&onto->pool, &onto_pool);
- onto->new_src = from->new_src;
+
+ if ((onto->opts.flags & GIT_DIFF_REVERSE) != 0)
+ onto->old_src = from->old_src;
+ else
+ onto->new_src = from->new_src;
/* prefix strings also come from old pool, so recreate those.*/
onto->opts.old_prefix =
@@ -230,9 +269,9 @@ int git_diff_find_similar__calc_similarity(
#define DEFAULT_RENAME_LIMIT 200
static int normalize_find_opts(
- git_diff_list *diff,
+ git_diff *diff,
git_diff_find_options *opts,
- git_diff_find_options *given)
+ const git_diff_find_options *given)
{
git_config *cfg = NULL;
@@ -328,7 +367,7 @@ static int normalize_find_opts(
}
static int apply_splits_and_deletes(
- git_diff_list *diff, size_t expected_size, bool actually_split)
+ git_diff *diff, size_t expected_size, bool actually_split)
{
git_vector onto = GIT_VECTOR_INIT;
size_t i;
@@ -350,6 +389,7 @@ static int apply_splits_and_deletes(
goto on_error;
deleted->status = GIT_DELTA_DELETED;
+ deleted->nfiles = 1;
memset(&deleted->new_file, 0, sizeof(deleted->new_file));
deleted->new_file.path = deleted->old_file.path;
deleted->new_file.flags |= GIT_DIFF_FLAG_VALID_OID;
@@ -361,6 +401,7 @@ static int apply_splits_and_deletes(
delta->status = GIT_DELTA_UNTRACKED;
else
delta->status = GIT_DELTA_ADDED;
+ delta->nfiles = 1;
memset(&delta->old_file, 0, sizeof(delta->old_file));
delta->old_file.path = delta->new_file.path;
delta->old_file.flags |= GIT_DIFF_FLAG_VALID_OID;
@@ -402,63 +443,105 @@ on_error:
return -1;
}
-GIT_INLINE(git_diff_file *) similarity_get_file(git_diff_list *diff, size_t idx)
+GIT_INLINE(git_diff_file *) similarity_get_file(git_diff *diff, size_t idx)
{
git_diff_delta *delta = git_vector_get(&diff->deltas, idx / 2);
return (idx & 1) ? &delta->new_file : &delta->old_file;
}
-static int similarity_calc(
- git_diff_list *diff,
+typedef struct {
+ size_t idx;
+ git_iterator_type_t src;
+ git_repository *repo;
+ git_diff_file *file;
+ git_buf data;
+ git_odb_object *odb_obj;
+ git_blob *blob;
+} similarity_info;
+
+static int similarity_init(
+ similarity_info *info, git_diff *diff, size_t file_idx)
+{
+ info->idx = file_idx;
+ info->src = (file_idx & 1) ? diff->new_src : diff->old_src;
+ info->repo = diff->repo;
+ info->file = similarity_get_file(diff, file_idx);
+ info->odb_obj = NULL;
+ info->blob = NULL;
+ git_buf_init(&info->data, 0);
+
+ if (info->file->size > 0)
+ return 0;
+
+ return git_diff_file__resolve_zero_size(
+ info->file, &info->odb_obj, info->repo);
+}
+
+static int similarity_sig(
+ similarity_info *info,
const git_diff_find_options *opts,
- size_t file_idx,
void **cache)
{
int error = 0;
- git_diff_file *file = similarity_get_file(diff, file_idx);
- git_iterator_type_t src = (file_idx & 1) ? diff->new_src : diff->old_src;
-
- if (src == GIT_ITERATOR_TYPE_WORKDIR) { /* compute hashsig from file */
- git_buf path = GIT_BUF_INIT;
-
- /* TODO: apply wd-to-odb filters to file data if necessary */
+ git_diff_file *file = info->file;
+ if (info->src == GIT_ITERATOR_TYPE_WORKDIR) {
if ((error = git_buf_joinpath(
- &path, git_repository_workdir(diff->repo), file->path)) < 0)
+ &info->data, git_repository_workdir(info->repo), file->path)) < 0)
return error;
/* if path is not a regular file, just skip this item */
- if (git_path_isfile(path.ptr))
- error = opts->metric->file_signature(
- &cache[file_idx], file, path.ptr, opts->metric->payload);
+ if (!git_path_isfile(info->data.ptr))
+ return 0;
- git_buf_free(&path);
- } else { /* compute hashsig from blob buffer */
- git_blob *blob = NULL;
- git_off_t blobsize;
+ /* TODO: apply wd-to-odb filters to file data if necessary */
- /* TODO: add max size threshold a la diff? */
+ error = opts->metric->file_signature(
+ &cache[info->idx], info->file,
+ info->data.ptr, opts->metric->payload);
+ } else {
+ /* if we didn't initially know the size, we might have an odb_obj
+ * around from earlier, so convert that, otherwise load the blob now
+ */
+ if (info->odb_obj != NULL)
+ error = git_object__from_odb_object(
+ (git_object **)&info->blob, info->repo,
+ info->odb_obj, GIT_OBJ_BLOB);
+ else
+ error = git_blob_lookup(&info->blob, info->repo, &file->oid);
- if (git_blob_lookup(&blob, diff->repo, &file->oid) < 0) {
+ if (error < 0) {
/* if lookup fails, just skip this item in similarity calc */
giterr_clear();
- return 0;
- }
+ } else {
+ size_t sz;
- blobsize = git_blob_rawsize(blob);
- if (!git__is_sizet(blobsize)) /* ? what to do ? */
- blobsize = (size_t)-1;
+ /* index size may not be actual blob size if filtered */
+ if (file->size != git_blob_rawsize(info->blob))
+ file->size = git_blob_rawsize(info->blob);
- error = opts->metric->buffer_signature(
- &cache[file_idx], file, git_blob_rawcontent(blob),
- (size_t)blobsize, opts->metric->payload);
+ sz = (size_t)(git__is_sizet(file->size) ? file->size : -1);
- git_blob_free(blob);
+ error = opts->metric->buffer_signature(
+ &cache[info->idx], info->file,
+ git_blob_rawcontent(info->blob), sz, opts->metric->payload);
+ }
}
return error;
}
+static void similarity_unload(similarity_info *info)
+{
+ if (info->odb_obj)
+ git_odb_object_free(info->odb_obj);
+
+ if (info->blob)
+ git_blob_free(info->blob);
+ else
+ git_buf_free(&info->data);
+}
+
#define FLAG_SET(opts,flag_name) (((opts)->flags & flag_name) != 0)
/* - score < 0 means files cannot be compared
@@ -467,7 +550,7 @@ static int similarity_calc(
*/
static int similarity_measure(
int *score,
- git_diff_list *diff,
+ git_diff *diff,
const git_diff_find_options *opts,
void **cache,
size_t a_idx,
@@ -476,6 +559,8 @@ static int similarity_measure(
git_diff_file *a_file = similarity_get_file(diff, a_idx);
git_diff_file *b_file = similarity_get_file(diff, b_idx);
bool exact_match = FLAG_SET(opts, GIT_DIFF_FIND_EXACT_MATCH_ONLY);
+ int error = 0;
+ similarity_info a_info, b_info;
*score = -1;
@@ -510,23 +595,48 @@ static int similarity_measure(
return 0;
}
+ memset(&a_info, 0, sizeof(a_info));
+ memset(&b_info, 0, sizeof(b_info));
+
+ /* set up similarity data (will try to update missing file sizes) */
+ if (!cache[a_idx] && (error = similarity_init(&a_info, diff, a_idx)) < 0)
+ return error;
+ if (!cache[b_idx] && (error = similarity_init(&b_info, diff, b_idx)) < 0)
+ goto cleanup;
+
+ /* check if file sizes are nowhere near each other */
+ if (a_file->size > 127 &&
+ b_file->size > 127 &&
+ (a_file->size > (b_file->size << 3) ||
+ b_file->size > (a_file->size << 3)))
+ goto cleanup;
+
/* update signature cache if needed */
- if (!cache[a_idx] && similarity_calc(diff, opts, a_idx, cache) < 0)
- return -1;
- if (!cache[b_idx] && similarity_calc(diff, opts, b_idx, cache) < 0)
- return -1;
+ if (!cache[a_idx]) {
+ if ((error = similarity_sig(&a_info, opts, cache)) < 0)
+ goto cleanup;
+ }
+ if (!cache[b_idx]) {
+ if ((error = similarity_sig(&b_info, opts, cache)) < 0)
+ goto cleanup;
+ }
- /* some metrics may not wish to process this file (too big / too small) */
- if (!cache[a_idx] || !cache[b_idx])
- return 0;
+ /* calculate similarity provided that the metric choose to process
+ * both the a and b files (some may not if file is too big, etc).
+ */
+ if (cache[a_idx] && cache[b_idx])
+ error = opts->metric->similarity(
+ score, cache[a_idx], cache[b_idx], opts->metric->payload);
+
+cleanup:
+ similarity_unload(&a_info);
+ similarity_unload(&b_info);
- /* compare signatures */
- return opts->metric->similarity(
- score, cache[a_idx], cache[b_idx], opts->metric->payload);
+ return error;
}
static int calc_self_similarity(
- git_diff_list *diff,
+ git_diff *diff,
const git_diff_find_options *opts,
size_t delta_idx,
void **cache)
@@ -543,7 +653,7 @@ static int calc_self_similarity(
return error;
if (similarity >= 0) {
- delta->similarity = (uint32_t)similarity;
+ delta->similarity = (uint16_t)similarity;
delta->flags |= GIT_DIFF_FLAG__HAS_SELF_SIMILARITY;
}
@@ -551,7 +661,7 @@ static int calc_self_similarity(
}
static bool is_rename_target(
- git_diff_list *diff,
+ git_diff *diff,
const git_diff_find_options *opts,
size_t delta_idx,
void **cache)
@@ -590,11 +700,13 @@ static bool is_rename_target(
return false;
case GIT_DELTA_UNTRACKED:
- case GIT_DELTA_IGNORED:
if (!FLAG_SET(opts, GIT_DIFF_FIND_FOR_UNTRACKED))
return false;
break;
+ case GIT_DELTA_IGNORED:
+ return false;
+
default: /* all other status values should be checked */
break;
}
@@ -604,7 +716,7 @@ static bool is_rename_target(
}
static bool is_rename_source(
- git_diff_list *diff,
+ git_diff *diff,
const git_diff_find_options *opts,
size_t delta_idx,
void **cache)
@@ -674,42 +786,49 @@ GIT_INLINE(bool) delta_is_new_only(git_diff_delta *delta)
}
GIT_INLINE(void) delta_make_rename(
- git_diff_delta *to, const git_diff_delta *from, uint32_t similarity)
+ git_diff_delta *to, const git_diff_delta *from, uint16_t similarity)
{
to->status = GIT_DELTA_RENAMED;
to->similarity = similarity;
+ to->nfiles = 2;
memcpy(&to->old_file, &from->old_file, sizeof(to->old_file));
to->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
}
typedef struct {
- uint32_t idx;
- uint32_t similarity;
+ size_t idx;
+ uint16_t similarity;
} diff_find_match;
int git_diff_find_similar(
- git_diff_list *diff,
- git_diff_find_options *given_opts)
+ git_diff *diff,
+ const git_diff_find_options *given_opts)
{
- size_t i, j, sigcache_size;
- int error = 0, similarity;
- git_diff_delta *from, *to;
+ size_t s, t;
+ int error = 0, result;
+ uint16_t similarity;
+ git_diff_delta *src, *tgt;
git_diff_find_options opts;
- size_t num_srcs = 0, num_tgts = 0, tried_srcs = 0, tried_tgts = 0;
+ size_t num_deltas, num_srcs = 0, num_tgts = 0;
+ size_t tried_srcs = 0, tried_tgts = 0;
size_t num_rewrites = 0, num_updates = 0, num_bumped = 0;
void **sigcache; /* cache of similarity metric file signatures */
- diff_find_match *match_srcs = NULL, *match_tgts = NULL, *best_match;
+ diff_find_match *tgt2src = NULL;
+ diff_find_match *src2tgt = NULL;
+ diff_find_match *tgt2src_copy = NULL;
+ diff_find_match *best_match;
git_diff_file swap;
if ((error = normalize_find_opts(diff, &opts, given_opts)) < 0)
return error;
+ num_deltas = diff->deltas.length;
+
/* TODO: maybe abort if deltas.length > rename_limit ??? */
- if (!git__is_uint32(diff->deltas.length))
+ if (!git__is_uint32(num_deltas))
return 0;
- sigcache_size = diff->deltas.length * 2; /* keep size b/c diff may change */
- sigcache = git__calloc(sigcache_size, sizeof(void *));
+ sigcache = git__calloc(num_deltas * 2, sizeof(void *));
GITERR_CHECK_ALLOC(sigcache);
/* Label rename sources and targets
@@ -717,22 +836,30 @@ int git_diff_find_similar(
* This will also set self-similarity scores for MODIFIED files and
* mark them for splitting if break-rewrites is enabled
*/
- git_vector_foreach(&diff->deltas, i, to) {
- if (is_rename_source(diff, &opts, i, sigcache))
+ git_vector_foreach(&diff->deltas, t, tgt) {
+ if (is_rename_source(diff, &opts, t, sigcache))
++num_srcs;
- if (is_rename_target(diff, &opts, i, sigcache))
+ if (is_rename_target(diff, &opts, t, sigcache))
++num_tgts;
+
+ if ((tgt->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0)
+ num_rewrites++;
}
/* if there are no candidate srcs or tgts, we're done */
if (!num_srcs || !num_tgts)
goto cleanup;
- match_tgts = git__calloc(diff->deltas.length, sizeof(diff_find_match));
- GITERR_CHECK_ALLOC(match_tgts);
- match_srcs = git__calloc(diff->deltas.length, sizeof(diff_find_match));
- GITERR_CHECK_ALLOC(match_srcs);
+ src2tgt = git__calloc(num_deltas, sizeof(diff_find_match));
+ GITERR_CHECK_ALLOC(src2tgt);
+ tgt2src = git__calloc(num_deltas, sizeof(diff_find_match));
+ GITERR_CHECK_ALLOC(tgt2src);
+
+ if (FLAG_SET(&opts, GIT_DIFF_FIND_COPIES)) {
+ tgt2src_copy = git__calloc(num_deltas, sizeof(diff_find_match));
+ GITERR_CHECK_ALLOC(tgt2src_copy);
+ }
/*
* Find best-fit matches for rename / copy candidates
@@ -741,47 +868,62 @@ int git_diff_find_similar(
find_best_matches:
tried_tgts = num_bumped = 0;
- git_vector_foreach(&diff->deltas, i, to) {
+ git_vector_foreach(&diff->deltas, t, tgt) {
/* skip things that are not rename targets */
- if ((to->flags & GIT_DIFF_FLAG__IS_RENAME_TARGET) == 0)
+ if ((tgt->flags & GIT_DIFF_FLAG__IS_RENAME_TARGET) == 0)
continue;
tried_srcs = 0;
- git_vector_foreach(&diff->deltas, j, from) {
+ git_vector_foreach(&diff->deltas, s, src) {
/* skip things that are not rename sources */
- if ((from->flags & GIT_DIFF_FLAG__IS_RENAME_SOURCE) == 0)
+ if ((src->flags & GIT_DIFF_FLAG__IS_RENAME_SOURCE) == 0)
continue;
/* calculate similarity for this pair and find best match */
- if (i == j)
- similarity = -1; /* don't measure self-similarity here */
+ if (s == t)
+ result = -1; /* don't measure self-similarity here */
else if ((error = similarity_measure(
- &similarity, diff, &opts, sigcache, 2 * j, 2 * i + 1)) < 0)
+ &result, diff, &opts, sigcache, 2 * s, 2 * t + 1)) < 0)
goto cleanup;
- /* if this pairing is better for the src and the tgt, keep it */
- if (similarity > 0 &&
- match_tgts[i].similarity < (uint32_t)similarity &&
- match_srcs[j].similarity < (uint32_t)similarity)
+ if (result < 0)
+ continue;
+ similarity = (uint16_t)result;
+
+ /* is this a better rename? */
+ if (tgt2src[t].similarity < similarity &&
+ src2tgt[s].similarity < similarity)
{
- if (match_tgts[i].similarity > 0) {
- match_tgts[match_srcs[j].idx].similarity = 0;
- match_srcs[match_tgts[i].idx].similarity = 0;
- ++num_bumped;
+ /* eject old mapping */
+ if (src2tgt[s].similarity > 0) {
+ tgt2src[src2tgt[s].idx].similarity = 0;
+ num_bumped++;
+ }
+ if (tgt2src[t].similarity > 0) {
+ src2tgt[tgt2src[t].idx].similarity = 0;
+ num_bumped++;
}
- match_tgts[i].similarity = (uint32_t)similarity;
- match_tgts[i].idx = (uint32_t)j;
+ /* write new mapping */
+ tgt2src[t].idx = s;
+ tgt2src[t].similarity = similarity;
+ src2tgt[s].idx = t;
+ src2tgt[s].similarity = similarity;
+ }
- match_srcs[j].similarity = (uint32_t)similarity;
- match_srcs[j].idx = (uint32_t)i;
+ /* keep best absolute match for copies */
+ if (tgt2src_copy != NULL &&
+ tgt2src_copy[t].similarity < similarity)
+ {
+ tgt2src_copy[t].idx = s;
+ tgt2src_copy[t].similarity = similarity;
}
if (++tried_srcs >= num_srcs)
break;
- /* cap on maximum targets we'll examine (per "to" file) */
+ /* cap on maximum targets we'll examine (per "tgt" file) */
if (tried_srcs > opts.rename_limit)
break;
}
@@ -799,18 +941,21 @@ find_best_matches:
tried_tgts = 0;
- git_vector_foreach(&diff->deltas, i, to) {
+ git_vector_foreach(&diff->deltas, t, tgt) {
/* skip things that are not rename targets */
- if ((to->flags & GIT_DIFF_FLAG__IS_RENAME_TARGET) == 0)
+ if ((tgt->flags & GIT_DIFF_FLAG__IS_RENAME_TARGET) == 0)
continue;
/* check if this delta was the target of a similarity */
- best_match = &match_tgts[i];
- if (!best_match->similarity)
+ if (tgt2src[t].similarity)
+ best_match = &tgt2src[t];
+ else if (tgt2src_copy && tgt2src_copy[t].similarity)
+ best_match = &tgt2src_copy[t];
+ else
continue;
- j = best_match->idx;
- from = GIT_VECTOR_GET(&diff->deltas, j);
+ s = best_match->idx;
+ src = GIT_VECTOR_GET(&diff->deltas, s);
/* possible scenarios:
* 1. from DELETE to ADD/UNTRACK/IGNORE = RENAME
@@ -820,101 +965,114 @@ find_best_matches:
* 5. from OTHER to ADD/UNTRACK/IGNORE = OTHER + COPY
*/
- if (from->status == GIT_DELTA_DELETED) {
+ if (src->status == GIT_DELTA_DELETED) {
- if (delta_is_new_only(to)) {
+ if (delta_is_new_only(tgt)) {
if (best_match->similarity < opts.rename_threshold)
continue;
- delta_make_rename(to, from, best_match->similarity);
+ delta_make_rename(tgt, src, best_match->similarity);
- from->flags |= GIT_DIFF_FLAG__TO_DELETE;
+ src->flags |= GIT_DIFF_FLAG__TO_DELETE;
num_rewrites++;
} else {
- assert(delta_is_split(to));
+ assert(delta_is_split(tgt));
if (best_match->similarity < opts.rename_from_rewrite_threshold)
continue;
- memcpy(&swap, &to->old_file, sizeof(swap));
+ memcpy(&swap, &tgt->old_file, sizeof(swap));
- delta_make_rename(to, from, best_match->similarity);
+ delta_make_rename(tgt, src, best_match->similarity);
num_rewrites--;
- from->status = GIT_DELTA_DELETED;
- memcpy(&from->old_file, &swap, sizeof(from->old_file));
- memset(&from->new_file, 0, sizeof(from->new_file));
- from->new_file.path = from->old_file.path;
- from->new_file.flags |= GIT_DIFF_FLAG_VALID_OID;
+ assert(src->status == GIT_DELTA_DELETED);
+ memcpy(&src->old_file, &swap, sizeof(src->old_file));
+ memset(&src->new_file, 0, sizeof(src->new_file));
+ src->new_file.path = src->old_file.path;
+ src->new_file.flags |= GIT_DIFF_FLAG_VALID_OID;
num_updates++;
+
+ if (src2tgt[t].similarity > 0 && src2tgt[t].idx > t) {
+ /* what used to be at src t is now at src s */
+ tgt2src[src2tgt[t].idx].idx = s;
+ }
}
}
- else if (delta_is_split(from)) {
+ else if (delta_is_split(src)) {
- if (delta_is_new_only(to)) {
+ if (delta_is_new_only(tgt)) {
if (best_match->similarity < opts.rename_threshold)
continue;
- delta_make_rename(to, from, best_match->similarity);
+ delta_make_rename(tgt, src, best_match->similarity);
- from->status = (diff->new_src == GIT_ITERATOR_TYPE_WORKDIR) ?
+ src->status = (diff->new_src == GIT_ITERATOR_TYPE_WORKDIR) ?
GIT_DELTA_UNTRACKED : GIT_DELTA_ADDED;
- memset(&from->old_file, 0, sizeof(from->old_file));
- from->old_file.path = from->new_file.path;
- from->old_file.flags |= GIT_DIFF_FLAG_VALID_OID;
+ src->nfiles = 1;
+ memset(&src->old_file, 0, sizeof(src->old_file));
+ src->old_file.path = src->new_file.path;
+ src->old_file.flags |= GIT_DIFF_FLAG_VALID_OID;
- from->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
+ src->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
num_rewrites--;
num_updates++;
} else {
- assert(delta_is_split(from));
+ assert(delta_is_split(src));
if (best_match->similarity < opts.rename_from_rewrite_threshold)
continue;
- memcpy(&swap, &to->old_file, sizeof(swap));
+ memcpy(&swap, &tgt->old_file, sizeof(swap));
- delta_make_rename(to, from, best_match->similarity);
+ delta_make_rename(tgt, src, best_match->similarity);
num_rewrites--;
num_updates++;
- memcpy(&from->old_file, &swap, sizeof(from->old_file));
+ memcpy(&src->old_file, &swap, sizeof(src->old_file));
/* if we've just swapped the new element into the correct
* place, clear the SPLIT flag
*/
- if (match_tgts[j].idx == i &&
- match_tgts[j].similarity >
+ if (tgt2src[s].idx == t &&
+ tgt2src[s].similarity >
opts.rename_from_rewrite_threshold) {
-
- from->status = GIT_DELTA_RENAMED;
- from->similarity = match_tgts[j].similarity;
- match_tgts[j].similarity = 0;
- from->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
+ src->status = GIT_DELTA_RENAMED;
+ src->similarity = tgt2src[s].similarity;
+ tgt2src[s].similarity = 0;
+ src->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
num_rewrites--;
}
/* otherwise, if we just overwrote a source, update mapping */
- else if (j > i && match_srcs[i].similarity > 0) {
- match_tgts[match_srcs[i].idx].idx = j;
+ else if (src2tgt[t].similarity > 0 && src2tgt[t].idx > t) {
+ /* what used to be at src t is now at src s */
+ tgt2src[src2tgt[t].idx].idx = s;
}
num_updates++;
}
}
- else if (delta_is_new_only(to)) {
- if (!FLAG_SET(&opts, GIT_DIFF_FIND_COPIES) ||
- best_match->similarity < opts.copy_threshold)
+ else if (delta_is_new_only(tgt)) {
+ if (!FLAG_SET(&opts, GIT_DIFF_FIND_COPIES))
continue;
- to->status = GIT_DELTA_COPIED;
- to->similarity = best_match->similarity;
- memcpy(&to->old_file, &from->old_file, sizeof(to->old_file));
+ if (tgt2src_copy[t].similarity < opts.copy_threshold)
+ continue;
+
+ /* always use best possible source for copy */
+ best_match = &tgt2src_copy[t];
+ src = GIT_VECTOR_GET(&diff->deltas, best_match->idx);
+
+ tgt->status = GIT_DELTA_COPIED;
+ tgt->similarity = best_match->similarity;
+ tgt->nfiles = 2;
+ memcpy(&tgt->old_file, &src->old_file, sizeof(tgt->old_file));
num_updates++;
}
@@ -927,15 +1085,17 @@ find_best_matches:
if (num_rewrites > 0 || num_updates > 0)
error = apply_splits_and_deletes(
diff, diff->deltas.length - num_rewrites,
- FLAG_SET(&opts, GIT_DIFF_BREAK_REWRITES));
+ FLAG_SET(&opts, GIT_DIFF_BREAK_REWRITES) &&
+ !FLAG_SET(&opts, GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY));
cleanup:
- git__free(match_srcs);
- git__free(match_tgts);
+ git__free(tgt2src);
+ git__free(src2tgt);
+ git__free(tgt2src_copy);
- for (i = 0; i < sigcache_size; ++i) {
- if (sigcache[i] != NULL)
- opts.metric->free_signature(sigcache[i], opts.metric->payload);
+ for (t = 0; t < num_deltas * 2; ++t) {
+ if (sigcache[t] != NULL)
+ opts.metric->free_signature(sigcache[t], opts.metric->payload);
}
git__free(sigcache);
diff --git a/src/diff_xdiff.c b/src/diff_xdiff.c
index 7694fb996..e0bc11f7f 100644
--- a/src/diff_xdiff.c
+++ b/src/diff_xdiff.c
@@ -24,26 +24,26 @@ static int git_xdiff_scan_int(const char **str, int *value)
return (digits > 0) ? 0 : -1;
}
-static int git_xdiff_parse_hunk(git_diff_range *range, const char *header)
+static int git_xdiff_parse_hunk(git_diff_hunk *hunk, const char *header)
{
/* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */
if (*header != '@')
return -1;
- if (git_xdiff_scan_int(&header, &range->old_start) < 0)
+ if (git_xdiff_scan_int(&header, &hunk->old_start) < 0)
return -1;
if (*header == ',') {
- if (git_xdiff_scan_int(&header, &range->old_lines) < 0)
+ if (git_xdiff_scan_int(&header, &hunk->old_lines) < 0)
return -1;
} else
- range->old_lines = 1;
- if (git_xdiff_scan_int(&header, &range->new_start) < 0)
+ hunk->old_lines = 1;
+ if (git_xdiff_scan_int(&header, &hunk->new_start) < 0)
return -1;
if (*header == ',') {
- if (git_xdiff_scan_int(&header, &range->new_lines) < 0)
+ if (git_xdiff_scan_int(&header, &hunk->new_lines) < 0)
return -1;
} else
- range->new_lines = 1;
- if (range->old_start < 0 || range->new_start < 0)
+ hunk->new_lines = 1;
+ if (hunk->old_start < 0 || hunk->new_start < 0)
return -1;
return 0;
@@ -51,38 +51,104 @@ static int git_xdiff_parse_hunk(git_diff_range *range, const char *header)
typedef struct {
git_xdiff_output *xo;
- git_diff_patch *patch;
- git_diff_range range;
+ git_patch *patch;
+ git_diff_hunk hunk;
+ int old_lineno, new_lineno;
+ mmfile_t xd_old_data, xd_new_data;
} git_xdiff_info;
+static int diff_update_lines(
+ git_xdiff_info *info,
+ git_diff_line *line,
+ const char *content,
+ size_t content_len)
+{
+ const char *scan = content, *scan_end = content + content_len;
+
+ for (line->num_lines = 0; scan < scan_end; ++scan)
+ if (*scan == '\n')
+ ++line->num_lines;
+
+ line->content = content;
+ line->content_len = content_len;
+
+ /* expect " "/"-"/"+", then data */
+ switch (line->origin) {
+ case GIT_DIFF_LINE_ADDITION:
+ case GIT_DIFF_LINE_DEL_EOFNL:
+ line->old_lineno = -1;
+ line->new_lineno = info->new_lineno;
+ info->new_lineno += (int)line->num_lines;
+ break;
+ case GIT_DIFF_LINE_DELETION:
+ case GIT_DIFF_LINE_ADD_EOFNL:
+ line->old_lineno = info->old_lineno;
+ line->new_lineno = -1;
+ info->old_lineno += (int)line->num_lines;
+ break;
+ case GIT_DIFF_LINE_CONTEXT:
+ case GIT_DIFF_LINE_CONTEXT_EOFNL:
+ line->old_lineno = info->old_lineno;
+ line->new_lineno = info->new_lineno;
+ info->old_lineno += (int)line->num_lines;
+ info->new_lineno += (int)line->num_lines;
+ break;
+ default:
+ giterr_set(GITERR_INVALID, "Unknown diff line origin %02x",
+ (unsigned int)line->origin);
+ return -1;
+ }
+
+ return 0;
+}
+
static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len)
{
git_xdiff_info *info = priv;
- git_diff_patch *patch = info->patch;
- const git_diff_delta *delta = git_diff_patch_delta(patch);
+ git_patch *patch = info->patch;
+ const git_diff_delta *delta = git_patch_get_delta(patch);
git_diff_output *output = &info->xo->output;
+ git_diff_line line;
if (len == 1) {
- output->error = git_xdiff_parse_hunk(&info->range, bufs[0].ptr);
+ output->error = git_xdiff_parse_hunk(&info->hunk, bufs[0].ptr);
if (output->error < 0)
return output->error;
+ info->hunk.header_len = bufs[0].size;
+ if (info->hunk.header_len >= sizeof(info->hunk.header))
+ info->hunk.header_len = sizeof(info->hunk.header) - 1;
+ memcpy(info->hunk.header, bufs[0].ptr, info->hunk.header_len);
+ info->hunk.header[info->hunk.header_len] = '\0';
+
if (output->hunk_cb != NULL &&
- output->hunk_cb(delta, &info->range,
- bufs[0].ptr, bufs[0].size, output->payload))
+ output->hunk_cb(delta, &info->hunk, output->payload))
output->error = GIT_EUSER;
+
+ info->old_lineno = info->hunk.old_start;
+ info->new_lineno = info->hunk.new_start;
}
if (len == 2 || len == 3) {
/* expect " "/"-"/"+", then data */
- char origin =
+ line.origin =
(*bufs[0].ptr == '+') ? GIT_DIFF_LINE_ADDITION :
(*bufs[0].ptr == '-') ? GIT_DIFF_LINE_DELETION :
GIT_DIFF_LINE_CONTEXT;
- if (output->data_cb != NULL &&
- output->data_cb(delta, &info->range,
- origin, bufs[1].ptr, bufs[1].size, output->payload))
+ if (line.origin == GIT_DIFF_LINE_ADDITION)
+ line.content_offset = bufs[1].ptr - info->xd_new_data.ptr;
+ else if (line.origin == GIT_DIFF_LINE_DELETION)
+ line.content_offset = bufs[1].ptr - info->xd_old_data.ptr;
+ else
+ line.content_offset = -1;
+
+ output->error = diff_update_lines(
+ info, &line, bufs[1].ptr, bufs[1].size);
+
+ if (!output->error &&
+ output->data_cb != NULL &&
+ output->data_cb(delta, &info->hunk, &line, output->payload))
output->error = GIT_EUSER;
}
@@ -92,26 +158,30 @@ static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len)
* If we have a '-' and a third buf, then we have removed a line
* with out a newline but added a blank line, so ADD_EOFNL.
*/
- char origin =
+ line.origin =
(*bufs[0].ptr == '+') ? GIT_DIFF_LINE_DEL_EOFNL :
(*bufs[0].ptr == '-') ? GIT_DIFF_LINE_ADD_EOFNL :
GIT_DIFF_LINE_CONTEXT_EOFNL;
- if (output->data_cb != NULL &&
- output->data_cb(delta, &info->range,
- origin, bufs[2].ptr, bufs[2].size, output->payload))
+ line.content_offset = -1;
+
+ output->error = diff_update_lines(
+ info, &line, bufs[2].ptr, bufs[2].size);
+
+ if (!output->error &&
+ output->data_cb != NULL &&
+ output->data_cb(delta, &info->hunk, &line, output->payload))
output->error = GIT_EUSER;
}
return output->error;
}
-static int git_xdiff(git_diff_output *output, git_diff_patch *patch)
+static int git_xdiff(git_diff_output *output, git_patch *patch)
{
git_xdiff_output *xo = (git_xdiff_output *)output;
git_xdiff_info info;
git_diff_find_context_payload findctxt;
- mmfile_t xd_old_data, xd_new_data;
memset(&info, 0, sizeof(info));
info.patch = patch;
@@ -120,7 +190,7 @@ static int git_xdiff(git_diff_output *output, git_diff_patch *patch)
xo->callback.priv = &info;
git_diff_find_context_init(
- &xo->config.find_func, &findctxt, git_diff_patch__driver(patch));
+ &xo->config.find_func, &findctxt, git_patch__driver(patch));
xo->config.find_func_priv = &findctxt;
if (xo->config.find_func != NULL)
@@ -132,10 +202,10 @@ static int git_xdiff(git_diff_output *output, git_diff_patch *patch)
* updates are needed to xo->params.flags
*/
- git_diff_patch__old_data(&xd_old_data.ptr, &xd_old_data.size, patch);
- git_diff_patch__new_data(&xd_new_data.ptr, &xd_new_data.size, patch);
+ git_patch__old_data(&info.xd_old_data.ptr, &info.xd_old_data.size, patch);
+ git_patch__new_data(&info.xd_new_data.ptr, &info.xd_new_data.size, patch);
- xdl_diff(&xd_old_data, &xd_new_data,
+ xdl_diff(&info.xd_old_data, &info.xd_new_data,
&xo->params, &xo->config, &xo->callback);
git_diff_find_context_clear(&findctxt);
@@ -145,7 +215,7 @@ static int git_xdiff(git_diff_output *output, git_diff_patch *patch)
void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts)
{
- uint32_t flags = opts ? opts->flags : GIT_DIFF_NORMAL;
+ uint32_t flags = opts ? opts->flags : 0;
xo->output.diff_cb = git_xdiff;
@@ -161,6 +231,11 @@ void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts)
if (flags & GIT_DIFF_IGNORE_WHITESPACE_EOL)
xo->params.flags |= XDF_IGNORE_WHITESPACE_AT_EOL;
+ if (flags & GIT_DIFF_PATIENCE)
+ xo->params.flags |= XDF_PATIENCE_DIFF;
+ if (flags & GIT_DIFF_MINIMAL)
+ xo->params.flags |= XDF_NEED_MINIMAL;
+
memset(&xo->callback, 0, sizeof(xo->callback));
xo->callback.outf = git_xdiff_cb;
}
diff --git a/src/errors.c b/src/errors.c
index e2629f69e..d04da4ca9 100644
--- a/src/errors.c
+++ b/src/errors.c
@@ -112,8 +112,25 @@ void giterr_clear(void)
#endif
}
+int giterr_detach(git_error *cpy)
+{
+ git_error *error = GIT_GLOBAL->last_error;
+
+ assert(cpy);
+
+ if (!error)
+ return -1;
+
+ cpy->message = error->message;
+ cpy->klass = error->klass;
+
+ error->message = NULL;
+ giterr_clear();
+
+ return 0;
+}
+
const git_error *giterr_last(void)
{
return GIT_GLOBAL->last_error;
}
-
diff --git a/src/fetch.c b/src/fetch.c
index 03fad5fec..276591821 100644
--- a/src/fetch.c
+++ b/src/fetch.c
@@ -19,55 +19,47 @@
#include "repository.h"
#include "refs.h"
-struct filter_payload {
- git_remote *remote;
- const git_refspec *spec, *tagspec;
- git_odb *odb;
- int found_head;
-};
-
-static int filter_ref__cb(git_remote_head *head, void *payload)
+static int maybe_want(git_remote *remote, git_remote_head *head, git_odb *odb, git_refspec *tagspec)
{
- struct filter_payload *p = payload;
int match = 0;
if (!git_reference_is_valid_name(head->name))
return 0;
- if (!p->found_head && strcmp(head->name, GIT_HEAD_FILE) == 0)
- p->found_head = 1;
- else if (p->remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) {
+ if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) {
/*
* If tagopt is --tags, then we only use the default
* tags refspec and ignore the remote's
*/
- if (git_refspec_src_matches(p->tagspec, head->name))
+ if (git_refspec_src_matches(tagspec, head->name))
match = 1;
else
return 0;
- } else if (git_remote__matching_refspec(p->remote, head->name))
+ } else if (git_remote__matching_refspec(remote, head->name))
match = 1;
if (!match)
return 0;
/* If we have the object, mark it so we don't ask for it */
- if (git_odb_exists(p->odb, &head->oid))
+ if (git_odb_exists(odb, &head->oid))
head->local = 1;
else
- p->remote->need_pack = 1;
+ remote->need_pack = 1;
- return git_vector_insert(&p->remote->refs, head);
+ return git_vector_insert(&remote->refs, head);
}
static int filter_wants(git_remote *remote)
{
- struct filter_payload p;
- git_refspec tagspec;
- int error = -1;
+ git_remote_head **heads;
+ git_refspec tagspec, head;
+ int error = 0;
+ git_odb *odb;
+ size_t i, heads_len;
git_vector_clear(&remote->refs);
- if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0)
+ if ((error = git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true)) < 0)
return error;
/*
@@ -76,14 +68,27 @@ static int filter_wants(git_remote *remote)
* not interested in any particular branch but just the remote's
* HEAD, which will be stored in FETCH_HEAD after the fetch.
*/
- p.tagspec = &tagspec;
- p.found_head = 0;
- p.remote = remote;
+ if (remote->active_refspecs.length == 0) {
+ if ((error = git_refspec__parse(&head, "HEAD", true)) < 0)
+ goto cleanup;
- if (git_repository_odb__weakptr(&p.odb, remote->repo) < 0)
+ error = git_refspec__dwim_one(&remote->active_refspecs, &head, &remote->refs);
+ git_refspec__free(&head);
+
+ if (error < 0)
+ goto cleanup;
+ }
+
+ if (git_repository_odb__weakptr(&odb, remote->repo) < 0)
+ goto cleanup;
+
+ if (git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote) < 0)
goto cleanup;
- error = git_remote_ls(remote, filter_ref__cb, &p);
+ for (i = 0; i < heads_len; i++) {
+ if ((error = maybe_want(remote, heads[i], odb, &tagspec)) < 0)
+ break;
+ }
cleanup:
git_refspec__free(&tagspec);
@@ -106,7 +111,7 @@ int git_fetch_negotiate(git_remote *remote)
}
/* Don't try to negotiate when we don't want anything */
- if (remote->refs.length == 0 || !remote->need_pack)
+ if (!remote->need_pack)
return 0;
/*
@@ -119,15 +124,13 @@ int git_fetch_negotiate(git_remote *remote)
remote->refs.length);
}
-int git_fetch_download_pack(
- git_remote *remote,
- git_transfer_progress_callback progress_cb,
- void *progress_payload)
+int git_fetch_download_pack(git_remote *remote)
{
git_transport *t = remote->transport;
if(!remote->need_pack)
return 0;
- return t->download_pack(t, remote->repo, &remote->stats, progress_cb, progress_payload);
+ return t->download_pack(t, remote->repo, &remote->stats,
+ remote->callbacks.transfer_progress, remote->callbacks.payload);
}
diff --git a/src/fetch.h b/src/fetch.h
index 059251d04..9605da1b5 100644
--- a/src/fetch.h
+++ b/src/fetch.h
@@ -11,10 +11,7 @@
int git_fetch_negotiate(git_remote *remote);
-int git_fetch_download_pack(
- git_remote *remote,
- git_transfer_progress_callback progress_cb,
- void *progress_payload);
+int git_fetch_download_pack(git_remote *remote);
int git_fetch__download_pack(
git_transport *t,
diff --git a/src/fetchhead.c b/src/fetchhead.c
index 4dcebb857..67089d13d 100644
--- a/src/fetchhead.c
+++ b/src/fetchhead.c
@@ -74,6 +74,7 @@ static int fetchhead_ref_write(
{
char oid[GIT_OID_HEXSZ + 1];
const char *type, *name;
+ int head = 0;
assert(file && fetchhead_ref);
@@ -87,11 +88,16 @@ static int fetchhead_ref_write(
GIT_REFS_TAGS_DIR) == 0) {
type = "tag ";
name = fetchhead_ref->ref_name + strlen(GIT_REFS_TAGS_DIR);
+ } else if (!git__strcmp(fetchhead_ref->ref_name, GIT_HEAD_FILE)) {
+ head = 1;
} else {
type = "";
name = fetchhead_ref->ref_name;
}
+ if (head)
+ return git_filebuf_printf(file, "%s\t\t%s\n", oid, fetchhead_ref->remote_url);
+
return git_filebuf_printf(file, "%s\t%s\t%s'%s' of %s\n",
oid,
(fetchhead_ref->is_merge) ? "" : "not-for-merge",
@@ -112,7 +118,7 @@ int git_fetchhead_write(git_repository *repo, git_vector *fetchhead_refs)
if (git_buf_joinpath(&path, repo->path_repository, GIT_FETCH_HEAD_FILE) < 0)
return -1;
- if (git_filebuf_open(&file, path.ptr, GIT_FILEBUF_FORCE) < 0) {
+ if (git_filebuf_open(&file, path.ptr, GIT_FILEBUF_FORCE, GIT_REFS_FILE_MODE) < 0) {
git_buf_free(&path);
return -1;
}
@@ -124,7 +130,7 @@ int git_fetchhead_write(git_repository *repo, git_vector *fetchhead_refs)
git_vector_foreach(fetchhead_refs, i, fetchhead_ref)
fetchhead_ref_write(&file, fetchhead_ref);
- return git_filebuf_commit(&file, GIT_REFS_FILE_MODE);
+ return git_filebuf_commit(&file);
}
static int fetchhead_ref_parse(
diff --git a/src/filebuf.c b/src/filebuf.c
index 246ae34e7..9c3dae811 100644
--- a/src/filebuf.c
+++ b/src/filebuf.c
@@ -10,8 +10,6 @@
#include "filebuf.h"
#include "fileops.h"
-#define GIT_LOCK_FILE_MODE 0644
-
static const size_t WRITE_BUFFER_SIZE = (4096 * 2);
enum buferr_t {
@@ -44,7 +42,7 @@ static int verify_last_error(git_filebuf *file)
}
}
-static int lock_file(git_filebuf *file, int flags)
+static int lock_file(git_filebuf *file, int flags, mode_t mode)
{
if (git_path_exists(file->path_lock) == true) {
if (flags & GIT_FILEBUF_FORCE)
@@ -53,20 +51,20 @@ static int lock_file(git_filebuf *file, int flags)
giterr_clear(); /* actual OS error code just confuses */
giterr_set(GITERR_OS,
"Failed to lock file '%s' for writing", file->path_lock);
- return -1;
+ return GIT_ELOCKED;
}
}
/* create path to the file buffer is required */
if (flags & GIT_FILEBUF_FORCE) {
/* XXX: Should dirmode here be configurable? Or is 0777 always fine? */
- file->fd = git_futils_creat_locked_withpath(file->path_lock, 0777, GIT_LOCK_FILE_MODE);
+ file->fd = git_futils_creat_locked_withpath(file->path_lock, 0777, mode);
} else {
- file->fd = git_futils_creat_locked(file->path_lock, GIT_LOCK_FILE_MODE);
+ file->fd = git_futils_creat_locked(file->path_lock, mode);
}
if (file->fd < 0)
- return -1;
+ return file->fd;
file->fd_is_open = true;
@@ -195,9 +193,9 @@ static int write_deflate(git_filebuf *file, void *source, size_t len)
return 0;
}
-int git_filebuf_open(git_filebuf *file, const char *path, int flags)
+int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode)
{
- int compression;
+ int compression, error = -1;
size_t path_len;
/* opening an already open buffer is a programming error;
@@ -255,7 +253,7 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags)
git_buf tmp_path = GIT_BUF_INIT;
/* Open the file as temporary for locking */
- file->fd = git_futils_mktmp(&tmp_path, path);
+ file->fd = git_futils_mktmp(&tmp_path, path, mode);
if (file->fd < 0) {
git_buf_free(&tmp_path);
@@ -282,7 +280,7 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags)
memcpy(file->path_lock + path_len, GIT_FILELOCK_EXTENSION, GIT_FILELOCK_EXTLENGTH);
/* open the file for locking */
- if (lock_file(file, flags) < 0)
+ if ((error = lock_file(file, flags, mode)) < 0)
goto cleanup;
}
@@ -290,7 +288,7 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags)
cleanup:
git_filebuf_cleanup(file);
- return -1;
+ return error;
}
int git_filebuf_hash(git_oid *oid, git_filebuf *file)
@@ -309,16 +307,16 @@ int git_filebuf_hash(git_oid *oid, git_filebuf *file)
return 0;
}
-int git_filebuf_commit_at(git_filebuf *file, const char *path, mode_t mode)
+int git_filebuf_commit_at(git_filebuf *file, const char *path)
{
git__free(file->path_original);
file->path_original = git__strdup(path);
GITERR_CHECK_ALLOC(file->path_original);
- return git_filebuf_commit(file, mode);
+ return git_filebuf_commit(file);
}
-int git_filebuf_commit(git_filebuf *file, mode_t mode)
+int git_filebuf_commit(git_filebuf *file)
{
/* temporary files cannot be committed */
assert(file && file->path_original);
@@ -338,11 +336,6 @@ int git_filebuf_commit(git_filebuf *file, mode_t mode)
file->fd = -1;
- if (p_chmod(file->path_lock, mode)) {
- giterr_set(GITERR_OS, "Failed to set attributes for file at '%s'", file->path_lock);
- goto on_error;
- }
-
p_unlink(file->path_original);
if (p_rename(file->path_lock, file->path_original) < 0) {
diff --git a/src/filebuf.h b/src/filebuf.h
index 823af81bf..044af5405 100644
--- a/src/filebuf.h
+++ b/src/filebuf.h
@@ -77,9 +77,9 @@ int git_filebuf_write(git_filebuf *lock, const void *buff, size_t len);
int git_filebuf_reserve(git_filebuf *file, void **buff, size_t len);
int git_filebuf_printf(git_filebuf *file, const char *format, ...) GIT_FORMAT_PRINTF(2, 3);
-int git_filebuf_open(git_filebuf *lock, const char *path, int flags);
-int git_filebuf_commit(git_filebuf *lock, mode_t mode);
-int git_filebuf_commit_at(git_filebuf *lock, const char *path, mode_t mode);
+int git_filebuf_open(git_filebuf *lock, const char *path, int flags, mode_t mode);
+int git_filebuf_commit(git_filebuf *lock);
+int git_filebuf_commit_at(git_filebuf *lock, const char *path);
void git_filebuf_cleanup(git_filebuf *lock);
int git_filebuf_hash(git_oid *oid, git_filebuf *file);
int git_filebuf_flush(git_filebuf *file);
diff --git a/src/fileops.c b/src/fileops.c
index d5f6acfad..5763b370b 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -6,6 +6,7 @@
*/
#include "common.h"
#include "fileops.h"
+#include "global.h"
#include <ctype.h>
#if GIT_WIN32
#include "win32/findfile.h"
@@ -18,9 +19,12 @@ int git_futils_mkpath2file(const char *file_path, const mode_t mode)
GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR);
}
-int git_futils_mktmp(git_buf *path_out, const char *filename)
+int git_futils_mktmp(git_buf *path_out, const char *filename, mode_t mode)
{
int fd;
+ mode_t mask;
+
+ p_umask(mask = p_umask(0));
git_buf_sets(path_out, filename);
git_buf_puts(path_out, "_git2_XXXXXX");
@@ -34,6 +38,12 @@ int git_futils_mktmp(git_buf *path_out, const char *filename)
return -1;
}
+ if (p_chmod(path_out->ptr, (mode & ~mask))) {
+ giterr_set(GITERR_OS,
+ "Failed to set permissions on file '%s'", path_out->ptr);
+ return -1;
+ }
+
return fd;
}
@@ -55,22 +65,12 @@ int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode
int git_futils_creat_locked(const char *path, const mode_t mode)
{
- int fd;
-
-#ifdef GIT_WIN32
- wchar_t buf[GIT_WIN_PATH];
-
- git__utf8_to_16(buf, GIT_WIN_PATH, path);
- fd = _wopen(buf, O_WRONLY | O_CREAT | O_TRUNC |
- O_EXCL | O_BINARY | O_CLOEXEC, mode);
-#else
- fd = open(path, O_WRONLY | O_CREAT | O_TRUNC |
+ int fd = p_open(path, O_WRONLY | O_CREAT | O_TRUNC |
O_EXCL | O_BINARY | O_CLOEXEC, mode);
-#endif
if (fd < 0) {
giterr_set(GITERR_OS, "Failed to create locked file '%s'", path);
- return -1;
+ return errno == EEXIST ? GIT_ELOCKED : -1;
}
return fd;
@@ -87,11 +87,8 @@ int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, con
int git_futils_open_ro(const char *path)
{
int fd = p_open(path, O_RDONLY);
- if (fd < 0) {
- if (errno == ENOENT || errno == ENOTDIR)
- fd = GIT_ENOTFOUND;
- giterr_set(GITERR_OS, "Failed to open '%s'", path);
- }
+ if (fd < 0)
+ return git_path_set_error(errno, path, "open");
return fd;
}
@@ -110,7 +107,7 @@ git_off_t git_futils_filesize(git_file fd)
mode_t git_futils_canonical_mode(mode_t raw_mode)
{
if (S_ISREG(raw_mode))
- return S_IFREG | GIT_CANONICAL_PERMS(raw_mode);
+ return S_IFREG | GIT_PERMS_CANONICAL(raw_mode);
else if (S_ISLNK(raw_mode))
return S_IFLNK;
else if (S_ISGITLINK(raw_mode))
@@ -156,11 +153,16 @@ int git_futils_readbuffer_updated(
if (updated != NULL)
*updated = 0;
- if ((fd = git_futils_open_ro(path)) < 0)
- return fd;
+ if (p_stat(path, &st) < 0)
+ return git_path_set_error(errno, path, "stat");
- if (p_fstat(fd, &st) < 0 || S_ISDIR(st.st_mode) || !git__is_sizet(st.st_size+1)) {
- p_close(fd);
+
+ if (S_ISDIR(st.st_mode)) {
+ giterr_set(GITERR_INVALID, "requested file is a directory");
+ return GIT_ENOTFOUND;
+ }
+
+ if (!git__is_sizet(st.st_size+1)) {
giterr_set(GITERR_OS, "Invalid regular file stat for '%s'", path);
return -1;
}
@@ -177,7 +179,6 @@ int git_futils_readbuffer_updated(
changed = true;
if (!changed) {
- p_close(fd);
return 0;
}
@@ -186,6 +187,9 @@ int git_futils_readbuffer_updated(
if (size != NULL)
*size = (size_t)st.st_size;
+ if ((fd = git_futils_open_ro(path)) < 0)
+ return fd;
+
if (git_futils_readbuffer_fd(buf, fd, (size_t)st.st_size) < 0) {
p_close(fd);
return -1;
@@ -222,6 +226,7 @@ int git_futils_writebuffer(
if ((error = p_write(fd, git_buf_cstr(buf), git_buf_len(buf))) < 0) {
giterr_set(GITERR_OS, "Could not write to '%s'", path);
(void)p_close(fd);
+ return error;
}
if ((error = p_close(fd)) < 0)
@@ -347,12 +352,11 @@ int git_futils_mkdir(
/* make directory */
if (p_mkdir(make_path.ptr, mode) < 0) {
- int tmp_errno = errno;
+ int tmp_errno = giterr_system_last();
/* ignore error if directory already exists */
- if (p_stat(make_path.ptr, &st) < 0 ||
- !(S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode))) {
- errno = tmp_errno;
+ if (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode)) {
+ giterr_system_set(tmp_errno);
giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path.ptr);
goto done;
}
@@ -400,8 +404,11 @@ typedef struct {
size_t baselen;
uint32_t flags;
int error;
+ int depth;
} futils__rmdir_data;
+#define FUTILS_MAX_DEPTH 100
+
static int futils__error_cannot_rmdir(const char *path, const char *filemsg)
{
if (filemsg)
@@ -440,51 +447,60 @@ static int futils__rm_first_parent(git_buf *path, const char *ceiling)
static int futils__rmdir_recurs_foreach(void *opaque, git_buf *path)
{
- struct stat st;
futils__rmdir_data *data = opaque;
+ int error = data->error;
+ struct stat st;
+
+ if (data->depth > FUTILS_MAX_DEPTH)
+ error = futils__error_cannot_rmdir(
+ path->ptr, "directory nesting too deep");
- if ((data->error = p_lstat_posixly(path->ptr, &st)) < 0) {
+ else if ((error = p_lstat_posixly(path->ptr, &st)) < 0) {
if (errno == ENOENT)
- data->error = 0;
+ error = 0;
else if (errno == ENOTDIR) {
/* asked to remove a/b/c/d/e and a/b is a normal file */
if ((data->flags & GIT_RMDIR_REMOVE_BLOCKERS) != 0)
- data->error = futils__rm_first_parent(path, data->base);
+ error = futils__rm_first_parent(path, data->base);
else
futils__error_cannot_rmdir(
path->ptr, "parent is not directory");
}
else
- futils__error_cannot_rmdir(path->ptr, "cannot access");
+ error = git_path_set_error(errno, path->ptr, "rmdir");
}
else if (S_ISDIR(st.st_mode)) {
- int error = git_path_direach(path, futils__rmdir_recurs_foreach, data);
+ data->depth++;
+
+ error = git_path_direach(path, 0, futils__rmdir_recurs_foreach, data);
if (error < 0)
return (error == GIT_EUSER) ? data->error : error;
- data->error = p_rmdir(path->ptr);
+ data->depth--;
+
+ if (data->depth == 0 && (data->flags & GIT_RMDIR_SKIP_ROOT) != 0)
+ return data->error;
- if (data->error < 0) {
+ if ((error = p_rmdir(path->ptr)) < 0) {
if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) != 0 &&
(errno == ENOTEMPTY || errno == EEXIST || errno == EBUSY))
- data->error = 0;
+ error = 0;
else
- futils__error_cannot_rmdir(path->ptr, NULL);
+ error = git_path_set_error(errno, path->ptr, "rmdir");
}
}
else if ((data->flags & GIT_RMDIR_REMOVE_FILES) != 0) {
- data->error = p_unlink(path->ptr);
-
- if (data->error < 0)
- futils__error_cannot_rmdir(path->ptr, "cannot be removed");
+ if (p_unlink(path->ptr) < 0)
+ error = git_path_set_error(errno, path->ptr, "remove");
}
else if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) == 0)
- data->error = futils__error_cannot_rmdir(path->ptr, "still present");
+ error = futils__error_cannot_rmdir(path->ptr, "still present");
- return data->error;
+ data->error = error;
+ return error;
}
static int futils__rmdir_empty_parent(void *opaque, git_buf *path)
@@ -507,7 +523,7 @@ static int futils__rmdir_empty_parent(void *opaque, git_buf *path)
giterr_clear();
error = GIT_ITEROVER;
} else {
- futils__error_cannot_rmdir(git_buf_cstr(path), NULL);
+ error = git_path_set_error(errno, git_buf_cstr(path), "rmdir");
}
}
@@ -519,7 +535,7 @@ int git_futils_rmdir_r(
{
int error;
git_buf fullpath = GIT_BUF_INIT;
- futils__rmdir_data data;
+ futils__rmdir_data data = { 0 };
/* build path and find "root" where we should start calling mkdir */
if (git_path_join_unrooted(&fullpath, path, base, NULL) < 0)
@@ -528,7 +544,6 @@ int git_futils_rmdir_r(
data.base = base ? base : "";
data.baselen = base ? strlen(base) : 0;
data.flags = flags;
- data.error = 0;
error = futils__rmdir_recurs_foreach(&data, &fullpath);
@@ -546,46 +561,11 @@ int git_futils_rmdir_r(
return error;
}
-int git_futils_cleanupdir_r(const char *path)
-{
- int error;
- git_buf fullpath = GIT_BUF_INIT;
- futils__rmdir_data data;
-
- if ((error = git_buf_put(&fullpath, path, strlen(path))) < 0)
- goto clean_up;
-
- data.base = "";
- data.baselen = 0;
- data.flags = GIT_RMDIR_REMOVE_FILES;
- data.error = 0;
-
- if (!git_path_exists(path)) {
- giterr_set(GITERR_OS, "Path does not exist: %s" , path);
- error = GIT_ERROR;
- goto clean_up;
- }
-
- if (!git_path_isdir(path)) {
- giterr_set(GITERR_OS, "Path is not a directory: %s" , path);
- error = GIT_ERROR;
- goto clean_up;
- }
-
- error = git_path_direach(&fullpath, futils__rmdir_recurs_foreach, &data);
- if (error == GIT_EUSER)
- error = data.error;
-
-clean_up:
- git_buf_free(&fullpath);
- return error;
-}
-
static int git_futils_guess_system_dirs(git_buf *out)
{
#ifdef GIT_WIN32
- return git_win32__find_system_dirs(out);
+ return git_win32__find_system_dirs(out, L"etc\\");
#else
return git_buf_sets(out, "/etc");
#endif
@@ -617,17 +597,48 @@ static int git_futils_guess_xdg_dirs(git_buf *out)
#endif
}
+static int git_futils_guess_template_dirs(git_buf *out)
+{
+#ifdef GIT_WIN32
+ return git_win32__find_system_dirs(out, L"share\\git-core\\templates");
+#else
+ return git_buf_sets(out, "/usr/share/git-core/templates");
+#endif
+}
+
typedef int (*git_futils_dirs_guess_cb)(git_buf *out);
static git_buf git_futils__dirs[GIT_FUTILS_DIR__MAX] =
- { GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT };
+ { GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT };
static git_futils_dirs_guess_cb git_futils__dir_guess[GIT_FUTILS_DIR__MAX] = {
git_futils_guess_system_dirs,
git_futils_guess_global_dirs,
git_futils_guess_xdg_dirs,
+ git_futils_guess_template_dirs,
};
+void git_futils_dirs_global_shutdown(void)
+{
+ int i;
+ for (i = 0; i < GIT_FUTILS_DIR__MAX; ++i)
+ git_buf_free(&git_futils__dirs[i]);
+}
+
+int git_futils_dirs_global_init(void)
+{
+ git_futils_dir_t i;
+ const git_buf *path;
+ int error = 0;
+
+ for (i = 0; !error && i < GIT_FUTILS_DIR__MAX; i++)
+ error = git_futils_dirs_get(&path, i);
+
+ git__on_shutdown(git_futils_dirs_global_shutdown);
+
+ return error;
+}
+
static int git_futils_check_selector(git_futils_dir_t which)
{
if (which < GIT_FUTILS_DIR__MAX)
@@ -707,13 +718,6 @@ int git_futils_dirs_set(git_futils_dir_t which, const char *search_path)
return git_buf_oom(&git_futils__dirs[which]) ? -1 : 0;
}
-void git_futils_dirs_free(void)
-{
- int i;
- for (i = 0; i < GIT_FUTILS_DIR__MAX; ++i)
- git_buf_free(&git_futils__dirs[i]);
-}
-
static int git_futils_find_in_dirlist(
git_buf *path, const char *name, git_futils_dir_t which, const char *label)
{
@@ -734,7 +738,8 @@ static int git_futils_find_in_dirlist(
continue;
GITERR_CHECK_ERROR(git_buf_set(path, scan, len));
- GITERR_CHECK_ERROR(git_buf_joinpath(path, path->ptr, name));
+ if (name)
+ GITERR_CHECK_ERROR(git_buf_joinpath(path, path->ptr, name));
if (git_path_exists(path->ptr))
return 0;
@@ -763,6 +768,12 @@ int git_futils_find_xdg_file(git_buf *path, const char *filename)
path, filename, GIT_FUTILS_DIR_XDG, "global/xdg");
}
+int git_futils_find_template_dir(git_buf *path)
+{
+ return git_futils_find_in_dirlist(
+ path, NULL, GIT_FUTILS_DIR_TEMPLATE, "template");
+}
+
int git_futils_fake_symlink(const char *old, const char *new)
{
int retcode = GIT_ERROR;
@@ -807,11 +818,8 @@ int git_futils_cp(const char *from, const char *to, mode_t filemode)
return ifd;
if ((ofd = p_open(to, O_WRONLY | O_CREAT | O_EXCL, filemode)) < 0) {
- if (errno == ENOENT || errno == ENOTDIR)
- ofd = GIT_ENOTFOUND;
- giterr_set(GITERR_OS, "Failed to open '%s' for writing", to);
p_close(ifd);
- return ofd;
+ return git_path_set_error(errno, to, "open for writing");
}
return cp_by_fd(ifd, ofd, true);
@@ -850,6 +858,7 @@ typedef struct {
uint32_t flags;
uint32_t mkdir_flags;
mode_t dirmode;
+ int error;
} cp_r_info;
#define GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT (1u << 10)
@@ -888,20 +897,22 @@ static int _cp_r_callback(void *ref, git_buf *from)
return 0;
if (git_buf_joinpath(
- &info->to, info->to_root, from->ptr + info->from_prefix) < 0)
- return -1;
+ &info->to, info->to_root, from->ptr + info->from_prefix) < 0) {
+ error = -1;
+ goto exit;
+ }
- if (p_lstat(info->to.ptr, &to_st) < 0) {
- if (errno != ENOENT && errno != ENOTDIR) {
- giterr_set(GITERR_OS,
- "Could not access %s while copying files", info->to.ptr);
- return -1;
- }
- } else
+ if (!(error = git_path_lstat(info->to.ptr, &to_st)))
exists = true;
+ else if (error != GIT_ENOTFOUND)
+ goto exit;
+ else {
+ giterr_clear();
+ error = 0;
+ }
if ((error = git_path_lstat(from->ptr, &from_st)) < 0)
- return error;
+ goto exit;
if (S_ISDIR(from_st.st_mode)) {
mode_t oldmode = info->dirmode;
@@ -915,13 +926,17 @@ static int _cp_r_callback(void *ref, git_buf *from)
error = _cp_r_mkdir(info, from);
/* recurse onto target directory */
- if (!error && (!exists || S_ISDIR(to_st.st_mode)))
- error = git_path_direach(from, _cp_r_callback, info);
+ if (!error && (!exists || S_ISDIR(to_st.st_mode))) {
+ error = git_path_direach(from, 0, _cp_r_callback, info);
+
+ if (error == GIT_EUSER)
+ error = info->error;
+ }
if (oldmode != 0)
info->dirmode = oldmode;
- return error;
+ goto exit;
}
if (exists) {
@@ -931,7 +946,8 @@ static int _cp_r_callback(void *ref, git_buf *from)
if (p_unlink(info->to.ptr) < 0) {
giterr_set(GITERR_OS, "Cannot overwrite existing file '%s'",
info->to.ptr);
- return -1;
+ error = -1;
+ goto exit;
}
}
@@ -944,7 +960,7 @@ static int _cp_r_callback(void *ref, git_buf *from)
/* Make container directory on demand if needed */
if ((info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0 &&
(error = _cp_r_mkdir(info, from)) < 0)
- return error;
+ goto exit;
/* make symlink or regular file */
if (S_ISLNK(from_st.st_mode))
@@ -953,11 +969,13 @@ static int _cp_r_callback(void *ref, git_buf *from)
mode_t usemode = from_st.st_mode;
if ((info->flags & GIT_CPDIR_SIMPLE_TO_MODE) != 0)
- usemode = (usemode & 0111) ? 0777 : 0666;
+ usemode = GIT_PERMS_FOR_WRITE(usemode);
error = git_futils_cp(from->ptr, info->to.ptr, usemode);
}
+exit:
+ info->error = error;
return error;
}
@@ -978,6 +996,7 @@ int git_futils_cp_r(
info.flags = flags;
info.dirmode = dirmode;
info.from_prefix = path.size;
+ info.error = 0;
git_buf_init(&info.to, 0);
/* precalculate mkdir flags */
@@ -999,6 +1018,9 @@ int git_futils_cp_r(
git_buf_free(&path);
git_buf_free(&info.to);
+ if (error == GIT_EUSER)
+ error = info.error;
+
return error;
}
diff --git a/src/fileops.h b/src/fileops.h
index f4e059c83..636c9b67d 100644
--- a/src/fileops.h
+++ b/src/fileops.h
@@ -115,12 +115,7 @@ extern int git_futils_mkpath2file(const char *path, const mode_t mode);
* * GIT_RMDIR_EMPTY_PARENTS - remove containing directories up to base
* if removing this item leaves them empty
* * GIT_RMDIR_REMOVE_BLOCKERS - remove blocking file that causes ENOTDIR
- *
- * The old values translate into the new as follows:
- *
- * * GIT_DIRREMOVAL_EMPTY_HIERARCHY == GIT_RMDIR_EMPTY_HIERARCHY
- * * GIT_DIRREMOVAL_FILES_AND_DIRS ~= GIT_RMDIR_REMOVE_FILES
- * * GIT_DIRREMOVAL_ONLY_EMPTY_DIRS == GIT_RMDIR_SKIP_NONEMPTY
+ * * GIT_RMDIR_SKIP_ROOT - don't remove root directory itself
*/
typedef enum {
GIT_RMDIR_EMPTY_HIERARCHY = 0,
@@ -128,6 +123,7 @@ typedef enum {
GIT_RMDIR_SKIP_NONEMPTY = (1 << 1),
GIT_RMDIR_EMPTY_PARENTS = (1 << 2),
GIT_RMDIR_REMOVE_BLOCKERS = (1 << 3),
+ GIT_RMDIR_SKIP_ROOT = (1 << 4),
} git_futils_rmdir_flags;
/**
@@ -141,19 +137,11 @@ typedef enum {
extern int git_futils_rmdir_r(const char *path, const char *base, uint32_t flags);
/**
- * Remove all files and directories beneath the specified path.
- *
- * @param path Path to the top level directory to process.
- * @return 0 on success; -1 on error.
- */
-extern int git_futils_cleanupdir_r(const char *path);
-
-/**
* Create and open a temporary file with a `_git2_` suffix.
* Writes the filename into path_out.
* @return On success, an open file descriptor, else an error code < 0.
*/
-extern int git_futils_mktmp(git_buf *path_out, const char *filename);
+extern int git_futils_mktmp(git_buf *path_out, const char *filename, mode_t mode);
/**
* Move a file on the filesystem, create the
@@ -223,9 +211,13 @@ extern int git_futils_open_ro(const char *path);
*/
extern git_off_t git_futils_filesize(git_file fd);
+#define GIT_PERMS_IS_EXEC(MODE) (((MODE) & 0111) != 0)
+#define GIT_PERMS_CANONICAL(MODE) (GIT_PERMS_IS_EXEC(MODE) ? 0755 : 0644)
+#define GIT_PERMS_FOR_WRITE(MODE) (GIT_PERMS_IS_EXEC(MODE) ? 0777 : 0666)
+
#define GIT_MODE_PERMS_MASK 0777
-#define GIT_CANONICAL_PERMS(MODE) (((MODE) & 0100) ? 0755 : 0644)
-#define GIT_MODE_TYPE(MODE) ((MODE) & ~GIT_MODE_PERMS_MASK)
+#define GIT_MODE_TYPE_MASK 0170000
+#define GIT_MODE_TYPE(MODE) ((MODE) & GIT_MODE_TYPE_MASK)
#define GIT_MODE_ISBLOB(MODE) (GIT_MODE_TYPE(MODE) == GIT_MODE_TYPE(GIT_FILEMODE_BLOB))
/**
@@ -244,7 +236,7 @@ extern mode_t git_futils_canonical_mode(mode_t raw_mode);
* @param out buffer to populate with the mapping information.
* @param fd open descriptor to configure the mapping from.
* @param begin first byte to map, this should be page aligned.
- * @param end number of bytes to map.
+ * @param len number of bytes to map.
* @return
* - 0 on success;
* - -1 on error.
@@ -278,7 +270,7 @@ extern void git_futils_mmap_free(git_map *map);
/**
* Find a "global" file (i.e. one in a user's home directory).
*
- * @param pathbuf buffer to write the full path into
+ * @param path buffer to write the full path into
* @param filename name of file to find in the home directory
* @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
*/
@@ -287,7 +279,7 @@ extern int git_futils_find_global_file(git_buf *path, const char *filename);
/**
* Find an "XDG" file (i.e. one in user's XDG config path).
*
- * @param pathbuf buffer to write the full path into
+ * @param path buffer to write the full path into
* @param filename name of file to find in the home directory
* @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
*/
@@ -296,20 +288,36 @@ extern int git_futils_find_xdg_file(git_buf *path, const char *filename);
/**
* Find a "system" file (i.e. one shared for all users of the system).
*
- * @param pathbuf buffer to write the full path into
+ * @param path buffer to write the full path into
* @param filename name of file to find in the home directory
* @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
*/
extern int git_futils_find_system_file(git_buf *path, const char *filename);
+/**
+ * Find template directory.
+ *
+ * @param path buffer to write the full path into
+ * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
+ */
+extern int git_futils_find_template_dir(git_buf *path);
+
typedef enum {
GIT_FUTILS_DIR_SYSTEM = 0,
GIT_FUTILS_DIR_GLOBAL = 1,
GIT_FUTILS_DIR_XDG = 2,
- GIT_FUTILS_DIR__MAX = 3,
+ GIT_FUTILS_DIR_TEMPLATE = 3,
+ GIT_FUTILS_DIR__MAX = 4,
} git_futils_dir_t;
/**
+ * Configures global data for configuration file search paths.
+ *
+ * @return 0 on success, <0 on failure
+ */
+extern int git_futils_dirs_global_init(void);
+
+/**
* Get the search path for global/system/xdg files
*
* @param out pointer to git_buf containing search path
@@ -343,11 +351,6 @@ extern int git_futils_dirs_get_str(
extern int git_futils_dirs_set(git_futils_dir_t which, const char *paths);
/**
- * Release / reset all search paths
- */
-extern void git_futils_dirs_free(void);
-
-/**
* Create a "fake" symlink (text file containing the target path).
*
* @param new symlink file to be created
@@ -396,4 +399,9 @@ extern int git_futils_filestamp_check(
extern void git_futils_filestamp_set(
git_futils_filestamp *tgt, const git_futils_filestamp *src);
+/**
+ * Free the configuration file search paths.
+ */
+extern void git_futils_dirs_global_shutdown(void);
+
#endif /* INCLUDE_fileops_h__ */
diff --git a/src/filter.c b/src/filter.c
index 9f749dcbd..9f866fe88 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -10,68 +10,594 @@
#include "hash.h"
#include "filter.h"
#include "repository.h"
+#include "global.h"
+#include "git2/sys/filter.h"
#include "git2/config.h"
#include "blob.h"
+#include "attr_file.h"
+#include "array.h"
-int git_filters_load(git_vector *filters, git_repository *repo, const char *path, int mode)
+struct git_filter_source {
+ git_repository *repo;
+ const char *path;
+ git_oid oid; /* zero if unknown (which is likely) */
+ uint16_t filemode; /* zero if unknown */
+ git_filter_mode_t mode;
+};
+
+typedef struct {
+ git_filter *filter;
+ void *payload;
+} git_filter_entry;
+
+struct git_filter_list {
+ git_array_t(git_filter_entry) filters;
+ git_filter_source source;
+ char path[GIT_FLEX_ARRAY];
+};
+
+typedef struct {
+ const char *filter_name;
+ git_filter *filter;
+ int priority;
+ int initialized;
+ size_t nattrs, nmatches;
+ char *attrdata;
+ const char *attrs[GIT_FLEX_ARRAY];
+} git_filter_def;
+
+static int filter_def_priority_cmp(const void *a, const void *b)
{
- int error;
+ int pa = ((const git_filter_def *)a)->priority;
+ int pb = ((const git_filter_def *)b)->priority;
+ return (pa < pb) ? -1 : (pa > pb) ? 1 : 0;
+}
- if (mode == GIT_FILTER_TO_ODB) {
- /* Load the CRLF cleanup filter when writing to the ODB */
- error = git_filter_add__crlf_to_odb(filters, repo, path);
- if (error < 0)
- return error;
- } else {
- error = git_filter_add__crlf_to_workdir(filters, repo, path);
- if (error < 0)
- return error;
+struct filter_registry {
+ git_vector filters;
+};
+
+static struct filter_registry *git__filter_registry = NULL;
+
+static void filter_registry_shutdown(void)
+{
+ struct filter_registry *reg = NULL;
+ size_t pos;
+ git_filter_def *fdef;
+
+ if ((reg = git__swap(git__filter_registry, NULL)) == NULL)
+ return;
+
+ git_vector_foreach(&reg->filters, pos, fdef) {
+ if (fdef->initialized && fdef->filter && fdef->filter->shutdown) {
+ fdef->filter->shutdown(fdef->filter);
+ fdef->initialized = false;
+ }
+
+ git__free(fdef->attrdata);
+ git__free(fdef);
}
- return (int)filters->length;
+ git_vector_free(&reg->filters);
+ git__free(reg);
}
-void git_filters_free(git_vector *filters)
+static int filter_registry_initialize(void)
{
- size_t i;
- git_filter *filter;
+ int error = 0;
+ struct filter_registry *reg;
+
+ if (git__filter_registry)
+ return 0;
+
+ reg = git__calloc(1, sizeof(struct filter_registry));
+ GITERR_CHECK_ALLOC(reg);
+
+ if ((error = git_vector_init(
+ &reg->filters, 2, filter_def_priority_cmp)) < 0)
+ goto cleanup;
+
+ reg = git__compare_and_swap(&git__filter_registry, NULL, reg);
+ if (reg != NULL)
+ goto cleanup;
+
+ git__on_shutdown(filter_registry_shutdown);
+
+ /* try to register both default filters */
+ {
+ git_filter *crlf = git_crlf_filter_new();
+ git_filter *ident = git_ident_filter_new();
+
+ if (crlf && git_filter_register(
+ GIT_FILTER_CRLF, crlf, GIT_FILTER_CRLF_PRIORITY) < 0)
+ crlf = NULL;
+ if (ident && git_filter_register(
+ GIT_FILTER_IDENT, ident, GIT_FILTER_IDENT_PRIORITY) < 0)
+ ident = NULL;
+
+ if (!crlf || !ident)
+ return -1;
+ }
+
+ return 0;
+
+cleanup:
+ git_vector_free(&reg->filters);
+ git__free(reg);
+ return error;
+}
+
+static int filter_def_scan_attrs(
+ git_buf *attrs, size_t *nattr, size_t *nmatch, const char *attr_str)
+{
+ const char *start, *scan = attr_str;
+ int has_eq;
+
+ *nattr = *nmatch = 0;
+
+ if (!scan)
+ return 0;
+
+ while (*scan) {
+ while (git__isspace(*scan)) scan++;
+
+ for (start = scan, has_eq = 0; *scan && !git__isspace(*scan); ++scan) {
+ if (*scan == '=')
+ has_eq = 1;
+ }
+
+ if (scan > start) {
+ (*nattr)++;
+ if (has_eq || *start == '-' || *start == '+' || *start == '!')
+ (*nmatch)++;
- git_vector_foreach(filters, i, filter) {
- if (filter->do_free != NULL)
- filter->do_free(filter);
- else
- git__free(filter);
+ if (has_eq)
+ git_buf_putc(attrs, '=');
+ git_buf_put(attrs, start, scan - start);
+ git_buf_putc(attrs, '\0');
+ }
}
- git_vector_free(filters);
+ return 0;
}
-int git_filters_apply(git_buf *dest, git_buf *source, git_vector *filters)
+static void filter_def_set_attrs(git_filter_def *fdef)
{
+ char *scan = fdef->attrdata;
size_t i;
- unsigned int src;
- git_buf *dbuffer[2];
- dbuffer[0] = source;
- dbuffer[1] = dest;
+ for (i = 0; i < fdef->nattrs; ++i) {
+ const char *name, *value;
+
+ switch (*scan) {
+ case '=':
+ name = scan + 1;
+ for (scan++; *scan != '='; scan++) /* find '=' */;
+ *scan++ = '\0';
+ value = scan;
+ break;
+ case '-':
+ name = scan + 1; value = git_attr__false; break;
+ case '+':
+ name = scan + 1; value = git_attr__true; break;
+ case '!':
+ name = scan + 1; value = git_attr__unset; break;
+ default:
+ name = scan; value = NULL; break;
+ }
+
+ fdef->attrs[i] = name;
+ fdef->attrs[i + fdef->nattrs] = value;
+
+ scan += strlen(scan) + 1;
+ }
+}
+
+static int filter_def_name_key_check(const void *key, const void *fdef)
+{
+ const char *name =
+ fdef ? ((const git_filter_def *)fdef)->filter_name : NULL;
+ return name ? git__strcmp(key, name) : -1;
+}
- src = 0;
+static int filter_def_filter_key_check(const void *key, const void *fdef)
+{
+ const void *filter = fdef ? ((const git_filter_def *)fdef)->filter : NULL;
+ return (key == filter) ? 0 : -1;
+}
+
+static int filter_registry_find(size_t *pos, const char *name)
+{
+ return git_vector_search2(
+ pos, &git__filter_registry->filters, filter_def_name_key_check, name);
+}
- if (git_buf_len(source) == 0) {
- git_buf_clear(dest);
+static git_filter_def *filter_registry_lookup(size_t *pos, const char *name)
+{
+ git_filter_def *fdef = NULL;
+
+ if (!filter_registry_find(pos, name))
+ fdef = git_vector_get(&git__filter_registry->filters, *pos);
+
+ return fdef;
+}
+
+int git_filter_register(
+ const char *name, git_filter *filter, int priority)
+{
+ git_filter_def *fdef;
+ size_t nattr = 0, nmatch = 0;
+ git_buf attrs = GIT_BUF_INIT;
+
+ if (filter_registry_initialize() < 0)
+ return -1;
+
+ if (!filter_registry_find(NULL, name)) {
+ giterr_set(
+ GITERR_FILTER, "Attempt to reregister existing filter '%s'", name);
+ return GIT_EEXISTS;
+ }
+
+ if (filter_def_scan_attrs(&attrs, &nattr, &nmatch, filter->attributes) < 0)
+ return -1;
+
+ fdef = git__calloc(
+ sizeof(git_filter_def) + 2 * nattr * sizeof(char *), 1);
+ GITERR_CHECK_ALLOC(fdef);
+
+ fdef->filter_name = name;
+ fdef->filter = filter;
+ fdef->priority = priority;
+ fdef->nattrs = nattr;
+ fdef->nmatches = nmatch;
+ fdef->attrdata = git_buf_detach(&attrs);
+
+ filter_def_set_attrs(fdef);
+
+ if (git_vector_insert(&git__filter_registry->filters, fdef) < 0) {
+ git__free(fdef->attrdata);
+ git__free(fdef);
+ return -1;
+ }
+
+ git_vector_sort(&git__filter_registry->filters);
+ return 0;
+}
+
+int git_filter_unregister(const char *name)
+{
+ size_t pos;
+ git_filter_def *fdef;
+
+ /* cannot unregister default filters */
+ if (!strcmp(GIT_FILTER_CRLF, name) || !strcmp(GIT_FILTER_IDENT, name)) {
+ giterr_set(GITERR_FILTER, "Cannot unregister filter '%s'", name);
+ return -1;
+ }
+
+ if ((fdef = filter_registry_lookup(&pos, name)) == NULL) {
+ giterr_set(GITERR_FILTER, "Cannot find filter '%s' to unregister", name);
+ return GIT_ENOTFOUND;
+ }
+
+ (void)git_vector_remove(&git__filter_registry->filters, pos);
+
+ if (fdef->initialized && fdef->filter && fdef->filter->shutdown) {
+ fdef->filter->shutdown(fdef->filter);
+ fdef->initialized = false;
+ }
+
+ git__free(fdef->attrdata);
+ git__free(fdef);
+
+ return 0;
+}
+
+static int filter_initialize(git_filter_def *fdef)
+{
+ int error = 0;
+
+ if (!fdef->initialized &&
+ fdef->filter &&
+ fdef->filter->initialize &&
+ (error = fdef->filter->initialize(fdef->filter)) < 0)
+ {
+ /* auto-unregister if initialize fails */
+ git_filter_unregister(fdef->filter_name);
+ return error;
+ }
+
+ fdef->initialized = true;
+ return 0;
+}
+
+git_filter *git_filter_lookup(const char *name)
+{
+ size_t pos;
+ git_filter_def *fdef;
+
+ if (filter_registry_initialize() < 0)
+ return NULL;
+
+ if ((fdef = filter_registry_lookup(&pos, name)) == NULL)
+ return NULL;
+
+ if (!fdef->initialized && filter_initialize(fdef) < 0)
+ return NULL;
+
+ return fdef->filter;
+}
+
+void git_filter_free(git_filter *filter)
+{
+ git__free(filter);
+}
+
+git_repository *git_filter_source_repo(const git_filter_source *src)
+{
+ return src->repo;
+}
+
+const char *git_filter_source_path(const git_filter_source *src)
+{
+ return src->path;
+}
+
+uint16_t git_filter_source_filemode(const git_filter_source *src)
+{
+ return src->filemode;
+}
+
+const git_oid *git_filter_source_id(const git_filter_source *src)
+{
+ return git_oid_iszero(&src->oid) ? NULL : &src->oid;
+}
+
+git_filter_mode_t git_filter_source_mode(const git_filter_source *src)
+{
+ return src->mode;
+}
+
+static int filter_list_new(
+ git_filter_list **out, const git_filter_source *src)
+{
+ git_filter_list *fl = NULL;
+ size_t pathlen = src->path ? strlen(src->path) : 0;
+
+ fl = git__calloc(1, sizeof(git_filter_list) + pathlen + 1);
+ GITERR_CHECK_ALLOC(fl);
+
+ if (src->path)
+ memcpy(fl->path, src->path, pathlen);
+ fl->source.repo = src->repo;
+ fl->source.path = fl->path;
+ fl->source.mode = src->mode;
+
+ *out = fl;
+ return 0;
+}
+
+static int filter_list_check_attributes(
+ const char ***out, git_filter_def *fdef, const git_filter_source *src)
+{
+ int error;
+ size_t i;
+ const char **strs = git__calloc(fdef->nattrs, sizeof(const char *));
+ GITERR_CHECK_ALLOC(strs);
+
+ error = git_attr_get_many(
+ strs, src->repo, 0, src->path, fdef->nattrs, fdef->attrs);
+
+ /* if no values were found but no matches are needed, it's okay! */
+ if (error == GIT_ENOTFOUND && !fdef->nmatches) {
+ giterr_clear();
+ git__free((void *)strs);
return 0;
}
- /* Pre-grow the destination buffer to more or less the size
- * we expect it to have */
- if (git_buf_grow(dest, git_buf_len(source)) < 0)
+ for (i = 0; !error && i < fdef->nattrs; ++i) {
+ const char *want = fdef->attrs[fdef->nattrs + i];
+ git_attr_t want_type, found_type;
+
+ if (!want)
+ continue;
+
+ want_type = git_attr_value(want);
+ found_type = git_attr_value(strs[i]);
+
+ if (want_type != found_type ||
+ (want_type == GIT_ATTR_VALUE_T && strcmp(want, strs[i])))
+ error = GIT_ENOTFOUND;
+ }
+
+ if (error)
+ git__free((void *)strs);
+ else
+ *out = strs;
+
+ return error;
+}
+
+int git_filter_list_new(
+ git_filter_list **out, git_repository *repo, git_filter_mode_t mode)
+{
+ git_filter_source src = { 0 };
+ src.repo = repo;
+ src.path = NULL;
+ src.mode = mode;
+ return filter_list_new(out, &src);
+}
+
+int git_filter_list_load(
+ git_filter_list **filters,
+ git_repository *repo,
+ git_blob *blob, /* can be NULL */
+ const char *path,
+ git_filter_mode_t mode)
+{
+ int error = 0;
+ git_filter_list *fl = NULL;
+ git_filter_source src = { 0 };
+ git_filter_entry *fe;
+ size_t idx;
+ git_filter_def *fdef;
+
+ if (filter_registry_initialize() < 0)
+ return -1;
+
+ src.repo = repo;
+ src.path = path;
+ src.mode = mode;
+ if (blob)
+ git_oid_cpy(&src.oid, git_blob_id(blob));
+
+ git_vector_foreach(&git__filter_registry->filters, idx, fdef) {
+ const char **values = NULL;
+ void *payload = NULL;
+
+ if (!fdef || !fdef->filter)
+ continue;
+
+ if (fdef->nattrs > 0) {
+ error = filter_list_check_attributes(&values, fdef, &src);
+ if (error == GIT_ENOTFOUND) {
+ error = 0;
+ continue;
+ } else if (error < 0)
+ break;
+ }
+
+ if (!fdef->initialized && (error = filter_initialize(fdef)) < 0)
+ break;
+
+ if (fdef->filter->check)
+ error = fdef->filter->check(
+ fdef->filter, &payload, &src, values);
+
+ git__free((void *)values);
+
+ if (error == GIT_PASSTHROUGH)
+ error = 0;
+ else if (error < 0)
+ break;
+ else {
+ if (!fl && (error = filter_list_new(&fl, &src)) < 0)
+ return error;
+
+ fe = git_array_alloc(fl->filters);
+ GITERR_CHECK_ALLOC(fe);
+ fe->filter = fdef->filter;
+ fe->payload = payload;
+ }
+ }
+
+ if (error && fl != NULL) {
+ git_array_clear(fl->filters);
+ git__free(fl);
+ fl = NULL;
+ }
+
+ *filters = fl;
+ return error;
+}
+
+void git_filter_list_free(git_filter_list *fl)
+{
+ uint32_t i;
+
+ if (!fl)
+ return;
+
+ for (i = 0; i < git_array_size(fl->filters); ++i) {
+ git_filter_entry *fe = git_array_get(fl->filters, i);
+ if (fe->filter->cleanup)
+ fe->filter->cleanup(fe->filter, fe->payload);
+ }
+
+ git_array_clear(fl->filters);
+ git__free(fl);
+}
+
+int git_filter_list_push(
+ git_filter_list *fl, git_filter *filter, void *payload)
+{
+ int error = 0;
+ size_t pos;
+ git_filter_def *fdef;
+ git_filter_entry *fe;
+
+ assert(fl && filter);
+
+ if (git_vector_search2(
+ &pos, &git__filter_registry->filters,
+ filter_def_filter_key_check, filter) < 0) {
+ giterr_set(GITERR_FILTER, "Cannot use an unregistered filter");
return -1;
+ }
+
+ fdef = git_vector_get(&git__filter_registry->filters, pos);
+
+ if (!fdef->initialized && (error = filter_initialize(fdef)) < 0)
+ return error;
+
+ fe = git_array_alloc(fl->filters);
+ GITERR_CHECK_ALLOC(fe);
+ fe->filter = filter;
+ fe->payload = payload;
+
+ return 0;
+}
+
+size_t git_filter_list_length(const git_filter_list *fl)
+{
+ return fl ? git_array_size(fl->filters) : 0;
+}
+
+static int filter_list_out_buffer_from_raw(
+ git_buf *out, const void *ptr, size_t size)
+{
+ if (git_buf_is_allocated(out))
+ git_buf_free(out);
- for (i = 0; i < filters->length; ++i) {
- git_filter *filter = git_vector_get(filters, i);
- unsigned int dst = 1 - src;
+ if (!size) {
+ git_buf_init(out, 0);
+ } else {
+ out->ptr = (char *)ptr;
+ out->asize = 0;
+ out->size = size;
+ }
+
+ return 0;
+}
+
+int git_filter_list_apply_to_data(
+ git_buf *tgt, git_filter_list *fl, git_buf *src)
+{
+ int error = 0;
+ uint32_t i;
+ git_buf *dbuffer[2], local = GIT_BUF_INIT;
+ unsigned int si = 0;
- git_buf_clear(dbuffer[dst]);
+ if (!fl)
+ return filter_list_out_buffer_from_raw(tgt, src->ptr, src->size);
+
+ dbuffer[0] = src;
+ dbuffer[1] = tgt;
+
+ /* if `src` buffer is reallocable, then use it, otherwise copy it */
+ if (!git_buf_is_allocated(src)) {
+ if (git_buf_set(&local, src->ptr, src->size) < 0)
+ return -1;
+ dbuffer[0] = &local;
+ }
+
+ for (i = 0; i < git_array_size(fl->filters); ++i) {
+ unsigned int di = 1 - si;
+ uint32_t fidx = (fl->source.mode == GIT_FILTER_TO_WORKTREE) ?
+ i : git_array_size(fl->filters) - 1 - i;
+ git_filter_entry *fe = git_array_get(fl->filters, fidx);
+
+ dbuffer[di]->size = 0;
/* Apply the filter from dbuffer[src] to the other buffer;
* if the filtering is canceled by the user mid-filter,
@@ -79,16 +605,72 @@ int git_filters_apply(git_buf *dest, git_buf *source, git_vector *filters)
* of the double buffering (so that the text goes through
* cleanly).
*/
- if (filter->apply(filter, dbuffer[dst], dbuffer[src]) == 0)
- src = dst;
- if (git_buf_oom(dbuffer[dst]))
- return -1;
+ error = fe->filter->apply(
+ fe->filter, &fe->payload, dbuffer[di], dbuffer[si], &fl->source);
+
+ if (error == GIT_PASSTHROUGH) {
+ /* PASSTHROUGH means filter decided not to process the buffer */
+ error = 0;
+ } else if (!error) {
+ git_buf_shorten(dbuffer[di], 0); /* force NUL termination */
+ si = di; /* swap buffers */
+ } else {
+ tgt->size = 0;
+ return error;
+ }
}
/* Ensure that the output ends up in dbuffer[1] (i.e. the dest) */
- if (src != 1)
- git_buf_swap(dest, source);
+ if (si != 1)
+ git_buf_swap(dbuffer[0], dbuffer[1]);
+
+ git_buf_free(&local); /* don't leak if we allocated locally */
return 0;
}
+
+int git_filter_list_apply_to_file(
+ git_buf *out,
+ git_filter_list *filters,
+ git_repository *repo,
+ const char *path)
+{
+ int error;
+ const char *base = repo ? git_repository_workdir(repo) : NULL;
+ git_buf abspath = GIT_BUF_INIT, raw = GIT_BUF_INIT;
+
+ if (!(error = git_path_join_unrooted(&abspath, path, base, NULL)) &&
+ !(error = git_futils_readbuffer(&raw, abspath.ptr)))
+ {
+ error = git_filter_list_apply_to_data(out, filters, &raw);
+
+ git_buf_free(&raw);
+ }
+
+ git_buf_free(&abspath);
+ return error;
+}
+
+int git_filter_list_apply_to_blob(
+ git_buf *out,
+ git_filter_list *filters,
+ git_blob *blob)
+{
+ git_buf in = GIT_BUF_INIT;
+ git_off_t rawsize = git_blob_rawsize(blob);
+
+ if (!git__is_sizet(rawsize)) {
+ giterr_set(GITERR_OS, "Blob is too large to filter");
+ return -1;
+ }
+
+ in.ptr = (char *)git_blob_rawcontent(blob);
+ in.asize = 0;
+ in.size = (size_t)rawsize;
+
+ if (filters)
+ git_oid_cpy(&filters->source.oid, git_blob_id(blob));
+
+ return git_filter_list_apply_to_data(out, filters, &in);
+}
diff --git a/src/filter.h b/src/filter.h
index 42a44ebdb..d0ace0f9a 100644
--- a/src/filter.h
+++ b/src/filter.h
@@ -8,19 +8,7 @@
#define INCLUDE_filter_h__
#include "common.h"
-#include "buffer.h"
-#include "git2/odb.h"
-#include "git2/repository.h"
-
-typedef struct git_filter {
- int (*apply)(struct git_filter *self, git_buf *dest, const git_buf *source);
- void (*do_free)(struct git_filter *self);
-} git_filter;
-
-typedef enum {
- GIT_FILTER_TO_WORKTREE,
- GIT_FILTER_TO_ODB
-} git_filter_mode;
+#include "git2/filter.h"
typedef enum {
GIT_CRLF_GUESS = -1,
@@ -31,64 +19,13 @@ typedef enum {
GIT_CRLF_AUTO,
} git_crlf_t;
-/*
- * FILTER API
- */
-
-/*
- * For any given path in the working directory, fill the `filters`
- * array with the relevant filters that need to be applied.
- *
- * Mode is either `GIT_FILTER_TO_WORKTREE` if you need to load the
- * filters that will be used when checking out a file to the working
- * directory, or `GIT_FILTER_TO_ODB` for the filters used when writing
- * a file to the ODB.
- *
- * @param filters Vector where to store all the loaded filters
- * @param repo Repository object that contains `path`
- * @param path Relative path of the file to be filtered
- * @param mode Filtering direction (WT->ODB or ODB->WT)
- * @return the number of filters loaded for the file (0 if the file
- * doesn't need filtering), or a negative error code
- */
-extern int git_filters_load(git_vector *filters, git_repository *repo, const char *path, int mode);
-
-/*
- * Apply one or more filters to a file.
- *
- * The file must have been loaded as a `git_buf` object. Both the `source`
- * and `dest` buffers are owned by the caller and must be freed once
- * they are no longer needed.
- *
- * NOTE: Because of the double-buffering schema, the `source` buffer that contains
- * the original file may be tampered once the filtering is complete. Regardless,
- * the `dest` buffer will always contain the final result of the filtering
- *
- * @param dest Buffer to store the result of the filtering
- * @param source Buffer containing the document to filter
- * @param filters A non-empty vector of filters as supplied by `git_filters_load`
- * @return 0 on success, an error code otherwise
- */
-extern int git_filters_apply(git_buf *dest, git_buf *source, git_vector *filters);
-
-/*
- * Free the `filters` array generated by `git_filters_load`.
- *
- * Note that this frees both the array and its contents. The array will
- * be clean/reusable after this call.
- *
- * @param filters A filters array as supplied by `git_filters_load`
- */
-extern void git_filters_free(git_vector *filters);
+extern void git_filter_free(git_filter *filter);
/*
* Available filters
*/
-/* Strip CRLF, from Worktree to ODB */
-extern int git_filter_add__crlf_to_odb(git_vector *filters, git_repository *repo, const char *path);
-
-/* Add CRLF, from ODB to worktree */
-extern int git_filter_add__crlf_to_workdir(git_vector *filters, git_repository *repo, const char *path);
+extern git_filter *git_crlf_filter_new(void);
+extern git_filter *git_ident_filter_new(void);
#endif
diff --git a/src/global.c b/src/global.c
index 2d40ca2fc..7d39c6fa8 100644
--- a/src/global.c
+++ b/src/global.c
@@ -14,6 +14,28 @@
git_mutex git__mwindow_mutex;
+#define MAX_SHUTDOWN_CB 8
+
+git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB];
+git_atomic git__n_shutdown_callbacks;
+
+void git__on_shutdown(git_global_shutdown_fn callback)
+{
+ int count = git_atomic_inc(&git__n_shutdown_callbacks);
+ assert(count <= MAX_SHUTDOWN_CB);
+ git__shutdown_callbacks[count - 1] = callback;
+}
+
+static void git__shutdown(void)
+{
+ int pos;
+
+ while ((pos = git_atomic_dec(&git__n_shutdown_callbacks)) >= 0) {
+ if (git__shutdown_callbacks[pos])
+ git__shutdown_callbacks[pos]();
+ }
+}
+
/**
* Handle the global state with TLS
*
@@ -51,47 +73,69 @@ git_mutex git__mwindow_mutex;
#if defined(GIT_THREADS) && defined(GIT_WIN32)
static DWORD _tls_index;
-static int _tls_init = 0;
+static DWORD _mutex = 0;
+static DWORD _n_inits = 0;
-int git_threads_init(void)
+static int synchronized_threads_init()
{
int error;
- if (_tls_init)
- return 0;
-
_tls_index = TlsAlloc();
if (git_mutex_init(&git__mwindow_mutex))
return -1;
/* Initialize any other subsystems that have global state */
if ((error = git_hash_global_init()) >= 0)
- _tls_init = 1;
+ error = git_futils_dirs_global_init();
- if (error == 0)
- _tls_init = 1;
+ win32_pthread_initialize();
- GIT_MEMORY_BARRIER;
+ return error;
+}
+
+int git_threads_init(void)
+{
+ int error = 0;
+
+ /* Enter the lock */
+ while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); }
+
+ /* Only do work on a 0 -> 1 transition of the refcount */
+ if (1 == ++_n_inits)
+ error = synchronized_threads_init();
+
+ /* Exit the lock */
+ InterlockedExchange(&_mutex, 0);
return error;
}
-void git_threads_shutdown(void)
+static void synchronized_threads_shutdown()
{
+ /* Shut down any subsystems that have global state */
+ git__shutdown();
TlsFree(_tls_index);
- _tls_init = 0;
git_mutex_free(&git__mwindow_mutex);
+}
- /* Shut down any subsystems that have global state */
- git_hash_global_shutdown();
- git_futils_dirs_free();
+void git_threads_shutdown(void)
+{
+ /* Enter the lock */
+ while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); }
+
+ /* Only do work on a 1 -> 0 transition of the refcount */
+ if (0 == --_n_inits)
+ synchronized_threads_shutdown();
+
+ /* Exit the lock */
+ InterlockedExchange(&_mutex, 0);
}
git_global_st *git__global_state(void)
{
void *ptr;
- assert(_tls_init);
+ assert(_n_inits);
if ((ptr = TlsGetValue(_tls_index)) != NULL)
return ptr;
@@ -108,55 +152,58 @@ git_global_st *git__global_state(void)
#elif defined(GIT_THREADS) && defined(_POSIX_THREADS)
static pthread_key_t _tls_key;
-static int _tls_init = 0;
+static pthread_once_t _once_init = PTHREAD_ONCE_INIT;
+static git_atomic git__n_inits;
+int init_error = 0;
static void cb__free_status(void *st)
{
git__free(st);
}
-int git_threads_init(void)
+static void init_once(void)
{
- int error = 0;
-
- if (_tls_init)
- return 0;
-
- if (git_mutex_init(&git__mwindow_mutex))
- return -1;
+ if ((init_error = git_mutex_init(&git__mwindow_mutex)) != 0)
+ return;
pthread_key_create(&_tls_key, &cb__free_status);
/* Initialize any other subsystems that have global state */
- if ((error = git_hash_global_init()) >= 0)
- _tls_init = 1;
+ if ((init_error = git_hash_global_init()) >= 0)
+ init_error = git_futils_dirs_global_init();
GIT_MEMORY_BARRIER;
+}
- return error;
+int git_threads_init(void)
+{
+ pthread_once(&_once_init, init_once);
+ git_atomic_inc(&git__n_inits);
+ return init_error;
}
void git_threads_shutdown(void)
{
- if (_tls_init) {
- void *ptr = pthread_getspecific(_tls_key);
- pthread_setspecific(_tls_key, NULL);
- git__free(ptr);
- }
+ pthread_once_t new_once = PTHREAD_ONCE_INIT;
- pthread_key_delete(_tls_key);
- _tls_init = 0;
- git_mutex_free(&git__mwindow_mutex);
+ if (git_atomic_dec(&git__n_inits) > 0) return;
/* Shut down any subsystems that have global state */
- git_hash_global_shutdown();
- git_futils_dirs_free();
+ git__shutdown();
+
+ void *ptr = pthread_getspecific(_tls_key);
+ pthread_setspecific(_tls_key, NULL);
+ git__free(ptr);
+
+ pthread_key_delete(_tls_key);
+ git_mutex_free(&git__mwindow_mutex);
+ _once_init = new_once;
}
git_global_st *git__global_state(void)
{
void *ptr;
- assert(_tls_init);
+ assert(git__n_inits.val);
if ((ptr = pthread_getspecific(_tls_key)) != NULL)
return ptr;
@@ -176,15 +223,14 @@ static git_global_st __state;
int git_threads_init(void)
{
- /* noop */
+ /* noop */
return 0;
}
void git_threads_shutdown(void)
{
/* Shut down any subsystems that have global state */
- git_hash_global_shutdown();
- git_futils_dirs_free();
+ git__shutdown();
}
git_global_st *git__global_state(void)
diff --git a/src/global.h b/src/global.h
index badbc0883..778250376 100644
--- a/src/global.h
+++ b/src/global.h
@@ -21,4 +21,8 @@ extern git_mutex git__mwindow_mutex;
#define GIT_GLOBAL (git__global_state())
+typedef void (*git_global_shutdown_fn)(void);
+
+extern void git__on_shutdown(git_global_shutdown_fn callback);
+
#endif
diff --git a/src/hash.h b/src/hash.h
index 5b848981f..c47f33549 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -13,8 +13,6 @@ typedef struct git_hash_prov git_hash_prov;
typedef struct git_hash_ctx git_hash_ctx;
int git_hash_global_init(void);
-void git_hash_global_shutdown(void);
-
int git_hash_ctx_init(git_hash_ctx *ctx);
void git_hash_ctx_cleanup(git_hash_ctx *ctx);
diff --git a/src/hash/hash_generic.h b/src/hash/hash_generic.h
index 6b60c98c4..daeb1cda8 100644
--- a/src/hash/hash_generic.h
+++ b/src/hash/hash_generic.h
@@ -17,7 +17,6 @@ struct git_hash_ctx {
};
#define git_hash_global_init() 0
-#define git_hash_global_shutdown() /* noop */
#define git_hash_ctx_init(ctx) git_hash_init(ctx)
#define git_hash_ctx_cleanup(ctx)
diff --git a/src/hash/hash_openssl.h b/src/hash/hash_openssl.h
index f83279a5a..9a55d472d 100644
--- a/src/hash/hash_openssl.h
+++ b/src/hash/hash_openssl.h
@@ -17,7 +17,6 @@ struct git_hash_ctx {
};
#define git_hash_global_init() 0
-#define git_hash_global_shutdown() /* noop */
#define git_hash_ctx_init(ctx) git_hash_init(ctx)
#define git_hash_ctx_cleanup(ctx)
diff --git a/src/hash/hash_win32.c b/src/hash/hash_win32.c
index 43d54ca6d..bb2231364 100644
--- a/src/hash/hash_win32.c
+++ b/src/hash/hash_win32.c
@@ -20,33 +20,16 @@ static struct git_hash_prov hash_prov = {0};
/* Initialize CNG, if available */
GIT_INLINE(int) hash_cng_prov_init(void)
{
- OSVERSIONINFOEX version_test = {0};
- DWORD version_test_mask;
- DWORDLONG version_condition_mask = 0;
char dll_path[MAX_PATH];
DWORD dll_path_len, size_len;
- return -1;
-
/* Only use CNG on Windows 2008 / Vista SP1 or better (Windows 6.0 SP1) */
- version_test.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
- version_test.dwMajorVersion = 6;
- version_test.dwMinorVersion = 0;
- version_test.wServicePackMajor = 1;
- version_test.wServicePackMinor = 0;
-
- version_test_mask = (VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR);
-
- VER_SET_CONDITION(version_condition_mask, VER_MAJORVERSION, VER_GREATER_EQUAL);
- VER_SET_CONDITION(version_condition_mask, VER_MINORVERSION, VER_GREATER_EQUAL);
- VER_SET_CONDITION(version_condition_mask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
- VER_SET_CONDITION(version_condition_mask, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL);
-
- if (!VerifyVersionInfo(&version_test, version_test_mask, version_condition_mask))
+ if (!git_has_win32_version(6, 0, 1))
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 ||
+ 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)
@@ -106,7 +89,15 @@ GIT_INLINE(void) hash_cryptoapi_prov_shutdown(void)
hash_prov.type = INVALID;
}
-int git_hash_global_init()
+static void git_hash_global_shutdown(void)
+{
+ if (hash_prov.type == CNG)
+ hash_cng_prov_shutdown();
+ else if(hash_prov.type == CRYPTOAPI)
+ hash_cryptoapi_prov_shutdown();
+}
+
+int git_hash_global_init(void)
{
int error = 0;
@@ -116,15 +107,9 @@ int git_hash_global_init()
if ((error = hash_cng_prov_init()) < 0)
error = hash_cryptoapi_prov_init();
- return error;
-}
+ git__on_shutdown(git_hash_global_shutdown);
-void git_hash_global_shutdown()
-{
- if (hash_prov.type == CNG)
- hash_cng_prov_shutdown();
- else if(hash_prov.type == CRYPTOAPI)
- hash_cryptoapi_prov_shutdown();
+ return error;
}
/* CryptoAPI: available in Windows XP and newer */
diff --git a/src/hashsig.c b/src/hashsig.c
index ab8d8b3f0..109f966ba 100644
--- a/src/hashsig.c
+++ b/src/hashsig.c
@@ -13,12 +13,15 @@ typedef uint64_t hashsig_state;
#define HASHSIG_SCALE 100
-#define HASHSIG_HASH_WINDOW 32
-#define HASHSIG_HASH_START 0
+#define HASHSIG_MAX_RUN 80
+#define HASHSIG_HASH_START 0x012345678ABCDEF0LL
#define HASHSIG_HASH_SHIFT 5
-#define HASHSIG_HASH_MASK 0x7FFFFFFF
+
+#define HASHSIG_HASH_MIX(S,CH) \
+ (S) = ((S) << HASHSIG_HASH_SHIFT) - (S) + (hashsig_state)(CH)
#define HASHSIG_HEAP_SIZE ((1 << 7) - 1)
+#define HASHSIG_HEAP_MIN_SIZE 4
typedef int (*hashsig_cmp)(const void *a, const void *b, void *);
@@ -28,14 +31,6 @@ typedef struct {
hashsig_t values[HASHSIG_HEAP_SIZE];
} hashsig_heap;
-typedef struct {
- hashsig_state state, shift_n;
- char window[HASHSIG_HASH_WINDOW];
- int win_len, win_pos, saw_lf;
-} hashsig_in_progress;
-
-#define HASHSIG_IN_PROGRESS_INIT { HASHSIG_HASH_START, 1, {0}, 0, 0, 1 }
-
struct git_hashsig {
hashsig_heap mins;
hashsig_heap maxs;
@@ -43,8 +38,8 @@ struct git_hashsig {
int considered;
};
-#define HEAP_LCHILD_OF(I) (((I)*2)+1)
-#define HEAP_RCHILD_OF(I) (((I)*2)+2)
+#define HEAP_LCHILD_OF(I) (((I)<<1)+1)
+#define HEAP_RCHILD_OF(I) (((I)<<1)+2)
#define HEAP_PARENT_OF(I) (((I)-1)>>1)
static void hashsig_heap_init(hashsig_heap *h, hashsig_cmp cmp)
@@ -115,134 +110,109 @@ static void hashsig_heap_sort(hashsig_heap *h)
static void hashsig_heap_insert(hashsig_heap *h, hashsig_t val)
{
- /* if heap is full, pop top if new element should replace it */
- if (h->size == h->asize && h->cmp(&val, &h->values[0], NULL) > 0) {
- h->size--;
- h->values[0] = h->values[h->size];
- hashsig_heap_down(h, 0);
- }
-
/* if heap is not full, insert new element */
if (h->size < h->asize) {
h->values[h->size++] = val;
hashsig_heap_up(h, h->size - 1);
}
-}
-
-GIT_INLINE(bool) hashsig_include_char(
- char ch, git_hashsig_option_t opt, int *saw_lf)
-{
- if ((opt & GIT_HASHSIG_IGNORE_WHITESPACE) && git__isspace(ch))
- return false;
-
- if (opt & GIT_HASHSIG_SMART_WHITESPACE) {
- if (ch == '\r' || (*saw_lf && git__isspace(ch)))
- return false;
- *saw_lf = (ch == '\n');
+ /* if heap is full, pop top if new element should replace it */
+ else if (h->cmp(&val, &h->values[0], NULL) > 0) {
+ h->size--;
+ h->values[0] = h->values[h->size];
+ hashsig_heap_down(h, 0);
}
- return true;
}
-static void hashsig_initial_window(
- git_hashsig *sig,
- const char **data,
- size_t size,
- hashsig_in_progress *prog)
-{
- hashsig_state state, shift_n;
- int win_len;
- const char *scan, *end;
-
- /* init until we have processed at least HASHSIG_HASH_WINDOW data */
-
- if (prog->win_len >= HASHSIG_HASH_WINDOW)
- return;
-
- state = prog->state;
- win_len = prog->win_len;
- shift_n = prog->shift_n;
-
- scan = *data;
- end = scan + size;
-
- while (scan < end && win_len < HASHSIG_HASH_WINDOW) {
- char ch = *scan++;
-
- if (!hashsig_include_char(ch, sig->opt, &prog->saw_lf))
- continue;
-
- state = (state * HASHSIG_HASH_SHIFT + ch) & HASHSIG_HASH_MASK;
-
- if (!win_len)
- shift_n = 1;
- else
- shift_n = (shift_n * HASHSIG_HASH_SHIFT) & HASHSIG_HASH_MASK;
-
- prog->window[win_len++] = ch;
- }
-
- /* insert initial hash if we just finished */
+typedef struct {
+ int use_ignores;
+ uint8_t ignore_ch[256];
+} hashsig_in_progress;
- if (win_len == HASHSIG_HASH_WINDOW) {
- hashsig_heap_insert(&sig->mins, (hashsig_t)state);
- hashsig_heap_insert(&sig->maxs, (hashsig_t)state);
- sig->considered = 1;
+static void hashsig_in_progress_init(
+ hashsig_in_progress *prog, git_hashsig *sig)
+{
+ int i;
+
+ switch (sig->opt) {
+ case GIT_HASHSIG_IGNORE_WHITESPACE:
+ for (i = 0; i < 256; ++i)
+ prog->ignore_ch[i] = git__isspace_nonlf(i);
+ prog->use_ignores = 1;
+ break;
+ case GIT_HASHSIG_SMART_WHITESPACE:
+ for (i = 0; i < 256; ++i)
+ prog->ignore_ch[i] = git__isspace(i);
+ prog->use_ignores = 1;
+ break;
+ default:
+ memset(prog, 0, sizeof(*prog));
+ break;
}
-
- prog->state = state;
- prog->win_len = win_len;
- prog->shift_n = shift_n;
-
- *data = scan;
}
+#define HASHSIG_IN_PROGRESS_INIT { 1 }
+
static int hashsig_add_hashes(
git_hashsig *sig,
- const char *data,
+ const uint8_t *data,
size_t size,
hashsig_in_progress *prog)
{
- const char *scan = data, *end = data + size;
- hashsig_state state, shift_n, rmv;
-
- if (prog->win_len < HASHSIG_HASH_WINDOW)
- hashsig_initial_window(sig, &scan, size, prog);
-
- state = prog->state;
- shift_n = prog->shift_n;
-
- /* advance window, adding new chars and removing old */
-
- for (; scan < end; ++scan) {
- char ch = *scan;
-
- if (!hashsig_include_char(ch, sig->opt, &prog->saw_lf))
- continue;
-
- rmv = shift_n * prog->window[prog->win_pos];
+ const uint8_t *scan = data, *end = data + size;
+ hashsig_state state = HASHSIG_HASH_START;
+ int use_ignores = prog->use_ignores, len;
+ uint8_t ch;
+
+ while (scan < end) {
+ state = HASHSIG_HASH_START;
+
+ for (len = 0; scan < end && len < HASHSIG_MAX_RUN; ) {
+ ch = *scan;
+
+ if (use_ignores)
+ for (; scan < end && git__isspace_nonlf(ch); ch = *scan)
+ ++scan;
+ else if (sig->opt != GIT_HASHSIG_NORMAL)
+ for (; scan < end && ch == '\r'; ch = *scan)
+ ++scan;
+
+ /* peek at next character to decide what to do next */
+ if (sig->opt == GIT_HASHSIG_SMART_WHITESPACE)
+ use_ignores = (ch == '\n');
+
+ if (scan >= end)
+ break;
+ ++scan;
+
+ /* check run terminator */
+ if (ch == '\n' || ch == '\0')
+ break;
+
+ ++len;
+ HASHSIG_HASH_MIX(state, ch);
+ }
- state = (state - rmv) & HASHSIG_HASH_MASK;
- state = (state * HASHSIG_HASH_SHIFT) & HASHSIG_HASH_MASK;
- state = (state + ch) & HASHSIG_HASH_MASK;
+ if (len > 0) {
+ hashsig_heap_insert(&sig->mins, (hashsig_t)state);
+ hashsig_heap_insert(&sig->maxs, (hashsig_t)state);
- hashsig_heap_insert(&sig->mins, (hashsig_t)state);
- hashsig_heap_insert(&sig->maxs, (hashsig_t)state);
- sig->considered++;
+ sig->considered++;
- prog->window[prog->win_pos] = ch;
- prog->win_pos = (prog->win_pos + 1) % HASHSIG_HASH_WINDOW;
+ while (scan < end && (*scan == '\n' || !*scan))
+ ++scan;
+ }
}
- prog->state = state;
+ prog->use_ignores = use_ignores;
return 0;
}
static int hashsig_finalize_hashes(git_hashsig *sig)
{
- if (sig->mins.size < HASHSIG_HEAP_SIZE) {
+ if (sig->mins.size < HASHSIG_HEAP_MIN_SIZE) {
giterr_set(GITERR_INVALID,
"File too small for similarity signature calculation");
return GIT_EBUFS;
@@ -274,11 +244,13 @@ int git_hashsig_create(
git_hashsig_option_t opts)
{
int error;
- hashsig_in_progress prog = HASHSIG_IN_PROGRESS_INIT;
+ hashsig_in_progress prog;
git_hashsig *sig = hashsig_alloc(opts);
GITERR_CHECK_ALLOC(sig);
- error = hashsig_add_hashes(sig, buf, buflen, &prog);
+ hashsig_in_progress_init(&prog, sig);
+
+ error = hashsig_add_hashes(sig, (const uint8_t *)buf, buflen, &prog);
if (!error)
error = hashsig_finalize_hashes(sig);
@@ -296,10 +268,10 @@ int git_hashsig_create_fromfile(
const char *path,
git_hashsig_option_t opts)
{
- char buf[4096];
+ uint8_t buf[0x1000];
ssize_t buflen = 0;
int error = 0, fd;
- hashsig_in_progress prog = HASHSIG_IN_PROGRESS_INIT;
+ hashsig_in_progress prog;
git_hashsig *sig = hashsig_alloc(opts);
GITERR_CHECK_ALLOC(sig);
@@ -308,6 +280,8 @@ int git_hashsig_create_fromfile(
return fd;
}
+ hashsig_in_progress_init(&prog, sig);
+
while (!error) {
if ((buflen = p_read(fd, buf, sizeof(buf))) <= 0) {
if ((error = (int)buflen) < 0)
@@ -362,6 +336,12 @@ static int hashsig_heap_compare(const hashsig_heap *a, const hashsig_heap *b)
int git_hashsig_compare(const git_hashsig *a, const git_hashsig *b)
{
- return (hashsig_heap_compare(&a->mins, &b->mins) +
- hashsig_heap_compare(&a->maxs, &b->maxs)) / 2;
+ /* if we have fewer than the maximum number of elements, then just use
+ * one array since the two arrays will be the same
+ */
+ if (a->mins.size < HASHSIG_HEAP_SIZE)
+ return hashsig_heap_compare(&a->mins, &b->mins);
+ else
+ return (hashsig_heap_compare(&a->mins, &b->mins) +
+ hashsig_heap_compare(&a->maxs, &b->maxs)) / 2;
}
diff --git a/src/ident.c b/src/ident.c
new file mode 100644
index 000000000..51630879d
--- /dev/null
+++ b/src/ident.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "git2/sys/filter.h"
+#include "filter.h"
+#include "buffer.h"
+#include "buf_text.h"
+
+static int ident_find_id(
+ const char **id_start, const char **id_end, const char *start, size_t len)
+{
+ const char *end = start + len, *found = NULL;
+
+ while (len > 3 && (found = memchr(start, '$', len)) != NULL) {
+ size_t remaining = (size_t)(end - found) - 1;
+ if (remaining < 3)
+ return GIT_ENOTFOUND;
+
+ start = found + 1;
+ len = remaining;
+
+ if (start[0] == 'I' && start[1] == 'd')
+ break;
+ }
+
+ if (len < 3 || !found)
+ return GIT_ENOTFOUND;
+ *id_start = found;
+
+ if ((found = memchr(start + 2, '$', len - 2)) == NULL)
+ return GIT_ENOTFOUND;
+
+ *id_end = found + 1;
+ return 0;
+}
+
+static int ident_insert_id(
+ git_buf *to, const git_buf *from, const git_filter_source *src)
+{
+ char oid[GIT_OID_HEXSZ+1];
+ const char *id_start, *id_end, *from_end = from->ptr + from->size;
+ size_t need_size;
+
+ /* replace $Id$ with blob id */
+
+ if (!git_filter_source_id(src))
+ return GIT_PASSTHROUGH;
+
+ git_oid_tostr(oid, sizeof(oid), git_filter_source_id(src));
+
+ if (ident_find_id(&id_start, &id_end, from->ptr, from->size) < 0)
+ return GIT_PASSTHROUGH;
+
+ need_size = (size_t)(id_start - from->ptr) +
+ 5 /* "$Id: " */ + GIT_OID_HEXSZ + 1 /* "$" */ +
+ (size_t)(from_end - id_end);
+
+ if (git_buf_grow(to, need_size) < 0)
+ return -1;
+
+ git_buf_set(to, from->ptr, (size_t)(id_start - from->ptr));
+ git_buf_put(to, "$Id: ", 5);
+ git_buf_put(to, oid, GIT_OID_HEXSZ);
+ git_buf_putc(to, '$');
+ git_buf_put(to, id_end, (size_t)(from_end - id_end));
+
+ return git_buf_oom(to) ? -1 : 0;
+}
+
+static int ident_remove_id(
+ git_buf *to, const git_buf *from)
+{
+ const char *id_start, *id_end, *from_end = from->ptr + from->size;
+ size_t need_size;
+
+ if (ident_find_id(&id_start, &id_end, from->ptr, from->size) < 0)
+ return GIT_PASSTHROUGH;
+
+ need_size = (size_t)(id_start - from->ptr) +
+ 4 /* "$Id$" */ + (size_t)(from_end - id_end);
+
+ if (git_buf_grow(to, need_size) < 0)
+ return -1;
+
+ git_buf_set(to, from->ptr, (size_t)(id_start - from->ptr));
+ git_buf_put(to, "$Id$", 4);
+ git_buf_put(to, id_end, (size_t)(from_end - id_end));
+
+ return git_buf_oom(to) ? -1 : 0;
+}
+
+static int ident_apply(
+ git_filter *self,
+ void **payload,
+ git_buf *to,
+ const git_buf *from,
+ const git_filter_source *src)
+{
+ GIT_UNUSED(self); GIT_UNUSED(payload);
+
+ /* Don't filter binary files */
+ if (git_buf_text_is_binary(from))
+ return GIT_PASSTHROUGH;
+
+ if (git_filter_source_mode(src) == GIT_FILTER_SMUDGE)
+ return ident_insert_id(to, from, src);
+ else
+ return ident_remove_id(to, from);
+}
+
+git_filter *git_ident_filter_new(void)
+{
+ git_filter *f = git__calloc(1, sizeof(git_filter));
+
+ f->version = GIT_FILTER_VERSION;
+ f->attributes = "+ident"; /* apply to files with ident attribute set */
+ f->shutdown = git_filter_free;
+ f->apply = ident_apply;
+
+ return f;
+}
diff --git a/src/ignore.c b/src/ignore.c
index cc90b0c61..27d7c7ec4 100644
--- a/src/ignore.c
+++ b/src/ignore.c
@@ -1,4 +1,5 @@
#include "git2/ignore.h"
+#include "common.h"
#include "ignore.h"
#include "attr.h"
#include "path.h"
@@ -37,7 +38,7 @@ static int parse_ignore_file(
GITERR_CHECK_ALLOC(match);
}
- match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE;
+ match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG;
if (!(error = git_attr_fnmatch__parse(
match, ignores->pool, context, &scan)))
@@ -159,17 +160,36 @@ int git_ignore__push_dir(git_ignores *ign, const char *dir)
{
if (git_buf_joinpath(&ign->dir, ign->dir.ptr, dir) < 0)
return -1;
- else
- return push_ignore_file(
- ign->repo, ign, &ign->ign_path, ign->dir.ptr, GIT_IGNORE_FILE);
+
+ return push_ignore_file(
+ ign->repo, ign, &ign->ign_path, ign->dir.ptr, GIT_IGNORE_FILE);
}
int git_ignore__pop_dir(git_ignores *ign)
{
if (ign->ign_path.length > 0) {
git_attr_file *file = git_vector_last(&ign->ign_path);
- if (git__suffixcmp(ign->dir.ptr, file->key + 2) == 0)
+ const char *start, *end, *scan;
+ size_t keylen;
+
+ /* - ign->dir looks something like "a/b" (or "a/b/c/d")
+ * - file->key looks something like "0#a/b/.gitignore
+ *
+ * We are popping the last directory off ign->dir. We also want to
+ * remove the file from the vector if the directory part of the key
+ * matches the ign->dir path. We need to test if the "a/b" part of
+ * the file key matches the path we are about to pop.
+ */
+
+ for (start = end = scan = &file->key[2]; *scan; ++scan)
+ if (*scan == '/')
+ end = scan; /* point 'end' to last '/' in key */
+ keylen = (end - start) + 1;
+
+ if (ign->dir.size >= keylen &&
+ !memcmp(ign->dir.ptr + ign->dir.size - keylen, start, keylen))
git_vector_pop(&ign->ign_path);
+
git_buf_rtruncate_at_char(&ign->dir, '/');
}
return 0;
@@ -298,12 +318,9 @@ int git_ignore_path_is_ignored(
path.full.size = (tail - path.full.ptr);
path.is_dir = (tail == end) ? full_is_dir : true;
- /* update ignores for new path fragment */
- if (path.basename == path.path)
- error = git_ignore__for_path(repo, path.path, &ignores);
- else
- error = git_ignore__push_dir(&ignores, path.basename);
- if (error < 0)
+ /* initialize ignores the first time through */
+ if (path.basename == path.path &&
+ (error = git_ignore__for_path(repo, path.path, &ignores)) < 0)
break;
/* first process builtins - success means path was found */
@@ -327,6 +344,10 @@ int git_ignore_path_is_ignored(
if (tail == end)
break;
+ /* now add this directory to list of ignores */
+ if ((error = git_ignore__push_dir(&ignores, path.path)) < 0)
+ break;
+
/* reinstate divider in path */
*tail = '/';
while (*tail == '/') tail++;
diff --git a/src/ignore.h b/src/ignore.h
index cc114b001..851c824bf 100644
--- a/src/ignore.h
+++ b/src/ignore.h
@@ -24,14 +24,15 @@
*/
typedef struct {
git_repository *repo;
- git_buf dir;
+ git_buf dir; /* current directory reflected in ign_path */
git_attr_file *ign_internal;
git_vector ign_path;
git_vector ign_global;
int ignore_case;
} git_ignores;
-extern int git_ignore__for_path(git_repository *repo, const char *path, git_ignores *ign);
+extern int git_ignore__for_path(
+ git_repository *repo, const char *path, git_ignores *ign);
extern int git_ignore__push_dir(git_ignores *ign, const char *dir);
diff --git a/src/index.c b/src/index.c
index 1d46779bf..09e7b2346 100644
--- a/src/index.c
+++ b/src/index.c
@@ -16,6 +16,7 @@
#include "iterator.h"
#include "pathspec.h"
#include "ignore.h"
+#include "blob.h"
#include "git2/odb.h"
#include "git2/oid.h"
@@ -101,8 +102,6 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
static bool is_index_extended(git_index *index);
static int write_index(git_index *index, git_filebuf *file);
-static int index_find(size_t *at_pos, git_index *index, const char *path, int stage);
-
static void index_entry_free(git_index_entry *entry);
static void index_entry_reuc_free(git_index_reuc_entry *reuc);
@@ -114,7 +113,7 @@ static int index_srch(const void *key, const void *array_member)
ret = strcmp(srch_key->path, entry->path);
- if (ret == 0)
+ if (ret == 0 && srch_key->stage != GIT_INDEX_STAGE_ANY)
ret = srch_key->stage - GIT_IDXENTRY_STAGE(entry);
return ret;
@@ -128,7 +127,7 @@ static int index_isrch(const void *key, const void *array_member)
ret = strcasecmp(srch_key->path, entry->path);
- if (ret == 0)
+ if (ret == 0 && srch_key->stage != GIT_INDEX_STAGE_ANY)
ret = srch_key->stage - GIT_IDXENTRY_STAGE(entry);
return ret;
@@ -261,6 +260,22 @@ static int reuc_icmp(const void *a, const void *b)
return strcasecmp(info_a->path, info_b->path);
}
+static void index_entry_reuc_free(git_index_reuc_entry *reuc)
+{
+ if (!reuc)
+ return;
+ git__free(reuc->path);
+ git__free(reuc);
+}
+
+static void index_entry_free(git_index_entry *entry)
+{
+ if (!entry)
+ return;
+ git__free(entry->path);
+ git__free(entry);
+}
+
static unsigned int index_create_mode(unsigned int mode)
{
if (S_ISLNK(mode))
@@ -269,7 +284,7 @@ static unsigned int index_create_mode(unsigned int mode)
if (S_ISDIR(mode) || (mode & S_IFMT) == (S_IFLNK | S_IFDIR))
return (S_IFLNK | S_IFDIR);
- return S_IFREG | ((mode & 0100) ? 0755 : 0644);
+ return S_IFREG | GIT_PERMS_CANONICAL(mode);
}
static unsigned int index_merge_mode(
@@ -306,6 +321,7 @@ void git_index__set_ignore_case(git_index *index, bool ignore_case)
int git_index_open(git_index **index_out, const char *index_path)
{
git_index *index;
+ int error;
assert(index_out);
@@ -331,10 +347,15 @@ int git_index_open(git_index **index_out, const char *index_path)
index->entries_search_path = index_srch_path;
index->reuc_search = reuc_srch;
+ if ((index_path != NULL) && ((error = git_index_read(index, true)) < 0)) {
+ git_index_free(index);
+ return error;
+ }
+
*index_out = index;
GIT_REFCOUNT_INC(index);
- return (index_path != NULL) ? git_index_read(index) : 0;
+ return 0;
}
int git_index_new(git_index **out)
@@ -367,11 +388,8 @@ static void index_entries_free(git_vector *entries)
{
size_t i;
- for (i = 0; i < entries->length; ++i) {
- git_index_entry *e = git_vector_get(entries, i);
- git__free(e->path);
- git__free(e);
- }
+ for (i = 0; i < entries->length; ++i)
+ index_entry_free(git__swap(entries->contents[i], NULL));
git_vector_clear(entries);
}
@@ -398,7 +416,7 @@ static int create_index_error(int error, const char *msg)
int git_index_set_caps(git_index *index, unsigned int caps)
{
- int old_ignore_case;
+ unsigned int old_ignore_case;
assert(index);
@@ -426,7 +444,7 @@ int git_index_set_caps(git_index *index, unsigned int caps)
}
if (old_ignore_case != index->ignore_case) {
- git_index__set_ignore_case(index, index->ignore_case);
+ git_index__set_ignore_case(index, (bool)index->ignore_case);
}
return 0;
@@ -439,24 +457,26 @@ unsigned int git_index_caps(const git_index *index)
(index->no_symlinks ? GIT_INDEXCAP_NO_SYMLINKS : 0));
}
-int git_index_read(git_index *index)
+int git_index_read(git_index *index, int force)
{
int error = 0, updated;
git_buf buffer = GIT_BUF_INIT;
- git_futils_filestamp stamp = {0};
+ git_futils_filestamp stamp = index->stamp;
if (!index->index_file_path)
return create_index_error(-1,
"Failed to read index: The index is in-memory only");
- if (!index->on_disk || git_path_exists(index->index_file_path) == false) {
- git_index_clear(index);
- index->on_disk = 0;
+ index->on_disk = git_path_exists(index->index_file_path);
+
+ if (!index->on_disk) {
+ if (force)
+ git_index_clear(index);
return 0;
}
updated = git_futils_filestamp_check(&stamp, index->index_file_path);
- if (updated <= 0)
+ if (updated < 0 || (!updated && !force))
return updated;
error = git_futils_readbuffer(&buffer, index->index_file_path);
@@ -486,15 +506,19 @@ int git_index_write(git_index *index)
git_vector_sort(&index->reuc);
if ((error = git_filebuf_open(
- &file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS)) < 0)
+ &file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS, GIT_INDEX_FILE_MODE)) < 0) {
+ if (error == GIT_ELOCKED)
+ giterr_set(GITERR_INDEX, "The index is locked. This might be due to a concurrrent or crashed process");
+
return error;
+ }
if ((error = write_index(index, &file)) < 0) {
git_filebuf_cleanup(&file);
return error;
}
- if ((error = git_filebuf_commit(&file, GIT_INDEX_FILE_MODE)) < 0)
+ if ((error = git_filebuf_commit(&file)) < 0)
return error;
error = git_futils_filestamp_check(&index->stamp, index->index_file_path);
@@ -505,6 +529,12 @@ int git_index_write(git_index *index)
return 0;
}
+const char * git_index_path(git_index *index)
+{
+ assert(index);
+ return index->index_file_path;
+}
+
int git_index_write_tree(git_oid *oid, git_index *index)
{
git_repository *repo;
@@ -549,7 +579,7 @@ const git_index_entry *git_index_get_bypath(
git_vector_sort(&index->entries);
- if (index_find(&pos, index, path, stage) < 0) {
+ if (git_index__find(&pos, index, path, stage) < 0) {
giterr_set(GITERR_INDEX, "Index does not contain %s", path);
return NULL;
}
@@ -557,7 +587,8 @@ const git_index_entry *git_index_get_bypath(
return git_index_get_byindex(index, pos);
}
-void git_index_entry__init_from_stat(git_index_entry *entry, struct stat *st)
+void git_index_entry__init_from_stat(
+ git_index_entry *entry, struct stat *st, bool trust_mode)
{
entry->ctime.seconds = (git_time_t)st->st_ctime;
entry->mtime.seconds = (git_time_t)st->st_mtime;
@@ -565,7 +596,8 @@ void git_index_entry__init_from_stat(git_index_entry *entry, struct stat *st)
/* entry->ctime.nanoseconds = st->st_ctimensec; */
entry->dev = st->st_rdev;
entry->ino = st->st_ino;
- entry->mode = index_create_mode(st->st_mode);
+ entry->mode = (!trust_mode && S_ISREG(st->st_mode)) ?
+ index_create_mode(0666) : index_create_mode(st->st_mode);
entry->uid = st->st_uid;
entry->gid = st->st_gid;
entry->file_size = st->st_size;
@@ -587,48 +619,29 @@ int git_index_entry__cmp_icase(const void *a, const void *b)
return strcasecmp(entry_a->path, entry_b->path);
}
-static int index_entry_init(git_index_entry **entry_out, git_index *index, const char *rel_path)
+static int index_entry_init(
+ git_index_entry **entry_out, git_index *index, const char *rel_path)
{
+ int error = 0;
git_index_entry *entry = NULL;
struct stat st;
git_oid oid;
- const char *workdir;
- git_buf full_path = GIT_BUF_INIT;
- int error;
if (INDEX_OWNER(index) == NULL)
return create_index_error(-1,
"Could not initialize index entry. "
"Index is not backed up by an existing repository.");
- workdir = git_repository_workdir(INDEX_OWNER(index));
-
- if (!workdir)
- return create_index_error(GIT_EBAREREPO,
- "Could not initialize index entry. Repository is bare");
-
- if ((error = git_buf_joinpath(&full_path, workdir, rel_path)) < 0)
- return error;
-
- if ((error = git_path_lstat(full_path.ptr, &st)) < 0) {
- git_buf_free(&full_path);
- return error;
- }
-
- git_buf_free(&full_path); /* done with full path */
-
- /* There is no need to validate the rel_path here, since it will be
- * immediately validated by the call to git_blob_create_fromfile.
- */
-
- /* write the blob to disk and get the oid */
- if ((error = git_blob_create_fromworkdir(&oid, INDEX_OWNER(index), rel_path)) < 0)
+ /* write the blob to disk and get the oid and stat info */
+ error = git_blob__create_from_paths(
+ &oid, &st, INDEX_OWNER(index), NULL, rel_path, 0, true);
+ if (error < 0)
return error;
entry = git__calloc(1, sizeof(git_index_entry));
GITERR_CHECK_ALLOC(entry);
- git_index_entry__init_from_stat(entry, &st);
+ git_index_entry__init_from_stat(entry, &st, !index->distrust_filemode);
entry->oid = oid;
entry->path = git__strdup(rel_path);
@@ -670,15 +683,6 @@ static int index_entry_reuc_init(git_index_reuc_entry **reuc_out,
return 0;
}
-static void index_entry_reuc_free(git_index_reuc_entry *reuc)
-{
- if (!reuc)
- return;
-
- git__free(reuc->path);
- git__free(reuc);
-}
-
static git_index_entry *index_entry_dup(const git_index_entry *source_entry)
{
git_index_entry *entry;
@@ -697,14 +701,6 @@ static git_index_entry *index_entry_dup(const git_index_entry *source_entry)
return entry;
}
-static void index_entry_free(git_index_entry *entry)
-{
- if (!entry)
- return;
- git__free(entry->path);
- git__free(entry);
-}
-
static int index_insert(git_index *index, git_index_entry *entry, int replace)
{
size_t path_length, position;
@@ -723,7 +719,8 @@ static int index_insert(git_index *index, git_index_entry *entry, int replace)
entry->flags |= GIT_IDXENTRY_NAMEMASK;
/* look if an entry with this path already exists */
- if (!index_find(&position, index, entry->path, GIT_IDXENTRY_STAGE(entry))) {
+ if (!git_index__find(
+ &position, index, entry->path, GIT_IDXENTRY_STAGE(entry))) {
existing = (git_index_entry **)&index->entries.contents[position];
/* update filemode to existing values if stat is not trusted */
@@ -835,7 +832,7 @@ int git_index_remove(git_index *index, const char *path, int stage)
git_vector_sort(&index->entries);
- if (index_find(&position, index, path, stage) < 0) {
+ if (git_index__find(&position, index, path, stage) < 0) {
giterr_set(GITERR_INDEX, "Index does not contain %s at stage %d",
path, stage);
return GIT_ENOTFOUND;
@@ -891,7 +888,8 @@ int git_index_remove_directory(git_index *index, const char *dir, int stage)
return error;
}
-static int index_find(size_t *at_pos, git_index *index, const char *path, int stage)
+int git_index__find(
+ size_t *at_pos, git_index *index, const char *path, int stage)
{
struct entry_srch_key srch_key;
@@ -900,7 +898,8 @@ static int index_find(size_t *at_pos, git_index *index, const char *path, int st
srch_key.path = path;
srch_key.stage = stage;
- return git_vector_bsearch2(at_pos, &index->entries, index->entries_search, &srch_key);
+ return git_vector_bsearch2(
+ at_pos, &index->entries, index->entries_search, &srch_key);
}
int git_index_find(size_t *at_pos, git_index *index, const char *path)
@@ -1357,14 +1356,11 @@ int git_index_reuc_remove(git_index *index, size_t position)
void git_index_reuc_clear(git_index *index)
{
size_t i;
- git_index_reuc_entry *reuc;
assert(index);
- git_vector_foreach(&index->reuc, i, reuc) {
- git__free(reuc->path);
- git__free(reuc);
- }
+ for (i = 0; i < index->reuc.length; ++i)
+ index_entry_reuc_free(git__swap(index->reuc.contents[i], NULL));
git_vector_clear(&index->reuc);
}
@@ -1389,7 +1385,7 @@ static int read_reuc(git_index *index, const char *buffer, size_t size)
while (size) {
git_index_reuc_entry *lost;
- len = strlen(buffer) + 1;
+ len = p_strnlen(buffer, size) + 1;
if (size <= len)
return index_error_invalid("reading reuc entries");
@@ -1409,14 +1405,18 @@ static int read_reuc(git_index *index, const char *buffer, size_t size)
if (git__strtol32(&tmp, buffer, &endptr, 8) < 0 ||
!endptr || endptr == buffer || *endptr ||
- (unsigned)tmp > UINT_MAX)
+ (unsigned)tmp > UINT_MAX) {
+ index_entry_reuc_free(lost);
return index_error_invalid("reading reuc entry stage");
+ }
lost->mode[i] = tmp;
len = (endptr + 1) - buffer;
- if (size <= len)
+ if (size <= len) {
+ index_entry_reuc_free(lost);
return index_error_invalid("reading reuc entry stage");
+ }
size -= len;
buffer += len;
@@ -1426,8 +1426,10 @@ static int read_reuc(git_index *index, const char *buffer, size_t size)
for (i = 0; i < 3; i++) {
if (!lost->mode[i])
continue;
- if (size < 20)
+ if (size < 20) {
+ index_entry_reuc_free(lost);
return index_error_invalid("reading reuc entry oid");
+ }
git_oid_fromraw(&lost->oid[i], (const unsigned char *) buffer);
size -= 20;
@@ -1456,7 +1458,7 @@ static int read_conflict_names(git_index *index, const char *buffer, size_t size
return -1;
#define read_conflict_name(ptr) \
- len = strlen(buffer) + 1; \
+ len = p_strnlen(buffer, size) + 1; \
if (size < len) \
return index_error_invalid("reading conflict name entries"); \
\
@@ -1583,7 +1585,8 @@ static size_t read_extension(git_index *index, const char *buffer, size_t buffer
total_size = dest.extension_size + sizeof(struct index_extension);
- if (buffer_size < total_size ||
+ if (dest.extension_size > total_size ||
+ buffer_size < total_size ||
buffer_size - total_size < INDEX_FOOTER_SIZE)
return 0;
@@ -1958,8 +1961,9 @@ int git_index_entry_stage(const git_index_entry *entry)
}
typedef struct read_tree_data {
- git_index *index;
git_vector *old_entries;
+ git_vector *new_entries;
+ git_vector_cmp entries_search;
} read_tree_data;
static int read_tree_cb(
@@ -1990,7 +1994,7 @@ static int read_tree_cb(
skey.stage = 0;
if (!git_vector_bsearch2(
- &pos, data->old_entries, data->index->entries_search, &skey) &&
+ &pos, data->old_entries, data->entries_search, &skey) &&
(old_entry = git_vector_get(data->old_entries, pos)) != NULL &&
entry->mode == old_entry->mode &&
git_oid_equal(&entry->oid, &old_entry->oid))
@@ -2008,7 +2012,7 @@ static int read_tree_cb(
entry->path = git_buf_detach(&path);
git_buf_free(&path);
- if (git_vector_insert(&data->index->entries, entry) < 0) {
+ if (git_vector_insert(data->new_entries, entry) < 0) {
index_entry_free(entry);
return -1;
}
@@ -2022,22 +2026,22 @@ int git_index_read_tree(git_index *index, const git_tree *tree)
git_vector entries = GIT_VECTOR_INIT;
read_tree_data data;
- git_vector_sort(&index->entries);
+ git_vector_set_cmp(&entries, index->entries._cmp); /* match sort */
- git_vector_set_cmp(&entries, index->entries._cmp);
- git_vector_swap(&entries, &index->entries);
+ data.old_entries = &index->entries;
+ data.new_entries = &entries;
+ data.entries_search = index->entries_search;
- git_index_clear(index);
-
- data.index = index;
- data.old_entries = &entries;
+ git_vector_sort(&index->entries);
error = git_tree_walk(tree, GIT_TREEWALK_POST, read_tree_cb, &data);
- index_entries_free(&entries);
- git_vector_free(&entries);
+ git_vector_sort(&entries);
- git_vector_sort(&index->entries);
+ git_index_clear(index);
+
+ git_vector_swap(&entries, &index->entries);
+ git_vector_free(&entries);
return error;
}
@@ -2059,7 +2063,7 @@ int git_index_add_all(
git_iterator *wditer = NULL;
const git_index_entry *wd = NULL;
git_index_entry *entry;
- git_pathspec_context ps;
+ git_pathspec ps;
const char *match;
size_t existing;
bool no_fnmatch = (flags & GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH) != 0;
@@ -2080,7 +2084,7 @@ int git_index_add_all(
if (git_repository__cvar(&ignorecase, repo, GIT_CVAR_IGNORECASE) < 0)
return -1;
- if ((error = git_pathspec_context_init(&ps, paths)) < 0)
+ if ((error = git_pathspec__init(&ps, paths)) < 0)
return error;
/* optionally check that pathspec doesn't mention any ignored files */
@@ -2097,14 +2101,14 @@ int git_index_add_all(
while (!(error = git_iterator_advance(&wd, wditer))) {
/* check if path actually matches */
- if (!git_pathspec_match_path(
- &ps.pathspec, wd->path, no_fnmatch, ignorecase, &match))
+ if (!git_pathspec__match(
+ &ps.pathspec, wd->path, no_fnmatch, (bool)ignorecase, &match, NULL))
continue;
/* skip ignored items that are not already in the index */
if ((flags & GIT_INDEX_ADD_FORCE) == 0 &&
git_iterator_current_is_ignored(wditer) &&
- index_find(&existing, index, wd->path, 0) < 0)
+ git_index__find(&existing, index, wd->path, 0) < 0)
continue;
/* issue notification callback if requested */
@@ -2154,7 +2158,7 @@ int git_index_add_all(
cleanup:
git_iterator_free(wditer);
- git_pathspec_context_free(&ps);
+ git_pathspec__clear(&ps);
return error;
}
@@ -2174,13 +2178,13 @@ static int index_apply_to_all(
{
int error = 0;
size_t i;
- git_pathspec_context ps;
+ git_pathspec ps;
const char *match;
git_buf path = GIT_BUF_INIT;
assert(index);
- if ((error = git_pathspec_context_init(&ps, paths)) < 0)
+ if ((error = git_pathspec__init(&ps, paths)) < 0)
return error;
git_vector_sort(&index->entries);
@@ -2189,8 +2193,9 @@ static int index_apply_to_all(
git_index_entry *entry = git_vector_get(&index->entries, i);
/* check if path actually matches */
- if (!git_pathspec_match_path(
- &ps.pathspec, entry->path, false, index->ignore_case, &match))
+ if (!git_pathspec__match(
+ &ps.pathspec, entry->path, false, (bool)index->ignore_case,
+ &match, NULL))
continue;
/* issue notification callback if requested */
@@ -2237,7 +2242,7 @@ static int index_apply_to_all(
}
git_buf_free(&path);
- git_pathspec_context_free(&ps);
+ git_pathspec__clear(&ps);
return error;
}
diff --git a/src/index.h b/src/index.h
index a59107a7b..4c448fabf 100644
--- a/src/index.h
+++ b/src/index.h
@@ -47,13 +47,17 @@ struct git_index_conflict_iterator {
size_t cur;
};
-extern void git_index_entry__init_from_stat(git_index_entry *entry, struct stat *st);
+extern void git_index_entry__init_from_stat(
+ git_index_entry *entry, struct stat *st, bool trust_mode);
extern size_t git_index__prefix_position(git_index *index, const char *path);
extern int git_index_entry__cmp(const void *a, const void *b);
extern int git_index_entry__cmp_icase(const void *a, const void *b);
+extern int git_index__find(
+ size_t *at_pos, git_index *index, const char *path, int stage);
+
extern void git_index__set_ignore_case(git_index *index, bool ignore_case);
#endif
diff --git a/src/indexer.c b/src/indexer.c
index 1b5339f23..df1ce7cfb 100644
--- a/src/indexer.c
+++ b/src/indexer.c
@@ -18,6 +18,7 @@
#include "filebuf.h"
#include "oid.h"
#include "oidmap.h"
+#include "compress.h"
#define UINT31_MAX (0x7FFFFFFF)
@@ -28,13 +29,15 @@ struct entry {
uint64_t offset_long;
};
-struct git_indexer_stream {
+struct git_indexer {
unsigned int parsed_header :1,
opened_pack :1,
have_stream :1,
have_delta :1;
+ struct git_pack_header hdr;
struct git_pack_file *pack;
git_filebuf pack_file;
+ unsigned int mode;
git_off_t off;
git_off_t entry_start;
git_packfile_stream stream;
@@ -47,13 +50,21 @@ struct git_indexer_stream {
git_transfer_progress_callback progress_cb;
void *progress_payload;
char objbuf[8*1024];
+
+ /* Needed to look up objects which we want to inject to fix a thin pack */
+ git_odb *odb;
+
+ /* Fields for calculating the packfile trailer (hash of everything before it) */
+ char inbuf[GIT_OID_RAWSZ];
+ size_t inbuf_len;
+ git_hash_ctx trailer;
};
struct delta_info {
git_off_t delta_off;
};
-const git_oid *git_indexer_stream_hash(const git_indexer_stream *idx)
+const git_oid *git_indexer_hash(const git_indexer *idx)
{
return &idx->hash;
}
@@ -106,28 +117,34 @@ static int objects_cmp(const void *a, const void *b)
return git_oid__cmp(&entrya->oid, &entryb->oid);
}
-int git_indexer_stream_new(
- git_indexer_stream **out,
+int git_indexer_new(
+ git_indexer **out,
const char *prefix,
+ unsigned int mode,
+ git_odb *odb,
git_transfer_progress_callback progress_cb,
void *progress_payload)
{
- git_indexer_stream *idx;
+ git_indexer *idx;
git_buf path = GIT_BUF_INIT;
static const char suff[] = "/pack";
int error;
- idx = git__calloc(1, sizeof(git_indexer_stream));
+ idx = git__calloc(1, sizeof(git_indexer));
GITERR_CHECK_ALLOC(idx);
+ idx->odb = odb;
idx->progress_cb = progress_cb;
idx->progress_payload = progress_payload;
+ idx->mode = mode ? mode : GIT_PACK_FILE_MODE;
+ git_hash_ctx_init(&idx->trailer);
error = git_buf_joinpath(&path, prefix, suff);
if (error < 0)
goto cleanup;
error = git_filebuf_open(&idx->pack_file, path.ptr,
- GIT_FILEBUF_TEMPORARY | GIT_FILEBUF_DO_NOT_BUFFER);
+ GIT_FILEBUF_TEMPORARY | GIT_FILEBUF_DO_NOT_BUFFER,
+ idx->mode);
git_buf_free(&path);
if (error < 0)
goto cleanup;
@@ -143,7 +160,7 @@ cleanup:
}
/* Try to store the delta so we can try to resolve it later */
-static int store_delta(git_indexer_stream *idx)
+static int store_delta(git_indexer *idx)
{
struct delta_info *delta;
@@ -166,7 +183,7 @@ static void hash_header(git_hash_ctx *ctx, git_off_t len, git_otype type)
git_hash_update(ctx, buffer, hdrlen);
}
-static int hash_object_stream(git_indexer_stream *idx, git_packfile_stream *stream)
+static int hash_object_stream(git_indexer*idx, git_packfile_stream *stream)
{
ssize_t read;
@@ -186,7 +203,7 @@ static int hash_object_stream(git_indexer_stream *idx, git_packfile_stream *stre
}
/* In order to create the packfile stream, we need to skip over the delta base description */
-static int advance_delta_offset(git_indexer_stream *idx, git_otype type)
+static int advance_delta_offset(git_indexer *idx, git_otype type)
{
git_mwindow *w = NULL;
@@ -205,7 +222,7 @@ static int advance_delta_offset(git_indexer_stream *idx, git_otype type)
}
/* Read from the stream and discard any output */
-static int read_object_stream(git_indexer_stream *idx, git_packfile_stream *stream)
+static int read_object_stream(git_indexer *idx, git_packfile_stream *stream)
{
ssize_t read;
@@ -245,7 +262,7 @@ static int crc_object(uint32_t *crc_out, git_mwindow_file *mwf, git_off_t start,
return 0;
}
-static int store_object(git_indexer_stream *idx)
+static int store_object(git_indexer *idx)
{
int i, error;
khiter_t k;
@@ -303,17 +320,10 @@ on_error:
return -1;
}
-static int hash_and_save(git_indexer_stream *idx, git_rawobj *obj, git_off_t entry_start)
+static int save_entry(git_indexer *idx, struct entry *entry, struct git_pack_entry *pentry, git_off_t entry_start)
{
int i, error;
khiter_t k;
- git_oid oid;
- size_t entry_size;
- struct entry *entry;
- struct git_pack_entry *pentry;
-
- entry = git__calloc(1, sizeof(*entry));
- GITERR_CHECK_ALLOC(entry);
if (entry_start > UINT31_MAX) {
entry->offset = UINT32_MAX;
@@ -322,25 +332,43 @@ static int hash_and_save(git_indexer_stream *idx, git_rawobj *obj, git_off_t ent
entry->offset = (uint32_t)entry_start;
}
- /* FIXME: Parse the object instead of hashing it */
- if (git_odb__hashobj(&oid, obj) < 0) {
- giterr_set(GITERR_INDEXER, "Failed to hash object");
+ pentry->offset = entry_start;
+ k = kh_put(oid, idx->pack->idx_cache, &pentry->sha1, &error);
+ if (!error)
+ return -1;
+
+ kh_value(idx->pack->idx_cache, k) = pentry;
+
+ /* Add the object to the list */
+ if (git_vector_insert(&idx->objects, entry) < 0)
return -1;
+
+ for (i = entry->oid.id[0]; i < 256; ++i) {
+ idx->fanout[i]++;
}
- pentry = git__calloc(1, sizeof(struct git_pack_entry));
- GITERR_CHECK_ALLOC(pentry);
+ return 0;
+}
- git_oid_cpy(&pentry->sha1, &oid);
- pentry->offset = entry_start;
- k = kh_put(oid, idx->pack->idx_cache, &pentry->sha1, &error);
- if (!error) {
- git__free(pentry);
+static int hash_and_save(git_indexer *idx, git_rawobj *obj, git_off_t entry_start)
+{
+ git_oid oid;
+ size_t entry_size;
+ struct entry *entry;
+ struct git_pack_entry *pentry;
+
+ entry = git__calloc(1, sizeof(*entry));
+ GITERR_CHECK_ALLOC(entry);
+
+ if (git_odb__hashobj(&oid, obj) < 0) {
+ giterr_set(GITERR_INDEXER, "Failed to hash object");
goto on_error;
}
- kh_value(idx->pack->idx_cache, k) = pentry;
+ pentry = git__calloc(1, sizeof(struct git_pack_entry));
+ GITERR_CHECK_ALLOC(pentry);
+ git_oid_cpy(&pentry->sha1, &oid);
git_oid_cpy(&entry->oid, &oid);
entry->crc = crc32(0L, Z_NULL, 0);
@@ -348,15 +376,7 @@ static int hash_and_save(git_indexer_stream *idx, git_rawobj *obj, git_off_t ent
if (crc_object(&entry->crc, &idx->pack->mwf, entry_start, entry_size) < 0)
goto on_error;
- /* Add the object to the list */
- if (git_vector_insert(&idx->objects, entry) < 0)
- goto on_error;
-
- for (i = oid.id[0]; i < 256; ++i) {
- idx->fanout[i]++;
- }
-
- return 0;
+ return save_entry(idx, entry, pentry, entry_start);
on_error:
git__free(entry);
@@ -364,17 +384,54 @@ on_error:
return -1;
}
-static int do_progress_callback(git_indexer_stream *idx, git_transfer_progress *stats)
+static int do_progress_callback(git_indexer *idx, git_transfer_progress *stats)
{
if (!idx->progress_cb) return 0;
return idx->progress_cb(stats, idx->progress_payload);
}
-int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t size, git_transfer_progress *stats)
+/* Hash everything but the last 20B of input */
+static void hash_partially(git_indexer *idx, const uint8_t *data, size_t size)
+{
+ size_t to_expell, to_keep;
+
+ if (size == 0)
+ return;
+
+ /* Easy case, dump the buffer and the data minus the last 20 bytes */
+ if (size >= GIT_OID_RAWSZ) {
+ git_hash_update(&idx->trailer, idx->inbuf, idx->inbuf_len);
+ git_hash_update(&idx->trailer, data, size - GIT_OID_RAWSZ);
+
+ data += size - GIT_OID_RAWSZ;
+ memcpy(idx->inbuf, data, GIT_OID_RAWSZ);
+ idx->inbuf_len = GIT_OID_RAWSZ;
+ return;
+ }
+
+ /* We can just append */
+ if (idx->inbuf_len + size <= GIT_OID_RAWSZ) {
+ memcpy(idx->inbuf + idx->inbuf_len, data, size);
+ idx->inbuf_len += size;
+ return;
+ }
+
+ /* We need to partially drain the buffer and then append */
+ to_keep = GIT_OID_RAWSZ - size;
+ to_expell = idx->inbuf_len - to_keep;
+
+ git_hash_update(&idx->trailer, idx->inbuf, to_expell);
+
+ memmove(idx->inbuf, idx->inbuf + to_expell, to_keep);
+ memcpy(idx->inbuf + to_keep, data, size);
+ idx->inbuf_len += size - to_expell;
+}
+
+int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_transfer_progress *stats)
{
int error = -1;
- struct git_pack_header hdr;
size_t processed;
+ struct git_pack_header *hdr = &idx->hdr;
git_mwindow_file *mwf = &idx->pack->mwf;
assert(idx && data && stats);
@@ -384,6 +441,8 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz
if (git_filebuf_write(&idx->pack_file, data, size) < 0)
return -1;
+ hash_partially(idx, data, (int)size);
+
/* Make sure we set the new size of the pack */
if (idx->opened_pack) {
idx->pack->mwf.size += size;
@@ -399,14 +458,14 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz
if (!idx->parsed_header) {
unsigned int total_objects;
- if ((unsigned)idx->pack->mwf.size < sizeof(hdr))
+ if ((unsigned)idx->pack->mwf.size < sizeof(struct git_pack_header))
return 0;
- if (parse_header(&hdr, idx->pack) < 0)
+ if (parse_header(&idx->hdr, idx->pack) < 0)
return -1;
idx->parsed_header = 1;
- idx->nr_objects = ntohl(hdr.hdr_entries);
+ idx->nr_objects = ntohl(hdr->hdr_entries);
idx->off = sizeof(struct git_pack_header);
/* for now, limit to 2^32 objects */
@@ -427,6 +486,9 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz
return -1;
stats->received_objects = 0;
+ stats->local_objects = 0;
+ stats->total_deltas = 0;
+ stats->indexed_deltas = 0;
processed = stats->indexed_objects = 0;
stats->total_objects = total_objects;
do_progress_callback(idx, stats);
@@ -512,6 +574,7 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz
stats->received_objects++;
if (do_progress_callback(idx, stats) != 0) {
+ giterr_clear();
error = GIT_EUSER;
goto on_error;
}
@@ -524,7 +587,7 @@ on_error:
return error;
}
-static int index_path_stream(git_buf *path, git_indexer_stream *idx, const char *suffix)
+static int index_path(git_buf *path, git_indexer *idx, const char *suffix)
{
const char prefix[] = "pack-";
size_t slash = (size_t)path->size;
@@ -546,68 +609,306 @@ static int index_path_stream(git_buf *path, git_indexer_stream *idx, const char
return git_buf_oom(path) ? -1 : 0;
}
-static int resolve_deltas(git_indexer_stream *idx, git_transfer_progress *stats)
+/**
+ * Rewind the packfile by the trailer, as we might need to fix the
+ * packfile by injecting objects at the tail and must overwrite it.
+ */
+static git_off_t seek_back_trailer(git_indexer *idx)
+{
+ git_off_t off;
+
+ if ((off = p_lseek(idx->pack_file.fd, -GIT_OID_RAWSZ, SEEK_CUR)) < 0)
+ return -1;
+
+ idx->pack->mwf.size -= GIT_OID_RAWSZ;
+ git_mwindow_free_all(&idx->pack->mwf);
+
+ return off;
+}
+
+static int inject_object(git_indexer *idx, git_oid *id)
{
+ git_odb_object *obj;
+ struct entry *entry;
+ struct git_pack_entry *pentry;
+ git_oid foo = {{0}};
+ unsigned char hdr[64];
+ git_buf buf = GIT_BUF_INIT;
+ git_off_t entry_start;
+ const void *data;
+ size_t len, hdr_len;
+ int error;
+
+ entry = git__calloc(1, sizeof(*entry));
+ GITERR_CHECK_ALLOC(entry);
+
+ entry_start = seek_back_trailer(idx);
+
+ if (git_odb_read(&obj, idx->odb, id) < 0)
+ return -1;
+
+ data = git_odb_object_data(obj);
+ len = git_odb_object_size(obj);
+
+ entry->crc = crc32(0L, Z_NULL, 0);
+
+ /* Write out the object header */
+ hdr_len = git_packfile__object_header(hdr, len, git_odb_object_type(obj));
+ git_filebuf_write(&idx->pack_file, hdr, hdr_len);
+ idx->pack->mwf.size += hdr_len;
+ entry->crc = crc32(entry->crc, hdr, hdr_len);
+
+ if ((error = git__compress(&buf, data, len)) < 0)
+ goto cleanup;
+
+ /* And then the compressed object */
+ git_filebuf_write(&idx->pack_file, buf.ptr, buf.size);
+ idx->pack->mwf.size += buf.size;
+ entry->crc = htonl(crc32(entry->crc, (unsigned char *)buf.ptr, (uInt)buf.size));
+ git_buf_free(&buf);
+
+ /* Write a fake trailer so the pack functions play ball */
+ if ((error = git_filebuf_write(&idx->pack_file, &foo, GIT_OID_RAWSZ)) < 0)
+ goto cleanup;
+
+ idx->pack->mwf.size += GIT_OID_RAWSZ;
+
+ pentry = git__calloc(1, sizeof(struct git_pack_entry));
+ GITERR_CHECK_ALLOC(pentry);
+
+ git_oid_cpy(&pentry->sha1, id);
+ git_oid_cpy(&entry->oid, id);
+ idx->off = entry_start + hdr_len + len;
+
+ if ((error = save_entry(idx, entry, pentry, entry_start)) < 0)
+ git__free(pentry);
+
+cleanup:
+ git_odb_object_free(obj);
+ return error;
+}
+
+static int fix_thin_pack(git_indexer *idx, git_transfer_progress *stats)
+{
+ int error, found_ref_delta = 0;
unsigned int i;
struct delta_info *delta;
+ size_t size;
+ git_otype type;
+ git_mwindow *w = NULL;
+ git_off_t curpos;
+ unsigned char *base_info;
+ unsigned int left = 0;
+ git_oid base;
+
+ assert(git_vector_length(&idx->deltas) > 0);
+
+ if (idx->odb == NULL) {
+ giterr_set(GITERR_INDEXER, "cannot fix a thin pack without an ODB");
+ return -1;
+ }
+ /* Loop until we find the first REF delta */
git_vector_foreach(&idx->deltas, i, delta) {
- git_rawobj obj;
+ curpos = delta->delta_off;
+ error = git_packfile_unpack_header(&size, &type, &idx->pack->mwf, &w, &curpos);
+ git_mwindow_close(&w);
+ if (error < 0)
+ return error;
+
+ if (type == GIT_OBJ_REF_DELTA) {
+ found_ref_delta = 1;
+ break;
+ }
+ }
+
+ if (!found_ref_delta) {
+ giterr_set(GITERR_INDEXER, "no REF_DELTA found, cannot inject object");
+ return -1;
+ }
+
+ /* curpos now points to the base information, which is an OID */
+ base_info = git_mwindow_open(&idx->pack->mwf, &w, curpos, GIT_OID_RAWSZ, &left);
+ if (base_info == NULL) {
+ giterr_set(GITERR_INDEXER, "failed to map delta information");
+ return -1;
+ }
+
+ git_oid_fromraw(&base, base_info);
+ git_mwindow_close(&w);
+
+ if (inject_object(idx, &base) < 0)
+ return -1;
+
+ stats->local_objects++;
+
+ return 0;
+}
+
+static int resolve_deltas(git_indexer *idx, git_transfer_progress *stats)
+{
+ unsigned int i;
+ struct delta_info *delta;
+ int progressed = 0;
+
+ while (idx->deltas.length > 0) {
+ progressed = 0;
+ git_vector_foreach(&idx->deltas, i, delta) {
+ git_rawobj obj;
+
+ idx->off = delta->delta_off;
+ if (git_packfile_unpack(&obj, idx->pack, &idx->off) < 0)
+ continue;
+
+ if (hash_and_save(idx, &obj, delta->delta_off) < 0)
+ continue;
+
+ git__free(obj.data);
+ stats->indexed_objects++;
+ stats->indexed_deltas++;
+ progressed = 1;
+ do_progress_callback(idx, stats);
+
+ /*
+ * Remove this delta from the list and
+ * decrease i so we don't skip over the next
+ * delta.
+ */
+ git_vector_remove(&idx->deltas, i);
+ git__free(delta);
+ i--;
+ }
- idx->off = delta->delta_off;
- if (git_packfile_unpack(&obj, idx->pack, &idx->off) < 0)
+ if (!progressed && (fix_thin_pack(idx, stats) < 0)) {
+ giterr_set(GITERR_INDEXER, "missing delta bases");
return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int update_header_and_rehash(git_indexer *idx, git_transfer_progress *stats)
+{
+ void *ptr;
+ size_t chunk = 1024*1024;
+ git_off_t hashed = 0;
+ git_mwindow *w = NULL;
+ git_mwindow_file *mwf;
+ unsigned int left;
+ git_hash_ctx *ctx;
+
+ mwf = &idx->pack->mwf;
+ ctx = &idx->trailer;
+
+ git_hash_ctx_init(ctx);
+ git_mwindow_free_all(mwf);
- if (hash_and_save(idx, &obj, delta->delta_off) < 0)
+ /* Update the header to include the numer of local objects we injected */
+ idx->hdr.hdr_entries = htonl(stats->total_objects + stats->local_objects);
+ if (p_lseek(idx->pack_file.fd, 0, SEEK_SET) < 0) {
+ giterr_set(GITERR_OS, "failed to seek to the beginning of the pack");
+ return -1;
+ }
+
+ if (p_write(idx->pack_file.fd, &idx->hdr, sizeof(struct git_pack_header)) < 0) {
+ giterr_set(GITERR_OS, "failed to update the pack header");
+ return -1;
+ }
+
+ /*
+ * We now use the same technique as before to determine the
+ * hash. We keep reading up to the end and let
+ * hash_partially() keep the existing trailer out of the
+ * calculation.
+ */
+ idx->inbuf_len = 0;
+ while (hashed < mwf->size) {
+ ptr = git_mwindow_open(mwf, &w, hashed, chunk, &left);
+ if (ptr == NULL)
return -1;
- git__free(obj.data);
- stats->indexed_objects++;
- do_progress_callback(idx, stats);
+ hash_partially(idx, ptr, left);
+ hashed += left;
+
+ git_mwindow_close(&w);
}
return 0;
}
-int git_indexer_stream_finalize(git_indexer_stream *idx, git_transfer_progress *stats)
+int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats)
{
git_mwindow *w = NULL;
unsigned int i, long_offsets = 0, left;
struct git_pack_idx_header hdr;
git_buf filename = GIT_BUF_INIT;
struct entry *entry;
- void *packfile_hash;
- git_oid file_hash;
+ git_oid trailer_hash, file_hash;
git_hash_ctx ctx;
git_filebuf index_file = {0};
+ void *packfile_trailer;
if (git_hash_ctx_init(&ctx) < 0)
return -1;
/* Test for this before resolve_deltas(), as it plays with idx->off */
- if (idx->off < idx->pack->mwf.size - GIT_OID_RAWSZ) {
- giterr_set(GITERR_INDEXER, "Indexing error: unexpected data at the end of the pack");
+ if (idx->off < idx->pack->mwf.size - 20) {
+ giterr_set(GITERR_INDEXER, "unexpected data at the end of the pack");
return -1;
}
- if (idx->deltas.length > 0)
- if (resolve_deltas(idx, stats) < 0)
- 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) {
+ git_mwindow_close(&w);
+ goto on_error;
+ }
+
+ /* Compare the packfile trailer as it was sent to us and what we calculated */
+ git_oid_fromraw(&file_hash, packfile_trailer);
+ git_mwindow_close(&w);
+
+ git_hash_final(&trailer_hash, &idx->trailer);
+ if (git_oid_cmp(&file_hash, &trailer_hash)) {
+ giterr_set(GITERR_INDEXER, "packfile trailer mismatch");
+ return -1;
+ }
+
+ /* Freeze the number of deltas */
+ stats->total_deltas = stats->total_objects - stats->indexed_objects;
+
+ if (resolve_deltas(idx, stats) < 0)
+ return -1;
if (stats->indexed_objects != stats->total_objects) {
- giterr_set(GITERR_INDEXER, "Indexing error: early EOF");
+ giterr_set(GITERR_INDEXER, "early EOF");
return -1;
}
+ if (stats->local_objects > 0) {
+ if (update_header_and_rehash(idx, stats) < 0)
+ return -1;
+
+ git_hash_final(&trailer_hash, &idx->trailer);
+ if (p_lseek(idx->pack_file.fd, -GIT_OID_RAWSZ, SEEK_END) < 0)
+ return -1;
+
+ if (p_write(idx->pack_file.fd, &trailer_hash, GIT_OID_RAWSZ) < 0) {
+ giterr_set(GITERR_OS, "failed to update pack trailer");
+ return -1;
+ }
+ }
+
git_vector_sort(&idx->objects);
git_buf_sets(&filename, idx->pack->pack_name);
- git_buf_truncate(&filename, filename.size - strlen("pack"));
+ git_buf_shorten(&filename, strlen("pack"));
git_buf_puts(&filename, "idx");
if (git_buf_oom(&filename))
return -1;
- if (git_filebuf_open(&index_file, filename.ptr, GIT_FILEBUF_HASH_CONTENTS) < 0)
+ if (git_filebuf_open(&index_file, filename.ptr,
+ GIT_FILEBUF_HASH_CONTENTS, idx->mode) < 0)
goto on_error;
/* Write out the header */
@@ -658,30 +959,22 @@ int git_indexer_stream_finalize(git_indexer_stream *idx, git_transfer_progress *
git_filebuf_write(&index_file, &split, sizeof(uint32_t) * 2);
}
- /* Write out the packfile trailer */
- packfile_hash = git_mwindow_open(&idx->pack->mwf, &w, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ, &left);
- if (packfile_hash == NULL) {
- git_mwindow_close(&w);
+ /* Write out the packfile trailer to the index */
+ if (git_filebuf_write(&index_file, &trailer_hash, GIT_OID_RAWSZ) < 0)
goto on_error;
- }
-
- memcpy(&file_hash, packfile_hash, GIT_OID_RAWSZ);
- git_mwindow_close(&w);
-
- git_filebuf_write(&index_file, &file_hash, sizeof(git_oid));
- /* Write out the packfile trailer to the idx file as well */
- if (git_filebuf_hash(&file_hash, &index_file) < 0)
+ /* Write out the hash of the idx */
+ if (git_filebuf_hash(&trailer_hash, &index_file) < 0)
goto on_error;
- git_filebuf_write(&index_file, &file_hash, sizeof(git_oid));
+ git_filebuf_write(&index_file, &trailer_hash, sizeof(git_oid));
/* Figure out what the final name should be */
- if (index_path_stream(&filename, idx, ".idx") < 0)
+ if (index_path(&filename, idx, ".idx") < 0)
goto on_error;
/* Commit file */
- if (git_filebuf_commit_at(&index_file, filename.ptr, GIT_PACK_FILE_MODE) < 0)
+ if (git_filebuf_commit_at(&index_file, filename.ptr) < 0)
goto on_error;
git_mwindow_free_all(&idx->pack->mwf);
@@ -689,10 +982,10 @@ int git_indexer_stream_finalize(git_indexer_stream *idx, git_transfer_progress *
p_close(idx->pack->mwf.fd);
idx->pack->mwf.fd = -1;
- if (index_path_stream(&filename, idx, ".pack") < 0)
+ if (index_path(&filename, idx, ".pack") < 0)
goto on_error;
/* And don't forget to rename the packfile to its new place. */
- if (git_filebuf_commit_at(&idx->pack_file, filename.ptr, GIT_PACK_FILE_MODE) < 0)
+ if (git_filebuf_commit_at(&idx->pack_file, filename.ptr) < 0)
return -1;
git_buf_free(&filename);
@@ -706,7 +999,7 @@ on_error:
return -1;
}
-void git_indexer_stream_free(git_indexer_stream *idx)
+void git_indexer_free(git_indexer *idx)
{
khiter_t k;
unsigned int i;
diff --git a/src/iterator.c b/src/iterator.c
index 5917f63fd..8646399ab 100644
--- a/src/iterator.c
+++ b/src/iterator.c
@@ -893,6 +893,7 @@ struct fs_iterator {
git_index_entry entry;
git_buf path;
size_t root_len;
+ uint32_t dirload_flags;
int depth;
int (*enter_dir_cb)(fs_iterator *self);
@@ -986,12 +987,25 @@ static int fs_iterator__expand_dir(fs_iterator *fi)
GITERR_CHECK_ALLOC(ff);
error = git_path_dirload_with_stat(
- fi->path.ptr, fi->root_len, iterator__ignore_case(fi),
+ fi->path.ptr, fi->root_len, fi->dirload_flags,
fi->base.start, fi->base.end, &ff->entries);
if (error < 0) {
+ git_error last_error = {0};
+
+ giterr_detach(&last_error);
+
+ /* these callbacks may clear the error message */
fs_iterator__free_frame(ff);
fs_iterator__advance_over(NULL, (git_iterator *)fi);
+ /* next time return value we skipped to */
+ fi->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS;
+
+ if (last_error.message) {
+ giterr_set_str(last_error.klass, last_error.message);
+ free(last_error.message);
+ }
+
return error;
}
@@ -1174,7 +1188,7 @@ static int fs_iterator__update_entry(fs_iterator *fi)
return GIT_ITEROVER;
fi->entry.path = ps->path;
- git_index_entry__init_from_stat(&fi->entry, &ps->st);
+ git_index_entry__init_from_stat(&fi->entry, &ps->st, true);
/* need different mode here to keep directories during iteration */
fi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);
@@ -1207,6 +1221,11 @@ static int fs_iterator__initialize(
}
fi->root_len = fi->path.size;
+ fi->dirload_flags =
+ (iterator__ignore_case(fi) ? GIT_PATH_DIR_IGNORE_CASE : 0) |
+ (iterator__flag(fi, PRECOMPOSE_UNICODE) ?
+ GIT_PATH_DIR_PRECOMPOSE_UNICODE : 0);
+
if ((error = fs_iterator__expand_dir(fi)) < 0) {
if (error == GIT_ENOTFOUND || error == GIT_ITEROVER) {
giterr_clear();
@@ -1329,7 +1348,7 @@ int git_iterator_for_workdir_ext(
const char *start,
const char *end)
{
- int error;
+ int error, precompose = 0;
workdir_iterator *wi;
if (!repo_workdir) {
@@ -1350,12 +1369,18 @@ int git_iterator_for_workdir_ext(
wi->fi.update_entry_cb = workdir_iterator__update_entry;
if ((error = iterator__update_ignore_case((git_iterator *)wi, flags)) < 0 ||
- (error = git_ignore__for_path(repo, "", &wi->ignores)) < 0)
+ (error = git_ignore__for_path(repo, ".gitignore", &wi->ignores)) < 0)
{
git_iterator_free((git_iterator *)wi);
return error;
}
+ /* try to look up precompose and set flag if appropriate */
+ if (git_repository__cvar(&precompose, repo, GIT_CVAR_PRECOMPOSE) < 0)
+ giterr_clear();
+ else if (precompose)
+ wi->fi.base.flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE;
+
return fs_iterator__initialize(out, &wi->fi, repo_workdir);
}
diff --git a/src/iterator.h b/src/iterator.h
index ea88fa6a2..751e139d0 100644
--- a/src/iterator.h
+++ b/src/iterator.h
@@ -24,13 +24,15 @@ typedef enum {
typedef enum {
/** ignore case for entry sort order */
- GIT_ITERATOR_IGNORE_CASE = (1 << 0),
+ GIT_ITERATOR_IGNORE_CASE = (1u << 0),
/** force case sensitivity for entry sort order */
- GIT_ITERATOR_DONT_IGNORE_CASE = (1 << 1),
+ GIT_ITERATOR_DONT_IGNORE_CASE = (1u << 1),
/** return tree items in addition to blob items */
- GIT_ITERATOR_INCLUDE_TREES = (1 << 2),
+ GIT_ITERATOR_INCLUDE_TREES = (1u << 2),
/** don't flatten trees, requiring advance_into (implies INCLUDE_TREES) */
- GIT_ITERATOR_DONT_AUTOEXPAND = (1 << 3),
+ GIT_ITERATOR_DONT_AUTOEXPAND = (1u << 3),
+ /** convert precomposed unicode to decomposed unicode */
+ GIT_ITERATOR_PRECOMPOSE_UNICODE = (1u << 4),
} git_iterator_flag_t;
typedef struct {
diff --git a/src/merge.c b/src/merge.c
index 82d2e6f37..115867971 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -58,7 +58,7 @@ struct merge_diff_df_data {
/* Merge base computation */
-int git_merge_base_many(git_oid *out, git_repository *repo, const git_oid input_array[], size_t length)
+int git_merge_base_many(git_oid *out, git_repository *repo, size_t length, const git_oid input_array[])
{
git_revwalk *walk;
git_vector list;
@@ -285,7 +285,7 @@ int git_repository_mergehead_foreach(git_repository *repo,
if ((error = git_oid_fromstr(&oid, line)) < 0)
goto cleanup;
- if (cb(&oid, payload) < 0) {
+ if (cb(&oid, payload) != 0) {
error = GIT_EUSER;
goto cleanup;
}
@@ -1615,17 +1615,14 @@ static int write_orig_head(
{
git_filebuf file = GIT_FILEBUF_INIT;
git_buf file_path = GIT_BUF_INIT;
- char orig_oid_str[GIT_OID_HEXSZ + 1];
int error = 0;
assert(repo && our_head);
- git_oid_tostr(orig_oid_str, GIT_OID_HEXSZ+1, &our_head->oid);
-
if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_ORIG_HEAD_FILE)) == 0 &&
- (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE)) == 0 &&
- (error = git_filebuf_printf(&file, "%s\n", orig_oid_str)) == 0)
- error = git_filebuf_commit(&file, 0666);
+ (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) == 0 &&
+ (error = git_filebuf_printf(&file, "%s\n", our_head->oid_str)) == 0)
+ error = git_filebuf_commit(&file);
if (error < 0)
git_filebuf_cleanup(&file);
@@ -1642,24 +1639,21 @@ static int write_merge_head(
{
git_filebuf file = GIT_FILEBUF_INIT;
git_buf file_path = GIT_BUF_INIT;
- char merge_oid_str[GIT_OID_HEXSZ + 1];
size_t i;
int error = 0;
assert(repo && heads);
if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_HEAD_FILE)) < 0 ||
- (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE)) < 0)
+ (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) < 0)
goto cleanup;
for (i = 0; i < heads_len; i++) {
- git_oid_tostr(merge_oid_str, GIT_OID_HEXSZ+1, &heads[i]->oid);
-
- if ((error = git_filebuf_printf(&file, "%s\n", merge_oid_str)) < 0)
+ if ((error = git_filebuf_printf(&file, "%s\n", heads[i]->oid_str)) < 0)
goto cleanup;
}
- error = git_filebuf_commit(&file, 0666);
+ error = git_filebuf_commit(&file);
cleanup:
if (error < 0)
@@ -1682,10 +1676,21 @@ static int write_merge_mode(git_repository *repo, unsigned int flags)
assert(repo);
if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MODE_FILE)) < 0 ||
- (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE)) < 0)
+ (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) < 0)
goto cleanup;
- error = git_filebuf_commit(&file, 0666);
+ /*
+ * no-ff is the only thing allowed here at present. One would
+ * presume they would be space-delimited when there are more, but
+ * this needs to be revisited.
+ */
+
+ if (flags & GIT_MERGE_NO_FASTFORWARD) {
+ if ((error = git_filebuf_write(&file, "no-ff", 5)) < 0)
+ goto cleanup;
+ }
+
+ error = git_filebuf_commit(&file);
cleanup:
if (error < 0)
@@ -1890,7 +1895,6 @@ static int write_merge_msg(
{
git_filebuf file = GIT_FILEBUF_INIT;
git_buf file_path = GIT_BUF_INIT;
- char oid_str[GIT_OID_HEXSZ + 1];
struct merge_msg_entry *entries;
git_vector matching = GIT_VECTOR_INIT;
size_t i;
@@ -1902,14 +1906,16 @@ static int write_merge_msg(
entries = git__calloc(heads_len, sizeof(struct merge_msg_entry));
GITERR_CHECK_ALLOC(entries);
- if (git_vector_init(&matching, heads_len, NULL) < 0)
+ if (git_vector_init(&matching, heads_len, NULL) < 0) {
+ git__free(entries);
return -1;
+ }
for (i = 0; i < heads_len; i++)
entries[i].merge_head = heads[i];
if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MSG_FILE)) < 0 ||
- (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE)) < 0 ||
+ (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) < 0 ||
(error = git_filebuf_write(&file, "Merge ", 6)) < 0)
goto cleanup;
@@ -1928,10 +1934,9 @@ static int write_merge_msg(
if (!msg_entry_is_oid(&entries[i]))
break;
- git_oid_fmt(oid_str, &entries[i].merge_head->oid);
- oid_str[GIT_OID_HEXSZ] = '\0';
-
- if ((error = git_filebuf_printf(&file, "%scommit '%s'", (i > 0) ? "; " : "", oid_str)) < 0)
+ if ((error = git_filebuf_printf(&file,
+ "%scommit '%s'", (i > 0) ? "; " : "",
+ entries[i].merge_head->oid_str)) < 0)
goto cleanup;
entries[i].written = 1;
@@ -1978,15 +1983,13 @@ static int write_merge_msg(
if (merge_msg_entry_written(&entries[i]))
continue;
- git_oid_fmt(oid_str, &entries[i].merge_head->oid);
- oid_str[GIT_OID_HEXSZ] = '\0';
-
- if ((error = git_filebuf_printf(&file, "; commit '%s'", oid_str)) < 0)
+ if ((error = git_filebuf_printf(&file, "; commit '%s'",
+ entries[i].merge_head->oid_str)) < 0)
goto cleanup;
}
if ((error = git_filebuf_printf(&file, "\n")) < 0 ||
- (error = git_filebuf_commit(&file, 0666)) < 0)
+ (error = git_filebuf_commit(&file)) < 0)
goto cleanup;
cleanup:
@@ -2001,6 +2004,477 @@ cleanup:
return error;
}
+/* Merge branches */
+
+static int merge_ancestor_head(
+ git_merge_head **ancestor_head,
+ git_repository *repo,
+ const git_merge_head *our_head,
+ const git_merge_head **their_heads,
+ size_t their_heads_len)
+{
+ git_oid *oids, ancestor_oid;
+ size_t i;
+ int error = 0;
+
+ assert(repo && our_head && their_heads);
+
+ oids = git__calloc(their_heads_len + 1, sizeof(git_oid));
+ GITERR_CHECK_ALLOC(oids);
+
+ git_oid_cpy(&oids[0], git_commit_id(our_head->commit));
+
+ for (i = 0; i < their_heads_len; i++)
+ git_oid_cpy(&oids[i + 1], &their_heads[i]->oid);
+
+ if ((error = git_merge_base_many(&ancestor_oid, repo, their_heads_len + 1, oids)) < 0)
+ goto on_error;
+
+ error = git_merge_head_from_oid(ancestor_head, repo, &ancestor_oid);
+
+on_error:
+ git__free(oids);
+ return error;
+}
+
+GIT_INLINE(bool) merge_check_uptodate(
+ git_merge_result *result,
+ const git_merge_head *ancestor_head,
+ const git_merge_head *their_head)
+{
+ if (git_oid_cmp(&ancestor_head->oid, &their_head->oid) == 0) {
+ result->is_uptodate = 1;
+ return true;
+ }
+
+ return false;
+}
+
+GIT_INLINE(bool) merge_check_fastforward(
+ git_merge_result *result,
+ const git_merge_head *ancestor_head,
+ const git_merge_head *our_head,
+ const git_merge_head *their_head,
+ unsigned int flags)
+{
+ if ((flags & GIT_MERGE_NO_FASTFORWARD) == 0 &&
+ git_oid_cmp(&ancestor_head->oid, &our_head->oid) == 0) {
+ result->is_fastforward = 1;
+ git_oid_cpy(&result->fastforward_oid, &their_head->oid);
+
+ return true;
+ }
+
+ return false;
+}
+
+const char *merge_their_label(const char *branchname)
+{
+ const char *slash;
+
+ if ((slash = strrchr(branchname, '/')) == NULL)
+ return branchname;
+
+ if (*(slash+1) == '\0')
+ return "theirs";
+
+ return slash+1;
+}
+
+static int merge_normalize_opts(
+ git_repository *repo,
+ git_merge_opts *opts,
+ const git_merge_opts *given,
+ size_t their_heads_len,
+ const git_merge_head **their_heads)
+{
+ int error = 0;
+ unsigned int default_checkout_strategy = GIT_CHECKOUT_SAFE_CREATE |
+ GIT_CHECKOUT_ALLOW_CONFLICTS;
+
+ GIT_UNUSED(repo);
+
+ if (given != NULL)
+ memcpy(opts, given, sizeof(git_merge_opts));
+ else {
+ git_merge_opts default_opts = GIT_MERGE_OPTS_INIT;
+ memcpy(opts, &default_opts, sizeof(git_merge_opts));
+ }
+
+ if (!opts->checkout_opts.checkout_strategy)
+ opts->checkout_opts.checkout_strategy = default_checkout_strategy;
+
+ if (!opts->checkout_opts.our_label)
+ opts->checkout_opts.our_label = "HEAD";
+
+ if (!opts->checkout_opts.their_label) {
+ if (their_heads_len == 1 && their_heads[0]->ref_name)
+ opts->checkout_opts.their_label = merge_their_label(their_heads[0]->ref_name);
+ else if (their_heads_len == 1)
+ opts->checkout_opts.their_label = their_heads[0]->oid_str;
+ else
+ opts->checkout_opts.their_label = "theirs";
+ }
+
+ return error;
+}
+
+static int merge_affected_paths(git_vector *paths, git_repository *repo, git_index *index_new)
+{
+ git_tree *head_tree = NULL;
+ git_iterator *iter_head = NULL, *iter_new = NULL;
+ git_diff *merged_list = NULL;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_delta *delta;
+ size_t i;
+ const git_index_entry *e;
+ char *path;
+ int error = 0;
+
+ if ((error = git_repository_head_tree(&head_tree, repo)) < 0 ||
+ (error = git_iterator_for_tree(&iter_head, head_tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 ||
+ (error = git_iterator_for_index(&iter_new, index_new, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 ||
+ (error = git_diff__from_iterators(&merged_list, repo, iter_head, iter_new, &opts)) < 0)
+ goto done;
+
+ git_vector_foreach(&merged_list->deltas, i, delta) {
+ path = git__strdup(delta->new_file.path);
+ GITERR_CHECK_ALLOC(path);
+
+ if ((error = git_vector_insert(paths, path)) < 0)
+ goto on_error;
+ }
+
+ for (i = 0; i < git_index_entrycount(index_new); i++) {
+ e = git_index_get_byindex(index_new, i);
+
+ if (git_index_entry_stage(e) != 0 &&
+ (git_vector_last(paths) == NULL ||
+ strcmp(git_vector_last(paths), e->path) != 0)) {
+
+ path = git__strdup(e->path);
+ GITERR_CHECK_ALLOC(path);
+
+ if ((error = git_vector_insert(paths, path)) < 0)
+ goto on_error;
+ }
+ }
+
+ goto done;
+
+on_error:
+ git_vector_foreach(paths, i, path)
+ git__free(path);
+
+ git_vector_clear(paths);
+
+done:
+ git_tree_free(head_tree);
+ git_iterator_free(iter_head);
+ git_iterator_free(iter_new);
+ git_diff_free(merged_list);
+
+ return error;
+}
+
+static int merge_check_index(size_t *conflicts, git_repository *repo, git_index *index_new, git_vector *merged_paths)
+{
+ git_tree *head_tree = NULL;
+ git_index *index_repo = NULL;
+ git_iterator *iter_repo = NULL, *iter_new = NULL;
+ git_diff *staged_diff_list = NULL, *index_diff_list = NULL;
+ git_diff_delta *delta;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_vector staged_paths = GIT_VECTOR_INIT;
+ size_t i;
+ int error = 0;
+
+ GIT_UNUSED(merged_paths);
+
+ *conflicts = 0;
+
+ /* No staged changes may exist unless the change staged is identical to
+ * the result of the merge. This allows one to apply to merge manually,
+ * then run merge. Any other staged change would be overwritten by
+ * a reset merge.
+ */
+ if ((error = git_repository_head_tree(&head_tree, repo)) < 0 ||
+ (error = git_repository_index(&index_repo, repo)) < 0 ||
+ (error = git_diff_tree_to_index(&staged_diff_list, repo, head_tree, index_repo, &opts)) < 0)
+ goto done;
+
+ if (staged_diff_list->deltas.length == 0)
+ goto done;
+
+ git_vector_foreach(&staged_diff_list->deltas, i, delta) {
+ if ((error = git_vector_insert(&staged_paths, (char *)delta->new_file.path)) < 0)
+ goto done;
+ }
+
+ opts.pathspec.count = staged_paths.length;
+ opts.pathspec.strings = (char **)staged_paths.contents;
+
+ if ((error = git_iterator_for_index(&iter_repo, index_repo, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 ||
+ (error = git_iterator_for_index(&iter_new, index_new, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 ||
+ (error = git_diff__from_iterators(&index_diff_list, repo, iter_repo, iter_new, &opts)) < 0)
+ goto done;
+
+ *conflicts = index_diff_list->deltas.length;
+
+done:
+ git_tree_free(head_tree);
+ git_index_free(index_repo);
+ git_iterator_free(iter_repo);
+ git_iterator_free(iter_new);
+ git_diff_free(staged_diff_list);
+ git_diff_free(index_diff_list);
+ git_vector_free(&staged_paths);
+
+ return error;
+}
+
+static int merge_check_workdir(size_t *conflicts, git_repository *repo, git_index *index_new, git_vector *merged_paths)
+{
+ git_tree *head_tree = NULL;
+ git_diff *wd_diff_list = NULL;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ int error = 0;
+
+ GIT_UNUSED(index_new);
+
+ *conflicts = 0;
+
+ opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
+
+ if ((error = git_repository_head_tree(&head_tree, repo)) < 0)
+ goto done;
+
+ /* Workdir changes may exist iff they do not conflict with changes that
+ * will be applied by the merge (including conflicts). Ensure that there
+ * are no changes in the workdir to these paths.
+ */
+ opts.pathspec.count = merged_paths->length;
+ opts.pathspec.strings = (char **)merged_paths->contents;
+
+ if ((error = git_diff_tree_to_workdir(&wd_diff_list, repo, head_tree, &opts)) < 0)
+ goto done;
+
+ *conflicts = wd_diff_list->deltas.length;
+
+done:
+ git_tree_free(head_tree);
+ git_diff_free(wd_diff_list);
+
+ return error;
+}
+
+static int merge_indexes(git_repository *repo, git_index *index_new)
+{
+ git_index *index_repo;
+ unsigned int index_repo_caps;
+ git_vector paths = GIT_VECTOR_INIT;
+ size_t index_conflicts = 0, wd_conflicts = 0, conflicts, i;
+ char *path;
+ const git_index_entry *e;
+ const git_index_name_entry *name;
+ const git_index_reuc_entry *reuc;
+ int error = 0;
+
+ if ((error = git_repository_index(&index_repo, repo)) < 0)
+ goto done;
+
+ /* Set the index to case sensitive to handle the merge */
+ index_repo_caps = git_index_caps(index_repo);
+
+ if ((error = git_index_set_caps(index_repo, (index_repo_caps & ~GIT_INDEXCAP_IGNORE_CASE))) < 0)
+ goto done;
+
+ /* Make sure the index and workdir state do not prevent merging */
+ if ((error = merge_affected_paths(&paths, repo, index_new)) < 0 ||
+ (error = merge_check_index(&index_conflicts, repo, index_new, &paths)) < 0 ||
+ (error = merge_check_workdir(&wd_conflicts, repo, index_new, &paths)) < 0)
+ goto done;
+
+ if ((conflicts = index_conflicts + wd_conflicts) > 0) {
+ giterr_set(GITERR_MERGE, "%d uncommitted change%s would be overwritten by merge",
+ conflicts, (conflicts != 1) ? "s" : "");
+ error = GIT_EMERGECONFLICT;
+
+ goto done;
+ }
+
+ /* Update the new index */
+ git_vector_foreach(&paths, i, path) {
+ if ((e = git_index_get_bypath(index_new, path, 0)) != NULL)
+ error = git_index_add(index_repo, e);
+ else
+ error = git_index_remove(index_repo, path, 0);
+ }
+
+ /* Add conflicts */
+ git_index_conflict_cleanup(index_repo);
+
+ for (i = 0; i < git_index_entrycount(index_new); i++) {
+ e = git_index_get_byindex(index_new, i);
+
+ if (git_index_entry_stage(e) != 0 &&
+ (error = git_index_add(index_repo, e)) < 0)
+ goto done;
+ }
+
+ /* Add name entries */
+ git_index_name_clear(index_repo);
+
+ for (i = 0; i < git_index_name_entrycount(index_new); i++) {
+ name = git_index_name_get_byindex(index_new, i);
+
+ if ((error = git_index_name_add(index_repo,
+ name->ancestor, name->ours, name->theirs)) < 0)
+ goto done;
+ }
+
+ /* Add the reuc */
+ git_index_reuc_clear(index_repo);
+
+ for (i = 0; i < git_index_reuc_entrycount(index_new); i++) {
+ reuc = (git_index_reuc_entry *)git_index_reuc_get_byindex(index_new, i);
+
+ if ((error = git_index_reuc_add(index_repo, reuc->path,
+ reuc->mode[0], &reuc->oid[0],
+ reuc->mode[1], &reuc->oid[1],
+ reuc->mode[2], &reuc->oid[2])) < 0)
+ goto done;
+ }
+
+done:
+ if (index_repo != NULL)
+ git_index_set_caps(index_repo, index_repo_caps);
+
+ git_index_free(index_repo);
+
+ git_vector_foreach(&paths, i, path)
+ git__free(path);
+
+ git_vector_free(&paths);
+
+ return error;
+}
+
+int git_merge(
+ git_merge_result **out,
+ git_repository *repo,
+ const git_merge_head **their_heads,
+ size_t their_heads_len,
+ const git_merge_opts *given_opts)
+{
+ git_merge_result *result;
+ git_merge_opts opts;
+ git_reference *our_ref = NULL;
+ git_merge_head *ancestor_head = NULL, *our_head = NULL;
+ git_tree *ancestor_tree = NULL, *our_tree = NULL, **their_trees = NULL;
+ git_index *index_new = NULL, *index_repo = NULL;
+ size_t i;
+ int error = 0;
+
+ assert(out && repo && their_heads);
+
+ *out = NULL;
+
+ if (their_heads_len != 1) {
+ giterr_set(GITERR_MERGE, "Can only merge a single branch");
+ return -1;
+ }
+
+ result = git__calloc(1, sizeof(git_merge_result));
+ GITERR_CHECK_ALLOC(result);
+
+ their_trees = git__calloc(their_heads_len, sizeof(git_tree *));
+ GITERR_CHECK_ALLOC(their_trees);
+
+ if ((error = merge_normalize_opts(repo, &opts, given_opts, their_heads_len, their_heads)) < 0)
+ goto on_error;
+
+ if ((error = git_repository__ensure_not_bare(repo, "merge")) < 0)
+ goto on_error;
+
+ if ((error = git_reference_lookup(&our_ref, repo, GIT_HEAD_FILE)) < 0 ||
+ (error = git_merge_head_from_ref(&our_head, repo, our_ref)) < 0)
+ goto on_error;
+
+ if ((error = merge_ancestor_head(&ancestor_head, repo, our_head, their_heads, their_heads_len)) < 0 &&
+ error != GIT_ENOTFOUND)
+ goto on_error;
+
+ if (their_heads_len == 1 &&
+ ancestor_head != NULL &&
+ (merge_check_uptodate(result, ancestor_head, their_heads[0]) ||
+ merge_check_fastforward(result, ancestor_head, our_head, their_heads[0], opts.merge_flags))) {
+ *out = result;
+ goto done;
+ }
+
+ /* If FASTFORWARD_ONLY is specified, fail. */
+ if ((opts.merge_flags & GIT_MERGE_FASTFORWARD_ONLY) ==
+ GIT_MERGE_FASTFORWARD_ONLY) {
+ giterr_set(GITERR_MERGE, "Not a fast-forward.");
+ error = GIT_ENONFASTFORWARD;
+ goto on_error;
+ }
+
+ /* Write the merge files to the repository. */
+ if ((error = git_merge__setup(repo, our_head, their_heads, their_heads_len, opts.merge_flags)) < 0)
+ goto on_error;
+
+ if (ancestor_head != NULL &&
+ (error = git_commit_tree(&ancestor_tree, ancestor_head->commit)) < 0)
+ goto on_error;
+
+ if ((error = git_commit_tree(&our_tree, our_head->commit)) < 0)
+ goto on_error;
+
+ for (i = 0; i < their_heads_len; i++) {
+ if ((error = git_commit_tree(&their_trees[i], their_heads[i]->commit)) < 0)
+ goto on_error;
+ }
+
+ /* TODO: recursive, octopus, etc... */
+
+ if ((error = git_merge_trees(&index_new, repo, ancestor_tree, our_tree, their_trees[0], &opts.merge_tree_opts)) < 0 ||
+ (error = merge_indexes(repo, index_new)) < 0 ||
+ (error = git_repository_index(&index_repo, repo)) < 0 ||
+ (error = git_checkout_index(repo, index_repo, &opts.checkout_opts)) < 0)
+ goto on_error;
+
+ result->index = index_new;
+
+ *out = result;
+ goto done;
+
+on_error:
+ git_repository_merge_cleanup(repo);
+
+ git_index_free(index_new);
+ git__free(result);
+
+done:
+ git_index_free(index_repo);
+
+ git_tree_free(ancestor_tree);
+ git_tree_free(our_tree);
+
+ for (i = 0; i < their_heads_len; i++)
+ git_tree_free(their_trees[i]);
+
+ git__free(their_trees);
+
+ git_merge_head_free(our_head);
+ git_merge_head_free(ancestor_head);
+
+ git_reference_free(our_ref);
+
+ return error;
+}
+
int git_merge__setup(
git_repository *repo,
const git_merge_head *our_head,
@@ -2011,7 +2485,7 @@ int git_merge__setup(
int error = 0;
assert (repo && our_head && heads);
-
+
if ((error = write_orig_head(repo, our_head)) == 0 &&
(error = write_merge_head(repo, heads, heads_len)) == 0 &&
(error = write_merge_mode(repo, flags)) == 0) {
@@ -2054,6 +2528,41 @@ cleanup:
return error;
}
+/* Merge result data */
+
+int git_merge_result_is_uptodate(git_merge_result *merge_result)
+{
+ assert(merge_result);
+
+ return merge_result->is_uptodate;
+}
+
+int git_merge_result_is_fastforward(git_merge_result *merge_result)
+{
+ assert(merge_result);
+
+ return merge_result->is_fastforward;
+}
+
+int git_merge_result_fastforward_oid(git_oid *out, git_merge_result *merge_result)
+{
+ assert(out && merge_result);
+
+ git_oid_cpy(out, &merge_result->fastforward_oid);
+ return 0;
+}
+
+void git_merge_result_free(git_merge_result *merge_result)
+{
+ if (merge_result == NULL)
+ return;
+
+ git_index_free(merge_result->index);
+ merge_result->index = NULL;
+
+ git__free(merge_result);
+}
+
/* Merge heads are the input to merge */
static int merge_head_init(
@@ -2085,6 +2594,9 @@ static int merge_head_init(
git_oid_cpy(&head->oid, oid);
+ git_oid_fmt(head->oid_str, oid);
+ head->oid_str[GIT_OID_HEXSZ] = '\0';
+
if ((error = git_commit_lookup(&head->commit, repo, &head->oid)) < 0) {
git_merge_head_free(head);
return error;
diff --git a/src/merge.h b/src/merge.h
index ba6725de9..d7d1c67b7 100644
--- a/src/merge.h
+++ b/src/merge.h
@@ -16,6 +16,7 @@
#define GIT_MERGE_MSG_FILE "MERGE_MSG"
#define GIT_MERGE_MODE_FILE "MERGE_MODE"
+#define GIT_MERGE_FILE_MODE 0666
#define GIT_MERGE_TREE_RENAME_THRESHOLD 50
#define GIT_MERGE_TREE_TARGET_LIMIT 1000
@@ -113,9 +114,20 @@ struct git_merge_head {
char *remote_url;
git_oid oid;
+ char oid_str[GIT_OID_HEXSZ+1];
git_commit *commit;
};
+/** Internal structure for merge results */
+struct git_merge_result {
+ bool is_uptodate;
+
+ bool is_fastforward;
+ git_oid fastforward_oid;
+
+ git_index *index;
+};
+
int git_merge__bases_many(
git_commit_list **out,
git_revwalk *walk,
diff --git a/src/merge_file.c b/src/merge_file.c
index c3477ccb9..48fc46e57 100644
--- a/src/merge_file.c
+++ b/src/merge_file.c
@@ -47,7 +47,7 @@ GIT_INLINE(int) merge_file_best_mode(
* assume executable. Otherwise, if any mode changed from the ancestor,
* use that one.
*/
- if (GIT_MERGE_FILE_SIDE_EXISTS(ancestor)) {
+ if (!GIT_MERGE_FILE_SIDE_EXISTS(ancestor)) {
if (ours->mode == GIT_FILEMODE_BLOB_EXECUTABLE ||
theirs->mode == GIT_FILEMODE_BLOB_EXECUTABLE)
return GIT_FILEMODE_BLOB_EXECUTABLE;
diff --git a/src/netops.c b/src/netops.c
index 69179dd1c..ad27d84cf 100644
--- a/src/netops.c
+++ b/src/netops.c
@@ -19,10 +19,6 @@
# endif
#endif
-#ifdef __FreeBSD__
-# include <netinet/in.h>
-#endif
-
#ifdef GIT_SSL
# include <openssl/ssl.h>
# include <openssl/err.h>
@@ -36,6 +32,7 @@
#include "netops.h"
#include "posix.h"
#include "buffer.h"
+#include "http_parser.h"
#ifdef GIT_WIN32
static void net_set_error(const char *str)
@@ -577,55 +574,161 @@ int gitno_select_in(gitno_buffer *buf, long int sec, long int usec)
return select((int)buf->socket->socket + 1, &fds, NULL, NULL, &tv);
}
+static const char *prefix_http = "http://";
+static const char *prefix_https = "https://";
+
+int gitno_connection_data_from_url(
+ gitno_connection_data *data,
+ const char *url,
+ const char *service_suffix)
+{
+ int error = -1;
+ const char *default_port = NULL, *path_search_start = NULL;
+ char *original_host = NULL;
+
+ /* service_suffix is optional */
+ assert(data && url);
+
+ /* Save these for comparison later */
+ original_host = data->host;
+ data->host = NULL;
+ gitno_connection_data_free_ptrs(data);
+
+ if (!git__prefixcmp(url, prefix_http)) {
+ path_search_start = url + strlen(prefix_http);
+ default_port = "80";
+
+ if (data->use_ssl) {
+ giterr_set(GITERR_NET, "Redirect from HTTPS to HTTP is not allowed");
+ goto cleanup;
+ }
+ } else if (!git__prefixcmp(url, prefix_https)) {
+ path_search_start = url + strlen(prefix_https);
+ default_port = "443";
+ data->use_ssl = true;
+ } else if (url[0] == '/')
+ default_port = data->use_ssl ? "443" : "80";
+
+ if (!default_port) {
+ giterr_set(GITERR_NET, "Unrecognized URL prefix");
+ goto cleanup;
+ }
+
+ error = gitno_extract_url_parts(
+ &data->host, &data->port, &data->path, &data->user, &data->pass,
+ url, default_port);
+
+ if (url[0] == '/') {
+ /* Relative redirect; reuse original host name and port */
+ path_search_start = url;
+ git__free(data->host);
+ data->host = original_host;
+ original_host = NULL;
+ }
+
+ if (!error) {
+ const char *path = strchr(path_search_start, '/');
+ size_t pathlen = strlen(path);
+ size_t suffixlen = service_suffix ? strlen(service_suffix) : 0;
+
+ if (suffixlen &&
+ !memcmp(path + pathlen - suffixlen, service_suffix, suffixlen)) {
+ git__free(data->path);
+ data->path = git__strndup(path, pathlen - suffixlen);
+ } else {
+ git__free(data->path);
+ data->path = git__strdup(path);
+ }
+
+ /* Check for errors in the resulting data */
+ if (original_host && url[0] != '/' && strcmp(original_host, data->host)) {
+ giterr_set(GITERR_NET, "Cross host redirect not allowed");
+ error = -1;
+ }
+ }
+
+cleanup:
+ if (original_host) git__free(original_host);
+ return error;
+}
+
+void gitno_connection_data_free_ptrs(gitno_connection_data *d)
+{
+ git__free(d->host); d->host = NULL;
+ git__free(d->port); d->port = NULL;
+ git__free(d->path); d->path = NULL;
+ git__free(d->user); d->user = NULL;
+ 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 *colon, *slash, *at, *end;
- const char *start;
-
- /*
- *
- * ==> [user[:pass]@]hostname.tld[:port]/resource
- */
-
- colon = strchr(url, ':');
- slash = strchr(url, '/');
- at = strchr(url, '@');
+ struct http_parser_url u = {0};
+ const char *_host, *_port, *_path, *_userinfo;
- if (slash == NULL) {
- giterr_set(GITERR_NET, "Malformed URL: missing /");
- return -1;
+ if (http_parser_parse_url(url, strlen(url), false, &u)) {
+ giterr_set(GITERR_NET, "Malformed URL '%s'", url);
+ return GIT_EINVALIDSPEC;
}
- start = url;
- if (at && at < slash) {
- start = at+1;
- *username = git__substrdup(url, at - url);
- }
+ _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;
- if (colon && colon < at) {
- git__free(*username);
- *username = git__substrdup(url, colon-url);
- *password = git__substrdup(colon+1, at-colon-1);
- colon = strchr(at, ':');
+ if (u.field_set & (1 << UF_HOST)) {
+ *host = git__substrdup(_host, u.field_data[UF_HOST].len);
+ GITERR_CHECK_ALLOC(*host);
}
- if (colon == NULL) {
+ if (u.field_set & (1 << UF_PORT))
+ *port = git__substrdup(_port, u.field_data[UF_PORT].len);
+ else
*port = git__strdup(default_port);
- } else {
- *port = git__substrdup(colon + 1, slash - colon - 1);
- }
GITERR_CHECK_ALLOC(*port);
- end = colon == NULL ? slash : colon;
+ if (u.field_set & (1 << UF_PATH)) {
+ *path = git__substrdup(_path, u.field_data[UF_PATH].len);
+ GITERR_CHECK_ALLOC(*path);
+ }
- *host = git__substrdup(start, end - start);
- GITERR_CHECK_ALLOC(*host);
+ if (u.field_set & (1 << UF_USERINFO)) {
+ const char *colon = memchr(_userinfo, ':', u.field_data[UF_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);
+ } else {
+ *username = git__substrdup(_userinfo, u.field_data[UF_USERINFO].len);
+ }
+ GITERR_CHECK_ALLOC(*username);
+
+ }
return 0;
}
diff --git a/src/netops.h b/src/netops.h
index d352bf3b6..666d66b12 100644
--- a/src/netops.h
+++ b/src/netops.h
@@ -66,9 +66,33 @@ int gitno_send(gitno_socket *socket, const char *msg, size_t len, int flags);
int gitno_close(gitno_socket *s);
int gitno_select_in(gitno_buffer *buf, long int sec, long int usec);
+typedef struct gitno_connection_data {
+ char *host;
+ char *port;
+ char *path;
+ char *user;
+ char *pass;
+ bool use_ssl;
+} gitno_connection_data;
+
+/*
+ * This replaces all the pointers in `data` with freshly-allocated strings,
+ * that the caller is responsible for freeing.
+ * `gitno_connection_data_free_ptrs` is good for this.
+ */
+
+int gitno_connection_data_from_url(
+ gitno_connection_data *data,
+ const char *url,
+ const char *service_suffix);
+
+/* This frees all the pointers IN the struct, but not the struct itself. */
+void gitno_connection_data_free_ptrs(gitno_connection_data *data);
+
int gitno_extract_url_parts(
char **host,
char **port,
+ char **path,
char **username,
char **password,
const char *url,
diff --git a/src/object.c b/src/object.c
index 9b8ccdd3e..3fc984b45 100644
--- a/src/object.c
+++ b/src/object.c
@@ -364,3 +364,38 @@ int git_object_dup(git_object **dest, git_object *source)
*dest = source;
return 0;
}
+
+int git_object_lookup_bypath(
+ git_object **out,
+ const git_object *treeish,
+ const char *path,
+ git_otype type)
+{
+ int error = -1;
+ git_tree *tree = NULL;
+ git_tree_entry *entry = NULL;
+
+ assert(out && treeish && path);
+
+ if ((error = git_object_peel((git_object**)&tree, treeish, GIT_OBJ_TREE) < 0) ||
+ (error = git_tree_entry_bypath(&entry, tree, path)) < 0)
+ {
+ goto cleanup;
+ }
+
+ if (type != GIT_OBJ_ANY && git_tree_entry_type(entry) != type)
+ {
+ giterr_set(GITERR_OBJECT,
+ "object at path '%s' is not of the asked-for type %d",
+ path, type);
+ error = GIT_EINVALIDSPEC;
+ goto cleanup;
+ }
+
+ error = git_tree_entry_to_object(out, git_object_owner(treeish), entry);
+
+cleanup:
+ git_tree_entry_free(entry);
+ git_tree_free(tree);
+ return error;
+}
diff --git a/src/odb.c b/src/odb.c
index 8e62efd00..b208b279e 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -124,6 +124,13 @@ git_otype git_odb_object_type(git_odb_object *object)
return object->cached.type;
}
+int git_odb_object_dup(git_odb_object **dest, git_odb_object *source)
+{
+ git_cached_obj_incref(source);
+ *dest = source;
+ return 0;
+}
+
void git_odb_object_free(git_odb_object *object)
{
if (object == NULL)
@@ -168,7 +175,6 @@ int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type)
error = -1;
goto done;
- return -1;
}
error = git_hash_final(out, &ctx);
@@ -179,28 +185,30 @@ done:
}
int git_odb__hashfd_filtered(
- git_oid *out, git_file fd, size_t size, git_otype type, git_vector *filters)
+ git_oid *out, git_file fd, size_t size, git_otype type, git_filter_list *fl)
{
int error;
git_buf raw = GIT_BUF_INIT;
- git_buf filtered = GIT_BUF_INIT;
- if (!filters || !filters->length)
+ if (!fl)
return git_odb__hashfd(out, fd, size, type);
/* size of data is used in header, so we have to read the whole file
* into memory to apply filters before beginning to calculate the hash
*/
- if (!(error = git_futils_readbuffer_fd(&raw, fd, size)))
- error = git_filters_apply(&filtered, &raw, filters);
+ if (!(error = git_futils_readbuffer_fd(&raw, fd, size))) {
+ git_buf post = GIT_BUF_INIT;
+
+ error = git_filter_list_apply_to_data(&post, fl, &raw);
- git_buf_free(&raw);
+ git_buf_free(&raw);
- if (!error)
- error = git_odb_hash(out, filtered.ptr, filtered.size, type);
+ if (!error)
+ error = git_odb_hash(out, post.ptr, post.size, type);
- git_buf_free(&filtered);
+ git_buf_free(&post);
+ }
return error;
}
@@ -232,6 +240,7 @@ int git_odb__hashlink(git_oid *out, const char *path)
link_data[size] = '\0';
if (read_len != (ssize_t)size) {
giterr_set(GITERR_OS, "Failed to read symlink data for '%s'", path);
+ git__free(link_data);
return -1;
}
@@ -290,10 +299,10 @@ typedef struct {
git_otype type;
} fake_wstream;
-static int fake_wstream__fwrite(git_oid *oid, git_odb_stream *_stream)
+static int fake_wstream__fwrite(git_odb_stream *_stream, const git_oid *oid)
{
fake_wstream *stream = (fake_wstream *)_stream;
- return _stream->backend->write(oid, _stream->backend, stream->buffer, stream->size, stream->type);
+ return _stream->backend->write(_stream->backend, oid, stream->buffer, stream->size, stream->type);
}
static int fake_wstream__write(git_odb_stream *_stream, const char *data, size_t len)
@@ -444,7 +453,7 @@ int git_odb_get_backend(git_odb_backend **out, git_odb *odb, size_t pos)
return 0;
}
- giterr_set(GITERR_ODB, "No ODB backend loaded at index " PRIuZ, pos);
+ giterr_set(GITERR_ODB, "No ODB backend loaded at index %" PRIuZ, pos);
return GIT_ENOTFOUND;
}
@@ -483,7 +492,7 @@ static int add_default_backends(
#endif
/* add the loose object backend */
- if (git_odb_backend_loose(&loose, objects_dir, -1, 0) < 0 ||
+ if (git_odb_backend_loose(&loose, objects_dir, -1, 0, 0, 0) < 0 ||
add_backend_internal(db, loose, GIT_LOOSE_PRIORITY, as_alternates, inode) < 0)
return -1;
@@ -607,7 +616,6 @@ int git_odb_exists(git_odb *db, const git_oid *id)
git_odb_object *object;
size_t i;
bool found = false;
- bool refreshed = false;
assert(db && id);
@@ -616,23 +624,12 @@ int git_odb_exists(git_odb *db, const git_oid *id)
return (int)true;
}
-attempt_lookup:
for (i = 0; i < db->backends.length && !found; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
if (b->exists != NULL)
- found = b->exists(b, id);
- }
-
- if (!found && !refreshed) {
- if (git_odb_refresh(db) < 0) {
- giterr_clear();
- return (int)false;
- }
-
- refreshed = true;
- goto attempt_lookup;
+ found = (bool)b->exists(b, id);
}
return (int)found;
@@ -699,7 +696,6 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
{
size_t i, reads = 0;
int error;
- bool refreshed = false;
git_rawobj raw;
git_odb_object *object;
@@ -709,7 +705,6 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
if (*out != NULL)
return 0;
-attempt_lookup:
error = GIT_ENOTFOUND;
for (i = 0; i < db->backends.length && error < 0; ++i) {
@@ -722,14 +717,6 @@ attempt_lookup:
}
}
- if (error == GIT_ENOTFOUND && !refreshed) {
- if ((error = git_odb_refresh(db)) < 0)
- return error;
-
- refreshed = true;
- goto attempt_lookup;
- }
-
if (error && error != GIT_PASSTHROUGH) {
if (!reads)
return git_odb__error_notfound("no match for id", id);
@@ -751,7 +738,7 @@ int git_odb_read_prefix(
git_oid found_full_oid = {{0}};
git_rawobj raw;
void *data = NULL;
- bool found = false, refreshed = false;
+ bool found = false;
git_odb_object *object;
assert(out && db);
@@ -768,7 +755,6 @@ int git_odb_read_prefix(
return 0;
}
-attempt_lookup:
for (i = 0; i < db->backends.length; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
@@ -785,22 +771,16 @@ attempt_lookup:
git__free(data);
data = raw.data;
- if (found && git_oid__cmp(&full_oid, &found_full_oid))
+ if (found && git_oid__cmp(&full_oid, &found_full_oid)) {
+ git__free(raw.data);
return git_odb__error_ambiguous("multiple matches for prefix");
+ }
found_full_oid = full_oid;
found = true;
}
}
- if (!found && !refreshed) {
- if ((error = git_odb_refresh(db)) < 0)
- return error;
-
- refreshed = true;
- goto attempt_lookup;
- }
-
if (!found)
return git_odb__error_notfound("no match for prefix", short_id);
@@ -848,7 +828,7 @@ int git_odb_write(
continue;
if (b->write != NULL)
- error = b->write(oid, b, data, len, type);
+ error = b->write(b, oid, data, len, type);
}
if (!error || error == GIT_PASSTHROUGH)
@@ -862,17 +842,27 @@ int git_odb_write(
return error;
stream->write(stream, data, len);
- error = stream->finalize_write(oid, stream);
- stream->free(stream);
+ error = stream->finalize_write(stream, oid);
+ git_odb_stream_free(stream);
return error;
}
+static void hash_header(git_hash_ctx *ctx, size_t size, git_otype type)
+{
+ char header[64];
+ int hdrlen;
+
+ hdrlen = git_odb__format_object_header(header, sizeof(header), size, type);
+ git_hash_update(ctx, header, hdrlen);
+}
+
int git_odb_open_wstream(
git_odb_stream **stream, git_odb *db, size_t size, git_otype type)
{
size_t i, writes = 0;
int error = GIT_ERROR;
+ git_hash_ctx *ctx;
assert(stream && db);
@@ -898,9 +888,71 @@ int git_odb_open_wstream(
if (error < 0 && !writes)
error = git_odb__error_unsupported_in_backend("write object");
+ ctx = git__malloc(sizeof(git_hash_ctx));
+ GITERR_CHECK_ALLOC(ctx);
+
+
+ git_hash_ctx_init(ctx);
+ hash_header(ctx, size, type);
+ (*stream)->hash_ctx = ctx;
+
+ (*stream)->declared_size = size;
+ (*stream)->received_bytes = 0;
+
return error;
}
+static int git_odb_stream__invalid_length(
+ const git_odb_stream *stream,
+ const char *action)
+{
+ giterr_set(GITERR_ODB,
+ "Cannot %s - "
+ "Invalid length. %"PRIuZ" was expected. The "
+ "total size of the received chunks amounts to %"PRIuZ".",
+ action, stream->declared_size, stream->received_bytes);
+
+ return -1;
+}
+
+int git_odb_stream_write(git_odb_stream *stream, const char *buffer, size_t len)
+{
+ git_hash_update(stream->hash_ctx, buffer, len);
+
+ stream->received_bytes += len;
+
+ if (stream->received_bytes > stream->declared_size)
+ return git_odb_stream__invalid_length(stream,
+ "stream_write()");
+
+ return stream->write(stream, buffer, len);
+}
+
+int git_odb_stream_finalize_write(git_oid *out, git_odb_stream *stream)
+{
+ if (stream->received_bytes != stream->declared_size)
+ return git_odb_stream__invalid_length(stream,
+ "stream_finalize_write()");
+
+ git_hash_final(out, stream->hash_ctx);
+
+ if (git_odb_exists(stream->backend->odb, out))
+ return 0;
+
+ return stream->finalize_write(stream, out);
+}
+
+int git_odb_stream_read(git_odb_stream *stream, char *buffer, size_t len)
+{
+ return stream->read(stream, buffer, len);
+}
+
+void git_odb_stream_free(git_odb_stream *stream)
+{
+ git__free(stream->hash_ctx);
+ stream->free(stream);
+}
+
int git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oid)
{
size_t i, reads = 0;
@@ -943,7 +995,7 @@ int git_odb_write_pack(struct git_odb_writepack **out, git_odb *db, git_transfer
if (b->writepack != NULL) {
++writes;
- error = b->writepack(out, b, progress_cb, progress_payload);
+ error = b->writepack(out, b, db, progress_cb, progress_payload);
}
}
diff --git a/src/odb.h b/src/odb.h
index 0d9f9e2ea..61dd9a7fd 100644
--- a/src/odb.h
+++ b/src/odb.h
@@ -14,6 +14,7 @@
#include "vector.h"
#include "cache.h"
#include "posix.h"
+#include "filter.h"
#define GIT_OBJECTS_DIR "objects/"
#define GIT_OBJECT_DIR_MODE 0777
@@ -66,7 +67,7 @@ int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type);
* Acts just like git_odb__hashfd with the addition of filters...
*/
int git_odb__hashfd_filtered(
- git_oid *out, git_file fd, size_t len, git_otype type, git_vector *filters);
+ git_oid *out, git_file fd, size_t len, git_otype type, git_filter_list *fl);
/*
* Hash a `path`, assuming it could be a POSIX symlink: if the path is a
diff --git a/src/odb_loose.c b/src/odb_loose.c
index 76ed8e232..ced272b33 100644
--- a/src/odb_loose.c
+++ b/src/odb_loose.c
@@ -33,6 +33,8 @@ typedef struct loose_backend {
int object_zlib_level; /** loose object zlib compression level. */
int fsync_object_files; /** loose object file fsync flag. */
+ mode_t object_file_mode;
+ mode_t object_dir_mode;
size_t objects_dirlen;
char objects_dir[GIT_FLEX_ARRAY];
@@ -79,7 +81,7 @@ static int object_file_name(
static int object_mkdir(const git_buf *name, const loose_backend *be)
{
return git_futils_mkdir(
- name->ptr + be->objects_dirlen, be->objects_dir, GIT_OBJECT_DIR_MODE,
+ name->ptr + be->objects_dirlen, be->objects_dir, be->object_dir_mode,
GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR);
}
@@ -499,7 +501,7 @@ static int fn_locate_object_short_oid(void *state, git_buf *pathbuf) {
}
if (sstate->found > 1)
- return git_odb__error_ambiguous("multiple matches in loose objects");
+ return GIT_EAMBIGUOUS;
return 0;
}
@@ -544,13 +546,17 @@ static int locate_object_short_oid(
/* Explore directory to find a unique object matching short_oid */
error = git_path_direach(
- object_location, fn_locate_object_short_oid, &state);
- if (error)
+ object_location, 0, fn_locate_object_short_oid, &state);
+
+ if (error && error != GIT_EUSER)
return error;
if (!state.found)
return git_odb__error_notfound("no matching loose object for prefix", short_oid);
+ if (state.found > 1)
+ return git_odb__error_ambiguous("multiple matches in loose objects");
+
/* Convert obtained hex formatted oid to raw */
error = git_oid_fromstr(res_oid, (char *)state.res_oid);
if (error)
@@ -641,10 +647,12 @@ static int loose_backend__read_prefix(
{
int error = 0;
+ assert(len <= GIT_OID_HEXSZ);
+
if (len < GIT_OID_MINPREFIXLEN)
error = git_odb__error_ambiguous("prefix length too short");
- else if (len >= GIT_OID_HEXSZ) {
+ else if (len == GIT_OID_HEXSZ) {
/* We can fall back to regular read method */
error = loose_backend__read(buffer_p, len_p, type_p, backend, short_oid);
if (!error)
@@ -739,7 +747,7 @@ static int foreach_cb(void *_state, git_buf *path)
{
struct foreach_state *state = (struct foreach_state *) _state;
- return git_path_direach(path, foreach_object_dir_cb, state);
+ return git_path_direach(path, 0, foreach_object_dir_cb, state);
}
static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb cb, void *data)
@@ -762,34 +770,26 @@ static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb
state.data = data;
state.dir_len = git_buf_len(&buf);
- error = git_path_direach(&buf, foreach_cb, &state);
+ error = git_path_direach(&buf, 0, foreach_cb, &state);
git_buf_free(&buf);
return state.cb_error ? state.cb_error : error;
}
-static int loose_backend__stream_fwrite(git_oid *oid, git_odb_stream *_stream)
+static int loose_backend__stream_fwrite(git_odb_stream *_stream, const git_oid *oid)
{
loose_writestream *stream = (loose_writestream *)_stream;
loose_backend *backend = (loose_backend *)_stream->backend;
git_buf final_path = GIT_BUF_INIT;
int error = 0;
- if (git_filebuf_hash(oid, &stream->fbuf) < 0 ||
- object_file_name(&final_path, backend, oid) < 0 ||
+ if (object_file_name(&final_path, backend, oid) < 0 ||
object_mkdir(&final_path, backend) < 0)
error = -1;
- /*
- * Don't try to add an existing object to the repository. This
- * is what git does and allows us to sidestep the fact that
- * we're not allowed to overwrite a read-only file on Windows.
- */
- else if (git_path_exists(final_path.ptr) == true)
- git_filebuf_cleanup(&stream->fbuf);
else
error = git_filebuf_commit_at(
- &stream->fbuf, final_path.ptr, GIT_OBJECT_FILE_MODE);
+ &stream->fbuf, final_path.ptr);
git_buf_free(&final_path);
@@ -810,17 +810,6 @@ static void loose_backend__stream_free(git_odb_stream *_stream)
git__free(stream);
}
-static int format_object_header(char *hdr, size_t n, size_t obj_len, git_otype obj_type)
-{
- const char *type_str = git_object_type2string(obj_type);
- int len = snprintf(hdr, n, "%s %"PRIuZ, type_str, obj_len);
-
- assert(len > 0); /* otherwise snprintf() is broken */
- assert(((size_t)len) < n); /* otherwise the caller is broken! */
-
- return len+1;
-}
-
static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_backend, size_t length, git_otype type)
{
loose_backend *backend;
@@ -834,7 +823,7 @@ static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_
backend = (loose_backend *)_backend;
*stream_out = NULL;
- hdrlen = format_object_header(hdr, sizeof(hdr), length, type);
+ hdrlen = git_odb__format_object_header(hdr, sizeof(hdr), length, type);
stream = git__calloc(1, sizeof(loose_writestream));
GITERR_CHECK_ALLOC(stream);
@@ -848,9 +837,9 @@ static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_
if (git_buf_joinpath(&tmp_path, backend->objects_dir, "tmp_object") < 0 ||
git_filebuf_open(&stream->fbuf, tmp_path.ptr,
- GIT_FILEBUF_HASH_CONTENTS |
GIT_FILEBUF_TEMPORARY |
- (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT)) < 0 ||
+ (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT),
+ backend->object_file_mode) < 0 ||
stream->stream.write((git_odb_stream *)stream, hdr, hdrlen) < 0)
{
git_filebuf_cleanup(&stream->fbuf);
@@ -863,7 +852,7 @@ static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_
return !stream ? -1 : 0;
}
-static int loose_backend__write(git_oid *oid, git_odb_backend *_backend, const void *data, size_t len, git_otype type)
+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;
git_buf final_path = GIT_BUF_INIT;
@@ -874,12 +863,13 @@ static int loose_backend__write(git_oid *oid, git_odb_backend *_backend, const v
backend = (loose_backend *)_backend;
/* prepare the header for the file */
- header_len = format_object_header(header, sizeof(header), len, type);
+ header_len = git_odb__format_object_header(header, sizeof(header), len, type);
if (git_buf_joinpath(&final_path, backend->objects_dir, "tmp_object") < 0 ||
git_filebuf_open(&fbuf, final_path.ptr,
GIT_FILEBUF_TEMPORARY |
- (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT)) < 0)
+ (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT),
+ backend->object_file_mode) < 0)
{
error = -1;
goto cleanup;
@@ -890,7 +880,7 @@ static int loose_backend__write(git_oid *oid, git_odb_backend *_backend, const v
if (object_file_name(&final_path, backend, oid) < 0 ||
object_mkdir(&final_path, backend) < 0 ||
- git_filebuf_commit_at(&fbuf, final_path.ptr, GIT_OBJECT_FILE_MODE) < 0)
+ git_filebuf_commit_at(&fbuf, final_path.ptr) < 0)
error = -1;
cleanup:
@@ -913,7 +903,9 @@ int git_odb_backend_loose(
git_odb_backend **backend_out,
const char *objects_dir,
int compression_level,
- int do_fsync)
+ int do_fsync,
+ unsigned int dir_mode,
+ unsigned int file_mode)
{
loose_backend *backend;
size_t objects_dirlen;
@@ -934,8 +926,16 @@ int git_odb_backend_loose(
if (compression_level < 0)
compression_level = Z_BEST_SPEED;
+ if (dir_mode == 0)
+ dir_mode = GIT_OBJECT_DIR_MODE;
+
+ if (file_mode == 0)
+ file_mode = GIT_OBJECT_FILE_MODE;
+
backend->object_zlib_level = compression_level;
backend->fsync_object_files = do_fsync;
+ backend->object_dir_mode = dir_mode;
+ backend->object_file_mode = file_mode;
backend->parent.read = &loose_backend__read;
backend->parent.write = &loose_backend__write;
diff --git a/src/odb_pack.c b/src/odb_pack.c
index eec79259b..fd2ca0fd8 100644
--- a/src/odb_pack.c
+++ b/src/odb_pack.c
@@ -29,7 +29,7 @@ struct pack_backend {
struct pack_writepack {
struct git_odb_writepack parent;
- git_indexer_stream *indexer_stream;
+ git_indexer *indexer;
};
/**
@@ -259,23 +259,26 @@ static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backen
return git_odb__error_notfound("failed to find pack entry", oid);
}
-static unsigned pack_entry_find_prefix_inner(
- struct git_pack_entry *e,
- struct pack_backend *backend,
- const git_oid *short_oid,
- size_t len,
- struct git_pack_file *last_found)
+static int pack_entry_find_prefix(
+ struct git_pack_entry *e,
+ struct pack_backend *backend,
+ const git_oid *short_oid,
+ size_t len)
{
int error;
size_t i;
- unsigned found = 0;
+ git_oid found_full_oid = {{0}};
+ bool found = false;
+ struct git_pack_file *last_found = backend->last_found;
if (last_found) {
error = git_pack_entry_find(e, last_found, short_oid, len);
if (error == GIT_EAMBIGUOUS)
return error;
- if (!error)
- found = 1;
+ if (!error) {
+ git_oid_cpy(&found_full_oid, &e->sha1);
+ found = true;
+ }
}
for (i = 0; i < backend->packs.length; ++i) {
@@ -289,28 +292,16 @@ static unsigned pack_entry_find_prefix_inner(
if (error == GIT_EAMBIGUOUS)
return error;
if (!error) {
- if (++found > 1)
- break;
+ if (found && git_oid_cmp(&e->sha1, &found_full_oid))
+ return git_odb__error_ambiguous("found multiple pack entries");
+ git_oid_cpy(&found_full_oid, &e->sha1);
+ found = true;
backend->last_found = p;
}
}
- return found;
-}
-
-static int pack_entry_find_prefix(
- struct git_pack_entry *e,
- struct pack_backend *backend,
- const git_oid *short_oid,
- size_t len)
-{
- struct git_pack_file *last_found = backend->last_found;
- unsigned int found = pack_entry_find_prefix_inner(e, backend, short_oid, len, last_found);
-
if (!found)
return git_odb__error_notfound("no matching pack entry for prefix", short_oid);
- else if (found > 1)
- return git_odb__error_ambiguous("found multiple pack entries");
else
return 0;
}
@@ -340,19 +331,20 @@ static int pack_backend__refresh(git_odb_backend *_backend)
git_buf_sets(&path, backend->pack_folder);
/* reload all packs */
- error = git_path_direach(&path, packfile_load__cb, (void *)backend);
+ error = git_path_direach(&path, 0, packfile_load__cb, backend);
git_buf_free(&path);
if (error < 0)
- return error;
+ return -1;
git_vector_sort(&backend->packs);
return 0;
}
-
-static int pack_backend__read_header(size_t *len_p, git_otype *type_p, struct git_odb_backend *backend, const git_oid *oid)
+static int pack_backend__read_header_internal(
+ size_t *len_p, git_otype *type_p,
+ struct git_odb_backend *backend, const git_oid *oid)
{
struct git_pack_entry e;
int error;
@@ -365,7 +357,26 @@ static int pack_backend__read_header(size_t *len_p, git_otype *type_p, struct gi
return git_packfile_resolve_header(len_p, type_p, e.p, e.offset);
}
-static int pack_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid)
+static int pack_backend__read_header(
+ size_t *len_p, git_otype *type_p,
+ struct git_odb_backend *backend, const git_oid *oid)
+{
+ int error;
+
+ error = pack_backend__read_header_internal(len_p, type_p, backend, oid);
+
+ if (error != GIT_ENOTFOUND)
+ return error;
+
+ if ((error = pack_backend__refresh(backend)) < 0)
+ return error;
+
+ return pack_backend__read_header_internal(len_p, type_p, backend, oid);
+}
+
+static int pack_backend__read_internal(
+ void **buffer_p, size_t *len_p, git_otype *type_p,
+ git_odb_backend *backend, const git_oid *oid)
{
struct git_pack_entry e;
git_rawobj raw;
@@ -382,7 +393,24 @@ static int pack_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p,
return 0;
}
-static int pack_backend__read_prefix(
+static int pack_backend__read(
+ void **buffer_p, size_t *len_p, git_otype *type_p,
+ git_odb_backend *backend, const git_oid *oid)
+{
+ int error;
+
+ error = pack_backend__read_internal(buffer_p, len_p, type_p, backend, oid);
+
+ if (error != GIT_ENOTFOUND)
+ return error;
+
+ if ((error = pack_backend__refresh(backend)) < 0)
+ return error;
+
+ return pack_backend__read_internal(buffer_p, len_p, type_p, backend, oid);
+}
+
+static int pack_backend__read_prefix_internal(
git_oid *out_oid,
void **buffer_p,
size_t *len_p,
@@ -419,9 +447,45 @@ static int pack_backend__read_prefix(
return error;
}
+static int pack_backend__read_prefix(
+ git_oid *out_oid,
+ void **buffer_p,
+ size_t *len_p,
+ git_otype *type_p,
+ git_odb_backend *backend,
+ const git_oid *short_oid,
+ size_t len)
+{
+ int error;
+
+ error = pack_backend__read_prefix_internal(
+ out_oid, buffer_p, len_p, type_p, backend, short_oid, len);
+
+ if (error != GIT_ENOTFOUND)
+ return error;
+
+ if ((error = pack_backend__refresh(backend)) < 0)
+ return error;
+
+ return pack_backend__read_prefix_internal(
+ out_oid, buffer_p, len_p, type_p, backend, short_oid, len);
+}
+
static int pack_backend__exists(git_odb_backend *backend, const git_oid *oid)
{
struct git_pack_entry e;
+ int error;
+
+ error = pack_entry_find(&e, (struct pack_backend *)backend, oid);
+
+ if (error != GIT_ENOTFOUND)
+ return error == 0;
+
+ if ((error = pack_backend__refresh(backend)) < 0) {
+ giterr_clear();
+ return (int)false;
+ }
+
return pack_entry_find(&e, (struct pack_backend *)backend, oid) == 0;
}
@@ -447,13 +511,13 @@ static int pack_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb c
return 0;
}
-static int pack_backend__writepack_add(struct git_odb_writepack *_writepack, const void *data, size_t size, git_transfer_progress *stats)
+static int pack_backend__writepack_append(struct git_odb_writepack *_writepack, const void *data, size_t size, git_transfer_progress *stats)
{
struct pack_writepack *writepack = (struct pack_writepack *)_writepack;
assert(writepack);
- return git_indexer_stream_add(writepack->indexer_stream, data, size, stats);
+ return git_indexer_append(writepack->indexer, data, size, stats);
}
static int pack_backend__writepack_commit(struct git_odb_writepack *_writepack, git_transfer_progress *stats)
@@ -462,7 +526,7 @@ static int pack_backend__writepack_commit(struct git_odb_writepack *_writepack,
assert(writepack);
- return git_indexer_stream_finalize(writepack->indexer_stream, stats);
+ return git_indexer_commit(writepack->indexer, stats);
}
static void pack_backend__writepack_free(struct git_odb_writepack *_writepack)
@@ -471,12 +535,13 @@ static void pack_backend__writepack_free(struct git_odb_writepack *_writepack)
assert(writepack);
- git_indexer_stream_free(writepack->indexer_stream);
+ git_indexer_free(writepack->indexer);
git__free(writepack);
}
static int pack_backend__writepack(struct git_odb_writepack **out,
git_odb_backend *_backend,
+ git_odb *odb,
git_transfer_progress_callback progress_cb,
void *progress_payload)
{
@@ -492,14 +557,14 @@ static int pack_backend__writepack(struct git_odb_writepack **out,
writepack = git__calloc(1, sizeof(struct pack_writepack));
GITERR_CHECK_ALLOC(writepack);
- if (git_indexer_stream_new(&writepack->indexer_stream,
- backend->pack_folder, progress_cb, progress_payload) < 0) {
+ if (git_indexer_new(&writepack->indexer,
+ backend->pack_folder, 0, odb, progress_cb, progress_payload) < 0) {
git__free(writepack);
return -1;
}
writepack->parent.backend = _backend;
- writepack->parent.add = pack_backend__writepack_add;
+ writepack->parent.append = pack_backend__writepack_append;
writepack->parent.commit = pack_backend__writepack_commit;
writepack->parent.free = pack_backend__writepack_free;
diff --git a/src/oid.c b/src/oid.c
index 8300e46c1..d56b6af24 100644
--- a/src/oid.c
+++ b/src/oid.c
@@ -211,7 +211,7 @@ int git_oid_strcmp(const git_oid *oid_a, const char *str)
for (a = oid_a->id; *str && (a - oid_a->id) < GIT_OID_RAWSZ; ++a) {
if ((hexval = git__fromhex(*str++)) < 0)
return -1;
- strval = hexval << 4;
+ strval = (unsigned char)(hexval << 4);
if (*str) {
if ((hexval = git__fromhex(*str++)) < 0)
return -1;
@@ -369,8 +369,10 @@ int git_oid_shorten_add(git_oid_shorten *os, const char *text_oid)
bool is_leaf;
node_index idx;
- if (os->full)
+ if (os->full) {
+ giterr_set(GITERR_INVALID, "Unable to shorten OID - OID set full");
return -1;
+ }
if (text_oid == NULL)
return os->min_length;
@@ -396,12 +398,19 @@ int git_oid_shorten_add(git_oid_shorten *os, const char *text_oid)
node->tail = NULL;
node = push_leaf(os, idx, git__fromhex(tail[0]), &tail[1]);
- GITERR_CHECK_ALLOC(node);
+ if (node == NULL) {
+ if (os->full)
+ giterr_set(GITERR_INVALID, "Unable to shorten OID - OID set full");
+ return -1;
+ }
}
if (node->children[c] == 0) {
- if (push_leaf(os, idx, c, &text_oid[i + 1]) == NULL)
+ if (push_leaf(os, idx, c, &text_oid[i + 1]) == NULL) {
+ if (os->full)
+ giterr_set(GITERR_INVALID, "Unable to shorten OID - OID set full");
return -1;
+ }
break;
}
diff --git a/src/oid.h b/src/oid.h
index 077d0a4c8..cfe7ca1b2 100644
--- a/src/oid.h
+++ b/src/oid.h
@@ -9,17 +9,8 @@
#include "git2/oid.h"
-/*
- * Compare two oid structures.
- *
- * @param a first oid structure.
- * @param b second oid structure.
- * @return <0, 0, >0 if a < b, a == b, a > b.
- */
-GIT_INLINE(int) git_oid__cmp(const git_oid *a, const git_oid *b)
+GIT_INLINE(int) git_oid__hashcmp(const unsigned char *sha1, const unsigned char *sha2)
{
- const unsigned char *sha1 = a->id;
- const unsigned char *sha2 = b->id;
int i;
for (i = 0; i < GIT_OID_RAWSZ; i++, sha1++, sha2++) {
@@ -30,4 +21,16 @@ GIT_INLINE(int) git_oid__cmp(const git_oid *a, const git_oid *b)
return 0;
}
+/*
+ * Compare two oid structures.
+ *
+ * @param a first oid structure.
+ * @param b second oid structure.
+ * @return <0, 0, >0 if a < b, a == b, a > b.
+ */
+GIT_INLINE(int) git_oid__cmp(const git_oid *a, const git_oid *b)
+{
+ return git_oid__hashcmp(a->id, b->id);
+}
+
#endif
diff --git a/src/pack-objects.c b/src/pack-objects.c
index 500104c55..2d62507f2 100644
--- a/src/pack-objects.c
+++ b/src/pack-objects.c
@@ -14,6 +14,7 @@
#include "pack.h"
#include "thread-utils.h"
#include "tree.h"
+#include "util.h"
#include "git2/pack.h"
#include "git2/commit.h"
@@ -25,7 +26,7 @@ struct unpacked {
git_pobject *object;
void *data;
struct git_delta_index *index;
- unsigned int depth;
+ int depth;
};
struct tree_walk_context {
@@ -34,7 +35,7 @@ struct tree_walk_context {
};
struct pack_write_context {
- git_indexer_stream *indexer;
+ git_indexer *indexer;
git_transfer_progress *stats;
};
@@ -57,6 +58,9 @@ struct pack_write_context {
#define git_packbuilder__progress_lock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, progress_mutex, lock)
#define git_packbuilder__progress_unlock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, progress_mutex, unlock)
+/* The minimal interval between progress updates (in seconds). */
+#define MIN_PROGRESS_UPDATE_INTERVAL 0.5
+
static unsigned name_hash(const char *name)
{
unsigned c, hash = 0;
@@ -213,41 +217,19 @@ int git_packbuilder_insert(git_packbuilder *pb, const git_oid *oid,
kh_value(pb->object_ix, pos) = po;
pb->done = false;
- return 0;
-}
-
-/*
- * The per-object header is a pretty dense thing, which is
- * - first byte: low four bits are "size",
- * then three bits of "type",
- * with the high bit being "size continues".
- * - each byte afterwards: low seven bits are size continuation,
- * with the high bit being "size continues"
- */
-static int gen_pack_object_header(
- unsigned char *hdr,
- unsigned long size,
- git_otype type)
-{
- unsigned char *hdr_base;
- unsigned char c;
-
- assert(type >= GIT_OBJ_COMMIT && type <= GIT_OBJ_REF_DELTA);
-
- /* TODO: add support for chunked objects; see git.git 6c0d19b1 */
-
- c = (unsigned char)((type << 4) | (size & 15));
- size >>= 4;
- hdr_base = hdr;
- while (size) {
- *hdr++ = c | 0x80;
- c = size & 0x7f;
- size >>= 7;
+ if (pb->progress_cb) {
+ double current_time = git__timer();
+ if ((current_time - pb->last_progress_report_time) >= MIN_PROGRESS_UPDATE_INTERVAL) {
+ pb->last_progress_report_time = current_time;
+ if (pb->progress_cb(GIT_PACKBUILDER_ADDING_OBJECTS, pb->nr_objects, 0, pb->progress_cb_payload)) {
+ giterr_clear();
+ return GIT_EUSER;
+ }
+ }
}
- *hdr++ = c;
- return (int)(hdr - hdr_base);
+ return 0;
}
static int get_delta(void **out, git_odb *odb, git_pobject *po)
@@ -290,7 +272,7 @@ static int write_object(git_buf *buf, git_packbuilder *pb, git_pobject *po)
git_buf zbuf = GIT_BUF_INIT;
git_otype type;
unsigned char hdr[10];
- unsigned int hdr_len;
+ size_t hdr_len;
unsigned long size;
void *data;
@@ -311,7 +293,7 @@ static int write_object(git_buf *buf, git_packbuilder *pb, git_pobject *po)
}
/* Write header */
- hdr_len = gen_pack_object_header(hdr, size, type);
+ hdr_len = git_packfile__object_header(hdr, size, type);
if (git_buf_put(buf, (char *)hdr, hdr_len) < 0)
goto on_error;
@@ -505,8 +487,10 @@ static git_pobject **compute_write_order(git_packbuilder *pb)
/*
* Mark objects that are at the tip of tags.
*/
- if (git_tag_foreach(pb->repo, &cb_tag_foreach, pb) < 0)
+ if (git_tag_foreach(pb->repo, &cb_tag_foreach, pb) < 0) {
+ git__free(wo);
return NULL;
+ }
/*
* Give the objects in the original recency order until
@@ -576,50 +560,52 @@ static int write_pack(git_packbuilder *pb,
git_buf buf = GIT_BUF_INIT;
enum write_one_status status;
struct git_pack_header ph;
+ git_oid entry_oid;
unsigned int i = 0;
+ int error = 0;
write_order = compute_write_order(pb);
- if (write_order == NULL)
- goto on_error;
+ if (write_order == NULL) {
+ error = -1;
+ goto done;
+ }
/* Write pack header */
ph.hdr_signature = htonl(PACK_SIGNATURE);
ph.hdr_version = htonl(PACK_VERSION);
ph.hdr_entries = htonl(pb->nr_objects);
- if (cb(&ph, sizeof(ph), data) < 0)
- goto on_error;
+ if ((error = cb(&ph, sizeof(ph), data)) < 0)
+ goto done;
- if (git_hash_update(&pb->ctx, &ph, sizeof(ph)) < 0)
- goto on_error;
+ if ((error = git_hash_update(&pb->ctx, &ph, sizeof(ph))) < 0)
+ goto done;
pb->nr_remaining = pb->nr_objects;
do {
pb->nr_written = 0;
for ( ; i < pb->nr_objects; ++i) {
po = write_order[i];
- if (write_one(&buf, pb, po, &status) < 0)
- goto on_error;
- if (cb(buf.ptr, buf.size, data) < 0)
- goto on_error;
+ if ((error = write_one(&buf, pb, po, &status)) < 0)
+ goto done;
+ if ((error = cb(buf.ptr, buf.size, data)) < 0)
+ goto done;
git_buf_clear(&buf);
}
pb->nr_remaining -= pb->nr_written;
} while (pb->nr_remaining && i < pb->nr_objects);
- git__free(write_order);
- git_buf_free(&buf);
- if (git_hash_final(&pb->pack_oid, &pb->ctx) < 0)
- goto on_error;
+ if ((error = git_hash_final(&entry_oid, &pb->ctx)) < 0)
+ goto done;
- return cb(pb->pack_oid.id, GIT_OID_RAWSZ, data);
+ error = cb(entry_oid.id, GIT_OID_RAWSZ, data);
-on_error:
+done:
git__free(write_order);
git_buf_free(&buf);
- return -1;
+ return error;
}
static int write_pack_buf(void *buf, size_t size, void *data)
@@ -674,7 +660,7 @@ static int delta_cacheable(git_packbuilder *pb, unsigned long src_size,
}
static int try_delta(git_packbuilder *pb, struct unpacked *trg,
- struct unpacked *src, unsigned int max_depth,
+ struct unpacked *src, int max_depth,
unsigned long *mem_usage, int *ret)
{
git_pobject *trg_object = trg->object;
@@ -839,7 +825,7 @@ static unsigned long free_unpacked(struct unpacked *n)
static int find_deltas(git_packbuilder *pb, git_pobject **list,
unsigned int *list_size, unsigned int window,
- unsigned int depth)
+ int depth)
{
git_pobject *po;
git_buf zbuf = GIT_BUF_INIT;
@@ -854,8 +840,7 @@ static int find_deltas(git_packbuilder *pb, git_pobject **list,
for (;;) {
struct unpacked *n = array + idx;
- unsigned int max_depth;
- int j, best_base = -1;
+ int max_depth, j, best_base = -1;
git_packbuilder__progress_lock(pb);
if (!*list_size) {
@@ -1048,7 +1033,7 @@ static void *threaded_find_deltas(void *arg)
static int ll_find_deltas(git_packbuilder *pb, git_pobject **list,
unsigned int list_size, unsigned int window,
- unsigned int depth)
+ int depth)
{
struct thread_params *p;
int i, ret, active_threads = 0;
@@ -1205,6 +1190,13 @@ static int prepare_pack(git_packbuilder *pb)
if (pb->nr_objects == 0 || pb->done)
return 0; /* nothing to do */
+ /*
+ * Although we do not report progress during deltafication, we
+ * at least report that we are in the deltafication stage
+ */
+ if (pb->progress_cb)
+ pb->progress_cb(GIT_PACKBUILDER_DELTAFICATION, 0, pb->nr_objects, pb->progress_cb_payload);
+
delta_list = git__malloc(pb->nr_objects * sizeof(*delta_list));
GITERR_CHECK_ALLOC(delta_list);
@@ -1250,40 +1242,48 @@ int git_packbuilder_write_buf(git_buf *buf, git_packbuilder *pb)
static int write_cb(void *buf, size_t len, void *payload)
{
struct pack_write_context *ctx = payload;
- return git_indexer_stream_add(ctx->indexer, buf, len, ctx->stats);
+ return git_indexer_append(ctx->indexer, buf, len, ctx->stats);
}
int git_packbuilder_write(
git_packbuilder *pb,
const char *path,
+ unsigned int mode,
git_transfer_progress_callback progress_cb,
void *progress_cb_payload)
{
- git_indexer_stream *indexer;
+ git_indexer *indexer;
git_transfer_progress stats;
struct pack_write_context ctx;
PREPARE_PACK;
- if (git_indexer_stream_new(
- &indexer, path, progress_cb, progress_cb_payload) < 0)
+ if (git_indexer_new(
+ &indexer, path, mode, pb->odb, progress_cb, progress_cb_payload) < 0)
return -1;
ctx.indexer = indexer;
ctx.stats = &stats;
if (git_packbuilder_foreach(pb, write_cb, &ctx) < 0 ||
- git_indexer_stream_finalize(indexer, &stats) < 0) {
- git_indexer_stream_free(indexer);
+ git_indexer_commit(indexer, &stats) < 0) {
+ git_indexer_free(indexer);
return -1;
}
- git_indexer_stream_free(indexer);
+ git_oid_cpy(&pb->pack_oid, git_indexer_hash(indexer));
+
+ git_indexer_free(indexer);
return 0;
}
#undef PREPARE_PACK
+const git_oid *git_packbuilder_hash(git_packbuilder *pb)
+{
+ return &pb->pack_oid;
+}
+
static int cb_tree_walk(const char *root, const git_tree_entry *entry, void *payload)
{
struct tree_walk_context *ctx = payload;
@@ -1346,6 +1346,17 @@ uint32_t git_packbuilder_written(git_packbuilder *pb)
return pb->nr_written;
}
+int git_packbuilder_set_callbacks(git_packbuilder *pb, git_packbuilder_progress progress_cb, void *progress_cb_payload)
+{
+ if (!pb)
+ return -1;
+
+ pb->progress_cb = progress_cb;
+ pb->progress_cb_payload = progress_cb_payload;
+
+ return 0;
+}
+
void git_packbuilder_free(git_packbuilder *pb)
{
if (pb == NULL)
diff --git a/src/pack-objects.h b/src/pack-objects.h
index 8e7ba7f78..0c94a5a7a 100644
--- a/src/pack-objects.h
+++ b/src/pack-objects.h
@@ -16,6 +16,7 @@
#include "netops.h"
#include "git2/oid.h"
+#include "git2/pack.h"
#define GIT_PACK_WINDOW 10 /* number of objects to possibly delta against */
#define GIT_PACK_DEPTH 50 /* max delta depth */
@@ -79,6 +80,10 @@ struct git_packbuilder {
int nr_threads; /* nr of threads to use */
+ git_packbuilder_progress progress_cb;
+ void *progress_cb_payload;
+ double last_progress_report_time; /* the time progress was last reported */
+
bool done;
};
diff --git a/src/pack.c b/src/pack.c
index 7ce7099e0..644b2d465 100644
--- a/src/pack.c
+++ b/src/pack.c
@@ -329,8 +329,10 @@ static int pack_index_open(struct git_pack_file *p)
memcpy(idx_name, p->pack_name, base_len);
memcpy(idx_name + base_len, ".idx", sizeof(".idx"));
- if ((error = git_mutex_lock(&p->lock)) < 0)
+ if ((error = git_mutex_lock(&p->lock)) < 0) {
+ git__free(idx_name);
return error;
+ }
if (p->index_version == -1)
error = pack_index_check(idx_name, p);
@@ -362,6 +364,38 @@ static unsigned char *pack_window_open(
return git_mwindow_open(&p->mwf, w_cursor, offset, 20, left);
}
+/*
+ * The per-object header is a pretty dense thing, which is
+ * - first byte: low four bits are "size",
+ * then three bits of "type",
+ * with the high bit being "size continues".
+ * - each byte afterwards: low seven bits are size continuation,
+ * with the high bit being "size continues"
+ */
+size_t git_packfile__object_header(unsigned char *hdr, size_t size, git_otype type)
+{
+ unsigned char *hdr_base;
+ unsigned char c;
+
+ assert(type >= GIT_OBJ_COMMIT && type <= GIT_OBJ_REF_DELTA);
+
+ /* TODO: add support for chunked objects; see git.git 6c0d19b1 */
+
+ c = (unsigned char)((type << 4) | (size & 15));
+ size >>= 4;
+ hdr_base = hdr;
+
+ while (size) {
+ *hdr++ = c | 0x80;
+ c = size & 0x7f;
+ size >>= 7;
+ }
+ *hdr++ = c;
+
+ return (hdr - hdr_base);
+}
+
+
static int packfile_unpack_header1(
unsigned long *usedp,
size_t *sizep,
@@ -820,7 +854,7 @@ void git_packfile_free(struct git_pack_file *p)
git_mwindow_free_all(&p->mwf);
- if (p->mwf.fd != -1)
+ if (p->mwf.fd >= 0)
p_close(p->mwf.fd);
pack_index_free(p);
@@ -903,7 +937,8 @@ static int packfile_open(struct git_pack_file *p)
cleanup:
giterr_set(GITERR_OS, "Invalid packfile '%s'", p->pack_name);
- p_close(p->mwf.fd);
+ if (p->mwf.fd >= 0)
+ p_close(p->mwf.fd);
p->mwf.fd = -1;
git_mutex_unlock(&p->lock);
@@ -1107,8 +1142,11 @@ static int pack_entry_find_offset(
short_oid->id[0], short_oid->id[1], short_oid->id[2], lo, hi, p->num_objects);
#endif
- /* Use git.git lookup code */
+#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 */
diff --git a/src/pack.h b/src/pack.h
index aeeac9ce1..28146ab30 100644
--- a/src/pack.h
+++ b/src/pack.h
@@ -112,6 +112,8 @@ typedef struct git_packfile_stream {
git_mwindow *mw;
} git_packfile_stream;
+size_t git_packfile__object_header(unsigned char *hdr, size_t size, git_otype type);
+
int git_packfile_unpack_header(
size_t *size_p,
git_otype *type_p,
diff --git a/src/path.c b/src/path.c
index 6437979d5..750dd3ef7 100644
--- a/src/path.c
+++ b/src/path.c
@@ -8,7 +8,6 @@
#include "path.h"
#include "posix.h"
#ifdef GIT_WIN32
-#include "win32/dir.h"
#include "win32/posix.h"
#else
#include <dirent.h>
@@ -243,8 +242,8 @@ int git_path_root(const char *path)
#ifdef GIT_WIN32
/* Are we dealing with a windows network path? */
- else if ((path[0] == '/' && path[1] == '/') ||
- (path[0] == '\\' && path[1] == '\\'))
+ else if ((path[0] == '/' && path[1] == '/' && path[2] != '/') ||
+ (path[0] == '\\' && path[1] == '\\' && path[2] != '\\'))
{
offset += 2;
@@ -484,74 +483,90 @@ bool git_path_isfile(const char *path)
bool git_path_is_empty_dir(const char *path)
{
- git_buf pathbuf = GIT_BUF_INIT;
HANDLE hFind = INVALID_HANDLE_VALUE;
- wchar_t wbuf[GIT_WIN_PATH];
+ git_win32_path wbuf;
+ int wbufsz;
WIN32_FIND_DATAW ffd;
bool retval = true;
- if (!git_path_isdir(path)) return false;
+ if (!git_path_isdir(path))
+ return false;
- git_buf_printf(&pathbuf, "%s\\*", path);
- git__utf8_to_16(wbuf, GIT_WIN_PATH, git_buf_cstr(&pathbuf));
+ wbufsz = git_win32_path_from_c(wbuf, path);
+ if (!wbufsz || wbufsz + 2 > GIT_WIN_PATH_UTF16)
+ return false;
+ memcpy(&wbuf[wbufsz - 1], L"\\*", 3 * sizeof(wchar_t));
hFind = FindFirstFileW(wbuf, &ffd);
- if (INVALID_HANDLE_VALUE == hFind) {
- giterr_set(GITERR_OS, "Couldn't open '%s'", path);
+ if (INVALID_HANDLE_VALUE == hFind)
return false;
- }
do {
if (!git_path_is_dot_or_dotdotW(ffd.cFileName)) {
retval = false;
+ break;
}
} while (FindNextFileW(hFind, &ffd) != 0);
FindClose(hFind);
- git_buf_free(&pathbuf);
return retval;
}
#else
-bool git_path_is_empty_dir(const char *path)
+static int path_found_entry(void *payload, git_buf *path)
{
- DIR *dir = NULL;
- struct dirent *e;
- bool retval = true;
+ GIT_UNUSED(payload);
+ return !git_path_is_dot_or_dotdot(path->ptr);
+}
- if (!git_path_isdir(path)) return false;
+bool git_path_is_empty_dir(const char *path)
+{
+ int error;
+ git_buf dir = GIT_BUF_INIT;
- dir = opendir(path);
- if (!dir) {
- giterr_set(GITERR_OS, "Couldn't open '%s'", path);
+ if (!git_path_isdir(path))
return false;
- }
- while ((e = readdir(dir)) != NULL) {
- if (!git_path_is_dot_or_dotdot(e->d_name)) {
- giterr_set(GITERR_INVALID,
- "'%s' exists and is not an empty directory", path);
- retval = false;
- break;
- }
- }
- closedir(dir);
+ if (!(error = git_buf_sets(&dir, path)))
+ error = git_path_direach(&dir, 0, path_found_entry, NULL);
- return retval;
+ git_buf_free(&dir);
+
+ return !error;
}
+
#endif
-int git_path_lstat(const char *path, struct stat *st)
+int git_path_set_error(int errno_value, const char *path, const char *action)
{
- int err = 0;
-
- if (p_lstat(path, st) < 0) {
- err = (errno == ENOENT) ? GIT_ENOTFOUND : -1;
- giterr_set(GITERR_OS, "Failed to stat file '%s'", path);
+ switch (errno_value) {
+ case ENOENT:
+ case ENOTDIR:
+ giterr_set(GITERR_OS, "Could not find '%s' to %s", path, action);
+ return GIT_ENOTFOUND;
+
+ case EINVAL:
+ case ENAMETOOLONG:
+ giterr_set(GITERR_OS, "Invalid path for filesystem '%s'", path);
+ return GIT_EINVALIDSPEC;
+
+ case EEXIST:
+ giterr_set(GITERR_OS, "Failed %s - '%s' already exists", action, path);
+ return GIT_EEXISTS;
+
+ default:
+ giterr_set(GITERR_OS, "Could not %s '%s'", action, path);
+ return -1;
}
+}
- return err;
+int git_path_lstat(const char *path, struct stat *st)
+{
+ if (p_lstat(path, st) == 0)
+ return 0;
+
+ return git_path_set_error(errno, path, "stat");
}
static bool _check_dir_contents(
@@ -564,7 +579,7 @@ static bool _check_dir_contents(
size_t sub_size = strlen(sub);
/* leave base valid even if we could not make space for subdir */
- if (git_buf_try_grow(dir, dir_size + sub_size + 2, false) < 0)
+ if (git_buf_try_grow(dir, dir_size + sub_size + 2, false, false) < 0)
return false;
/* save excursion */
@@ -603,7 +618,7 @@ int git_path_find_dir(git_buf *dir, const char *path, const char *base)
}
/* call dirname if this is not a directory */
- if (!error && git_path_isdir(dir->ptr) == false)
+ if (!error) /* && git_path_isdir(dir->ptr) == false) */
error = git_path_dirname_r(dir, dir->ptr);
if (!error)
@@ -645,12 +660,33 @@ int git_path_resolve_relative(git_buf *path, size_t ceiling)
/* do nothing with singleton dot */;
else if (len == 2 && from[0] == '.' && from[1] == '.') {
- while (to > base && to[-1] == '/') to--;
- while (to > base && to[-1] != '/') to--;
- }
+ /* error out if trying to up one from a hard base */
+ if (to == base && ceiling != 0) {
+ giterr_set(GITERR_INVALID,
+ "Cannot strip root component off url");
+ return -1;
+ }
- else {
- if (*next == '/')
+ /* no more path segments to strip,
+ * use '../' as a new base path */
+ if (to == base) {
+ if (*next == '/')
+ len++;
+
+ if (to != from)
+ memmove(to, from, len);
+
+ to += len;
+ /* this is now the base, can't back up from a
+ * relative prefix */
+ base = to;
+ } else {
+ /* back up a path segment */
+ while (to > base && to[-1] == '/') to--;
+ while (to > base && to[-1] != '/') to--;
+ }
+ } else {
+ if (*next == '/' && *from != '/')
len++;
if (to != from)
@@ -702,14 +738,108 @@ int git_path_cmp(
return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
}
+bool git_path_has_non_ascii(const char *path, size_t pathlen)
+{
+ const uint8_t *scan = (const uint8_t *)path, *end;
+
+ for (end = scan + pathlen; scan < end; ++scan)
+ if (*scan & 0x80)
+ return true;
+
+ return false;
+}
+
+#ifdef GIT_USE_ICONV
+
+int git_path_iconv_init_precompose(git_path_iconv_t *ic)
+{
+ git_buf_init(&ic->buf, 0);
+ ic->map = iconv_open(GIT_PATH_REPO_ENCODING, GIT_PATH_NATIVE_ENCODING);
+ return 0;
+}
+
+void git_path_iconv_clear(git_path_iconv_t *ic)
+{
+ if (ic) {
+ if (ic->map != (iconv_t)-1)
+ iconv_close(ic->map);
+ git_buf_free(&ic->buf);
+ }
+}
+
+int git_path_iconv(git_path_iconv_t *ic, char **in, size_t *inlen)
+{
+ char *nfd = *in, *nfc;
+ size_t nfdlen = *inlen, nfclen, wantlen = nfdlen, rv;
+ int retry = 1;
+
+ if (!ic || ic->map == (iconv_t)-1 ||
+ !git_path_has_non_ascii(*in, *inlen))
+ return 0;
+
+ while (1) {
+ if (git_buf_grow(&ic->buf, wantlen) < 0)
+ return -1;
+
+ nfc = ic->buf.ptr + ic->buf.size;
+ nfclen = ic->buf.asize - ic->buf.size;
+
+ rv = iconv(ic->map, &nfd, &nfdlen, &nfc, &nfclen);
+
+ ic->buf.size = (nfc - ic->buf.ptr);
+
+ if (rv != (size_t)-1)
+ break;
+
+ if (errno != E2BIG)
+ goto fail;
+
+ /* make space for 2x the remaining data to be converted
+ * (with per retry overhead to avoid infinite loops)
+ */
+ wantlen = ic->buf.size + max(nfclen, nfdlen) * 2 + (size_t)(retry * 4);
+
+ if (retry++ > 4)
+ goto fail;
+ }
+
+ ic->buf.ptr[ic->buf.size] = '\0';
+
+ *in = ic->buf.ptr;
+ *inlen = ic->buf.size;
+
+ return 0;
+
+fail:
+ giterr_set(GITERR_OS, "Unable to convert unicode path data");
+ return -1;
+}
+
+#endif
+
+#if defined(__sun) || defined(__GNU__)
+typedef char path_dirent_data[sizeof(struct dirent) + FILENAME_MAX + 1];
+#else
+typedef struct dirent path_dirent_data;
+#endif
+
int git_path_direach(
git_buf *path,
+ uint32_t flags,
int (*fn)(void *, git_buf *),
void *arg)
{
+ int error = 0;
ssize_t wd_len;
DIR *dir;
- struct dirent *de, *de_buf;
+ path_dirent_data de_data;
+ struct dirent *de, *de_buf = (struct dirent *)&de_data;
+
+ (void)flags;
+
+#ifdef GIT_USE_ICONV
+ git_path_iconv_t ic = GIT_PATH_ICONV_INIT;
+#endif
if (git_path_to_dir(path) < 0)
return -1;
@@ -721,64 +851,80 @@ int git_path_direach(
return -1;
}
-#if defined(__sun) || defined(__GNU__)
- de_buf = git__malloc(sizeof(struct dirent) + FILENAME_MAX + 1);
-#else
- de_buf = git__malloc(sizeof(struct dirent));
+#ifdef GIT_USE_ICONV
+ if ((flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0)
+ (void)git_path_iconv_init_precompose(&ic);
#endif
while (p_readdir_r(dir, de_buf, &de) == 0 && de != NULL) {
- int result;
+ char *de_path = de->d_name;
+ size_t de_len = strlen(de_path);
- if (git_path_is_dot_or_dotdot(de->d_name))
+ if (git_path_is_dot_or_dotdot(de_path))
continue;
- if (git_buf_puts(path, de->d_name) < 0) {
- closedir(dir);
- git__free(de_buf);
- return -1;
- }
+#ifdef GIT_USE_ICONV
+ if ((error = git_path_iconv(&ic, &de_path, &de_len)) < 0)
+ break;
+#endif
+
+ if ((error = git_buf_put(path, de_path, de_len)) < 0)
+ break;
- result = fn(arg, path);
+ error = fn(arg, path);
git_buf_truncate(path, wd_len); /* restore path */
- if (result < 0) {
- closedir(dir);
- git__free(de_buf);
- return -1;
+ if (error) {
+ error = GIT_EUSER;
+ break;
}
}
closedir(dir);
- git__free(de_buf);
- return 0;
+
+#ifdef GIT_USE_ICONV
+ git_path_iconv_clear(&ic);
+#endif
+
+ return error;
}
int git_path_dirload(
const char *path,
size_t prefix_len,
size_t alloc_extra,
+ unsigned int flags,
git_vector *contents)
{
int error, need_slash;
DIR *dir;
- struct dirent *de, *de_buf;
size_t path_len;
+ path_dirent_data de_data;
+ struct dirent *de, *de_buf = (struct dirent *)&de_data;
+
+ (void)flags;
+
+#ifdef GIT_USE_ICONV
+ git_path_iconv_t ic = GIT_PATH_ICONV_INIT;
+#endif
+
+ assert(path && contents);
- assert(path != NULL && contents != NULL);
path_len = strlen(path);
- assert(path_len > 0 && path_len >= prefix_len);
+ if (!path_len || path_len < prefix_len) {
+ giterr_set(GITERR_INVALID, "Invalid directory path '%s'", path);
+ return -1;
+ }
if ((dir = opendir(path)) == NULL) {
giterr_set(GITERR_OS, "Failed to open directory '%s'", path);
return -1;
}
-#if defined(__sun) || defined(__GNU__)
- de_buf = git__malloc(sizeof(struct dirent) + FILENAME_MAX + 1);
-#else
- de_buf = git__malloc(sizeof(struct dirent));
+#ifdef GIT_USE_ICONV
+ if ((flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0)
+ (void)git_path_iconv_init_precompose(&ic);
#endif
path += prefix_len;
@@ -786,34 +932,38 @@ int git_path_dirload(
need_slash = (path_len > 0 && path[path_len-1] != '/') ? 1 : 0;
while ((error = p_readdir_r(dir, de_buf, &de)) == 0 && de != NULL) {
- char *entry_path;
- size_t entry_len;
+ char *entry_path, *de_path = de->d_name;
+ size_t alloc_size, de_len = strlen(de_path);
- if (git_path_is_dot_or_dotdot(de->d_name))
+ if (git_path_is_dot_or_dotdot(de_path))
continue;
- entry_len = strlen(de->d_name);
+#ifdef GIT_USE_ICONV
+ if ((error = git_path_iconv(&ic, &de_path, &de_len)) < 0)
+ break;
+#endif
- entry_path = git__malloc(
- path_len + need_slash + entry_len + 1 + alloc_extra);
- GITERR_CHECK_ALLOC(entry_path);
+ alloc_size = path_len + need_slash + de_len + 1 + alloc_extra;
+ if ((entry_path = git__calloc(alloc_size, 1)) == NULL) {
+ error = -1;
+ break;
+ }
if (path_len)
memcpy(entry_path, path, path_len);
if (need_slash)
entry_path[path_len] = '/';
- memcpy(&entry_path[path_len + need_slash], de->d_name, entry_len);
- entry_path[path_len + need_slash + entry_len] = '\0';
+ memcpy(&entry_path[path_len + need_slash], de_path, de_len);
- if (git_vector_insert(contents, entry_path) < 0) {
- closedir(dir);
- git__free(de_buf);
- return -1;
- }
+ if ((error = git_vector_insert(contents, entry_path)) < 0)
+ break;
}
closedir(dir);
- git__free(de_buf);
+
+#ifdef GIT_USE_ICONV
+ git_path_iconv_clear(&ic);
+#endif
if (error != 0)
giterr_set(GITERR_OS, "Failed to process directory entry in '%s'", path);
@@ -836,7 +986,7 @@ int git_path_with_stat_cmp_icase(const void *a, const void *b)
int git_path_dirload_with_stat(
const char *path,
size_t prefix_len,
- bool ignore_case,
+ unsigned int flags,
const char *start_stat,
const char *end_stat,
git_vector *contents)
@@ -853,13 +1003,14 @@ int git_path_dirload_with_stat(
return -1;
error = git_path_dirload(
- path, prefix_len, sizeof(git_path_with_stat) + 1, contents);
+ path, prefix_len, sizeof(git_path_with_stat) + 1, flags, contents);
if (error < 0) {
git_buf_free(&full);
return error;
}
- strncomp = ignore_case ? git__strncasecmp : git__strncmp;
+ strncomp = (flags & GIT_PATH_DIR_IGNORE_CASE) != 0 ?
+ git__strncasecmp : git__strncmp;
/* stat struct at start of git_path_with_stat, so shift path text */
git_vector_foreach(contents, i, ps) {
@@ -880,8 +1031,16 @@ int git_path_dirload_with_stat(
git_buf_truncate(&full, prefix_len);
if ((error = git_buf_joinpath(&full, full.ptr, ps->path)) < 0 ||
- (error = git_path_lstat(full.ptr, &ps->st)) < 0)
+ (error = git_path_lstat(full.ptr, &ps->st)) < 0) {
+ if (error == GIT_ENOTFOUND) {
+ giterr_clear();
+ error = 0;
+ git_vector_remove(contents, i--);
+ continue;
+ }
+
break;
+ }
if (S_ISDIR(ps->st.st_mode)) {
if ((error = git_buf_joinpath(&full, full.ptr, ".git")) < 0)
diff --git a/src/path.h b/src/path.h
index ead4fa338..3daafd265 100644
--- a/src/path.h
+++ b/src/path.h
@@ -175,7 +175,6 @@ extern bool git_path_contains(git_buf *dir, const char *item);
*
* @param parent Directory path that might contain subdir
* @param subdir Subdirectory name to look for in parent
- * @param append_if_exists If true, then subdir will be appended to the parent path if it does exist
* @return true if subdirectory exists, false otherwise.
*/
extern bool git_path_contains_dir(git_buf *parent, const char *subdir);
@@ -185,7 +184,6 @@ extern bool git_path_contains_dir(git_buf *parent, const char *subdir);
*
* @param dir Directory path that might contain file
* @param file File name to look for in parent
- * @param append_if_exists If true, then file will be appended to the path if it does exist
* @return true if file exists, false otherwise.
*/
extern bool git_path_contains_file(git_buf *dir, const char *file);
@@ -244,21 +242,28 @@ extern int git_path_resolve_relative(git_buf *path, size_t ceiling);
*/
extern int git_path_apply_relative(git_buf *target, const char *relpath);
+enum {
+ GIT_PATH_DIR_IGNORE_CASE = (1u << 0),
+ GIT_PATH_DIR_PRECOMPOSE_UNICODE = (1u << 1),
+};
+
/**
* Walk each directory entry, except '.' and '..', calling fn(state).
*
- * @param pathbuf buffer the function reads the initial directory
+ * @param pathbuf Buffer the function reads the initial directory
* path from, and updates with each successive entry's name.
- * @param fn function to invoke with each entry. The first arg is
- * the input state and the second arg is pathbuf. The function
- * may modify the pathbuf, but only by appending new text.
- * @param state to pass to fn as the first arg.
+ * @param flags Combination of GIT_PATH_DIR flags.
+ * @param callback Callback for each entry. Passed the `payload` and each
+ * successive path inside the directory as a full path. This may
+ * safely append text to the pathbuf if needed.
+ * @param payload Passed to callback as first argument.
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
*/
extern int git_path_direach(
git_buf *pathbuf,
- int (*fn)(void *, git_buf *),
- void *state);
+ uint32_t flags,
+ int (*callback)(void *payload, git_buf *path),
+ void *payload);
/**
* Sort function to order two paths
@@ -278,19 +283,19 @@ extern int git_path_cmp(
* @param pathbuf Buffer the function reads the directory from and
* and updates with each successive name.
* @param ceiling Prefix of path at which to stop walking up. If NULL,
- * this will walk all the way up to the root. If not a prefix of
- * pathbuf, the callback will be invoked a single time on the
- * original input path.
- * @param fn Function to invoke on each path. The first arg is the
- * input satte and the second arg is the pathbuf. The function
- * should not modify the pathbuf.
+ * this will walk all the way up to the root. If not a prefix of
+ * pathbuf, the callback will be invoked a single time on the
+ * original input path.
+ * @param callback Function to invoke on each path. Passed the `payload`
+ * and the buffer containing the current path. The path should not
+ * be modified in any way.
* @param state Passed to fn as the first ath.
*/
extern int git_path_walk_up(
git_buf *pathbuf,
const char *ceiling,
- int (*fn)(void *state, git_buf *),
- void *state);
+ int (*callback)(void *payload, git_buf *path),
+ void *payload);
/**
* Load all directory entries (except '.' and '..') into a vector.
@@ -306,12 +311,14 @@ extern int git_path_walk_up(
* prefix_len 3, the entries will look like "b/e1", "b/e2", etc.
* @param alloc_extra Extra bytes to add to each string allocation in
* case you want to append anything funny.
+ * @param flags Combination of GIT_PATH_DIR flags.
* @param contents Vector to fill with directory entry names.
*/
extern int git_path_dirload(
const char *path,
size_t prefix_len,
size_t alloc_extra,
+ uint32_t flags,
git_vector *contents);
@@ -338,7 +345,7 @@ extern int git_path_with_stat_cmp_icase(const void *a, const void *b);
*
* @param path The directory to read from
* @param prefix_len The trailing part of path to prefix to entry paths
- * @param ignore_case How to sort and compare paths with start/end limits
+ * @param flags GIT_PATH_DIR flags from above
* @param start_stat As optimization, only stat values after this prefix
* @param end_stat As optimization, only stat values before this prefix
* @param contents Vector to fill with git_path_with_stat structures
@@ -346,9 +353,78 @@ extern int git_path_with_stat_cmp_icase(const void *a, const void *b);
extern int git_path_dirload_with_stat(
const char *path,
size_t prefix_len,
- bool ignore_case,
+ uint32_t flags,
const char *start_stat,
const char *end_stat,
git_vector *contents);
+enum { GIT_PATH_NOTEQUAL = 0, GIT_PATH_EQUAL = 1, GIT_PATH_PREFIX = 2 };
+
+/*
+ * Determines if a path is equal to or potentially a child of another.
+ * @param parent The possible parent
+ * @param child The possible child
+ */
+GIT_INLINE(int) git_path_equal_or_prefixed(
+ const char *parent,
+ const char *child)
+{
+ const char *p = parent, *c = child;
+
+ while (*p && *c) {
+ if (*p++ != *c++)
+ return GIT_PATH_NOTEQUAL;
+ }
+
+ if (*p != '\0')
+ return GIT_PATH_NOTEQUAL;
+ if (*c == '\0')
+ return GIT_PATH_EQUAL;
+ if (*c == '/')
+ return GIT_PATH_PREFIX;
+
+ return GIT_PATH_NOTEQUAL;
+}
+
+/* translate errno to libgit2 error code and set error message */
+extern int git_path_set_error(
+ int errno_value, const char *path, const char *action);
+
+/* check if non-ascii characters are present in filename */
+extern bool git_path_has_non_ascii(const char *path, size_t pathlen);
+
+#define GIT_PATH_REPO_ENCODING "UTF-8"
+
+#ifdef __APPLE__
+#define GIT_PATH_NATIVE_ENCODING "UTF-8-MAC"
+#else
+#define GIT_PATH_NATIVE_ENCODING "UTF-8"
+#endif
+
+#ifdef GIT_USE_ICONV
+
+#include <iconv.h>
+
+typedef struct {
+ iconv_t map;
+ git_buf buf;
+} git_path_iconv_t;
+
+#define GIT_PATH_ICONV_INIT { (iconv_t)-1, GIT_BUF_INIT }
+
+/* Init iconv data for converting decomposed UTF-8 to precomposed */
+extern int git_path_iconv_init_precompose(git_path_iconv_t *ic);
+
+/* Clear allocated iconv data */
+extern void git_path_iconv_clear(git_path_iconv_t *ic);
+
+/*
+ * Rewrite `in` buffer using iconv map if necessary, replacing `in`
+ * pointer internal iconv buffer if rewrite happened. The `in` pointer
+ * will be left unchanged if no rewrite was needed.
+ */
+extern int git_path_iconv(git_path_iconv_t *ic, char **in, size_t *inlen);
+
+#endif /* GIT_USE_ICONV */
+
#endif
diff --git a/src/pathspec.c b/src/pathspec.c
index f029836d0..1e7e65e90 100644
--- a/src/pathspec.c
+++ b/src/pathspec.c
@@ -5,9 +5,16 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "git2/pathspec.h"
+#include "git2/diff.h"
#include "pathspec.h"
#include "buf_text.h"
#include "attr_file.h"
+#include "iterator.h"
+#include "repository.h"
+#include "index.h"
+#include "bitvec.h"
+#include "diff.h"
/* what is the common non-wildcard prefix for all items in the pathspec */
char *git_pathspec_prefix(const git_strarray *pathspec)
@@ -56,7 +63,7 @@ bool git_pathspec_is_empty(const git_strarray *pathspec)
}
/* build a vector of fnmatch patterns to evaluate efficiently */
-int git_pathspec_init(
+int git_pathspec__vinit(
git_vector *vspec, const git_strarray *strspec, git_pool *strpool)
{
size_t i;
@@ -76,7 +83,7 @@ int git_pathspec_init(
if (!match)
return -1;
- match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE;
+ match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG;
ret = git_attr_fnmatch__parse(match, strpool, NULL, &pattern);
if (ret == GIT_ENOTFOUND) {
@@ -93,7 +100,7 @@ int git_pathspec_init(
}
/* free data from the pathspec vector */
-void git_pathspec_free(git_vector *vspec)
+void git_pathspec__vfree(git_vector *vspec)
{
git_attr_fnmatch *match;
unsigned int i;
@@ -106,88 +113,612 @@ void git_pathspec_free(git_vector *vspec)
git_vector_free(vspec);
}
+struct pathspec_match_context {
+ int fnmatch_flags;
+ int (*strcomp)(const char *, const char *);
+ int (*strncomp)(const char *, const char *, size_t);
+};
+
+static void pathspec_match_context_init(
+ struct pathspec_match_context *ctxt,
+ bool disable_fnmatch,
+ bool casefold)
+{
+ if (disable_fnmatch)
+ ctxt->fnmatch_flags = -1;
+ else if (casefold)
+ ctxt->fnmatch_flags = FNM_CASEFOLD;
+ else
+ ctxt->fnmatch_flags = 0;
+
+ if (casefold) {
+ ctxt->strcomp = git__strcasecmp;
+ ctxt->strncomp = git__strncasecmp;
+ } else {
+ ctxt->strcomp = git__strcmp;
+ ctxt->strncomp = git__strncmp;
+ }
+}
+
+static int pathspec_match_one(
+ const git_attr_fnmatch *match,
+ struct pathspec_match_context *ctxt,
+ const char *path)
+{
+ int result = (match->flags & GIT_ATTR_FNMATCH_MATCH_ALL) ? 0 : FNM_NOMATCH;
+
+ if (result == FNM_NOMATCH)
+ result = ctxt->strcomp(match->pattern, path) ? FNM_NOMATCH : 0;
+
+ if (ctxt->fnmatch_flags >= 0 && result == FNM_NOMATCH)
+ result = p_fnmatch(match->pattern, path, ctxt->fnmatch_flags);
+
+ /* if we didn't match, look for exact dirname prefix match */
+ if (result == FNM_NOMATCH &&
+ (match->flags & GIT_ATTR_FNMATCH_HASWILD) == 0 &&
+ ctxt->strncomp(path, match->pattern, match->length) == 0 &&
+ path[match->length] == '/')
+ result = 0;
+
+ /* if we didn't match and this is a negative match, check for exact
+ * match of filename with leading '!'
+ */
+ if (result == FNM_NOMATCH &&
+ (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0 &&
+ *path == '!' &&
+ ctxt->strncomp(path + 1, match->pattern, match->length) == 0 &&
+ (!path[match->length + 1] || path[match->length + 1] == '/'))
+ return 1;
+
+ if (result == 0)
+ return (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) ? 0 : 1;
+ return -1;
+}
+
+static int git_pathspec__match_at(
+ size_t *matched_at,
+ const git_vector *vspec,
+ struct pathspec_match_context *ctxt,
+ const char *path0,
+ const char *path1)
+{
+ int result = GIT_ENOTFOUND;
+ size_t i = 0;
+ const git_attr_fnmatch *match;
+
+ git_vector_foreach(vspec, i, match) {
+ if (path0 && (result = pathspec_match_one(match, ctxt, path0)) >= 0)
+ break;
+ if (path1 && (result = pathspec_match_one(match, ctxt, path1)) >= 0)
+ break;
+ }
+
+ *matched_at = i;
+ return result;
+}
+
/* match a path against the vectorized pathspec */
-bool git_pathspec_match_path(
- git_vector *vspec,
+bool git_pathspec__match(
+ const git_vector *vspec,
const char *path,
bool disable_fnmatch,
bool casefold,
- const char **matched_pathspec)
+ const char **matched_pathspec,
+ size_t *matched_at)
{
- size_t i;
- git_attr_fnmatch *match;
- int fnmatch_flags = 0;
- int (*use_strcmp)(const char *, const char *);
- int (*use_strncmp)(const char *, const char *, size_t);
+ int result;
+ size_t pos;
+ struct pathspec_match_context ctxt;
if (matched_pathspec)
*matched_pathspec = NULL;
+ if (matched_at)
+ *matched_at = GIT_PATHSPEC_NOMATCH;
if (!vspec || !vspec->length)
return true;
- if (disable_fnmatch)
- fnmatch_flags = -1;
- else if (casefold)
- fnmatch_flags = FNM_CASEFOLD;
+ pathspec_match_context_init(&ctxt, disable_fnmatch, casefold);
- if (casefold) {
- use_strcmp = git__strcasecmp;
- use_strncmp = git__strncasecmp;
- } else {
- use_strcmp = git__strcmp;
- use_strncmp = git__strncmp;
+ result = git_pathspec__match_at(&pos, vspec, &ctxt, path, NULL);
+ if (result >= 0) {
+ if (matched_pathspec) {
+ const git_attr_fnmatch *match = git_vector_get(vspec, pos);
+ *matched_pathspec = match->pattern;
+ }
+
+ if (matched_at)
+ *matched_at = pos;
}
- git_vector_foreach(vspec, i, match) {
- int result = (match->flags & GIT_ATTR_FNMATCH_MATCH_ALL) ? 0 : FNM_NOMATCH;
+ return (result > 0);
+}
+
+
+int git_pathspec__init(git_pathspec *ps, const git_strarray *paths)
+{
+ int error = 0;
+
+ memset(ps, 0, sizeof(*ps));
+
+ ps->prefix = git_pathspec_prefix(paths);
+
+ if ((error = git_pool_init(&ps->pool, 1, 0)) < 0 ||
+ (error = git_pathspec__vinit(&ps->pathspec, paths, &ps->pool)) < 0)
+ git_pathspec__clear(ps);
+
+ return error;
+}
+
+void git_pathspec__clear(git_pathspec *ps)
+{
+ git__free(ps->prefix);
+ git_pathspec__vfree(&ps->pathspec);
+ git_pool_clear(&ps->pool);
+ memset(ps, 0, sizeof(*ps));
+}
+
+int git_pathspec_new(git_pathspec **out, const git_strarray *pathspec)
+{
+ int error = 0;
+ git_pathspec *ps = git__malloc(sizeof(git_pathspec));
+ GITERR_CHECK_ALLOC(ps);
+
+ if ((error = git_pathspec__init(ps, pathspec)) < 0) {
+ git__free(ps);
+ return error;
+ }
+
+ GIT_REFCOUNT_INC(ps);
+ *out = ps;
+ return 0;
+}
+
+static void pathspec_free(git_pathspec *ps)
+{
+ git_pathspec__clear(ps);
+ git__free(ps);
+}
+
+void git_pathspec_free(git_pathspec *ps)
+{
+ if (!ps)
+ return;
+ GIT_REFCOUNT_DEC(ps, pathspec_free);
+}
+
+int git_pathspec_matches_path(
+ const git_pathspec *ps, uint32_t flags, const char *path)
+{
+ bool no_fnmatch = (flags & GIT_PATHSPEC_NO_GLOB) != 0;
+ bool casefold = (flags & GIT_PATHSPEC_IGNORE_CASE) != 0;
+
+ assert(ps && path);
+
+ return (0 != git_pathspec__match(
+ &ps->pathspec, path, no_fnmatch, casefold, NULL, NULL));
+}
+
+static void pathspec_match_free(git_pathspec_match_list *m)
+{
+ git_pathspec_free(m->pathspec);
+ m->pathspec = NULL;
+
+ git_array_clear(m->matches);
+ git_array_clear(m->failures);
+ git_pool_clear(&m->pool);
+ git__free(m);
+}
+
+static git_pathspec_match_list *pathspec_match_alloc(
+ git_pathspec *ps, int datatype)
+{
+ git_pathspec_match_list *m = git__calloc(1, sizeof(git_pathspec_match_list));
+
+ if (m != NULL && git_pool_init(&m->pool, 1, 0) < 0) {
+ pathspec_match_free(m);
+ m = NULL;
+ }
+
+ /* need to keep reference to pathspec and increment refcount because
+ * failures array stores pointers to the pattern strings of the
+ * pathspec that had no matches
+ */
+ GIT_REFCOUNT_INC(ps);
+ m->pathspec = ps;
+ m->datatype = datatype;
+
+ return m;
+}
+
+GIT_INLINE(size_t) pathspec_mark_pattern(git_bitvec *used, size_t pos)
+{
+ if (!git_bitvec_get(used, pos)) {
+ git_bitvec_set(used, pos, true);
+ return 1;
+ }
+
+ return 0;
+}
+
+static size_t pathspec_mark_remaining(
+ git_bitvec *used,
+ git_vector *patterns,
+ struct pathspec_match_context *ctxt,
+ size_t start,
+ const char *path0,
+ const char *path1)
+{
+ size_t count = 0;
+
+ if (path1 == path0)
+ path1 = NULL;
+
+ for (; start < patterns->length; ++start) {
+ const git_attr_fnmatch *pat = git_vector_get(patterns, start);
+
+ if (git_bitvec_get(used, start))
+ continue;
+
+ if (path0 && pathspec_match_one(pat, ctxt, path0) > 0)
+ count += pathspec_mark_pattern(used, start);
+ else if (path1 && pathspec_match_one(pat, ctxt, path1) > 0)
+ count += pathspec_mark_pattern(used, start);
+ }
+
+ return count;
+}
+
+static int pathspec_build_failure_array(
+ git_pathspec_string_array_t *failures,
+ git_vector *patterns,
+ git_bitvec *used,
+ git_pool *pool)
+{
+ size_t pos;
+ char **failed;
+ const git_attr_fnmatch *pat;
+
+ for (pos = 0; pos < patterns->length; ++pos) {
+ if (git_bitvec_get(used, pos))
+ continue;
+
+ if ((failed = git_array_alloc(*failures)) == NULL)
+ return -1;
+
+ pat = git_vector_get(patterns, pos);
+
+ if ((*failed = git_pool_strdup(pool, pat->pattern)) == NULL)
+ return -1;
+ }
+
+ return 0;
+}
- if (result == FNM_NOMATCH)
- result = use_strcmp(match->pattern, path) ? FNM_NOMATCH : 0;
+static int pathspec_match_from_iterator(
+ git_pathspec_match_list **out,
+ git_iterator *iter,
+ uint32_t flags,
+ git_pathspec *ps)
+{
+ int error = 0;
+ git_pathspec_match_list *m = NULL;
+ const git_index_entry *entry = NULL;
+ struct pathspec_match_context ctxt;
+ git_vector *patterns = &ps->pathspec;
+ bool find_failures = out && (flags & GIT_PATHSPEC_FIND_FAILURES) != 0;
+ bool failures_only = !out || (flags & GIT_PATHSPEC_FAILURES_ONLY) != 0;
+ size_t pos, used_ct = 0, found_files = 0;
+ git_index *index = NULL;
+ git_bitvec used_patterns;
+ char **file;
+
+ if (git_bitvec_init(&used_patterns, patterns->length) < 0)
+ return -1;
+
+ if (out) {
+ *out = m = pathspec_match_alloc(ps, PATHSPEC_DATATYPE_STRINGS);
+ GITERR_CHECK_ALLOC(m);
+ }
+
+ if ((error = git_iterator_reset(iter, ps->prefix, ps->prefix)) < 0)
+ goto done;
+
+ if (git_iterator_type(iter) == GIT_ITERATOR_TYPE_WORKDIR &&
+ (error = git_repository_index__weakptr(
+ &index, git_iterator_owner(iter))) < 0)
+ goto done;
- if (fnmatch_flags >= 0 && result == FNM_NOMATCH)
- result = p_fnmatch(match->pattern, path, fnmatch_flags);
+ pathspec_match_context_init(
+ &ctxt, (flags & GIT_PATHSPEC_NO_GLOB) != 0,
+ git_iterator_ignore_case(iter));
- /* if we didn't match, look for exact dirname prefix match */
- if (result == FNM_NOMATCH &&
- (match->flags & GIT_ATTR_FNMATCH_HASWILD) == 0 &&
- use_strncmp(path, match->pattern, match->length) == 0 &&
- path[match->length] == '/')
- result = 0;
+ while (!(error = git_iterator_advance(&entry, iter))) {
+ /* search for match with entry->path */
+ int result = git_pathspec__match_at(
+ &pos, patterns, &ctxt, entry->path, NULL);
- if (result == 0) {
- if (matched_pathspec)
- *matched_pathspec = match->pattern;
+ /* no matches for this path */
+ if (result < 0)
+ continue;
- return (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) ? false : true;
+ /* if result was a negative pattern match, then don't list file */
+ if (!result) {
+ used_ct += pathspec_mark_pattern(&used_patterns, pos);
+ continue;
+ }
+
+ /* check if path is ignored and untracked */
+ if (index != NULL &&
+ git_iterator_current_is_ignored(iter) &&
+ git_index__find(NULL, index, entry->path, GIT_INDEX_STAGE_ANY) < 0)
+ continue;
+
+ /* mark the matched pattern as used */
+ used_ct += pathspec_mark_pattern(&used_patterns, pos);
+ ++found_files;
+
+ /* if find_failures is on, check if any later patterns also match */
+ if (find_failures && used_ct < patterns->length)
+ used_ct += pathspec_mark_remaining(
+ &used_patterns, patterns, &ctxt, pos + 1, entry->path, NULL);
+
+ /* if only looking at failures, exit early or just continue */
+ if (failures_only || !out) {
+ if (used_ct == patterns->length)
+ break;
+ continue;
+ }
+
+ /* insert matched path into matches array */
+ if ((file = (char **)git_array_alloc(m->matches)) == NULL ||
+ (*file = git_pool_strdup(&m->pool, entry->path)) == NULL) {
+ error = -1;
+ goto done;
}
}
- return false;
+ if (error < 0 && error != GIT_ITEROVER)
+ goto done;
+ error = 0;
+
+ /* insert patterns that had no matches into failures array */
+ if (find_failures && used_ct < patterns->length &&
+ (error = pathspec_build_failure_array(
+ &m->failures, patterns, &used_patterns, &m->pool)) < 0)
+ goto done;
+
+ /* if every pattern failed to match, then we have failed */
+ if ((flags & GIT_PATHSPEC_NO_MATCH_ERROR) != 0 && !found_files) {
+ giterr_set(GITERR_INVALID, "No matching files were found");
+ error = GIT_ENOTFOUND;
+ }
+
+done:
+ git_bitvec_free(&used_patterns);
+
+ if (error < 0) {
+ pathspec_match_free(m);
+ if (out) *out = NULL;
+ }
+
+ return error;
}
+static git_iterator_flag_t pathspec_match_iter_flags(uint32_t flags)
+{
+ git_iterator_flag_t f = 0;
-int git_pathspec_context_init(
- git_pathspec_context *ctxt, const git_strarray *paths)
+ if ((flags & GIT_PATHSPEC_IGNORE_CASE) != 0)
+ f |= GIT_ITERATOR_IGNORE_CASE;
+ else if ((flags & GIT_PATHSPEC_USE_CASE) != 0)
+ f |= GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ return f;
+}
+
+int git_pathspec_match_workdir(
+ git_pathspec_match_list **out,
+ git_repository *repo,
+ uint32_t flags,
+ git_pathspec *ps)
+{
+ int error = 0;
+ git_iterator *iter;
+
+ assert(repo);
+
+ if (!(error = git_iterator_for_workdir(
+ &iter, repo, pathspec_match_iter_flags(flags), NULL, NULL))) {
+
+ error = pathspec_match_from_iterator(out, iter, flags, ps);
+
+ git_iterator_free(iter);
+ }
+
+ return error;
+}
+
+int git_pathspec_match_index(
+ git_pathspec_match_list **out,
+ git_index *index,
+ uint32_t flags,
+ git_pathspec *ps)
{
int error = 0;
+ git_iterator *iter;
+
+ assert(index);
- memset(ctxt, 0, sizeof(*ctxt));
+ if (!(error = git_iterator_for_index(
+ &iter, index, pathspec_match_iter_flags(flags), NULL, NULL))) {
- ctxt->prefix = git_pathspec_prefix(paths);
+ error = pathspec_match_from_iterator(out, iter, flags, ps);
- if ((error = git_pool_init(&ctxt->pool, 1, 0)) < 0 ||
- (error = git_pathspec_init(&ctxt->pathspec, paths, &ctxt->pool)) < 0)
- git_pathspec_context_free(ctxt);
+ git_iterator_free(iter);
+ }
return error;
}
-void git_pathspec_context_free(
- git_pathspec_context *ctxt)
+int git_pathspec_match_tree(
+ git_pathspec_match_list **out,
+ git_tree *tree,
+ uint32_t flags,
+ git_pathspec *ps)
{
- git__free(ctxt->prefix);
- git_pathspec_free(&ctxt->pathspec);
- git_pool_clear(&ctxt->pool);
- memset(ctxt, 0, sizeof(*ctxt));
+ int error = 0;
+ git_iterator *iter;
+
+ assert(tree);
+
+ if (!(error = git_iterator_for_tree(
+ &iter, tree, pathspec_match_iter_flags(flags), NULL, NULL))) {
+
+ error = pathspec_match_from_iterator(out, iter, flags, ps);
+
+ git_iterator_free(iter);
+ }
+
+ return error;
}
+
+int git_pathspec_match_diff(
+ git_pathspec_match_list **out,
+ git_diff *diff,
+ uint32_t flags,
+ git_pathspec *ps)
+{
+ int error = 0;
+ git_pathspec_match_list *m = NULL;
+ struct pathspec_match_context ctxt;
+ git_vector *patterns = &ps->pathspec;
+ bool find_failures = out && (flags & GIT_PATHSPEC_FIND_FAILURES) != 0;
+ bool failures_only = !out || (flags & GIT_PATHSPEC_FAILURES_ONLY) != 0;
+ size_t i, pos, used_ct = 0, found_deltas = 0;
+ const git_diff_delta *delta, **match;
+ git_bitvec used_patterns;
+
+ assert(diff);
+
+ if (git_bitvec_init(&used_patterns, patterns->length) < 0)
+ return -1;
+
+ if (out) {
+ *out = m = pathspec_match_alloc(ps, PATHSPEC_DATATYPE_DIFF);
+ GITERR_CHECK_ALLOC(m);
+ }
+
+ pathspec_match_context_init(
+ &ctxt, (flags & GIT_PATHSPEC_NO_GLOB) != 0,
+ git_diff_is_sorted_icase(diff));
+
+ git_vector_foreach(&diff->deltas, i, delta) {
+ /* search for match with delta */
+ int result = git_pathspec__match_at(
+ &pos, patterns, &ctxt, delta->old_file.path, delta->new_file.path);
+
+ /* no matches for this path */
+ if (result < 0)
+ continue;
+
+ /* mark the matched pattern as used */
+ used_ct += pathspec_mark_pattern(&used_patterns, pos);
+
+ /* if result was a negative pattern match, then don't list file */
+ if (!result)
+ continue;
+
+ ++found_deltas;
+
+ /* if find_failures is on, check if any later patterns also match */
+ if (find_failures && used_ct < patterns->length)
+ used_ct += pathspec_mark_remaining(
+ &used_patterns, patterns, &ctxt, pos + 1,
+ delta->old_file.path, delta->new_file.path);
+
+ /* if only looking at failures, exit early or just continue */
+ if (failures_only || !out) {
+ if (used_ct == patterns->length)
+ break;
+ continue;
+ }
+
+ /* insert matched delta into matches array */
+ if (!(match = (const git_diff_delta **)git_array_alloc(m->matches))) {
+ error = -1;
+ goto done;
+ } else {
+ *match = delta;
+ }
+ }
+
+ /* insert patterns that had no matches into failures array */
+ if (find_failures && used_ct < patterns->length &&
+ (error = pathspec_build_failure_array(
+ &m->failures, patterns, &used_patterns, &m->pool)) < 0)
+ goto done;
+
+ /* if every pattern failed to match, then we have failed */
+ if ((flags & GIT_PATHSPEC_NO_MATCH_ERROR) != 0 && !found_deltas) {
+ giterr_set(GITERR_INVALID, "No matching deltas were found");
+ error = GIT_ENOTFOUND;
+ }
+
+done:
+ git_bitvec_free(&used_patterns);
+
+ if (error < 0) {
+ pathspec_match_free(m);
+ if (out) *out = NULL;
+ }
+
+ return error;
+}
+
+void git_pathspec_match_list_free(git_pathspec_match_list *m)
+{
+ if (m)
+ pathspec_match_free(m);
+}
+
+size_t git_pathspec_match_list_entrycount(
+ const git_pathspec_match_list *m)
+{
+ return m ? git_array_size(m->matches) : 0;
+}
+
+const char *git_pathspec_match_list_entry(
+ const git_pathspec_match_list *m, size_t pos)
+{
+ if (!m || m->datatype != PATHSPEC_DATATYPE_STRINGS ||
+ !git_array_valid_index(m->matches, pos))
+ return NULL;
+
+ return *((const char **)git_array_get(m->matches, pos));
+}
+
+const git_diff_delta *git_pathspec_match_list_diff_entry(
+ const git_pathspec_match_list *m, size_t pos)
+{
+ if (!m || m->datatype != PATHSPEC_DATATYPE_DIFF ||
+ !git_array_valid_index(m->matches, pos))
+ return NULL;
+
+ return *((const git_diff_delta **)git_array_get(m->matches, pos));
+}
+
+size_t git_pathspec_match_list_failed_entrycount(
+ const git_pathspec_match_list *m)
+{
+ return m ? git_array_size(m->failures) : 0;
+}
+
+const char * git_pathspec_match_list_failed_entry(
+ const git_pathspec_match_list *m, size_t pos)
+{
+ char **entry = m ? git_array_get(m->failures, pos) : NULL;
+
+ return entry ? *entry : NULL;
+}
+
diff --git a/src/pathspec.h b/src/pathspec.h
index f6509df4c..40cd21c3f 100644
--- a/src/pathspec.h
+++ b/src/pathspec.h
@@ -8,9 +8,35 @@
#define INCLUDE_pathspec_h__
#include "common.h"
+#include <git2/pathspec.h>
#include "buffer.h"
#include "vector.h"
#include "pool.h"
+#include "array.h"
+
+/* public compiled pathspec */
+struct git_pathspec {
+ git_refcount rc;
+ char *prefix;
+ git_vector pathspec;
+ git_pool pool;
+};
+
+enum {
+ PATHSPEC_DATATYPE_STRINGS = 0,
+ PATHSPEC_DATATYPE_DIFF = 1,
+};
+
+typedef git_array_t(char *) git_pathspec_string_array_t;
+
+/* public interface to pathspec matching */
+struct git_pathspec_match_list {
+ git_pathspec *pathspec;
+ git_array_t(void *) matches;
+ git_pathspec_string_array_t failures;
+ git_pool pool;
+ int datatype;
+};
/* what is the common non-wildcard prefix for all items in the pathspec */
extern char *git_pathspec_prefix(const git_strarray *pathspec);
@@ -19,36 +45,31 @@ extern char *git_pathspec_prefix(const git_strarray *pathspec);
extern bool git_pathspec_is_empty(const git_strarray *pathspec);
/* build a vector of fnmatch patterns to evaluate efficiently */
-extern int git_pathspec_init(
+extern int git_pathspec__vinit(
git_vector *vspec, const git_strarray *strspec, git_pool *strpool);
/* free data from the pathspec vector */
-extern void git_pathspec_free(git_vector *vspec);
+extern void git_pathspec__vfree(git_vector *vspec);
+
+#define GIT_PATHSPEC_NOMATCH ((size_t)-1)
/*
* Match a path against the vectorized pathspec.
* The matched pathspec is passed back into the `matched_pathspec` parameter,
* unless it is passed as NULL by the caller.
*/
-extern bool git_pathspec_match_path(
- git_vector *vspec,
+extern bool git_pathspec__match(
+ const git_vector *vspec,
const char *path,
bool disable_fnmatch,
bool casefold,
- const char **matched_pathspec);
+ const char **matched_pathspec,
+ size_t *matched_at);
/* easy pathspec setup */
-typedef struct {
- char *prefix;
- git_vector pathspec;
- git_pool pool;
-} git_pathspec_context;
-
-extern int git_pathspec_context_init(
- git_pathspec_context *ctxt, const git_strarray *paths);
+extern int git_pathspec__init(git_pathspec *ps, const git_strarray *paths);
-extern void git_pathspec_context_free(
- git_pathspec_context *ctxt);
+extern void git_pathspec__clear(git_pathspec *ps);
#endif
diff --git a/src/posix.h b/src/posix.h
index 40bcc1ab0..14c92b93d 100644
--- a/src/posix.h
+++ b/src/posix.h
@@ -39,7 +39,7 @@ typedef int git_file;
*
* Some of the methods are slightly wrapped to provide
* saner defaults. Some of these methods are emulated
- * in Windows platforns.
+ * in Windows platforms.
*
* Use your manpages to check the docs on these.
*/
@@ -71,16 +71,12 @@ typedef int GIT_SOCKET;
#define p_localtime_r localtime_r
#define p_gmtime_r gmtime_r
-#define p_gettimeofday gettimeofday
#else
typedef SOCKET GIT_SOCKET;
-struct timezone;
extern struct tm * p_localtime_r (const time_t *timer, struct tm *result);
extern struct tm * p_gmtime_r (const time_t *timer, struct tm *result);
-extern int p_gettimeofday(struct timeval *tv, struct timezone *tz);
-
#endif
@@ -93,6 +89,10 @@ extern int p_gettimeofday(struct timeval *tv, struct timezone *tz);
# include "unix/posix.h"
#endif
+#ifndef __MINGW32__
+# define p_strnlen strnlen
+#endif
+
#ifdef NO_READDIR_R
# include <dirent.h>
GIT_INLINE(int) p_readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
diff --git a/src/pqueue.h b/src/pqueue.h
index ed7139285..9061f8279 100644
--- a/src/pqueue.h
+++ b/src/pqueue.h
@@ -48,7 +48,7 @@ typedef struct {
* should be preallocated
* @param cmppri the callback function to compare two nodes of the queue
*
- * @Return the handle or NULL for insufficent memory
+ * @return the handle or NULL for insufficent memory
*/
int git_pqueue_init(git_pqueue *q, size_t n, git_pqueue_cmp cmppri);
@@ -83,8 +83,7 @@ int git_pqueue_insert(git_pqueue *q, void *d);
/**
* pop the highest-ranking item from the queue.
- * @param p the queue
- * @param d where to copy the entry to
+ * @param q the queue
* @return NULL on error, otherwise the entry
*/
void *git_pqueue_pop(git_pqueue *q);
@@ -93,7 +92,6 @@ void *git_pqueue_pop(git_pqueue *q);
/**
* access highest-ranking item without removing it.
* @param q the queue
- * @param d the entry
* @return NULL on error, otherwise the entry
*/
void *git_pqueue_peek(git_pqueue *q);
diff --git a/src/push.c b/src/push.c
index 452d71789..3c9d5bb35 100644
--- a/src/push.c
+++ b/src/push.c
@@ -70,6 +70,25 @@ int git_push_set_options(git_push *push, const git_push_options *opts)
return 0;
}
+int git_push_set_callbacks(
+ git_push *push,
+ git_packbuilder_progress pack_progress_cb,
+ void *pack_progress_cb_payload,
+ git_push_transfer_progress transfer_progress_cb,
+ void *transfer_progress_cb_payload)
+{
+ if (!push)
+ return -1;
+
+ push->pack_progress_cb = pack_progress_cb;
+ push->pack_progress_cb_payload = pack_progress_cb_payload;
+
+ push->transfer_progress_cb = transfer_progress_cb;
+ push->transfer_progress_cb_payload = transfer_progress_cb_payload;
+
+ return 0;
+}
+
static void free_refspec(push_spec *spec)
{
if (spec == NULL)
@@ -233,6 +252,37 @@ on_error:
return error;
}
+/**
+ * Insert all tags until we find a non-tag object, which is returned
+ * in `out`.
+ */
+static int enqueue_tag(git_object **out, git_push *push, git_oid *id)
+{
+ git_object *obj = NULL, *target = NULL;
+ int error;
+
+ if ((error = git_object_lookup(&obj, push->repo, id, GIT_OBJ_TAG)) < 0)
+ return error;
+
+ while (git_object_type(obj) == GIT_OBJ_TAG) {
+ if ((error = git_packbuilder_insert(push->pb, git_object_id(obj), NULL)) < 0)
+ break;
+
+ if ((error = git_tag_target(&target, (git_tag *) obj)) < 0)
+ break;
+
+ git_object_free(obj);
+ obj = target;
+ }
+
+ if (error < 0)
+ git_object_free(obj);
+ else
+ *out = obj;
+
+ return error;
+}
+
static int revwalk(git_vector *commits, git_push *push)
{
git_remote_head *head;
@@ -265,21 +315,11 @@ static int revwalk(git_vector *commits, git_push *push)
goto on_error;
if (type == GIT_OBJ_TAG) {
- git_tag *tag;
git_object *target;
- if (git_packbuilder_insert(push->pb, &spec->loid, NULL) < 0)
- goto on_error;
-
- if (git_tag_lookup(&tag, push->repo, &spec->loid) < 0)
+ if ((error = enqueue_tag(&target, push, &spec->loid)) < 0)
goto on_error;
- if (git_tag_peel(&target, tag) < 0) {
- git_tag_free(tag);
- goto on_error;
- }
- git_tag_free(tag);
-
if (git_object_type(target) == GIT_OBJ_COMMIT) {
if (git_revwalk_push(rw, git_object_id(target)) < 0) {
git_object_free(target);
@@ -542,7 +582,7 @@ static int calculate_work(git_push *push)
static int do_push(git_push *push)
{
- int error;
+ int error = 0;
git_transport *transport = push->remote->transport;
if (!transport->push) {
@@ -562,28 +602,36 @@ static int do_push(git_push *push)
git_packbuilder_set_threads(push->pb, push->pb_parallelism);
+ if (push->pack_progress_cb)
+ if ((error = git_packbuilder_set_callbacks(push->pb, push->pack_progress_cb, push->pack_progress_cb_payload)) < 0)
+ goto on_error;
+
if ((error = calculate_work(push)) < 0 ||
(error = queue_objects(push)) < 0 ||
(error = transport->push(transport, push)) < 0)
goto on_error;
- error = 0;
-
on_error:
git_packbuilder_free(push->pb);
return error;
}
-static int cb_filter_refs(git_remote_head *ref, void *data)
-{
- git_remote *remote = (git_remote *) data;
- return git_vector_insert(&remote->refs, ref);
-}
-
static int filter_refs(git_remote *remote)
{
+ const git_remote_head **heads;
+ size_t heads_len, i;
+
git_vector_clear(&remote->refs);
- return git_remote_ls(remote, cb_filter_refs, remote);
+
+ if (git_remote_ls(&heads, &heads_len, remote) < 0)
+ return -1;
+
+ for (i = 0; i < heads_len; i++) {
+ if (git_vector_insert(&remote->refs, (void *)heads[i]) < 0)
+ return -1;
+ }
+
+ return 0;
}
int git_push_finish(git_push *push)
diff --git a/src/push.h b/src/push.h
index e982b8385..6c8bf7229 100644
--- a/src/push.h
+++ b/src/push.h
@@ -39,6 +39,11 @@ struct git_push {
/* options */
unsigned pb_parallelism;
+
+ git_packbuilder_progress pack_progress_cb;
+ void *pack_progress_cb_payload;
+ git_push_transfer_progress transfer_progress_cb;
+ void *transfer_progress_cb_payload;
};
/**
diff --git a/src/refdb.c b/src/refdb.c
index 4de7188b2..adb58806e 100644
--- a/src/refdb.c
+++ b/src/refdb.c
@@ -16,6 +16,7 @@
#include "hash.h"
#include "refdb.h"
#include "refs.h"
+#include "reflog.h"
int git_refdb_new(git_refdb **out, git_repository *repo)
{
@@ -201,5 +202,20 @@ int git_refdb_rename(
int git_refdb_delete(struct git_refdb *db, const char *ref_name)
{
assert(db && db->backend);
- return db->backend->delete(db->backend, ref_name);
+ return db->backend->del(db->backend, ref_name);
+}
+
+int git_refdb_reflog_read(git_reflog **out, git_refdb *db, const char *name)
+{
+ int error;
+
+ assert(db && db->backend);
+
+ if ((error = db->backend->reflog_read(out, db->backend, name)) < 0)
+ return error;
+
+ GIT_REFCOUNT_INC(db);
+ (*out)->db = db;
+
+ return 0;
}
diff --git a/src/refdb.h b/src/refdb.h
index 3aea37b62..0ee60d911 100644
--- a/src/refdb.h
+++ b/src/refdb.h
@@ -43,4 +43,8 @@ void git_refdb_iterator_free(git_reference_iterator *iter);
int git_refdb_write(git_refdb *refdb, git_reference *ref, int force);
int git_refdb_delete(git_refdb *refdb, const char *ref_name);
+int git_refdb_reflog_read(git_reflog **out, git_refdb *db, const char *name);
+int git_refdb_reflog_write(git_reflog *reflog);
+
+
#endif
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index b9e283ac5..62d5c1047 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -15,12 +15,15 @@
#include "refdb.h"
#include "refdb_fs.h"
#include "iterator.h"
+#include "sortedcache.h"
+#include "signature.h"
#include <git2/tag.h>
#include <git2/object.h>
#include <git2/refdb.h>
#include <git2/sys/refdb_backend.h>
#include <git2/sys/refs.h>
+#include <git2/sys/reflog.h>
GIT__USE_STRMAP;
@@ -53,243 +56,151 @@ typedef struct refdb_fs_backend {
git_repository *repo;
char *path;
- git_refcache refcache;
+ git_sortedcache *refcache;
int peeling_mode;
+ git_iterator_flag_t iterator_flags;
+ uint32_t direach_flags;
} refdb_fs_backend;
-static int reference_read(
- git_buf *file_content,
- time_t *mtime,
- const char *repo_path,
- const char *ref_name,
- int *updated)
+static int packref_cmp(const void *a_, const void *b_)
{
- git_buf path = GIT_BUF_INIT;
- int result;
-
- assert(file_content && repo_path && ref_name);
-
- /* Determine the full path of the file */
- if (git_buf_joinpath(&path, repo_path, ref_name) < 0)
- return -1;
-
- result = git_futils_readbuffer_updated(file_content, path.ptr, mtime, NULL, updated);
- git_buf_free(&path);
-
- return result;
-}
-
-static int packed_parse_oid(
- struct packref **ref_out,
- const char **buffer_out,
- const char *buffer_end)
-{
- struct packref *ref = NULL;
-
- const char *buffer = *buffer_out;
- const char *refname_begin, *refname_end;
-
- size_t refname_len;
- git_oid id;
-
- refname_begin = (buffer + GIT_OID_HEXSZ + 1);
- if (refname_begin >= buffer_end || refname_begin[-1] != ' ')
- goto corrupt;
-
- /* Is this a valid object id? */
- if (git_oid_fromstr(&id, buffer) < 0)
- goto corrupt;
-
- refname_end = memchr(refname_begin, '\n', buffer_end - refname_begin);
- if (refname_end == NULL)
- refname_end = buffer_end;
-
- if (refname_end[-1] == '\r')
- refname_end--;
-
- refname_len = refname_end - refname_begin;
-
- ref = git__calloc(1, sizeof(struct packref) + refname_len + 1);
- GITERR_CHECK_ALLOC(ref);
-
- memcpy(ref->name, refname_begin, refname_len);
- ref->name[refname_len] = 0;
-
- git_oid_cpy(&ref->oid, &id);
-
- *ref_out = ref;
- *buffer_out = refname_end + 1;
- return 0;
-
-corrupt:
- git__free(ref);
- giterr_set(GITERR_REFERENCE, "The packed references file is corrupted");
- return -1;
+ const struct packref *a = a_, *b = b_;
+ return strcmp(a->name, b->name);
}
-static int packed_parse_peel(
- struct packref *tag_ref,
- const char **buffer_out,
- const char *buffer_end)
+static int packed_reload(refdb_fs_backend *backend)
{
- const char *buffer = *buffer_out + 1;
-
- assert(buffer[-1] == '^');
-
- /* Ensure it's not the first entry of the file */
- if (tag_ref == NULL)
- goto corrupt;
-
- if (buffer + GIT_OID_HEXSZ > buffer_end)
- goto corrupt;
-
- /* Is this a valid object id? */
- if (git_oid_fromstr(&tag_ref->peel, buffer) < 0)
- goto corrupt;
-
- buffer = buffer + GIT_OID_HEXSZ;
- if (*buffer == '\r')
- buffer++;
-
- if (buffer != buffer_end) {
- if (*buffer == '\n')
- buffer++;
- else
- goto corrupt;
- }
-
- tag_ref->flags |= PACKREF_HAS_PEEL;
- *buffer_out = buffer;
- return 0;
-
-corrupt:
- giterr_set(GITERR_REFERENCE, "The packed references file is corrupted");
- return -1;
-}
-
-static int packed_load(refdb_fs_backend *backend)
-{
- int result, updated;
- git_buf packfile = GIT_BUF_INIT;
- const char *buffer_start, *buffer_end;
- git_refcache *ref_cache = &backend->refcache;
-
- /* First we make sure we have allocated the hash table */
- if (ref_cache->packfile == NULL) {
- ref_cache->packfile = git_strmap_alloc();
- GITERR_CHECK_ALLOC(ref_cache->packfile);
- }
+ int error;
+ git_buf packedrefs = GIT_BUF_INIT;
+ char *scan, *eof, *eol;
- if (backend->path == NULL)
+ if (!backend->path)
return 0;
- result = reference_read(&packfile, &ref_cache->packfile_time,
- backend->path, GIT_PACKEDREFS_FILE, &updated);
+ error = git_sortedcache_lockandload(backend->refcache, &packedrefs);
/*
- * If we couldn't find the file, we need to clear the table and
- * return. On any other error, we return that error. If everything
- * went fine and the file wasn't updated, then there's nothing new
- * for us here, so just return. Anything else means we need to
- * refresh the packed refs.
+ * If we can't find the packed-refs, clear table and return.
+ * Any other error just gets passed through.
+ * If no error, and file wasn't changed, just return.
+ * Anything else means we need to refresh the packed refs.
*/
- if (result == GIT_ENOTFOUND) {
- git_strmap_clear(ref_cache->packfile);
- return 0;
+ if (error <= 0) {
+ if (error == GIT_ENOTFOUND) {
+ git_sortedcache_clear(backend->refcache, true);
+ giterr_clear();
+ error = 0;
+ }
+ return error;
}
- if (result < 0)
- return -1;
-
- if (!updated)
- return 0;
+ /* At this point, refresh the packed refs from the loaded buffer. */
- /*
- * At this point, we want to refresh the packed refs. We already
- * have the contents in our buffer.
- */
- git_strmap_clear(ref_cache->packfile);
+ git_sortedcache_clear(backend->refcache, false);
- buffer_start = (const char *)packfile.ptr;
- buffer_end = (const char *)(buffer_start) + packfile.size;
+ scan = (char *)packedrefs.ptr;
+ eof = scan + packedrefs.size;
backend->peeling_mode = PEELING_NONE;
- if (buffer_start[0] == '#') {
+ if (*scan == '#') {
static const char *traits_header = "# pack-refs with: ";
- if (git__prefixcmp(buffer_start, traits_header) == 0) {
- char *traits = (char *)buffer_start + strlen(traits_header);
- char *traits_end = strchr(traits, '\n');
+ if (git__prefixcmp(scan, traits_header) == 0) {
+ scan += strlen(traits_header);
+ eol = strchr(scan, '\n');
- if (traits_end == NULL)
+ if (!eol)
goto parse_failed;
+ *eol = '\0';
- *traits_end = '\0';
-
- if (strstr(traits, " fully-peeled ") != NULL) {
+ if (strstr(scan, " fully-peeled ") != NULL) {
backend->peeling_mode = PEELING_FULL;
- } else if (strstr(traits, " peeled ") != NULL) {
+ } else if (strstr(scan, " peeled ") != NULL) {
backend->peeling_mode = PEELING_STANDARD;
}
- buffer_start = traits_end + 1;
+ scan = eol + 1;
}
}
- while (buffer_start < buffer_end && buffer_start[0] == '#') {
- buffer_start = strchr(buffer_start, '\n');
- if (buffer_start == NULL)
+ while (scan < eof && *scan == '#') {
+ if (!(eol = strchr(scan, '\n')))
goto parse_failed;
-
- buffer_start++;
+ scan = eol + 1;
}
- while (buffer_start < buffer_end) {
- int err;
- struct packref *ref = NULL;
+ while (scan < eof) {
+ struct packref *ref;
+ git_oid oid;
+
+ /* parse "<OID> <refname>\n" */
- if (packed_parse_oid(&ref, &buffer_start, buffer_end) < 0)
+ if (git_oid_fromstr(&oid, scan) < 0)
goto parse_failed;
+ scan += GIT_OID_HEXSZ;
- if (buffer_start[0] == '^') {
- if (packed_parse_peel(ref, &buffer_start, buffer_end) < 0)
- goto parse_failed;
- } else if (backend->peeling_mode == PEELING_FULL ||
- (backend->peeling_mode == PEELING_STANDARD &&
- git__prefixcmp(ref->name, GIT_REFS_TAGS_DIR) == 0)) {
- ref->flags |= PACKREF_CANNOT_PEEL;
- }
+ if (*scan++ != ' ')
+ goto parse_failed;
+ if (!(eol = strchr(scan, '\n')))
+ goto parse_failed;
+ *eol = '\0';
+ if (eol[-1] == '\r')
+ eol[-1] = '\0';
- git_strmap_insert(ref_cache->packfile, ref->name, ref, err);
- if (err < 0)
+ if (git_sortedcache_upsert((void **)&ref, backend->refcache, scan) < 0)
goto parse_failed;
+ scan = eol + 1;
+
+ git_oid_cpy(&ref->oid, &oid);
+
+ /* look for optional "^<OID>\n" */
+
+ if (*scan == '^') {
+ if (git_oid_fromstr(&oid, scan + 1) < 0)
+ goto parse_failed;
+ scan += GIT_OID_HEXSZ + 1;
+
+ if (scan < eof) {
+ if (!(eol = strchr(scan, '\n')))
+ goto parse_failed;
+ scan = eol + 1;
+ }
+
+ git_oid_cpy(&ref->peel, &oid);
+ ref->flags |= PACKREF_HAS_PEEL;
+ }
+ else if (backend->peeling_mode == PEELING_FULL ||
+ (backend->peeling_mode == PEELING_STANDARD &&
+ git__prefixcmp(ref->name, GIT_REFS_TAGS_DIR) == 0))
+ ref->flags |= PACKREF_CANNOT_PEEL;
}
- git_buf_free(&packfile);
+ git_sortedcache_wunlock(backend->refcache);
+ git_buf_free(&packedrefs);
+
return 0;
parse_failed:
- git_strmap_free(ref_cache->packfile);
- ref_cache->packfile = NULL;
- git_buf_free(&packfile);
+ giterr_set(GITERR_REFERENCE, "Corrupted packed references file");
+
+ git_sortedcache_clear(backend->refcache, false);
+ git_sortedcache_wunlock(backend->refcache);
+ git_buf_free(&packedrefs);
+
return -1;
}
-static int loose_parse_oid(git_oid *oid, const char *filename, git_buf *file_content)
+static int loose_parse_oid(
+ git_oid *oid, const char *filename, git_buf *file_content)
{
- size_t len;
- const char *str;
+ const char *str = git_buf_cstr(file_content);
- len = git_buf_len(file_content);
- if (len < GIT_OID_HEXSZ)
+ if (git_buf_len(file_content) < GIT_OID_HEXSZ)
goto corrupted;
- /* str is guranteed to be zero-terminated */
- str = git_buf_cstr(file_content);
-
/* we need to get 40 OID characters from the file */
- if (git_oid_fromstr(oid, git_buf_cstr(file_content)) < 0)
+ if (git_oid_fromstr(oid, str) < 0)
goto corrupted;
/* If the file is longer than 40 chars, the 41st must be a space */
@@ -302,68 +213,72 @@ corrupted:
return -1;
}
-static int loose_lookup_to_packfile(
- struct packref **ref_out,
- refdb_fs_backend *backend,
- const char *name)
+static int loose_readbuffer(git_buf *buf, const char *base, const char *path)
+{
+ int error;
+
+ /* build full path to file */
+ if ((error = git_buf_joinpath(buf, base, path)) < 0 ||
+ (error = git_futils_readbuffer(buf, buf->ptr)) < 0)
+ git_buf_free(buf);
+
+ return error;
+}
+
+static int loose_lookup_to_packfile(refdb_fs_backend *backend, const char *name)
{
+ int error = 0;
git_buf ref_file = GIT_BUF_INIT;
struct packref *ref = NULL;
- size_t name_len;
+ git_oid oid;
- *ref_out = NULL;
+ /* if we fail to load the loose reference, assume someone changed
+ * the filesystem under us and skip it...
+ */
+ if (loose_readbuffer(&ref_file, backend->path, name) < 0) {
+ giterr_clear();
+ goto done;
+ }
- if (reference_read(&ref_file, NULL, backend->path, name, NULL) < 0)
- return -1;
+ /* skip symbolic refs */
+ if (!git__prefixcmp(git_buf_cstr(&ref_file), GIT_SYMREF))
+ goto done;
- git_buf_rtrim(&ref_file);
+ /* parse OID from file */
+ if ((error = loose_parse_oid(&oid, name, &ref_file)) < 0)
+ goto done;
- name_len = strlen(name);
- ref = git__calloc(1, sizeof(struct packref) + name_len + 1);
- GITERR_CHECK_ALLOC(ref);
+ git_sortedcache_wlock(backend->refcache);
- memcpy(ref->name, name, name_len);
- ref->name[name_len] = 0;
+ if (!(error = git_sortedcache_upsert(
+ (void **)&ref, backend->refcache, name))) {
- if (loose_parse_oid(&ref->oid, name, &ref_file) < 0) {
- git_buf_free(&ref_file);
- git__free(ref);
- return -1;
+ git_oid_cpy(&ref->oid, &oid);
+ ref->flags = PACKREF_WAS_LOOSE;
}
- ref->flags = PACKREF_WAS_LOOSE;
+ git_sortedcache_wunlock(backend->refcache);
- *ref_out = ref;
+done:
git_buf_free(&ref_file);
- return 0;
+ return error;
}
-
static int _dirent_loose_load(void *data, git_buf *full_path)
{
refdb_fs_backend *backend = (refdb_fs_backend *)data;
- void *old_ref = NULL;
- struct packref *ref;
const char *file_path;
- int err;
-
- if (git_path_isdir(full_path->ptr) == true)
- return git_path_direach(full_path, _dirent_loose_load, backend);
- file_path = full_path->ptr + strlen(backend->path);
+ if (git__suffixcmp(full_path->ptr, ".lock") == 0)
+ return 0;
- if (loose_lookup_to_packfile(&ref, backend, file_path) < 0)
- return -1;
+ if (git_path_isdir(full_path->ptr))
+ return git_path_direach(
+ full_path, backend->direach_flags, _dirent_loose_load, backend);
- git_strmap_insert2(
- backend->refcache.packfile, ref->name, ref, old_ref, err);
- if (err < 0) {
- git__free(ref);
- return -1;
- }
+ file_path = full_path->ptr + strlen(backend->path);
- git__free(old_ref);
- return 0;
+ return loose_lookup_to_packfile(backend, file_path);
}
/*
@@ -374,11 +289,8 @@ static int _dirent_loose_load(void *data, git_buf *full_path)
*/
static int packed_loadloose(refdb_fs_backend *backend)
{
+ int error;
git_buf refs_path = GIT_BUF_INIT;
- int result;
-
- /* the packfile must have been previously loaded! */
- assert(backend->refcache.packfile);
if (git_buf_joinpath(&refs_path, backend->path, GIT_REFS_DIR) < 0)
return -1;
@@ -388,10 +300,12 @@ static int packed_loadloose(refdb_fs_backend *backend)
* This will overwrite any old packed entries with their
* updated loose versions
*/
- result = git_path_direach(&refs_path, _dirent_loose_load, backend);
+ error = git_path_direach(
+ &refs_path, backend->direach_flags, _dirent_loose_load, backend);
+
git_buf_free(&refs_path);
- return result;
+ return (error == GIT_EUSER) ? -1 : error;
}
static int refdb_fs_backend__exists(
@@ -399,23 +313,17 @@ static int refdb_fs_backend__exists(
git_refdb_backend *_backend,
const char *ref_name)
{
- refdb_fs_backend *backend;
+ refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
git_buf ref_path = GIT_BUF_INIT;
- assert(_backend);
- backend = (refdb_fs_backend *)_backend;
-
- if (packed_load(backend) < 0)
- return -1;
+ assert(backend);
- if (git_buf_joinpath(&ref_path, backend->path, ref_name) < 0)
+ if (packed_reload(backend) < 0 ||
+ git_buf_joinpath(&ref_path, backend->path, ref_name) < 0)
return -1;
- if (git_path_isfile(ref_path.ptr) == true ||
- git_strmap_exists(backend->refcache.packfile, ref_path.ptr))
- *exists = 1;
- else
- *exists = 0;
+ *exists = git_path_isfile(ref_path.ptr) ||
+ (git_sortedcache_lookup(backend->refcache, ref_name) != NULL);
git_buf_free(&ref_path);
return 0;
@@ -447,64 +355,39 @@ static int loose_lookup(
refdb_fs_backend *backend,
const char *ref_name)
{
- const char *target;
- git_oid oid;
git_buf ref_file = GIT_BUF_INIT;
int error = 0;
- error = reference_read(&ref_file, NULL, backend->path, ref_name, NULL);
+ if (out)
+ *out = NULL;
- if (error < 0)
- goto done;
+ if ((error = loose_readbuffer(&ref_file, backend->path, ref_name)) < 0)
+ /* cannot read loose ref file - gah */;
+ else if (git__prefixcmp(git_buf_cstr(&ref_file), GIT_SYMREF) == 0) {
+ const char *target;
- if (git__prefixcmp((const char *)(ref_file.ptr), GIT_SYMREF) == 0) {
git_buf_rtrim(&ref_file);
- if ((target = loose_parse_symbolic(&ref_file)) == NULL) {
+ if (!(target = loose_parse_symbolic(&ref_file)))
error = -1;
- goto done;
- }
-
- *out = git_reference__alloc_symbolic(ref_name, target);
+ else if (out != NULL)
+ *out = git_reference__alloc_symbolic(ref_name, target);
} else {
- if ((error = loose_parse_oid(&oid, ref_name, &ref_file)) < 0)
- goto done;
+ git_oid oid;
- *out = git_reference__alloc(ref_name, &oid, NULL);
+ if (!(error = loose_parse_oid(&oid, ref_name, &ref_file)) &&
+ out != NULL)
+ *out = git_reference__alloc(ref_name, &oid, NULL);
}
- if (*out == NULL)
- error = -1;
-
-done:
git_buf_free(&ref_file);
return error;
}
-static int packed_map_entry(
- struct packref **entry,
- khiter_t *pos,
- refdb_fs_backend *backend,
- const char *ref_name)
+static int ref_error_notfound(const char *name)
{
- git_strmap *packfile_refs;
-
- if (packed_load(backend) < 0)
- return -1;
-
- /* Look up on the packfile */
- packfile_refs = backend->refcache.packfile;
-
- *pos = git_strmap_lookup_index(packfile_refs, ref_name);
-
- if (!git_strmap_valid_index(packfile_refs, *pos)) {
- giterr_set(GITERR_REFERENCE, "Reference '%s' not found", ref_name);
- return GIT_ENOTFOUND;
- }
-
- *entry = git_strmap_value_at(packfile_refs, *pos);
-
- return 0;
+ giterr_set(GITERR_REFERENCE, "Reference '%s' not found", name);
+ return GIT_ENOTFOUND;
}
static int packed_lookup(
@@ -512,18 +395,27 @@ static int packed_lookup(
refdb_fs_backend *backend,
const char *ref_name)
{
- struct packref *entry;
- khiter_t pos;
int error = 0;
+ struct packref *entry;
- if ((error = packed_map_entry(&entry, &pos, backend, ref_name)) < 0)
- return error;
+ if (packed_reload(backend) < 0)
+ return -1;
- if ((*out = git_reference__alloc(ref_name,
- &entry->oid, &entry->peel)) == NULL)
+ if (git_sortedcache_rlock(backend->refcache) < 0)
return -1;
- return 0;
+ entry = git_sortedcache_lookup(backend->refcache, ref_name);
+ if (!entry) {
+ error = ref_error_notfound(ref_name);
+ } else {
+ *out = git_reference__alloc(ref_name, &entry->oid, &entry->peel);
+ if (!*out)
+ error = -1;
+ }
+
+ git_sortedcache_runlock(backend->refcache);
+
+ return error;
}
static int refdb_fs_backend__lookup(
@@ -531,73 +423,68 @@ static int refdb_fs_backend__lookup(
git_refdb_backend *_backend,
const char *ref_name)
{
- refdb_fs_backend *backend;
- int result;
-
- assert(_backend);
+ refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
+ int error;
- backend = (refdb_fs_backend *)_backend;
+ assert(backend);
- if ((result = loose_lookup(out, backend, ref_name)) == 0)
+ if (!(error = loose_lookup(out, backend, ref_name)))
return 0;
/* only try to lookup this reference on the packfile if it
* wasn't found on the loose refs; not if there was a critical error */
- if (result == GIT_ENOTFOUND) {
+ if (error == GIT_ENOTFOUND) {
giterr_clear();
- result = packed_lookup(out, backend, ref_name);
+ error = packed_lookup(out, backend, ref_name);
}
- return result;
+ return error;
}
typedef struct {
git_reference_iterator parent;
char *glob;
+
+ git_pool pool;
git_vector loose;
- unsigned int loose_pos;
- khiter_t packed_pos;
+
+ size_t loose_pos;
+ size_t packed_pos;
} refdb_fs_iter;
static void refdb_fs_backend__iterator_free(git_reference_iterator *_iter)
{
refdb_fs_iter *iter = (refdb_fs_iter *) _iter;
- char *loose_path;
- size_t i;
-
- git_vector_foreach(&iter->loose, i, loose_path) {
- git__free(loose_path);
- }
git_vector_free(&iter->loose);
-
- git__free(iter->glob);
+ git_pool_clear(&iter->pool);
git__free(iter);
}
static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter)
{
- git_strmap *packfile = backend->refcache.packfile;
+ int error = 0;
git_buf path = GIT_BUF_INIT;
- git_iterator *fsit;
+ git_iterator *fsit = NULL;
const git_index_entry *entry = NULL;
if (!backend->path) /* do nothing if no path for loose refs */
return 0;
- if (git_buf_printf(&path, "%s/refs", backend->path) < 0)
- return -1;
-
- if (git_iterator_for_filesystem(&fsit, git_buf_cstr(&path), 0, NULL, NULL) < 0)
- return -1;
+ if ((error = git_buf_printf(&path, "%s/refs", backend->path)) < 0 ||
+ (error = git_iterator_for_filesystem(
+ &fsit, path.ptr, backend->iterator_flags, NULL, NULL)) < 0) {
+ git_buf_free(&path);
+ return error;
+ }
- git_vector_init(&iter->loose, 8, NULL);
- git_buf_sets(&path, GIT_REFS_DIR);
+ error = git_buf_sets(&path, GIT_REFS_DIR);
- while (!git_iterator_advance(&entry, fsit)) {
+ while (!error && !git_iterator_advance(&entry, fsit)) {
const char *ref_name;
- khiter_t pos;
+ struct packref *ref;
+ char *ref_dup;
git_buf_truncate(&path, strlen(GIT_REFS_DIR));
git_buf_puts(&path, entry->path);
@@ -607,27 +494,32 @@ static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter)
(iter->glob && p_fnmatch(iter->glob, ref_name, 0) != 0))
continue;
- pos = git_strmap_lookup_index(packfile, ref_name);
- if (git_strmap_valid_index(packfile, pos)) {
- struct packref *ref = git_strmap_value_at(packfile, pos);
+ git_sortedcache_rlock(backend->refcache);
+ ref = git_sortedcache_lookup(backend->refcache, ref_name);
+ if (ref)
ref->flags |= PACKREF_SHADOWED;
- }
+ git_sortedcache_runlock(backend->refcache);
- git_vector_insert(&iter->loose, git__strdup(ref_name));
+ ref_dup = git_pool_strdup(&iter->pool, ref_name);
+ if (!ref_dup)
+ error = -1;
+ else
+ error = git_vector_insert(&iter->loose, ref_dup);
}
git_iterator_free(fsit);
git_buf_free(&path);
- return 0;
+ return error;
}
static int refdb_fs_backend__iterator_next(
git_reference **out, git_reference_iterator *_iter)
{
+ int error = GIT_ITEROVER;
refdb_fs_iter *iter = (refdb_fs_iter *)_iter;
refdb_fs_backend *backend = (refdb_fs_backend *)iter->parent.db->backend;
- git_strmap *packfile = backend->refcache.packfile;
+ struct packref *ref;
while (iter->loose_pos < iter->loose.length) {
const char *path = git_vector_get(&iter->loose, iter->loose_pos++);
@@ -638,99 +530,102 @@ static int refdb_fs_backend__iterator_next(
giterr_clear();
}
- while (iter->packed_pos < kh_end(packfile)) {
- struct packref *ref = NULL;
-
- while (!kh_exist(packfile, iter->packed_pos)) {
- iter->packed_pos++;
- if (iter->packed_pos == kh_end(packfile))
- return GIT_ITEROVER;
- }
+ git_sortedcache_rlock(backend->refcache);
- ref = kh_val(packfile, iter->packed_pos);
- iter->packed_pos++;
+ while (iter->packed_pos < git_sortedcache_entrycount(backend->refcache)) {
+ ref = git_sortedcache_entry(backend->refcache, iter->packed_pos++);
+ if (!ref) /* stop now if another thread deleted refs and we past end */
+ break;
if (ref->flags & PACKREF_SHADOWED)
continue;
-
if (iter->glob && p_fnmatch(iter->glob, ref->name, 0) != 0)
continue;
*out = git_reference__alloc(ref->name, &ref->oid, &ref->peel);
- if (*out == NULL)
- return -1;
-
- return 0;
+ error = (*out != NULL) ? 0 : -1;
+ break;
}
- return GIT_ITEROVER;
+ git_sortedcache_runlock(backend->refcache);
+ return error;
}
static int refdb_fs_backend__iterator_next_name(
const char **out, git_reference_iterator *_iter)
{
+ int error = GIT_ITEROVER;
refdb_fs_iter *iter = (refdb_fs_iter *)_iter;
refdb_fs_backend *backend = (refdb_fs_backend *)iter->parent.db->backend;
- git_strmap *packfile = backend->refcache.packfile;
+ struct packref *ref;
while (iter->loose_pos < iter->loose.length) {
const char *path = git_vector_get(&iter->loose, iter->loose_pos++);
- if (git_strmap_exists(packfile, path))
- continue;
+ if (loose_lookup(NULL, backend, path) == 0) {
+ *out = path;
+ return 0;
+ }
- *out = path;
- return 0;
+ giterr_clear();
}
- while (iter->packed_pos < kh_end(packfile)) {
- while (!kh_exist(packfile, iter->packed_pos)) {
- iter->packed_pos++;
- if (iter->packed_pos == kh_end(packfile))
- return GIT_ITEROVER;
- }
+ git_sortedcache_rlock(backend->refcache);
- *out = kh_key(packfile, iter->packed_pos);
- iter->packed_pos++;
+ while (iter->packed_pos < git_sortedcache_entrycount(backend->refcache)) {
+ ref = git_sortedcache_entry(backend->refcache, iter->packed_pos++);
+ if (!ref) /* stop now if another thread deleted refs and we past end */
+ break;
- if (iter->glob && p_fnmatch(iter->glob, *out, 0) != 0)
+ if (ref->flags & PACKREF_SHADOWED)
+ continue;
+ if (iter->glob && p_fnmatch(iter->glob, ref->name, 0) != 0)
continue;
- return 0;
+ *out = ref->name;
+ error = 0;
+ break;
}
- return GIT_ITEROVER;
+ git_sortedcache_runlock(backend->refcache);
+ return error;
}
static int refdb_fs_backend__iterator(
git_reference_iterator **out, git_refdb_backend *_backend, const char *glob)
{
refdb_fs_iter *iter;
- refdb_fs_backend *backend;
+ refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
- assert(_backend);
- backend = (refdb_fs_backend *)_backend;
+ assert(backend);
- if (packed_load(backend) < 0)
+ if (packed_reload(backend) < 0)
return -1;
iter = git__calloc(1, sizeof(refdb_fs_iter));
GITERR_CHECK_ALLOC(iter);
- if (glob != NULL)
- iter->glob = git__strdup(glob);
+ if (git_pool_init(&iter->pool, 1, 0) < 0 ||
+ git_vector_init(&iter->loose, 8, NULL) < 0)
+ goto fail;
+
+ if (glob != NULL &&
+ (iter->glob = git_pool_strdup(&iter->pool, glob)) == NULL)
+ goto fail;
iter->parent.next = refdb_fs_backend__iterator_next;
iter->parent.next_name = refdb_fs_backend__iterator_next_name;
iter->parent.free = refdb_fs_backend__iterator_free;
- if (iter_load_loose_paths(backend, iter) < 0) {
- refdb_fs_backend__iterator_free((git_reference_iterator *)iter);
- return -1;
- }
+ if (iter_load_loose_paths(backend, iter) < 0)
+ goto fail;
*out = (git_reference_iterator *)iter;
return 0;
+
+fail:
+ refdb_fs_backend__iterator_free((git_reference_iterator *)iter);
+ return -1;
}
static bool ref_is_available(
@@ -756,33 +651,40 @@ static int reference_path_available(
const char* old_ref,
int force)
{
- struct packref *this_ref;
+ size_t i;
- if (packed_load(backend) < 0)
+ if (packed_reload(backend) < 0)
return -1;
if (!force) {
int exists;
- if (refdb_fs_backend__exists(&exists, (git_refdb_backend *)backend, new_ref) < 0)
+ if (refdb_fs_backend__exists(
+ &exists, (git_refdb_backend *)backend, new_ref) < 0)
return -1;
if (exists) {
giterr_set(GITERR_REFERENCE,
"Failed to write reference '%s': a reference with "
- " that name already exists.", new_ref);
+ "that name already exists.", new_ref);
return GIT_EEXISTS;
}
}
- git_strmap_foreach_value(backend->refcache.packfile, this_ref, {
- if (!ref_is_available(old_ref, new_ref, this_ref->name)) {
+ git_sortedcache_rlock(backend->refcache);
+
+ for (i = 0; i < git_sortedcache_entrycount(backend->refcache); ++i) {
+ struct packref *ref = git_sortedcache_entry(backend->refcache, i);
+
+ if (ref && !ref_is_available(old_ref, new_ref, ref->name)) {
+ git_sortedcache_runlock(backend->refcache);
giterr_set(GITERR_REFERENCE,
- "The path to reference '%s' collides with an existing one", new_ref);
+ "Path to reference '%s' collides with existing one", new_ref);
return -1;
}
- });
-
+ }
+
+ git_sortedcache_runlock(backend->refcache);
return 0;
}
@@ -800,7 +702,7 @@ static int loose_write(refdb_fs_backend *backend, const git_reference *ref)
if (git_buf_joinpath(&ref_path, backend->path, ref->name) < 0)
return -1;
- if (git_filebuf_open(&file, ref_path.ptr, GIT_FILEBUF_FORCE) < 0) {
+ if (git_filebuf_open(&file, ref_path.ptr, GIT_FILEBUF_FORCE, GIT_REFS_FILE_MODE) < 0) {
git_buf_free(&ref_path);
return -1;
}
@@ -809,27 +711,16 @@ static int loose_write(refdb_fs_backend *backend, const git_reference *ref)
if (ref->type == GIT_REF_OID) {
char oid[GIT_OID_HEXSZ + 1];
-
- git_oid_fmt(oid, &ref->target.oid);
- oid[GIT_OID_HEXSZ] = '\0';
+ git_oid_nfmt(oid, sizeof(oid), &ref->target.oid);
git_filebuf_printf(&file, "%s\n", oid);
-
} else if (ref->type == GIT_REF_SYMBOLIC) {
git_filebuf_printf(&file, GIT_SYMREF "%s\n", ref->target.symbolic);
} else {
assert(0); /* don't let this happen */
}
- return git_filebuf_commit(&file, GIT_REFS_FILE_MODE);
-}
-
-static int packed_sort(const void *a, const void *b)
-{
- const struct packref *ref_a = (const struct packref *)a;
- const struct packref *ref_b = (const struct packref *)b;
-
- return strcmp(ref_a->name, ref_b->name);
+ return git_filebuf_commit(&file);
}
/*
@@ -884,9 +775,7 @@ static int packed_find_peel(refdb_fs_backend *backend, struct packref *ref)
static int packed_write_ref(struct packref *ref, git_filebuf *file)
{
char oid[GIT_OID_HEXSZ + 1];
-
- git_oid_fmt(oid, &ref->oid);
- oid[GIT_OID_HEXSZ] = 0;
+ git_oid_nfmt(oid, sizeof(oid), &ref->oid);
/*
* For references that peel to an object in the repo, we must
@@ -900,8 +789,7 @@ static int packed_write_ref(struct packref *ref, git_filebuf *file)
*/
if (ref->flags & PACKREF_HAS_PEEL) {
char peel[GIT_OID_HEXSZ + 1];
- git_oid_fmt(peel, &ref->peel);
- peel[GIT_OID_HEXSZ] = 0;
+ git_oid_nfmt(peel, sizeof(peel), &ref->peel);
if (git_filebuf_printf(file, "%s %s\n^%s\n", oid, ref->name, peel) < 0)
return -1;
@@ -924,31 +812,30 @@ static int packed_write_ref(struct packref *ref, git_filebuf *file)
* is well-written, because we are destructing references
* here otherwise.
*/
-static int packed_remove_loose(
- refdb_fs_backend *backend,
- git_vector *packing_list)
+static int packed_remove_loose(refdb_fs_backend *backend)
{
size_t i;
git_buf full_path = GIT_BUF_INIT;
int failed = 0;
- for (i = 0; i < packing_list->length; ++i) {
- struct packref *ref = git_vector_get(packing_list, i);
+ /* backend->refcache is already locked when this is called */
+
+ for (i = 0; i < git_sortedcache_entrycount(backend->refcache); ++i) {
+ struct packref *ref = git_sortedcache_entry(backend->refcache, i);
- if ((ref->flags & PACKREF_WAS_LOOSE) == 0)
+ if (!ref || !(ref->flags & PACKREF_WAS_LOOSE))
continue;
if (git_buf_joinpath(&full_path, backend->path, ref->name) < 0)
return -1; /* critical; do not try to recover on oom */
- if (git_path_exists(full_path.ptr) == true && p_unlink(full_path.ptr) < 0) {
+ if (git_path_exists(full_path.ptr) && p_unlink(full_path.ptr) < 0) {
if (failed)
continue;
giterr_set(GITERR_REFERENCE,
"Failed to remove loose reference '%s' after packing: %s",
full_path.ptr, strerror(errno));
-
failed = 1;
}
@@ -969,85 +856,53 @@ static int packed_remove_loose(
*/
static int packed_write(refdb_fs_backend *backend)
{
+ git_sortedcache *refcache = backend->refcache;
git_filebuf pack_file = GIT_FILEBUF_INIT;
size_t i;
- git_buf pack_file_path = GIT_BUF_INIT;
- git_vector packing_list;
- unsigned int total_refs;
-
- assert(backend && backend->refcache.packfile);
-
- total_refs =
- (unsigned int)git_strmap_num_entries(backend->refcache.packfile);
- if (git_vector_init(&packing_list, total_refs, packed_sort) < 0)
+ /* lock the cache to updates while we do this */
+ if (git_sortedcache_wlock(refcache) < 0)
return -1;
- /* Load all the packfile into a vector */
- {
- struct packref *reference;
-
- /* cannot fail: vector already has the right size */
- git_strmap_foreach_value(backend->refcache.packfile, reference, {
- git_vector_insert(&packing_list, reference);
- });
- }
-
- /* sort the vector so the entries appear sorted on the packfile */
- git_vector_sort(&packing_list);
-
- /* Now we can open the file! */
- if (git_buf_joinpath(&pack_file_path,
- backend->path, GIT_PACKEDREFS_FILE) < 0)
- goto cleanup_memory;
-
- if (git_filebuf_open(&pack_file, pack_file_path.ptr, 0) < 0)
- goto cleanup_packfile;
+ /* Open the file! */
+ if (git_filebuf_open(&pack_file, git_sortedcache_path(refcache), 0, GIT_PACKEDREFS_FILE_MODE) < 0)
+ goto fail;
/* Packfiles have a header... apparently
* This is in fact not required, but we might as well print it
* just for kicks */
if (git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER) < 0)
- goto cleanup_packfile;
+ goto fail;
- for (i = 0; i < packing_list.length; ++i) {
- struct packref *ref = (struct packref *)git_vector_get(&packing_list, i);
+ for (i = 0; i < git_sortedcache_entrycount(refcache); ++i) {
+ struct packref *ref = git_sortedcache_entry(refcache, i);
if (packed_find_peel(backend, ref) < 0)
- goto cleanup_packfile;
+ goto fail;
if (packed_write_ref(ref, &pack_file) < 0)
- goto cleanup_packfile;
+ goto fail;
}
/* if we've written all the references properly, we can commit
* the packfile to make the changes effective */
- if (git_filebuf_commit(&pack_file, GIT_PACKEDREFS_FILE_MODE) < 0)
- goto cleanup_memory;
+ if (git_filebuf_commit(&pack_file) < 0)
+ goto fail;
/* when and only when the packfile has been properly written,
* we can go ahead and remove the loose refs */
- if (packed_remove_loose(backend, &packing_list) < 0)
- goto cleanup_memory;
-
- {
- struct stat st;
- if (p_stat(pack_file_path.ptr, &st) == 0)
- backend->refcache.packfile_time = st.st_mtime;
- }
+ if (packed_remove_loose(backend) < 0)
+ goto fail;
- git_vector_free(&packing_list);
- git_buf_free(&pack_file_path);
+ git_sortedcache_updated(refcache);
+ git_sortedcache_wunlock(refcache);
/* we're good now */
return 0;
-cleanup_packfile:
+fail:
git_filebuf_cleanup(&pack_file);
-
-cleanup_memory:
- git_vector_free(&packing_list);
- git_buf_free(&pack_file_path);
+ git_sortedcache_wunlock(refcache);
return -1;
}
@@ -1057,11 +912,10 @@ static int refdb_fs_backend__write(
const git_reference *ref,
int force)
{
- refdb_fs_backend *backend;
+ refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
int error;
- assert(_backend);
- backend = (refdb_fs_backend *)_backend;
+ assert(backend);
error = reference_path_available(backend, ref->name, NULL, force);
if (error < 0)
@@ -1074,17 +928,13 @@ static int refdb_fs_backend__delete(
git_refdb_backend *_backend,
const char *ref_name)
{
- refdb_fs_backend *backend;
+ refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
git_buf loose_path = GIT_BUF_INIT;
- struct packref *pack_ref;
- khiter_t pack_ref_pos;
+ size_t pack_pos;
int error = 0;
bool loose_deleted = 0;
- assert(_backend);
- assert(ref_name);
-
- backend = (refdb_fs_backend *)_backend;
+ assert(backend && ref_name);
/* If a loose reference exists, remove it from the filesystem */
if (git_buf_joinpath(&loose_path, backend->path, ref_name) < 0)
@@ -1100,19 +950,23 @@ static int refdb_fs_backend__delete(
if (error != 0)
return error;
+ if (packed_reload(backend) < 0)
+ return -1;
+
/* If a packed reference exists, remove it from the packfile and repack */
- error = packed_map_entry(&pack_ref, &pack_ref_pos, backend, ref_name);
+ if (git_sortedcache_wlock(backend->refcache) < 0)
+ return -1;
- if (error == GIT_ENOTFOUND)
- return loose_deleted ? 0 : GIT_ENOTFOUND;
+ if (!(error = git_sortedcache_lookup_index(
+ &pack_pos, backend->refcache, ref_name)))
+ error = git_sortedcache_remove(backend->refcache, pack_pos);
- if (error == 0) {
- git_strmap_delete_at(backend->refcache.packfile, pack_ref_pos);
- git__free(pack_ref);
- error = packed_write(backend);
- }
+ git_sortedcache_wunlock(backend->refcache);
- return error;
+ if (error == GIT_ENOTFOUND)
+ return loose_deleted ? 0 : ref_error_notfound(ref_name);
+
+ return packed_write(backend);
}
static int refdb_fs_backend__rename(
@@ -1122,53 +976,44 @@ static int refdb_fs_backend__rename(
const char *new_name,
int force)
{
- refdb_fs_backend *backend;
+ refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
git_reference *old, *new;
int error;
- assert(_backend);
- backend = (refdb_fs_backend *)_backend;
+ assert(backend);
- error = reference_path_available(backend, new_name, old_name, force);
- if (error < 0)
+ if ((error = reference_path_available(
+ backend, new_name, old_name, force)) < 0 ||
+ (error = refdb_fs_backend__lookup(&old, _backend, old_name)) < 0)
return error;
- error = refdb_fs_backend__lookup(&old, _backend, old_name);
- if (error < 0)
- return error;
-
- error = refdb_fs_backend__delete(_backend, old_name);
- if (error < 0) {
+ if ((error = refdb_fs_backend__delete(_backend, old_name)) < 0) {
git_reference_free(old);
return error;
}
- new = realloc(old, sizeof(git_reference) + strlen(new_name) + 1);
- memcpy(new->name, new_name, strlen(new_name) + 1);
-
- error = loose_write(backend, new);
- if (error < 0) {
- git_reference_free(new);
- return error;
+ new = git_reference__set_name(old, new_name);
+ if (!new) {
+ git_reference_free(old);
+ return -1;
}
- if (out) {
- *out = new;
- } else {
+ if ((error = loose_write(backend, new)) < 0 || out == NULL) {
git_reference_free(new);
+ return error;
}
+ *out = new;
return 0;
}
static int refdb_fs_backend__compress(git_refdb_backend *_backend)
{
- refdb_fs_backend *backend;
+ refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
- assert(_backend);
- backend = (refdb_fs_backend *)_backend;
+ assert(backend);
- if (packed_load(backend) < 0 || /* load the existing packfile */
+ if (packed_reload(backend) < 0 || /* load the existing packfile */
packed_loadloose(backend) < 0 || /* add all the loose refs */
packed_write(backend) < 0) /* write back to disk */
return -1;
@@ -1176,29 +1021,13 @@ static int refdb_fs_backend__compress(git_refdb_backend *_backend)
return 0;
}
-static void refcache_free(git_refcache *refs)
-{
- assert(refs);
-
- if (refs->packfile) {
- struct packref *reference;
-
- git_strmap_foreach_value(refs->packfile, reference, {
- git__free(reference);
- });
-
- git_strmap_free(refs->packfile);
- }
-}
-
static void refdb_fs_backend__free(git_refdb_backend *_backend)
{
- refdb_fs_backend *backend;
+ refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
- assert(_backend);
- backend = (refdb_fs_backend *)_backend;
+ assert(backend);
- refcache_free(&backend->refcache);
+ git_sortedcache_free(backend->refcache);
git__free(backend->path);
git__free(backend);
}
@@ -1222,7 +1051,7 @@ static int setup_namespace(git_buf *path, git_repository *repo)
if (parts == NULL)
return -1;
- /**
+ /*
* From `man gitnamespaces`:
* namespaces which include a / will expand to a hierarchy
* of namespaces; for example, GIT_NAMESPACE=foo/bar will store
@@ -1239,15 +1068,355 @@ static int setup_namespace(git_buf *path, git_repository *repo)
if (git_futils_mkdir_r(git_buf_cstr(path), repo->path_repository, 0777) < 0)
return -1;
- /* Return the root of the namespaced path, i.e. without the trailing '/refs' */
+ /* Return root of the namespaced path, i.e. without the trailing '/refs' */
git_buf_rtruncate_at_char(path, '/');
return 0;
}
+static int reflog_alloc(git_reflog **reflog, const char *name)
+{
+ git_reflog *log;
+
+ *reflog = NULL;
+
+ log = git__calloc(1, sizeof(git_reflog));
+ GITERR_CHECK_ALLOC(log);
+
+ log->ref_name = git__strdup(name);
+ GITERR_CHECK_ALLOC(log->ref_name);
+
+ if (git_vector_init(&log->entries, 0, NULL) < 0) {
+ git__free(log->ref_name);
+ git__free(log);
+ return -1;
+ }
+
+ *reflog = log;
+
+ return 0;
+}
+
+static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size)
+{
+ const char *ptr;
+ git_reflog_entry *entry;
+
+#define seek_forward(_increase) do { \
+ if (_increase >= buf_size) { \
+ giterr_set(GITERR_INVALID, "Ran out of data while parsing reflog"); \
+ goto fail; \
+ } \
+ buf += _increase; \
+ buf_size -= _increase; \
+ } while (0)
+
+ while (buf_size > GIT_REFLOG_SIZE_MIN) {
+ entry = git__calloc(1, sizeof(git_reflog_entry));
+ GITERR_CHECK_ALLOC(entry);
+
+ entry->committer = git__malloc(sizeof(git_signature));
+ GITERR_CHECK_ALLOC(entry->committer);
+
+ if (git_oid_fromstrn(&entry->oid_old, buf, GIT_OID_HEXSZ) < 0)
+ goto fail;
+ seek_forward(GIT_OID_HEXSZ + 1);
+
+ if (git_oid_fromstrn(&entry->oid_cur, buf, GIT_OID_HEXSZ) < 0)
+ goto fail;
+ seek_forward(GIT_OID_HEXSZ + 1);
+
+ ptr = buf;
+
+ /* Seek forward to the end of the signature. */
+ while (*buf && *buf != '\t' && *buf != '\n')
+ seek_forward(1);
+
+ if (git_signature__parse(entry->committer, &ptr, buf + 1, NULL, *buf) < 0)
+ goto fail;
+
+ if (*buf == '\t') {
+ /* We got a message. Read everything till we reach LF. */
+ seek_forward(1);
+ ptr = buf;
+
+ while (*buf && *buf != '\n')
+ seek_forward(1);
+
+ entry->msg = git__strndup(ptr, buf - ptr);
+ GITERR_CHECK_ALLOC(entry->msg);
+ } else
+ entry->msg = NULL;
+
+ while (*buf && *buf == '\n' && buf_size > 1)
+ seek_forward(1);
+
+ if (git_vector_insert(&log->entries, entry) < 0)
+ goto fail;
+ }
+
+ return 0;
+
+#undef seek_forward
+
+fail:
+ if (entry)
+ git_reflog_entry__free(entry);
+
+ return -1;
+}
+
+static int create_new_reflog_file(const char *filepath)
+{
+ int fd, error;
+
+ if ((error = git_futils_mkpath2file(filepath, GIT_REFLOG_DIR_MODE)) < 0)
+ return error;
+
+ if ((fd = p_open(filepath,
+ O_WRONLY | O_CREAT | O_TRUNC,
+ GIT_REFLOG_FILE_MODE)) < 0)
+ return -1;
+
+ return p_close(fd);
+}
+
+GIT_INLINE(int) retrieve_reflog_path(git_buf *path, git_repository *repo, const char *name)
+{
+ return git_buf_join_n(path, '/', 3, repo->path_repository, GIT_REFLOG_DIR, name);
+}
+
+static int refdb_reflog_fs__read(git_reflog **out, git_refdb_backend *_backend, const char *name)
+{
+ int error = -1;
+ git_buf log_path = GIT_BUF_INIT;
+ git_buf log_file = GIT_BUF_INIT;
+ git_reflog *log = NULL;
+ git_repository *repo;
+ refdb_fs_backend *backend;
+
+ assert(out && _backend && name);
+
+ backend = (refdb_fs_backend *) _backend;
+ repo = backend->repo;
+
+ if (reflog_alloc(&log, name) < 0)
+ return -1;
+
+ if (retrieve_reflog_path(&log_path, repo, name) < 0)
+ goto cleanup;
+
+ error = git_futils_readbuffer(&log_file, git_buf_cstr(&log_path));
+ if (error < 0 && error != GIT_ENOTFOUND)
+ goto cleanup;
+
+ if ((error == GIT_ENOTFOUND) &&
+ ((error = create_new_reflog_file(git_buf_cstr(&log_path))) < 0))
+ goto cleanup;
+
+ if ((error = reflog_parse(log,
+ git_buf_cstr(&log_file), git_buf_len(&log_file))) < 0)
+ goto cleanup;
+
+ *out = log;
+ goto success;
+
+cleanup:
+ git_reflog_free(log);
+
+success:
+ git_buf_free(&log_file);
+ git_buf_free(&log_path);
+
+ return error;
+}
+
+static int serialize_reflog_entry(
+ git_buf *buf,
+ const git_oid *oid_old,
+ const git_oid *oid_new,
+ const git_signature *committer,
+ const char *msg)
+{
+ char raw_old[GIT_OID_HEXSZ+1];
+ char raw_new[GIT_OID_HEXSZ+1];
+
+ git_oid_tostr(raw_old, GIT_OID_HEXSZ+1, oid_old);
+ git_oid_tostr(raw_new, GIT_OID_HEXSZ+1, oid_new);
+
+ git_buf_clear(buf);
+
+ git_buf_puts(buf, raw_old);
+ git_buf_putc(buf, ' ');
+ git_buf_puts(buf, raw_new);
+
+ git_signature__writebuf(buf, " ", committer);
+
+ /* drop trailing LF */
+ git_buf_rtrim(buf);
+
+ if (msg) {
+ git_buf_putc(buf, '\t');
+ git_buf_puts(buf, msg);
+ }
+
+ git_buf_putc(buf, '\n');
+
+ return git_buf_oom(buf);
+}
+
+static int refdb_reflog_fs__write(git_refdb_backend *_backend, git_reflog *reflog)
+{
+ int error = -1;
+ unsigned int i;
+ git_reflog_entry *entry;
+ git_repository *repo;
+ refdb_fs_backend *backend;
+ git_buf log_path = GIT_BUF_INIT;
+ git_buf log = GIT_BUF_INIT;
+ git_filebuf fbuf = GIT_FILEBUF_INIT;
+
+ assert(_backend && reflog);
+
+ backend = (refdb_fs_backend *) _backend;
+ repo = backend->repo;
+
+ if (retrieve_reflog_path(&log_path, repo, reflog->ref_name) < 0)
+ return -1;
+
+ if (!git_path_isfile(git_buf_cstr(&log_path))) {
+ giterr_set(GITERR_INVALID,
+ "Log file for reference '%s' doesn't exist.", reflog->ref_name);
+ goto cleanup;
+ }
+
+ if ((error = git_filebuf_open(&fbuf, git_buf_cstr(&log_path), 0, GIT_REFLOG_FILE_MODE)) < 0)
+ goto cleanup;
+
+ git_vector_foreach(&reflog->entries, i, entry) {
+ if (serialize_reflog_entry(&log, &(entry->oid_old), &(entry->oid_cur), entry->committer, entry->msg) < 0)
+ goto cleanup;
+
+ if ((error = git_filebuf_write(&fbuf, log.ptr, log.size)) < 0)
+ goto cleanup;
+ }
+
+ error = git_filebuf_commit(&fbuf);
+ goto success;
+
+cleanup:
+ git_filebuf_cleanup(&fbuf);
+
+success:
+ git_buf_free(&log);
+ git_buf_free(&log_path);
+ return error;
+}
+
+static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_name, const char *new_name)
+{
+ int error = 0, fd;
+ git_buf old_path = GIT_BUF_INIT;
+ git_buf new_path = GIT_BUF_INIT;
+ git_buf temp_path = GIT_BUF_INIT;
+ git_buf normalized = GIT_BUF_INIT;
+ git_repository *repo;
+ refdb_fs_backend *backend;
+
+ assert(_backend && old_name && new_name);
+
+ backend = (refdb_fs_backend *) _backend;
+ repo = backend->repo;
+
+ if ((error = git_reference__normalize_name(
+ &normalized, new_name, GIT_REF_FORMAT_ALLOW_ONELEVEL)) < 0)
+ return error;
+
+ if (git_buf_joinpath(&temp_path, repo->path_repository, GIT_REFLOG_DIR) < 0)
+ return -1;
+
+ if (git_buf_joinpath(&old_path, git_buf_cstr(&temp_path), old_name) < 0)
+ return -1;
+
+ if (git_buf_joinpath(&new_path, git_buf_cstr(&temp_path), git_buf_cstr(&normalized)) < 0)
+ return -1;
+
+ /*
+ * Move the reflog to a temporary place. This two-phase renaming is required
+ * in order to cope with funny renaming use cases when one tries to move a reference
+ * to a partially colliding namespace:
+ * - a/b -> a/b/c
+ * - a/b/c/d -> a/b/c
+ */
+ if (git_buf_joinpath(&temp_path, git_buf_cstr(&temp_path), "temp_reflog") < 0)
+ return -1;
+
+ if ((fd = git_futils_mktmp(&temp_path, git_buf_cstr(&temp_path), GIT_REFLOG_FILE_MODE)) < 0) {
+ error = -1;
+ goto cleanup;
+ }
+
+ p_close(fd);
+
+ if (p_rename(git_buf_cstr(&old_path), git_buf_cstr(&temp_path)) < 0) {
+ giterr_set(GITERR_OS, "Failed to rename reflog for %s", new_name);
+ error = -1;
+ goto cleanup;
+ }
+
+ if (git_path_isdir(git_buf_cstr(&new_path)) &&
+ (git_futils_rmdir_r(git_buf_cstr(&new_path), NULL, GIT_RMDIR_SKIP_NONEMPTY) < 0)) {
+ error = -1;
+ goto cleanup;
+ }
+
+ if (git_futils_mkpath2file(git_buf_cstr(&new_path), GIT_REFLOG_DIR_MODE) < 0) {
+ error = -1;
+ goto cleanup;
+ }
+
+ if (p_rename(git_buf_cstr(&temp_path), git_buf_cstr(&new_path)) < 0) {
+ giterr_set(GITERR_OS, "Failed to rename reflog for %s", new_name);
+ error = -1;
+ }
+
+cleanup:
+ git_buf_free(&temp_path);
+ git_buf_free(&old_path);
+ git_buf_free(&new_path);
+ git_buf_free(&normalized);
+
+ return error;
+}
+
+static int refdb_reflog_fs__delete(git_refdb_backend *_backend, const char *name)
+{
+ int error;
+ git_buf path = GIT_BUF_INIT;
+
+ git_repository *repo;
+ refdb_fs_backend *backend;
+
+ assert(_backend && name);
+
+ backend = (refdb_fs_backend *) _backend;
+ repo = backend->repo;
+
+ error = retrieve_reflog_path(&path, repo, name);
+
+ if (!error && git_path_exists(path.ptr))
+ error = p_unlink(path.ptr);
+
+ git_buf_free(&path);
+
+ return error;
+
+}
+
int git_refdb_backend_fs(
git_refdb_backend **backend_out,
git_repository *repository)
{
+ int t = 0;
git_buf path = GIT_BUF_INIT;
refdb_fs_backend *backend;
@@ -1256,22 +1425,47 @@ int git_refdb_backend_fs(
backend->repo = repository;
- if (setup_namespace(&path, repository) < 0) {
- git__free(backend);
- return -1;
- }
+ if (setup_namespace(&path, repository) < 0)
+ goto fail;
backend->path = git_buf_detach(&path);
+ if (git_buf_joinpath(&path, backend->path, GIT_PACKEDREFS_FILE) < 0 ||
+ git_sortedcache_new(
+ &backend->refcache, offsetof(struct packref, name),
+ NULL, NULL, packref_cmp, git_buf_cstr(&path)) < 0)
+ goto fail;
+
+ git_buf_free(&path);
+
+ if (!git_repository__cvar(&t, backend->repo, GIT_CVAR_IGNORECASE) && t) {
+ backend->iterator_flags |= GIT_ITERATOR_IGNORE_CASE;
+ backend->direach_flags |= GIT_PATH_DIR_IGNORE_CASE;
+ }
+ if (!git_repository__cvar(&t, backend->repo, GIT_CVAR_PRECOMPOSE) && t) {
+ backend->iterator_flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE;
+ backend->direach_flags |= GIT_PATH_DIR_PRECOMPOSE_UNICODE;
+ }
+
backend->parent.exists = &refdb_fs_backend__exists;
backend->parent.lookup = &refdb_fs_backend__lookup;
backend->parent.iterator = &refdb_fs_backend__iterator;
backend->parent.write = &refdb_fs_backend__write;
- backend->parent.delete = &refdb_fs_backend__delete;
+ backend->parent.del = &refdb_fs_backend__delete;
backend->parent.rename = &refdb_fs_backend__rename;
backend->parent.compress = &refdb_fs_backend__compress;
backend->parent.free = &refdb_fs_backend__free;
+ backend->parent.reflog_read = &refdb_reflog_fs__read;
+ backend->parent.reflog_write = &refdb_reflog_fs__write;
+ backend->parent.reflog_rename = &refdb_reflog_fs__rename;
+ backend->parent.reflog_delete = &refdb_reflog_fs__delete;
*backend_out = (git_refdb_backend *)backend;
return 0;
+
+fail:
+ git_buf_free(&path);
+ git__free(backend->path);
+ git__free(backend);
+ return -1;
}
diff --git a/src/reflog.c b/src/reflog.c
index 4cc20d2c7..cebb87d86 100644
--- a/src/reflog.c
+++ b/src/reflog.c
@@ -9,82 +9,16 @@
#include "repository.h"
#include "filebuf.h"
#include "signature.h"
+#include "refdb.h"
-static int reflog_init(git_reflog **reflog, const git_reference *ref)
-{
- git_reflog *log;
-
- *reflog = NULL;
-
- log = git__calloc(1, sizeof(git_reflog));
- GITERR_CHECK_ALLOC(log);
-
- log->ref_name = git__strdup(ref->name);
- GITERR_CHECK_ALLOC(log->ref_name);
-
- if (git_vector_init(&log->entries, 0, NULL) < 0) {
- git__free(log->ref_name);
- git__free(log);
- return -1;
- }
-
- log->owner = git_reference_owner(ref);
- *reflog = log;
+#include <git2/sys/refdb_backend.h>
- return 0;
-}
-
-static int serialize_reflog_entry(
- git_buf *buf,
- const git_oid *oid_old,
- const git_oid *oid_new,
- const git_signature *committer,
- const char *msg)
+git_reflog_entry *git_reflog_entry__alloc(void)
{
- char raw_old[GIT_OID_HEXSZ+1];
- char raw_new[GIT_OID_HEXSZ+1];
-
- git_oid_tostr(raw_old, GIT_OID_HEXSZ+1, oid_old);
- git_oid_tostr(raw_new, GIT_OID_HEXSZ+1, oid_new);
-
- git_buf_clear(buf);
-
- git_buf_puts(buf, raw_old);
- git_buf_putc(buf, ' ');
- git_buf_puts(buf, raw_new);
-
- git_signature__writebuf(buf, " ", committer);
-
- /* drop trailing LF */
- git_buf_rtrim(buf);
-
- if (msg) {
- git_buf_putc(buf, '\t');
- git_buf_puts(buf, msg);
- }
-
- git_buf_putc(buf, '\n');
-
- return git_buf_oom(buf);
-}
-
-static int reflog_entry_new(git_reflog_entry **entry)
-{
- git_reflog_entry *e;
-
- assert(entry);
-
- e = git__malloc(sizeof(git_reflog_entry));
- GITERR_CHECK_ALLOC(e);
-
- memset(e, 0, sizeof(git_reflog_entry));
-
- *entry = e;
-
- return 0;
+ return git__calloc(1, sizeof(git_reflog_entry));
}
-static void reflog_entry_free(git_reflog_entry *entry)
+void git_reflog_entry__free(git_reflog_entry *entry)
{
git_signature_free(entry->committer);
@@ -92,75 +26,6 @@ static void reflog_entry_free(git_reflog_entry *entry)
git__free(entry);
}
-static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size)
-{
- const char *ptr;
- git_reflog_entry *entry;
-
-#define seek_forward(_increase) do { \
- if (_increase >= buf_size) { \
- giterr_set(GITERR_INVALID, "Ran out of data while parsing reflog"); \
- goto fail; \
- } \
- buf += _increase; \
- buf_size -= _increase; \
- } while (0)
-
- while (buf_size > GIT_REFLOG_SIZE_MIN) {
- if (reflog_entry_new(&entry) < 0)
- return -1;
-
- entry->committer = git__malloc(sizeof(git_signature));
- GITERR_CHECK_ALLOC(entry->committer);
-
- if (git_oid_fromstrn(&entry->oid_old, buf, GIT_OID_HEXSZ) < 0)
- goto fail;
- seek_forward(GIT_OID_HEXSZ + 1);
-
- if (git_oid_fromstrn(&entry->oid_cur, buf, GIT_OID_HEXSZ) < 0)
- goto fail;
- seek_forward(GIT_OID_HEXSZ + 1);
-
- ptr = buf;
-
- /* Seek forward to the end of the signature. */
- while (*buf && *buf != '\t' && *buf != '\n')
- seek_forward(1);
-
- if (git_signature__parse(entry->committer, &ptr, buf + 1, NULL, *buf) < 0)
- goto fail;
-
- if (*buf == '\t') {
- /* We got a message. Read everything till we reach LF. */
- seek_forward(1);
- ptr = buf;
-
- while (*buf && *buf != '\n')
- seek_forward(1);
-
- entry->msg = git__strndup(ptr, buf - ptr);
- GITERR_CHECK_ALLOC(entry->msg);
- } else
- entry->msg = NULL;
-
- while (*buf && *buf == '\n' && buf_size > 1)
- seek_forward(1);
-
- if (git_vector_insert(&log->entries, entry) < 0)
- goto fail;
- }
-
- return 0;
-
-#undef seek_forward
-
-fail:
- if (entry)
- reflog_entry_free(entry);
-
- return -1;
-}
-
void git_reflog_free(git_reflog *reflog)
{
size_t i;
@@ -169,10 +34,13 @@ void git_reflog_free(git_reflog *reflog)
if (reflog == NULL)
return;
+ if (reflog->db)
+ GIT_REFCOUNT_DEC(reflog->db, git_refdb__free);
+
for (i=0; i < reflog->entries.length; i++) {
entry = git_vector_get(&reflog->entries, i);
- reflog_entry_free(entry);
+ git_reflog_entry__free(entry);
}
git_vector_free(&reflog->entries);
@@ -180,115 +48,30 @@ void git_reflog_free(git_reflog *reflog)
git__free(reflog);
}
-static int retrieve_reflog_path(git_buf *path, const git_reference *ref)
+int git_reflog_read(git_reflog **reflog, git_repository *repo, const char *name)
{
- return git_buf_join_n(path, '/', 3,
- git_reference_owner(ref)->path_repository, GIT_REFLOG_DIR, ref->name);
-}
+ git_refdb *refdb;
+ int error;
-static int create_new_reflog_file(const char *filepath)
-{
- int fd, error;
+ assert(reflog && repo && name);
- if ((error = git_futils_mkpath2file(filepath, GIT_REFLOG_DIR_MODE)) < 0)
+ if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
return error;
- if ((fd = p_open(filepath,
- O_WRONLY | O_CREAT | O_TRUNC,
- GIT_REFLOG_FILE_MODE)) < 0)
- return -1;
-
- return p_close(fd);
-}
-
-int git_reflog_read(git_reflog **reflog, const git_reference *ref)
-{
- int error = -1;
- git_buf log_path = GIT_BUF_INIT;
- git_buf log_file = GIT_BUF_INIT;
- git_reflog *log = NULL;
-
- assert(reflog && ref);
-
- *reflog = NULL;
-
- if (reflog_init(&log, ref) < 0)
- return -1;
-
- if (retrieve_reflog_path(&log_path, ref) < 0)
- goto cleanup;
-
- error = git_futils_readbuffer(&log_file, git_buf_cstr(&log_path));
- if (error < 0 && error != GIT_ENOTFOUND)
- goto cleanup;
-
- if ((error == GIT_ENOTFOUND) &&
- ((error = create_new_reflog_file(git_buf_cstr(&log_path))) < 0))
- goto cleanup;
-
- if ((error = reflog_parse(log,
- git_buf_cstr(&log_file), git_buf_len(&log_file))) < 0)
- goto cleanup;
-
- *reflog = log;
- goto success;
-
-cleanup:
- git_reflog_free(log);
-
-success:
- git_buf_free(&log_file);
- git_buf_free(&log_path);
-
- return error;
+ return git_refdb_reflog_read(reflog, refdb, name);
}
int git_reflog_write(git_reflog *reflog)
{
- int error = -1;
- unsigned int i;
- git_reflog_entry *entry;
- git_buf log_path = GIT_BUF_INIT;
- git_buf log = GIT_BUF_INIT;
- git_filebuf fbuf = GIT_FILEBUF_INIT;
+ git_refdb *db;
- assert(reflog);
+ assert(reflog && reflog->db);
- if (git_buf_join_n(&log_path, '/', 3,
- git_repository_path(reflog->owner), GIT_REFLOG_DIR, reflog->ref_name) < 0)
- return -1;
-
- if (!git_path_isfile(git_buf_cstr(&log_path))) {
- giterr_set(GITERR_INVALID,
- "Log file for reference '%s' doesn't exist.", reflog->ref_name);
- goto cleanup;
- }
-
- if ((error = git_filebuf_open(&fbuf, git_buf_cstr(&log_path), 0)) < 0)
- goto cleanup;
-
- git_vector_foreach(&reflog->entries, i, entry) {
- if (serialize_reflog_entry(&log, &(entry->oid_old), &(entry->oid_cur), entry->committer, entry->msg) < 0)
- goto cleanup;
-
- if ((error = git_filebuf_write(&fbuf, log.ptr, log.size)) < 0)
- goto cleanup;
- }
-
- error = git_filebuf_commit(&fbuf, GIT_REFLOG_FILE_MODE);
- goto success;
-
-cleanup:
- git_filebuf_cleanup(&fbuf);
-
-success:
- git_buf_free(&log);
- git_buf_free(&log_path);
- return error;
+ db = reflog->db;
+ return db->backend->reflog_write(db->backend, reflog);
}
-int git_reflog_append(git_reflog *reflog, const git_oid *new_oid,
- const git_signature *committer, const char *msg)
+int git_reflog_append(git_reflog *reflog, const git_oid *new_oid, const git_signature *committer, const char *msg)
{
git_reflog_entry *entry;
const git_reflog_entry *previous;
@@ -296,8 +79,8 @@ int git_reflog_append(git_reflog *reflog, const git_oid *new_oid,
assert(reflog && new_oid && committer);
- if (reflog_entry_new(&entry) < 0)
- return -1;
+ entry = git__calloc(1, sizeof(git_reflog_entry));
+ GITERR_CHECK_ALLOC(entry);
if ((entry->committer = git_signature_dup(committer)) == NULL)
goto cleanup;
@@ -333,94 +116,30 @@ int git_reflog_append(git_reflog *reflog, const git_oid *new_oid,
return 0;
cleanup:
- reflog_entry_free(entry);
+ git_reflog_entry__free(entry);
return -1;
}
-int git_reflog_rename(git_reference *ref, const char *new_name)
+int git_reflog_rename(git_repository *repo, const char *old_name, const char *new_name)
{
- int error = 0, fd;
- git_buf old_path = GIT_BUF_INIT;
- git_buf new_path = GIT_BUF_INIT;
- git_buf temp_path = GIT_BUF_INIT;
- git_buf normalized = GIT_BUF_INIT;
-
- assert(ref && new_name);
-
- if ((error = git_reference__normalize_name(
- &normalized, new_name, GIT_REF_FORMAT_ALLOW_ONELEVEL)) < 0)
- return error;
-
- if (git_buf_joinpath(&temp_path, git_reference_owner(ref)->path_repository, GIT_REFLOG_DIR) < 0)
- return -1;
-
- if (git_buf_joinpath(&old_path, git_buf_cstr(&temp_path), ref->name) < 0)
- return -1;
-
- if (git_buf_joinpath(&new_path, git_buf_cstr(&temp_path), git_buf_cstr(&normalized)) < 0)
- return -1;
+ git_refdb *refdb;
+ int error;
- /*
- * Move the reflog to a temporary place. This two-phase renaming is required
- * in order to cope with funny renaming use cases when one tries to move a reference
- * to a partially colliding namespace:
- * - a/b -> a/b/c
- * - a/b/c/d -> a/b/c
- */
- if (git_buf_joinpath(&temp_path, git_buf_cstr(&temp_path), "temp_reflog") < 0)
+ if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
return -1;
- if ((fd = git_futils_mktmp(&temp_path, git_buf_cstr(&temp_path))) < 0) {
- error = -1;
- goto cleanup;
- }
-
- p_close(fd);
-
- if (p_rename(git_buf_cstr(&old_path), git_buf_cstr(&temp_path)) < 0) {
- giterr_set(GITERR_OS, "Failed to rename reflog for %s", new_name);
- error = -1;
- goto cleanup;
- }
-
- if (git_path_isdir(git_buf_cstr(&new_path)) &&
- (git_futils_rmdir_r(git_buf_cstr(&new_path), NULL, GIT_RMDIR_SKIP_NONEMPTY) < 0)) {
- error = -1;
- goto cleanup;
- }
-
- if (git_futils_mkpath2file(git_buf_cstr(&new_path), GIT_REFLOG_DIR_MODE) < 0) {
- error = -1;
- goto cleanup;
- }
-
- if (p_rename(git_buf_cstr(&temp_path), git_buf_cstr(&new_path)) < 0) {
- giterr_set(GITERR_OS, "Failed to rename reflog for %s", new_name);
- error = -1;
- }
-
-cleanup:
- git_buf_free(&temp_path);
- git_buf_free(&old_path);
- git_buf_free(&new_path);
- git_buf_free(&normalized);
-
- return error;
+ return refdb->backend->reflog_rename(refdb->backend, old_name, new_name);
}
-int git_reflog_delete(git_reference *ref)
+int git_reflog_delete(git_repository *repo, const char *name)
{
+ git_refdb *refdb;
int error;
- git_buf path = GIT_BUF_INIT;
-
- error = retrieve_reflog_path(&path, ref);
- if (!error && git_path_exists(path.ptr))
- error = p_unlink(path.ptr);
-
- git_buf_free(&path);
+ if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
+ return -1;
- return error;
+ return refdb->backend->reflog_delete(refdb->backend, name);
}
size_t git_reflog_entrycount(git_reflog *reflog)
@@ -429,11 +148,6 @@ size_t git_reflog_entrycount(git_reflog *reflog)
return reflog->entries.length;
}
-GIT_INLINE(size_t) reflog_inverse_index(size_t idx, size_t total)
-{
- return (total - 1) - idx;
-}
-
const git_reflog_entry * git_reflog_entry_byindex(git_reflog *reflog, size_t idx)
{
assert(reflog);
@@ -469,16 +183,11 @@ const char * git_reflog_entry_message(const git_reflog_entry *entry)
return entry->msg;
}
-int git_reflog_drop(
- git_reflog *reflog,
- size_t idx,
- int rewrite_previous_entry)
+int git_reflog_drop(git_reflog *reflog, size_t idx, int rewrite_previous_entry)
{
size_t entrycount;
git_reflog_entry *entry, *previous;
- assert(reflog);
-
entrycount = git_reflog_entrycount(reflog);
entry = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx);
@@ -488,7 +197,7 @@ int git_reflog_drop(
return GIT_ENOTFOUND;
}
- reflog_entry_free(entry);
+ git_reflog_entry__free(entry);
if (git_vector_remove(
&reflog->entries, reflog_inverse_index(idx, entrycount)) < 0)
@@ -521,3 +230,22 @@ int git_reflog_drop(
return 0;
}
+
+int git_reflog_append_to(git_repository *repo, const char *name, const git_oid *id,
+ const git_signature *committer, const char *msg)
+{
+ int error;
+ git_reflog *reflog;
+
+ if ((error = git_reflog_read(&reflog, repo, name)) < 0)
+ return error;
+
+ if ((error = git_reflog_append(reflog, id, committer, msg)) < 0)
+ goto cleanup;
+
+ error = git_reflog_write(reflog);
+
+cleanup:
+ git_reflog_free(reflog);
+ return error;
+}
diff --git a/src/reflog.h b/src/reflog.h
index 9444ebd10..2d31ae47d 100644
--- a/src/reflog.h
+++ b/src/reflog.h
@@ -27,9 +27,14 @@ struct git_reflog_entry {
};
struct git_reflog {
+ git_refdb *db;
char *ref_name;
- git_repository *owner;
git_vector entries;
};
+GIT_INLINE(size_t) reflog_inverse_index(size_t idx, size_t total)
+{
+ return (total - 1) - idx;
+}
+
#endif /* INCLUDE_reflog_h__ */
diff --git a/src/refs.c b/src/refs.c
index c0e460cc3..472a79890 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -88,6 +88,17 @@ git_reference *git_reference__alloc(
return ref;
}
+git_reference *git_reference__set_name(
+ git_reference *ref, const char *name)
+{
+ size_t namelen = strlen(name);
+ git_reference *rewrite =
+ git__realloc(ref, sizeof(git_reference) + namelen + 1);
+ if (rewrite != NULL)
+ memcpy(rewrite->name, name, namelen + 1);
+ return rewrite;
+}
+
void git_reference_free(git_reference *reference)
{
if (reference == NULL)
@@ -127,6 +138,22 @@ int git_reference_name_to_id(
return 0;
}
+static int reference_normalize_for_repo(
+ char *out,
+ size_t out_size,
+ git_repository *repo,
+ const char *name)
+{
+ int precompose;
+ unsigned int flags = GIT_REF_FORMAT_ALLOW_ONELEVEL;
+
+ if (!git_repository__cvar(&precompose, repo, GIT_CVAR_PRECOMPOSE) &&
+ precompose)
+ flags |= GIT_REF_FORMAT__PRECOMPOSE_UNICODE;
+
+ return git_reference_normalize_name(out, out_size, name, flags);
+}
+
int git_reference_lookup_resolved(
git_reference **ref_out,
git_repository *repo,
@@ -148,13 +175,13 @@ int git_reference_lookup_resolved(
else if (max_nesting < 0)
max_nesting = DEFAULT_NESTING_LEVEL;
- strncpy(scan_name, name, GIT_REFNAME_MAX);
scan_type = GIT_REF_SYMBOLIC;
- if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
- return -1;
+ if ((error = reference_normalize_for_repo(
+ scan_name, sizeof(scan_name), repo, name)) < 0)
+ return error;
- if ((error = git_reference__normalize_name_lax(scan_name, GIT_REFNAME_MAX, name)) < 0)
+ if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
return error;
for (nesting = max_nesting;
@@ -162,7 +189,7 @@ int git_reference_lookup_resolved(
nesting--)
{
if (nesting != max_nesting) {
- strncpy(scan_name, ref->target.symbolic, GIT_REFNAME_MAX);
+ strncpy(scan_name, ref->target.symbolic, sizeof(scan_name));
git_reference_free(ref);
}
@@ -456,7 +483,7 @@ int git_reference_rename(
if (reference_has_log < 0)
return reference_has_log;
- if (reference_has_log && (error = git_reflog_rename(ref, new_name)) < 0)
+ if (reference_has_log && (error = git_reflog_rename(git_reference_owner(ref), git_reference_name(ref), new_name)) < 0)
return error;
return 0;
@@ -700,17 +727,21 @@ static bool is_all_caps_and_underscore(const char *name, size_t len)
return true;
}
+/* Inspired from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/refs.c#L36-100 */
int git_reference__normalize_name(
git_buf *buf,
const char *name,
unsigned int flags)
{
- // Inspired from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/refs.c#L36-100
-
char *current;
int segment_len, segments_count = 0, error = GIT_EINVALIDSPEC;
unsigned int process_flags;
bool normalize = (buf != NULL);
+
+#ifdef GIT_USE_ICONV
+ git_path_iconv_t ic = GIT_PATH_ICONV_INIT;
+#endif
+
assert(name);
process_flags = flags;
@@ -722,6 +753,16 @@ int git_reference__normalize_name(
if (normalize)
git_buf_clear(buf);
+#ifdef GIT_USE_ICONV
+ if ((flags & GIT_REF_FORMAT__PRECOMPOSE_UNICODE) != 0) {
+ size_t namelen = strlen(current);
+ if ((error = git_path_iconv_init_precompose(&ic)) < 0 ||
+ (error = git_path_iconv(&ic, &current, &namelen)) < 0)
+ goto cleanup;
+ error = GIT_EINVALIDSPEC;
+ }
+#endif
+
while (true) {
segment_len = ensure_segment_validity(current);
if (segment_len < 0) {
@@ -798,6 +839,10 @@ cleanup:
if (error && normalize)
git_buf_free(buf);
+#ifdef GIT_USE_ICONV
+ git_path_iconv_clear(&ic);
+#endif
+
return error;
}
@@ -952,6 +997,17 @@ int git_reference_is_remote(git_reference *ref)
return git_reference__is_remote(ref->name);
}
+int git_reference__is_tag(const char *ref_name)
+{
+ return git__prefixcmp(ref_name, GIT_REFS_TAGS_DIR) == 0;
+}
+
+int git_reference_is_tag(git_reference *ref)
+{
+ assert(ref);
+ return git_reference__is_tag(ref->name);
+}
+
static int peel_error(int error, git_reference *ref, const char* msg)
{
giterr_set(
@@ -961,9 +1017,9 @@ static int peel_error(int error, git_reference *ref, const char* msg)
}
int git_reference_peel(
- git_object **peeled,
- git_reference *ref,
- git_otype target_type)
+ git_object **peeled,
+ git_reference *ref,
+ git_otype target_type)
{
git_reference *resolved = NULL;
git_object *target = NULL;
@@ -1005,24 +1061,19 @@ cleanup:
return error;
}
-int git_reference__is_valid_name(
- const char *refname,
- unsigned int flags)
+int git_reference__is_valid_name(const char *refname, unsigned int flags)
{
- int error;
-
- error = git_reference__normalize_name(NULL, refname, flags) == 0;
- giterr_clear();
+ if (git_reference__normalize_name(NULL, refname, flags) < 0) {
+ giterr_clear();
+ return false;
+ }
- return error;
+ return true;
}
-int git_reference_is_valid_name(
- const char *refname)
+int git_reference_is_valid_name(const char *refname)
{
- return git_reference__is_valid_name(
- refname,
- GIT_REF_FORMAT_ALLOW_ONELEVEL);
+ return git_reference__is_valid_name(refname, GIT_REF_FORMAT_ALLOW_ONELEVEL);
}
const char *git_reference_shorthand(git_reference *ref)
diff --git a/src/refs.h b/src/refs.h
index f487ee3fc..80c7703fc 100644
--- a/src/refs.h
+++ b/src/refs.h
@@ -46,6 +46,8 @@
#define GIT_STASH_FILE "stash"
#define GIT_REFS_STASH_FILE GIT_REFS_DIR GIT_STASH_FILE
+#define GIT_REF_FORMAT__PRECOMPOSE_UNICODE (1u << 16)
+
#define GIT_REFNAME_MAX 1024
struct git_reference {
@@ -61,12 +63,15 @@ struct git_reference {
char name[0];
};
+git_reference *git_reference__set_name(git_reference *ref, const char *name);
+
int git_reference__normalize_name_lax(char *buffer_out, size_t out_size, const char *name);
int git_reference__normalize_name(git_buf *buf, const char *name, unsigned int flags);
int git_reference__update_terminal(git_repository *repo, const char *ref_name, const git_oid *oid);
int git_reference__is_valid_name(const char *refname, unsigned int flags);
int git_reference__is_branch(const char *ref_name);
int git_reference__is_remote(const char *ref_name);
+int git_reference__is_tag(const char *ref_name);
/**
* Lookup a reference by name and try to resolve to an OID.
diff --git a/src/refspec.c b/src/refspec.c
index a907df84c..a97340071 100644
--- a/src/refspec.c
+++ b/src/refspec.c
@@ -12,6 +12,7 @@
#include "util.h"
#include "posix.h"
#include "refs.h"
+#include "vector.h"
int git_refspec__parse(git_refspec *refspec, const char *input, bool is_fetch)
{
@@ -225,25 +226,31 @@ int git_refspec_rtransform(char *out, size_t outlen, const git_refspec *spec, co
return refspec_transform_internal(out, outlen, spec->dst, spec->src, name);
}
-static int refspec_transform(git_buf *out, const char *from, const char *to, const char *name)
+static int refspec_transform(
+ git_buf *out, const char *from, const char *to, const char *name)
{
- if (git_buf_sets(out, to) < 0)
+ size_t to_len = to ? strlen(to) : 0;
+ size_t from_len = from ? strlen(from) : 0;
+ size_t name_len = name ? strlen(name) : 0;
+
+ if (git_buf_set(out, to, to_len) < 0)
return -1;
- /*
- * No '*' at the end means that it's mapped to one specific
- * branch, so no actual transformation is needed.
- */
- if (git_buf_len(out) > 0 && out->ptr[git_buf_len(out) - 1] != '*')
- return 0;
+ if (to_len > 0) {
+ /* No '*' at the end of 'to' means that refspec is mapped to one
+ * specific branch, so no actual transformation is needed.
+ */
+ if (out->ptr[to_len - 1] != '*')
+ return 0;
+ git_buf_shorten(out, 1); /* remove trailing '*' copied from 'to' */
+ }
- git_buf_truncate(out, git_buf_len(out) - 1); /* remove trailing '*' */
- git_buf_puts(out, name + strlen(from) - 1);
+ if (from_len > 0) /* ignore trailing '*' from 'from' */
+ from_len--;
+ if (from_len > name_len)
+ from_len = name_len;
- if (git_buf_oom(out))
- return -1;
-
- return 0;
+ return git_buf_put(out, name + from_len, name_len - from_len);
}
int git_refspec_transform_r(git_buf *out, const git_refspec *spec, const char *name)
@@ -281,3 +288,70 @@ git_direction git_refspec_direction(const git_refspec *spec)
return spec->push;
}
+
+int git_refspec__dwim_one(git_vector *out, git_refspec *spec, git_vector *refs)
+{
+ git_buf buf = GIT_BUF_INIT;
+ size_t j, pos;
+ git_remote_head key;
+
+ const char* formatters[] = {
+ GIT_REFS_DIR "%s",
+ GIT_REFS_TAGS_DIR "%s",
+ GIT_REFS_HEADS_DIR "%s",
+ NULL
+ };
+
+ git_refspec *cur = git__calloc(1, sizeof(git_refspec));
+ GITERR_CHECK_ALLOC(cur);
+
+ cur->force = spec->force;
+ cur->push = spec->push;
+ cur->pattern = spec->pattern;
+ cur->matching = spec->matching;
+ cur->string = git__strdup(spec->string);
+
+ /* shorthand on the lhs */
+ if (git__prefixcmp(spec->src, GIT_REFS_DIR)) {
+ for (j = 0; formatters[j]; j++) {
+ git_buf_clear(&buf);
+ if (git_buf_printf(&buf, formatters[j], spec->src) < 0)
+ return -1;
+
+ key.name = (char *) git_buf_cstr(&buf);
+ if (!git_vector_search(&pos, refs, &key)) {
+ /* we found something to match the shorthand, set src to that */
+ cur->src = git_buf_detach(&buf);
+ }
+ }
+ }
+
+ /* No shorthands found, copy over the name */
+ if (cur->src == NULL && spec->src != NULL) {
+ cur->src = git__strdup(spec->src);
+ GITERR_CHECK_ALLOC(cur->src);
+ }
+
+ if (spec->dst && git__prefixcmp(spec->dst, GIT_REFS_DIR)) {
+ /* if it starts with "remotes" then we just prepend "refs/" */
+ if (!git__prefixcmp(spec->dst, "remotes/")) {
+ git_buf_puts(&buf, GIT_REFS_DIR);
+ } else {
+ git_buf_puts(&buf, GIT_REFS_HEADS_DIR);
+ }
+
+ if (git_buf_puts(&buf, spec->dst) < 0)
+ return -1;
+
+ cur->dst = git_buf_detach(&buf);
+ }
+
+ git_buf_free(&buf);
+
+ if (cur->dst == NULL && spec->dst != NULL) {
+ cur->dst = git__strdup(spec->dst);
+ GITERR_CHECK_ALLOC(cur->dst);
+ }
+
+ return git_vector_insert(out, cur);
+}
diff --git a/src/refspec.h b/src/refspec.h
index 44d484c7b..51b7bfee9 100644
--- a/src/refspec.h
+++ b/src/refspec.h
@@ -9,6 +9,7 @@
#include "git2/refspec.h"
#include "buffer.h"
+#include "vector.h"
struct git_refspec {
char *string;
@@ -17,7 +18,6 @@ struct git_refspec {
unsigned int force :1,
push : 1,
pattern :1,
- dwim :1,
matching :1;
};
@@ -63,4 +63,10 @@ int git_refspec__serialize(git_buf *out, const git_refspec *refspec);
*/
int git_refspec_is_wildcard(const git_refspec *spec);
+/**
+ * DWIM `spec` with `refs` existing on the remote, append the dwim'ed
+ * result in `out`.
+ */
+int git_refspec__dwim_one(git_vector *out, git_refspec *spec, git_vector *refs);
+
#endif
diff --git a/src/remote.c b/src/remote.c
index 0e8354a11..3d890a5f1 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -10,6 +10,7 @@
#include "git2/oid.h"
#include "git2/net.h"
+#include "common.h"
#include "config.h"
#include "repository.h"
#include "remote.h"
@@ -18,7 +19,7 @@
#include "refspec.h"
#include "fetchhead.h"
-#include <regex.h>
+static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs);
static int add_refspec(git_remote *remote, const char *string, bool is_fetch)
{
@@ -81,6 +82,37 @@ static int ensure_remote_name_is_valid(const char *name)
return error;
}
+static int get_check_cert(int *out, git_repository *repo)
+{
+ git_config *cfg;
+ const char *val;
+ int error = 0;
+
+ assert(out && repo);
+
+ /* By default, we *DO* want to verify the certificate. */
+ *out = 1;
+
+ /* Go through the possible sources for SSL verification settings, from
+ * most specific to least specific. */
+
+ /* GIT_SSL_NO_VERIFY environment variable */
+ if ((val = getenv("GIT_SSL_NO_VERIFY")) != NULL)
+ return git_config_parse_bool(out, val);
+
+ /* http.sslVerify config setting */
+ if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
+ return error;
+
+ if ((error = git_config_get_bool(out, cfg, "http.sslVerify")) == 0)
+ return 0;
+ else if (error != GIT_ENOTFOUND)
+ return error;
+
+ giterr_clear();
+ return 0;
+}
+
static int create_internal(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch)
{
git_remote *remote;
@@ -94,9 +126,11 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n
GITERR_CHECK_ALLOC(remote);
remote->repo = repo;
- remote->check_cert = 1;
remote->update_fetchhead = 1;
+ if (get_check_cert(&remote->check_cert, repo) < 0)
+ goto on_error;
+
if (git_vector_init(&remote->refs, 32, NULL) < 0)
goto on_error;
@@ -183,6 +217,32 @@ on_error:
return -1;
}
+int git_remote_create_with_fetchspec(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch)
+{
+ git_remote *remote = NULL;
+ int error;
+
+ if ((error = ensure_remote_name_is_valid(name)) < 0)
+ return error;
+
+ if ((error = ensure_remote_doesnot_exist(repo, name)) < 0)
+ return error;
+
+ if (create_internal(&remote, repo, name, url, fetch) < 0)
+ goto on_error;
+
+ if (git_remote_save(remote) < 0)
+ goto on_error;
+
+ *out = remote;
+
+ return 0;
+
+on_error:
+ git_remote_free(remote);
+ return -1;
+}
+
int git_remote_create_inmemory(git_remote **out, git_repository *repo, const char *fetch, const char *url)
{
int error;
@@ -208,7 +268,8 @@ static int refspec_cb(const git_config_entry *entry, void *payload)
}
static int get_optional_config(
- git_config *config, git_buf *buf, git_config_foreach_cb cb, void *payload)
+ bool *found, git_config *config, git_buf *buf,
+ git_config_foreach_cb cb, void *payload)
{
int error = 0;
const char *key = git_buf_cstr(buf);
@@ -217,10 +278,13 @@ static int get_optional_config(
return -1;
if (cb != NULL)
- error = git_config_get_multivar(config, key, NULL, cb, payload);
+ error = git_config_get_multivar_foreach(config, key, NULL, cb, payload);
else
error = git_config_get_string(payload, config, key);
+ if (found)
+ *found = !error;
+
if (error == GIT_ENOTFOUND) {
giterr_clear();
error = 0;
@@ -240,6 +304,7 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
int error = 0;
git_config *config;
struct refspec_cb_data data;
+ bool optional_setting_found = false, found;
assert(out && repo && name);
@@ -253,13 +318,16 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
GITERR_CHECK_ALLOC(remote);
memset(remote, 0x0, sizeof(git_remote));
- remote->check_cert = 1;
remote->update_fetchhead = 1;
remote->name = git__strdup(name);
GITERR_CHECK_ALLOC(remote->name);
+ if ((error = get_check_cert(&remote->check_cert, repo)) < 0)
+ goto cleanup;
+
if ((git_vector_init(&remote->refs, 32, NULL) < 0) ||
- (git_vector_init(&remote->refspecs, 2, NULL))) {
+ (git_vector_init(&remote->refspecs, 2, NULL) < 0) ||
+ (git_vector_init(&remote->active_refspecs, 2, NULL) < 0)) {
error = -1;
goto cleanup;
}
@@ -269,27 +337,33 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
goto cleanup;
}
- if ((error = git_config_get_string(&val, config, git_buf_cstr(&buf))) < 0)
+ if ((error = get_optional_config(&found, config, &buf, NULL, (void *)&val)) < 0)
goto cleanup;
- if (strlen(val) == 0) {
- giterr_set(GITERR_INVALID, "Malformed remote '%s' - missing URL", name);
- error = -1;
- goto cleanup;
- }
+ optional_setting_found |= found;
remote->repo = repo;
- remote->url = git__strdup(val);
- GITERR_CHECK_ALLOC(remote->url);
+
+ if (found && strlen(val) > 0) {
+ remote->url = git__strdup(val);
+ GITERR_CHECK_ALLOC(remote->url);
+ }
val = NULL;
git_buf_clear(&buf);
git_buf_printf(&buf, "remote.%s.pushurl", name);
- if ((error = get_optional_config(config, &buf, NULL, (void *)&val)) < 0)
+ if ((error = get_optional_config(&found, config, &buf, NULL, (void *)&val)) < 0)
goto cleanup;
- if (val) {
+ optional_setting_found |= found;
+
+ if (!optional_setting_found) {
+ error = GIT_ENOTFOUND;
+ goto cleanup;
+ }
+
+ if (found && strlen(val) > 0) {
remote->pushurl = git__strdup(val);
GITERR_CHECK_ALLOC(remote->pushurl);
}
@@ -299,19 +373,23 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name)
git_buf_clear(&buf);
git_buf_printf(&buf, "remote.%s.fetch", name);
- if ((error = get_optional_config(config, &buf, refspec_cb, &data)) < 0)
+ if ((error = get_optional_config(NULL, config, &buf, refspec_cb, &data)) < 0)
goto cleanup;
data.fetch = false;
git_buf_clear(&buf);
git_buf_printf(&buf, "remote.%s.push", name);
- if ((error = get_optional_config(config, &buf, refspec_cb, &data)) < 0)
+ if ((error = get_optional_config(NULL, config, &buf, refspec_cb, &data)) < 0)
goto cleanup;
if (download_tags_value(remote, config) < 0)
goto cleanup;
+ /* Move the data over to where the matching functions can find them */
+ if (dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs) < 0)
+ goto cleanup;
+
*out = remote;
cleanup:
@@ -326,20 +404,22 @@ cleanup:
static int update_config_refspec(const git_remote *remote, git_config *config, int direction)
{
git_buf name = GIT_BUF_INIT;
- int push;
+ unsigned int push;
const char *dir;
size_t i;
int error = 0;
+ const char *cname;
push = direction == GIT_DIRECTION_PUSH;
dir = push ? "push" : "fetch";
if (git_buf_printf(&name, "remote.%s.%s", remote->name, dir) < 0)
return -1;
+ cname = git_buf_cstr(&name);
/* Clear out the existing config */
while (!error)
- error = git_config_delete_entry(config, git_buf_cstr(&name));
+ error = git_config_delete_multivar(config, cname, ".*");
if (error != GIT_ENOTFOUND)
return error;
@@ -350,8 +430,11 @@ static int update_config_refspec(const git_remote *remote, git_config *config, i
if (spec->push != push)
continue;
+ // "$^" is a unmatcheable regexp: it will not match anything at all, so
+ // all values will be considered new and we will not replace any
+ // present value.
if ((error = git_config_set_multivar(
- config, git_buf_cstr(&name), "", spec->string)) < 0) {
+ config, cname, "$^", spec->string)) < 0) {
goto cleanup;
}
}
@@ -467,6 +550,12 @@ const char *git_remote_name(const git_remote *remote)
return remote->name;
}
+git_repository *git_remote_owner(const git_remote *remote)
+{
+ assert(remote);
+ return remote->repo;
+}
+
const char *git_remote_url(const git_remote *remote)
{
assert(remote);
@@ -509,6 +598,8 @@ const char* git_remote__urlfordirection(git_remote *remote, int direction)
{
assert(remote);
+ assert(direction == GIT_DIRECTION_FETCH || direction == GIT_DIRECTION_PUSH);
+
if (direction == GIT_DIRECTION_FETCH) {
return remote->url;
}
@@ -525,28 +616,32 @@ int git_remote_connect(git_remote *remote, git_direction direction)
git_transport *t;
const char *url;
int flags = GIT_TRANSPORTFLAGS_NONE;
+ int error;
assert(remote);
t = remote->transport;
url = git_remote__urlfordirection(remote, direction);
- if (url == NULL )
+ if (url == NULL) {
+ giterr_set(GITERR_INVALID,
+ "Malformed remote '%s' - missing URL", remote->name);
return -1;
+ }
/* A transport could have been supplied in advance with
* git_remote_set_transport */
- if (!t && git_transport_new(&t, remote, url) < 0)
- return -1;
+ if (!t && (error = git_transport_new(&t, remote, url)) < 0)
+ return error;
if (t->set_callbacks &&
- t->set_callbacks(t, remote->callbacks.progress, NULL, remote->callbacks.payload) < 0)
+ (error = t->set_callbacks(t, remote->callbacks.progress, NULL, remote->callbacks.payload)) < 0)
goto on_error;
if (!remote->check_cert)
flags |= GIT_TRANSPORTFLAGS_NO_CHECK_CERT;
- if (t->connect(t, url, remote->cred_acquire_cb, remote->cred_acquire_payload, direction, flags) < 0)
+ if ((error = t->connect(t, url, remote->callbacks.credentials, remote->callbacks.payload, direction, flags)) < 0)
goto on_error;
remote->transport = t;
@@ -559,20 +654,21 @@ on_error:
if (t == remote->transport)
remote->transport = NULL;
- return -1;
+ return error;
}
-int git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload)
+int git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote)
{
assert(remote);
- return remote->transport->ls(remote->transport, list_cb, payload);
+ return remote->transport->ls(out, size, remote->transport);
}
int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_url)
{
git_config *cfg;
const char *val;
+ int error;
assert(remote);
@@ -581,8 +677,8 @@ int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_ur
*proxy_url = NULL;
- if (git_repository_config__weakptr(&cfg, remote->repo) < 0)
- return -1;
+ if ((error = git_repository_config__weakptr(&cfg, remote->repo)) < 0)
+ return error;
/* Go through the possible sources for proxy configuration, from most specific
* to least specific. */
@@ -591,28 +687,33 @@ int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_ur
if (remote->name && 0 != *(remote->name)) {
git_buf buf = GIT_BUF_INIT;
- if (git_buf_printf(&buf, "remote.%s.proxy", remote->name) < 0)
- return -1;
+ if ((error = git_buf_printf(&buf, "remote.%s.proxy", remote->name)) < 0)
+ return error;
- if (!git_config_get_string(&val, cfg, git_buf_cstr(&buf)) &&
+ if ((error = git_config_get_string(&val, cfg, git_buf_cstr(&buf))) == 0 &&
val && ('\0' != *val)) {
git_buf_free(&buf);
*proxy_url = git__strdup(val);
GITERR_CHECK_ALLOC(*proxy_url);
return 0;
- }
+ } else if (error != GIT_ENOTFOUND)
+ return error;
+ giterr_clear();
git_buf_free(&buf);
}
/* http.proxy config setting */
- if (!git_config_get_string(&val, cfg, "http.proxy") &&
+ if ((error = git_config_get_string(&val, cfg, "http.proxy")) == 0 &&
val && ('\0' != *val)) {
*proxy_url = git__strdup(val);
GITERR_CHECK_ALLOC(*proxy_url);
return 0;
- }
+ } else if (error != GIT_ENOTFOUND)
+ return error;
+
+ giterr_clear();
/* HTTP_PROXY / HTTPS_PROXY environment variables */
val = use_ssl ? getenv("HTTPS_PROXY") : getenv("HTTP_PROXY");
@@ -626,67 +727,31 @@ int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_ur
return 0;
}
-static int store_refs(git_remote_head *head, void *payload)
+/* DWIM `refspecs` based on `refs` and append the output to `out` */
+static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs)
{
- git_vector *refs = (git_vector *)payload;
-
- return git_vector_insert(refs, head);
-}
-
-static int dwim_refspecs(git_vector *refspecs, git_vector *refs)
-{
- git_buf buf = GIT_BUF_INIT;
+ size_t i;
git_refspec *spec;
- size_t i, j, pos;
- git_remote_head key;
-
- const char* formatters[] = {
- GIT_REFS_DIR "%s",
- GIT_REFS_TAGS_DIR "%s",
- GIT_REFS_HEADS_DIR "%s",
- NULL
- };
git_vector_foreach(refspecs, i, spec) {
- if (spec->dwim)
- continue;
-
- /* shorthand on the lhs */
- if (git__prefixcmp(spec->src, GIT_REFS_DIR)) {
- for (j = 0; formatters[j]; j++) {
- git_buf_clear(&buf);
- if (git_buf_printf(&buf, formatters[j], spec->src) < 0)
- return -1;
-
- key.name = (char *) git_buf_cstr(&buf);
- if (!git_vector_search(&pos, refs, &key)) {
- /* we found something to match the shorthand, set src to that */
- git__free(spec->src);
- spec->src = git_buf_detach(&buf);
- }
- }
- }
-
- if (spec->dst && git__prefixcmp(spec->dst, GIT_REFS_DIR)) {
- /* if it starts with "remotes" then we just prepend "refs/" */
- if (!git__prefixcmp(spec->dst, "remotes/")) {
- git_buf_puts(&buf, GIT_REFS_DIR);
- } else {
- git_buf_puts(&buf, GIT_REFS_HEADS_DIR);
- }
+ if (git_refspec__dwim_one(out, spec, refs) < 0)
+ return -1;
+ }
- if (git_buf_puts(&buf, spec->dst) < 0)
- return -1;
+ return 0;
+}
- git__free(spec->dst);
- spec->dst = git_buf_detach(&buf);
- }
+static void free_refspecs(git_vector *vec)
+{
+ size_t i;
+ git_refspec *spec;
- spec->dwim = 1;
+ git_vector_foreach(vec, i, spec) {
+ git_refspec__free(spec);
+ git__free(spec);
}
- git_buf_free(&buf);
- return 0;
+ git_vector_clear(vec);
}
static int remote_head_cmp(const void *_a, const void *_b)
@@ -697,32 +762,65 @@ static int remote_head_cmp(const void *_a, const void *_b)
return git__strcmp_cb(a->name, b->name);
}
-int git_remote_download(
- git_remote *remote,
- git_transfer_progress_callback progress_cb,
- void *progress_payload)
+static int ls_to_vector(git_vector *out, git_remote *remote)
+{
+ git_remote_head **heads;
+ size_t heads_len, i;
+
+ if (git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote) < 0)
+ return -1;
+
+ if (git_vector_init(out, heads_len, remote_head_cmp) < 0)
+ return -1;
+
+ for (i = 0; i < heads_len; i++) {
+ if (git_vector_insert(out, heads[i]) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+int git_remote_download(git_remote *remote)
{
int error;
git_vector refs;
assert(remote);
- if (git_vector_init(&refs, 16, remote_head_cmp) < 0)
+ if (ls_to_vector(&refs, remote) < 0)
return -1;
- if (git_remote_ls(remote, store_refs, &refs) < 0) {
- return -1;
- }
+ free_refspecs(&remote->active_refspecs);
- error = dwim_refspecs(&remote->refspecs, &refs);
+ error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &refs);
git_vector_free(&refs);
+
if (error < 0)
return -1;
if ((error = git_fetch_negotiate(remote)) < 0)
return error;
- return git_fetch_download_pack(remote, progress_cb, progress_payload);
+ return git_fetch_download_pack(remote);
+}
+
+int git_remote_fetch(git_remote *remote)
+{
+ int error;
+
+ /* Connect and download everything */
+ if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH)) < 0)
+ return error;
+
+ if ((error = git_remote_download(remote)) < 0)
+ return error;
+
+ /* We don't need to be connected anymore */
+ git_remote_disconnect(remote);
+
+ /* Create "remote/foo" branches for all remote branches */
+ return git_remote_update_tips(remote);
}
static int remote_head_for_fetchspec_src(git_remote_head **out, git_vector *update_heads, const char *fetchspec_src)
@@ -759,7 +857,7 @@ static int remote_head_for_ref(git_remote_head **out, git_refspec *spec, git_vec
(!git_reference_is_branch(resolved_ref)) ||
(error = git_branch_upstream(&tracking_ref, resolved_ref)) < 0 ||
(error = git_refspec_transform_l(&remote_name, spec, git_reference_name(tracking_ref))) < 0) {
- /* Not an error if HEAD is orphaned or no tracking branch */
+ /* Not an error if HEAD is unborn or no tracking branch */
if (error == GIT_ENOTFOUND)
error = 0;
@@ -947,10 +1045,8 @@ int git_remote_update_tips(git_remote *remote)
if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0)
return -1;
- if (git_vector_init(&refs, 16, NULL) < 0)
- return -1;
- if ((error = git_remote_ls(remote, store_refs, &refs)) < 0)
+ if ((error = ls_to_vector(&refs, remote)) < 0)
goto out;
if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) {
@@ -958,7 +1054,7 @@ int git_remote_update_tips(git_remote *remote)
goto out;
}
- git_vector_foreach(&remote->refspecs, i, spec) {
+ git_vector_foreach(&remote->active_refspecs, i, spec) {
if (spec->push)
continue;
@@ -967,8 +1063,8 @@ int git_remote_update_tips(git_remote *remote)
}
out:
- git_refspec__free(&tagspec);
git_vector_free(&refs);
+ git_refspec__free(&tagspec);
return error;
}
@@ -1001,9 +1097,6 @@ void git_remote_disconnect(git_remote *remote)
void git_remote_free(git_remote *remote)
{
- git_refspec *spec;
- size_t i;
-
if (remote == NULL)
return;
@@ -1016,67 +1109,55 @@ void git_remote_free(git_remote *remote)
git_vector_free(&remote->refs);
- git_vector_foreach(&remote->refspecs, i, spec) {
- git_refspec__free(spec);
- git__free(spec);
- }
+ free_refspecs(&remote->refspecs);
git_vector_free(&remote->refspecs);
+ free_refspecs(&remote->active_refspecs);
+ git_vector_free(&remote->active_refspecs);
+
git__free(remote->url);
git__free(remote->pushurl);
git__free(remote->name);
git__free(remote);
}
-struct cb_data {
- git_vector *list;
- regex_t *preg;
-};
-
-static int remote_list_cb(const git_config_entry *entry, void *data_)
+static int remote_list_cb(const git_config_entry *entry, void *payload)
{
- struct cb_data *data = (struct cb_data *)data_;
- size_t nmatch = 2;
- regmatch_t pmatch[2];
- const char *name = entry->name;
+ git_vector *list = payload;
+ const char *name = entry->name + strlen("remote.");
+ size_t namelen = strlen(name);
+ char *remote_name;
- if (!regexec(data->preg, name, nmatch, pmatch, 0)) {
- char *remote_name = git__strndup(&name[pmatch[1].rm_so], pmatch[1].rm_eo - pmatch[1].rm_so);
- GITERR_CHECK_ALLOC(remote_name);
+ /* we know name matches "remote.<stuff>.(push)?url" */
- if (git_vector_insert(data->list, remote_name) < 0)
- return -1;
- }
+ if (!strcmp(&name[namelen - 4], ".url"))
+ remote_name = git__strndup(name, namelen - 4); /* strip ".url" */
+ else
+ remote_name = git__strndup(name, namelen - 8); /* strip ".pushurl" */
+ GITERR_CHECK_ALLOC(remote_name);
- return 0;
+ return git_vector_insert(list, remote_name);
}
int git_remote_list(git_strarray *remotes_list, git_repository *repo)
{
git_config *cfg;
git_vector list;
- regex_t preg;
- struct cb_data data;
int error;
if (git_repository_config__weakptr(&cfg, repo) < 0)
return -1;
- if (git_vector_init(&list, 4, NULL) < 0)
+ if (git_vector_init(&list, 4, git__strcmp_cb) < 0)
return -1;
- if (regcomp(&preg, "^remote\\.(.*)\\.url$", REG_EXTENDED) < 0) {
- giterr_set(GITERR_OS, "Remote catch regex failed to compile");
- return -1;
- }
+ error = git_config_foreach_match(
+ cfg, "^remote\\..*\\.(push)?url$", remote_list_cb, &list);
- data.list = &list;
- data.preg = &preg;
- error = git_config_foreach(cfg, remote_list_cb, &data);
- regfree(&preg);
if (error < 0) {
size_t i;
char *elem;
+
git_vector_foreach(&list, i, elem) {
git__free(elem);
}
@@ -1090,6 +1171,8 @@ int git_remote_list(git_strarray *remotes_list, git_repository *repo)
return error;
}
+ git_vector_uniq(&list, git__free);
+
remotes_list->strings = (char **)list.contents;
remotes_list->count = list.length;
@@ -1103,7 +1186,7 @@ void git_remote_check_cert(git_remote *remote, int check)
remote->check_cert = check;
}
-int git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callbacks)
+int git_remote_set_callbacks(git_remote *remote, const git_remote_callbacks *callbacks)
{
assert(remote && callbacks);
@@ -1112,7 +1195,7 @@ int git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callbacks
memcpy(&remote->callbacks, callbacks, sizeof(git_remote_callbacks));
if (remote->transport && remote->transport->set_callbacks)
- remote->transport->set_callbacks(remote->transport,
+ return remote->transport->set_callbacks(remote->transport,
remote->callbacks.progress,
NULL,
remote->callbacks.payload);
@@ -1120,17 +1203,6 @@ int git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callbacks
return 0;
}
-void git_remote_set_cred_acquire_cb(
- git_remote *remote,
- git_cred_acquire_cb cred_acquire_cb,
- void *payload)
-{
- assert(remote);
-
- remote->cred_acquire_cb = cred_acquire_cb;
- remote->cred_acquire_payload = payload;
-}
-
int git_remote_set_transport(git_remote *remote, git_transport *transport)
{
assert(remote && transport);
@@ -1418,12 +1490,12 @@ int git_remote_rename(
int git_remote_update_fetchhead(git_remote *remote)
{
- return remote->update_fetchhead;
+ return (remote->update_fetchhead != 0);
}
void git_remote_set_update_fetchhead(git_remote *remote, int value)
{
- remote->update_fetchhead = value;
+ remote->update_fetchhead = (value != 0);
}
int git_remote_is_valid_name(
@@ -1451,7 +1523,7 @@ git_refspec *git_remote__matching_refspec(git_remote *remote, const char *refnam
git_refspec *spec;
size_t i;
- git_vector_foreach(&remote->refspecs, i, spec) {
+ git_vector_foreach(&remote->active_refspecs, i, spec) {
if (spec->push)
continue;
@@ -1467,7 +1539,7 @@ git_refspec *git_remote__matching_dst_refspec(git_remote *remote, const char *re
git_refspec *spec;
size_t i;
- git_vector_foreach(&remote->refspecs, i, spec) {
+ git_vector_foreach(&remote->active_refspecs, i, spec) {
if (spec->push)
continue;
@@ -1490,17 +1562,71 @@ void git_remote_clear_refspecs(git_remote *remote)
git_vector_clear(&remote->refspecs);
}
+static int add_and_dwim(git_remote *remote, const char *str, int push)
+{
+ git_refspec *spec;
+ git_vector *vec;
+
+ if (add_refspec(remote, str, !push) < 0)
+ return -1;
+
+ vec = &remote->refspecs;
+ spec = git_vector_get(vec, vec->length - 1);
+ return git_refspec__dwim_one(&remote->active_refspecs, spec, &remote->refs);
+}
+
int git_remote_add_fetch(git_remote *remote, const char *refspec)
{
- return add_refspec(remote, refspec, true);
+ return add_and_dwim(remote, refspec, false);
}
int git_remote_add_push(git_remote *remote, const char *refspec)
{
- return add_refspec(remote, refspec, false);
+ return add_and_dwim(remote, refspec, true);
}
-static int copy_refspecs(git_strarray *array, git_remote *remote, int push)
+static int set_refspecs(git_remote *remote, git_strarray *array, int push)
+{
+ git_vector *vec = &remote->refspecs;
+ git_refspec *spec;
+ size_t i;
+
+ /* Start by removing any refspecs of the same type */
+ for (i = 0; i < vec->length; i++) {
+ spec = git_vector_get(vec, i);
+ if (spec->push != push)
+ continue;
+
+ git_refspec__free(spec);
+ git__free(spec);
+ git_vector_remove(vec, i);
+ i--;
+ }
+
+ /* And now we add the new ones */
+
+ for (i = 0; i < array->count; i++) {
+ if (add_refspec(remote, array->strings[i], !push) < 0)
+ return -1;
+ }
+
+ free_refspecs(&remote->active_refspecs);
+ git_vector_clear(&remote->active_refspecs);
+
+ return dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs);
+}
+
+int git_remote_set_fetch_refspecs(git_remote *remote, git_strarray *array)
+{
+ return set_refspecs(remote, array, false);
+}
+
+int git_remote_set_push_refspecs(git_remote *remote, git_strarray *array)
+{
+ return set_refspecs(remote, array, true);
+}
+
+static int copy_refspecs(git_strarray *array, git_remote *remote, unsigned int push)
{
size_t i;
git_vector refspecs;
@@ -1555,18 +1681,3 @@ const git_refspec *git_remote_get_refspec(git_remote *remote, size_t n)
{
return git_vector_get(&remote->refspecs, n);
}
-
-int git_remote_remove_refspec(git_remote *remote, size_t n)
-{
- git_refspec *spec;
-
- assert(remote);
-
- spec = git_vector_get(&remote->refspecs, n);
- if (spec) {
- git_refspec__free(spec);
- git__free(spec);
- }
-
- return git_vector_remove(&remote->refspecs, n);
-}
diff --git a/src/remote.h b/src/remote.h
index dce4803ed..4164a14b3 100644
--- a/src/remote.h
+++ b/src/remote.h
@@ -21,16 +21,15 @@ struct git_remote {
char *pushurl;
git_vector refs;
git_vector refspecs;
- git_cred_acquire_cb cred_acquire_cb;
- void *cred_acquire_payload;
+ git_vector active_refspecs;
git_transport *transport;
git_repository *repo;
git_remote_callbacks callbacks;
git_transfer_progress stats;
unsigned int need_pack;
git_remote_autotag_option_t download_tags;
- unsigned int check_cert;
- unsigned int update_fetchhead;
+ int check_cert;
+ int update_fetchhead;
};
const char* git_remote__urlfordirection(struct git_remote *remote, int direction);
diff --git a/src/repository.c b/src/repository.c
index ed9469c59..dcc02e4fb 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -33,8 +33,6 @@
#define GIT_REPO_VERSION 0
-#define GIT_TEMPLATE_DIR "/usr/share/git-core/templates"
-
static void set_odb(git_repository *repo, git_odb *odb)
{
if (odb) {
@@ -266,7 +264,7 @@ static int find_ceiling_dir_offset(
buf[--len] = '\0';
if (!strncmp(path, buf2, len) &&
- path[len] == '/' &&
+ (path[len] == '/' || !path[len]) &&
len > max_len)
{
max_len = len;
@@ -322,17 +320,18 @@ static int find_repo(
git_buf path = GIT_BUF_INIT;
struct stat st;
dev_t initial_device = 0;
- bool try_with_dot_git = false;
+ bool try_with_dot_git = ((flags & GIT_REPOSITORY_OPEN_BARE) != 0);
int ceiling_offset;
git_buf_free(repo_path);
- if ((error = git_path_prettify_dir(&path, start_path, NULL)) < 0)
+ if ((error = git_path_prettify(&path, start_path, NULL)) < 0)
return error;
ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs);
- if ((error = git_buf_joinpath(&path, path.ptr, DOT_GIT)) < 0)
+ if (!try_with_dot_git &&
+ (error = git_buf_joinpath(&path, path.ptr, DOT_GIT)) < 0)
return error;
while (!error && !git_buf_len(repo_path)) {
@@ -384,7 +383,7 @@ static int find_repo(
try_with_dot_git = !try_with_dot_git;
}
- if (!error && parent_path != NULL) {
+ if (!error && parent_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) {
if (!git_buf_len(repo_path))
git_buf_clear(parent_path);
else {
@@ -460,7 +459,9 @@ int git_repository_open_ext(
repo->path_repository = git_buf_detach(&path);
GITERR_CHECK_ALLOC(repo->path_repository);
- if ((error = load_config_data(repo)) < 0 ||
+ if ((flags & GIT_REPOSITORY_OPEN_BARE) != 0)
+ repo->is_bare = 1;
+ else if ((error = load_config_data(repo)) < 0 ||
(error = load_workdir(repo, &parent)) < 0)
{
git_repository_free(repo);
@@ -815,7 +816,7 @@ static int repo_init_create_head(const char *git_dir, const char *ref_name)
const char *fmt;
if (git_buf_joinpath(&ref_path, git_dir, GIT_HEAD_FILE) < 0 ||
- git_filebuf_open(&ref, ref_path.ptr, 0) < 0)
+ git_filebuf_open(&ref, ref_path.ptr, 0, GIT_REFS_FILE_MODE) < 0)
goto fail;
if (!ref_name)
@@ -827,7 +828,7 @@ static int repo_init_create_head(const char *git_dir, const char *ref_name)
fmt = "ref: " GIT_REFS_HEADS_DIR "%s\n";
if (git_filebuf_printf(&ref, fmt, ref_name) < 0 ||
- git_filebuf_commit(&ref, GIT_REFS_FILE_MODE) < 0)
+ git_filebuf_commit(&ref) < 0)
goto fail;
git_buf_free(&ref_path);
@@ -842,10 +843,6 @@ fail:
static bool is_chmod_supported(const char *file_path)
{
struct stat st1, st2;
- static int _is_supported = -1;
-
- if (_is_supported > -1)
- return _is_supported;
if (p_stat(file_path, &st1) < 0)
return false;
@@ -856,27 +853,19 @@ static bool is_chmod_supported(const char *file_path)
if (p_stat(file_path, &st2) < 0)
return false;
- _is_supported = (st1.st_mode != st2.st_mode);
-
- return _is_supported;
+ return (st1.st_mode != st2.st_mode);
}
static bool is_filesystem_case_insensitive(const char *gitdir_path)
{
git_buf path = GIT_BUF_INIT;
- static int _is_insensitive = -1;
-
- if (_is_insensitive > -1)
- return _is_insensitive;
+ int is_insensitive = -1;
- if (git_buf_joinpath(&path, gitdir_path, "CoNfIg") < 0)
- goto cleanup;
-
- _is_insensitive = git_path_exists(git_buf_cstr(&path));
+ if (!git_buf_joinpath(&path, gitdir_path, "CoNfIg"))
+ is_insensitive = git_path_exists(git_buf_cstr(&path));
-cleanup:
git_buf_free(&path);
- return _is_insensitive;
+ return is_insensitive;
}
static bool are_symlinks_supported(const char *wd_path)
@@ -884,26 +873,77 @@ static bool are_symlinks_supported(const char *wd_path)
git_buf path = GIT_BUF_INIT;
int fd;
struct stat st;
- static int _symlinks_supported = -1;
-
- if (_symlinks_supported > -1)
- return _symlinks_supported;
+ int symlinks_supported = -1;
- if ((fd = git_futils_mktmp(&path, wd_path)) < 0 ||
+ if ((fd = git_futils_mktmp(&path, wd_path, 0666)) < 0 ||
p_close(fd) < 0 ||
p_unlink(path.ptr) < 0 ||
p_symlink("testing", path.ptr) < 0 ||
p_lstat(path.ptr, &st) < 0)
- _symlinks_supported = false;
+ symlinks_supported = false;
else
- _symlinks_supported = (S_ISLNK(st.st_mode) != 0);
+ symlinks_supported = (S_ISLNK(st.st_mode) != 0);
(void)p_unlink(path.ptr);
git_buf_free(&path);
- return _symlinks_supported;
+ return symlinks_supported;
+}
+
+#ifdef GIT_USE_ICONV
+
+static const char *nfc_file = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D.XXXXXX";
+static const char *nfd_file = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D.XXXXXX";
+
+/* Check if the platform is decomposing unicode data for us. We will
+ * emulate core Git and prefer to use precomposed unicode data internally
+ * on these platforms, composing the decomposed unicode on the fly.
+ *
+ * This mainly happens on the Mac where HDFS stores filenames as
+ * decomposed unicode. Even on VFAT and SAMBA file systems, the Mac will
+ * return decomposed unicode from readdir() even when the actual
+ * filesystem is storing precomposed unicode.
+ */
+static bool does_fs_decompose_unicode_paths(const char *wd_path)
+{
+ git_buf path = GIT_BUF_INIT;
+ int fd;
+ bool found_decomposed = false;
+ char tmp[6];
+
+ /* Create a file using a precomposed path and then try to find it
+ * using the decomposed name. If the lookup fails, then we will mark
+ * that we should precompose unicode for this repository.
+ */
+ if (git_buf_joinpath(&path, wd_path, nfc_file) < 0 ||
+ (fd = p_mkstemp(path.ptr)) < 0)
+ goto done;
+ p_close(fd);
+
+ /* record trailing digits generated by mkstemp */
+ memcpy(tmp, path.ptr + path.size - sizeof(tmp), sizeof(tmp));
+
+ /* try to look up as NFD path */
+ if (git_buf_joinpath(&path, wd_path, nfd_file) < 0)
+ goto done;
+ memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp));
+
+ found_decomposed = git_path_exists(path.ptr);
+
+ /* remove temporary file (using original precomposed path) */
+ if (git_buf_joinpath(&path, wd_path, nfc_file) < 0)
+ goto done;
+ memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp));
+
+ (void)p_unlink(path.ptr);
+
+done:
+ git_buf_free(&path);
+ return found_decomposed;
}
+#endif
+
static int create_empty_file(const char *path, mode_t mode)
{
int fd;
@@ -921,71 +961,131 @@ static int create_empty_file(const char *path, mode_t mode)
return 0;
}
+static int repo_local_config(
+ git_config **out,
+ git_buf *config_dir,
+ git_repository *repo,
+ const char *repo_dir)
+{
+ int error = 0;
+ git_config *parent;
+ const char *cfg_path;
+
+ if (git_buf_joinpath(config_dir, repo_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
+ return -1;
+ cfg_path = git_buf_cstr(config_dir);
+
+ /* make LOCAL config if missing */
+ if (!git_path_isfile(cfg_path) &&
+ (error = create_empty_file(cfg_path, GIT_CONFIG_FILE_MODE)) < 0)
+ return error;
+
+ /* if no repo, just open that file directly */
+ if (!repo)
+ return git_config_open_ondisk(out, cfg_path);
+
+ /* otherwise, open parent config and get that level */
+ if ((error = git_repository_config__weakptr(&parent, repo)) < 0)
+ return error;
+
+ if (git_config_open_level(out, parent, GIT_CONFIG_LEVEL_LOCAL) < 0) {
+ giterr_clear();
+
+ if (!(error = git_config_add_file_ondisk(
+ parent, cfg_path, GIT_CONFIG_LEVEL_LOCAL, false)))
+ error = git_config_open_level(out, parent, GIT_CONFIG_LEVEL_LOCAL);
+ }
+
+ git_config_free(parent);
+
+ return error;
+}
+
+static int repo_init_fs_configs(
+ git_config *cfg,
+ const char *cfg_path,
+ const char *repo_dir,
+ const char *work_dir,
+ bool update_ignorecase)
+{
+ int error = 0;
+
+ if (!work_dir)
+ work_dir = repo_dir;
+
+ if ((error = git_config_set_bool(
+ cfg, "core.filemode", is_chmod_supported(cfg_path))) < 0)
+ return error;
+
+ if (!are_symlinks_supported(work_dir)) {
+ if ((error = git_config_set_bool(cfg, "core.symlinks", false)) < 0)
+ return error;
+ } else if (git_config_delete_entry(cfg, "core.symlinks") < 0)
+ giterr_clear();
+
+ if (update_ignorecase) {
+ if (is_filesystem_case_insensitive(repo_dir)) {
+ if ((error = git_config_set_bool(cfg, "core.ignorecase", true)) < 0)
+ return error;
+ } else if (git_config_delete_entry(cfg, "core.ignorecase") < 0)
+ giterr_clear();
+ }
+
+#ifdef GIT_USE_ICONV
+ if ((error = git_config_set_bool(
+ cfg, "core.precomposeunicode",
+ does_fs_decompose_unicode_paths(work_dir))) < 0)
+ return error;
+#endif
+
+ return 0;
+}
+
static int repo_init_config(
const char *repo_dir,
const char *work_dir,
- git_repository_init_options *opts)
+ uint32_t flags,
+ uint32_t mode)
{
int error = 0;
git_buf cfg_path = GIT_BUF_INIT;
git_config *config = NULL;
+ bool is_bare = ((flags & GIT_REPOSITORY_INIT_BARE) != 0);
+ bool is_reinit = ((flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0);
-#define SET_REPO_CONFIG(TYPE, NAME, VAL) do {\
- if ((error = git_config_set_##TYPE(config, NAME, VAL)) < 0) \
- goto cleanup; } while (0)
+ if ((error = repo_local_config(&config, &cfg_path, NULL, repo_dir)) < 0)
+ goto cleanup;
- if (git_buf_joinpath(&cfg_path, repo_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
- return -1;
+ if (is_reinit && (error = check_repositoryformatversion(config)) < 0)
+ goto cleanup;
- if (!git_path_isfile(git_buf_cstr(&cfg_path)) &&
- create_empty_file(git_buf_cstr(&cfg_path), GIT_CONFIG_FILE_MODE) < 0) {
- git_buf_free(&cfg_path);
- return -1;
- }
+#define SET_REPO_CONFIG(TYPE, NAME, VAL) do { \
+ if ((error = git_config_set_##TYPE(config, NAME, VAL)) < 0) \
+ goto cleanup; } while (0)
- if (git_config_open_ondisk(&config, git_buf_cstr(&cfg_path)) < 0) {
- git_buf_free(&cfg_path);
- return -1;
- }
+ SET_REPO_CONFIG(bool, "core.bare", is_bare);
+ SET_REPO_CONFIG(int32, "core.repositoryformatversion", GIT_REPO_VERSION);
- if ((opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0 &&
- (error = check_repositoryformatversion(config)) < 0)
+ if ((error = repo_init_fs_configs(
+ config, cfg_path.ptr, repo_dir, work_dir, !is_reinit)) < 0)
goto cleanup;
- SET_REPO_CONFIG(
- bool, "core.bare", (opts->flags & GIT_REPOSITORY_INIT_BARE) != 0);
- SET_REPO_CONFIG(
- int32, "core.repositoryformatversion", GIT_REPO_VERSION);
- SET_REPO_CONFIG(
- bool, "core.filemode", is_chmod_supported(git_buf_cstr(&cfg_path)));
-
- if (!(opts->flags & GIT_REPOSITORY_INIT_BARE)) {
+ if (!is_bare) {
SET_REPO_CONFIG(bool, "core.logallrefupdates", true);
- if (!are_symlinks_supported(work_dir))
- SET_REPO_CONFIG(bool, "core.symlinks", false);
-
- if (!(opts->flags & GIT_REPOSITORY_INIT__NATURAL_WD)) {
+ if (!(flags & GIT_REPOSITORY_INIT__NATURAL_WD))
SET_REPO_CONFIG(string, "core.worktree", work_dir);
- }
- else if ((opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0) {
+ else if (is_reinit) {
if (git_config_delete_entry(config, "core.worktree") < 0)
giterr_clear();
}
- } else {
- if (!are_symlinks_supported(repo_dir))
- SET_REPO_CONFIG(bool, "core.symlinks", false);
}
- if (!(opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) &&
- is_filesystem_case_insensitive(repo_dir))
- SET_REPO_CONFIG(bool, "core.ignorecase", true);
-
- if (opts->mode == GIT_REPOSITORY_INIT_SHARED_GROUP) {
+ if (mode == GIT_REPOSITORY_INIT_SHARED_GROUP) {
SET_REPO_CONFIG(int32, "core.sharedrepository", 1);
SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
}
- else if (opts->mode == GIT_REPOSITORY_INIT_SHARED_ALL) {
+ else if (mode == GIT_REPOSITORY_INIT_SHARED_ALL) {
SET_REPO_CONFIG(int32, "core.sharedrepository", 2);
SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
}
@@ -997,6 +1097,41 @@ cleanup:
return error;
}
+static int repo_reinit_submodule_fs(git_submodule *sm, const char *n, void *p)
+{
+ git_repository *smrepo = NULL;
+ GIT_UNUSED(n); GIT_UNUSED(p);
+
+ if (git_submodule_open(&smrepo, sm) < 0 ||
+ git_repository_reinit_filesystem(smrepo, true) < 0)
+ giterr_clear();
+ git_repository_free(smrepo);
+
+ return 0;
+}
+
+int git_repository_reinit_filesystem(git_repository *repo, int recurse)
+{
+ int error = 0;
+ git_buf path = GIT_BUF_INIT;
+ git_config *config = NULL;
+ const char *repo_dir = git_repository_path(repo);
+
+ if (!(error = repo_local_config(&config, &path, repo, repo_dir)))
+ error = repo_init_fs_configs(
+ config, path.ptr, repo_dir, git_repository_workdir(repo), true);
+
+ git_config_free(config);
+ git_buf_free(&path);
+
+ git_repository__cvar_cache_clear(repo);
+
+ if (!repo->is_bare && recurse)
+ (void)git_submodule_foreach(repo, repo_reinit_submodule_fs, NULL);
+
+ return error;
+}
+
static int repo_write_template(
const char *git_dir,
bool allow_overwrite,
@@ -1131,31 +1266,34 @@ static int repo_init_structure(
/* Copy external template if requested */
if (external_tpl) {
- git_config *cfg;
- const char *tdir;
+ git_config *cfg = NULL;
+ const char *tdir = NULL;
+ bool default_template = false;
+ git_buf template_buf = GIT_BUF_INIT;
if (opts->template_path)
tdir = opts->template_path;
- else if ((error = git_config_open_default(&cfg)) < 0)
- return error;
- else {
+ else if ((error = git_config_open_default(&cfg)) >= 0) {
error = git_config_get_string(&tdir, cfg, "init.templatedir");
-
- git_config_free(cfg);
-
- if (error && error != GIT_ENOTFOUND)
- return error;
-
giterr_clear();
- tdir = GIT_TEMPLATE_DIR;
}
- error = git_futils_cp_r(tdir, repo_dir,
- GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_CHMOD_DIRS |
- GIT_CPDIR_SIMPLE_TO_MODE, dmode);
+ if (!tdir) {
+ if (!(error = git_futils_find_template_dir(&template_buf)))
+ tdir = template_buf.ptr;
+ default_template = true;
+ }
+
+ if (tdir)
+ error = git_futils_cp_r(tdir, repo_dir,
+ GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_CHMOD_DIRS |
+ GIT_CPDIR_SIMPLE_TO_MODE, dmode);
+
+ git_buf_free(&template_buf);
+ git_config_free(cfg);
if (error < 0) {
- if (strcmp(tdir, GIT_TEMPLATE_DIR) != 0)
+ if (!default_template)
return error;
/* if template was default, ignore error and use internal */
@@ -1382,22 +1520,22 @@ int git_repository_init_ext(
opts->flags |= GIT_REPOSITORY_INIT__IS_REINIT;
error = repo_init_config(
- git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts);
+ repo_path.ptr, wd_path.ptr, opts->flags, opts->mode);
/* TODO: reinitialize the templates */
}
else {
if (!(error = repo_init_structure(
- git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts)) &&
+ repo_path.ptr, wd_path.ptr, opts)) &&
!(error = repo_init_config(
- git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts)))
+ repo_path.ptr, wd_path.ptr, opts->flags, opts->mode)))
error = repo_init_create_head(
- git_buf_cstr(&repo_path), opts->initial_head);
+ repo_path.ptr, opts->initial_head);
}
if (error < 0)
goto cleanup;
- error = git_repository_open(out, git_buf_cstr(&repo_path));
+ error = git_repository_open(out, repo_path.ptr);
if (!error && opts->origin_url)
error = repo_init_create_origin(*out, opts->origin_url);
@@ -1448,10 +1586,10 @@ int git_repository_head(git_reference **head_out, git_repository *repo)
error = git_reference_lookup_resolved(head_out, repo, git_reference_symbolic_target(head), -1);
git_reference_free(head);
- return error == GIT_ENOTFOUND ? GIT_EORPHANEDHEAD : error;
+ return error == GIT_ENOTFOUND ? GIT_EUNBORNBRANCH : error;
}
-int git_repository_head_orphan(git_repository *repo)
+int git_repository_head_unborn(git_repository *repo)
{
git_reference *ref = NULL;
int error;
@@ -1459,7 +1597,7 @@ int git_repository_head_orphan(git_repository *repo)
error = git_repository_head(&ref, repo);
git_reference_free(ref);
- if (error == GIT_EORPHANEDHEAD)
+ if (error == GIT_EUNBORNBRANCH)
return 1;
if (error < 0)
@@ -1492,24 +1630,20 @@ static int repo_contains_no_reference(git_repository *repo)
int git_repository_is_empty(git_repository *repo)
{
git_reference *head = NULL;
- int error;
+ int is_empty = 0;
if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0)
return -1;
- if (!(error = git_reference_type(head) == GIT_REF_SYMBOLIC))
- goto cleanup;
-
- if (!(error = strcmp(
- git_reference_symbolic_target(head),
- GIT_REFS_HEADS_DIR "master") == 0))
- goto cleanup;
-
- error = repo_contains_no_reference(repo);
+ if (git_reference_type(head) == GIT_REF_SYMBOLIC)
+ is_empty =
+ (strcmp(git_reference_symbolic_target(head),
+ GIT_REFS_HEADS_DIR "master") == 0) &&
+ repo_contains_no_reference(repo);
-cleanup:
git_reference_free(head);
- return error < 0 ? -1 : error;
+
+ return is_empty;
}
const char *git_repository_path(git_repository *repo)
@@ -1650,7 +1784,7 @@ int git_repository_hashfile(
const char *as_path)
{
int error;
- git_vector filters = GIT_VECTOR_INIT;
+ git_filter_list *fl = NULL;
git_file fd = -1;
git_off_t len;
git_buf full_path = GIT_BUF_INIT;
@@ -1672,7 +1806,8 @@ int git_repository_hashfile(
/* passing empty string for "as_path" indicated --no-filters */
if (strlen(as_path) > 0) {
- error = git_filters_load(&filters, repo, as_path, GIT_FILTER_TO_ODB);
+ error = git_filter_list_load(
+ &fl, repo, NULL, as_path, GIT_FILTER_TO_ODB);
if (error < 0)
return error;
} else {
@@ -1699,12 +1834,12 @@ int git_repository_hashfile(
goto cleanup;
}
- error = git_odb__hashfd_filtered(out, fd, (size_t)len, type, &filters);
+ error = git_odb__hashfd_filtered(out, fd, (size_t)len, type, fl);
cleanup:
if (fd >= 0)
p_close(fd);
- git_filters_free(&filters);
+ git_filter_list_free(fl);
git_buf_free(&full_path);
return error;
diff --git a/src/repository.h b/src/repository.h
index 12dc50d51..832df3bd2 100644
--- a/src/repository.h
+++ b/src/repository.h
@@ -37,6 +37,7 @@ typedef enum {
GIT_CVAR_IGNORESTAT, /* core.ignorestat */
GIT_CVAR_TRUSTCTIME, /* core.trustctime */
GIT_CVAR_ABBREV, /* core.abbrev */
+ GIT_CVAR_PRECOMPOSE, /* core.precomposeunicode */
GIT_CVAR_CACHE_MAX
} git_cvar_cached;
@@ -86,6 +87,8 @@ typedef enum {
GIT_TRUSTCTIME_DEFAULT = GIT_CVAR_TRUE,
/* core.abbrev */
GIT_ABBREV_DEFAULT = 7,
+ /* core.precomposeunicode */
+ GIT_PRECOMPOSE_DEFAULT = GIT_CVAR_FALSE,
} git_cvar_value;
diff --git a/src/reset.c b/src/reset.c
index cea212a93..a9780bfbc 100644
--- a/src/reset.c
+++ b/src/reset.c
@@ -24,10 +24,9 @@ int git_reset_default(
{
git_object *commit = NULL;
git_tree *tree = NULL;
- git_diff_list *diff = NULL;
+ git_diff *diff = NULL;
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- size_t i;
- git_diff_delta *delta;
+ size_t i, max_i;
git_index_entry entry;
int error;
git_index *index = NULL;
@@ -58,7 +57,9 @@ int git_reset_default(
&diff, repo, tree, index, &opts)) < 0)
goto cleanup;
- git_vector_foreach(&diff->deltas, i, delta) {
+ for (i = 0, max_i = git_diff_num_deltas(diff); i < max_i; ++i) {
+ const git_diff_delta *delta = git_diff_get_delta(diff, i);
+
if ((error = git_index_conflict_remove(index, delta->old_file.path)) < 0)
goto cleanup;
@@ -85,7 +86,7 @@ cleanup:
git_object_free(commit);
git_tree_free(tree);
git_index_free(index);
- git_diff_list_free(diff);
+ git_diff_free(diff);
return error;
}
@@ -135,7 +136,7 @@ int git_reset(
if (reset_type == GIT_RESET_HARD) {
/* overwrite working directory with HEAD */
- opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE | GIT_CHECKOUT_SKIP_UNMERGED;
if ((error = git_checkout_tree(repo, (git_object *)tree, &opts)) < 0)
goto cleanup;
diff --git a/src/revparse.c b/src/revparse.c
index bcfb0843f..c120b466f 100644
--- a/src/revparse.c
+++ b/src/revparse.c
@@ -93,11 +93,7 @@ static int revparse_lookup_object(
int error;
git_reference *ref;
- error = maybe_sha(object_out, repo, spec);
- if (!error)
- return 0;
-
- if (error < 0 && error != GIT_ENOTFOUND)
+ if ((error = maybe_sha(object_out, repo, spec)) != GIT_ENOTFOUND)
return error;
error = git_reference_dwim(&ref, repo, spec);
@@ -112,24 +108,17 @@ static int revparse_lookup_object(
return error;
}
- if (error < 0 && error != GIT_ENOTFOUND)
- return error;
-
- error = maybe_abbrev(object_out, repo, spec);
- if (!error)
- return 0;
-
- if (error < 0 && error != GIT_ENOTFOUND)
+ if (error != GIT_ENOTFOUND)
return error;
- error = maybe_describe(object_out, repo, spec);
- if (!error)
- return 0;
+ if ((strlen(spec) < GIT_OID_HEXSZ) &&
+ ((error = maybe_abbrev(object_out, repo, spec)) != GIT_ENOTFOUND))
+ return error;
- if (error < 0 && error != GIT_ENOTFOUND)
+ if ((error = maybe_describe(object_out, repo, spec)) != GIT_ENOTFOUND)
return error;
- giterr_set(GITERR_REFERENCE, "Refspec '%s' not found.", spec);
+ giterr_set(GITERR_REFERENCE, "Revspec '%s' not found.", spec);
return GIT_ENOTFOUND;
}
@@ -171,7 +160,7 @@ static int retrieve_previously_checked_out_branch_or_revision(git_object **out,
if (git_reference_lookup(&ref, repo, GIT_HEAD_FILE) < 0)
goto cleanup;
- if (git_reflog_read(&reflog, ref) < 0)
+ if (git_reflog_read(&reflog, repo, GIT_HEAD_FILE) < 0)
goto cleanup;
numentries = git_reflog_entrycount(reflog);
@@ -219,7 +208,7 @@ static int retrieve_oid_from_reflog(git_oid *oid, git_reference *ref, size_t ide
const git_reflog_entry *entry;
bool search_by_pos = (identifier <= 100000000);
- if (git_reflog_read(&reflog, ref) < 0)
+ if (git_reflog_read(&reflog, git_reference_owner(ref), git_reference_name(ref)) < 0)
return -1;
numentries = git_reflog_entrycount(reflog);
@@ -228,7 +217,7 @@ static int retrieve_oid_from_reflog(git_oid *oid, git_reference *ref, size_t ide
if (numentries < identifier + 1) {
giterr_set(
GITERR_REFERENCE,
- "Reflog for '%s' has only "PRIuZ" entries, asked for "PRIuZ,
+ "Reflog for '%s' has only %"PRIuZ" entries, asked for %"PRIuZ,
git_reference_name(ref), numentries, identifier);
error = GIT_ENOTFOUND;
@@ -685,6 +674,8 @@ int revparse__ext(
git_reference *reference = NULL;
git_object *base_rev = NULL;
+ bool should_return_reference = true;
+
assert(object_out && reference_out && repo && spec);
*object_out = NULL;
@@ -693,6 +684,8 @@ int revparse__ext(
while (spec[pos]) {
switch (spec[pos]) {
case '^':
+ should_return_reference = false;
+
if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0)
goto cleanup;
@@ -725,6 +718,8 @@ int revparse__ext(
{
git_object *temp_object = NULL;
+ should_return_reference = false;
+
if ((error = extract_how_many(&n, spec, &pos)) < 0)
goto cleanup;
@@ -743,6 +738,8 @@ int revparse__ext(
{
git_object *temp_object = NULL;
+ should_return_reference = false;
+
if ((error = extract_path(&buf, spec, &pos)) < 0)
goto cleanup;
@@ -807,6 +804,11 @@ int revparse__ext(
if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0)
goto cleanup;
+ if (!should_return_reference) {
+ git_reference_free(reference);
+ reference = NULL;
+ }
+
*object_out = base_rev;
*reference_out = reference;
*identifier_len_out = identifier_len;
@@ -899,13 +901,9 @@ int git_revparse(
rstr++;
}
- if ((error = git_revparse_single(&revspec->from, repo, lstr)) < 0) {
- return error;
- }
-
- if ((error = git_revparse_single(&revspec->to, repo, rstr)) < 0) {
- return error;
- }
+ error = git_revparse_single(&revspec->from, repo, lstr);
+ if (!error)
+ error = git_revparse_single(&revspec->to, repo, rstr);
git__free((void*)lstr);
} else {
diff --git a/src/revwalk.c b/src/revwalk.c
index 528d02b20..3dd14b419 100644
--- a/src/revwalk.c
+++ b/src/revwalk.c
@@ -14,8 +14,6 @@
#include "git2/revparse.h"
#include "merge.h"
-#include <regex.h>
-
git_commit_list_node *git_revwalk__commit_lookup(
git_revwalk *walk, const git_oid *oid)
{
@@ -41,28 +39,50 @@ git_commit_list_node *git_revwalk__commit_lookup(
return commit;
}
-static void mark_uninteresting(git_commit_list_node *commit)
+static int mark_uninteresting(git_commit_list_node *commit)
{
unsigned short i;
+ git_array_t(git_commit_list_node *) pending = GIT_ARRAY_INIT;
+ git_commit_list_node **tmp;
+
assert(commit);
- commit->uninteresting = 1;
+ git_array_alloc(pending);
+ GITERR_CHECK_ARRAY(pending);
- /* This means we've reached a merge base, so there's no need to walk any more */
- if ((commit->flags & (RESULT | STALE)) == RESULT)
- return;
+ do {
+ commit->uninteresting = 1;
+
+ /* This means we've reached a merge base, so there's no need to walk any more */
+ if ((commit->flags & (RESULT | STALE)) == RESULT) {
+ tmp = git_array_pop(pending);
+ commit = tmp ? *tmp : NULL;
+ continue;
+ }
+
+ for (i = 0; i < commit->out_degree; ++i)
+ if (!commit->parents[i]->uninteresting) {
+ git_commit_list_node **node = git_array_alloc(pending);
+ GITERR_CHECK_ALLOC(node);
+ *node = commit->parents[i];
+ }
+
+ tmp = git_array_pop(pending);
+ commit = tmp ? *tmp : NULL;
- for (i = 0; i < commit->out_degree; ++i)
- if (!commit->parents[i]->uninteresting)
- mark_uninteresting(commit->parents[i]);
+ } while (git_array_size(pending) > 0);
+
+ git_array_clear(pending);
+
+ return 0;
}
static int process_commit(git_revwalk *walk, git_commit_list_node *commit, int hide)
{
int error;
- if (hide)
- mark_uninteresting(commit);
+ if (hide && mark_uninteresting(commit) < 0)
+ return -1;
if (commit->seen)
return 0;
@@ -77,10 +97,14 @@ static int process_commit(git_revwalk *walk, git_commit_list_node *commit, int h
static int process_commit_parents(git_revwalk *walk, git_commit_list_node *commit)
{
- unsigned short i;
+ unsigned short i, max;
int error = 0;
- for (i = 0; i < commit->out_degree && !error; ++i)
+ max = commit->out_degree;
+ if (walk->first_parent && commit->out_degree)
+ max = 1;
+
+ for (i = 0; i < max && !error; ++i)
error = process_commit(walk, commit->parents[i], commit->uninteresting);
return error;
@@ -155,48 +179,35 @@ static int push_glob_cb(const char *refname, void *data_)
static int push_glob(git_revwalk *walk, const char *glob, int hide)
{
+ int error = 0;
git_buf buf = GIT_BUF_INIT;
struct push_cb_data data;
- regex_t preg;
+ size_t wildcard;
assert(walk && glob);
/* refs/ is implied if not given in the glob */
- if (strncmp(glob, GIT_REFS_DIR, strlen(GIT_REFS_DIR))) {
- git_buf_printf(&buf, GIT_REFS_DIR "%s", glob);
- } else {
+ if (git__prefixcmp(glob, GIT_REFS_DIR) != 0)
+ git_buf_joinpath(&buf, GIT_REFS_DIR, glob);
+ else
git_buf_puts(&buf, glob);
- }
/* If no '?', '*' or '[' exist, we append '/ *' to the glob */
- memset(&preg, 0x0, sizeof(regex_t));
- if (regcomp(&preg, "[?*[]", REG_EXTENDED)) {
- giterr_set(GITERR_OS, "Regex failed to compile");
- git_buf_free(&buf);
- return -1;
- }
-
- if (regexec(&preg, glob, 0, NULL, 0))
- git_buf_puts(&buf, "/*");
-
- if (git_buf_oom(&buf))
- goto on_error;
+ wildcard = strcspn(glob, "?*[");
+ if (!glob[wildcard])
+ git_buf_put(&buf, "/*", 2);
data.walk = walk;
data.hide = hide;
- if (git_reference_foreach_glob(
- walk->repo, git_buf_cstr(&buf), push_glob_cb, &data) < 0)
- goto on_error;
-
- regfree(&preg);
- git_buf_free(&buf);
- return 0;
+ if (git_buf_oom(&buf))
+ error = -1;
+ else
+ error = git_reference_foreach_glob(
+ walk->repo, git_buf_cstr(&buf), push_glob_cb, &data);
-on_error:
- regfree(&preg);
git_buf_free(&buf);
- return -1;
+ return error;
}
int git_revwalk_push_glob(git_revwalk *walk, const char *glob)
@@ -311,7 +322,7 @@ static int revwalk_next_unsorted(git_commit_list_node **object_out, git_revwalk
static int revwalk_next_toposort(git_commit_list_node **object_out, git_revwalk *walk)
{
git_commit_list_node *next;
- unsigned short i;
+ unsigned short i, max;
for (;;) {
next = git_commit_list_pop(&walk->iterator_topo);
@@ -325,7 +336,12 @@ static int revwalk_next_toposort(git_commit_list_node **object_out, git_revwalk
continue;
}
- for (i = 0; i < next->out_degree; ++i) {
+
+ max = next->out_degree;
+ if (walk->first_parent && next->out_degree)
+ max = 1;
+
+ for (i = 0; i < max; ++i) {
git_commit_list_node *parent = next->parents[i];
if (--parent->in_degree == 0 && parent->topo_delay) {
@@ -483,6 +499,11 @@ void git_revwalk_sorting(git_revwalk *walk, unsigned int sort_mode)
}
}
+void git_revwalk_simplify_first_parent(git_revwalk *walk)
+{
+ walk->first_parent = 1;
+}
+
int git_revwalk_next(git_oid *oid, git_revwalk *walk)
{
int error;
diff --git a/src/revwalk.h b/src/revwalk.h
index 22696dfcd..8c821d098 100644
--- a/src/revwalk.h
+++ b/src/revwalk.h
@@ -31,7 +31,8 @@ struct git_revwalk {
int (*get_next)(git_commit_list_node **, git_revwalk *);
int (*enqueue)(git_revwalk *, git_commit_list_node *);
- unsigned walking:1;
+ unsigned walking:1,
+ first_parent: 1;
unsigned int sorting;
/* merge base calculation */
diff --git a/src/sha1_lookup.c b/src/sha1_lookup.c
index b7e66cc69..c6b561340 100644
--- a/src/sha1_lookup.c
+++ b/src/sha1_lookup.c
@@ -9,6 +9,7 @@
#include "sha1_lookup.h"
#include "common.h"
+#include "oid.h"
/*
* Conventional binary search loop looks like this:
@@ -108,7 +109,54 @@ int sha1_entry_pos(const void *table,
* 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];
@@ -176,3 +224,26 @@ int sha1_entry_pos(const void *table,
} while (lo < hi);
return -((int)lo)-1;
}
+
+int sha1_position(const void *table,
+ size_t stride,
+ unsigned lo, unsigned hi,
+ const unsigned char *key)
+{
+ const unsigned char *base = table;
+
+ do {
+ unsigned mi = (lo + hi) / 2;
+ int cmp = git_oid__hashcmp(base + mi * stride, key);
+
+ if (!cmp)
+ return mi;
+
+ if (cmp > 0)
+ hi = mi;
+ else
+ lo = mi+1;
+ } while (lo < hi);
+
+ return -((int)lo)-1;
+}
diff --git a/src/sha1_lookup.h b/src/sha1_lookup.h
index 9a3537273..3799620c7 100644
--- a/src/sha1_lookup.h
+++ b/src/sha1_lookup.h
@@ -15,4 +15,9 @@ int sha1_entry_pos(const void *table,
unsigned lo, unsigned hi, unsigned nr,
const unsigned char *key);
+int sha1_position(const void *table,
+ size_t stride,
+ unsigned lo, unsigned hi,
+ const unsigned char *key);
+
#endif
diff --git a/src/signature.c b/src/signature.c
index 0a34ccfaa..ec51a42e9 100644
--- a/src/signature.c
+++ b/src/signature.c
@@ -74,7 +74,7 @@ int git_signature_new(git_signature **sig_out, const char *name, const char *ema
git_signature_free(p);
return signature_error("Signature cannot have an empty name");
}
-
+
p->when.time = time;
p->when.offset = offset;
@@ -84,8 +84,12 @@ int git_signature_new(git_signature **sig_out, const char *name, const char *ema
git_signature *git_signature_dup(const git_signature *sig)
{
- git_signature *new = git__calloc(1, sizeof(git_signature));
+ git_signature *new;
+ if (sig == NULL)
+ return NULL;
+
+ new = git__calloc(1, sizeof(git_signature));
if (new == NULL)
return NULL;
@@ -129,6 +133,23 @@ int git_signature_now(git_signature **sig_out, const char *name, const char *ema
return 0;
}
+int git_signature_default(git_signature **out, git_repository *repo)
+{
+ int error;
+ git_config *cfg;
+ const char *user_name, *user_email;
+
+ if ((error = git_repository_config(&cfg, repo)) < 0)
+ return error;
+
+ if (!(error = git_config_get_string(&user_name, cfg, "user.name")) &&
+ !(error = git_config_get_string(&user_email, cfg, "user.email")))
+ error = git_signature_now(out, user_name, user_email);
+
+ git_config_free(cfg);
+ return error;
+}
+
int git_signature__parse(git_signature *sig, const char **buffer_out,
const char *buffer_end, const char *header, char ender)
{
diff --git a/src/sortedcache.c b/src/sortedcache.c
new file mode 100644
index 000000000..466e55dbe
--- /dev/null
+++ b/src/sortedcache.c
@@ -0,0 +1,380 @@
+#include "sortedcache.h"
+
+GIT__USE_STRMAP;
+
+int git_sortedcache_new(
+ git_sortedcache **out,
+ size_t item_path_offset,
+ git_sortedcache_free_item_fn free_item,
+ void *free_item_payload,
+ git_vector_cmp item_cmp,
+ const char *path)
+{
+ git_sortedcache *sc;
+ size_t pathlen;
+
+ pathlen = path ? strlen(path) : 0;
+
+ sc = git__calloc(sizeof(git_sortedcache) + pathlen + 1, 1);
+ GITERR_CHECK_ALLOC(sc);
+
+ if (git_pool_init(&sc->pool, 1, 0) < 0 ||
+ git_vector_init(&sc->items, 4, item_cmp) < 0 ||
+ (sc->map = git_strmap_alloc()) == NULL)
+ goto fail;
+
+ if (git_rwlock_init(&sc->lock)) {
+ giterr_set(GITERR_OS, "Failed to initialize lock");
+ goto fail;
+ }
+
+ sc->item_path_offset = item_path_offset;
+ sc->free_item = free_item;
+ sc->free_item_payload = free_item_payload;
+ GIT_REFCOUNT_INC(sc);
+ if (pathlen)
+ memcpy(sc->path, path, pathlen);
+
+ *out = sc;
+ return 0;
+
+fail:
+ if (sc->map)
+ git_strmap_free(sc->map);
+ git_vector_free(&sc->items);
+ git_pool_clear(&sc->pool);
+ git__free(sc);
+ return -1;
+}
+
+void git_sortedcache_incref(git_sortedcache *sc)
+{
+ GIT_REFCOUNT_INC(sc);
+}
+
+const char *git_sortedcache_path(git_sortedcache *sc)
+{
+ return sc->path;
+}
+
+static void sortedcache_clear(git_sortedcache *sc)
+{
+ git_strmap_clear(sc->map);
+
+ if (sc->free_item) {
+ size_t i;
+ void *item;
+
+ git_vector_foreach(&sc->items, i, item) {
+ sc->free_item(sc->free_item_payload, item);
+ }
+ }
+
+ git_vector_clear(&sc->items);
+
+ git_pool_clear(&sc->pool);
+}
+
+static void sortedcache_free(git_sortedcache *sc)
+{
+ /* acquire write lock to make sure everyone else is done */
+ if (git_sortedcache_wlock(sc) < 0)
+ return;
+
+ sortedcache_clear(sc);
+ git_vector_free(&sc->items);
+ git_strmap_free(sc->map);
+
+ git_sortedcache_wunlock(sc);
+
+ git_rwlock_free(&sc->lock);
+ git__free(sc);
+}
+
+void git_sortedcache_free(git_sortedcache *sc)
+{
+ if (!sc)
+ return;
+ GIT_REFCOUNT_DEC(sc, sortedcache_free);
+}
+
+static int sortedcache_copy_item(void *payload, void *tgt_item, void *src_item)
+{
+ git_sortedcache *sc = payload;
+ /* path will already have been copied by upsert */
+ memcpy(tgt_item, src_item, sc->item_path_offset);
+ return 0;
+}
+
+/* copy a sorted cache */
+int git_sortedcache_copy(
+ git_sortedcache **out,
+ git_sortedcache *src,
+ bool lock,
+ int (*copy_item)(void *payload, void *tgt_item, void *src_item),
+ void *payload)
+{
+ int error = 0;
+ git_sortedcache *tgt;
+ size_t i;
+ void *src_item, *tgt_item;
+
+ /* just use memcpy if no special copy fn is passed in */
+ if (!copy_item) {
+ copy_item = sortedcache_copy_item;
+ payload = src;
+ }
+
+ if ((error = git_sortedcache_new(
+ &tgt, src->item_path_offset,
+ src->free_item, src->free_item_payload,
+ src->items._cmp, src->path)) < 0)
+ return error;
+
+ if (lock && git_sortedcache_rlock(src) < 0) {
+ git_sortedcache_free(tgt);
+ return -1;
+ }
+
+ git_vector_foreach(&src->items, i, src_item) {
+ char *path = ((char *)src_item) + src->item_path_offset;
+
+ if ((error = git_sortedcache_upsert(&tgt_item, tgt, path)) < 0 ||
+ (error = copy_item(payload, tgt_item, src_item)) < 0)
+ break;
+ }
+
+ if (lock)
+ git_sortedcache_runlock(src);
+ if (error)
+ git_sortedcache_free(tgt);
+
+ *out = !error ? tgt : NULL;
+
+ return error;
+}
+
+/* lock sortedcache while making modifications */
+int git_sortedcache_wlock(git_sortedcache *sc)
+{
+ GIT_UNUSED(sc); /* prevent warning when compiled w/o threads */
+
+ if (git_rwlock_wrlock(&sc->lock) < 0) {
+ giterr_set(GITERR_OS, "Unable to acquire write lock on cache");
+ return -1;
+ }
+ return 0;
+}
+
+/* unlock sorted cache when done with modifications */
+void git_sortedcache_wunlock(git_sortedcache *sc)
+{
+ git_vector_sort(&sc->items);
+ git_rwlock_wrunlock(&sc->lock);
+}
+
+/* lock sortedcache for read */
+int git_sortedcache_rlock(git_sortedcache *sc)
+{
+ GIT_UNUSED(sc); /* prevent warning when compiled w/o threads */
+
+ if (git_rwlock_rdlock(&sc->lock) < 0) {
+ giterr_set(GITERR_OS, "Unable to acquire read lock on cache");
+ return -1;
+ }
+ return 0;
+}
+
+/* unlock sorted cache when done reading */
+void git_sortedcache_runlock(git_sortedcache *sc)
+{
+ GIT_UNUSED(sc); /* prevent warning when compiled w/o threads */
+ git_rwlock_rdunlock(&sc->lock);
+}
+
+/* if the file has changed, lock cache and load file contents into buf;
+ * returns <0 on error, >0 if file has not changed
+ */
+int git_sortedcache_lockandload(git_sortedcache *sc, git_buf *buf)
+{
+ int error, fd;
+
+ if ((error = git_sortedcache_wlock(sc)) < 0)
+ return error;
+
+ if ((error = git_futils_filestamp_check(&sc->stamp, sc->path)) <= 0)
+ goto unlock;
+
+ if (!git__is_sizet(sc->stamp.size)) {
+ giterr_set(GITERR_INVALID, "Unable to load file larger than size_t");
+ error = -1;
+ goto unlock;
+ }
+
+ if ((fd = git_futils_open_ro(sc->path)) < 0) {
+ error = fd;
+ goto unlock;
+ }
+
+ if (buf)
+ error = git_futils_readbuffer_fd(buf, fd, (size_t)sc->stamp.size);
+
+ (void)p_close(fd);
+
+ if (error < 0)
+ goto unlock;
+
+ return 1; /* return 1 -> file needs reload and was successfully loaded */
+
+unlock:
+ git_sortedcache_wunlock(sc);
+ return error;
+}
+
+void git_sortedcache_updated(git_sortedcache *sc)
+{
+ /* update filestamp to latest value */
+ if (git_futils_filestamp_check(&sc->stamp, sc->path) < 0)
+ giterr_clear();
+}
+
+/* release all items in sorted cache */
+int git_sortedcache_clear(git_sortedcache *sc, bool wlock)
+{
+ if (wlock && git_sortedcache_wlock(sc) < 0)
+ return -1;
+
+ sortedcache_clear(sc);
+
+ if (wlock)
+ git_sortedcache_wunlock(sc);
+
+ return 0;
+}
+
+/* find and/or insert item, returning pointer to item data */
+int git_sortedcache_upsert(void **out, git_sortedcache *sc, const char *key)
+{
+ int error = 0;
+ khiter_t pos;
+ void *item;
+ size_t keylen, itemlen;
+ char *item_key;
+
+ pos = git_strmap_lookup_index(sc->map, key);
+ if (git_strmap_valid_index(sc->map, pos)) {
+ item = git_strmap_value_at(sc->map, pos);
+ goto done;
+ }
+
+ keylen = strlen(key);
+ itemlen = sc->item_path_offset + keylen + 1;
+ itemlen = (itemlen + 7) & ~7;
+
+ if ((item = git_pool_mallocz(&sc->pool, (uint32_t)itemlen)) == NULL) {
+ /* don't use GITERR_CHECK_ALLOC b/c of lock */
+ error = -1;
+ goto done;
+ }
+
+ /* one strange thing is that even if the vector or hash table insert
+ * fail, there is no way to free the pool item so we just abandon it
+ */
+
+ item_key = ((char *)item) + sc->item_path_offset;
+ memcpy(item_key, key, keylen);
+
+ pos = kh_put(str, sc->map, item_key, &error);
+ if (error < 0)
+ goto done;
+
+ if (!error)
+ kh_key(sc->map, pos) = item_key;
+ kh_val(sc->map, pos) = item;
+
+ error = git_vector_insert(&sc->items, item);
+ if (error < 0)
+ git_strmap_delete_at(sc->map, pos);
+
+done:
+ if (out)
+ *out = !error ? item : NULL;
+ return error;
+}
+
+/* lookup item by key */
+void *git_sortedcache_lookup(const git_sortedcache *sc, const char *key)
+{
+ khiter_t pos = git_strmap_lookup_index(sc->map, key);
+ if (git_strmap_valid_index(sc->map, pos))
+ return git_strmap_value_at(sc->map, pos);
+ return NULL;
+}
+
+/* find out how many items are in the cache */
+size_t git_sortedcache_entrycount(const git_sortedcache *sc)
+{
+ return git_vector_length(&sc->items);
+}
+
+/* lookup item by index */
+void *git_sortedcache_entry(git_sortedcache *sc, size_t pos)
+{
+ /* make sure the items are sorted so this gets the correct item */
+ if (!sc->items.sorted)
+ git_vector_sort(&sc->items);
+
+ return git_vector_get(&sc->items, pos);
+}
+
+/* helper struct so bsearch callback can know offset + key value for cmp */
+struct sortedcache_magic_key {
+ size_t offset;
+ const char *key;
+};
+
+static int sortedcache_magic_cmp(const void *key, const void *value)
+{
+ const struct sortedcache_magic_key *magic = key;
+ const char *value_key = ((const char *)value) + magic->offset;
+ return strcmp(magic->key, value_key);
+}
+
+/* lookup index of item by key */
+int git_sortedcache_lookup_index(
+ size_t *out, git_sortedcache *sc, const char *key)
+{
+ struct sortedcache_magic_key magic;
+
+ magic.offset = sc->item_path_offset;
+ magic.key = key;
+
+ return git_vector_bsearch2(out, &sc->items, sortedcache_magic_cmp, &magic);
+}
+
+/* remove entry from cache */
+int git_sortedcache_remove(git_sortedcache *sc, size_t pos)
+{
+ char *item;
+ khiter_t mappos;
+
+ /* because of pool allocation, this can't actually remove the item,
+ * but we can remove it from the items vector and the hash table.
+ */
+
+ if ((item = git_vector_get(&sc->items, pos)) == NULL) {
+ giterr_set(GITERR_INVALID, "Removing item out of range");
+ return GIT_ENOTFOUND;
+ }
+
+ (void)git_vector_remove(&sc->items, pos);
+
+ mappos = git_strmap_lookup_index(sc->map, item + sc->item_path_offset);
+ git_strmap_delete_at(sc->map, mappos);
+
+ if (sc->free_item)
+ sc->free_item(sc->free_item_payload, item);
+
+ return 0;
+}
+
diff --git a/src/sortedcache.h b/src/sortedcache.h
new file mode 100644
index 000000000..4cacad62b
--- /dev/null
+++ b/src/sortedcache.h
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_sorted_cache_h__
+#define INCLUDE_sorted_cache_h__
+
+#include "util.h"
+#include "fileops.h"
+#include "vector.h"
+#include "thread-utils.h"
+#include "pool.h"
+#include "strmap.h"
+
+#include <stddef.h>
+
+/*
+ * The purpose of this data structure is to cache the parsed contents of a
+ * file (a.k.a. the backing file) where each item in the file can be
+ * identified by a key string and you want to both look them up by name
+ * and traverse them in sorted order. Each item is assumed to itself end
+ * in a GIT_FLEX_ARRAY.
+ */
+
+typedef void (*git_sortedcache_free_item_fn)(void *payload, void *item);
+
+typedef struct {
+ git_refcount rc;
+ git_rwlock lock;
+ size_t item_path_offset;
+ git_sortedcache_free_item_fn free_item;
+ void *free_item_payload;
+ git_pool pool;
+ git_vector items;
+ git_strmap *map;
+ git_futils_filestamp stamp;
+ char path[GIT_FLEX_ARRAY];
+} git_sortedcache;
+
+/* Create a new sortedcache
+ *
+ * Even though every sortedcache stores items with a GIT_FLEX_ARRAY at
+ * the end containing their key string, you have to provide the item_cmp
+ * sorting function because the sorting function doesn't get a payload
+ * and therefore can't know the offset to the item key string. :-(
+ *
+ * @param out The allocated git_sortedcache
+ * @param item_path_offset Offset to the GIT_FLEX_ARRAY item key in the
+ * struct - use offsetof(struct mine, key-field) to get this
+ * @param free_item Optional callback to free each item
+ * @param free_item_payload Optional payload passed to free_item callback
+ * @param item_cmp Compare the keys of two items
+ * @param path The path to the backing store file for this cache; this
+ * may be NULL. The cache makes it easy to load this and check
+ * if it has been modified since the last load and/or write.
+ */
+int git_sortedcache_new(
+ git_sortedcache **out,
+ size_t item_path_offset, /* use offsetof(struct, path-field) macro */
+ git_sortedcache_free_item_fn free_item,
+ void *free_item_payload,
+ git_vector_cmp item_cmp,
+ const char *path);
+
+/* Copy a sorted cache
+ *
+ * - `copy_item` can be NULL to just use memcpy
+ * - if `lock`, grabs read lock on `src` during copy and releases after
+ */
+int git_sortedcache_copy(
+ git_sortedcache **out,
+ git_sortedcache *src,
+ bool lock,
+ int (*copy_item)(void *payload, void *tgt_item, void *src_item),
+ void *payload);
+
+/* Free sorted cache (first calling `free_item` callbacks)
+ *
+ * Don't call on a locked collection - it may acquire a write lock
+ */
+void git_sortedcache_free(git_sortedcache *sc);
+
+/* Increment reference count - balance with call to free */
+void git_sortedcache_incref(git_sortedcache *sc);
+
+/* Get the pathname associated with this cache at creation time */
+const char *git_sortedcache_path(git_sortedcache *sc);
+
+/*
+ * CACHE WRITE FUNCTIONS
+ *
+ * The following functions require you to have a writer lock to make the
+ * modification. Some of the functions take a `wlock` parameter and
+ * will optionally lock and unlock for you if that is passed as true.
+ *
+ */
+
+/* Lock sortedcache for write */
+int git_sortedcache_wlock(git_sortedcache *sc);
+
+/* Unlock sorted cache when done with write */
+void git_sortedcache_wunlock(git_sortedcache *sc);
+
+/* Lock cache and load backing file into a buffer.
+ *
+ * This grabs a write lock on the cache then looks at the modification
+ * time and size of the file on disk.
+ *
+ * If the file appears to have changed, this loads the file contents into
+ * the buffer and returns a positive value leaving the cache locked - the
+ * caller should parse the file content, update the cache as needed, then
+ * release the lock. NOTE: In this case, the caller MUST unlock the cache.
+ *
+ * If the file appears to be unchanged, then this automatically releases
+ * the lock on the cache, clears the buffer, and returns 0.
+ *
+ * @return 0 if up-to-date, 1 if out-of-date, <0 on error
+ */
+int git_sortedcache_lockandload(git_sortedcache *sc, git_buf *buf);
+
+/* Refresh file timestamp after write completes
+ * You should already be holding the write lock when you call this.
+ */
+void git_sortedcache_updated(git_sortedcache *sc);
+
+/* Release all items in sorted cache
+ *
+ * If `wlock` is true, grabs write lock and releases when done, otherwise
+ * you should already be holding a write lock when you call this.
+ */
+int git_sortedcache_clear(git_sortedcache *sc, bool wlock);
+
+/* Find and/or insert item, returning pointer to item data.
+ * You should already be holding the write lock when you call this.
+ */
+int git_sortedcache_upsert(
+ void **out, git_sortedcache *sc, const char *key);
+
+/* Removes entry at pos from cache
+ * You should already be holding the write lock when you call this.
+ */
+int git_sortedcache_remove(git_sortedcache *sc, size_t pos);
+
+/*
+ * CACHE READ FUNCTIONS
+ *
+ * The following functions access items in the cache. To prevent the
+ * results from being invalidated before they can be used, you should be
+ * holding either a read lock or a write lock when using these functions.
+ *
+ */
+
+/* Lock sortedcache for read */
+int git_sortedcache_rlock(git_sortedcache *sc);
+
+/* Unlock sorted cache when done with read */
+void git_sortedcache_runlock(git_sortedcache *sc);
+
+/* Lookup item by key - returns NULL if not found */
+void *git_sortedcache_lookup(const git_sortedcache *sc, const char *key);
+
+/* Get how many items are in the cache
+ *
+ * You can call this function without holding a lock, but be aware
+ * that it may change before you use it.
+ */
+size_t git_sortedcache_entrycount(const git_sortedcache *sc);
+
+/* Lookup item by index - returns NULL if out of range */
+void *git_sortedcache_entry(git_sortedcache *sc, size_t pos);
+
+/* Lookup index of item by key - returns GIT_ENOTFOUND if not found */
+int git_sortedcache_lookup_index(
+ size_t *out, git_sortedcache *sc, const char *key);
+
+#endif
diff --git a/src/stash.c b/src/stash.c
index 1222634d5..083c2a4cd 100644
--- a/src/stash.c
+++ b/src/stash.c
@@ -27,7 +27,7 @@ static int retrieve_head(git_reference **out, git_repository *repo)
{
int error = git_repository_head(out, repo);
- if (error == GIT_EORPHANEDHEAD)
+ if (error == GIT_EUNBORNBRANCH)
return create_error(error, "You do not have the initial commit yet.");
return error;
@@ -117,7 +117,7 @@ static int build_tree_from_index(git_tree **out, git_index *index)
static int commit_index(
git_commit **i_commit,
git_index *index,
- git_signature *stasher,
+ const git_signature *stasher,
const char *message,
const git_commit *parent)
{
@@ -153,65 +153,61 @@ cleanup:
return error;
}
-struct cb_data {
- git_index *index;
-
- int error;
-
+struct stash_update_rules {
bool include_changed;
bool include_untracked;
bool include_ignored;
};
-static int update_index_cb(
- const git_diff_delta *delta,
- float progress,
- void *payload)
+static int stash_update_index_from_diff(
+ git_index *index,
+ const git_diff *diff,
+ struct stash_update_rules *data)
{
- struct cb_data *data = (struct cb_data *)payload;
- const char *add_path = NULL;
-
- GIT_UNUSED(progress);
-
- switch (delta->status) {
- case GIT_DELTA_IGNORED:
- if (data->include_ignored)
- add_path = delta->new_file.path;
- break;
-
- case GIT_DELTA_UNTRACKED:
- if (data->include_untracked)
- add_path = delta->new_file.path;
- break;
-
- case GIT_DELTA_ADDED:
- case GIT_DELTA_MODIFIED:
- if (data->include_changed)
- add_path = delta->new_file.path;
- break;
-
- case GIT_DELTA_DELETED:
- if (!data->include_changed)
+ int error = 0;
+ size_t d, max_d = git_diff_num_deltas(diff);
+
+ for (d = 0; !error && d < max_d; ++d) {
+ const char *add_path = NULL;
+ const git_diff_delta *delta = git_diff_get_delta(diff, d);
+
+ switch (delta->status) {
+ case GIT_DELTA_IGNORED:
+ if (data->include_ignored)
+ add_path = delta->new_file.path;
break;
- if (git_index_find(NULL, data->index, delta->old_file.path) == 0)
- data->error = git_index_remove(
- data->index, delta->old_file.path, 0);
- break;
-
- default:
- /* Unimplemented */
- giterr_set(
- GITERR_INVALID,
- "Cannot update index. Unimplemented status (%d)",
- delta->status);
- data->error = -1;
- break;
- }
- if (add_path != NULL)
- data->error = git_index_add_bypath(data->index, add_path);
+ case GIT_DELTA_UNTRACKED:
+ if (data->include_untracked)
+ add_path = delta->new_file.path;
+ break;
+
+ case GIT_DELTA_ADDED:
+ case GIT_DELTA_MODIFIED:
+ if (data->include_changed)
+ add_path = delta->new_file.path;
+ break;
+
+ case GIT_DELTA_DELETED:
+ if (data->include_changed &&
+ !git_index_find(NULL, index, delta->old_file.path))
+ error = git_index_remove(index, delta->old_file.path, 0);
+ break;
+
+ default:
+ /* Unimplemented */
+ giterr_set(
+ GITERR_INVALID,
+ "Cannot update index. Unimplemented status (%d)",
+ delta->status);
+ return -1;
+ }
+
+ if (add_path != NULL)
+ error = git_index_add_bypath(index, add_path);
+ }
- return data->error;
+ return error;
}
static int build_untracked_tree(
@@ -221,15 +217,13 @@ static int build_untracked_tree(
uint32_t flags)
{
git_tree *i_tree = NULL;
- git_diff_list *diff = NULL;
+ git_diff *diff = NULL;
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- struct cb_data data = {0};
+ struct stash_update_rules data = {0};
int error;
git_index_clear(index);
- data.index = index;
-
if (flags & GIT_STASH_INCLUDE_UNTRACKED) {
opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED |
GIT_DIFF_RECURSE_UNTRACKED_DIRS;
@@ -248,18 +242,13 @@ static int build_untracked_tree(
&diff, git_index_owner(index), i_tree, &opts)) < 0)
goto cleanup;
- if ((error = git_diff_foreach(
- diff, update_index_cb, NULL, NULL, &data)) < 0)
- {
- if (error == GIT_EUSER)
- error = data.error;
+ if ((error = stash_update_index_from_diff(index, diff, &data)) < 0)
goto cleanup;
- }
error = build_tree_from_index(tree_out, index);
cleanup:
- git_diff_list_free(diff);
+ git_diff_free(diff);
git_tree_free(i_tree);
return error;
}
@@ -267,7 +256,7 @@ cleanup:
static int commit_untracked(
git_commit **u_commit,
git_index *index,
- git_signature *stasher,
+ const git_signature *stasher,
const char *message,
git_commit *i_commit,
uint32_t flags)
@@ -311,41 +300,29 @@ static int build_workdir_tree(
{
git_repository *repo = git_index_owner(index);
git_tree *b_tree = NULL;
- git_diff_list *diff = NULL, *diff2 = NULL;
+ git_diff *diff = NULL;
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- struct cb_data data = {0};
+ struct stash_update_rules data = {0};
int error;
- if ((error = git_commit_tree(&b_tree, b_commit)) < 0)
- goto cleanup;
+ opts.flags = GIT_DIFF_IGNORE_SUBMODULES;
- if ((error = git_diff_tree_to_index(&diff, repo, b_tree, NULL, &opts)) < 0)
- goto cleanup;
-
- if ((error = git_diff_index_to_workdir(&diff2, repo, NULL, &opts)) < 0)
+ if ((error = git_commit_tree(&b_tree, b_commit)) < 0)
goto cleanup;
- if ((error = git_diff_merge(diff, diff2)) < 0)
+ if ((error = git_diff_tree_to_workdir_with_index(
+ &diff, repo, b_tree, &opts)) < 0)
goto cleanup;
- data.index = index;
data.include_changed = true;
- if ((error = git_diff_foreach(
- diff, update_index_cb, NULL, NULL, &data)) < 0)
- {
- if (error == GIT_EUSER)
- error = data.error;
+ if ((error = stash_update_index_from_diff(index, diff, &data)) < 0)
goto cleanup;
- }
-
- if ((error = build_tree_from_index(tree_out, index)) < 0)
- goto cleanup;
+ error = build_tree_from_index(tree_out, index);
cleanup:
- git_diff_list_free(diff);
- git_diff_list_free(diff2);
+ git_diff_free(diff);
git_tree_free(b_tree);
return error;
@@ -354,7 +331,7 @@ cleanup:
static int commit_worktree(
git_oid *w_commit_oid,
git_index *index,
- git_signature *stasher,
+ const git_signature *stasher,
const char *message,
git_commit *i_commit,
git_commit *b_commit,
@@ -431,17 +408,19 @@ cleanup:
static int update_reflog(
git_oid *w_commit_oid,
git_repository *repo,
- git_signature *stasher,
+ const git_signature *stasher,
const char *message)
{
- git_reference *stash = NULL;
+ git_reference *stash;
git_reflog *reflog = NULL;
int error;
if ((error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, w_commit_oid, 1)) < 0)
goto cleanup;
- if ((error = git_reflog_read(&reflog, stash)) < 0)
+ git_reference_free(stash);
+
+ if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE) < 0))
goto cleanup;
if ((error = git_reflog_append(reflog, w_commit_oid, stasher, message)) < 0)
@@ -451,7 +430,6 @@ static int update_reflog(
goto cleanup;
cleanup:
- git_reference_free(stash);
git_reflog_free(reflog);
return error;
}
@@ -474,12 +452,14 @@ static int ensure_there_are_changes_to_stash(
git_status_options opts = GIT_STATUS_OPTIONS_INIT;
opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+ opts.flags = GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
+
if (include_untracked_files)
- opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
if (include_ignored_files)
- opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED;
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED;
error = git_status_foreach_ext(repo, &opts, is_dirty_cb, NULL);
@@ -510,7 +490,7 @@ static int reset_index_and_workdir(
int git_stash_save(
git_oid *out,
git_repository *repo,
- git_signature *stasher,
+ const git_signature *stasher,
const char *message,
uint32_t flags)
{
@@ -595,7 +575,7 @@ int git_stash_foreach(
if (error < 0)
goto cleanup;
- if ((error = git_reflog_read(&reflog, stash)) < 0)
+ if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE)) < 0)
goto cleanup;
max = git_reflog_entrycount(reflog);
@@ -629,7 +609,7 @@ int git_stash_drop(
if ((error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE)) < 0)
return error;
- if ((error = git_reflog_read(&reflog, stash)) < 0)
+ if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE)) < 0)
goto cleanup;
max = git_reflog_entrycount(reflog);
diff --git a/src/status.c b/src/status.c
index e520c1017..07fdcb5c3 100644
--- a/src/status.c
+++ b/src/status.c
@@ -52,7 +52,7 @@ static unsigned int index_delta2status(const git_diff_delta *head2idx)
}
static unsigned int workdir_delta2status(
- git_diff_list *diff, git_diff_delta *idx2wd)
+ git_diff *diff, git_diff_delta *idx2wd)
{
git_status_t st = GIT_STATUS_CURRENT;
@@ -225,24 +225,6 @@ static git_status_list *git_status_list_alloc(git_index *index)
return status;
}
-/*
-static int newfile_cmp(const void *a, const void *b)
-{
- const git_diff_delta *delta_a = a;
- const git_diff_delta *delta_b = b;
-
- return git__strcmp(delta_a->new_file.path, delta_b->new_file.path);
-}
-
-static int newfile_casecmp(const void *a, const void *b)
-{
- const git_diff_delta *delta_a = a;
- const git_diff_delta *delta_b = b;
-
- return git__strcasecmp(delta_a->new_file.path, delta_b->new_file.path);
-}
-*/
-
int git_status_list_new(
git_status_list **out,
git_repository *repo,
@@ -251,14 +233,14 @@ int git_status_list_new(
git_index *index = NULL;
git_status_list *status = NULL;
git_diff_options diffopt = GIT_DIFF_OPTIONS_INIT;
- git_diff_find_options findopts_i2w = GIT_DIFF_FIND_OPTIONS_INIT;
+ git_diff_find_options findopt = GIT_DIFF_FIND_OPTIONS_INIT;
git_tree *head = NULL;
git_status_show_t show =
opts ? opts->show : GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
int error = 0;
unsigned int flags = opts ? opts->flags : GIT_STATUS_OPT_DEFAULTS;
- assert(show <= GIT_STATUS_SHOW_INDEX_THEN_WORKDIR);
+ assert(show <= GIT_STATUS_SHOW_WORKDIR_ONLY);
*out = NULL;
@@ -269,12 +251,17 @@ int git_status_list_new(
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) &&
- error != GIT_ENOTFOUND && error != GIT_EORPHANEDHEAD) {
- git_index_free(index); /* release index */
- return error;
+ 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 */
+ if ((flags & GIT_STATUS_OPT_NO_REFRESH) == 0 &&
+ git_index_read(index, false) < 0)
+ giterr_clear();
+
status = git_status_list_alloc(index);
GITERR_CHECK_ALLOC(status);
@@ -284,6 +271,7 @@ int git_status_list_new(
}
diffopt.flags = GIT_DIFF_INCLUDE_TYPECHANGE;
+ findopt.flags = GIT_DIFF_FIND_FOR_UNTRACKED;
if ((flags & GIT_STATUS_OPT_INCLUDE_UNTRACKED) != 0)
diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNTRACKED;
@@ -300,37 +288,32 @@ int git_status_list_new(
if ((flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES) != 0)
diffopt.flags = diffopt.flags | GIT_DIFF_IGNORE_SUBMODULES;
- findopts_i2w.flags |= GIT_DIFF_FIND_FOR_UNTRACKED;
+ if ((flags & GIT_STATUS_OPT_RENAMES_FROM_REWRITES) != 0)
+ findopt.flags = findopt.flags |
+ GIT_DIFF_FIND_AND_BREAK_REWRITES |
+ GIT_DIFF_FIND_RENAMES_FROM_REWRITES |
+ GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY;
if (show != GIT_STATUS_SHOW_WORKDIR_ONLY) {
if ((error = git_diff_tree_to_index(
- &status->head2idx, repo, head, NULL, &diffopt)) < 0)
+ &status->head2idx, repo, head, index, &diffopt)) < 0)
goto done;
if ((flags & GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX) != 0 &&
- (error = git_diff_find_similar(status->head2idx, NULL)) < 0)
+ (error = git_diff_find_similar(status->head2idx, &findopt)) < 0)
goto done;
}
if (show != GIT_STATUS_SHOW_INDEX_ONLY) {
if ((error = git_diff_index_to_workdir(
- &status->idx2wd, repo, NULL, &diffopt)) < 0)
+ &status->idx2wd, repo, index, &diffopt)) < 0)
goto done;
if ((flags & GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR) != 0 &&
- (error = git_diff_find_similar(status->idx2wd, &findopts_i2w)) < 0)
+ (error = git_diff_find_similar(status->idx2wd, &findopt)) < 0)
goto done;
}
- if (show == GIT_STATUS_SHOW_INDEX_THEN_WORKDIR) {
- if ((error = git_diff__paired_foreach(
- status->head2idx, NULL, status_collect, status)) < 0)
- goto done;
-
- git_diff_list_free(status->head2idx);
- status->head2idx = NULL;
- }
-
if ((error = git_diff__paired_foreach(
status->head2idx, status->idx2wd, status_collect, status)) < 0)
goto done;
@@ -383,8 +366,8 @@ void git_status_list_free(git_status_list *status)
if (status == NULL)
return;
- git_diff_list_free(status->head2idx);
- git_diff_list_free(status->idx2wd);
+ git_diff_free(status->head2idx);
+ git_diff_free(status->idx2wd);
git_vector_foreach(&status->paired, i, status_entry)
git__free(status_entry);
diff --git a/src/status.h b/src/status.h
index b58e0ebd6..33008b89c 100644
--- a/src/status.h
+++ b/src/status.h
@@ -14,8 +14,8 @@
struct git_status_list {
git_status_options opts;
- git_diff_list *head2idx;
- git_diff_list *idx2wd;
+ git_diff *head2idx;
+ git_diff *idx2wd;
git_vector paired;
};
diff --git a/src/strmap.c b/src/strmap.c
new file mode 100644
index 000000000..b26a13d1f
--- /dev/null
+++ b/src/strmap.c
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "strmap.h"
+
+int git_strmap_next(
+ void **data,
+ git_strmap_iter* iter,
+ git_strmap *map)
+{
+ if (!map)
+ return GIT_ERROR;
+
+ while (*iter != git_strmap_end(map)) {
+ if (!(git_strmap_has_data(map, *iter))) {
+ ++(*iter);
+ continue;
+ }
+
+ *data = git_strmap_value_at(map, *iter);
+
+ ++(*iter);
+
+ return GIT_OK;
+ }
+
+ return GIT_ITEROVER;
+}
diff --git a/src/strmap.h b/src/strmap.h
index 44176a0fc..8276ab468 100644
--- a/src/strmap.h
+++ b/src/strmap.h
@@ -17,6 +17,7 @@
__KHASH_TYPE(str, const char *, void *);
typedef khash_t(str) git_strmap;
+typedef khiter_t git_strmap_iter;
#define GIT__USE_STRMAP \
__KHASH_IMPL(str, static kh_inline, const char *, void *, 1, kh_str_hash_func, kh_str_hash_equal)
@@ -31,7 +32,9 @@ typedef khash_t(str) git_strmap;
#define git_strmap_valid_index(h, idx) (idx != kh_end(h))
#define git_strmap_exists(h, k) (kh_get(str, h, k) != kh_end(h))
+#define git_strmap_has_data(h, idx) kh_exist(h, idx)
+#define git_strmap_key(h, idx) kh_key(h, idx)
#define git_strmap_value_at(h, idx) kh_val(h, idx)
#define git_strmap_set_value_at(h, idx, v) kh_val(h, idx) = v
#define git_strmap_delete_at(h, idx) kh_del(str, h, idx)
@@ -61,4 +64,12 @@ typedef khash_t(str) git_strmap;
#define git_strmap_foreach kh_foreach
#define git_strmap_foreach_value kh_foreach_value
+#define git_strmap_begin kh_begin
+#define git_strmap_end kh_end
+
+int git_strmap_next(
+ void **data,
+ git_strmap_iter* iter,
+ git_strmap *map);
+
#endif
diff --git a/src/submodule.c b/src/submodule.c
index 89eba2aa4..586494fed 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -9,9 +9,7 @@
#include "git2/config.h"
#include "git2/sys/config.h"
#include "git2/types.h"
-#include "git2/repository.h"
#include "git2/index.h"
-#include "git2/submodule.h"
#include "buffer.h"
#include "buf_text.h"
#include "vector.h"
@@ -32,6 +30,8 @@ static git_cvar_map _sm_update_map[] = {
{GIT_CVAR_STRING, "rebase", GIT_SUBMODULE_UPDATE_REBASE},
{GIT_CVAR_STRING, "merge", GIT_SUBMODULE_UPDATE_MERGE},
{GIT_CVAR_STRING, "none", GIT_SUBMODULE_UPDATE_NONE},
+ {GIT_CVAR_FALSE, NULL, GIT_SUBMODULE_UPDATE_NONE},
+ {GIT_CVAR_TRUE, NULL, GIT_SUBMODULE_UPDATE_CHECKOUT},
};
static git_cvar_map _sm_ignore_map[] = {
@@ -39,6 +39,8 @@ static git_cvar_map _sm_ignore_map[] = {
{GIT_CVAR_STRING, "untracked", GIT_SUBMODULE_IGNORE_UNTRACKED},
{GIT_CVAR_STRING, "dirty", GIT_SUBMODULE_IGNORE_DIRTY},
{GIT_CVAR_STRING, "all", GIT_SUBMODULE_IGNORE_ALL},
+ {GIT_CVAR_FALSE, NULL, GIT_SUBMODULE_IGNORE_NONE},
+ {GIT_CVAR_TRUE, NULL, GIT_SUBMODULE_IGNORE_ALL},
};
static kh_inline khint_t str_hash_no_trailing_slash(const char *s)
@@ -73,15 +75,11 @@ static int load_submodule_config(git_repository *repo);
static git_config_backend *open_gitmodules(git_repository *, bool, const git_oid *);
static int lookup_head_remote(git_buf *url, git_repository *repo);
static int submodule_get(git_submodule **, git_repository *, const char *, const char *);
-static void submodule_release(git_submodule *sm, int decr);
-static int submodule_load_from_index(git_repository *, const git_index_entry *);
-static int submodule_load_from_head(git_repository*, const char*, const git_oid*);
static int submodule_load_from_config(const git_config_entry *, void *);
static int submodule_load_from_wd_lite(git_submodule *, const char *, void *);
static int submodule_update_config(git_submodule *, const char *, const char *, bool, bool);
-static void submodule_mode_mismatch(git_repository *, const char *, unsigned int);
-static int submodule_index_status(unsigned int *status, git_submodule *sm);
-static int submodule_wd_status(unsigned int *status, git_submodule *sm);
+static void submodule_get_index_status(unsigned int *, git_submodule *);
+static void submodule_get_wd_status(unsigned int *, git_submodule *, git_repository *, git_submodule_ignore_t);
static int submodule_cmp(const void *a, const void *b)
{
@@ -163,7 +161,7 @@ int git_submodule_foreach(
* us from issuing a callback twice for a submodule where the name
* and path are not the same.
*/
- if (sm->refcount > 1) {
+ if (GIT_REFCOUNT_VAL(sm) > 1) {
if (git_vector_bsearch(NULL, &seen, sm) != GIT_ENOTFOUND)
continue;
if ((error = git_vector_insert(&seen, sm)) < 0)
@@ -195,9 +193,7 @@ void git_submodule_config_free(git_repository *repo)
if (smcfg == NULL)
return;
- git_strmap_foreach_value(smcfg, sm, {
- submodule_release(sm,1);
- });
+ git_strmap_foreach_value(smcfg, sm, { git_submodule_free(sm); });
git_strmap_free(smcfg);
}
@@ -338,7 +334,7 @@ int git_submodule_add_finalize(git_submodule *sm)
assert(sm);
- if ((error = git_repository_index__weakptr(&index, sm->owner)) < 0 ||
+ if ((error = git_repository_index__weakptr(&index, sm->repo)) < 0 ||
(error = git_index_add_bypath(index, GIT_MODULES_FILE)) < 0)
return error;
@@ -348,7 +344,7 @@ int git_submodule_add_finalize(git_submodule *sm)
int git_submodule_add_to_index(git_submodule *sm, int write_index)
{
int error;
- git_repository *repo, *sm_repo = NULL;
+ git_repository *sm_repo = NULL;
git_index *index;
git_buf path = GIT_BUF_INIT;
git_commit *head;
@@ -357,14 +353,12 @@ int git_submodule_add_to_index(git_submodule *sm, int write_index)
assert(sm);
- repo = sm->owner;
-
/* force reload of wd OID by git_submodule_open */
sm->flags = sm->flags & ~GIT_SUBMODULE_STATUS__WD_OID_VALID;
- if ((error = git_repository_index__weakptr(&index, repo)) < 0 ||
+ if ((error = git_repository_index__weakptr(&index, sm->repo)) < 0 ||
(error = git_buf_joinpath(
- &path, git_repository_workdir(repo), sm->path)) < 0 ||
+ &path, git_repository_workdir(sm->repo), sm->path)) < 0 ||
(error = git_submodule_open(&sm_repo, sm)) < 0)
goto cleanup;
@@ -378,7 +372,8 @@ int git_submodule_add_to_index(git_submodule *sm, int write_index)
memset(&entry, 0, sizeof(entry));
entry.path = sm->path;
- git_index_entry__init_from_stat(&entry, &st);
+ git_index_entry__init_from_stat(
+ &entry, &st, !(git_index_caps(index) & GIT_INDEXCAP_NO_FILEMODE));
/* calling git_submodule_open will have set sm->wd_oid if possible */
if ((sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) == 0) {
@@ -416,15 +411,34 @@ cleanup:
return error;
}
+const char *git_submodule_ignore_to_str(git_submodule_ignore_t ignore)
+{
+ int i;
+ for (i = 0; i < (int)ARRAY_SIZE(_sm_ignore_map); ++i)
+ if (_sm_ignore_map[i].map_value == ignore)
+ return _sm_ignore_map[i].str_match;
+ return NULL;
+}
+
+const char *git_submodule_update_to_str(git_submodule_update_t update)
+{
+ int i;
+ for (i = 0; i < (int)ARRAY_SIZE(_sm_update_map); ++i)
+ if (_sm_update_map[i].map_value == update)
+ return _sm_update_map[i].str_match;
+ return NULL;
+}
+
int git_submodule_save(git_submodule *submodule)
{
int error = 0;
git_config_backend *mods;
git_buf key = GIT_BUF_INIT;
+ const char *val;
assert(submodule);
- mods = open_gitmodules(submodule->owner, true, NULL);
+ mods = open_gitmodules(submodule->repo, true, NULL);
if (!mods) {
giterr_set(GITERR_SUBMODULE,
"Adding submodules to a bare repository is not supported (for now)");
@@ -445,22 +459,14 @@ int git_submodule_save(git_submodule *submodule)
goto cleanup;
if (!(error = submodule_config_key_trunc_puts(&key, "update")) &&
- submodule->update != GIT_SUBMODULE_UPDATE_DEFAULT)
- {
- const char *val = (submodule->update == GIT_SUBMODULE_UPDATE_CHECKOUT) ?
- NULL : _sm_update_map[submodule->update].str_match;
+ (val = git_submodule_update_to_str(submodule->update)) != NULL)
error = git_config_file_set_string(mods, key.ptr, val);
- }
if (error < 0)
goto cleanup;
if (!(error = submodule_config_key_trunc_puts(&key, "ignore")) &&
- submodule->ignore != GIT_SUBMODULE_IGNORE_DEFAULT)
- {
- const char *val = (submodule->ignore == GIT_SUBMODULE_IGNORE_NONE) ?
- NULL : _sm_ignore_map[submodule->ignore].str_match;
+ (val = git_submodule_ignore_to_str(submodule->ignore)) != NULL)
error = git_config_file_set_string(mods, key.ptr, val);
- }
if (error < 0)
goto cleanup;
@@ -487,7 +493,7 @@ cleanup:
git_repository *git_submodule_owner(git_submodule *submodule)
{
assert(submodule);
- return submodule->owner;
+ return submodule->repo;
}
const char *git_submodule_name(git_submodule *submodule)
@@ -544,11 +550,12 @@ const git_oid *git_submodule_wd_id(git_submodule *submodule)
{
assert(submodule);
+ /* load unless we think we have a valid oid */
if (!(submodule->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID)) {
git_repository *subrepo;
/* calling submodule open grabs the HEAD OID if possible */
- if (!git_submodule_open(&subrepo, submodule))
+ if (!git_submodule_open_bare(&subrepo, submodule))
git_repository_free(subrepo);
else
giterr_clear();
@@ -563,7 +570,8 @@ const git_oid *git_submodule_wd_id(git_submodule *submodule)
git_submodule_ignore_t git_submodule_ignore(git_submodule *submodule)
{
assert(submodule);
- return submodule->ignore;
+ return (submodule->ignore < GIT_SUBMODULE_IGNORE_NONE) ?
+ GIT_SUBMODULE_IGNORE_NONE : submodule->ignore;
}
git_submodule_ignore_t git_submodule_set_ignore(
@@ -573,7 +581,7 @@ git_submodule_ignore_t git_submodule_set_ignore(
assert(submodule);
- if (ignore == GIT_SUBMODULE_IGNORE_DEFAULT)
+ if (ignore == GIT_SUBMODULE_IGNORE_RESET)
ignore = submodule->ignore_default;
old = submodule->ignore;
@@ -584,7 +592,8 @@ git_submodule_ignore_t git_submodule_set_ignore(
git_submodule_update_t git_submodule_update(git_submodule *submodule)
{
assert(submodule);
- return submodule->update;
+ return (submodule->update < GIT_SUBMODULE_UPDATE_CHECKOUT) ?
+ GIT_SUBMODULE_UPDATE_CHECKOUT : submodule->update;
}
git_submodule_update_t git_submodule_set_update(
@@ -594,7 +603,7 @@ git_submodule_update_t git_submodule_set_update(
assert(submodule);
- if (update == GIT_SUBMODULE_UPDATE_DEFAULT)
+ if (update == GIT_SUBMODULE_UPDATE_RESET)
update = submodule->update_default;
old = submodule->update;
@@ -625,6 +634,7 @@ int git_submodule_set_fetch_recurse_submodules(
int git_submodule_init(git_submodule *submodule, int overwrite)
{
int error;
+ const char *val;
/* write "submodule.NAME.url" */
@@ -641,14 +651,10 @@ int git_submodule_init(git_submodule *submodule, int overwrite)
/* write "submodule.NAME.update" if not default */
- if (submodule->update == GIT_SUBMODULE_UPDATE_CHECKOUT)
- error = submodule_update_config(
- submodule, "update", NULL, (overwrite != 0), false);
- else if (submodule->update != GIT_SUBMODULE_UPDATE_DEFAULT)
- error = submodule_update_config(
- submodule, "update",
- _sm_update_map[submodule->update].str_match,
- (overwrite != 0), false);
+ val = (submodule->update == GIT_SUBMODULE_UPDATE_CHECKOUT) ?
+ NULL : git_submodule_update_to_str(submodule->update);
+ error = submodule_update_config(
+ submodule, "update", val, (overwrite != 0), false);
return error;
}
@@ -667,51 +673,70 @@ int git_submodule_sync(git_submodule *submodule)
submodule, "url", submodule->url, true, true);
}
-int git_submodule_open(
- git_repository **subrepo,
- git_submodule *submodule)
+static int git_submodule__open(
+ git_repository **subrepo, git_submodule *sm, bool bare)
{
int error;
git_buf path = GIT_BUF_INIT;
- git_repository *repo;
- const char *workdir;
+ unsigned int flags = GIT_REPOSITORY_OPEN_NO_SEARCH;
+ const char *wd;
- assert(submodule && subrepo);
+ assert(sm && subrepo);
- repo = submodule->owner;
- workdir = git_repository_workdir(repo);
+ if (git_repository__ensure_not_bare(
+ sm->repo, "open submodule repository") < 0)
+ return GIT_EBAREREPO;
- if (!workdir) {
- giterr_set(GITERR_REPOSITORY,
- "Cannot open submodule repository in a bare repo");
- return GIT_ENOTFOUND;
- }
-
- if ((submodule->flags & GIT_SUBMODULE_STATUS_IN_WD) == 0) {
- giterr_set(GITERR_REPOSITORY,
- "Cannot open submodule repository that is not checked out");
- return GIT_ENOTFOUND;
- }
+ wd = git_repository_workdir(sm->repo);
- if (git_buf_joinpath(&path, workdir, submodule->path) < 0)
+ if (git_buf_joinpath(&path, wd, sm->path) < 0 ||
+ git_buf_joinpath(&path, path.ptr, DOT_GIT) < 0)
return -1;
- error = git_repository_open(subrepo, path.ptr);
+ sm->flags = sm->flags &
+ ~(GIT_SUBMODULE_STATUS_IN_WD |
+ GIT_SUBMODULE_STATUS__WD_OID_VALID |
+ GIT_SUBMODULE_STATUS__WD_SCANNED);
- git_buf_free(&path);
+ if (bare)
+ flags |= GIT_REPOSITORY_OPEN_BARE;
+
+ error = git_repository_open_ext(subrepo, path.ptr, flags, wd);
- /* if we have opened the submodule successfully, let's grab the HEAD OID */
+ /* if we opened the submodule successfully, grab HEAD OID, etc. */
if (!error) {
- if (!git_reference_name_to_id(
- &submodule->wd_oid, *subrepo, GIT_HEAD_FILE))
- submodule->flags |= GIT_SUBMODULE_STATUS__WD_OID_VALID;
+ sm->flags |= GIT_SUBMODULE_STATUS_IN_WD |
+ GIT_SUBMODULE_STATUS__WD_SCANNED;
+
+ if (!git_reference_name_to_id(&sm->wd_oid, *subrepo, GIT_HEAD_FILE))
+ sm->flags |= GIT_SUBMODULE_STATUS__WD_OID_VALID;
else
giterr_clear();
+ } else if (git_path_exists(path.ptr)) {
+ sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED |
+ GIT_SUBMODULE_STATUS_IN_WD;
+ } else {
+ git_buf_rtruncate_at_char(&path, '/'); /* remove "/.git" */
+
+ if (git_path_isdir(path.ptr))
+ sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED;
}
+ git_buf_free(&path);
+
return error;
}
+int git_submodule_open_bare(git_repository **subrepo, git_submodule *sm)
+{
+ return git_submodule__open(subrepo, sm, true);
+}
+
+int git_submodule_open(git_repository **subrepo, git_submodule *sm)
+{
+ return git_submodule__open(subrepo, sm, false);
+}
+
int git_submodule_reload_all(git_repository *repo)
{
assert(repo);
@@ -719,74 +744,100 @@ int git_submodule_reload_all(git_repository *repo)
return load_submodule_config(repo);
}
-int git_submodule_reload(git_submodule *submodule)
+static void submodule_update_from_index_entry(
+ git_submodule *sm, const git_index_entry *ie)
{
- git_repository *repo;
- git_index *index;
- int error;
- size_t pos;
- git_tree *head;
- git_config_backend *mods;
+ bool already_found = (sm->flags & GIT_SUBMODULE_STATUS_IN_INDEX) != 0;
- assert(submodule);
+ if (!S_ISGITLINK(ie->mode)) {
+ if (!already_found)
+ sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE;
+ } else {
+ if (already_found)
+ sm->flags |= GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES;
+ else
+ git_oid_cpy(&sm->index_oid, &ie->oid);
- /* refresh index data */
+ sm->flags |= GIT_SUBMODULE_STATUS_IN_INDEX |
+ GIT_SUBMODULE_STATUS__INDEX_OID_VALID;
+ }
+}
+
+static int submodule_update_index(git_submodule *sm)
+{
+ git_index *index;
+ const git_index_entry *ie;
- repo = submodule->owner;
- if (git_repository_index__weakptr(&index, repo) < 0)
+ if (git_repository_index__weakptr(&index, sm->repo) < 0)
return -1;
- submodule->flags = submodule->flags &
+ sm->flags = sm->flags &
~(GIT_SUBMODULE_STATUS_IN_INDEX |
GIT_SUBMODULE_STATUS__INDEX_OID_VALID);
- if (!git_index_find(&pos, index, submodule->path)) {
- const git_index_entry *entry = git_index_get_byindex(index, pos);
+ if (!(ie = git_index_get_bypath(index, sm->path, 0)))
+ return 0;
- if (S_ISGITLINK(entry->mode)) {
- if ((error = submodule_load_from_index(repo, entry)) < 0)
- return error;
- } else {
- submodule_mode_mismatch(
- repo, entry->path, GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE);
- }
+ submodule_update_from_index_entry(sm, ie);
+
+ return 0;
+}
+
+static void submodule_update_from_head_data(
+ git_submodule *sm, mode_t mode, const git_oid *id)
+{
+ if (!S_ISGITLINK(mode))
+ sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE;
+ else {
+ git_oid_cpy(&sm->head_oid, id);
+
+ sm->flags |= GIT_SUBMODULE_STATUS_IN_HEAD |
+ GIT_SUBMODULE_STATUS__HEAD_OID_VALID;
}
+}
- /* refresh HEAD tree data */
+static int submodule_update_head(git_submodule *submodule)
+{
+ git_tree *head = NULL;
+ git_tree_entry *te = NULL;
- if (!(error = git_repository_head_tree(&head, repo))) {
- git_tree_entry *te;
+ submodule->flags = submodule->flags &
+ ~(GIT_SUBMODULE_STATUS_IN_HEAD |
+ GIT_SUBMODULE_STATUS__HEAD_OID_VALID);
- submodule->flags = submodule->flags &
- ~(GIT_SUBMODULE_STATUS_IN_HEAD |
- GIT_SUBMODULE_STATUS__HEAD_OID_VALID);
+ /* if we can't look up file in current head, then done */
+ if (git_repository_head_tree(&head, submodule->repo) < 0 ||
+ git_tree_entry_bypath(&te, head, submodule->path) < 0)
+ giterr_clear();
+ else
+ submodule_update_from_head_data(submodule, te->attr, &te->oid);
- if (!(error = git_tree_entry_bypath(&te, head, submodule->path))) {
+ git_tree_entry_free(te);
+ git_tree_free(head);
+ return 0;
+}
- if (S_ISGITLINK(te->attr)) {
- error = submodule_load_from_head(repo, submodule->path, &te->oid);
- } else {
- submodule_mode_mismatch(
- repo, submodule->path,
- GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE);
- }
+int git_submodule_reload(git_submodule *submodule)
+{
+ int error = 0;
+ git_config_backend *mods;
- git_tree_entry_free(te);
- }
- else if (error == GIT_ENOTFOUND) {
- giterr_clear();
- error = 0;
- }
+ assert(submodule);
- git_tree_free(head);
- }
+ /* refresh index data */
- if (error < 0)
- return error;
+ if (submodule_update_index(submodule) < 0)
+ return -1;
+
+ /* refresh HEAD tree data */
+
+ if (submodule_update_head(submodule) < 0)
+ return -1;
/* refresh config data */
- if ((mods = open_gitmodules(repo, false, NULL)) != NULL) {
+ mods = open_gitmodules(submodule->repo, false, NULL);
+ if (mods != NULL) {
git_buf path = GIT_BUF_INIT;
git_buf_sets(&path, "submodule\\.");
@@ -797,7 +848,7 @@ int git_submodule_reload(git_submodule *submodule)
error = -1;
else
error = git_config_file_foreach_match(
- mods, path.ptr, submodule_load_from_config, repo);
+ mods, path.ptr, submodule_load_from_config, submodule->repo);
git_buf_free(&path);
git_config_file_free(mods);
@@ -816,38 +867,90 @@ int git_submodule_reload(git_submodule *submodule)
return error;
}
-int git_submodule_status(
- unsigned int *status,
- git_submodule *submodule)
+static void submodule_copy_oid_maybe(
+ git_oid *tgt, const git_oid *src, bool valid)
{
- int error = 0;
- unsigned int status_val;
+ if (tgt) {
+ if (valid)
+ memcpy(tgt, src, sizeof(*tgt));
+ else
+ memset(tgt, 0, sizeof(*tgt));
+ }
+}
+
+int git_submodule__status(
+ unsigned int *out_status,
+ git_oid *out_head_id,
+ git_oid *out_index_id,
+ git_oid *out_wd_id,
+ git_submodule *sm,
+ git_submodule_ignore_t ign)
+{
+ unsigned int status;
+ git_repository *smrepo = NULL;
+
+ if (ign < GIT_SUBMODULE_IGNORE_NONE)
+ ign = sm->ignore;
- assert(status && submodule);
+ /* only return location info if ignore == all */
+ if (ign == GIT_SUBMODULE_IGNORE_ALL) {
+ *out_status = (sm->flags & GIT_SUBMODULE_STATUS__IN_FLAGS);
+ return 0;
+ }
- status_val = GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(submodule->flags);
+ /* refresh the index OID */
+ if (submodule_update_index(sm) < 0)
+ return -1;
+
+ /* refresh the HEAD OID */
+ if (submodule_update_head(sm) < 0)
+ return -1;
- if (submodule->ignore != GIT_SUBMODULE_IGNORE_ALL) {
- if (!(error = submodule_index_status(&status_val, submodule)))
- error = submodule_wd_status(&status_val, submodule);
+ /* for ignore == dirty, don't scan the working directory */
+ if (ign == GIT_SUBMODULE_IGNORE_DIRTY) {
+ /* git_submodule_open_bare will load WD OID data */
+ if (git_submodule_open_bare(&smrepo, sm) < 0)
+ giterr_clear();
+ else
+ git_repository_free(smrepo);
+ smrepo = NULL;
+ } else if (git_submodule_open(&smrepo, sm) < 0) {
+ giterr_clear();
+ smrepo = NULL;
}
- *status = status_val;
+ status = GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(sm->flags);
- return error;
+ submodule_get_index_status(&status, sm);
+ submodule_get_wd_status(&status, sm, smrepo, ign);
+
+ git_repository_free(smrepo);
+
+ *out_status = status;
+
+ submodule_copy_oid_maybe(out_head_id, &sm->head_oid,
+ (sm->flags & GIT_SUBMODULE_STATUS__HEAD_OID_VALID) != 0);
+ submodule_copy_oid_maybe(out_index_id, &sm->index_oid,
+ (sm->flags & GIT_SUBMODULE_STATUS__INDEX_OID_VALID) != 0);
+ submodule_copy_oid_maybe(out_wd_id, &sm->wd_oid,
+ (sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) != 0);
+
+ return 0;
}
-int git_submodule_location(
- unsigned int *location_status,
- git_submodule *submodule)
+int git_submodule_status(unsigned int *status, git_submodule *sm)
{
- assert(location_status && submodule);
+ assert(status && sm);
- *location_status = submodule->flags &
- (GIT_SUBMODULE_STATUS_IN_HEAD | GIT_SUBMODULE_STATUS_IN_INDEX |
- GIT_SUBMODULE_STATUS_IN_CONFIG | GIT_SUBMODULE_STATUS_IN_WD);
+ return git_submodule__status(status, NULL, NULL, NULL, sm, 0);
+}
- return 0;
+int git_submodule_location(unsigned int *location, git_submodule *sm)
+{
+ assert(location && sm);
+
+ return git_submodule__status(
+ location, NULL, NULL, NULL, sm, GIT_SUBMODULE_IGNORE_ALL);
}
@@ -857,54 +960,50 @@ int git_submodule_location(
static git_submodule *submodule_alloc(git_repository *repo, const char *name)
{
+ size_t namelen;
git_submodule *sm;
- if (!name || !strlen(name)) {
+ if (!name || !(namelen = strlen(name))) {
giterr_set(GITERR_SUBMODULE, "Invalid submodule name");
return NULL;
}
sm = git__calloc(1, sizeof(git_submodule));
if (sm == NULL)
- goto fail;
+ return NULL;
- sm->path = sm->name = git__strdup(name);
- if (!sm->name)
- goto fail;
+ sm->name = sm->path = git__strdup(name);
+ if (!sm->name) {
+ git__free(sm);
+ return NULL;
+ }
- sm->owner = repo;
- sm->refcount = 1;
+ GIT_REFCOUNT_INC(sm);
+ sm->ignore = sm->ignore_default = GIT_SUBMODULE_IGNORE_NONE;
+ sm->update = sm->update_default = GIT_SUBMODULE_UPDATE_CHECKOUT;
+ sm->repo = repo;
return sm;
-
-fail:
- submodule_release(sm, 0);
- return NULL;
}
-static void submodule_release(git_submodule *sm, int decr)
+static void submodule_release(git_submodule *sm)
{
if (!sm)
return;
- sm->refcount -= decr;
-
- if (sm->refcount == 0) {
- if (sm->name != sm->path) {
- git__free(sm->path);
- sm->path = NULL;
- }
-
- git__free(sm->name);
- sm->name = NULL;
-
- git__free(sm->url);
- sm->url = NULL;
-
- sm->owner = NULL;
+ if (sm->path != sm->name)
+ git__free(sm->path);
+ git__free(sm->name);
+ git__free(sm->url);
+ git__memzero(sm, sizeof(*sm));
+ git__free(sm);
+}
- git__free(sm);
- }
+void git_submodule_free(git_submodule *sm)
+{
+ if (!sm)
+ return;
+ GIT_REFCOUNT_DEC(sm, submodule_release);
}
static int submodule_get(
@@ -927,6 +1026,7 @@ static int submodule_get(
if (!git_strmap_valid_index(smcfg, pos)) {
sm = submodule_alloc(repo, name);
+ GITERR_CHECK_ALLOC(sm);
/* insert value at name - if another thread beats us to it, then use
* their record and release our own.
@@ -934,10 +1034,10 @@ static int submodule_get(
pos = kh_put(str, smcfg, sm->name, &error);
if (error < 0) {
- submodule_release(sm, 1);
+ git_submodule_free(sm);
sm = NULL;
} else if (error == 0) {
- submodule_release(sm, 1);
+ git_submodule_free(sm);
sm = git_strmap_value_at(smcfg, pos);
} else {
git_strmap_set_value_at(smcfg, pos, sm);
@@ -951,50 +1051,41 @@ static int submodule_get(
return (sm != NULL) ? 0 : -1;
}
-static int submodule_load_from_index(
- git_repository *repo, const git_index_entry *entry)
+static int submodule_config_error(const char *property, const char *value)
{
- git_submodule *sm;
+ giterr_set(GITERR_INVALID,
+ "Invalid value for submodule '%s' property: '%s'", property, value);
+ return -1;
+}
- if (submodule_get(&sm, repo, entry->path, NULL) < 0)
- return -1;
+int git_submodule_parse_ignore(git_submodule_ignore_t *out, const char *value)
+{
+ int val;
- if (sm->flags & GIT_SUBMODULE_STATUS_IN_INDEX) {
- sm->flags |= GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES;
- return 0;
+ if (git_config_lookup_map_value(
+ &val, _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), value) < 0) {
+ *out = GIT_SUBMODULE_IGNORE_NONE;
+ return submodule_config_error("ignore", value);
}
- sm->flags |= GIT_SUBMODULE_STATUS_IN_INDEX;
-
- git_oid_cpy(&sm->index_oid, &entry->oid);
- sm->flags |= GIT_SUBMODULE_STATUS__INDEX_OID_VALID;
-
+ *out = (git_submodule_ignore_t)val;
return 0;
}
-static int submodule_load_from_head(
- git_repository *repo, const char *path, const git_oid *oid)
+int git_submodule_parse_update(git_submodule_update_t *out, const char *value)
{
- git_submodule *sm;
-
- if (submodule_get(&sm, repo, path, NULL) < 0)
- return -1;
-
- sm->flags |= GIT_SUBMODULE_STATUS_IN_HEAD;
+ int val;
- git_oid_cpy(&sm->head_oid, oid);
- sm->flags |= GIT_SUBMODULE_STATUS__HEAD_OID_VALID;
+ if (git_config_lookup_map_value(
+ &val, _sm_update_map, ARRAY_SIZE(_sm_update_map), value) < 0) {
+ *out = GIT_SUBMODULE_UPDATE_CHECKOUT;
+ return submodule_config_error("update", value);
+ }
+ *out = (git_submodule_update_t)val;
return 0;
}
-static int submodule_config_error(const char *property, const char *value)
-{
- giterr_set(GITERR_INVALID,
- "Invalid value for submodule '%s' property: '%s'", property, value);
- return -1;
-}
-
static int submodule_load_from_config(
const git_config_entry *entry, void *data)
{
@@ -1012,8 +1103,10 @@ static int submodule_load_from_config(
namestart = key + strlen("submodule.");
property = strrchr(namestart, '.');
- if (property == NULL)
+
+ if (!property || (property == namestart))
return 0;
+
property++;
is_path = (strcasecmp(property, "path") == 0);
@@ -1047,11 +1140,11 @@ static int submodule_load_from_config(
git_strmap_insert2(smcfg, alternate, sm, old_sm, error);
if (error >= 0)
- sm->refcount++; /* inserted under a new key */
+ GIT_REFCOUNT_INC(sm); /* inserted under a new key */
/* if we replaced an old module under this key, release the old one */
if (old_sm && ((git_submodule *)old_sm) != sm) {
- submodule_release(old_sm, 1);
+ git_submodule_free(old_sm);
/* TODO: log warning about multiple submodules with same path */
}
}
@@ -1076,22 +1169,18 @@ static int submodule_load_from_config(
return -1;
}
else if (strcasecmp(property, "update") == 0) {
- int val;
- if (git_config_lookup_map_value(
- &val, _sm_update_map, ARRAY_SIZE(_sm_update_map), value) < 0)
- return submodule_config_error("update", value);
- sm->update_default = sm->update = (git_submodule_update_t)val;
+ if (git_submodule_parse_update(&sm->update, value) < 0)
+ return -1;
+ sm->update_default = sm->update;
}
else if (strcasecmp(property, "fetchRecurseSubmodules") == 0) {
if (git__parse_bool(&sm->fetch_recurse, value) < 0)
return submodule_config_error("fetchRecurseSubmodules", value);
}
else if (strcasecmp(property, "ignore") == 0) {
- int val;
- if (git_config_lookup_map_value(
- &val, _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), value) < 0)
- return submodule_config_error("ignore", value);
- sm->ignore_default = sm->ignore = (git_submodule_ignore_t)val;
+ if (git_submodule_parse_ignore(&sm->ignore, value) < 0)
+ return -1;
+ sm->ignore_default = sm->ignore;
}
/* ignore other unknown submodule properties */
@@ -1101,13 +1190,15 @@ static int submodule_load_from_config(
static int submodule_load_from_wd_lite(
git_submodule *sm, const char *name, void *payload)
{
- git_repository *repo = git_submodule_owner(sm);
git_buf path = GIT_BUF_INIT;
GIT_UNUSED(name);
GIT_UNUSED(payload);
- if (git_buf_joinpath(&path, git_repository_workdir(repo), sm->path) < 0)
+ if (git_repository_is_bare(sm->repo))
+ return 0;
+
+ if (git_buf_joinpath(&path, git_repository_workdir(sm->repo), sm->path) < 0)
return -1;
if (git_path_isdir(path.ptr))
@@ -1121,18 +1212,6 @@ static int submodule_load_from_wd_lite(
return 0;
}
-static void submodule_mode_mismatch(
- git_repository *repo, const char *path, unsigned int flag)
-{
- khiter_t pos = git_strmap_lookup_index(repo->submodules, path);
-
- if (git_strmap_valid_index(repo->submodules, pos)) {
- git_submodule *sm = git_strmap_value_at(repo->submodules, pos);
-
- sm->flags |= flag;
- }
-}
-
static int load_submodule_config_from_index(
git_repository *repo, git_oid *gitmodules_oid)
{
@@ -1146,18 +1225,21 @@ static int load_submodule_config_from_index(
return error;
while (!(error = git_iterator_advance(&entry, i))) {
-
- if (S_ISGITLINK(entry->mode)) {
- error = submodule_load_from_index(repo, entry);
- if (error < 0)
- break;
- } else {
- submodule_mode_mismatch(
- repo, entry->path, GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE);
-
- if (strcmp(entry->path, GIT_MODULES_FILE) == 0)
- git_oid_cpy(gitmodules_oid, &entry->oid);
- }
+ khiter_t pos = git_strmap_lookup_index(repo->submodules, entry->path);
+ git_submodule *sm;
+
+ if (git_strmap_valid_index(repo->submodules, pos)) {
+ sm = git_strmap_value_at(repo->submodules, pos);
+
+ if (S_ISGITLINK(entry->mode))
+ submodule_update_from_index_entry(sm, entry);
+ else
+ sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE;
+ } else if (S_ISGITLINK(entry->mode)) {
+ if (!submodule_get(&sm, repo, entry->path, NULL))
+ submodule_update_from_index_entry(sm, entry);
+ } else if (strcmp(entry->path, GIT_MODULES_FILE) == 0)
+ git_oid_cpy(gitmodules_oid, &entry->oid);
}
if (error == GIT_ITEROVER)
@@ -1176,8 +1258,11 @@ static int load_submodule_config_from_head(
git_iterator *i;
const git_index_entry *entry;
- if ((error = git_repository_head_tree(&head, repo)) < 0)
- return error;
+ /* if we can't look up current head, then there's no submodule in it */
+ if (git_repository_head_tree(&head, repo) < 0) {
+ giterr_clear();
+ return 0;
+ }
if ((error = git_iterator_for_tree(&i, head, 0, NULL, NULL)) < 0) {
git_tree_free(head);
@@ -1185,18 +1270,24 @@ static int load_submodule_config_from_head(
}
while (!(error = git_iterator_advance(&entry, i))) {
-
- if (S_ISGITLINK(entry->mode)) {
- error = submodule_load_from_head(repo, entry->path, &entry->oid);
- if (error < 0)
- break;
- } else {
- submodule_mode_mismatch(
- repo, entry->path, GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE);
-
- if (strcmp(entry->path, GIT_MODULES_FILE) == 0 &&
- git_oid_iszero(gitmodules_oid))
- git_oid_cpy(gitmodules_oid, &entry->oid);
+ khiter_t pos = git_strmap_lookup_index(repo->submodules, entry->path);
+ git_submodule *sm;
+
+ if (git_strmap_valid_index(repo->submodules, pos)) {
+ sm = git_strmap_value_at(repo->submodules, pos);
+
+ if (S_ISGITLINK(entry->mode))
+ submodule_update_from_head_data(
+ sm, entry->mode, &entry->oid);
+ else
+ sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE;
+ } else if (S_ISGITLINK(entry->mode)) {
+ if (!submodule_get(&sm, repo, entry->path, NULL))
+ submodule_update_from_head_data(
+ sm, entry->mode, &entry->oid);
+ } else if (strcmp(entry->path, GIT_MODULES_FILE) == 0 &&
+ git_oid_iszero(gitmodules_oid)) {
+ git_oid_cpy(gitmodules_oid, &entry->oid);
}
}
@@ -1381,7 +1472,7 @@ static int submodule_update_config(
assert(submodule);
- error = git_repository_config__weakptr(&config, submodule->owner);
+ error = git_repository_config__weakptr(&config, submodule->repo);
if (error < 0)
return error;
@@ -1409,11 +1500,13 @@ cleanup:
return error;
}
-static int submodule_index_status(unsigned int *status, git_submodule *sm)
+static void submodule_get_index_status(unsigned int *status, git_submodule *sm)
{
const git_oid *head_oid = git_submodule_head_id(sm);
const git_oid *index_oid = git_submodule_index_id(sm);
+ *status = *status & ~GIT_SUBMODULE_STATUS__INDEX_FLAGS;
+
if (!head_oid) {
if (index_oid)
*status |= GIT_SUBMODULE_STATUS_INDEX_ADDED;
@@ -1422,27 +1515,23 @@ static int submodule_index_status(unsigned int *status, git_submodule *sm)
*status |= GIT_SUBMODULE_STATUS_INDEX_DELETED;
else if (!git_oid_equal(head_oid, index_oid))
*status |= GIT_SUBMODULE_STATUS_INDEX_MODIFIED;
-
- return 0;
}
-static int submodule_wd_status(unsigned int *status, git_submodule *sm)
+static void submodule_get_wd_status(
+ unsigned int *status,
+ git_submodule *sm,
+ git_repository *sm_repo,
+ git_submodule_ignore_t ign)
{
- int error = 0;
- const git_oid *wd_oid, *index_oid;
- git_repository *sm_repo = NULL;
-
- /* open repo now if we need it (so wd_id() call won't reopen) */
- if ((sm->ignore == GIT_SUBMODULE_IGNORE_NONE ||
- sm->ignore == GIT_SUBMODULE_IGNORE_UNTRACKED) &&
- (sm->flags & GIT_SUBMODULE_STATUS_IN_WD) != 0)
- {
- if ((error = git_submodule_open(&sm_repo, sm)) < 0)
- return error;
- }
+ const git_oid *index_oid = git_submodule_index_id(sm);
+ const git_oid *wd_oid =
+ (sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) ? &sm->wd_oid : NULL;
+ git_tree *sm_head = NULL;
+ git_index *index = NULL;
+ git_diff_options opt = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff;
- index_oid = git_submodule_index_id(sm);
- wd_oid = git_submodule_wd_id(sm);
+ *status = *status & ~GIT_SUBMODULE_STATUS__WD_FLAGS;
if (!index_oid) {
if (wd_oid)
@@ -1458,59 +1547,51 @@ static int submodule_wd_status(unsigned int *status, git_submodule *sm)
else if (!git_oid_equal(index_oid, wd_oid))
*status |= GIT_SUBMODULE_STATUS_WD_MODIFIED;
- if (sm_repo != NULL) {
- git_tree *sm_head;
- git_diff_options opt = GIT_DIFF_OPTIONS_INIT;
- git_diff_list *diff;
-
- /* the diffs below could be optimized with an early termination
- * option to the git_diff functions, but for now this is sufficient
- * (and certainly no worse that what core git does).
- */
-
- /* perform head-to-index diff on submodule */
+ /* if we have no repo, then we're done */
+ if (!sm_repo)
+ return;
- if ((error = git_repository_head_tree(&sm_head, sm_repo)) < 0)
- return error;
+ /* the diffs below could be optimized with an early termination
+ * option to the git_diff functions, but for now this is sufficient
+ * (and certainly no worse that what core git does).
+ */
- if (sm->ignore == GIT_SUBMODULE_IGNORE_NONE)
- opt.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
+ if (ign == GIT_SUBMODULE_IGNORE_NONE)
+ opt.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
- error = git_diff_tree_to_index(&diff, sm_repo, sm_head, NULL, &opt);
+ (void)git_repository_index__weakptr(&index, sm_repo);
- if (!error) {
+ /* if we don't have an unborn head, check diff with index */
+ if (git_repository_head_tree(&sm_head, sm_repo) < 0)
+ giterr_clear();
+ else {
+ /* perform head to index diff on submodule */
+ if (git_diff_tree_to_index(&diff, sm_repo, sm_head, index, &opt) < 0)
+ giterr_clear();
+ else {
if (git_diff_num_deltas(diff) > 0)
*status |= GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED;
-
- git_diff_list_free(diff);
+ git_diff_free(diff);
diff = NULL;
}
git_tree_free(sm_head);
+ }
- if (error < 0)
- return error;
-
- /* perform index-to-workdir diff on submodule */
-
- error = git_diff_index_to_workdir(&diff, sm_repo, NULL, &opt);
-
- if (!error) {
- size_t untracked =
- git_diff_num_deltas_of_type(diff, GIT_DELTA_UNTRACKED);
-
- if (untracked > 0)
- *status |= GIT_SUBMODULE_STATUS_WD_UNTRACKED;
+ /* perform index-to-workdir diff on submodule */
+ if (git_diff_index_to_workdir(&diff, sm_repo, index, &opt) < 0)
+ giterr_clear();
+ else {
+ size_t untracked =
+ git_diff_num_deltas_of_type(diff, GIT_DELTA_UNTRACKED);
- if (git_diff_num_deltas(diff) != untracked)
- *status |= GIT_SUBMODULE_STATUS_WD_WD_MODIFIED;
+ if (untracked > 0)
+ *status |= GIT_SUBMODULE_STATUS_WD_UNTRACKED;
- git_diff_list_free(diff);
- diff = NULL;
- }
+ if (git_diff_num_deltas(diff) != untracked)
+ *status |= GIT_SUBMODULE_STATUS_WD_WD_MODIFIED;
- git_repository_free(sm_repo);
+ git_diff_free(diff);
+ diff = NULL;
}
-
- return error;
}
diff --git a/src/submodule.h b/src/submodule.h
index ba8e2518e..b05937503 100644
--- a/src/submodule.h
+++ b/src/submodule.h
@@ -7,6 +7,10 @@
#ifndef INCLUDE_submodule_h__
#define INCLUDE_submodule_h__
+#include "git2/submodule.h"
+#include "git2/repository.h"
+#include "fileops.h"
+
/* Notes:
*
* Submodule information can be in four places: the index, the config files
@@ -44,44 +48,51 @@
* an entry for every submodule found in the HEAD and index, and for every
* submodule described in .gitmodules. The fields are as follows:
*
- * - `owner` is the git_repository containing this submodule
+ * - `rc` tracks the refcount of how many hash table entries in the
+ * git_submodule_cache there are for this submodule. It only comes into
+ * play if the name and path of the submodule differ.
+ *
* - `name` is the name of the submodule from .gitmodules.
* - `path` is the path to the submodule from the repo root. It is almost
* always the same as `name`.
* - `url` is the url for the submodule.
- * - `tree_oid` is the SHA1 for the submodule path in the repo HEAD.
- * - `index_oid` is the SHA1 for the submodule recorded in the index.
- * - `workdir_oid` is the SHA1 for the HEAD of the checked out submodule.
* - `update` is a git_submodule_update_t value - see gitmodules(5) update.
+ * - `update_default` is the update value from the config
* - `ignore` is a git_submodule_ignore_t value - see gitmodules(5) ignore.
+ * - `ignore_default` is the ignore value from the config
* - `fetch_recurse` is 0 or 1 - see gitmodules(5) fetchRecurseSubmodules.
- * - `refcount` tracks how many hashmap entries there are for this submodule.
- * It only comes into play if the name and path of the submodule differ.
- * - `flags` is for internal use, tracking where this submodule has been
- * found (head, index, config, workdir) and other misc info about it.
+ *
+ * - `repo` is the parent repository that contains this submodule.
+ * - `flags` after for internal use, tracking where this submodule has been
+ * found (head, index, config, workdir) and known status info, etc.
+ * - `head_oid` is the SHA1 for the submodule path in the repo HEAD.
+ * - `index_oid` is the SHA1 for the submodule recorded in the index.
+ * - `wd_oid` is the SHA1 for the HEAD of the checked out submodule.
*
* If the submodule has been added to .gitmodules but not yet git added,
- * then the `index_oid` will be valid and zero. If the submodule has been
- * deleted, but the delete has not been committed yet, then the `index_oid`
- * will be set, but the `url` will be NULL.
+ * then the `index_oid` will be zero but still marked valid. If the
+ * submodule has been deleted, but the delete has not been committed yet,
+ * then the `index_oid` will be set, but the `url` will be NULL.
*/
struct git_submodule {
- git_repository *owner;
+ git_refcount rc;
+
+ /* information from config */
char *name;
- char *path; /* important: may point to same string data as "name" */
+ char *path; /* important: may just point to "name" string */
char *url;
- uint32_t flags;
- git_oid head_oid;
- git_oid index_oid;
- git_oid wd_oid;
- /* information from config */
git_submodule_update_t update;
git_submodule_update_t update_default;
git_submodule_ignore_t ignore;
git_submodule_ignore_t ignore_default;
int fetch_recurse;
+
/* internal information */
- int refcount;
+ git_repository *repo;
+ uint32_t flags;
+ git_oid head_oid;
+ git_oid index_oid;
+ git_oid wd_oid;
};
/* Additional flags on top of public GIT_SUBMODULE_STATUS values */
@@ -99,4 +110,29 @@ enum {
#define GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(S) \
((S) & ~(0xFFFFFFFFu << 20))
+/* Internal status fn returns status and optionally the various OIDs */
+extern int git_submodule__status(
+ unsigned int *out_status,
+ git_oid *out_head_id,
+ git_oid *out_index_id,
+ git_oid *out_wd_id,
+ git_submodule *sm,
+ git_submodule_ignore_t ign);
+
+/* Open submodule repository as bare repo for quick HEAD check, etc. */
+extern int git_submodule_open_bare(
+ git_repository **repo,
+ git_submodule *submodule);
+
+/* Release reference to submodule object - not currently for external use */
+extern void git_submodule_free(git_submodule *sm);
+
+extern int git_submodule_parse_ignore(
+ git_submodule_ignore_t *out, const char *value);
+extern int git_submodule_parse_update(
+ git_submodule_update_t *out, const char *value);
+
+extern const char *git_submodule_ignore_to_str(git_submodule_ignore_t);
+extern const char *git_submodule_update_to_str(git_submodule_update_t);
+
#endif
diff --git a/src/tag.c b/src/tag.c
index 71f4c1eb1..31a3c8b80 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -366,10 +366,10 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu
if (git_odb_open_wstream(&stream, odb, strlen(buffer), GIT_OBJ_TAG) < 0)
return -1;
- stream->write(stream, buffer, strlen(buffer));
+ git_odb_stream_write(stream, buffer, strlen(buffer));
- error = stream->finalize_write(oid, stream);
- stream->free(stream);
+ error = git_odb_stream_finalize_write(oid, stream);
+ git_odb_stream_free(stream);
if (error < 0) {
git_buf_free(&ref_name);
diff --git a/src/thread-utils.h b/src/thread-utils.h
index 83148188d..914c1357d 100644
--- a/src/thread-utils.h
+++ b/src/thread-utils.h
@@ -38,15 +38,11 @@ typedef git_atomic git_atomic_ssize;
#endif
-GIT_INLINE(void) git_atomic_set(git_atomic *a, int val)
-{
- a->val = val;
-}
-
#ifdef GIT_THREADS
#define git_thread pthread_t
-#define git_thread_create(thread, attr, start_routine, arg) pthread_create(thread, attr, start_routine, arg)
+#define git_thread_create(thread, attr, start_routine, arg) \
+ pthread_create(thread, attr, start_routine, arg)
#define git_thread_kill(thread) pthread_cancel(thread)
#define git_thread_exit(status) pthread_exit(status)
#define git_thread_join(id, status) pthread_join(id, status)
@@ -66,6 +62,41 @@ GIT_INLINE(void) git_atomic_set(git_atomic *a, int val)
#define git_cond_signal(c) pthread_cond_signal(c)
#define git_cond_broadcast(c) pthread_cond_broadcast(c)
+/* Pthread (-ish) rwlock
+ *
+ * This differs from normal pthreads rwlocks in two ways:
+ * 1. Separate APIs for releasing read locks and write locks (as
+ * opposed to the pure POSIX API which only has one unlock fn)
+ * 2. You should not use recursive read locks (i.e. grabbing a read
+ * lock in a thread that already holds a read lock) because the
+ * Windows implementation doesn't support it
+ */
+#define git_rwlock pthread_rwlock_t
+#define git_rwlock_init(a) pthread_rwlock_init(a, NULL)
+#define git_rwlock_rdlock(a) pthread_rwlock_rdlock(a)
+#define git_rwlock_rdunlock(a) pthread_rwlock_rdunlock(a)
+#define git_rwlock_wrlock(a) pthread_rwlock_wrlock(a)
+#define git_rwlock_wrunlock(a) pthread_rwlock_wrunlock(a)
+#define git_rwlock_free(a) pthread_rwlock_destroy(a)
+#define GIT_RWLOCK_STATIC_INIT PTHREAD_RWLOCK_INITIALIZER
+
+#ifndef GIT_WIN32
+#define pthread_rwlock_rdunlock pthread_rwlock_unlock
+#define pthread_rwlock_wrunlock pthread_rwlock_unlock
+#endif
+
+
+GIT_INLINE(void) git_atomic_set(git_atomic *a, int val)
+{
+#if defined(GIT_WIN32)
+ InterlockedExchange(&a->val, (LONG)val);
+#elif defined(__GNUC__)
+ __sync_lock_test_and_set(&a->val, val);
+#else
+# error "Unsupported architecture for atomic operations"
+#endif
+}
+
GIT_INLINE(int) git_atomic_inc(git_atomic *a)
{
#if defined(GIT_WIN32)
@@ -100,11 +131,11 @@ GIT_INLINE(int) git_atomic_dec(git_atomic *a)
}
GIT_INLINE(void *) git___compare_and_swap(
- volatile void **ptr, void *oldval, void *newval)
+ void * volatile *ptr, void *oldval, void *newval)
{
volatile void *foundval;
#if defined(GIT_WIN32)
- foundval = InterlockedCompareExchangePointer(ptr, newval, oldval);
+ foundval = InterlockedCompareExchangePointer((volatile PVOID *)ptr, newval, oldval);
#elif defined(__GNUC__)
foundval = __sync_val_compare_and_swap(ptr, oldval, newval);
#else
@@ -113,6 +144,16 @@ GIT_INLINE(void *) git___compare_and_swap(
return (foundval == oldval) ? oldval : newval;
}
+GIT_INLINE(volatile void *) git___swap(
+ void * volatile *ptr, void *newval)
+{
+#if defined(GIT_WIN32)
+ return InterlockedExchangePointer(ptr, newval);
+#else
+ return __sync_lock_test_and_set(ptr, newval);
+#endif
+}
+
#ifdef GIT_ARCH_64
GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend)
@@ -131,7 +172,7 @@ GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend)
#else
#define git_thread unsigned int
-#define git_thread_create(thread, attr, start_routine, arg) (void)0
+#define git_thread_create(thread, attr, start_routine, arg) 0
#define git_thread_kill(thread) (void)0
#define git_thread_exit(status) (void)0
#define git_thread_join(id, status) (void)0
@@ -151,6 +192,22 @@ GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend)
#define git_cond_signal(c) (void)0
#define git_cond_broadcast(c) (void)0
+/* Pthreads rwlock */
+#define git_rwlock unsigned int
+#define git_rwlock_init(a) 0
+#define git_rwlock_rdlock(a) 0
+#define git_rwlock_rdunlock(a) (void)0
+#define git_rwlock_wrlock(a) 0
+#define git_rwlock_wrunlock(a) (void)0
+#define git_rwlock_free(a) (void)0
+#define GIT_RWLOCK_STATIC_INIT 0
+
+
+GIT_INLINE(void) git_atomic_set(git_atomic *a, int val)
+{
+ a->val = val;
+}
+
GIT_INLINE(int) git_atomic_inc(git_atomic *a)
{
return ++a->val;
@@ -168,7 +225,7 @@ GIT_INLINE(int) git_atomic_dec(git_atomic *a)
}
GIT_INLINE(void *) git___compare_and_swap(
- volatile void **ptr, void *oldval, void *newval)
+ void * volatile *ptr, void *oldval, void *newval)
{
if (*ptr == oldval)
*ptr = newval;
@@ -177,6 +234,14 @@ GIT_INLINE(void *) git___compare_and_swap(
return oldval;
}
+GIT_INLINE(volatile void *) git___swap(
+ void * volatile *ptr, void *newval)
+{
+ volatile void *old = *ptr;
+ *ptr = newval;
+ return old;
+}
+
#ifdef GIT_ARCH_64
GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend)
@@ -189,13 +254,18 @@ GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend)
#endif
+GIT_INLINE(int) git_atomic_get(git_atomic *a)
+{
+ return (int)a->val;
+}
+
/* Atomically replace oldval with newval
* @return oldval if it was replaced or newval if it was not
*/
#define git__compare_and_swap(P,O,N) \
- git___compare_and_swap((volatile void **)P, O, N)
+ git___compare_and_swap((void * volatile *)P, O, N)
-#define git__swap(ptr, val) git__compare_and_swap(&ptr, ptr, val)
+#define git__swap(ptr, val) (void *)git___swap((void * volatile *)&ptr, val)
extern int git_online_cpus(void);
diff --git a/src/transport.c b/src/transport.c
index 37c244c97..ff926b1be 100644
--- a/src/transport.c
+++ b/src/transport.c
@@ -42,6 +42,8 @@ static transport_definition transports[] = {
{NULL, 0, 0}
};
+static git_vector additional_transports = GIT_VECTOR_INIT;
+
#define GIT_TRANSPORT_COUNT (sizeof(transports)/sizeof(transports[0])) - 1
static int transport_find_fn(const char *url, git_transport_cb *callback, void **param)
@@ -61,6 +63,14 @@ static int transport_find_fn(const char *url, git_transport_cb *callback, void *
definition = definition_iter;
}
+ git_vector_foreach(&additional_transports, i, definition_iter) {
+ if (strncasecmp(url, definition_iter->prefix, strlen(definition_iter->prefix)))
+ continue;
+
+ if (definition_iter->priority > priority)
+ definition = definition_iter;
+ }
+
#ifdef GIT_WIN32
/* On Windows, it might not be possible to discern between absolute local
* and ssh paths - first check if this is a valid local path that points
@@ -73,7 +83,7 @@ static int transport_find_fn(const char *url, git_transport_cb *callback, void *
/* It could be a SSH remote path. Check to see if there's a :
* SSH is an unsupported transport mechanism in this version of libgit2 */
if (!definition && strrchr(url, ':'))
- definition = &dummy_transport_definition;
+ definition = &dummy_transport_definition;
#else
/* For other systems, perform the SSH check first, to avoid going to the
* filesystem if it is not necessary */
@@ -97,7 +107,7 @@ static int transport_find_fn(const char *url, git_transport_cb *callback, void *
*callback = definition->fn;
*param = definition->param;
-
+
return 0;
}
@@ -135,6 +145,62 @@ int git_transport_new(git_transport **out, git_remote *owner, const char *url)
return 0;
}
+int git_transport_register(
+ const char *prefix,
+ unsigned priority,
+ git_transport_cb cb,
+ void *param)
+{
+ transport_definition *d;
+
+ d = git__calloc(sizeof(transport_definition), 1);
+ GITERR_CHECK_ALLOC(d);
+
+ d->prefix = git__strdup(prefix);
+
+ if (!d->prefix)
+ goto on_error;
+
+ d->priority = priority;
+ d->fn = cb;
+ d->param = param;
+
+ if (git_vector_insert(&additional_transports, d) < 0)
+ goto on_error;
+
+ return 0;
+
+on_error:
+ git__free(d->prefix);
+ git__free(d);
+ return -1;
+}
+
+int git_transport_unregister(
+ const char *prefix,
+ unsigned priority)
+{
+ transport_definition *d;
+ unsigned i;
+
+ git_vector_foreach(&additional_transports, i, d) {
+ if (d->priority == priority && !strcasecmp(d->prefix, prefix)) {
+ if (git_vector_remove(&additional_transports, i) < 0)
+ return -1;
+
+ git__free(d->prefix);
+ git__free(d);
+
+ if (!additional_transports.length)
+ git_vector_free(&additional_transports);
+
+ return 0;
+ }
+ }
+
+ return GIT_ENOTFOUND;
+}
+
/* from remote.h */
int git_remote_valid_url(const char *url)
{
diff --git a/src/transports/cred.c b/src/transports/cred.c
index ba5de6e93..05d2c8dc6 100644
--- a/src/transports/cred.c
+++ b/src/transports/cred.c
@@ -9,19 +9,49 @@
#include "smart.h"
#include "git2/cred_helpers.h"
+int git_cred_has_username(git_cred *cred)
+{
+ int ret = 0;
+
+ switch (cred->credtype) {
+ case GIT_CREDTYPE_USERPASS_PLAINTEXT: {
+ git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
+ ret = !!c->username;
+ break;
+ }
+ case GIT_CREDTYPE_SSH_KEY: {
+ git_cred_ssh_key *c = (git_cred_ssh_key *)cred;
+ ret = !!c->username;
+ break;
+ }
+ case GIT_CREDTYPE_SSH_CUSTOM: {
+ git_cred_ssh_custom *c = (git_cred_ssh_custom *)cred;
+ ret = !!c->username;
+ break;
+ }
+ case GIT_CREDTYPE_DEFAULT: {
+ ret = 0;
+ break;
+ }
+ }
+
+ return ret;
+}
+
static void plaintext_free(struct git_cred *cred)
{
git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
- size_t pass_len = strlen(c->password);
git__free(c->username);
/* Zero the memory which previously held the password */
- git__memzero(c->password, pass_len);
- git__free(c->password);
-
- memset(c, 0, sizeof(*c));
+ if (c->password) {
+ size_t pass_len = strlen(c->password);
+ git__memzero(c->password, pass_len);
+ git__free(c->password);
+ }
+ git__memzero(c, sizeof(*c));
git__free(c);
}
@@ -32,8 +62,7 @@ int git_cred_userpass_plaintext_new(
{
git_cred_userpass_plaintext *c;
- if (!cred)
- return -1;
+ assert(cred && username && password);
c = git__malloc(sizeof(git_cred_userpass_plaintext));
GITERR_CHECK_ALLOC(c);
@@ -59,53 +88,74 @@ int git_cred_userpass_plaintext_new(
return 0;
}
-#ifdef GIT_SSH
-static void ssh_keyfile_passphrase_free(struct git_cred *cred)
+static void ssh_key_free(struct git_cred *cred)
{
- git_cred_ssh_keyfile_passphrase *c = (git_cred_ssh_keyfile_passphrase *)cred;
- size_t pass_len = strlen(c->passphrase);
+ git_cred_ssh_key *c =
+ (git_cred_ssh_key *)cred;
- if (c->publickey) {
- git__free(c->publickey);
- }
-
+ git__free(c->username);
+ git__free(c->publickey);
git__free(c->privatekey);
- if (c->passphrase) {
- /* Zero the memory which previously held the passphrase */
- git__memzero(c->passphrase, pass_len);
- git__free(c->passphrase);
- }
+ if (c->passphrase) {
+ /* Zero the memory which previously held the passphrase */
+ size_t pass_len = strlen(c->passphrase);
+ git__memzero(c->passphrase, pass_len);
+ git__free(c->passphrase);
+ }
+
+ git__memzero(c, sizeof(*c));
+ git__free(c);
+}
+
+static void ssh_custom_free(struct git_cred *cred)
+{
+ git_cred_ssh_custom *c = (git_cred_ssh_custom *)cred;
- memset(c, 0, sizeof(*c));
+ git__free(c->username);
+ git__free(c->publickey);
+
+ git__memzero(c, sizeof(*c));
+ git__free(c);
+}
+
+static void default_free(struct git_cred *cred)
+{
+ git_cred_default *c = (git_cred_default *)cred;
git__free(c);
}
-int git_cred_ssh_keyfile_passphrase_new(
+int git_cred_ssh_key_new(
git_cred **cred,
+ const char *username,
const char *publickey,
const char *privatekey,
const char *passphrase)
{
- git_cred_ssh_keyfile_passphrase *c;
+ git_cred_ssh_key *c;
assert(cred && privatekey);
- c = git__calloc(1, sizeof(git_cred_ssh_keyfile_passphrase));
+ c = git__calloc(1, sizeof(git_cred_ssh_key));
GITERR_CHECK_ALLOC(c);
- c->parent.credtype = GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE;
- c->parent.free = ssh_keyfile_passphrase_free;
-
- c->privatekey = git__strdup(privatekey);
+ c->parent.credtype = GIT_CREDTYPE_SSH_KEY;
+ c->parent.free = ssh_key_free;
+
+ if (username) {
+ c->username = git__strdup(username);
+ GITERR_CHECK_ALLOC(c->username);
+ }
+
+ c->privatekey = git__strdup(privatekey);
GITERR_CHECK_ALLOC(c->privatekey);
-
- if (publickey) {
+
+ if (publickey) {
c->publickey = git__strdup(publickey);
GITERR_CHECK_ALLOC(c->publickey);
}
-
+
if (passphrase) {
c->passphrase = git__strdup(passphrase);
GITERR_CHECK_ALLOC(c->passphrase);
@@ -115,48 +165,56 @@ int git_cred_ssh_keyfile_passphrase_new(
return 0;
}
-static void ssh_publickey_free(struct git_cred *cred)
+int git_cred_ssh_custom_new(
+ git_cred **cred,
+ const char *username,
+ const char *publickey,
+ size_t publickey_len,
+ git_cred_sign_callback sign_callback,
+ void *sign_data)
{
- git_cred_ssh_publickey *c = (git_cred_ssh_publickey *)cred;
+ git_cred_ssh_custom *c;
+
+ assert(cred);
- git__free(c->publickey);
+ c = git__calloc(1, sizeof(git_cred_ssh_custom));
+ GITERR_CHECK_ALLOC(c);
- c->sign_callback = NULL;
- c->sign_data = NULL;
-
- memset(c, 0, sizeof(*c));
+ c->parent.credtype = GIT_CREDTYPE_SSH_CUSTOM;
+ c->parent.free = ssh_custom_free;
- git__free(c);
+ if (username) {
+ c->username = git__strdup(username);
+ GITERR_CHECK_ALLOC(c->username);
+ }
+
+ if (publickey_len > 0) {
+ c->publickey = git__malloc(publickey_len);
+ GITERR_CHECK_ALLOC(c->publickey);
+
+ memcpy(c->publickey, publickey, publickey_len);
+ }
+
+ c->publickey_len = publickey_len;
+ c->sign_callback = sign_callback;
+ c->sign_data = sign_data;
+
+ *cred = &c->parent;
+ return 0;
}
-int git_cred_ssh_publickey_new(
- git_cred **cred,
- const char *publickey,
- size_t publickey_len,
- LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC((*sign_callback)),
- void *sign_data)
+int git_cred_default_new(git_cred **cred)
{
- git_cred_ssh_publickey *c;
+ git_cred_default *c;
- if (!cred)
- return -1;
+ assert(cred);
- c = git__malloc(sizeof(git_cred_ssh_publickey));
+ c = git__calloc(1, sizeof(git_cred_default));
GITERR_CHECK_ALLOC(c);
- c->parent.credtype = GIT_CREDTYPE_SSH_PUBLICKEY;
- c->parent.free = ssh_publickey_free;
-
- c->publickey = git__malloc(publickey_len);
- GITERR_CHECK_ALLOC(c->publickey);
-
- memcpy(c->publickey, publickey, publickey_len);
-
- c->publickey_len = publickey_len;
- c->sign_callback = sign_callback;
- c->sign_data = sign_data;
+ c->credtype = GIT_CREDTYPE_DEFAULT;
+ c->free = default_free;
- *cred = &c->parent;
+ *cred = c;
return 0;
}
-#endif
diff --git a/src/transports/git.c b/src/transports/git.c
index 3a0b86345..5dcd4eff7 100644
--- a/src/transports/git.c
+++ b/src/transports/git.c
@@ -179,39 +179,33 @@ static int _git_uploadpack_ls(
const char *url,
git_smart_subtransport_stream **stream)
{
- char *host, *port, *user=NULL, *pass=NULL;
+ char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL;
+ const char *stream_url = url;
git_stream *s;
+ int error = -1;
*stream = NULL;
-
if (!git__prefixcmp(url, prefix_git))
- url += strlen(prefix_git);
+ stream_url += strlen(prefix_git);
- if (git_stream_alloc(t, url, cmd_uploadpack, stream) < 0)
+ if (git_stream_alloc(t, stream_url, cmd_uploadpack, stream) < 0)
return -1;
s = (git_stream *)*stream;
- if (gitno_extract_url_parts(&host, &port, &user, &pass, url, GIT_DEFAULT_PORT) < 0)
- goto on_error;
-
- if (gitno_connect(&s->socket, host, port, 0) < 0)
- goto on_error;
-
- t->current_stream = s;
- git__free(host);
- git__free(port);
- git__free(user);
- git__free(pass);
- return 0;
+ if (!(error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT))) {
+ if (!(error = gitno_connect(&s->socket, host, port, 0)))
+ t->current_stream = s;
-on_error:
- if (*stream)
+ git__free(host);
+ git__free(port);
+ git__free(path);
+ git__free(user);
+ git__free(pass);
+ } else if (*stream)
git_stream_free(*stream);
- git__free(host);
- git__free(port);
- return -1;
+ return error;
}
static int _git_uploadpack(
@@ -235,39 +229,33 @@ static int _git_receivepack_ls(
const char *url,
git_smart_subtransport_stream **stream)
{
- char *host, *port, *user=NULL, *pass=NULL;
+ char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL;
+ const char *stream_url = url;
git_stream *s;
+ int error;
*stream = NULL;
-
if (!git__prefixcmp(url, prefix_git))
- url += strlen(prefix_git);
+ stream_url += strlen(prefix_git);
- if (git_stream_alloc(t, url, cmd_receivepack, stream) < 0)
+ if (git_stream_alloc(t, stream_url, cmd_receivepack, stream) < 0)
return -1;
s = (git_stream *)*stream;
- if (gitno_extract_url_parts(&host, &port, &user, &pass, url, GIT_DEFAULT_PORT) < 0)
- goto on_error;
-
- if (gitno_connect(&s->socket, host, port, 0) < 0)
- goto on_error;
-
- t->current_stream = s;
- git__free(host);
- git__free(port);
- git__free(user);
- git__free(pass);
- return 0;
+ if (!(error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT))) {
+ if (!(error = gitno_connect(&s->socket, host, port, 0)))
+ t->current_stream = s;
-on_error:
- if (*stream)
+ git__free(host);
+ git__free(port);
+ git__free(path);
+ git__free(user);
+ git__free(pass);
+ } else if (*stream)
git_stream_free(*stream);
- git__free(host);
- git__free(port);
- return -1;
+ return error;
}
static int _git_receivepack(
diff --git a/src/transports/http.c b/src/transports/http.c
index eca06ead2..ace0d97d0 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -12,8 +12,6 @@
#include "netops.h"
#include "smart.h"
-static const char *prefix_http = "http://";
-static const char *prefix_https = "https://";
static const char *upload_pack_service = "upload-pack";
static const char *upload_pack_ls_service_url = "/info/refs?service=git-upload-pack";
static const char *upload_pack_service_url = "/git-upload-pack";
@@ -59,16 +57,11 @@ typedef struct {
git_smart_subtransport parent;
transport_smart *owner;
gitno_socket socket;
- const char *path;
- char *host;
- char *port;
- char *user_from_url;
- char *pass_from_url;
+ gitno_connection_data connection_data;
git_cred *cred;
git_cred *url_cred;
http_authmechanism_t auth_mechanism;
- unsigned connected : 1,
- use_ssl : 1;
+ bool connected;
/* Parser structures */
http_parser parser;
@@ -125,18 +118,12 @@ static int gen_request(
size_t content_length)
{
http_subtransport *t = OWNING_SUBTRANSPORT(s);
+ const char *path = t->connection_data.path ? t->connection_data.path : "/";
- if (!t->path)
- t->path = "/";
-
- /* If we were redirected, make sure to respect that here */
- if (s->redirect_url)
- git_buf_printf(buf, "%s %s HTTP/1.1\r\n", s->verb, s->redirect_url);
- else
- git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, t->path, s->service_url);
+ git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, path, s->service_url);
git_buf_puts(buf, "User-Agent: git/1.0 (libgit2 " LIBGIT2_VERSION ")\r\n");
- git_buf_printf(buf, "Host: %s\r\n", t->host);
+ git_buf_printf(buf, "Host: %s\r\n", t->connection_data.host);
if (s->chunked || content_length > 0) {
git_buf_printf(buf, "Accept: application/x-git-%s-result\r\n", s->service);
@@ -156,9 +143,9 @@ static int gen_request(
return -1;
/* Use url-parsed basic auth if username and password are both provided */
- if (!t->cred && t->user_from_url && t->pass_from_url) {
- if (!t->url_cred &&
- git_cred_userpass_plaintext_new(&t->url_cred, t->user_from_url, t->pass_from_url) < 0)
+ if (!t->cred && t->connection_data.user && t->connection_data.pass) {
+ if (!t->url_cred && git_cred_userpass_plaintext_new(&t->url_cred,
+ t->connection_data.user, t->connection_data.pass) < 0)
return -1;
if (apply_basic_credential(buf, t->url_cred) < 0) return -1;
}
@@ -209,7 +196,7 @@ static int on_header_ready(http_subtransport *t)
}
else if (!strcasecmp("Location", git_buf_cstr(name))) {
if (!t->location) {
- t->location= git__strdup(git_buf_cstr(value));
+ t->location = git__strdup(git_buf_cstr(value));
GITERR_CHECK_ALLOC(t->location);
}
}
@@ -283,7 +270,7 @@ static int on_headers_complete(http_parser *parser)
if (t->owner->cred_acquire_cb(&t->cred,
t->owner->url,
- t->user_from_url,
+ t->connection_data.user,
allowed_types,
t->owner->cred_acquire_payload) < 0)
return PARSE_ERROR_GENERIC;
@@ -298,20 +285,18 @@ static int on_headers_complete(http_parser *parser)
/* Check for a redirect.
* Right now we only permit a redirect to the same hostname. */
if ((parser->status_code == 301 ||
- parser->status_code == 302 ||
- (parser->status_code == 303 && get_verb == s->verb) ||
- parser->status_code == 307) &&
- t->location) {
+ parser->status_code == 302 ||
+ (parser->status_code == 303 && get_verb == s->verb) ||
+ parser->status_code == 307) &&
+ t->location) {
if (s->redirect_count >= 7) {
giterr_set(GITERR_NET, "Too many redirects");
return t->parse_error = PARSE_ERROR_GENERIC;
}
- if (t->location[0] != '/') {
- giterr_set(GITERR_NET, "Only relative redirects are supported");
+ if (gitno_connection_data_from_url(&t->connection_data, t->location, s->service_url) < 0)
return t->parse_error = PARSE_ERROR_GENERIC;
- }
/* Set the redirect URL on the stream. This is a transfer of
* ownership of the memory. */
@@ -468,7 +453,7 @@ static int http_connect(http_subtransport *t)
if (t->socket.socket)
gitno_close(&t->socket);
- if (t->use_ssl) {
+ if (t->connection_data.use_ssl) {
int tflags;
if (t->owner->parent.read_flags(&t->owner->parent, &tflags) < 0)
@@ -480,7 +465,7 @@ static int http_connect(http_subtransport *t)
flags |= GITNO_CONNECT_SSL_NO_CHECK_CERT;
}
- if (gitno_connect(&t->socket, t->host, t->port, flags) < 0)
+ if (gitno_connect(&t->socket, t->connection_data.host, t->connection_data.port, flags) < 0)
return -1;
t->connected = 1;
@@ -822,50 +807,30 @@ static int http_action(
git_smart_service_t action)
{
http_subtransport *t = (http_subtransport *)subtransport;
- const char *default_port = NULL;
int ret;
if (!stream)
return -1;
- if (!t->host || !t->port || !t->path) {
- if (!git__prefixcmp(url, prefix_http)) {
- url = url + strlen(prefix_http);
- default_port = "80";
- }
-
- if (!git__prefixcmp(url, prefix_https)) {
- url += strlen(prefix_https);
- default_port = "443";
- t->use_ssl = 1;
- }
-
- if (!default_port)
- return -1;
-
- if ((ret = gitno_extract_url_parts(&t->host, &t->port,
- &t->user_from_url, &t->pass_from_url, url, default_port)) < 0)
- return ret;
-
- t->path = strchr(url, '/');
- }
+ if ((!t->connection_data.host || !t->connection_data.port || !t->connection_data.path) &&
+ (ret = gitno_connection_data_from_url(&t->connection_data, url, NULL)) < 0)
+ return ret;
if (http_connect(t) < 0)
return -1;
- switch (action)
- {
- case GIT_SERVICE_UPLOADPACK_LS:
- return http_uploadpack_ls(t, stream);
+ switch (action) {
+ case GIT_SERVICE_UPLOADPACK_LS:
+ return http_uploadpack_ls(t, stream);
- case GIT_SERVICE_UPLOADPACK:
- return http_uploadpack(t, stream);
+ case GIT_SERVICE_UPLOADPACK:
+ return http_uploadpack(t, stream);
- case GIT_SERVICE_RECEIVEPACK_LS:
- return http_receivepack_ls(t, stream);
+ case GIT_SERVICE_RECEIVEPACK_LS:
+ return http_receivepack_ls(t, stream);
- case GIT_SERVICE_RECEIVEPACK:
- return http_receivepack(t, stream);
+ case GIT_SERVICE_RECEIVEPACK:
+ return http_receivepack(t, stream);
}
*stream = NULL;
@@ -893,25 +858,7 @@ static int http_close(git_smart_subtransport *subtransport)
t->url_cred = NULL;
}
- if (t->host) {
- git__free(t->host);
- t->host = NULL;
- }
-
- if (t->port) {
- git__free(t->port);
- t->port = NULL;
- }
-
- if (t->user_from_url) {
- git__free(t->user_from_url);
- t->user_from_url = NULL;
- }
-
- if (t->pass_from_url) {
- git__free(t->pass_from_url);
- t->pass_from_url = NULL;
- }
+ gitno_connection_data_free_ptrs(&t->connection_data);
return 0;
}
diff --git a/src/transports/local.c b/src/transports/local.c
index 550060958..4502f0202 100644
--- a/src/transports/local.c
+++ b/src/transports/local.c
@@ -119,15 +119,24 @@ on_error:
static int store_refs(transport_local *t)
{
- unsigned int i;
+ size_t i;
+ git_remote_head *head;
git_strarray ref_names = {0};
assert(t);
- if (git_reference_list(&ref_names, t->repo) < 0 ||
- git_vector_init(&t->refs, ref_names.count, NULL) < 0)
+ if (git_reference_list(&ref_names, t->repo) < 0)
goto on_error;
+ /* Clear all heads we might have fetched in a previous connect */
+ git_vector_foreach(&t->refs, i, head) {
+ git__free(head->name);
+ git__free(head);
+ }
+
+ /* Clear the vector so we can reuse it */
+ git_vector_clear(&t->refs);
+
/* Sort the references first */
git__tsort((void **)ref_names.strings, ref_names.count, &git__strcmp_cb);
@@ -204,21 +213,17 @@ static int local_connect(
return 0;
}
-static int local_ls(git_transport *transport, git_headlist_cb list_cb, void *payload)
+static int local_ls(const git_remote_head ***out, size_t *size, git_transport *transport)
{
transport_local *t = (transport_local *)transport;
- unsigned int i;
- git_remote_head *head = NULL;
if (!t->have_refs) {
giterr_set(GITERR_NET, "The transport has not yet loaded the refs");
return -1;
}
- git_vector_foreach(&t->refs, i, head) {
- if (list_cb(head, payload))
- return GIT_EUSER;
- }
+ *out = (const git_remote_head **) t->refs.contents;
+ *size = t->refs.length;
return 0;
}
@@ -278,9 +283,9 @@ static int local_push_copy_object(
odb_obj_size, odb_obj_type)) < 0)
goto on_error;
- if (odb_stream->write(odb_stream, (char *)git_odb_object_data(odb_obj),
+ if (git_odb_stream_write(odb_stream, (char *)git_odb_object_data(odb_obj),
odb_obj_size) < 0 ||
- odb_stream->finalize_write(&remote_odb_obj_oid, odb_stream) < 0) {
+ git_odb_stream_finalize_write(&remote_odb_obj_oid, odb_stream) < 0) {
error = -1;
} else if (git_oid__cmp(&obj->id, &remote_odb_obj_oid) != 0) {
giterr_set(GITERR_ODB, "Error when writing object to remote odb "
@@ -289,7 +294,7 @@ static int local_push_copy_object(
error = -1;
}
- odb_stream->free(odb_stream);
+ git_odb_stream_free(odb_stream);
on_error:
git_odb_object_free(odb_obj);
@@ -352,7 +357,8 @@ static int local_push(
non-bare repo push support would require checking configs to see if
we should override the default 'don't let this happen' behavior */
if (!remote_repo->is_bare) {
- error = -1;
+ error = GIT_EBAREREPO;
+ giterr_set(GITERR_INVALID, "Local push doesn't (yet) support pushing to non-bare repos.");
goto on_error;
}
@@ -424,7 +430,7 @@ static int local_push(
if (!url || t->parent.close(&t->parent) < 0 ||
t->parent.connect(&t->parent, url,
- push->remote->cred_acquire_cb, NULL, GIT_DIRECTION_PUSH, flags))
+ push->remote->callbacks.credentials, NULL, GIT_DIRECTION_PUSH, flags))
goto on_error;
}
@@ -449,7 +455,7 @@ static int foreach_cb(void *buf, size_t len, void *payload)
foreach_data *data = (foreach_data*)payload;
data->stats->received_bytes += len;
- return data->writepack->add(data->writepack, buf, len, data->stats);
+ return data->writepack->append(data->writepack, buf, len, data->stats);
}
static int local_download_pack(
@@ -571,8 +577,6 @@ static void local_cancel(git_transport *transport)
static int local_close(git_transport *transport)
{
transport_local *t = (transport_local *)transport;
- size_t i;
- git_remote_head *head;
t->connected = 0;
@@ -586,19 +590,21 @@ static int local_close(git_transport *transport)
t->url = NULL;
}
- git_vector_foreach(&t->refs, i, head) {
- git__free(head->name);
- git__free(head);
- }
-
- git_vector_free(&t->refs);
-
return 0;
}
static void local_free(git_transport *transport)
{
transport_local *t = (transport_local *)transport;
+ size_t i;
+ git_remote_head *head;
+
+ git_vector_foreach(&t->refs, i, head) {
+ git__free(head->name);
+ git__free(head);
+ }
+
+ git_vector_free(&t->refs);
/* Close the transport, if it's still open. */
local_close(transport);
@@ -632,6 +638,7 @@ int git_transport_local(git_transport **out, git_remote *owner, void *param)
t->parent.read_flags = local_read_flags;
t->parent.cancel = local_cancel;
+ git_vector_init(&t->refs, 0, NULL);
t->owner = owner;
*out = (git_transport *) t;
diff --git a/src/transports/smart.c b/src/transports/smart.c
index 416eb221f..5242beb65 100644
--- a/src/transports/smart.c
+++ b/src/transports/smart.c
@@ -23,8 +23,13 @@ static int git_smart__recv_cb(gitno_buffer *buf)
buf->offset += bytes_read;
- if (t->packetsize_cb)
- t->packetsize_cb(bytes_read, t->packetsize_payload);
+ if (t->packetsize_cb && !t->cancelled.val)
+ if (t->packetsize_cb(bytes_read, t->packetsize_payload)) {
+ git_atomic_set(&t->cancelled, 1);
+
+ giterr_clear();
+ return GIT_EUSER;
+ }
return (int)(buf->offset - old_len);
}
@@ -58,6 +63,24 @@ static int git_smart__set_callbacks(
return 0;
}
+int git_smart__update_heads(transport_smart *t)
+{
+ size_t i;
+ git_pkt *pkt;
+
+ git_vector_clear(&t->heads);
+ git_vector_foreach(&t->refs, i, pkt) {
+ git_pkt_ref *ref = (git_pkt_ref *) pkt;
+ if (pkt->type != GIT_PKT_REF)
+ continue;
+
+ if (git_vector_insert(&t->heads, &ref->head) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
static int git_smart__connect(
git_transport *transport,
const char *url,
@@ -135,6 +158,9 @@ static int git_smart__connect(
git_pkt_free((git_pkt *)first);
}
+ /* Keep a list of heads for _ls */
+ git_smart__update_heads(t);
+
if (t->rpc && git_smart__reset_stream(t, false) < 0)
return -1;
@@ -144,28 +170,17 @@ static int git_smart__connect(
return 0;
}
-static int git_smart__ls(git_transport *transport, git_headlist_cb list_cb, void *payload)
+static int git_smart__ls(const git_remote_head ***out, size_t *size, git_transport *transport)
{
transport_smart *t = (transport_smart *)transport;
- unsigned int i;
- git_pkt *p = NULL;
if (!t->have_refs) {
giterr_set(GITERR_NET, "The transport has not yet loaded the refs");
return -1;
}
- git_vector_foreach(&t->refs, i, p) {
- git_pkt_ref *pkt = NULL;
-
- if (p->type != GIT_PKT_REF)
- continue;
-
- pkt = (git_pkt_ref *)p;
-
- if (list_cb(&pkt->head, payload))
- return GIT_EUSER;
- }
+ *out = (const git_remote_head **) t->heads.contents;
+ *size = t->heads.length;
return 0;
}
@@ -288,6 +303,7 @@ static void git_smart__free(git_transport *transport)
/* Free the subtransport */
t->wrapped->free(t->wrapped);
+ git_vector_free(&t->heads);
git_vector_foreach(refs, i, p)
git_pkt_free(p);
@@ -335,6 +351,11 @@ int git_transport_smart(git_transport **out, git_remote *owner, void *param)
return -1;
}
+ if (git_vector_init(&t->heads, 16, ref_name_cmp) < 0) {
+ git__free(t);
+ return -1;
+ }
+
if (definition->callback(&t->wrapped, &t->parent) < 0) {
git__free(t);
return -1;
diff --git a/src/transports/smart.h b/src/transports/smart.h
index c52401a3c..32f0be7f2 100644
--- a/src/transports/smart.h
+++ b/src/transports/smart.h
@@ -16,11 +16,13 @@
#define GIT_CAP_OFS_DELTA "ofs-delta"
#define GIT_CAP_MULTI_ACK "multi_ack"
+#define GIT_CAP_MULTI_ACK_DETAILED "multi_ack_detailed"
#define GIT_CAP_SIDE_BAND "side-band"
#define GIT_CAP_SIDE_BAND_64K "side-band-64k"
#define GIT_CAP_INCLUDE_TAG "include-tag"
#define GIT_CAP_DELETE_REFS "delete-refs"
#define GIT_CAP_REPORT_STATUS "report-status"
+#define GIT_CAP_THIN_PACK "thin-pack"
enum git_pkt_type {
GIT_PKT_CMD,
@@ -39,7 +41,7 @@ enum git_pkt_type {
GIT_PKT_UNPACK,
};
-/* Used for multi-ack */
+/* Used for multi_ack and mutli_ack_detailed */
enum git_ack_status {
GIT_ACK_NONE,
GIT_ACK_CONTINUE,
@@ -112,14 +114,16 @@ typedef struct transport_smart_caps {
int common:1,
ofs_delta:1,
multi_ack: 1,
+ multi_ack_detailed: 1,
side_band:1,
side_band_64k:1,
include_tag:1,
delete_refs:1,
- report_status:1;
+ report_status:1,
+ thin_pack:1;
} transport_smart_caps;
-typedef void (*packetsize_cb)(size_t received, void *payload);
+typedef int (*packetsize_cb)(size_t received, void *payload);
typedef struct {
git_transport parent;
@@ -136,6 +140,7 @@ typedef struct {
git_smart_subtransport_stream *current_stream;
transport_smart_caps caps;
git_vector refs;
+ git_vector heads;
git_vector common;
git_atomic cancelled;
packetsize_cb packetsize_cb;
@@ -169,6 +174,8 @@ int git_smart__download_pack(
int git_smart__negotiation_step(git_transport *transport, void *data, size_t len);
int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream **out);
+int git_smart__update_heads(transport_smart *t);
+
/* smart_pkt.c */
int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t len);
int git_pkt_buffer_flush(git_buf *buf);
diff --git a/src/transports/smart_pkt.c b/src/transports/smart_pkt.c
index 99da37567..2bb09c750 100644
--- a/src/transports/smart_pkt.c
+++ b/src/transports/smart_pkt.c
@@ -39,7 +39,7 @@ static int flush_pkt(git_pkt **out)
return 0;
}
-/* the rest of the line will be useful for multi_ack */
+/* the rest of the line will be useful for multi_ack and multi_ack_detailed */
static int ack_pkt(git_pkt **out, const char *line, size_t len)
{
git_pkt_ack *pkt;
@@ -62,6 +62,10 @@ static int ack_pkt(git_pkt **out, const char *line, size_t len)
if (len >= 7) {
if (!git__prefixcmp(line + 1, "continue"))
pkt->status = GIT_ACK_CONTINUE;
+ if (!git__prefixcmp(line + 1, "common"))
+ pkt->status = GIT_ACK_COMMON;
+ if (!git__prefixcmp(line + 1, "ready"))
+ pkt->status = GIT_ACK_READY;
}
*out = (git_pkt *) pkt;
@@ -456,22 +460,27 @@ static int buffer_want_with_caps(const git_remote_head *head, transport_smart_ca
char oid[GIT_OID_HEXSZ +1] = {0};
unsigned int len;
- /* Prefer side-band-64k if the server supports both */
- if (caps->side_band) {
- if (caps->side_band_64k)
- git_buf_printf(&str, "%s ", GIT_CAP_SIDE_BAND_64K);
- else
- git_buf_printf(&str, "%s ", GIT_CAP_SIDE_BAND);
- }
- if (caps->ofs_delta)
- git_buf_puts(&str, GIT_CAP_OFS_DELTA " ");
-
- if (caps->multi_ack)
+ /* Prefer multi_ack_detailed */
+ if (caps->multi_ack_detailed)
+ git_buf_puts(&str, GIT_CAP_MULTI_ACK_DETAILED " ");
+ else if (caps->multi_ack)
git_buf_puts(&str, GIT_CAP_MULTI_ACK " ");
+ /* Prefer side-band-64k if the server supports both */
+ if (caps->side_band_64k)
+ git_buf_printf(&str, "%s ", GIT_CAP_SIDE_BAND_64K);
+ else if (caps->side_band)
+ git_buf_printf(&str, "%s ", GIT_CAP_SIDE_BAND);
+
if (caps->include_tag)
git_buf_puts(&str, GIT_CAP_INCLUDE_TAG " ");
+ if (caps->thin_pack)
+ git_buf_puts(&str, GIT_CAP_THIN_PACK " ");
+
+ if (caps->ofs_delta)
+ git_buf_puts(&str, GIT_CAP_OFS_DELTA " ");
+
if (git_buf_oom(&str))
return -1;
diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c
index 636616717..3bf1f9329 100644
--- a/src/transports/smart_protocol.c
+++ b/src/transports/smart_protocol.c
@@ -13,8 +13,11 @@
#include "push.h"
#include "pack-objects.h"
#include "remote.h"
+#include "util.h"
#define NETWORK_XFER_THRESHOLD (100*1024)
+/* The minimal interval between progress updates (in seconds). */
+#define MIN_PROGRESS_UPDATE_INTERVAL 0.5
int git_smart__store_refs(transport_smart *t, int flushes)
{
@@ -46,7 +49,7 @@ int git_smart__store_refs(transport_smart *t, int flushes)
if (error == GIT_EBUFS) {
if ((recvd = gitno_recv(buf)) < 0)
- return -1;
+ return recvd;
if (recvd == 0 && !flush) {
giterr_set(GITERR_NET, "Early EOF");
@@ -94,6 +97,13 @@ int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps)
continue;
}
+ /* Keep multi_ack_detailed before multi_ack */
+ if (!git__prefixcmp(ptr, GIT_CAP_MULTI_ACK_DETAILED)) {
+ caps->common = caps->multi_ack_detailed = 1;
+ ptr += strlen(GIT_CAP_MULTI_ACK_DETAILED);
+ continue;
+ }
+
if (!git__prefixcmp(ptr, GIT_CAP_MULTI_ACK)) {
caps->common = caps->multi_ack = 1;
ptr += strlen(GIT_CAP_MULTI_ACK);
@@ -125,6 +135,12 @@ int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps)
continue;
}
+ if (!git__prefixcmp(ptr, GIT_CAP_THIN_PACK)) {
+ caps->common = caps->thin_pack = 1;
+ ptr += strlen(GIT_CAP_THIN_PACK);
+ continue;
+ }
+
/* We don't know this capability, so skip it */
ptr = strchr(ptr, ' ');
}
@@ -148,10 +164,10 @@ static int recv_pkt(git_pkt **out, gitno_buffer *buf)
break; /* return the pkt */
if (error < 0 && error != GIT_EBUFS)
- return -1;
+ return error;
if ((ret = gitno_recv(buf)) < 0)
- return -1;
+ return ret;
} while (error);
gitno_consume(buf, line_end);
@@ -168,10 +184,11 @@ static int store_common(transport_smart *t)
{
git_pkt *pkt = NULL;
gitno_buffer *buf = &t->buffer;
+ int error;
do {
- if (recv_pkt(&pkt, buf) < 0)
- return -1;
+ if ((error = recv_pkt(&pkt, buf)) < 0)
+ return error;
if (pkt->type == GIT_PKT_ACK) {
if (git_vector_insert(&t->common, pkt) < 0)
@@ -211,6 +228,7 @@ static int fetch_setup_walk(git_revwalk **out, git_repository *repo)
if (git_reference_type(ref) == GIT_REF_SYMBOLIC)
continue;
+
if (git_revwalk_push(walk, git_reference_target(ref)) < 0)
goto on_error;
@@ -227,7 +245,33 @@ on_error:
return -1;
}
-int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, const git_remote_head * const *refs, size_t count)
+static int wait_while_ack(gitno_buffer *buf)
+{
+ int error;
+ git_pkt_ack *pkt = NULL;
+
+ while (1) {
+ git__free(pkt);
+
+ if ((error = recv_pkt((git_pkt **)&pkt, buf)) < 0)
+ return error;
+
+ if (pkt->type == GIT_PKT_NAK)
+ break;
+
+ if (pkt->type == GIT_PKT_ACK &&
+ (pkt->status != GIT_ACK_CONTINUE ||
+ pkt->status != GIT_ACK_COMMON)) {
+ git__free(pkt);
+ return 0;
+ }
+ }
+
+ git__free(pkt);
+ return 0;
+}
+
+int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, const git_remote_head * const *wants, size_t count)
{
transport_smart *t = (transport_smart *)transport;
gitno_buffer *buf = &t->buffer;
@@ -237,19 +281,20 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
unsigned int i;
git_oid oid;
- /* No own logic, do our thing */
- if ((error = git_pkt_buffer_wants(refs, count, &t->caps, &data)) < 0)
+ if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
return error;
if ((error = fetch_setup_walk(&walk, repo)) < 0)
goto on_error;
+
/*
- * We don't support any kind of ACK extensions, so the negotiation
- * boils down to sending what we have and listening for an ACK
- * every once in a while.
+ * Our support for ACK extensions is simply to parse them. On
+ * the first ACK we will accept that as enough common
+ * objects. We give up if we haven't found an answer in the
+ * first 256 we send.
*/
i = 0;
- while (true) {
+ while (i < 256) {
error = git_revwalk_next(&oid, walk);
if (error < 0) {
@@ -278,7 +323,7 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
goto on_error;
git_buf_clear(&data);
- if (t->caps.multi_ack) {
+ if (t->caps.multi_ack || t->caps.multi_ack_detailed) {
if ((error = store_common(t)) < 0)
goto on_error;
} else {
@@ -307,7 +352,7 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
git_pkt_ack *pkt;
unsigned int i;
- if ((error = git_pkt_buffer_wants(refs, count, &t->caps, &data)) < 0)
+ if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
goto on_error;
git_vector_foreach(&t->common, i, pkt) {
@@ -327,7 +372,7 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
git_pkt_ack *pkt;
unsigned int i;
- if ((error = git_pkt_buffer_wants(refs, count, &t->caps, &data)) < 0)
+ if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
goto on_error;
git_vector_foreach(&t->common, i, pkt) {
@@ -356,7 +401,7 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
git_revwalk_free(walk);
/* Now let's eat up whatever the server gives us */
- if (!t->caps.multi_ack) {
+ if (!t->caps.multi_ack && !t->caps.multi_ack_detailed) {
pkt_type = recv_pkt(NULL, buf);
if (pkt_type < 0) {
@@ -366,22 +411,10 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c
return -1;
}
} else {
- git_pkt_ack *pkt;
- do {
- if ((error = recv_pkt((git_pkt **)&pkt, buf)) < 0)
- return error;
-
- if (pkt->type == GIT_PKT_NAK ||
- (pkt->type == GIT_PKT_ACK && pkt->status != GIT_ACK_CONTINUE)) {
- git__free(pkt);
- break;
- }
-
- git__free(pkt);
- } while (1);
+ error = wait_while_ack(buf);
}
- return 0;
+ return error;
on_error:
git_revwalk_free(walk);
@@ -399,16 +432,16 @@ static int no_sideband(transport_smart *t, struct git_odb_writepack *writepack,
return GIT_EUSER;
}
- if (writepack->add(writepack, buf->data, buf->offset, stats) < 0)
+ if (writepack->append(writepack, buf->data, buf->offset, stats) < 0)
return -1;
gitno_consume_n(buf, buf->offset);
if ((recvd = gitno_recv(buf)) < 0)
- return -1;
+ return recvd;
} while(recvd > 0);
- if (writepack->commit(writepack, stats))
+ if (writepack->commit(writepack, stats) < 0)
return -1;
return 0;
@@ -422,7 +455,7 @@ struct network_packetsize_payload
size_t last_fired_bytes;
};
-static void network_packetsize(size_t received, void *payload)
+static int network_packetsize(size_t received, void *payload)
{
struct network_packetsize_payload *npp = (struct network_packetsize_payload*)payload;
@@ -432,8 +465,12 @@ static void network_packetsize(size_t received, void *payload)
/* Fire notification if the threshold is reached */
if ((npp->stats->received_bytes - npp->last_fired_bytes) > NETWORK_XFER_THRESHOLD) {
npp->last_fired_bytes = npp->stats->received_bytes;
- npp->callback(npp->stats, npp->payload);
+
+ if (npp->callback(npp->stats, npp->payload))
+ return GIT_EUSER;
}
+
+ return 0;
}
int git_smart__download_pack(
@@ -447,7 +484,7 @@ int git_smart__download_pack(
gitno_buffer *buf = &t->buffer;
git_odb *odb;
struct git_odb_writepack *writepack = NULL;
- int error = -1;
+ int error = 0;
struct network_packetsize_payload npp = {0};
memset(stats, 0, sizeof(git_transfer_progress));
@@ -460,13 +497,14 @@ int git_smart__download_pack(
t->packetsize_payload = &npp;
/* We might have something in the buffer already from negotiate_fetch */
- if (t->buffer.offset > 0)
- t->packetsize_cb(t->buffer.offset, t->packetsize_payload);
+ if (t->buffer.offset > 0 && !t->cancelled.val)
+ if (t->packetsize_cb(t->buffer.offset, t->packetsize_payload))
+ git_atomic_set(&t->cancelled, 1);
}
if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) < 0))
- goto on_error;
+ goto done;
/*
* If the remote doesn't support the side-band, we can feed
@@ -474,37 +512,46 @@ int git_smart__download_pack(
* check which one belongs there.
*/
if (!t->caps.side_band && !t->caps.side_band_64k) {
- if (no_sideband(t, writepack, buf, stats) < 0)
- goto on_error;
-
- goto on_success;
+ error = no_sideband(t, writepack, buf, stats);
+ goto done;
}
do {
git_pkt *pkt;
+ /* Check cancellation before network call */
if (t->cancelled.val) {
giterr_set(GITERR_NET, "The fetch was cancelled by the user");
error = GIT_EUSER;
- goto on_error;
+ goto done;
}
- if (recv_pkt(&pkt, buf) < 0)
- goto on_error;
+ if ((error = recv_pkt(&pkt, buf)) < 0)
+ goto done;
+
+ /* Check cancellation after network call */
+ if (t->cancelled.val) {
+ giterr_set(GITERR_NET, "The fetch was cancelled by the user");
+ error = GIT_EUSER;
+ goto done;
+ }
if (pkt->type == GIT_PKT_PROGRESS) {
if (t->progress_cb) {
git_pkt_progress *p = (git_pkt_progress *) pkt;
- t->progress_cb(p->data, p->len, t->message_cb_payload);
+ if (t->progress_cb(p->data, p->len, t->message_cb_payload)) {
+ giterr_set(GITERR_NET, "The fetch was cancelled by the user");
+ return GIT_EUSER;
+ }
}
git__free(pkt);
} else if (pkt->type == GIT_PKT_DATA) {
git_pkt_data *p = (git_pkt_data *) pkt;
- error = writepack->add(writepack, p->data, p->len, stats);
+ error = writepack->append(writepack, p->data, p->len, stats);
git__free(pkt);
if (error < 0)
- goto on_error;
+ goto done;
} else if (pkt->type == GIT_PKT_FLUSH) {
/* A flush indicates the end of the packfile */
git__free(pkt);
@@ -512,13 +559,9 @@ int git_smart__download_pack(
}
} while (1);
- if (writepack->commit(writepack, stats) < 0)
- goto on_error;
-
-on_success:
- error = 0;
+ error = writepack->commit(writepack, stats);
-on_error:
+done:
if (writepack)
writepack->free(writepack);
@@ -656,7 +699,7 @@ static int parse_report(gitno_buffer *buf, git_push *push)
if (error == GIT_EBUFS) {
if ((recvd = gitno_recv(buf)) < 0)
- return -1;
+ return recvd;
if (recvd == 0) {
giterr_set(GITERR_NET, "Early EOF");
@@ -801,22 +844,56 @@ static int update_refs_from_report(
return 0;
}
+struct push_packbuilder_payload
+{
+ git_smart_subtransport_stream *stream;
+ git_packbuilder *pb;
+ git_push_transfer_progress cb;
+ void *cb_payload;
+ size_t last_bytes;
+ double last_progress_report_time;
+};
+
static int stream_thunk(void *buf, size_t size, void *data)
{
- git_smart_subtransport_stream *s = (git_smart_subtransport_stream *)data;
+ int error = 0;
+ struct push_packbuilder_payload *payload = data;
- return s->write(s, (const char *)buf, size);
+ if ((error = payload->stream->write(payload->stream, (const char *)buf, size)) < 0)
+ return error;
+
+ if (payload->cb) {
+ double current_time = git__timer();
+ payload->last_bytes += size;
+
+ if ((current_time - payload->last_progress_report_time) >= MIN_PROGRESS_UPDATE_INTERVAL) {
+ payload->last_progress_report_time = current_time;
+ if (payload->cb(payload->pb->nr_written, payload->pb->nr_objects, payload->last_bytes, payload->cb_payload)) {
+ giterr_clear();
+ error = GIT_EUSER;
+ }
+ }
+ }
+
+ return error;
}
int git_smart__push(git_transport *transport, git_push *push)
{
transport_smart *t = (transport_smart *)transport;
- git_smart_subtransport_stream *s;
+ struct push_packbuilder_payload packbuilder_payload = {0};
git_buf pktline = GIT_BUF_INIT;
- int error = -1, need_pack = 0;
+ int error = 0, need_pack = 0;
push_spec *spec;
unsigned int i;
+ packbuilder_payload.pb = push->pb;
+
+ if (push->transfer_progress_cb) {
+ packbuilder_payload.cb = push->transfer_progress_cb;
+ packbuilder_payload.cb_payload = push->transfer_progress_cb_payload;
+ }
+
#ifdef PUSH_DEBUG
{
git_remote_head *head;
@@ -848,29 +925,36 @@ int git_smart__push(git_transport *transport, git_push *push)
}
}
- if (git_smart__get_push_stream(t, &s) < 0 ||
- gen_pktline(&pktline, push) < 0 ||
- s->write(s, git_buf_cstr(&pktline), git_buf_len(&pktline)) < 0)
- goto on_error;
+ if ((error = git_smart__get_push_stream(t, &packbuilder_payload.stream)) < 0 ||
+ (error = gen_pktline(&pktline, push)) < 0 ||
+ (error = packbuilder_payload.stream->write(packbuilder_payload.stream, git_buf_cstr(&pktline), git_buf_len(&pktline))) < 0)
+ goto done;
- if (need_pack && git_packbuilder_foreach(push->pb, &stream_thunk, s) < 0)
- goto on_error;
+ if (need_pack &&
+ (error = git_packbuilder_foreach(push->pb, &stream_thunk, &packbuilder_payload)) < 0)
+ goto done;
/* If we sent nothing or the server doesn't support report-status, then
* we consider the pack to have been unpacked successfully */
if (!push->specs.length || !push->report_status)
push->unpack_ok = 1;
- else if (parse_report(&t->buffer, push) < 0)
- goto on_error;
+ else if ((error = parse_report(&t->buffer, push)) < 0)
+ goto done;
- if (push->status.length &&
- update_refs_from_report(&t->refs, &push->specs, &push->status) < 0)
- goto on_error;
+ /* If progress is being reported write the final report */
+ if (push->transfer_progress_cb) {
+ push->transfer_progress_cb(push->pb->nr_written, push->pb->nr_objects, packbuilder_payload.last_bytes, push->transfer_progress_cb_payload);
+ }
- error = 0;
+ if (push->status.length) {
+ error = update_refs_from_report(&t->refs, &push->specs, &push->status);
+ if (error < 0)
+ goto done;
-on_error:
- git_buf_free(&pktline);
+ error = git_smart__update_heads(t);
+ }
+done:
+ git_buf_free(&pktline);
return error;
}
diff --git a/src/transports/ssh.c b/src/transports/ssh.c
index a312c8d08..4a905e3c9 100644
--- a/src/transports/ssh.c
+++ b/src/transports/ssh.c
@@ -5,19 +5,18 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
-#ifdef GIT_SSH
-
#include "git2.h"
#include "buffer.h"
#include "netops.h"
#include "smart.h"
+#ifdef GIT_SSH
+
#include <libssh2.h>
#define OWNING_SUBTRANSPORT(s) ((ssh_subtransport *)(s)->parent.subtransport)
static const char prefix_ssh[] = "ssh://";
-static const char default_user[] = "git";
static const char cmd_uploadpack[] = "git-upload-pack";
static const char cmd_receivepack[] = "git-receive-pack";
@@ -38,6 +37,14 @@ typedef struct {
git_cred *cred;
} ssh_subtransport;
+static void ssh_error(LIBSSH2_SESSION *session, const char *errmsg)
+{
+ char *ssherr;
+ libssh2_session_last_error(session, &ssherr, NULL, 0);
+
+ giterr_set(GITERR_SSH, "%s: %s", errmsg, ssherr);
+}
+
/*
* Create a git protocol request.
*
@@ -46,27 +53,29 @@ typedef struct {
static int gen_proto(git_buf *request, const char *cmd, const char *url)
{
char *repo;
-
+
if (!git__prefixcmp(url, prefix_ssh)) {
url = url + strlen(prefix_ssh);
repo = strchr(url, '/');
} else {
repo = strchr(url, ':');
+ if (repo) repo++;
}
-
+
if (!repo) {
+ giterr_set(GITERR_NET, "Malformed git protocol URL");
return -1;
}
-
+
int 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');
-
+
if (git_buf_oom(request))
return -1;
-
+
return 0;
}
@@ -74,21 +83,19 @@ static int send_command(ssh_stream *s)
{
int error;
git_buf request = GIT_BUF_INIT;
-
+
error = gen_proto(&request, s->cmd, s->url);
if (error < 0)
goto cleanup;
-
- error = libssh2_channel_exec(
- s->channel,
- request.ptr
- );
- if (0 != error)
+ error = libssh2_channel_exec(s->channel, request.ptr);
+ if (error < LIBSSH2_ERROR_NONE) {
+ ssh_error(s->session, "SSH could not execute request");
goto cleanup;
-
+ }
+
s->sent_command = 1;
-
+
cleanup:
git_buf_free(&request);
return error;
@@ -100,19 +107,21 @@ static int ssh_stream_read(
size_t buf_size,
size_t *bytes_read)
{
+ int rc;
ssh_stream *s = (ssh_stream *)stream;
-
+
*bytes_read = 0;
-
+
if (!s->sent_command && send_command(s) < 0)
return -1;
-
- int rc = libssh2_channel_read(s->channel, buffer, buf_size);
- if (rc < 0)
+
+ if ((rc = libssh2_channel_read(s->channel, buffer, buf_size)) < LIBSSH2_ERROR_NONE) {
+ ssh_error(s->session, "SSH could not read data");;
return -1;
-
+ }
+
*bytes_read = rc;
-
+
return 0;
}
@@ -122,16 +131,16 @@ static int ssh_stream_write(
size_t len)
{
ssh_stream *s = (ssh_stream *)stream;
-
+
if (!s->sent_command && send_command(s) < 0)
return -1;
-
- int rc = libssh2_channel_write(s->channel, buffer, len);
- if (rc < 0) {
+
+ if (libssh2_channel_write(s->channel, buffer, len) < LIBSSH2_ERROR_NONE) {
+ ssh_error(s->session, "SSH could not write data");
return -1;
}
-
- return rc;
+
+ return 0;
}
static void ssh_stream_free(git_smart_subtransport_stream *stream)
@@ -139,26 +148,27 @@ static void ssh_stream_free(git_smart_subtransport_stream *stream)
ssh_stream *s = (ssh_stream *)stream;
ssh_subtransport *t = OWNING_SUBTRANSPORT(s);
int ret;
-
+
GIT_UNUSED(ret);
-
+
t->current_stream = NULL;
-
+
if (s->channel) {
libssh2_channel_close(s->channel);
- libssh2_channel_free(s->channel);
- s->channel = NULL;
+ libssh2_channel_free(s->channel);
+ s->channel = NULL;
}
-
+
if (s->session) {
- libssh2_session_free(s->session), s->session = NULL;
+ libssh2_session_free(s->session);
+ s->session = NULL;
}
-
+
if (s->socket.socket) {
- ret = gitno_close(&s->socket);
- assert(!ret);
+ (void)gitno_close(&s->socket);
+ /* can't do anything here with error return value */
}
-
+
git__free(s->url);
git__free(s);
}
@@ -170,26 +180,25 @@ static int ssh_stream_alloc(
git_smart_subtransport_stream **stream)
{
ssh_stream *s;
-
- if (!stream)
- return -1;
-
+
+ assert(stream);
+
s = git__calloc(sizeof(ssh_stream), 1);
GITERR_CHECK_ALLOC(s);
-
+
s->parent.subtransport = &t->parent;
s->parent.read = ssh_stream_read;
s->parent.write = ssh_stream_write;
s->parent.free = ssh_stream_free;
-
+
s->cmd = cmd;
+
s->url = git__strdup(url);
-
if (!s->url) {
git__free(s);
return -1;
}
-
+
*stream = &s->parent;
return 0;
}
@@ -201,136 +210,127 @@ static int git_ssh_extract_url_parts(
{
char *colon, *at;
const char *start;
-
- colon = strchr(url, ':');
-
- if (colon == NULL) {
- giterr_set(GITERR_NET, "Malformed URL: missing :");
- return -1;
- }
-
+
+ colon = strchr(url, ':');
+
+
at = strchr(url, '@');
if (at) {
- start = at+1;
+ start = at + 1;
*username = git__substrdup(url, at - url);
+ GITERR_CHECK_ALLOC(*username);
} else {
start = url;
- *username = git__strdup(default_user);
+ *username = NULL;
+ }
+
+ if (colon == NULL || (colon < start)) {
+ giterr_set(GITERR_NET, "Malformed URL");
+ return -1;
}
-
+
*host = git__substrdup(start, colon - start);
-
+ GITERR_CHECK_ALLOC(*host);
+
return 0;
}
static int _git_ssh_authenticate_session(
LIBSSH2_SESSION* session,
const char *user,
- git_cred* cred
-)
+ git_cred* cred)
{
int rc;
+
do {
switch (cred->credtype) {
- case GIT_CREDTYPE_USERPASS_PLAINTEXT: {
- git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
- rc = libssh2_userauth_password(
- session,
- c->username,
- c->password
- );
- break;
- }
- case GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE: {
- git_cred_ssh_keyfile_passphrase *c = (git_cred_ssh_keyfile_passphrase *)cred;
- rc = libssh2_userauth_publickey_fromfile(
- session,
- user,
- c->publickey,
- c->privatekey,
- c->passphrase
- );
- break;
- }
- case GIT_CREDTYPE_SSH_PUBLICKEY: {
- git_cred_ssh_publickey *c = (git_cred_ssh_publickey *)cred;
- rc = libssh2_userauth_publickey(
- session,
- user,
- (const unsigned char *)c->publickey,
- c->publickey_len,
- c->sign_callback,
- &c->sign_data
- );
- break;
- }
- default:
- rc = LIBSSH2_ERROR_AUTHENTICATION_FAILED;
+ case GIT_CREDTYPE_USERPASS_PLAINTEXT: {
+ git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
+ user = c->username ? c->username : user;
+ rc = libssh2_userauth_password(session, user, c->password);
+ break;
+ }
+ case GIT_CREDTYPE_SSH_KEY: {
+ git_cred_ssh_key *c = (git_cred_ssh_key *)cred;
+ user = c->username ? c->username : user;
+ rc = libssh2_userauth_publickey_fromfile(
+ session, c->username, c->publickey, c->privatekey, c->passphrase);
+ break;
+ }
+ case GIT_CREDTYPE_SSH_CUSTOM: {
+ git_cred_ssh_custom *c = (git_cred_ssh_custom *)cred;
+
+ user = c->username ? c->username : user;
+ rc = libssh2_userauth_publickey(
+ session, c->username, (const unsigned char *)c->publickey,
+ c->publickey_len, c->sign_callback, &c->sign_data);
+ break;
+ }
+ default:
+ rc = LIBSSH2_ERROR_AUTHENTICATION_FAILED;
}
- } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
-
- return rc;
+ } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
+
+ if (rc != LIBSSH2_ERROR_NONE) {
+ ssh_error(session, "Failed to authenticate SSH session");
+ return -1;
+ }
+
+ return 0;
}
-static int _git_ssh_session_create
-(
+static int _git_ssh_session_create(
LIBSSH2_SESSION** session,
- gitno_socket socket
-)
+ gitno_socket socket)
{
- if (!session) {
+ int rc = 0;
+ LIBSSH2_SESSION* s;
+
+ assert(session);
+
+ s = libssh2_session_init();
+ if (!s) {
+ giterr_set(GITERR_NET, "Failed to initialize SSH session");
+ return -1;
+ }
+
+ do {
+ rc = libssh2_session_startup(s, socket.socket);
+ } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
+
+ if (rc != LIBSSH2_ERROR_NONE) {
+ ssh_error(s, "Failed to start SSH session");
+ libssh2_session_free(s);
return -1;
}
-
- LIBSSH2_SESSION* s = libssh2_session_init();
- if (!s)
- return -1;
-
- int rc = 0;
- do {
- rc = libssh2_session_startup(s, socket.socket);
- } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
-
- if (0 != rc) {
- goto on_error;
- }
-
+
libssh2_session_set_blocking(s, 1);
-
+
*session = s;
-
+
return 0;
-
-on_error:
- if (s) {
- libssh2_session_free(s), s = NULL;
- }
-
- return -1;
}
static int _git_ssh_setup_conn(
ssh_subtransport *t,
const char *url,
const char *cmd,
- git_smart_subtransport_stream **stream
-)
+ git_smart_subtransport_stream **stream)
{
- char *host, *port=NULL, *user=NULL, *pass=NULL;
+ char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL;
const char *default_port="22";
ssh_stream *s;
LIBSSH2_SESSION* session=NULL;
LIBSSH2_CHANNEL* channel=NULL;
-
+
*stream = NULL;
if (ssh_stream_alloc(t, url, cmd, stream) < 0)
return -1;
-
+
s = (ssh_stream *)*stream;
-
+
if (!git__prefixcmp(url, prefix_ssh)) {
- url = url + strlen(prefix_ssh);
- if (gitno_extract_url_parts(&host, &port, &user, &pass, url, default_port) < 0)
+ if (gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, default_port) < 0)
goto on_error;
} else {
if (git_ssh_extract_url_parts(&host, &user, url) < 0)
@@ -338,61 +338,78 @@ static int _git_ssh_setup_conn(
port = git__strdup(default_port);
GITERR_CHECK_ALLOC(port);
}
-
+
if (gitno_connect(&s->socket, host, port, 0) < 0)
goto on_error;
-
+
if (user && pass) {
if (git_cred_userpass_plaintext_new(&t->cred, user, pass) < 0)
goto on_error;
- } else {
- if (t->owner->cred_acquire_cb(&t->cred,
- t->owner->url,
- user,
- GIT_CREDTYPE_USERPASS_PLAINTEXT | GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE,
+ } else if (t->owner->cred_acquire_cb) {
+ if (t->owner->cred_acquire_cb(
+ &t->cred, t->owner->url, user,
+ GIT_CREDTYPE_USERPASS_PLAINTEXT |
+ GIT_CREDTYPE_SSH_KEY |
+ GIT_CREDTYPE_SSH_CUSTOM,
t->owner->cred_acquire_payload) < 0)
- return -1;
+ goto on_error;
+
+ if (!t->cred) {
+ giterr_set(GITERR_SSH, "Callback failed to initialize SSH credentials");
+ goto on_error;
+ }
+ } else {
+ giterr_set(GITERR_SSH, "Cannot set up SSH connection without credentials");
+ goto on_error;
}
assert(t->cred);
-
- if (!user) {
- user = git__strdup(default_user);
+
+ if (!user && !git_cred_has_username(t->cred)) {
+ giterr_set_str(GITERR_NET, "Cannot authenticate without a username");
+ goto on_error;
}
-
+
if (_git_ssh_session_create(&session, s->socket) < 0)
goto on_error;
-
- if (_git_ssh_authenticate_session(session, user, t->cred) < 0)
+
+ if (_git_ssh_authenticate_session(session, user, t->cred) < 0)
goto on_error;
-
+
channel = libssh2_channel_open_session(session);
- if (!channel)
- goto on_error;
-
+ if (!channel) {
+ ssh_error(session, "Failed to open SSH channel");
+ goto on_error;
+ }
+
libssh2_channel_set_blocking(channel, 1);
-
+
s->session = session;
s->channel = channel;
-
+
t->current_stream = s;
git__free(host);
git__free(port);
+ git__free(path);
git__free(user);
git__free(pass);
return 0;
-
+
on_error:
+ s->session = NULL;
+ s->channel = NULL;
+ t->current_stream = NULL;
+
if (*stream)
ssh_stream_free(*stream);
-
+
git__free(host);
git__free(port);
git__free(user);
git__free(pass);
if (session)
- libssh2_session_free(session), session = NULL;
+ libssh2_session_free(session);
return -1;
}
@@ -404,7 +421,7 @@ static int ssh_uploadpack_ls(
{
if (_git_ssh_setup_conn(t, url, cmd_uploadpack, stream) < 0)
return -1;
-
+
return 0;
}
@@ -414,12 +431,12 @@ static int ssh_uploadpack(
git_smart_subtransport_stream **stream)
{
GIT_UNUSED(url);
-
+
if (t->current_stream) {
*stream = &t->current_stream->parent;
return 0;
}
-
+
giterr_set(GITERR_NET, "Must call UPLOADPACK_LS before UPLOADPACK");
return -1;
}
@@ -431,7 +448,7 @@ static int ssh_receivepack_ls(
{
if (_git_ssh_setup_conn(t, url, cmd_receivepack, stream) < 0)
return -1;
-
+
return 0;
}
@@ -441,12 +458,12 @@ static int ssh_receivepack(
git_smart_subtransport_stream **stream)
{
GIT_UNUSED(url);
-
+
if (t->current_stream) {
*stream = &t->current_stream->parent;
return 0;
}
-
+
giterr_set(GITERR_NET, "Must call RECEIVEPACK_LS before RECEIVEPACK");
return -1;
}
@@ -458,21 +475,21 @@ static int _ssh_action(
git_smart_service_t action)
{
ssh_subtransport *t = (ssh_subtransport *) subtransport;
-
+
switch (action) {
case GIT_SERVICE_UPLOADPACK_LS:
return ssh_uploadpack_ls(t, url, stream);
-
+
case GIT_SERVICE_UPLOADPACK:
return ssh_uploadpack(t, url, stream);
-
+
case GIT_SERVICE_RECEIVEPACK_LS:
return ssh_receivepack_ls(t, url, stream);
-
+
case GIT_SERVICE_RECEIVEPACK:
return ssh_receivepack(t, url, stream);
}
-
+
*stream = NULL;
return -1;
}
@@ -480,40 +497,49 @@ static int _ssh_action(
static int _ssh_close(git_smart_subtransport *subtransport)
{
ssh_subtransport *t = (ssh_subtransport *) subtransport;
-
+
assert(!t->current_stream);
-
+
GIT_UNUSED(t);
-
+
return 0;
}
static void _ssh_free(git_smart_subtransport *subtransport)
{
ssh_subtransport *t = (ssh_subtransport *) subtransport;
-
+
assert(!t->current_stream);
-
+
git__free(t);
}
+#endif
-int git_smart_subtransport_ssh(git_smart_subtransport **out, git_transport *owner)
+int git_smart_subtransport_ssh(
+ git_smart_subtransport **out, git_transport *owner)
{
+#ifdef GIT_SSH
ssh_subtransport *t;
-
- if (!out)
- return -1;
-
+
+ assert(out);
+
t = git__calloc(sizeof(ssh_subtransport), 1);
GITERR_CHECK_ALLOC(t);
-
+
t->owner = (transport_smart *)owner;
t->parent.action = _ssh_action;
t->parent.close = _ssh_close;
t->parent.free = _ssh_free;
-
+
*out = (git_smart_subtransport *) t;
return 0;
-}
+#else
+ GIT_UNUSED(owner);
+
+ assert(out);
+ *out = NULL;
+ giterr_set(GITERR_INVALID, "Cannot create SSH transport. Library was built without SSH support");
+ return -1;
#endif
+}
diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c
index 95e422dc0..673cd0faf 100644
--- a/src/transports/winhttp.c
+++ b/src/transports/winhttp.c
@@ -52,6 +52,7 @@ static const int no_check_cert_flags = SECURITY_FLAG_IGNORE_CERT_CN_INVALID |
typedef enum {
GIT_WINHTTP_AUTH_BASIC = 1,
+ GIT_WINHTTP_AUTH_NEGOTIATE = 2,
} winhttp_authmechanism_t;
typedef struct {
@@ -73,17 +74,12 @@ typedef struct {
typedef struct {
git_smart_subtransport parent;
transport_smart *owner;
- const char *path;
- char *host;
- char *port;
- char *user_from_url;
- char *pass_from_url;
+ gitno_connection_data connection_data;
git_cred *cred;
git_cred *url_cred;
int auth_mechanism;
HINTERNET session;
HINTERNET connection;
- unsigned use_ssl : 1;
} winhttp_subtransport;
static int apply_basic_credential(HINTERNET request, git_cred *cred)
@@ -143,6 +139,22 @@ on_error:
return error;
}
+static int apply_default_credentials(HINTERNET request)
+{
+ /* If we are explicitly asked to deliver default credentials, turn set
+ * the security level to low which will guarantee they are delivered.
+ * The default 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;
+
+ if (!WinHttpSetOption(request, WINHTTP_OPTION_AUTOLOGON_POLICY, &data, sizeof(DWORD)))
+ return -1;
+
+ return 0;
+}
+
static int winhttp_stream_connect(winhttp_stream *s)
{
winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
@@ -152,9 +164,10 @@ static int winhttp_stream_connect(winhttp_stream *s)
wchar_t *types[] = { L"*/*", NULL };
BOOL peerdist = FALSE;
int error = -1, wide_len;
+ unsigned long disable_redirects = WINHTTP_DISABLE_REDIRECTS;
/* Prepare URL */
- git_buf_printf(&buf, "%s%s", t->path, s->service_url);
+ git_buf_printf(&buf, "%s%s", t->connection_data.path, s->service_url);
if (git_buf_oom(&buf))
return -1;
@@ -187,7 +200,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
NULL,
WINHTTP_NO_REFERER,
types,
- t->use_ssl ? WINHTTP_FLAG_SECURE : 0);
+ t->connection_data.use_ssl ? WINHTTP_FLAG_SECURE : 0);
if (!s->request) {
giterr_set(GITERR_OS, "Failed to open request");
@@ -195,7 +208,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
}
/* Set proxy if necessary */
- if (git_remote__get_http_proxy(t->owner->owner, t->use_ssl, &proxy_url) < 0)
+ if (git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &proxy_url) < 0)
goto on_error;
if (proxy_url) {
@@ -244,6 +257,17 @@ static int winhttp_stream_connect(winhttp_stream *s)
git__free(proxy_wide);
}
+ /* Disable WinHTTP redirects so we can handle them manually. Why, you ask?
+ * http://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/b2ff8879-ab9f-4218-8f09-16d25dff87ae
+ */
+ if (!WinHttpSetOption(s->request,
+ WINHTTP_OPTION_DISABLE_FEATURE,
+ &disable_redirects,
+ sizeof(disable_redirects))) {
+ giterr_set(GITERR_OS, "Failed to disable redirects");
+ goto on_error;
+ }
+
/* Strip unwanted headers (X-P2P-PeerDist, X-P2P-PeerDistEx) that WinHTTP
* adds itself. This option may not be supported by the underlying
* platform, so we do not error-check it */
@@ -258,22 +282,39 @@ static int winhttp_stream_connect(winhttp_stream *s)
goto on_error;
}
- /* Send Content-Type header -- only necessary on a POST */
if (post_verb == s->verb) {
+ /* Send Content-Type and Accept headers -- only necessary on a POST */
git_buf_clear(&buf);
- if (git_buf_printf(&buf, "Content-Type: application/x-git-%s-request", s->service) < 0)
+ if (git_buf_printf(&buf,
+ "Content-Type: application/x-git-%s-request",
+ s->service) < 0)
goto on_error;
git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf));
- if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG) -1L, WINHTTP_ADDREQ_FLAG_ADD)) {
+ if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L,
+ WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) {
+ giterr_set(GITERR_OS, "Failed to add a header to the request");
+ goto on_error;
+ }
+
+ git_buf_clear(&buf);
+ if (git_buf_printf(&buf,
+ "Accept: application/x-git-%s-result",
+ s->service) < 0)
+ goto on_error;
+
+ git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf));
+
+ if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L,
+ WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) {
giterr_set(GITERR_OS, "Failed to add a header to the request");
goto on_error;
}
}
/* If requested, disable certificate validation */
- if (t->use_ssl) {
+ if (t->connection_data.use_ssl) {
int flags;
if (t->owner->parent.read_flags(&t->owner->parent, &flags) < 0)
@@ -293,12 +334,17 @@ static int winhttp_stream_connect(winhttp_stream *s)
t->auth_mechanism == GIT_WINHTTP_AUTH_BASIC &&
apply_basic_credential(s->request, t->cred) < 0)
goto on_error;
+ else if (t->cred &&
+ t->cred->credtype == GIT_CREDTYPE_DEFAULT &&
+ t->auth_mechanism == GIT_WINHTTP_AUTH_NEGOTIATE &&
+ apply_default_credentials(s->request) < 0)
+ goto on_error;
/* If no other credentials have been applied and the URL has username and
* password, use those */
- if (!t->cred && t->user_from_url && t->pass_from_url) {
+ if (!t->cred && t->connection_data.user && t->connection_data.pass) {
if (!t->url_cred &&
- git_cred_userpass_plaintext_new(&t->url_cred, t->user_from_url, t->pass_from_url) < 0)
+ git_cred_userpass_plaintext_new(&t->url_cred, t->connection_data.user, t->connection_data.pass) < 0)
goto on_error;
if (apply_basic_credential(s->request, t->url_cred) < 0)
goto on_error;
@@ -337,6 +383,12 @@ static int parse_unauthorized_response(
*auth_mechanism = GIT_WINHTTP_AUTH_BASIC;
}
+ if ((WINHTTP_AUTH_SCHEME_NTLM & supported) ||
+ (WINHTTP_AUTH_SCHEME_NEGOTIATE & supported)) {
+ *allowed_types |= GIT_CREDTYPE_DEFAULT;
+ *auth_mechanism = GIT_WINHTTP_AUTH_NEGOTIATE;
+ }
+
return 0;
}
@@ -380,6 +432,50 @@ static int write_chunk(HINTERNET request, const char *buffer, size_t len)
return 0;
}
+static int winhttp_connect(
+ winhttp_subtransport *t,
+ const char *url)
+{
+ wchar_t *ua = L"git/1.0 (libgit2 " WIDEN(LIBGIT2_VERSION) L")";
+ git_win32_path host;
+ int32_t port;
+ const char *default_port = "80";
+
+ /* Prepare port */
+ if (git__strtol32(&port, t->connection_data.port, NULL, 10) < 0)
+ return -1;
+
+ /* Prepare host */
+ git_win32_path_from_c(host, t->connection_data.host);
+
+ /* Establish session */
+ t->session = WinHttpOpen(
+ ua,
+ WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
+ WINHTTP_NO_PROXY_NAME,
+ WINHTTP_NO_PROXY_BYPASS,
+ 0);
+
+ if (!t->session) {
+ giterr_set(GITERR_OS, "Failed to init WinHTTP");
+ return -1;
+ }
+
+ /* Establish connection */
+ t->connection = WinHttpConnect(
+ t->session,
+ host,
+ (INTERNET_PORT) port,
+ 0);
+
+ if (!t->connection) {
+ giterr_set(GITERR_OS, "Failed to connect to host");
+ return -1;
+ }
+
+ return 0;
+}
+
static int winhttp_stream_read(
git_smart_subtransport_stream *stream,
char *buffer,
@@ -511,50 +607,53 @@ replay:
/* Check for Windows 7. This workaround is only necessary on
* Windows Vista and earlier. Windows 7 is version 6.1. */
- if (!git_has_win32_version(6, 1)) {
- wchar_t *location;
- DWORD location_length;
- int redirect_cmp;
-
- /* OK, fetch the Location header from the redirect. */
- if (WinHttpQueryHeaders(s->request,
- WINHTTP_QUERY_LOCATION,
- WINHTTP_HEADER_NAME_BY_INDEX,
- WINHTTP_NO_OUTPUT_BUFFER,
- &location_length,
- WINHTTP_NO_HEADER_INDEX) ||
- GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
- giterr_set(GITERR_OS, "Failed to read Location header");
- return -1;
- }
-
- location = git__malloc(location_length);
- GITERR_CHECK_ALLOC(location);
-
- if (!WinHttpQueryHeaders(s->request,
- WINHTTP_QUERY_LOCATION,
- WINHTTP_HEADER_NAME_BY_INDEX,
- location,
- &location_length,
- WINHTTP_NO_HEADER_INDEX)) {
- giterr_set(GITERR_OS, "Failed to read Location header");
- git__free(location);
- return -1;
- }
+ wchar_t *location;
+ DWORD location_length;
+ char *location8;
+
+ /* OK, fetch the Location header from the redirect. */
+ if (WinHttpQueryHeaders(s->request,
+ WINHTTP_QUERY_LOCATION,
+ WINHTTP_HEADER_NAME_BY_INDEX,
+ WINHTTP_NO_OUTPUT_BUFFER,
+ &location_length,
+ WINHTTP_NO_HEADER_INDEX) ||
+ GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ giterr_set(GITERR_OS, "Failed to read Location header");
+ return -1;
+ }
- /* Compare the Location header with the request URI */
- redirect_cmp = wcscmp(location, s->request_uri);
+ location = git__malloc(location_length);
+ location8 = git__malloc(location_length);
+ GITERR_CHECK_ALLOC(location);
+
+ if (!WinHttpQueryHeaders(s->request,
+ WINHTTP_QUERY_LOCATION,
+ WINHTTP_HEADER_NAME_BY_INDEX,
+ location,
+ &location_length,
+ WINHTTP_NO_HEADER_INDEX)) {
+ giterr_set(GITERR_OS, "Failed to read Location header");
git__free(location);
+ return -1;
+ }
+ git__utf16_to_8(location8, location_length, location);
+ git__free(location);
- if (!redirect_cmp) {
- /* Replay the request */
- WinHttpCloseHandle(s->request);
- s->request = NULL;
- s->sent_request = 0;
+ /* Replay the request */
+ WinHttpCloseHandle(s->request);
+ s->request = NULL;
+ s->sent_request = 0;
- goto replay;
- }
+ if (!git__prefixcmp_icase(location8, prefix_https)) {
+ /* Upgrade to secure connection; disconnect and start over */
+ if (gitno_connection_data_from_url(&t->connection_data, location8, s->service_url) < 0)
+ return -1;
+ winhttp_connect(t, location8);
}
+
+ git__free(location8);
+ goto replay;
}
/* Handle authentication failures */
@@ -568,8 +667,9 @@ replay:
if (allowed_types &&
(!t->cred || 0 == (t->cred->credtype & allowed_types))) {
- if (t->owner->cred_acquire_cb(&t->cred, t->owner->url, t->user_from_url, allowed_types, t->owner->cred_acquire_payload) < 0)
- return -1;
+ if (t->owner->cred_acquire_cb(&t->cred, t->owner->url, t->connection_data.user, allowed_types,
+ t->owner->cred_acquire_payload) < 0)
+ return GIT_EUSER;
assert(t->cred);
@@ -888,68 +988,6 @@ static int winhttp_stream_alloc(winhttp_subtransport *t, winhttp_stream **stream
return 0;
}
-static int winhttp_connect(
- winhttp_subtransport *t,
- const char *url)
-{
- wchar_t *ua = L"git/1.0 (libgit2 " WIDEN(LIBGIT2_VERSION) L")";
- wchar_t host[GIT_WIN_PATH];
- int32_t port;
- const char *default_port = "80";
- int ret;
-
- if (!git__prefixcmp(url, prefix_http)) {
- url = url + strlen(prefix_http);
- default_port = "80";
- }
-
- if (!git__prefixcmp(url, prefix_https)) {
- url += strlen(prefix_https);
- default_port = "443";
- t->use_ssl = 1;
- }
-
- if ((ret = gitno_extract_url_parts(&t->host, &t->port, &t->user_from_url,
- &t->pass_from_url, url, default_port)) < 0)
- return ret;
-
- t->path = strchr(url, '/');
-
- /* Prepare port */
- if (git__strtol32(&port, t->port, NULL, 10) < 0)
- return -1;
-
- /* Prepare host */
- git__utf8_to_16(host, GIT_WIN_PATH, t->host);
-
- /* Establish session */
- t->session = WinHttpOpen(
- ua,
- WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
- WINHTTP_NO_PROXY_NAME,
- WINHTTP_NO_PROXY_BYPASS,
- 0);
-
- if (!t->session) {
- giterr_set(GITERR_OS, "Failed to init WinHTTP");
- return -1;
- }
-
- /* Establish connection */
- t->connection = WinHttpConnect(
- t->session,
- host,
- port,
- 0);
-
- if (!t->connection) {
- giterr_set(GITERR_OS, "Failed to connect to host");
- return -1;
- }
-
- return 0;
-}
-
static int winhttp_uploadpack_ls(
winhttp_subtransport *t,
winhttp_stream *s)
@@ -989,7 +1027,7 @@ static int winhttp_receivepack(
{
/* WinHTTP only supports Transfer-Encoding: chunked
* on Windows Vista (NT 6.0) and higher. */
- s->chunked = git_has_win32_version(6, 0);
+ s->chunked = git_has_win32_version(6, 0, 0);
if (s->chunked)
s->parent.write = winhttp_stream_write_chunked;
@@ -1013,9 +1051,10 @@ static int winhttp_action(
winhttp_stream *s;
int ret = -1;
- if (!t->connection &&
- winhttp_connect(t, url) < 0)
- return -1;
+ if (!t->connection)
+ if (gitno_connection_data_from_url(&t->connection_data, url, NULL) < 0 ||
+ winhttp_connect(t, url) < 0)
+ return -1;
if (winhttp_stream_alloc(t, &s) < 0)
return -1;
@@ -1056,25 +1095,7 @@ static int winhttp_close(git_smart_subtransport *subtransport)
winhttp_subtransport *t = (winhttp_subtransport *)subtransport;
int ret = 0;
- if (t->host) {
- git__free(t->host);
- t->host = NULL;
- }
-
- if (t->port) {
- git__free(t->port);
- t->port = NULL;
- }
-
- if (t->user_from_url) {
- git__free(t->user_from_url);
- t->user_from_url = NULL;
- }
-
- if (t->pass_from_url) {
- git__free(t->pass_from_url);
- t->pass_from_url = NULL;
- }
+ gitno_connection_data_free_ptrs(&t->connection_data);
if (t->cred) {
t->cred->free(t->cred);
diff --git a/src/tree-cache.c b/src/tree-cache.c
index 97ffc2acf..1d3997154 100644
--- a/src/tree-cache.c
+++ b/src/tree-cache.c
@@ -138,9 +138,11 @@ static int read_tree_internal(git_tree_cache **out,
tree->children = git__malloc(tree->children_count * sizeof(git_tree_cache *));
GITERR_CHECK_ALLOC(tree->children);
+ memset(tree->children, 0x0, tree->children_count * sizeof(git_tree_cache *));
+
for (i = 0; i < tree->children_count; ++i) {
if (read_tree_internal(&tree->children[i], &buffer, buffer_end, tree) < 0)
- return -1;
+ goto corrupted;
}
}
@@ -150,7 +152,7 @@ static int read_tree_internal(git_tree_cache **out,
corrupted:
git_tree_cache_free(tree);
- giterr_set(GITERR_INDEX, "Corruped TREE extension in index");
+ giterr_set(GITERR_INDEX, "Corrupted TREE extension in index");
return -1;
}
@@ -162,7 +164,7 @@ int git_tree_cache_read(git_tree_cache **tree, const char *buffer, size_t buffer
return -1;
if (buffer < buffer_end) {
- giterr_set(GITERR_INDEX, "Corruped TREE extension in index (unexpected trailing data)");
+ giterr_set(GITERR_INDEX, "Corrupted TREE extension in index (unexpected trailing data)");
return -1;
}
@@ -176,9 +178,12 @@ void git_tree_cache_free(git_tree_cache *tree)
if (tree == NULL)
return;
- for (i = 0; i < tree->children_count; ++i)
- git_tree_cache_free(tree->children[i]);
+ if (tree->children != NULL) {
+ for (i = 0; i < tree->children_count; ++i)
+ git_tree_cache_free(tree->children[i]);
+
+ git__free(tree->children);
+ }
- git__free(tree->children);
git__free(tree);
}
diff --git a/src/tree.c b/src/tree.c
index 65d01b4d5..bb59ff82b 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -10,7 +10,7 @@
#include "tree.h"
#include "git2/repository.h"
#include "git2/object.h"
-#include "path.h"
+#include "fileops.h"
#include "tree-cache.h"
#include "index.h"
@@ -29,19 +29,19 @@ static bool valid_filemode(const int filemode)
GIT_INLINE(git_filemode_t) normalize_filemode(git_filemode_t filemode)
{
/* Tree bits set, but it's not a commit */
- if (filemode & GIT_FILEMODE_TREE && !(filemode & 0100000))
+ if (GIT_MODE_TYPE(filemode) == GIT_FILEMODE_TREE)
return GIT_FILEMODE_TREE;
- /* If any of the x bits is set */
- if (filemode & 0111)
+ /* If any of the x bits are set */
+ if (GIT_PERMS_IS_EXEC(filemode))
return GIT_FILEMODE_BLOB_EXECUTABLE;
/* 16XXXX means commit */
- if ((filemode & GIT_FILEMODE_COMMIT) == GIT_FILEMODE_COMMIT)
+ if (GIT_MODE_TYPE(filemode) == GIT_FILEMODE_COMMIT)
return GIT_FILEMODE_COMMIT;
/* 12XXXX means commit */
- if ((filemode & GIT_FILEMODE_LINK) == GIT_FILEMODE_LINK)
+ if (GIT_MODE_TYPE(filemode) == GIT_FILEMODE_LINK)
return GIT_FILEMODE_LINK;
/* Otherwise, return a blob */
@@ -237,7 +237,12 @@ void git_tree__free(void *_tree)
git_filemode_t git_tree_entry_filemode(const git_tree_entry *entry)
{
- return (git_filemode_t)entry->attr;
+ return normalize_filemode(entry->attr);
+}
+
+git_filemode_t git_tree_entry_filemode_raw(const git_tree_entry *entry)
+{
+ return entry->attr;
}
const char *git_tree_entry_name(const git_tree_entry *entry)
@@ -386,8 +391,6 @@ int git_tree__parse(void *_tree, git_odb_object *odb_obj)
if (git__strtol32(&attr, buffer, &buffer, 8) < 0 || !buffer)
return tree_error("Failed to parse tree. Can't parse filemode", NULL);
- attr = normalize_filemode(attr); /* make sure to normalize the filemode */
-
if (*buffer++ != ' ')
return tree_error("Failed to parse tree. Object is corrupted", NULL);
@@ -881,8 +884,10 @@ static int tree_walk(
git_vector_foreach(&tree->entries, i, entry) {
if (preorder) {
error = callback(path->ptr, entry, payload);
- if (error > 0)
+ if (error > 0) {
+ error = 0;
continue;
+ }
if (error < 0) {
giterr_clear();
return GIT_EUSER;
@@ -905,11 +910,12 @@ static int tree_walk(
return -1;
error = tree_walk(subtree, callback, path, payload, preorder);
+ git_tree_free(subtree);
+
if (error != 0)
break;
git_buf_truncate(path, path_len);
- git_tree_free(subtree);
}
if (!preorder && callback(path->ptr, entry, payload) < 0) {
diff --git a/src/util.c b/src/util.c
index c543a3d21..47516a8f7 100644
--- a/src/util.c
+++ b/src/util.c
@@ -33,6 +33,9 @@ int git_libgit2_capabilities()
#if defined(GIT_SSL) || defined(GIT_WINHTTP)
| GIT_CAP_HTTPS
#endif
+#if defined(GIT_SSH)
+ | GIT_CAP_SSH
+#endif
;
}
@@ -114,6 +117,19 @@ int git_libgit2_opts(int key, ...)
*(va_arg(ap, ssize_t *)) = git_cache__current_storage.val;
*(va_arg(ap, ssize_t *)) = git_cache__max_storage;
break;
+
+ case GIT_OPT_GET_TEMPLATE_PATH:
+ {
+ char *out = va_arg(ap, char *);
+ size_t outlen = va_arg(ap, size_t);
+
+ error = git_futils_dirs_get_str(out, outlen, GIT_FUTILS_DIR_TEMPLATE);
+ }
+ break;
+
+ case GIT_OPT_SET_TEMPLATE_PATH:
+ error = git_futils_dirs_set(GIT_FUTILS_DIR_TEMPLATE, va_arg(ap, const char *));
+ break;
}
va_end(ap);
@@ -676,6 +692,9 @@ size_t git__unescape(char *str)
{
char *scan, *pos = str;
+ if (!str)
+ return 0;
+
for (scan = str; *scan; pos++, scan++) {
if (*scan == '\\' && *(scan + 1) != '\0')
scan++; /* skip '\' but include next char */
@@ -707,8 +726,9 @@ static int GIT_STDLIB_CALL git__qsort_r_glue_cmp(
void git__qsort_r(
void *els, size_t nel, size_t elsize, git__sort_r_cmp cmp, void *payload)
{
-#if defined(__MINGW32__) || defined(__OpenBSD__) || defined(AMIGA) || \
- defined(__gnu_hurd__) || \
+#if defined(__MINGW32__) || defined(AMIGA) || \
+ defined(__OpenBSD__) || defined(__NetBSD__) || \
+ defined(__gnu_hurd__) || defined(__ANDROID_API__) || \
(__GLIBC__ == 2 && __GLIBC_MINOR__ < 8)
git__insertsort_r(els, nel, elsize, NULL, cmp, payload);
#elif defined(GIT_WIN32)
diff --git a/src/util.h b/src/util.h
index e0088399c..f9de909e9 100644
--- a/src/util.h
+++ b/src/util.h
@@ -55,6 +55,9 @@ GIT_INLINE(char *) git__strndup(const char *str, size_t n)
ptr = (char*)git__malloc(length + 1);
+ if (!ptr)
+ return NULL;
+
if (length)
memcpy(ptr, str, length);
@@ -79,7 +82,10 @@ GIT_INLINE(void *) git__realloc(void *ptr, size_t size)
return new_ptr;
}
-#define git__free(ptr) free(ptr)
+GIT_INLINE(void) git__free(void *ptr)
+{
+ free(ptr);
+}
#define STRCMP_CASESELECT(IGNORE_CASE, STR1, STR2) \
((IGNORE_CASE) ? strcasecmp((STR1), (STR2)) : strcmp((STR1), (STR2)))
@@ -221,6 +227,9 @@ typedef void (*git_refcount_freeptr)(void *r);
#define GIT_REFCOUNT_OWNER(r) (((git_refcount *)(r))->owner)
+#define GIT_REFCOUNT_VAL(r) git_atomic_get(&((git_refcount *)(r))->refcount)
+
+
static signed char from_hex[] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 00 */
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10 */
@@ -291,6 +300,11 @@ GIT_INLINE(bool) git__isspace(int c)
return (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r' || c == '\v' || c == 0x85 /* Unicode CR+LF */);
}
+GIT_INLINE(bool) git__isspace_nonlf(int c)
+{
+ return (c == ' ' || c == '\t' || c == '\f' || c == '\r' || c == '\v' || c == 0x85 /* Unicode CR+LF */);
+}
+
GIT_INLINE(bool) git__iswildcard(int c)
{
return (c == '*' || c == '?' || c == '[');
@@ -339,4 +353,65 @@ GIT_INLINE(void) git__memzero(void *data, size_t size)
#endif
}
+#ifdef GIT_WIN32
+
+GIT_INLINE(double) git__timer(void)
+{
+ /* We need the initial tick count to detect if the tick
+ * count has rolled over. */
+ static DWORD initial_tick_count = 0;
+
+ /* GetTickCount returns the number of milliseconds that have
+ * elapsed since the system was started. */
+ DWORD count = GetTickCount();
+
+ if(initial_tick_count == 0) {
+ initial_tick_count = count;
+ } else if (count < initial_tick_count) {
+ /* The tick count has rolled over - adjust for it. */
+ count = (0xFFFFFFFF - initial_tick_count) + count;
+ }
+
+ return (double) count / (double) 1000;
+}
+
+#elif __APPLE__
+
+#include <mach/mach_time.h>
+
+GIT_INLINE(double) git__timer(void)
+{
+ uint64_t time = mach_absolute_time();
+ static double scaling_factor = 0;
+
+ if (scaling_factor == 0) {
+ mach_timebase_info_data_t info;
+ (void)mach_timebase_info(&info);
+ scaling_factor = (double)info.numer / (double)info.denom;
+ }
+
+ return (double)time * scaling_factor / 1.0E-9;
+}
+
+#else
+
+#include <sys/time.h>
+
+GIT_INLINE(double) git__timer(void)
+{
+ struct timespec tp;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) {
+ return (double) tp.tv_sec + (double) tp.tv_nsec / 1E-9;
+ } else {
+ /* Fall back to using gettimeofday */
+ struct timeval tv;
+ struct timezone tz;
+ gettimeofday(&tv, &tz);
+ return (double)tv.tv_sec + (double)tv.tv_usec / 1E-6;
+ }
+}
+
+#endif
+
#endif /* INCLUDE_util_h__ */
diff --git a/src/vector.c b/src/vector.c
index 5ba2fab18..362e7b0c0 100644
--- a/src/vector.c
+++ b/src/vector.c
@@ -220,7 +220,7 @@ void git_vector_pop(git_vector *v)
v->length--;
}
-void git_vector_uniq(git_vector *v)
+void git_vector_uniq(git_vector *v, void (*git_free_cb)(void *))
{
git_vector_cmp cmp;
size_t i, j;
@@ -232,9 +232,12 @@ void git_vector_uniq(git_vector *v)
cmp = v->_cmp ? v->_cmp : strict_comparison;
for (i = 0, j = 1 ; j < v->length; ++j)
- if (!cmp(v->contents[i], v->contents[j]))
+ if (!cmp(v->contents[i], v->contents[j])) {
+ if (git_free_cb)
+ git_free_cb(v->contents[i]);
+
v->contents[i] = v->contents[j];
- else
+ } else
v->contents[++i] = v->contents[j];
v->length -= j - i - 1;
diff --git a/src/vector.h b/src/vector.h
index 1bda9c93d..279f5c6ee 100644
--- a/src/vector.h
+++ b/src/vector.h
@@ -55,6 +55,11 @@ GIT_INLINE(void *) git_vector_get(const git_vector *v, size_t position)
#define GIT_VECTOR_GET(V,I) ((I) < (V)->length ? (V)->contents[(I)] : NULL)
+GIT_INLINE(size_t) git_vector_length(const git_vector *v)
+{
+ return v->length;
+}
+
GIT_INLINE(void *) git_vector_last(const git_vector *v)
{
return (v->length > 0) ? git_vector_get(v, v->length - 1) : NULL;
@@ -71,7 +76,7 @@ int git_vector_insert_sorted(git_vector *v, void *element,
int (*on_dup)(void **old, void *new));
int git_vector_remove(git_vector *v, size_t idx);
void git_vector_pop(git_vector *v);
-void git_vector_uniq(git_vector *v);
+void git_vector_uniq(git_vector *v, void (*git_free_cb)(void *));
void git_vector_remove_matching(
git_vector *v, int (*match)(const git_vector *v, size_t idx));
diff --git a/src/win32/dir.c b/src/win32/dir.c
index 8c51d8378..f7859b73f 100644
--- a/src/win32/dir.c
+++ b/src/win32/dir.c
@@ -5,8 +5,7 @@
* a Linking Exception. For full terms see the included COPYING file.
*/
#define GIT__WIN32_NO_WRAP_DIR
-#include "dir.h"
-#include "utf-conv.h"
+#include "posix.h"
static int init_filter(char *filter, size_t n, const char *dir)
{
@@ -25,36 +24,32 @@ static int init_filter(char *filter, size_t n, const char *dir)
git__DIR *git__opendir(const char *dir)
{
- char filter[GIT_WIN_PATH];
- wchar_t filter_w[GIT_WIN_PATH];
+ git_win32_path_as_utf8 filter;
+ git_win32_path filter_w;
git__DIR *new = NULL;
+ size_t dirlen;
if (!dir || !init_filter(filter, sizeof(filter), dir))
return NULL;
- new = git__calloc(1, sizeof(*new));
+ dirlen = strlen(dir);
+
+ new = git__calloc(sizeof(*new) + dirlen + 1, 1);
if (!new)
return NULL;
+ memcpy(new->dir, dir, dirlen);
- new->dir = git__strdup(dir);
- if (!new->dir)
- goto fail;
-
- git__utf8_to_16(filter_w, GIT_WIN_PATH, filter);
+ git_win32_path_from_c(filter_w, filter);
new->h = FindFirstFileW(filter_w, &new->f);
if (new->h == INVALID_HANDLE_VALUE) {
giterr_set(GITERR_OS, "Could not open directory '%s'", dir);
- goto fail;
+ git__free(new);
+ return NULL;
}
new->first = 1;
return new;
-
-fail:
- git__free(new->dir);
- git__free(new);
- return NULL;
}
int git__readdir_ext(
@@ -80,7 +75,7 @@ int git__readdir_ext(
if (wcslen(d->f.cFileName) >= sizeof(entry->d_name))
return -1;
- git__utf16_to_8(entry->d_name, d->f.cFileName);
+ git_win32_path_to_c(entry->d_name, d->f.cFileName);
entry->d_ino = 0;
*result = entry;
@@ -101,8 +96,8 @@ struct git__dirent *git__readdir(git__DIR *d)
void git__rewinddir(git__DIR *d)
{
- char filter[GIT_WIN_PATH];
- wchar_t filter_w[GIT_WIN_PATH];
+ git_win32_path_as_utf8 filter;
+ git_win32_path filter_w;
if (!d)
return;
@@ -116,7 +111,7 @@ void git__rewinddir(git__DIR *d)
if (!init_filter(filter, sizeof(filter), d->dir))
return;
- git__utf8_to_16(filter_w, GIT_WIN_PATH, filter);
+ git_win32_path_from_c(filter_w, filter);
d->h = FindFirstFileW(filter_w, &d->f);
if (d->h == INVALID_HANDLE_VALUE)
@@ -134,8 +129,7 @@ int git__closedir(git__DIR *d)
FindClose(d->h);
d->h = INVALID_HANDLE_VALUE;
}
- git__free(d->dir);
- d->dir = NULL;
+
git__free(d);
return 0;
}
diff --git a/src/win32/dir.h b/src/win32/dir.h
index 7696d468e..24d48f6ba 100644
--- a/src/win32/dir.h
+++ b/src/win32/dir.h
@@ -11,15 +11,15 @@
struct git__dirent {
int d_ino;
- char d_name[261];
+ git_win32_path_as_utf8 d_name;
};
typedef struct {
HANDLE h;
WIN32_FIND_DATAW f;
struct git__dirent entry;
- char *dir;
int first;
+ char dir[GIT_FLEX_ARRAY];
} git__DIR;
extern git__DIR *git__opendir(const char *);
diff --git a/src/win32/error.c b/src/win32/error.c
index a62a07e82..bc598ae32 100644
--- a/src/win32/error.c
+++ b/src/win32/error.c
@@ -47,7 +47,7 @@ char *git_win32_get_error_message(DWORD error_code)
(LPWSTR)&lpMsgBuf, 0, NULL)) {
/* Invalid code point check supported on Vista+ only */
- if (git_has_win32_version(6, 0))
+ if (git_has_win32_version(6, 0, 0))
dwFlags = WC_ERR_INVALID_CHARS;
else
dwFlags = 0;
diff --git a/src/win32/findfile.c b/src/win32/findfile.c
index 5dd3de13d..a9e812e28 100644
--- a/src/win32/findfile.c
+++ b/src/win32/findfile.c
@@ -23,11 +23,11 @@ int git_win32__expand_path(struct git_win32__path *s_root, const wchar_t *templ)
return s_root->len ? 0 : -1;
}
-static int win32_path_utf16_to_8(git_buf *path_utf8, const wchar_t *path_utf16)
+static int win32_path_to_8(git_buf *path_utf8, const wchar_t *path)
{
char temp_utf8[GIT_PATH_MAX];
- git__utf16_to_8(temp_utf8, path_utf16);
+ git__utf16_to_8(temp_utf8, GIT_PATH_MAX, path);
git_path_mkposix(temp_utf8);
return git_buf_sets(path_utf8, temp_utf8);
@@ -53,7 +53,7 @@ int git_win32__find_file(
if (*filename == '/' || *filename == '\\')
filename++;
- git__utf8_to_16(file_utf16 + root->len - 1, alloc_len, filename);
+ git__utf8_to_16(file_utf16 + root->len - 1, alloc_len - root->len, filename);
/* check access */
if (_waccess(file_utf16, F_OK) < 0) {
@@ -61,7 +61,7 @@ int git_win32__find_file(
return GIT_ENOTFOUND;
}
- win32_path_utf16_to_8(path, file_utf16);
+ win32_path_to_8(path, file_utf16);
git__free(file_utf16);
return 0;
@@ -86,7 +86,7 @@ static wchar_t* win32_walkpath(wchar_t *path, wchar_t *buf, size_t buflen)
return (path != base) ? path : NULL;
}
-static int win32_find_git_in_path(git_buf *buf, const wchar_t *gitexe)
+static int win32_find_git_in_path(git_buf *buf, const wchar_t *gitexe, const wchar_t *subdir)
{
wchar_t *env = _wgetenv(L"PATH"), lastch;
struct git_win32__path root;
@@ -110,10 +110,10 @@ static int win32_find_git_in_path(git_buf *buf, const wchar_t *gitexe)
wcscpy(&root.path[root.len], gitexe);
if (_waccess(root.path, F_OK) == 0 && root.len > 5) {
- /* replace "bin\\" or "cmd\\" with "etc\\" */
- wcscpy(&root.path[root.len - 4], L"etc\\");
+ /* replace "bin\\" or "cmd\\" with subdir */
+ wcscpy(&root.path[root.len - 4], subdir);
- win32_path_utf16_to_8(buf, root.path);
+ win32_path_to_8(buf, root.path);
return 0;
}
}
@@ -122,7 +122,7 @@ static int win32_find_git_in_path(git_buf *buf, const wchar_t *gitexe)
}
static int win32_find_git_in_registry(
- git_buf *buf, const HKEY hieve, const wchar_t *key)
+ git_buf *buf, const HKEY hieve, const wchar_t *key, const wchar_t *subdir)
{
HKEY hKey;
DWORD dwType = REG_SZ;
@@ -130,9 +130,9 @@ static int win32_find_git_in_registry(
assert(buf);
- path16.len = 0;
+ path16.len = MAX_PATH;
- if (RegOpenKeyExW(hieve, key, 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS) {
+ if (RegOpenKeyExW(hieve, key, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
if (RegQueryValueExW(hKey, L"InstallLocation", NULL, &dwType,
(LPBYTE)&path16.path, &path16.len) == ERROR_SUCCESS)
{
@@ -143,10 +143,10 @@ static int win32_find_git_in_registry(
return -1;
}
- wcscat(path16.path, L"etc\\");
+ wcscat(path16.path, subdir);
path16.len += 4;
- win32_path_utf16_to_8(buf, path16.path);
+ win32_path_to_8(buf, path16.path);
}
RegCloseKey(hKey);
@@ -156,7 +156,7 @@ static int win32_find_git_in_registry(
}
static int win32_find_existing_dirs(
- git_buf *out, const wchar_t *tmpl[], char *temp[])
+ git_buf *out, const wchar_t *tmpl[])
{
struct git_win32__path path16;
git_buf buf = GIT_BUF_INIT;
@@ -168,7 +168,7 @@ static int win32_find_existing_dirs(
path16.path[0] != L'%' &&
!_waccess(path16.path, F_OK))
{
- win32_path_utf16_to_8(&buf, path16.path);
+ win32_path_to_8(&buf, path16.path);
if (buf.size)
git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
@@ -180,26 +180,26 @@ static int win32_find_existing_dirs(
return (git_buf_oom(out) ? -1 : 0);
}
-int git_win32__find_system_dirs(git_buf *out)
+int git_win32__find_system_dirs(git_buf *out, const wchar_t *subdir)
{
git_buf buf = GIT_BUF_INIT;
/* directories where git.exe & git.cmd are found */
- if (!win32_find_git_in_path(&buf, L"git.exe") && buf.size)
+ if (!win32_find_git_in_path(&buf, L"git.exe", subdir) && buf.size)
git_buf_set(out, buf.ptr, buf.size);
else
git_buf_clear(out);
- if (!win32_find_git_in_path(&buf, L"git.cmd") && buf.size)
+ if (!win32_find_git_in_path(&buf, L"git.cmd", subdir) && buf.size)
git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
/* directories where git is installed according to registry */
if (!win32_find_git_in_registry(
- &buf, HKEY_CURRENT_USER, REG_MSYSGIT_INSTALL_LOCAL) && buf.size)
+ &buf, HKEY_CURRENT_USER, REG_MSYSGIT_INSTALL_LOCAL, subdir) && buf.size)
git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
if (!win32_find_git_in_registry(
- &buf, HKEY_LOCAL_MACHINE, REG_MSYSGIT_INSTALL) && buf.size)
+ &buf, HKEY_LOCAL_MACHINE, REG_MSYSGIT_INSTALL, subdir) && buf.size)
git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
git_buf_free(&buf);
@@ -209,7 +209,6 @@ int git_win32__find_system_dirs(git_buf *out)
int git_win32__find_global_dirs(git_buf *out)
{
- char *temp[3];
static const wchar_t *global_tmpls[4] = {
L"%HOME%\\",
L"%HOMEDRIVE%%HOMEPATH%\\",
@@ -217,12 +216,11 @@ int git_win32__find_global_dirs(git_buf *out)
NULL,
};
- return win32_find_existing_dirs(out, global_tmpls, temp);
+ return win32_find_existing_dirs(out, global_tmpls);
}
int git_win32__find_xdg_dirs(git_buf *out)
{
- char *temp[6];
static const wchar_t *global_tmpls[7] = {
L"%XDG_CONFIG_HOME%\\git",
L"%APPDATA%\\git",
@@ -233,5 +231,5 @@ int git_win32__find_xdg_dirs(git_buf *out)
NULL,
};
- return win32_find_existing_dirs(out, global_tmpls, temp);
+ return win32_find_existing_dirs(out, global_tmpls);
}
diff --git a/src/win32/findfile.h b/src/win32/findfile.h
index fc79e1b72..11bf7e620 100644
--- a/src/win32/findfile.h
+++ b/src/win32/findfile.h
@@ -19,7 +19,7 @@ extern int git_win32__expand_path(
extern int git_win32__find_file(
git_buf *path, const struct git_win32__path *root, const char *filename);
-extern int git_win32__find_system_dirs(git_buf *out);
+extern int git_win32__find_system_dirs(git_buf *out, const wchar_t *subpath);
extern int git_win32__find_global_dirs(git_buf *out);
extern int git_win32__find_xdg_dirs(git_buf *out);
diff --git a/src/win32/mingw-compat.h b/src/win32/mingw-compat.h
index 7b97b48db..fe0abfb54 100644
--- a/src/win32/mingw-compat.h
+++ b/src/win32/mingw-compat.h
@@ -19,6 +19,11 @@
# define S_IFLNK _S_IFLNK
# define S_ISLNK(m) (((m) & _S_IFMT) == _S_IFLNK)
+GIT_INLINE(size_t) p_strnlen(const char *s, size_t maxlen) {
+ const char *end = memchr(s, 0, maxlen);
+ return end ? (size_t)(end - s) : maxlen;
+}
+
#endif
#endif /* INCLUDE_mingw_compat__ */
diff --git a/src/win32/posix.h b/src/win32/posix.h
index c49c2175c..24cba23e0 100644
--- a/src/win32/posix.h
+++ b/src/win32/posix.h
@@ -8,7 +8,16 @@
#define INCLUDE_posix__w32_h__
#include "common.h"
+#include "../posix.h"
#include "utf-conv.h"
+#include "dir.h"
+
+/* define some standard errnos that the runtime may be missing. for example,
+ * mingw lacks EAFNOSUPPORT. */
+
+#ifndef EAFNOSUPPORT
+# define EAFNOSUPPORT (INT_MAX-1)
+#endif
GIT_INLINE(int) p_link(const char *old, const char *new)
{
@@ -20,9 +29,9 @@ GIT_INLINE(int) p_link(const char *old, const char *new)
GIT_INLINE(int) p_mkdir(const char *path, mode_t mode)
{
- wchar_t buf[GIT_WIN_PATH];
+ git_win32_path buf;
GIT_UNUSED(mode);
- git__utf8_to_16(buf, GIT_WIN_PATH, path);
+ git_win32_path_from_c(buf, path);
return _wmkdir(buf);
}
diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c
index f04974428..18f717b0f 100644
--- a/src/win32/posix_w32.c
+++ b/src/win32/posix_w32.c
@@ -16,8 +16,8 @@
int p_unlink(const char *path)
{
- wchar_t buf[GIT_WIN_PATH];
- git__utf8_to_16(buf, GIT_WIN_PATH, path);
+ git_win32_path buf;
+ git_win32_path_from_c(buf, path);
_wchmod(buf, 0666);
return _wunlink(buf);
}
@@ -59,10 +59,11 @@ static int do_lstat(
const char *file_name, struct stat *buf, int posix_enotdir)
{
WIN32_FILE_ATTRIBUTE_DATA fdata;
- wchar_t fbuf[GIT_WIN_PATH], lastch;
+ git_win32_path fbuf;
+ wchar_t lastch;
int flen;
- flen = git__utf8_to_16(fbuf, GIT_WIN_PATH, file_name);
+ flen = git_win32_path_from_c(fbuf, file_name);
/* truncate trailing slashes */
for (; flen > 0; --flen) {
@@ -90,6 +91,9 @@ static int do_lstat(
if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
fMode |= S_IFLNK;
+ if ((fMode & (S_IFDIR | S_IFLNK)) == (S_IFDIR | S_IFLNK)) // junction
+ fMode ^= S_IFLNK;
+
buf->st_ino = 0;
buf->st_gid = 0;
buf->st_uid = 0;
@@ -105,10 +109,10 @@ static int do_lstat(
* the length of the path pointed to, which we expect everywhere else
*/
if (S_ISLNK(fMode)) {
- char target[GIT_WIN_PATH];
+ git_win32_path_as_utf8 target;
int readlink_result;
- readlink_result = p_readlink(file_name, target, GIT_WIN_PATH);
+ readlink_result = p_readlink(file_name, target, sizeof(target));
if (readlink_result == -1)
return -1;
@@ -121,8 +125,8 @@ static int do_lstat(
errno = ENOENT;
- /* We need POSIX behavior, then ENOTDIR must set when any of the folders in the
- * file path is a regular file,otherwise ENOENT must be set.
+ /* To match POSIX behavior, set ENOTDIR when any of the folders in the
+ * file path is a regular file, otherwise set ENOENT.
*/
if (posix_enotdir) {
/* scan up path until we find an existing item */
@@ -156,13 +160,22 @@ int p_lstat_posixly(const char *filename, struct stat *buf)
return do_lstat(filename, buf, 1);
}
+
+/*
+ * Parts of the The p_readlink function are heavily inspired by the php
+ * readlink function in link_win32.c
+ *
+ * Copyright (c) 1999 - 2012 The PHP Group. All rights reserved.
+ *
+ * For details of the PHP license see http://www.php.net/license/3_01.txt
+ */
int p_readlink(const char *link, char *target, size_t target_len)
{
typedef DWORD (WINAPI *fpath_func)(HANDLE, LPWSTR, DWORD, DWORD);
static fpath_func pGetFinalPath = NULL;
HANDLE hFile;
DWORD dwRet;
- wchar_t link_w[GIT_WIN_PATH];
+ git_win32_path link_w;
wchar_t* target_w;
int error = 0;
@@ -185,7 +198,7 @@ int p_readlink(const char *link, char *target, size_t target_len)
}
}
- git__utf8_to_16(link_w, GIT_WIN_PATH, link);
+ git_win32_path_from_c(link_w, link);
hFile = CreateFileW(link_w, // file to open
GENERIC_READ, // open for reading
@@ -251,10 +264,10 @@ int p_symlink(const char *old, const char *new)
int p_open(const char *path, int flags, ...)
{
- wchar_t buf[GIT_WIN_PATH];
+ git_win32_path buf;
mode_t mode = 0;
- git__utf8_to_16(buf, GIT_WIN_PATH, path);
+ git_win32_path_from_c(buf, path);
if (flags & O_CREAT) {
va_list arg_list;
@@ -269,8 +282,8 @@ int p_open(const char *path, int flags, ...)
int p_creat(const char *path, mode_t mode)
{
- wchar_t buf[GIT_WIN_PATH];
- git__utf8_to_16(buf, GIT_WIN_PATH, path);
+ git_win32_path buf;
+ git_win32_path_from_c(buf, path);
return _wopen(buf, _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY, mode);
}
@@ -296,7 +309,7 @@ int p_getcwd(char *buffer_out, size_t size)
int p_stat(const char* path, struct stat* buf)
{
- char target[GIT_WIN_PATH];
+ git_win32_path_as_utf8 target;
int error = 0;
error = do_lstat(path, buf, 0);
@@ -304,7 +317,7 @@ int p_stat(const char* path, struct stat* buf)
/* We need not do this in a loop to unwind chains of symlinks since
* p_readlink calls GetFinalPathNameByHandle which does it for us. */
if (error >= 0 && S_ISLNK(buf->st_mode) &&
- (error = p_readlink(path, target, GIT_WIN_PATH)) >= 0)
+ (error = p_readlink(path, target, sizeof(target))) >= 0)
error = do_lstat(target, buf, 0);
return error;
@@ -312,23 +325,23 @@ int p_stat(const char* path, struct stat* buf)
int p_chdir(const char* path)
{
- wchar_t buf[GIT_WIN_PATH];
- git__utf8_to_16(buf, GIT_WIN_PATH, path);
+ git_win32_path buf;
+ git_win32_path_from_c(buf, path);
return _wchdir(buf);
}
int p_chmod(const char* path, mode_t mode)
{
- wchar_t buf[GIT_WIN_PATH];
- git__utf8_to_16(buf, GIT_WIN_PATH, path);
+ git_win32_path buf;
+ git_win32_path_from_c(buf, path);
return _wchmod(buf, mode);
}
int p_rmdir(const char* path)
{
int error;
- wchar_t buf[GIT_WIN_PATH];
- git__utf8_to_16(buf, GIT_WIN_PATH, path);
+ git_win32_path buf;
+ git_win32_path_from_c(buf, path);
error = _wrmdir(buf);
@@ -344,24 +357,24 @@ int p_rmdir(const char* path)
int p_hide_directory__w32(const char *path)
{
- wchar_t buf[GIT_WIN_PATH];
- git__utf8_to_16(buf, GIT_WIN_PATH, path);
+ git_win32_path buf;
+ git_win32_path_from_c(buf, path);
return (SetFileAttributesW(buf, FILE_ATTRIBUTE_HIDDEN) != 0) ? 0 : -1;
}
char *p_realpath(const char *orig_path, char *buffer)
{
int ret;
- wchar_t orig_path_w[GIT_WIN_PATH];
- wchar_t buffer_w[GIT_WIN_PATH];
+ git_win32_path orig_path_w;
+ git_win32_path buffer_w;
- git__utf8_to_16(orig_path_w, GIT_WIN_PATH, orig_path);
+ git_win32_path_from_c(orig_path_w, orig_path);
/* Implicitly use GetCurrentDirectory which can be a threading issue */
- ret = GetFullPathNameW(orig_path_w, GIT_WIN_PATH, buffer_w, NULL);
+ ret = GetFullPathNameW(orig_path_w, GIT_WIN_PATH_UTF16, buffer_w, NULL);
/* According to MSDN, a return value equals to zero means a failure. */
- if (ret == 0 || ret > GIT_WIN_PATH)
+ if (ret == 0 || ret > GIT_WIN_PATH_UTF16)
buffer = NULL;
else if (GetFileAttributesW(buffer_w) == INVALID_FILE_ATTRIBUTES) {
@@ -445,18 +458,18 @@ int p_setenv(const char* name, const char* value, int overwrite)
int p_access(const char* path, mode_t mode)
{
- wchar_t buf[GIT_WIN_PATH];
- git__utf8_to_16(buf, GIT_WIN_PATH, path);
+ git_win32_path buf;
+ git_win32_path_from_c(buf, path);
return _waccess(buf, mode);
}
int p_rename(const char *from, const char *to)
{
- wchar_t wfrom[GIT_WIN_PATH];
- wchar_t wto[GIT_WIN_PATH];
+ git_win32_path wfrom;
+ git_win32_path wto;
- git__utf8_to_16(wfrom, GIT_WIN_PATH, from);
- git__utf8_to_16(wto, GIT_WIN_PATH, to);
+ git_win32_path_from_c(wfrom, from);
+ git_win32_path_from_c(wto, to);
return MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? 0 : -1;
}
@@ -505,94 +518,40 @@ p_gmtime_r (const time_t *timer, struct tm *result)
return result;
}
-#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
-#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64
-#else
-#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
-#endif
-
-#ifndef _TIMEZONE_DEFINED
-#define _TIMEZONE_DEFINED
-struct timezone
-{
- int tz_minuteswest; /* minutes W of Greenwich */
- int tz_dsttime; /* type of dst correction */
-};
-#endif
-
-int p_gettimeofday(struct timeval *tv, struct timezone *tz)
+int p_inet_pton(int af, const char *src, void *dst)
{
- FILETIME ft;
- unsigned __int64 tmpres = 0;
- static int tzflag;
-
- if (NULL != tv)
- {
- GetSystemTimeAsFileTime(&ft);
-
- tmpres |= ft.dwHighDateTime;
- tmpres <<= 32;
- tmpres |= ft.dwLowDateTime;
-
- /*converting file time to unix epoch*/
- tmpres /= 10; /*convert into microseconds*/
- tmpres -= DELTA_EPOCH_IN_MICROSECS;
- tv->tv_sec = (long)(tmpres / 1000000UL);
- tv->tv_usec = (long)(tmpres % 1000000UL);
- }
+ struct sockaddr_storage sin;
+ void *addr;
+ int sin_len = sizeof(struct sockaddr_storage), addr_len;
+ int error = 0;
- if (NULL != tz)
- {
- if (!tzflag)
- {
- _tzset();
- tzflag++;
- }
- tz->tz_minuteswest = _timezone / 60;
- tz->tz_dsttime = _daylight;
+ if (af == AF_INET) {
+ addr = &((struct sockaddr_in *)&sin)->sin_addr;
+ addr_len = sizeof(struct in_addr);
+ } else if (af == AF_INET6) {
+ addr = &((struct sockaddr_in6 *)&sin)->sin6_addr;
+ addr_len = sizeof(struct in6_addr);
+ } else {
+ errno = EAFNOSUPPORT;
+ return -1;
}
- return 0;
-}
-
-int p_inet_pton(int af, const char* src, void* dst)
-{
- union {
- struct sockaddr_in6 sin6;
- struct sockaddr_in sin;
- } sa;
- int srcsize;
-
- switch(af)
- {
- case AF_INET:
- sa.sin.sin_family = AF_INET;
- srcsize = (int)sizeof(sa.sin);
- break;
- case AF_INET6:
- sa.sin6.sin6_family = AF_INET6;
- srcsize = (int)sizeof(sa.sin6);
- break;
- default:
- errno = WSAEPFNOSUPPORT;
- return -1;
+ if ((error = WSAStringToAddressA((LPSTR)src, af, NULL, (LPSOCKADDR)&sin, &sin_len)) == 0) {
+ memcpy(dst, addr, addr_len);
+ return 1;
}
- if (WSAStringToAddress((LPSTR)src, af, NULL, (struct sockaddr *) &sa, &srcsize) != 0)
- {
- errno = WSAGetLastError();
+ switch(WSAGetLastError()) {
+ case WSAEINVAL:
+ return 0;
+ case WSAEFAULT:
+ errno = ENOSPC;
+ return -1;
+ case WSA_NOT_ENOUGH_MEMORY:
+ errno = ENOMEM;
return -1;
}
- switch(af)
- {
- case AF_INET:
- memcpy(dst, &sa.sin.sin_addr, sizeof(sa.sin.sin_addr));
- break;
- case AF_INET6:
- memcpy(dst, &sa.sin6.sin6_addr, sizeof(sa.sin6.sin6_addr));
- break;
- }
-
- return 1;
+ errno = EINVAL;
+ return -1;
}
diff --git a/src/win32/precompiled.h b/src/win32/precompiled.h
index 5de7e6f34..cbfe98812 100644
--- a/src/win32/precompiled.h
+++ b/src/win32/precompiled.h
@@ -1,4 +1,5 @@
#include "git2.h"
+#include "common.h"
#include <assert.h>
#include <errno.h>
@@ -6,6 +7,8 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <fcntl.h>
+#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
diff --git a/src/win32/pthread.c b/src/win32/pthread.c
index 2f263b3e0..db8927471 100644
--- a/src/win32/pthread.c
+++ b/src/win32/pthread.c
@@ -6,6 +6,7 @@
*/
#include "pthread.h"
+#include "../global.h"
int pthread_create(
pthread_t *GIT_RESTRICT thread,
@@ -127,9 +128,10 @@ int pthread_cond_signal(pthread_cond_t *cond)
return 0;
}
-/* pthread_cond_broadcast is not implemented because doing so with just Win32 events
- * is quite complicated, and no caller in libgit2 uses it yet. */
-
+/* pthread_cond_broadcast is not implemented because doing so with just
+ * Win32 events is quite complicated, and no caller in libgit2 uses it
+ * yet.
+ */
int pthread_num_processors_np(void)
{
DWORD_PTR p, s;
@@ -142,3 +144,111 @@ int pthread_num_processors_np(void)
return n ? n : 1;
}
+
+static HINSTANCE win32_kernel32_dll;
+
+typedef void (WINAPI *win32_srwlock_fn)(GIT_SRWLOCK *);
+
+static win32_srwlock_fn win32_srwlock_initialize;
+static win32_srwlock_fn win32_srwlock_acquire_shared;
+static win32_srwlock_fn win32_srwlock_release_shared;
+static win32_srwlock_fn win32_srwlock_acquire_exclusive;
+static win32_srwlock_fn win32_srwlock_release_exclusive;
+
+int pthread_rwlock_init(
+ pthread_rwlock_t *GIT_RESTRICT lock,
+ const pthread_rwlockattr_t *GIT_RESTRICT attr)
+{
+ (void)attr;
+
+ if (win32_srwlock_initialize)
+ win32_srwlock_initialize(&lock->native.srwl);
+ else
+ InitializeCriticalSection(&lock->native.csec);
+
+ return 0;
+}
+
+int pthread_rwlock_rdlock(pthread_rwlock_t *lock)
+{
+ if (win32_srwlock_acquire_shared)
+ win32_srwlock_acquire_shared(&lock->native.srwl);
+ else
+ EnterCriticalSection(&lock->native.csec);
+
+ return 0;
+}
+
+int pthread_rwlock_rdunlock(pthread_rwlock_t *lock)
+{
+ if (win32_srwlock_release_shared)
+ win32_srwlock_release_shared(&lock->native.srwl);
+ else
+ LeaveCriticalSection(&lock->native.csec);
+
+ return 0;
+}
+
+int pthread_rwlock_wrlock(pthread_rwlock_t *lock)
+{
+ if (win32_srwlock_acquire_exclusive)
+ win32_srwlock_acquire_exclusive(&lock->native.srwl);
+ else
+ EnterCriticalSection(&lock->native.csec);
+
+ return 0;
+}
+
+int pthread_rwlock_wrunlock(pthread_rwlock_t *lock)
+{
+ if (win32_srwlock_release_exclusive)
+ win32_srwlock_release_exclusive(&lock->native.srwl);
+ else
+ LeaveCriticalSection(&lock->native.csec);
+
+ return 0;
+}
+
+int pthread_rwlock_destroy(pthread_rwlock_t *lock)
+{
+ if (!win32_srwlock_initialize)
+ DeleteCriticalSection(&lock->native.csec);
+ git__memzero(lock, sizeof(*lock));
+ return 0;
+}
+
+
+static void win32_pthread_shutdown(void)
+{
+ if (win32_kernel32_dll) {
+ FreeLibrary(win32_kernel32_dll);
+ win32_kernel32_dll = NULL;
+ }
+}
+
+int win32_pthread_initialize(void)
+{
+ if (win32_kernel32_dll)
+ return 0;
+
+ win32_kernel32_dll = LoadLibrary("Kernel32.dll");
+ if (!win32_kernel32_dll) {
+ giterr_set(GITERR_OS, "Could not load Kernel32.dll!");
+ return -1;
+ }
+
+ win32_srwlock_initialize = (win32_srwlock_fn)
+ GetProcAddress(win32_kernel32_dll, "InitializeSRWLock");
+ win32_srwlock_acquire_shared = (win32_srwlock_fn)
+ GetProcAddress(win32_kernel32_dll, "AcquireSRWLockShared");
+ win32_srwlock_release_shared = (win32_srwlock_fn)
+ GetProcAddress(win32_kernel32_dll, "ReleaseSRWLockShared");
+ win32_srwlock_acquire_exclusive = (win32_srwlock_fn)
+ GetProcAddress(win32_kernel32_dll, "AcquireSRWLockExclusive");
+ win32_srwlock_release_exclusive = (win32_srwlock_fn)
+ GetProcAddress(win32_kernel32_dll, "ReleaseSRWLockExclusive");
+
+ git__on_shutdown(win32_pthread_shutdown);
+
+ return 0;
+}
diff --git a/src/win32/pthread.h b/src/win32/pthread.h
index 8277ecf6e..af5b121f0 100644
--- a/src/win32/pthread.h
+++ b/src/win32/pthread.h
@@ -19,22 +19,34 @@
typedef int pthread_mutexattr_t;
typedef int pthread_condattr_t;
typedef int pthread_attr_t;
+typedef int pthread_rwlockattr_t;
+
typedef CRITICAL_SECTION pthread_mutex_t;
typedef HANDLE pthread_t;
typedef HANDLE pthread_cond_t;
-#define PTHREAD_MUTEX_INITIALIZER {(void*)-1};
+typedef struct { void *Ptr; } GIT_SRWLOCK;
+
+typedef struct {
+ union {
+ GIT_SRWLOCK srwl;
+ CRITICAL_SECTION csec;
+ } native;
+} pthread_rwlock_t;
+
+#define PTHREAD_MUTEX_INITIALIZER {(void*)-1}
int pthread_create(
- pthread_t *GIT_RESTRICT,
- const pthread_attr_t *GIT_RESTRICT,
+ pthread_t *GIT_RESTRICT thread,
+ const pthread_attr_t *GIT_RESTRICT attr,
void *(*start_routine)(void*),
- void *__restrict);
+ void *GIT_RESTRICT arg);
int pthread_join(pthread_t, void **);
int pthread_mutex_init(
- pthread_mutex_t *GIT_RESTRICT, const pthread_mutexattr_t *GIT_RESTRICT);
+ pthread_mutex_t *GIT_RESTRICT mutex,
+ const pthread_mutexattr_t *GIT_RESTRICT mutexattr);
int pthread_mutex_destroy(pthread_mutex_t *);
int pthread_mutex_lock(pthread_mutex_t *);
int pthread_mutex_unlock(pthread_mutex_t *);
@@ -47,4 +59,15 @@ int pthread_cond_signal(pthread_cond_t *);
int pthread_num_processors_np(void);
+int pthread_rwlock_init(
+ pthread_rwlock_t *GIT_RESTRICT lock,
+ const pthread_rwlockattr_t *GIT_RESTRICT attr);
+int pthread_rwlock_rdlock(pthread_rwlock_t *);
+int pthread_rwlock_rdunlock(pthread_rwlock_t *);
+int pthread_rwlock_wrlock(pthread_rwlock_t *);
+int pthread_rwlock_wrunlock(pthread_rwlock_t *);
+int pthread_rwlock_destroy(pthread_rwlock_t *);
+
+extern int win32_pthread_initialize(void);
+
#endif
diff --git a/src/win32/utf-conv.c b/src/win32/utf-conv.c
index c06f3a8c2..d4dbfbab9 100644
--- a/src/win32/utf-conv.c
+++ b/src/win32/utf-conv.c
@@ -70,12 +70,12 @@ void git__utf8_to_16(wchar_t *dest, size_t length, const char *src)
}
#endif
-int git__utf8_to_16(wchar_t *dest, size_t length, const char *src)
+int git__utf8_to_16(wchar_t * dest, size_t dest_size, const char *src)
{
- return MultiByteToWideChar(CP_UTF8, 0, src, -1, dest, (int)length);
+ return MultiByteToWideChar(CP_UTF8, 0, src, -1, dest, (int)dest_size);
}
-int git__utf16_to_8(char *out, const wchar_t *input)
+int git__utf16_to_8(char *dest, size_t dest_size, const wchar_t *src)
{
- return WideCharToMultiByte(CP_UTF8, 0, input, -1, out, GIT_WIN_PATH, NULL, NULL);
+ return WideCharToMultiByte(CP_UTF8, 0, src, -1, dest, (int)dest_size, NULL, NULL);
}
diff --git a/src/win32/utf-conv.h b/src/win32/utf-conv.h
index 6cc9205f7..3af77580e 100644
--- a/src/win32/utf-conv.h
+++ b/src/win32/utf-conv.h
@@ -4,16 +4,35 @@
* This file 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__
#include <wchar.h>
+#include "common.h"
-#ifndef INCLUDE_git_utfconv_h__
-#define INCLUDE_git_utfconv_h__
+/* Maximum characters in a Windows path plus one for NUL byte */
+#define GIT_WIN_PATH_UTF16 (260 + 1)
-#define GIT_WIN_PATH (260 + 1)
+/* Maximum bytes necessary to convert a full-length UTF16 path to UTF8 */
+#define GIT_WIN_PATH_UTF8 (260 * 4 + 1)
-int git__utf8_to_16(wchar_t *dest, size_t length, const char *src);
-int git__utf16_to_8(char *dest, const wchar_t *src);
+typedef wchar_t git_win32_path[GIT_WIN_PATH_UTF16];
-#endif
+typedef char git_win32_path_as_utf8[GIT_WIN_PATH_UTF8];
+/* dest_size is the size of dest in wchar_t's */
+int git__utf8_to_16(wchar_t * dest, size_t dest_size, const char *src);
+/* dest_size is the size of dest in char's */
+int git__utf16_to_8(char *dest, size_t dest_size, const wchar_t *src);
+
+GIT_INLINE(int) git_win32_path_from_c(git_win32_path dest, const char *src)
+{
+ return git__utf8_to_16(dest, GIT_WIN_PATH_UTF16, src);
+}
+
+GIT_INLINE(int) git_win32_path_to_c(git_win32_path_as_utf8 dest, const wchar_t *src)
+{
+ return git__utf16_to_8(dest, GIT_WIN_PATH_UTF8, src);
+}
+
+#endif
diff --git a/src/win32/version.h b/src/win32/version.h
index 483962b57..79667697f 100644
--- a/src/win32/version.h
+++ b/src/win32/version.h
@@ -9,12 +9,29 @@
#include <windows.h>
-GIT_INLINE(int) git_has_win32_version(int major, int minor)
+GIT_INLINE(int) git_has_win32_version(int major, int minor, int service_pack)
{
- WORD wVersion = LOWORD(GetVersion());
+ OSVERSIONINFOEX version_test = {0};
+ DWORD version_test_mask;
+ DWORDLONG version_condition_mask = 0;
+
+ version_test.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+ version_test.dwMajorVersion = major;
+ version_test.dwMinorVersion = minor;
+ version_test.wServicePackMajor = (WORD)service_pack;
+ version_test.wServicePackMinor = 0;
- return LOBYTE(wVersion) > major ||
- (LOBYTE(wVersion) == major && HIBYTE(wVersion) >= minor);
+ version_test_mask = (VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR);
+
+ VER_SET_CONDITION(version_condition_mask, VER_MAJORVERSION, VER_GREATER_EQUAL);
+ VER_SET_CONDITION(version_condition_mask, VER_MINORVERSION, VER_GREATER_EQUAL);
+ VER_SET_CONDITION(version_condition_mask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
+ VER_SET_CONDITION(version_condition_mask, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL);
+
+ if (!VerifyVersionInfo(&version_test, version_test_mask, version_condition_mask))
+ return 0;
+
+ return 1;
}
#endif
diff --git a/tests-clar/attr/file.c b/tests-clar/attr/file.c
deleted file mode 100644
index 8866fd9bd..000000000
--- a/tests-clar/attr/file.c
+++ /dev/null
@@ -1,226 +0,0 @@
-#include "clar_libgit2.h"
-#include "attr_file.h"
-#include "attr_expect.h"
-
-#define get_rule(X) ((git_attr_rule *)git_vector_get(&file->rules,(X)))
-#define get_assign(R,Y) ((git_attr_assignment *)git_vector_get(&(R)->assigns,(Y)))
-
-void test_attr_file__simple_read(void)
-{
- git_attr_file *file;
- git_attr_assignment *assign;
- git_attr_rule *rule;
-
- cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr0")));
-
- cl_assert_equal_s(cl_fixture("attr/attr0"), file->key + 2);
- cl_assert(file->rules.length == 1);
-
- rule = get_rule(0);
- cl_assert(rule != NULL);
- cl_assert_equal_s("*", rule->match.pattern);
- cl_assert(rule->match.length == 1);
- cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_HASWILD) != 0);
-
- cl_assert(rule->assigns.length == 1);
- assign = get_assign(rule, 0);
- cl_assert(assign != NULL);
- cl_assert_equal_s("binary", assign->name);
- cl_assert(GIT_ATTR_TRUE(assign->value));
-
- git_attr_file__free(file);
-}
-
-void test_attr_file__match_variants(void)
-{
- git_attr_file *file;
- git_attr_rule *rule;
- git_attr_assignment *assign;
-
- cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr1")));
-
- cl_assert_equal_s(cl_fixture("attr/attr1"), file->key + 2);
- cl_assert(file->rules.length == 10);
-
- /* let's do a thorough check of this rule, then just verify
- * the things that are unique for the later rules
- */
- rule = get_rule(0);
- cl_assert(rule);
- cl_assert_equal_s("pat0", rule->match.pattern);
- cl_assert(rule->match.length == strlen("pat0"));
- cl_assert(rule->match.flags == 0);
- cl_assert(rule->assigns.length == 1);
- assign = get_assign(rule,0);
- cl_assert_equal_s("attr0", assign->name);
- cl_assert(assign->name_hash == git_attr_file__name_hash(assign->name));
- cl_assert(GIT_ATTR_TRUE(assign->value));
-
- rule = get_rule(1);
- cl_assert_equal_s("pat1", rule->match.pattern);
- cl_assert(rule->match.length == strlen("pat1"));
- cl_assert(rule->match.flags == GIT_ATTR_FNMATCH_NEGATIVE);
-
- rule = get_rule(2);
- cl_assert_equal_s("pat2", rule->match.pattern);
- cl_assert(rule->match.length == strlen("pat2"));
- cl_assert(rule->match.flags == GIT_ATTR_FNMATCH_DIRECTORY);
-
- rule = get_rule(3);
- cl_assert_equal_s("pat3dir/pat3file", rule->match.pattern);
- cl_assert(rule->match.flags == GIT_ATTR_FNMATCH_FULLPATH);
-
- rule = get_rule(4);
- cl_assert_equal_s("pat4.*", rule->match.pattern);
- cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_HASWILD) != 0);
-
- rule = get_rule(5);
- cl_assert_equal_s("*.pat5", rule->match.pattern);
- cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_HASWILD) != 0);
-
- rule = get_rule(7);
- cl_assert_equal_s("pat7[a-e]??[xyz]", rule->match.pattern);
- cl_assert(rule->assigns.length == 1);
- cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_HASWILD) != 0);
- assign = get_assign(rule,0);
- cl_assert_equal_s("attr7", assign->name);
- cl_assert(GIT_ATTR_TRUE(assign->value));
-
- rule = get_rule(8);
- cl_assert_equal_s("pat8 with spaces", rule->match.pattern);
- cl_assert(rule->match.length == strlen("pat8 with spaces"));
- cl_assert(rule->match.flags == 0);
-
- rule = get_rule(9);
- cl_assert_equal_s("pat9", rule->match.pattern);
-
- git_attr_file__free(file);
-}
-
-static void check_one_assign(
- git_attr_file *file,
- int rule_idx,
- int assign_idx,
- const char *pattern,
- const char *name,
- enum attr_expect_t expected,
- const char *expected_str)
-{
- git_attr_rule *rule = get_rule(rule_idx);
- git_attr_assignment *assign = get_assign(rule, assign_idx);
-
- cl_assert_equal_s(pattern, rule->match.pattern);
- cl_assert(rule->assigns.length == 1);
- cl_assert_equal_s(name, assign->name);
- cl_assert(assign->name_hash == git_attr_file__name_hash(assign->name));
-
- attr_check_expected(expected, expected_str, assign->name, assign->value);
-}
-
-void test_attr_file__assign_variants(void)
-{
- git_attr_file *file;
- git_attr_rule *rule;
- git_attr_assignment *assign;
-
- cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr2")));
-
- cl_assert_equal_s(cl_fixture("attr/attr2"), file->key + 2);
- cl_assert(file->rules.length == 11);
-
- check_one_assign(file, 0, 0, "pat0", "simple", EXPECT_TRUE, NULL);
- check_one_assign(file, 1, 0, "pat1", "neg", EXPECT_FALSE, NULL);
- check_one_assign(file, 2, 0, "*", "notundef", EXPECT_TRUE, NULL);
- check_one_assign(file, 3, 0, "pat2", "notundef", EXPECT_UNDEFINED, NULL);
- check_one_assign(file, 4, 0, "pat3", "assigned", EXPECT_STRING, "test-value");
- check_one_assign(file, 5, 0, "pat4", "rule-with-more-chars", EXPECT_STRING, "value-with-more-chars");
- check_one_assign(file, 6, 0, "pat5", "empty", EXPECT_TRUE, NULL);
- check_one_assign(file, 7, 0, "pat6", "negempty", EXPECT_FALSE, NULL);
-
- rule = get_rule(8);
- cl_assert_equal_s("pat7", rule->match.pattern);
- cl_assert(rule->assigns.length == 5);
- /* assignments will be sorted by hash value, so we have to do
- * lookups by search instead of by position
- */
- assign = git_attr_rule__lookup_assignment(rule, "multiple");
- cl_assert(assign);
- cl_assert_equal_s("multiple", assign->name);
- cl_assert(GIT_ATTR_TRUE(assign->value));
- assign = git_attr_rule__lookup_assignment(rule, "single");
- cl_assert(assign);
- cl_assert_equal_s("single", assign->name);
- cl_assert(GIT_ATTR_FALSE(assign->value));
- assign = git_attr_rule__lookup_assignment(rule, "values");
- cl_assert(assign);
- cl_assert_equal_s("values", assign->name);
- cl_assert_equal_s("1", assign->value);
- assign = git_attr_rule__lookup_assignment(rule, "also");
- cl_assert(assign);
- cl_assert_equal_s("also", assign->name);
- cl_assert_equal_s("a-really-long-value/*", assign->value);
- assign = git_attr_rule__lookup_assignment(rule, "happy");
- cl_assert(assign);
- cl_assert_equal_s("happy", assign->name);
- cl_assert_equal_s("yes!", assign->value);
- assign = git_attr_rule__lookup_assignment(rule, "other");
- cl_assert(!assign);
-
- rule = get_rule(9);
- cl_assert_equal_s("pat8", rule->match.pattern);
- cl_assert(rule->assigns.length == 2);
- assign = git_attr_rule__lookup_assignment(rule, "again");
- cl_assert(assign);
- cl_assert_equal_s("again", assign->name);
- cl_assert(GIT_ATTR_TRUE(assign->value));
- assign = git_attr_rule__lookup_assignment(rule, "another");
- cl_assert(assign);
- cl_assert_equal_s("another", assign->name);
- cl_assert_equal_s("12321", assign->value);
-
- check_one_assign(file, 10, 0, "pat9", "at-eof", EXPECT_FALSE, NULL);
-
- git_attr_file__free(file);
-}
-
-void test_attr_file__check_attr_examples(void)
-{
- git_attr_file *file;
- git_attr_rule *rule;
- git_attr_assignment *assign;
-
- cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr3")));
- cl_assert_equal_s(cl_fixture("attr/attr3"), file->key + 2);
- cl_assert(file->rules.length == 3);
-
- rule = get_rule(0);
- cl_assert_equal_s("*.java", rule->match.pattern);
- cl_assert(rule->assigns.length == 3);
- assign = git_attr_rule__lookup_assignment(rule, "diff");
- cl_assert_equal_s("diff", assign->name);
- cl_assert_equal_s("java", assign->value);
- assign = git_attr_rule__lookup_assignment(rule, "crlf");
- cl_assert_equal_s("crlf", assign->name);
- cl_assert(GIT_ATTR_FALSE(assign->value));
- assign = git_attr_rule__lookup_assignment(rule, "myAttr");
- cl_assert_equal_s("myAttr", assign->name);
- cl_assert(GIT_ATTR_TRUE(assign->value));
- assign = git_attr_rule__lookup_assignment(rule, "missing");
- cl_assert(assign == NULL);
-
- rule = get_rule(1);
- cl_assert_equal_s("NoMyAttr.java", rule->match.pattern);
- cl_assert(rule->assigns.length == 1);
- assign = get_assign(rule, 0);
- cl_assert_equal_s("myAttr", assign->name);
- cl_assert(GIT_ATTR_UNSPECIFIED(assign->value));
-
- rule = get_rule(2);
- cl_assert_equal_s("README", rule->match.pattern);
- cl_assert(rule->assigns.length == 1);
- assign = get_assign(rule, 0);
- cl_assert_equal_s("caveat", assign->name);
- cl_assert_equal_s("unspecified", assign->value);
-
- git_attr_file__free(file);
-}
diff --git a/tests-clar/attr/repo.c b/tests-clar/attr/repo.c
deleted file mode 100644
index ca3e71e7f..000000000
--- a/tests-clar/attr/repo.c
+++ /dev/null
@@ -1,294 +0,0 @@
-#include "clar_libgit2.h"
-#include "fileops.h"
-#include "git2/attr.h"
-#include "attr.h"
-
-#include "attr_expect.h"
-
-static git_repository *g_repo = NULL;
-
-void test_attr_repo__initialize(void)
-{
- /* Before each test, instantiate the attr repo from the fixtures and
- * rename the .gitted to .git so it is a repo with a working dir.
- * Also rename gitattributes to .gitattributes, because it contains
- * macro definitions which are only allowed in the root.
- */
- g_repo = cl_git_sandbox_init("attr");
-}
-
-void test_attr_repo__cleanup(void)
-{
- cl_git_sandbox_cleanup();
- g_repo = NULL;
-}
-
-void test_attr_repo__get_one(void)
-{
- struct attr_expected test_cases[] = {
- { "root_test1", "repoattr", EXPECT_TRUE, NULL },
- { "root_test1", "rootattr", EXPECT_TRUE, NULL },
- { "root_test1", "missingattr", EXPECT_UNDEFINED, NULL },
- { "root_test1", "subattr", EXPECT_UNDEFINED, NULL },
- { "root_test1", "negattr", EXPECT_UNDEFINED, NULL },
- { "root_test2", "repoattr", EXPECT_TRUE, NULL },
- { "root_test2", "rootattr", EXPECT_FALSE, NULL },
- { "root_test2", "missingattr", EXPECT_UNDEFINED, NULL },
- { "root_test2", "multiattr", EXPECT_FALSE, NULL },
- { "root_test3", "repoattr", EXPECT_TRUE, NULL },
- { "root_test3", "rootattr", EXPECT_UNDEFINED, NULL },
- { "root_test3", "multiattr", EXPECT_STRING, "3" },
- { "root_test3", "multi2", EXPECT_UNDEFINED, NULL },
- { "sub/subdir_test1", "repoattr", EXPECT_TRUE, NULL },
- { "sub/subdir_test1", "rootattr", EXPECT_TRUE, NULL },
- { "sub/subdir_test1", "missingattr", EXPECT_UNDEFINED, NULL },
- { "sub/subdir_test1", "subattr", EXPECT_STRING, "yes" },
- { "sub/subdir_test1", "negattr", EXPECT_FALSE, NULL },
- { "sub/subdir_test1", "another", EXPECT_UNDEFINED, NULL },
- { "sub/subdir_test2.txt", "repoattr", EXPECT_TRUE, NULL },
- { "sub/subdir_test2.txt", "rootattr", EXPECT_TRUE, NULL },
- { "sub/subdir_test2.txt", "missingattr", EXPECT_UNDEFINED, NULL },
- { "sub/subdir_test2.txt", "subattr", EXPECT_STRING, "yes" },
- { "sub/subdir_test2.txt", "negattr", EXPECT_FALSE, NULL },
- { "sub/subdir_test2.txt", "another", EXPECT_STRING, "zero" },
- { "sub/subdir_test2.txt", "reposub", EXPECT_TRUE, NULL },
- { "sub/sub/subdir.txt", "another", EXPECT_STRING, "one" },
- { "sub/sub/subdir.txt", "reposubsub", EXPECT_TRUE, NULL },
- { "sub/sub/subdir.txt", "reposub", EXPECT_UNDEFINED, NULL },
- { "does-not-exist", "foo", EXPECT_STRING, "yes" },
- { "sub/deep/file", "deepdeep", EXPECT_TRUE, NULL },
- { "sub/sub/d/no", "test", EXPECT_STRING, "a/b/d/*" },
- { "sub/sub/d/yes", "test", EXPECT_UNDEFINED, NULL },
- { NULL, NULL, 0, NULL }
- }, *scan;
-
- for (scan = test_cases; scan->path != NULL; scan++) {
- const char *value;
- cl_git_pass(git_attr_get(&value, g_repo, 0, scan->path, scan->attr));
- attr_check_expected(scan->expected, scan->expected_str, scan->attr, value);
- }
-
- cl_assert(git_attr_cache__is_cached(g_repo, 0, ".git/info/attributes"));
- cl_assert(git_attr_cache__is_cached(g_repo, 0, ".gitattributes"));
- cl_assert(git_attr_cache__is_cached(g_repo, 0, "sub/.gitattributes"));
-}
-
-void test_attr_repo__get_many(void)
-{
- const char *names[4] = { "repoattr", "rootattr", "missingattr", "subattr" };
- const char *values[4];
-
- cl_git_pass(git_attr_get_many(values, g_repo, 0, "root_test1", 4, names));
-
- cl_assert(GIT_ATTR_TRUE(values[0]));
- cl_assert(GIT_ATTR_TRUE(values[1]));
- cl_assert(GIT_ATTR_UNSPECIFIED(values[2]));
- cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));
-
- cl_git_pass(git_attr_get_many(values, g_repo, 0, "root_test2", 4, names));
-
- cl_assert(GIT_ATTR_TRUE(values[0]));
- cl_assert(GIT_ATTR_FALSE(values[1]));
- cl_assert(GIT_ATTR_UNSPECIFIED(values[2]));
- cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));
-
- cl_git_pass(git_attr_get_many(values, g_repo, 0, "sub/subdir_test1", 4, names));
-
- cl_assert(GIT_ATTR_TRUE(values[0]));
- cl_assert(GIT_ATTR_TRUE(values[1]));
- cl_assert(GIT_ATTR_UNSPECIFIED(values[2]));
- cl_assert_equal_s("yes", values[3]);
-}
-
-static int count_attrs(
- const char *name,
- const char *value,
- void *payload)
-{
- GIT_UNUSED(name);
- GIT_UNUSED(value);
-
- *((int *)payload) += 1;
-
- return 0;
-}
-
-static int cancel_iteration(
- const char *name,
- const char *value,
- void *payload)
-{
- GIT_UNUSED(name);
- GIT_UNUSED(value);
-
- *((int *)payload) -= 1;
-
- if (*((int *)payload) < 0)
- return -1;
-
- return 0;
-}
-
-void test_attr_repo__foreach(void)
-{
- int count;
-
- count = 0;
- cl_git_pass(git_attr_foreach(
- g_repo, 0, "root_test1", &count_attrs, &count));
- cl_assert(count == 2);
-
- count = 0;
- cl_git_pass(git_attr_foreach(g_repo, 0, "sub/subdir_test1",
- &count_attrs, &count));
- cl_assert(count == 4); /* repoattr, rootattr, subattr, negattr */
-
- count = 0;
- cl_git_pass(git_attr_foreach(g_repo, 0, "sub/subdir_test2.txt",
- &count_attrs, &count));
- cl_assert(count == 6); /* repoattr, rootattr, subattr, reposub, negattr, another */
-
- count = 2;
- cl_assert_equal_i(
- GIT_EUSER, git_attr_foreach(
- g_repo, 0, "sub/subdir_test1", &cancel_iteration, &count)
- );
-}
-
-void test_attr_repo__manpage_example(void)
-{
- const char *value;
-
- cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "foo"));
- cl_assert(GIT_ATTR_TRUE(value));
-
- cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "bar"));
- cl_assert(GIT_ATTR_UNSPECIFIED(value));
-
- cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "baz"));
- cl_assert(GIT_ATTR_FALSE(value));
-
- cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "merge"));
- cl_assert_equal_s("filfre", value);
-
- cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "frotz"));
- cl_assert(GIT_ATTR_UNSPECIFIED(value));
-}
-
-void test_attr_repo__macros(void)
-{
- const char *names[5] = { "rootattr", "binary", "diff", "crlf", "frotz" };
- const char *names2[5] = { "mymacro", "positive", "negative", "rootattr", "another" };
- const char *names3[3] = { "macro2", "multi2", "multi3" };
- const char *values[5];
-
- cl_git_pass(git_attr_get_many(values, g_repo, 0, "binfile", 5, names));
-
- cl_assert(GIT_ATTR_TRUE(values[0]));
- cl_assert(GIT_ATTR_TRUE(values[1]));
- cl_assert(GIT_ATTR_FALSE(values[2]));
- cl_assert(GIT_ATTR_FALSE(values[3]));
- cl_assert(GIT_ATTR_UNSPECIFIED(values[4]));
-
- cl_git_pass(git_attr_get_many(values, g_repo, 0, "macro_test", 5, names2));
-
- cl_assert(GIT_ATTR_TRUE(values[0]));
- cl_assert(GIT_ATTR_TRUE(values[1]));
- cl_assert(GIT_ATTR_FALSE(values[2]));
- cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));
- cl_assert_equal_s("77", values[4]);
-
- cl_git_pass(git_attr_get_many(values, g_repo, 0, "macro_test", 3, names3));
-
- cl_assert(GIT_ATTR_TRUE(values[0]));
- cl_assert(GIT_ATTR_FALSE(values[1]));
- cl_assert_equal_s("answer", values[2]);
-}
-
-void test_attr_repo__bad_macros(void)
-{
- const char *names[6] = { "rootattr", "positive", "negative",
- "firstmacro", "secondmacro", "thirdmacro" };
- const char *values[6];
-
- cl_git_pass(git_attr_get_many(values, g_repo, 0, "macro_bad", 6, names));
-
- /* these three just confirm that the "mymacro" rule ran */
- cl_assert(GIT_ATTR_UNSPECIFIED(values[0]));
- cl_assert(GIT_ATTR_TRUE(values[1]));
- cl_assert(GIT_ATTR_FALSE(values[2]));
-
- /* file contains:
- * # let's try some malicious macro defs
- * [attr]firstmacro -thirdmacro -secondmacro
- * [attr]secondmacro firstmacro -firstmacro
- * [attr]thirdmacro secondmacro=hahaha -firstmacro
- * macro_bad firstmacro secondmacro thirdmacro
- *
- * firstmacro assignment list ends up with:
- * -thirdmacro -secondmacro
- * secondmacro assignment list expands "firstmacro" and ends up with:
- * -thirdmacro -secondmacro -firstmacro
- * thirdmacro assignment don't expand so list ends up with:
- * secondmacro="hahaha"
- *
- * macro_bad assignment list ends up with:
- * -thirdmacro -secondmacro firstmacro &&
- * -thirdmacro -secondmacro -firstmacro secondmacro &&
- * secondmacro="hahaha" thirdmacro
- *
- * so summary results should be:
- * -firstmacro secondmacro="hahaha" thirdmacro
- */
- cl_assert(GIT_ATTR_FALSE(values[3]));
- cl_assert_equal_s("hahaha", values[4]);
- cl_assert(GIT_ATTR_TRUE(values[5]));
-}
-
-#define CONTENT "I'm going to be dynamically processed\r\n" \
- "And my line endings...\r\n" \
- "...are going to be\n" \
- "normalized!\r\n"
-
-#define GITATTR "* text=auto\n" \
- "*.txt text\n" \
- "*.data binary\n"
-
-static void add_to_workdir(const char *filename, const char *content)
-{
- git_buf buf = GIT_BUF_INIT;
-
- cl_git_pass(git_buf_joinpath(&buf, "attr", filename));
- cl_git_rewritefile(git_buf_cstr(&buf), content);
-
- git_buf_free(&buf);
-}
-
-static void assert_proper_normalization(git_index *index, const char *filename, const char *expected_sha)
-{
- size_t index_pos;
- const git_index_entry *entry;
-
- add_to_workdir(filename, CONTENT);
- cl_git_pass(git_index_add_bypath(index, filename));
-
- cl_assert(!git_index_find(&index_pos, index, filename));
-
- entry = git_index_get_byindex(index, index_pos);
- cl_assert_equal_i(0, git_oid_streq(&entry->oid, expected_sha));
-}
-
-void test_attr_repo__staging_properly_normalizes_line_endings_according_to_gitattributes_directives(void)
-{
- git_index* index;
-
- cl_git_pass(git_repository_index(&index, g_repo));
-
- add_to_workdir(".gitattributes", GITATTR);
-
- assert_proper_normalization(index, "text.txt", "22c74203bace3c2e950278c7ab08da0fca9f4e9b");
- assert_proper_normalization(index, "huh.dunno", "22c74203bace3c2e950278c7ab08da0fca9f4e9b");
- assert_proper_normalization(index, "binary.data", "66eeff1fcbacf589e6d70aa70edd3fce5be2b37c");
-
- git_index_free(index);
-}
diff --git a/tests-clar/checkout/checkout_helpers.c b/tests-clar/checkout/checkout_helpers.c
deleted file mode 100644
index 8da024dda..000000000
--- a/tests-clar/checkout/checkout_helpers.c
+++ /dev/null
@@ -1,188 +0,0 @@
-#include "clar_libgit2.h"
-#include "checkout_helpers.h"
-#include "refs.h"
-#include "fileops.h"
-
-/* this is essentially the code from git__unescape modified slightly */
-void strip_cr_from_buf(git_buf *buf)
-{
- char *scan, *pos = buf->ptr, *end = pos + buf->size;
-
- for (scan = pos; scan < end; pos++, scan++) {
- if (*scan == '\r')
- scan++; /* skip '\r' */
- if (pos != scan)
- *pos = *scan;
- }
-
- *pos = '\0';
- buf->size = (pos - buf->ptr);
-}
-
-void assert_on_branch(git_repository *repo, const char *branch)
-{
- git_reference *head;
- git_buf bname = GIT_BUF_INIT;
-
- cl_git_pass(git_reference_lookup(&head, repo, GIT_HEAD_FILE));
- cl_assert_(git_reference_type(head) == GIT_REF_SYMBOLIC, branch);
-
- cl_git_pass(git_buf_joinpath(&bname, "refs/heads", branch));
- cl_assert_equal_s(bname.ptr, git_reference_symbolic_target(head));
-
- git_reference_free(head);
- git_buf_free(&bname);
-}
-
-void reset_index_to_treeish(git_object *treeish)
-{
- git_object *tree;
- git_index *index;
- git_repository *repo = git_object_owner(treeish);
-
- cl_git_pass(git_object_peel(&tree, treeish, GIT_OBJ_TREE));
-
- cl_git_pass(git_repository_index(&index, repo));
- cl_git_pass(git_index_read_tree(index, (git_tree *)tree));
- cl_git_pass(git_index_write(index));
-
- git_object_free(tree);
- git_index_free(index);
-}
-
-static void check_file_contents_internal(
- const char *path,
- const char *expected_content,
- bool strip_cr,
- const char *file,
- int line,
- const char *msg)
-{
- int fd;
- char data[1024] = {0};
- git_buf buf = GIT_BUF_INIT;
- size_t expected_len = expected_content ? strlen(expected_content) : 0;
-
- fd = p_open(path, O_RDONLY);
- cl_assert(fd >= 0);
-
- buf.ptr = data;
- buf.size = p_read(fd, buf.ptr, sizeof(data));
-
- cl_git_pass(p_close(fd));
-
- if (strip_cr)
- strip_cr_from_buf(&buf);
-
- clar__assert_equal_i((int)expected_len, (int)buf.size, file, line, "strlen(expected_content) != strlen(actual_content)", 1);
- clar__assert_equal_s(expected_content, buf.ptr, file, line, msg, 1);
-}
-
-void check_file_contents_at_line(
- const char *path, const char *expected,
- const char *file, int line, const char *msg)
-{
- check_file_contents_internal(path, expected, false, file, line, msg);
-}
-
-void check_file_contents_nocr_at_line(
- const char *path, const char *expected,
- const char *file, int line, const char *msg)
-{
- check_file_contents_internal(path, expected, true, file, line, msg);
-}
-
-int checkout_count_callback(
- git_checkout_notify_t why,
- const char *path,
- const git_diff_file *baseline,
- const git_diff_file *target,
- const git_diff_file *workdir,
- void *payload)
-{
- checkout_counts *ct = payload;
-
- GIT_UNUSED(baseline); GIT_UNUSED(target); GIT_UNUSED(workdir);
-
- if (why & GIT_CHECKOUT_NOTIFY_CONFLICT) {
- ct->n_conflicts++;
-
- if (ct->debug) {
- if (workdir) {
- if (baseline) {
- if (target)
- fprintf(stderr, "M %s (conflicts with M %s)\n",
- workdir->path, target->path);
- else
- fprintf(stderr, "M %s (conflicts with D %s)\n",
- workdir->path, baseline->path);
- } else {
- if (target)
- fprintf(stderr, "Existing %s (conflicts with A %s)\n",
- workdir->path, target->path);
- else
- fprintf(stderr, "How can an untracked file be a conflict (%s)\n", workdir->path);
- }
- } else {
- if (baseline) {
- if (target)
- fprintf(stderr, "D %s (conflicts with M %s)\n",
- target->path, baseline->path);
- else
- fprintf(stderr, "D %s (conflicts with D %s)\n",
- baseline->path, baseline->path);
- } else {
- if (target)
- fprintf(stderr, "How can an added file with no workdir be a conflict (%s)\n", target->path);
- else
- fprintf(stderr, "How can a nonexistent file be a conflict (%s)\n", path);
- }
- }
- }
- }
-
- if (why & GIT_CHECKOUT_NOTIFY_DIRTY) {
- ct->n_dirty++;
-
- if (ct->debug) {
- if (workdir)
- fprintf(stderr, "M %s\n", workdir->path);
- else
- fprintf(stderr, "D %s\n", baseline->path);
- }
- }
-
- if (why & GIT_CHECKOUT_NOTIFY_UPDATED) {
- ct->n_updates++;
-
- if (ct->debug) {
- if (baseline) {
- if (target)
- fprintf(stderr, "update: M %s\n", path);
- else
- fprintf(stderr, "update: D %s\n", path);
- } else {
- if (target)
- fprintf(stderr, "update: A %s\n", path);
- else
- fprintf(stderr, "update: this makes no sense %s\n", path);
- }
- }
- }
-
- if (why & GIT_CHECKOUT_NOTIFY_UNTRACKED) {
- ct->n_untracked++;
-
- if (ct->debug)
- fprintf(stderr, "? %s\n", path);
- }
-
- if (why & GIT_CHECKOUT_NOTIFY_IGNORED) {
- ct->n_ignored++;
-
- if (ct->debug)
- fprintf(stderr, "I %s\n", path);
- }
-
- return 0;
-}
diff --git a/tests-clar/checkout/checkout_helpers.h b/tests-clar/checkout/checkout_helpers.h
deleted file mode 100644
index 0e8da31d1..000000000
--- a/tests-clar/checkout/checkout_helpers.h
+++ /dev/null
@@ -1,38 +0,0 @@
-#include "buffer.h"
-#include "git2/object.h"
-#include "git2/repository.h"
-
-extern void strip_cr_from_buf(git_buf *buf);
-extern void assert_on_branch(git_repository *repo, const char *branch);
-extern void reset_index_to_treeish(git_object *treeish);
-
-extern void check_file_contents_at_line(
- const char *path, const char *expected,
- const char *file, int line, const char *msg);
-
-extern void check_file_contents_nocr_at_line(
- const char *path, const char *expected,
- const char *file, int line, const char *msg);
-
-#define check_file_contents(PATH,EXP) \
- check_file_contents_at_line(PATH,EXP,__FILE__,__LINE__,"String mismatch: " #EXP " != " #PATH)
-
-#define check_file_contents_nocr(PATH,EXP) \
- check_file_contents_nocr_at_line(PATH,EXP,__FILE__,__LINE__,"String mismatch: " #EXP " != " #PATH)
-
-typedef struct {
- int n_conflicts;
- int n_dirty;
- int n_updates;
- int n_untracked;
- int n_ignored;
- int debug;
-} checkout_counts;
-
-extern int checkout_count_callback(
- git_checkout_notify_t why,
- const char *path,
- const git_diff_file *baseline,
- const git_diff_file *target,
- const git_diff_file *workdir,
- void *payload);
diff --git a/tests-clar/checkout/crlf.c b/tests-clar/checkout/crlf.c
deleted file mode 100644
index 285b1f272..000000000
--- a/tests-clar/checkout/crlf.c
+++ /dev/null
@@ -1,147 +0,0 @@
-#include "clar_libgit2.h"
-#include "checkout_helpers.h"
-
-#include "git2/checkout.h"
-#include "repository.h"
-
-#define UTF8_BOM "\xEF\xBB\xBF"
-#define ALL_CRLF_TEXT_RAW "crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n"
-#define ALL_LF_TEXT_RAW "lf\nlf\nlf\nlf\nlf\n"
-#define MORE_CRLF_TEXT_RAW "crlf\r\ncrlf\r\nlf\ncrlf\r\ncrlf\r\n"
-#define MORE_LF_TEXT_RAW "lf\nlf\ncrlf\r\nlf\nlf\n"
-
-#define ALL_LF_TEXT_AS_CRLF "lf\r\nlf\r\nlf\r\nlf\r\nlf\r\n"
-#define MORE_CRLF_TEXT_AS_CRLF "crlf\r\ncrlf\r\nlf\r\ncrlf\r\ncrlf\r\n"
-#define MORE_LF_TEXT_AS_CRLF "lf\r\nlf\r\ncrlf\r\nlf\r\nlf\r\n"
-
-static git_repository *g_repo;
-
-void test_checkout_crlf__initialize(void)
-{
- g_repo = cl_git_sandbox_init("crlf");
-}
-
-void test_checkout_crlf__cleanup(void)
-{
- cl_git_sandbox_cleanup();
-}
-
-void test_checkout_crlf__detect_crlf_autocrlf_false(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
-
- cl_repo_set_bool(g_repo, "core.autocrlf", false);
-
- git_checkout_head(g_repo, &opts);
-
- check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW);
- check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
-}
-
-void test_checkout_crlf__autocrlf_false_index_size_is_unfiltered_size(void)
-{
- git_index *index;
- const git_index_entry *entry;
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
-
- cl_repo_set_bool(g_repo, "core.autocrlf", false);
-
- git_checkout_head(g_repo, &opts);
-
- git_repository_index(&index, g_repo);
-
- cl_assert((entry = git_index_get_bypath(index, "all-lf", 0)) != NULL);
- cl_assert(entry->file_size == strlen(ALL_LF_TEXT_RAW));
-
- cl_assert((entry = git_index_get_bypath(index, "all-crlf", 0)) != NULL);
- cl_assert(entry->file_size == strlen(ALL_CRLF_TEXT_RAW));
-
- git_index_free(index);
-}
-
-void test_checkout_crlf__detect_crlf_autocrlf_true(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
-
- cl_repo_set_bool(g_repo, "core.autocrlf", true);
-
- git_checkout_head(g_repo, &opts);
-
- if (GIT_EOL_NATIVE == GIT_EOL_LF)
- check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW);
- else
- check_file_contents("./crlf/all-lf", ALL_LF_TEXT_AS_CRLF);
-
- check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
-}
-
-void test_checkout_crlf__more_lf_autocrlf_true(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
-
- cl_repo_set_bool(g_repo, "core.autocrlf", true);
-
- git_checkout_head(g_repo, &opts);
-
- if (GIT_EOL_NATIVE == GIT_EOL_LF)
- check_file_contents("./crlf/more-lf", MORE_LF_TEXT_RAW);
- else
- check_file_contents("./crlf/more-lf", MORE_LF_TEXT_AS_CRLF);
-}
-
-void test_checkout_crlf__more_crlf_autocrlf_true(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
-
- cl_repo_set_bool(g_repo, "core.autocrlf", true);
-
- git_checkout_head(g_repo, &opts);
-
- if (GIT_EOL_NATIVE == GIT_EOL_LF)
- check_file_contents("./crlf/more-crlf", MORE_CRLF_TEXT_RAW);
- else
- check_file_contents("./crlf/more-crlf", MORE_CRLF_TEXT_AS_CRLF);
-}
-
-void test_checkout_crlf__all_crlf_autocrlf_true(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
-
- cl_repo_set_bool(g_repo, "core.autocrlf", true);
-
- git_checkout_head(g_repo, &opts);
-
- check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
-}
-
-void test_checkout_crlf__autocrlf_true_index_size_is_filtered_size(void)
-{
- git_index *index;
- const git_index_entry *entry;
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
-
- cl_repo_set_bool(g_repo, "core.autocrlf", true);
-
- git_checkout_head(g_repo, &opts);
-
- git_repository_index(&index, g_repo);
-
- cl_assert((entry = git_index_get_bypath(index, "all-lf", 0)) != NULL);
-
- if (GIT_EOL_NATIVE == GIT_EOL_LF)
- cl_assert_equal_sz(strlen(ALL_LF_TEXT_RAW), entry->file_size);
- else
- cl_assert_equal_sz(strlen(ALL_LF_TEXT_AS_CRLF), entry->file_size);
-
- cl_assert((entry = git_index_get_bypath(index, "all-crlf", 0)) != NULL);
- cl_assert_equal_sz(strlen(ALL_CRLF_TEXT_RAW), entry->file_size);
-
- git_index_free(index);
-}
diff --git a/tests-clar/checkout/head.c b/tests-clar/checkout/head.c
deleted file mode 100644
index 46646f8bf..000000000
--- a/tests-clar/checkout/head.c
+++ /dev/null
@@ -1,63 +0,0 @@
-#include "clar_libgit2.h"
-#include "refs.h"
-#include "repo/repo_helpers.h"
-#include "path.h"
-#include "fileops.h"
-
-static git_repository *g_repo;
-
-void test_checkout_head__initialize(void)
-{
- g_repo = cl_git_sandbox_init("testrepo");
-}
-
-void test_checkout_head__cleanup(void)
-{
- cl_git_sandbox_cleanup();
-}
-
-void test_checkout_head__orphaned_head_returns_GIT_EORPHANEDHEAD(void)
-{
- make_head_orphaned(g_repo, NON_EXISTING_HEAD);
-
- cl_assert_equal_i(GIT_EORPHANEDHEAD, git_checkout_head(g_repo, NULL));
-}
-
-void test_checkout_head__with_index_only_tree(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- git_index *index;
-
- /* let's start by getting things into a known state */
-
- opts.checkout_strategy = GIT_CHECKOUT_FORCE;
- cl_git_pass(git_checkout_head(g_repo, &opts));
-
- /* now let's stage some new stuff including a new directory */
-
- cl_git_pass(git_repository_index(&index, g_repo));
-
- p_mkdir("testrepo/newdir", 0777);
- cl_git_mkfile("testrepo/newdir/newfile.txt", "new file\n");
-
- cl_git_pass(git_index_add_bypath(index, "newdir/newfile.txt"));
- cl_git_pass(git_index_write(index));
-
- cl_assert(git_path_isfile("testrepo/newdir/newfile.txt"));
- cl_assert(git_index_get_bypath(index, "newdir/newfile.txt", 0) != NULL);
-
- git_index_free(index);
-
- /* okay, so now we have staged this new file; let's see if we can remove */
-
- opts.checkout_strategy = GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED;
- cl_git_pass(git_checkout_head(g_repo, &opts));
-
- cl_git_pass(git_repository_index(&index, g_repo));
- cl_git_pass(git_index_read(index)); /* reload if needed */
-
- cl_assert(!git_path_isfile("testrepo/newdir/newfile.txt"));
- cl_assert(git_index_get_bypath(index, "newdir/newfile.txt", 0) == NULL);
-
- git_index_free(index);
-}
diff --git a/tests-clar/checkout/index.c b/tests-clar/checkout/index.c
deleted file mode 100644
index 9d8b321ae..000000000
--- a/tests-clar/checkout/index.c
+++ /dev/null
@@ -1,612 +0,0 @@
-#include "clar_libgit2.h"
-#include "checkout_helpers.h"
-
-#include "git2/checkout.h"
-#include "fileops.h"
-#include "repository.h"
-
-static git_repository *g_repo;
-
-void test_checkout_index__initialize(void)
-{
- git_tree *tree;
-
- g_repo = cl_git_sandbox_init("testrepo");
-
- cl_git_pass(git_repository_head_tree(&tree, g_repo));
-
- reset_index_to_treeish((git_object *)tree);
- git_tree_free(tree);
-
- cl_git_rewritefile(
- "./testrepo/.gitattributes",
- "* text eol=lf\n");
-}
-
-void test_checkout_index__cleanup(void)
-{
- cl_git_sandbox_cleanup();
-
- /* try to remove alternative dir */
- if (git_path_isdir("alternative"))
- git_futils_rmdir_r("alternative", NULL, GIT_RMDIR_REMOVE_FILES);
-}
-
-void test_checkout_index__cannot_checkout_a_bare_repository(void)
-{
- test_checkout_index__cleanup();
-
- g_repo = cl_git_sandbox_init("testrepo.git");
-
- cl_git_fail(git_checkout_index(g_repo, NULL, NULL));
-}
-
-void test_checkout_index__can_create_missing_files(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
-
- cl_assert_equal_i(false, git_path_isfile("./testrepo/README"));
- cl_assert_equal_i(false, git_path_isfile("./testrepo/branch_file.txt"));
- cl_assert_equal_i(false, git_path_isfile("./testrepo/new.txt"));
-
- opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
-
- cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
-
- check_file_contents("./testrepo/README", "hey there\n");
- check_file_contents("./testrepo/branch_file.txt", "hi\nbye!\n");
- check_file_contents("./testrepo/new.txt", "my new file\n");
-}
-
-void test_checkout_index__can_remove_untracked_files(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
-
- git_futils_mkdir("./testrepo/dir/subdir/subsubdir", NULL, 0755, GIT_MKDIR_PATH);
- cl_git_mkfile("./testrepo/dir/one", "one\n");
- cl_git_mkfile("./testrepo/dir/subdir/two", "two\n");
-
- cl_assert_equal_i(true, git_path_isdir("./testrepo/dir/subdir/subsubdir"));
-
- opts.checkout_strategy =
- GIT_CHECKOUT_SAFE_CREATE | GIT_CHECKOUT_REMOVE_UNTRACKED;
-
- cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
-
- cl_assert_equal_i(false, git_path_isdir("./testrepo/dir"));
-}
-
-void test_checkout_index__honor_the_specified_pathspecs(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- char *entries[] = { "*.txt" };
-
- opts.paths.strings = entries;
- opts.paths.count = 1;
-
- cl_assert_equal_i(false, git_path_isfile("./testrepo/README"));
- cl_assert_equal_i(false, git_path_isfile("./testrepo/branch_file.txt"));
- cl_assert_equal_i(false, git_path_isfile("./testrepo/new.txt"));
-
- opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
-
- cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
-
- cl_assert_equal_i(false, git_path_isfile("./testrepo/README"));
- check_file_contents("./testrepo/branch_file.txt", "hi\nbye!\n");
- check_file_contents("./testrepo/new.txt", "my new file\n");
-}
-
-void test_checkout_index__honor_the_gitattributes_directives(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- const char *attributes =
- "branch_file.txt text eol=crlf\n"
- "new.txt text eol=lf\n";
-
- cl_git_mkfile("./testrepo/.gitattributes", attributes);
- cl_repo_set_bool(g_repo, "core.autocrlf", false);
-
- opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
-
- cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
-
- check_file_contents("./testrepo/README", "hey there\n");
- check_file_contents("./testrepo/new.txt", "my new file\n");
- check_file_contents("./testrepo/branch_file.txt", "hi\r\nbye!\r\n");
-}
-
-void test_checkout_index__honor_coreautocrlf_setting_set_to_true(void)
-{
-#ifdef GIT_WIN32
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- const char *expected_readme_text = "hey there\r\n";
-
- cl_git_pass(p_unlink("./testrepo/.gitattributes"));
- cl_repo_set_bool(g_repo, "core.autocrlf", true);
-
- opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
-
- cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
-
- check_file_contents("./testrepo/README", expected_readme_text);
-#endif
-}
-
-void test_checkout_index__honor_coresymlinks_setting_set_to_true(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
-
- cl_repo_set_bool(g_repo, "core.symlinks", true);
-
- opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
-
- cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
-
-#ifdef GIT_WIN32
- check_file_contents("./testrepo/link_to_new.txt", "new.txt");
-#else
- {
- char link_data[1024];
- size_t link_size = 1024;
-
- link_size = p_readlink("./testrepo/link_to_new.txt", link_data, link_size);
- link_data[link_size] = '\0';
- cl_assert_equal_i(link_size, strlen("new.txt"));
- cl_assert_equal_s(link_data, "new.txt");
- check_file_contents("./testrepo/link_to_new.txt", "my new file\n");
- }
-#endif
-}
-
-void test_checkout_index__honor_coresymlinks_setting_set_to_false(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
-
- cl_repo_set_bool(g_repo, "core.symlinks", false);
-
- opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
-
- cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
-
- check_file_contents("./testrepo/link_to_new.txt", "new.txt");
-}
-
-void test_checkout_index__donot_overwrite_modified_file_by_default(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
-
- cl_git_mkfile("./testrepo/new.txt", "This isn't what's stored!");
-
- /* set this up to not return an error code on conflicts, but it
- * still will not have permission to overwrite anything...
- */
- opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_ALLOW_CONFLICTS;
-
- cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
-
- check_file_contents("./testrepo/new.txt", "This isn't what's stored!");
-}
-
-void test_checkout_index__can_overwrite_modified_file(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
-
- cl_git_mkfile("./testrepo/new.txt", "This isn't what's stored!");
-
- opts.checkout_strategy = GIT_CHECKOUT_FORCE;
-
- cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
-
- check_file_contents("./testrepo/new.txt", "my new file\n");
-}
-
-void test_checkout_index__options_disable_filters(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
-
- cl_git_mkfile("./testrepo/.gitattributes", "*.txt text eol=crlf\n");
-
- opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
- opts.disable_filters = false;
-
- cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
-
- check_file_contents("./testrepo/new.txt", "my new file\r\n");
-
- p_unlink("./testrepo/new.txt");
-
- opts.disable_filters = true;
- cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
-
- check_file_contents("./testrepo/new.txt", "my new file\n");
-}
-
-void test_checkout_index__options_dir_modes(void)
-{
-#ifndef GIT_WIN32
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- struct stat st;
- git_oid oid;
- git_commit *commit;
-
- cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
- cl_git_pass(git_commit_lookup(&commit, g_repo, &oid));
-
- reset_index_to_treeish((git_object *)commit);
-
- opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
- opts.dir_mode = 0701;
-
- cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
-
- cl_git_pass(p_stat("./testrepo/a", &st));
- cl_assert_equal_i(st.st_mode & 0777, 0701);
-
- /* File-mode test, since we're on the 'dir' branch */
- cl_git_pass(p_stat("./testrepo/a/b.txt", &st));
- cl_assert_equal_i(st.st_mode & 0777, 0755);
-
- git_commit_free(commit);
-#endif
-}
-
-void test_checkout_index__options_override_file_modes(void)
-{
-#ifndef GIT_WIN32
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- struct stat st;
-
- opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
- opts.file_mode = 0700;
-
- cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
-
- cl_git_pass(p_stat("./testrepo/new.txt", &st));
- cl_assert_equal_i(st.st_mode & 0777, 0700);
-#endif
-}
-
-void test_checkout_index__options_open_flags(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
-
- cl_git_mkfile("./testrepo/new.txt", "hi\n");
-
- opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
- opts.file_open_flags = O_CREAT | O_RDWR | O_APPEND;
-
- opts.checkout_strategy = GIT_CHECKOUT_FORCE;
- cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
-
- check_file_contents("./testrepo/new.txt", "hi\nmy new file\n");
-}
-
-struct notify_data {
- const char *file;
- const char *sha;
-};
-
-static int test_checkout_notify_cb(
- git_checkout_notify_t why,
- const char *path,
- const git_diff_file *baseline,
- const git_diff_file *target,
- const git_diff_file *workdir,
- void *payload)
-{
- struct notify_data *expectations = (struct notify_data *)payload;
-
- GIT_UNUSED(workdir);
-
- cl_assert_equal_i(GIT_CHECKOUT_NOTIFY_CONFLICT, why);
- cl_assert_equal_s(expectations->file, path);
- cl_assert_equal_i(0, git_oid_streq(&baseline->oid, expectations->sha));
- cl_assert_equal_i(0, git_oid_streq(&target->oid, expectations->sha));
-
- return 0;
-}
-
-void test_checkout_index__can_notify_of_skipped_files(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- struct notify_data data;
-
- cl_git_mkfile("./testrepo/new.txt", "This isn't what's stored!");
-
- /*
- * $ git ls-tree HEAD
- * 100644 blob a8233120f6ad708f843d861ce2b7228ec4e3dec6 README
- * 100644 blob 3697d64be941a53d4ae8f6a271e4e3fa56b022cc branch_file.txt
- * 100644 blob a71586c1dfe8a71c6cbf6c129f404c5642ff31bd new.txt
- */
- data.file = "new.txt";
- data.sha = "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd";
-
- opts.checkout_strategy =
- GIT_CHECKOUT_SAFE_CREATE | GIT_CHECKOUT_ALLOW_CONFLICTS;
- opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT;
- opts.notify_cb = test_checkout_notify_cb;
- opts.notify_payload = &data;
-
- cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
-}
-
-static int dont_notify_cb(
- git_checkout_notify_t why,
- const char *path,
- const git_diff_file *baseline,
- const git_diff_file *target,
- const git_diff_file *workdir,
- void *payload)
-{
- GIT_UNUSED(why);
- GIT_UNUSED(path);
- GIT_UNUSED(baseline);
- GIT_UNUSED(target);
- GIT_UNUSED(workdir);
- GIT_UNUSED(payload);
-
- cl_assert(false);
-
- return 0;
-}
-
-void test_checkout_index__wont_notify_of_expected_line_ending_changes(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
-
- cl_git_pass(p_unlink("./testrepo/.gitattributes"));
- cl_repo_set_bool(g_repo, "core.autocrlf", true);
-
- cl_git_mkfile("./testrepo/new.txt", "my new file\r\n");
-
- opts.checkout_strategy =
- GIT_CHECKOUT_SAFE_CREATE | GIT_CHECKOUT_ALLOW_CONFLICTS;
- opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT;
- opts.notify_cb = dont_notify_cb;
- opts.notify_payload = NULL;
-
- cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
-}
-
-static void checkout_progress_counter(
- const char *path, size_t cur, size_t tot, void *payload)
-{
- GIT_UNUSED(path); GIT_UNUSED(cur); GIT_UNUSED(tot);
- (*(int *)payload)++;
-}
-
-void test_checkout_index__calls_progress_callback(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- int calls = 0;
-
- opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
- opts.progress_cb = checkout_progress_counter;
- opts.progress_payload = &calls;
-
- cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
- cl_assert(calls > 0);
-}
-
-void test_checkout_index__can_overcome_name_clashes(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- git_index *index;
-
- cl_git_pass(git_repository_index(&index, g_repo));
- git_index_clear(index);
-
- cl_git_mkfile("./testrepo/path0", "content\r\n");
- cl_git_pass(p_mkdir("./testrepo/path1", 0777));
- cl_git_mkfile("./testrepo/path1/file1", "content\r\n");
-
- cl_git_pass(git_index_add_bypath(index, "path0"));
- cl_git_pass(git_index_add_bypath(index, "path1/file1"));
-
- cl_git_pass(p_unlink("./testrepo/path0"));
- cl_git_pass(git_futils_rmdir_r(
- "./testrepo/path1", NULL, GIT_RMDIR_REMOVE_FILES));
-
- cl_git_mkfile("./testrepo/path1", "content\r\n");
- cl_git_pass(p_mkdir("./testrepo/path0", 0777));
- cl_git_mkfile("./testrepo/path0/file0", "content\r\n");
-
- cl_assert(git_path_isfile("./testrepo/path1"));
- cl_assert(git_path_isfile("./testrepo/path0/file0"));
-
- opts.checkout_strategy =
- GIT_CHECKOUT_SAFE_CREATE | GIT_CHECKOUT_ALLOW_CONFLICTS;
- cl_git_pass(git_checkout_index(g_repo, index, &opts));
-
- cl_assert(git_path_isfile("./testrepo/path1"));
- cl_assert(git_path_isfile("./testrepo/path0/file0"));
-
- opts.checkout_strategy = GIT_CHECKOUT_FORCE;
- cl_git_pass(git_checkout_index(g_repo, index, &opts));
-
- cl_assert(git_path_isfile("./testrepo/path0"));
- cl_assert(git_path_isfile("./testrepo/path1/file1"));
-
- git_index_free(index);
-}
-
-void test_checkout_index__validates_struct_version(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- const git_error *err;
-
- opts.version = 1024;
- cl_git_fail(git_checkout_index(g_repo, NULL, &opts));
-
- err = giterr_last();
- cl_assert_equal_i(err->klass, GITERR_INVALID);
-
- opts.version = 0;
- giterr_clear();
- cl_git_fail(git_checkout_index(g_repo, NULL, &opts));
-
- err = giterr_last();
- cl_assert_equal_i(err->klass, GITERR_INVALID);
-}
-
-void test_checkout_index__can_update_prefixed_files(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
-
- cl_assert_equal_i(false, git_path_isfile("./testrepo/README"));
- cl_assert_equal_i(false, git_path_isfile("./testrepo/branch_file.txt"));
- cl_assert_equal_i(false, git_path_isfile("./testrepo/new.txt"));
-
- cl_git_mkfile("./testrepo/READ", "content\n");
- cl_git_mkfile("./testrepo/README.after", "content\n");
- cl_git_pass(p_mkdir("./testrepo/branch_file", 0777));
- cl_git_pass(p_mkdir("./testrepo/branch_file/contained_dir", 0777));
- cl_git_mkfile("./testrepo/branch_file/contained_file", "content\n");
- cl_git_pass(p_mkdir("./testrepo/branch_file.txt.after", 0777));
-
- opts.checkout_strategy =
- GIT_CHECKOUT_SAFE_CREATE | GIT_CHECKOUT_REMOVE_UNTRACKED;
-
- cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
-
- /* remove untracked will remove the .gitattributes file before the blobs
- * were created, so they will have had crlf filtering applied on Windows
- */
- check_file_contents_nocr("./testrepo/README", "hey there\n");
- check_file_contents_nocr("./testrepo/branch_file.txt", "hi\nbye!\n");
- check_file_contents_nocr("./testrepo/new.txt", "my new file\n");
-
- cl_assert(!git_path_exists("testrepo/READ"));
- cl_assert(!git_path_exists("testrepo/README.after"));
- cl_assert(!git_path_exists("testrepo/branch_file"));
- cl_assert(!git_path_exists("testrepo/branch_file.txt.after"));
-}
-
-void test_checkout_index__can_checkout_a_newly_initialized_repository(void)
-{
- test_checkout_index__cleanup();
-
- g_repo = cl_git_sandbox_init("empty_standard_repo");
- cl_git_remove_placeholders(git_repository_path(g_repo), "dummy-marker.txt");
-
- cl_git_pass(git_checkout_index(g_repo, NULL, NULL));
-}
-
-void test_checkout_index__issue_1397(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
-
- test_checkout_index__cleanup();
-
- g_repo = cl_git_sandbox_init("issue_1397");
-
- cl_repo_set_bool(g_repo, "core.autocrlf", true);
-
- opts.checkout_strategy = GIT_CHECKOUT_FORCE;
-
- cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
-
- check_file_contents("./issue_1397/crlf_file.txt", "first line\r\nsecond line\r\nboth with crlf");
-}
-
-void test_checkout_index__target_directory(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- checkout_counts cts;
- memset(&cts, 0, sizeof(cts));
-
- opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
- opts.target_directory = "alternative";
- cl_assert(!git_path_isdir("alternative"));
-
- opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
- opts.notify_cb = checkout_count_callback;
- opts.notify_payload = &cts;
-
- /* create some files that *would* conflict if we were using the wd */
- cl_git_mkfile("testrepo/README", "I'm in the way!\n");
- cl_git_mkfile("testrepo/new.txt", "my new file\n");
-
- cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
-
- cl_assert_equal_i(0, cts.n_untracked);
- cl_assert_equal_i(0, cts.n_ignored);
- cl_assert_equal_i(4, cts.n_updates);
-
- check_file_contents("./alternative/README", "hey there\n");
- check_file_contents("./alternative/branch_file.txt", "hi\nbye!\n");
- check_file_contents("./alternative/new.txt", "my new file\n");
-
- cl_git_pass(git_futils_rmdir_r(
- "alternative", NULL, GIT_RMDIR_REMOVE_FILES));
-}
-
-void test_checkout_index__target_directory_from_bare(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- git_index *index;
- git_object *head = NULL;
- checkout_counts cts;
- memset(&cts, 0, sizeof(cts));
-
- test_checkout_index__cleanup();
-
- g_repo = cl_git_sandbox_init("testrepo.git");
- cl_assert(git_repository_is_bare(g_repo));
-
- cl_git_pass(git_repository_index(&index, g_repo));
- cl_git_pass(git_revparse_single(&head, g_repo, "HEAD^{tree}"));
- cl_git_pass(git_index_read_tree(index, (const git_tree *)head));
- cl_git_pass(git_index_write(index));
- git_index_free(index);
-
- opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
-
- opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
- opts.notify_cb = checkout_count_callback;
- opts.notify_payload = &cts;
-
- /* fail to checkout a bare repo */
- cl_git_fail(git_checkout_index(g_repo, NULL, &opts));
-
- opts.target_directory = "alternative";
- cl_assert(!git_path_isdir("alternative"));
-
- cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
-
- cl_assert_equal_i(0, cts.n_untracked);
- cl_assert_equal_i(0, cts.n_ignored);
- cl_assert_equal_i(3, cts.n_updates);
-
- /* files will have been filtered if needed, so strip CR */
- check_file_contents_nocr("./alternative/README", "hey there\n");
- check_file_contents_nocr("./alternative/branch_file.txt", "hi\nbye!\n");
- check_file_contents_nocr("./alternative/new.txt", "my new file\n");
-
- cl_git_pass(git_futils_rmdir_r(
- "alternative", NULL, GIT_RMDIR_REMOVE_FILES));
-}
-
-void test_checkout_index__can_get_repo_from_index(void)
-{
- git_index *index;
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
-
- cl_assert_equal_i(false, git_path_isfile("./testrepo/README"));
- cl_assert_equal_i(false, git_path_isfile("./testrepo/branch_file.txt"));
- cl_assert_equal_i(false, git_path_isfile("./testrepo/new.txt"));
-
- opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
-
- cl_git_pass(git_repository_index(&index, g_repo));
-
- cl_git_pass(git_checkout_index(NULL, index, &opts));
-
- check_file_contents("./testrepo/README", "hey there\n");
- check_file_contents("./testrepo/branch_file.txt", "hi\nbye!\n");
- check_file_contents("./testrepo/new.txt", "my new file\n");
-
- git_index_free(index);
-}
diff --git a/tests-clar/checkout/tree.c b/tests-clar/checkout/tree.c
deleted file mode 100644
index e4bfbce06..000000000
--- a/tests-clar/checkout/tree.c
+++ /dev/null
@@ -1,679 +0,0 @@
-#include "clar_libgit2.h"
-#include "checkout_helpers.h"
-
-#include "git2/checkout.h"
-#include "repository.h"
-#include "buffer.h"
-#include "fileops.h"
-
-static git_repository *g_repo;
-static git_checkout_opts g_opts;
-static git_object *g_object;
-
-void test_checkout_tree__initialize(void)
-{
- g_repo = cl_git_sandbox_init("testrepo");
-
- GIT_INIT_STRUCTURE(&g_opts, GIT_CHECKOUT_OPTS_VERSION);
- g_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
-}
-
-void test_checkout_tree__cleanup(void)
-{
- git_object_free(g_object);
- g_object = NULL;
-
- cl_git_sandbox_cleanup();
-
- if (git_path_isdir("alternative"))
- git_futils_rmdir_r("alternative", NULL, GIT_RMDIR_REMOVE_FILES);
-}
-
-void test_checkout_tree__cannot_checkout_a_non_treeish(void)
-{
- /* blob */
- cl_git_pass(git_revparse_single(&g_object, g_repo, "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd"));
- cl_git_fail(git_checkout_tree(g_repo, g_object, NULL));
-}
-
-void test_checkout_tree__can_checkout_a_subdirectory_from_a_commit(void)
-{
- char *entries[] = { "ab/de/" };
-
- g_opts.paths.strings = entries;
- g_opts.paths.count = 1;
-
- cl_git_pass(git_revparse_single(&g_object, g_repo, "subtrees"));
-
- cl_assert_equal_i(false, git_path_isdir("./testrepo/ab/"));
-
- cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
-
- cl_assert_equal_i(true, git_path_isfile("./testrepo/ab/de/2.txt"));
- cl_assert_equal_i(true, git_path_isfile("./testrepo/ab/de/fgh/1.txt"));
-}
-
-void test_checkout_tree__can_checkout_and_remove_directory(void)
-{
- cl_assert_equal_i(false, git_path_isdir("./testrepo/ab/"));
-
- /* Checkout brach "subtrees" and update HEAD, so that HEAD matches the
- * current working tree
- */
- cl_git_pass(git_revparse_single(&g_object, g_repo, "subtrees"));
- cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
-
- cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees"));
-
- cl_assert_equal_i(true, git_path_isdir("./testrepo/ab/"));
- cl_assert_equal_i(true, git_path_isfile("./testrepo/ab/de/2.txt"));
- cl_assert_equal_i(true, git_path_isfile("./testrepo/ab/de/fgh/1.txt"));
-
- git_object_free(g_object);
- g_object = NULL;
-
- /* Checkout brach "master" and update HEAD, so that HEAD matches the
- * current working tree
- */
- cl_git_pass(git_revparse_single(&g_object, g_repo, "master"));
- cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
-
- cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master"));
-
- /* This directory should no longer exist */
- cl_assert_equal_i(false, git_path_isdir("./testrepo/ab/"));
-}
-
-void test_checkout_tree__can_checkout_a_subdirectory_from_a_subtree(void)
-{
- char *entries[] = { "de/" };
-
- g_opts.paths.strings = entries;
- g_opts.paths.count = 1;
-
- cl_git_pass(git_revparse_single(&g_object, g_repo, "subtrees:ab"));
-
- cl_assert_equal_i(false, git_path_isdir("./testrepo/de/"));
-
- cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
-
- cl_assert_equal_i(true, git_path_isfile("./testrepo/de/2.txt"));
- cl_assert_equal_i(true, git_path_isfile("./testrepo/de/fgh/1.txt"));
-}
-
-static void progress(const char *path, size_t cur, size_t tot, void *payload)
-{
- bool *was_called = (bool*)payload;
- GIT_UNUSED(path); GIT_UNUSED(cur); GIT_UNUSED(tot);
- *was_called = true;
-}
-
-void test_checkout_tree__calls_progress_callback(void)
-{
- bool was_called = 0;
-
- g_opts.progress_cb = progress;
- g_opts.progress_payload = &was_called;
-
- cl_git_pass(git_revparse_single(&g_object, g_repo, "master"));
-
- cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
-
- cl_assert_equal_i(was_called, true);
-}
-
-void test_checkout_tree__doesnt_write_unrequested_files_to_worktree(void)
-{
- git_oid master_oid;
- git_oid chomped_oid;
- git_commit* p_master_commit;
- git_commit* p_chomped_commit;
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
-
- git_oid_fromstr(&master_oid, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
- git_oid_fromstr(&chomped_oid, "e90810b8df3e80c413d903f631643c716887138d");
- cl_git_pass(git_commit_lookup(&p_master_commit, g_repo, &master_oid));
- cl_git_pass(git_commit_lookup(&p_chomped_commit, g_repo, &chomped_oid));
-
- /* GIT_CHECKOUT_NONE should not add any file to the working tree from the
- * index as it is supposed to be a dry run.
- */
- opts.checkout_strategy = GIT_CHECKOUT_NONE;
- git_checkout_tree(g_repo, (git_object*)p_chomped_commit, &opts);
- cl_assert_equal_i(false, git_path_isfile("testrepo/readme.txt"));
-
- git_commit_free(p_master_commit);
- git_commit_free(p_chomped_commit);
-}
-
-void test_checkout_tree__can_switch_branches(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- git_oid oid;
- git_object *obj = NULL;
-
- assert_on_branch(g_repo, "master");
-
- /* do first checkout with FORCE because we don't know if testrepo
- * base data is clean for a checkout or not
- */
- opts.checkout_strategy = GIT_CHECKOUT_FORCE;
-
- cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
- cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
-
- cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
- cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir"));
-
- cl_assert(git_path_isfile("testrepo/README"));
- cl_assert(git_path_isfile("testrepo/branch_file.txt"));
- cl_assert(git_path_isfile("testrepo/new.txt"));
- cl_assert(git_path_isfile("testrepo/a/b.txt"));
-
- cl_assert(!git_path_isdir("testrepo/ab"));
-
- assert_on_branch(g_repo, "dir");
-
- git_object_free(obj);
-
- /* do second checkout safe because we should be clean after first */
- opts.checkout_strategy = GIT_CHECKOUT_SAFE;
-
- cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/subtrees"));
- cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
-
- cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
- cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees"));
-
- cl_assert(git_path_isfile("testrepo/README"));
- cl_assert(git_path_isfile("testrepo/branch_file.txt"));
- cl_assert(git_path_isfile("testrepo/new.txt"));
- cl_assert(git_path_isfile("testrepo/ab/4.txt"));
- cl_assert(git_path_isfile("testrepo/ab/c/3.txt"));
- cl_assert(git_path_isfile("testrepo/ab/de/2.txt"));
- cl_assert(git_path_isfile("testrepo/ab/de/fgh/1.txt"));
-
- cl_assert(!git_path_isdir("testrepo/a"));
-
- assert_on_branch(g_repo, "subtrees");
-
- git_object_free(obj);
-}
-
-void test_checkout_tree__can_remove_untracked(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
-
- opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_REMOVE_UNTRACKED;
-
- cl_git_mkfile("testrepo/untracked_file", "as you wish");
- cl_assert(git_path_isfile("testrepo/untracked_file"));
-
- cl_git_pass(git_checkout_head(g_repo, &opts));
-
- cl_assert(!git_path_isfile("testrepo/untracked_file"));
-}
-
-void test_checkout_tree__can_remove_ignored(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- int ignored = 0;
-
- opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_REMOVE_IGNORED;
-
- cl_git_mkfile("testrepo/ignored_file", "as you wish");
-
- cl_git_pass(git_ignore_add_rule(g_repo, "ignored_file\n"));
-
- cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "ignored_file"));
- cl_assert_equal_i(1, ignored);
-
- cl_assert(git_path_isfile("testrepo/ignored_file"));
-
- cl_git_pass(git_checkout_head(g_repo, &opts));
-
- cl_assert(!git_path_isfile("testrepo/ignored_file"));
-}
-
-void test_checkout_tree__can_update_only(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- git_oid oid;
- git_object *obj = NULL;
-
- /* first let's get things into a known state - by checkout out the HEAD */
-
- assert_on_branch(g_repo, "master");
-
- opts.checkout_strategy = GIT_CHECKOUT_FORCE;
- cl_git_pass(git_checkout_head(g_repo, &opts));
-
- cl_assert(!git_path_isdir("testrepo/a"));
-
- check_file_contents_nocr("testrepo/branch_file.txt", "hi\nbye!\n");
-
- /* now checkout branch but with update only */
-
- opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY;
-
- cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
- cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
-
- cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
- cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir"));
-
- assert_on_branch(g_repo, "dir");
-
- /* this normally would have been created (which was tested separately in
- * the test_checkout_tree__can_switch_branches test), but with
- * UPDATE_ONLY it will not have been created.
- */
- cl_assert(!git_path_isdir("testrepo/a"));
-
- /* but this file still should have been updated */
- check_file_contents_nocr("testrepo/branch_file.txt", "hi\n");
-
- git_object_free(obj);
-}
-
-void test_checkout_tree__can_checkout_with_pattern(void)
-{
- char *entries[] = { "[l-z]*.txt" };
-
- /* reset to beginning of history (i.e. just a README file) */
-
- g_opts.checkout_strategy =
- GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED;
-
- cl_git_pass(git_revparse_single(&g_object, g_repo, "8496071c1b46c854b31185ea97743be6a8774479"));
-
- cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
- cl_git_pass(
- git_repository_set_head_detached(g_repo, git_object_id(g_object)));
-
- git_object_free(g_object);
- g_object = NULL;
-
- cl_assert(git_path_exists("testrepo/README"));
- cl_assert(!git_path_exists("testrepo/branch_file.txt"));
- cl_assert(!git_path_exists("testrepo/link_to_new.txt"));
- cl_assert(!git_path_exists("testrepo/new.txt"));
-
- /* now to a narrow patterned checkout */
-
- g_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
- g_opts.paths.strings = entries;
- g_opts.paths.count = 1;
-
- cl_git_pass(git_revparse_single(&g_object, g_repo, "refs/heads/master"));
-
- cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
-
- cl_assert(git_path_exists("testrepo/README"));
- cl_assert(!git_path_exists("testrepo/branch_file.txt"));
- cl_assert(git_path_exists("testrepo/link_to_new.txt"));
- cl_assert(git_path_exists("testrepo/new.txt"));
-}
-
-void test_checkout_tree__can_disable_pattern_match(void)
-{
- char *entries[] = { "b*.txt" };
-
- /* reset to beginning of history (i.e. just a README file) */
-
- g_opts.checkout_strategy =
- GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED;
-
- cl_git_pass(git_revparse_single(&g_object, g_repo, "8496071c1b46c854b31185ea97743be6a8774479"));
-
- cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
- cl_git_pass(
- git_repository_set_head_detached(g_repo, git_object_id(g_object)));
-
- git_object_free(g_object);
- g_object = NULL;
-
- cl_assert(!git_path_isfile("testrepo/branch_file.txt"));
-
- /* now to a narrow patterned checkout, but disable pattern */
-
- g_opts.checkout_strategy =
- GIT_CHECKOUT_SAFE_CREATE | GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH;
- g_opts.paths.strings = entries;
- g_opts.paths.count = 1;
-
- cl_git_pass(git_revparse_single(&g_object, g_repo, "refs/heads/master"));
-
- cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
-
- cl_assert(!git_path_isfile("testrepo/branch_file.txt"));
-
- /* let's try that again, but allow the pattern match */
-
- g_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
-
- cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
-
- cl_assert(git_path_isfile("testrepo/branch_file.txt"));
-}
-
-void assert_conflict(
- const char *entry_path,
- const char *new_content,
- const char *parent_sha,
- const char *commit_sha)
-{
- git_index *index;
- git_object *hack_tree;
- git_reference *branch, *head;
- git_buf file_path = GIT_BUF_INIT;
-
- cl_git_pass(git_repository_index(&index, g_repo));
-
- /* Create a branch pointing at the parent */
- cl_git_pass(git_revparse_single(&g_object, g_repo, parent_sha));
- cl_git_pass(git_branch_create(&branch, g_repo,
- "potential_conflict", (git_commit *)g_object, 0));
-
- /* Make HEAD point to this branch */
- cl_git_pass(git_reference_symbolic_create(
- &head, g_repo, "HEAD", git_reference_name(branch), 1));
- git_reference_free(head);
- git_reference_free(branch);
-
- /* Checkout the parent */
- g_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
- cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
-
- /* Hack-ishy workaound to ensure *all* the index entries
- * match the content of the tree
- */
- cl_git_pass(git_object_peel(&hack_tree, g_object, GIT_OBJ_TREE));
- cl_git_pass(git_index_read_tree(index, (git_tree *)hack_tree));
- git_object_free(hack_tree);
- git_object_free(g_object);
- g_object = NULL;
-
- /* Create a conflicting file */
- cl_git_pass(git_buf_joinpath(&file_path, "./testrepo", entry_path));
- cl_git_mkfile(git_buf_cstr(&file_path), new_content);
- git_buf_free(&file_path);
-
- /* Trying to checkout the original commit */
- cl_git_pass(git_revparse_single(&g_object, g_repo, commit_sha));
-
- g_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
- cl_assert_equal_i(
- GIT_EMERGECONFLICT, git_checkout_tree(g_repo, g_object, &g_opts));
-
- /* Stage the conflicting change */
- cl_git_pass(git_index_add_bypath(index, entry_path));
- cl_git_pass(git_index_write(index));
- git_index_free(index);
-
- cl_assert_equal_i(
- GIT_EMERGECONFLICT, git_checkout_tree(g_repo, g_object, &g_opts));
-}
-
-void test_checkout_tree__checking_out_a_conflicting_type_change_returns_EMERGECONFLICT(void)
-{
- /*
- * 099faba adds a symlink named 'link_to_new.txt'
- * a65fedf is the parent of 099faba
- */
-
- assert_conflict("link_to_new.txt", "old.txt", "a65fedf", "099faba");
-}
-
-void test_checkout_tree__checking_out_a_conflicting_type_change_returns_EMERGECONFLICT_2(void)
-{
- /*
- * cf80f8d adds a directory named 'a/'
- * a4a7dce is the parent of cf80f8d
- */
-
- assert_conflict("a", "hello\n", "a4a7dce", "cf80f8d");
-}
-
-void test_checkout_tree__checking_out_a_conflicting_content_change_returns_EMERGECONFLICT(void)
-{
- /*
- * c47800c adds a symlink named 'branch_file.txt'
- * 5b5b025 is the parent of 763d71a
- */
-
- assert_conflict("branch_file.txt", "hello\n", "5b5b025", "c47800c");
-}
-
-void test_checkout_tree__donot_update_deleted_file_by_default(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- git_oid old_id, new_id;
- git_commit *old_commit = NULL, *new_commit = NULL;
- git_index *index = NULL;
- checkout_counts ct;
-
- opts.checkout_strategy = GIT_CHECKOUT_SAFE;
-
- memset(&ct, 0, sizeof(ct));
- opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
- opts.notify_cb = checkout_count_callback;
- opts.notify_payload = &ct;
-
- cl_git_pass(git_repository_index(&index, g_repo));
-
- cl_git_pass(git_oid_fromstr(&old_id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
- cl_git_pass(git_commit_lookup(&old_commit, g_repo, &old_id));
- cl_git_pass(git_reset(g_repo, (git_object *)old_commit, GIT_RESET_HARD));
-
- cl_git_pass(p_unlink("testrepo/branch_file.txt"));
- cl_git_pass(git_index_remove_bypath(index ,"branch_file.txt"));
- cl_git_pass(git_index_write(index));
-
- cl_assert(!git_path_exists("testrepo/branch_file.txt"));
-
- cl_git_pass(git_oid_fromstr(&new_id, "099fabac3a9ea935598528c27f866e34089c2eff"));
- cl_git_pass(git_commit_lookup(&new_commit, g_repo, &new_id));
-
-
- cl_git_fail(git_checkout_tree(g_repo, (git_object *)new_commit, &opts));
-
- cl_assert_equal_i(1, ct.n_conflicts);
- cl_assert_equal_i(1, ct.n_updates);
-
- git_commit_free(old_commit);
- git_commit_free(new_commit);
- git_index_free(index);
-}
-
-void test_checkout_tree__can_checkout_with_last_workdir_item_missing(void)
-{
- git_index *index = NULL;
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- git_oid tree_id, commit_id;
- git_tree *tree = NULL;
- git_commit *commit = NULL;
-
- git_repository_index(&index, g_repo);
-
- opts.checkout_strategy = GIT_CHECKOUT_FORCE;
-
- cl_git_pass(git_reference_name_to_id(&commit_id, g_repo, "refs/heads/master"));
- cl_git_pass(git_commit_lookup(&commit, g_repo, &commit_id));
-
- cl_git_pass(git_checkout_tree(g_repo, (git_object *)commit, &opts));
- cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master"));
-
- cl_git_pass(p_mkdir("./testrepo/this-is-dir", 0777));
- cl_git_mkfile("./testrepo/this-is-dir/contained_file", "content\n");
-
- cl_git_pass(git_index_add_bypath(index, "this-is-dir/contained_file"));
- git_index_write_tree(&tree_id, index);
- cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
-
- cl_git_pass(p_unlink("./testrepo/this-is-dir/contained_file"));
-
- opts.checkout_strategy = GIT_CHECKOUT_SAFE;
-
- opts.checkout_strategy = 1;
- git_checkout_tree(g_repo, (git_object *)tree, &opts);
-
- git_tree_free(tree);
- git_commit_free(commit);
- git_index_free(index);
-}
-
-void test_checkout_tree__issue_1397(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- const char *partial_oid = "8a7ef04";
- git_object *tree = NULL;
-
- test_checkout_tree__cleanup(); /* cleanup default checkout */
-
- g_repo = cl_git_sandbox_init("issue_1397");
-
- cl_repo_set_bool(g_repo, "core.autocrlf", true);
-
- cl_git_pass(git_revparse_single(&tree, g_repo, partial_oid));
-
- opts.checkout_strategy = GIT_CHECKOUT_FORCE;
-
- cl_git_pass(git_checkout_tree(g_repo, tree, &opts));
-
- check_file_contents("./issue_1397/crlf_file.txt", "first line\r\nsecond line\r\nboth with crlf");
-
- git_object_free(tree);
-}
-
-void test_checkout_tree__can_write_to_empty_dirs(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- git_oid oid;
- git_object *obj = NULL;
-
- assert_on_branch(g_repo, "master");
-
- cl_git_pass(p_mkdir("testrepo/a", 0777));
-
- /* do first checkout with FORCE because we don't know if testrepo
- * base data is clean for a checkout or not
- */
- opts.checkout_strategy = GIT_CHECKOUT_FORCE;
-
- cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
- cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
-
- cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
-
- cl_assert(git_path_isfile("testrepo/a/b.txt"));
-
- git_object_free(obj);
-}
-
-void test_checkout_tree__fails_when_dir_in_use(void)
-{
-#ifdef GIT_WIN32
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- git_oid oid;
- git_object *obj = NULL;
-
- opts.checkout_strategy = GIT_CHECKOUT_FORCE;
-
- cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
- cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
-
- cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
-
- cl_assert(git_path_isfile("testrepo/a/b.txt"));
-
- git_object_free(obj);
-
- cl_git_pass(p_chdir("testrepo/a"));
-
- cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/master"));
- cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
-
- cl_git_fail(git_checkout_tree(g_repo, obj, &opts));
-
- cl_git_pass(p_chdir("../.."));
-
- cl_assert(git_path_is_empty_dir("testrepo/a"));
-
- git_object_free(obj);
-#endif
-}
-
-void test_checkout_tree__can_continue_when_dir_in_use(void)
-{
-#ifdef GIT_WIN32
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- git_oid oid;
- git_object *obj = NULL;
-
- opts.checkout_strategy = GIT_CHECKOUT_FORCE |
- GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES;
-
- cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
- cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
-
- cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
-
- cl_assert(git_path_isfile("testrepo/a/b.txt"));
-
- git_object_free(obj);
-
- cl_git_pass(p_chdir("testrepo/a"));
-
- cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/master"));
- cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
-
- cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
-
- cl_git_pass(p_chdir("../.."));
-
- cl_assert(git_path_is_empty_dir("testrepo/a"));
-
- git_object_free(obj);
-#endif
-}
-
-void test_checkout_tree__target_directory_from_bare(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- git_oid oid;
- checkout_counts cts;
- memset(&cts, 0, sizeof(cts));
-
- test_checkout_tree__cleanup(); /* cleanup default checkout */
-
- g_repo = cl_git_sandbox_init("testrepo.git");
- cl_assert(git_repository_is_bare(g_repo));
-
- opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
-
- opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
- opts.notify_cb = checkout_count_callback;
- opts.notify_payload = &cts;
-
- cl_git_pass(git_reference_name_to_id(&oid, g_repo, "HEAD"));
- cl_git_pass(git_object_lookup(&g_object, g_repo, &oid, GIT_OBJ_ANY));
-
- cl_git_fail(git_checkout_tree(g_repo, g_object, &opts));
-
- opts.target_directory = "alternative";
- cl_assert(!git_path_isdir("alternative"));
-
- cl_git_pass(git_checkout_tree(g_repo, g_object, &opts));
-
- cl_assert_equal_i(0, cts.n_untracked);
- cl_assert_equal_i(0, cts.n_ignored);
- cl_assert_equal_i(3, cts.n_updates);
-
- check_file_contents_nocr("./alternative/README", "hey there\n");
- check_file_contents_nocr("./alternative/branch_file.txt", "hi\nbye!\n");
- check_file_contents_nocr("./alternative/new.txt", "my new file\n");
-
- cl_git_pass(git_futils_rmdir_r(
- "alternative", NULL, GIT_RMDIR_REMOVE_FILES));
-}
diff --git a/tests-clar/clar.c b/tests-clar/clar.c
deleted file mode 100644
index fb10dd397..000000000
--- a/tests-clar/clar.c
+++ /dev/null
@@ -1,459 +0,0 @@
-/*
- * Copyright (c) Vicent Marti. All rights reserved.
- *
- * This file is part of clar, distributed under the ISC license.
- * For full terms see the included COPYING file.
- */
-#include <assert.h>
-#include <setjmp.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <math.h>
-#include <stdarg.h>
-
-/* required for sandboxing */
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#ifdef _WIN32
-# include <windows.h>
-# include <io.h>
-# include <shellapi.h>
-# include <direct.h>
-
-# define _MAIN_CC __cdecl
-
-# define stat(path, st) _stat(path, st)
-# define mkdir(path, mode) _mkdir(path)
-# define chdir(path) _chdir(path)
-# define access(path, mode) _access(path, mode)
-# define strdup(str) _strdup(str)
-# define strcasecmp(a,b) _stricmp(a,b)
-
-# ifndef __MINGW32__
-# pragma comment(lib, "shell32")
-# define strncpy(to, from, to_size) strncpy_s(to, to_size, from, _TRUNCATE)
-# define W_OK 02
-# define S_ISDIR(x) ((x & _S_IFDIR) != 0)
-# define snprint_eq(buf,sz,fmt,a,b) _snprintf_s(buf,sz,_TRUNCATE,fmt,a,b)
-# else
-# define snprint_eq snprintf
-# endif
- typedef struct _stat STAT_T;
-#else
-# include <sys/wait.h> /* waitpid(2) */
-# include <unistd.h>
-# define _MAIN_CC
-# define snprint_eq snprintf
- typedef struct stat STAT_T;
-#endif
-
-#include "clar.h"
-
-static void fs_rm(const char *_source);
-static void fs_copy(const char *_source, const char *dest);
-
-static const char *
-fixture_path(const char *base, const char *fixture_name);
-
-struct clar_error {
- const char *test;
- int test_number;
- const char *suite;
- const char *file;
- int line_number;
- const char *error_msg;
- char *description;
-
- struct clar_error *next;
-};
-
-static struct {
- const char *active_test;
- const char *active_suite;
-
- int suite_errors;
- int total_errors;
-
- int tests_ran;
- int suites_ran;
-
- int report_errors_only;
- int exit_on_error;
- int report_suite_names;
-
- struct clar_error *errors;
- struct clar_error *last_error;
-
- void (*local_cleanup)(void *);
- void *local_cleanup_payload;
-
- jmp_buf trampoline;
- int trampoline_enabled;
-} _clar;
-
-struct clar_func {
- const char *name;
- void (*ptr)(void);
-};
-
-struct clar_suite {
- const char *name;
- struct clar_func initialize;
- struct clar_func cleanup;
- const struct clar_func *tests;
- size_t test_count;
- int enabled;
-};
-
-/* From clar_print_*.c */
-static void clar_print_init(int test_count, int suite_count, const char *suite_names);
-static void clar_print_shutdown(int test_count, int suite_count, int error_count);
-static void clar_print_error(int num, const struct clar_error *error);
-static void clar_print_ontest(const char *test_name, int test_number, int failed);
-static void clar_print_onsuite(const char *suite_name, int suite_index);
-static void clar_print_onabort(const char *msg, ...);
-
-/* From clar_sandbox.c */
-static void clar_unsandbox(void);
-static int clar_sandbox(void);
-
-/* Load the declarations for the test suite */
-#include "clar.suite"
-
-/* Core test functions */
-static void
-clar_report_errors(void)
-{
- int i = 1;
- struct clar_error *error, *next;
-
- error = _clar.errors;
- while (error != NULL) {
- next = error->next;
- clar_print_error(i++, error);
- free(error->description);
- free(error);
- error = next;
- }
-
- _clar.errors = _clar.last_error = NULL;
-}
-
-static void
-clar_run_test(
- const struct clar_func *test,
- const struct clar_func *initialize,
- const struct clar_func *cleanup)
-{
- int error_st = _clar.suite_errors;
-
- _clar.trampoline_enabled = 1;
-
- if (setjmp(_clar.trampoline) == 0) {
- if (initialize->ptr != NULL)
- initialize->ptr();
-
- test->ptr();
- }
-
- _clar.trampoline_enabled = 0;
-
- if (_clar.local_cleanup != NULL)
- _clar.local_cleanup(_clar.local_cleanup_payload);
-
- if (cleanup->ptr != NULL)
- cleanup->ptr();
-
- _clar.tests_ran++;
-
- /* remove any local-set cleanup methods */
- _clar.local_cleanup = NULL;
- _clar.local_cleanup_payload = NULL;
-
- if (_clar.report_errors_only)
- clar_report_errors();
- else
- clar_print_ontest(
- test->name,
- _clar.tests_ran,
- (_clar.suite_errors > error_st)
- );
-}
-
-static void
-clar_run_suite(const struct clar_suite *suite, const char *filter)
-{
- const struct clar_func *test = suite->tests;
- size_t i, matchlen;
-
- if (!suite->enabled)
- return;
-
- if (_clar.exit_on_error && _clar.total_errors)
- return;
-
- if (!_clar.report_errors_only)
- clar_print_onsuite(suite->name, ++_clar.suites_ran);
-
- _clar.active_suite = suite->name;
- _clar.suite_errors = 0;
-
- if (filter) {
- size_t suitelen = strlen(suite->name);
- matchlen = strlen(filter);
- if (matchlen <= suitelen) {
- filter = NULL;
- } else {
- filter += suitelen;
- while (*filter == ':')
- ++filter;
- matchlen = strlen(filter);
- }
- }
-
- for (i = 0; i < suite->test_count; ++i) {
- if (filter && strncmp(test[i].name, filter, matchlen))
- continue;
-
- _clar.active_test = test[i].name;
- clar_run_test(&test[i], &suite->initialize, &suite->cleanup);
-
- if (_clar.exit_on_error && _clar.total_errors)
- return;
- }
-}
-
-static void
-clar_usage(const char *arg)
-{
- printf("Usage: %s [options]\n\n", arg);
- printf("Options:\n");
- printf(" -sname\tRun only the suite with `name` (can go to individual test name)\n");
- printf(" -iname\tInclude the suite with `name`\n");
- printf(" -xname\tExclude the suite with `name`\n");
- printf(" -q \tOnly report tests that had an error\n");
- printf(" -Q \tQuit as soon as a test fails\n");
- printf(" -l \tPrint suite names\n");
- exit(-1);
-}
-
-static void
-clar_parse_args(int argc, char **argv)
-{
- int i;
-
- for (i = 1; i < argc; ++i) {
- char *argument = argv[i];
-
- if (argument[0] != '-')
- clar_usage(argv[0]);
-
- switch (argument[1]) {
- case 's':
- case 'i':
- case 'x': { /* given suite name */
- int offset = (argument[2] == '=') ? 3 : 2, found = 0;
- char action = argument[1];
- size_t j, arglen, suitelen, cmplen;
-
- argument += offset;
- arglen = strlen(argument);
-
- if (arglen == 0)
- clar_usage(argv[0]);
-
- for (j = 0; j < _clar_suite_count; ++j) {
- suitelen = strlen(_clar_suites[j].name);
- cmplen = (arglen < suitelen) ? arglen : suitelen;
-
- if (strncmp(argument, _clar_suites[j].name, cmplen) == 0) {
- int exact = (arglen >= suitelen);
-
- ++found;
-
- if (!exact)
- _clar.report_suite_names = 1;
-
- switch (action) {
- case 's': clar_run_suite(&_clar_suites[j], argument); break;
- case 'i': _clar_suites[j].enabled = 1; break;
- case 'x': _clar_suites[j].enabled = 0; break;
- }
-
- if (exact)
- break;
- }
- }
-
- if (!found) {
- clar_print_onabort("No suite matching '%s' found.\n", argument);
- exit(-1);
- }
- break;
- }
-
- case 'q':
- _clar.report_errors_only = 1;
- break;
-
- case 'Q':
- _clar.exit_on_error = 1;
- break;
-
- case 'l': {
- size_t j;
- printf("Test suites (use -s<name> to run just one):\n");
- for (j = 0; j < _clar_suite_count; ++j)
- printf(" %3d: %s\n", (int)j, _clar_suites[j].name);
-
- exit(0);
- }
-
- default:
- clar_usage(argv[0]);
- }
- }
-}
-
-int
-clar_test(int argc, char **argv)
-{
- clar_print_init(
- (int)_clar_callback_count,
- (int)_clar_suite_count,
- ""
- );
-
- if (clar_sandbox() < 0) {
- clar_print_onabort("Failed to sandbox the test runner.\n");
- exit(-1);
- }
-
- if (argc > 1)
- clar_parse_args(argc, argv);
-
- if (!_clar.suites_ran) {
- size_t i;
- for (i = 0; i < _clar_suite_count; ++i)
- clar_run_suite(&_clar_suites[i], NULL);
- }
-
- clar_print_shutdown(
- _clar.tests_ran,
- (int)_clar_suite_count,
- _clar.total_errors
- );
-
- clar_unsandbox();
- return _clar.total_errors;
-}
-
-void clar__fail(
- const char *file,
- int line,
- const char *error_msg,
- const char *description,
- int should_abort)
-{
- struct clar_error *error = calloc(1, sizeof(struct clar_error));
-
- if (_clar.errors == NULL)
- _clar.errors = error;
-
- if (_clar.last_error != NULL)
- _clar.last_error->next = error;
-
- _clar.last_error = error;
-
- error->test = _clar.active_test;
- error->test_number = _clar.tests_ran;
- error->suite = _clar.active_suite;
- error->file = file;
- error->line_number = line;
- error->error_msg = error_msg;
-
- if (description != NULL)
- error->description = strdup(description);
-
- _clar.suite_errors++;
- _clar.total_errors++;
-
- if (should_abort) {
- if (!_clar.trampoline_enabled) {
- clar_print_onabort(
- "Fatal error: a cleanup method raised an exception.");
- clar_report_errors();
- exit(-1);
- }
-
- longjmp(_clar.trampoline, -1);
- }
-}
-
-void clar__assert(
- int condition,
- const char *file,
- int line,
- const char *error_msg,
- const char *description,
- int should_abort)
-{
- if (condition)
- return;
-
- clar__fail(file, line, error_msg, description, should_abort);
-}
-
-void clar__assert_equal_s(
- const char *s1,
- const char *s2,
- const char *file,
- int line,
- const char *err,
- int should_abort)
-{
- int match = (s1 == NULL || s2 == NULL) ? (s1 == s2) : (strcmp(s1, s2) == 0);
-
- if (!match) {
- char buf[4096];
-
- if (s1 && s2) {
- int pos;
- for (pos = 0; s1[pos] == s2[pos] && s1[pos] && s2[pos]; ++pos)
- /* find differing byte offset */;
- snprint_eq(buf, sizeof(buf), "'%s' != '%s' (at byte %d)", s1, s2, pos);
- } else {
- snprint_eq(buf, sizeof(buf), "'%s' != '%s'", s1, s2);
- }
-
- clar__fail(file, line, err, buf, should_abort);
- }
-}
-
-void clar__assert_equal_i(
- int i1,
- int i2,
- const char *file,
- int line,
- const char *err,
- int should_abort)
-{
- if (i1 != i2) {
- char buf[128];
- snprint_eq(buf, sizeof(buf), "%d != %d", i1, i2);
- clar__fail(file, line, err, buf, should_abort);
- }
-}
-
-void cl_set_cleanup(void (*cleanup)(void *), void *opaque)
-{
- _clar.local_cleanup = cleanup;
- _clar.local_cleanup_payload = opaque;
-}
-
-#include "clar/sandbox.h"
-#include "clar/fixtures.h"
-#include "clar/fs.h"
-#include "clar/print.h"
diff --git a/tests-clar/clar.h b/tests-clar/clar.h
deleted file mode 100644
index d92318bd4..000000000
--- a/tests-clar/clar.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (c) Vicent Marti. All rights reserved.
- *
- * This file is part of clar, distributed under the ISC license.
- * For full terms see the included COPYING file.
- */
-#ifndef __CLAR_TEST_H__
-#define __CLAR_TEST_H__
-
-#include <stdlib.h>
-
-int clar_test(int argc, char *argv[]);
-
-void cl_set_cleanup(void (*cleanup)(void *), void *opaque);
-void cl_fs_cleanup(void);
-
-#ifdef CLAR_FIXTURE_PATH
-const char *cl_fixture(const char *fixture_name);
-void cl_fixture_sandbox(const char *fixture_name);
-void cl_fixture_cleanup(const char *fixture_name);
-#endif
-
-/**
- * Assertion macros with explicit error message
- */
-#define cl_must_pass_(expr, desc) clar__assert((expr) >= 0, __FILE__, __LINE__, "Function call failed: " #expr, desc, 1)
-#define cl_must_fail_(expr, desc) clar__assert((expr) < 0, __FILE__, __LINE__, "Expected function call to fail: " #expr, desc, 1)
-#define cl_assert_(expr, desc) clar__assert((expr) != 0, __FILE__, __LINE__, "Expression is not true: " #expr, desc, 1)
-
-/**
- * Check macros with explicit error message
- */
-#define cl_check_pass_(expr, desc) clar__assert((expr) >= 0, __FILE__, __LINE__, "Function call failed: " #expr, desc, 0)
-#define cl_check_fail_(expr, desc) clar__assert((expr) < 0, __FILE__, __LINE__, "Expected function call to fail: " #expr, desc, 0)
-#define cl_check_(expr, desc) clar__assert((expr) != 0, __FILE__, __LINE__, "Expression is not true: " #expr, desc, 0)
-
-/**
- * Assertion macros with no error message
- */
-#define cl_must_pass(expr) cl_must_pass_(expr, NULL)
-#define cl_must_fail(expr) cl_must_fail_(expr, NULL)
-#define cl_assert(expr) cl_assert_(expr, NULL)
-
-/**
- * Check macros with no error message
- */
-#define cl_check_pass(expr) cl_check_pass_(expr, NULL)
-#define cl_check_fail(expr) cl_check_fail_(expr, NULL)
-#define cl_check(expr) cl_check_(expr, NULL)
-
-/**
- * Forced failure/warning
- */
-#define cl_fail(desc) clar__fail(__FILE__, __LINE__, "Test failed.", desc, 1)
-#define cl_warning(desc) clar__fail(__FILE__, __LINE__, "Warning during test execution:", desc, 0)
-
-/**
- * Typed assertion macros
- */
-#define cl_assert_equal_s(s1,s2) clar__assert_equal_s((s1),(s2),__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2, 1)
-#define cl_assert_equal_s_(s1,s2,note) clar__assert_equal_s((s1),(s2),__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1)
-
-#define cl_assert_equal_i(i1,i2) clar__assert_equal_i((i1),(i2),__FILE__,__LINE__,#i1 " != " #i2, 1)
-#define cl_assert_equal_i_(i1,i2,note) clar__assert_equal_i((i1),(i2),__FILE__,__LINE__,#i1 " != " #i2 " (" #note ")", 1)
-
-#define cl_assert_equal_b(b1,b2) clar__assert_equal_i(!!(b1),!!(b2),__FILE__,__LINE__,#b1 " != " #b2, 1)
-
-#define cl_assert_equal_p(p1,p2) cl_assert((p1) == (p2))
-
-void clar__fail(
- const char *file,
- int line,
- const char *error,
- const char *description,
- int should_abort);
-
-void clar__assert(
- int condition,
- const char *file,
- int line,
- const char *error,
- const char *description,
- int should_abort);
-
-void clar__assert_equal_s(const char *,const char *,const char *,int,const char *,int);
-void clar__assert_equal_i(int,int,const char *,int,const char *,int);
-
-#endif
diff --git a/tests-clar/clar/sandbox.h b/tests-clar/clar/sandbox.h
deleted file mode 100644
index 1ca6fcae8..000000000
--- a/tests-clar/clar/sandbox.h
+++ /dev/null
@@ -1,131 +0,0 @@
-static char _clar_path[4096];
-
-static int
-is_valid_tmp_path(const char *path)
-{
- STAT_T st;
-
- if (stat(path, &st) != 0)
- return 0;
-
- if (!S_ISDIR(st.st_mode))
- return 0;
-
- return (access(path, W_OK) == 0);
-}
-
-static int
-find_tmp_path(char *buffer, size_t length)
-{
-#ifndef _WIN32
- static const size_t var_count = 5;
- static const char *env_vars[] = {
- "CLAR_TMP", "TMPDIR", "TMP", "TEMP", "USERPROFILE"
- };
-
- size_t i;
-
- for (i = 0; i < var_count; ++i) {
- const char *env = getenv(env_vars[i]);
- if (!env)
- continue;
-
- if (is_valid_tmp_path(env)) {
- strncpy(buffer, env, length);
- return 0;
- }
- }
-
- /* If the environment doesn't say anything, try to use /tmp */
- if (is_valid_tmp_path("/tmp")) {
- strncpy(buffer, "/tmp", length);
- return 0;
- }
-
-#else
- DWORD env_len;
-
- if ((env_len = GetEnvironmentVariable("CLAR_TMP", buffer, length)) > 0 &&
- env_len < length)
- return 0;
-
- if (GetTempPath((DWORD)length, buffer))
- return 0;
-#endif
-
- /* This system doesn't like us, try to use the current directory */
- if (is_valid_tmp_path(".")) {
- strncpy(buffer, ".", length);
- return 0;
- }
-
- return -1;
-}
-
-static void clar_unsandbox(void)
-{
- if (_clar_path[0] == '\0')
- return;
-
- chdir("..");
-
- fs_rm(_clar_path);
-}
-
-static int build_sandbox_path(void)
-{
- const char path_tail[] = "clar_tmp_XXXXXX";
- size_t len;
-
- if (find_tmp_path(_clar_path, sizeof(_clar_path)) < 0)
- return -1;
-
- len = strlen(_clar_path);
-
-#ifdef _WIN32
- { /* normalize path to POSIX forward slashes */
- size_t i;
- for (i = 0; i < len; ++i) {
- if (_clar_path[i] == '\\')
- _clar_path[i] = '/';
- }
- }
-#endif
-
- if (_clar_path[len - 1] != '/') {
- _clar_path[len++] = '/';
- }
-
- strncpy(_clar_path + len, path_tail, sizeof(_clar_path) - len);
-
-#if defined(__MINGW32__)
- if (_mktemp(_clar_path) == NULL)
- return -1;
-
- if (mkdir(_clar_path, 0700) != 0)
- return -1;
-#elif defined(_WIN32)
- if (_mktemp_s(_clar_path, sizeof(_clar_path)) != 0)
- return -1;
-
- if (mkdir(_clar_path, 0700) != 0)
- return -1;
-#else
- if (mkdtemp(_clar_path) == NULL)
- return -1;
-#endif
-
- return 0;
-}
-
-static int clar_sandbox(void)
-{
- if (_clar_path[0] == '\0' && build_sandbox_path() < 0)
- return -1;
-
- if (chdir(_clar_path) != 0)
- return -1;
-
- return 0;
-}
-
diff --git a/tests-clar/clar_libgit2.c b/tests-clar/clar_libgit2.c
deleted file mode 100644
index de0e41bf7..000000000
--- a/tests-clar/clar_libgit2.c
+++ /dev/null
@@ -1,345 +0,0 @@
-#include "clar_libgit2.h"
-#include "posix.h"
-#include "path.h"
-
-void cl_git_report_failure(
- int error, const char *file, int line, const char *fncall)
-{
- char msg[4096];
- const git_error *last = giterr_last();
- p_snprintf(msg, 4096, "error %d - %s",
- error, last ? last->message : "<no message>");
- clar__assert(0, file, line, fncall, msg, 1);
-}
-
-void cl_git_mkfile(const char *filename, const char *content)
-{
- int fd;
-
- fd = p_creat(filename, 0666);
- cl_assert(fd != 0);
-
- if (content) {
- cl_must_pass(p_write(fd, content, strlen(content)));
- } else {
- cl_must_pass(p_write(fd, filename, strlen(filename)));
- cl_must_pass(p_write(fd, "\n", 1));
- }
-
- cl_must_pass(p_close(fd));
-}
-
-void cl_git_write2file(
- const char *filename, const char *new_content, int flags, unsigned int mode)
-{
- int fd = p_open(filename, flags, mode);
- cl_assert(fd >= 0);
- if (!new_content)
- new_content = "\n";
- cl_must_pass(p_write(fd, new_content, strlen(new_content)));
- cl_must_pass(p_close(fd));
-}
-
-void cl_git_append2file(const char *filename, const char *new_content)
-{
- cl_git_write2file(filename, new_content, O_WRONLY | O_CREAT | O_APPEND, 0644);
-}
-
-void cl_git_rewritefile(const char *filename, const char *new_content)
-{
- cl_git_write2file(filename, new_content, O_WRONLY | O_CREAT | O_TRUNC, 0644);
-}
-
-#ifdef GIT_WIN32
-
-#include "win32/utf-conv.h"
-
-char *cl_getenv(const char *name)
-{
- wchar_t name_utf16[GIT_WIN_PATH];
- DWORD alloc_len;
- wchar_t *value_utf16;
- char *value_utf8;
-
- git__utf8_to_16(name_utf16, GIT_WIN_PATH, name);
- alloc_len = GetEnvironmentVariableW(name_utf16, NULL, 0);
- if (alloc_len <= 0)
- return NULL;
-
- alloc_len = GIT_WIN_PATH;
- cl_assert(value_utf16 = git__calloc(alloc_len, sizeof(wchar_t)));
-
- GetEnvironmentVariableW(name_utf16, value_utf16, alloc_len);
-
- cl_assert(value_utf8 = git__malloc(alloc_len));
- git__utf16_to_8(value_utf8, value_utf16);
-
- git__free(value_utf16);
-
- return value_utf8;
-}
-
-int cl_setenv(const char *name, const char *value)
-{
- wchar_t name_utf16[GIT_WIN_PATH];
- wchar_t value_utf16[GIT_WIN_PATH];
-
- git__utf8_to_16(name_utf16, GIT_WIN_PATH, name);
-
- if (value) {
- git__utf8_to_16(value_utf16, GIT_WIN_PATH, value);
- cl_assert(SetEnvironmentVariableW(name_utf16, value_utf16));
- } else {
- /* Windows XP returns 0 (failed) when passing NULL for lpValue when
- * lpName does not exist in the environment block. This behavior
- * seems to have changed in later versions. Don't check return value
- * of SetEnvironmentVariable when passing NULL for lpValue.
- */
- SetEnvironmentVariableW(name_utf16, NULL);
- }
-
- return 0;
-}
-
-/* This function performs retries on calls to MoveFile in order
- * to provide enhanced reliability in the face of antivirus
- * agents that may be scanning the source (or in the case that
- * the source is a directory, a child of the source). */
-int cl_rename(const char *source, const char *dest)
-{
- wchar_t source_utf16[GIT_WIN_PATH];
- wchar_t dest_utf16[GIT_WIN_PATH];
- unsigned retries = 1;
-
- git__utf8_to_16(source_utf16, GIT_WIN_PATH, source);
- git__utf8_to_16(dest_utf16, GIT_WIN_PATH, dest);
-
- while (!MoveFileW(source_utf16, dest_utf16)) {
- /* Only retry if the error is ERROR_ACCESS_DENIED;
- * this may indicate that an antivirus agent is
- * preventing the rename from source to target */
- if (retries > 5 ||
- ERROR_ACCESS_DENIED != GetLastError())
- return -1;
-
- /* With 5 retries and a coefficient of 10ms, the maximum
- * delay here is 550 ms */
- Sleep(10 * retries * retries);
- retries++;
- }
-
- return 0;
-}
-
-#else
-
-#include <stdlib.h>
-char *cl_getenv(const char *name)
-{
- return getenv(name);
-}
-
-int cl_setenv(const char *name, const char *value)
-{
- return (value == NULL) ? unsetenv(name) : setenv(name, value, 1);
-}
-
-int cl_rename(const char *source, const char *dest)
-{
- return p_rename(source, dest);
-}
-
-#endif
-
-static const char *_cl_sandbox = NULL;
-static git_repository *_cl_repo = NULL;
-
-git_repository *cl_git_sandbox_init(const char *sandbox)
-{
- /* Copy the whole sandbox folder from our fixtures to our test sandbox
- * area. After this it can be accessed with `./sandbox`
- */
- cl_fixture_sandbox(sandbox);
- _cl_sandbox = sandbox;
-
- cl_git_pass(p_chdir(sandbox));
-
- /* If this is not a bare repo, then rename `sandbox/.gitted` to
- * `sandbox/.git` which must be done since we cannot store a folder
- * named `.git` inside the fixtures folder of our libgit2 repo.
- */
- if (p_access(".gitted", F_OK) == 0)
- cl_git_pass(cl_rename(".gitted", ".git"));
-
- /* If we have `gitattributes`, rename to `.gitattributes`. This may
- * be necessary if we don't want the attributes to be applied in the
- * libgit2 repo, but just during testing.
- */
- if (p_access("gitattributes", F_OK) == 0)
- cl_git_pass(cl_rename("gitattributes", ".gitattributes"));
-
- /* As with `gitattributes`, we may need `gitignore` just for testing. */
- if (p_access("gitignore", F_OK) == 0)
- cl_git_pass(cl_rename("gitignore", ".gitignore"));
-
- cl_git_pass(p_chdir(".."));
-
- /* Now open the sandbox repository and make it available for tests */
- cl_git_pass(git_repository_open(&_cl_repo, sandbox));
-
- return _cl_repo;
-}
-
-git_repository *cl_git_sandbox_reopen(void)
-{
- if (_cl_repo) {
- git_repository_free(_cl_repo);
- _cl_repo = NULL;
-
- cl_git_pass(git_repository_open(&_cl_repo, _cl_sandbox));
- }
-
- return _cl_repo;
-}
-
-void cl_git_sandbox_cleanup(void)
-{
- if (_cl_repo) {
- git_repository_free(_cl_repo);
- _cl_repo = NULL;
- }
- if (_cl_sandbox) {
- cl_fixture_cleanup(_cl_sandbox);
- _cl_sandbox = NULL;
- }
-}
-
-bool cl_toggle_filemode(const char *filename)
-{
- struct stat st1, st2;
-
- cl_must_pass(p_stat(filename, &st1));
- cl_must_pass(p_chmod(filename, st1.st_mode ^ 0100));
- cl_must_pass(p_stat(filename, &st2));
-
- return (st1.st_mode != st2.st_mode);
-}
-
-bool cl_is_chmod_supported(void)
-{
- static int _is_supported = -1;
-
- if (_is_supported < 0) {
- cl_git_mkfile("filemode.t", "Test if filemode can be modified");
- _is_supported = cl_toggle_filemode("filemode.t");
- cl_must_pass(p_unlink("filemode.t"));
- }
-
- return _is_supported;
-}
-
-const char* cl_git_fixture_url(const char *fixturename)
-{
- return cl_git_path_url(cl_fixture(fixturename));
-}
-
-const char* cl_git_path_url(const char *path)
-{
- static char url[4096];
-
- const char *in_buf;
- git_buf path_buf = GIT_BUF_INIT;
- git_buf url_buf = GIT_BUF_INIT;
-
- cl_git_pass(git_path_prettify_dir(&path_buf, path, NULL));
- cl_git_pass(git_buf_puts(&url_buf, "file://"));
-
-#ifdef GIT_WIN32
- /*
- * A FILE uri matches the following format: file://[host]/path
- * where "host" can be empty and "path" is an absolute path to the resource.
- *
- * In this test, no hostname is used, but we have to ensure the leading triple slashes:
- *
- * *nix: file:///usr/home/...
- * Windows: file:///C:/Users/...
- */
- cl_git_pass(git_buf_putc(&url_buf, '/'));
-#endif
-
- in_buf = git_buf_cstr(&path_buf);
-
- /*
- * A very hacky Url encoding that only takes care of escaping the spaces
- */
- while (*in_buf) {
- if (*in_buf == ' ')
- cl_git_pass(git_buf_puts(&url_buf, "%20"));
- else
- cl_git_pass(git_buf_putc(&url_buf, *in_buf));
-
- in_buf++;
- }
-
- strncpy(url, git_buf_cstr(&url_buf), 4096);
- git_buf_free(&url_buf);
- git_buf_free(&path_buf);
- return url;
-}
-
-typedef struct {
- const char *filename;
- size_t filename_len;
-} remove_data;
-
-static int remove_placeholders_recurs(void *_data, git_buf *path)
-{
- remove_data *data = (remove_data *)_data;
- size_t pathlen;
-
- if (git_path_isdir(path->ptr) == true)
- return git_path_direach(path, remove_placeholders_recurs, data);
-
- pathlen = path->size;
-
- if (pathlen < data->filename_len)
- return 0;
-
- /* if path ends in '/'+filename (or equals filename) */
- if (!strcmp(data->filename, path->ptr + pathlen - data->filename_len) &&
- (pathlen == data->filename_len ||
- path->ptr[pathlen - data->filename_len - 1] == '/'))
- return p_unlink(path->ptr);
-
- return 0;
-}
-
-int cl_git_remove_placeholders(const char *directory_path, const char *filename)
-{
- int error;
- remove_data data;
- git_buf buffer = GIT_BUF_INIT;
-
- if (git_path_isdir(directory_path) == false)
- return -1;
-
- if (git_buf_sets(&buffer, directory_path) < 0)
- return -1;
-
- data.filename = filename;
- data.filename_len = strlen(filename);
-
- error = remove_placeholders_recurs(&data, &buffer);
-
- git_buf_free(&buffer);
-
- return error;
-}
-
-void cl_repo_set_bool(git_repository *repo, const char *cfg, int value)
-{
- git_config *config;
- cl_git_pass(git_repository_config(&config, repo));
- cl_git_pass(git_config_set_bool(config, cfg, value != 0));
- git_config_free(config);
-}
diff --git a/tests-clar/clar_libgit2.h b/tests-clar/clar_libgit2.h
deleted file mode 100644
index 3fcf45a37..000000000
--- a/tests-clar/clar_libgit2.h
+++ /dev/null
@@ -1,75 +0,0 @@
-#ifndef __CLAR_LIBGIT2__
-#define __CLAR_LIBGIT2__
-
-#include "clar.h"
-#include <git2.h>
-#include "common.h"
-
-/**
- * Replace for `clar_must_pass` that passes the last library error as the
- * test failure message.
- *
- * Use this wrapper around all `git_` library calls that return error codes!
- */
-#define cl_git_pass(expr) do { \
- int _lg2_error; \
- giterr_clear(); \
- if ((_lg2_error = (expr)) != 0) \
- cl_git_report_failure(_lg2_error, __FILE__, __LINE__, "Function call failed: " #expr); \
- } while (0)
-
-/**
- * Wrapper for `clar_must_fail` -- this one is
- * just for consistency. Use with `git_` library
- * calls that are supposed to fail!
- */
-#define cl_git_fail(expr) cl_must_fail(expr)
-
-#define cl_git_fail_with(expr, error) cl_assert_equal_i(error,expr)
-
-void cl_git_report_failure(int, const char *, int, const char *);
-
-#define cl_assert_equal_sz(sz1,sz2) cl_assert_equal_i((int)sz1, (int)(sz2))
-
-/*
- * Some utility macros for building long strings
- */
-#define REP4(STR) STR STR STR STR
-#define REP15(STR) REP4(STR) REP4(STR) REP4(STR) STR STR STR
-#define REP16(STR) REP4(REP4(STR))
-#define REP256(STR) REP16(REP16(STR))
-#define REP1024(STR) REP4(REP256(STR))
-
-/* Write the contents of a buffer to disk */
-void cl_git_mkfile(const char *filename, const char *content);
-void cl_git_append2file(const char *filename, const char *new_content);
-void cl_git_rewritefile(const char *filename, const char *new_content);
-void cl_git_write2file(const char *filename, const char *new_content, int flags, unsigned int mode);
-
-bool cl_toggle_filemode(const char *filename);
-bool cl_is_chmod_supported(void);
-
-/* Environment wrappers */
-char *cl_getenv(const char *name);
-int cl_setenv(const char *name, const char *value);
-
-/* Reliable rename */
-int cl_rename(const char *source, const char *dest);
-
-/* Git sandbox setup helpers */
-
-git_repository *cl_git_sandbox_init(const char *sandbox);
-void cl_git_sandbox_cleanup(void);
-git_repository *cl_git_sandbox_reopen(void);
-
-/* Local-repo url helpers */
-const char* cl_git_fixture_url(const char *fixturename);
-const char* cl_git_path_url(const char *path);
-
-/* Test repository cleaner */
-int cl_git_remove_placeholders(const char *directory_path, const char *filename);
-
-/* config setting helpers */
-void cl_repo_set_bool(git_repository *repo, const char *cfg, int value);
-
-#endif
diff --git a/tests-clar/clone/empty.c b/tests-clar/clone/empty.c
deleted file mode 100644
index f92fa6cbb..000000000
--- a/tests-clar/clone/empty.c
+++ /dev/null
@@ -1,83 +0,0 @@
-#include "clar_libgit2.h"
-
-#include "git2/clone.h"
-#include "repository.h"
-
-static git_clone_options g_options;
-static git_repository *g_repo;
-static git_repository *g_repo_cloned;
-
-void test_clone_empty__initialize(void)
-{
- git_repository *sandbox = cl_git_sandbox_init("empty_bare.git");
- cl_git_remove_placeholders(git_repository_path(sandbox), "dummy-marker.txt");
-
- g_repo = NULL;
-
- memset(&g_options, 0, sizeof(git_clone_options));
- g_options.version = GIT_CLONE_OPTIONS_VERSION;
-}
-
-void test_clone_empty__cleanup(void)
-{
- cl_git_sandbox_cleanup();
-}
-
-static void cleanup_repository(void *path)
-{
- cl_fixture_cleanup((const char *)path);
-
- git_repository_free(g_repo_cloned);
- g_repo_cloned = NULL;
-}
-
-void test_clone_empty__can_clone_an_empty_local_repo_barely(void)
-{
- char *local_name = "refs/heads/master";
- const char *expected_tracked_branch_name = "refs/remotes/origin/master";
- const char *expected_remote_name = "origin";
- char buffer[1024];
- git_reference *ref;
-
- cl_set_cleanup(&cleanup_repository, "./empty");
-
- g_options.bare = true;
- cl_git_pass(git_clone(&g_repo_cloned, "./empty_bare.git", "./empty", &g_options));
-
- /* Although the HEAD is orphaned... */
- cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&ref, g_repo_cloned, local_name));
-
- /* ...one can still retrieve the name of the remote tracking reference */
- cl_assert_equal_i((int)strlen(expected_tracked_branch_name) + 1,
- git_branch_upstream_name(buffer, 1024, g_repo_cloned, local_name));
-
- cl_assert_equal_s(expected_tracked_branch_name, buffer);
-
- /* ...and the name of the remote... */
- cl_assert_equal_i((int)strlen(expected_remote_name) + 1,
- git_branch_remote_name(buffer, 1024, g_repo_cloned, expected_tracked_branch_name));
-
- cl_assert_equal_s(expected_remote_name, buffer);
-
- /* ...even when the remote HEAD is orphaned as well */
- cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&ref, g_repo_cloned,
- expected_tracked_branch_name));
-}
-
-void test_clone_empty__can_clone_an_empty_local_repo(void)
-{
- cl_set_cleanup(&cleanup_repository, "./empty");
-
- cl_git_pass(git_clone(&g_repo_cloned, "./empty_bare.git", "./empty", &g_options));
-}
-
-void test_clone_empty__can_clone_an_empty_standard_repo(void)
-{
- cl_git_sandbox_cleanup();
- g_repo = cl_git_sandbox_init("empty_standard_repo");
- cl_git_remove_placeholders(git_repository_path(g_repo), "dummy-marker.txt");
-
- cl_set_cleanup(&cleanup_repository, "./empty");
-
- cl_git_pass(git_clone(&g_repo_cloned, "./empty_standard_repo", "./empty", &g_options));
-}
diff --git a/tests-clar/clone/nonetwork.c b/tests-clar/clone/nonetwork.c
deleted file mode 100644
index 339b1e70d..000000000
--- a/tests-clar/clone/nonetwork.c
+++ /dev/null
@@ -1,260 +0,0 @@
-#include "clar_libgit2.h"
-
-#include "git2/clone.h"
-#include "remote.h"
-#include "fileops.h"
-#include "repository.h"
-
-#define LIVE_REPO_URL "git://github.com/libgit2/TestGitRepository"
-
-static git_clone_options g_options;
-static git_repository *g_repo;
-static git_reference* g_ref;
-static git_remote* g_remote;
-
-void test_clone_nonetwork__initialize(void)
-{
- git_checkout_opts dummy_opts = GIT_CHECKOUT_OPTS_INIT;
-
- g_repo = NULL;
-
- memset(&g_options, 0, sizeof(git_clone_options));
- g_options.version = GIT_CLONE_OPTIONS_VERSION;
- g_options.checkout_opts = dummy_opts;
- g_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
-}
-
-void test_clone_nonetwork__cleanup(void)
-{
- if (g_repo) {
- git_repository_free(g_repo);
- g_repo = NULL;
- }
-
- if (g_ref) {
- git_reference_free(g_ref);
- g_ref = NULL;
- }
-
- if (g_remote) {
- git_remote_free(g_remote);
- g_remote = NULL;
- }
-
- cl_fixture_cleanup("./foo");
-}
-
-void test_clone_nonetwork__bad_url(void)
-{
- /* Clone should clean up the mess if the URL isn't a git repository */
- cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
- cl_assert(!git_path_exists("./foo"));
- g_options.bare = true;
- cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
- cl_assert(!git_path_exists("./foo"));
-}
-
-static int dont_call_me(void *state, git_buf *path)
-{
- GIT_UNUSED(state);
- GIT_UNUSED(path);
- return GIT_ERROR;
-}
-
-void test_clone_nonetwork__do_not_clean_existing_directory(void)
-{
- git_buf path_buf = GIT_BUF_INIT;
-
- git_buf_put(&path_buf, "./foo", 5);
-
- /* Clone should not remove the directory if it already exists, but
- * Should clean up entries it creates. */
- p_mkdir("./foo", GIT_DIR_MODE);
- cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
- cl_assert(git_path_exists("./foo"));
-
- /* Make sure the directory is empty. */
- cl_git_pass(git_path_direach(&path_buf,
- dont_call_me,
- NULL));
-
- /* Try again with a bare repository. */
- g_options.bare = true;
- cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
- cl_assert(git_path_exists("./foo"));
-
- /* Make sure the directory is empty. */
- cl_git_pass(git_path_direach(&path_buf,
- dont_call_me,
- NULL));
-
- git_buf_free(&path_buf);
-}
-
-void test_clone_nonetwork__local(void)
-{
- cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
-}
-
-void test_clone_nonetwork__local_absolute_path(void)
-{
- const char *local_src;
- local_src = cl_fixture("testrepo.git");
- cl_git_pass(git_clone(&g_repo, local_src, "./foo", &g_options));
-}
-
-void test_clone_nonetwork__local_bare(void)
-{
- g_options.bare = true;
- cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
-}
-
-void test_clone_nonetwork__fail_when_the_target_is_a_file(void)
-{
- cl_git_mkfile("./foo", "Bar!");
- cl_git_fail(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
-}
-
-void test_clone_nonetwork__fail_with_already_existing_but_non_empty_directory(void)
-{
- p_mkdir("./foo", GIT_DIR_MODE);
- cl_git_mkfile("./foo/bar", "Baz!");
- cl_git_fail(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
-}
-
-void test_clone_nonetwork__custom_origin_name(void)
-{
- g_options.remote_name = "my_origin";
- cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
-
- cl_git_pass(git_remote_load(&g_remote, g_repo, "my_origin"));
-}
-
-void test_clone_nonetwork__custom_push_url(void)
-{
- const char *url = "http://example.com";
-
- g_options.pushurl = url;
- cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
-
- cl_git_pass(git_remote_load(&g_remote, g_repo, "origin"));
- cl_assert_equal_s(url, git_remote_pushurl(g_remote));
-}
-
-void test_clone_nonetwork__custom_fetch_spec(void)
-{
- const git_refspec *actual_fs;
- const char *spec = "+refs/heads/master:refs/heads/foo";
-
- g_options.fetch_spec = spec;
- cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
-
- cl_git_pass(git_remote_load(&g_remote, g_repo, "origin"));
- actual_fs = git_remote_get_refspec(g_remote, 0);
- cl_assert_equal_s("refs/heads/master", git_refspec_src(actual_fs));
- cl_assert_equal_s("refs/heads/foo", git_refspec_dst(actual_fs));
-
- cl_git_pass(git_reference_lookup(&g_ref, g_repo, "refs/heads/foo"));
-}
-
-void test_clone_nonetwork__custom_push_spec(void)
-{
- const git_refspec *actual_fs;
- const char *spec = "+refs/heads/master:refs/heads/foo";
-
- g_options.push_spec = spec;
- cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
-
- cl_git_pass(git_remote_load(&g_remote, g_repo, "origin"));
- actual_fs = git_remote_get_refspec(g_remote, git_remote_refspec_count(g_remote) - 1);
- cl_assert_equal_s("refs/heads/master", git_refspec_src(actual_fs));
- cl_assert_equal_s("refs/heads/foo", git_refspec_dst(actual_fs));
-}
-
-void test_clone_nonetwork__custom_autotag(void)
-{
- git_remote *origin;
- git_strarray tags = {0};
-
- g_options.remote_autotag = GIT_REMOTE_DOWNLOAD_TAGS_NONE;
- cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
-
- cl_git_pass(git_tag_list(&tags, g_repo));
- cl_assert_equal_sz(0, tags.count);
-
- cl_git_pass(git_remote_load(&origin, g_repo, "origin"));
- cl_assert_equal_i(GIT_REMOTE_DOWNLOAD_TAGS_NONE, origin->download_tags);
-
- git_strarray_free(&tags);
- git_remote_free(origin);
-}
-
-void test_clone_nonetwork__custom_autotag_tags_all(void)
-{
- git_strarray tags = {0};
- git_remote *origin;
-
- g_options.remote_autotag = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
- cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
-
- cl_git_pass(git_remote_load(&origin, g_repo, "origin"));
- cl_assert_equal_i(GIT_REMOTE_DOWNLOAD_TAGS_ALL, origin->download_tags);
-
- git_strarray_free(&tags);
- git_remote_free(origin);
-}
-
-void test_clone_nonetwork__cope_with_already_existing_directory(void)
-{
- p_mkdir("./foo", GIT_DIR_MODE);
- cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
-}
-
-void test_clone_nonetwork__can_prevent_the_checkout_of_a_standard_repo(void)
-{
- git_buf path = GIT_BUF_INIT;
-
- g_options.checkout_opts.checkout_strategy = 0;
- cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
-
- cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "master.txt"));
- cl_assert_equal_i(false, git_path_isfile(git_buf_cstr(&path)));
-
- git_buf_free(&path);
-}
-
-void test_clone_nonetwork__can_checkout_given_branch(void)
-{
- g_options.checkout_branch = "test";
- cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
-
- cl_assert_equal_i(0, git_repository_head_orphan(g_repo));
-
- cl_git_pass(git_repository_head(&g_ref, g_repo));
- cl_assert_equal_s(git_reference_name(g_ref), "refs/heads/test");
-}
-
-void test_clone_nonetwork__can_detached_head(void)
-{
- git_object *obj;
- git_repository *cloned;
- git_reference *cloned_head;
-
- cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
-
- cl_git_pass(git_revparse_single(&obj, g_repo, "master~1"));
- cl_git_pass(git_repository_set_head_detached(g_repo, git_object_id(obj)));
-
- cl_git_pass(git_clone(&cloned, "./foo", "./foo1", &g_options));
-
- cl_assert(git_repository_head_detached(cloned));
-
- cl_git_pass(git_repository_head(&cloned_head, cloned));
- cl_assert(!git_oid_cmp(git_object_id(obj), git_reference_target(cloned_head)));
-
- git_object_free(obj);
- git_reference_free(cloned_head);
- git_repository_free(cloned);
-
- cl_fixture_cleanup("./foo1");
-}
diff --git a/tests-clar/commit/parse.c b/tests-clar/commit/parse.c
deleted file mode 100644
index 415860a6e..000000000
--- a/tests-clar/commit/parse.c
+++ /dev/null
@@ -1,392 +0,0 @@
-#include "clar_libgit2.h"
-#include <git2/types.h>
-#include "commit.h"
-#include "signature.h"
-
-// Fixture setup
-static git_repository *g_repo;
-void test_commit_parse__initialize(void)
-{
- g_repo = cl_git_sandbox_init("testrepo");
-}
-void test_commit_parse__cleanup(void)
-{
- cl_git_sandbox_cleanup();
-}
-
-
-// Header parsing
-typedef struct {
- const char *line;
- const char *header;
-} parse_test_case;
-
-static parse_test_case passing_header_cases[] = {
- { "parent 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "parent " },
- { "tree 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree " },
- { "random_heading 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "random_heading " },
- { "stuck_heading05452d6349abcd67aa396dfb28660d765d8b2a36\n", "stuck_heading" },
- { "tree 5F4BEFFC0759261D015AA63A3A85613FF2F235DE\n", "tree " },
- { "tree 1A669B8AB81B5EB7D9DB69562D34952A38A9B504\n", "tree " },
- { "tree 5B20DCC6110FCC75D31C6CEDEBD7F43ECA65B503\n", "tree " },
- { "tree 173E7BF00EA5C33447E99E6C1255954A13026BE4\n", "tree " },
- { NULL, NULL }
-};
-
-static parse_test_case failing_header_cases[] = {
- { "parent 05452d6349abcd67aa396dfb28660d765d8b2a36", "parent " },
- { "05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree " },
- { "parent05452d6349abcd67aa396dfb28660d765d8b2a6a\n", "parent " },
- { "parent 05452d6349abcd67aa396dfb280d765d8b2a6\n", "parent " },
- { "tree 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree " },
- { "parent 0545xd6349abcd67aa396dfb28660d765d8b2a36\n", "parent " },
- { "parent 0545xd6349abcd67aa396dfb28660d765d8b2a36FF\n", "parent " },
- { "", "tree " },
- { "", "" },
- { NULL, NULL }
-};
-
-void test_commit_parse__header(void)
-{
- git_oid oid;
-
- parse_test_case *testcase;
- for (testcase = passing_header_cases; testcase->line != NULL; testcase++)
- {
- const char *line = testcase->line;
- const char *line_end = line + strlen(line);
-
- cl_git_pass(git_oid__parse(&oid, &line, line_end, testcase->header));
- cl_assert(line == line_end);
- }
-
- for (testcase = failing_header_cases; testcase->line != NULL; testcase++)
- {
- const char *line = testcase->line;
- const char *line_end = line + strlen(line);
-
- cl_git_fail(git_oid__parse(&oid, &line, line_end, testcase->header));
- }
-}
-
-
-// Signature parsing
-typedef struct {
- const char *string;
- const char *header;
- const char *name;
- const char *email;
- git_time_t time;
- int offset;
-} passing_signature_test_case;
-
-passing_signature_test_case passing_signature_cases[] = {
- {"author Vicent Marti <tanoku@gmail.com> 12345 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 12345, 0},
- {"author Vicent Marti <> 12345 \n", "author ", "Vicent Marti", "", 12345, 0},
- {"author Vicent Marti <tanoku@gmail.com> 231301 +1020\n", "author ", "Vicent Marti", "tanoku@gmail.com", 231301, 620},
- {"author Vicent Marti with an outrageously long name which will probably overflow the buffer <tanoku@gmail.com> 12345 \n", "author ", "Vicent Marti with an outrageously long name which will probably overflow the buffer", "tanoku@gmail.com", 12345, 0},
- {"author Vicent Marti <tanokuwithaveryveryverylongemailwhichwillprobablyvoverflowtheemailbuffer@gmail.com> 12345 \n", "author ", "Vicent Marti", "tanokuwithaveryveryverylongemailwhichwillprobablyvoverflowtheemailbuffer@gmail.com", 12345, 0},
- {"committer Vicent Marti <tanoku@gmail.com> 123456 +0000 \n", "committer ", "Vicent Marti", "tanoku@gmail.com", 123456, 0},
- {"committer Vicent Marti <tanoku@gmail.com> 123456 +0100 \n", "committer ", "Vicent Marti", "tanoku@gmail.com", 123456, 60},
- {"committer Vicent Marti <tanoku@gmail.com> 123456 -0100 \n", "committer ", "Vicent Marti", "tanoku@gmail.com", 123456, -60},
- // Parse a signature without an author field
- {"committer <tanoku@gmail.com> 123456 -0100 \n", "committer ", "", "tanoku@gmail.com", 123456, -60},
- // Parse a signature without an author field
- {"committer <tanoku@gmail.com> 123456 -0100 \n", "committer ", "", "tanoku@gmail.com", 123456, -60},
- // Parse a signature with an empty author field
- {"committer <tanoku@gmail.com> 123456 -0100 \n", "committer ", "", "tanoku@gmail.com", 123456, -60},
- // Parse a signature with an empty email field
- {"committer Vicent Marti <> 123456 -0100 \n", "committer ", "Vicent Marti", "", 123456, -60},
- // Parse a signature with an empty email field
- {"committer Vicent Marti < > 123456 -0100 \n", "committer ", "Vicent Marti", "", 123456, -60},
- // Parse a signature with empty name and email
- {"committer <> 123456 -0100 \n", "committer ", "", "", 123456, -60},
- // Parse a signature with empty name and email
- {"committer <> 123456 -0100 \n", "committer ", "", "", 123456, -60},
- // Parse a signature with empty name and email
- {"committer < > 123456 -0100 \n", "committer ", "", "", 123456, -60},
- // Parse an obviously invalid signature
- {"committer foo<@bar> 123456 -0100 \n", "committer ", "foo", "@bar", 123456, -60},
- // Parse an obviously invalid signature
- {"committer foo<@bar> 123456 -0100 \n", "committer ", "foo", "@bar", 123456, -60},
- // Parse an obviously invalid signature
- {"committer <>\n", "committer ", "", "", 0, 0},
- {"committer Vicent Marti <tanoku@gmail.com> 123456 -1500 \n", "committer ", "Vicent Marti", "tanoku@gmail.com", 123456, 0},
- {"committer Vicent Marti <tanoku@gmail.com> 123456 +0163 \n", "committer ", "Vicent Marti", "tanoku@gmail.com", 123456, 0},
- {"author Vicent Marti <tanoku@gmail.com>\n", "author ", "Vicent Marti", "tanoku@gmail.com", 0, 0},
- /* a variety of dates */
- {"author Vicent Marti <tanoku@gmail.com> 0 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 0, 0},
- {"author Vicent Marti <tanoku@gmail.com> 1234567890 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 1234567890, 0},
- {"author Vicent Marti <tanoku@gmail.com> 2147483647 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 0x7fffffff, 0},
- {"author Vicent Marti <tanoku@gmail.com> 4294967295 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 0xffffffff, 0},
- {"author Vicent Marti <tanoku@gmail.com> 4294967296 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 4294967296, 0},
- {"author Vicent Marti <tanoku@gmail.com> 8589934592 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 8589934592, 0},
-
- {NULL,NULL,NULL,NULL,0,0}
-};
-
-typedef struct {
- const char *string;
- const char *header;
-} failing_signature_test_case;
-
-failing_signature_test_case failing_signature_cases[] = {
- {"committer Vicent Marti tanoku@gmail.com> 123456 -0100 \n", "committer "},
- {"author Vicent Marti <tanoku@gmail.com> 12345 \n", "author "},
- {"author Vicent Marti <tanoku@gmail.com> 12345 \n", "committer "},
- {"author Vicent Marti 12345 \n", "author "},
- {"author Vicent Marti <broken@email 12345 \n", "author "},
- {"committer Vicent Marti ><\n", "committer "},
- {"author ", "author "},
- {NULL, NULL,}
-};
-
-void test_commit_parse__signature(void)
-{
- passing_signature_test_case *passcase;
- failing_signature_test_case *failcase;
-
- for (passcase = passing_signature_cases; passcase->string != NULL; passcase++)
- {
- const char *str = passcase->string;
- size_t len = strlen(passcase->string);
- struct git_signature person = {0};
-
- cl_git_pass(git_signature__parse(&person, &str, str + len, passcase->header, '\n'));
- cl_assert_equal_s(passcase->name, person.name);
- cl_assert_equal_s(passcase->email, person.email);
- cl_assert_equal_i((int)passcase->time, (int)person.when.time);
- cl_assert_equal_i(passcase->offset, person.when.offset);
- git__free(person.name); git__free(person.email);
- }
-
- for (failcase = failing_signature_cases; failcase->string != NULL; failcase++)
- {
- const char *str = failcase->string;
- size_t len = strlen(failcase->string);
- git_signature person = {0};
- cl_git_fail(git_signature__parse(&person, &str, str + len, failcase->header, '\n'));
- git__free(person.name); git__free(person.email);
- }
-}
-
-
-
-static char *failing_commit_cases[] = {
-// empty commit
-"",
-// random garbage
-"asd97sa9du902e9a0jdsuusad09as9du098709aweu8987sd\n",
-// broken endlines 1
-"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\r\n\
-parent 05452d6349abcd67aa396dfb28660d765d8b2a36\r\n\
-author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\r\n\
-committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\r\n\
-\r\n\
-a test commit with broken endlines\r\n",
-// broken endlines 2
-"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\
-parent 05452d6349abcd67aa396dfb28660d765d8b2a36\
-author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\
-committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\
-\
-another test commit with broken endlines",
-// starting endlines
-"\ntree f6c0dad3c7b3481caa9d73db21f91964894a945b\n\
-parent 05452d6349abcd67aa396dfb28660d765d8b2a36\n\
-author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
-committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
-\n\
-a test commit with a starting endline\n",
-// corrupted commit 1
-"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\n\
-parent 05452d6349abcd67aa396df",
-// corrupted commit 2
-"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\n\
-parent ",
-// corrupted commit 3
-"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\n\
-parent ",
-// corrupted commit 4
-"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\n\
-par",
-};
-
-
-static char *passing_commit_cases[] = {
-// simple commit with no message
-"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
-author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
-committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
-\n",
-// simple commit, no parent
-"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
-author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
-committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
-\n\
-a simple commit which works\n",
-// simple commit, no parent, no newline in message
-"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
-author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
-committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
-\n\
-a simple commit which works",
-// simple commit, 1 parent
-"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
-parent e90810b8df3e80c413d903f631643c716887138d\n\
-author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
-committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
-\n\
-a simple commit which works\n",
-/* simple commit with GPG signature */
-"tree 6b79e22d69bf46e289df0345a14ca059dfc9bdf6\n\
-parent 34734e478d6cf50c27c9d69026d93974d052c454\n\
-author Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
-committer Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
-gpgsig -----BEGIN PGP SIGNATURE-----\n\
- Version: GnuPG v1.4.12 (Darwin)\n\
- \n\
- iQIcBAABAgAGBQJQ+FMIAAoJEH+LfPdZDSs1e3EQAJMjhqjWF+WkGLHju7pTw2al\n\
- o6IoMAhv0Z/LHlWhzBd9e7JeCnanRt12bAU7yvYp9+Z+z+dbwqLwDoFp8LVuigl8\n\
- JGLcnwiUW3rSvhjdCp9irdb4+bhKUnKUzSdsR2CK4/hC0N2i/HOvMYX+BRsvqweq\n\
- AsAkA6dAWh+gAfedrBUkCTGhlNYoetjdakWqlGL1TiKAefEZrtA1TpPkGn92vbLq\n\
- SphFRUY9hVn1ZBWrT3hEpvAIcZag3rTOiRVT1X1flj8B2vGCEr3RrcwOIZikpdaW\n\
- who/X3xh/DGbI2RbuxmmJpxxP/8dsVchRJJzBwG+yhwU/iN3MlV2c5D69tls/Dok\n\
- 6VbyU4lm/ae0y3yR83D9dUlkycOnmmlBAHKIZ9qUts9X7mWJf0+yy2QxJVpjaTGG\n\
- cmnQKKPeNIhGJk2ENnnnzjEve7L7YJQF6itbx5VCOcsGh3Ocb3YR7DMdWjt7f8pu\n\
- c6j+q1rP7EpE2afUN/geSlp5i3x8aXZPDj67jImbVCE/Q1X9voCtyzGJH7MXR0N9\n\
- ZpRF8yzveRfMH8bwAJjSOGAFF5XkcR/RNY95o+J+QcgBLdX48h+ZdNmUf6jqlu3J\n\
- 7KmTXXQcOVpN6dD3CmRFsbjq+x6RHwa8u1iGn+oIkX908r97ckfB/kHKH7ZdXIJc\n\
- cpxtDQQMGYFpXK/71stq\n\
- =ozeK\n\
- -----END PGP SIGNATURE-----\n\
-\n\
-a simple commit which works\n",
-};
-
-static int parse_commit(git_commit **out, const char *buffer)
-{
- git_commit *commit;
- git_odb_object fake_odb_object;
- int error;
-
- commit = (git_commit*)git__malloc(sizeof(git_commit));
- memset(commit, 0x0, sizeof(git_commit));
- commit->object.repo = g_repo;
-
- memset(&fake_odb_object, 0x0, sizeof(git_odb_object));
- fake_odb_object.buffer = (char *)buffer;
- fake_odb_object.cached.size = strlen(fake_odb_object.buffer);
-
- error = git_commit__parse(commit, &fake_odb_object);
-
- *out = commit;
- return error;
-}
-
-void test_commit_parse__entire_commit(void)
-{
- const int failing_commit_count = ARRAY_SIZE(failing_commit_cases);
- const int passing_commit_count = ARRAY_SIZE(passing_commit_cases);
- int i;
- git_commit *commit;
-
- for (i = 0; i < failing_commit_count; ++i) {
- cl_git_fail(parse_commit(&commit, failing_commit_cases[i]));
- git_commit__free(commit);
- }
-
- for (i = 0; i < passing_commit_count; ++i) {
- cl_git_pass(parse_commit(&commit, passing_commit_cases[i]));
-
- if (!i)
- cl_assert_equal_s("", git_commit_message(commit));
- else
- cl_assert(git__prefixcmp(
- git_commit_message(commit), "a simple commit which works") == 0);
-
- git_commit__free(commit);
- }
-}
-
-
-// query the details on a parsed commit
-void test_commit_parse__details0(void) {
- static const char *commit_ids[] = {
- "a4a7dce85cf63874e984719f4fdd239f5145052f", /* 0 */
- "9fd738e8f7967c078dceed8190330fc8648ee56a", /* 1 */
- "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", /* 2 */
- "c47800c7266a2be04c571c04d5a6614691ea99bd", /* 3 */
- "8496071c1b46c854b31185ea97743be6a8774479", /* 4 */
- "5b5b025afb0b4c913b4c338a42934a3863bf3644", /* 5 */
- "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", /* 6 */
- };
- const size_t commit_count = sizeof(commit_ids) / sizeof(const char *);
- unsigned int i;
-
- for (i = 0; i < commit_count; ++i) {
- git_oid id;
- git_commit *commit;
-
- const git_signature *author, *committer;
- const char *message;
- git_time_t commit_time;
- unsigned int parents, p;
- git_commit *parent = NULL, *old_parent = NULL;
-
- git_oid_fromstr(&id, commit_ids[i]);
-
- cl_git_pass(git_commit_lookup(&commit, g_repo, &id));
-
- message = git_commit_message(commit);
- author = git_commit_author(commit);
- committer = git_commit_committer(commit);
- commit_time = git_commit_time(commit);
- parents = git_commit_parentcount(commit);
-
- cl_assert_equal_s("Scott Chacon", author->name);
- cl_assert_equal_s("schacon@gmail.com", author->email);
- cl_assert_equal_s("Scott Chacon", committer->name);
- cl_assert_equal_s("schacon@gmail.com", committer->email);
- cl_assert(message != NULL);
- cl_assert(strchr(message, '\n') != NULL);
- cl_assert(commit_time > 0);
- cl_assert(parents <= 2);
- for (p = 0;p < parents;p++) {
- if (old_parent != NULL)
- git_commit_free(old_parent);
-
- old_parent = parent;
- cl_git_pass(git_commit_parent(&parent, commit, p));
- cl_assert(parent != NULL);
- cl_assert(git_commit_author(parent) != NULL); // is it really a commit?
- }
- git_commit_free(old_parent);
- git_commit_free(parent);
-
- cl_git_fail(git_commit_parent(&parent, commit, parents));
- git_commit_free(commit);
- }
-}
-
-void test_commit_parse__leading_lf(void)
-{
- git_commit *commit;
- const char *buffer =
-"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
-parent e90810b8df3e80c413d903f631643c716887138d\n\
-author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
-committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
-\n\
-\n\
-\n\
-This commit has a few LF at the start of the commit message";
- const char *message =
-"\n\
-\n\
-This commit has a few LF at the start of the commit message";
-
- cl_git_pass(parse_commit(&commit, buffer));
- cl_assert_equal_s(message, git_commit_message(commit));
- git_commit__free(commit);
-}
diff --git a/tests-clar/config/global.c b/tests-clar/config/global.c
deleted file mode 100644
index 2ecdf97d8..000000000
--- a/tests-clar/config/global.c
+++ /dev/null
@@ -1,67 +0,0 @@
-#include "clar_libgit2.h"
-#include "buffer.h"
-#include "fileops.h"
-
-void test_config_global__initialize(void)
-{
- git_buf path = GIT_BUF_INIT;
-
- cl_must_pass(p_mkdir("home", 0777));
- cl_git_pass(git_path_prettify(&path, "home", NULL));
- cl_git_pass(git_libgit2_opts(
- GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
-
- cl_must_pass(p_mkdir("xdg", 0777));
- cl_git_pass(git_path_prettify(&path, "xdg", NULL));
- cl_git_pass(git_libgit2_opts(
- GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr));
-
- cl_git_pass(git_libgit2_opts(
- GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, NULL));
-
- git_buf_free(&path);
-}
-
-void test_config_global__cleanup(void)
-{
- cl_git_pass(git_futils_rmdir_r("home", NULL, GIT_RMDIR_REMOVE_FILES));
- cl_git_pass(git_futils_rmdir_r("xdg", NULL, GIT_RMDIR_REMOVE_FILES));
-}
-
-void test_config_global__open_global(void)
-{
- git_config *cfg, *global, *selected, *dummy;
-
- cl_git_pass(git_config_open_default(&cfg));
- cl_git_pass(git_config_open_level(&global, cfg, GIT_CONFIG_LEVEL_GLOBAL));
- cl_git_fail(git_config_open_level(&dummy, cfg, GIT_CONFIG_LEVEL_XDG));
- cl_git_pass(git_config_open_global(&selected, cfg));
-
- git_config_free(selected);
- git_config_free(global);
- git_config_free(cfg);
-}
-
-void test_config_global__open_xdg(void)
-{
- git_config *cfg, *xdg, *selected;
- const char *val, *str = "teststring";
- const char *key = "this.variable";
-
- p_setenv("XDG_CONFIG_HOME", "xdg", 1);
-
- cl_must_pass(p_mkdir("xdg/git/", 0777));
- cl_git_mkfile("xdg/git/config", "");
-
- cl_git_pass(git_config_open_default(&cfg));
- cl_git_pass(git_config_open_level(&xdg, cfg, GIT_CONFIG_LEVEL_XDG));
- cl_git_pass(git_config_open_global(&selected, cfg));
-
- cl_git_pass(git_config_set_string(xdg, key, str));
- cl_git_pass(git_config_get_string(&val, selected, key));
- cl_assert_equal_s(str, val);
-
- git_config_free(selected);
- git_config_free(xdg);
- git_config_free(cfg);
-}
diff --git a/tests-clar/config/multivar.c b/tests-clar/config/multivar.c
deleted file mode 100644
index 0bda6bcec..000000000
--- a/tests-clar/config/multivar.c
+++ /dev/null
@@ -1,165 +0,0 @@
-#include "clar_libgit2.h"
-
-static const char *_name = "remote.fancy.url";
-
-void test_config_multivar__initialize(void)
-{
- cl_fixture_sandbox("config");
-}
-
-void test_config_multivar__cleanup(void)
-{
- cl_fixture_cleanup("config");
-}
-
-static int mv_read_cb(const git_config_entry *entry, void *data)
-{
- int *n = (int *) data;
-
- if (!strcmp(entry->name, _name))
- (*n)++;
-
- return 0;
-}
-
-void test_config_multivar__foreach(void)
-{
- git_config *cfg;
- int n = 0;
-
- cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config11")));
-
- cl_git_pass(git_config_foreach(cfg, mv_read_cb, &n));
- cl_assert(n == 2);
-
- git_config_free(cfg);
-}
-
-static int cb(const git_config_entry *entry, void *data)
-{
- int *n = (int *) data;
-
- GIT_UNUSED(entry);
-
- (*n)++;
-
- return 0;
-}
-
-void test_config_multivar__get(void)
-{
- git_config *cfg;
- int n;
-
- cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
-
- n = 0;
- cl_git_pass(git_config_get_multivar(cfg, _name, NULL, cb, &n));
- cl_assert(n == 2);
-
- n = 0;
- cl_git_pass(git_config_get_multivar(cfg, _name, "example", cb, &n));
- cl_assert(n == 1);
-
- git_config_free(cfg);
-}
-
-void test_config_multivar__add(void)
-{
- git_config *cfg;
- int n;
-
- cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
- cl_git_pass(git_config_set_multivar(cfg, _name, "nonexistant", "git://git.otherplace.org/libgit2"));
-
- n = 0;
- cl_git_pass(git_config_get_multivar(cfg, _name, NULL, cb, &n));
- cl_assert(n == 3);
-
- n = 0;
- cl_git_pass(git_config_get_multivar(cfg, _name, "otherplace", cb, &n));
- cl_assert(n == 1);
-
- git_config_free(cfg);
-
- /* We know it works in memory, let's see if the file is written correctly */
-
- cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
-
- n = 0;
- cl_git_pass(git_config_get_multivar(cfg, _name, NULL, cb, &n));
- cl_assert(n == 3);
-
- n = 0;
- cl_git_pass(git_config_get_multivar(cfg, _name, "otherplace", cb, &n));
- cl_assert(n == 1);
-
- git_config_free(cfg);
-}
-
-void test_config_multivar__add_new(void)
-{
- const char *var = "a.brand.new";
- git_config *cfg;
- int n;
-
- cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
-
- cl_git_pass(git_config_set_multivar(cfg, var, "", "variable"));
- n = 0;
- cl_git_pass(git_config_get_multivar(cfg, var, NULL, cb, &n));
- cl_assert(n == 1);
-
- git_config_free(cfg);
-}
-
-void test_config_multivar__replace(void)
-{
- git_config *cfg;
- int n;
-
- cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
-
- n = 0;
- cl_git_pass(git_config_get_multivar(cfg, _name, NULL, cb, &n));
- cl_assert(n == 2);
-
- cl_git_pass(git_config_set_multivar(cfg, _name, "github", "git://git.otherplace.org/libgit2"));
-
- n = 0;
- cl_git_pass(git_config_get_multivar(cfg, _name, NULL, cb, &n));
- cl_assert(n == 2);
-
- git_config_free(cfg);
-
- cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
-
- n = 0;
- cl_git_pass(git_config_get_multivar(cfg, _name, NULL, cb, &n));
- cl_assert(n == 2);
-
- git_config_free(cfg);
-}
-
-void test_config_multivar__replace_multiple(void)
-{
- git_config *cfg;
- int n;
-
- cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
- cl_git_pass(git_config_set_multivar(cfg, _name, "git://", "git://git.otherplace.org/libgit2"));
-
- n = 0;
- cl_git_pass(git_config_get_multivar(cfg, _name, "otherplace", cb, &n));
- cl_assert(n == 2);
-
- git_config_free(cfg);
-
- cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
-
- n = 0;
- cl_git_pass(git_config_get_multivar(cfg, _name, "otherplace", cb, &n));
- cl_assert(n == 2);
-
- git_config_free(cfg);
-}
diff --git a/tests-clar/config/read.c b/tests-clar/config/read.c
deleted file mode 100644
index 9f943d0f6..000000000
--- a/tests-clar/config/read.c
+++ /dev/null
@@ -1,451 +0,0 @@
-#include "clar_libgit2.h"
-
-void test_config_read__simple_read(void)
-{
- git_config *cfg;
- int32_t i;
-
- cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config0")));
-
- cl_git_pass(git_config_get_int32(&i, cfg, "core.repositoryformatversion"));
- cl_assert(i == 0);
- cl_git_pass(git_config_get_bool(&i, cfg, "core.filemode"));
- cl_assert(i == 1);
- cl_git_pass(git_config_get_bool(&i, cfg, "core.bare"));
- cl_assert(i == 0);
- cl_git_pass(git_config_get_bool(&i, cfg, "core.logallrefupdates"));
- cl_assert(i == 1);
-
- git_config_free(cfg);
-}
-
-void test_config_read__case_sensitive(void)
-{
- git_config *cfg;
- int i;
- const char *str;
-
- cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config1")));
-
- cl_git_pass(git_config_get_string(&str, cfg, "this.that.other"));
- cl_assert_equal_s(str, "true");
- cl_git_pass(git_config_get_string(&str, cfg, "this.That.other"));
- cl_assert_equal_s(str, "yes");
-
- cl_git_pass(git_config_get_bool(&i, cfg, "this.that.other"));
- cl_assert(i == 1);
- cl_git_pass(git_config_get_bool(&i, cfg, "this.That.other"));
- cl_assert(i == 1);
-
- /* This one doesn't exist */
- cl_must_fail(git_config_get_bool(&i, cfg, "this.thaT.other"));
-
- git_config_free(cfg);
-}
-
-/*
- * If \ is the last non-space character on the line, we read the next
- * one, separating each line with SP.
- */
-void test_config_read__multiline_value(void)
-{
- git_config *cfg;
- const char *str;
-
- cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config2")));
-
- cl_git_pass(git_config_get_string(&str, cfg, "this.That.and"));
- cl_assert_equal_s(str, "one one one two two three three");
-
- git_config_free(cfg);
-}
-
-/*
- * This kind of subsection declaration is case-insensitive
- */
-void test_config_read__subsection_header(void)
-{
- git_config *cfg;
- const char *str;
-
- cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config3")));
-
- cl_git_pass(git_config_get_string(&str, cfg, "section.subsection.var"));
- cl_assert_equal_s(str, "hello");
-
- /* The subsection is transformed to lower-case */
- cl_must_fail(git_config_get_string(&str, cfg, "section.subSectIon.var"));
-
- git_config_free(cfg);
-}
-
-void test_config_read__lone_variable(void)
-{
- git_config *cfg;
- const char *str;
- int i;
-
- cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config4")));
-
- cl_git_fail(git_config_get_int32(&i, cfg, "some.section.variable"));
-
- cl_git_pass(git_config_get_string(&str, cfg, "some.section.variable"));
- cl_assert_equal_s(str, "");
-
- cl_git_pass(git_config_get_bool(&i, cfg, "some.section.variable"));
- cl_assert(i == 1);
-
- cl_git_pass(git_config_get_string(&str, cfg, "some.section.variableeq"));
- cl_assert_equal_s(str, "");
-
- cl_git_pass(git_config_get_bool(&i, cfg, "some.section.variableeq"));
- cl_assert(i == 0);
-
- git_config_free(cfg);
-}
-
-void test_config_read__number_suffixes(void)
-{
- git_config *cfg;
- int64_t i;
-
- cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config5")));
-
- cl_git_pass(git_config_get_int64(&i, cfg, "number.simple"));
- cl_assert(i == 1);
-
- cl_git_pass(git_config_get_int64(&i, cfg, "number.k"));
- cl_assert(i == 1 * 1024);
-
- cl_git_pass(git_config_get_int64(&i, cfg, "number.kk"));
- cl_assert(i == 1 * 1024);
-
- cl_git_pass(git_config_get_int64(&i, cfg, "number.m"));
- cl_assert(i == 1 * 1024 * 1024);
-
- cl_git_pass(git_config_get_int64(&i, cfg, "number.mm"));
- cl_assert(i == 1 * 1024 * 1024);
-
- cl_git_pass(git_config_get_int64(&i, cfg, "number.g"));
- cl_assert(i == 1 * 1024 * 1024 * 1024);
-
- cl_git_pass(git_config_get_int64(&i, cfg, "number.gg"));
- cl_assert(i == 1 * 1024 * 1024 * 1024);
-
- git_config_free(cfg);
-}
-
-void test_config_read__blank_lines(void)
-{
- git_config *cfg;
- int i;
-
- cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config6")));
-
- cl_git_pass(git_config_get_bool(&i, cfg, "valid.subsection.something"));
- cl_assert(i == 1);
-
- cl_git_pass(git_config_get_bool(&i, cfg, "something.else.something"));
- cl_assert(i == 0);
-
- git_config_free(cfg);
-}
-
-void test_config_read__invalid_ext_headers(void)
-{
- git_config *cfg;
- cl_must_fail(git_config_open_ondisk(&cfg, cl_fixture("config/config7")));
-}
-
-void test_config_read__empty_files(void)
-{
- git_config *cfg;
- cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config8")));
- git_config_free(cfg);
-}
-
-void test_config_read__header_in_last_line(void)
-{
- git_config *cfg;
-
- cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config10")));
- git_config_free(cfg);
-}
-
-void test_config_read__prefixes(void)
-{
- git_config *cfg;
- const char *str;
-
- cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config9")));
- cl_git_pass(git_config_get_string(&str, cfg, "remote.ab.url"));
- cl_assert_equal_s(str, "http://example.com/git/ab");
-
- cl_git_pass(git_config_get_string(&str, cfg, "remote.abba.url"));
- cl_assert_equal_s(str, "http://example.com/git/abba");
-
- git_config_free(cfg);
-}
-
-void test_config_read__escaping_quotes(void)
-{
- git_config *cfg;
- const char *str;
-
- cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config13")));
- cl_git_pass(git_config_get_string(&str, cfg, "core.editor"));
- cl_assert_equal_s("\"C:/Program Files/Nonsense/bah.exe\" \"--some option\"", str);
-
- git_config_free(cfg);
-}
-
-static int count_cfg_entries_and_compare_levels(
- const git_config_entry *entry, void *payload)
-{
- int *count = payload;
-
- if (!strcmp(entry->value, "7") || !strcmp(entry->value, "17"))
- cl_assert(entry->level == GIT_CONFIG_LEVEL_GLOBAL);
- else
- cl_assert(entry->level == GIT_CONFIG_LEVEL_SYSTEM);
-
- (*count)++;
- return 0;
-}
-
-static int cfg_callback_countdown(const git_config_entry *entry, void *payload)
-{
- int *count = payload;
- GIT_UNUSED(entry);
- (*count)--;
- if (*count == 0)
- return -100;
- return 0;
-}
-
-void test_config_read__foreach(void)
-{
- git_config *cfg;
- int count, ret;
-
- cl_git_pass(git_config_new(&cfg));
- cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
- GIT_CONFIG_LEVEL_SYSTEM, 0));
- cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
- GIT_CONFIG_LEVEL_GLOBAL, 0));
-
- count = 0;
- cl_git_pass(git_config_foreach(cfg, count_cfg_entries_and_compare_levels, &count));
- cl_assert_equal_i(7, count);
-
- count = 3;
- cl_git_fail(ret = git_config_foreach(cfg, cfg_callback_countdown, &count));
- cl_assert_equal_i(GIT_EUSER, ret);
-
- git_config_free(cfg);
-}
-
-static int count_cfg_entries(const git_config_entry *entry, void *payload)
-{
- int *count = payload;
- GIT_UNUSED(entry);
- (*count)++;
- return 0;
-}
-
-void test_config_read__foreach_match(void)
-{
- git_config *cfg;
- int count;
-
- cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config9")));
-
- count = 0;
- cl_git_pass(
- git_config_foreach_match(cfg, "core.*", count_cfg_entries, &count));
- cl_assert_equal_i(3, count);
-
- count = 0;
- cl_git_pass(
- git_config_foreach_match(cfg, "remote\\.ab.*", count_cfg_entries, &count));
- cl_assert_equal_i(2, count);
-
- count = 0;
- cl_git_pass(
- git_config_foreach_match(cfg, ".*url$", count_cfg_entries, &count));
- cl_assert_equal_i(2, count);
-
- count = 0;
- cl_git_pass(
- git_config_foreach_match(cfg, ".*dummy.*", count_cfg_entries, &count));
- cl_assert_equal_i(2, count);
-
- count = 0;
- cl_git_pass(
- git_config_foreach_match(cfg, ".*nomatch.*", count_cfg_entries, &count));
- cl_assert_equal_i(0, count);
-
- git_config_free(cfg);
-}
-
-void test_config_read__whitespace_not_required_around_assignment(void)
-{
- git_config *cfg;
- const char *str;
-
- cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config14")));
-
- cl_git_pass(git_config_get_string(&str, cfg, "a.b"));
- cl_assert_equal_s(str, "c");
-
- cl_git_pass(git_config_get_string(&str, cfg, "d.e"));
- cl_assert_equal_s(str, "f");
-
- git_config_free(cfg);
-}
-
-void test_config_read__read_git_config_entry(void)
-{
- git_config *cfg;
- const git_config_entry *entry;
-
- cl_git_pass(git_config_new(&cfg));
- cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
- GIT_CONFIG_LEVEL_SYSTEM, 0));
-
- cl_git_pass(git_config_get_entry(&entry, cfg, "core.dummy2"));
- cl_assert_equal_s("core.dummy2", entry->name);
- cl_assert_equal_s("42", entry->value);
- cl_assert_equal_i(GIT_CONFIG_LEVEL_SYSTEM, entry->level);
-
- git_config_free(cfg);
-}
-
-/*
- * At the beginning of the test:
- * - config9 has: core.dummy2=42
- * - config15 has: core.dummy2=7
- * - config16 has: core.dummy2=28
- */
-void test_config_read__local_config_overrides_global_config_overrides_system_config(void)
-{
- git_config *cfg;
- int32_t i;
-
- cl_git_pass(git_config_new(&cfg));
- cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
- GIT_CONFIG_LEVEL_SYSTEM, 0));
- cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
- GIT_CONFIG_LEVEL_GLOBAL, 0));
- cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config16"),
- GIT_CONFIG_LEVEL_LOCAL, 0));
-
- cl_git_pass(git_config_get_int32(&i, cfg, "core.dummy2"));
- cl_assert_equal_i(28, i);
-
- git_config_free(cfg);
-
- cl_git_pass(git_config_new(&cfg));
- cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
- GIT_CONFIG_LEVEL_SYSTEM, 0));
- cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
- GIT_CONFIG_LEVEL_GLOBAL, 0));
-
- cl_git_pass(git_config_get_int32(&i, cfg, "core.dummy2"));
- cl_assert_equal_i(7, i);
-
- git_config_free(cfg);
-}
-
-/*
- * At the beginning of the test:
- * - config9 has: core.global does not exist
- * - config15 has: core.global=17
- * - config16 has: core.global=29
- *
- * And also:
- * - config9 has: core.system does not exist
- * - config15 has: core.system does not exist
- * - config16 has: core.system=11
- */
-void test_config_read__fallback_from_local_to_global_and_from_global_to_system(void)
-{
- git_config *cfg;
- int32_t i;
-
- cl_git_pass(git_config_new(&cfg));
- cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
- GIT_CONFIG_LEVEL_SYSTEM, 0));
- cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
- GIT_CONFIG_LEVEL_GLOBAL, 0));
- cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config16"),
- GIT_CONFIG_LEVEL_LOCAL, 0));
-
- cl_git_pass(git_config_get_int32(&i, cfg, "core.global"));
- cl_assert_equal_i(17, i);
- cl_git_pass(git_config_get_int32(&i, cfg, "core.system"));
- cl_assert_equal_i(11, i);
-
- git_config_free(cfg);
-}
-
-/*
- * At the beginning of the test, config18 has:
- * int32global = 28
- * int64global = 9223372036854775803
- * boolglobal = true
- * stringglobal = I'm a global config value!
- *
- * And config19 has:
- * int32global = -1
- * int64global = -2
- * boolglobal = false
- * stringglobal = don't find me!
- *
- */
-void test_config_read__simple_read_from_specific_level(void)
-{
- git_config *cfg, *cfg_specific;
- int i;
- int64_t l, expected = +9223372036854775803;
- const char *s;
-
- 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));
- cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config19"),
- GIT_CONFIG_LEVEL_SYSTEM, 0));
-
- cl_git_pass(git_config_open_level(&cfg_specific, cfg, GIT_CONFIG_LEVEL_GLOBAL));
-
- cl_git_pass(git_config_get_int32(&i, cfg_specific, "core.int32global"));
- cl_assert_equal_i(28, i);
- cl_git_pass(git_config_get_int64(&l, cfg_specific, "core.int64global"));
- cl_assert(l == expected);
- cl_git_pass(git_config_get_bool(&i, cfg_specific, "core.boolglobal"));
- cl_assert_equal_b(true, i);
- cl_git_pass(git_config_get_string(&s, cfg_specific, "core.stringglobal"));
- cl_assert_equal_s("I'm a global config value!", s);
-
- git_config_free(cfg_specific);
- git_config_free(cfg);
-}
-
-static void clean_empty_config(void *unused)
-{
- GIT_UNUSED(unused);
- cl_fixture_cleanup("./empty");
-}
-
-void test_config_read__can_load_and_parse_an_empty_config_file(void)
-{
- git_config *cfg;
- int i;
-
- cl_set_cleanup(&clean_empty_config, NULL);
- cl_git_mkfile("./empty", "");
- cl_git_pass(git_config_open_ondisk(&cfg, "./empty"));
- cl_assert_equal_i(GIT_ENOTFOUND, git_config_get_int32(&i, cfg, "nope.neither"));
-
- git_config_free(cfg);
-}
diff --git a/tests-clar/config/stress.c b/tests-clar/config/stress.c
deleted file mode 100644
index 8cc64d23c..000000000
--- a/tests-clar/config/stress.c
+++ /dev/null
@@ -1,92 +0,0 @@
-#include "clar_libgit2.h"
-
-#include "filebuf.h"
-#include "fileops.h"
-#include "posix.h"
-
-#define TEST_CONFIG "git-test-config"
-
-void test_config_stress__initialize(void)
-{
- git_filebuf file = GIT_FILEBUF_INIT;
-
- cl_git_pass(git_filebuf_open(&file, TEST_CONFIG, 0));
-
- git_filebuf_printf(&file, "[color]\n\tui = auto\n");
- git_filebuf_printf(&file, "[core]\n\teditor = \n");
-
- cl_git_pass(git_filebuf_commit(&file, 0666));
-}
-
-void test_config_stress__cleanup(void)
-{
- p_unlink(TEST_CONFIG);
-}
-
-void test_config_stress__dont_break_on_invalid_input(void)
-{
- const char *editor, *color;
- git_config *config;
-
- cl_assert(git_path_exists(TEST_CONFIG));
- cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
-
- cl_git_pass(git_config_get_string(&color, config, "color.ui"));
- cl_git_pass(git_config_get_string(&editor, config, "core.editor"));
-
- git_config_free(config);
-}
-
-void test_config_stress__comments(void)
-{
- git_config *config;
- const char *str;
-
- cl_git_pass(git_config_open_ondisk(&config, cl_fixture("config/config12")));
-
- cl_git_pass(git_config_get_string(&str, config, "some.section.other"));
- cl_assert_equal_s("hello! \" ; ; ; ", str);
-
- cl_git_pass(git_config_get_string(&str, config, "some.section.multi"));
- cl_assert_equal_s("hi, this is a ; multiline comment # with ;\n special chars and other stuff !@#", str);
-
- cl_git_pass(git_config_get_string(&str, config, "some.section.back"));
- cl_assert_equal_s("this is \ba phrase", str);
-
- git_config_free(config);
-}
-
-void test_config_stress__escape_subsection_names(void)
-{
- git_config *config;
- const char *str;
-
- cl_assert(git_path_exists("git-test-config"));
- cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
-
- cl_git_pass(git_config_set_string(config, "some.sec\\tion.other", "foo"));
- git_config_free(config);
-
- cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
-
- cl_git_pass(git_config_get_string(&str, config, "some.sec\\tion.other"));
- cl_assert_equal_s("foo", str);
- git_config_free(config);
-}
-
-void test_config_stress__trailing_backslash(void)
-{
- git_config *config;
- const char *str;
- const char *path = "C:\\iam\\some\\windows\\path\\";
-
- cl_assert(git_path_exists("git-test-config"));
- cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
- cl_git_pass(git_config_set_string(config, "windows.path", path));
- git_config_free(config);
-
- cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
- cl_git_pass(git_config_get_string(&str, config, "windows.path"));
- cl_assert_equal_s(path, str);
- git_config_free(config);
-}
diff --git a/tests-clar/config/validkeyname.c b/tests-clar/config/validkeyname.c
deleted file mode 100644
index 03c13d723..000000000
--- a/tests-clar/config/validkeyname.c
+++ /dev/null
@@ -1,68 +0,0 @@
-#include "clar_libgit2.h"
-
-#include "config.h"
-
-static git_config *cfg;
-static const char *value;
-
-void test_config_validkeyname__initialize(void)
-{
- cl_fixture_sandbox("config/config10");
-
- cl_git_pass(git_config_open_ondisk(&cfg, "config10"));
-}
-
-void test_config_validkeyname__cleanup(void)
-{
- git_config_free(cfg);
- cfg = NULL;
-
- cl_fixture_cleanup("config10");
-}
-
-static void assert_invalid_config_key_name(const char *name)
-{
- cl_git_fail_with(git_config_get_string(&value, cfg, name),
- GIT_EINVALIDSPEC);
- cl_git_fail_with(git_config_set_string(cfg, name, "42"),
- GIT_EINVALIDSPEC);
- cl_git_fail_with(git_config_delete_entry(cfg, name),
- GIT_EINVALIDSPEC);
- cl_git_fail_with(git_config_get_multivar(cfg, name, "*", NULL, NULL),
- GIT_EINVALIDSPEC);
- cl_git_fail_with(git_config_set_multivar(cfg, name, "*", "42"),
- GIT_EINVALIDSPEC);
-}
-
-void test_config_validkeyname__accessing_requires_a_valid_name(void)
-{
- assert_invalid_config_key_name("");
- assert_invalid_config_key_name(".");
- assert_invalid_config_key_name("..");
- assert_invalid_config_key_name("core.");
- assert_invalid_config_key_name("d#ff.dirstat.lines");
- assert_invalid_config_key_name("diff.dirstat.lines#");
- assert_invalid_config_key_name("dif\nf.dirstat.lines");
- assert_invalid_config_key_name("dif.dir\nstat.lines");
- assert_invalid_config_key_name("dif.dirstat.li\nes");
-}
-
-static void assert_invalid_config_section_name(git_repository *repo, const char *name)
-{
- cl_git_fail_with(git_config_rename_section(repo, "branch.remoteless", name), GIT_EINVALIDSPEC);
-}
-
-void test_config_validkeyname__renaming_a_section_requires_a_valid_name(void)
-{
- git_repository *repo;
-
- cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
-
- assert_invalid_config_section_name(repo, "");
- assert_invalid_config_section_name(repo, "bra\nch");
- assert_invalid_config_section_name(repo, "branc#");
- assert_invalid_config_section_name(repo, "bra\nch.duh");
- assert_invalid_config_section_name(repo, "branc#.duh");
-
- git_repository_free(repo);
-}
diff --git a/tests-clar/config/write.c b/tests-clar/config/write.c
deleted file mode 100644
index d70612a97..000000000
--- a/tests-clar/config/write.c
+++ /dev/null
@@ -1,244 +0,0 @@
-#include "clar_libgit2.h"
-
-void test_config_write__initialize(void)
-{
- cl_fixture_sandbox("config/config9");
- cl_fixture_sandbox("config/config15");
- cl_fixture_sandbox("config/config17");
-}
-
-void test_config_write__cleanup(void)
-{
- cl_fixture_cleanup("config9");
- cl_fixture_cleanup("config15");
- cl_fixture_cleanup("config17");
-}
-
-void test_config_write__replace_value(void)
-{
- git_config *cfg;
- int i;
- int64_t l, expected = +9223372036854775803;
-
- /* By freeing the config, we make sure we flush the values */
- cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
- cl_git_pass(git_config_set_int32(cfg, "core.dummy", 5));
- git_config_free(cfg);
-
- cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
- cl_git_pass(git_config_get_int32(&i, cfg, "core.dummy"));
- cl_assert(i == 5);
- git_config_free(cfg);
-
- cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
- cl_git_pass(git_config_set_int32(cfg, "core.dummy", 1));
- git_config_free(cfg);
-
- cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
- cl_git_pass(git_config_set_int64(cfg, "core.verylong", expected));
- git_config_free(cfg);
-
- cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
- cl_git_pass(git_config_get_int64(&l, cfg, "core.verylong"));
- cl_assert(l == expected);
- git_config_free(cfg);
-
- cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
- cl_must_fail(git_config_get_int32(&i, cfg, "core.verylong"));
- git_config_free(cfg);
-
- cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
- cl_git_pass(git_config_set_int64(cfg, "core.verylong", 1));
- git_config_free(cfg);
-}
-
-void test_config_write__delete_value(void)
-{
- git_config *cfg;
- int32_t i;
-
- cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
- cl_git_pass(git_config_set_int32(cfg, "core.dummy", 5));
- git_config_free(cfg);
-
- cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
- cl_git_pass(git_config_delete_entry(cfg, "core.dummy"));
- git_config_free(cfg);
-
- cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
- cl_assert(git_config_get_int32(&i, cfg, "core.dummy") == GIT_ENOTFOUND);
- cl_git_pass(git_config_set_int32(cfg, "core.dummy", 1));
- git_config_free(cfg);
-}
-
-/*
- * At the beginning of the test:
- * - config9 has: core.dummy2=42
- * - config15 has: core.dummy2=7
- */
-void test_config_write__delete_value_at_specific_level(void)
-{
- git_config *cfg, *cfg_specific;
- int32_t i;
-
- cl_git_pass(git_config_open_ondisk(&cfg, "config15"));
- cl_git_pass(git_config_get_int32(&i, cfg, "core.dummy2"));
- cl_assert(i == 7);
- git_config_free(cfg);
-
- cl_git_pass(git_config_new(&cfg));
- cl_git_pass(git_config_add_file_ondisk(cfg, "config9",
- GIT_CONFIG_LEVEL_LOCAL, 0));
- cl_git_pass(git_config_add_file_ondisk(cfg, "config15",
- GIT_CONFIG_LEVEL_GLOBAL, 0));
-
- cl_git_pass(git_config_open_level(&cfg_specific, cfg, GIT_CONFIG_LEVEL_GLOBAL));
-
- cl_git_pass(git_config_delete_entry(cfg_specific, "core.dummy2"));
- git_config_free(cfg);
-
- cl_git_pass(git_config_open_ondisk(&cfg, "config15"));
- cl_assert(git_config_get_int32(&i, cfg, "core.dummy2") == GIT_ENOTFOUND);
- cl_git_pass(git_config_set_int32(cfg, "core.dummy2", 7));
-
- git_config_free(cfg_specific);
- git_config_free(cfg);
-}
-
-void test_config_write__write_subsection(void)
-{
- git_config *cfg;
- const char *str;
-
- cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
- cl_git_pass(git_config_set_string(cfg, "my.own.var", "works"));
- git_config_free(cfg);
-
- cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
- cl_git_pass(git_config_get_string(&str, cfg, "my.own.var"));
- cl_assert_equal_s("works", str);
- git_config_free(cfg);
-}
-
-void test_config_write__delete_inexistent(void)
-{
- git_config *cfg;
-
- cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
- cl_assert(git_config_delete_entry(cfg, "core.imaginary") == GIT_ENOTFOUND);
- git_config_free(cfg);
-}
-
-void test_config_write__value_containing_quotes(void)
-{
- git_config *cfg;
- const char* str;
-
- cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
- cl_git_pass(git_config_set_string(cfg, "core.somevar", "this \"has\" quotes"));
- cl_git_pass(git_config_get_string(&str, cfg, "core.somevar"));
- cl_assert_equal_s(str, "this \"has\" quotes");
- git_config_free(cfg);
-
- cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
- cl_git_pass(git_config_get_string(&str, cfg, "core.somevar"));
- cl_assert_equal_s(str, "this \"has\" quotes");
- git_config_free(cfg);
-
- /* The code path for values that already exist is different, check that one as well */
- cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
- cl_git_pass(git_config_set_string(cfg, "core.somevar", "this also \"has\" quotes"));
- cl_git_pass(git_config_get_string(&str, cfg, "core.somevar"));
- cl_assert_equal_s(str, "this also \"has\" quotes");
- git_config_free(cfg);
-
- cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
- cl_git_pass(git_config_get_string(&str, cfg, "core.somevar"));
- cl_assert_equal_s(str, "this also \"has\" quotes");
- git_config_free(cfg);
-}
-
-void test_config_write__escape_value(void)
-{
- git_config *cfg;
- const char* str;
-
- cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
- cl_git_pass(git_config_set_string(cfg, "core.somevar", "this \"has\" quotes and \t"));
- cl_git_pass(git_config_get_string(&str, cfg, "core.somevar"));
- cl_assert_equal_s(str, "this \"has\" quotes and \t");
- git_config_free(cfg);
-
- cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
- cl_git_pass(git_config_get_string(&str, cfg, "core.somevar"));
- cl_assert_equal_s(str, "this \"has\" quotes and \t");
- git_config_free(cfg);
-}
-
-void test_config_write__add_value_at_specific_level(void)
-{
- git_config *cfg, *cfg_specific;
- int i;
- int64_t l, expected = +9223372036854775803;
- const char *s;
-
- // 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));
- cl_git_pass(git_config_add_file_ondisk(cfg, "config15",
- GIT_CONFIG_LEVEL_GLOBAL, 0));
-
- cl_git_pass(git_config_open_level(&cfg_specific, cfg, GIT_CONFIG_LEVEL_GLOBAL));
-
- cl_git_pass(git_config_set_int32(cfg_specific, "core.int32global", 28));
- cl_git_pass(git_config_set_int64(cfg_specific, "core.int64global", expected));
- cl_git_pass(git_config_set_bool(cfg_specific, "core.boolglobal", true));
- cl_git_pass(git_config_set_string(cfg_specific, "core.stringglobal", "I'm a global config value!"));
- git_config_free(cfg_specific);
- git_config_free(cfg);
-
- // open config15 as local level config file
- cl_git_pass(git_config_open_ondisk(&cfg, "config15"));
-
- cl_git_pass(git_config_get_int32(&i, cfg, "core.int32global"));
- cl_assert_equal_i(28, i);
- cl_git_pass(git_config_get_int64(&l, cfg, "core.int64global"));
- cl_assert(l == expected);
- cl_git_pass(git_config_get_bool(&i, cfg, "core.boolglobal"));
- cl_assert_equal_b(true, i);
- cl_git_pass(git_config_get_string(&s, cfg, "core.stringglobal"));
- cl_assert_equal_s("I'm a global config value!", s);
-
- git_config_free(cfg);
-}
-
-void test_config_write__add_value_at_file_with_no_clrf_at_the_end(void)
-{
- git_config *cfg;
- int i;
-
- cl_git_pass(git_config_open_ondisk(&cfg, "config17"));
- cl_git_pass(git_config_set_int32(cfg, "core.newline", 7));
- git_config_free(cfg);
-
- cl_git_pass(git_config_open_ondisk(&cfg, "config17"));
- cl_git_pass(git_config_get_int32(&i, cfg, "core.newline"));
- cl_assert_equal_i(7, i);
-
- git_config_free(cfg);
-}
-
-void test_config_write__can_set_a_value_to_NULL(void)
-{
- git_repository *repository;
- git_config *config;
-
- repository = cl_git_sandbox_init("testrepo.git");
-
- cl_git_pass(git_repository_config(&config, repository));
- cl_git_fail(git_config_set_string(config, "a.b.c", NULL));
- git_config_free(config);
-
- cl_git_sandbox_cleanup();
-}
diff --git a/tests-clar/core/buffer.c b/tests-clar/core/buffer.c
deleted file mode 100644
index 3d8221e04..000000000
--- a/tests-clar/core/buffer.c
+++ /dev/null
@@ -1,988 +0,0 @@
-#include "clar_libgit2.h"
-#include "buffer.h"
-#include "buf_text.h"
-#include "hashsig.h"
-#include "fileops.h"
-
-#define TESTSTR "Have you seen that? Have you seeeen that??"
-const char *test_string = TESTSTR;
-const char *test_string_x2 = TESTSTR TESTSTR;
-
-#define TESTSTR_4096 REP1024("1234")
-#define TESTSTR_8192 REP1024("12341234")
-const char *test_4096 = TESTSTR_4096;
-const char *test_8192 = TESTSTR_8192;
-
-/* test basic data concatenation */
-void test_core_buffer__0(void)
-{
- git_buf buf = GIT_BUF_INIT;
-
- cl_assert(buf.size == 0);
-
- git_buf_puts(&buf, test_string);
- cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_equal_s(test_string, git_buf_cstr(&buf));
-
- git_buf_puts(&buf, test_string);
- cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_equal_s(test_string_x2, git_buf_cstr(&buf));
-
- git_buf_free(&buf);
-}
-
-/* test git_buf_printf */
-void test_core_buffer__1(void)
-{
- git_buf buf = GIT_BUF_INIT;
-
- git_buf_printf(&buf, "%s %s %d ", "shoop", "da", 23);
- cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_equal_s("shoop da 23 ", git_buf_cstr(&buf));
-
- git_buf_printf(&buf, "%s %d", "woop", 42);
- cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_equal_s("shoop da 23 woop 42", git_buf_cstr(&buf));
-
- git_buf_free(&buf);
-}
-
-/* more thorough test of concatenation options */
-void test_core_buffer__2(void)
-{
- git_buf buf = GIT_BUF_INIT;
- int i;
- char data[128];
-
- cl_assert(buf.size == 0);
-
- /* this must be safe to do */
- git_buf_free(&buf);
- cl_assert(buf.size == 0);
- cl_assert(buf.asize == 0);
-
- /* empty buffer should be empty string */
- cl_assert_equal_s("", git_buf_cstr(&buf));
- cl_assert(buf.size == 0);
- /* cl_assert(buf.asize == 0); -- should not assume what git_buf does */
-
- /* free should set us back to the beginning */
- git_buf_free(&buf);
- cl_assert(buf.size == 0);
- cl_assert(buf.asize == 0);
-
- /* add letter */
- git_buf_putc(&buf, '+');
- cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_equal_s("+", git_buf_cstr(&buf));
-
- /* add letter again */
- git_buf_putc(&buf, '+');
- cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_equal_s("++", git_buf_cstr(&buf));
-
- /* let's try that a few times */
- for (i = 0; i < 16; ++i) {
- git_buf_putc(&buf, '+');
- cl_assert(git_buf_oom(&buf) == 0);
- }
- cl_assert_equal_s("++++++++++++++++++", git_buf_cstr(&buf));
-
- git_buf_free(&buf);
-
- /* add data */
- git_buf_put(&buf, "xo", 2);
- cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_equal_s("xo", git_buf_cstr(&buf));
-
- /* add letter again */
- git_buf_put(&buf, "xo", 2);
- cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_equal_s("xoxo", git_buf_cstr(&buf));
-
- /* let's try that a few times */
- for (i = 0; i < 16; ++i) {
- git_buf_put(&buf, "xo", 2);
- cl_assert(git_buf_oom(&buf) == 0);
- }
- cl_assert_equal_s("xoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxo",
- git_buf_cstr(&buf));
-
- git_buf_free(&buf);
-
- /* set to string */
- git_buf_sets(&buf, test_string);
- cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_equal_s(test_string, git_buf_cstr(&buf));
-
- /* append string */
- git_buf_puts(&buf, test_string);
- cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_equal_s(test_string_x2, git_buf_cstr(&buf));
-
- /* set to string again (should overwrite - not append) */
- git_buf_sets(&buf, test_string);
- cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_equal_s(test_string, git_buf_cstr(&buf));
-
- /* test clear */
- git_buf_clear(&buf);
- cl_assert_equal_s("", git_buf_cstr(&buf));
-
- git_buf_free(&buf);
-
- /* test extracting data into buffer */
- git_buf_puts(&buf, REP4("0123456789"));
- cl_assert(git_buf_oom(&buf) == 0);
-
- git_buf_copy_cstr(data, sizeof(data), &buf);
- cl_assert_equal_s(REP4("0123456789"), data);
- git_buf_copy_cstr(data, 11, &buf);
- cl_assert_equal_s("0123456789", data);
- git_buf_copy_cstr(data, 3, &buf);
- cl_assert_equal_s("01", data);
- git_buf_copy_cstr(data, 1, &buf);
- cl_assert_equal_s("", data);
-
- git_buf_copy_cstr(data, sizeof(data), &buf);
- cl_assert_equal_s(REP4("0123456789"), data);
-
- git_buf_sets(&buf, REP256("x"));
- git_buf_copy_cstr(data, sizeof(data), &buf);
- /* since sizeof(data) == 128, only 127 bytes should be copied */
- cl_assert_equal_s(REP4(REP16("x")) REP16("x") REP16("x")
- REP16("x") "xxxxxxxxxxxxxxx", data);
-
- git_buf_free(&buf);
-
- git_buf_copy_cstr(data, sizeof(data), &buf);
- cl_assert_equal_s("", data);
-}
-
-/* let's do some tests with larger buffers to push our limits */
-void test_core_buffer__3(void)
-{
- git_buf buf = GIT_BUF_INIT;
-
- /* set to string */
- git_buf_set(&buf, test_4096, 4096);
- cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_equal_s(test_4096, git_buf_cstr(&buf));
-
- /* append string */
- git_buf_puts(&buf, test_4096);
- cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_equal_s(test_8192, git_buf_cstr(&buf));
-
- /* set to string again (should overwrite - not append) */
- git_buf_set(&buf, test_4096, 4096);
- cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_equal_s(test_4096, git_buf_cstr(&buf));
-
- git_buf_free(&buf);
-}
-
-/* let's try some producer/consumer tests */
-void test_core_buffer__4(void)
-{
- git_buf buf = GIT_BUF_INIT;
- int i;
-
- for (i = 0; i < 10; ++i) {
- git_buf_puts(&buf, "1234"); /* add 4 */
- cl_assert(git_buf_oom(&buf) == 0);
- git_buf_consume(&buf, buf.ptr + 2); /* eat the first two */
- cl_assert(strlen(git_buf_cstr(&buf)) == (size_t)((i + 1) * 2));
- }
- /* we have appended 1234 10x and removed the first 20 letters */
- cl_assert_equal_s("12341234123412341234", git_buf_cstr(&buf));
-
- git_buf_consume(&buf, NULL);
- cl_assert_equal_s("12341234123412341234", git_buf_cstr(&buf));
-
- git_buf_consume(&buf, "invalid pointer");
- cl_assert_equal_s("12341234123412341234", git_buf_cstr(&buf));
-
- git_buf_consume(&buf, buf.ptr);
- cl_assert_equal_s("12341234123412341234", git_buf_cstr(&buf));
-
- git_buf_consume(&buf, buf.ptr + 1);
- cl_assert_equal_s("2341234123412341234", git_buf_cstr(&buf));
-
- git_buf_consume(&buf, buf.ptr + buf.size);
- cl_assert_equal_s("", git_buf_cstr(&buf));
-
- git_buf_free(&buf);
-}
-
-
-static void
-check_buf_append(
- const char* data_a,
- const char* data_b,
- const char* expected_data,
- size_t expected_size,
- size_t expected_asize)
-{
- git_buf tgt = GIT_BUF_INIT;
-
- git_buf_sets(&tgt, data_a);
- cl_assert(git_buf_oom(&tgt) == 0);
- git_buf_puts(&tgt, data_b);
- cl_assert(git_buf_oom(&tgt) == 0);
- cl_assert_equal_s(expected_data, git_buf_cstr(&tgt));
- cl_assert(tgt.size == expected_size);
- if (expected_asize > 0)
- cl_assert(tgt.asize == expected_asize);
-
- git_buf_free(&tgt);
-}
-
-static void
-check_buf_append_abc(
- const char* buf_a,
- const char* buf_b,
- const char* buf_c,
- const char* expected_ab,
- const char* expected_abc,
- const char* expected_abca,
- const char* expected_abcab,
- const char* expected_abcabc)
-{
- git_buf buf = GIT_BUF_INIT;
-
- git_buf_sets(&buf, buf_a);
- cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_equal_s(buf_a, git_buf_cstr(&buf));
-
- git_buf_puts(&buf, buf_b);
- cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_equal_s(expected_ab, git_buf_cstr(&buf));
-
- git_buf_puts(&buf, buf_c);
- cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_equal_s(expected_abc, git_buf_cstr(&buf));
-
- git_buf_puts(&buf, buf_a);
- cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_equal_s(expected_abca, git_buf_cstr(&buf));
-
- git_buf_puts(&buf, buf_b);
- cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_equal_s(expected_abcab, git_buf_cstr(&buf));
-
- git_buf_puts(&buf, buf_c);
- cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_equal_s(expected_abcabc, git_buf_cstr(&buf));
-
- git_buf_free(&buf);
-}
-
-/* more variations on append tests */
-void test_core_buffer__5(void)
-{
- check_buf_append("", "", "", 0, 8);
- check_buf_append("a", "", "a", 1, 8);
- check_buf_append("", "a", "a", 1, 8);
- check_buf_append("", "a", "a", 1, 8);
- check_buf_append("a", "", "a", 1, 8);
- check_buf_append("a", "b", "ab", 2, 8);
- check_buf_append("", "abcdefgh", "abcdefgh", 8, 16);
- check_buf_append("abcdefgh", "", "abcdefgh", 8, 16);
-
- /* buffer with starting asize will grow to:
- * 1 -> 2, 2 -> 3, 3 -> 5, 4 -> 6, 5 -> 8, 6 -> 9,
- * 7 -> 11, 8 -> 12, 9 -> 14, 10 -> 15, 11 -> 17, 12 -> 18,
- * 13 -> 20, 14 -> 21, 15 -> 23, 16 -> 24, 17 -> 26, 18 -> 27,
- * 19 -> 29, 20 -> 30, 21 -> 32, 22 -> 33, 23 -> 35, 24 -> 36,
- * ...
- * follow sequence until value > target size,
- * then round up to nearest multiple of 8.
- */
-
- check_buf_append("abcdefgh", "/", "abcdefgh/", 9, 16);
- check_buf_append("abcdefgh", "ijklmno", "abcdefghijklmno", 15, 16);
- check_buf_append("abcdefgh", "ijklmnop", "abcdefghijklmnop", 16, 24);
- check_buf_append("0123456789", "0123456789",
- "01234567890123456789", 20, 24);
- check_buf_append(REP16("x"), REP16("o"),
- REP16("x") REP16("o"), 32, 40);
-
- check_buf_append(test_4096, "", test_4096, 4096, 4104);
- check_buf_append(test_4096, test_4096, test_8192, 8192, 9240);
-
- /* check sequences of appends */
- check_buf_append_abc("a", "b", "c",
- "ab", "abc", "abca", "abcab", "abcabc");
- check_buf_append_abc("a1", "b2", "c3",
- "a1b2", "a1b2c3", "a1b2c3a1",
- "a1b2c3a1b2", "a1b2c3a1b2c3");
- check_buf_append_abc("a1/", "b2/", "c3/",
- "a1/b2/", "a1/b2/c3/", "a1/b2/c3/a1/",
- "a1/b2/c3/a1/b2/", "a1/b2/c3/a1/b2/c3/");
-}
-
-/* test swap */
-void test_core_buffer__6(void)
-{
- git_buf a = GIT_BUF_INIT;
- git_buf b = GIT_BUF_INIT;
-
- git_buf_sets(&a, "foo");
- cl_assert(git_buf_oom(&a) == 0);
- git_buf_sets(&b, "bar");
- cl_assert(git_buf_oom(&b) == 0);
-
- cl_assert_equal_s("foo", git_buf_cstr(&a));
- cl_assert_equal_s("bar", git_buf_cstr(&b));
-
- git_buf_swap(&a, &b);
-
- cl_assert_equal_s("bar", git_buf_cstr(&a));
- cl_assert_equal_s("foo", git_buf_cstr(&b));
-
- git_buf_free(&a);
- git_buf_free(&b);
-}
-
-
-/* test detach/attach data */
-void test_core_buffer__7(void)
-{
- const char *fun = "This is fun";
- git_buf a = GIT_BUF_INIT;
- char *b = NULL;
-
- git_buf_sets(&a, "foo");
- cl_assert(git_buf_oom(&a) == 0);
- cl_assert_equal_s("foo", git_buf_cstr(&a));
-
- b = git_buf_detach(&a);
-
- cl_assert_equal_s("foo", b);
- cl_assert_equal_s("", a.ptr);
- git__free(b);
-
- b = git_buf_detach(&a);
-
- cl_assert_equal_s(NULL, b);
- cl_assert_equal_s("", a.ptr);
-
- git_buf_free(&a);
-
- b = git__strdup(fun);
- git_buf_attach(&a, b, 0);
-
- cl_assert_equal_s(fun, a.ptr);
- cl_assert(a.size == strlen(fun));
- cl_assert(a.asize == strlen(fun) + 1);
-
- git_buf_free(&a);
-
- b = git__strdup(fun);
- git_buf_attach(&a, b, strlen(fun) + 1);
-
- cl_assert_equal_s(fun, a.ptr);
- cl_assert(a.size == strlen(fun));
- cl_assert(a.asize == strlen(fun) + 1);
-
- git_buf_free(&a);
-}
-
-
-static void
-check_joinbuf_2(
- const char *a,
- const char *b,
- const char *expected)
-{
- char sep = '/';
- git_buf buf = GIT_BUF_INIT;
-
- git_buf_join(&buf, sep, a, b);
- cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_equal_s(expected, git_buf_cstr(&buf));
- git_buf_free(&buf);
-}
-
-static void
-check_joinbuf_n_2(
- const char *a,
- const char *b,
- const char *expected)
-{
- char sep = '/';
- git_buf buf = GIT_BUF_INIT;
-
- git_buf_sets(&buf, a);
- cl_assert(git_buf_oom(&buf) == 0);
-
- git_buf_join_n(&buf, sep, 1, b);
- cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_equal_s(expected, git_buf_cstr(&buf));
-
- git_buf_free(&buf);
-}
-
-static void
-check_joinbuf_n_4(
- const char *a,
- const char *b,
- const char *c,
- const char *d,
- const char *expected)
-{
- char sep = ';';
- git_buf buf = GIT_BUF_INIT;
- git_buf_join_n(&buf, sep, 4, a, b, c, d);
- cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_equal_s(expected, git_buf_cstr(&buf));
- git_buf_free(&buf);
-}
-
-/* test join */
-void test_core_buffer__8(void)
-{
- git_buf a = GIT_BUF_INIT;
-
- git_buf_join_n(&a, '/', 1, "foo");
- cl_assert(git_buf_oom(&a) == 0);
- cl_assert_equal_s("foo", git_buf_cstr(&a));
-
- git_buf_join_n(&a, '/', 1, "bar");
- cl_assert(git_buf_oom(&a) == 0);
- cl_assert_equal_s("foo/bar", git_buf_cstr(&a));
-
- git_buf_join_n(&a, '/', 1, "baz");
- cl_assert(git_buf_oom(&a) == 0);
- cl_assert_equal_s("foo/bar/baz", git_buf_cstr(&a));
-
- git_buf_free(&a);
-
- check_joinbuf_2(NULL, "", "");
- check_joinbuf_2(NULL, "a", "a");
- check_joinbuf_2(NULL, "/a", "/a");
- check_joinbuf_2("", "", "");
- check_joinbuf_2("", "a", "a");
- check_joinbuf_2("", "/a", "/a");
- check_joinbuf_2("a", "", "a/");
- check_joinbuf_2("a", "/", "a/");
- check_joinbuf_2("a", "b", "a/b");
- check_joinbuf_2("/", "a", "/a");
- check_joinbuf_2("/", "", "/");
- check_joinbuf_2("/a", "/b", "/a/b");
- check_joinbuf_2("/a", "/b/", "/a/b/");
- check_joinbuf_2("/a/", "b/", "/a/b/");
- check_joinbuf_2("/a/", "/b/", "/a/b/");
- check_joinbuf_2("/a/", "//b/", "/a/b/");
- check_joinbuf_2("/abcd", "/defg", "/abcd/defg");
- check_joinbuf_2("/abcd", "/defg/", "/abcd/defg/");
- check_joinbuf_2("/abcd/", "defg/", "/abcd/defg/");
- check_joinbuf_2("/abcd/", "/defg/", "/abcd/defg/");
-
- check_joinbuf_n_2("", "", "");
- check_joinbuf_n_2("", "a", "a");
- check_joinbuf_n_2("", "/a", "/a");
- check_joinbuf_n_2("a", "", "a/");
- check_joinbuf_n_2("a", "/", "a/");
- check_joinbuf_n_2("a", "b", "a/b");
- check_joinbuf_n_2("/", "a", "/a");
- check_joinbuf_n_2("/", "", "/");
- check_joinbuf_n_2("/a", "/b", "/a/b");
- check_joinbuf_n_2("/a", "/b/", "/a/b/");
- check_joinbuf_n_2("/a/", "b/", "/a/b/");
- check_joinbuf_n_2("/a/", "/b/", "/a/b/");
- check_joinbuf_n_2("/abcd", "/defg", "/abcd/defg");
- check_joinbuf_n_2("/abcd", "/defg/", "/abcd/defg/");
- check_joinbuf_n_2("/abcd/", "defg/", "/abcd/defg/");
- check_joinbuf_n_2("/abcd/", "/defg/", "/abcd/defg/");
-
- check_joinbuf_n_4("", "", "", "", "");
- check_joinbuf_n_4("", "a", "", "", "a;");
- check_joinbuf_n_4("a", "", "", "", "a;");
- check_joinbuf_n_4("", "", "", "a", "a");
- check_joinbuf_n_4("a", "b", "", ";c;d;", "a;b;c;d;");
- check_joinbuf_n_4("a", "b", "", ";c;d", "a;b;c;d");
- check_joinbuf_n_4("abcd", "efgh", "ijkl", "mnop", "abcd;efgh;ijkl;mnop");
- check_joinbuf_n_4("abcd;", "efgh;", "ijkl;", "mnop;", "abcd;efgh;ijkl;mnop;");
- check_joinbuf_n_4(";abcd;", ";efgh;", ";ijkl;", ";mnop;", ";abcd;efgh;ijkl;mnop;");
-}
-
-void test_core_buffer__9(void)
-{
- git_buf buf = GIT_BUF_INIT;
-
- /* just some exhaustive tests of various separator placement */
- char *a[] = { "", "-", "a-", "-a", "-a-" };
- char *b[] = { "", "-", "b-", "-b", "-b-" };
- char sep[] = { 0, '-', '/' };
- char *expect_null[] = { "", "-", "a-", "-a", "-a-",
- "-", "--", "a--", "-a-", "-a--",
- "b-", "-b-", "a-b-", "-ab-", "-a-b-",
- "-b", "--b", "a--b", "-a-b", "-a--b",
- "-b-", "--b-", "a--b-", "-a-b-", "-a--b-" };
- char *expect_dash[] = { "", "-", "a-", "-a-", "-a-",
- "-", "-", "a-", "-a-", "-a-",
- "b-", "-b-", "a-b-", "-a-b-", "-a-b-",
- "-b", "-b", "a-b", "-a-b", "-a-b",
- "-b-", "-b-", "a-b-", "-a-b-", "-a-b-" };
- char *expect_slas[] = { "", "-/", "a-/", "-a/", "-a-/",
- "-", "-/-", "a-/-", "-a/-", "-a-/-",
- "b-", "-/b-", "a-/b-", "-a/b-", "-a-/b-",
- "-b", "-/-b", "a-/-b", "-a/-b", "-a-/-b",
- "-b-", "-/-b-", "a-/-b-", "-a/-b-", "-a-/-b-" };
- char **expect_values[] = { expect_null, expect_dash, expect_slas };
- char separator, **expect;
- unsigned int s, i, j;
-
- for (s = 0; s < sizeof(sep) / sizeof(char); ++s) {
- separator = sep[s];
- expect = expect_values[s];
-
- for (j = 0; j < sizeof(b) / sizeof(char*); ++j) {
- for (i = 0; i < sizeof(a) / sizeof(char*); ++i) {
- git_buf_join(&buf, separator, a[i], b[j]);
- cl_assert_equal_s(*expect, buf.ptr);
- expect++;
- }
- }
- }
-
- git_buf_free(&buf);
-}
-
-void test_core_buffer__10(void)
-{
- git_buf a = GIT_BUF_INIT;
-
- cl_git_pass(git_buf_join_n(&a, '/', 1, "test"));
- cl_assert_equal_s(a.ptr, "test");
- cl_git_pass(git_buf_join_n(&a, '/', 1, "string"));
- cl_assert_equal_s(a.ptr, "test/string");
- git_buf_clear(&a);
- cl_git_pass(git_buf_join_n(&a, '/', 3, "test", "string", "join"));
- cl_assert_equal_s(a.ptr, "test/string/join");
- cl_git_pass(git_buf_join_n(&a, '/', 2, a.ptr, "more"));
- cl_assert_equal_s(a.ptr, "test/string/join/test/string/join/more");
-
- git_buf_free(&a);
-}
-
-void test_core_buffer__11(void)
-{
- git_buf a = GIT_BUF_INIT;
- git_strarray t;
- char *t1[] = { "nothing", "in", "common" };
- char *t2[] = { "something", "something else", "some other" };
- char *t3[] = { "something", "some fun", "no fun" };
- char *t4[] = { "happy", "happier", "happiest" };
- char *t5[] = { "happiest", "happier", "happy" };
- char *t6[] = { "no", "nope", "" };
- char *t7[] = { "", "doesn't matter" };
-
- t.strings = t1;
- t.count = 3;
- cl_git_pass(git_buf_text_common_prefix(&a, &t));
- cl_assert_equal_s(a.ptr, "");
-
- t.strings = t2;
- t.count = 3;
- cl_git_pass(git_buf_text_common_prefix(&a, &t));
- cl_assert_equal_s(a.ptr, "some");
-
- t.strings = t3;
- t.count = 3;
- cl_git_pass(git_buf_text_common_prefix(&a, &t));
- cl_assert_equal_s(a.ptr, "");
-
- t.strings = t4;
- t.count = 3;
- cl_git_pass(git_buf_text_common_prefix(&a, &t));
- cl_assert_equal_s(a.ptr, "happ");
-
- t.strings = t5;
- t.count = 3;
- cl_git_pass(git_buf_text_common_prefix(&a, &t));
- cl_assert_equal_s(a.ptr, "happ");
-
- t.strings = t6;
- t.count = 3;
- cl_git_pass(git_buf_text_common_prefix(&a, &t));
- cl_assert_equal_s(a.ptr, "");
-
- t.strings = t7;
- t.count = 3;
- cl_git_pass(git_buf_text_common_prefix(&a, &t));
- cl_assert_equal_s(a.ptr, "");
-
- git_buf_free(&a);
-}
-
-void test_core_buffer__rfind_variants(void)
-{
- git_buf a = GIT_BUF_INIT;
- ssize_t len;
-
- cl_git_pass(git_buf_sets(&a, "/this/is/it/"));
-
- len = (ssize_t)git_buf_len(&a);
-
- cl_assert(git_buf_rfind(&a, '/') == len - 1);
- cl_assert(git_buf_rfind_next(&a, '/') == len - 4);
-
- cl_assert(git_buf_rfind(&a, 'i') == len - 3);
- cl_assert(git_buf_rfind_next(&a, 'i') == len - 3);
-
- cl_assert(git_buf_rfind(&a, 'h') == 2);
- cl_assert(git_buf_rfind_next(&a, 'h') == 2);
-
- cl_assert(git_buf_rfind(&a, 'q') == -1);
- cl_assert(git_buf_rfind_next(&a, 'q') == -1);
-
- git_buf_free(&a);
-}
-
-void test_core_buffer__puts_escaped(void)
-{
- git_buf a = GIT_BUF_INIT;
-
- git_buf_clear(&a);
- cl_git_pass(git_buf_text_puts_escaped(&a, "this is a test", "", ""));
- cl_assert_equal_s("this is a test", a.ptr);
-
- git_buf_clear(&a);
- cl_git_pass(git_buf_text_puts_escaped(&a, "this is a test", "t", "\\"));
- cl_assert_equal_s("\\this is a \\tes\\t", a.ptr);
-
- git_buf_clear(&a);
- cl_git_pass(git_buf_text_puts_escaped(&a, "this is a test", "i ", "__"));
- cl_assert_equal_s("th__is__ __is__ a__ test", a.ptr);
-
- git_buf_clear(&a);
- cl_git_pass(git_buf_text_puts_escape_regex(&a, "^match\\s*[A-Z]+.*"));
- cl_assert_equal_s("\\^match\\\\s\\*\\[A-Z\\]\\+\\.\\*", a.ptr);
-
- git_buf_free(&a);
-}
-
-static void assert_unescape(char *expected, char *to_unescape) {
- git_buf buf = GIT_BUF_INIT;
-
- cl_git_pass(git_buf_sets(&buf, to_unescape));
- git_buf_text_unescape(&buf);
- cl_assert_equal_s(expected, buf.ptr);
- cl_assert_equal_sz(strlen(expected), buf.size);
-
- git_buf_free(&buf);
-}
-
-void test_core_buffer__unescape(void)
-{
- assert_unescape("Escaped\\", "Es\\ca\\ped\\");
- assert_unescape("Es\\caped\\", "Es\\\\ca\\ped\\\\");
- assert_unescape("\\", "\\");
- assert_unescape("\\", "\\\\");
- assert_unescape("", "");
-}
-
-void test_core_buffer__base64(void)
-{
- git_buf buf = GIT_BUF_INIT;
-
- /* t h i s
- * 0x 74 68 69 73
- * 0b 01110100 01101000 01101001 01110011
- * 0b 011101 000110 100001 101001 011100 110000
- * 0x 1d 06 21 29 1c 30
- * d G h p c w
- */
- cl_git_pass(git_buf_put_base64(&buf, "this", 4));
- cl_assert_equal_s("dGhpcw==", buf.ptr);
-
- git_buf_clear(&buf);
- cl_git_pass(git_buf_put_base64(&buf, "this!", 5));
- cl_assert_equal_s("dGhpcyE=", buf.ptr);
-
- git_buf_clear(&buf);
- cl_git_pass(git_buf_put_base64(&buf, "this!\n", 6));
- cl_assert_equal_s("dGhpcyEK", buf.ptr);
-
- git_buf_free(&buf);
-}
-
-void test_core_buffer__classify_with_utf8(void)
-{
- char *data0 = "Simple text\n";
- size_t data0len = 12;
- char *data1 = "Is that UTF-8 data I see…\nYep!\n";
- size_t data1len = 31;
- char *data2 = "Internal NUL!!!\000\n\nI see you!\n";
- size_t data2len = 29;
- git_buf b;
-
- b.ptr = data0; b.size = b.asize = data0len;
- cl_assert(!git_buf_text_is_binary(&b));
- cl_assert(!git_buf_text_contains_nul(&b));
-
- b.ptr = data1; b.size = b.asize = data1len;
- cl_assert(git_buf_text_is_binary(&b));
- cl_assert(!git_buf_text_contains_nul(&b));
-
- b.ptr = data2; b.size = b.asize = data2len;
- cl_assert(git_buf_text_is_binary(&b));
- cl_assert(git_buf_text_contains_nul(&b));
-}
-
-#define SIMILARITY_TEST_DATA_1 \
- "test data\nright here\ninline\ntada\nneeds more data\nlots of data\n" \
- "is this enough?\nthere has to be enough data to fill the hash array!\n" \
- "Apparently 191 bytes is the minimum amount of data needed.\nHere goes!\n" \
- "Let's make sure we've got plenty to go with here.\n smile \n"
-
-void test_core_buffer__similarity_metric(void)
-{
- git_hashsig *a, *b;
- git_buf buf = GIT_BUF_INIT;
- int sim;
-
- /* in the first case, we compare data to itself and expect 100% match */
-
- cl_git_pass(git_buf_sets(&buf, SIMILARITY_TEST_DATA_1));
- cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
- cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
-
- cl_assert_equal_i(100, git_hashsig_compare(a, b));
-
- git_hashsig_free(a);
- git_hashsig_free(b);
-
- /* if we change just a single byte, how much does that change magnify? */
-
- cl_git_pass(git_buf_sets(&buf, SIMILARITY_TEST_DATA_1));
- cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
- cl_git_pass(git_buf_sets(&buf,
- "Test data\nright here\ninline\ntada\nneeds more data\nlots of data\n"
- "is this enough?\nthere has to be enough data to fill the hash array!\n"
- "Apparently 191 bytes is the minimum amount of data needed.\nHere goes!\n"
- "Let's make sure we've got plenty to go with here.\n smile \n"));
- cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
-
- sim = git_hashsig_compare(a, b);
-
- cl_assert(95 < sim && sim < 100); /* expect >95% similarity */
-
- git_hashsig_free(a);
- git_hashsig_free(b);
-
- /* let's try comparing data to a superset of itself */
-
- cl_git_pass(git_buf_sets(&buf, SIMILARITY_TEST_DATA_1));
- cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
- cl_git_pass(git_buf_sets(&buf, SIMILARITY_TEST_DATA_1
- "and if I add some more, it should still be pretty similar, yes?\n"));
- cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
-
- sim = git_hashsig_compare(a, b);
-
- cl_assert(70 < sim && sim < 80); /* expect in the 70-80% similarity range */
-
- git_hashsig_free(a);
- git_hashsig_free(b);
-
- /* what if we keep about half the original data and add half new */
-
- cl_git_pass(git_buf_sets(&buf, SIMILARITY_TEST_DATA_1));
- cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
- cl_git_pass(git_buf_sets(&buf,
- "test data\nright here\ninline\ntada\nneeds more data\nlots of data\n"
- "is this enough?\nthere has to be enough data to fill the hash array!\n"
- "okay, that's half the original\nwhat else can we add?\nmore data\n"
- "one more line will complete this\nshort\nlines\ndon't\nmatter\n"));
- cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
-
- sim = git_hashsig_compare(a, b);
-
- cl_assert(40 < sim && sim < 60); /* expect in the 40-60% similarity range */
-
- git_hashsig_free(a);
- git_hashsig_free(b);
-
- /* lastly, let's check that we can hash file content as well */
-
- cl_git_pass(git_buf_sets(&buf, SIMILARITY_TEST_DATA_1));
- cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
-
- cl_git_pass(git_futils_mkdir("scratch", NULL, 0755, GIT_MKDIR_PATH));
- cl_git_mkfile("scratch/testdata", SIMILARITY_TEST_DATA_1);
- cl_git_pass(git_hashsig_create_fromfile(
- &b, "scratch/testdata", GIT_HASHSIG_NORMAL));
-
- cl_assert_equal_i(100, git_hashsig_compare(a, b));
-
- git_hashsig_free(a);
- git_hashsig_free(b);
-
- git_buf_free(&buf);
- git_futils_rmdir_r("scratch", NULL, GIT_RMDIR_REMOVE_FILES);
-}
-
-
-void test_core_buffer__similarity_metric_whitespace(void)
-{
- git_hashsig *a, *b;
- git_buf buf = GIT_BUF_INIT;
- int sim, i, j;
- git_hashsig_option_t opt;
- const char *tabbed =
- " for (s = 0; s < sizeof(sep) / sizeof(char); ++s) {\n"
- " separator = sep[s];\n"
- " expect = expect_values[s];\n"
- "\n"
- " for (j = 0; j < sizeof(b) / sizeof(char*); ++j) {\n"
- " for (i = 0; i < sizeof(a) / sizeof(char*); ++i) {\n"
- " git_buf_join(&buf, separator, a[i], b[j]);\n"
- " cl_assert_equal_s(*expect, buf.ptr);\n"
- " expect++;\n"
- " }\n"
- " }\n"
- " }\n";
- const char *spaced =
- " for (s = 0; s < sizeof(sep) / sizeof(char); ++s) {\n"
- " separator = sep[s];\n"
- " expect = expect_values[s];\n"
- "\n"
- " for (j = 0; j < sizeof(b) / sizeof(char*); ++j) {\n"
- " for (i = 0; i < sizeof(a) / sizeof(char*); ++i) {\n"
- " git_buf_join(&buf, separator, a[i], b[j]);\n"
- " cl_assert_equal_s(*expect, buf.ptr);\n"
- " expect++;\n"
- " }\n"
- " }\n"
- " }\n";
- const char *crlf_spaced2 =
- " for (s = 0; s < sizeof(sep) / sizeof(char); ++s) {\r\n"
- " separator = sep[s];\r\n"
- " expect = expect_values[s];\r\n"
- "\r\n"
- " for (j = 0; j < sizeof(b) / sizeof(char*); ++j) {\r\n"
- " for (i = 0; i < sizeof(a) / sizeof(char*); ++i) {\r\n"
- " git_buf_join(&buf, separator, a[i], b[j]);\r\n"
- " cl_assert_equal_s(*expect, buf.ptr);\r\n"
- " expect++;\r\n"
- " }\r\n"
- " }\r\n"
- " }\r\n";
- const char *text[3] = { tabbed, spaced, crlf_spaced2 };
-
- /* let's try variations of our own code with whitespace changes */
-
- for (opt = GIT_HASHSIG_NORMAL; opt <= GIT_HASHSIG_SMART_WHITESPACE; ++opt) {
- for (i = 0; i < 3; ++i) {
- for (j = 0; j < 3; ++j) {
- cl_git_pass(git_buf_sets(&buf, text[i]));
- cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, opt));
-
- cl_git_pass(git_buf_sets(&buf, text[j]));
- cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, opt));
-
- sim = git_hashsig_compare(a, b);
-
- if (opt == GIT_HASHSIG_NORMAL) {
- if (i == j)
- cl_assert_equal_i(100, sim);
- else
- cl_assert(sim < 30); /* expect pretty different */
- } else {
- cl_assert_equal_i(100, sim);
- }
-
- git_hashsig_free(a);
- git_hashsig_free(b);
- }
- }
- }
-
- git_buf_free(&buf);
-}
-
-#define check_buf(expected,buf) do { \
- cl_assert_equal_s(expected, buf.ptr); \
- cl_assert_equal_sz(strlen(expected), buf.size); } while (0)
-
-void test_core_buffer__lf_and_crlf_conversions(void)
-{
- git_buf src = GIT_BUF_INIT, tgt = GIT_BUF_INIT;
-
- /* LF source */
-
- git_buf_sets(&src, "lf\nlf\nlf\nlf\n");
-
- cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
- check_buf("lf\r\nlf\r\nlf\r\nlf\r\n", tgt);
-
- cl_assert_equal_i(GIT_ENOTFOUND, git_buf_text_crlf_to_lf(&tgt, &src));
- /* no conversion needed if all LFs already */
-
- git_buf_sets(&src, "\nlf\nlf\nlf\nlf\nlf");
-
- cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
- check_buf("\r\nlf\r\nlf\r\nlf\r\nlf\r\nlf", tgt);
-
- cl_assert_equal_i(GIT_ENOTFOUND, git_buf_text_crlf_to_lf(&tgt, &src));
- /* no conversion needed if all LFs already */
-
- /* CRLF source */
-
- git_buf_sets(&src, "crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n");
-
- cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
- check_buf("crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n", tgt);
- check_buf(src.ptr, tgt);
-
- cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
- check_buf("crlf\ncrlf\ncrlf\ncrlf\n", tgt);
-
- git_buf_sets(&src, "\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf");
-
- cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
- check_buf("\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf", tgt);
- check_buf(src.ptr, tgt);
-
- cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
- check_buf("\ncrlf\ncrlf\ncrlf\ncrlf\ncrlf", tgt);
-
- /* CRLF in LF text */
-
- git_buf_sets(&src, "\nlf\nlf\ncrlf\r\nlf\nlf\ncrlf\r\n");
-
- cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
- check_buf("\r\nlf\r\nlf\r\ncrlf\r\nlf\r\nlf\r\ncrlf\r\n", tgt);
- cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
- check_buf("\nlf\nlf\ncrlf\nlf\nlf\ncrlf\n", tgt);
-
- /* LF in CRLF text */
-
- git_buf_sets(&src, "\ncrlf\r\ncrlf\r\nlf\ncrlf\r\ncrlf");
-
- cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
- check_buf("\r\ncrlf\r\ncrlf\r\nlf\r\ncrlf\r\ncrlf", tgt);
- cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
- check_buf("\ncrlf\ncrlf\nlf\ncrlf\ncrlf", tgt);
-
- /* bare CR test */
-
- git_buf_sets(&src, "\rcrlf\r\nlf\nlf\ncr\rcrlf\r\nlf\ncr\r");
-
- cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
- check_buf("\rcrlf\r\nlf\r\nlf\r\ncr\rcrlf\r\nlf\r\ncr\r", tgt);
- cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
- check_buf("\rcrlf\nlf\nlf\ncr\rcrlf\nlf\ncr\r", tgt);
-
- git_buf_sets(&src, "\rcr\r");
- cl_assert_equal_i(GIT_ENOTFOUND, git_buf_text_lf_to_crlf(&tgt, &src));
- cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
- check_buf("\rcr\r", tgt);
-
- git_buf_free(&src);
- git_buf_free(&tgt);
-}
diff --git a/tests-clar/core/dirent.c b/tests-clar/core/dirent.c
deleted file mode 100644
index 5a7859d1b..000000000
--- a/tests-clar/core/dirent.c
+++ /dev/null
@@ -1,235 +0,0 @@
-#include "clar_libgit2.h"
-#include "fileops.h"
-
-typedef struct name_data {
- int count; /* return count */
- char *name; /* filename */
-} name_data;
-
-typedef struct walk_data {
- char *sub; /* sub-directory name */
- name_data *names; /* name state data */
- git_buf path;
-} walk_data;
-
-
-static char *top_dir = "dir-walk";
-static walk_data *state_loc;
-
-static void setup(walk_data *d)
-{
- name_data *n;
-
- cl_must_pass(p_mkdir(top_dir, 0777));
-
- cl_must_pass(p_chdir(top_dir));
-
- if (strcmp(d->sub, ".") != 0)
- cl_must_pass(p_mkdir(d->sub, 0777));
-
- cl_git_pass(git_buf_sets(&d->path, d->sub));
-
- state_loc = d;
-
- for (n = d->names; n->name; n++) {
- git_file fd = p_creat(n->name, 0666);
- cl_assert(fd >= 0);
- p_close(fd);
- n->count = 0;
- }
-}
-
-static void dirent_cleanup__cb(void *_d)
-{
- walk_data *d = _d;
- name_data *n;
-
- for (n = d->names; n->name; n++) {
- cl_must_pass(p_unlink(n->name));
- }
-
- if (strcmp(d->sub, ".") != 0)
- cl_must_pass(p_rmdir(d->sub));
-
- cl_must_pass(p_chdir(".."));
-
- cl_must_pass(p_rmdir(top_dir));
-
- git_buf_free(&d->path);
-}
-
-static void check_counts(walk_data *d)
-{
- name_data *n;
-
- for (n = d->names; n->name; n++) {
- cl_assert(n->count == 1);
- }
-}
-
-static int one_entry(void *state, git_buf *path)
-{
- walk_data *d = (walk_data *) state;
- name_data *n;
-
- if (state != state_loc)
- return GIT_ERROR;
-
- if (path != &d->path)
- return GIT_ERROR;
-
- for (n = d->names; n->name; n++) {
- if (!strcmp(n->name, path->ptr)) {
- n->count++;
- return 0;
- }
- }
-
- return GIT_ERROR;
-}
-
-static int dont_call_me(void *state, git_buf *path)
-{
- GIT_UNUSED(state);
- GIT_UNUSED(path);
- return GIT_ERROR;
-}
-
-
-
-static name_data dot_names[] = {
- { 0, "./a" },
- { 0, "./asdf" },
- { 0, "./pack-foo.pack" },
- { 0, NULL }
-};
-static walk_data dot = {
- ".",
- dot_names,
- GIT_BUF_INIT
-};
-
-/* make sure that the '.' folder is not traversed */
-void test_core_dirent__dont_traverse_dot(void)
-{
- cl_set_cleanup(&dirent_cleanup__cb, &dot);
- setup(&dot);
-
- cl_git_pass(git_path_direach(&dot.path,
- one_entry,
- &dot));
-
- check_counts(&dot);
-}
-
-
-static name_data sub_names[] = {
- { 0, "sub/a" },
- { 0, "sub/asdf" },
- { 0, "sub/pack-foo.pack" },
- { 0, NULL }
-};
-static walk_data sub = {
- "sub",
- sub_names,
- GIT_BUF_INIT
-};
-
-/* traverse a subfolder */
-void test_core_dirent__traverse_subfolder(void)
-{
- cl_set_cleanup(&dirent_cleanup__cb, &sub);
- setup(&sub);
-
- cl_git_pass(git_path_direach(&sub.path,
- one_entry,
- &sub));
-
- check_counts(&sub);
-}
-
-
-static walk_data sub_slash = {
- "sub/",
- sub_names,
- GIT_BUF_INIT
-};
-
-/* traverse a slash-terminated subfolder */
-void test_core_dirent__traverse_slash_terminated_folder(void)
-{
- cl_set_cleanup(&dirent_cleanup__cb, &sub_slash);
- setup(&sub_slash);
-
- cl_git_pass(git_path_direach(&sub_slash.path,
- one_entry,
- &sub_slash));
-
- check_counts(&sub_slash);
-}
-
-
-static name_data empty_names[] = {
- { 0, NULL }
-};
-static walk_data empty = {
- "empty",
- empty_names,
- GIT_BUF_INIT
-};
-
-/* make sure that empty folders are not traversed */
-void test_core_dirent__dont_traverse_empty_folders(void)
-{
- cl_set_cleanup(&dirent_cleanup__cb, &empty);
- setup(&empty);
-
- cl_git_pass(git_path_direach(&empty.path,
- one_entry,
- &empty));
-
- check_counts(&empty);
-
- /* make sure callback not called */
- cl_git_pass(git_path_direach(&empty.path,
- dont_call_me,
- &empty));
-}
-
-static name_data odd_names[] = {
- { 0, "odd/.a" },
- { 0, "odd/..c" },
- /* the following don't work on cygwin/win32 */
- /* { 0, "odd/.b." }, */
- /* { 0, "odd/..d.." }, */
- { 0, NULL }
-};
-static walk_data odd = {
- "odd",
- odd_names,
- GIT_BUF_INIT
-};
-
-/* make sure that strange looking filenames ('..c') are traversed */
-void test_core_dirent__traverse_weird_filenames(void)
-{
- cl_set_cleanup(&dirent_cleanup__cb, &odd);
- setup(&odd);
-
- cl_git_pass(git_path_direach(&odd.path,
- one_entry,
- &odd));
-
- check_counts(&odd);
-}
-
-/* test filename length limits */
-void test_core_dirent__length_limits(void)
-{
- char *big_filename = (char *)git__malloc(FILENAME_MAX + 1);
- memset(big_filename, 'a', FILENAME_MAX + 1);
- big_filename[FILENAME_MAX] = 0;
-
- cl_must_fail(p_creat(big_filename, 0666));
- git__free(big_filename);
-}
diff --git a/tests-clar/core/filebuf.c b/tests-clar/core/filebuf.c
deleted file mode 100644
index 4451c01c7..000000000
--- a/tests-clar/core/filebuf.c
+++ /dev/null
@@ -1,92 +0,0 @@
-#include "clar_libgit2.h"
-#include "filebuf.h"
-
-/* make sure git_filebuf_open doesn't delete an existing lock */
-void test_core_filebuf__0(void)
-{
- git_filebuf file = GIT_FILEBUF_INIT;
- int fd;
- char test[] = "test", testlock[] = "test.lock";
-
- fd = p_creat(testlock, 0744); //-V536
-
- cl_must_pass(fd);
- cl_must_pass(p_close(fd));
-
- cl_git_fail(git_filebuf_open(&file, test, 0));
- cl_assert(git_path_exists(testlock));
-
- cl_must_pass(p_unlink(testlock));
-}
-
-
-/* make sure GIT_FILEBUF_APPEND works as expected */
-void test_core_filebuf__1(void)
-{
- git_filebuf file = GIT_FILEBUF_INIT;
- int fd;
- char test[] = "test";
-
- fd = p_creat(test, 0666); //-V536
- cl_must_pass(fd);
- cl_must_pass(p_write(fd, "libgit2 rocks\n", 14));
- cl_must_pass(p_close(fd));
-
- cl_git_pass(git_filebuf_open(&file, test, GIT_FILEBUF_APPEND));
- cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
- cl_git_pass(git_filebuf_commit(&file, 0666));
-
- cl_must_pass(p_unlink(test));
-}
-
-
-/* make sure git_filebuf_write writes large buffer correctly */
-void test_core_filebuf__2(void)
-{
- git_filebuf file = GIT_FILEBUF_INIT;
- char test[] = "test";
- unsigned char buf[4096 * 4]; /* 2 * WRITE_BUFFER_SIZE */
-
- memset(buf, 0xfe, sizeof(buf));
-
- cl_git_pass(git_filebuf_open(&file, test, 0));
- cl_git_pass(git_filebuf_write(&file, buf, sizeof(buf)));
- cl_git_pass(git_filebuf_commit(&file, 0666));
-
- cl_must_pass(p_unlink(test));
-}
-
-/* make sure git_filebuf_cleanup clears the buffer */
-void test_core_filebuf__4(void)
-{
- git_filebuf file = GIT_FILEBUF_INIT;
- char test[] = "test";
-
- cl_assert(file.buffer == NULL);
-
- cl_git_pass(git_filebuf_open(&file, test, 0));
- cl_assert(file.buffer != NULL);
-
- git_filebuf_cleanup(&file);
- cl_assert(file.buffer == NULL);
-}
-
-
-/* make sure git_filebuf_commit clears the buffer */
-void test_core_filebuf__5(void)
-{
- git_filebuf file = GIT_FILEBUF_INIT;
- char test[] = "test";
-
- cl_assert(file.buffer == NULL);
-
- cl_git_pass(git_filebuf_open(&file, test, 0));
- cl_assert(file.buffer != NULL);
- cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
- cl_assert(file.buffer != NULL);
-
- cl_git_pass(git_filebuf_commit(&file, 0666));
- cl_assert(file.buffer == NULL);
-
- cl_must_pass(p_unlink(test));
-}
diff --git a/tests-clar/core/mkdir.c b/tests-clar/core/mkdir.c
deleted file mode 100644
index 1e50b4336..000000000
--- a/tests-clar/core/mkdir.c
+++ /dev/null
@@ -1,182 +0,0 @@
-#include "clar_libgit2.h"
-#include "fileops.h"
-#include "path.h"
-#include "posix.h"
-
-static void cleanup_basic_dirs(void *ref)
-{
- GIT_UNUSED(ref);
- git_futils_rmdir_r("d0", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
- git_futils_rmdir_r("d1", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
- git_futils_rmdir_r("d2", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
- git_futils_rmdir_r("d3", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
- git_futils_rmdir_r("d4", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
-}
-
-void test_core_mkdir__basic(void)
-{
- cl_set_cleanup(cleanup_basic_dirs, NULL);
-
- /* make a directory */
- cl_assert(!git_path_isdir("d0"));
- cl_git_pass(git_futils_mkdir("d0", NULL, 0755, 0));
- cl_assert(git_path_isdir("d0"));
-
- /* make a path */
- cl_assert(!git_path_isdir("d1"));
- cl_git_pass(git_futils_mkdir("d1/d1.1/d1.2", NULL, 0755, GIT_MKDIR_PATH));
- cl_assert(git_path_isdir("d1"));
- cl_assert(git_path_isdir("d1/d1.1"));
- cl_assert(git_path_isdir("d1/d1.1/d1.2"));
-
- /* make a dir exclusively */
- cl_assert(!git_path_isdir("d2"));
- cl_git_pass(git_futils_mkdir("d2", NULL, 0755, GIT_MKDIR_EXCL));
- cl_assert(git_path_isdir("d2"));
-
- /* make exclusive failure */
- cl_git_fail(git_futils_mkdir("d2", NULL, 0755, GIT_MKDIR_EXCL));
-
- /* make a path exclusively */
- cl_assert(!git_path_isdir("d3"));
- cl_git_pass(git_futils_mkdir("d3/d3.1/d3.2", NULL, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
- cl_assert(git_path_isdir("d3"));
- cl_assert(git_path_isdir("d3/d3.1/d3.2"));
-
- /* make exclusive path failure */
- cl_git_fail(git_futils_mkdir("d3/d3.1/d3.2", NULL, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
- /* ??? Should EXCL only apply to the last item in the path? */
-
- /* path with trailing slash? */
- cl_assert(!git_path_isdir("d4"));
- cl_git_pass(git_futils_mkdir("d4/d4.1/", NULL, 0755, GIT_MKDIR_PATH));
- cl_assert(git_path_isdir("d4/d4.1"));
-}
-
-static void cleanup_basedir(void *ref)
-{
- GIT_UNUSED(ref);
- git_futils_rmdir_r("base", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
-}
-
-void test_core_mkdir__with_base(void)
-{
-#define BASEDIR "base/dir/here"
-
- cl_set_cleanup(cleanup_basedir, NULL);
-
- cl_git_pass(git_futils_mkdir(BASEDIR, NULL, 0755, GIT_MKDIR_PATH));
-
- cl_git_pass(git_futils_mkdir("a", BASEDIR, 0755, 0));
- cl_assert(git_path_isdir(BASEDIR "/a"));
-
- cl_git_pass(git_futils_mkdir("b/b1/b2", BASEDIR, 0755, GIT_MKDIR_PATH));
- cl_assert(git_path_isdir(BASEDIR "/b/b1/b2"));
-
- /* exclusive with existing base */
- cl_git_pass(git_futils_mkdir("c/c1/c2", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
-
- /* fail: exclusive with duplicated suffix */
- cl_git_fail(git_futils_mkdir("c/c1/c3", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
-
- /* fail: exclusive with any duplicated component */
- cl_git_fail(git_futils_mkdir("c/cz/cz", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
-
- /* success: exclusive without path */
- cl_git_pass(git_futils_mkdir("c/c1/c3", BASEDIR, 0755, GIT_MKDIR_EXCL));
-
- /* path with shorter base and existing dirs */
- cl_git_pass(git_futils_mkdir("dir/here/d/", "base", 0755, GIT_MKDIR_PATH));
- cl_assert(git_path_isdir("base/dir/here/d"));
-
- /* fail: path with shorter base and existing dirs */
- cl_git_fail(git_futils_mkdir("dir/here/e/", "base", 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
-
- /* fail: base with missing components */
- cl_git_fail(git_futils_mkdir("f/", "base/missing", 0755, GIT_MKDIR_PATH));
-
- /* success: shift missing component to path */
- cl_git_pass(git_futils_mkdir("missing/f/", "base/", 0755, GIT_MKDIR_PATH));
-}
-
-static void cleanup_chmod_root(void *ref)
-{
- mode_t *mode = ref;
-
- if (*mode != 0) {
- (void)p_umask(*mode);
- git__free(mode);
- }
-
- git_futils_rmdir_r("r", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
-}
-
-static void check_mode(mode_t expected, mode_t actual)
-{
-#ifdef GIT_WIN32
- /* chmod on Win32 doesn't support exec bit, not group/world bits */
- cl_assert((expected & 0600) == (actual & 0777));
-#else
- cl_assert(expected == (actual & 0777));
-#endif
-}
-
-void test_core_mkdir__chmods(void)
-{
- struct stat st;
- mode_t *old = git__malloc(sizeof(mode_t));
- *old = p_umask(022);
-
- cl_set_cleanup(cleanup_chmod_root, old);
-
- cl_git_pass(git_futils_mkdir("r", NULL, 0777, 0));
-
- cl_git_pass(git_futils_mkdir("mode/is/important", "r", 0777, GIT_MKDIR_PATH));
-
- cl_git_pass(git_path_lstat("r/mode", &st));
- check_mode(0755, st.st_mode);
- cl_git_pass(git_path_lstat("r/mode/is", &st));
- check_mode(0755, st.st_mode);
- cl_git_pass(git_path_lstat("r/mode/is/important", &st));
- check_mode(0755, st.st_mode);
-
- cl_git_pass(git_futils_mkdir("mode2/is2/important2", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD));
-
- cl_git_pass(git_path_lstat("r/mode2", &st));
- check_mode(0755, st.st_mode);
- cl_git_pass(git_path_lstat("r/mode2/is2", &st));
- check_mode(0755, st.st_mode);
- cl_git_pass(git_path_lstat("r/mode2/is2/important2", &st));
- check_mode(0777, st.st_mode);
-
- cl_git_pass(git_futils_mkdir("mode3/is3/important3", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD_PATH));
-
- cl_git_pass(git_path_lstat("r/mode3", &st));
- check_mode(0777, st.st_mode);
- cl_git_pass(git_path_lstat("r/mode3/is3", &st));
- check_mode(0777, st.st_mode);
- cl_git_pass(git_path_lstat("r/mode3/is3/important3", &st));
- check_mode(0777, st.st_mode);
-
- /* test that we chmod existing dir */
-
- cl_git_pass(git_futils_mkdir("mode/is/important", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD));
-
- cl_git_pass(git_path_lstat("r/mode", &st));
- check_mode(0755, st.st_mode);
- cl_git_pass(git_path_lstat("r/mode/is", &st));
- check_mode(0755, st.st_mode);
- cl_git_pass(git_path_lstat("r/mode/is/important", &st));
- check_mode(0777, st.st_mode);
-
- /* test that we chmod even existing dirs if CHMOD_PATH is set */
-
- cl_git_pass(git_futils_mkdir("mode2/is2/important2.1", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD_PATH));
-
- cl_git_pass(git_path_lstat("r/mode2", &st));
- check_mode(0777, st.st_mode);
- cl_git_pass(git_path_lstat("r/mode2/is2", &st));
- check_mode(0777, st.st_mode);
- cl_git_pass(git_path_lstat("r/mode2/is2/important2.1", &st));
- check_mode(0777, st.st_mode);
-}
diff --git a/tests-clar/core/path.c b/tests-clar/core/path.c
deleted file mode 100644
index 407770baa..000000000
--- a/tests-clar/core/path.c
+++ /dev/null
@@ -1,480 +0,0 @@
-#include "clar_libgit2.h"
-#include <fileops.h>
-
-static void
-check_dirname(const char *A, const char *B)
-{
- git_buf dir = GIT_BUF_INIT;
- char *dir2;
-
- cl_assert(git_path_dirname_r(&dir, A) >= 0);
- cl_assert_equal_s(B, dir.ptr);
- git_buf_free(&dir);
-
- cl_assert((dir2 = git_path_dirname(A)) != NULL);
- cl_assert_equal_s(B, dir2);
- git__free(dir2);
-}
-
-static void
-check_basename(const char *A, const char *B)
-{
- git_buf base = GIT_BUF_INIT;
- char *base2;
-
- cl_assert(git_path_basename_r(&base, A) >= 0);
- cl_assert_equal_s(B, base.ptr);
- git_buf_free(&base);
-
- cl_assert((base2 = git_path_basename(A)) != NULL);
- cl_assert_equal_s(B, base2);
- git__free(base2);
-}
-
-static void
-check_topdir(const char *A, const char *B)
-{
- const char *dir;
-
- cl_assert((dir = git_path_topdir(A)) != NULL);
- cl_assert_equal_s(B, dir);
-}
-
-static void
-check_joinpath(const char *path_a, const char *path_b, const char *expected_path)
-{
- git_buf joined_path = GIT_BUF_INIT;
-
- cl_git_pass(git_buf_joinpath(&joined_path, path_a, path_b));
- cl_assert_equal_s(expected_path, joined_path.ptr);
-
- git_buf_free(&joined_path);
-}
-
-static void
-check_joinpath_n(
- const char *path_a,
- const char *path_b,
- const char *path_c,
- const char *path_d,
- const char *expected_path)
-{
- git_buf joined_path = GIT_BUF_INIT;
-
- cl_git_pass(git_buf_join_n(&joined_path, '/', 4,
- path_a, path_b, path_c, path_d));
- cl_assert_equal_s(expected_path, joined_path.ptr);
-
- git_buf_free(&joined_path);
-}
-
-
-/* get the dirname of a path */
-void test_core_path__00_dirname(void)
-{
- check_dirname(NULL, ".");
- check_dirname("", ".");
- check_dirname("a", ".");
- check_dirname("/", "/");
- check_dirname("/usr", "/");
- check_dirname("/usr/", "/");
- check_dirname("/usr/lib", "/usr");
- check_dirname("/usr/lib/", "/usr");
- check_dirname("/usr/lib//", "/usr");
- check_dirname("usr/lib", "usr");
- check_dirname("usr/lib/", "usr");
- check_dirname("usr/lib//", "usr");
- check_dirname(".git/", ".");
-
- check_dirname(REP16("/abc"), REP15("/abc"));
-
-#ifdef GIT_WIN32
- check_dirname("C:/path/", "C:/");
- check_dirname("C:/path", "C:/");
- check_dirname("//computername/path/", "//computername/");
- check_dirname("//computername/path", "//computername/");
- check_dirname("//computername/sub/path/", "//computername/sub");
- check_dirname("//computername/sub/path", "//computername/sub");
-#endif
-}
-
-/* get the base name of a path */
-void test_core_path__01_basename(void)
-{
- check_basename(NULL, ".");
- check_basename("", ".");
- check_basename("a", "a");
- check_basename("/", "/");
- check_basename("/usr", "usr");
- check_basename("/usr/", "usr");
- check_basename("/usr/lib", "lib");
- check_basename("/usr/lib//", "lib");
- check_basename("usr/lib", "lib");
-
- check_basename(REP16("/abc"), "abc");
- check_basename(REP1024("/abc"), "abc");
-}
-
-/* get the latest component in a path */
-void test_core_path__02_topdir(void)
-{
- check_topdir(".git/", ".git/");
- check_topdir("/.git/", ".git/");
- check_topdir("usr/local/.git/", ".git/");
- check_topdir("./.git/", ".git/");
- check_topdir("/usr/.git/", ".git/");
- check_topdir("/", "/");
- check_topdir("a/", "a/");
-
- cl_assert(git_path_topdir("/usr/.git") == NULL);
- cl_assert(git_path_topdir(".") == NULL);
- cl_assert(git_path_topdir("") == NULL);
- cl_assert(git_path_topdir("a") == NULL);
-}
-
-/* properly join path components */
-void test_core_path__05_joins(void)
-{
- check_joinpath("", "", "");
- check_joinpath("", "a", "a");
- check_joinpath("", "/a", "/a");
- check_joinpath("a", "", "a/");
- check_joinpath("a", "/", "a/");
- check_joinpath("a", "b", "a/b");
- check_joinpath("/", "a", "/a");
- check_joinpath("/", "", "/");
- check_joinpath("/a", "/b", "/a/b");
- check_joinpath("/a", "/b/", "/a/b/");
- check_joinpath("/a/", "b/", "/a/b/");
- check_joinpath("/a/", "/b/", "/a/b/");
-
- check_joinpath("/abcd", "/defg", "/abcd/defg");
- check_joinpath("/abcd", "/defg/", "/abcd/defg/");
- check_joinpath("/abcd/", "defg/", "/abcd/defg/");
- check_joinpath("/abcd/", "/defg/", "/abcd/defg/");
-
- check_joinpath("/abcdefgh", "/12345678", "/abcdefgh/12345678");
- check_joinpath("/abcdefgh", "/12345678/", "/abcdefgh/12345678/");
- check_joinpath("/abcdefgh/", "12345678/", "/abcdefgh/12345678/");
-
- check_joinpath(REP1024("aaaa"), "", REP1024("aaaa") "/");
- check_joinpath(REP1024("aaaa/"), "", REP1024("aaaa/"));
- check_joinpath(REP1024("/aaaa"), "", REP1024("/aaaa") "/");
-
- check_joinpath(REP1024("aaaa"), REP1024("bbbb"),
- REP1024("aaaa") "/" REP1024("bbbb"));
- check_joinpath(REP1024("/aaaa"), REP1024("/bbbb"),
- REP1024("/aaaa") REP1024("/bbbb"));
-}
-
-/* properly join path components for more than one path */
-void test_core_path__06_long_joins(void)
-{
- check_joinpath_n("", "", "", "", "");
- check_joinpath_n("", "a", "", "", "a/");
- check_joinpath_n("a", "", "", "", "a/");
- check_joinpath_n("", "", "", "a", "a");
- check_joinpath_n("a", "b", "", "/c/d/", "a/b/c/d/");
- check_joinpath_n("a", "b", "", "/c/d", "a/b/c/d");
- check_joinpath_n("abcd", "efgh", "ijkl", "mnop", "abcd/efgh/ijkl/mnop");
- check_joinpath_n("abcd/", "efgh/", "ijkl/", "mnop/", "abcd/efgh/ijkl/mnop/");
- check_joinpath_n("/abcd/", "/efgh/", "/ijkl/", "/mnop/", "/abcd/efgh/ijkl/mnop/");
-
- check_joinpath_n(REP1024("a"), REP1024("b"), REP1024("c"), REP1024("d"),
- REP1024("a") "/" REP1024("b") "/"
- REP1024("c") "/" REP1024("d"));
- check_joinpath_n(REP1024("/a"), REP1024("/b"), REP1024("/c"), REP1024("/d"),
- REP1024("/a") REP1024("/b")
- REP1024("/c") REP1024("/d"));
-}
-
-
-static void
-check_path_to_dir(
- const char* path,
- const char* expected)
-{
- git_buf tgt = GIT_BUF_INIT;
-
- git_buf_sets(&tgt, path);
- cl_git_pass(git_path_to_dir(&tgt));
- cl_assert_equal_s(expected, tgt.ptr);
-
- git_buf_free(&tgt);
-}
-
-static void
-check_string_to_dir(
- const char* path,
- size_t maxlen,
- const char* expected)
-{
- size_t len = strlen(path);
- char *buf = git__malloc(len + 2);
- cl_assert(buf);
-
- strncpy(buf, path, len + 2);
-
- git_path_string_to_dir(buf, maxlen);
-
- cl_assert_equal_s(expected, buf);
-
- git__free(buf);
-}
-
-/* convert paths to dirs */
-void test_core_path__07_path_to_dir(void)
-{
- check_path_to_dir("", "");
- check_path_to_dir(".", "./");
- check_path_to_dir("./", "./");
- check_path_to_dir("a/", "a/");
- check_path_to_dir("ab", "ab/");
- /* make sure we try just under and just over an expansion that will
- * require a realloc
- */
- check_path_to_dir("abcdef", "abcdef/");
- check_path_to_dir("abcdefg", "abcdefg/");
- check_path_to_dir("abcdefgh", "abcdefgh/");
- check_path_to_dir("abcdefghi", "abcdefghi/");
- check_path_to_dir(REP1024("abcd") "/", REP1024("abcd") "/");
- check_path_to_dir(REP1024("abcd"), REP1024("abcd") "/");
-
- check_string_to_dir("", 1, "");
- check_string_to_dir(".", 1, ".");
- check_string_to_dir(".", 2, "./");
- check_string_to_dir(".", 3, "./");
- check_string_to_dir("abcd", 3, "abcd");
- check_string_to_dir("abcd", 4, "abcd");
- check_string_to_dir("abcd", 5, "abcd/");
- check_string_to_dir("abcd", 6, "abcd/");
-}
-
-/* join path to itself */
-void test_core_path__08_self_join(void)
-{
- git_buf path = GIT_BUF_INIT;
- size_t asize = 0;
-
- asize = path.asize;
- cl_git_pass(git_buf_sets(&path, "/foo"));
- cl_assert_equal_s(path.ptr, "/foo");
- cl_assert(asize < path.asize);
-
- asize = path.asize;
- cl_git_pass(git_buf_joinpath(&path, path.ptr, "this is a new string"));
- cl_assert_equal_s(path.ptr, "/foo/this is a new string");
- cl_assert(asize < path.asize);
-
- asize = path.asize;
- cl_git_pass(git_buf_joinpath(&path, path.ptr, "/grow the buffer, grow the buffer, grow the buffer"));
- cl_assert_equal_s(path.ptr, "/foo/this is a new string/grow the buffer, grow the buffer, grow the buffer");
- cl_assert(asize < path.asize);
-
- git_buf_free(&path);
- cl_git_pass(git_buf_sets(&path, "/foo/bar"));
-
- cl_git_pass(git_buf_joinpath(&path, path.ptr + 4, "baz"));
- cl_assert_equal_s(path.ptr, "/bar/baz");
-
- asize = path.asize;
- cl_git_pass(git_buf_joinpath(&path, path.ptr + 4, "somethinglongenoughtorealloc"));
- cl_assert_equal_s(path.ptr, "/baz/somethinglongenoughtorealloc");
- cl_assert(asize < path.asize);
-
- git_buf_free(&path);
-}
-
-static void check_percent_decoding(const char *expected_result, const char *input)
-{
- git_buf buf = GIT_BUF_INIT;
-
- cl_git_pass(git__percent_decode(&buf, input));
- cl_assert_equal_s(expected_result, git_buf_cstr(&buf));
-
- git_buf_free(&buf);
-}
-
-void test_core_path__09_percent_decode(void)
-{
- check_percent_decoding("abcd", "abcd");
- check_percent_decoding("a2%", "a2%");
- check_percent_decoding("a2%3", "a2%3");
- check_percent_decoding("a2%%3", "a2%%3");
- check_percent_decoding("a2%3z", "a2%3z");
- check_percent_decoding("a,", "a%2c");
- check_percent_decoding("a21", "a2%31");
- check_percent_decoding("a2%1", "a2%%31");
- check_percent_decoding("a bc ", "a%20bc%20");
- check_percent_decoding("Vicent Mart" "\355", "Vicent%20Mart%ED");
-}
-
-static void check_fromurl(const char *expected_result, const char *input, int should_fail)
-{
- git_buf buf = GIT_BUF_INIT;
-
- assert(should_fail || expected_result);
-
- if (!should_fail) {
- cl_git_pass(git_path_fromurl(&buf, input));
- cl_assert_equal_s(expected_result, git_buf_cstr(&buf));
- } else
- cl_git_fail(git_path_fromurl(&buf, input));
-
- git_buf_free(&buf);
-}
-
-#ifdef GIT_WIN32
-#define ABS_PATH_MARKER ""
-#else
-#define ABS_PATH_MARKER "/"
-#endif
-
-void test_core_path__10_fromurl(void)
-{
- /* Failing cases */
- check_fromurl(NULL, "a", 1);
- check_fromurl(NULL, "http:///c:/Temp%20folder/note.txt", 1);
- check_fromurl(NULL, "file://c:/Temp%20folder/note.txt", 1);
- check_fromurl(NULL, "file:////c:/Temp%20folder/note.txt", 1);
- check_fromurl(NULL, "file:///", 1);
- check_fromurl(NULL, "file:////", 1);
- check_fromurl(NULL, "file://servername/c:/Temp%20folder/note.txt", 1);
-
- /* Passing cases */
- check_fromurl(ABS_PATH_MARKER "c:/Temp folder/note.txt", "file:///c:/Temp%20folder/note.txt", 0);
- check_fromurl(ABS_PATH_MARKER "c:/Temp folder/note.txt", "file://localhost/c:/Temp%20folder/note.txt", 0);
- check_fromurl(ABS_PATH_MARKER "c:/Temp+folder/note.txt", "file:///c:/Temp+folder/note.txt", 0);
- check_fromurl(ABS_PATH_MARKER "a", "file:///a", 0);
-}
-
-typedef struct {
- int expect_idx;
- char **expect;
-} check_walkup_info;
-
-static int check_one_walkup_step(void *ref, git_buf *path)
-{
- check_walkup_info *info = (check_walkup_info *)ref;
- cl_assert(info->expect[info->expect_idx] != NULL);
- cl_assert_equal_s(info->expect[info->expect_idx], path->ptr);
- info->expect_idx++;
- return 0;
-}
-
-void test_core_path__11_walkup(void)
-{
- git_buf p = GIT_BUF_INIT;
- char *expect[] = {
- "/a/b/c/d/e/", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
- "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
- "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
- "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
- "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", NULL,
- "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", NULL,
- "this is a path", NULL,
- "///a///b///c///d///e///", "///a///b///c///d///", "///a///b///c///", "///a///b///", "///a///", "///", NULL,
- NULL
- };
- char *root[] = { NULL, NULL, "/", "", "/a/b", "/a/b/", NULL, NULL, NULL };
- int i, j;
- check_walkup_info info;
-
- info.expect = expect;
-
- for (i = 0, j = 0; expect[i] != NULL; i++, j++) {
-
- git_buf_sets(&p, expect[i]);
-
- info.expect_idx = i;
- cl_git_pass(
- git_path_walk_up(&p, root[j], check_one_walkup_step, &info)
- );
-
- cl_assert_equal_s(p.ptr, expect[i]);
-
- /* skip to next run of expectations */
- while (expect[i] != NULL) i++;
- }
-
- git_buf_free(&p);
-}
-
-void test_core_path__12_offset_to_path_root(void)
-{
- cl_assert(git_path_root("non/rooted/path") == -1);
- cl_assert(git_path_root("/rooted/path") == 0);
-
-#ifdef GIT_WIN32
- /* Windows specific tests */
- cl_assert(git_path_root("C:non/rooted/path") == -1);
- cl_assert(git_path_root("C:/rooted/path") == 2);
- cl_assert(git_path_root("//computername/sharefolder/resource") == 14);
- cl_assert(git_path_root("//computername/sharefolder") == 14);
- cl_assert(git_path_root("//computername") == -1);
-#endif
-}
-
-#define NON_EXISTING_FILEPATH "i_hope_i_do_not_exist"
-
-void test_core_path__13_cannot_prettify_a_non_existing_file(void)
-{
- git_buf p = GIT_BUF_INIT;
-
- cl_must_pass(git_path_exists(NON_EXISTING_FILEPATH) == false);
- cl_assert_equal_i(GIT_ENOTFOUND, git_path_prettify(&p, NON_EXISTING_FILEPATH, NULL));
- cl_assert_equal_i(GIT_ENOTFOUND, git_path_prettify(&p, NON_EXISTING_FILEPATH "/so-do-i", NULL));
-
- git_buf_free(&p);
-}
-
-void test_core_path__14_apply_relative(void)
-{
- git_buf p = GIT_BUF_INIT;
-
- cl_git_pass(git_buf_sets(&p, "/this/is/a/base"));
-
- cl_git_pass(git_path_apply_relative(&p, "../test"));
- cl_assert_equal_s("/this/is/a/test", p.ptr);
-
- cl_git_pass(git_path_apply_relative(&p, "../../the/./end"));
- cl_assert_equal_s("/this/is/the/end", p.ptr);
-
- cl_git_pass(git_path_apply_relative(&p, "./of/this/../the/string"));
- cl_assert_equal_s("/this/is/the/end/of/the/string", p.ptr);
-
- cl_git_pass(git_path_apply_relative(&p, "../../../../../.."));
- cl_assert_equal_s("/this/", p.ptr);
-
- cl_git_pass(git_path_apply_relative(&p, "../../../../../"));
- cl_assert_equal_s("/", p.ptr);
-
- cl_git_pass(git_path_apply_relative(&p, "../../../../.."));
- cl_assert_equal_s("/", p.ptr);
-
-
- cl_git_pass(git_buf_sets(&p, "d:/another/test"));
-
- cl_git_pass(git_path_apply_relative(&p, "../../../../.."));
- cl_assert_equal_s("d:/", p.ptr);
-
- cl_git_pass(git_path_apply_relative(&p, "from/here/to/../and/./back/."));
- cl_assert_equal_s("d:/from/here/and/back/", p.ptr);
-
-
- cl_git_pass(git_buf_sets(&p, "https://my.url.com/test.git"));
-
- cl_git_pass(git_path_apply_relative(&p, "../another.git"));
- cl_assert_equal_s("https://my.url.com/another.git", p.ptr);
-
- cl_git_pass(git_path_apply_relative(&p, "../full/path/url.patch"));
- cl_assert_equal_s("https://my.url.com/full/path/url.patch", p.ptr);
-
- cl_git_pass(git_path_apply_relative(&p, ".."));
- cl_assert_equal_s("https://my.url.com/full/path/", p.ptr);
-
- cl_git_pass(git_path_apply_relative(&p, "../../../../../"));
- cl_assert_equal_s("https://", p.ptr);
-
- git_buf_free(&p);
-}
diff --git a/tests-clar/core/vector.c b/tests-clar/core/vector.c
deleted file mode 100644
index c9e43a149..000000000
--- a/tests-clar/core/vector.c
+++ /dev/null
@@ -1,275 +0,0 @@
-#include "clar_libgit2.h"
-#include "vector.h"
-
-/* initial size of 1 would cause writing past array bounds */
-void test_core_vector__0(void)
-{
- git_vector x;
- int i;
- git_vector_init(&x, 1, NULL);
- for (i = 0; i < 10; ++i) {
- git_vector_insert(&x, (void*) 0xabc);
- }
- git_vector_free(&x);
-}
-
-
-/* don't read past array bounds on remove() */
-void test_core_vector__1(void)
-{
- git_vector x;
- // make initial capacity exact for our insertions.
- git_vector_init(&x, 3, NULL);
- git_vector_insert(&x, (void*) 0xabc);
- git_vector_insert(&x, (void*) 0xdef);
- git_vector_insert(&x, (void*) 0x123);
-
- git_vector_remove(&x, 0); // used to read past array bounds.
- git_vector_free(&x);
-}
-
-
-static int test_cmp(const void *a, const void *b)
-{
- return *(const int *)a - *(const int *)b;
-}
-
-/* remove duplicates */
-void test_core_vector__2(void)
-{
- git_vector x;
- int *ptrs[2];
-
- ptrs[0] = git__malloc(sizeof(int));
- ptrs[1] = git__malloc(sizeof(int));
-
- *ptrs[0] = 2;
- *ptrs[1] = 1;
-
- cl_git_pass(git_vector_init(&x, 5, test_cmp));
- cl_git_pass(git_vector_insert(&x, ptrs[0]));
- cl_git_pass(git_vector_insert(&x, ptrs[1]));
- cl_git_pass(git_vector_insert(&x, ptrs[1]));
- cl_git_pass(git_vector_insert(&x, ptrs[0]));
- cl_git_pass(git_vector_insert(&x, ptrs[1]));
- cl_assert(x.length == 5);
-
- git_vector_uniq(&x);
- cl_assert(x.length == 2);
-
- git_vector_free(&x);
-
- git__free(ptrs[0]);
- git__free(ptrs[1]);
-}
-
-
-static int compare_them(const void *a, const void *b)
-{
- return (int)((long)a - (long)b);
-}
-
-/* insert_sorted */
-void test_core_vector__3(void)
-{
- git_vector x;
- long i;
- git_vector_init(&x, 1, &compare_them);
-
- for (i = 0; i < 10; i += 2) {
- git_vector_insert_sorted(&x, (void*)(i + 1), NULL);
- }
-
- for (i = 9; i > 0; i -= 2) {
- git_vector_insert_sorted(&x, (void*)(i + 1), NULL);
- }
-
- cl_assert(x.length == 10);
- for (i = 0; i < 10; ++i) {
- cl_assert(git_vector_get(&x, i) == (void*)(i + 1));
- }
-
- git_vector_free(&x);
-}
-
-/* insert_sorted with duplicates */
-void test_core_vector__4(void)
-{
- git_vector x;
- long i;
- git_vector_init(&x, 1, &compare_them);
-
- for (i = 0; i < 10; i += 2) {
- git_vector_insert_sorted(&x, (void*)(i + 1), NULL);
- }
-
- for (i = 9; i > 0; i -= 2) {
- git_vector_insert_sorted(&x, (void*)(i + 1), NULL);
- }
-
- for (i = 0; i < 10; i += 2) {
- git_vector_insert_sorted(&x, (void*)(i + 1), NULL);
- }
-
- for (i = 9; i > 0; i -= 2) {
- git_vector_insert_sorted(&x, (void*)(i + 1), NULL);
- }
-
- cl_assert(x.length == 20);
- for (i = 0; i < 20; ++i) {
- cl_assert(git_vector_get(&x, i) == (void*)(i / 2 + 1));
- }
-
- git_vector_free(&x);
-}
-
-typedef struct {
- int content;
- int count;
-} my_struct;
-
-static int _struct_count = 0;
-
-static int compare_structs(const void *a, const void *b)
-{
- return ((const my_struct *)a)->content -
- ((const my_struct *)b)->content;
-}
-
-static int merge_structs(void **old_raw, void *new)
-{
- my_struct *old = *(my_struct **)old_raw;
- cl_assert(((my_struct *)old)->content == ((my_struct *)new)->content);
- ((my_struct *)old)->count += 1;
- git__free(new);
- _struct_count--;
- return GIT_EEXISTS;
-}
-
-static my_struct *alloc_struct(int value)
-{
- my_struct *st = git__malloc(sizeof(my_struct));
- st->content = value;
- st->count = 0;
- _struct_count++;
- return st;
-}
-
-/* insert_sorted with duplicates and special handling */
-void test_core_vector__5(void)
-{
- git_vector x;
- int i;
-
- git_vector_init(&x, 1, &compare_structs);
-
- for (i = 0; i < 10; i += 2)
- git_vector_insert_sorted(&x, alloc_struct(i), &merge_structs);
-
- for (i = 9; i > 0; i -= 2)
- git_vector_insert_sorted(&x, alloc_struct(i), &merge_structs);
-
- cl_assert(x.length == 10);
- cl_assert(_struct_count == 10);
-
- for (i = 0; i < 10; i += 2)
- git_vector_insert_sorted(&x, alloc_struct(i), &merge_structs);
-
- for (i = 9; i > 0; i -= 2)
- git_vector_insert_sorted(&x, alloc_struct(i), &merge_structs);
-
- cl_assert(x.length == 10);
- cl_assert(_struct_count == 10);
-
- for (i = 0; i < 10; ++i) {
- cl_assert(((my_struct *)git_vector_get(&x, i))->content == i);
- git__free(git_vector_get(&x, i));
- _struct_count--;
- }
-
- git_vector_free(&x);
-}
-
-static int remove_ones(const git_vector *v, size_t idx)
-{
- return (git_vector_get(v, idx) == (void *)0x001);
-}
-
-/* Test removal based on callback */
-void test_core_vector__remove_matching(void)
-{
- git_vector x;
- size_t i;
- void *compare;
-
- git_vector_init(&x, 1, NULL);
- git_vector_insert(&x, (void*) 0x001);
-
- cl_assert(x.length == 1);
- git_vector_remove_matching(&x, remove_ones);
- cl_assert(x.length == 0);
-
- git_vector_insert(&x, (void*) 0x001);
- git_vector_insert(&x, (void*) 0x001);
- git_vector_insert(&x, (void*) 0x001);
-
- cl_assert(x.length == 3);
- git_vector_remove_matching(&x, remove_ones);
- cl_assert(x.length == 0);
-
- git_vector_insert(&x, (void*) 0x002);
- git_vector_insert(&x, (void*) 0x001);
- git_vector_insert(&x, (void*) 0x002);
- git_vector_insert(&x, (void*) 0x001);
-
- cl_assert(x.length == 4);
- git_vector_remove_matching(&x, remove_ones);
- cl_assert(x.length == 2);
-
- git_vector_foreach(&x, i, compare) {
- cl_assert(compare != (void *)0x001);
- }
-
- git_vector_clear(&x);
-
- git_vector_insert(&x, (void*) 0x001);
- git_vector_insert(&x, (void*) 0x002);
- git_vector_insert(&x, (void*) 0x002);
- git_vector_insert(&x, (void*) 0x001);
-
- cl_assert(x.length == 4);
- git_vector_remove_matching(&x, remove_ones);
- cl_assert(x.length == 2);
-
- git_vector_foreach(&x, i, compare) {
- cl_assert(compare != (void *)0x001);
- }
-
- git_vector_clear(&x);
-
- git_vector_insert(&x, (void*) 0x002);
- git_vector_insert(&x, (void*) 0x001);
- git_vector_insert(&x, (void*) 0x002);
- git_vector_insert(&x, (void*) 0x001);
-
- cl_assert(x.length == 4);
- git_vector_remove_matching(&x, remove_ones);
- cl_assert(x.length == 2);
-
- git_vector_foreach(&x, i, compare) {
- cl_assert(compare != (void *)0x001);
- }
-
- git_vector_clear(&x);
-
- git_vector_insert(&x, (void*) 0x002);
- git_vector_insert(&x, (void*) 0x003);
- git_vector_insert(&x, (void*) 0x002);
- git_vector_insert(&x, (void*) 0x003);
-
- cl_assert(x.length == 4);
- git_vector_remove_matching(&x, remove_ones);
- cl_assert(x.length == 4);
-
- git_vector_free(&x);
-}
diff --git a/tests-clar/diff/blob.c b/tests-clar/diff/blob.c
deleted file mode 100644
index 42b9fcd5f..000000000
--- a/tests-clar/diff/blob.c
+++ /dev/null
@@ -1,1068 +0,0 @@
-#include "clar_libgit2.h"
-#include "diff_helpers.h"
-
-static git_repository *g_repo = NULL;
-static diff_expects expected;
-static git_diff_options opts;
-static git_blob *d, *alien;
-
-static void quick_diff_blob_to_str(
- const git_blob *blob, const char *blob_path,
- const char *str, size_t len, const char *str_path)
-{
- memset(&expected, 0, sizeof(expected));
-
- if (str && !len)
- len = strlen(str);
-
- cl_git_pass(git_diff_blob_to_buffer(
- blob, blob_path, str, len, str_path,
- &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
-}
-
-void test_diff_blob__initialize(void)
-{
- git_oid oid;
-
- g_repo = cl_git_sandbox_init("attr");
-
- GIT_INIT_STRUCTURE(&opts, GIT_DIFF_OPTIONS_VERSION);
- opts.context_lines = 1;
- opts.interhunk_lines = 0;
-
- memset(&expected, 0, sizeof(expected));
-
- /* tests/resources/attr/root_test4.txt */
- cl_git_pass(git_oid_fromstrn(&oid, "a0f7217a", 8));
- cl_git_pass(git_blob_lookup_prefix(&d, g_repo, &oid, 4));
-
- /* alien.png */
- cl_git_pass(git_oid_fromstrn(&oid, "edf3dcee", 8));
- cl_git_pass(git_blob_lookup_prefix(&alien, g_repo, &oid, 4));
-}
-
-void test_diff_blob__cleanup(void)
-{
- git_blob_free(d);
- d = NULL;
-
- git_blob_free(alien);
- alien = NULL;
-
- cl_git_sandbox_cleanup();
-}
-
-void test_diff_blob__can_compare_text_blobs(void)
-{
- git_blob *a, *b, *c;
- git_oid a_oid, b_oid, c_oid;
-
- /* tests/resources/attr/root_test1 */
- cl_git_pass(git_oid_fromstrn(&a_oid, "45141a79", 8));
- 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));
-
- /* tests/resources/attr/root_test3 */
- cl_git_pass(git_oid_fromstrn(&c_oid, "c96bbb2c2557a832", 16));
- cl_git_pass(git_blob_lookup_prefix(&c, g_repo, &c_oid, 8));
-
- /* Doing the equivalent of a `git diff -U1` on these files */
-
- /* diff on tests/resources/attr/root_test1 */
- cl_git_pass(git_diff_blobs(
- a, NULL, b, NULL, &opts,
- diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
-
- cl_assert_equal_i(1, expected.files);
- cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(0, expected.files_binary);
-
- cl_assert_equal_i(1, expected.hunks);
- cl_assert_equal_i(6, expected.lines);
- cl_assert_equal_i(1, expected.line_ctxt);
- cl_assert_equal_i(5, expected.line_adds);
- cl_assert_equal_i(0, expected.line_dels);
-
- /* diff on tests/resources/attr/root_test2 */
- memset(&expected, 0, sizeof(expected));
- cl_git_pass(git_diff_blobs(
- b, NULL, c, NULL, &opts,
- diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
-
- cl_assert_equal_i(1, expected.files);
- cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(0, expected.files_binary);
-
- cl_assert_equal_i(1, expected.hunks);
- cl_assert_equal_i(15, expected.lines);
- cl_assert_equal_i(3, expected.line_ctxt);
- cl_assert_equal_i(9, expected.line_adds);
- cl_assert_equal_i(3, expected.line_dels);
-
- /* diff on tests/resources/attr/root_test3 */
- memset(&expected, 0, sizeof(expected));
- cl_git_pass(git_diff_blobs(
- a, NULL, c, NULL, &opts,
- diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
-
- cl_assert_equal_i(1, expected.files);
- cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(0, expected.files_binary);
-
- cl_assert_equal_i(1, expected.hunks);
- cl_assert_equal_i(13, expected.lines);
- cl_assert_equal_i(0, expected.line_ctxt);
- cl_assert_equal_i(12, expected.line_adds);
- cl_assert_equal_i(1, expected.line_dels);
-
- memset(&expected, 0, sizeof(expected));
- cl_git_pass(git_diff_blobs(
- c, NULL, d, NULL, &opts,
- diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
-
- cl_assert_equal_i(1, expected.files);
- cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(0, expected.files_binary);
-
- cl_assert_equal_i(2, expected.hunks);
- cl_assert_equal_i(14, expected.lines);
- cl_assert_equal_i(4, expected.line_ctxt);
- cl_assert_equal_i(6, expected.line_adds);
- cl_assert_equal_i(4, expected.line_dels);
-
- git_blob_free(a);
- git_blob_free(b);
- git_blob_free(c);
-}
-
-void test_diff_blob__can_compare_text_blobs_with_patch(void)
-{
- git_blob *a, *b, *c;
- git_oid a_oid, b_oid, c_oid;
- git_diff_patch *p;
- const git_diff_delta *delta;
- size_t tc, ta, td;
-
- /* 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));
-
- /* tests/resources/attr/root_test3 */
- cl_git_pass(git_oid_fromstrn(&c_oid, "c96bbb2c2557a832", 16));
- cl_git_pass(git_blob_lookup_prefix(&c, g_repo, &c_oid, 8));
-
- /* Doing the equivalent of a `git diff -U1` on these files */
-
- /* diff on tests/resources/attr/root_test1 */
- cl_git_pass(git_diff_patch_from_blobs(&p, a, NULL, b, NULL, &opts));
-
- cl_assert(p != NULL);
-
- delta = git_diff_patch_delta(p);
- cl_assert(delta != NULL);
- cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status);
- cl_assert(git_oid_equal(git_blob_id(a), &delta->old_file.oid));
- cl_assert_equal_sz(git_blob_rawsize(a), delta->old_file.size);
- cl_assert(git_oid_equal(git_blob_id(b), &delta->new_file.oid));
- cl_assert_equal_sz(git_blob_rawsize(b), delta->new_file.size);
-
- cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p));
- cl_assert_equal_i(6, git_diff_patch_num_lines_in_hunk(p, 0));
-
- cl_git_pass(git_diff_patch_line_stats(&tc, &ta, &td, p));
- cl_assert_equal_i(1, (int)tc);
- cl_assert_equal_i(5, (int)ta);
- cl_assert_equal_i(0, (int)td);
-
- git_diff_patch_free(p);
-
- /* diff on tests/resources/attr/root_test2 */
- cl_git_pass(git_diff_patch_from_blobs(&p, b, NULL, c, NULL, &opts));
-
- cl_assert(p != NULL);
-
- delta = git_diff_patch_delta(p);
- cl_assert(delta != NULL);
- cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status);
- cl_assert(git_oid_equal(git_blob_id(b), &delta->old_file.oid));
- cl_assert_equal_sz(git_blob_rawsize(b), delta->old_file.size);
- cl_assert(git_oid_equal(git_blob_id(c), &delta->new_file.oid));
- cl_assert_equal_sz(git_blob_rawsize(c), delta->new_file.size);
-
- cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p));
- cl_assert_equal_i(15, git_diff_patch_num_lines_in_hunk(p, 0));
-
- cl_git_pass(git_diff_patch_line_stats(&tc, &ta, &td, p));
- cl_assert_equal_i(3, (int)tc);
- cl_assert_equal_i(9, (int)ta);
- cl_assert_equal_i(3, (int)td);
-
- git_diff_patch_free(p);
-
- /* diff on tests/resources/attr/root_test3 */
- cl_git_pass(git_diff_patch_from_blobs(&p, a, NULL, c, NULL, &opts));
-
- cl_assert(p != NULL);
-
- delta = git_diff_patch_delta(p);
- cl_assert(delta != NULL);
- cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status);
- cl_assert(git_oid_equal(git_blob_id(a), &delta->old_file.oid));
- cl_assert_equal_sz(git_blob_rawsize(a), delta->old_file.size);
- cl_assert(git_oid_equal(git_blob_id(c), &delta->new_file.oid));
- cl_assert_equal_sz(git_blob_rawsize(c), delta->new_file.size);
-
- cl_git_pass(git_diff_patch_line_stats(&tc, &ta, &td, p));
- cl_assert_equal_i(0, (int)tc);
- cl_assert_equal_i(12, (int)ta);
- cl_assert_equal_i(1, (int)td);
-
- git_diff_patch_free(p);
-
- /* one more */
- cl_git_pass(git_diff_patch_from_blobs(&p, c, NULL, d, NULL, &opts));
-
- cl_assert(p != NULL);
-
- delta = git_diff_patch_delta(p);
- cl_assert(delta != NULL);
- cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status);
- cl_assert(git_oid_equal(git_blob_id(c), &delta->old_file.oid));
- cl_assert_equal_sz(git_blob_rawsize(c), delta->old_file.size);
- cl_assert(git_oid_equal(git_blob_id(d), &delta->new_file.oid));
- cl_assert_equal_sz(git_blob_rawsize(d), delta->new_file.size);
-
- cl_assert_equal_i(2, (int)git_diff_patch_num_hunks(p));
- cl_assert_equal_i(5, git_diff_patch_num_lines_in_hunk(p, 0));
- cl_assert_equal_i(9, git_diff_patch_num_lines_in_hunk(p, 1));
-
- cl_git_pass(git_diff_patch_line_stats(&tc, &ta, &td, p));
- cl_assert_equal_i(4, (int)tc);
- cl_assert_equal_i(6, (int)ta);
- cl_assert_equal_i(4, (int)td);
-
- git_diff_patch_free(p);
-
- git_blob_free(a);
- git_blob_free(b);
- git_blob_free(c);
-}
-
-void test_diff_blob__can_compare_against_null_blobs(void)
-{
- git_blob *e = NULL;
-
- cl_git_pass(git_diff_blobs(
- d, NULL, e, NULL, &opts,
- diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
-
- cl_assert_equal_i(1, expected.files);
- cl_assert_equal_i(1, expected.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(0, expected.files_binary);
-
- cl_assert_equal_i(1, expected.hunks);
- cl_assert_equal_i(14, expected.hunk_old_lines);
- cl_assert_equal_i(14, expected.lines);
- cl_assert_equal_i(14, expected.line_dels);
-
- opts.flags |= GIT_DIFF_REVERSE;
- memset(&expected, 0, sizeof(expected));
-
- cl_git_pass(git_diff_blobs(
- d, NULL, e, NULL, &opts,
- diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
-
- cl_assert_equal_i(1, expected.files);
- cl_assert_equal_i(1, expected.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(0, expected.files_binary);
-
- cl_assert_equal_i(1, expected.hunks);
- cl_assert_equal_i(14, expected.hunk_new_lines);
- cl_assert_equal_i(14, expected.lines);
- cl_assert_equal_i(14, expected.line_adds);
-
- opts.flags ^= GIT_DIFF_REVERSE;
- memset(&expected, 0, sizeof(expected));
-
- cl_git_pass(git_diff_blobs(
- alien, NULL, NULL, NULL, &opts,
- diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
-
- cl_assert_equal_i(1, expected.files);
- cl_assert_equal_i(1, expected.files_binary);
- cl_assert_equal_i(1, expected.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(0, expected.hunks);
- cl_assert_equal_i(0, expected.lines);
-
- memset(&expected, 0, sizeof(expected));
-
- cl_git_pass(git_diff_blobs(
- NULL, NULL, alien, NULL, &opts,
- diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
-
- cl_assert_equal_i(1, expected.files);
- cl_assert_equal_i(1, expected.files_binary);
- cl_assert_equal_i(1, expected.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(0, expected.hunks);
- cl_assert_equal_i(0, expected.lines);
-}
-
-void test_diff_blob__can_compare_against_null_blobs_with_patch(void)
-{
- git_blob *e = NULL;
- git_diff_patch *p;
- const git_diff_delta *delta;
- int line;
- char origin;
-
- cl_git_pass(git_diff_patch_from_blobs(&p, d, NULL, e, NULL, &opts));
-
- cl_assert(p != NULL);
-
- delta = git_diff_patch_delta(p);
- cl_assert(delta != NULL);
- cl_assert_equal_i(GIT_DELTA_DELETED, delta->status);
- cl_assert(git_oid_equal(git_blob_id(d), &delta->old_file.oid));
- cl_assert_equal_sz(git_blob_rawsize(d), delta->old_file.size);
- cl_assert(git_oid_iszero(&delta->new_file.oid));
- cl_assert_equal_sz(0, delta->new_file.size);
-
- cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p));
- cl_assert_equal_i(14, git_diff_patch_num_lines_in_hunk(p, 0));
-
- for (line = 0; line < git_diff_patch_num_lines_in_hunk(p, 0); ++line) {
- cl_git_pass(git_diff_patch_get_line_in_hunk(
- &origin, NULL, NULL, NULL, NULL, p, 0, line));
- cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)origin);
- }
-
- git_diff_patch_free(p);
-
- opts.flags |= GIT_DIFF_REVERSE;
-
- cl_git_pass(git_diff_patch_from_blobs(&p, d, NULL, e, NULL, &opts));
-
- cl_assert(p != NULL);
-
- delta = git_diff_patch_delta(p);
- cl_assert(delta != NULL);
- cl_assert_equal_i(GIT_DELTA_ADDED, delta->status);
- cl_assert(git_oid_iszero(&delta->old_file.oid));
- cl_assert_equal_sz(0, delta->old_file.size);
- cl_assert(git_oid_equal(git_blob_id(d), &delta->new_file.oid));
- cl_assert_equal_sz(git_blob_rawsize(d), delta->new_file.size);
-
- cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p));
- cl_assert_equal_i(14, git_diff_patch_num_lines_in_hunk(p, 0));
-
- for (line = 0; line < git_diff_patch_num_lines_in_hunk(p, 0); ++line) {
- cl_git_pass(git_diff_patch_get_line_in_hunk(
- &origin, NULL, NULL, NULL, NULL, p, 0, line));
- cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)origin);
- }
-
- git_diff_patch_free(p);
-
- opts.flags ^= GIT_DIFF_REVERSE;
-
- cl_git_pass(git_diff_patch_from_blobs(&p, alien, NULL, NULL, NULL, &opts));
-
- cl_assert(p != NULL);
-
- delta = git_diff_patch_delta(p);
- cl_assert(delta != NULL);
- cl_assert_equal_i(GIT_DELTA_DELETED, delta->status);
- cl_assert((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
-
- cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p));
-
- git_diff_patch_free(p);
-
- cl_git_pass(git_diff_patch_from_blobs(&p, NULL, NULL, alien, NULL, &opts));
-
- cl_assert(p != NULL);
-
- delta = git_diff_patch_delta(p);
- cl_assert(delta != NULL);
- cl_assert_equal_i(GIT_DELTA_ADDED, delta->status);
- cl_assert((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
-
- cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p));
-
- git_diff_patch_free(p);
-}
-
-static void assert_identical_blobs_comparison(diff_expects *expected)
-{
- cl_assert_equal_i(1, expected->files);
- cl_assert_equal_i(1, expected->file_status[GIT_DELTA_UNMODIFIED]);
- cl_assert_equal_i(0, expected->hunks);
- cl_assert_equal_i(0, expected->lines);
-}
-
-void test_diff_blob__can_compare_identical_blobs(void)
-{
- opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
-
- cl_git_pass(git_diff_blobs(
- d, NULL, d, NULL, &opts,
- diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
-
- assert_identical_blobs_comparison(&expected);
- cl_assert_equal_i(0, expected.files_binary);
-
- memset(&expected, 0, sizeof(expected));
- cl_git_pass(git_diff_blobs(
- NULL, NULL, NULL, NULL, &opts,
- diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
-
- assert_identical_blobs_comparison(&expected);
- cl_assert_equal_i(0, expected.files_binary);
-
- memset(&expected, 0, sizeof(expected));
- cl_git_pass(git_diff_blobs(
- alien, NULL, alien, NULL, &opts,
- diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
-
- assert_identical_blobs_comparison(&expected);
- cl_assert(expected.files_binary > 0);
-}
-
-void test_diff_blob__can_compare_identical_blobs_with_patch(void)
-{
- git_diff_patch *p;
- const git_diff_delta *delta;
-
- cl_git_pass(git_diff_patch_from_blobs(&p, d, NULL, d, NULL, &opts));
- cl_assert(p != NULL);
-
- delta = git_diff_patch_delta(p);
- cl_assert(delta != NULL);
- cl_assert_equal_i(GIT_DELTA_UNMODIFIED, delta->status);
- cl_assert_equal_sz(delta->old_file.size, git_blob_rawsize(d));
- cl_assert(git_oid_equal(git_blob_id(d), &delta->old_file.oid));
- cl_assert_equal_sz(delta->new_file.size, git_blob_rawsize(d));
- cl_assert(git_oid_equal(git_blob_id(d), &delta->new_file.oid));
-
- cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p));
- git_diff_patch_free(p);
-
- cl_git_pass(git_diff_patch_from_blobs(&p, NULL, NULL, NULL, NULL, &opts));
- cl_assert(p != NULL);
-
- delta = git_diff_patch_delta(p);
- cl_assert(delta != NULL);
- cl_assert_equal_i(GIT_DELTA_UNMODIFIED, delta->status);
- cl_assert_equal_sz(0, delta->old_file.size);
- cl_assert(git_oid_iszero(&delta->old_file.oid));
- cl_assert_equal_sz(0, delta->new_file.size);
- cl_assert(git_oid_iszero(&delta->new_file.oid));
-
- cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p));
- git_diff_patch_free(p);
-
- cl_git_pass(git_diff_patch_from_blobs(&p, alien, NULL, alien, NULL, &opts));
- cl_assert(p != NULL);
- cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_diff_patch_delta(p)->status);
- cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p));
- git_diff_patch_free(p);
-}
-
-static void assert_binary_blobs_comparison(diff_expects *expected)
-{
- cl_assert(expected->files_binary > 0);
-
- cl_assert_equal_i(1, expected->files);
- cl_assert_equal_i(1, expected->file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(0, expected->hunks);
- cl_assert_equal_i(0, expected->lines);
-}
-
-void test_diff_blob__can_compare_two_binary_blobs(void)
-{
- git_blob *heart;
- git_oid h_oid;
-
- /* heart.png */
- cl_git_pass(git_oid_fromstrn(&h_oid, "de863bff", 8));
- cl_git_pass(git_blob_lookup_prefix(&heart, g_repo, &h_oid, 4));
-
- cl_git_pass(git_diff_blobs(
- alien, NULL, heart, NULL, &opts,
- diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
-
- assert_binary_blobs_comparison(&expected);
-
- memset(&expected, 0, sizeof(expected));
-
- cl_git_pass(git_diff_blobs(
- heart, NULL, alien, NULL, &opts,
- diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
-
- assert_binary_blobs_comparison(&expected);
-
- git_blob_free(heart);
-}
-
-void test_diff_blob__can_compare_a_binary_blob_and_a_text_blob(void)
-{
- cl_git_pass(git_diff_blobs(
- alien, NULL, d, NULL, &opts,
- diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
-
- assert_binary_blobs_comparison(&expected);
-
- memset(&expected, 0, sizeof(expected));
-
- cl_git_pass(git_diff_blobs(
- d, NULL, alien, NULL, &opts,
- diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
-
- assert_binary_blobs_comparison(&expected);
-}
-
-/*
- * $ git diff fe773770 a0f7217
- * diff --git a/fe773770 b/a0f7217
- * index fe77377..a0f7217 100644
- * --- a/fe773770
- * +++ b/a0f7217
- * @@ -1,6 +1,6 @@
- * Here is some stuff at the start
- *
- * -This should go in one hunk
- * +This should go in one hunk (first)
- *
- * Some additional lines
- *
- * @@ -8,7 +8,7 @@ Down here below the other lines
- *
- * With even more at the end
- *
- * -Followed by a second hunk of stuff
- * +Followed by a second hunk of stuff (second)
- *
- * That happens down here
- */
-void test_diff_blob__comparing_two_text_blobs_honors_interhunkcontext(void)
-{
- git_blob *old_d;
- git_oid old_d_oid;
-
- opts.context_lines = 3;
-
- /* tests/resources/attr/root_test1 from commit f5b0af1 */
- cl_git_pass(git_oid_fromstrn(&old_d_oid, "fe773770", 8));
- cl_git_pass(git_blob_lookup_prefix(&old_d, g_repo, &old_d_oid, 4));
-
- /* Test with default inter-hunk-context (not set) => default is 0 */
- cl_git_pass(git_diff_blobs(
- old_d, NULL, d, NULL, &opts,
- diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
-
- cl_assert_equal_i(2, expected.hunks);
-
- /* Test with inter-hunk-context explicitly set to 0 */
- opts.interhunk_lines = 0;
- memset(&expected, 0, sizeof(expected));
- cl_git_pass(git_diff_blobs(
- old_d, NULL, d, NULL, &opts,
- diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
-
- cl_assert_equal_i(2, expected.hunks);
-
- /* Test with inter-hunk-context explicitly set to 1 */
- opts.interhunk_lines = 1;
- memset(&expected, 0, sizeof(expected));
- cl_git_pass(git_diff_blobs(
- old_d, NULL, d, NULL, &opts,
- diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
-
- cl_assert_equal_i(1, expected.hunks);
-
- git_blob_free(old_d);
-}
-
-void test_diff_blob__checks_options_version_too_low(void)
-{
- const git_error *err;
-
- opts.version = 0;
- cl_git_fail(git_diff_blobs(
- d, NULL, alien, NULL, &opts,
- diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
- err = giterr_last();
- cl_assert_equal_i(GITERR_INVALID, err->klass);
-}
-
-void test_diff_blob__checks_options_version_too_high(void)
-{
- const git_error *err;
-
- opts.version = 1024;
- cl_git_fail(git_diff_blobs(
- d, NULL, alien, NULL, &opts,
- diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
- err = giterr_last();
- cl_assert_equal_i(GITERR_INVALID, err->klass);
-}
-
-void test_diff_blob__can_correctly_detect_a_binary_blob_as_binary(void)
-{
- /* alien.png */
- cl_assert_equal_i(true, git_blob_is_binary(alien));
-}
-
-void test_diff_blob__can_correctly_detect_a_textual_blob_as_non_binary(void)
-{
- /* tests/resources/attr/root_test4.txt */
- cl_assert_equal_i(false, git_blob_is_binary(d));
-}
-
-/*
- * git_diff_blob_to_buffer tests
- */
-
-static void assert_changed_single_one_line_file(
- diff_expects *expected, git_delta_t mod)
-{
- cl_assert_equal_i(1, expected->files);
- cl_assert_equal_i(1, expected->file_status[mod]);
- cl_assert_equal_i(1, expected->hunks);
- cl_assert_equal_i(1, expected->lines);
-
- if (mod == GIT_DELTA_ADDED)
- cl_assert_equal_i(1, expected->line_adds);
- else if (mod == GIT_DELTA_DELETED)
- cl_assert_equal_i(1, expected->line_dels);
-}
-
-void test_diff_blob__can_compare_blob_to_buffer(void)
-{
- git_blob *a;
- git_oid a_oid;
- const char *a_content = "Hello from the root\n";
- const char *b_content = "Hello from the root\n\nSome additional lines\n\nDown here below\n\n";
-
- /* tests/resources/attr/root_test1 */
- cl_git_pass(git_oid_fromstrn(&a_oid, "45141a79", 8));
- cl_git_pass(git_blob_lookup_prefix(&a, g_repo, &a_oid, 4));
-
- /* diff from blob a to content of b */
- quick_diff_blob_to_str(a, NULL, b_content, 0, NULL);
- cl_assert_equal_i(1, expected.files);
- cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(0, expected.files_binary);
- cl_assert_equal_i(1, expected.hunks);
- cl_assert_equal_i(6, expected.lines);
- cl_assert_equal_i(1, expected.line_ctxt);
- cl_assert_equal_i(5, expected.line_adds);
- cl_assert_equal_i(0, expected.line_dels);
-
- /* diff from blob a to content of a */
- opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
- quick_diff_blob_to_str(a, NULL, a_content, 0, NULL);
- assert_identical_blobs_comparison(&expected);
-
- /* diff from NULL blob to content of a */
- memset(&expected, 0, sizeof(expected));
- quick_diff_blob_to_str(NULL, NULL, a_content, 0, NULL);
- assert_changed_single_one_line_file(&expected, GIT_DELTA_ADDED);
-
- /* diff from blob a to NULL buffer */
- memset(&expected, 0, sizeof(expected));
- quick_diff_blob_to_str(a, NULL, NULL, 0, NULL);
- assert_changed_single_one_line_file(&expected, GIT_DELTA_DELETED);
-
- /* diff with reverse */
- opts.flags ^= GIT_DIFF_REVERSE;
-
- memset(&expected, 0, sizeof(expected));
- quick_diff_blob_to_str(a, NULL, NULL, 0, NULL);
- assert_changed_single_one_line_file(&expected, GIT_DELTA_ADDED);
-
- git_blob_free(a);
-}
-
-void test_diff_blob__can_compare_blob_to_buffer_with_patch(void)
-{
- git_diff_patch *p;
- git_blob *a;
- git_oid a_oid;
- const char *a_content = "Hello from the root\n";
- const char *b_content = "Hello from the root\n\nSome additional lines\n\nDown here below\n\n";
- size_t tc, ta, td;
-
- /* tests/resources/attr/root_test1 */
- cl_git_pass(git_oid_fromstrn(&a_oid, "45141a79", 8));
- cl_git_pass(git_blob_lookup_prefix(&a, g_repo, &a_oid, 4));
-
- /* diff from blob a to content of b */
- cl_git_pass(git_diff_patch_from_blob_and_buffer(
- &p, a, NULL, b_content, strlen(b_content), NULL, &opts));
-
- cl_assert(p != NULL);
- cl_assert_equal_i(GIT_DELTA_MODIFIED, git_diff_patch_delta(p)->status);
- cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p));
- cl_assert_equal_i(6, git_diff_patch_num_lines_in_hunk(p, 0));
-
- cl_git_pass(git_diff_patch_line_stats(&tc, &ta, &td, p));
- cl_assert_equal_i(1, (int)tc);
- cl_assert_equal_i(5, (int)ta);
- cl_assert_equal_i(0, (int)td);
-
- git_diff_patch_free(p);
-
- /* diff from blob a to content of a */
- opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
- cl_git_pass(git_diff_patch_from_blob_and_buffer(
- &p, a, NULL, a_content, strlen(a_content), NULL, &opts));
- cl_assert(p != NULL);
- cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_diff_patch_delta(p)->status);
- cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p));
- git_diff_patch_free(p);
-
- /* diff from NULL blob to content of a */
- cl_git_pass(git_diff_patch_from_blob_and_buffer(
- &p, NULL, NULL, a_content, strlen(a_content), NULL, &opts));
- cl_assert(p != NULL);
- cl_assert_equal_i(GIT_DELTA_ADDED, git_diff_patch_delta(p)->status);
- cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p));
- cl_assert_equal_i(1, git_diff_patch_num_lines_in_hunk(p, 0));
- git_diff_patch_free(p);
-
- /* diff from blob a to NULL buffer */
- cl_git_pass(git_diff_patch_from_blob_and_buffer(
- &p, a, NULL, NULL, 0, NULL, &opts));
- cl_assert(p != NULL);
- cl_assert_equal_i(GIT_DELTA_DELETED, git_diff_patch_delta(p)->status);
- cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p));
- cl_assert_equal_i(1, git_diff_patch_num_lines_in_hunk(p, 0));
- git_diff_patch_free(p);
-
- /* diff with reverse */
- opts.flags ^= GIT_DIFF_REVERSE;
-
- cl_git_pass(git_diff_patch_from_blob_and_buffer(
- &p, a, NULL, NULL, 0, NULL, &opts));
- cl_assert(p != NULL);
- cl_assert_equal_i(GIT_DELTA_ADDED, git_diff_patch_delta(p)->status);
- cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p));
- cl_assert_equal_i(1, git_diff_patch_num_lines_in_hunk(p, 0));
- git_diff_patch_free(p);
-
- git_blob_free(a);
-}
-
-static void assert_one_modified_with_lines(diff_expects *expected, int lines)
-{
- cl_assert_equal_i(1, expected->files);
- cl_assert_equal_i(1, expected->file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(0, expected->files_binary);
- cl_assert_equal_i(lines, expected->lines);
-}
-
-void test_diff_blob__binary_data_comparisons(void)
-{
- git_blob *bin, *nonbin;
- git_oid oid;
- const char *nonbin_content = "Hello from the root\n";
- size_t nonbin_len = 20;
- const char *bin_content = "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\n0123456789\n";
- size_t bin_len = 33;
-
- opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
-
- cl_git_pass(git_oid_fromstrn(&oid, "45141a79", 8));
- cl_git_pass(git_blob_lookup_prefix(&nonbin, g_repo, &oid, 4));
-
- cl_git_pass(git_oid_fromstrn(&oid, "b435cd56", 8));
- cl_git_pass(git_blob_lookup_prefix(&bin, g_repo, &oid, 4));
-
- /* non-binary to reference content */
-
- quick_diff_blob_to_str(nonbin, NULL, nonbin_content, nonbin_len, NULL);
- assert_identical_blobs_comparison(&expected);
- cl_assert_equal_i(0, expected.files_binary);
-
- /* binary to reference content */
-
- quick_diff_blob_to_str(bin, NULL, bin_content, bin_len, NULL);
- assert_identical_blobs_comparison(&expected);
-
- cl_assert_equal_i(1, expected.files_binary);
-
- /* non-binary to binary content */
-
- quick_diff_blob_to_str(nonbin, NULL, bin_content, bin_len, NULL);
- assert_binary_blobs_comparison(&expected);
-
- /* binary to non-binary content */
-
- quick_diff_blob_to_str(bin, NULL, nonbin_content, nonbin_len, NULL);
- assert_binary_blobs_comparison(&expected);
-
- /* non-binary to binary blob */
-
- memset(&expected, 0, sizeof(expected));
- cl_git_pass(git_diff_blobs(
- bin, NULL, nonbin, NULL, &opts,
- diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
- assert_binary_blobs_comparison(&expected);
-
- /*
- * repeat with FORCE_TEXT
- */
-
- opts.flags |= GIT_DIFF_FORCE_TEXT;
-
- quick_diff_blob_to_str(bin, NULL, bin_content, bin_len, NULL);
- assert_identical_blobs_comparison(&expected);
-
- quick_diff_blob_to_str(nonbin, NULL, bin_content, bin_len, NULL);
- assert_one_modified_with_lines(&expected, 4);
-
- quick_diff_blob_to_str(bin, NULL, nonbin_content, nonbin_len, NULL);
- assert_one_modified_with_lines(&expected, 4);
-
- memset(&expected, 0, sizeof(expected));
- cl_git_pass(git_diff_blobs(
- bin, NULL, nonbin, NULL, &opts,
- diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
- assert_one_modified_with_lines(&expected, 4);
-
- /* cleanup */
- git_blob_free(bin);
- git_blob_free(nonbin);
-}
-
-void test_diff_blob__using_path_and_attributes(void)
-{
- git_config *cfg;
- git_blob *bin, *nonbin;
- git_oid oid;
- const char *nonbin_content = "Hello from the root\n";
- const char *bin_content =
- "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\n0123456789\n";
- size_t bin_len = 33;
- const char *changed;
- git_diff_patch *p;
- char *pout;
-
- /* set up custom diff drivers and 'diff' attribute mappings for them */
-
- cl_git_pass(git_repository_config(&cfg, g_repo));
- cl_git_pass(git_config_set_bool(cfg, "diff.iam_binary.binary", 1));
- cl_git_pass(git_config_set_bool(cfg, "diff.iam_text.binary", 0));
- cl_git_pass(git_config_set_string(
- cfg, "diff.iam_alphactx.xfuncname", "^[A-Za-z]"));
- cl_git_pass(git_config_set_bool(cfg, "diff.iam_textalpha.binary", 0));
- cl_git_pass(git_config_set_string(
- cfg, "diff.iam_textalpha.xfuncname", "^[A-Za-z]"));
- cl_git_pass(git_config_set_string(
- cfg, "diff.iam_numctx.funcname", "^[0-9]"));
- cl_git_pass(git_config_set_bool(cfg, "diff.iam_textnum.binary", 0));
- cl_git_pass(git_config_set_string(
- cfg, "diff.iam_textnum.funcname", "^[0-9]"));
- git_config_free(cfg);
-
- cl_git_append2file(
- "attr/.gitattributes",
- "\n\n# test_diff_blob__using_path_and_attributes extra\n\n"
- "*.binary diff=iam_binary\n"
- "*.textary diff=iam_text\n"
- "*.alphary diff=iam_alphactx\n"
- "*.textalphary diff=iam_textalpha\n"
- "*.textnumary diff=iam_textnum\n"
- "*.numary diff=iam_numctx\n\n");
-
- opts.context_lines = 0;
- opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
-
- cl_git_pass(git_oid_fromstrn(&oid, "45141a79", 8));
- cl_git_pass(git_blob_lookup_prefix(&nonbin, g_repo, &oid, 4));
- /* 20b: "Hello from the root\n" */
-
- cl_git_pass(git_oid_fromstrn(&oid, "b435cd56", 8));
- cl_git_pass(git_blob_lookup_prefix(&bin, g_repo, &oid, 4));
- /* 33b: "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\n0123456789\n" */
-
- /* non-binary to reference content */
-
- quick_diff_blob_to_str(nonbin, NULL, nonbin_content, 0, NULL);
- assert_identical_blobs_comparison(&expected);
- cl_assert_equal_i(0, expected.files_binary);
-
- /* binary to reference content */
-
- quick_diff_blob_to_str(bin, NULL, bin_content, bin_len, NULL);
- assert_identical_blobs_comparison(&expected);
- cl_assert_equal_i(1, expected.files_binary);
-
- /* add some text */
-
- changed = "Hello from the root\nMore lines\nAnd more\nGo here\n";
-
- quick_diff_blob_to_str(nonbin, NULL, changed, 0, NULL);
- cl_assert_equal_i(1, expected.files);
- cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(0, expected.files_binary);
- cl_assert_equal_i(1, expected.hunks);
- cl_assert_equal_i(3, expected.lines);
- cl_assert_equal_i(0, expected.line_ctxt);
- cl_assert_equal_i(3, expected.line_adds);
- cl_assert_equal_i(0, expected.line_dels);
-
- quick_diff_blob_to_str(nonbin, "foo/bar.binary", changed, 0, NULL);
- cl_assert_equal_i(1, expected.files);
- cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(1, expected.files_binary);
- cl_assert_equal_i(0, expected.hunks);
- cl_assert_equal_i(0, expected.lines);
- cl_assert_equal_i(0, expected.line_ctxt);
- cl_assert_equal_i(0, expected.line_adds);
- cl_assert_equal_i(0, expected.line_dels);
-
- quick_diff_blob_to_str(nonbin, "foo/bar.textary", changed, 0, NULL);
- cl_assert_equal_i(1, expected.files);
- cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(0, expected.files_binary);
- cl_assert_equal_i(1, expected.hunks);
- cl_assert_equal_i(3, expected.lines);
- cl_assert_equal_i(0, expected.line_ctxt);
- cl_assert_equal_i(3, expected.line_adds);
- cl_assert_equal_i(0, expected.line_dels);
-
- quick_diff_blob_to_str(nonbin, "foo/bar.alphary", changed, 0, NULL);
- cl_assert_equal_i(1, expected.files);
- cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(0, expected.files_binary);
- cl_assert_equal_i(1, expected.hunks);
- cl_assert_equal_i(3, expected.lines);
- cl_assert_equal_i(0, expected.line_ctxt);
- cl_assert_equal_i(3, expected.line_adds);
- cl_assert_equal_i(0, expected.line_dels);
-
- cl_git_pass(git_diff_patch_from_blob_and_buffer(
- &p, nonbin, "zzz.normal", changed, strlen(changed), NULL, &opts));
- cl_git_pass(git_diff_patch_to_str(&pout, p));
- cl_assert_equal_s(
- "diff --git a/zzz.normal b/zzz.normal\n"
- "index 45141a7..75b0dbb 100644\n"
- "--- a/zzz.normal\n"
- "+++ b/zzz.normal\n"
- "@@ -1,0 +2,3 @@ Hello from the root\n"
- "+More lines\n"
- "+And more\n"
- "+Go here\n", pout);
- git__free(pout);
- git_diff_patch_free(p);
-
- cl_git_pass(git_diff_patch_from_blob_and_buffer(
- &p, nonbin, "zzz.binary", changed, strlen(changed), NULL, &opts));
- cl_git_pass(git_diff_patch_to_str(&pout, p));
- cl_assert_equal_s(
- "diff --git a/zzz.binary b/zzz.binary\n"
- "index 45141a7..75b0dbb 100644\n"
- "Binary files a/zzz.binary and b/zzz.binary differ\n", pout);
- git__free(pout);
- git_diff_patch_free(p);
-
- cl_git_pass(git_diff_patch_from_blob_and_buffer(
- &p, nonbin, "zzz.alphary", changed, strlen(changed), NULL, &opts));
- cl_git_pass(git_diff_patch_to_str(&pout, p));
- cl_assert_equal_s(
- "diff --git a/zzz.alphary b/zzz.alphary\n"
- "index 45141a7..75b0dbb 100644\n"
- "--- a/zzz.alphary\n"
- "+++ b/zzz.alphary\n"
- "@@ -1,0 +2,3 @@ Hello from the root\n"
- "+More lines\n"
- "+And more\n"
- "+Go here\n", pout);
- git__free(pout);
- git_diff_patch_free(p);
-
- cl_git_pass(git_diff_patch_from_blob_and_buffer(
- &p, nonbin, "zzz.numary", changed, strlen(changed), NULL, &opts));
- cl_git_pass(git_diff_patch_to_str(&pout, p));
- cl_assert_equal_s(
- "diff --git a/zzz.numary b/zzz.numary\n"
- "index 45141a7..75b0dbb 100644\n"
- "--- a/zzz.numary\n"
- "+++ b/zzz.numary\n"
- "@@ -1,0 +2,3 @@\n"
- "+More lines\n"
- "+And more\n"
- "+Go here\n", pout);
- git__free(pout);
- git_diff_patch_free(p);
-
- /* "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\n0123456789\n"
- * 33 bytes
- */
-
- changed = "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\nreplace a line\n";
-
- cl_git_pass(git_diff_patch_from_blob_and_buffer(
- &p, bin, "zzz.normal", changed, 37, NULL, &opts));
- cl_git_pass(git_diff_patch_to_str(&pout, p));
- cl_assert_equal_s(
- "diff --git a/zzz.normal b/zzz.normal\n"
- "index b435cd5..1604519 100644\n"
- "Binary files a/zzz.normal and b/zzz.normal differ\n", pout);
- git__free(pout);
- git_diff_patch_free(p);
-
- cl_git_pass(git_diff_patch_from_blob_and_buffer(
- &p, bin, "zzz.textary", changed, 37, NULL, &opts));
- cl_git_pass(git_diff_patch_to_str(&pout, p));
- cl_assert_equal_s(
- "diff --git a/zzz.textary b/zzz.textary\n"
- "index b435cd5..1604519 100644\n"
- "--- a/zzz.textary\n"
- "+++ b/zzz.textary\n"
- "@@ -3 +3 @@\n"
- "-0123456789\n"
- "+replace a line\n", pout);
- git__free(pout);
- git_diff_patch_free(p);
-
- cl_git_pass(git_diff_patch_from_blob_and_buffer(
- &p, bin, "zzz.textalphary", changed, 37, NULL, &opts));
- cl_git_pass(git_diff_patch_to_str(&pout, p));
- cl_assert_equal_s(
- "diff --git a/zzz.textalphary b/zzz.textalphary\n"
- "index b435cd5..1604519 100644\n"
- "--- a/zzz.textalphary\n"
- "+++ b/zzz.textalphary\n"
- "@@ -3 +3 @@\n"
- "-0123456789\n"
- "+replace a line\n", pout);
- git__free(pout);
- git_diff_patch_free(p);
-
- cl_git_pass(git_diff_patch_from_blob_and_buffer(
- &p, bin, "zzz.textnumary", changed, 37, NULL, &opts));
- cl_git_pass(git_diff_patch_to_str(&pout, p));
- cl_assert_equal_s(
- "diff --git a/zzz.textnumary b/zzz.textnumary\n"
- "index b435cd5..1604519 100644\n"
- "--- a/zzz.textnumary\n"
- "+++ b/zzz.textnumary\n"
- "@@ -3 +3 @@ 0123456789\n"
- "-0123456789\n"
- "+replace a line\n", pout);
- git__free(pout);
- git_diff_patch_free(p);
-
- git_blob_free(nonbin);
- git_blob_free(bin);
-}
diff --git a/tests-clar/diff/diff_helpers.c b/tests-clar/diff/diff_helpers.c
deleted file mode 100644
index 4e23792a6..000000000
--- a/tests-clar/diff/diff_helpers.c
+++ /dev/null
@@ -1,220 +0,0 @@
-#include "clar_libgit2.h"
-#include "diff_helpers.h"
-
-git_tree *resolve_commit_oid_to_tree(
- git_repository *repo,
- const char *partial_oid)
-{
- size_t len = strlen(partial_oid);
- git_oid oid;
- git_object *obj = NULL;
- git_tree *tree = NULL;
-
- if (git_oid_fromstrn(&oid, partial_oid, len) == 0)
- git_object_lookup_prefix(&obj, repo, &oid, len, GIT_OBJ_ANY);
- cl_assert(obj);
- if (git_object_type(obj) == GIT_OBJ_TREE)
- return (git_tree *)obj;
- cl_assert(git_object_type(obj) == GIT_OBJ_COMMIT);
- cl_git_pass(git_commit_tree(&tree, (git_commit *)obj));
- git_object_free(obj);
- return tree;
-}
-
-int diff_file_cb(
- const git_diff_delta *delta,
- float progress,
- void *payload)
-{
- diff_expects *e = payload;
-
- if (e->debug)
- fprintf(stderr, "%c %s (%.3f)\n",
- git_diff_status_char(delta->status),
- delta->old_file.path, progress);
-
- if (e->names)
- cl_assert_equal_s(e->names[e->files], delta->old_file.path);
- if (e->statuses)
- cl_assert_equal_i(e->statuses[e->files], (int)delta->status);
-
- e->files++;
-
- if ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0)
- e->files_binary++;
-
- cl_assert(delta->status <= GIT_DELTA_TYPECHANGE);
-
- e->file_status[delta->status] += 1;
-
- return 0;
-}
-
-int diff_print_file_cb(
- const git_diff_delta *delta,
- float progress,
- void *payload)
-{
- fprintf(stderr, "%c %s\n",
- git_diff_status_char(delta->status), delta->old_file.path);
- return diff_file_cb(delta, progress, payload);
-}
-
-int diff_hunk_cb(
- const git_diff_delta *delta,
- const git_diff_range *range,
- const char *header,
- size_t header_len,
- void *payload)
-{
- diff_expects *e = payload;
-
- GIT_UNUSED(delta);
- GIT_UNUSED(header);
- GIT_UNUSED(header_len);
-
- e->hunks++;
- e->hunk_old_lines += range->old_lines;
- e->hunk_new_lines += range->new_lines;
- return 0;
-}
-
-int diff_line_cb(
- const git_diff_delta *delta,
- const git_diff_range *range,
- char line_origin,
- const char *content,
- size_t content_len,
- void *payload)
-{
- diff_expects *e = payload;
-
- GIT_UNUSED(delta);
- GIT_UNUSED(range);
- GIT_UNUSED(content);
- GIT_UNUSED(content_len);
-
- e->lines++;
- switch (line_origin) {
- case GIT_DIFF_LINE_CONTEXT:
- case GIT_DIFF_LINE_CONTEXT_EOFNL: /* techically not a line */
- e->line_ctxt++;
- break;
- case GIT_DIFF_LINE_ADDITION:
- case GIT_DIFF_LINE_ADD_EOFNL: /* technically not a line add */
- e->line_adds++;
- break;
- case GIT_DIFF_LINE_DELETION:
- case GIT_DIFF_LINE_DEL_EOFNL: /* technically not a line delete */
- e->line_dels++;
- break;
- default:
- break;
- }
- return 0;
-}
-
-int diff_foreach_via_iterator(
- git_diff_list *diff,
- git_diff_file_cb file_cb,
- git_diff_hunk_cb hunk_cb,
- git_diff_data_cb line_cb,
- void *data)
-{
- size_t d, num_d = git_diff_num_deltas(diff);
-
- for (d = 0; d < num_d; ++d) {
- git_diff_patch *patch;
- const git_diff_delta *delta;
- size_t h, num_h;
-
- cl_git_pass(git_diff_get_patch(&patch, &delta, diff, d));
- cl_assert(delta);
-
- /* call file_cb for this file */
- if (file_cb != NULL && file_cb(delta, (float)d / num_d, data) != 0) {
- git_diff_patch_free(patch);
- goto abort;
- }
-
- /* if there are no changes, then the patch will be NULL */
- if (!patch) {
- cl_assert(delta->status == GIT_DELTA_UNMODIFIED ||
- (delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
- continue;
- }
-
- if (!hunk_cb && !line_cb) {
- git_diff_patch_free(patch);
- continue;
- }
-
- num_h = git_diff_patch_num_hunks(patch);
-
- for (h = 0; h < num_h; h++) {
- const git_diff_range *range;
- const char *hdr;
- size_t hdr_len, l, num_l;
-
- cl_git_pass(git_diff_patch_get_hunk(
- &range, &hdr, &hdr_len, &num_l, patch, h));
-
- if (hunk_cb && hunk_cb(delta, range, hdr, hdr_len, data) != 0) {
- git_diff_patch_free(patch);
- goto abort;
- }
-
- for (l = 0; l < num_l; ++l) {
- char origin;
- const char *line;
- size_t line_len;
- int old_lineno, new_lineno;
-
- cl_git_pass(git_diff_patch_get_line_in_hunk(
- &origin, &line, &line_len, &old_lineno, &new_lineno,
- patch, h, l));
-
- if (line_cb &&
- line_cb(delta, range, origin, line, line_len, data) != 0) {
- git_diff_patch_free(patch);
- goto abort;
- }
- }
- }
-
- git_diff_patch_free(patch);
- }
-
- return 0;
-
-abort:
- giterr_clear();
- return GIT_EUSER;
-}
-
-static int diff_print_cb(
- const git_diff_delta *delta,
- const git_diff_range *range,
- char line_origin, /**< GIT_DIFF_LINE_... value from above */
- const char *content,
- size_t content_len,
- void *payload)
-{
- GIT_UNUSED(payload);
- GIT_UNUSED(delta);
- GIT_UNUSED(range);
- GIT_UNUSED(line_origin);
- GIT_UNUSED(content_len);
- fputs(content, (FILE *)payload);
- return 0;
-}
-
-void diff_print(FILE *fp, git_diff_list *diff)
-{
- cl_git_pass(git_diff_print_patch(diff, diff_print_cb, fp ? fp : stderr));
-}
-
-void diff_print_raw(FILE *fp, git_diff_list *diff)
-{
- cl_git_pass(git_diff_print_raw(diff, diff_print_cb, fp ? fp : stderr));
-}
diff --git a/tests-clar/diff/diff_helpers.h b/tests-clar/diff/diff_helpers.h
deleted file mode 100644
index bb76d0076..000000000
--- a/tests-clar/diff/diff_helpers.h
+++ /dev/null
@@ -1,68 +0,0 @@
-#include "fileops.h"
-#include "git2/diff.h"
-
-extern git_tree *resolve_commit_oid_to_tree(
- git_repository *repo, const char *partial_oid);
-
-typedef struct {
- int files;
- int files_binary;
-
- int file_status[10]; /* indexed by git_delta_t value */
-
- int hunks;
- int hunk_new_lines;
- int hunk_old_lines;
-
- int lines;
- int line_ctxt;
- int line_adds;
- int line_dels;
-
- /* optional arrays of expected specific values */
- const char **names;
- int *statuses;
-
- int debug;
-
-} diff_expects;
-
-typedef struct {
- const char *path;
- const char *matched_pathspec;
-} notify_expected;
-
-extern int diff_file_cb(
- const git_diff_delta *delta,
- float progress,
- void *cb_data);
-
-extern int diff_print_file_cb(
- const git_diff_delta *delta,
- float progress,
- void *cb_data);
-
-extern int diff_hunk_cb(
- const git_diff_delta *delta,
- const git_diff_range *range,
- const char *header,
- size_t header_len,
- void *cb_data);
-
-extern int diff_line_cb(
- const git_diff_delta *delta,
- const git_diff_range *range,
- char line_origin,
- const char *content,
- size_t content_len,
- void *cb_data);
-
-extern int diff_foreach_via_iterator(
- git_diff_list *diff,
- git_diff_file_cb file_cb,
- git_diff_hunk_cb hunk_cb,
- git_diff_data_cb line_cb,
- void *data);
-
-extern void diff_print(FILE *fp, git_diff_list *diff);
-extern void diff_print_raw(FILE *fp, git_diff_list *diff);
diff --git a/tests-clar/diff/diffiter.c b/tests-clar/diff/diffiter.c
deleted file mode 100644
index 932d720f2..000000000
--- a/tests-clar/diff/diffiter.c
+++ /dev/null
@@ -1,465 +0,0 @@
-#include "clar_libgit2.h"
-#include "diff_helpers.h"
-
-void test_diff_diffiter__initialize(void)
-{
-}
-
-void test_diff_diffiter__cleanup(void)
-{
- cl_git_sandbox_cleanup();
-}
-
-void test_diff_diffiter__create(void)
-{
- git_repository *repo = cl_git_sandbox_init("attr");
- git_diff_list *diff;
- size_t d, num_d;
-
- cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
-
- num_d = git_diff_num_deltas(diff);
- for (d = 0; d < num_d; ++d) {
- const git_diff_delta *delta;
- cl_git_pass(git_diff_get_patch(NULL, &delta, diff, d));
- }
-
- git_diff_list_free(diff);
-}
-
-void test_diff_diffiter__iterate_files(void)
-{
- git_repository *repo = cl_git_sandbox_init("attr");
- git_diff_list *diff;
- size_t d, num_d;
- int count = 0;
-
- cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
-
- num_d = git_diff_num_deltas(diff);
- cl_assert_equal_i(6, (int)num_d);
-
- for (d = 0; d < num_d; ++d) {
- const git_diff_delta *delta;
- cl_git_pass(git_diff_get_patch(NULL, &delta, diff, d));
- cl_assert(delta != NULL);
- count++;
- }
- cl_assert_equal_i(6, count);
-
- git_diff_list_free(diff);
-}
-
-void test_diff_diffiter__iterate_files_2(void)
-{
- git_repository *repo = cl_git_sandbox_init("status");
- git_diff_list *diff;
- size_t d, num_d;
- int count = 0;
-
- cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
-
- num_d = git_diff_num_deltas(diff);
- cl_assert_equal_i(8, (int)num_d);
-
- for (d = 0; d < num_d; ++d) {
- const git_diff_delta *delta;
- cl_git_pass(git_diff_get_patch(NULL, &delta, diff, d));
- cl_assert(delta != NULL);
- count++;
- }
- cl_assert_equal_i(8, count);
-
- git_diff_list_free(diff);
-}
-
-void test_diff_diffiter__iterate_files_and_hunks(void)
-{
- git_repository *repo = cl_git_sandbox_init("status");
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_diff_list *diff = NULL;
- size_t d, num_d;
- int file_count = 0, hunk_count = 0;
-
- opts.context_lines = 3;
- opts.interhunk_lines = 1;
- opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
-
- cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
-
- num_d = git_diff_num_deltas(diff);
-
- for (d = 0; d < num_d; ++d) {
- git_diff_patch *patch;
- const git_diff_delta *delta;
- size_t h, num_h;
-
- cl_git_pass(git_diff_get_patch(&patch, &delta, diff, d));
-
- cl_assert(delta);
- cl_assert(patch);
-
- file_count++;
-
- num_h = git_diff_patch_num_hunks(patch);
-
- for (h = 0; h < num_h; h++) {
- const git_diff_range *range;
- const char *header;
- size_t header_len, num_l;
-
- cl_git_pass(git_diff_patch_get_hunk(
- &range, &header, &header_len, &num_l, patch, h));
-
- cl_assert(range);
- cl_assert(header);
-
- hunk_count++;
- }
-
- git_diff_patch_free(patch);
- }
-
- cl_assert_equal_i(13, file_count);
- cl_assert_equal_i(8, hunk_count);
-
- git_diff_list_free(diff);
-}
-
-void test_diff_diffiter__max_size_threshold(void)
-{
- git_repository *repo = cl_git_sandbox_init("status");
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_diff_list *diff = NULL;
- int file_count = 0, binary_count = 0, hunk_count = 0;
- size_t d, num_d;
-
- opts.context_lines = 3;
- opts.interhunk_lines = 1;
- opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
-
- cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
- num_d = git_diff_num_deltas(diff);
-
- for (d = 0; d < num_d; ++d) {
- git_diff_patch *patch;
- const git_diff_delta *delta;
-
- cl_git_pass(git_diff_get_patch(&patch, &delta, diff, d));
- cl_assert(delta);
- cl_assert(patch);
-
- file_count++;
- hunk_count += (int)git_diff_patch_num_hunks(patch);
-
- assert((delta->flags & (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY)) != 0);
- binary_count += ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
-
- git_diff_patch_free(patch);
- }
-
- cl_assert_equal_i(13, file_count);
- cl_assert_equal_i(0, binary_count);
- cl_assert_equal_i(8, hunk_count);
-
- git_diff_list_free(diff);
-
- /* try again with low file size threshold */
-
- file_count = binary_count = hunk_count = 0;
-
- opts.context_lines = 3;
- opts.interhunk_lines = 1;
- opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
- opts.max_size = 50; /* treat anything over 50 bytes as binary! */
-
- cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
- num_d = git_diff_num_deltas(diff);
-
- for (d = 0; d < num_d; ++d) {
- git_diff_patch *patch;
- const git_diff_delta *delta;
-
- cl_git_pass(git_diff_get_patch(&patch, &delta, diff, d));
-
- file_count++;
- hunk_count += (int)git_diff_patch_num_hunks(patch);
-
- assert((delta->flags & (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY)) != 0);
- binary_count += ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
-
- git_diff_patch_free(patch);
- }
-
- cl_assert_equal_i(13, file_count);
- /* Three files are over the 50 byte threshold:
- * - staged_changes_file_deleted
- * - staged_changes_modified_file
- * - staged_new_file_modified_file
- */
- cl_assert_equal_i(3, binary_count);
- cl_assert_equal_i(5, hunk_count);
-
- git_diff_list_free(diff);
-}
-
-
-void test_diff_diffiter__iterate_all(void)
-{
- git_repository *repo = cl_git_sandbox_init("status");
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_diff_list *diff = NULL;
- diff_expects exp = {0};
- size_t d, num_d;
-
- opts.context_lines = 3;
- opts.interhunk_lines = 1;
- opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
-
- cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
-
- num_d = git_diff_num_deltas(diff);
- for (d = 0; d < num_d; ++d) {
- git_diff_patch *patch;
- const git_diff_delta *delta;
- size_t h, num_h;
-
- cl_git_pass(git_diff_get_patch(&patch, &delta, diff, d));
- cl_assert(patch && delta);
- exp.files++;
-
- num_h = git_diff_patch_num_hunks(patch);
- for (h = 0; h < num_h; h++) {
- const git_diff_range *range;
- const char *header;
- size_t header_len, l, num_l;
-
- cl_git_pass(git_diff_patch_get_hunk(
- &range, &header, &header_len, &num_l, patch, h));
- cl_assert(range && header);
- exp.hunks++;
-
- for (l = 0; l < num_l; ++l) {
- char origin;
- const char *content;
- size_t content_len;
-
- cl_git_pass(git_diff_patch_get_line_in_hunk(
- &origin, &content, &content_len, NULL, NULL, patch, h, l));
- cl_assert(content);
- exp.lines++;
- }
- }
-
- git_diff_patch_free(patch);
- }
-
- cl_assert_equal_i(13, exp.files);
- cl_assert_equal_i(8, exp.hunks);
- cl_assert_equal_i(14, exp.lines);
-
- git_diff_list_free(diff);
-}
-
-static void iterate_over_patch(git_diff_patch *patch, diff_expects *exp)
-{
- size_t h, num_h = git_diff_patch_num_hunks(patch), num_l;
-
- exp->files++;
- exp->hunks += (int)num_h;
-
- /* let's iterate in reverse, just because we can! */
- for (h = 1, num_l = 0; h <= num_h; ++h)
- num_l += git_diff_patch_num_lines_in_hunk(patch, num_h - h);
-
- exp->lines += (int)num_l;
-}
-
-#define PATCH_CACHE 5
-
-void test_diff_diffiter__iterate_randomly_while_saving_state(void)
-{
- git_repository *repo = cl_git_sandbox_init("status");
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_diff_list *diff = NULL;
- diff_expects exp = {0};
- git_diff_patch *patches[PATCH_CACHE];
- size_t p, d, num_d;
-
- memset(patches, 0, sizeof(patches));
-
- opts.context_lines = 3;
- opts.interhunk_lines = 1;
- opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
-
- cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
-
- num_d = git_diff_num_deltas(diff);
-
- /* To make sure that references counts work for diff and patch objects,
- * this generates patches and randomly caches them. Only when the patch
- * is removed from the cache are hunks and lines counted. At the end,
- * there are still patches in the cache, so free the diff and try to
- * process remaining patches after the diff is freed.
- */
-
- srand(121212);
- p = rand() % PATCH_CACHE;
-
- for (d = 0; d < num_d; ++d) {
- /* take old patch */
- git_diff_patch *patch = patches[p];
- patches[p] = NULL;
-
- /* cache new patch */
- cl_git_pass(git_diff_get_patch(&patches[p], NULL, diff, d));
- cl_assert(patches[p] != NULL);
-
- /* process old patch if non-NULL */
- if (patch != NULL) {
- iterate_over_patch(patch, &exp);
- git_diff_patch_free(patch);
- }
-
- p = rand() % PATCH_CACHE;
- }
-
- /* free diff list now - refcounts should keep things safe */
- git_diff_list_free(diff);
-
- /* process remaining unprocessed patches */
- for (p = 0; p < PATCH_CACHE; p++) {
- git_diff_patch *patch = patches[p];
-
- if (patch != NULL) {
- iterate_over_patch(patch, &exp);
- git_diff_patch_free(patch);
- }
- }
-
- /* hopefully it all still added up right */
- cl_assert_equal_i(13, exp.files);
- cl_assert_equal_i(8, exp.hunks);
- cl_assert_equal_i(14, exp.lines);
-}
-
-/* This output is taken directly from `git diff` on the status test data */
-static const char *expected_patch_text[8] = {
- /* 0 */
- "diff --git a/file_deleted b/file_deleted\n"
- "deleted file mode 100644\n"
- "index 5452d32..0000000\n"
- "--- a/file_deleted\n"
- "+++ /dev/null\n"
- "@@ -1 +0,0 @@\n"
- "-file_deleted\n",
- /* 1 */
- "diff --git a/modified_file b/modified_file\n"
- "index 452e424..0a53963 100644\n"
- "--- a/modified_file\n"
- "+++ b/modified_file\n"
- "@@ -1 +1,2 @@\n"
- " modified_file\n"
- "+modified_file\n",
- /* 2 */
- "diff --git a/staged_changes_file_deleted b/staged_changes_file_deleted\n"
- "deleted file mode 100644\n"
- "index a6be623..0000000\n"
- "--- a/staged_changes_file_deleted\n"
- "+++ /dev/null\n"
- "@@ -1,2 +0,0 @@\n"
- "-staged_changes_file_deleted\n"
- "-staged_changes_file_deleted\n",
- /* 3 */
- "diff --git a/staged_changes_modified_file b/staged_changes_modified_file\n"
- "index 906ee77..011c344 100644\n"
- "--- a/staged_changes_modified_file\n"
- "+++ b/staged_changes_modified_file\n"
- "@@ -1,2 +1,3 @@\n"
- " staged_changes_modified_file\n"
- " staged_changes_modified_file\n"
- "+staged_changes_modified_file\n",
- /* 4 */
- "diff --git a/staged_new_file_deleted_file b/staged_new_file_deleted_file\n"
- "deleted file mode 100644\n"
- "index 90b8c29..0000000\n"
- "--- a/staged_new_file_deleted_file\n"
- "+++ /dev/null\n"
- "@@ -1 +0,0 @@\n"
- "-staged_new_file_deleted_file\n",
- /* 5 */
- "diff --git a/staged_new_file_modified_file b/staged_new_file_modified_file\n"
- "index ed06290..8b090c0 100644\n"
- "--- a/staged_new_file_modified_file\n"
- "+++ b/staged_new_file_modified_file\n"
- "@@ -1 +1,2 @@\n"
- " staged_new_file_modified_file\n"
- "+staged_new_file_modified_file\n",
- /* 6 */
- "diff --git a/subdir/deleted_file b/subdir/deleted_file\n"
- "deleted file mode 100644\n"
- "index 1888c80..0000000\n"
- "--- a/subdir/deleted_file\n"
- "+++ /dev/null\n"
- "@@ -1 +0,0 @@\n"
- "-subdir/deleted_file\n",
- /* 7 */
- "diff --git a/subdir/modified_file b/subdir/modified_file\n"
- "index a619198..57274b7 100644\n"
- "--- a/subdir/modified_file\n"
- "+++ b/subdir/modified_file\n"
- "@@ -1 +1,2 @@\n"
- " subdir/modified_file\n"
- "+subdir/modified_file\n"
-};
-
-void test_diff_diffiter__iterate_and_generate_patch_text(void)
-{
- git_repository *repo = cl_git_sandbox_init("status");
- git_diff_list *diff;
- size_t d, num_d;
-
- cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
-
- num_d = git_diff_num_deltas(diff);
- cl_assert_equal_i(8, (int)num_d);
-
- for (d = 0; d < num_d; ++d) {
- git_diff_patch *patch;
- char *text;
-
- cl_git_pass(git_diff_get_patch(&patch, NULL, diff, d));
- cl_assert(patch != NULL);
-
- cl_git_pass(git_diff_patch_to_str(&text, patch));
-
- cl_assert_equal_s(expected_patch_text[d], text);
-
- git__free(text);
- git_diff_patch_free(patch);
- }
-
- git_diff_list_free(diff);
-}
-
-void test_diff_diffiter__checks_options_version(void)
-{
- git_repository *repo = cl_git_sandbox_init("status");
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_diff_list *diff = NULL;
- const git_error *err;
-
- opts.version = 0;
- opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
-
- cl_git_fail(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
- err = giterr_last();
- cl_assert_equal_i(GITERR_INVALID, err->klass);
-
- giterr_clear();
- opts.version = 1024;
- cl_git_fail(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
- err = giterr_last();
- cl_assert_equal_i(GITERR_INVALID, err->klass);
-}
-
diff --git a/tests-clar/diff/drivers.c b/tests-clar/diff/drivers.c
deleted file mode 100644
index 06ab2ff14..000000000
--- a/tests-clar/diff/drivers.c
+++ /dev/null
@@ -1,125 +0,0 @@
-#include "clar_libgit2.h"
-#include "diff_helpers.h"
-#include "repository.h"
-#include "diff_driver.h"
-
-static git_repository *g_repo = NULL;
-
-void test_diff_drivers__initialize(void)
-{
-}
-
-void test_diff_drivers__cleanup(void)
-{
- cl_git_sandbox_cleanup();
- g_repo = NULL;
-}
-
-void test_diff_drivers__patterns(void)
-{
- git_config *cfg;
- const char *one_sha = "19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13";
- git_tree *one;
- git_diff_list *diff;
- git_diff_patch *patch;
- char *text;
- const char *expected0 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\n--- a/untimely.txt\n+++ b/untimely.txt\n@@ -22,3 +22,5 @@ Comes through the blood of the vanguards who\n dreamed--too soon--it had sounded.\r\n \r\n -- Rudyard Kipling\r\n+\r\n+Some new stuff\r\n";
- const char *expected1 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\nBinary files a/untimely.txt and b/untimely.txt differ\n";
- const char *expected2 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\n--- a/untimely.txt\n+++ b/untimely.txt\n@@ -22,3 +22,5 @@ Heaven delivers on earth the Hour that cannot be\n dreamed--too soon--it had sounded.\r\n \r\n -- Rudyard Kipling\r\n+\r\n+Some new stuff\r\n";
-
- g_repo = cl_git_sandbox_init("renames");
-
- one = resolve_commit_oid_to_tree(g_repo, one_sha);
-
- /* no diff */
-
- cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
- cl_assert_equal_i(0, (int)git_diff_num_deltas(diff));
- git_diff_list_free(diff);
-
- /* default diff */
-
- cl_git_append2file("renames/untimely.txt", "\r\nSome new stuff\r\n");
-
- cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
- cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
-
- cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0));
- cl_git_pass(git_diff_patch_to_str(&text, patch));
- cl_assert_equal_s(expected0, text);
-
- git__free(text);
- git_diff_patch_free(patch);
- git_diff_list_free(diff);
-
- /* attribute diff set to false */
-
- cl_git_rewritefile("renames/.gitattributes", "untimely.txt -diff\n");
-
- cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
- cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
-
- cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0));
- cl_git_pass(git_diff_patch_to_str(&text, patch));
- cl_assert_equal_s(expected1, text);
-
- git__free(text);
- git_diff_patch_free(patch);
- git_diff_list_free(diff);
-
- /* attribute diff set to unconfigured value (should use default) */
-
- cl_git_rewritefile("renames/.gitattributes", "untimely.txt diff=kipling0\n");
-
- cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
- cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
-
- cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0));
- cl_git_pass(git_diff_patch_to_str(&text, patch));
- cl_assert_equal_s(expected0, text);
-
- git__free(text);
- git_diff_patch_free(patch);
- git_diff_list_free(diff);
-
- /* let's define that driver */
-
- cl_git_pass(git_repository_config(&cfg, g_repo));
- cl_git_pass(git_config_set_bool(cfg, "diff.kipling0.binary", 1));
- git_config_free(cfg);
-
- cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
- cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
-
- cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0));
- cl_git_pass(git_diff_patch_to_str(&text, patch));
- cl_assert_equal_s(expected1, text);
-
- git__free(text);
- git_diff_patch_free(patch);
- git_diff_list_free(diff);
-
- /* let's use a real driver with some regular expressions */
-
- git_diff_driver_registry_free(g_repo->diff_drivers);
- g_repo->diff_drivers = NULL;
-
- cl_git_pass(git_repository_config(&cfg, g_repo));
- cl_git_pass(git_config_set_bool(cfg, "diff.kipling0.binary", 0));
- cl_git_pass(git_config_set_string(cfg, "diff.kipling0.xfuncname", "^H"));
- git_config_free(cfg);
-
- cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
- cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
-
- cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0));
- cl_git_pass(git_diff_patch_to_str(&text, patch));
- cl_assert_equal_s(expected2, text);
-
- git__free(text);
- git_diff_patch_free(patch);
- git_diff_list_free(diff);
-
- git_tree_free(one);
-}
-
diff --git a/tests-clar/diff/index.c b/tests-clar/diff/index.c
deleted file mode 100644
index e1c617dae..000000000
--- a/tests-clar/diff/index.c
+++ /dev/null
@@ -1,167 +0,0 @@
-#include "clar_libgit2.h"
-#include "diff_helpers.h"
-
-static git_repository *g_repo = NULL;
-
-void test_diff_index__initialize(void)
-{
- g_repo = cl_git_sandbox_init("status");
-}
-
-void test_diff_index__cleanup(void)
-{
- cl_git_sandbox_cleanup();
-}
-
-void test_diff_index__0(void)
-{
- /* grabbed a couple of commit oids from the history of the attr repo */
- const char *a_commit = "26a125ee1bf"; /* the current HEAD */
- const char *b_commit = "0017bd4ab1ec3"; /* the start */
- git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
- git_tree *b = resolve_commit_oid_to_tree(g_repo, b_commit);
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_diff_list *diff = NULL;
- diff_expects exp;
-
- cl_assert(a);
- cl_assert(b);
-
- opts.context_lines = 1;
- opts.interhunk_lines = 1;
-
- memset(&exp, 0, sizeof(exp));
-
- cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
-
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- /* to generate these values:
- * - cd to tests/resources/status,
- * - mv .gitted .git
- * - git diff --name-status --cached 26a125ee1bf
- * - git diff -U1 --cached 26a125ee1bf
- * - mv .git .gitted
- */
- cl_assert_equal_i(8, exp.files);
- cl_assert_equal_i(3, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
-
- cl_assert_equal_i(8, exp.hunks);
-
- cl_assert_equal_i(11, exp.lines);
- cl_assert_equal_i(3, exp.line_ctxt);
- cl_assert_equal_i(6, exp.line_adds);
- cl_assert_equal_i(2, exp.line_dels);
-
- git_diff_list_free(diff);
- diff = NULL;
- memset(&exp, 0, sizeof(exp));
-
- cl_git_pass(git_diff_tree_to_index(&diff, g_repo, b, NULL, &opts));
-
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- /* to generate these values:
- * - cd to tests/resources/status,
- * - mv .gitted .git
- * - git diff --name-status --cached 0017bd4ab1ec3
- * - git diff -U1 --cached 0017bd4ab1ec3
- * - mv .git .gitted
- */
- cl_assert_equal_i(12, exp.files);
- cl_assert_equal_i(7, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
-
- cl_assert_equal_i(12, exp.hunks);
-
- cl_assert_equal_i(16, exp.lines);
- cl_assert_equal_i(3, exp.line_ctxt);
- cl_assert_equal_i(11, exp.line_adds);
- cl_assert_equal_i(2, exp.line_dels);
-
- git_diff_list_free(diff);
- diff = NULL;
-
- git_tree_free(a);
- git_tree_free(b);
-}
-
-static int diff_stop_after_2_files(
- const git_diff_delta *delta,
- float progress,
- void *payload)
-{
- diff_expects *e = payload;
-
- GIT_UNUSED(progress);
- GIT_UNUSED(delta);
-
- e->files++;
-
- return (e->files == 2);
-}
-
-void test_diff_index__1(void)
-{
- /* grabbed a couple of commit oids from the history of the attr repo */
- const char *a_commit = "26a125ee1bf"; /* the current HEAD */
- const char *b_commit = "0017bd4ab1ec3"; /* the start */
- git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
- git_tree *b = resolve_commit_oid_to_tree(g_repo, b_commit);
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_diff_list *diff = NULL;
- diff_expects exp;
-
- cl_assert(a);
- cl_assert(b);
-
- opts.context_lines = 1;
- opts.interhunk_lines = 1;
-
- memset(&exp, 0, sizeof(exp));
-
- cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
-
- cl_assert_equal_i(
- GIT_EUSER,
- git_diff_foreach(diff, diff_stop_after_2_files, NULL, NULL, &exp)
- );
-
- cl_assert_equal_i(2, exp.files);
-
- git_diff_list_free(diff);
- diff = NULL;
-
- git_tree_free(a);
- git_tree_free(b);
-}
-
-void test_diff_index__checks_options_version(void)
-{
- const char *a_commit = "26a125ee1bf";
- git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_diff_list *diff = NULL;
- const git_error *err;
-
- opts.version = 0;
- cl_git_fail(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
- err = giterr_last();
- cl_assert_equal_i(GITERR_INVALID, err->klass);
- cl_assert_equal_p(diff, NULL);
-
- giterr_clear();
- opts.version = 1024;
- cl_git_fail(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
- err = giterr_last();
- cl_assert_equal_i(GITERR_INVALID, err->klass);
- cl_assert_equal_p(diff, NULL);
-
- git_tree_free(a);
-}
-
diff --git a/tests-clar/diff/notify.c b/tests-clar/diff/notify.c
deleted file mode 100644
index 433b4a9c1..000000000
--- a/tests-clar/diff/notify.c
+++ /dev/null
@@ -1,228 +0,0 @@
-#include "clar_libgit2.h"
-#include "diff_helpers.h"
-
-static git_repository *g_repo = NULL;
-
-void test_diff_notify__initialize(void)
-{
-}
-
-void test_diff_notify__cleanup(void)
-{
- cl_git_sandbox_cleanup();
-}
-
-static int assert_called_notifications(
- const git_diff_list *diff_so_far,
- const git_diff_delta *delta_to_add,
- const char *matched_pathspec,
- void *payload)
-{
- bool found = false;
- notify_expected *exp = (notify_expected*)payload;
- notify_expected *e;;
-
- GIT_UNUSED(diff_so_far);
-
- for (e = exp; e->path != NULL; e++) {
- if (strcmp(e->path, delta_to_add->new_file.path))
- continue;
-
- cl_assert_equal_s(e->matched_pathspec, matched_pathspec);
-
- found = true;
- break;
- }
-
- cl_assert(found);
- return 0;
-}
-
-static void test_notify(
- char **searched_pathspecs,
- int pathspecs_count,
- notify_expected *expected_matched_pathspecs,
- int expected_diffed_files_count)
-{
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_diff_list *diff = NULL;
- diff_expects exp;
-
- g_repo = cl_git_sandbox_init("status");
-
- opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
- opts.notify_cb = assert_called_notifications;
- opts.pathspec.strings = searched_pathspecs;
- opts.pathspec.count = pathspecs_count;
-
- opts.notify_payload = expected_matched_pathspecs;
- memset(&exp, 0, sizeof(exp));
-
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
- cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
-
- cl_assert_equal_i(expected_diffed_files_count, exp.files);
-
- git_diff_list_free(diff);
-}
-
-void test_diff_notify__notify_single_pathspec(void)
-{
- char *searched_pathspecs[] = {
- "*_deleted",
- };
- notify_expected expected_matched_pathspecs[] = {
- { "file_deleted", "*_deleted" },
- { "staged_changes_file_deleted", "*_deleted" },
- { NULL, NULL }
- };
-
- test_notify(searched_pathspecs, 1, expected_matched_pathspecs, 2);
-}
-
-void test_diff_notify__notify_multiple_pathspec(void)
-{
- char *searched_pathspecs[] = {
- "staged_changes_cant_find_me",
- "subdir/modified_cant_find_me",
- "subdir/*",
- "staged*"
- };
- notify_expected expected_matched_pathspecs[] = {
- { "staged_changes_file_deleted", "staged*" },
- { "staged_changes_modified_file", "staged*" },
- { "staged_delete_modified_file", "staged*" },
- { "staged_new_file_deleted_file", "staged*" },
- { "staged_new_file_modified_file", "staged*" },
- { "subdir/deleted_file", "subdir/*" },
- { "subdir/modified_file", "subdir/*" },
- { "subdir/new_file", "subdir/*" },
- { NULL, NULL }
- };
-
- test_notify(searched_pathspecs, 4, expected_matched_pathspecs, 8);
-}
-
-void test_diff_notify__notify_catchall_with_empty_pathspecs(void)
-{
- char *searched_pathspecs[] = {
- "",
- ""
- };
- notify_expected expected_matched_pathspecs[] = {
- { "file_deleted", NULL },
- { "ignored_file", NULL },
- { "modified_file", NULL },
- { "new_file", NULL },
- { "\xe8\xbf\x99", NULL },
- { "staged_changes_file_deleted", NULL },
- { "staged_changes_modified_file", NULL },
- { "staged_delete_modified_file", NULL },
- { "staged_new_file_deleted_file", NULL },
- { "staged_new_file_modified_file", NULL },
- { "subdir/deleted_file", NULL },
- { "subdir/modified_file", NULL },
- { "subdir/new_file", NULL },
- { NULL, NULL }
- };
-
- test_notify(searched_pathspecs, 1, expected_matched_pathspecs, 13);
-}
-
-void test_diff_notify__notify_catchall(void)
-{
- char *searched_pathspecs[] = {
- "*",
- };
- notify_expected expected_matched_pathspecs[] = {
- { "file_deleted", "*" },
- { "ignored_file", "*" },
- { "modified_file", "*" },
- { "new_file", "*" },
- { "\xe8\xbf\x99", "*" },
- { "staged_changes_file_deleted", "*" },
- { "staged_changes_modified_file", "*" },
- { "staged_delete_modified_file", "*" },
- { "staged_new_file_deleted_file", "*" },
- { "staged_new_file_modified_file", "*" },
- { "subdir/deleted_file", "*" },
- { "subdir/modified_file", "*" },
- { "subdir/new_file", "*" },
- { NULL, NULL }
- };
-
- test_notify(searched_pathspecs, 1, expected_matched_pathspecs, 13);
-}
-
-static int abort_diff(
- const git_diff_list *diff_so_far,
- const git_diff_delta *delta_to_add,
- const char *matched_pathspec,
- void *payload)
-{
- GIT_UNUSED(diff_so_far);
- GIT_UNUSED(delta_to_add);
- GIT_UNUSED(matched_pathspec);
- GIT_UNUSED(payload);
-
- return -42;
-}
-
-void test_diff_notify__notify_cb_can_abort_diff(void)
-{
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_diff_list *diff = NULL;
- char *pathspec = NULL;
-
- g_repo = cl_git_sandbox_init("status");
-
- opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
- opts.notify_cb = abort_diff;
- opts.pathspec.strings = &pathspec;
- opts.pathspec.count = 1;
-
- pathspec = "file_deleted";
- cl_git_fail(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
-
- pathspec = "staged_changes_modified_file";
- cl_git_fail(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
-}
-
-static int filter_all(
- const git_diff_list *diff_so_far,
- const git_diff_delta *delta_to_add,
- const char *matched_pathspec,
- void *payload)
-{
- GIT_UNUSED(diff_so_far);
- GIT_UNUSED(delta_to_add);
- GIT_UNUSED(matched_pathspec);
- GIT_UNUSED(payload);
-
- return 42;
-}
-
-void test_diff_notify__notify_cb_can_be_used_as_filtering_function(void)
-{
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_diff_list *diff = NULL;
- char *pathspec = NULL;
- diff_expects exp;
-
- g_repo = cl_git_sandbox_init("status");
-
- opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
- opts.notify_cb = filter_all;
- opts.pathspec.strings = &pathspec;
- opts.pathspec.count = 1;
-
- pathspec = "*_deleted";
- memset(&exp, 0, sizeof(exp));
-
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
- cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
-
- cl_assert_equal_i(0, exp.files);
-
- git_diff_list_free(diff);
-}
diff --git a/tests-clar/diff/patch.c b/tests-clar/diff/patch.c
deleted file mode 100644
index 3f14a0de7..000000000
--- a/tests-clar/diff/patch.c
+++ /dev/null
@@ -1,559 +0,0 @@
-#include "clar_libgit2.h"
-#include "git2/sys/repository.h"
-
-#include "diff_helpers.h"
-#include "repository.h"
-#include "buf_text.h"
-
-static git_repository *g_repo = NULL;
-
-void test_diff_patch__initialize(void)
-{
-}
-
-void test_diff_patch__cleanup(void)
-{
- cl_git_sandbox_cleanup();
-}
-
-#define EXPECTED_HEADER "diff --git a/subdir.txt b/subdir.txt\n" \
- "deleted file mode 100644\n" \
- "index e8ee89e..0000000\n" \
- "--- a/subdir.txt\n" \
- "+++ /dev/null\n"
-
-#define EXPECTED_HUNK "@@ -1,2 +0,0 @@\n"
-
-static int check_removal_cb(
- const git_diff_delta *delta,
- const git_diff_range *range,
- char line_origin,
- const char *formatted_output,
- size_t output_len,
- void *payload)
-{
- GIT_UNUSED(payload);
- GIT_UNUSED(output_len);
-
- switch (line_origin) {
- case GIT_DIFF_LINE_FILE_HDR:
- cl_assert_equal_s(EXPECTED_HEADER, formatted_output);
- cl_assert(range == NULL);
- goto check_delta;
-
- case GIT_DIFF_LINE_HUNK_HDR:
- cl_assert_equal_s(EXPECTED_HUNK, formatted_output);
- /* Fall through */
-
- case GIT_DIFF_LINE_CONTEXT:
- case GIT_DIFF_LINE_DELETION:
- goto check_range;
-
- default:
- /* unexpected code path */
- return -1;
- }
-
-check_range:
- cl_assert(range != NULL);
- cl_assert_equal_i(1, range->old_start);
- cl_assert_equal_i(2, range->old_lines);
- cl_assert_equal_i(0, range->new_start);
- cl_assert_equal_i(0, range->new_lines);
-
-check_delta:
- cl_assert_equal_s("subdir.txt", delta->old_file.path);
- cl_assert_equal_s("subdir.txt", delta->new_file.path);
- cl_assert_equal_i(GIT_DELTA_DELETED, delta->status);
-
- return 0;
-}
-
-void test_diff_patch__can_properly_display_the_removal_of_a_file(void)
-{
- /*
- * $ git diff 26a125e..735b6a2
- * diff --git a/subdir.txt b/subdir.txt
- * deleted file mode 100644
- * index e8ee89e..0000000
- * --- a/subdir.txt
- * +++ /dev/null
- * @@ -1,2 +0,0 @@
- * -Is it a bird?
- * -Is it a plane?
- */
-
- const char *one_sha = "26a125e";
- const char *another_sha = "735b6a2";
- git_tree *one, *another;
- git_diff_list *diff;
-
- g_repo = cl_git_sandbox_init("status");
-
- one = resolve_commit_oid_to_tree(g_repo, one_sha);
- another = resolve_commit_oid_to_tree(g_repo, another_sha);
-
- cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, one, another, NULL));
-
- cl_git_pass(git_diff_print_patch(diff, check_removal_cb, NULL));
-
- git_diff_list_free(diff);
-
- git_tree_free(another);
- git_tree_free(one);
-}
-
-void test_diff_patch__to_string(void)
-{
- const char *one_sha = "26a125e";
- const char *another_sha = "735b6a2";
- git_tree *one, *another;
- git_diff_list *diff;
- git_diff_patch *patch;
- char *text;
- const char *expected = "diff --git a/subdir.txt b/subdir.txt\ndeleted file mode 100644\nindex e8ee89e..0000000\n--- a/subdir.txt\n+++ /dev/null\n@@ -1,2 +0,0 @@\n-Is it a bird?\n-Is it a plane?\n";
-
- g_repo = cl_git_sandbox_init("status");
-
- one = resolve_commit_oid_to_tree(g_repo, one_sha);
- another = resolve_commit_oid_to_tree(g_repo, another_sha);
-
- cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, one, another, NULL));
-
- cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
-
- cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0));
-
- cl_git_pass(git_diff_patch_to_str(&text, patch));
-
- cl_assert_equal_s(expected, text);
-
- git__free(text);
- git_diff_patch_free(patch);
- git_diff_list_free(diff);
- git_tree_free(another);
- git_tree_free(one);
-}
-
-void test_diff_patch__config_options(void)
-{
- const char *one_sha = "26a125e"; /* current HEAD */
- git_tree *one;
- git_config *cfg;
- git_diff_list *diff;
- git_diff_patch *patch;
- char *text;
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- char *onefile = "staged_changes_modified_file";
- const char *expected1 = "diff --git c/staged_changes_modified_file i/staged_changes_modified_file\nindex 70bd944..906ee77 100644\n--- c/staged_changes_modified_file\n+++ i/staged_changes_modified_file\n@@ -1 +1,2 @@\n staged_changes_modified_file\n+staged_changes_modified_file\n";
- const char *expected2 = "diff --git i/staged_changes_modified_file w/staged_changes_modified_file\nindex 906ee77..011c344 100644\n--- i/staged_changes_modified_file\n+++ w/staged_changes_modified_file\n@@ -1,2 +1,3 @@\n staged_changes_modified_file\n staged_changes_modified_file\n+staged_changes_modified_file\n";
- const char *expected3 = "diff --git staged_changes_modified_file staged_changes_modified_file\nindex 906ee77..011c344 100644\n--- staged_changes_modified_file\n+++ staged_changes_modified_file\n@@ -1,2 +1,3 @@\n staged_changes_modified_file\n staged_changes_modified_file\n+staged_changes_modified_file\n";
- const char *expected4 = "diff --git staged_changes_modified_file staged_changes_modified_file\nindex 70bd9443ada0..906ee7711f4f 100644\n--- staged_changes_modified_file\n+++ staged_changes_modified_file\n@@ -1 +1,2 @@\n staged_changes_modified_file\n+staged_changes_modified_file\n";
-
- g_repo = cl_git_sandbox_init("status");
- cl_git_pass(git_repository_config(&cfg, g_repo));
- one = resolve_commit_oid_to_tree(g_repo, one_sha);
- opts.pathspec.count = 1;
- opts.pathspec.strings = &onefile;
-
-
- cl_git_pass(git_config_set_string(cfg, "diff.mnemonicprefix", "true"));
-
- cl_git_pass(git_diff_tree_to_index(&diff, g_repo, one, NULL, &opts));
-
- cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
- cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0));
- cl_git_pass(git_diff_patch_to_str(&text, patch));
- cl_assert_equal_s(expected1, text);
-
- git__free(text);
- git_diff_patch_free(patch);
- git_diff_list_free(diff);
-
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
-
- cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
- cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0));
- cl_git_pass(git_diff_patch_to_str(&text, patch));
- cl_assert_equal_s(expected2, text);
-
- git__free(text);
- git_diff_patch_free(patch);
- git_diff_list_free(diff);
-
-
- cl_git_pass(git_config_set_string(cfg, "diff.noprefix", "true"));
-
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
-
- cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
- cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0));
- cl_git_pass(git_diff_patch_to_str(&text, patch));
- cl_assert_equal_s(expected3, text);
-
- git__free(text);
- git_diff_patch_free(patch);
- git_diff_list_free(diff);
-
-
- cl_git_pass(git_config_set_int32(cfg, "core.abbrev", 12));
-
- cl_git_pass(git_diff_tree_to_index(&diff, g_repo, one, NULL, &opts));
-
- cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
- cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0));
- cl_git_pass(git_diff_patch_to_str(&text, patch));
- cl_assert_equal_s(expected4, text);
-
- git__free(text);
- git_diff_patch_free(patch);
- git_diff_list_free(diff);
-
- git_tree_free(one);
- git_config_free(cfg);
-}
-
-void test_diff_patch__hunks_have_correct_line_numbers(void)
-{
- git_config *cfg;
- git_tree *head;
- git_diff_options opt = GIT_DIFF_OPTIONS_INIT;
- git_diff_list *diff;
- git_diff_patch *patch;
- const git_diff_delta *delta;
- const git_diff_range *range;
- const char *hdr, *text;
- size_t hdrlen, hunklen, textlen;
- char origin;
- int oldno, newno;
- git_buf old_content = GIT_BUF_INIT, actual = GIT_BUF_INIT;
- const char *new_content = "The Song of Seven Cities\n------------------------\n\nI WAS Lord of Cities very sumptuously builded.\nSeven roaring Cities paid me tribute from afar.\nIvory their outposts were--the guardrooms of them gilded,\nAnd garrisoned with Amazons invincible in war.\n\nThis is some new text;\nNot as good as the old text;\nBut here it is.\n\nSo they warred and trafficked only yesterday, my Cities.\nTo-day there is no mark or mound of where my Cities stood.\nFor the River rose at midnight and it washed away my Cities.\nThey are evened with Atlantis and the towns before the Flood.\n\nRain on rain-gorged channels raised the water-levels round them,\nFreshet backed on freshet swelled and swept their world from sight,\nTill the emboldened floods linked arms and, flashing forward, drowned them--\nDrowned my Seven Cities and their peoples in one night!\n\nLow among the alders lie their derelict foundations,\nThe beams wherein they trusted and the plinths whereon they built--\nMy rulers and their treasure and their unborn populations,\nDead, destroyed, aborted, and defiled with mud and silt!\n\nAnother replacement;\nBreaking up the poem;\nGenerating some hunks.\n\nTo the sound of trumpets shall their seed restore my Cities\nWealthy and well-weaponed, that once more may I behold\nAll the world go softly when it walks before my Cities,\nAnd the horses and the chariots fleeing from them as of old!\n\n -- Rudyard Kipling\n";
-
- g_repo = cl_git_sandbox_init("renames");
-
- cl_git_pass(git_config_new(&cfg));
- git_repository_set_config(g_repo, cfg);
-
- cl_git_pass(
- git_futils_readbuffer(&old_content, "renames/songof7cities.txt"));
-
- cl_git_rewritefile("renames/songof7cities.txt", new_content);
-
- cl_git_pass(git_repository_head_tree(&head, g_repo));
-
- cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, head, &opt));
-
- cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
-
- cl_git_pass(git_diff_get_patch(&patch, &delta, diff, 0));
-
- cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
- cl_assert_equal_i(2, (int)git_diff_patch_num_hunks(patch));
-
- /* check hunk 0 */
-
- cl_git_pass(
- git_diff_patch_get_hunk(&range, &hdr, &hdrlen, &hunklen, patch, 0));
-
- cl_assert_equal_i(18, (int)hunklen);
-
- cl_assert_equal_i(6, (int)range->old_start);
- cl_assert_equal_i(15, (int)range->old_lines);
- cl_assert_equal_i(6, (int)range->new_start);
- cl_assert_equal_i(9, (int)range->new_lines);
-
- cl_assert_equal_i(18, (int)git_diff_patch_num_lines_in_hunk(patch, 0));
-
- cl_git_pass(git_diff_patch_get_line_in_hunk(
- &origin, &text, &textlen, &oldno, &newno, patch, 0, 0));
- cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)origin);
- cl_git_pass(git_buf_set(&actual, text, textlen));
- cl_assert_equal_s("Ivory their outposts were--the guardrooms of them gilded,\n", actual.ptr);
- cl_assert_equal_i(6, oldno);
- cl_assert_equal_i(6, newno);
-
- cl_git_pass(git_diff_patch_get_line_in_hunk(
- &origin, &text, &textlen, &oldno, &newno, patch, 0, 3));
- cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)origin);
- cl_git_pass(git_buf_set(&actual, text, textlen));
- cl_assert_equal_s("All the world went softly when it walked before my Cities--\n", actual.ptr);
- cl_assert_equal_i(9, oldno);
- cl_assert_equal_i(-1, newno);
-
- cl_git_pass(git_diff_patch_get_line_in_hunk(
- &origin, &text, &textlen, &oldno, &newno, patch, 0, 12));
- cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)origin);
- cl_git_pass(git_buf_set(&actual, text, textlen));
- cl_assert_equal_s("This is some new text;\n", actual.ptr);
- cl_assert_equal_i(-1, oldno);
- cl_assert_equal_i(9, newno);
-
- /* check hunk 1 */
-
- cl_git_pass(
- git_diff_patch_get_hunk(&range, &hdr, &hdrlen, &hunklen, patch, 1));
-
- cl_assert_equal_i(18, (int)hunklen);
-
- cl_assert_equal_i(31, (int)range->old_start);
- cl_assert_equal_i(15, (int)range->old_lines);
- cl_assert_equal_i(25, (int)range->new_start);
- cl_assert_equal_i(9, (int)range->new_lines);
-
- cl_assert_equal_i(18, (int)git_diff_patch_num_lines_in_hunk(patch, 1));
-
- cl_git_pass(git_diff_patch_get_line_in_hunk(
- &origin, &text, &textlen, &oldno, &newno, patch, 1, 0));
- cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)origin);
- cl_git_pass(git_buf_set(&actual, text, textlen));
- cl_assert_equal_s("My rulers and their treasure and their unborn populations,\n", actual.ptr);
- cl_assert_equal_i(31, oldno);
- cl_assert_equal_i(25, newno);
-
- cl_git_pass(git_diff_patch_get_line_in_hunk(
- &origin, &text, &textlen, &oldno, &newno, patch, 1, 3));
- cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)origin);
- cl_git_pass(git_buf_set(&actual, text, textlen));
- cl_assert_equal_s("The Daughters of the Palace whom they cherished in my Cities,\n", actual.ptr);
- cl_assert_equal_i(34, oldno);
- cl_assert_equal_i(-1, newno);
-
- cl_git_pass(git_diff_patch_get_line_in_hunk(
- &origin, &text, &textlen, &oldno, &newno, patch, 1, 12));
- cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)origin);
- cl_git_pass(git_buf_set(&actual, text, textlen));
- cl_assert_equal_s("Another replacement;\n", actual.ptr);
- cl_assert_equal_i(-1, oldno);
- cl_assert_equal_i(28, newno);
-
- git_diff_patch_free(patch);
- git_diff_list_free(diff);
-
- /* Let's check line numbers when there is no newline */
-
- git_buf_rtrim(&old_content);
- cl_git_rewritefile("renames/songof7cities.txt", old_content.ptr);
-
- cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, head, &opt));
-
- cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
-
- cl_git_pass(git_diff_get_patch(&patch, &delta, diff, 0));
-
- cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
- cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(patch));
-
- /* check hunk 0 */
-
- cl_git_pass(
- git_diff_patch_get_hunk(&range, &hdr, &hdrlen, &hunklen, patch, 0));
-
- cl_assert_equal_i(6, (int)hunklen);
-
- cl_assert_equal_i(46, (int)range->old_start);
- cl_assert_equal_i(4, (int)range->old_lines);
- cl_assert_equal_i(46, (int)range->new_start);
- cl_assert_equal_i(4, (int)range->new_lines);
-
- cl_assert_equal_i(6, (int)git_diff_patch_num_lines_in_hunk(patch, 0));
-
- cl_git_pass(git_diff_patch_get_line_in_hunk(
- &origin, &text, &textlen, &oldno, &newno, patch, 0, 1));
- cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)origin);
- cl_git_pass(git_buf_set(&actual, text, textlen));
- cl_assert_equal_s("And the horses and the chariots fleeing from them as of old!\n", actual.ptr);
- cl_assert_equal_i(47, oldno);
- cl_assert_equal_i(47, newno);
-
- cl_git_pass(git_diff_patch_get_line_in_hunk(
- &origin, &text, &textlen, &oldno, &newno, patch, 0, 2));
- cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)origin);
- cl_git_pass(git_buf_set(&actual, text, textlen));
- cl_assert_equal_s("\n", actual.ptr);
- cl_assert_equal_i(48, oldno);
- cl_assert_equal_i(48, newno);
-
- cl_git_pass(git_diff_patch_get_line_in_hunk(
- &origin, &text, &textlen, &oldno, &newno, patch, 0, 3));
- cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)origin);
- cl_git_pass(git_buf_set(&actual, text, textlen));
- cl_assert_equal_s(" -- Rudyard Kipling\n", actual.ptr);
- cl_assert_equal_i(49, oldno);
- cl_assert_equal_i(-1, newno);
-
- cl_git_pass(git_diff_patch_get_line_in_hunk(
- &origin, &text, &textlen, &oldno, &newno, patch, 0, 4));
- cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)origin);
- cl_git_pass(git_buf_set(&actual, text, textlen));
- cl_assert_equal_s(" -- Rudyard Kipling", actual.ptr);
- cl_assert_equal_i(-1, oldno);
- cl_assert_equal_i(49, newno);
-
- cl_git_pass(git_diff_patch_get_line_in_hunk(
- &origin, &text, &textlen, &oldno, &newno, patch, 0, 5));
- cl_assert_equal_i(GIT_DIFF_LINE_DEL_EOFNL, (int)origin);
- cl_git_pass(git_buf_set(&actual, text, textlen));
- cl_assert_equal_s("\n\\ No newline at end of file\n", actual.ptr);
- cl_assert_equal_i(-1, oldno);
- cl_assert_equal_i(49, newno);
-
- git_diff_patch_free(patch);
- git_diff_list_free(diff);
-
- git_buf_free(&actual);
- git_buf_free(&old_content);
- git_tree_free(head);
- git_config_free(cfg);
-}
-
-static void check_single_patch_stats(
- git_repository *repo, size_t hunks,
- size_t adds, size_t dels, size_t ctxt,
- const char *expected)
-{
- git_diff_list *diff;
- git_diff_patch *patch;
- const git_diff_delta *delta;
- size_t actual_ctxt, actual_adds, actual_dels;
-
- cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
-
- cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
-
- cl_git_pass(git_diff_get_patch(&patch, &delta, diff, 0));
- cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
-
- cl_assert_equal_i((int)hunks, (int)git_diff_patch_num_hunks(patch));
-
- cl_git_pass( git_diff_patch_line_stats(
- &actual_ctxt, &actual_adds, &actual_dels, patch) );
-
- cl_assert_equal_sz(ctxt, actual_ctxt);
- cl_assert_equal_sz(adds, actual_adds);
- cl_assert_equal_sz(dels, actual_dels);
-
- if (expected != NULL) {
- char *text;
- cl_git_pass(git_diff_patch_to_str(&text, patch));
- cl_assert_equal_s(expected, text);
- git__free(text);
- }
-
- /* walk lines in hunk with basic sanity checks */
- for (; hunks > 0; --hunks) {
- size_t i, max_i;
- int lastoldno = -1, oldno, lastnewno = -1, newno;
- char origin;
-
- max_i = git_diff_patch_num_lines_in_hunk(patch, hunks - 1);
-
- for (i = 0; i < max_i; ++i) {
- int expected = 1;
-
- cl_git_pass(git_diff_patch_get_line_in_hunk(
- &origin, NULL, NULL, &oldno, &newno, patch, hunks - 1, i));
-
- if (origin == GIT_DIFF_LINE_ADD_EOFNL ||
- origin == GIT_DIFF_LINE_DEL_EOFNL ||
- origin == GIT_DIFF_LINE_CONTEXT_EOFNL)
- expected = 0;
-
- if (oldno >= 0) {
- if (lastoldno >= 0)
- cl_assert_equal_i(expected, oldno - lastoldno);
- lastoldno = oldno;
- }
- if (newno >= 0) {
- if (lastnewno >= 0)
- cl_assert_equal_i(expected, newno - lastnewno);
- lastnewno = newno;
- }
- }
- }
-
- git_diff_patch_free(patch);
- git_diff_list_free(diff);
-}
-
-void test_diff_patch__line_counts_with_eofnl(void)
-{
- git_config *cfg;
- git_buf content = GIT_BUF_INIT;
- const char *end;
- git_index *index;
-
- g_repo = cl_git_sandbox_init("renames");
-
- cl_git_pass(git_config_new(&cfg));
- git_repository_set_config(g_repo, cfg);
-
- cl_git_pass(git_futils_readbuffer(&content, "renames/songof7cities.txt"));
-
- /* remove first line */
-
- end = git_buf_cstr(&content) + git_buf_find(&content, '\n') + 1;
- git_buf_consume(&content, end);
- cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
-
- check_single_patch_stats(g_repo, 1, 0, 1, 3, NULL);
-
- /* remove trailing whitespace */
-
- git_buf_rtrim(&content);
- cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
-
- check_single_patch_stats(g_repo, 2, 1, 2, 6, NULL);
-
- /* add trailing whitespace */
-
- cl_git_pass(git_repository_index(&index, g_repo));
- cl_git_pass(git_index_add_bypath(index, "songof7cities.txt"));
- cl_git_pass(git_index_write(index));
- git_index_free(index);
-
- cl_git_pass(git_buf_putc(&content, '\n'));
- cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
-
- check_single_patch_stats(g_repo, 1, 1, 1, 3, NULL);
-
- /* no trailing whitespace as context line */
-
- {
- /* walk back a couple lines, make space and insert char */
- char *scan = content.ptr + content.size;
- int i;
-
- for (i = 0; i < 5; ++i) {
- for (--scan; scan > content.ptr && *scan != '\n'; --scan)
- /* seek to prev \n */;
- }
- cl_assert(scan > content.ptr);
-
- /* overwrite trailing \n with right-shifted content */
- memmove(scan + 1, scan, content.size - (scan - content.ptr) - 1);
- /* insert '#' char into space we created */
- scan[1] = '#';
- }
- cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
-
- check_single_patch_stats(
- g_repo, 1, 1, 1, 6,
- /* below is pasted output of 'git diff' with fn context removed */
- "diff --git a/songof7cities.txt b/songof7cities.txt\n"
- "index 378a7d9..3d0154e 100644\n"
- "--- a/songof7cities.txt\n"
- "+++ b/songof7cities.txt\n"
- "@@ -42,7 +42,7 @@ With peoples undefeated of the dark, enduring blood.\n"
- " \n"
- " To the sound of trumpets shall their seed restore my Cities\n"
- " Wealthy and well-weaponed, that once more may I behold\n"
- "-All the world go softly when it walks before my Cities,\n"
- "+#All the world go softly when it walks before my Cities,\n"
- " And the horses and the chariots fleeing from them as of old!\n"
- " \n"
- " -- Rudyard Kipling\n"
- "\\ No newline at end of file\n");
-
- git_buf_free(&content);
- git_config_free(cfg);
-}
diff --git a/tests-clar/diff/rename.c b/tests-clar/diff/rename.c
deleted file mode 100644
index 2b1873bd5..000000000
--- a/tests-clar/diff/rename.c
+++ /dev/null
@@ -1,1124 +0,0 @@
-#include "clar_libgit2.h"
-#include "diff_helpers.h"
-#include "buf_text.h"
-
-static git_repository *g_repo = NULL;
-
-void test_diff_rename__initialize(void)
-{
- g_repo = cl_git_sandbox_init("renames");
-}
-
-void test_diff_rename__cleanup(void)
-{
- cl_git_sandbox_cleanup();
-}
-
-/*
- * Renames repo has:
- *
- * commit 31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 -
- * serving.txt (25 lines)
- * sevencities.txt (50 lines)
- * commit 2bc7f351d20b53f1c72c16c4b036e491c478c49a -
- * serving.txt -> sixserving.txt (rename, no change, 100% match)
- * sevencities.txt -> sevencities.txt (no change)
- * sevencities.txt -> songofseven.txt (copy, no change, 100% match)
- * commit 1c068dee5790ef1580cfc4cd670915b48d790084
- * songofseven.txt -> songofseven.txt (major rewrite, <20% match - split)
- * sixserving.txt -> sixserving.txt (indentation change)
- * sixserving.txt -> ikeepsix.txt (copy, add title, >80% match)
- * sevencities.txt (no change)
- * commit 19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13
- * songofseven.txt -> untimely.txt (rename, convert to crlf)
- * ikeepsix.txt -> ikeepsix.txt (reorder sections in file)
- * sixserving.txt -> sixserving.txt (whitespace change - not just indent)
- * sevencities.txt -> songof7cities.txt (rename, small text changes)
- */
-
-void test_diff_rename__match_oid(void)
-{
- const char *old_sha = "31e47d8c1fa36d7f8d537b96158e3f024de0a9f2";
- const char *new_sha = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
- git_tree *old_tree, *new_tree;
- git_diff_list *diff;
- git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
- git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
- diff_expects exp;
-
- old_tree = resolve_commit_oid_to_tree(g_repo, old_sha);
- new_tree = resolve_commit_oid_to_tree(g_repo, new_sha);
-
- /* Must pass GIT_DIFF_INCLUDE_UNMODIFIED if you expect to emulate
- * --find-copies-harder during rename transformion...
- */
- diffopts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
-
- cl_git_pass(git_diff_tree_to_tree(
- &diff, g_repo, old_tree, new_tree, &diffopts));
-
- /* git diff --no-renames \
- * 31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 \
- * 2bc7f351d20b53f1c72c16c4b036e491c478c49a
- */
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(4, exp.files);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
-
- /* git diff 31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 \
- * 2bc7f351d20b53f1c72c16c4b036e491c478c49a
- * don't use NULL opts to avoid config `diff.renames` contamination
- */
- opts.flags = GIT_DIFF_FIND_RENAMES;
- cl_git_pass(git_diff_find_similar(diff, &opts));
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(3, exp.files);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
-
- git_diff_list_free(diff);
-
- cl_git_pass(git_diff_tree_to_tree(
- &diff, g_repo, old_tree, new_tree, &diffopts));
-
- /* git diff --find-copies-harder \
- * 31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 \
- * 2bc7f351d20b53f1c72c16c4b036e491c478c49a
- */
- opts.flags = GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED;
- cl_git_pass(git_diff_find_similar(diff, &opts));
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(3, exp.files);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_COPIED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
-
- git_diff_list_free(diff);
-
- git_tree_free(old_tree);
- git_tree_free(new_tree);
-}
-
-void test_diff_rename__checks_options_version(void)
-{
- const char *old_sha = "31e47d8c1fa36d7f8d537b96158e3f024de0a9f2";
- const char *new_sha = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
- git_tree *old_tree, *new_tree;
- git_diff_list *diff;
- git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
- git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
- const git_error *err;
-
- old_tree = resolve_commit_oid_to_tree(g_repo, old_sha);
- new_tree = resolve_commit_oid_to_tree(g_repo, new_sha);
- diffopts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
- cl_git_pass(git_diff_tree_to_tree(
- &diff, g_repo, old_tree, new_tree, &diffopts));
-
- opts.version = 0;
- cl_git_fail(git_diff_find_similar(diff, &opts));
- err = giterr_last();
- cl_assert_equal_i(GITERR_INVALID, err->klass);
-
- giterr_clear();
- opts.version = 1024;
- cl_git_fail(git_diff_find_similar(diff, &opts));
- err = giterr_last();
- cl_assert_equal_i(GITERR_INVALID, err->klass);
-
- git_diff_list_free(diff);
- git_tree_free(old_tree);
- git_tree_free(new_tree);
-}
-
-void test_diff_rename__not_exact_match(void)
-{
- const char *sha0 = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
- const char *sha1 = "1c068dee5790ef1580cfc4cd670915b48d790084";
- const char *sha2 = "19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13";
- git_tree *old_tree, *new_tree;
- git_diff_list *diff;
- git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
- git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
- diff_expects exp;
-
- /* == Changes =====================================================
- * songofseven.txt -> songofseven.txt (major rewrite, <20% match - split)
- * sixserving.txt -> sixserving.txt (indentation change)
- * sixserving.txt -> ikeepsix.txt (copy, add title, >80% match)
- * sevencities.txt (no change)
- */
-
- old_tree = resolve_commit_oid_to_tree(g_repo, sha0);
- new_tree = resolve_commit_oid_to_tree(g_repo, sha1);
-
- /* Must pass GIT_DIFF_INCLUDE_UNMODIFIED if you expect to emulate
- * --find-copies-harder during rename transformion...
- */
- diffopts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
-
- cl_git_pass(git_diff_tree_to_tree(
- &diff, g_repo, old_tree, new_tree, &diffopts));
-
- /* git diff --no-renames \
- * 2bc7f351d20b53f1c72c16c4b036e491c478c49a \
- * 1c068dee5790ef1580cfc4cd670915b48d790084
- */
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(4, exp.files);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
-
- /* git diff -M 2bc7f351d20b53f1c72c16c4b036e491c478c49a \
- * 1c068dee5790ef1580cfc4cd670915b48d790084
- *
- * must not pass NULL for opts because it will pick up environment
- * values for "diff.renames" and test won't be consistent.
- */
- opts.flags = GIT_DIFF_FIND_RENAMES;
- cl_git_pass(git_diff_find_similar(diff, &opts));
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(4, exp.files);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
-
- git_diff_list_free(diff);
-
- /* git diff -M -C \
- * 2bc7f351d20b53f1c72c16c4b036e491c478c49a \
- * 1c068dee5790ef1580cfc4cd670915b48d790084
- */
- cl_git_pass(git_diff_tree_to_tree(
- &diff, g_repo, old_tree, new_tree, &diffopts));
-
- opts.flags = GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES;
- cl_git_pass(git_diff_find_similar(diff, &opts));
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(4, exp.files);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_COPIED]);
-
- git_diff_list_free(diff);
-
- /* git diff -M -C --find-copies-harder --break-rewrites \
- * 2bc7f351d20b53f1c72c16c4b036e491c478c49a \
- * 1c068dee5790ef1580cfc4cd670915b48d790084
- */
- cl_git_pass(git_diff_tree_to_tree(
- &diff, g_repo, old_tree, new_tree, &diffopts));
-
- opts.flags = GIT_DIFF_FIND_ALL;
- cl_git_pass(git_diff_find_similar(diff, &opts));
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(5, exp.files);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_COPIED]);
-
- git_diff_list_free(diff);
-
- /* == Changes =====================================================
- * songofseven.txt -> untimely.txt (rename, convert to crlf)
- * ikeepsix.txt -> ikeepsix.txt (reorder sections in file)
- * sixserving.txt -> sixserving.txt (whitespace - not just indent)
- * sevencities.txt -> songof7cities.txt (rename, small text changes)
- */
-
- git_tree_free(old_tree);
- old_tree = new_tree;
- new_tree = resolve_commit_oid_to_tree(g_repo, sha2);
-
- cl_git_pass(git_diff_tree_to_tree(
- &diff, g_repo, old_tree, new_tree, &diffopts));
-
- /* git diff --no-renames \
- * 1c068dee5790ef1580cfc4cd670915b48d790084 \
- * 19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13
- */
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(6, exp.files);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
- git_diff_list_free(diff);
-
- /* git diff -M -C \
- * 1c068dee5790ef1580cfc4cd670915b48d790084 \
- * 19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13
- */
- cl_git_pass(git_diff_tree_to_tree(
- &diff, g_repo, old_tree, new_tree, &diffopts));
-
- opts.flags = GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES;
- cl_git_pass(git_diff_find_similar(diff, &opts));
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(4, exp.files);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_RENAMED]);
-
- git_diff_list_free(diff);
-
- /* git diff -M -C --find-copies-harder --break-rewrites \
- * 1c068dee5790ef1580cfc4cd670915b48d790084 \
- * 19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13
- * with libgit2 default similarity comparison...
- */
- cl_git_pass(git_diff_tree_to_tree(
- &diff, g_repo, old_tree, new_tree, &diffopts));
-
- opts.flags = GIT_DIFF_FIND_ALL;
- cl_git_pass(git_diff_find_similar(diff, &opts));
-
- /* the default match algorithm is going to find the internal
- * whitespace differences in the lines of sixserving.txt to be
- * significant enough that this will decide to split it into
- * an ADD and a DELETE
- */
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(5, exp.files);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_RENAMED]);
-
- git_diff_list_free(diff);
-
- /* git diff -M -C --find-copies-harder --break-rewrites \
- * 1c068dee5790ef1580cfc4cd670915b48d790084 \
- * 19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13
- * with ignore_space whitespace comparision
- */
- cl_git_pass(git_diff_tree_to_tree(
- &diff, g_repo, old_tree, new_tree, &diffopts));
-
- opts.flags = GIT_DIFF_FIND_ALL | GIT_DIFF_FIND_IGNORE_WHITESPACE;
- cl_git_pass(git_diff_find_similar(diff, &opts));
-
- /* Ignoring whitespace, this should no longer split sixserver.txt */
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(4, exp.files);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_RENAMED]);
-
- git_diff_list_free(diff);
-
- git_tree_free(old_tree);
- git_tree_free(new_tree);
-}
-
-void test_diff_rename__handles_small_files(void)
-{
- const char *tree_sha = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
- git_index *index;
- git_tree *tree;
- git_diff_list *diff;
- git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
- git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
-
- cl_git_pass(git_repository_index(&index, g_repo));
-
- tree = resolve_commit_oid_to_tree(g_repo, tree_sha);
-
- cl_git_rewritefile("renames/songof7cities.txt", "single line\n");
- cl_git_pass(git_index_add_bypath(index, "songof7cities.txt"));
-
- cl_git_rewritefile("renames/untimely.txt", "untimely\n");
- cl_git_pass(git_index_add_bypath(index, "untimely.txt"));
-
- /* Tests that we can invoke find_similar on small files
- * and that the GIT_EBUFS (too small) error code is not
- * propagated to the caller.
- */
- cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
-
- opts.flags = GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES |
- GIT_DIFF_FIND_AND_BREAK_REWRITES;
- cl_git_pass(git_diff_find_similar(diff, &opts));
-
- git_diff_list_free(diff);
- git_tree_free(tree);
- git_index_free(index);
-}
-
-void test_diff_rename__working_directory_changes(void)
-{
- const char *sha0 = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
- const char *blobsha = "66311f5cfbe7836c27510a3ba2f43e282e2c8bba";
- git_oid id;
- git_tree *tree;
- git_blob *blob;
- git_diff_list *diff;
- git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
- git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
- diff_expects exp;
- git_buf old_content = GIT_BUF_INIT, content = GIT_BUF_INIT;;
-
- tree = resolve_commit_oid_to_tree(g_repo, sha0);
- diffopts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED | GIT_DIFF_INCLUDE_UNTRACKED;
-
- /*
- $ git cat-file -p 2bc7f351d20b53f1c72c16c4b036e491c478c49a^{tree}
-
- 100644 blob 66311f5cfbe7836c27510a3ba2f43e282e2c8bba sevencities.txt
- 100644 blob ad0a8e55a104ac54a8a29ed4b84b49e76837a113 sixserving.txt
- 100644 blob 66311f5cfbe7836c27510a3ba2f43e282e2c8bba songofseven.txt
-
- $ for f in *.txt; do
- echo `git hash-object -t blob $f` $f
- done
-
- eaf4a3e3bfe68585e90cada20736ace491cd100b ikeepsix.txt
- f90d4fc20ecddf21eebe6a37e9225d244339d2b5 sixserving.txt
- 4210ffd5c390b21dd5483375e75288dea9ede512 songof7cities.txt
- 9a69d960ae94b060f56c2a8702545e2bb1abb935 untimely.txt
- */
-
- cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &diffopts));
-
- /* git diff --no-renames 2bc7f351d20b53f1c72c16c4b036e491c478c49a */
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(6, exp.files);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
-
- /* git diff -M 2bc7f351d20b53f1c72c16c4b036e491c478c49a */
- opts.flags = GIT_DIFF_FIND_ALL;
- cl_git_pass(git_diff_find_similar(diff, &opts));
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(5, exp.files);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_RENAMED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
-
- git_diff_list_free(diff);
-
- /* rewrite files in the working directory with / without CRLF changes */
-
- cl_git_pass(
- git_futils_readbuffer(&old_content, "renames/songof7cities.txt"));
- cl_git_pass(
- git_buf_text_lf_to_crlf(&content, &old_content));
- cl_git_pass(
- git_futils_writebuffer(&content, "renames/songof7cities.txt", 0, 0));
-
- cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &diffopts));
-
- /* git diff -M 2bc7f351d20b53f1c72c16c4b036e491c478c49a */
- opts.flags = GIT_DIFF_FIND_ALL;
- cl_git_pass(git_diff_find_similar(diff, &opts));
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(5, exp.files);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_RENAMED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
-
- git_diff_list_free(diff);
-
- /* try a different whitespace option */
-
- cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &diffopts));
-
- opts.flags = GIT_DIFF_FIND_ALL | GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE;
- cl_git_pass(git_diff_find_similar(diff, &opts));
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(6, exp.files);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
-
- git_diff_list_free(diff);
-
- /* try a different matching option */
-
- cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &diffopts));
-
- opts.flags = GIT_DIFF_FIND_ALL | GIT_DIFF_FIND_EXACT_MATCH_ONLY;
- cl_git_pass(git_diff_find_similar(diff, &opts));
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(6, exp.files);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
-
- git_diff_list_free(diff);
-
- /* again with exact match blob */
-
- cl_git_pass(git_oid_fromstr(&id, blobsha));
- cl_git_pass(git_blob_lookup(&blob, g_repo, &id));
- cl_git_pass(git_buf_set(
- &content, git_blob_rawcontent(blob), git_blob_rawsize(blob)));
- cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
- git_blob_free(blob);
-
- cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &diffopts));
-
- opts.flags = GIT_DIFF_FIND_ALL | GIT_DIFF_FIND_EXACT_MATCH_ONLY;
- cl_git_pass(git_diff_find_similar(diff, &opts));
-
- /*
- fprintf(stderr, "\n\n");
- diff_print_raw(stderr, diff);
- */
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(5, exp.files);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
-
- git_diff_list_free(diff);
-
- git_tree_free(tree);
- git_buf_free(&content);
- git_buf_free(&old_content);
-}
-
-void test_diff_rename__patch(void)
-{
- const char *sha0 = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
- const char *sha1 = "1c068dee5790ef1580cfc4cd670915b48d790084";
- git_tree *old_tree, *new_tree;
- git_diff_list *diff;
- git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
- git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
- git_diff_patch *patch;
- const git_diff_delta *delta;
- char *text;
- const char *expected = "diff --git a/sixserving.txt b/ikeepsix.txt\nindex ad0a8e5..36020db 100644\n--- a/sixserving.txt\n+++ b/ikeepsix.txt\n@@ -1,3 +1,6 @@\n+I Keep Six Honest Serving-Men\n+=============================\n+\n I KEEP six honest serving-men\n (They taught me all I knew);\n Their names are What and Why and When\n@@ -21,4 +24,4 @@ She sends'em abroad on her own affairs,\n One million Hows, two million Wheres,\n And seven million Whys!\n \n- -- Rudyard Kipling\n+ -- Rudyard Kipling\n";
-
- old_tree = resolve_commit_oid_to_tree(g_repo, sha0);
- new_tree = resolve_commit_oid_to_tree(g_repo, sha1);
-
- diffopts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
- cl_git_pass(git_diff_tree_to_tree(
- &diff, g_repo, old_tree, new_tree, &diffopts));
-
- opts.flags = GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES;
- cl_git_pass(git_diff_find_similar(diff, &opts));
-
- /* == Changes =====================================================
- * sixserving.txt -> ikeepsix.txt (copy, add title, >80% match)
- * sevencities.txt (no change)
- * sixserving.txt -> sixserving.txt (indentation change)
- * songofseven.txt -> songofseven.txt (major rewrite, <20% match - split)
- */
-
- cl_assert_equal_i(4, (int)git_diff_num_deltas(diff));
-
- cl_git_pass(git_diff_get_patch(&patch, &delta, diff, 0));
- cl_assert_equal_i(GIT_DELTA_COPIED, (int)delta->status);
-
- cl_git_pass(git_diff_patch_to_str(&text, patch));
- cl_assert_equal_s(expected, text);
- git__free(text);
-
- git_diff_patch_free(patch);
-
- cl_git_pass(git_diff_get_patch(NULL, &delta, diff, 1));
- cl_assert_equal_i(GIT_DELTA_UNMODIFIED, (int)delta->status);
-
- cl_git_pass(git_diff_get_patch(NULL, &delta, diff, 2));
- cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
-
- cl_git_pass(git_diff_get_patch(NULL, &delta, diff, 3));
- cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
-
- git_diff_list_free(diff);
- git_tree_free(old_tree);
- git_tree_free(new_tree);
-}
-
-void test_diff_rename__file_exchange(void)
-{
- git_buf c1 = GIT_BUF_INIT, c2 = GIT_BUF_INIT;
- git_index *index;
- git_tree *tree;
- git_diff_list *diff;
- git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
- git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
- diff_expects exp;
-
- cl_git_pass(git_futils_readbuffer(&c1, "renames/untimely.txt"));
- cl_git_pass(git_futils_readbuffer(&c2, "renames/songof7cities.txt"));
- cl_git_pass(git_futils_writebuffer(&c1, "renames/songof7cities.txt", 0, 0));
- cl_git_pass(git_futils_writebuffer(&c2, "renames/untimely.txt", 0, 0));
-
- cl_git_pass(
- git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
-
- cl_git_pass(git_repository_index(&index, g_repo));
- cl_git_pass(git_index_read_tree(index, tree));
- cl_git_pass(git_index_add_bypath(index, "songof7cities.txt"));
- cl_git_pass(git_index_add_bypath(index, "untimely.txt"));
-
- cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
- cl_assert_equal_i(2, exp.files);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
-
- opts.flags = GIT_DIFF_FIND_ALL;
- cl_git_pass(git_diff_find_similar(diff, &opts));
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
- cl_assert_equal_i(2, exp.files);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_RENAMED]);
-
- git_diff_list_free(diff);
- git_tree_free(tree);
- git_index_free(index);
-
- git_buf_free(&c1);
- git_buf_free(&c2);
-}
-
-void test_diff_rename__file_exchange_three(void)
-{
- git_buf c1 = GIT_BUF_INIT, c2 = GIT_BUF_INIT, c3 = GIT_BUF_INIT;
- git_index *index;
- git_tree *tree;
- git_diff_list *diff;
- git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
- git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
- diff_expects exp;
-
- cl_git_pass(git_futils_readbuffer(&c1, "renames/untimely.txt"));
- cl_git_pass(git_futils_readbuffer(&c2, "renames/songof7cities.txt"));
- cl_git_pass(git_futils_readbuffer(&c3, "renames/ikeepsix.txt"));
-
- cl_git_pass(git_futils_writebuffer(&c1, "renames/ikeepsix.txt", 0, 0));
- cl_git_pass(git_futils_writebuffer(&c2, "renames/untimely.txt", 0, 0));
- cl_git_pass(git_futils_writebuffer(&c3, "renames/songof7cities.txt", 0, 0));
-
- cl_git_pass(
- git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
-
- cl_git_pass(git_repository_index(&index, g_repo));
- cl_git_pass(git_index_read_tree(index, tree));
- cl_git_pass(git_index_add_bypath(index, "songof7cities.txt"));
- cl_git_pass(git_index_add_bypath(index, "untimely.txt"));
- cl_git_pass(git_index_add_bypath(index, "ikeepsix.txt"));
-
- cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
- cl_assert_equal_i(3, exp.files);
- cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
-
- opts.flags = GIT_DIFF_FIND_ALL;
- cl_git_pass(git_diff_find_similar(diff, &opts));
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
- cl_assert_equal_i(3, exp.files);
- cl_assert_equal_i(3, exp.file_status[GIT_DELTA_RENAMED]);
-
- git_diff_list_free(diff);
- git_tree_free(tree);
- git_index_free(index);
-
- git_buf_free(&c1);
- git_buf_free(&c2);
- git_buf_free(&c3);
-}
-
-void test_diff_rename__file_partial_exchange(void)
-{
- git_buf c1 = GIT_BUF_INIT, c2 = GIT_BUF_INIT;
- git_index *index;
- git_tree *tree;
- git_diff_list *diff;
- git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
- git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
- diff_expects exp;
- int i;
-
- cl_git_pass(git_futils_readbuffer(&c1, "renames/untimely.txt"));
- cl_git_pass(git_futils_writebuffer(&c1, "renames/songof7cities.txt", 0, 0));
- for (i = 0; i < 100; ++i)
- cl_git_pass(git_buf_puts(&c2, "this is not the content you are looking for\n"));
- cl_git_pass(git_futils_writebuffer(&c2, "renames/untimely.txt", 0, 0));
-
- cl_git_pass(
- git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
-
- cl_git_pass(git_repository_index(&index, g_repo));
- cl_git_pass(git_index_read_tree(index, tree));
- cl_git_pass(git_index_add_bypath(index, "songof7cities.txt"));
- cl_git_pass(git_index_add_bypath(index, "untimely.txt"));
-
- cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
- cl_assert_equal_i(2, exp.files);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
-
- opts.flags = GIT_DIFF_FIND_ALL;
- cl_git_pass(git_diff_find_similar(diff, &opts));
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
- cl_assert_equal_i(3, exp.files);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
-
- git_diff_list_free(diff);
- git_tree_free(tree);
- git_index_free(index);
-
- git_buf_free(&c1);
- git_buf_free(&c2);
-}
-
-void test_diff_rename__rename_and_copy_from_same_source(void)
-{
- git_buf c1 = GIT_BUF_INIT, c2 = GIT_BUF_INIT;
- git_index *index;
- git_tree *tree;
- git_diff_list *diff;
- git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
- git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
- diff_expects exp;
-
- /* put the first 2/3 of file into one new place
- * and the second 2/3 of file into another new place
- */
- cl_git_pass(git_futils_readbuffer(&c1, "renames/songof7cities.txt"));
- cl_git_pass(git_buf_set(&c2, c1.ptr, c1.size));
- git_buf_truncate(&c1, c1.size * 2 / 3);
- git_buf_consume(&c2, ((char *)c2.ptr) + (c2.size / 3));
- cl_git_pass(git_futils_writebuffer(&c1, "renames/song_a.txt", 0, 0));
- cl_git_pass(git_futils_writebuffer(&c2, "renames/song_b.txt", 0, 0));
-
- cl_git_pass(
- git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
-
- cl_git_pass(git_repository_index(&index, g_repo));
- cl_git_pass(git_index_read_tree(index, tree));
- cl_git_pass(git_index_add_bypath(index, "song_a.txt"));
- cl_git_pass(git_index_add_bypath(index, "song_b.txt"));
-
- diffopts.flags = GIT_DIFF_INCLUDE_UNMODIFIED;
-
- cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
- cl_assert_equal_i(6, exp.files);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNMODIFIED]);
-
- opts.flags = GIT_DIFF_FIND_ALL;
- cl_git_pass(git_diff_find_similar(diff, &opts));
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
- cl_assert_equal_i(6, exp.files);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_COPIED]);
- cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNMODIFIED]);
-
- git_diff_list_free(diff);
- git_tree_free(tree);
- git_index_free(index);
-
- git_buf_free(&c1);
- git_buf_free(&c2);
-}
-
-void test_diff_rename__from_deleted_to_split(void)
-{
- git_buf c1 = GIT_BUF_INIT;
- git_index *index;
- git_tree *tree;
- git_diff_list *diff;
- git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
- git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
- diff_expects exp;
-
- /* old file is missing, new file is actually old file renamed */
-
- cl_git_pass(git_futils_readbuffer(&c1, "renames/songof7cities.txt"));
- cl_git_pass(git_futils_writebuffer(&c1, "renames/untimely.txt", 0, 0));
-
- cl_git_pass(
- git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
-
- cl_git_pass(git_repository_index(&index, g_repo));
- cl_git_pass(git_index_read_tree(index, tree));
- cl_git_pass(git_index_remove_bypath(index, "songof7cities.txt"));
- cl_git_pass(git_index_add_bypath(index, "untimely.txt"));
-
- diffopts.flags = GIT_DIFF_INCLUDE_UNMODIFIED;
-
- cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
- cl_assert_equal_i(4, exp.files);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNMODIFIED]);
-
- opts.flags = GIT_DIFF_FIND_ALL;
- cl_git_pass(git_diff_find_similar(diff, &opts));
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
- cl_assert_equal_i(4, exp.files);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNMODIFIED]);
-
- git_diff_list_free(diff);
- git_tree_free(tree);
- git_index_free(index);
-
- git_buf_free(&c1);
-}
-
-struct rename_expected
-{
- size_t len;
- const char **sources;
- const char **targets;
-
- size_t idx;
-};
-
-int test_names_expected(const git_diff_delta *delta, float progress, void *p)
-{
- struct rename_expected *expected = p;
-
- GIT_UNUSED(progress);
-
- cl_assert(expected->idx < expected->len);
-
- cl_assert_equal_i(delta->status, GIT_DELTA_RENAMED);
-
- cl_assert(git__strcmp(expected->sources[expected->idx],
- delta->old_file.path) == 0);
- cl_assert(git__strcmp(expected->targets[expected->idx],
- delta->new_file.path) == 0);
-
- expected->idx++;
-
- return 0;
-}
-
-void test_diff_rename__rejected_match_can_match_others(void)
-{
- git_reference *head, *selfsimilar;
- git_index *index;
- git_tree *tree;
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- git_diff_list *diff;
- git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
- git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
- git_buf one = GIT_BUF_INIT, two = GIT_BUF_INIT;
- const char *sources[] = { "Class1.cs", "Class2.cs" };
- const char *targets[] = { "ClassA.cs", "ClassB.cs" };
- struct rename_expected expect = { 2, sources, targets };
- char *ptr;
-
- opts.checkout_strategy = GIT_CHECKOUT_FORCE;
-
- cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
- cl_git_pass(git_reference_symbolic_set_target(
- &selfsimilar, head, "refs/heads/renames_similar"));
- cl_git_pass(git_checkout_head(g_repo, &opts));
- cl_git_pass(git_repository_index(&index, g_repo));
-
- cl_git_pass(git_futils_readbuffer(&one, "renames/Class1.cs"));
- cl_git_pass(git_futils_readbuffer(&two, "renames/Class2.cs"));
-
- cl_git_pass(p_unlink("renames/Class1.cs"));
- cl_git_pass(p_unlink("renames/Class2.cs"));
-
- cl_git_pass(git_index_remove_bypath(index, "Class1.cs"));
- cl_git_pass(git_index_remove_bypath(index, "Class2.cs"));
-
- cl_assert(ptr = strstr(one.ptr, "Class1"));
- ptr[5] = 'A';
-
- cl_assert(ptr = strstr(two.ptr, "Class2"));
- ptr[5] = 'B';
-
- cl_git_pass(
- git_futils_writebuffer(&one, "renames/ClassA.cs", O_RDWR|O_CREAT, 0777));
- cl_git_pass(
- git_futils_writebuffer(&two, "renames/ClassB.cs", O_RDWR|O_CREAT, 0777));
-
- cl_git_pass(git_index_add_bypath(index, "ClassA.cs"));
- cl_git_pass(git_index_add_bypath(index, "ClassB.cs"));
-
- cl_git_pass(git_index_write(index));
-
- cl_git_pass(
- git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
-
- cl_git_pass(
- git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
-
- cl_git_pass(git_diff_find_similar(diff, &findopts));
-
- cl_git_pass(
- git_diff_foreach(diff, test_names_expected, NULL, NULL, &expect));
-
- git_diff_list_free(diff);
- git_tree_free(tree);
- git_index_free(index);
- git_reference_free(head);
- git_reference_free(selfsimilar);
- git_buf_free(&one);
- git_buf_free(&two);
-}
-
-static void write_similarity_file_two(const char *filename, size_t b_lines)
-{
- git_buf contents = GIT_BUF_INIT;
- size_t i;
-
- for (i = 0; i < b_lines; i++)
- git_buf_printf(&contents, "%02d - bbbbb\r\n", (int)(i+1));
-
- for (i = b_lines; i < 50; i++)
- git_buf_printf(&contents, "%02d - aaaaa%s", (int)(i+1), (i == 49 ? "" : "\r\n"));
-
- cl_git_pass(
- git_futils_writebuffer(&contents, filename, O_RDWR|O_CREAT, 0777));
-
- git_buf_free(&contents);
-}
-
-void test_diff_rename__rejected_match_can_match_others_two(void)
-{
- git_reference *head, *selfsimilar;
- git_index *index;
- git_tree *tree;
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
- git_diff_list *diff;
- git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
- git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
- const char *sources[] = { "a.txt", "b.txt" };
- const char *targets[] = { "c.txt", "d.txt" };
- struct rename_expected expect = { 2, sources, targets };
-
- opts.checkout_strategy = GIT_CHECKOUT_FORCE;
-
- cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
- cl_git_pass(git_reference_symbolic_set_target(
- &selfsimilar, head, "refs/heads/renames_similar_two"));
- cl_git_pass(git_checkout_head(g_repo, &opts));
- cl_git_pass(git_repository_index(&index, g_repo));
-
- cl_git_pass(p_unlink("renames/a.txt"));
- cl_git_pass(p_unlink("renames/b.txt"));
-
- cl_git_pass(git_index_remove_bypath(index, "a.txt"));
- cl_git_pass(git_index_remove_bypath(index, "b.txt"));
-
- write_similarity_file_two("renames/c.txt", 7);
- write_similarity_file_two("renames/d.txt", 8);
-
- cl_git_pass(git_index_add_bypath(index, "c.txt"));
- cl_git_pass(git_index_add_bypath(index, "d.txt"));
-
- cl_git_pass(git_index_write(index));
-
- cl_git_pass(
- git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
-
- cl_git_pass(
- git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
-
- cl_git_pass(git_diff_find_similar(diff, &findopts));
-
- cl_git_pass(
- git_diff_foreach(diff, test_names_expected, NULL, NULL, &expect));
- cl_assert(expect.idx > 0);
-
- git_diff_list_free(diff);
- git_tree_free(tree);
- git_index_free(index);
- git_reference_free(head);
- git_reference_free(selfsimilar);
-}
-
-void test_diff_rename__case_changes_are_split(void)
-{
- git_index *index;
- git_tree *tree;
- git_diff_list *diff = NULL;
- diff_expects exp;
- git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
-
- cl_git_pass(git_repository_index(&index, g_repo));
-
- cl_git_pass(
- git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
-
- cl_git_pass(p_rename("renames/ikeepsix.txt", "renames/IKEEPSIX.txt"));
-
- cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
- cl_git_pass(git_index_add_bypath(index, "IKEEPSIX.txt"));
- cl_git_pass(git_index_write(index));
-
- cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, NULL));
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
- cl_assert_equal_i(2, exp.files);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
-
- opts.flags = GIT_DIFF_FIND_ALL;
- cl_git_pass(git_diff_find_similar(diff, &opts));
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
- cl_assert_equal_i(1, exp.files);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
-
- git_diff_list_free(diff);
- git_index_free(index);
- git_tree_free(tree);
-}
-
-void test_diff_rename__unmodified_can_be_renamed(void)
-{
- git_index *index;
- git_tree *tree;
- git_diff_list *diff = NULL;
- diff_expects exp;
- git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
- git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
-
- cl_git_pass(git_repository_index(&index, g_repo));
- cl_git_pass(
- git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
-
- cl_git_pass(p_rename("renames/ikeepsix.txt", "renames/ikeepsix2.txt"));
-
- cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
- cl_git_pass(git_index_add_bypath(index, "ikeepsix2.txt"));
- cl_git_pass(git_index_write(index));
-
- cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
- cl_assert_equal_i(2, exp.files);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
-
- opts.flags = GIT_DIFF_FIND_ALL;
- cl_git_pass(git_diff_find_similar(diff, &opts));
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
- cl_assert_equal_i(1, exp.files);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
- cl_assert_equal_i(1, exp.files);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
-
- git_diff_list_free(diff);
- git_index_free(index);
- git_tree_free(tree);
-}
diff --git a/tests-clar/diff/submodules.c b/tests-clar/diff/submodules.c
deleted file mode 100644
index 6e52a6319..000000000
--- a/tests-clar/diff/submodules.c
+++ /dev/null
@@ -1,169 +0,0 @@
-#include "clar_libgit2.h"
-#include "repository.h"
-#include "posix.h"
-#include "../submodule/submodule_helpers.h"
-
-static git_repository *g_repo = NULL;
-
-static void setup_submodules(void)
-{
- g_repo = cl_git_sandbox_init("submodules");
- cl_fixture_sandbox("testrepo.git");
- rewrite_gitmodules(git_repository_workdir(g_repo));
- p_rename("submodules/testrepo/.gitted", "submodules/testrepo/.git");
-}
-
-static void setup_submodules2(void)
-{
- g_repo = cl_git_sandbox_init("submod2");
-
- cl_fixture_sandbox("submod2_target");
- p_rename("submod2_target/.gitted", "submod2_target/.git");
-
- rewrite_gitmodules(git_repository_workdir(g_repo));
- p_rename("submod2/not-submodule/.gitted", "submod2/not-submodule/.git");
- p_rename("submod2/not/.gitted", "submod2/not/.git");
-}
-
-void test_diff_submodules__initialize(void)
-{
-}
-
-void test_diff_submodules__cleanup(void)
-{
- cl_git_sandbox_cleanup();
-
- cl_fixture_cleanup("testrepo.git");
- cl_fixture_cleanup("submod2_target");
-}
-
-static void check_diff_patches(git_diff_list *diff, const char **expected)
-{
- const git_diff_delta *delta;
- git_diff_patch *patch = NULL;
- size_t d, num_d = git_diff_num_deltas(diff);
- char *patch_text;
-
- for (d = 0; d < num_d; ++d, git_diff_patch_free(patch)) {
- cl_git_pass(git_diff_get_patch(&patch, &delta, diff, d));
-
- if (delta->status == GIT_DELTA_UNMODIFIED)
- continue;
-
- if (expected[d] && !strcmp(expected[d], "<SKIP>"))
- continue;
- if (expected[d] && !strcmp(expected[d], "<END>"))
- cl_assert(0);
-
- cl_git_pass(git_diff_patch_to_str(&patch_text, patch));
-
- cl_assert_equal_s(expected[d], patch_text);
- git__free(patch_text);
- }
-
- cl_assert(expected[d] && !strcmp(expected[d], "<END>"));
-}
-
-void test_diff_submodules__unmodified_submodule(void)
-{
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_diff_list *diff = NULL;
- static const char *expected[] = {
- "<SKIP>", /* .gitmodules */
- NULL, /* added */
- NULL, /* ignored */
- "diff --git a/modified b/modified\nindex 092bfb9..452216e 100644\n--- a/modified\n+++ b/modified\n@@ -1 +1,2 @@\n-yo\n+changed\n+\n", /* modified */
- NULL, /* testrepo.git */
- NULL, /* unmodified */
- NULL, /* untracked */
- "<END>"
- };
-
- setup_submodules();
-
- opts.flags = GIT_DIFF_INCLUDE_IGNORED |
- GIT_DIFF_INCLUDE_UNTRACKED |
- GIT_DIFF_INCLUDE_UNMODIFIED;
-
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
- check_diff_patches(diff, expected);
- git_diff_list_free(diff);
-}
-
-void test_diff_submodules__dirty_submodule(void)
-{
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_diff_list *diff = NULL;
- static const char *expected[] = {
- "<SKIP>", /* .gitmodules */
- NULL, /* added */
- NULL, /* ignored */
- "diff --git a/modified b/modified\nindex 092bfb9..452216e 100644\n--- a/modified\n+++ b/modified\n@@ -1 +1,2 @@\n-yo\n+changed\n+\n", /* modified */
- "diff --git a/testrepo b/testrepo\nindex a65fedf..a65fedf 160000\n--- a/testrepo\n+++ b/testrepo\n@@ -1 +1 @@\n-Subproject commit a65fedf39aefe402d3bb6e24df4d4f5fe4547750\n+Subproject commit a65fedf39aefe402d3bb6e24df4d4f5fe4547750-dirty\n", /* testrepo.git */
- NULL, /* unmodified */
- NULL, /* untracked */
- "<END>"
- };
-
- setup_submodules();
-
- cl_git_rewritefile("submodules/testrepo/README", "heyheyhey");
- cl_git_mkfile("submodules/testrepo/all_new.txt", "never seen before");
-
- opts.flags = GIT_DIFF_INCLUDE_IGNORED |
- GIT_DIFF_INCLUDE_UNTRACKED |
- GIT_DIFF_INCLUDE_UNMODIFIED;
-
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
- check_diff_patches(diff, expected);
- git_diff_list_free(diff);
-}
-
-void test_diff_submodules__submod2_index_to_wd(void)
-{
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_diff_list *diff = NULL;
- static const char *expected[] = {
- "<SKIP>", /* .gitmodules */
- NULL, /* not-submodule */
- NULL, /* not */
- "diff --git a/sm_changed_file b/sm_changed_file\nindex 4800958..4800958 160000\n--- a/sm_changed_file\n+++ b/sm_changed_file\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_file */
- "diff --git a/sm_changed_head b/sm_changed_head\nindex 4800958..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n", /* sm_changed_head */
- "diff --git a/sm_changed_index b/sm_changed_index\nindex 4800958..4800958 160000\n--- a/sm_changed_index\n+++ b/sm_changed_index\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_index */
- "diff --git a/sm_changed_untracked_file b/sm_changed_untracked_file\nindex 4800958..4800958 160000\n--- a/sm_changed_untracked_file\n+++ b/sm_changed_untracked_file\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_untracked_file */
- "diff --git a/sm_missing_commits b/sm_missing_commits\nindex 4800958..5e49635 160000\n--- a/sm_missing_commits\n+++ b/sm_missing_commits\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 5e4963595a9774b90524d35a807169049de8ccad\n", /* sm_missing_commits */
- "<END>"
- };
-
- setup_submodules2();
-
- opts.flags = GIT_DIFF_INCLUDE_UNTRACKED;
-
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
- check_diff_patches(diff, expected);
- git_diff_list_free(diff);
-}
-
-void test_diff_submodules__submod2_head_to_index(void)
-{
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_tree *head;
- git_diff_list *diff = NULL;
- static const char *expected[] = {
- "<SKIP>", /* .gitmodules */
- "diff --git a/sm_added_and_uncommited b/sm_added_and_uncommited\nnew file mode 160000\nindex 0000000..4800958\n--- /dev/null\n+++ b/sm_added_and_uncommited\n@@ -0,0 +1 @@\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n", /* sm_added_and_uncommited */
- "<END>"
- };
-
- setup_submodules2();
-
- cl_git_pass(git_repository_head_tree(&head, g_repo));
-
- opts.flags = GIT_DIFF_INCLUDE_UNTRACKED;
-
- cl_git_pass(git_diff_tree_to_index(&diff, g_repo, head, NULL, &opts));
- check_diff_patches(diff, expected);
- git_diff_list_free(diff);
-
- git_tree_free(head);
-}
diff --git a/tests-clar/diff/tree.c b/tests-clar/diff/tree.c
deleted file mode 100644
index f05c7869e..000000000
--- a/tests-clar/diff/tree.c
+++ /dev/null
@@ -1,530 +0,0 @@
-#include "clar_libgit2.h"
-#include "diff_helpers.h"
-
-static git_repository *g_repo = NULL;
-static git_diff_options opts;
-static git_diff_list *diff;
-static git_tree *a, *b;
-static diff_expects expect;
-
-void test_diff_tree__initialize(void)
-{
- GIT_INIT_STRUCTURE(&opts, GIT_DIFF_OPTIONS_VERSION);
- /* The default context lines is set by _INIT which we can't use here */
- opts.context_lines = 3;
-
- memset(&expect, 0, sizeof(expect));
-
- diff = NULL;
- a = NULL;
- b = NULL;
-}
-
-void test_diff_tree__cleanup(void)
-{
- git_diff_list_free(diff);
- git_tree_free(a);
- git_tree_free(b);
-
- cl_git_sandbox_cleanup();
-
-}
-
-void test_diff_tree__0(void)
-{
- /* grabbed a couple of commit oids from the history of the attr repo */
- const char *a_commit = "605812a";
- const char *b_commit = "370fe9ec22";
- const char *c_commit = "f5b0af1fb4f5c";
- git_tree *c;
-
- g_repo = cl_git_sandbox_init("attr");
-
- cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
- cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
- cl_assert((c = resolve_commit_oid_to_tree(g_repo, c_commit)) != NULL);
-
- opts.context_lines = 1;
- opts.interhunk_lines = 1;
-
-
- cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
-
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &expect));
-
- cl_assert_equal_i(5, expect.files);
- cl_assert_equal_i(2, expect.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(1, expect.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(2, expect.file_status[GIT_DELTA_MODIFIED]);
-
- cl_assert_equal_i(5, expect.hunks);
-
- cl_assert_equal_i(7 + 24 + 1 + 6 + 6, expect.lines);
- cl_assert_equal_i(1, expect.line_ctxt);
- cl_assert_equal_i(24 + 1 + 5 + 5, expect.line_adds);
- cl_assert_equal_i(7 + 1, expect.line_dels);
-
- git_diff_list_free(diff);
- diff = NULL;
-
- memset(&expect, 0, sizeof(expect));
-
- cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, c, b, &opts));
-
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &expect));
-
- cl_assert_equal_i(2, expect.files);
- cl_assert_equal_i(0, expect.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(0, expect.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(2, expect.file_status[GIT_DELTA_MODIFIED]);
-
- cl_assert_equal_i(2, expect.hunks);
-
- cl_assert_equal_i(8 + 15, expect.lines);
- cl_assert_equal_i(1, expect.line_ctxt);
- cl_assert_equal_i(1, expect.line_adds);
- cl_assert_equal_i(7 + 14, expect.line_dels);
-
- git_tree_free(c);
-}
-
-void test_diff_tree__options(void)
-{
- /* grabbed a couple of commit oids from the history of the attr repo */
- const char *a_commit = "6bab5c79cd5140d0";
- const char *b_commit = "605812ab7fe421fdd";
- const char *c_commit = "f5b0af1fb4f5";
- const char *d_commit = "a97cc019851";
- git_tree *c, *d;
- diff_expects actual;
- int test_ab_or_cd[] = { 0, 0, 0, 0, 1, 1, 1, 1, 1 };
- git_diff_options test_options[] = {
- /* a vs b tests */
- { 1, GIT_DIFF_NORMAL, 1, 1, NULL, NULL, {0} },
- { 1, GIT_DIFF_NORMAL, 3, 1, NULL, NULL, {0} },
- { 1, GIT_DIFF_REVERSE, 2, 1, NULL, NULL, {0} },
- { 1, GIT_DIFF_FORCE_TEXT, 2, 1, NULL, NULL, {0} },
- /* c vs d tests */
- { 1, GIT_DIFF_NORMAL, 3, 1, NULL, NULL, {0} },
- { 1, GIT_DIFF_IGNORE_WHITESPACE, 3, 1, NULL, NULL, {0} },
- { 1, GIT_DIFF_IGNORE_WHITESPACE_CHANGE, 3, 1, NULL, NULL, {0} },
- { 1, GIT_DIFF_IGNORE_WHITESPACE_EOL, 3, 1, NULL, NULL, {0} },
- { 1, GIT_DIFF_IGNORE_WHITESPACE | GIT_DIFF_REVERSE, 1, 1, NULL, NULL, {0} },
- };
-
- /* to generate these values:
- * - cd to tests/resources/attr,
- * - mv .gitted .git
- * - git diff [options] 6bab5c79cd5140d0 605812ab7fe421fdd
- * - mv .git .gitted
- */
-#define EXPECT_STATUS_ADM(ADDS,DELS,MODS) { 0, ADDS, DELS, MODS, 0, 0, 0, 0, 0 }
-
- diff_expects test_expects[] = {
- /* a vs b tests */
- { 5, 0, EXPECT_STATUS_ADM(3, 0, 2), 4, 0, 0, 51, 2, 46, 3 },
- { 5, 0, EXPECT_STATUS_ADM(3, 0, 2), 4, 0, 0, 53, 4, 46, 3 },
- { 5, 0, EXPECT_STATUS_ADM(0, 3, 2), 4, 0, 0, 52, 3, 3, 46 },
- { 5, 0, EXPECT_STATUS_ADM(3, 0, 2), 5, 0, 0, 54, 3, 47, 4 },
- /* c vs d tests */
- { 1, 0, EXPECT_STATUS_ADM(0, 0, 1), 1, 0, 0, 22, 9, 10, 3 },
- { 1, 0, EXPECT_STATUS_ADM(0, 0, 1), 1, 0, 0, 19, 12, 7, 0 },
- { 1, 0, EXPECT_STATUS_ADM(0, 0, 1), 1, 0, 0, 20, 11, 8, 1 },
- { 1, 0, EXPECT_STATUS_ADM(0, 0, 1), 1, 0, 0, 20, 11, 8, 1 },
- { 1, 0, EXPECT_STATUS_ADM(0, 0, 1), 1, 0, 0, 18, 11, 0, 7 },
- { 0 },
- };
- diff_expects *expected;
- int i, j;
-
- g_repo = cl_git_sandbox_init("attr");
-
- cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
- cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
- cl_assert((c = resolve_commit_oid_to_tree(g_repo, c_commit)) != NULL);
- cl_assert((d = resolve_commit_oid_to_tree(g_repo, d_commit)) != NULL);
-
- for (i = 0; test_expects[i].files > 0; i++) {
- memset(&actual, 0, sizeof(actual)); /* clear accumulator */
- opts = test_options[i];
-
- if (test_ab_or_cd[i] == 0)
- cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
- else
- cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, c, d, &opts));
-
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &actual));
-
- expected = &test_expects[i];
- cl_assert_equal_i(actual.files, expected->files);
- for (j = GIT_DELTA_UNMODIFIED; j <= GIT_DELTA_TYPECHANGE; ++j)
- cl_assert_equal_i(expected->file_status[j], actual.file_status[j]);
- cl_assert_equal_i(actual.hunks, expected->hunks);
- cl_assert_equal_i(actual.lines, expected->lines);
- cl_assert_equal_i(actual.line_ctxt, expected->line_ctxt);
- cl_assert_equal_i(actual.line_adds, expected->line_adds);
- cl_assert_equal_i(actual.line_dels, expected->line_dels);
-
- git_diff_list_free(diff);
- diff = NULL;
- }
-
- git_tree_free(c);
- git_tree_free(d);
-}
-
-void test_diff_tree__bare(void)
-{
- const char *a_commit = "8496071c1b46c85";
- const char *b_commit = "be3563ae3f79";
-
- g_repo = cl_git_sandbox_init("testrepo.git");
-
- cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
- cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
-
- opts.context_lines = 1;
- opts.interhunk_lines = 1;
-
- cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
-
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &expect));
-
- cl_assert_equal_i(3, expect.files);
- cl_assert_equal_i(2, expect.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(0, expect.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(1, expect.file_status[GIT_DELTA_MODIFIED]);
-
- cl_assert_equal_i(3, expect.hunks);
-
- cl_assert_equal_i(4, expect.lines);
- cl_assert_equal_i(0, expect.line_ctxt);
- cl_assert_equal_i(3, expect.line_adds);
- cl_assert_equal_i(1, expect.line_dels);
-}
-
-void test_diff_tree__merge(void)
-{
- /* grabbed a couple of commit oids from the history of the attr repo */
- const char *a_commit = "605812a";
- const char *b_commit = "370fe9ec22";
- const char *c_commit = "f5b0af1fb4f5c";
- git_tree *c;
- git_diff_list *diff1 = NULL, *diff2 = NULL;
-
- g_repo = cl_git_sandbox_init("attr");
-
- cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
- cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
- cl_assert((c = resolve_commit_oid_to_tree(g_repo, c_commit)) != NULL);
-
- cl_git_pass(git_diff_tree_to_tree(&diff1, g_repo, a, b, NULL));
-
- cl_git_pass(git_diff_tree_to_tree(&diff2, g_repo, c, b, NULL));
-
- git_tree_free(c);
-
- cl_git_pass(git_diff_merge(diff1, diff2));
-
- git_diff_list_free(diff2);
-
- cl_git_pass(git_diff_foreach(
- diff1, diff_file_cb, diff_hunk_cb, diff_line_cb, &expect));
-
- cl_assert_equal_i(6, expect.files);
- cl_assert_equal_i(2, expect.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(1, expect.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(3, expect.file_status[GIT_DELTA_MODIFIED]);
-
- cl_assert_equal_i(6, expect.hunks);
-
- cl_assert_equal_i(59, expect.lines);
- cl_assert_equal_i(1, expect.line_ctxt);
- cl_assert_equal_i(36, expect.line_adds);
- cl_assert_equal_i(22, expect.line_dels);
-
- git_diff_list_free(diff1);
-}
-
-void test_diff_tree__larger_hunks(void)
-{
- const char *a_commit = "d70d245ed97ed2aa596dd1af6536e4bfdb047b69";
- const char *b_commit = "7a9e0b02e63179929fed24f0a3e0f19168114d10";
- size_t d, num_d, h, num_h, l, num_l, header_len, line_len;
- const git_diff_delta *delta;
- git_diff_patch *patch;
- const git_diff_range *range;
- const char *header, *line;
- char origin;
-
- g_repo = cl_git_sandbox_init("diff");
-
- cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
- cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
-
- opts.context_lines = 1;
- opts.interhunk_lines = 0;
-
- cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
-
- num_d = git_diff_num_deltas(diff);
- for (d = 0; d < num_d; ++d) {
- cl_git_pass(git_diff_get_patch(&patch, &delta, diff, d));
- cl_assert(patch && delta);
-
- num_h = git_diff_patch_num_hunks(patch);
- for (h = 0; h < num_h; h++) {
- cl_git_pass(git_diff_patch_get_hunk(
- &range, &header, &header_len, &num_l, patch, h));
-
- for (l = 0; l < num_l; ++l) {
- cl_git_pass(git_diff_patch_get_line_in_hunk(
- &origin, &line, &line_len, NULL, NULL, patch, h, l));
- cl_assert(line);
- }
-
- cl_git_fail(git_diff_patch_get_line_in_hunk(
- &origin, &line, &line_len, NULL, NULL, patch, h, num_l));
- }
-
- cl_git_fail(git_diff_patch_get_hunk(
- &range, &header, &header_len, &num_l, patch, num_h));
-
- git_diff_patch_free(patch);
- }
-
- cl_git_fail(git_diff_get_patch(&patch, &delta, diff, num_d));
-
- cl_assert_equal_i(2, (int)num_d);
-}
-
-void test_diff_tree__checks_options_version(void)
-{
- const char *a_commit = "8496071c1b46c85";
- const char *b_commit = "be3563ae3f79";
- const git_error *err;
-
- g_repo = cl_git_sandbox_init("testrepo.git");
-
- cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
- cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
-
- opts.version = 0;
- cl_git_fail(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
- err = giterr_last();
- cl_assert_equal_i(GITERR_INVALID, err->klass);
-
- giterr_clear();
- opts.version = 1024;
- cl_git_fail(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
- err = giterr_last();
-}
-
-void process_tree_to_tree_diffing(
- const char *old_commit,
- const char *new_commit)
-{
- g_repo = cl_git_sandbox_init("unsymlinked.git");
-
- cl_assert((a = resolve_commit_oid_to_tree(g_repo, old_commit)) != NULL);
- cl_assert((b = resolve_commit_oid_to_tree(g_repo, new_commit)) != NULL);
-
- cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
-
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, NULL, NULL, &expect));
-}
-
-void test_diff_tree__symlink_blob_mode_changed_to_regular_file(void)
-{
- /*
- * $ git diff 7fccd7..806999
- * diff --git a/include/Nu/Nu.h b/include/Nu/Nu.h
- * deleted file mode 120000
- * index 19bf568..0000000
- * --- a/include/Nu/Nu.h
- * +++ /dev/null
- * @@ -1 +0,0 @@
- * -../../objc/Nu.h
- * \ No newline at end of file
- * diff --git a/include/Nu/Nu.h b/include/Nu/Nu.h
- * new file mode 100644
- * index 0000000..f9e6561
- * --- /dev/null
- * +++ b/include/Nu/Nu.h
- * @@ -0,0 +1 @@
- * +awesome content
- * diff --git a/objc/Nu.h b/objc/Nu.h
- * deleted file mode 100644
- * index f9e6561..0000000
- * --- a/objc/Nu.h
- * +++ /dev/null
- * @@ -1 +0,0 @@
- * -awesome content
- */
-
- process_tree_to_tree_diffing("7fccd7", "806999");
-
- cl_assert_equal_i(3, expect.files);
- cl_assert_equal_i(2, expect.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(0, expect.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(1, expect.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(0, expect.file_status[GIT_DELTA_TYPECHANGE]);
-}
-
-void test_diff_tree__symlink_blob_mode_changed_to_regular_file_as_typechange(void)
-{
- /*
- * $ git diff 7fccd7..a8595c
- * diff --git a/include/Nu/Nu.h b/include/Nu/Nu.h
- * deleted file mode 120000
- * index 19bf568..0000000
- * --- a/include/Nu/Nu.h
- * +++ /dev/null
- * @@ -1 +0,0 @@
- * -../../objc/Nu.h
- * \ No newline at end of file
- * diff --git a/include/Nu/Nu.h b/include/Nu/Nu.h
- * new file mode 100755
- * index 0000000..f9e6561
- * --- /dev/null
- * +++ b/include/Nu/Nu.h
- * @@ -0,0 +1 @@
- * +awesome content
- * diff --git a/objc/Nu.h b/objc/Nu.h
- * deleted file mode 100644
- * index f9e6561..0000000
- * --- a/objc/Nu.h
- * +++ /dev/null
- * @@ -1 +0,0 @@
- * -awesome content
- */
-
- opts.flags = GIT_DIFF_INCLUDE_TYPECHANGE;
- process_tree_to_tree_diffing("7fccd7", "a8595c");
-
- cl_assert_equal_i(2, expect.files);
- cl_assert_equal_i(1, expect.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(0, expect.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(0, expect.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(1, expect.file_status[GIT_DELTA_TYPECHANGE]);
-}
-
-void test_diff_tree__regular_blob_mode_changed_to_executable_file(void)
-{
- /*
- * $ git diff 806999..a8595c
- * diff --git a/include/Nu/Nu.h b/include/Nu/Nu.h
- * old mode 100644
- * new mode 100755
- */
-
- process_tree_to_tree_diffing("806999", "a8595c");
-
- cl_assert_equal_i(1, expect.files);
- cl_assert_equal_i(0, expect.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(1, expect.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(0, expect.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(0, expect.file_status[GIT_DELTA_TYPECHANGE]);
-}
-
-void test_diff_tree__issue_1397(void)
-{
- /* this test shows that it is not needed */
-
- g_repo = cl_git_sandbox_init("issue_1397");
-
- cl_repo_set_bool(g_repo, "core.autocrlf", true);
-
- cl_assert((a = resolve_commit_oid_to_tree(g_repo, "8a7ef04")) != NULL);
- cl_assert((b = resolve_commit_oid_to_tree(g_repo, "7f483a7")) != NULL);
-
- cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
-
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &expect));
-
- cl_assert_equal_i(1, expect.files);
- cl_assert_equal_i(0, expect.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(1, expect.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(0, expect.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(0, expect.file_status[GIT_DELTA_TYPECHANGE]);
-}
-
-static void set_config_int(git_repository *repo, const char *name, int value)
-{
- git_config *cfg;
-
- cl_git_pass(git_repository_config(&cfg, repo));
- cl_git_pass(git_config_set_int32(cfg, name, value));
- git_config_free(cfg);
-}
-
-void test_diff_tree__diff_configs(void)
-{
- const char *a_commit = "d70d245e";
- const char *b_commit = "7a9e0b02";
-
- g_repo = cl_git_sandbox_init("diff");
-
- cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
- cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
-
- cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, NULL));
-
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &expect));
-
- cl_assert_equal_i(2, expect.files);
- cl_assert_equal_i(2, expect.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(6, expect.hunks);
- cl_assert_equal_i(55, expect.lines);
- cl_assert_equal_i(33, expect.line_ctxt);
- cl_assert_equal_i(7, expect.line_adds);
- cl_assert_equal_i(15, expect.line_dels);
-
- git_diff_list_free(diff);
- diff = NULL;
-
- set_config_int(g_repo, "diff.context", 1);
-
- memset(&expect, 0, sizeof(expect));
-
- cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, NULL));
-
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &expect));
-
- cl_assert_equal_i(2, expect.files);
- cl_assert_equal_i(2, expect.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(7, expect.hunks);
- cl_assert_equal_i(34, expect.lines);
- cl_assert_equal_i(12, expect.line_ctxt);
- cl_assert_equal_i(7, expect.line_adds);
- cl_assert_equal_i(15, expect.line_dels);
-
- git_diff_list_free(diff);
- diff = NULL;
-
- set_config_int(g_repo, "diff.context", 0);
- set_config_int(g_repo, "diff.noprefix", 1);
-
- memset(&expect, 0, sizeof(expect));
-
- cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, NULL));
-
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &expect));
-
- cl_assert_equal_i(2, expect.files);
- cl_assert_equal_i(2, expect.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(7, expect.hunks);
- cl_assert_equal_i(22, expect.lines);
- cl_assert_equal_i(0, expect.line_ctxt);
- cl_assert_equal_i(7, expect.line_adds);
- cl_assert_equal_i(15, expect.line_dels);
-}
diff --git a/tests-clar/diff/workdir.c b/tests-clar/diff/workdir.c
deleted file mode 100644
index 6a2504d09..000000000
--- a/tests-clar/diff/workdir.c
+++ /dev/null
@@ -1,1267 +0,0 @@
-#include "clar_libgit2.h"
-#include "diff_helpers.h"
-#include "repository.h"
-
-static git_repository *g_repo = NULL;
-
-void test_diff_workdir__initialize(void)
-{
-}
-
-void test_diff_workdir__cleanup(void)
-{
- cl_git_sandbox_cleanup();
-}
-
-void test_diff_workdir__to_index(void)
-{
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_diff_list *diff = NULL;
- diff_expects exp;
- int use_iterator;
-
- g_repo = cl_git_sandbox_init("status");
-
- opts.context_lines = 3;
- opts.interhunk_lines = 1;
- opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
-
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
-
- for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
- memset(&exp, 0, sizeof(exp));
-
- if (use_iterator)
- cl_git_pass(diff_foreach_via_iterator(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
- else
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- /* to generate these values:
- * - cd to tests/resources/status,
- * - mv .gitted .git
- * - git diff --name-status
- * - git diff
- * - mv .git .gitted
- */
- cl_assert_equal_i(13, exp.files);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
- cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNTRACKED]);
-
- cl_assert_equal_i(8, exp.hunks);
-
- cl_assert_equal_i(14, exp.lines);
- cl_assert_equal_i(5, exp.line_ctxt);
- cl_assert_equal_i(4, exp.line_adds);
- cl_assert_equal_i(5, exp.line_dels);
- }
-
- git_diff_list_free(diff);
-}
-
-void test_diff_workdir__to_tree(void)
-{
- /* grabbed a couple of commit oids from the history of the attr repo */
- const char *a_commit = "26a125ee1bf"; /* the current HEAD */
- const char *b_commit = "0017bd4ab1ec3"; /* the start */
- git_tree *a, *b;
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_diff_list *diff = NULL;
- git_diff_list *diff2 = NULL;
- diff_expects exp;
- int use_iterator;
-
- g_repo = cl_git_sandbox_init("status");
-
- a = resolve_commit_oid_to_tree(g_repo, a_commit);
- b = resolve_commit_oid_to_tree(g_repo, b_commit);
-
- opts.context_lines = 3;
- opts.interhunk_lines = 1;
- opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
-
- /* You can't really generate the equivalent of git_diff_tree_to_workdir()
- * using C git. It really wants to interpose the index into the diff.
- *
- * To validate the following results with command line git, I ran the
- * following:
- * - git ls-tree 26a125
- * - find . ! -path ./.git/\* -a -type f | git hash-object --stdin-paths
- * The results are documented at the bottom of this file in the
- * long comment entitled "PREPARATION OF TEST DATA".
- */
- cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts));
-
- for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
- memset(&exp, 0, sizeof(exp));
-
- if (use_iterator)
- cl_git_pass(diff_foreach_via_iterator(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
- else
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(14, exp.files);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
- cl_assert_equal_i(5, exp.file_status[GIT_DELTA_UNTRACKED]);
- }
-
- /* Since there is no git diff equivalent, let's just assume that the
- * text diffs produced by git_diff_foreach are accurate here. We will
- * do more apples-to-apples test comparison below.
- */
-
- git_diff_list_free(diff);
- diff = NULL;
- memset(&exp, 0, sizeof(exp));
-
- /* This is a compatible emulation of "git diff <sha>" which looks like
- * a workdir to tree diff (even though it is not really). This is what
- * you would get from "git diff --name-status 26a125ee1bf"
- */
- cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
- cl_git_pass(git_diff_index_to_workdir(&diff2, g_repo, NULL, &opts));
- cl_git_pass(git_diff_merge(diff, diff2));
- git_diff_list_free(diff2);
-
- for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
- memset(&exp, 0, sizeof(exp));
-
- if (use_iterator)
- cl_git_pass(diff_foreach_via_iterator(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
- else
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(15, exp.files);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(5, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
- cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
-
- cl_assert_equal_i(11, exp.hunks);
-
- cl_assert_equal_i(17, exp.lines);
- cl_assert_equal_i(4, exp.line_ctxt);
- cl_assert_equal_i(8, exp.line_adds);
- cl_assert_equal_i(5, exp.line_dels);
- }
-
- git_diff_list_free(diff);
- diff = NULL;
- memset(&exp, 0, sizeof(exp));
-
- /* Again, emulating "git diff <sha>" for testing purposes using
- * "git diff --name-status 0017bd4ab1ec3" instead.
- */
- cl_git_pass(git_diff_tree_to_index(&diff, g_repo, b, NULL, &opts));
- cl_git_pass(git_diff_index_to_workdir(&diff2, g_repo, NULL, &opts));
- cl_git_pass(git_diff_merge(diff, diff2));
- git_diff_list_free(diff2);
-
- for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
- memset(&exp, 0, sizeof(exp));
-
- if (use_iterator)
- cl_git_pass(diff_foreach_via_iterator(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
- else
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(16, exp.files);
- cl_assert_equal_i(5, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
- cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
-
- cl_assert_equal_i(12, exp.hunks);
-
- cl_assert_equal_i(19, exp.lines);
- cl_assert_equal_i(3, exp.line_ctxt);
- cl_assert_equal_i(12, exp.line_adds);
- cl_assert_equal_i(4, exp.line_dels);
- }
-
- git_diff_list_free(diff);
-
- git_tree_free(a);
- git_tree_free(b);
-}
-
-void test_diff_workdir__to_index_with_pathspec(void)
-{
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_diff_list *diff = NULL;
- diff_expects exp;
- char *pathspec = NULL;
- int use_iterator;
-
- g_repo = cl_git_sandbox_init("status");
-
- opts.context_lines = 3;
- opts.interhunk_lines = 1;
- opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
- opts.pathspec.strings = &pathspec;
- opts.pathspec.count = 1;
-
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
-
- for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
- memset(&exp, 0, sizeof(exp));
-
- if (use_iterator)
- cl_git_pass(diff_foreach_via_iterator(
- diff, diff_file_cb, NULL, NULL, &exp));
- else
- cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
-
- cl_assert_equal_i(13, exp.files);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
- cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNTRACKED]);
- }
-
- git_diff_list_free(diff);
-
- pathspec = "modified_file";
-
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
-
- for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
- memset(&exp, 0, sizeof(exp));
-
- if (use_iterator)
- cl_git_pass(diff_foreach_via_iterator(
- diff, diff_file_cb, NULL, NULL, &exp));
- else
- cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
-
- cl_assert_equal_i(1, exp.files);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_UNTRACKED]);
- }
-
- git_diff_list_free(diff);
-
- pathspec = "subdir";
-
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
-
- for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
- memset(&exp, 0, sizeof(exp));
-
- if (use_iterator)
- cl_git_pass(diff_foreach_via_iterator(
- diff, diff_file_cb, NULL, NULL, &exp));
- else
- cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
-
- cl_assert_equal_i(3, exp.files);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
- }
-
- git_diff_list_free(diff);
-
- pathspec = "*_deleted";
-
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
-
- for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
- memset(&exp, 0, sizeof(exp));
-
- if (use_iterator)
- cl_git_pass(diff_foreach_via_iterator(
- diff, diff_file_cb, NULL, NULL, &exp));
- else
- cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
-
- cl_assert_equal_i(2, exp.files);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_UNTRACKED]);
- }
-
- git_diff_list_free(diff);
-}
-
-void test_diff_workdir__filemode_changes(void)
-{
- git_diff_list *diff = NULL;
- diff_expects exp;
- int use_iterator;
-
- if (!cl_is_chmod_supported())
- return;
-
- g_repo = cl_git_sandbox_init("issue_592");
-
- cl_repo_set_bool(g_repo, "core.filemode", true);
-
- /* test once with no mods */
-
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
-
- for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
- memset(&exp, 0, sizeof(exp));
-
- if (use_iterator)
- cl_git_pass(diff_foreach_via_iterator(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
- else
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(0, exp.files);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(0, exp.hunks);
- }
-
- git_diff_list_free(diff);
-
- /* chmod file and test again */
-
- cl_assert(cl_toggle_filemode("issue_592/a.txt"));
-
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
-
- for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
- memset(&exp, 0, sizeof(exp));
-
- if (use_iterator)
- cl_git_pass(diff_foreach_via_iterator(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
- else
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(1, exp.files);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(0, exp.hunks);
- }
-
- git_diff_list_free(diff);
-
- cl_assert(cl_toggle_filemode("issue_592/a.txt"));
-}
-
-void test_diff_workdir__filemode_changes_with_filemode_false(void)
-{
- git_diff_list *diff = NULL;
- diff_expects exp;
-
- if (!cl_is_chmod_supported())
- return;
-
- g_repo = cl_git_sandbox_init("issue_592");
-
- cl_repo_set_bool(g_repo, "core.filemode", false);
-
- /* test once with no mods */
-
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(0, exp.files);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(0, exp.hunks);
-
- git_diff_list_free(diff);
-
- /* chmod file and test again */
-
- cl_assert(cl_toggle_filemode("issue_592/a.txt"));
-
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(0, exp.files);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(0, exp.hunks);
-
- git_diff_list_free(diff);
-
- cl_assert(cl_toggle_filemode("issue_592/a.txt"));
-}
-
-void test_diff_workdir__head_index_and_workdir_all_differ(void)
-{
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_diff_list *diff_i2t = NULL, *diff_w2i = NULL;
- diff_expects exp;
- char *pathspec = "staged_changes_modified_file";
- git_tree *tree;
- int use_iterator;
-
- /* For this file,
- * - head->index diff has 1 line of context, 1 line of diff
- * - index->workdir diff has 2 lines of context, 1 line of diff
- * but
- * - head->workdir diff has 1 line of context, 2 lines of diff
- * Let's make sure the right one is returned from each fn.
- */
-
- g_repo = cl_git_sandbox_init("status");
-
- tree = resolve_commit_oid_to_tree(g_repo, "26a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f");
-
- opts.pathspec.strings = &pathspec;
- opts.pathspec.count = 1;
-
- cl_git_pass(git_diff_tree_to_index(&diff_i2t, g_repo, tree, NULL, &opts));
- cl_git_pass(git_diff_index_to_workdir(&diff_w2i, g_repo, NULL, &opts));
-
- for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
- memset(&exp, 0, sizeof(exp));
-
- if (use_iterator)
- cl_git_pass(diff_foreach_via_iterator(
- diff_i2t, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
- else
- cl_git_pass(git_diff_foreach(
- diff_i2t, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(1, exp.files);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(1, exp.hunks);
- cl_assert_equal_i(2, exp.lines);
- cl_assert_equal_i(1, exp.line_ctxt);
- cl_assert_equal_i(1, exp.line_adds);
- cl_assert_equal_i(0, exp.line_dels);
- }
-
- for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
- memset(&exp, 0, sizeof(exp));
-
- if (use_iterator)
- cl_git_pass(diff_foreach_via_iterator(
- diff_w2i, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
- else
- cl_git_pass(git_diff_foreach(
- diff_w2i, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(1, exp.files);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(1, exp.hunks);
- cl_assert_equal_i(3, exp.lines);
- cl_assert_equal_i(2, exp.line_ctxt);
- cl_assert_equal_i(1, exp.line_adds);
- cl_assert_equal_i(0, exp.line_dels);
- }
-
- cl_git_pass(git_diff_merge(diff_i2t, diff_w2i));
-
- for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
- memset(&exp, 0, sizeof(exp));
-
- if (use_iterator)
- cl_git_pass(diff_foreach_via_iterator(
- diff_i2t, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
- else
- cl_git_pass(git_diff_foreach(
- diff_i2t, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(1, exp.files);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(1, exp.hunks);
- cl_assert_equal_i(3, exp.lines);
- cl_assert_equal_i(1, exp.line_ctxt);
- cl_assert_equal_i(2, exp.line_adds);
- cl_assert_equal_i(0, exp.line_dels);
- }
-
- git_diff_list_free(diff_i2t);
- git_diff_list_free(diff_w2i);
-
- git_tree_free(tree);
-}
-
-void test_diff_workdir__eof_newline_changes(void)
-{
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_diff_list *diff = NULL;
- diff_expects exp;
- char *pathspec = "current_file";
- int use_iterator;
-
- g_repo = cl_git_sandbox_init("status");
-
- opts.pathspec.strings = &pathspec;
- opts.pathspec.count = 1;
-
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
-
- for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
- memset(&exp, 0, sizeof(exp));
-
- if (use_iterator)
- cl_git_pass(diff_foreach_via_iterator(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
- else
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(0, exp.files);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(0, exp.hunks);
- cl_assert_equal_i(0, exp.lines);
- cl_assert_equal_i(0, exp.line_ctxt);
- cl_assert_equal_i(0, exp.line_adds);
- cl_assert_equal_i(0, exp.line_dels);
- }
-
- git_diff_list_free(diff);
-
- cl_git_append2file("status/current_file", "\n");
-
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
-
- for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
- memset(&exp, 0, sizeof(exp));
-
- if (use_iterator)
- cl_git_pass(diff_foreach_via_iterator(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
- else
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(1, exp.files);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(1, exp.hunks);
- cl_assert_equal_i(2, exp.lines);
- cl_assert_equal_i(1, exp.line_ctxt);
- cl_assert_equal_i(1, exp.line_adds);
- cl_assert_equal_i(0, exp.line_dels);
- }
-
- git_diff_list_free(diff);
-
- cl_git_rewritefile("status/current_file", "current_file");
-
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
-
- for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
- memset(&exp, 0, sizeof(exp));
-
- if (use_iterator)
- cl_git_pass(diff_foreach_via_iterator(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
- else
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(1, exp.files);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(1, exp.hunks);
- cl_assert_equal_i(3, exp.lines);
- cl_assert_equal_i(0, exp.line_ctxt);
- cl_assert_equal_i(1, exp.line_adds);
- cl_assert_equal_i(2, exp.line_dels);
- }
-
- git_diff_list_free(diff);
-}
-
-/* PREPARATION OF TEST DATA
- *
- * Since there is no command line equivalent of git_diff_tree_to_workdir,
- * it was a bit of a pain to confirm that I was getting the expected
- * results in the first part of this tests. Here is what I ended up
- * doing to set my expectation for the file counts and results:
- *
- * Running "git ls-tree 26a125" and "git ls-tree aa27a6" shows:
- *
- * A a0de7e0ac200c489c41c59dfa910154a70264e6e current_file
- * B 5452d32f1dd538eb0405e8a83cc185f79e25e80f file_deleted
- * C 452e4244b5d083ddf0460acf1ecc74db9dcfa11a modified_file
- * D 32504b727382542f9f089e24fddac5e78533e96c staged_changes
- * E 061d42a44cacde5726057b67558821d95db96f19 staged_changes_file_deleted
- * F 70bd9443ada07063e7fbf0b3ff5c13f7494d89c2 staged_changes_modified_file
- * G e9b9107f290627c04d097733a10055af941f6bca staged_delete_file_deleted
- * H dabc8af9bd6e9f5bbe96a176f1a24baf3d1f8916 staged_delete_modified_file
- * I 53ace0d1cc1145a5f4fe4f78a186a60263190733 subdir/current_file
- * J 1888c805345ba265b0ee9449b8877b6064592058 subdir/deleted_file
- * K a6191982709b746d5650e93c2acf34ef74e11504 subdir/modified_file
- * L e8ee89e15bbe9b20137715232387b3de5b28972e subdir.txt
- *
- * --------
- *
- * find . ! -path ./.git/\* -a -type f | git hash-object --stdin-paths
- *
- * A a0de7e0ac200c489c41c59dfa910154a70264e6e current_file
- * M 6a79f808a9c6bc9531ac726c184bbcd9351ccf11 ignored_file
- * C 0a539630525aca2e7bc84975958f92f10a64c9b6 modified_file
- * N d4fa8600b4f37d7516bef4816ae2c64dbf029e3a new_file
- * D 55d316c9ba708999f1918e9677d01dfcae69c6b9 staged_changes
- * F 011c3440d5c596e21d836aa6d7b10eb581f68c49 staged_changes_modified_file
- * H dabc8af9bd6e9f5bbe96a176f1a24baf3d1f8916 staged_delete_modified_file
- * O 529a16e8e762d4acb7b9636ff540a00831f9155a staged_new_file
- * P 8b090c06d14ffa09c4e880088ebad33893f921d1 staged_new_file_modified_file
- * I 53ace0d1cc1145a5f4fe4f78a186a60263190733 subdir/current_file
- * K 57274b75eeb5f36fd55527806d567b2240a20c57 subdir/modified_file
- * Q 80a86a6931b91bc01c2dbf5ca55bdd24ad1ef466 subdir/new_file
- * L e8ee89e15bbe9b20137715232387b3de5b28972e subdir.txt
- *
- * --------
- *
- * A - current_file (UNMODIFIED) -> not in results
- * B D file_deleted
- * M I ignored_file (IGNORED)
- * C M modified_file
- * N U new_file (UNTRACKED)
- * D M staged_changes
- * E D staged_changes_file_deleted
- * F M staged_changes_modified_file
- * G D staged_delete_file_deleted
- * H - staged_delete_modified_file (UNMODIFIED) -> not in results
- * O U staged_new_file
- * P U staged_new_file_modified_file
- * I - subdir/current_file (UNMODIFIED) -> not in results
- * J D subdir/deleted_file
- * K M subdir/modified_file
- * Q U subdir/new_file
- * L - subdir.txt (UNMODIFIED) -> not in results
- *
- * Expect 13 files, 0 ADD, 4 DEL, 4 MOD, 1 IGN, 4 UNTR
- */
-
-
-void test_diff_workdir__larger_hunks(void)
-{
- const char *a_commit = "d70d245ed97ed2aa596dd1af6536e4bfdb047b69";
- const char *b_commit = "7a9e0b02e63179929fed24f0a3e0f19168114d10";
- git_tree *a, *b;
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- size_t i, d, num_d, h, num_h, l, num_l, header_len, line_len;
-
- g_repo = cl_git_sandbox_init("diff");
-
- cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
- cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
-
- opts.context_lines = 1;
- opts.interhunk_lines = 0;
-
- for (i = 0; i <= 2; ++i) {
- git_diff_list *diff = NULL;
- git_diff_patch *patch;
- const git_diff_range *range;
- const char *header, *line;
- char origin;
-
- /* okay, this is a bit silly, but oh well */
- switch (i) {
- case 0:
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
- break;
- case 1:
- cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts));
- break;
- case 2:
- cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, b, &opts));
- break;
- }
-
- num_d = git_diff_num_deltas(diff);
- cl_assert_equal_i(2, (int)num_d);
-
- for (d = 0; d < num_d; ++d) {
- cl_git_pass(git_diff_get_patch(&patch, NULL, diff, d));
- cl_assert(patch);
-
- num_h = git_diff_patch_num_hunks(patch);
- for (h = 0; h < num_h; h++) {
- cl_git_pass(git_diff_patch_get_hunk(
- &range, &header, &header_len, &num_l, patch, h));
-
- for (l = 0; l < num_l; ++l) {
- cl_git_pass(git_diff_patch_get_line_in_hunk(
- &origin, &line, &line_len, NULL, NULL, patch, h, l));
- cl_assert(line);
- }
-
- /* confirm fail after the last item */
- cl_git_fail(git_diff_patch_get_line_in_hunk(
- &origin, &line, &line_len, NULL, NULL, patch, h, num_l));
- }
-
- /* confirm fail after the last item */
- cl_git_fail(git_diff_patch_get_hunk(
- &range, &header, &header_len, &num_l, patch, num_h));
-
- git_diff_patch_free(patch);
- }
-
- git_diff_list_free(diff);
- }
-
- git_tree_free(a);
- git_tree_free(b);
-}
-
-/* Set up a test that exercises this code. The easiest test using existing
- * test data is probably to create a sandbox of submod2 and then run a
- * git_diff_tree_to_workdir against tree
- * 873585b94bdeabccea991ea5e3ec1a277895b698. As for what you should actually
- * test, you can start by just checking that the number of lines of diff
- * content matches the actual output of git diff. That will at least
- * demonstrate that the submodule content is being used to generate somewhat
- * comparable outputs. It is a test that would fail without this code and
- * will succeed with it.
- */
-
-#include "../submodule/submodule_helpers.h"
-
-void test_diff_workdir__submodules(void)
-{
- const char *a_commit = "873585b94bdeabccea991ea5e3ec1a277895b698";
- git_tree *a;
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_diff_list *diff = NULL;
- diff_expects exp;
-
- g_repo = cl_git_sandbox_init("submod2");
-
- cl_fixture_sandbox("submod2_target");
- p_rename("submod2_target/.gitted", "submod2_target/.git");
-
- rewrite_gitmodules(git_repository_workdir(g_repo));
- p_rename("submod2/not-submodule/.gitted", "submod2/not-submodule/.git");
- p_rename("submod2/not/.gitted", "submod2/not/.git");
-
- cl_fixture_cleanup("submod2_target");
-
- a = resolve_commit_oid_to_tree(g_repo, a_commit);
-
- opts.flags =
- GIT_DIFF_INCLUDE_UNTRACKED |
- GIT_DIFF_RECURSE_UNTRACKED_DIRS |
- GIT_DIFF_INCLUDE_UNTRACKED_CONTENT;
-
- cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts));
-
- /* diff_print(stderr, diff); */
-
- /* essentially doing: git diff 873585b94bdeabccea991ea5e3ec1a277895b698 */
-
- memset(&exp, 0, sizeof(exp));
-
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- /* so "git diff 873585" returns:
- * M .gitmodules
- * A just_a_dir/contents
- * A just_a_file
- * A sm_added_and_uncommited
- * A sm_changed_file
- * A sm_changed_head
- * A sm_changed_index
- * A sm_changed_untracked_file
- * M sm_missing_commits
- * A sm_unchanged
- * which is a little deceptive because of the difference between the
- * "git diff <treeish>" results from "git_diff_tree_to_workdir". The
- * only significant difference is that those Added items will show up
- * as Untracked items in the pure libgit2 diff.
- *
- * Then add in the two extra untracked items "not" and "not-submodule"
- * to get the 12 files reported here.
- */
-
- cl_assert_equal_i(12, exp.files);
-
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
- cl_assert_equal_i(10, exp.file_status[GIT_DELTA_UNTRACKED]);
-
- /* the following numbers match "git diff 873585" exactly */
-
- cl_assert_equal_i(9, exp.hunks);
-
- cl_assert_equal_i(33, exp.lines);
- cl_assert_equal_i(2, exp.line_ctxt);
- cl_assert_equal_i(30, exp.line_adds);
- cl_assert_equal_i(1, exp.line_dels);
-
- git_diff_list_free(diff);
- git_tree_free(a);
-}
-
-void test_diff_workdir__cannot_diff_against_a_bare_repository(void)
-{
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_diff_list *diff = NULL;
- git_tree *tree;
-
- g_repo = cl_git_sandbox_init("testrepo.git");
-
- cl_assert_equal_i(
- GIT_EBAREREPO, git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
-
- cl_git_pass(git_repository_head_tree(&tree, g_repo));
-
- cl_assert_equal_i(
- GIT_EBAREREPO, git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
-
- git_tree_free(tree);
-}
-
-void test_diff_workdir__to_null_tree(void)
-{
- git_diff_list *diff;
- diff_expects exp;
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-
- opts.flags = GIT_DIFF_INCLUDE_UNTRACKED |
- GIT_DIFF_RECURSE_UNTRACKED_DIRS;
-
- g_repo = cl_git_sandbox_init("status");
-
- cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, NULL, &opts));
-
- memset(&exp, 0, sizeof(exp));
-
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(exp.files, exp.file_status[GIT_DELTA_UNTRACKED]);
-
- git_diff_list_free(diff);
-}
-
-void test_diff_workdir__checks_options_version(void)
-{
- git_diff_list *diff;
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- const git_error *err;
-
- g_repo = cl_git_sandbox_init("status");
-
- opts.version = 0;
- cl_git_fail(git_diff_tree_to_workdir(&diff, g_repo, NULL, &opts));
- err = giterr_last();
- cl_assert_equal_i(GITERR_INVALID, err->klass);
-
- giterr_clear();
- opts.version = 1024;
- cl_git_fail(git_diff_tree_to_workdir(&diff, g_repo, NULL, &opts));
- err = giterr_last();
- cl_assert_equal_i(GITERR_INVALID, err->klass);
-}
-
-void test_diff_workdir__can_diff_empty_file(void)
-{
- git_diff_list *diff;
- git_tree *tree;
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- struct stat st;
- git_diff_patch *patch;
-
- g_repo = cl_git_sandbox_init("attr_index");
-
- tree = resolve_commit_oid_to_tree(g_repo, "3812cfef3661"); /* HEAD */
-
- /* baseline - make sure there are no outstanding diffs */
-
- cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
- cl_assert_equal_i(2, (int)git_diff_num_deltas(diff));
- git_diff_list_free(diff);
-
- /* empty contents of file */
-
- cl_git_rewritefile("attr_index/README.txt", "");
- cl_git_pass(git_path_lstat("attr_index/README.txt", &st));
- cl_assert_equal_i(0, (int)st.st_size);
-
- cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
- cl_assert_equal_i(3, (int)git_diff_num_deltas(diff));
- /* diffs are: .gitattributes, README.txt, sub/sub/.gitattributes */
- cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 1));
- git_diff_patch_free(patch);
- git_diff_list_free(diff);
-
- /* remove a file altogether */
-
- cl_git_pass(p_unlink("attr_index/README.txt"));
- cl_assert(!git_path_exists("attr_index/README.txt"));
-
- cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
- cl_assert_equal_i(3, (int)git_diff_num_deltas(diff));
- cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 1));
- git_diff_patch_free(patch);
- git_diff_list_free(diff);
-
- git_tree_free(tree);
-}
-
-void test_diff_workdir__to_index_issue_1397(void)
-{
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_diff_list *diff = NULL;
- diff_expects exp;
-
- g_repo = cl_git_sandbox_init("issue_1397");
-
- cl_repo_set_bool(g_repo, "core.autocrlf", true);
-
- opts.context_lines = 3;
- opts.interhunk_lines = 1;
-
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(0, exp.files);
- cl_assert_equal_i(0, exp.hunks);
- cl_assert_equal_i(0, exp.lines);
-
- git_diff_list_free(diff);
- diff = NULL;
-
- cl_git_rewritefile("issue_1397/crlf_file.txt",
- "first line\r\nsecond line modified\r\nboth with crlf");
-
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(1, exp.files);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
-
- cl_assert_equal_i(1, exp.hunks);
-
- cl_assert_equal_i(5, exp.lines);
- cl_assert_equal_i(3, exp.line_ctxt);
- cl_assert_equal_i(1, exp.line_adds);
- cl_assert_equal_i(1, exp.line_dels);
-
- git_diff_list_free(diff);
-}
-
-void test_diff_workdir__to_tree_issue_1397(void)
-{
- const char *a_commit = "7f483a738"; /* the current HEAD */
- git_tree *a;
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_diff_list *diff = NULL;
- git_diff_list *diff2 = NULL;
- diff_expects exp;
-
- g_repo = cl_git_sandbox_init("issue_1397");
-
- cl_repo_set_bool(g_repo, "core.autocrlf", true);
-
- a = resolve_commit_oid_to_tree(g_repo, a_commit);
-
- opts.context_lines = 3;
- opts.interhunk_lines = 1;
-
- cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts));
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(0, exp.files);
- cl_assert_equal_i(0, exp.hunks);
- cl_assert_equal_i(0, exp.lines);
-
- git_diff_list_free(diff);
- diff = NULL;
-
- cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
- cl_git_pass(git_diff_index_to_workdir(&diff2, g_repo, NULL, &opts));
- cl_git_pass(git_diff_merge(diff, diff2));
- git_diff_list_free(diff2);
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
-
- cl_assert_equal_i(0, exp.files);
- cl_assert_equal_i(0, exp.hunks);
- cl_assert_equal_i(0, exp.lines);
-
- git_diff_list_free(diff);
- git_tree_free(a);
-}
-
-void test_diff_workdir__untracked_directory_scenarios(void)
-{
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_diff_list *diff = NULL;
- diff_expects exp;
- char *pathspec = NULL;
- static const char *files0[] = {
- "subdir/deleted_file",
- "subdir/modified_file",
- "subdir/new_file",
- NULL
- };
- static const char *files1[] = {
- "subdir/deleted_file",
- "subdir/directory/",
- "subdir/modified_file",
- "subdir/new_file",
- NULL
- };
- static const char *files2[] = {
- "subdir/deleted_file",
- "subdir/directory/more/notignored",
- "subdir/modified_file",
- "subdir/new_file",
- NULL
- };
-
- g_repo = cl_git_sandbox_init("status");
- cl_git_mkfile("status/.gitignore", "ignored\n");
-
- opts.context_lines = 3;
- opts.interhunk_lines = 1;
- opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
- opts.pathspec.strings = &pathspec;
- opts.pathspec.count = 1;
- pathspec = "subdir";
-
- /* baseline for "subdir" pathspec */
-
- memset(&exp, 0, sizeof(exp));
- exp.names = files0;
-
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
-
- cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
-
- cl_assert_equal_i(3, exp.files);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
-
- git_diff_list_free(diff);
-
- /* empty directory */
-
- cl_git_pass(p_mkdir("status/subdir/directory", 0777));
-
- memset(&exp, 0, sizeof(exp));
- exp.names = files1;
-
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
-
- cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
-
- cl_assert_equal_i(4, exp.files);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
-
- git_diff_list_free(diff);
-
- /* empty directory in empty directory */
-
- cl_git_pass(p_mkdir("status/subdir/directory/empty", 0777));
-
- memset(&exp, 0, sizeof(exp));
- exp.names = files1;
-
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
-
- cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
-
- cl_assert_equal_i(4, exp.files);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
-
- git_diff_list_free(diff);
-
- /* directory with only ignored files */
-
- cl_git_pass(p_mkdir("status/subdir/directory/deeper", 0777));
- cl_git_mkfile("status/subdir/directory/deeper/ignored", "ignore me\n");
-
- cl_git_pass(p_mkdir("status/subdir/directory/another", 0777));
- cl_git_mkfile("status/subdir/directory/another/ignored", "ignore me\n");
-
- memset(&exp, 0, sizeof(exp));
- exp.names = files1;
-
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
-
- cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
-
- cl_assert_equal_i(4, exp.files);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
-
- git_diff_list_free(diff);
-
- /* directory with ignored directory (contents irrelevant) */
-
- cl_git_pass(p_mkdir("status/subdir/directory/more", 0777));
- cl_git_pass(p_mkdir("status/subdir/directory/more/ignored", 0777));
- cl_git_mkfile("status/subdir/directory/more/ignored/notignored",
- "inside ignored dir\n");
-
- memset(&exp, 0, sizeof(exp));
- exp.names = files1;
-
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
-
- cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
-
- cl_assert_equal_i(4, exp.files);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
-
- git_diff_list_free(diff);
-
- /* quick version avoids directory scan */
-
- opts.flags = opts.flags | GIT_DIFF_FAST_UNTRACKED_DIRS;
-
- memset(&exp, 0, sizeof(exp));
- exp.names = files1;
-
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
-
- cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
-
- cl_assert_equal_i(4, exp.files);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
-
- git_diff_list_free(diff);
-
- /* directory with nested non-ignored content */
-
- opts.flags = opts.flags & ~GIT_DIFF_FAST_UNTRACKED_DIRS;
-
- cl_git_mkfile("status/subdir/directory/more/notignored",
- "not ignored deep under untracked\n");
-
- memset(&exp, 0, sizeof(exp));
- exp.names = files1;
-
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
-
- cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
-
- cl_assert_equal_i(4, exp.files);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
-
- git_diff_list_free(diff);
-
- /* use RECURSE_UNTRACKED_DIRS to get actual untracked files (no ignores) */
-
- opts.flags = opts.flags & ~GIT_DIFF_INCLUDE_IGNORED;
- opts.flags = opts.flags | GIT_DIFF_RECURSE_UNTRACKED_DIRS;
-
- memset(&exp, 0, sizeof(exp));
- exp.names = files2;
-
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
-
- cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
-
- cl_assert_equal_i(4, exp.files);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
- cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
- cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
- cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
-
- git_diff_list_free(diff);
-}
-
-
-void test_diff_workdir__untracked_directory_comes_last(void)
-{
- git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
- git_diff_list *diff = NULL;
-
- g_repo = cl_git_sandbox_init("renames");
-
- cl_git_mkfile("renames/.gitignore", "*.ign\n");
- cl_git_pass(p_mkdir("renames/zzz_untracked", 0777));
- cl_git_mkfile("renames/zzz_untracked/an.ign", "ignore me please");
- cl_git_mkfile("renames/zzz_untracked/skip.ign", "ignore me really");
- cl_git_mkfile("renames/zzz_untracked/test.ign", "ignore me now");
-
- opts.context_lines = 3;
- opts.interhunk_lines = 1;
- opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
-
- cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
-
- cl_assert(diff != NULL);
-
- git_diff_list_free(diff);
-}
diff --git a/tests-clar/index/addall.c b/tests-clar/index/addall.c
deleted file mode 100644
index fca6e77fa..000000000
--- a/tests-clar/index/addall.c
+++ /dev/null
@@ -1,274 +0,0 @@
-#include "clar_libgit2.h"
-#include "../status/status_helpers.h"
-#include "posix.h"
-
-git_repository *g_repo = NULL;
-
-void test_index_addall__initialize(void)
-{
-}
-
-void test_index_addall__cleanup(void)
-{
- git_repository_free(g_repo);
- g_repo = NULL;
-}
-
-#define STATUS_INDEX_FLAGS \
- (GIT_STATUS_INDEX_NEW | GIT_STATUS_INDEX_MODIFIED | \
- GIT_STATUS_INDEX_DELETED | GIT_STATUS_INDEX_RENAMED | \
- GIT_STATUS_INDEX_TYPECHANGE)
-
-#define STATUS_WT_FLAGS \
- (GIT_STATUS_WT_NEW | GIT_STATUS_WT_MODIFIED | \
- GIT_STATUS_WT_DELETED | GIT_STATUS_WT_TYPECHANGE | \
- GIT_STATUS_WT_RENAMED)
-
-typedef struct {
- size_t index_adds;
- size_t index_dels;
- size_t index_mods;
- size_t wt_adds;
- size_t wt_dels;
- size_t wt_mods;
- size_t ignores;
-} index_status_counts;
-
-static int index_status_cb(
- const char *path, unsigned int status_flags, void *payload)
-{
- index_status_counts *vals = payload;
-
- /* cb_status__print(path, status_flags, NULL); */
-
- GIT_UNUSED(path);
-
- if (status_flags & GIT_STATUS_INDEX_NEW)
- vals->index_adds++;
- if (status_flags & GIT_STATUS_INDEX_MODIFIED)
- vals->index_mods++;
- if (status_flags & GIT_STATUS_INDEX_DELETED)
- vals->index_dels++;
- if (status_flags & GIT_STATUS_INDEX_TYPECHANGE)
- vals->index_mods++;
-
- if (status_flags & GIT_STATUS_WT_NEW)
- vals->wt_adds++;
- if (status_flags & GIT_STATUS_WT_MODIFIED)
- vals->wt_mods++;
- if (status_flags & GIT_STATUS_WT_DELETED)
- vals->wt_dels++;
- if (status_flags & GIT_STATUS_WT_TYPECHANGE)
- vals->wt_mods++;
-
- if (status_flags & GIT_STATUS_IGNORED)
- vals->ignores++;
-
- return 0;
-}
-
-static void check_status(
- git_repository *repo,
- size_t index_adds, size_t index_dels, size_t index_mods,
- size_t wt_adds, size_t wt_dels, size_t wt_mods, size_t ignores)
-{
- index_status_counts vals;
-
- memset(&vals, 0, sizeof(vals));
-
- cl_git_pass(git_status_foreach(repo, index_status_cb, &vals));
-
- cl_assert_equal_sz(index_adds, vals.index_adds);
- cl_assert_equal_sz(index_dels, vals.index_dels);
- cl_assert_equal_sz(index_mods, vals.index_mods);
- cl_assert_equal_sz(wt_adds, vals.wt_adds);
- cl_assert_equal_sz(wt_dels, vals.wt_dels);
- cl_assert_equal_sz(wt_mods, vals.wt_mods);
- cl_assert_equal_sz(ignores, vals.ignores);
-}
-
-static void check_stat_data(git_index *index, const char *path, bool match)
-{
- const git_index_entry *entry;
- struct stat st;
-
- cl_must_pass(p_lstat(path, &st));
-
- /* skip repo base dir name */
- while (*path != '/')
- ++path;
- ++path;
-
- entry = git_index_get_bypath(index, path, 0);
- cl_assert(entry);
-
- if (match) {
- cl_assert(st.st_ctime == entry->ctime.seconds);
- cl_assert(st.st_mtime == entry->mtime.seconds);
- cl_assert(st.st_size == entry->file_size);
- cl_assert(st.st_uid == entry->uid);
- cl_assert(st.st_gid == entry->gid);
- cl_assert_equal_b(st.st_mode & ~0777, entry->mode & ~0777);
- cl_assert_equal_b(st.st_mode & 0111, entry->mode & 0111);
- } else {
- /* most things will still match */
- cl_assert(st.st_size != entry->file_size);
- /* would check mtime, but with second resolution it won't work :( */
- }
-}
-
-static void commit_index_to_head(
- git_repository *repo,
- const char *commit_message)
-{
- git_index *index;
- git_oid tree_id, commit_id;
- git_tree *tree;
- git_signature *sig;
- git_commit *parent = NULL;
-
- git_revparse_single((git_object **)&parent, repo, "HEAD");
- /* it is okay if looking up the HEAD fails */
-
- cl_git_pass(git_repository_index(&index, repo));
- cl_git_pass(git_index_write_tree(&tree_id, index));
- cl_git_pass(git_index_write(index)); /* not needed, but might as well */
- git_index_free(index);
-
- cl_git_pass(git_tree_lookup(&tree, repo, &tree_id));
-
- cl_git_pass(git_signature_now(&sig, "Testy McTester", "tt@tester.test"));
-
- cl_git_pass(git_commit_create_v(
- &commit_id, repo, "HEAD", sig, sig,
- NULL, commit_message, tree, parent ? 1 : 0, parent));
-
- git_commit_free(parent);
- git_tree_free(tree);
- git_signature_free(sig);
-}
-
-void test_index_addall__repo_lifecycle(void)
-{
- int error;
- git_index *index;
- git_strarray paths = { NULL, 0 };
- char *strs[1];
-
- cl_git_pass(git_repository_init(&g_repo, "addall", false));
- check_status(g_repo, 0, 0, 0, 0, 0, 0, 0);
-
- cl_git_pass(git_repository_index(&index, g_repo));
-
- cl_git_mkfile("addall/file.foo", "a file");
- check_status(g_repo, 0, 0, 0, 1, 0, 0, 0);
-
- cl_git_mkfile("addall/.gitignore", "*.foo\n");
- check_status(g_repo, 0, 0, 0, 1, 0, 0, 1);
-
- cl_git_mkfile("addall/file.bar", "another file");
- check_status(g_repo, 0, 0, 0, 2, 0, 0, 1);
-
- strs[0] = "file.*";
- paths.strings = strs;
- paths.count = 1;
-
- cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL));
- check_stat_data(index, "addall/file.bar", true);
- check_status(g_repo, 1, 0, 0, 1, 0, 0, 1);
-
- cl_git_rewritefile("addall/file.bar", "new content for file");
- check_stat_data(index, "addall/file.bar", false);
- check_status(g_repo, 1, 0, 0, 1, 0, 1, 1);
-
- cl_git_mkfile("addall/file.zzz", "yet another one");
- cl_git_mkfile("addall/other.zzz", "yet another one");
- cl_git_mkfile("addall/more.zzz", "yet another one");
- check_status(g_repo, 1, 0, 0, 4, 0, 1, 1);
-
- cl_git_pass(git_index_update_all(index, NULL, NULL, NULL));
- check_stat_data(index, "addall/file.bar", true);
- check_status(g_repo, 1, 0, 0, 4, 0, 0, 1);
-
- cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL));
- check_stat_data(index, "addall/file.zzz", true);
- check_status(g_repo, 2, 0, 0, 3, 0, 0, 1);
-
- commit_index_to_head(g_repo, "first commit");
- check_status(g_repo, 0, 0, 0, 3, 0, 0, 1);
-
- /* attempt to add an ignored file - does nothing */
- strs[0] = "file.foo";
- cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL));
- check_status(g_repo, 0, 0, 0, 3, 0, 0, 1);
-
- /* add with check - should generate error */
- error = git_index_add_all(
- index, &paths, GIT_INDEX_ADD_CHECK_PATHSPEC, NULL, NULL);
- cl_assert_equal_i(GIT_EINVALIDSPEC, error);
- check_status(g_repo, 0, 0, 0, 3, 0, 0, 1);
-
- /* add with force - should allow */
- cl_git_pass(git_index_add_all(
- index, &paths, GIT_INDEX_ADD_FORCE, NULL, NULL));
- check_stat_data(index, "addall/file.foo", true);
- check_status(g_repo, 1, 0, 0, 3, 0, 0, 0);
-
- /* now it's in the index, so regular add should work */
- cl_git_rewritefile("addall/file.foo", "new content for file");
- check_stat_data(index, "addall/file.foo", false);
- check_status(g_repo, 1, 0, 0, 3, 0, 1, 0);
-
- cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL));
- check_stat_data(index, "addall/file.foo", true);
- check_status(g_repo, 1, 0, 0, 3, 0, 0, 0);
-
- cl_git_pass(git_index_add_bypath(index, "more.zzz"));
- check_stat_data(index, "addall/more.zzz", true);
- check_status(g_repo, 2, 0, 0, 2, 0, 0, 0);
-
- cl_git_rewritefile("addall/file.zzz", "new content for file");
- check_status(g_repo, 2, 0, 0, 2, 0, 1, 0);
-
- cl_git_pass(git_index_add_bypath(index, "file.zzz"));
- check_stat_data(index, "addall/file.zzz", true);
- check_status(g_repo, 2, 0, 1, 2, 0, 0, 0);
-
- strs[0] = "*.zzz";
- cl_git_pass(git_index_remove_all(index, &paths, NULL, NULL));
- check_status(g_repo, 1, 1, 0, 4, 0, 0, 0);
-
- cl_git_pass(git_index_add_bypath(index, "file.zzz"));
- check_status(g_repo, 1, 0, 1, 3, 0, 0, 0);
-
- commit_index_to_head(g_repo, "second commit");
- check_status(g_repo, 0, 0, 0, 3, 0, 0, 0);
-
- cl_must_pass(p_unlink("addall/file.zzz"));
- check_status(g_repo, 0, 0, 0, 3, 1, 0, 0);
-
- /* update_all should be able to remove entries */
- cl_git_pass(git_index_update_all(index, NULL, NULL, NULL));
- check_status(g_repo, 0, 1, 0, 3, 0, 0, 0);
-
- strs[0] = "*";
- cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL));
- check_status(g_repo, 3, 1, 0, 0, 0, 0, 0);
-
- /* must be able to remove at any position while still updating other files */
- cl_must_pass(p_unlink("addall/.gitignore"));
- cl_git_rewritefile("addall/file.zzz", "reconstructed file");
- cl_git_rewritefile("addall/more.zzz", "altered file reality");
- check_status(g_repo, 3, 1, 0, 1, 1, 1, 0);
-
- cl_git_pass(git_index_update_all(index, NULL, NULL, NULL));
- check_status(g_repo, 2, 1, 0, 1, 0, 0, 0);
- /* this behavior actually matches 'git add -u' where "file.zzz" has
- * been removed from the index, so when you go to update, even though
- * it exists in the HEAD, it is not re-added to the index, leaving it
- * as a DELETE when comparing HEAD to index and as an ADD comparing
- * index to worktree
- */
-
- git_index_free(index);
-}
diff --git a/tests-clar/index/filemodes.c b/tests-clar/index/filemodes.c
deleted file mode 100644
index e56a9c069..000000000
--- a/tests-clar/index/filemodes.c
+++ /dev/null
@@ -1,153 +0,0 @@
-#include "clar_libgit2.h"
-#include "buffer.h"
-#include "posix.h"
-#include "index.h"
-
-static git_repository *g_repo = NULL;
-
-void test_index_filemodes__initialize(void)
-{
- g_repo = cl_git_sandbox_init("filemodes");
-}
-
-void test_index_filemodes__cleanup(void)
-{
- cl_git_sandbox_cleanup();
-}
-
-void test_index_filemodes__read(void)
-{
- git_index *index;
- unsigned int i;
- static bool expected[6] = { 0, 1, 0, 1, 0, 1 };
-
- cl_git_pass(git_repository_index(&index, g_repo));
- cl_assert_equal_i(6, (int)git_index_entrycount(index));
-
- for (i = 0; i < 6; ++i) {
- const git_index_entry *entry = git_index_get_byindex(index, i);
- cl_assert(entry != NULL);
- cl_assert(((entry->mode & 0100) ? 1 : 0) == expected[i]);
- }
-
- git_index_free(index);
-}
-
-static void replace_file_with_mode(
- const char *filename, const char *backup, unsigned int create_mode)
-{
- git_buf path = GIT_BUF_INIT, content = GIT_BUF_INIT;
-
- cl_git_pass(git_buf_joinpath(&path, "filemodes", filename));
- cl_git_pass(git_buf_printf(&content, "%s as %08u (%d)",
- filename, create_mode, rand()));
-
- cl_git_pass(p_rename(path.ptr, backup));
- cl_git_write2file(
- path.ptr, content.ptr, O_WRONLY|O_CREAT|O_TRUNC, create_mode);
-
- git_buf_free(&path);
- git_buf_free(&content);
-}
-
-static void add_and_check_mode(
- git_index *index, const char *filename, unsigned int expect_mode)
-{
- size_t pos;
- const git_index_entry *entry;
-
- cl_git_pass(git_index_add_bypath(index, filename));
-
- cl_assert(!git_index_find(&pos, index, filename));
-
- entry = git_index_get_byindex(index, pos);
- cl_assert(entry->mode == expect_mode);
-}
-
-void test_index_filemodes__untrusted(void)
-{
- git_index *index;
- bool can_filemode = cl_is_chmod_supported();
-
- cl_repo_set_bool(g_repo, "core.filemode", false);
-
- cl_git_pass(git_repository_index(&index, g_repo));
- cl_assert((git_index_caps(index) & GIT_INDEXCAP_NO_FILEMODE) != 0);
-
- /* 1 - add 0644 over existing 0644 -> expect 0644 */
- replace_file_with_mode("exec_off", "filemodes/exec_off.0", 0644);
- add_and_check_mode(index, "exec_off", GIT_FILEMODE_BLOB);
-
- /* 2 - add 0644 over existing 0755 -> expect 0755 */
- replace_file_with_mode("exec_on", "filemodes/exec_on.0", 0644);
- add_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB_EXECUTABLE);
-
- /* 3 - add 0755 over existing 0644 -> expect 0644 */
- replace_file_with_mode("exec_off", "filemodes/exec_off.1", 0755);
- add_and_check_mode(index, "exec_off", GIT_FILEMODE_BLOB);
-
- /* 4 - add 0755 over existing 0755 -> expect 0755 */
- replace_file_with_mode("exec_on", "filemodes/exec_on.1", 0755);
- add_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB_EXECUTABLE);
-
- /* 5 - add new 0644 -> expect 0644 */
- cl_git_write2file("filemodes/new_off", "blah",
- O_WRONLY | O_CREAT | O_TRUNC, 0644);
- add_and_check_mode(index, "new_off", GIT_FILEMODE_BLOB);
-
- /* this test won't give predictable results on a platform
- * that doesn't support filemodes correctly, so skip it.
- */
- if (can_filemode) {
- /* 6 - add 0755 -> expect 0755 */
- cl_git_write2file("filemodes/new_on", "blah",
- O_WRONLY | O_CREAT | O_TRUNC, 0755);
- add_and_check_mode(index, "new_on", GIT_FILEMODE_BLOB_EXECUTABLE);
- }
-
- git_index_free(index);
-}
-
-void test_index_filemodes__trusted(void)
-{
- git_index *index;
-
- /* Only run these tests on platforms where I can actually
- * chmod a file and get the stat results I expect!
- */
- if (!cl_is_chmod_supported())
- return;
-
- cl_repo_set_bool(g_repo, "core.filemode", true);
-
- cl_git_pass(git_repository_index(&index, g_repo));
- cl_assert((git_index_caps(index) & GIT_INDEXCAP_NO_FILEMODE) == 0);
-
- /* 1 - add 0644 over existing 0644 -> expect 0644 */
- replace_file_with_mode("exec_off", "filemodes/exec_off.0", 0644);
- add_and_check_mode(index, "exec_off", GIT_FILEMODE_BLOB);
-
- /* 2 - add 0644 over existing 0755 -> expect 0644 */
- replace_file_with_mode("exec_on", "filemodes/exec_on.0", 0644);
- add_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB);
-
- /* 3 - add 0755 over existing 0644 -> expect 0755 */
- replace_file_with_mode("exec_off", "filemodes/exec_off.1", 0755);
- add_and_check_mode(index, "exec_off", GIT_FILEMODE_BLOB_EXECUTABLE);
-
- /* 4 - add 0755 over existing 0755 -> expect 0755 */
- replace_file_with_mode("exec_on", "filemodes/exec_on.1", 0755);
- add_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB_EXECUTABLE);
-
- /* 5 - add new 0644 -> expect 0644 */
- cl_git_write2file("filemodes/new_off", "blah",
- O_WRONLY | O_CREAT | O_TRUNC, 0644);
- add_and_check_mode(index, "new_off", GIT_FILEMODE_BLOB);
-
- /* 6 - add 0755 -> expect 0755 */
- cl_git_write2file("filemodes/new_on", "blah",
- O_WRONLY | O_CREAT | O_TRUNC, 0755);
- add_and_check_mode(index, "new_on", GIT_FILEMODE_BLOB_EXECUTABLE);
-
- git_index_free(index);
-}
diff --git a/tests-clar/index/names.c b/tests-clar/index/names.c
deleted file mode 100644
index 95a560ee4..000000000
--- a/tests-clar/index/names.c
+++ /dev/null
@@ -1,84 +0,0 @@
-#include "clar_libgit2.h"
-#include "index.h"
-#include "git2/sys/index.h"
-#include "git2/repository.h"
-#include "../reset/reset_helpers.h"
-
-static git_repository *repo;
-static git_index *repo_index;
-
-#define TEST_REPO_PATH "mergedrepo"
-#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
-
-// Fixture setup and teardown
-void test_index_names__initialize(void)
-{
- repo = cl_git_sandbox_init("mergedrepo");
- git_repository_index(&repo_index, repo);
-}
-
-void test_index_names__cleanup(void)
-{
- git_index_free(repo_index);
- repo_index = NULL;
-
- cl_git_sandbox_cleanup();
-}
-
-void test_index_names__add(void)
-{
- const git_index_name_entry *conflict_name;
-
- cl_git_pass(git_index_name_add(repo_index, "ancestor", "ours", "theirs"));
- cl_git_pass(git_index_name_add(repo_index, "ancestor2", "ours2", NULL));
- cl_git_pass(git_index_name_add(repo_index, "ancestor3", NULL, "theirs3"));
-
- cl_assert(git_index_name_entrycount(repo_index) == 3);
-
- conflict_name = git_index_name_get_byindex(repo_index, 0);
- cl_assert(strcmp(conflict_name->ancestor, "ancestor") == 0);
- cl_assert(strcmp(conflict_name->ours, "ours") == 0);
- cl_assert(strcmp(conflict_name->theirs, "theirs") == 0);
-
- conflict_name = git_index_name_get_byindex(repo_index, 1);
- cl_assert(strcmp(conflict_name->ancestor, "ancestor2") == 0);
- cl_assert(strcmp(conflict_name->ours, "ours2") == 0);
- cl_assert(conflict_name->theirs == NULL);
-
- conflict_name = git_index_name_get_byindex(repo_index, 2);
- cl_assert(strcmp(conflict_name->ancestor, "ancestor3") == 0);
- cl_assert(conflict_name->ours == NULL);
- cl_assert(strcmp(conflict_name->theirs, "theirs3") == 0);
-}
-
-void test_index_names__roundtrip(void)
-{
- const git_index_name_entry *conflict_name;
-
- cl_git_pass(git_index_name_add(repo_index, "ancestor", "ours", "theirs"));
- cl_git_pass(git_index_name_add(repo_index, "ancestor2", "ours2", NULL));
- cl_git_pass(git_index_name_add(repo_index, "ancestor3", NULL, "theirs3"));
-
- cl_git_pass(git_index_write(repo_index));
- git_index_clear(repo_index);
- cl_assert(git_index_name_entrycount(repo_index) == 0);
-
- cl_git_pass(git_index_read(repo_index));
- cl_assert(git_index_name_entrycount(repo_index) == 3);
-
- conflict_name = git_index_name_get_byindex(repo_index, 0);
- cl_assert(strcmp(conflict_name->ancestor, "ancestor") == 0);
- cl_assert(strcmp(conflict_name->ours, "ours") == 0);
- cl_assert(strcmp(conflict_name->theirs, "theirs") == 0);
-
- conflict_name = git_index_name_get_byindex(repo_index, 1);
- cl_assert(strcmp(conflict_name->ancestor, "ancestor2") == 0);
- cl_assert(strcmp(conflict_name->ours, "ours2") == 0);
- cl_assert(conflict_name->theirs == NULL);
-
- conflict_name = git_index_name_get_byindex(repo_index, 2);
- cl_assert(strcmp(conflict_name->ancestor, "ancestor3") == 0);
- cl_assert(conflict_name->ours == NULL);
- cl_assert(strcmp(conflict_name->theirs, "theirs3") == 0);
-
-}
diff --git a/tests-clar/index/reuc.c b/tests-clar/index/reuc.c
deleted file mode 100644
index 69ed4a933..000000000
--- a/tests-clar/index/reuc.c
+++ /dev/null
@@ -1,374 +0,0 @@
-#include "clar_libgit2.h"
-#include "index.h"
-#include "git2/sys/index.h"
-#include "git2/repository.h"
-#include "../reset/reset_helpers.h"
-
-static git_repository *repo;
-static git_index *repo_index;
-
-#define TEST_REPO_PATH "mergedrepo"
-#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
-
-#define ONE_ANCESTOR_OID "478871385b9cd03908c5383acfd568bef023c6b3"
-#define ONE_OUR_OID "4458b8bc9e72b6c8755ae456f60e9844d0538d8c"
-#define ONE_THEIR_OID "8b72416545c7e761b64cecad4f1686eae4078aa8"
-
-#define TWO_ANCESTOR_OID "9d81f82fccc7dcd7de7a1ffead1815294c2e092c"
-#define TWO_OUR_OID "8f3c06cff9a83757cec40c80bc9bf31a2582bde9"
-#define TWO_THEIR_OID "887b153b165d32409c70163e0f734c090f12f673"
-
-// Fixture setup and teardown
-void test_index_reuc__initialize(void)
-{
- repo = cl_git_sandbox_init("mergedrepo");
- git_repository_index(&repo_index, repo);
-}
-
-void test_index_reuc__cleanup(void)
-{
- git_index_free(repo_index);
- repo_index = NULL;
-
- cl_git_sandbox_cleanup();
-}
-
-void test_index_reuc__add(void)
-{
- git_oid ancestor_oid, our_oid, their_oid;
- const git_index_reuc_entry *reuc;
-
- git_oid_fromstr(&ancestor_oid, ONE_ANCESTOR_OID);
- git_oid_fromstr(&our_oid, ONE_OUR_OID);
- git_oid_fromstr(&their_oid, ONE_THEIR_OID);
-
- cl_git_pass(git_index_reuc_add(repo_index, "newfile.txt",
- 0100644, &ancestor_oid,
- 0100644, &our_oid,
- 0100644, &their_oid));
-
- cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "newfile.txt"));
-
- cl_assert_equal_s("newfile.txt", reuc->path);
- cl_assert(reuc->mode[0] == 0100644);
- cl_assert(reuc->mode[1] == 0100644);
- cl_assert(reuc->mode[2] == 0100644);
- cl_assert(git_oid_cmp(&reuc->oid[0], &ancestor_oid) == 0);
- cl_assert(git_oid_cmp(&reuc->oid[1], &our_oid) == 0);
- cl_assert(git_oid_cmp(&reuc->oid[2], &their_oid) == 0);
-}
-
-void test_index_reuc__add_no_ancestor(void)
-{
- git_oid ancestor_oid, our_oid, their_oid;
- const git_index_reuc_entry *reuc;
-
- memset(&ancestor_oid, 0x0, sizeof(git_oid));
- git_oid_fromstr(&our_oid, ONE_OUR_OID);
- git_oid_fromstr(&their_oid, ONE_THEIR_OID);
-
- cl_git_pass(git_index_reuc_add(repo_index, "newfile.txt",
- 0, NULL,
- 0100644, &our_oid,
- 0100644, &their_oid));
-
- cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "newfile.txt"));
-
- cl_assert_equal_s("newfile.txt", reuc->path);
- cl_assert(reuc->mode[0] == 0);
- cl_assert(reuc->mode[1] == 0100644);
- cl_assert(reuc->mode[2] == 0100644);
- cl_assert(git_oid_cmp(&reuc->oid[0], &ancestor_oid) == 0);
- cl_assert(git_oid_cmp(&reuc->oid[1], &our_oid) == 0);
- cl_assert(git_oid_cmp(&reuc->oid[2], &their_oid) == 0);
-}
-
-void test_index_reuc__read_bypath(void)
-{
- const git_index_reuc_entry *reuc;
- git_oid oid;
-
- cl_assert_equal_i(2, git_index_reuc_entrycount(repo_index));
-
- cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "two.txt"));
-
- cl_assert_equal_s("two.txt", reuc->path);
- cl_assert(reuc->mode[0] == 0100644);
- cl_assert(reuc->mode[1] == 0100644);
- cl_assert(reuc->mode[2] == 0100644);
- git_oid_fromstr(&oid, TWO_ANCESTOR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
- git_oid_fromstr(&oid, TWO_OUR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
- git_oid_fromstr(&oid, TWO_THEIR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
-
- cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "one.txt"));
-
- cl_assert_equal_s("one.txt", reuc->path);
- cl_assert(reuc->mode[0] == 0100644);
- cl_assert(reuc->mode[1] == 0100644);
- cl_assert(reuc->mode[2] == 0100644);
- git_oid_fromstr(&oid, ONE_ANCESTOR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
- git_oid_fromstr(&oid, ONE_OUR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
- git_oid_fromstr(&oid, ONE_THEIR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
-}
-
-void test_index_reuc__ignore_case(void)
-{
- const git_index_reuc_entry *reuc;
- git_oid oid;
- int index_caps;
-
- index_caps = git_index_caps(repo_index);
-
- index_caps &= ~GIT_INDEXCAP_IGNORE_CASE;
- cl_git_pass(git_index_set_caps(repo_index, index_caps));
-
- cl_assert(!git_index_reuc_get_bypath(repo_index, "TWO.txt"));
-
- index_caps |= GIT_INDEXCAP_IGNORE_CASE;
- cl_git_pass(git_index_set_caps(repo_index, index_caps));
-
- cl_assert_equal_i(2, git_index_reuc_entrycount(repo_index));
-
- cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "TWO.txt"));
-
- cl_assert_equal_s("two.txt", reuc->path);
- cl_assert(reuc->mode[0] == 0100644);
- cl_assert(reuc->mode[1] == 0100644);
- cl_assert(reuc->mode[2] == 0100644);
- git_oid_fromstr(&oid, TWO_ANCESTOR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
- git_oid_fromstr(&oid, TWO_OUR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
- git_oid_fromstr(&oid, TWO_THEIR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
-}
-
-void test_index_reuc__read_byindex(void)
-{
- const git_index_reuc_entry *reuc;
- git_oid oid;
-
- cl_assert_equal_i(2, git_index_reuc_entrycount(repo_index));
-
- cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 0));
-
- cl_assert_equal_s("one.txt", reuc->path);
- cl_assert(reuc->mode[0] == 0100644);
- cl_assert(reuc->mode[1] == 0100644);
- cl_assert(reuc->mode[2] == 0100644);
- git_oid_fromstr(&oid, ONE_ANCESTOR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
- git_oid_fromstr(&oid, ONE_OUR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
- git_oid_fromstr(&oid, ONE_THEIR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
-
- cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 1));
-
- cl_assert_equal_s("two.txt", reuc->path);
- cl_assert(reuc->mode[0] == 0100644);
- cl_assert(reuc->mode[1] == 0100644);
- cl_assert(reuc->mode[2] == 0100644);
- git_oid_fromstr(&oid, TWO_ANCESTOR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
- git_oid_fromstr(&oid, TWO_OUR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
- git_oid_fromstr(&oid, TWO_THEIR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
-}
-
-void test_index_reuc__updates_existing(void)
-{
- const git_index_reuc_entry *reuc;
- git_oid ancestor_oid, our_oid, their_oid, oid;
- int index_caps;
-
- git_index_clear(repo_index);
-
- index_caps = git_index_caps(repo_index);
-
- index_caps |= GIT_INDEXCAP_IGNORE_CASE;
- cl_git_pass(git_index_set_caps(repo_index, index_caps));
-
- git_oid_fromstr(&ancestor_oid, TWO_ANCESTOR_OID);
- git_oid_fromstr(&our_oid, TWO_OUR_OID);
- git_oid_fromstr(&their_oid, TWO_THEIR_OID);
-
- cl_git_pass(git_index_reuc_add(repo_index, "two.txt",
- 0100644, &ancestor_oid,
- 0100644, &our_oid,
- 0100644, &their_oid));
-
- cl_git_pass(git_index_reuc_add(repo_index, "TWO.txt",
- 0100644, &our_oid,
- 0100644, &their_oid,
- 0100644, &ancestor_oid));
-
- cl_assert_equal_i(1, git_index_reuc_entrycount(repo_index));
-
- cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 0));
-
- cl_assert_equal_s("TWO.txt", reuc->path);
- git_oid_fromstr(&oid, TWO_OUR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
- git_oid_fromstr(&oid, TWO_THEIR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
- git_oid_fromstr(&oid, TWO_ANCESTOR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
-}
-
-void test_index_reuc__remove(void)
-{
- git_oid oid;
- const git_index_reuc_entry *reuc;
-
- cl_assert_equal_i(2, git_index_reuc_entrycount(repo_index));
-
- cl_git_pass(git_index_reuc_remove(repo_index, 0));
- cl_git_fail(git_index_reuc_remove(repo_index, 1));
-
- cl_assert_equal_i(1, git_index_reuc_entrycount(repo_index));
-
- cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 0));
-
- cl_assert_equal_s("two.txt", reuc->path);
- cl_assert(reuc->mode[0] == 0100644);
- cl_assert(reuc->mode[1] == 0100644);
- cl_assert(reuc->mode[2] == 0100644);
- git_oid_fromstr(&oid, TWO_ANCESTOR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
- git_oid_fromstr(&oid, TWO_OUR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
- git_oid_fromstr(&oid, TWO_THEIR_OID);
- cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
-}
-
-void test_index_reuc__write(void)
-{
- git_oid ancestor_oid, our_oid, their_oid;
- const git_index_reuc_entry *reuc;
-
- git_index_clear(repo_index);
-
- /* Write out of order to ensure sorting is correct */
- git_oid_fromstr(&ancestor_oid, TWO_ANCESTOR_OID);
- git_oid_fromstr(&our_oid, TWO_OUR_OID);
- git_oid_fromstr(&their_oid, TWO_THEIR_OID);
-
- cl_git_pass(git_index_reuc_add(repo_index, "two.txt",
- 0100644, &ancestor_oid,
- 0100644, &our_oid,
- 0100644, &their_oid));
-
- git_oid_fromstr(&ancestor_oid, ONE_ANCESTOR_OID);
- git_oid_fromstr(&our_oid, ONE_OUR_OID);
- git_oid_fromstr(&their_oid, ONE_THEIR_OID);
-
- cl_git_pass(git_index_reuc_add(repo_index, "one.txt",
- 0100644, &ancestor_oid,
- 0100644, &our_oid,
- 0100644, &their_oid));
-
- cl_git_pass(git_index_write(repo_index));
-
- cl_git_pass(git_index_read(repo_index));
- cl_assert_equal_i(2, git_index_reuc_entrycount(repo_index));
-
- /* ensure sort order was round-tripped correct */
- cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 0));
- cl_assert_equal_s("one.txt", reuc->path);
-
- cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 1));
- cl_assert_equal_s("two.txt", reuc->path);
-}
-
-static int reuc_entry_exists(void)
-{
- return (git_index_reuc_get_bypath(repo_index, "newfile.txt") != NULL);
-}
-
-void test_index_reuc__cleaned_on_reset_hard(void)
-{
- git_object *target;
-
- retrieve_target_from_oid(&target, repo, "3a34580a35add43a4cf361e8e9a30060a905c876");
-
- test_index_reuc__add();
- cl_git_pass(git_reset(repo, target, GIT_RESET_HARD));
- cl_assert(reuc_entry_exists() == false);
-
- git_object_free(target);
-}
-
-void test_index_reuc__cleaned_on_reset_mixed(void)
-{
- git_object *target;
-
- retrieve_target_from_oid(&target, repo, "3a34580a35add43a4cf361e8e9a30060a905c876");
-
- test_index_reuc__add();
- cl_git_pass(git_reset(repo, target, GIT_RESET_MIXED));
- cl_assert(reuc_entry_exists() == false);
-
- git_object_free(target);
-}
-
-void test_index_reuc__retained_on_reset_soft(void)
-{
- git_object *target;
-
- retrieve_target_from_oid(&target, repo, "3a34580a35add43a4cf361e8e9a30060a905c876");
-
- git_reset(repo, target, GIT_RESET_HARD);
-
- test_index_reuc__add();
- cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT));
- cl_assert(reuc_entry_exists() == true);
-
- git_object_free(target);
-}
-
-void test_index_reuc__cleaned_on_checkout_tree(void)
-{
- git_oid oid;
- git_object *obj;
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
-
- opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY;
-
- test_index_reuc__add();
- git_reference_name_to_id(&oid, repo, "refs/heads/master");
- git_object_lookup(&obj, repo, &oid, GIT_OBJ_ANY);
- git_checkout_tree(repo, obj, &opts);
- cl_assert(reuc_entry_exists() == false);
-
- git_object_free(obj);
-}
-
-void test_index_reuc__cleaned_on_checkout_head(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
-
- opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY;
-
- test_index_reuc__add();
- git_checkout_head(repo, &opts);
- cl_assert(reuc_entry_exists() == false);
-}
-
-void test_index_reuc__retained_on_checkout_index(void)
-{
- git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
-
- opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY;
-
- test_index_reuc__add();
- git_checkout_index(repo, repo_index, &opts);
- cl_assert(reuc_entry_exists() == true);
-}
diff --git a/tests-clar/index/tests.c b/tests-clar/index/tests.c
deleted file mode 100644
index 1bc5e6a07..000000000
--- a/tests-clar/index/tests.c
+++ /dev/null
@@ -1,461 +0,0 @@
-#include "clar_libgit2.h"
-#include "index.h"
-
-static const size_t index_entry_count = 109;
-static const size_t index_entry_count_2 = 1437;
-#define TEST_INDEX_PATH cl_fixture("testrepo.git/index")
-#define TEST_INDEX2_PATH cl_fixture("gitgit.index")
-#define TEST_INDEXBIG_PATH cl_fixture("big.index")
-
-
-// Suite data
-struct test_entry {
- size_t index;
- char path[128];
- git_off_t file_size;
- git_time_t mtime;
-};
-
-static struct test_entry test_entries[] = {
- {4, "Makefile", 5064, 0x4C3F7F33},
- {62, "tests/Makefile", 2631, 0x4C3F7F33},
- {36, "src/index.c", 10014, 0x4C43368D},
- {6, "git.git-authors", 2709, 0x4C3F7F33},
- {48, "src/revobject.h", 1448, 0x4C3F7FE2}
-};
-
-// Helpers
-static void copy_file(const char *src, const char *dst)
-{
- git_buf source_buf = GIT_BUF_INIT;
- git_file dst_fd;
-
- cl_git_pass(git_futils_readbuffer(&source_buf, src));
-
- dst_fd = git_futils_creat_withpath(dst, 0777, 0666); //-V536
- if (dst_fd < 0)
- goto cleanup;
-
- cl_git_pass(p_write(dst_fd, source_buf.ptr, source_buf.size));
-
-cleanup:
- git_buf_free(&source_buf);
- p_close(dst_fd);
-}
-
-static void files_are_equal(const char *a, const char *b)
-{
- git_buf buf_a = GIT_BUF_INIT;
- git_buf buf_b = GIT_BUF_INIT;
- int pass;
-
- if (git_futils_readbuffer(&buf_a, a) < 0)
- cl_assert(0);
-
- if (git_futils_readbuffer(&buf_b, b) < 0) {
- git_buf_free(&buf_a);
- cl_assert(0);
- }
-
- pass = (buf_a.size == buf_b.size && !memcmp(buf_a.ptr, buf_b.ptr, buf_a.size));
-
- git_buf_free(&buf_a);
- git_buf_free(&buf_b);
-
- cl_assert(pass);
-}
-
-
-// Fixture setup and teardown
-void test_index_tests__initialize(void)
-{
-}
-
-void test_index_tests__empty_index(void)
-{
- git_index *index;
-
- cl_git_pass(git_index_open(&index, "in-memory-index"));
- cl_assert(index->on_disk == 0);
-
- cl_assert(git_index_entrycount(index) == 0);
- cl_assert(index->entries.sorted);
-
- git_index_free(index);
-}
-
-void test_index_tests__default_test_index(void)
-{
- git_index *index;
- unsigned int i;
- git_index_entry **entries;
-
- cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
- cl_assert(index->on_disk);
-
- cl_assert(git_index_entrycount(index) == index_entry_count);
- cl_assert(index->entries.sorted);
-
- entries = (git_index_entry **)index->entries.contents;
-
- for (i = 0; i < ARRAY_SIZE(test_entries); ++i) {
- git_index_entry *e = entries[test_entries[i].index];
-
- cl_assert_equal_s(e->path, test_entries[i].path);
- cl_assert(e->mtime.seconds == test_entries[i].mtime);
- cl_assert(e->file_size == test_entries[i].file_size);
- }
-
- git_index_free(index);
-}
-
-void test_index_tests__gitgit_index(void)
-{
- git_index *index;
-
- cl_git_pass(git_index_open(&index, TEST_INDEX2_PATH));
- cl_assert(index->on_disk);
-
- cl_assert(git_index_entrycount(index) == index_entry_count_2);
- cl_assert(index->entries.sorted);
- cl_assert(index->tree != NULL);
-
- git_index_free(index);
-}
-
-void test_index_tests__find_in_existing(void)
-{
- git_index *index;
- unsigned int i;
-
- cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
-
- for (i = 0; i < ARRAY_SIZE(test_entries); ++i) {
- size_t idx;
-
- cl_assert(!git_index_find(&idx, index, test_entries[i].path));
- cl_assert(idx == test_entries[i].index);
- }
-
- git_index_free(index);
-}
-
-void test_index_tests__find_in_empty(void)
-{
- git_index *index;
- unsigned int i;
-
- cl_git_pass(git_index_open(&index, "fake-index"));
-
- for (i = 0; i < ARRAY_SIZE(test_entries); ++i) {
- cl_assert(GIT_ENOTFOUND == git_index_find(NULL, index, test_entries[i].path));
- }
-
- git_index_free(index);
-}
-
-void test_index_tests__write(void)
-{
- git_index *index;
-
- copy_file(TEST_INDEXBIG_PATH, "index_rewrite");
-
- cl_git_pass(git_index_open(&index, "index_rewrite"));
- cl_assert(index->on_disk);
-
- cl_git_pass(git_index_write(index));
- files_are_equal(TEST_INDEXBIG_PATH, "index_rewrite");
-
- git_index_free(index);
-
- p_unlink("index_rewrite");
-}
-
-void test_index_tests__sort0(void)
-{
- // sort the entires in an index
- /*
- * TODO: This no longer applies:
- * index sorting in Git uses some specific changes to the way
- * directories are sorted.
- *
- * We need to specificially check for this by creating a new
- * index, adding entries in random order and then
- * checking for consistency
- */
-}
-
-void test_index_tests__sort1(void)
-{
- // sort the entires in an empty index
- git_index *index;
-
- cl_git_pass(git_index_open(&index, "fake-index"));
-
- /* FIXME: this test is slightly dumb */
- cl_assert(index->entries.sorted);
-
- git_index_free(index);
-}
-
-static void cleanup_myrepo(void *opaque)
-{
- GIT_UNUSED(opaque);
- cl_fixture_cleanup("myrepo");
-}
-
-void test_index_tests__add(void)
-{
- git_index *index;
- git_filebuf file = GIT_FILEBUF_INIT;
- git_repository *repo;
- const git_index_entry *entry;
- git_oid id1;
-
- cl_set_cleanup(&cleanup_myrepo, NULL);
-
- /* Intialize a new repository */
- cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
-
- /* Ensure we're the only guy in the room */
- cl_git_pass(git_repository_index(&index, repo));
- cl_assert(git_index_entrycount(index) == 0);
-
- /* Create a new file in the working directory */
- cl_git_pass(git_futils_mkpath2file("myrepo/test.txt", 0777));
- cl_git_pass(git_filebuf_open(&file, "myrepo/test.txt", 0));
- cl_git_pass(git_filebuf_write(&file, "hey there\n", 10));
- cl_git_pass(git_filebuf_commit(&file, 0666));
-
- /* Store the expected hash of the file/blob
- * This has been generated by executing the following
- * $ echo "hey there" | git hash-object --stdin
- */
- cl_git_pass(git_oid_fromstr(&id1, "a8233120f6ad708f843d861ce2b7228ec4e3dec6"));
-
- /* Add the new file to the index */
- cl_git_pass(git_index_add_bypath(index, "test.txt"));
-
- /* Wow... it worked! */
- cl_assert(git_index_entrycount(index) == 1);
- entry = git_index_get_byindex(index, 0);
-
- /* And the built-in hashing mechanism worked as expected */
- cl_assert(git_oid_cmp(&id1, &entry->oid) == 0);
-
- /* Test access by path instead of index */
- cl_assert((entry = git_index_get_bypath(index, "test.txt", 0)) != NULL);
- cl_assert(git_oid_cmp(&id1, &entry->oid) == 0);
-
- git_index_free(index);
- git_repository_free(repo);
-}
-
-static void cleanup_1397(void *opaque)
-{
- GIT_UNUSED(opaque);
- cl_git_sandbox_cleanup();
-}
-
-void test_index_tests__add_issue_1397(void)
-{
- git_index *index;
- git_repository *repo;
- const git_index_entry *entry;
- git_oid id1;
-
- cl_set_cleanup(&cleanup_1397, NULL);
-
- repo = cl_git_sandbox_init("issue_1397");
-
- cl_repo_set_bool(repo, "core.autocrlf", true);
-
- /* Ensure we're the only guy in the room */
- cl_git_pass(git_repository_index(&index, repo));
-
- /* Store the expected hash of the file/blob
- * This has been generated by executing the following
- * $ git hash-object crlf_file.txt
- */
- cl_git_pass(git_oid_fromstr(&id1, "8312e0889a9cbab77c732b6bc39b51a683e3a318"));
-
- /* Make sure the initial SHA-1 is correct */
- cl_assert((entry = git_index_get_bypath(index, "crlf_file.txt", 0)) != NULL);
- cl_assert_(git_oid_cmp(&id1, &entry->oid) == 0, "first oid check");
-
- /* Update the index */
- cl_git_pass(git_index_add_bypath(index, "crlf_file.txt"));
-
- /* Check the new SHA-1 */
- cl_assert((entry = git_index_get_bypath(index, "crlf_file.txt", 0)) != NULL);
- cl_assert_(git_oid_cmp(&id1, &entry->oid) == 0, "second oid check");
-
- git_index_free(index);
-}
-
-void test_index_tests__add_bypath_to_a_bare_repository_returns_EBAREPO(void)
-{
- git_repository *bare_repo;
- git_index *index;
-
- cl_git_pass(git_repository_open(&bare_repo, cl_fixture("testrepo.git")));
- cl_git_pass(git_repository_index(&index, bare_repo));
-
- cl_assert_equal_i(GIT_EBAREREPO, git_index_add_bypath(index, "test.txt"));
-
- git_index_free(index);
- git_repository_free(bare_repo);
-}
-
-/* Test that writing an invalid filename fails */
-void test_index_tests__write_invalid_filename(void)
-{
- git_repository *repo;
- git_index *index;
- git_oid expected;
-
- p_mkdir("read_tree", 0700);
-
- cl_git_pass(git_repository_init(&repo, "./read_tree", 0));
- cl_git_pass(git_repository_index(&index, repo));
-
- cl_assert(git_index_entrycount(index) == 0);
-
- cl_git_mkfile("./read_tree/.git/hello", NULL);
-
- cl_git_pass(git_index_add_bypath(index, ".git/hello"));
-
- /* write-tree */
- cl_git_fail(git_index_write_tree(&expected, index));
-
- git_index_free(index);
- git_repository_free(repo);
-
- cl_fixture_cleanup("read_tree");
-}
-
-void test_index_tests__remove_entry(void)
-{
- git_repository *repo;
- git_index *index;
-
- p_mkdir("index_test", 0770);
-
- cl_git_pass(git_repository_init(&repo, "index_test", 0));
- cl_git_pass(git_repository_index(&index, repo));
- cl_assert(git_index_entrycount(index) == 0);
-
- cl_git_mkfile("index_test/hello", NULL);
- cl_git_pass(git_index_add_bypath(index, "hello"));
- cl_git_pass(git_index_write(index));
-
- cl_git_pass(git_index_read(index)); /* reload */
- cl_assert(git_index_entrycount(index) == 1);
- cl_assert(git_index_get_bypath(index, "hello", 0) != NULL);
-
- cl_git_pass(git_index_remove(index, "hello", 0));
- cl_git_pass(git_index_write(index));
-
- cl_git_pass(git_index_read(index)); /* reload */
- cl_assert(git_index_entrycount(index) == 0);
- cl_assert(git_index_get_bypath(index, "hello", 0) == NULL);
-
- git_index_free(index);
- git_repository_free(repo);
- cl_fixture_cleanup("index_test");
-}
-
-void test_index_tests__remove_directory(void)
-{
- git_repository *repo;
- git_index *index;
-
- p_mkdir("index_test", 0770);
-
- cl_git_pass(git_repository_init(&repo, "index_test", 0));
- cl_git_pass(git_repository_index(&index, repo));
- cl_assert_equal_i(0, (int)git_index_entrycount(index));
-
- p_mkdir("index_test/a", 0770);
- cl_git_mkfile("index_test/a/1.txt", NULL);
- cl_git_mkfile("index_test/a/2.txt", NULL);
- cl_git_mkfile("index_test/a/3.txt", NULL);
- cl_git_mkfile("index_test/b.txt", NULL);
-
- cl_git_pass(git_index_add_bypath(index, "a/1.txt"));
- cl_git_pass(git_index_add_bypath(index, "a/2.txt"));
- cl_git_pass(git_index_add_bypath(index, "a/3.txt"));
- cl_git_pass(git_index_add_bypath(index, "b.txt"));
- cl_git_pass(git_index_write(index));
-
- cl_git_pass(git_index_read(index)); /* reload */
- cl_assert_equal_i(4, (int)git_index_entrycount(index));
- cl_assert(git_index_get_bypath(index, "a/1.txt", 0) != NULL);
- cl_assert(git_index_get_bypath(index, "a/2.txt", 0) != NULL);
- cl_assert(git_index_get_bypath(index, "b.txt", 0) != NULL);
-
- cl_git_pass(git_index_remove(index, "a/1.txt", 0));
- cl_git_pass(git_index_write(index));
-
- cl_git_pass(git_index_read(index)); /* reload */
- cl_assert_equal_i(3, (int)git_index_entrycount(index));
- cl_assert(git_index_get_bypath(index, "a/1.txt", 0) == NULL);
- cl_assert(git_index_get_bypath(index, "a/2.txt", 0) != NULL);
- cl_assert(git_index_get_bypath(index, "b.txt", 0) != NULL);
-
- cl_git_pass(git_index_remove_directory(index, "a", 0));
- cl_git_pass(git_index_write(index));
-
- cl_git_pass(git_index_read(index)); /* reload */
- cl_assert_equal_i(1, (int)git_index_entrycount(index));
- cl_assert(git_index_get_bypath(index, "a/1.txt", 0) == NULL);
- cl_assert(git_index_get_bypath(index, "a/2.txt", 0) == NULL);
- cl_assert(git_index_get_bypath(index, "b.txt", 0) != NULL);
-
- git_index_free(index);
- git_repository_free(repo);
- cl_fixture_cleanup("index_test");
-}
-
-void test_index_tests__preserves_case(void)
-{
- git_repository *repo;
- git_index *index;
- const git_index_entry *entry;
- int index_caps;
-
- cl_set_cleanup(&cleanup_myrepo, NULL);
-
- cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
- cl_git_pass(git_repository_index(&index, repo));
-
- index_caps = git_index_caps(index);
-
- cl_git_rewritefile("myrepo/test.txt", "hey there\n");
- cl_git_pass(git_index_add_bypath(index, "test.txt"));
-
- cl_git_pass(p_rename("myrepo/test.txt", "myrepo/TEST.txt"));
- cl_git_rewritefile("myrepo/TEST.txt", "hello again\n");
- cl_git_pass(git_index_add_bypath(index, "TEST.txt"));
-
- if (index_caps & GIT_INDEXCAP_IGNORE_CASE)
- cl_assert_equal_i(1, (int)git_index_entrycount(index));
- else
- cl_assert_equal_i(2, (int)git_index_entrycount(index));
-
- /* Test access by path instead of index */
- cl_assert((entry = git_index_get_bypath(index, "test.txt", 0)) != NULL);
- /* The path should *not* have changed without an explicit remove */
- cl_assert(git__strcmp(entry->path, "test.txt") == 0);
-
- cl_assert((entry = git_index_get_bypath(index, "TEST.txt", 0)) != NULL);
- if (index_caps & GIT_INDEXCAP_IGNORE_CASE)
- /* The path should *not* have changed without an explicit remove */
- cl_assert(git__strcmp(entry->path, "test.txt") == 0);
- else
- cl_assert(git__strcmp(entry->path, "TEST.txt") == 0);
-
- git_index_free(index);
- git_repository_free(repo);
-}
-
diff --git a/tests-clar/merge/merge_helpers.c b/tests-clar/merge/merge_helpers.c
deleted file mode 100644
index e4092787c..000000000
--- a/tests-clar/merge/merge_helpers.c
+++ /dev/null
@@ -1,310 +0,0 @@
-#include "clar_libgit2.h"
-#include "fileops.h"
-#include "refs.h"
-#include "tree.h"
-#include "merge_helpers.h"
-#include "merge.h"
-#include "git2/merge.h"
-#include "git2/sys/index.h"
-
-int merge_trees_from_branches(
- git_index **index, git_repository *repo,
- const char *ours_name, const char *theirs_name,
- git_merge_tree_opts *opts)
-{
- git_commit *our_commit, *their_commit, *ancestor_commit = NULL;
- git_tree *our_tree, *their_tree, *ancestor_tree = NULL;
- git_oid our_oid, their_oid, ancestor_oid;
- git_buf branch_buf = GIT_BUF_INIT;
- int error;
-
- git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, ours_name);
- cl_git_pass(git_reference_name_to_id(&our_oid, repo, branch_buf.ptr));
- cl_git_pass(git_commit_lookup(&our_commit, repo, &our_oid));
-
- git_buf_clear(&branch_buf);
- git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, theirs_name);
- cl_git_pass(git_reference_name_to_id(&their_oid, repo, branch_buf.ptr));
- cl_git_pass(git_commit_lookup(&their_commit, repo, &their_oid));
-
- error = git_merge_base(&ancestor_oid, repo, git_commit_id(our_commit), git_commit_id(their_commit));
-
- if (error != GIT_ENOTFOUND) {
- cl_git_pass(error);
-
- cl_git_pass(git_commit_lookup(&ancestor_commit, repo, &ancestor_oid));
- cl_git_pass(git_commit_tree(&ancestor_tree, ancestor_commit));
- }
-
- cl_git_pass(git_commit_tree(&our_tree, our_commit));
- cl_git_pass(git_commit_tree(&their_tree, their_commit));
-
- cl_git_pass(git_merge_trees(index, repo, ancestor_tree, our_tree, their_tree, opts));
-
- git_buf_free(&branch_buf);
- git_tree_free(our_tree);
- git_tree_free(their_tree);
- git_tree_free(ancestor_tree);
- git_commit_free(our_commit);
- git_commit_free(their_commit);
- git_commit_free(ancestor_commit);
-
- return 0;
-}
-
-void merge__dump_index_entries(git_vector *index_entries)
-{
- size_t i;
- const git_index_entry *index_entry;
-
- printf ("\nINDEX [%d]:\n", (int)index_entries->length);
- for (i = 0; i < index_entries->length; i++) {
- index_entry = index_entries->contents[i];
-
- printf("%o ", index_entry->mode);
- printf("%s ", git_oid_allocfmt(&index_entry->oid));
- printf("%d ", git_index_entry_stage(index_entry));
- printf("%s ", index_entry->path);
- printf("\n");
- }
- printf("\n");
-}
-
-void merge__dump_names(git_index *index)
-{
- size_t i;
- const git_index_name_entry *conflict_name;
-
- for (i = 0; i < git_index_name_entrycount(index); i++) {
- conflict_name = git_index_name_get_byindex(index, i);
-
- printf("%s %s %s\n", conflict_name->ancestor, conflict_name->ours, conflict_name->theirs);
- }
- printf("\n");
-}
-
-void merge__dump_reuc(git_index *index)
-{
- size_t i;
- const git_index_reuc_entry *reuc;
-
- printf ("\nREUC:\n");
- for (i = 0; i < git_index_reuc_entrycount(index); i++) {
- reuc = git_index_reuc_get_byindex(index, i);
-
- printf("%s ", reuc->path);
- printf("%o ", reuc->mode[0]);
- printf("%s\n", git_oid_allocfmt(&reuc->oid[0]));
- printf(" %o ", reuc->mode[1]);
- printf(" %s\n", git_oid_allocfmt(&reuc->oid[1]));
- printf(" %o ", reuc->mode[2]);
- printf(" %s ", git_oid_allocfmt(&reuc->oid[2]));
- printf("\n");
- }
- printf("\n");
-}
-
-static int index_entry_eq_merge_index_entry(const struct merge_index_entry *expected, const git_index_entry *actual)
-{
- git_oid expected_oid;
- bool test_oid;
-
- if (strlen(expected->oid_str) != 0) {
- cl_git_pass(git_oid_fromstr(&expected_oid, expected->oid_str));
- test_oid = 1;
- } else
- test_oid = 0;
-
- if (actual->mode != expected->mode ||
- (test_oid && git_oid_cmp(&actual->oid, &expected_oid) != 0) ||
- git_index_entry_stage(actual) != expected->stage)
- return 0;
-
- if (actual->mode == 0 && (actual->path != NULL || strlen(expected->path) > 0))
- return 0;
-
- if (actual->mode != 0 && (strcmp(actual->path, expected->path) != 0))
- return 0;
-
- return 1;
-}
-
-static int name_entry_eq(const char *expected, const char *actual)
-{
- if (strlen(expected) == 0)
- return (actual == NULL) ? 1 : 0;
-
- return (strcmp(expected, actual) == 0) ? 1 : 0;
-}
-
-static int name_entry_eq_merge_name_entry(const struct merge_name_entry *expected, const git_index_name_entry *actual)
-{
- if (name_entry_eq(expected->ancestor_path, actual->ancestor) == 0 ||
- name_entry_eq(expected->our_path, actual->ours) == 0 ||
- name_entry_eq(expected->their_path, actual->theirs) == 0)
- return 0;
-
- return 1;
-}
-
-static int index_conflict_data_eq_merge_diff(const struct merge_index_conflict_data *expected, git_merge_diff *actual)
-{
- if (!index_entry_eq_merge_index_entry(&expected->ancestor.entry, &actual->ancestor_entry) ||
- !index_entry_eq_merge_index_entry(&expected->ours.entry, &actual->our_entry) ||
- !index_entry_eq_merge_index_entry(&expected->theirs.entry, &actual->their_entry))
- return 0;
-
- if (expected->ours.status != actual->our_status ||
- expected->theirs.status != actual->their_status)
- return 0;
-
- return 1;
-}
-
-int merge_test_merge_conflicts(git_vector *conflicts, const struct merge_index_conflict_data expected[], size_t expected_len)
-{
- git_merge_diff *actual;
- size_t i;
-
- if (conflicts->length != expected_len)
- return 0;
-
- for (i = 0; i < expected_len; i++) {
- actual = conflicts->contents[i];
-
- if (!index_conflict_data_eq_merge_diff(&expected[i], actual))
- return 0;
- }
-
- return 1;
-}
-
-int merge_test_index(git_index *index, const struct merge_index_entry expected[], size_t expected_len)
-{
- size_t i;
- const git_index_entry *index_entry;
-
- /*
- dump_index_entries(&index->entries);
- */
-
- if (git_index_entrycount(index) != expected_len)
- return 0;
-
- for (i = 0; i < expected_len; i++) {
- if ((index_entry = git_index_get_byindex(index, i)) == NULL)
- return 0;
-
- if (!index_entry_eq_merge_index_entry(&expected[i], index_entry))
- return 0;
- }
-
- return 1;
-}
-
-int merge_test_names(git_index *index, const struct merge_name_entry expected[], size_t expected_len)
-{
- size_t i;
- const git_index_name_entry *name_entry;
-
- /*
- dump_names(index);
- */
-
- if (git_index_name_entrycount(index) != expected_len)
- return 0;
-
- for (i = 0; i < expected_len; i++) {
- if ((name_entry = git_index_name_get_byindex(index, i)) == NULL)
- return 0;
-
- if (! name_entry_eq_merge_name_entry(&expected[i], name_entry))
- return 0;
- }
-
- return 1;
-}
-
-int merge_test_reuc(git_index *index, const struct merge_reuc_entry expected[], size_t expected_len)
-{
- size_t i;
- const git_index_reuc_entry *reuc_entry;
- git_oid expected_oid;
-
- /*
- dump_reuc(index);
- */
-
- if (git_index_reuc_entrycount(index) != expected_len)
- return 0;
-
- for (i = 0; i < expected_len; i++) {
- if ((reuc_entry = git_index_reuc_get_byindex(index, i)) == NULL)
- return 0;
-
- if (strcmp(reuc_entry->path, expected[i].path) != 0 ||
- reuc_entry->mode[0] != expected[i].ancestor_mode ||
- reuc_entry->mode[1] != expected[i].our_mode ||
- reuc_entry->mode[2] != expected[i].their_mode)
- return 0;
-
- if (expected[i].ancestor_mode > 0) {
- cl_git_pass(git_oid_fromstr(&expected_oid, expected[i].ancestor_oid_str));
-
- if (git_oid_cmp(&reuc_entry->oid[0], &expected_oid) != 0)
- return 0;
- }
-
- if (expected[i].our_mode > 0) {
- cl_git_pass(git_oid_fromstr(&expected_oid, expected[i].our_oid_str));
-
- if (git_oid_cmp(&reuc_entry->oid[1], &expected_oid) != 0)
- return 0;
- }
-
- if (expected[i].their_mode > 0) {
- cl_git_pass(git_oid_fromstr(&expected_oid, expected[i].their_oid_str));
-
- if (git_oid_cmp(&reuc_entry->oid[2], &expected_oid) != 0)
- return 0;
- }
- }
-
- return 1;
-}
-
-int dircount(void *payload, git_buf *pathbuf)
-{
- int *entries = payload;
- size_t len = git_buf_len(pathbuf);
-
- if (len < 5 || strcmp(pathbuf->ptr + (git_buf_len(pathbuf) - 5), "/.git") != 0)
- (*entries)++;
-
- return 0;
-}
-
-int merge_test_workdir(git_repository *repo, const struct merge_index_entry expected[], size_t expected_len)
-{
- size_t actual_len = 0, i;
- git_oid actual_oid, expected_oid;
- git_buf wd = GIT_BUF_INIT;
-
- git_buf_puts(&wd, repo->workdir);
- git_path_direach(&wd, dircount, &actual_len);
-
- if (actual_len != expected_len)
- return 0;
-
- for (i = 0; i < expected_len; i++) {
- git_blob_create_fromworkdir(&actual_oid, repo, expected[i].path);
- git_oid_fromstr(&expected_oid, expected[i].oid_str);
-
- if (git_oid_cmp(&actual_oid, &expected_oid) != 0)
- return 0;
- }
-
- git_buf_free(&wd);
-
- return 1;
-}
diff --git a/tests-clar/merge/merge_helpers.h b/tests-clar/merge/merge_helpers.h
deleted file mode 100644
index cb718e01a..000000000
--- a/tests-clar/merge/merge_helpers.h
+++ /dev/null
@@ -1,59 +0,0 @@
-#ifndef INCLUDE_cl_merge_helpers_h__
-#define INCLUDE_cl_merge_helpers_h__
-
-#include "merge.h"
-#include "git2/merge.h"
-
-struct merge_index_entry {
- uint16_t mode;
- char oid_str[41];
- int stage;
- char path[128];
-};
-
-struct merge_name_entry {
- char ancestor_path[128];
- char our_path[128];
- char their_path[128];
-};
-
-struct merge_index_with_status {
- struct merge_index_entry entry;
- unsigned int status;
-};
-
-struct merge_reuc_entry {
- char path[128];
- unsigned int ancestor_mode;
- unsigned int our_mode;
- unsigned int their_mode;
- char ancestor_oid_str[41];
- char our_oid_str[41];
- char their_oid_str[41];
-};
-
-struct merge_index_conflict_data {
- struct merge_index_with_status ancestor;
- struct merge_index_with_status ours;
- struct merge_index_with_status theirs;
- git_merge_diff_type_t change_type;
-};
-
-int merge_trees_from_branches(
- git_index **index, git_repository *repo,
- const char *ours_name, const char *theirs_name,
- git_merge_tree_opts *opts);
-
-int merge_test_diff_list(git_merge_diff_list *diff_list, const struct merge_index_entry expected[], size_t expected_len);
-
-int merge_test_merge_conflicts(git_vector *conflicts, const struct merge_index_conflict_data expected[], size_t expected_len);
-
-int merge_test_index(git_index *index, const struct merge_index_entry expected[], size_t expected_len);
-
-int merge_test_names(git_index *index, const struct merge_name_entry expected[], size_t expected_len);
-
-int merge_test_reuc(git_index *index, const struct merge_reuc_entry expected[], size_t expected_len);
-
-int merge_test_workdir(git_repository *repo, const struct merge_index_entry expected[], size_t expected_len);
-
-#endif
diff --git a/tests-clar/merge/trees/automerge.c b/tests-clar/merge/trees/automerge.c
deleted file mode 100644
index 04a7beff6..000000000
--- a/tests-clar/merge/trees/automerge.c
+++ /dev/null
@@ -1,217 +0,0 @@
-#include "clar_libgit2.h"
-#include "git2/repository.h"
-#include "git2/merge.h"
-#include "buffer.h"
-#include "merge.h"
-#include "../merge_helpers.h"
-#include "fileops.h"
-
-static git_repository *repo;
-
-#define TEST_REPO_PATH "merge-resolve"
-#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
-
-#define THEIRS_AUTOMERGE_BRANCH "branch"
-
-#define THEIRS_UNRELATED_BRANCH "unrelated"
-#define THEIRS_UNRELATED_OID "55b4e4687e7a0d9ca367016ed930f385d4022e6f"
-#define THEIRS_UNRELATED_PARENT "d6cf6c7741b3316826af1314042550c97ded1d50"
-
-#define OURS_DIRECTORY_FILE "df_side1"
-#define THEIRS_DIRECTORY_FILE "df_side2"
-
-/* Non-conflicting files, index entries are common to every merge operation */
-#define ADDED_IN_MASTER_INDEX_ENTRY \
- { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" }
-#define AUTOMERGEABLE_INDEX_ENTRY \
- { 0100644, "f2e1550a0c9e53d5811175864a29536642ae3821", 0, "automergeable.txt" }
-#define CHANGED_IN_BRANCH_INDEX_ENTRY \
- { 0100644, "4eb04c9e79e88f6640d01ff5b25ca2a60764f216", 0, "changed-in-branch.txt" }
-#define CHANGED_IN_MASTER_INDEX_ENTRY \
- { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" }
-#define UNCHANGED_INDEX_ENTRY \
- { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" }
-
-/* Expected REUC entries */
-#define AUTOMERGEABLE_REUC_ENTRY \
- { "automergeable.txt", 0100644, 0100644, 0100644, \
- "6212c31dab5e482247d7977e4f0dd3601decf13b", \
- "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", \
- "058541fc37114bfc1dddf6bd6bffc7fae5c2e6fe" }
-#define CONFLICTING_REUC_ENTRY \
- { "conflicting.txt", 0100644, 0100644, 0100644, \
- "d427e0b2e138501a3d15cc376077a3631e15bd46", \
- "4e886e602529caa9ab11d71f86634bd1b6e0de10", \
- "2bd0a343aeef7a2cf0d158478966a6e587ff3863" }
-#define REMOVED_IN_BRANCH_REUC_ENTRY \
- { "removed-in-branch.txt", 0100644, 0100644, 0, \
- "dfe3f22baa1f6fce5447901c3086bae368de6bdd", \
- "dfe3f22baa1f6fce5447901c3086bae368de6bdd", \
- "" }
-#define REMOVED_IN_MASTER_REUC_ENTRY \
- { "removed-in-master.txt", 0100644, 0, 0100644, \
- "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", \
- "", \
- "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5" }
-
-#define AUTOMERGEABLE_MERGED_FILE \
- "this file is changed in master\n" \
- "this file is automergeable\n" \
- "this file is automergeable\n" \
- "this file is automergeable\n" \
- "this file is automergeable\n" \
- "this file is automergeable\n" \
- "this file is automergeable\n" \
- "this file is automergeable\n" \
- "this file is changed in branch\n"
-
-#define AUTOMERGEABLE_MERGED_FILE_CRLF \
- "this file is changed in master\r\n" \
- "this file is automergeable\r\n" \
- "this file is automergeable\r\n" \
- "this file is automergeable\r\n" \
- "this file is automergeable\r\n" \
- "this file is automergeable\r\n" \
- "this file is automergeable\r\n" \
- "this file is automergeable\r\n" \
- "this file is changed in branch\r\n"
-
-// Fixture setup and teardown
-void test_merge_trees_automerge__initialize(void)
-{
- repo = cl_git_sandbox_init(TEST_REPO_PATH);
-}
-
-void test_merge_trees_automerge__cleanup(void)
-{
- cl_git_sandbox_cleanup();
-}
-
-void test_merge_trees_automerge__automerge(void)
-{
- git_index *index;
- const git_index_entry *entry;
- git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT;
- git_blob *blob;
-
- struct merge_index_entry merge_index_entries[] = {
- ADDED_IN_MASTER_INDEX_ENTRY,
- AUTOMERGEABLE_INDEX_ENTRY,
- CHANGED_IN_BRANCH_INDEX_ENTRY,
- CHANGED_IN_MASTER_INDEX_ENTRY,
-
- { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" },
- { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
- { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" },
-
- UNCHANGED_INDEX_ENTRY,
- };
-
- struct merge_reuc_entry merge_reuc_entries[] = {
- AUTOMERGEABLE_REUC_ENTRY,
- REMOVED_IN_BRANCH_REUC_ENTRY,
- REMOVED_IN_MASTER_REUC_ENTRY
- };
-
- cl_git_pass(merge_trees_from_branches(&index, repo, "master", THEIRS_AUTOMERGE_BRANCH, &opts));
-
- cl_assert(merge_test_index(index, merge_index_entries, 8));
- cl_assert(merge_test_reuc(index, merge_reuc_entries, 3));
-
- cl_assert((entry = git_index_get_bypath(index, "automergeable.txt", 0)) != NULL);
- cl_assert(entry->file_size == strlen(AUTOMERGEABLE_MERGED_FILE));
-
- cl_git_pass(git_object_lookup((git_object **)&blob, repo, &entry->oid, GIT_OBJ_BLOB));
- cl_assert(memcmp(git_blob_rawcontent(blob), AUTOMERGEABLE_MERGED_FILE, entry->file_size) == 0);
-
- git_index_free(index);
- git_blob_free(blob);
-}
-
-void test_merge_trees_automerge__favor_ours(void)
-{
- git_index *index;
- git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT;
-
- struct merge_index_entry merge_index_entries[] = {
- ADDED_IN_MASTER_INDEX_ENTRY,
- AUTOMERGEABLE_INDEX_ENTRY,
- CHANGED_IN_BRANCH_INDEX_ENTRY,
- CHANGED_IN_MASTER_INDEX_ENTRY,
- { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt" },
- UNCHANGED_INDEX_ENTRY,
- };
-
- struct merge_reuc_entry merge_reuc_entries[] = {
- AUTOMERGEABLE_REUC_ENTRY,
- CONFLICTING_REUC_ENTRY,
- REMOVED_IN_BRANCH_REUC_ENTRY,
- REMOVED_IN_MASTER_REUC_ENTRY,
- };
-
- opts.automerge_flags = GIT_MERGE_AUTOMERGE_FAVOR_OURS;
-
- cl_git_pass(merge_trees_from_branches(&index, repo, "master", THEIRS_AUTOMERGE_BRANCH, &opts));
-
- cl_assert(merge_test_index(index, merge_index_entries, 6));
- cl_assert(merge_test_reuc(index, merge_reuc_entries, 4));
-
- git_index_free(index);
-}
-
-void test_merge_trees_automerge__favor_theirs(void)
-{
- git_index *index;
- git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT;
-
- struct merge_index_entry merge_index_entries[] = {
- ADDED_IN_MASTER_INDEX_ENTRY,
- AUTOMERGEABLE_INDEX_ENTRY,
- CHANGED_IN_BRANCH_INDEX_ENTRY,
- CHANGED_IN_MASTER_INDEX_ENTRY,
- { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 0, "conflicting.txt" },
- UNCHANGED_INDEX_ENTRY,
- };
-
- struct merge_reuc_entry merge_reuc_entries[] = {
- AUTOMERGEABLE_REUC_ENTRY,
- CONFLICTING_REUC_ENTRY,
- REMOVED_IN_BRANCH_REUC_ENTRY,
- REMOVED_IN_MASTER_REUC_ENTRY,
- };
-
- opts.automerge_flags = GIT_MERGE_AUTOMERGE_FAVOR_THEIRS;
-
- cl_git_pass(merge_trees_from_branches(&index, repo, "master", THEIRS_AUTOMERGE_BRANCH, &opts));
-
- cl_assert(merge_test_index(index, merge_index_entries, 6));
- cl_assert(merge_test_reuc(index, merge_reuc_entries, 4));
-
- git_index_free(index);
-}
-
-void test_merge_trees_automerge__unrelated(void)
-{
- git_index *index;
- git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT;
-
- struct merge_index_entry merge_index_entries[] = {
- { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" },
- { 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 2, "automergeable.txt" },
- { 0100644, "d07ec190c306ec690bac349e87d01c4358e49bb2", 3, "automergeable.txt" },
- { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt" },
- { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" },
- { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
- { 0100644, "4b253da36a0ae8bfce63aeabd8c5b58429925594", 3, "conflicting.txt" },
- { 0100644, "ef58fdd8086c243bdc81f99e379acacfd21d32d6", 0, "new-in-unrelated1.txt" },
- { 0100644, "948ba6e701c1edab0c2d394fb7c5538334129793", 0, "new-in-unrelated2.txt" },
- { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" },
- { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" },
- };
-
- cl_git_pass(merge_trees_from_branches(&index, repo, "master", THEIRS_UNRELATED_BRANCH, &opts));
-
- cl_assert(merge_test_index(index, merge_index_entries, 11));
-
- git_index_free(index);
-}
diff --git a/tests-clar/merge/workdir/setup.c b/tests-clar/merge/workdir/setup.c
deleted file mode 100644
index 1c8403221..000000000
--- a/tests-clar/merge/workdir/setup.c
+++ /dev/null
@@ -1,966 +0,0 @@
-#include "clar_libgit2.h"
-#include "git2/repository.h"
-#include "git2/merge.h"
-#include "merge.h"
-#include "refs.h"
-#include "fileops.h"
-
-static git_repository *repo;
-static git_index *repo_index;
-
-#define TEST_REPO_PATH "merge-resolve"
-#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
-
-#define ORIG_HEAD "bd593285fc7fe4ca18ccdbabf027f5d689101452"
-
-#define THEIRS_SIMPLE_BRANCH "branch"
-#define THEIRS_SIMPLE_OID "7cb63eed597130ba4abb87b3e544b85021905520"
-
-#define OCTO1_BRANCH "octo1"
-#define OCTO1_OID "16f825815cfd20a07a75c71554e82d8eede0b061"
-
-#define OCTO2_BRANCH "octo2"
-#define OCTO2_OID "158dc7bedb202f5b26502bf3574faa7f4238d56c"
-
-#define OCTO3_BRANCH "octo3"
-#define OCTO3_OID "50ce7d7d01217679e26c55939eef119e0c93e272"
-
-#define OCTO4_BRANCH "octo4"
-#define OCTO4_OID "54269b3f6ec3d7d4ede24dd350dd5d605495c3ae"
-
-#define OCTO5_BRANCH "octo5"
-#define OCTO5_OID "e4f618a2c3ed0669308735727df5ebf2447f022f"
-
-// Fixture setup and teardown
-void test_merge_workdir_setup__initialize(void)
-{
- repo = cl_git_sandbox_init(TEST_REPO_PATH);
- git_repository_index(&repo_index, repo);
-}
-
-void test_merge_workdir_setup__cleanup(void)
-{
- git_index_free(repo_index);
- cl_git_sandbox_cleanup();
-}
-
-static bool test_file_contents(const char *filename, const char *expected)
-{
- git_buf file_path_buf = GIT_BUF_INIT, file_buf = GIT_BUF_INIT;
- bool equals;
-
- git_buf_printf(&file_path_buf, "%s/%s", git_repository_path(repo), filename);
-
- cl_git_pass(git_futils_readbuffer(&file_buf, file_path_buf.ptr));
- equals = (strcmp(file_buf.ptr, expected) == 0);
-
- git_buf_free(&file_path_buf);
- git_buf_free(&file_buf);
-
- return equals;
-}
-
-static void write_file_contents(const char *filename, const char *output)
-{
- git_buf file_path_buf = GIT_BUF_INIT;
-
- git_buf_printf(&file_path_buf, "%s/%s", git_repository_path(repo),
- filename);
- cl_git_rewritefile(file_path_buf.ptr, output);
-
- git_buf_free(&file_path_buf);
-}
-
-/* git merge --no-ff octo1 */
-void test_merge_workdir_setup__one_branch(void)
-{
- git_oid our_oid;
- git_reference *octo1_ref;
- git_merge_head *our_head, *their_heads[1];
-
- cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
- cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
-
- cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
-
- cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 1, 0));
-
- cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n"));
- cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
- cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
- cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO1_BRANCH "'\n"));
-
- git_reference_free(octo1_ref);
-
- git_merge_head_free(our_head);
- git_merge_head_free(their_heads[0]);
-}
-
-/* git merge --no-ff 16f825815cfd20a07a75c71554e82d8eede0b061 */
-void test_merge_workdir_setup__one_oid(void)
-{
- git_oid our_oid;
- git_oid octo1_oid;
- git_merge_head *our_head, *their_heads[1];
-
- cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
- cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
-
- cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID));
- cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &octo1_oid));
-
- cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 1, 0));
-
- cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n"));
- cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
- cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
- cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge commit '" OCTO1_OID "'\n"));
-
- git_merge_head_free(our_head);
- git_merge_head_free(their_heads[0]);
-}
-
-/* git merge octo1 octo2 */
-void test_merge_workdir_setup__two_branches(void)
-{
- git_oid our_oid;
- git_reference *octo1_ref;
- git_reference *octo2_ref;
- git_merge_head *our_head, *their_heads[2];
-
- cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
- cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
-
- cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
-
- cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref));
-
- cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 2, 0));
-
- cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n"));
- cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
- cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
- cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "' and '" OCTO2_BRANCH "'\n"));
-
- git_reference_free(octo1_ref);
- git_reference_free(octo2_ref);
-
- git_merge_head_free(our_head);
- git_merge_head_free(their_heads[0]);
- git_merge_head_free(their_heads[1]);
-}
-
-/* git merge octo1 octo2 octo3 */
-void test_merge_workdir_setup__three_branches(void)
-{
- git_oid our_oid;
- git_reference *octo1_ref;
- git_reference *octo2_ref;
- git_reference *octo3_ref;
- git_merge_head *our_head, *their_heads[3];
-
- cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
- cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
-
- cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
-
- cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref));
-
- cl_git_pass(git_reference_lookup(&octo3_ref, repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[2], repo, octo3_ref));
-
- cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 3, 0));
-
- cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n"));
- cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
- cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
- cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "', '" OCTO2_BRANCH "' and '" OCTO3_BRANCH "'\n"));
-
- git_reference_free(octo1_ref);
- git_reference_free(octo2_ref);
- git_reference_free(octo3_ref);
-
- git_merge_head_free(our_head);
- git_merge_head_free(their_heads[0]);
- git_merge_head_free(their_heads[1]);
- git_merge_head_free(their_heads[2]);
-}
-
-/* git merge 16f825815cfd20a07a75c71554e82d8eede0b061 158dc7bedb202f5b26502bf3574faa7f4238d56c 50ce7d7d01217679e26c55939eef119e0c93e272 */
-void test_merge_workdir_setup__three_oids(void)
-{
- git_oid our_oid;
- git_oid octo1_oid;
- git_oid octo2_oid;
- git_oid octo3_oid;
- git_merge_head *our_head, *their_heads[3];
-
- cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
- cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
-
- cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID));
- cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &octo1_oid));
-
- cl_git_pass(git_oid_fromstr(&octo2_oid, OCTO2_OID));
- cl_git_pass(git_merge_head_from_oid(&their_heads[1], repo, &octo2_oid));
-
- cl_git_pass(git_oid_fromstr(&octo3_oid, OCTO3_OID));
- cl_git_pass(git_merge_head_from_oid(&their_heads[2], repo, &octo3_oid));
-
- cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 3, 0));
-
- cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n"));
- cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
- cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
- cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge commit '" OCTO1_OID "'; commit '" OCTO2_OID "'; commit '" OCTO3_OID "'\n"));
-
- git_merge_head_free(our_head);
- git_merge_head_free(their_heads[0]);
- git_merge_head_free(their_heads[1]);
- git_merge_head_free(their_heads[2]);
-}
-
-/* git merge octo1 158dc7bedb202f5b26502bf3574faa7f4238d56c */
-void test_merge_workdir_setup__branches_and_oids_1(void)
-{
- git_oid our_oid;
- git_reference *octo1_ref;
- git_oid octo2_oid;
- git_merge_head *our_head, *their_heads[2];
-
- cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
- cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
-
- cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
-
- cl_git_pass(git_oid_fromstr(&octo2_oid, OCTO2_OID));
- cl_git_pass(git_merge_head_from_oid(&their_heads[1], repo, &octo2_oid));
-
- cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 2, 0));
-
- cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n"));
- cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
- cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
- cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO1_BRANCH "'; commit '" OCTO2_OID "'\n"));
-
- git_reference_free(octo1_ref);
-
- git_merge_head_free(our_head);
- git_merge_head_free(their_heads[0]);
- git_merge_head_free(their_heads[1]);
-}
-
-/* git merge octo1 158dc7bedb202f5b26502bf3574faa7f4238d56c octo3 54269b3f6ec3d7d4ede24dd350dd5d605495c3ae */
-void test_merge_workdir_setup__branches_and_oids_2(void)
-{
- git_oid our_oid;
- git_reference *octo1_ref;
- git_oid octo2_oid;
- git_reference *octo3_ref;
- git_oid octo4_oid;
- git_merge_head *our_head, *their_heads[4];
-
- cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
- cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
-
- cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
-
- cl_git_pass(git_oid_fromstr(&octo2_oid, OCTO2_OID));
- cl_git_pass(git_merge_head_from_oid(&their_heads[1], repo, &octo2_oid));
-
- cl_git_pass(git_reference_lookup(&octo3_ref, repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[2], repo, octo3_ref));
-
- cl_git_pass(git_oid_fromstr(&octo4_oid, OCTO4_OID));
- cl_git_pass(git_merge_head_from_oid(&their_heads[3], repo, &octo4_oid));
-
- cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 4, 0));
-
- cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n" OCTO4_OID "\n"));
- cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
- cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
- cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "' and '" OCTO3_BRANCH "'; commit '" OCTO2_OID "'; commit '" OCTO4_OID "'\n"));
-
- git_reference_free(octo1_ref);
- git_reference_free(octo3_ref);
-
- git_merge_head_free(our_head);
- git_merge_head_free(their_heads[0]);
- git_merge_head_free(their_heads[1]);
- git_merge_head_free(their_heads[2]);
- git_merge_head_free(their_heads[3]);
-}
-
-/* git merge 16f825815cfd20a07a75c71554e82d8eede0b061 octo2 50ce7d7d01217679e26c55939eef119e0c93e272 octo4 */
-void test_merge_workdir_setup__branches_and_oids_3(void)
-{
- git_oid our_oid;
- git_oid octo1_oid;
- git_reference *octo2_ref;
- git_oid octo3_oid;
- git_reference *octo4_ref;
- git_merge_head *our_head, *their_heads[4];
-
- cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
- cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
-
- cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID));
- cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &octo1_oid));
-
- cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref));
-
- cl_git_pass(git_oid_fromstr(&octo3_oid, OCTO3_OID));
- cl_git_pass(git_merge_head_from_oid(&their_heads[2], repo, &octo3_oid));
-
- cl_git_pass(git_reference_lookup(&octo4_ref, repo, GIT_REFS_HEADS_DIR OCTO4_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[3], repo, octo4_ref));
-
- cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 4, 0));
-
- cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n" OCTO4_OID "\n"));
- cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
- cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
- cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge commit '" OCTO1_OID "'; branches '" OCTO2_BRANCH "' and '" OCTO4_BRANCH "'; commit '" OCTO3_OID "'\n"));
-
- git_reference_free(octo2_ref);
- git_reference_free(octo4_ref);
-
- git_merge_head_free(our_head);
- git_merge_head_free(their_heads[0]);
- git_merge_head_free(their_heads[1]);
- git_merge_head_free(their_heads[2]);
- git_merge_head_free(their_heads[3]);
-}
-
-/* git merge 16f825815cfd20a07a75c71554e82d8eede0b061 octo2 50ce7d7d01217679e26c55939eef119e0c93e272 octo4 octo5 */
-void test_merge_workdir_setup__branches_and_oids_4(void)
-{
- git_oid our_oid;
- git_oid octo1_oid;
- git_reference *octo2_ref;
- git_oid octo3_oid;
- git_reference *octo4_ref;
- git_reference *octo5_ref;
- git_merge_head *our_head, *their_heads[5];
-
- cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
- cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
-
- cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID));
- cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &octo1_oid));
-
- cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref));
-
- cl_git_pass(git_oid_fromstr(&octo3_oid, OCTO3_OID));
- cl_git_pass(git_merge_head_from_oid(&their_heads[2], repo, &octo3_oid));
-
- cl_git_pass(git_reference_lookup(&octo4_ref, repo, GIT_REFS_HEADS_DIR OCTO4_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[3], repo, octo4_ref));
-
- cl_git_pass(git_reference_lookup(&octo5_ref, repo, GIT_REFS_HEADS_DIR OCTO5_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[4], repo, octo5_ref));
-
- cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 5, 0));
-
- cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n" OCTO4_OID "\n" OCTO5_OID "\n"));
- cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
- cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
- cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge commit '" OCTO1_OID "'; branches '" OCTO2_BRANCH "', '" OCTO4_BRANCH "' and '" OCTO5_BRANCH "'; commit '" OCTO3_OID "'\n"));
-
- git_reference_free(octo2_ref);
- git_reference_free(octo4_ref);
- git_reference_free(octo5_ref);
-
- git_merge_head_free(our_head);
- git_merge_head_free(their_heads[0]);
- git_merge_head_free(their_heads[1]);
- git_merge_head_free(their_heads[2]);
- git_merge_head_free(their_heads[3]);
- git_merge_head_free(their_heads[4]);
-}
-
-/* git merge octo1 octo1 octo1 */
-void test_merge_workdir_setup__three_same_branches(void)
-{
- git_oid our_oid;
- git_reference *octo1_1_ref;
- git_reference *octo1_2_ref;
- git_reference *octo1_3_ref;
- git_merge_head *our_head, *their_heads[3];
-
- cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
- cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
-
- cl_git_pass(git_reference_lookup(&octo1_1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_1_ref));
-
- cl_git_pass(git_reference_lookup(&octo1_2_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo1_2_ref));
-
- cl_git_pass(git_reference_lookup(&octo1_3_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[2], repo, octo1_3_ref));
-
- cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 3, 0));
-
- cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO1_OID "\n" OCTO1_OID "\n"));
- cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
- cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
- cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "', '" OCTO1_BRANCH "' and '" OCTO1_BRANCH "'\n"));
-
- git_reference_free(octo1_1_ref);
- git_reference_free(octo1_2_ref);
- git_reference_free(octo1_3_ref);
-
- git_merge_head_free(our_head);
- git_merge_head_free(their_heads[0]);
- git_merge_head_free(their_heads[1]);
- git_merge_head_free(their_heads[2]);
-}
-
-/* git merge 16f825815cfd20a07a75c71554e82d8eede0b061 16f825815cfd20a07a75c71554e82d8eede0b061 16f825815cfd20a07a75c71554e82d8eede0b061 */
-void test_merge_workdir_setup__three_same_oids(void)
-{
- git_oid our_oid;
- git_oid octo1_1_oid;
- git_oid octo1_2_oid;
- git_oid octo1_3_oid;
- git_merge_head *our_head, *their_heads[3];
-
- cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
- cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
-
- cl_git_pass(git_oid_fromstr(&octo1_1_oid, OCTO1_OID));
- cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &octo1_1_oid));
-
- cl_git_pass(git_oid_fromstr(&octo1_2_oid, OCTO1_OID));
- cl_git_pass(git_merge_head_from_oid(&their_heads[1], repo, &octo1_2_oid));
-
- cl_git_pass(git_oid_fromstr(&octo1_3_oid, OCTO1_OID));
- cl_git_pass(git_merge_head_from_oid(&their_heads[2], repo, &octo1_3_oid));
-
- cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 3, 0));
-
- cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO1_OID "\n" OCTO1_OID "\n"));
- cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
- cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
- cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge commit '" OCTO1_OID "'; commit '" OCTO1_OID "'; commit '" OCTO1_OID "'\n"));
-
- git_merge_head_free(our_head);
- git_merge_head_free(their_heads[0]);
- git_merge_head_free(their_heads[1]);
- git_merge_head_free(their_heads[2]);
-}
-
-static int create_remote_tracking_branch(const char *branch_name, const char *oid_str)
-{
- int error = 0;
-
- git_buf remotes_path = GIT_BUF_INIT,
- origin_path = GIT_BUF_INIT,
- filename = GIT_BUF_INIT,
- data = GIT_BUF_INIT;
-
- if ((error = git_buf_puts(&remotes_path, git_repository_path(repo))) < 0 ||
- (error = git_buf_puts(&remotes_path, GIT_REFS_REMOTES_DIR)) < 0)
- goto done;
-
- if (!git_path_exists(git_buf_cstr(&remotes_path)) &&
- (error = p_mkdir(git_buf_cstr(&remotes_path), 0777)) < 0)
- goto done;
-
- if ((error = git_buf_puts(&origin_path, git_buf_cstr(&remotes_path))) < 0 ||
- (error = git_buf_puts(&origin_path, "origin")) < 0)
- goto done;
-
- if (!git_path_exists(git_buf_cstr(&origin_path)) &&
- (error = p_mkdir(git_buf_cstr(&origin_path), 0777)) < 0)
- goto done;
-
- if ((error = git_buf_puts(&filename, git_buf_cstr(&origin_path))) < 0 ||
- (error = git_buf_puts(&filename, "/")) < 0 ||
- (error = git_buf_puts(&filename, branch_name)) < 0 ||
- (error = git_buf_puts(&data, oid_str)) < 0 ||
- (error = git_buf_puts(&data, "\n")) < 0)
- goto done;
-
- cl_git_rewritefile(git_buf_cstr(&filename), git_buf_cstr(&data));
-
-done:
- git_buf_free(&remotes_path);
- git_buf_free(&origin_path);
- git_buf_free(&filename);
- git_buf_free(&data);
-
- return error;
-}
-
-/* git merge refs/remotes/origin/octo1 */
-void test_merge_workdir_setup__remote_tracking_one_branch(void)
-{
- git_oid our_oid;
- git_reference *octo1_ref;
- git_merge_head *our_head, *their_heads[1];
-
- cl_git_pass(create_remote_tracking_branch(OCTO1_BRANCH, OCTO1_OID));
-
- cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
- cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
-
- cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO1_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
-
- cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 1, 0));
-
- cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n"));
- cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
- cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
- cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge remote-tracking branch 'refs/remotes/origin/" OCTO1_BRANCH "'\n"));
-
- git_reference_free(octo1_ref);
-
- git_merge_head_free(our_head);
- git_merge_head_free(their_heads[0]);
-}
-
-/* git merge refs/remotes/origin/octo1 refs/remotes/origin/octo2 */
-void test_merge_workdir_setup__remote_tracking_two_branches(void)
-{
- git_oid our_oid;
- git_reference *octo1_ref;
- git_reference *octo2_ref;
- git_merge_head *our_head, *their_heads[2];
-
- cl_git_pass(create_remote_tracking_branch(OCTO1_BRANCH, OCTO1_OID));
- cl_git_pass(create_remote_tracking_branch(OCTO2_BRANCH, OCTO2_OID));
-
- cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
- cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
-
- cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO1_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
-
- cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO2_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref));
-
- cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 2, 0));
-
- cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n"));
- cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
- cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
- cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge remote-tracking branches 'refs/remotes/origin/" OCTO1_BRANCH "' and 'refs/remotes/origin/" OCTO2_BRANCH "'\n"));
-
- git_reference_free(octo1_ref);
- git_reference_free(octo2_ref);
-
- git_merge_head_free(our_head);
- git_merge_head_free(their_heads[0]);
- git_merge_head_free(their_heads[1]);
-}
-
-/* git merge refs/remotes/origin/octo1 refs/remotes/origin/octo2 refs/remotes/origin/octo3 */
-void test_merge_workdir_setup__remote_tracking_three_branches(void)
-{
- git_oid our_oid;
- git_reference *octo1_ref;
- git_reference *octo2_ref;
- git_reference *octo3_ref;
- git_merge_head *our_head, *their_heads[3];
-
- cl_git_pass(create_remote_tracking_branch(OCTO1_BRANCH, OCTO1_OID));
- cl_git_pass(create_remote_tracking_branch(OCTO2_BRANCH, OCTO2_OID));
- cl_git_pass(create_remote_tracking_branch(OCTO3_BRANCH, OCTO3_OID));
-
- cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
- cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
-
- cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO1_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
-
- cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO2_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref));
-
- cl_git_pass(git_reference_lookup(&octo3_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO3_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[2], repo, octo3_ref));
-
- cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 3, 0));
-
- cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n"));
- cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
- cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
- cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge remote-tracking branches 'refs/remotes/origin/" OCTO1_BRANCH "', 'refs/remotes/origin/" OCTO2_BRANCH "' and 'refs/remotes/origin/" OCTO3_BRANCH "'\n"));
-
- git_reference_free(octo1_ref);
- git_reference_free(octo2_ref);
- git_reference_free(octo3_ref);
-
- git_merge_head_free(our_head);
- git_merge_head_free(their_heads[0]);
- git_merge_head_free(their_heads[1]);
- git_merge_head_free(their_heads[2]);
-}
-
-/* git merge octo1 refs/remotes/origin/octo2 */
-void test_merge_workdir_setup__normal_branch_and_remote_tracking_branch(void)
-{
- git_oid our_oid;
- git_reference *octo1_ref;
- git_reference *octo2_ref;
- git_merge_head *our_head, *their_heads[2];
-
- cl_git_pass(create_remote_tracking_branch(OCTO2_BRANCH, OCTO2_OID));
-
- cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
- cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
-
- cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
-
- cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO2_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref));
-
- cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 2, 0));
-
- cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n"));
- cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
- cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
- cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO1_BRANCH "', remote-tracking branch 'refs/remotes/origin/" OCTO2_BRANCH "'\n"));
-
- git_reference_free(octo1_ref);
- git_reference_free(octo2_ref);
-
- git_merge_head_free(our_head);
- git_merge_head_free(their_heads[0]);
- git_merge_head_free(their_heads[1]);
-}
-
-/* git merge refs/remotes/origin/octo1 octo2 */
-void test_merge_workdir_setup__remote_tracking_branch_and_normal_branch(void)
-{
- git_oid our_oid;
- git_reference *octo1_ref;
- git_reference *octo2_ref;
- git_merge_head *our_head, *their_heads[2];
-
- cl_git_pass(create_remote_tracking_branch(OCTO1_BRANCH, OCTO1_OID));
-
- cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
- cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
-
- cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO1_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
-
- cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref));
-
- cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 2, 0));
-
- cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n"));
- cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
- cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
- cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO2_BRANCH "', remote-tracking branch 'refs/remotes/origin/" OCTO1_BRANCH "'\n"));
-
- git_reference_free(octo1_ref);
- git_reference_free(octo2_ref);
-
- git_merge_head_free(our_head);
- git_merge_head_free(their_heads[0]);
- git_merge_head_free(their_heads[1]);
-}
-
-/* git merge octo1 refs/remotes/origin/octo2 octo3 refs/remotes/origin/octo4 */
-void test_merge_workdir_setup__two_remote_tracking_branch_and_two_normal_branches(void)
-{
- git_oid our_oid;
- git_reference *octo1_ref;
- git_reference *octo2_ref;
- git_reference *octo3_ref;
- git_reference *octo4_ref;
- git_merge_head *our_head, *their_heads[4];
-
- cl_git_pass(create_remote_tracking_branch(OCTO2_BRANCH, OCTO2_OID));
- cl_git_pass(create_remote_tracking_branch(OCTO4_BRANCH, OCTO4_OID));
-
- cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
- cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
-
- cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
-
- cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO2_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref));
-
- cl_git_pass(git_reference_lookup(&octo3_ref, repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[2], repo, octo3_ref));
-
- cl_git_pass(git_reference_lookup(&octo4_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO4_BRANCH));
- cl_git_pass(git_merge_head_from_ref(&their_heads[3], repo, octo4_ref));
-
- cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 4, 0));
-
- cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n" OCTO4_OID "\n"));
- cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
- cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
- cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "' and '" OCTO3_BRANCH "', remote-tracking branches 'refs/remotes/origin/" OCTO2_BRANCH "' and 'refs/remotes/origin/" OCTO4_BRANCH "'\n"));
-
- git_reference_free(octo1_ref);
- git_reference_free(octo2_ref);
- git_reference_free(octo3_ref);
- git_reference_free(octo4_ref);
-
- git_merge_head_free(our_head);
- git_merge_head_free(their_heads[0]);
- git_merge_head_free(their_heads[1]);
- git_merge_head_free(their_heads[2]);
- git_merge_head_free(their_heads[3]);
-}
-
-/* git pull origin branch octo1 */
-void test_merge_workdir_setup__pull_one(void)
-{
- git_oid our_oid;
- git_oid octo1_1_oid;
- git_merge_head *our_head, *their_heads[1];
-
- cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
- cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
-
- cl_git_pass(git_oid_fromstr(&octo1_1_oid, OCTO1_OID));
- cl_git_pass(git_merge_head_from_fetchhead(&their_heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.url/repo.git", &octo1_1_oid));
-
- cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 1, 0));
-
- cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n"));
- cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
- cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
- cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch 'octo1' of http://remote.url/repo.git\n"));
-
- git_merge_head_free(our_head);
- git_merge_head_free(their_heads[0]);
-}
-
-/* git pull origin octo1 octo2 */
-void test_merge_workdir_setup__pull_two(void)
-{
- git_oid our_oid;
- git_oid octo1_oid;
- git_oid octo2_oid;
- git_merge_head *our_head, *their_heads[2];
-
- cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
- cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
-
- cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID));
- cl_git_pass(git_merge_head_from_fetchhead(&their_heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.url/repo.git", &octo1_oid));
-
- cl_git_pass(git_oid_fromstr(&octo2_oid, OCTO2_OID));
- cl_git_pass(git_merge_head_from_fetchhead(&their_heads[1], repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH, "http://remote.url/repo.git", &octo2_oid));
-
- cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 2, 0));
-
- cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n"));
- cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
- cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
- cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "' and '" OCTO2_BRANCH "' of http://remote.url/repo.git\n"));
-
- git_merge_head_free(our_head);
- git_merge_head_free(their_heads[0]);
- git_merge_head_free(their_heads[1]);
-}
-
-/* git pull origin octo1 octo2 octo3 */
-void test_merge_workdir_setup__pull_three(void)
-{
- git_oid our_oid;
- git_oid octo1_oid;
- git_oid octo2_oid;
- git_oid octo3_oid;
- git_merge_head *our_head, *their_heads[3];
-
- cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
- cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
-
- cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID));
- cl_git_pass(git_merge_head_from_fetchhead(&their_heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.url/repo.git", &octo1_oid));
-
- cl_git_pass(git_oid_fromstr(&octo2_oid, OCTO2_OID));
- cl_git_pass(git_merge_head_from_fetchhead(&their_heads[1], repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH, "http://remote.url/repo.git", &octo2_oid));
-
- cl_git_pass(git_oid_fromstr(&octo3_oid, OCTO3_OID));
- cl_git_pass(git_merge_head_from_fetchhead(&their_heads[2], repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH, "http://remote.url/repo.git", &octo3_oid));
-
- cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 3, 0));
-
- cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n"));
- cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
- cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
- cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "', '" OCTO2_BRANCH "' and '" OCTO3_BRANCH "' of http://remote.url/repo.git\n"));
-
- git_merge_head_free(our_head);
- git_merge_head_free(their_heads[0]);
- git_merge_head_free(their_heads[1]);
- git_merge_head_free(their_heads[2]);
-}
-
-void test_merge_workdir_setup__three_remotes(void)
-{
- git_oid our_oid;
- git_oid octo1_oid;
- git_oid octo2_oid;
- git_oid octo3_oid;
- git_merge_head *our_head, *their_heads[3];
-
- cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
- cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
-
- cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID));
- cl_git_pass(git_merge_head_from_fetchhead(&their_heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.first/repo.git", &octo1_oid));
-
- cl_git_pass(git_oid_fromstr(&octo2_oid, OCTO2_OID));
- cl_git_pass(git_merge_head_from_fetchhead(&their_heads[1], repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH, "http://remote.second/repo.git", &octo2_oid));
-
- cl_git_pass(git_oid_fromstr(&octo3_oid, OCTO3_OID));
- cl_git_pass(git_merge_head_from_fetchhead(&their_heads[2], repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH, "http://remote.third/repo.git", &octo3_oid));
-
- cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 3, 0));
-
- cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n"));
- cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
- cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
- cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO1_BRANCH "' of http://remote.first/repo.git, branch '" OCTO2_BRANCH "' of http://remote.second/repo.git, branch '" OCTO3_BRANCH "' of http://remote.third/repo.git\n"));
-
- git_merge_head_free(our_head);
- git_merge_head_free(their_heads[0]);
- git_merge_head_free(their_heads[1]);
- git_merge_head_free(their_heads[2]);
-}
-
-void test_merge_workdir_setup__two_remotes(void)
-{
- git_oid our_oid;
- git_oid octo1_oid;
- git_oid octo2_oid;
- git_oid octo3_oid;
- git_oid octo4_oid;
- git_merge_head *our_head, *their_heads[4];
-
- cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
- cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
-
- cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID));
- cl_git_pass(git_merge_head_from_fetchhead(&their_heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.first/repo.git", &octo1_oid));
-
- cl_git_pass(git_oid_fromstr(&octo2_oid, OCTO2_OID));
- cl_git_pass(git_merge_head_from_fetchhead(&their_heads[1], repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH, "http://remote.second/repo.git", &octo2_oid));
-
- cl_git_pass(git_oid_fromstr(&octo3_oid, OCTO3_OID));
- cl_git_pass(git_merge_head_from_fetchhead(&their_heads[2], repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH, "http://remote.first/repo.git", &octo3_oid));
-
- cl_git_pass(git_oid_fromstr(&octo4_oid, OCTO4_OID));
- cl_git_pass(git_merge_head_from_fetchhead(&their_heads[3], repo, GIT_REFS_HEADS_DIR OCTO4_BRANCH, "http://remote.second/repo.git", &octo4_oid));
-
- cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 4, 0));
-
- cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n" OCTO4_OID "\n"));
- cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
- cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
- cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "' and '" OCTO3_BRANCH "' of http://remote.first/repo.git, branches '" OCTO2_BRANCH "' and '" OCTO4_BRANCH "' of http://remote.second/repo.git\n"));
-
- git_merge_head_free(our_head);
- git_merge_head_free(their_heads[0]);
- git_merge_head_free(their_heads[1]);
- git_merge_head_free(their_heads[2]);
- git_merge_head_free(their_heads[3]);
-}
-
-struct merge_head_cb_data {
- const char **oid_str;
- unsigned int len;
-
- unsigned int i;
-};
-
-static int merge_head_foreach_cb(const git_oid *oid, void *payload)
-{
- git_oid expected_oid;
- struct merge_head_cb_data *cb_data = payload;
-
- git_oid_fromstr(&expected_oid, cb_data->oid_str[cb_data->i]);
- cl_assert(git_oid_cmp(&expected_oid, oid) == 0);
- cb_data->i++;
- return 0;
-}
-
-void test_merge_workdir_setup__head_notfound(void)
-{
- int error;
-
- cl_git_fail((error = git_repository_mergehead_foreach(repo,
- merge_head_foreach_cb, NULL)));
- cl_assert(error == GIT_ENOTFOUND);
-}
-
-void test_merge_workdir_setup__head_invalid_oid(void)
-{
- int error;
-
- write_file_contents(GIT_MERGE_HEAD_FILE, "invalid-oid\n");
-
- cl_git_fail((error = git_repository_mergehead_foreach(repo,
- merge_head_foreach_cb, NULL)));
- cl_assert(error == -1);
-}
-
-void test_merge_workdir_setup__head_foreach_nonewline(void)
-{
- int error;
-
- write_file_contents(GIT_MERGE_HEAD_FILE, THEIRS_SIMPLE_OID);
-
- cl_git_fail((error = git_repository_mergehead_foreach(repo,
- merge_head_foreach_cb, NULL)));
- cl_assert(error == -1);
-}
-
-void test_merge_workdir_setup__head_foreach_one(void)
-{
- const char *expected = THEIRS_SIMPLE_OID;
-
- struct merge_head_cb_data cb_data = { &expected, 1 };
-
- write_file_contents(GIT_MERGE_HEAD_FILE, THEIRS_SIMPLE_OID "\n");
-
- cl_git_pass(git_repository_mergehead_foreach(repo,
- merge_head_foreach_cb, &cb_data));
-
- cl_assert(cb_data.i == cb_data.len);
-}
-
-void test_merge_workdir_setup__head_foreach_octopus(void)
-{
- const char *expected[] = { THEIRS_SIMPLE_OID,
- OCTO1_OID, OCTO2_OID, OCTO3_OID, OCTO4_OID, OCTO5_OID };
-
- struct merge_head_cb_data cb_data = { expected, 6 };
-
- write_file_contents(GIT_MERGE_HEAD_FILE,
- THEIRS_SIMPLE_OID "\n"
- OCTO1_OID "\n"
- OCTO2_OID "\n"
- OCTO3_OID "\n"
- OCTO4_OID "\n"
- OCTO5_OID "\n");
-
- cl_git_pass(git_repository_mergehead_foreach(repo,
- merge_head_foreach_cb, &cb_data));
-
- cl_assert(cb_data.i == cb_data.len);
-}
diff --git a/tests-clar/network/fetchlocal.c b/tests-clar/network/fetchlocal.c
deleted file mode 100644
index 09335b3df..000000000
--- a/tests-clar/network/fetchlocal.c
+++ /dev/null
@@ -1,78 +0,0 @@
-#include "clar_libgit2.h"
-
-#include "buffer.h"
-#include "path.h"
-#include "remote.h"
-
-static int transfer_cb(const git_transfer_progress *stats, void *payload)
-{
- int *callcount = (int*)payload;
- GIT_UNUSED(stats);
- (*callcount)++;
- return 0;
-}
-
-static void cleanup_local_repo(void *path)
-{
- cl_fixture_cleanup((char *)path);
-}
-
-void test_network_fetchlocal__complete(void)
-{
- git_repository *repo;
- git_remote *origin;
- int callcount = 0;
- git_strarray refnames = {0};
-
- const char *url = cl_git_fixture_url("testrepo.git");
-
- cl_set_cleanup(&cleanup_local_repo, "foo");
- cl_git_pass(git_repository_init(&repo, "foo", true));
-
- cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url));
- cl_git_pass(git_remote_connect(origin, GIT_DIRECTION_FETCH));
- cl_git_pass(git_remote_download(origin, transfer_cb, &callcount));
- cl_git_pass(git_remote_update_tips(origin));
-
- cl_git_pass(git_reference_list(&refnames, repo));
- cl_assert_equal_i(19, (int)refnames.count);
- cl_assert(callcount > 0);
-
- git_strarray_free(&refnames);
- git_remote_free(origin);
- 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");
- git_remote *origin;
- int callcount = 0;
- git_strarray refnames = {0};
- const char *url;
-
- cl_set_cleanup(&cleanup_sandbox, NULL);
- cl_git_pass(git_reference_list(&refnames, repo));
- cl_assert_equal_i(1, (int)refnames.count);
-
- url = cl_git_fixture_url("testrepo.git");
- cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url));
- cl_git_pass(git_remote_connect(origin, GIT_DIRECTION_FETCH));
- cl_git_pass(git_remote_download(origin, transfer_cb, &callcount));
- cl_git_pass(git_remote_update_tips(origin));
-
- git_strarray_free(&refnames);
-
- cl_git_pass(git_reference_list(&refnames, repo));
- cl_assert_equal_i(20, (int)refnames.count); /* 18 remote + 1 local */
- cl_assert(callcount > 0);
-
- git_strarray_free(&refnames);
- git_remote_free(origin);
-}
diff --git a/tests-clar/network/remote/local.c b/tests-clar/network/remote/local.c
deleted file mode 100644
index 3cb8a25d6..000000000
--- a/tests-clar/network/remote/local.c
+++ /dev/null
@@ -1,160 +0,0 @@
-#include "clar_libgit2.h"
-#include "buffer.h"
-#include "path.h"
-#include "posix.h"
-
-static git_repository *repo;
-static git_buf file_path_buf = GIT_BUF_INIT;
-static git_remote *remote;
-
-void test_network_remote_local__initialize(void)
-{
- cl_git_pass(git_repository_init(&repo, "remotelocal/", 0));
- cl_assert(repo != NULL);
-}
-
-void test_network_remote_local__cleanup(void)
-{
- git_buf_free(&file_path_buf);
-
- git_remote_free(remote);
- remote = NULL;
-
- git_repository_free(repo);
- repo = NULL;
-
- cl_fixture_cleanup("remotelocal");
-}
-
-static int count_ref__cb(git_remote_head *head, void *payload)
-{
- int *count = (int *)payload;
-
- (void)head;
- (*count)++;
-
- return 0;
-}
-
-static int ensure_peeled__cb(git_remote_head *head, void *payload)
-{
- GIT_UNUSED(payload);
-
- if(strcmp(head->name, "refs/tags/test^{}") != 0)
- return 0;
-
- return git_oid_streq(&head->oid, "e90810b8df3e80c413d903f631643c716887138d");
-}
-
-static void connect_to_local_repository(const char *local_repository)
-{
- git_buf_sets(&file_path_buf, cl_git_path_url(local_repository));
-
- cl_git_pass(git_remote_create_inmemory(&remote, repo, NULL, git_buf_cstr(&file_path_buf)));
- cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
-
-}
-
-void test_network_remote_local__connected(void)
-{
- connect_to_local_repository(cl_fixture("testrepo.git"));
- cl_assert(git_remote_connected(remote));
-
- git_remote_disconnect(remote);
- cl_assert(!git_remote_connected(remote));
-}
-
-void test_network_remote_local__retrieve_advertised_references(void)
-{
- int how_many_refs = 0;
-
- connect_to_local_repository(cl_fixture("testrepo.git"));
-
- cl_git_pass(git_remote_ls(remote, &count_ref__cb, &how_many_refs));
-
- cl_assert_equal_i(how_many_refs, 28);
-}
-
-void test_network_remote_local__retrieve_advertised_references_from_spaced_repository(void)
-{
- int how_many_refs = 0;
-
- cl_fixture_sandbox("testrepo.git");
- cl_git_pass(p_rename("testrepo.git", "spaced testrepo.git"));
-
- connect_to_local_repository("spaced testrepo.git");
-
- cl_git_pass(git_remote_ls(remote, &count_ref__cb, &how_many_refs));
-
- cl_assert_equal_i(how_many_refs, 28);
-
- git_remote_free(remote); /* Disconnect from the "spaced repo" before the cleanup */
- remote = NULL;
-
- cl_fixture_cleanup("spaced testrepo.git");
-}
-
-void test_network_remote_local__nested_tags_are_completely_peeled(void)
-{
- connect_to_local_repository(cl_fixture("testrepo.git"));
-
- cl_git_pass(git_remote_ls(remote, &ensure_peeled__cb, NULL));
-}
-
-void test_network_remote_local__shorthand_fetch_refspec0(void)
-{
- const char *refspec = "master:remotes/sloppy/master";
- const char *refspec2 = "master:boh/sloppy/master";
-
- git_reference *ref;
-
- connect_to_local_repository(cl_fixture("testrepo.git"));
- cl_git_pass(git_remote_add_fetch(remote, refspec));
- cl_git_pass(git_remote_add_fetch(remote, refspec2));
-
- cl_git_pass(git_remote_download(remote, NULL, NULL));
- cl_git_pass(git_remote_update_tips(remote));
-
- cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/sloppy/master"));
- git_reference_free(ref);
-
- cl_git_pass(git_reference_lookup(&ref, repo, "refs/heads/boh/sloppy/master"));
- git_reference_free(ref);
-}
-
-void test_network_remote_local__shorthand_fetch_refspec1(void)
-{
- const char *refspec = "master";
- const char *refspec2 = "hard_tag";
-
- git_reference *ref;
-
- connect_to_local_repository(cl_fixture("testrepo.git"));
- git_remote_clear_refspecs(remote);
- cl_git_pass(git_remote_add_fetch(remote, refspec));
- cl_git_pass(git_remote_add_fetch(remote, refspec2));
-
- cl_git_pass(git_remote_download(remote, NULL, NULL));
- cl_git_pass(git_remote_update_tips(remote));
-
- cl_git_fail(git_reference_lookup(&ref, repo, "refs/remotes/master"));
-
- cl_git_fail(git_reference_lookup(&ref, repo, "refs/tags/hard_tag"));
-}
-
-void test_network_remote_local__tagopt(void)
-{
- git_reference *ref;
-
- connect_to_local_repository(cl_fixture("testrepo.git"));
- git_remote_set_autotag(remote, GIT_REMOTE_DOWNLOAD_TAGS_ALL);
-
- cl_git_pass(git_remote_download(remote, NULL, NULL));
- cl_git_pass(git_remote_update_tips(remote));
-
-
- cl_git_fail(git_reference_lookup(&ref, repo, "refs/remotes/master"));
-
- cl_git_pass(git_reference_lookup(&ref, repo, "refs/tags/hard_tag"));
- git_reference_free(ref);
-}
diff --git a/tests-clar/network/remote/remotes.c b/tests-clar/network/remote/remotes.c
deleted file mode 100644
index 3c4fa96fa..000000000
--- a/tests-clar/network/remote/remotes.c
+++ /dev/null
@@ -1,457 +0,0 @@
-#include "clar_libgit2.h"
-#include "buffer.h"
-#include "refspec.h"
-#include "remote.h"
-
-static git_remote *_remote;
-static git_repository *_repo;
-static const git_refspec *_refspec;
-
-void test_network_remote_remotes__initialize(void)
-{
- _repo = cl_git_sandbox_init("testrepo.git");
-
- cl_git_pass(git_remote_load(&_remote, _repo, "test"));
-
- _refspec = git_remote_get_refspec(_remote, 0);
- cl_assert(_refspec != NULL);
-}
-
-void test_network_remote_remotes__cleanup(void)
-{
- git_remote_free(_remote);
- _remote = NULL;
-
- cl_git_sandbox_cleanup();
-}
-
-void test_network_remote_remotes__parsing(void)
-{
- git_remote *_remote2 = NULL;
-
- cl_assert_equal_s(git_remote_name(_remote), "test");
- cl_assert_equal_s(git_remote_url(_remote), "git://github.com/libgit2/libgit2");
- cl_assert(git_remote_pushurl(_remote) == NULL);
-
- cl_assert_equal_s(git_remote__urlfordirection(_remote, GIT_DIRECTION_FETCH),
- "git://github.com/libgit2/libgit2");
- cl_assert_equal_s(git_remote__urlfordirection(_remote, GIT_DIRECTION_PUSH),
- "git://github.com/libgit2/libgit2");
-
- cl_git_pass(git_remote_load(&_remote2, _repo, "test_with_pushurl"));
- cl_assert_equal_s(git_remote_name(_remote2), "test_with_pushurl");
- cl_assert_equal_s(git_remote_url(_remote2), "git://github.com/libgit2/fetchlibgit2");
- cl_assert_equal_s(git_remote_pushurl(_remote2), "git://github.com/libgit2/pushlibgit2");
-
- cl_assert_equal_s(git_remote__urlfordirection(_remote2, GIT_DIRECTION_FETCH),
- "git://github.com/libgit2/fetchlibgit2");
- cl_assert_equal_s(git_remote__urlfordirection(_remote2, GIT_DIRECTION_PUSH),
- "git://github.com/libgit2/pushlibgit2");
-
- git_remote_free(_remote2);
-}
-
-void test_network_remote_remotes__pushurl(void)
-{
- cl_git_pass(git_remote_set_pushurl(_remote, "git://github.com/libgit2/notlibgit2"));
- cl_assert_equal_s(git_remote_pushurl(_remote), "git://github.com/libgit2/notlibgit2");
-
- cl_git_pass(git_remote_set_pushurl(_remote, NULL));
- cl_assert(git_remote_pushurl(_remote) == NULL);
-}
-
-void test_network_remote_remotes__error_when_no_push_available(void)
-{
- git_remote *r;
- git_transport *t;
- git_push *p;
-
- cl_git_pass(git_remote_create_inmemory(&r, _repo, NULL, cl_fixture("testrepo.git")));
-
- cl_git_pass(git_transport_local(&t,r,NULL));
-
- /* Make sure that push is really not available */
- t->push = NULL;
- cl_git_pass(git_remote_set_transport(r, t));
-
- cl_git_pass(git_remote_connect(r, GIT_DIRECTION_PUSH));
- cl_git_pass(git_push_new(&p, r));
- cl_git_pass(git_push_add_refspec(p, "refs/heads/master"));
- cl_git_fail_with(git_push_finish(p), GIT_ERROR);
-
- git_push_free(p);
- git_remote_free(r);
-}
-
-void test_network_remote_remotes__parsing_ssh_remote(void)
-{
- cl_assert( git_remote_valid_url("git@github.com:libgit2/libgit2.git") );
-}
-
-void test_network_remote_remotes__parsing_local_path_fails_if_path_not_found(void)
-{
- cl_assert( !git_remote_valid_url("/home/git/repos/libgit2.git") );
-}
-
-void test_network_remote_remotes__supported_transport_methods_are_supported(void)
-{
- cl_assert( git_remote_supported_url("git://github.com/libgit2/libgit2") );
-}
-
-void test_network_remote_remotes__unsupported_transport_methods_are_unsupported(void)
-{
-#ifndef GIT_SSH
- cl_assert( !git_remote_supported_url("git@github.com:libgit2/libgit2.git") );
-#endif
-}
-
-void test_network_remote_remotes__refspec_parsing(void)
-{
- cl_assert_equal_s(git_refspec_src(_refspec), "refs/heads/*");
- cl_assert_equal_s(git_refspec_dst(_refspec), "refs/remotes/test/*");
-}
-
-void test_network_remote_remotes__add_fetchspec(void)
-{
- size_t size;
-
- size = git_remote_refspec_count(_remote);
-
- cl_git_pass(git_remote_add_fetch(_remote, "refs/*:refs/*"));
-
- size++;
- cl_assert_equal_i((int)size, (int)git_remote_refspec_count(_remote));
-
- _refspec = git_remote_get_refspec(_remote, size - 1);
- cl_assert_equal_s(git_refspec_src(_refspec), "refs/*");
- cl_assert_equal_s(git_refspec_dst(_refspec), "refs/*");
- cl_assert_equal_s(git_refspec_string(_refspec), "refs/*:refs/*");
- cl_assert_equal_b(_refspec->push, false);
-}
-
-void test_network_remote_remotes__add_pushspec(void)
-{
- size_t size;
-
- size = git_remote_refspec_count(_remote);
-
- cl_git_pass(git_remote_add_push(_remote, "refs/*:refs/*"));
- size++;
- cl_assert_equal_i((int)size, (int)git_remote_refspec_count(_remote));
-
- _refspec = git_remote_get_refspec(_remote, size - 1);
- cl_assert_equal_s(git_refspec_src(_refspec), "refs/*");
- cl_assert_equal_s(git_refspec_dst(_refspec), "refs/*");
- cl_assert_equal_s(git_refspec_string(_refspec), "refs/*:refs/*");
-
- cl_assert_equal_b(_refspec->push, true);
-}
-
-void test_network_remote_remotes__save(void)
-{
- git_strarray array;
- const char *fetch_refspec = "refs/heads/*:refs/remotes/upstream/*";
- const char *push_refspec = "refs/heads/*:refs/heads/*";
-
- git_remote_free(_remote);
- _remote = NULL;
-
- /* Set up the remote and save it to config */
- cl_git_pass(git_remote_create(&_remote, _repo, "upstream", "git://github.com/libgit2/libgit2"));
- git_remote_clear_refspecs(_remote);
-
- cl_git_pass(git_remote_add_fetch(_remote, fetch_refspec));
- cl_git_pass(git_remote_add_push(_remote, push_refspec));
- cl_git_pass(git_remote_set_pushurl(_remote, "git://github.com/libgit2/libgit2_push"));
- cl_git_pass(git_remote_save(_remote));
- git_remote_free(_remote);
- _remote = NULL;
-
- /* Load it from config and make sure everything matches */
- cl_git_pass(git_remote_load(&_remote, _repo, "upstream"));
-
- cl_git_pass(git_remote_get_fetch_refspecs(&array, _remote));
- cl_assert_equal_i(1, (int)array.count);
- cl_assert_equal_s(fetch_refspec, array.strings[0]);
- git_strarray_free(&array);
-
- cl_git_pass(git_remote_get_push_refspecs(&array, _remote));
- cl_assert_equal_i(1, (int)array.count);
- cl_assert_equal_s(push_refspec, array.strings[0]);
- cl_assert_equal_s(git_remote_url(_remote), "git://github.com/libgit2/libgit2");
- cl_assert_equal_s(git_remote_pushurl(_remote), "git://github.com/libgit2/libgit2_push");
- git_strarray_free(&array);
-
- /* remove the pushurl again and see if we can save that too */
- cl_git_pass(git_remote_set_pushurl(_remote, NULL));
- cl_git_pass(git_remote_save(_remote));
- git_remote_free(_remote);
- _remote = NULL;
-
- cl_git_pass(git_remote_load(&_remote, _repo, "upstream"));
- cl_assert(git_remote_pushurl(_remote) == NULL);
-}
-
-void test_network_remote_remotes__fnmatch(void)
-{
- cl_assert(git_refspec_src_matches(_refspec, "refs/heads/master"));
- cl_assert(git_refspec_src_matches(_refspec, "refs/heads/multi/level/branch"));
-}
-
-void test_network_remote_remotes__transform(void)
-{
- char ref[1024] = {0};
-
- cl_git_pass(git_refspec_transform(ref, sizeof(ref), _refspec, "refs/heads/master"));
- cl_assert_equal_s(ref, "refs/remotes/test/master");
-}
-
-void test_network_remote_remotes__transform_destination_to_source(void)
-{
- char ref[1024] = {0};
-
- cl_git_pass(git_refspec_rtransform(ref, sizeof(ref), _refspec, "refs/remotes/test/master"));
- cl_assert_equal_s(ref, "refs/heads/master");
-}
-
-void test_network_remote_remotes__transform_r(void)
-{
- git_buf buf = GIT_BUF_INIT;
-
- cl_git_pass(git_refspec_transform_r(&buf, _refspec, "refs/heads/master"));
- cl_assert_equal_s(git_buf_cstr(&buf), "refs/remotes/test/master");
- git_buf_free(&buf);
-}
-
-void test_network_remote_remotes__missing_refspecs(void)
-{
- git_config *cfg;
-
- git_remote_free(_remote);
- _remote = NULL;
-
- cl_git_pass(git_repository_config(&cfg, _repo));
- cl_git_pass(git_config_set_string(cfg, "remote.specless.url", "http://example.com"));
- cl_git_pass(git_remote_load(&_remote, _repo, "specless"));
-
- git_config_free(cfg);
-}
-
-void test_network_remote_remotes__list(void)
-{
- git_strarray list;
- git_config *cfg;
-
- cl_git_pass(git_remote_list(&list, _repo));
- cl_assert(list.count == 4);
- git_strarray_free(&list);
-
- cl_git_pass(git_repository_config(&cfg, _repo));
- cl_git_pass(git_config_set_string(cfg, "remote.specless.url", "http://example.com"));
- cl_git_pass(git_remote_list(&list, _repo));
- cl_assert(list.count == 5);
- git_strarray_free(&list);
-
- git_config_free(cfg);
-}
-
-void test_network_remote_remotes__loading_a_missing_remote_returns_ENOTFOUND(void)
-{
- git_remote_free(_remote);
- _remote = NULL;
-
- cl_assert_equal_i(GIT_ENOTFOUND, git_remote_load(&_remote, _repo, "just-left-few-minutes-ago"));
-}
-
-void test_network_remote_remotes__loading_with_an_invalid_name_returns_EINVALIDSPEC(void)
-{
- git_remote_free(_remote);
- _remote = NULL;
-
- cl_assert_equal_i(GIT_EINVALIDSPEC, git_remote_load(&_remote, _repo, "Inv@{id"));
-}
-
-/*
- * $ git remote add addtest http://github.com/libgit2/libgit2
- *
- * $ cat .git/config
- * [...]
- * [remote "addtest"]
- * url = http://github.com/libgit2/libgit2
- * fetch = +refs/heads/\*:refs/remotes/addtest/\*
- */
-void test_network_remote_remotes__add(void)
-{
- git_remote_free(_remote);
- _remote = NULL;
-
- cl_git_pass(git_remote_create(&_remote, _repo, "addtest", "http://github.com/libgit2/libgit2"));
- cl_assert_equal_i(GIT_REMOTE_DOWNLOAD_TAGS_AUTO, git_remote_autotag(_remote));
-
- git_remote_free(_remote);
- _remote = NULL;
-
- cl_git_pass(git_remote_load(&_remote, _repo, "addtest"));
- cl_assert_equal_i(GIT_REMOTE_DOWNLOAD_TAGS_AUTO, git_remote_autotag(_remote));
-
- _refspec = git_vector_get(&_remote->refspecs, 0);
- cl_assert_equal_s("refs/heads/*", git_refspec_src(_refspec));
- cl_assert(git_refspec_force(_refspec) == 1);
- cl_assert_equal_s("refs/remotes/addtest/*", git_refspec_dst(_refspec));
- cl_assert_equal_s(git_remote_url(_remote), "http://github.com/libgit2/libgit2");
-}
-
-void test_network_remote_remotes__cannot_add_a_nameless_remote(void)
-{
- git_remote *remote;
-
- cl_assert_equal_i(
- GIT_EINVALIDSPEC,
- git_remote_create(&remote, _repo, NULL, "git://github.com/libgit2/libgit2"));
-}
-
-void test_network_remote_remotes__cannot_save_an_inmemory_remote(void)
-{
- git_remote *remote;
-
- cl_git_pass(git_remote_create_inmemory(&remote, _repo, NULL, "git://github.com/libgit2/libgit2"));
-
- cl_assert_equal_p(NULL, git_remote_name(remote));
-
- cl_git_fail(git_remote_save(remote));
- git_remote_free(remote);
-}
-
-void test_network_remote_remotes__cannot_add_a_remote_with_an_invalid_name(void)
-{
- git_remote *remote = NULL;
-
- cl_assert_equal_i(
- GIT_EINVALIDSPEC,
- git_remote_create(&remote, _repo, "Inv@{id", "git://github.com/libgit2/libgit2"));
- cl_assert_equal_p(remote, NULL);
-
- cl_assert_equal_i(
- GIT_EINVALIDSPEC,
- git_remote_create(&remote, _repo, "", "git://github.com/libgit2/libgit2"));
- cl_assert_equal_p(remote, NULL);
-}
-
-void test_network_remote_remotes__tagopt(void)
-{
- const char *opt;
- git_config *cfg;
-
- cl_git_pass(git_repository_config(&cfg, _repo));
-
- git_remote_set_autotag(_remote, GIT_REMOTE_DOWNLOAD_TAGS_ALL);
- cl_git_pass(git_remote_save(_remote));
- cl_git_pass(git_config_get_string(&opt, cfg, "remote.test.tagopt"));
- cl_assert_equal_s("--tags", opt);
-
- git_remote_set_autotag(_remote, GIT_REMOTE_DOWNLOAD_TAGS_NONE);
- cl_git_pass(git_remote_save(_remote));
- cl_git_pass(git_config_get_string(&opt, cfg, "remote.test.tagopt"));
- cl_assert_equal_s("--no-tags", opt);
-
- git_remote_set_autotag(_remote, GIT_REMOTE_DOWNLOAD_TAGS_AUTO);
- cl_git_pass(git_remote_save(_remote));
- cl_assert(git_config_get_string(&opt, cfg, "remote.test.tagopt") == GIT_ENOTFOUND);
-
- git_config_free(cfg);
-}
-
-void test_network_remote_remotes__cannot_load_with_an_empty_url(void)
-{
- git_remote *remote = NULL;
-
- cl_git_fail(git_remote_load(&remote, _repo, "empty-remote-url"));
- cl_assert(giterr_last()->klass == GITERR_INVALID);
- cl_assert_equal_p(remote, NULL);
-}
-
-void test_network_remote_remotes__check_structure_version(void)
-{
- git_transport transport = GIT_TRANSPORT_INIT;
- const git_error *err;
-
- git_remote_free(_remote);
- _remote = NULL;
- cl_git_pass(git_remote_create_inmemory(&_remote, _repo, NULL, "test-protocol://localhost"));
-
- transport.version = 0;
- cl_git_fail(git_remote_set_transport(_remote, &transport));
- err = giterr_last();
- cl_assert_equal_i(GITERR_INVALID, err->klass);
-
- giterr_clear();
- transport.version = 1024;
- cl_git_fail(git_remote_set_transport(_remote, &transport));
- err = giterr_last();
- cl_assert_equal_i(GITERR_INVALID, err->klass);
-}
-
-void assert_cannot_create_remote(const char *name, int expected_error)
-{
- git_remote *remote = NULL;
-
- cl_git_fail_with(
- git_remote_create(&remote, _repo, name, "git://github.com/libgit2/libgit2"),
- expected_error);
-
- cl_assert_equal_p(remote, NULL);
-}
-
-void test_network_remote_remotes__cannot_create_a_remote_which_name_conflicts_with_an_existing_remote(void)
-{
- assert_cannot_create_remote("test", GIT_EEXISTS);
-}
-
-
-void test_network_remote_remotes__cannot_create_a_remote_which_name_is_invalid(void)
-{
- assert_cannot_create_remote("/", GIT_EINVALIDSPEC);
- assert_cannot_create_remote("//", GIT_EINVALIDSPEC);
- assert_cannot_create_remote(".lock", GIT_EINVALIDSPEC);
- assert_cannot_create_remote("a.lock", GIT_EINVALIDSPEC);
-}
-
-static const char *fetch_refspecs[] = {
- "+refs/heads/*:refs/remotes/origin/*",
- "refs/tags/*:refs/tags/*",
- "+refs/pull/*:refs/pull/*",
-};
-
-static const char *push_refspecs[] = {
- "refs/heads/*:refs/heads/*",
- "refs/tags/*:refs/tags/*",
- "refs/notes/*:refs/notes/*",
-};
-
-void test_network_remote_remotes__query_refspecs(void)
-{
- git_remote *remote;
- git_strarray array;
- int i;
-
- cl_git_pass(git_remote_create_inmemory(&remote, _repo, NULL, "git://github.com/libgit2/libgit2"));
-
- for (i = 0; i < 3; i++) {
- cl_git_pass(git_remote_add_fetch(remote, fetch_refspecs[i]));
- cl_git_pass(git_remote_add_push(remote, push_refspecs[i]));
- }
-
- cl_git_pass(git_remote_get_fetch_refspecs(&array, remote));
- for (i = 0; i < 3; i++) {
- cl_assert_equal_s(fetch_refspecs[i], array.strings[i]);
- }
- git_strarray_free(&array);
-
- cl_git_pass(git_remote_get_push_refspecs(&array, remote));
- for (i = 0; i < 3; i++) {
- cl_assert_equal_s(push_refspecs[i], array.strings[i]);
- }
- git_strarray_free(&array);
-
- git_remote_free(remote);
-}
diff --git a/tests-clar/network/urlparse.c b/tests-clar/network/urlparse.c
deleted file mode 100644
index 173e57d0f..000000000
--- a/tests-clar/network/urlparse.c
+++ /dev/null
@@ -1,82 +0,0 @@
-#include "clar_libgit2.h"
-#include "netops.h"
-
-char *host, *port, *user, *pass;
-
-void test_network_urlparse__initialize(void)
-{
- host = port = user = pass = NULL;
-}
-
-void test_network_urlparse__cleanup(void)
-{
-#define FREE_AND_NULL(x) if (x) { git__free(x); x = NULL; }
- FREE_AND_NULL(host);
- FREE_AND_NULL(port);
- FREE_AND_NULL(user);
- FREE_AND_NULL(pass);
-}
-
-void test_network_urlparse__trivial(void)
-{
- cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass,
- "example.com/resource", "8080"));
- cl_assert_equal_s(host, "example.com");
- cl_assert_equal_s(port, "8080");
- cl_assert_equal_p(user, NULL);
- cl_assert_equal_p(pass, NULL);
-}
-
-void test_network_urlparse__user(void)
-{
- cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass,
- "user@example.com/resource", "8080"));
- cl_assert_equal_s(host, "example.com");
- cl_assert_equal_s(port, "8080");
- cl_assert_equal_s(user, "user");
- cl_assert_equal_p(pass, NULL);
-}
-
-void test_network_urlparse__user_pass(void)
-{
- /* user:pass@hostname.tld/resource */
- cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass,
- "user:pass@example.com/resource", "8080"));
- cl_assert_equal_s(host, "example.com");
- cl_assert_equal_s(port, "8080");
- cl_assert_equal_s(user, "user");
- cl_assert_equal_s(pass, "pass");
-}
-
-void test_network_urlparse__port(void)
-{
- /* hostname.tld:port/resource */
- cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass,
- "example.com:9191/resource", "8080"));
- cl_assert_equal_s(host, "example.com");
- cl_assert_equal_s(port, "9191");
- cl_assert_equal_p(user, NULL);
- cl_assert_equal_p(pass, NULL);
-}
-
-void test_network_urlparse__user_port(void)
-{
- /* user@hostname.tld:port/resource */
- cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass,
- "user@example.com:9191/resource", "8080"));
- cl_assert_equal_s(host, "example.com");
- cl_assert_equal_s(port, "9191");
- cl_assert_equal_s(user, "user");
- cl_assert_equal_p(pass, NULL);
-}
-
-void test_network_urlparse__user_pass_port(void)
-{
- /* user:pass@hostname.tld:port/resource */
- cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass,
- "user:pass@example.com:9191/resource", "8080"));
- cl_assert_equal_s(host, "example.com");
- cl_assert_equal_s(port, "9191");
- cl_assert_equal_s(user, "user");
- cl_assert_equal_s(pass, "pass");
-}
diff --git a/tests-clar/object/blob/filter.c b/tests-clar/object/blob/filter.c
deleted file mode 100644
index 042bddab7..000000000
--- a/tests-clar/object/blob/filter.c
+++ /dev/null
@@ -1,132 +0,0 @@
-#include "clar_libgit2.h"
-#include "posix.h"
-#include "blob.h"
-#include "filter.h"
-#include "buf_text.h"
-
-static git_repository *g_repo = NULL;
-#define NUM_TEST_OBJECTS 8
-static git_oid g_oids[NUM_TEST_OBJECTS];
-static const char *g_raw[NUM_TEST_OBJECTS] = {
- "",
- "foo\nbar\n",
- "foo\rbar\r",
- "foo\r\nbar\r\n",
- "foo\nbar\rboth\r\nreversed\n\ragain\nproblems\r",
- "123\n\000\001\002\003\004abc\255\254\253\r\n",
- "\xEF\xBB\xBFThis is UTF-8\n",
- "\xFE\xFF\x00T\x00h\x00i\x00s\x00!"
-};
-static git_off_t g_len[NUM_TEST_OBJECTS] = { -1, -1, -1, -1, -1, 17, -1, 12 };
-static git_buf_text_stats g_stats[NUM_TEST_OBJECTS] = {
- { 0, 0, 0, 0, 0, 0, 0 },
- { 0, 0, 0, 2, 0, 6, 0 },
- { 0, 0, 2, 0, 0, 6, 0 },
- { 0, 0, 2, 2, 2, 6, 0 },
- { 0, 0, 4, 4, 1, 31, 0 },
- { 0, 1, 1, 2, 1, 9, 5 },
- { GIT_BOM_UTF8, 0, 0, 1, 0, 16, 0 },
- { GIT_BOM_UTF16_BE, 5, 0, 0, 0, 7, 5 },
-};
-static git_buf g_crlf_filtered[NUM_TEST_OBJECTS] = {
- { "", 0, 0 },
- { "foo\nbar\n", 0, 8 },
- { "foo\rbar\r", 0, 8 },
- { "foo\nbar\n", 0, 8 },
- { "foo\nbar\rboth\nreversed\n\ragain\nproblems\r", 0, 38 },
- { "123\n\000\001\002\003\004abc\255\254\253\n", 0, 16 },
- { "\xEF\xBB\xBFThis is UTF-8\n", 0, 17 },
- { "\xFE\xFF\x00T\x00h\x00i\x00s\x00!", 0, 12 }
-};
-
-void test_object_blob_filter__initialize(void)
-{
- int i;
-
- cl_fixture_sandbox("empty_standard_repo");
- cl_git_pass(p_rename(
- "empty_standard_repo/.gitted", "empty_standard_repo/.git"));
- cl_git_pass(git_repository_open(&g_repo, "empty_standard_repo"));
-
- for (i = 0; i < NUM_TEST_OBJECTS; i++) {
- size_t len = (g_len[i] < 0) ? strlen(g_raw[i]) : (size_t)g_len[i];
- g_len[i] = (git_off_t)len;
-
- cl_git_pass(
- git_blob_create_frombuffer(&g_oids[i], g_repo, g_raw[i], len)
- );
- }
-}
-
-void test_object_blob_filter__cleanup(void)
-{
- git_repository_free(g_repo);
- g_repo = NULL;
- cl_fixture_cleanup("empty_standard_repo");
-}
-
-void test_object_blob_filter__unfiltered(void)
-{
- int i;
- git_blob *blob;
-
- for (i = 0; i < NUM_TEST_OBJECTS; i++) {
- cl_git_pass(git_blob_lookup(&blob, g_repo, &g_oids[i]));
- cl_assert(g_len[i] == git_blob_rawsize(blob));
- cl_assert(memcmp(git_blob_rawcontent(blob), g_raw[i], (size_t)g_len[i]) == 0);
- git_blob_free(blob);
- }
-}
-
-void test_object_blob_filter__stats(void)
-{
- int i;
- git_blob *blob;
- git_buf buf = GIT_BUF_INIT;
- git_buf_text_stats stats;
-
- for (i = 0; i < NUM_TEST_OBJECTS; i++) {
- cl_git_pass(git_blob_lookup(&blob, g_repo, &g_oids[i]));
- cl_git_pass(git_blob__getbuf(&buf, blob));
- git_buf_text_gather_stats(&stats, &buf, false);
- cl_assert(memcmp(&g_stats[i], &stats, sizeof(stats)) == 0);
- git_blob_free(blob);
- }
-
- git_buf_free(&buf);
-}
-
-void test_object_blob_filter__to_odb(void)
-{
- git_vector filters = GIT_VECTOR_INIT;
- git_config *cfg;
- int i;
- git_blob *blob;
- git_buf orig = GIT_BUF_INIT, out = GIT_BUF_INIT;
-
- cl_git_pass(git_repository_config(&cfg, g_repo));
- cl_assert(cfg);
-
- git_attr_cache_flush(g_repo);
- cl_git_append2file("empty_standard_repo/.gitattributes", "*.txt text\n");
-
- cl_assert(git_filters_load(
- &filters, g_repo, "filename.txt", GIT_FILTER_TO_ODB) > 0);
- cl_assert(filters.length == 1);
-
- for (i = 0; i < NUM_TEST_OBJECTS; i++) {
- cl_git_pass(git_blob_lookup(&blob, g_repo, &g_oids[i]));
- cl_git_pass(git_blob__getbuf(&orig, blob));
-
- cl_git_pass(git_filters_apply(&out, &orig, &filters));
- cl_assert(git_buf_cmp(&out, &g_crlf_filtered[i]) == 0);
-
- git_blob_free(blob);
- }
-
- git_filters_free(&filters);
- git_buf_free(&orig);
- git_buf_free(&out);
- git_config_free(cfg);
-}
-
diff --git a/tests-clar/object/blob/fromchunks.c b/tests-clar/object/blob/fromchunks.c
deleted file mode 100644
index dc57d4fbe..000000000
--- a/tests-clar/object/blob/fromchunks.c
+++ /dev/null
@@ -1,87 +0,0 @@
-#include "clar_libgit2.h"
-#include "buffer.h"
-#include "posix.h"
-#include "path.h"
-#include "fileops.h"
-
-static git_repository *repo;
-static char textual_content[] = "libgit2\n\r\n\0";
-
-void test_object_blob_fromchunks__initialize(void)
-{
- repo = cl_git_sandbox_init("testrepo.git");
-}
-
-void test_object_blob_fromchunks__cleanup(void)
-{
- cl_git_sandbox_cleanup();
-}
-
-static int text_chunked_source_cb(char *content, size_t max_length, void *payload)
-{
- int *count;
-
- GIT_UNUSED(max_length);
-
- count = (int *)payload;
- (*count)--;
-
- if (*count == 0)
- return 0;
-
- strcpy(content, textual_content);
- return (int)strlen(textual_content);
-}
-
-void test_object_blob_fromchunks__can_create_a_blob_from_a_in_memory_chunk_provider(void)
-{
- git_oid expected_oid, oid;
- git_object *blob;
- int howmany = 7;
-
- cl_git_pass(git_oid_fromstr(&expected_oid, "321cbdf08803c744082332332838df6bd160f8f9"));
-
- cl_git_fail(git_object_lookup(&blob, repo, &expected_oid, GIT_OBJ_ANY));
-
- cl_git_pass(git_blob_create_fromchunks(&oid, repo, NULL, text_chunked_source_cb, &howmany));
-
- cl_git_pass(git_object_lookup(&blob, repo, &expected_oid, GIT_OBJ_ANY));
- git_object_free(blob);
-}
-
-#define GITATTR "* text=auto\n" \
- "*.txt text\n" \
- "*.data binary\n"
-
-static void write_attributes(git_repository *repo)
-{
- git_buf buf = GIT_BUF_INIT;
-
- cl_git_pass(git_buf_joinpath(&buf, git_repository_path(repo), "info"));
- cl_git_pass(git_buf_joinpath(&buf, git_buf_cstr(&buf), "attributes"));
-
- cl_git_pass(git_futils_mkpath2file(git_buf_cstr(&buf), 0777));
- cl_git_rewritefile(git_buf_cstr(&buf), GITATTR);
-
- git_buf_free(&buf);
-}
-
-static void assert_named_chunked_blob(const char *expected_sha, const char *fake_name)
-{
- git_oid expected_oid, oid;
- int howmany = 7;
-
- cl_git_pass(git_oid_fromstr(&expected_oid, expected_sha));
-
- cl_git_pass(git_blob_create_fromchunks(&oid, repo, fake_name, text_chunked_source_cb, &howmany));
- cl_assert(git_oid_cmp(&expected_oid, &oid) == 0);
-}
-
-void test_object_blob_fromchunks__creating_a_blob_from_chunks_honors_the_attributes_directives(void)
-{
- write_attributes(repo);
-
- assert_named_chunked_blob("321cbdf08803c744082332332838df6bd160f8f9", "dummy.data");
- assert_named_chunked_blob("e9671e138a780833cb689753570fd10a55be84fb", "dummy.txt");
- assert_named_chunked_blob("e9671e138a780833cb689753570fd10a55be84fb", "dummy.dunno");
-}
diff --git a/tests-clar/object/raw/write.c b/tests-clar/object/raw/write.c
deleted file mode 100644
index 9709c0302..000000000
--- a/tests-clar/object/raw/write.c
+++ /dev/null
@@ -1,462 +0,0 @@
-#include "clar_libgit2.h"
-#include "git2/odb_backend.h"
-
-#include "fileops.h"
-#include "odb.h"
-
-typedef struct object_data {
- char *id; /* object id (sha1) */
- char *dir; /* object store (fan-out) directory name */
- char *file; /* object store filename */
-} object_data;
-
-static const char *odb_dir = "test-objects";
-
-void test_body(object_data *d, git_rawobj *o);
-
-
-
-// Helpers
-static void remove_object_files(object_data *d)
-{
- cl_git_pass(p_unlink(d->file));
- cl_git_pass(p_rmdir(d->dir));
- cl_assert(errno != ENOTEMPTY);
- cl_git_pass(p_rmdir(odb_dir) < 0);
-}
-
-static void streaming_write(git_oid *oid, git_odb *odb, git_rawobj *raw)
-{
- git_odb_stream *stream;
- int error;
-
- cl_git_pass(git_odb_open_wstream(&stream, odb, raw->len, raw->type));
- stream->write(stream, raw->data, raw->len);
- error = stream->finalize_write(oid, stream);
- stream->free(stream);
- cl_git_pass(error);
-}
-
-static void check_object_files(object_data *d)
-{
- cl_assert(git_path_exists(d->dir));
- cl_assert(git_path_exists(d->file));
-}
-
-static void cmp_objects(git_rawobj *o1, git_rawobj *o2)
-{
- cl_assert(o1->type == o2->type);
- cl_assert(o1->len == o2->len);
- if (o1->len > 0)
- cl_assert(memcmp(o1->data, o2->data, o1->len) == 0);
-}
-
-static void make_odb_dir(void)
-{
- cl_git_pass(p_mkdir(odb_dir, GIT_OBJECT_DIR_MODE));
-}
-
-
-// Standard test form
-void test_body(object_data *d, git_rawobj *o)
-{
- git_odb *db;
- git_oid id1, id2;
- git_odb_object *obj;
- git_rawobj tmp;
-
- make_odb_dir();
- cl_git_pass(git_odb_open(&db, odb_dir));
- cl_git_pass(git_oid_fromstr(&id1, d->id));
-
- streaming_write(&id2, db, o);
- cl_assert(git_oid_cmp(&id1, &id2) == 0);
- check_object_files(d);
-
- cl_git_pass(git_odb_read(&obj, db, &id1));
-
- tmp.data = obj->buffer;
- tmp.len = obj->cached.size;
- tmp.type = obj->cached.type;
-
- cmp_objects(&tmp, o);
-
- git_odb_object_free(obj);
- git_odb_free(db);
- remove_object_files(d);
-}
-
-
-void test_object_raw_write__loose_object(void)
-{
- object_data commit = {
- "3d7f8a6af076c8c3f20071a8935cdbe8228594d1",
- "test-objects/3d",
- "test-objects/3d/7f8a6af076c8c3f20071a8935cdbe8228594d1",
- };
-
- unsigned char commit_data[] = {
- 0x74, 0x72, 0x65, 0x65, 0x20, 0x64, 0x66, 0x66,
- 0x32, 0x64, 0x61, 0x39, 0x30, 0x62, 0x32, 0x35,
- 0x34, 0x65, 0x31, 0x62, 0x65, 0x62, 0x38, 0x38,
- 0x39, 0x64, 0x31, 0x66, 0x31, 0x66, 0x31, 0x32,
- 0x38, 0x38, 0x62, 0x65, 0x31, 0x38, 0x30, 0x33,
- 0x37, 0x38, 0x32, 0x64, 0x66, 0x0a, 0x61, 0x75,
- 0x74, 0x68, 0x6f, 0x72, 0x20, 0x41, 0x20, 0x55,
- 0x20, 0x54, 0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61,
- 0x75, 0x74, 0x68, 0x6f, 0x72, 0x40, 0x65, 0x78,
- 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
- 0x6d, 0x3e, 0x20, 0x31, 0x32, 0x32, 0x37, 0x38,
- 0x31, 0x34, 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30,
- 0x30, 0x30, 0x30, 0x0a, 0x63, 0x6f, 0x6d, 0x6d,
- 0x69, 0x74, 0x74, 0x65, 0x72, 0x20, 0x43, 0x20,
- 0x4f, 0x20, 0x4d, 0x69, 0x74, 0x74, 0x65, 0x72,
- 0x20, 0x3c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
- 0x74, 0x65, 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d,
- 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e,
- 0x20, 0x31, 0x32, 0x32, 0x37, 0x38, 0x31, 0x34,
- 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30, 0x30, 0x30,
- 0x30, 0x0a, 0x0a, 0x41, 0x20, 0x6f, 0x6e, 0x65,
- 0x2d, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x63, 0x6f,
- 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x73, 0x75, 0x6d,
- 0x6d, 0x61, 0x72, 0x79, 0x0a, 0x0a, 0x54, 0x68,
- 0x65, 0x20, 0x62, 0x6f, 0x64, 0x79, 0x20, 0x6f,
- 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f,
- 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x6d, 0x65, 0x73,
- 0x73, 0x61, 0x67, 0x65, 0x2c, 0x20, 0x63, 0x6f,
- 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67,
- 0x20, 0x66, 0x75, 0x72, 0x74, 0x68, 0x65, 0x72,
- 0x20, 0x65, 0x78, 0x70, 0x6c, 0x61, 0x6e, 0x61,
- 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x6f, 0x66, 0x20,
- 0x74, 0x68, 0x65, 0x20, 0x70, 0x75, 0x72, 0x70,
- 0x6f, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74,
- 0x68, 0x65, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67,
- 0x65, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x72, 0x6f,
- 0x64, 0x75, 0x63, 0x65, 0x64, 0x20, 0x62, 0x79,
- 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d,
- 0x6d, 0x69, 0x74, 0x2e, 0x0a, 0x0a, 0x53, 0x69,
- 0x67, 0x6e, 0x65, 0x64, 0x2d, 0x6f, 0x66, 0x2d,
- 0x62, 0x79, 0x3a, 0x20, 0x41, 0x20, 0x55, 0x20,
- 0x54, 0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61, 0x75,
- 0x74, 0x68, 0x6f, 0x72, 0x40, 0x65, 0x78, 0x61,
- 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
- 0x3e, 0x0a,
- };
-
- git_rawobj commit_obj = {
- commit_data,
- sizeof(commit_data),
- GIT_OBJ_COMMIT
- };
-
- test_body(&commit, &commit_obj);
-}
-
-void test_object_raw_write__loose_tree(void)
-{
- static object_data tree = {
- "dff2da90b254e1beb889d1f1f1288be1803782df",
- "test-objects/df",
- "test-objects/df/f2da90b254e1beb889d1f1f1288be1803782df",
- };
-
- static unsigned char tree_data[] = {
- 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x6f,
- 0x6e, 0x65, 0x00, 0x8b, 0x13, 0x78, 0x91, 0x79,
- 0x1f, 0xe9, 0x69, 0x27, 0xad, 0x78, 0xe6, 0x4b,
- 0x0a, 0xad, 0x7b, 0xde, 0xd0, 0x8b, 0xdc, 0x31,
- 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x73, 0x6f,
- 0x6d, 0x65, 0x00, 0xfd, 0x84, 0x30, 0xbc, 0x86,
- 0x4c, 0xfc, 0xd5, 0xf1, 0x0e, 0x55, 0x90, 0xf8,
- 0xa4, 0x47, 0xe0, 0x1b, 0x94, 0x2b, 0xfe, 0x31,
- 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x77,
- 0x6f, 0x00, 0x78, 0x98, 0x19, 0x22, 0x61, 0x3b,
- 0x2a, 0xfb, 0x60, 0x25, 0x04, 0x2f, 0xf6, 0xbd,
- 0x87, 0x8a, 0xc1, 0x99, 0x4e, 0x85, 0x31, 0x30,
- 0x30, 0x36, 0x34, 0x34, 0x20, 0x7a, 0x65, 0x72,
- 0x6f, 0x00, 0xe6, 0x9d, 0xe2, 0x9b, 0xb2, 0xd1,
- 0xd6, 0x43, 0x4b, 0x8b, 0x29, 0xae, 0x77, 0x5a,
- 0xd8, 0xc2, 0xe4, 0x8c, 0x53, 0x91,
- };
-
- static git_rawobj tree_obj = {
- tree_data,
- sizeof(tree_data),
- GIT_OBJ_TREE
- };
-
- test_body(&tree, &tree_obj);
-}
-
-void test_object_raw_write__loose_tag(void)
-{
- static object_data tag = {
- "09d373e1dfdc16b129ceec6dd649739911541e05",
- "test-objects/09",
- "test-objects/09/d373e1dfdc16b129ceec6dd649739911541e05",
- };
-
- static unsigned char tag_data[] = {
- 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x33,
- 0x64, 0x37, 0x66, 0x38, 0x61, 0x36, 0x61, 0x66,
- 0x30, 0x37, 0x36, 0x63, 0x38, 0x63, 0x33, 0x66,
- 0x32, 0x30, 0x30, 0x37, 0x31, 0x61, 0x38, 0x39,
- 0x33, 0x35, 0x63, 0x64, 0x62, 0x65, 0x38, 0x32,
- 0x32, 0x38, 0x35, 0x39, 0x34, 0x64, 0x31, 0x0a,
- 0x74, 0x79, 0x70, 0x65, 0x20, 0x63, 0x6f, 0x6d,
- 0x6d, 0x69, 0x74, 0x0a, 0x74, 0x61, 0x67, 0x20,
- 0x76, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x0a, 0x74,
- 0x61, 0x67, 0x67, 0x65, 0x72, 0x20, 0x43, 0x20,
- 0x4f, 0x20, 0x4d, 0x69, 0x74, 0x74, 0x65, 0x72,
- 0x20, 0x3c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
- 0x74, 0x65, 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d,
- 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e,
- 0x20, 0x31, 0x32, 0x32, 0x37, 0x38, 0x31, 0x34,
- 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30, 0x30, 0x30,
- 0x30, 0x0a, 0x0a, 0x54, 0x68, 0x69, 0x73, 0x20,
- 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74,
- 0x61, 0x67, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63,
- 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x72, 0x65,
- 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20, 0x76, 0x30,
- 0x2e, 0x30, 0x2e, 0x31, 0x0a,
- };
-
- static git_rawobj tag_obj = {
- tag_data,
- sizeof(tag_data),
- GIT_OBJ_TAG
- };
-
-
- test_body(&tag, &tag_obj);
-}
-
-void test_object_raw_write__zero_length(void)
-{
- static object_data zero = {
- "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
- "test-objects/e6",
- "test-objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391",
- };
-
- static unsigned char zero_data[] = {
- 0x00 /* dummy data */
- };
-
- static git_rawobj zero_obj = {
- zero_data,
- 0,
- GIT_OBJ_BLOB
- };
-
- test_body(&zero, &zero_obj);
-}
-
-void test_object_raw_write__one_byte(void)
-{
- static object_data one = {
- "8b137891791fe96927ad78e64b0aad7bded08bdc",
- "test-objects/8b",
- "test-objects/8b/137891791fe96927ad78e64b0aad7bded08bdc",
- };
-
- static unsigned char one_data[] = {
- 0x0a,
- };
-
- static git_rawobj one_obj = {
- one_data,
- sizeof(one_data),
- GIT_OBJ_BLOB
- };
-
- test_body(&one, &one_obj);
-}
-
-void test_object_raw_write__two_byte(void)
-{
- static object_data two = {
- "78981922613b2afb6025042ff6bd878ac1994e85",
- "test-objects/78",
- "test-objects/78/981922613b2afb6025042ff6bd878ac1994e85",
- };
-
- static unsigned char two_data[] = {
- 0x61, 0x0a,
- };
-
- static git_rawobj two_obj = {
- two_data,
- sizeof(two_data),
- GIT_OBJ_BLOB
- };
-
- test_body(&two, &two_obj);
-}
-
-void test_object_raw_write__several_bytes(void)
-{
- static object_data some = {
- "fd8430bc864cfcd5f10e5590f8a447e01b942bfe",
- "test-objects/fd",
- "test-objects/fd/8430bc864cfcd5f10e5590f8a447e01b942bfe",
- };
-
- static unsigned char some_data[] = {
- 0x2f, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x54, 0x68,
- 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20,
- 0x69, 0x73, 0x20, 0x66, 0x72, 0x65, 0x65, 0x20,
- 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
- 0x3b, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x63, 0x61,
- 0x6e, 0x20, 0x72, 0x65, 0x64, 0x69, 0x73, 0x74,
- 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x69,
- 0x74, 0x20, 0x61, 0x6e, 0x64, 0x2f, 0x6f, 0x72,
- 0x20, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0a,
- 0x20, 0x2a, 0x20, 0x69, 0x74, 0x20, 0x75, 0x6e,
- 0x64, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20,
- 0x74, 0x65, 0x72, 0x6d, 0x73, 0x20, 0x6f, 0x66,
- 0x20, 0x74, 0x68, 0x65, 0x20, 0x47, 0x4e, 0x55,
- 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
- 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
- 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x2c,
- 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
- 0x20, 0x32, 0x2c, 0x0a, 0x20, 0x2a, 0x20, 0x61,
- 0x73, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73,
- 0x68, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74,
- 0x68, 0x65, 0x20, 0x46, 0x72, 0x65, 0x65, 0x20,
- 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
- 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74,
- 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x2a, 0x0a,
- 0x20, 0x2a, 0x20, 0x49, 0x6e, 0x20, 0x61, 0x64,
- 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74,
- 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x65,
- 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e,
- 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65,
- 0x20, 0x47, 0x4e, 0x55, 0x20, 0x47, 0x65, 0x6e,
- 0x65, 0x72, 0x61, 0x6c, 0x20, 0x50, 0x75, 0x62,
- 0x6c, 0x69, 0x63, 0x20, 0x4c, 0x69, 0x63, 0x65,
- 0x6e, 0x73, 0x65, 0x2c, 0x0a, 0x20, 0x2a, 0x20,
- 0x74, 0x68, 0x65, 0x20, 0x61, 0x75, 0x74, 0x68,
- 0x6f, 0x72, 0x73, 0x20, 0x67, 0x69, 0x76, 0x65,
- 0x20, 0x79, 0x6f, 0x75, 0x20, 0x75, 0x6e, 0x6c,
- 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x20, 0x70,
- 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f,
- 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x6c, 0x69, 0x6e,
- 0x6b, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f,
- 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x64, 0x0a, 0x20,
- 0x2a, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
- 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69,
- 0x73, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x69,
- 0x6e, 0x74, 0x6f, 0x20, 0x63, 0x6f, 0x6d, 0x62,
- 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
- 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6f, 0x74,
- 0x68, 0x65, 0x72, 0x20, 0x70, 0x72, 0x6f, 0x67,
- 0x72, 0x61, 0x6d, 0x73, 0x2c, 0x0a, 0x20, 0x2a,
- 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x6f, 0x20,
- 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75,
- 0x74, 0x65, 0x20, 0x74, 0x68, 0x6f, 0x73, 0x65,
- 0x20, 0x63, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x61,
- 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x77, 0x69,
- 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x61, 0x6e,
- 0x79, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69,
- 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x2a,
- 0x20, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x20,
- 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65,
- 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20,
- 0x74, 0x68, 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c,
- 0x65, 0x2e, 0x20, 0x20, 0x28, 0x54, 0x68, 0x65,
- 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
- 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
- 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x0a,
- 0x20, 0x2a, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72,
- 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20,
- 0x64, 0x6f, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x79,
- 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x74, 0x68, 0x65,
- 0x72, 0x20, 0x72, 0x65, 0x73, 0x70, 0x65, 0x63,
- 0x74, 0x73, 0x3b, 0x20, 0x66, 0x6f, 0x72, 0x20,
- 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c,
- 0x20, 0x74, 0x68, 0x65, 0x79, 0x20, 0x63, 0x6f,
- 0x76, 0x65, 0x72, 0x0a, 0x20, 0x2a, 0x20, 0x6d,
- 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
- 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74,
- 0x68, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x2c,
- 0x20, 0x61, 0x6e, 0x64, 0x20, 0x64, 0x69, 0x73,
- 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f,
- 0x6e, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x6e,
- 0x6f, 0x74, 0x20, 0x6c, 0x69, 0x6e, 0x6b, 0x65,
- 0x64, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x0a, 0x20,
- 0x2a, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x62,
- 0x69, 0x6e, 0x65, 0x64, 0x20, 0x65, 0x78, 0x65,
- 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e,
- 0x29, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20,
- 0x54, 0x68, 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c,
- 0x65, 0x20, 0x69, 0x73, 0x20, 0x64, 0x69, 0x73,
- 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x64,
- 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20,
- 0x68, 0x6f, 0x70, 0x65, 0x20, 0x74, 0x68, 0x61,
- 0x74, 0x20, 0x69, 0x74, 0x20, 0x77, 0x69, 0x6c,
- 0x6c, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65,
- 0x66, 0x75, 0x6c, 0x2c, 0x20, 0x62, 0x75, 0x74,
- 0x0a, 0x20, 0x2a, 0x20, 0x57, 0x49, 0x54, 0x48,
- 0x4f, 0x55, 0x54, 0x20, 0x41, 0x4e, 0x59, 0x20,
- 0x57, 0x41, 0x52, 0x52, 0x41, 0x4e, 0x54, 0x59,
- 0x3b, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75,
- 0x74, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x20, 0x74,
- 0x68, 0x65, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x69,
- 0x65, 0x64, 0x20, 0x77, 0x61, 0x72, 0x72, 0x61,
- 0x6e, 0x74, 0x79, 0x20, 0x6f, 0x66, 0x0a, 0x20,
- 0x2a, 0x20, 0x4d, 0x45, 0x52, 0x43, 0x48, 0x41,
- 0x4e, 0x54, 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54,
- 0x59, 0x20, 0x6f, 0x72, 0x20, 0x46, 0x49, 0x54,
- 0x4e, 0x45, 0x53, 0x53, 0x20, 0x46, 0x4f, 0x52,
- 0x20, 0x41, 0x20, 0x50, 0x41, 0x52, 0x54, 0x49,
- 0x43, 0x55, 0x4c, 0x41, 0x52, 0x20, 0x50, 0x55,
- 0x52, 0x50, 0x4f, 0x53, 0x45, 0x2e, 0x20, 0x20,
- 0x53, 0x65, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20,
- 0x47, 0x4e, 0x55, 0x0a, 0x20, 0x2a, 0x20, 0x47,
- 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x20, 0x50,
- 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x4c, 0x69,
- 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x66, 0x6f,
- 0x72, 0x20, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x64,
- 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x0a,
- 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x59, 0x6f,
- 0x75, 0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64,
- 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x72, 0x65,
- 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x20, 0x61,
- 0x20, 0x63, 0x6f, 0x70, 0x79, 0x20, 0x6f, 0x66,
- 0x20, 0x74, 0x68, 0x65, 0x20, 0x47, 0x4e, 0x55,
- 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
- 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
- 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x0a,
- 0x20, 0x2a, 0x20, 0x61, 0x6c, 0x6f, 0x6e, 0x67,
- 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68,
- 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72,
- 0x61, 0x6d, 0x3b, 0x20, 0x73, 0x65, 0x65, 0x20,
- 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x65,
- 0x20, 0x43, 0x4f, 0x50, 0x59, 0x49, 0x4e, 0x47,
- 0x2e, 0x20, 0x20, 0x49, 0x66, 0x20, 0x6e, 0x6f,
- 0x74, 0x2c, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65,
- 0x20, 0x74, 0x6f, 0x0a, 0x20, 0x2a, 0x20, 0x74,
- 0x68, 0x65, 0x20, 0x46, 0x72, 0x65, 0x65, 0x20,
- 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
- 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74,
- 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x35, 0x31, 0x20,
- 0x46, 0x72, 0x61, 0x6e, 0x6b, 0x6c, 0x69, 0x6e,
- 0x20, 0x53, 0x74, 0x72, 0x65, 0x65, 0x74, 0x2c,
- 0x20, 0x46, 0x69, 0x66, 0x74, 0x68, 0x20, 0x46,
- 0x6c, 0x6f, 0x6f, 0x72, 0x2c, 0x0a, 0x20, 0x2a,
- 0x20, 0x42, 0x6f, 0x73, 0x74, 0x6f, 0x6e, 0x2c,
- 0x20, 0x4d, 0x41, 0x20, 0x30, 0x32, 0x31, 0x31,
- 0x30, 0x2d, 0x31, 0x33, 0x30, 0x31, 0x2c, 0x20,
- 0x55, 0x53, 0x41, 0x2e, 0x0a, 0x20, 0x2a, 0x2f,
- 0x0a,
- };
-
- static git_rawobj some_obj = {
- some_data,
- sizeof(some_data),
- GIT_OBJ_BLOB
- };
-
- test_body(&some, &some_obj);
-}
diff --git a/tests-clar/object/tree/attributes.c b/tests-clar/object/tree/attributes.c
deleted file mode 100644
index cc93b45d2..000000000
--- a/tests-clar/object/tree/attributes.c
+++ /dev/null
@@ -1,114 +0,0 @@
-#include "clar_libgit2.h"
-#include "tree.h"
-
-static const char *blob_oid = "3d0970ec547fc41ef8a5882dde99c6adce65b021";
-static const char *tree_oid = "1b05fdaa881ee45b48cbaa5e9b037d667a47745e";
-
-void test_object_tree_attributes__ensure_correctness_of_attributes_on_insertion(void)
-{
- git_treebuilder *builder;
- git_oid oid;
-
- cl_git_pass(git_oid_fromstr(&oid, blob_oid));
-
- cl_git_pass(git_treebuilder_create(&builder, NULL));
-
- cl_git_fail(git_treebuilder_insert(NULL, builder, "one.txt", &oid, (git_filemode_t)0777777));
- cl_git_fail(git_treebuilder_insert(NULL, builder, "one.txt", &oid, (git_filemode_t)0100666));
- cl_git_fail(git_treebuilder_insert(NULL, builder, "one.txt", &oid, (git_filemode_t)0000001));
-
- git_treebuilder_free(builder);
-}
-
-void test_object_tree_attributes__group_writable_tree_entries_created_with_an_antique_git_version_can_still_be_accessed(void)
-{
- git_repository *repo;
- git_oid tid;
- git_tree *tree;
- const git_tree_entry *entry;
-
- cl_git_pass(git_repository_open(&repo, cl_fixture("deprecated-mode.git")));
-
- cl_git_pass(git_oid_fromstr(&tid, tree_oid));
- cl_git_pass(git_tree_lookup(&tree, repo, &tid));
-
- entry = git_tree_entry_byname(tree, "old_mode.txt");
- cl_assert_equal_i(
- GIT_FILEMODE_BLOB,
- git_tree_entry_filemode(entry));
-
- git_tree_free(tree);
- git_repository_free(repo);
-}
-
-void test_object_tree_attributes__treebuilder_reject_invalid_filemode(void)
-{
- git_treebuilder *builder;
- git_oid bid;
- const git_tree_entry *entry;
-
- cl_git_pass(git_oid_fromstr(&bid, blob_oid));
- cl_git_pass(git_treebuilder_create(&builder, NULL));
-
- cl_git_fail(git_treebuilder_insert(
- &entry,
- builder,
- "normalized.txt",
- &bid,
- GIT_FILEMODE_BLOB_GROUP_WRITABLE));
-
- git_treebuilder_free(builder);
-}
-
-void test_object_tree_attributes__normalize_attributes_when_creating_a_tree_from_an_existing_one(void)
-{
- git_repository *repo;
- git_treebuilder *builder;
- git_oid tid, tid2;
- git_tree *tree;
- const git_tree_entry *entry;
-
- repo = cl_git_sandbox_init("deprecated-mode.git");
-
- cl_git_pass(git_oid_fromstr(&tid, tree_oid));
- cl_git_pass(git_tree_lookup(&tree, repo, &tid));
-
- cl_git_pass(git_treebuilder_create(&builder, tree));
-
- entry = git_treebuilder_get(builder, "old_mode.txt");
- cl_assert_equal_i(
- GIT_FILEMODE_BLOB,
- git_tree_entry_filemode(entry));
-
- cl_git_pass(git_treebuilder_write(&tid2, repo, builder));
- git_treebuilder_free(builder);
- git_tree_free(tree);
-
- cl_git_pass(git_tree_lookup(&tree, repo, &tid2));
- entry = git_tree_entry_byname(tree, "old_mode.txt");
- cl_assert_equal_i(
- GIT_FILEMODE_BLOB,
- git_tree_entry_filemode(entry));
-
- git_tree_free(tree);
- cl_git_sandbox_cleanup();
-}
-
-void test_object_tree_attributes__normalize_600(void)
-{
- git_oid id;
- git_tree *tree;
- git_repository *repo;
- const git_tree_entry *entry;
-
- repo = cl_git_sandbox_init("deprecated-mode.git");
-
- git_oid_fromstr(&id, "0810fb7818088ff5ac41ee49199b51473b1bd6c7");
- cl_git_pass(git_tree_lookup(&tree, repo, &id));
-
- entry = git_tree_entry_byname(tree, "ListaTeste.xml");
- cl_assert_equal_i(entry->attr, GIT_FILEMODE_BLOB);
-
- git_tree_free(tree);
- cl_git_sandbox_cleanup();
-}
diff --git a/tests-clar/object/tree/walk.c b/tests-clar/object/tree/walk.c
deleted file mode 100644
index b7af4924d..000000000
--- a/tests-clar/object/tree/walk.c
+++ /dev/null
@@ -1,103 +0,0 @@
-#include "clar_libgit2.h"
-#include "tree.h"
-
-static const char *tree_oid = "1810dff58d8a660512d4832e740f692884338ccd";
-static git_repository *g_repo;
-
-void test_object_tree_walk__initialize(void)
-{
- g_repo = cl_git_sandbox_init("testrepo");
-}
-
-void test_object_tree_walk__cleanup(void)
-{
- cl_git_sandbox_cleanup();
-}
-
-static int treewalk_count_cb(
- const char *root, const git_tree_entry *entry, void *payload)
-{
- int *count = payload;
-
- GIT_UNUSED(root);
- GIT_UNUSED(entry);
-
- (*count) += 1;
-
- return 0;
-}
-
-void test_object_tree_walk__0(void)
-{
- git_oid id;
- git_tree *tree;
- int ct;
-
- git_oid_fromstr(&id, tree_oid);
-
- cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
-
- ct = 0;
- cl_git_pass(git_tree_walk(tree, GIT_TREEWALK_PRE, treewalk_count_cb, &ct));
- cl_assert_equal_i(3, ct);
-
- ct = 0;
- cl_git_pass(git_tree_walk(tree, GIT_TREEWALK_POST, treewalk_count_cb, &ct));
- cl_assert_equal_i(3, ct);
-
- git_tree_free(tree);
-}
-
-
-static int treewalk_stop_cb(
- const char *root, const git_tree_entry *entry, void *payload)
-{
- int *count = payload;
-
- GIT_UNUSED(root);
- GIT_UNUSED(entry);
-
- (*count) += 1;
-
- return (*count == 2) ? -1 : 0;
-}
-
-static int treewalk_stop_immediately_cb(
- const char *root, const git_tree_entry *entry, void *payload)
-{
- GIT_UNUSED(root);
- GIT_UNUSED(entry);
- GIT_UNUSED(payload);
- return -100;
-}
-
-void test_object_tree_walk__1(void)
-{
- git_oid id;
- git_tree *tree;
- int ct;
-
- git_oid_fromstr(&id, tree_oid);
-
- cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
-
- ct = 0;
- cl_assert_equal_i(
- GIT_EUSER, git_tree_walk(tree, GIT_TREEWALK_PRE, treewalk_stop_cb, &ct));
- cl_assert_equal_i(2, ct);
-
- ct = 0;
- cl_assert_equal_i(
- GIT_EUSER, git_tree_walk(tree, GIT_TREEWALK_POST, treewalk_stop_cb, &ct));
- cl_assert_equal_i(2, ct);
-
- cl_assert_equal_i(
- GIT_EUSER, git_tree_walk(
- tree, GIT_TREEWALK_PRE, treewalk_stop_immediately_cb, NULL));
-
- cl_assert_equal_i(
- GIT_EUSER, git_tree_walk(
- tree, GIT_TREEWALK_POST, treewalk_stop_immediately_cb, NULL));
-
- git_tree_free(tree);
-}
diff --git a/tests-clar/odb/alternates.c b/tests-clar/odb/alternates.c
deleted file mode 100644
index 4e876c2b3..000000000
--- a/tests-clar/odb/alternates.c
+++ /dev/null
@@ -1,80 +0,0 @@
-#include "clar_libgit2.h"
-#include "odb.h"
-#include "filebuf.h"
-
-static git_buf destpath, filepath;
-static const char *paths[] = {
- "A.git", "B.git", "C.git", "D.git", "E.git", "F.git", "G.git"
-};
-static git_filebuf file;
-static git_repository *repo;
-
-void test_odb_alternates__cleanup(void)
-{
- size_t i;
-
- git_buf_free(&destpath);
- git_buf_free(&filepath);
-
- for (i = 0; i < ARRAY_SIZE(paths); i++)
- cl_fixture_cleanup(paths[i]);
-}
-
-static void init_linked_repo(const char *path, const char *alternate)
-{
- git_buf_clear(&destpath);
- git_buf_clear(&filepath);
-
- cl_git_pass(git_repository_init(&repo, path, 1));
- cl_git_pass(git_path_prettify(&destpath, alternate, NULL));
- cl_git_pass(git_buf_joinpath(&destpath, destpath.ptr, "objects"));
- cl_git_pass(git_buf_joinpath(&filepath, git_repository_path(repo), "objects/info"));
- cl_git_pass(git_futils_mkdir(filepath.ptr, NULL, 0755, GIT_MKDIR_PATH));
- cl_git_pass(git_buf_joinpath(&filepath, filepath.ptr , "alternates"));
-
- cl_git_pass(git_filebuf_open(&file, git_buf_cstr(&filepath), 0));
- git_filebuf_printf(&file, "%s\n", git_buf_cstr(&destpath));
- cl_git_pass(git_filebuf_commit(&file, 0644));
-
- git_repository_free(repo);
-}
-
-void test_odb_alternates__chained(void)
-{
- git_commit *commit;
- git_oid oid;
-
- /* Set the alternate A -> testrepo.git */
- init_linked_repo(paths[0], cl_fixture("testrepo.git"));
-
- /* Set the alternate B -> A */
- init_linked_repo(paths[1], paths[0]);
-
- /* Now load B and see if we can find an object from testrepo.git */
- cl_git_pass(git_repository_open(&repo, paths[1]));
- git_oid_fromstr(&oid, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
- cl_git_pass(git_commit_lookup(&commit, repo, &oid));
- git_commit_free(commit);
- git_repository_free(repo);
-}
-
-void test_odb_alternates__long_chain(void)
-{
- git_commit *commit;
- git_oid oid;
- size_t i;
-
- /* Set the alternate A -> testrepo.git */
- init_linked_repo(paths[0], cl_fixture("testrepo.git"));
-
- /* Set up the five-element chain */
- for (i = 1; i < ARRAY_SIZE(paths); i++) {
- init_linked_repo(paths[i], paths[i-1]);
- }
-
- /* Now load the last one and see if we can find an object from testrepo.git */
- cl_git_pass(git_repository_open(&repo, paths[ARRAY_SIZE(paths)-1]));
- git_oid_fromstr(&oid, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
- cl_git_fail(git_commit_lookup(&commit, repo, &oid));
- git_repository_free(repo);
-}
diff --git a/tests-clar/odb/foreach.c b/tests-clar/odb/foreach.c
deleted file mode 100644
index f643d9621..000000000
--- a/tests-clar/odb/foreach.c
+++ /dev/null
@@ -1,80 +0,0 @@
-#include "clar_libgit2.h"
-#include "odb.h"
-#include "git2/odb_backend.h"
-#include "pack.h"
-
-static git_odb *_odb;
-static git_repository *_repo;
-static int nobj;
-
-void test_odb_foreach__cleanup(void)
-{
- git_odb_free(_odb);
- git_repository_free(_repo);
-
- _odb = NULL;
- _repo = NULL;
-}
-
-static int foreach_cb(const git_oid *oid, void *data)
-{
- GIT_UNUSED(data);
- GIT_UNUSED(oid);
-
- nobj++;
-
- return 0;
-}
-
-/*
- * $ git --git-dir tests-clar/resources/testrepo.git count-objects --verbose
- * count: 47
- * size: 4
- * in-pack: 1640
- * packs: 3
- * size-pack: 425
- * prune-packable: 0
- * garbage: 0
- */
-void test_odb_foreach__foreach(void)
-{
- cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git")));
- git_repository_odb(&_odb, _repo);
-
- cl_git_pass(git_odb_foreach(_odb, foreach_cb, NULL));
- cl_assert_equal_i(47 + 1640, nobj); /* count + in-pack */
-}
-
-void test_odb_foreach__one_pack(void)
-{
- git_odb_backend *backend = NULL;
-
- cl_git_pass(git_odb_new(&_odb));
- cl_git_pass(git_odb_backend_one_pack(&backend, cl_fixture("testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx")));
- cl_git_pass(git_odb_add_backend(_odb, backend, 1));
- _repo = NULL;
-
- nobj = 0;
- cl_git_pass(git_odb_foreach(_odb, foreach_cb, NULL));
- cl_assert(nobj == 1628);
-}
-
-static int foreach_stop_cb(const git_oid *oid, void *data)
-{
- GIT_UNUSED(data);
- GIT_UNUSED(oid);
-
- nobj++;
-
- return (nobj == 1000);
-}
-
-void test_odb_foreach__interrupt_foreach(void)
-{
- nobj = 0;
- cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git")));
- git_repository_odb(&_odb, _repo);
-
- cl_assert_equal_i(GIT_EUSER, git_odb_foreach(_odb, foreach_stop_cb, NULL));
- cl_assert(nobj == 1000);
-}
diff --git a/tests-clar/odb/loose.c b/tests-clar/odb/loose.c
deleted file mode 100644
index 9539bb24c..000000000
--- a/tests-clar/odb/loose.c
+++ /dev/null
@@ -1,89 +0,0 @@
-#include "clar_libgit2.h"
-#include "odb.h"
-#include "posix.h"
-#include "loose_data.h"
-
-static void write_object_files(object_data *d)
-{
- int fd;
-
- if (p_mkdir(d->dir, GIT_OBJECT_DIR_MODE) < 0)
- cl_assert(errno == EEXIST);
-
- cl_assert((fd = p_creat(d->file, S_IREAD | S_IWRITE)) >= 0);
- cl_must_pass(p_write(fd, d->bytes, d->blen));
-
- p_close(fd);
-}
-
-static void cmp_objects(git_rawobj *o, object_data *d)
-{
- cl_assert(o->type == git_object_string2type(d->type));
- cl_assert(o->len == d->dlen);
-
- if (o->len > 0)
- cl_assert(memcmp(o->data, d->data, o->len) == 0);
-}
-
-static void test_read_object(object_data *data)
-{
- git_oid id;
- git_odb_object *obj;
- git_odb *odb;
- git_rawobj tmp;
-
- 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(&obj, odb, &id));
-
- tmp.data = obj->buffer;
- tmp.len = obj->cached.size;
- tmp.type = obj->cached.type;
-
- cmp_objects(&tmp, data);
-
- git_odb_object_free(obj);
- git_odb_free(odb);
-}
-
-void test_odb_loose__initialize(void)
-{
- cl_must_pass(p_mkdir("test-objects", GIT_OBJECT_DIR_MODE));
-}
-
-void test_odb_loose__cleanup(void)
-{
- cl_fixture_cleanup("test-objects");
-}
-
-void test_odb_loose__exists(void)
-{
- git_oid id, id2;
- git_odb *odb;
-
- write_object_files(&one);
- cl_git_pass(git_odb_open(&odb, "test-objects"));
-
- cl_git_pass(git_oid_fromstr(&id, one.id));
-
- cl_assert(git_odb_exists(odb, &id));
-
- /* Test for a non-existant object */
- cl_git_pass(git_oid_fromstr(&id2, "8b137891791fe96927ad78e64b0aad7bded08baa"));
- cl_assert(!git_odb_exists(odb, &id2));
-
- git_odb_free(odb);
-}
-
-void test_odb_loose__simple_reads(void)
-{
- test_read_object(&commit);
- test_read_object(&tree);
- test_read_object(&tag);
- test_read_object(&zero);
- test_read_object(&one);
- test_read_object(&two);
- test_read_object(&some);
-}
diff --git a/tests-clar/odb/mixed.c b/tests-clar/odb/mixed.c
deleted file mode 100644
index da0ed97d7..000000000
--- a/tests-clar/odb/mixed.c
+++ /dev/null
@@ -1,25 +0,0 @@
-#include "clar_libgit2.h"
-#include "odb.h"
-
-static git_odb *_odb;
-
-void test_odb_mixed__initialize(void)
-{
- cl_git_pass(git_odb_open(&_odb, cl_fixture("duplicate.git/objects")));
-}
-
-void test_odb_mixed__cleanup(void)
-{
- git_odb_free(_odb);
- _odb = NULL;
-}
-
-void test_odb_mixed__dup_oid(void) {
- const char hex[] = "ce013625030ba8dba906f756967f9e9ca394464a";
- git_oid oid;
- git_odb_object *obj;
- cl_git_pass(git_oid_fromstr(&oid, hex));
- cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, GIT_OID_HEXSZ));
- git_odb_object_free(obj);
-}
-
diff --git a/tests-clar/online/clone.c b/tests-clar/online/clone.c
deleted file mode 100644
index bc4285a00..000000000
--- a/tests-clar/online/clone.c
+++ /dev/null
@@ -1,204 +0,0 @@
-#include "clar_libgit2.h"
-
-#include "git2/clone.h"
-#include "git2/cred_helpers.h"
-#include "remote.h"
-#include "fileops.h"
-#include "refs.h"
-
-#define LIVE_REPO_URL "http://github.com/libgit2/TestGitRepository"
-#define LIVE_EMPTYREPO_URL "http://github.com/libgit2/TestEmptyRepository"
-#define BB_REPO_URL "https://libgit2@bitbucket.org/libgit2/testgitrepository.git"
-#define BB_REPO_URL_WITH_PASS "https://libgit2:libgit2@bitbucket.org/libgit2/testgitrepository.git"
-#define BB_REPO_URL_WITH_WRONG_PASS "https://libgit2:wrong@bitbucket.org/libgit2/testgitrepository.git"
-
-static git_repository *g_repo;
-static git_clone_options g_options;
-
-void test_online_clone__initialize(void)
-{
- git_checkout_opts dummy_opts = GIT_CHECKOUT_OPTS_INIT;
-
- g_repo = NULL;
-
- memset(&g_options, 0, sizeof(git_clone_options));
- g_options.version = GIT_CLONE_OPTIONS_VERSION;
- g_options.checkout_opts = dummy_opts;
- g_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
-}
-
-void test_online_clone__cleanup(void)
-{
- if (g_repo) {
- git_repository_free(g_repo);
- g_repo = NULL;
- }
- cl_fixture_cleanup("./foo");
-}
-
-void test_online_clone__network_full(void)
-{
- git_remote *origin;
-
- cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
- cl_assert(!git_repository_is_bare(g_repo));
- cl_git_pass(git_remote_load(&origin, g_repo, "origin"));
-
- cl_assert_equal_i(GIT_REMOTE_DOWNLOAD_TAGS_AUTO, origin->download_tags);
-
- git_remote_free(origin);
-}
-
-void test_online_clone__network_bare(void)
-{
- git_remote *origin;
-
- g_options.bare = true;
-
- cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
- cl_assert(git_repository_is_bare(g_repo));
- cl_git_pass(git_remote_load(&origin, g_repo, "origin"));
-
- git_remote_free(origin);
-}
-
-void test_online_clone__empty_repository(void)
-{
- git_reference *head;
-
- cl_git_pass(git_clone(&g_repo, LIVE_EMPTYREPO_URL, "./foo", &g_options));
-
- cl_assert_equal_i(true, git_repository_is_empty(g_repo));
- cl_assert_equal_i(true, git_repository_head_orphan(g_repo));
-
- cl_git_pass(git_reference_lookup(&head, g_repo, GIT_HEAD_FILE));
- cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head));
- cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
-
- git_reference_free(head);
-}
-
-static void checkout_progress(const char *path, size_t cur, size_t tot, void *payload)
-{
- bool *was_called = (bool*)payload;
- GIT_UNUSED(path); GIT_UNUSED(cur); GIT_UNUSED(tot);
- (*was_called) = true;
-}
-
-static int fetch_progress(const git_transfer_progress *stats, void *payload)
-{
- bool *was_called = (bool*)payload;
- GIT_UNUSED(stats);
- (*was_called) = true;
- return 0;
-}
-
-void test_online_clone__can_checkout_a_cloned_repo(void)
-{
- git_buf path = GIT_BUF_INIT;
- git_reference *head;
- bool checkout_progress_cb_was_called = false,
- fetch_progress_cb_was_called = false;
-
- g_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
- g_options.checkout_opts.progress_cb = &checkout_progress;
- g_options.checkout_opts.progress_payload = &checkout_progress_cb_was_called;
- g_options.fetch_progress_cb = &fetch_progress;
- g_options.fetch_progress_payload = &fetch_progress_cb_was_called;
-
- cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
-
- cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "master.txt"));
- cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&path)));
-
- cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
- cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head));
- cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
-
- cl_assert_equal_i(true, checkout_progress_cb_was_called);
- cl_assert_equal_i(true, fetch_progress_cb_was_called);
-
- git_reference_free(head);
- git_buf_free(&path);
-}
-
-static int update_tips(const char *refname, const git_oid *a, const git_oid *b, void *payload)
-{
- int *callcount = (int*)payload;
- GIT_UNUSED(refname); GIT_UNUSED(a); GIT_UNUSED(b);
- *callcount = *callcount + 1;
- return 0;
-}
-
-void test_online_clone__custom_remote_callbacks(void)
-{
- git_remote_callbacks remote_callbacks = GIT_REMOTE_CALLBACKS_INIT;
- int callcount = 0;
-
- g_options.remote_callbacks = &remote_callbacks;
- remote_callbacks.update_tips = update_tips;
- remote_callbacks.payload = &callcount;
-
- cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
- cl_assert(callcount > 0);
-}
-
-void test_online_clone__credentials(void)
-{
- /* Remote URL environment variable must be set. User and password are optional. */
- const char *remote_url = cl_getenv("GITTEST_REMOTE_URL");
- git_cred_userpass_payload user_pass = {
- cl_getenv("GITTEST_REMOTE_USER"),
- cl_getenv("GITTEST_REMOTE_PASS")
- };
-
- if (!remote_url) return;
-
- g_options.cred_acquire_cb = git_cred_userpass;
- g_options.cred_acquire_payload = &user_pass;
-
- cl_git_pass(git_clone(&g_repo, remote_url, "./foo", &g_options));
- git_repository_free(g_repo); g_repo = NULL;
- cl_fixture_cleanup("./foo");
-}
-
-void test_online_clone__bitbucket_style(void)
-{
- git_cred_userpass_payload user_pass = {
- "libgit2", "libgit2"
- };
-
- g_options.cred_acquire_cb = git_cred_userpass;
- g_options.cred_acquire_payload = &user_pass;
-
- cl_git_pass(git_clone(&g_repo, BB_REPO_URL, "./foo", &g_options));
- git_repository_free(g_repo); g_repo = NULL;
- cl_fixture_cleanup("./foo");
-
- /* User and pass from URL */
- user_pass.password = "wrong";
- 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";
- 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");
-}
-
-static int cancel_at_half(const git_transfer_progress *stats, void *payload)
-{
- GIT_UNUSED(payload);
-
- if (stats->received_objects > (stats->total_objects/2))
- return 1;
- return 0;
-}
-
-void test_online_clone__can_cancel(void)
-{
- g_options.fetch_progress_cb = cancel_at_half;
- cl_git_fail_with(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options), GIT_EUSER);
-}
diff --git a/tests-clar/online/fetch.c b/tests-clar/online/fetch.c
deleted file mode 100644
index bfa1eb972..000000000
--- a/tests-clar/online/fetch.c
+++ /dev/null
@@ -1,163 +0,0 @@
-#include "clar_libgit2.h"
-
-static git_repository *_repo;
-static int counter;
-
-void test_online_fetch__initialize(void)
-{
- cl_git_pass(git_repository_init(&_repo, "./fetch", 0));
-}
-
-void test_online_fetch__cleanup(void)
-{
- git_repository_free(_repo);
- _repo = NULL;
-
- cl_fixture_cleanup("./fetch");
-}
-
-static int update_tips(const char *refname, const git_oid *a, const git_oid *b, void *data)
-{
- GIT_UNUSED(refname); GIT_UNUSED(a); GIT_UNUSED(b); GIT_UNUSED(data);
-
- ++counter;
-
- return 0;
-}
-
-static int progress(const git_transfer_progress *stats, void *payload)
-{
- size_t *bytes_received = (size_t *)payload;
- *bytes_received = stats->received_bytes;
- return 0;
-}
-
-static void do_fetch(const char *url, git_remote_autotag_option_t flag, int n)
-{
- git_remote *remote;
- git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
- size_t bytes_received = 0;
-
- callbacks.update_tips = update_tips;
- counter = 0;
-
- cl_git_pass(git_remote_create(&remote, _repo, "test", url));
- git_remote_set_callbacks(remote, &callbacks);
- git_remote_set_autotag(remote, flag);
- cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
- cl_git_pass(git_remote_download(remote, progress, &bytes_received));
- cl_git_pass(git_remote_update_tips(remote));
- git_remote_disconnect(remote);
- cl_assert_equal_i(counter, n);
- cl_assert(bytes_received > 0);
-
- git_remote_free(remote);
-}
-
-void test_online_fetch__default_git(void)
-{
- do_fetch("git://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_AUTO, 6);
-}
-
-void test_online_fetch__default_http(void)
-{
- do_fetch("http://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_AUTO, 6);
-}
-
-void test_online_fetch__no_tags_git(void)
-{
- do_fetch("git://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_NONE, 3);
-}
-
-void test_online_fetch__no_tags_http(void)
-{
- do_fetch("http://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_NONE, 3);
-}
-
-static int transferProgressCallback(const git_transfer_progress *stats, void *payload)
-{
- bool *invoked = (bool *)payload;
-
- GIT_UNUSED(stats);
- *invoked = true;
- return 0;
-}
-
-void test_online_fetch__doesnt_retrieve_a_pack_when_the_repository_is_up_to_date(void)
-{
- git_repository *_repository;
- bool invoked = false;
- git_remote *remote;
- git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
- opts.bare = true;
-
- cl_git_pass(git_clone(&_repository, "https://github.com/libgit2/TestGitRepository.git",
- "./fetch/lg2", &opts));
- git_repository_free(_repository);
-
- cl_git_pass(git_repository_open(&_repository, "./fetch/lg2"));
-
- cl_git_pass(git_remote_load(&remote, _repository, "origin"));
- cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
-
- cl_assert_equal_i(false, invoked);
-
- cl_git_pass(git_remote_download(remote, &transferProgressCallback, &invoked));
-
- cl_assert_equal_i(false, invoked);
-
- cl_git_pass(git_remote_update_tips(remote));
- git_remote_disconnect(remote);
-
- git_remote_free(remote);
- git_repository_free(_repository);
-}
-
-static int cancel_at_half(const git_transfer_progress *stats, void *payload)
-{
- GIT_UNUSED(payload);
-
- if (stats->received_objects > (stats->total_objects/2))
- return -1;
- return 0;
-}
-
-void test_online_fetch__can_cancel(void)
-{
- git_remote *remote;
- size_t bytes_received = 0;
-
- cl_git_pass(git_remote_create(&remote, _repo, "test",
- "http://github.com/libgit2/TestGitRepository.git"));
- cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
- cl_git_fail_with(git_remote_download(remote, cancel_at_half, &bytes_received), GIT_EUSER);
- git_remote_disconnect(remote);
- git_remote_free(remote);
-}
-
-int ls_cb(git_remote_head *rhead, void *payload)
-{
- int *nr = payload;
- GIT_UNUSED(rhead);
-
- (*nr)++;
-
- return 0;
-}
-
-void test_online_fetch__ls_disconnected(void)
-{
- git_remote *remote;
- int nr_before = 0, nr_after = 0;
-
- cl_git_pass(git_remote_create(&remote, _repo, "test",
- "http://github.com/libgit2/TestGitRepository.git"));
- cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
- cl_git_pass(git_remote_ls(remote, ls_cb, &nr_before));
- git_remote_disconnect(remote);
- cl_git_pass(git_remote_ls(remote, ls_cb, &nr_after));
-
- cl_assert_equal_i(nr_before, nr_after);
-
- git_remote_free(remote);
-}
diff --git a/tests-clar/online/fetchhead.c b/tests-clar/online/fetchhead.c
deleted file mode 100644
index 58717eef8..000000000
--- a/tests-clar/online/fetchhead.c
+++ /dev/null
@@ -1,89 +0,0 @@
-#include "clar_libgit2.h"
-
-#include "fileops.h"
-#include "fetchhead.h"
-#include "../fetchhead/fetchhead_data.h"
-#include "git2/clone.h"
-
-#define LIVE_REPO_URL "git://github.com/libgit2/TestGitRepository"
-
-static git_repository *g_repo;
-static git_clone_options g_options;
-
-void test_online_fetchhead__initialize(void)
-{
- g_repo = NULL;
-
- memset(&g_options, 0, sizeof(git_clone_options));
- g_options.version = GIT_CLONE_OPTIONS_VERSION;
-}
-
-void test_online_fetchhead__cleanup(void)
-{
- if (g_repo) {
- git_repository_free(g_repo);
- g_repo = NULL;
- }
-
- cl_fixture_cleanup("./foo");
-}
-
-static void fetchhead_test_clone(void)
-{
- cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
-}
-
-static void fetchhead_test_fetch(const char *fetchspec, const char *expected_fetchhead)
-{
- git_remote *remote;
- git_buf fetchhead_buf = GIT_BUF_INIT;
- int equals = 0;
-
- cl_git_pass(git_remote_load(&remote, g_repo, "origin"));
- git_remote_set_autotag(remote, GIT_REMOTE_DOWNLOAD_TAGS_AUTO);
-
- if(fetchspec != NULL) {
- git_remote_clear_refspecs(remote);
- git_remote_add_fetch(remote, fetchspec);
- }
-
- cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
- cl_git_pass(git_remote_download(remote, NULL, NULL));
- cl_git_pass(git_remote_update_tips(remote));
- git_remote_disconnect(remote);
- git_remote_free(remote);
-
- cl_git_pass(git_futils_readbuffer(&fetchhead_buf, "./foo/.git/FETCH_HEAD"));
-
- equals = (strcmp(fetchhead_buf.ptr, expected_fetchhead) == 0);
-
- git_buf_free(&fetchhead_buf);
-
- cl_assert(equals);
-}
-
-void test_online_fetchhead__wildcard_spec(void)
-{
- fetchhead_test_clone();
- fetchhead_test_fetch(NULL, FETCH_HEAD_WILDCARD_DATA);
-}
-
-void test_online_fetchhead__explicit_spec(void)
-{
- fetchhead_test_clone();
- fetchhead_test_fetch("refs/heads/first-merge:refs/remotes/origin/first-merge", FETCH_HEAD_EXPLICIT_DATA);
-}
-
-void test_online_fetchhead__no_merges(void)
-{
- git_config *config;
-
- fetchhead_test_clone();
-
- cl_git_pass(git_repository_config(&config, g_repo));
- cl_git_pass(git_config_delete_entry(config, "branch.master.remote"));
- cl_git_pass(git_config_delete_entry(config, "branch.master.merge"));
- git_config_free(config);
-
- fetchhead_test_fetch(NULL, FETCH_HEAD_NO_MERGE_DATA);
-}
diff --git a/tests-clar/online/push.c b/tests-clar/online/push.c
deleted file mode 100644
index 5dc7974c7..000000000
--- a/tests-clar/online/push.c
+++ /dev/null
@@ -1,711 +0,0 @@
-#include "clar_libgit2.h"
-#include "buffer.h"
-#include "posix.h"
-#include "vector.h"
-#include "../submodule/submodule_helpers.h"
-#include "push_util.h"
-#include "refspec.h"
-#include "remote.h"
-
-static git_repository *_repo;
-
-static char *_remote_url;
-static char *_remote_user;
-static char *_remote_pass;
-
-static git_remote *_remote;
-static bool _cred_acquire_called;
-static record_callbacks_data _record_cbs_data = {{ 0 }};
-static git_remote_callbacks _record_cbs = RECORD_CALLBACKS_INIT(&_record_cbs_data);
-
-static git_oid _oid_b6;
-static git_oid _oid_b5;
-static git_oid _oid_b4;
-static git_oid _oid_b3;
-static git_oid _oid_b2;
-static git_oid _oid_b1;
-
-static git_oid _tag_commit;
-static git_oid _tag_tree;
-static git_oid _tag_blob;
-static git_oid _tag_lightweight;
-
-static int cred_acquire_cb(
- git_cred **cred,
- const char *url,
- const char *user_from_url,
- unsigned int allowed_types,
- void *payload)
-{
- GIT_UNUSED(url);
- GIT_UNUSED(user_from_url);
-
- *((bool*)payload) = true;
-
- if ((GIT_CREDTYPE_USERPASS_PLAINTEXT & allowed_types) == 0 ||
- git_cred_userpass_plaintext_new(cred, _remote_user, _remote_pass) < 0)
- return -1;
-
- return 0;
-}
-
-typedef struct {
- const char *ref;
- const char *msg;
-} push_status;
-
-/**
- * git_push_status_foreach callback that records status entries.
- * @param data (git_vector *) of push_status instances
- */
-static int record_push_status_cb(const char *ref, const char *msg, void *data)
-{
- git_vector *statuses = (git_vector *)data;
- push_status *s;
-
- cl_assert(s = git__malloc(sizeof(*s)));
- s->ref = ref;
- s->msg = msg;
-
- git_vector_insert(statuses, s);
-
- return 0;
-}
-
-static void do_verify_push_status(git_push *push, const push_status expected[], const size_t expected_len)
-{
- git_vector actual = GIT_VECTOR_INIT;
- push_status *iter;
- bool failed = false;
- size_t i;
-
- git_push_status_foreach(push, record_push_status_cb, &actual);
-
- if (expected_len != actual.length)
- failed = true;
- else
- git_vector_foreach(&actual, i, iter)
- if (strcmp(expected[i].ref, iter->ref) ||
- (expected[i].msg && !iter->msg) ||
- (!expected[i].msg && iter->msg) ||
- (expected[i].msg && iter->msg && strcmp(expected[i].msg, iter->msg))) {
- failed = true;
- break;
- }
-
- if (failed) {
- git_buf msg = GIT_BUF_INIT;
-
- git_buf_puts(&msg, "Expected and actual push statuses differ:\nEXPECTED:\n");
-
- for(i = 0; i < expected_len; i++) {
- git_buf_printf(&msg, "%s: %s\n",
- expected[i].ref,
- expected[i].msg ? expected[i].msg : "<NULL>");
- }
-
- git_buf_puts(&msg, "\nACTUAL:\n");
-
- git_vector_foreach(&actual, i, iter)
- git_buf_printf(&msg, "%s: %s\n", iter->ref, iter->msg);
-
- cl_fail(git_buf_cstr(&msg));
-
- git_buf_free(&msg);
- }
-
- git_vector_foreach(&actual, i, iter)
- git__free(iter);
-
- git_vector_free(&actual);
-}
-
-/**
- * Verifies that after git_push_finish(), refs on a remote have the expected
- * names, oids, and order.
- *
- * @param remote remote to verify
- * @param expected_refs expected remote refs after push
- * @param expected_refs_len length of expected_refs
- */
-static void verify_refs(git_remote *remote, expected_ref expected_refs[], size_t expected_refs_len)
-{
- git_vector actual_refs = GIT_VECTOR_INIT;
-
- git_remote_ls(remote, record_ref_cb, &actual_refs);
- verify_remote_refs(&actual_refs, expected_refs, expected_refs_len);
-
- git_vector_free(&actual_refs);
-}
-
-static int tracking_branch_list_cb(const char *branch_name, git_branch_t branch_type, void *payload)
-{
- git_vector *tracking = (git_vector *)payload;
-
- if (branch_type == GIT_BRANCH_REMOTE)
- git_vector_insert(tracking, git__strdup(branch_name));
- else
- GIT_UNUSED(branch_name);
-
- return 0;
-}
-
-/**
- * Verifies that after git_push_update_tips(), remote tracking branches have the expected
- * names and oids.
- *
- * @param remote remote to verify
- * @param expected_refs expected remote refs after push
- * @param expected_refs_len length of expected_refs
- */
-static void verify_tracking_branches(git_remote *remote, expected_ref expected_refs[], size_t expected_refs_len)
-{
- git_refspec *fetch_spec;
- size_t i, j;
- git_buf msg = GIT_BUF_INIT;
- git_buf ref_name = GIT_BUF_INIT;
- git_buf canonical_ref_name = GIT_BUF_INIT;
- git_vector actual_refs = GIT_VECTOR_INIT;
- char *actual_ref;
- git_oid oid;
- int failed = 0;
-
- /* Get current remote branches */
- cl_git_pass(git_branch_foreach(remote->repo, GIT_BRANCH_REMOTE, tracking_branch_list_cb, &actual_refs));
-
- /* Loop through expected refs, make sure they exist */
- for (i = 0; i < expected_refs_len; i++) {
-
- /* Convert remote reference name into tracking branch name.
- * If the spec is not under refs/heads/, then skip.
- */
- fetch_spec = git_remote__matching_refspec(remote, expected_refs[i].name);
- if (!fetch_spec)
- continue;
-
- cl_git_pass(git_refspec_transform_r(&ref_name, fetch_spec, expected_refs[i].name));
-
- /* Find matching remote branch */
- git_vector_foreach(&actual_refs, j, actual_ref) {
-
- /* Construct canonical ref name from the actual_ref name */
- git_buf_clear(&canonical_ref_name);
- cl_git_pass(git_buf_printf(&canonical_ref_name, "refs/remotes/%s", actual_ref));
- if (!strcmp(git_buf_cstr(&ref_name), git_buf_cstr(&canonical_ref_name)))
- break;
- }
-
- if (j == actual_refs.length) {
- git_buf_printf(&msg, "Did not find expected tracking branch '%s'.", git_buf_cstr(&ref_name));
- failed = 1;
- goto failed;
- }
-
- /* Make sure tracking branch is at expected commit ID */
- cl_git_pass(git_reference_name_to_id(&oid, remote->repo, git_buf_cstr(&canonical_ref_name)));
-
- if (git_oid_cmp(expected_refs[i].oid, &oid) != 0) {
- git_buf_puts(&msg, "Tracking branch commit does not match expected ID.");
- failed = 1;
- goto failed;
- }
-
- git__free(actual_ref);
- cl_git_pass(git_vector_remove(&actual_refs, j));
- }
-
- /* Make sure there are no extra branches */
- if (actual_refs.length > 0) {
- git_buf_puts(&msg, "Unexpected remote tracking branches exist.");
- failed = 1;
- goto failed;
- }
-
-failed:
-
- if(failed)
- cl_fail(git_buf_cstr(&msg));
-
- git_vector_foreach(&actual_refs, i, actual_ref)
- git__free(actual_ref);
-
- git_vector_free(&actual_refs);
- git_buf_free(&msg);
- git_buf_free(&canonical_ref_name);
- git_buf_free(&ref_name);
- return;
-}
-
-void test_online_push__initialize(void)
-{
- git_vector delete_specs = GIT_VECTOR_INIT;
- size_t i;
- char *curr_del_spec;
- _cred_acquire_called = false;
-
- _repo = cl_git_sandbox_init("push_src");
-
- cl_fixture_sandbox("testrepo.git");
- cl_rename("push_src/submodule/.gitted", "push_src/submodule/.git");
-
- rewrite_gitmodules(git_repository_workdir(_repo));
-
- /* git log --format=oneline --decorate --graph
- * *-. 951bbbb90e2259a4c8950db78946784fb53fcbce (HEAD, b6) merge b3, b4, and b5 to b6
- * |\ \
- * | | * fa38b91f199934685819bea316186d8b008c52a2 (b5) added submodule named 'submodule' pointing to '../testrepo.git'
- * | * | 27b7ce66243eb1403862d05f958c002312df173d (b4) edited fold\b.txt
- * | |/
- * * | d9b63a88223d8367516f50bd131a5f7349b7f3e4 (b3) edited a.txt
- * |/
- * * a78705c3b2725f931d3ee05348d83cc26700f247 (b2, b1) added fold and fold/b.txt
- * * 5c0bb3d1b9449d1cc69d7519fd05166f01840915 added a.txt
- */
- git_oid_fromstr(&_oid_b6, "951bbbb90e2259a4c8950db78946784fb53fcbce");
- git_oid_fromstr(&_oid_b5, "fa38b91f199934685819bea316186d8b008c52a2");
- git_oid_fromstr(&_oid_b4, "27b7ce66243eb1403862d05f958c002312df173d");
- git_oid_fromstr(&_oid_b3, "d9b63a88223d8367516f50bd131a5f7349b7f3e4");
- git_oid_fromstr(&_oid_b2, "a78705c3b2725f931d3ee05348d83cc26700f247");
- git_oid_fromstr(&_oid_b1, "a78705c3b2725f931d3ee05348d83cc26700f247");
-
- git_oid_fromstr(&_tag_commit, "805c54522e614f29f70d2413a0470247d8b424ac");
- git_oid_fromstr(&_tag_tree, "ff83aa4c5e5d28e3bcba2f5c6e2adc61286a4e5e");
- git_oid_fromstr(&_tag_blob, "b483ae7ba66decee9aee971f501221dea84b1498");
- git_oid_fromstr(&_tag_lightweight, "951bbbb90e2259a4c8950db78946784fb53fcbce");
-
- /* Remote URL environment variable must be set. User and password are optional. */
- _remote_url = cl_getenv("GITTEST_REMOTE_URL");
- _remote_user = cl_getenv("GITTEST_REMOTE_USER");
- _remote_pass = cl_getenv("GITTEST_REMOTE_PASS");
- _remote = NULL;
-
- if (_remote_url) {
- cl_git_pass(git_remote_create(&_remote, _repo, "test", _remote_url));
-
- git_remote_set_cred_acquire_cb(_remote, cred_acquire_cb, &_cred_acquire_called);
- record_callbacks_data_clear(&_record_cbs_data);
- git_remote_set_callbacks(_remote, &_record_cbs);
-
- cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH));
-
- /* Clean up previously pushed branches. Fails if receive.denyDeletes is
- * set on the remote. Also, on Git 1.7.0 and newer, you must run
- * 'git config receive.denyDeleteCurrent ignore' in the remote repo in
- * order to delete the remote branch pointed to by HEAD (usually master).
- * See: https://raw.github.com/git/git/master/Documentation/RelNotes/1.7.0.txt
- */
- cl_git_pass(git_remote_ls(_remote, delete_ref_cb, &delete_specs));
- if (delete_specs.length) {
- git_push *push;
-
- cl_git_pass(git_push_new(&push, _remote));
-
- git_vector_foreach(&delete_specs, i, curr_del_spec) {
- git_push_add_refspec(push, curr_del_spec);
- git__free(curr_del_spec);
- }
-
- cl_git_pass(git_push_finish(push));
- git_push_free(push);
- }
-
- git_remote_disconnect(_remote);
- git_vector_free(&delete_specs);
-
- /* Now that we've deleted everything, fetch from the remote */
- cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_FETCH));
- cl_git_pass(git_remote_download(_remote, NULL, NULL));
- cl_git_pass(git_remote_update_tips(_remote));
- git_remote_disconnect(_remote);
- } else
- printf("GITTEST_REMOTE_URL unset; skipping push test\n");
-}
-
-void test_online_push__cleanup(void)
-{
- if (_remote)
- git_remote_free(_remote);
- _remote = NULL;
-
- /* Freed by cl_git_sandbox_cleanup */
- _repo = NULL;
-
- record_callbacks_data_clear(&_record_cbs_data);
-
- cl_fixture_cleanup("testrepo.git");
- cl_git_sandbox_cleanup();
-}
-
-/**
- * Calls push and relists refs on remote to verify success.
- *
- * @param refspecs refspecs to push
- * @param refspecs_len length of refspecs
- * @param expected_refs expected remote refs after push
- * @param expected_refs_len length of expected_refs
- * @param expected_ret expected return value from git_push_finish()
- */
-static void do_push(const char *refspecs[], size_t refspecs_len,
- push_status expected_statuses[], size_t expected_statuses_len,
- expected_ref expected_refs[], size_t expected_refs_len, int expected_ret)
-{
- git_push *push;
- git_push_options opts = GIT_PUSH_OPTIONS_INIT;
- size_t i;
- int ret;
-
- if (_remote) {
- /* Auto-detect the number of threads to use */
- opts.pb_parallelism = 0;
-
- cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH));
-
- cl_git_pass(git_push_new(&push, _remote));
- cl_git_pass(git_push_set_options(push, &opts));
-
- for (i = 0; i < refspecs_len; i++)
- cl_git_pass(git_push_add_refspec(push, refspecs[i]));
-
- if (expected_ret < 0) {
- cl_git_fail(ret = git_push_finish(push));
- cl_assert_equal_i(0, git_push_unpack_ok(push));
- }
- else {
- cl_git_pass(ret = git_push_finish(push));
- cl_assert_equal_i(1, git_push_unpack_ok(push));
- }
-
- do_verify_push_status(push, expected_statuses, expected_statuses_len);
-
- cl_assert_equal_i(expected_ret, ret);
-
- verify_refs(_remote, expected_refs, expected_refs_len);
-
- cl_git_pass(git_push_update_tips(push));
- verify_tracking_branches(_remote, expected_refs, expected_refs_len);
-
- git_push_free(push);
-
- git_remote_disconnect(_remote);
- }
-}
-
-/* Call push_finish() without ever calling git_push_add_refspec() */
-void test_online_push__noop(void)
-{
- do_push(NULL, 0, NULL, 0, NULL, 0, 0);
-}
-
-void test_online_push__b1(void)
-{
- const char *specs[] = { "refs/heads/b1:refs/heads/b1" };
- push_status exp_stats[] = { { "refs/heads/b1", NULL } };
- expected_ref exp_refs[] = { { "refs/heads/b1", &_oid_b1 } };
- do_push(specs, ARRAY_SIZE(specs),
- exp_stats, ARRAY_SIZE(exp_stats),
- exp_refs, ARRAY_SIZE(exp_refs), 0);
-}
-
-void test_online_push__b2(void)
-{
- const char *specs[] = { "refs/heads/b2:refs/heads/b2" };
- push_status exp_stats[] = { { "refs/heads/b2", NULL } };
- expected_ref exp_refs[] = { { "refs/heads/b2", &_oid_b2 } };
- do_push(specs, ARRAY_SIZE(specs),
- exp_stats, ARRAY_SIZE(exp_stats),
- exp_refs, ARRAY_SIZE(exp_refs), 0);
-}
-
-void test_online_push__b3(void)
-{
- const char *specs[] = { "refs/heads/b3:refs/heads/b3" };
- push_status exp_stats[] = { { "refs/heads/b3", NULL } };
- expected_ref exp_refs[] = { { "refs/heads/b3", &_oid_b3 } };
- do_push(specs, ARRAY_SIZE(specs),
- exp_stats, ARRAY_SIZE(exp_stats),
- exp_refs, ARRAY_SIZE(exp_refs), 0);
-}
-
-void test_online_push__b4(void)
-{
- const char *specs[] = { "refs/heads/b4:refs/heads/b4" };
- push_status exp_stats[] = { { "refs/heads/b4", NULL } };
- expected_ref exp_refs[] = { { "refs/heads/b4", &_oid_b4 } };
- do_push(specs, ARRAY_SIZE(specs),
- exp_stats, ARRAY_SIZE(exp_stats),
- exp_refs, ARRAY_SIZE(exp_refs), 0);
-}
-
-void test_online_push__b5(void)
-{
- const char *specs[] = { "refs/heads/b5:refs/heads/b5" };
- push_status exp_stats[] = { { "refs/heads/b5", NULL } };
- expected_ref exp_refs[] = { { "refs/heads/b5", &_oid_b5 } };
- do_push(specs, ARRAY_SIZE(specs),
- exp_stats, ARRAY_SIZE(exp_stats),
- exp_refs, ARRAY_SIZE(exp_refs), 0);
-}
-
-void test_online_push__multi(void)
-{
- const char *specs[] = {
- "refs/heads/b1:refs/heads/b1",
- "refs/heads/b2:refs/heads/b2",
- "refs/heads/b3:refs/heads/b3",
- "refs/heads/b4:refs/heads/b4",
- "refs/heads/b5:refs/heads/b5"
- };
- push_status exp_stats[] = {
- { "refs/heads/b1", NULL },
- { "refs/heads/b2", NULL },
- { "refs/heads/b3", NULL },
- { "refs/heads/b4", NULL },
- { "refs/heads/b5", NULL }
- };
- expected_ref exp_refs[] = {
- { "refs/heads/b1", &_oid_b1 },
- { "refs/heads/b2", &_oid_b2 },
- { "refs/heads/b3", &_oid_b3 },
- { "refs/heads/b4", &_oid_b4 },
- { "refs/heads/b5", &_oid_b5 }
- };
- do_push(specs, ARRAY_SIZE(specs),
- exp_stats, ARRAY_SIZE(exp_stats),
- exp_refs, ARRAY_SIZE(exp_refs), 0);
-}
-
-void test_online_push__implicit_tgt(void)
-{
- const char *specs1[] = { "refs/heads/b1:" };
- push_status exp_stats1[] = { { "refs/heads/b1", NULL } };
- expected_ref exp_refs1[] = { { "refs/heads/b1", &_oid_b1 } };
-
- const char *specs2[] = { "refs/heads/b2:" };
- push_status exp_stats2[] = { { "refs/heads/b2", NULL } };
- expected_ref exp_refs2[] = {
- { "refs/heads/b1", &_oid_b1 },
- { "refs/heads/b2", &_oid_b2 }
- };
-
- do_push(specs1, ARRAY_SIZE(specs1),
- exp_stats1, ARRAY_SIZE(exp_stats1),
- exp_refs1, ARRAY_SIZE(exp_refs1), 0);
- do_push(specs2, ARRAY_SIZE(specs2),
- exp_stats2, ARRAY_SIZE(exp_stats2),
- exp_refs2, ARRAY_SIZE(exp_refs2), 0);
-}
-
-void test_online_push__fast_fwd(void)
-{
- /* Fast forward b1 in tgt from _oid_b1 to _oid_b6. */
-
- const char *specs_init[] = { "refs/heads/b1:refs/heads/b1" };
- push_status exp_stats_init[] = { { "refs/heads/b1", NULL } };
- expected_ref exp_refs_init[] = { { "refs/heads/b1", &_oid_b1 } };
-
- const char *specs_ff[] = { "refs/heads/b6:refs/heads/b1" };
- push_status exp_stats_ff[] = { { "refs/heads/b1", NULL } };
- expected_ref exp_refs_ff[] = { { "refs/heads/b1", &_oid_b6 } };
-
- /* Do a force push to reset b1 in target back to _oid_b1 */
- const char *specs_reset[] = { "+refs/heads/b1:refs/heads/b1" };
- /* Force should have no effect on a fast forward push */
- const char *specs_ff_force[] = { "+refs/heads/b6:refs/heads/b1" };
-
- do_push(specs_init, ARRAY_SIZE(specs_init),
- exp_stats_init, ARRAY_SIZE(exp_stats_init),
- exp_refs_init, ARRAY_SIZE(exp_refs_init), 0);
-
- do_push(specs_ff, ARRAY_SIZE(specs_ff),
- exp_stats_ff, ARRAY_SIZE(exp_stats_ff),
- exp_refs_ff, ARRAY_SIZE(exp_refs_ff), 0);
-
- do_push(specs_reset, ARRAY_SIZE(specs_reset),
- exp_stats_init, ARRAY_SIZE(exp_stats_init),
- exp_refs_init, ARRAY_SIZE(exp_refs_init), 0);
-
- do_push(specs_ff_force, ARRAY_SIZE(specs_ff_force),
- exp_stats_ff, ARRAY_SIZE(exp_stats_ff),
- exp_refs_ff, ARRAY_SIZE(exp_refs_ff), 0);
-}
-
-void test_online_push__tag_commit(void)
-{
- const char *specs[] = { "refs/tags/tag-commit:refs/tags/tag-commit" };
- push_status exp_stats[] = { { "refs/tags/tag-commit", NULL } };
- expected_ref exp_refs[] = { { "refs/tags/tag-commit", &_tag_commit } };
- do_push(specs, ARRAY_SIZE(specs),
- exp_stats, ARRAY_SIZE(exp_stats),
- exp_refs, ARRAY_SIZE(exp_refs), 0);
-}
-
-void test_online_push__tag_tree(void)
-{
- const char *specs[] = { "refs/tags/tag-tree:refs/tags/tag-tree" };
- push_status exp_stats[] = { { "refs/tags/tag-tree", NULL } };
- expected_ref exp_refs[] = { { "refs/tags/tag-tree", &_tag_tree } };
- do_push(specs, ARRAY_SIZE(specs),
- exp_stats, ARRAY_SIZE(exp_stats),
- exp_refs, ARRAY_SIZE(exp_refs), 0);
-}
-
-void test_online_push__tag_blob(void)
-{
- const char *specs[] = { "refs/tags/tag-blob:refs/tags/tag-blob" };
- push_status exp_stats[] = { { "refs/tags/tag-blob", NULL } };
- expected_ref exp_refs[] = { { "refs/tags/tag-blob", &_tag_blob } };
- do_push(specs, ARRAY_SIZE(specs),
- exp_stats, ARRAY_SIZE(exp_stats),
- exp_refs, ARRAY_SIZE(exp_refs), 0);
-}
-
-void test_online_push__tag_lightweight(void)
-{
- const char *specs[] = { "refs/tags/tag-lightweight:refs/tags/tag-lightweight" };
- push_status exp_stats[] = { { "refs/tags/tag-lightweight", NULL } };
- expected_ref exp_refs[] = { { "refs/tags/tag-lightweight", &_tag_lightweight } };
- do_push(specs, ARRAY_SIZE(specs),
- exp_stats, ARRAY_SIZE(exp_stats),
- exp_refs, ARRAY_SIZE(exp_refs), 0);
-}
-
-void test_online_push__force(void)
-{
- const char *specs1[] = {"refs/heads/b3:refs/heads/tgt"};
- push_status exp_stats1[] = { { "refs/heads/tgt", NULL } };
- expected_ref exp_refs1[] = { { "refs/heads/tgt", &_oid_b3 } };
-
- const char *specs2[] = {"refs/heads/b4:refs/heads/tgt"};
-
- const char *specs2_force[] = {"+refs/heads/b4:refs/heads/tgt"};
- push_status exp_stats2_force[] = { { "refs/heads/tgt", NULL } };
- expected_ref exp_refs2_force[] = { { "refs/heads/tgt", &_oid_b4 } };
-
- do_push(specs1, ARRAY_SIZE(specs1),
- exp_stats1, ARRAY_SIZE(exp_stats1),
- exp_refs1, ARRAY_SIZE(exp_refs1), 0);
-
- do_push(specs2, ARRAY_SIZE(specs2),
- NULL, 0,
- exp_refs1, ARRAY_SIZE(exp_refs1), GIT_ENONFASTFORWARD);
-
- /* Non-fast-forward update with force should pass. */
- do_push(specs2_force, ARRAY_SIZE(specs2_force),
- exp_stats2_force, ARRAY_SIZE(exp_stats2_force),
- exp_refs2_force, ARRAY_SIZE(exp_refs2_force), 0);
-}
-
-void test_online_push__delete(void)
-{
- const char *specs1[] = {
- "refs/heads/b1:refs/heads/tgt1",
- "refs/heads/b1:refs/heads/tgt2"
- };
- push_status exp_stats1[] = {
- { "refs/heads/tgt1", NULL },
- { "refs/heads/tgt2", NULL }
- };
- expected_ref exp_refs1[] = {
- { "refs/heads/tgt1", &_oid_b1 },
- { "refs/heads/tgt2", &_oid_b1 }
- };
-
- const char *specs_del_fake[] = { ":refs/heads/fake" };
- /* Force has no effect for delete. */
- const char *specs_del_fake_force[] = { "+:refs/heads/fake" };
- push_status exp_stats_fake[] = { { "refs/heads/fake", NULL } };
-
- const char *specs_delete[] = { ":refs/heads/tgt1" };
- push_status exp_stats_delete[] = { { "refs/heads/tgt1", NULL } };
- expected_ref exp_refs_delete[] = { { "refs/heads/tgt2", &_oid_b1 } };
- /* Force has no effect for delete. */
- const char *specs_delete_force[] = { "+:refs/heads/tgt1" };
-
- do_push(specs1, ARRAY_SIZE(specs1),
- exp_stats1, ARRAY_SIZE(exp_stats1),
- exp_refs1, ARRAY_SIZE(exp_refs1), 0);
-
- /* When deleting a non-existent branch, the git client sends zero for both
- * the old and new commit id. This should succeed on the server with the
- * same status report as if the branch were actually deleted. The server
- * returns a warning on the side-band iff the side-band is supported.
- * Since libgit2 doesn't support the side-band yet, there are no warnings.
- */
- do_push(specs_del_fake, ARRAY_SIZE(specs_del_fake),
- exp_stats_fake, 1,
- exp_refs1, ARRAY_SIZE(exp_refs1), 0);
- do_push(specs_del_fake_force, ARRAY_SIZE(specs_del_fake_force),
- exp_stats_fake, 1,
- exp_refs1, ARRAY_SIZE(exp_refs1), 0);
-
- /* Delete one of the pushed branches. */
- do_push(specs_delete, ARRAY_SIZE(specs_delete),
- exp_stats_delete, ARRAY_SIZE(exp_stats_delete),
- exp_refs_delete, ARRAY_SIZE(exp_refs_delete), 0);
-
- /* Re-push branches and retry delete with force. */
- do_push(specs1, ARRAY_SIZE(specs1),
- exp_stats1, ARRAY_SIZE(exp_stats1),
- exp_refs1, ARRAY_SIZE(exp_refs1), 0);
- do_push(specs_delete_force, ARRAY_SIZE(specs_delete_force),
- exp_stats_delete, ARRAY_SIZE(exp_stats_delete),
- exp_refs_delete, ARRAY_SIZE(exp_refs_delete), 0);
-}
-
-void test_online_push__bad_refspecs(void)
-{
- /* All classes of refspecs that should be rejected by
- * git_push_add_refspec() should go in this test.
- */
- git_push *push;
-
- if (_remote) {
-// cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH));
- cl_git_pass(git_push_new(&push, _remote));
-
- /* Unexpanded branch names not supported */
- cl_git_fail(git_push_add_refspec(push, "b6:b6"));
-
- git_push_free(push);
- }
-}
-
-void test_online_push__expressions(void)
-{
- /* TODO: Expressions in refspecs doesn't actually work yet */
- const char *specs_left_expr[] = { "refs/heads/b2~1:refs/heads/b2" };
-
- const char *specs_right_expr[] = { "refs/heads/b2:refs/heads/b2~1" };
- push_status exp_stats_right_expr[] = { { "refs/heads/b2~1", "funny refname" } };
-
- /* TODO: Find a more precise way of checking errors than a exit code of -1. */
- do_push(specs_left_expr, ARRAY_SIZE(specs_left_expr),
- NULL, 0,
- NULL, 0, -1);
-
- do_push(specs_right_expr, ARRAY_SIZE(specs_right_expr),
- exp_stats_right_expr, ARRAY_SIZE(exp_stats_right_expr),
- NULL, 0, 0);
-}
-
-void test_online_push__notes(void)
-{
- git_oid note_oid, *target_oid, expected_oid;
- git_signature *signature;
- const char *specs[] = { "refs/notes/commits:refs/notes/commits" };
- push_status exp_stats[] = { { "refs/notes/commits", NULL } };
- expected_ref exp_refs[] = { { "refs/notes/commits", &expected_oid } };
- git_oid_fromstr(&expected_oid, "8461a99b27b7043e58ff6e1f5d2cf07d282534fb");
-
- target_oid = &_oid_b6;
-
- /* Create note to push */
- cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60)); /* Wed Dec 14 08:29:03 2011 +0100 */
- cl_git_pass(git_note_create(&note_oid, _repo, signature, signature, NULL, target_oid, "hello world\n", 0));
-
- do_push(specs, ARRAY_SIZE(specs),
- exp_stats, ARRAY_SIZE(exp_stats),
- exp_refs, ARRAY_SIZE(exp_refs), 0);
-
- git_signature_free(signature);
-}
diff --git a/tests-clar/online/push_util.c b/tests-clar/online/push_util.c
deleted file mode 100644
index 2e457844d..000000000
--- a/tests-clar/online/push_util.c
+++ /dev/null
@@ -1,126 +0,0 @@
-
-#include "clar_libgit2.h"
-#include "buffer.h"
-#include "vector.h"
-#include "push_util.h"
-
-const git_oid OID_ZERO = {{ 0 }};
-
-void updated_tip_free(updated_tip *t)
-{
- git__free(t->name);
- git__free(t->old_oid);
- git__free(t->new_oid);
- git__free(t);
-}
-
-void record_callbacks_data_clear(record_callbacks_data *data)
-{
- size_t i;
- updated_tip *tip;
-
- git_vector_foreach(&data->updated_tips, i, tip)
- updated_tip_free(tip);
-
- git_vector_free(&data->updated_tips);
-}
-
-int record_update_tips_cb(const char *refname, const git_oid *a, const git_oid *b, void *data)
-{
- updated_tip *t;
- record_callbacks_data *record_data = (record_callbacks_data *)data;
-
- cl_assert(t = git__malloc(sizeof(*t)));
-
- cl_assert(t->name = git__strdup(refname));
- cl_assert(t->old_oid = git__malloc(sizeof(*t->old_oid)));
- git_oid_cpy(t->old_oid, a);
-
- cl_assert(t->new_oid = git__malloc(sizeof(*t->new_oid)));
- git_oid_cpy(t->new_oid, b);
-
- git_vector_insert(&record_data->updated_tips, t);
-
- return 0;
-}
-
-int delete_ref_cb(git_remote_head *head, void *payload)
-{
- git_vector *delete_specs = (git_vector *)payload;
- git_buf del_spec = GIT_BUF_INIT;
-
- /* Ignore malformed ref names (which also saves us from tag^{} */
- if (!git_reference_is_valid_name(head->name))
- return 0;
-
- /* Create a refspec that deletes a branch in the remote */
- if (strcmp(head->name, "refs/heads/master")) {
- cl_git_pass(git_buf_putc(&del_spec, ':'));
- cl_git_pass(git_buf_puts(&del_spec, head->name));
- cl_git_pass(git_vector_insert(delete_specs, git_buf_detach(&del_spec)));
- }
-
- return 0;
-}
-
-int record_ref_cb(git_remote_head *head, void *payload)
-{
- git_vector *refs = (git_vector *) payload;
- return git_vector_insert(refs, head);
-}
-
-void verify_remote_refs(git_vector *actual_refs, const expected_ref expected_refs[], size_t expected_refs_len)
-{
- size_t i, j = 0;
- git_buf msg = GIT_BUF_INIT;
- git_remote_head *actual;
- char *oid_str;
- bool master_present = false;
-
- /* We don't care whether "master" is present on the other end or not */
- git_vector_foreach(actual_refs, i, actual) {
- if (!strcmp(actual->name, "refs/heads/master")) {
- master_present = true;
- break;
- }
- }
-
- if (expected_refs_len + (master_present ? 1 : 0) != actual_refs->length)
- goto failed;
-
- git_vector_foreach(actual_refs, i, actual) {
- if (master_present && !strcmp(actual->name, "refs/heads/master"))
- continue;
-
- if (strcmp(expected_refs[j].name, actual->name) ||
- git_oid_cmp(expected_refs[j].oid, &actual->oid))
- goto failed;
-
- j++;
- }
-
- return;
-
-failed:
- git_buf_puts(&msg, "Expected and actual refs differ:\nEXPECTED:\n");
-
- for(i = 0; i < expected_refs_len; i++) {
- cl_assert(oid_str = git_oid_allocfmt(expected_refs[i].oid));
- cl_git_pass(git_buf_printf(&msg, "%s = %s\n", expected_refs[i].name, oid_str));
- git__free(oid_str);
- }
-
- git_buf_puts(&msg, "\nACTUAL:\n");
- git_vector_foreach(actual_refs, i, actual) {
- if (master_present && !strcmp(actual->name, "refs/heads/master"))
- continue;
-
- cl_assert(oid_str = git_oid_allocfmt(&actual->oid));
- cl_git_pass(git_buf_printf(&msg, "%s = %s\n", actual->name, oid_str));
- git__free(oid_str);
- }
-
- cl_fail(git_buf_cstr(&msg));
-
- git_buf_free(&msg);
-}
diff --git a/tests-clar/online/push_util.h b/tests-clar/online/push_util.h
deleted file mode 100644
index 759122aa6..000000000
--- a/tests-clar/online/push_util.h
+++ /dev/null
@@ -1,69 +0,0 @@
-#ifndef INCLUDE_cl_push_util_h__
-#define INCLUDE_cl_push_util_h__
-
-#include "git2/oid.h"
-
-/* Constant for zero oid */
-extern const git_oid OID_ZERO;
-
-/**
- * Macro for initializing git_remote_callbacks to use test helpers that
- * record data in a record_callbacks_data instance.
- * @param data pointer to a record_callbacks_data instance
- */
-#define RECORD_CALLBACKS_INIT(data) \
- { GIT_REMOTE_CALLBACKS_VERSION, NULL, NULL, record_update_tips_cb, data }
-
-typedef struct {
- char *name;
- git_oid *old_oid;
- git_oid *new_oid;
-} updated_tip;
-
-typedef struct {
- git_vector updated_tips;
-} record_callbacks_data;
-
-typedef struct {
- const char *name;
- const git_oid *oid;
-} expected_ref;
-
-void updated_tip_free(updated_tip *t);
-
-void record_callbacks_data_clear(record_callbacks_data *data);
-
-/**
- * Callback for git_remote_update_tips that records updates
- *
- * @param data (git_vector *) of updated_tip instances
- */
-int record_update_tips_cb(const char *refname, const git_oid *a, const git_oid *b, void *data);
-
-/**
- * Callback for git_remote_list that adds refspecs to delete each ref
- *
- * @param head a ref on the remote
- * @param payload a git_push instance
- */
-int delete_ref_cb(git_remote_head *head, void *payload);
-
-/**
- * Callback for git_remote_list that adds refspecs to vector
- *
- * @param head a ref on the remote
- * @param payload (git_vector *) of git_remote_head instances
- */
-int record_ref_cb(git_remote_head *head, void *payload);
-
-/**
- * Verifies that refs on remote stored by record_ref_cb match the expected
- * names, oids, and order.
- *
- * @param actual_refs actual refs stored by record_ref_cb()
- * @param expected_refs expected remote refs
- * @param expected_refs_len length of expected_refs
- */
-void verify_remote_refs(git_vector *actual_refs, const expected_ref expected_refs[], size_t expected_refs_len);
-
-#endif /* INCLUDE_cl_push_util_h__ */
diff --git a/tests-clar/pack/packbuilder.c b/tests-clar/pack/packbuilder.c
deleted file mode 100644
index 764fba213..000000000
--- a/tests-clar/pack/packbuilder.c
+++ /dev/null
@@ -1,148 +0,0 @@
-#include "clar_libgit2.h"
-#include "fileops.h"
-#include "hash.h"
-#include "iterator.h"
-#include "vector.h"
-#include "posix.h"
-
-static git_repository *_repo;
-static git_revwalk *_revwalker;
-static git_packbuilder *_packbuilder;
-static git_indexer_stream *_indexer;
-static git_vector _commits;
-static int _commits_is_initialized;
-
-void test_pack_packbuilder__initialize(void)
-{
- _repo = cl_git_sandbox_init("testrepo.git");
- cl_git_pass(git_revwalk_new(&_revwalker, _repo));
- cl_git_pass(git_packbuilder_new(&_packbuilder, _repo));
- cl_git_pass(git_vector_init(&_commits, 0, NULL));
- _commits_is_initialized = 1;
-}
-
-void test_pack_packbuilder__cleanup(void)
-{
- git_oid *o;
- unsigned int i;
-
- if (_commits_is_initialized) {
- _commits_is_initialized = 0;
- git_vector_foreach(&_commits, i, o) {
- git__free(o);
- }
- git_vector_free(&_commits);
- }
-
- git_packbuilder_free(_packbuilder);
- _packbuilder = NULL;
-
- git_revwalk_free(_revwalker);
- _revwalker = NULL;
-
- git_indexer_stream_free(_indexer);
- _indexer = NULL;
-
- cl_git_sandbox_cleanup();
- _repo = NULL;
-}
-
-static void seed_packbuilder(void)
-{
- git_oid oid, *o;
- unsigned int i;
-
- git_revwalk_sorting(_revwalker, GIT_SORT_TIME);
- cl_git_pass(git_revwalk_push_ref(_revwalker, "HEAD"));
-
- while (git_revwalk_next(&oid, _revwalker) == 0) {
- o = git__malloc(GIT_OID_RAWSZ);
- cl_assert(o != NULL);
- git_oid_cpy(o, &oid);
- cl_git_pass(git_vector_insert(&_commits, o));
- }
-
- git_vector_foreach(&_commits, i, o) {
- cl_git_pass(git_packbuilder_insert(_packbuilder, o, NULL));
- }
-
- git_vector_foreach(&_commits, i, o) {
- git_object *obj;
- cl_git_pass(git_object_lookup(&obj, _repo, o, GIT_OBJ_COMMIT));
- cl_git_pass(git_packbuilder_insert_tree(_packbuilder,
- git_commit_tree_id((git_commit *)obj)));
- git_object_free(obj);
- }
-}
-
-static int feed_indexer(void *ptr, size_t len, void *payload)
-{
- git_transfer_progress *stats = (git_transfer_progress *)payload;
-
- return git_indexer_stream_add(_indexer, ptr, len, stats);
-}
-
-void test_pack_packbuilder__create_pack(void)
-{
- git_transfer_progress stats;
- git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
- git_hash_ctx ctx;
- git_oid hash;
- char hex[41]; hex[40] = '\0';
-
- seed_packbuilder();
-
- cl_git_pass(git_indexer_stream_new(&_indexer, ".", NULL, NULL));
- cl_git_pass(git_packbuilder_foreach(_packbuilder, feed_indexer, &stats));
- cl_git_pass(git_indexer_stream_finalize(_indexer, &stats));
-
- git_oid_fmt(hex, git_indexer_stream_hash(_indexer));
- git_buf_printf(&path, "pack-%s.pack", hex);
-
- /*
- * By default, packfiles are created with only one thread.
- * Therefore we can predict the object ordering and make sure
- * we create exactly the same pack as git.git does when *not*
- * reusing existing deltas (as libgit2).
- *
- * $ cd tests-clar/resources/testrepo.git
- * $ git rev-list --objects HEAD | \
- * git pack-objects -q --no-reuse-delta --threads=1 pack
- * $ sha1sum git-80e61eb315239ef3c53033e37fee43b744d57122.pack
- * 5d410bdf97cf896f9007681b92868471d636954b
- *
- */
-
- cl_git_pass(git_futils_readbuffer(&buf, git_buf_cstr(&path)));
-
- cl_git_pass(git_hash_ctx_init(&ctx));
- cl_git_pass(git_hash_update(&ctx, buf.ptr, buf.size));
- cl_git_pass(git_hash_final(&hash, &ctx));
- git_hash_ctx_cleanup(&ctx);
-
- git_buf_free(&path);
- git_buf_free(&buf);
-
- git_oid_fmt(hex, &hash);
-
- cl_assert_equal_s(hex, "5d410bdf97cf896f9007681b92868471d636954b");
-}
-
-static git_transfer_progress stats;
-static int foreach_cb(void *buf, size_t len, void *payload)
-{
- git_indexer_stream *idx = (git_indexer_stream *) payload;
- cl_git_pass(git_indexer_stream_add(idx, buf, len, &stats));
- return 0;
-}
-
-void test_pack_packbuilder__foreach(void)
-{
- git_indexer_stream *idx;
-
- seed_packbuilder();
- cl_git_pass(git_indexer_stream_new(&idx, ".", NULL, NULL));
- cl_git_pass(git_packbuilder_foreach(_packbuilder, foreach_cb, idx));
- cl_git_pass(git_indexer_stream_finalize(idx, &stats));
- git_indexer_stream_free(idx);
-}
diff --git a/tests-clar/refs/branches/delete.c b/tests-clar/refs/branches/delete.c
deleted file mode 100644
index 7af5a3e86..000000000
--- a/tests-clar/refs/branches/delete.c
+++ /dev/null
@@ -1,117 +0,0 @@
-#include "clar_libgit2.h"
-#include "refs.h"
-#include "repo/repo_helpers.h"
-#include "config/config_helpers.h"
-
-static git_repository *repo;
-static git_reference *fake_remote;
-
-void test_refs_branches_delete__initialize(void)
-{
- git_oid id;
-
- cl_fixture_sandbox("testrepo.git");
- cl_git_pass(git_repository_open(&repo, "testrepo.git"));
-
- cl_git_pass(git_oid_fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
- cl_git_pass(git_reference_create(&fake_remote, repo, "refs/remotes/nulltoken/master", &id, 0));
-}
-
-void test_refs_branches_delete__cleanup(void)
-{
- git_reference_free(fake_remote);
- fake_remote = NULL;
-
- git_repository_free(repo);
- repo = NULL;
-
- cl_fixture_cleanup("testrepo.git");
-}
-
-void test_refs_branches_delete__can_not_delete_a_branch_pointed_at_by_HEAD(void)
-{
- git_reference *head;
- git_reference *branch;
-
- /* Ensure HEAD targets the local master branch */
- cl_git_pass(git_reference_lookup(&head, repo, GIT_HEAD_FILE));
- cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
- git_reference_free(head);
-
- cl_git_pass(git_branch_lookup(&branch, repo, "master", GIT_BRANCH_LOCAL));
- cl_git_fail(git_branch_delete(branch));
- git_reference_free(branch);
-}
-
-void test_refs_branches_delete__can_delete_a_branch_even_if_HEAD_is_missing(void)
-{
- git_reference *head;
- git_reference *branch;
-
- cl_git_pass(git_reference_lookup(&head, repo, GIT_HEAD_FILE));
- git_reference_delete(head);
- git_reference_free(head);
-
- cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_LOCAL));
- cl_git_pass(git_branch_delete(branch));
- git_reference_free(branch);
-}
-
-void test_refs_branches_delete__can_delete_a_branch_when_HEAD_is_orphaned(void)
-{
- git_reference *branch;
-
- make_head_orphaned(repo, NON_EXISTING_HEAD);
-
- cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_LOCAL));
- cl_git_pass(git_branch_delete(branch));
- git_reference_free(branch);
-}
-
-void test_refs_branches_delete__can_delete_a_branch_pointed_at_by_detached_HEAD(void)
-{
- git_reference *head, *branch;
-
- cl_git_pass(git_reference_lookup(&head, repo, GIT_HEAD_FILE));
- cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head));
- cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
- git_reference_free(head);
-
- /* Detach HEAD and make it target the commit that "master" points to */
- git_repository_detach_head(repo);
-
- cl_git_pass(git_branch_lookup(&branch, repo, "master", GIT_BRANCH_LOCAL));
- cl_git_pass(git_branch_delete(branch));
- git_reference_free(branch);
-}
-
-void test_refs_branches_delete__can_delete_a_local_branch(void)
-{
- git_reference *branch;
- cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_LOCAL));
- cl_git_pass(git_branch_delete(branch));
- git_reference_free(branch);
-}
-
-void test_refs_branches_delete__can_delete_a_remote_branch(void)
-{
- git_reference *branch;
- cl_git_pass(git_branch_lookup(&branch, repo, "nulltoken/master", GIT_BRANCH_REMOTE));
- cl_git_pass(git_branch_delete(branch));
- git_reference_free(branch);
-}
-
-void test_refs_branches_delete__deleting_a_branch_removes_related_configuration_data(void)
-{
- git_reference *branch;
-
- assert_config_entry_existence(repo, "branch.track-local.remote", true);
- assert_config_entry_existence(repo, "branch.track-local.merge", true);
-
- cl_git_pass(git_branch_lookup(&branch, repo, "track-local", GIT_BRANCH_LOCAL));
- cl_git_pass(git_branch_delete(branch));
- git_reference_free(branch);
-
- assert_config_entry_existence(repo, "branch.track-local.remote", false);
- assert_config_entry_existence(repo, "branch.track-local.merge", false);
-}
diff --git a/tests-clar/refs/branches/foreach.c b/tests-clar/refs/branches/foreach.c
deleted file mode 100644
index 433812cb4..000000000
--- a/tests-clar/refs/branches/foreach.c
+++ /dev/null
@@ -1,173 +0,0 @@
-#include "clar_libgit2.h"
-#include "refs.h"
-
-static git_repository *repo;
-static git_reference *fake_remote;
-
-void test_refs_branches_foreach__initialize(void)
-{
- git_oid id;
-
- cl_fixture_sandbox("testrepo.git");
- cl_git_pass(git_repository_open(&repo, "testrepo.git"));
-
- cl_git_pass(git_oid_fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
- cl_git_pass(git_reference_create(&fake_remote, repo, "refs/remotes/nulltoken/master", &id, 0));
-}
-
-void test_refs_branches_foreach__cleanup(void)
-{
- git_reference_free(fake_remote);
- fake_remote = NULL;
-
- git_repository_free(repo);
- repo = NULL;
-
- cl_fixture_cleanup("testrepo.git");
-
- cl_git_sandbox_cleanup();
-}
-
-static int count_branch_list_cb(const char *branch_name, git_branch_t branch_type, void *payload)
-{
- int *count;
-
- GIT_UNUSED(branch_type);
- GIT_UNUSED(branch_name);
-
- count = (int *)payload;
- (*count)++;
-
- return 0;
-}
-
-static void assert_retrieval(unsigned int flags, unsigned int expected_count)
-{
- int count = 0;
-
- cl_git_pass(git_branch_foreach(repo, flags, count_branch_list_cb, &count));
-
- cl_assert_equal_i(expected_count, count);
-}
-
-void test_refs_branches_foreach__retrieve_all_branches(void)
-{
- assert_retrieval(GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE, 14);
-}
-
-void test_refs_branches_foreach__retrieve_remote_branches(void)
-{
- assert_retrieval(GIT_BRANCH_REMOTE, 2);
-}
-
-void test_refs_branches_foreach__retrieve_local_branches(void)
-{
- assert_retrieval(GIT_BRANCH_LOCAL, 12);
-}
-
-struct expectations {
- const char *branch_name;
- int encounters;
-};
-
-static void assert_branch_has_been_found(struct expectations *findings, const char* expected_branch_name)
-{
- int pos = 0;
-
- for (pos = 0; findings[pos].branch_name; ++pos) {
- if (strcmp(expected_branch_name, findings[pos].branch_name) == 0) {
- cl_assert_equal_i(1, findings[pos].encounters);
- return;
- }
- }
-
- cl_fail("expected branch not found in list.");
-}
-
-static int contains_branch_list_cb(const char *branch_name, git_branch_t branch_type, void *payload)
-{
- int pos = 0;
- struct expectations *exp;
-
- GIT_UNUSED(branch_type);
-
- exp = (struct expectations *)payload;
-
- for (pos = 0; exp[pos].branch_name; ++pos) {
- if (strcmp(branch_name, exp[pos].branch_name) == 0)
- exp[pos].encounters++;
- }
-
- return 0;
-}
-
-/*
- * $ git branch -r
- * nulltoken/HEAD -> nulltoken/master
- * nulltoken/master
- */
-void test_refs_branches_foreach__retrieve_remote_symbolic_HEAD_when_present(void)
-{
- struct expectations exp[] = {
- { "nulltoken/HEAD", 0 },
- { "nulltoken/master", 0 },
- { NULL, 0 }
- };
-
- git_reference_free(fake_remote);
- cl_git_pass(git_reference_symbolic_create(&fake_remote, repo, "refs/remotes/nulltoken/HEAD", "refs/remotes/nulltoken/master", 0));
-
- assert_retrieval(GIT_BRANCH_REMOTE, 3);
-
- cl_git_pass(git_branch_foreach(repo, GIT_BRANCH_REMOTE, contains_branch_list_cb, &exp));
-
- assert_branch_has_been_found(exp, "nulltoken/HEAD");
- assert_branch_has_been_found(exp, "nulltoken/master");
-}
-
-static int branch_list_interrupt_cb(
- const char *branch_name, git_branch_t branch_type, void *payload)
-{
- int *count;
-
- GIT_UNUSED(branch_type);
- GIT_UNUSED(branch_name);
-
- count = (int *)payload;
- (*count)++;
-
- return (*count == 5);
-}
-
-void test_refs_branches_foreach__can_cancel(void)
-{
- int count = 0;
-
- cl_assert_equal_i(GIT_EUSER,
- git_branch_foreach(repo, GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE,
- branch_list_interrupt_cb, &count));
-
- cl_assert_equal_i(5, count);
-}
-
-void test_refs_branches_foreach__mix_of_packed_and_loose(void)
-{
- struct expectations exp[] = {
- { "master", 0 },
- { "origin/HEAD", 0 },
- { "origin/master", 0 },
- { "origin/packed", 0 },
- { NULL, 0 }
- };
- git_repository *r2;
-
- r2 = cl_git_sandbox_init("testrepo2");
-
- cl_git_pass(git_branch_foreach(r2, GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE,
- contains_branch_list_cb, &exp));
-
- assert_branch_has_been_found(exp, "master");
- assert_branch_has_been_found(exp, "origin/HEAD");
- assert_branch_has_been_found(exp, "origin/master");
- assert_branch_has_been_found(exp, "origin/packed");
-}
diff --git a/tests-clar/refs/branches/ishead.c b/tests-clar/refs/branches/ishead.c
deleted file mode 100644
index dfcf1b5f1..000000000
--- a/tests-clar/refs/branches/ishead.c
+++ /dev/null
@@ -1,116 +0,0 @@
-#include "clar_libgit2.h"
-#include "refs.h"
-#include "repo/repo_helpers.h"
-
-static git_repository *repo;
-static git_reference *branch;
-
-void test_refs_branches_ishead__initialize(void)
-{
- cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
-}
-
-void test_refs_branches_ishead__cleanup(void)
-{
- git_reference_free(branch);
- branch = NULL;
-
- git_repository_free(repo);
- repo = NULL;
-}
-
-void test_refs_branches_ishead__can_tell_if_a_branch_is_pointed_at_by_HEAD(void)
-{
- cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master"));
-
- cl_assert_equal_i(true, git_branch_is_head(branch));
-}
-
-void test_refs_branches_ishead__can_properly_handle_orphaned_HEAD(void)
-{
- git_repository_free(repo);
-
- repo = cl_git_sandbox_init("testrepo.git");
-
- make_head_orphaned(repo, NON_EXISTING_HEAD);
-
- cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master"));
-
- cl_assert_equal_i(false, git_branch_is_head(branch));
-
- cl_git_sandbox_cleanup();
- repo = NULL;
-}
-
-void test_refs_branches_ishead__can_properly_handle_missing_HEAD(void)
-{
- git_repository_free(repo);
-
- repo = cl_git_sandbox_init("testrepo.git");
-
- delete_head(repo);
-
- cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master"));
-
- cl_assert_equal_i(false, git_branch_is_head(branch));
-
- cl_git_sandbox_cleanup();
- repo = NULL;
-}
-
-void test_refs_branches_ishead__can_tell_if_a_branch_is_not_pointed_at_by_HEAD(void)
-{
- cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/br2"));
-
- cl_assert_equal_i(false, git_branch_is_head(branch));
-}
-
-void test_refs_branches_ishead__wont_be_fooled_by_a_non_branch(void)
-{
- cl_git_pass(git_reference_lookup(&branch, repo, "refs/tags/e90810b"));
-
- cl_assert_equal_i(false, git_branch_is_head(branch));
-}
-
-/*
- * $ git init .
- * Initialized empty Git repository in d:/temp/tempee/.git/
- *
- * $ touch a && git add a
- * $ git commit -m" boom"
- * [master (root-commit) b47b758] boom
- * 0 files changed
- * create mode 100644 a
- *
- * $ echo "ref: refs/heads/master" > .git/refs/heads/linked
- * $ echo "ref: refs/heads/linked" > .git/refs/heads/super
- * $ echo "ref: refs/heads/super" > .git/HEAD
- *
- * $ git branch
- * linked -> master
- * * master
- * super -> master
- */
-void test_refs_branches_ishead__only_direct_references_are_considered(void)
-{
- git_reference *linked, *super, *head;
-
- git_repository_free(repo);
- repo = cl_git_sandbox_init("testrepo.git");
-
- cl_git_pass(git_reference_symbolic_create(&linked, repo, "refs/heads/linked", "refs/heads/master", 0));
- cl_git_pass(git_reference_symbolic_create(&super, repo, "refs/heads/super", "refs/heads/linked", 0));
- cl_git_pass(git_reference_symbolic_create(&head, repo, GIT_HEAD_FILE, "refs/heads/super", 1));
-
- cl_assert_equal_i(false, git_branch_is_head(linked));
- cl_assert_equal_i(false, git_branch_is_head(super));
-
- cl_git_pass(git_repository_head(&branch, repo));
- cl_assert_equal_s("refs/heads/master", git_reference_name(branch));
-
- git_reference_free(linked);
- git_reference_free(super);
- git_reference_free(head);
- cl_git_sandbox_cleanup();
- repo = NULL;
-}
diff --git a/tests-clar/refs/list.c b/tests-clar/refs/list.c
deleted file mode 100644
index c9c2af4a7..000000000
--- a/tests-clar/refs/list.c
+++ /dev/null
@@ -1,57 +0,0 @@
-#include "clar_libgit2.h"
-
-#include "repository.h"
-#include "git2/reflog.h"
-#include "reflog.h"
-
-static git_repository *g_repo;
-
-
-
-void test_refs_list__initialize(void)
-{
- g_repo = cl_git_sandbox_init("testrepo");
-}
-
-void test_refs_list__cleanup(void)
-{
- cl_git_sandbox_cleanup();
-}
-
-
-
-void test_refs_list__all(void)
-{
- // try to list all the references in our test repo
- git_strarray ref_list;
-
- cl_git_pass(git_reference_list(&ref_list, g_repo));
-
- /*{
- unsigned short i;
- for (i = 0; i < ref_list.count; ++i)
- printf("# %s\n", ref_list.strings[i]);
- }*/
-
- /* We have exactly 12 refs in total if we include the packed ones:
- * there is a reference that exists both in the packfile and as
- * loose, but we only list it once */
- cl_assert_equal_i((int)ref_list.count, 13);
-
- git_strarray_free(&ref_list);
-}
-
-void test_refs_list__do_not_retrieve_references_which_name_end_with_a_lock_extension(void)
-{
- git_strarray ref_list;
-
- /* Create a fake locked reference */
- cl_git_mkfile(
- "./testrepo/.git/refs/heads/hanwen.lock",
- "144344043ba4d4a405da03de3844aa829ae8be0e\n");
-
- cl_git_pass(git_reference_list(&ref_list, g_repo));
- cl_assert_equal_i((int)ref_list.count, 13);
-
- git_strarray_free(&ref_list);
-}
diff --git a/tests-clar/refs/lookup.c b/tests-clar/refs/lookup.c
deleted file mode 100644
index 0dbebc5c2..000000000
--- a/tests-clar/refs/lookup.c
+++ /dev/null
@@ -1,48 +0,0 @@
-#include "clar_libgit2.h"
-#include "refs.h"
-
-static git_repository *g_repo;
-
-void test_refs_lookup__initialize(void)
-{
- g_repo = cl_git_sandbox_init("testrepo.git");
-}
-
-void test_refs_lookup__cleanup(void)
-{
- cl_git_sandbox_cleanup();
-}
-
-void test_refs_lookup__with_resolve(void)
-{
- git_reference *a, *b, *temp;
-
- cl_git_pass(git_reference_lookup(&temp, g_repo, "HEAD"));
- cl_git_pass(git_reference_resolve(&a, temp));
- git_reference_free(temp);
-
- cl_git_pass(git_reference_lookup_resolved(&b, g_repo, "HEAD", 5));
- cl_assert(git_reference_cmp(a, b) == 0);
- git_reference_free(b);
-
- cl_git_pass(git_reference_lookup_resolved(&b, g_repo, "HEAD_TRACKER", 5));
- cl_assert(git_reference_cmp(a, b) == 0);
- git_reference_free(b);
-
- git_reference_free(a);
-}
-
-void test_refs_lookup__invalid_name(void)
-{
- git_oid oid;
- cl_git_fail(git_reference_name_to_id(&oid, g_repo, "/refs/tags/point_to_blob"));
-}
-
-void test_refs_lookup__oid(void)
-{
- git_oid tag, expected;
-
- cl_git_pass(git_reference_name_to_id(&tag, g_repo, "refs/tags/point_to_blob"));
- cl_git_pass(git_oid_fromstr(&expected, "1385f264afb75a56a5bec74243be9b367ba4ca08"));
- cl_assert(git_oid_cmp(&tag, &expected) == 0);
-}
diff --git a/tests-clar/refs/pack.c b/tests-clar/refs/pack.c
deleted file mode 100644
index d8d5cc6d0..000000000
--- a/tests-clar/refs/pack.c
+++ /dev/null
@@ -1,79 +0,0 @@
-#include "clar_libgit2.h"
-
-#include "fileops.h"
-#include "git2/reflog.h"
-#include "git2/refdb.h"
-#include "reflog.h"
-#include "refs.h"
-#include "ref_helpers.h"
-
-static const char *loose_tag_ref_name = "refs/tags/e90810b";
-
-static git_repository *g_repo;
-
-void test_refs_pack__initialize(void)
-{
- g_repo = cl_git_sandbox_init("testrepo");
-}
-
-void test_refs_pack__cleanup(void)
-{
- cl_git_sandbox_cleanup();
-}
-
-static void packall(void)
-{
- git_refdb *refdb;
-
- cl_git_pass(git_repository_refdb(&refdb, g_repo));
- cl_git_pass(git_refdb_compress(refdb));
- git_refdb_free(refdb);
-}
-
-void test_refs_pack__empty(void)
-{
- // create a packfile for an empty folder
- git_buf temp_path = GIT_BUF_INIT;
-
- cl_git_pass(git_buf_join_n(&temp_path, '/', 3, git_repository_path(g_repo), GIT_REFS_HEADS_DIR, "empty_dir"));
- cl_git_pass(git_futils_mkdir_r(temp_path.ptr, NULL, GIT_REFS_DIR_MODE));
- git_buf_free(&temp_path);
-
- packall();
-}
-
-void test_refs_pack__loose(void)
-{
- // create a packfile from all the loose rn a repo
- git_reference *reference;
- git_buf temp_path = GIT_BUF_INIT;
-
- /* Ensure a known loose ref can be looked up */
- cl_git_pass(git_reference_lookup(&reference, g_repo, loose_tag_ref_name));
- cl_assert(reference_is_packed(reference) == 0);
- cl_assert_equal_s(reference->name, loose_tag_ref_name);
- git_reference_free(reference);
-
- /*
- * We are now trying to pack also a loose reference
- * called `points_to_blob`, to make sure we can properly
- * pack weak tags
- */
- packall();
-
- /* Ensure the packed-refs file exists */
- cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), GIT_PACKEDREFS_FILE));
- cl_assert(git_path_exists(temp_path.ptr));
-
- /* Ensure the known ref can still be looked up but is now packed */
- cl_git_pass(git_reference_lookup(&reference, g_repo, loose_tag_ref_name));
- cl_assert(reference_is_packed(reference));
- cl_assert_equal_s(reference->name, loose_tag_ref_name);
-
- /* Ensure the known ref has been removed from the loose folder structure */
- cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), loose_tag_ref_name));
- cl_assert(!git_path_exists(temp_path.ptr));
-
- git_reference_free(reference);
- git_buf_free(&temp_path);
-}
diff --git a/tests-clar/refs/read.c b/tests-clar/refs/read.c
deleted file mode 100644
index afb6be008..000000000
--- a/tests-clar/refs/read.c
+++ /dev/null
@@ -1,268 +0,0 @@
-#include "clar_libgit2.h"
-
-#include "repository.h"
-#include "git2/reflog.h"
-#include "reflog.h"
-#include "ref_helpers.h"
-
-static const char *loose_tag_ref_name = "refs/tags/e90810b";
-static const char *non_existing_tag_ref_name = "refs/tags/i-do-not-exist";
-static const char *head_tracker_sym_ref_name = "HEAD_TRACKER";
-static const char *current_head_target = "refs/heads/master";
-static const char *current_master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750";
-static const char *packed_head_name = "refs/heads/packed";
-static const char *packed_test_head_name = "refs/heads/packed-test";
-
-static git_repository *g_repo;
-
-void test_refs_read__initialize(void)
-{
- cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
-}
-
-void test_refs_read__cleanup(void)
-{
- git_repository_free(g_repo);
- g_repo = NULL;
-}
-
-void test_refs_read__loose_tag(void)
-{
- // lookup a loose tag reference
- git_reference *reference;
- git_object *object;
- git_buf ref_name_from_tag_name = GIT_BUF_INIT;
-
- cl_git_pass(git_reference_lookup(&reference, g_repo, loose_tag_ref_name));
- cl_assert(git_reference_type(reference) & GIT_REF_OID);
- cl_assert(reference_is_packed(reference) == 0);
- cl_assert_equal_s(reference->name, loose_tag_ref_name);
-
- cl_git_pass(git_object_lookup(&object, g_repo, git_reference_target(reference), GIT_OBJ_ANY));
- cl_assert(object != NULL);
- cl_assert(git_object_type(object) == GIT_OBJ_TAG);
-
- /* Ensure the name of the tag matches the name of the reference */
- cl_git_pass(git_buf_joinpath(&ref_name_from_tag_name, GIT_REFS_TAGS_DIR, git_tag_name((git_tag *)object)));
- cl_assert_equal_s(ref_name_from_tag_name.ptr, loose_tag_ref_name);
- git_buf_free(&ref_name_from_tag_name);
-
- git_object_free(object);
-
- git_reference_free(reference);
-}
-
-void test_refs_read__nonexisting_tag(void)
-{
- // lookup a loose tag reference that doesn't exist
- git_reference *reference;
-
- cl_git_fail(git_reference_lookup(&reference, g_repo, non_existing_tag_ref_name));
-
- git_reference_free(reference);
-}
-
-
-void test_refs_read__symbolic(void)
-{
- // lookup a symbolic reference
- git_reference *reference, *resolved_ref;
- git_object *object;
- git_oid id;
-
- cl_git_pass(git_reference_lookup(&reference, g_repo, GIT_HEAD_FILE));
- cl_assert(git_reference_type(reference) & GIT_REF_SYMBOLIC);
- cl_assert(reference_is_packed(reference) == 0);
- cl_assert_equal_s(reference->name, GIT_HEAD_FILE);
-
- cl_git_pass(git_reference_resolve(&resolved_ref, reference));
- cl_assert(git_reference_type(resolved_ref) == GIT_REF_OID);
-
- cl_git_pass(git_object_lookup(&object, g_repo, git_reference_target(resolved_ref), GIT_OBJ_ANY));
- cl_assert(object != NULL);
- cl_assert(git_object_type(object) == GIT_OBJ_COMMIT);
-
- git_oid_fromstr(&id, current_master_tip);
- cl_assert(git_oid_cmp(&id, git_object_id(object)) == 0);
-
- git_object_free(object);
-
- git_reference_free(reference);
- git_reference_free(resolved_ref);
-}
-
-void test_refs_read__nested_symbolic(void)
-{
- // lookup a nested symbolic reference
- git_reference *reference, *resolved_ref;
- git_object *object;
- git_oid id;
-
- cl_git_pass(git_reference_lookup(&reference, g_repo, head_tracker_sym_ref_name));
- cl_assert(git_reference_type(reference) & GIT_REF_SYMBOLIC);
- cl_assert(reference_is_packed(reference) == 0);
- cl_assert_equal_s(reference->name, head_tracker_sym_ref_name);
-
- cl_git_pass(git_reference_resolve(&resolved_ref, reference));
- cl_assert(git_reference_type(resolved_ref) == GIT_REF_OID);
-
- cl_git_pass(git_object_lookup(&object, g_repo, git_reference_target(resolved_ref), GIT_OBJ_ANY));
- cl_assert(object != NULL);
- cl_assert(git_object_type(object) == GIT_OBJ_COMMIT);
-
- git_oid_fromstr(&id, current_master_tip);
- cl_assert(git_oid_cmp(&id, git_object_id(object)) == 0);
-
- git_object_free(object);
-
- git_reference_free(reference);
- git_reference_free(resolved_ref);
-}
-
-void test_refs_read__head_then_master(void)
-{
- // lookup the HEAD and resolve the master branch
- git_reference *reference, *resolved_ref, *comp_base_ref;
-
- cl_git_pass(git_reference_lookup(&reference, g_repo, head_tracker_sym_ref_name));
- cl_git_pass(git_reference_resolve(&comp_base_ref, reference));
- git_reference_free(reference);
-
- cl_git_pass(git_reference_lookup(&reference, g_repo, GIT_HEAD_FILE));
- cl_git_pass(git_reference_resolve(&resolved_ref, reference));
- cl_git_pass(git_oid_cmp(git_reference_target(comp_base_ref), git_reference_target(resolved_ref)));
- git_reference_free(reference);
- git_reference_free(resolved_ref);
-
- cl_git_pass(git_reference_lookup(&reference, g_repo, current_head_target));
- cl_git_pass(git_reference_resolve(&resolved_ref, reference));
- cl_git_pass(git_oid_cmp(git_reference_target(comp_base_ref), git_reference_target(resolved_ref)));
- git_reference_free(reference);
- git_reference_free(resolved_ref);
-
- git_reference_free(comp_base_ref);
-}
-
-void test_refs_read__master_then_head(void)
-{
- // lookup the master branch and then the HEAD
- git_reference *reference, *master_ref, *resolved_ref;
-
- cl_git_pass(git_reference_lookup(&master_ref, g_repo, current_head_target));
- cl_git_pass(git_reference_lookup(&reference, g_repo, GIT_HEAD_FILE));
-
- cl_git_pass(git_reference_resolve(&resolved_ref, reference));
- cl_git_pass(git_oid_cmp(git_reference_target(master_ref), git_reference_target(resolved_ref)));
-
- git_reference_free(reference);
- git_reference_free(resolved_ref);
- git_reference_free(master_ref);
-}
-
-
-void test_refs_read__packed(void)
-{
- // lookup a packed reference
- git_reference *reference;
- git_object *object;
-
- cl_git_pass(git_reference_lookup(&reference, g_repo, packed_head_name));
- cl_assert(git_reference_type(reference) & GIT_REF_OID);
- cl_assert(reference_is_packed(reference));
- cl_assert_equal_s(reference->name, packed_head_name);
-
- cl_git_pass(git_object_lookup(&object, g_repo, git_reference_target(reference), GIT_OBJ_ANY));
- cl_assert(object != NULL);
- cl_assert(git_object_type(object) == GIT_OBJ_COMMIT);
-
- git_object_free(object);
-
- git_reference_free(reference);
-}
-
-void test_refs_read__loose_first(void)
-{
- // assure that a loose reference is looked up before a packed reference
- git_reference *reference;
-
- cl_git_pass(git_reference_lookup(&reference, g_repo, packed_head_name));
- git_reference_free(reference);
- cl_git_pass(git_reference_lookup(&reference, g_repo, packed_test_head_name));
- cl_assert(git_reference_type(reference) & GIT_REF_OID);
- cl_assert(reference_is_packed(reference) == 0);
- cl_assert_equal_s(reference->name, packed_test_head_name);
-
- git_reference_free(reference);
-}
-
-void test_refs_read__chomped(void)
-{
- git_reference *test, *chomped;
-
- cl_git_pass(git_reference_lookup(&test, g_repo, "refs/heads/test"));
- cl_git_pass(git_reference_lookup(&chomped, g_repo, "refs/heads/chomped"));
- cl_git_pass(git_oid_cmp(git_reference_target(test), git_reference_target(chomped)));
-
- git_reference_free(test);
- git_reference_free(chomped);
-}
-
-void test_refs_read__trailing(void)
-{
- git_reference *test, *trailing;
-
- cl_git_pass(git_reference_lookup(&test, g_repo, "refs/heads/test"));
- cl_git_pass(git_reference_lookup(&trailing, g_repo, "refs/heads/trailing"));
- cl_git_pass(git_oid_cmp(git_reference_target(test), git_reference_target(trailing)));
- git_reference_free(trailing);
- cl_git_pass(git_reference_lookup(&trailing, g_repo, "FETCH_HEAD"));
-
- git_reference_free(test);
- git_reference_free(trailing);
-}
-
-void test_refs_read__unfound_return_ENOTFOUND(void)
-{
- git_reference *reference;
- git_oid id;
-
- cl_assert_equal_i(GIT_ENOTFOUND,
- git_reference_lookup(&reference, g_repo, "TEST_MASTER"));
- cl_assert_equal_i(GIT_ENOTFOUND,
- git_reference_lookup(&reference, g_repo, "refs/test/master"));
- cl_assert_equal_i(GIT_ENOTFOUND,
- git_reference_lookup(&reference, g_repo, "refs/tags/test/master"));
- cl_assert_equal_i(GIT_ENOTFOUND,
- git_reference_lookup(&reference, g_repo, "refs/tags/test/farther/master"));
-
- cl_assert_equal_i(GIT_ENOTFOUND,
- git_reference_name_to_id(&id, g_repo, "refs/tags/test/farther/master"));
-}
-
-static void assert_is_branch(const char *name, bool expected_branchness)
-{
- git_reference *reference;
- cl_git_pass(git_reference_lookup(&reference, g_repo, name));
- cl_assert_equal_i(expected_branchness, git_reference_is_branch(reference));
- git_reference_free(reference);
-}
-
-void test_refs_read__can_determine_if_a_reference_is_a_local_branch(void)
-{
- assert_is_branch("refs/heads/master", true);
- assert_is_branch("refs/heads/packed", true);
- assert_is_branch("refs/remotes/test/master", false);
- assert_is_branch("refs/tags/e90810b", false);
-}
-
-void test_refs_read__invalid_name_returns_EINVALIDSPEC(void)
-{
- git_reference *reference;
- git_oid id;
-
- cl_assert_equal_i(GIT_EINVALIDSPEC,
- git_reference_lookup(&reference, g_repo, "refs/heads/Inv@{id"));
-
- cl_assert_equal_i(GIT_EINVALIDSPEC,
- git_reference_name_to_id(&id, g_repo, "refs/heads/Inv@{id"));
-}
diff --git a/tests-clar/refs/reflog/drop.c b/tests-clar/refs/reflog/drop.c
deleted file mode 100644
index 21cc847bf..000000000
--- a/tests-clar/refs/reflog/drop.c
+++ /dev/null
@@ -1,124 +0,0 @@
-#include "clar_libgit2.h"
-
-#include "reflog.h"
-
-static git_repository *g_repo;
-static git_reflog *g_reflog;
-static size_t entrycount;
-
-void test_refs_reflog_drop__initialize(void)
-{
- git_reference *ref;
-
- g_repo = cl_git_sandbox_init("testrepo.git");
- cl_git_pass(git_reference_lookup(&ref, g_repo, "HEAD"));
-
- git_reflog_read(&g_reflog, ref);
- entrycount = git_reflog_entrycount(g_reflog);
-
- git_reference_free(ref);
-}
-
-void test_refs_reflog_drop__cleanup(void)
-{
- git_reflog_free(g_reflog);
- g_reflog = NULL;
-
- cl_git_sandbox_cleanup();
-}
-
-void test_refs_reflog_drop__dropping_a_non_exisiting_entry_from_the_log_returns_ENOTFOUND(void)
-{
- cl_assert_equal_i(GIT_ENOTFOUND, git_reflog_drop(g_reflog, entrycount, 0));
-
- cl_assert_equal_sz(entrycount, git_reflog_entrycount(g_reflog));
-}
-
-void test_refs_reflog_drop__can_drop_an_entry(void)
-{
- cl_assert(entrycount > 4);
-
- cl_git_pass(git_reflog_drop(g_reflog, 2, 0));
- cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog));
-}
-
-void test_refs_reflog_drop__can_drop_an_entry_and_rewrite_the_log_history(void)
-{
- const git_reflog_entry *before_current;
- const git_reflog_entry *after_current;
- git_oid before_current_old_oid, before_current_cur_oid;
-
- cl_assert(entrycount > 4);
-
- before_current = git_reflog_entry_byindex(g_reflog, 1);
-
- git_oid_cpy(&before_current_old_oid, &before_current->oid_old);
- git_oid_cpy(&before_current_cur_oid, &before_current->oid_cur);
-
- cl_git_pass(git_reflog_drop(g_reflog, 1, 1));
-
- cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog));
-
- after_current = git_reflog_entry_byindex(g_reflog, 0);
-
- cl_assert_equal_i(0, git_oid_cmp(&before_current_old_oid, &after_current->oid_old));
- cl_assert(0 != git_oid_cmp(&before_current_cur_oid, &after_current->oid_cur));
-}
-
-void test_refs_reflog_drop__can_drop_the_oldest_entry(void)
-{
- const git_reflog_entry *entry;
-
- cl_assert(entrycount > 2);
-
- cl_git_pass(git_reflog_drop(g_reflog, entrycount - 1, 0));
- cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog));
-
- entry = git_reflog_entry_byindex(g_reflog, entrycount - 2);
- cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) != 0);
-}
-
-void test_refs_reflog_drop__can_drop_the_oldest_entry_and_rewrite_the_log_history(void)
-{
- const git_reflog_entry *entry;
-
- cl_assert(entrycount > 2);
-
- cl_git_pass(git_reflog_drop(g_reflog, entrycount - 1, 1));
- cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog));
-
- entry = git_reflog_entry_byindex(g_reflog, entrycount - 2);
- cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) == 0);
-}
-
-void test_refs_reflog_drop__can_drop_all_the_entries(void)
-{
- cl_assert(--entrycount > 0);
-
- do {
- cl_git_pass(git_reflog_drop(g_reflog, 0, 1));
- } while (--entrycount > 0);
-
- cl_git_pass(git_reflog_drop(g_reflog, 0, 1));
-
- cl_assert_equal_i(0, (int)git_reflog_entrycount(g_reflog));
-}
-
-void test_refs_reflog_drop__can_persist_deletion_on_disk(void)
-{
- git_reference *ref;
-
- cl_assert(entrycount > 2);
-
- cl_git_pass(git_reference_lookup(&ref, g_repo, g_reflog->ref_name));
- cl_git_pass(git_reflog_drop(g_reflog, 0, 1));
- cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog));
- cl_git_pass(git_reflog_write(g_reflog));
-
- git_reflog_free(g_reflog);
-
- git_reflog_read(&g_reflog, ref);
- git_reference_free(ref);
-
- cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog));
-}
diff --git a/tests-clar/refs/reflog/reflog.c b/tests-clar/refs/reflog/reflog.c
deleted file mode 100644
index 095cabf04..000000000
--- a/tests-clar/refs/reflog/reflog.c
+++ /dev/null
@@ -1,186 +0,0 @@
-#include "clar_libgit2.h"
-
-#include "fileops.h"
-#include "git2/reflog.h"
-#include "reflog.h"
-
-
-static const char *new_ref = "refs/heads/test-reflog";
-static const char *current_master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750";
-#define commit_msg "commit: bla bla"
-
-static git_repository *g_repo;
-
-
-// helpers
-static void assert_signature(git_signature *expected, git_signature *actual)
-{
- cl_assert(actual);
- cl_assert_equal_s(expected->name, actual->name);
- cl_assert_equal_s(expected->email, actual->email);
- cl_assert(expected->when.offset == actual->when.offset);
- cl_assert(expected->when.time == actual->when.time);
-}
-
-
-// Fixture setup and teardown
-void test_refs_reflog_reflog__initialize(void)
-{
- g_repo = cl_git_sandbox_init("testrepo.git");
-}
-
-void test_refs_reflog_reflog__cleanup(void)
-{
- cl_git_sandbox_cleanup();
-}
-
-void test_refs_reflog_reflog__append_then_read(void)
-{
- // write a reflog for a given reference and ensure it can be read back
- git_repository *repo2;
- git_reference *ref, *lookedup_ref;
- git_oid oid;
- git_signature *committer;
- git_reflog *reflog;
- const git_reflog_entry *entry;
-
- /* Create a new branch pointing at the HEAD */
- git_oid_fromstr(&oid, current_master_tip);
- cl_git_pass(git_reference_create(&ref, g_repo, new_ref, &oid, 0));
-
- cl_git_pass(git_signature_now(&committer, "foo", "foo@bar"));
-
- cl_git_pass(git_reflog_read(&reflog, ref));
-
- cl_git_fail(git_reflog_append(reflog, &oid, committer, "no inner\nnewline"));
- cl_git_pass(git_reflog_append(reflog, &oid, committer, NULL));
- cl_git_pass(git_reflog_append(reflog, &oid, committer, commit_msg "\n"));
- cl_git_pass(git_reflog_write(reflog));
- git_reflog_free(reflog);
-
- /* Reopen a new instance of the repository */
- cl_git_pass(git_repository_open(&repo2, "testrepo.git"));
-
- /* Lookup the previously created branch */
- cl_git_pass(git_reference_lookup(&lookedup_ref, repo2, new_ref));
-
- /* Read and parse the reflog for this branch */
- cl_git_pass(git_reflog_read(&reflog, lookedup_ref));
- cl_assert_equal_i(2, (int)git_reflog_entrycount(reflog));
-
- entry = git_reflog_entry_byindex(reflog, 1);
- assert_signature(committer, entry->committer);
- cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) == 0);
- cl_assert(git_oid_cmp(&oid, &entry->oid_cur) == 0);
- cl_assert(entry->msg == NULL);
-
- entry = git_reflog_entry_byindex(reflog, 0);
- assert_signature(committer, entry->committer);
- cl_assert(git_oid_cmp(&oid, &entry->oid_old) == 0);
- cl_assert(git_oid_cmp(&oid, &entry->oid_cur) == 0);
- cl_assert_equal_s(commit_msg, entry->msg);
-
- git_signature_free(committer);
- git_reflog_free(reflog);
- git_repository_free(repo2);
-
- git_reference_free(ref);
- git_reference_free(lookedup_ref);
-}
-
-void test_refs_reflog_reflog__renaming_the_reference_moves_the_reflog(void)
-{
- git_reference *master, *new_master;
- git_buf master_log_path = GIT_BUF_INIT, moved_log_path = GIT_BUF_INIT;
-
- git_buf_joinpath(&master_log_path, git_repository_path(g_repo), GIT_REFLOG_DIR);
- git_buf_puts(&moved_log_path, git_buf_cstr(&master_log_path));
- git_buf_joinpath(&master_log_path, git_buf_cstr(&master_log_path), "refs/heads/master");
- git_buf_joinpath(&moved_log_path, git_buf_cstr(&moved_log_path), "refs/moved");
-
- cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&master_log_path)));
- cl_assert_equal_i(false, git_path_isfile(git_buf_cstr(&moved_log_path)));
-
- cl_git_pass(git_reference_lookup(&master, g_repo, "refs/heads/master"));
- cl_git_pass(git_reference_rename(&new_master, master, "refs/moved", 0));
- git_reference_free(master);
-
- cl_assert_equal_i(false, git_path_isfile(git_buf_cstr(&master_log_path)));
- cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&moved_log_path)));
-
- git_reference_free(new_master);
- git_buf_free(&moved_log_path);
- git_buf_free(&master_log_path);
-}
-
-static void assert_has_reflog(bool expected_result, const char *name)
-{
- git_reference *ref;
-
- cl_git_pass(git_reference_lookup(&ref, g_repo, name));
-
- cl_assert_equal_i(expected_result, git_reference_has_log(ref));
-
- git_reference_free(ref);
-}
-
-void test_refs_reflog_reflog__reference_has_reflog(void)
-{
- assert_has_reflog(true, "HEAD");
- assert_has_reflog(true, "refs/heads/master");
- assert_has_reflog(false, "refs/heads/subtrees");
-}
-
-void test_refs_reflog_reflog__reading_the_reflog_from_a_reference_with_no_log_returns_an_empty_one(void)
-{
- git_reference *subtrees;
- git_reflog *reflog;
- git_buf subtrees_log_path = GIT_BUF_INIT;
-
- cl_git_pass(git_reference_lookup(&subtrees, g_repo, "refs/heads/subtrees"));
-
- git_buf_join_n(&subtrees_log_path, '/', 3, git_repository_path(g_repo), GIT_REFLOG_DIR, git_reference_name(subtrees));
- cl_assert_equal_i(false, git_path_isfile(git_buf_cstr(&subtrees_log_path)));
-
- cl_git_pass(git_reflog_read(&reflog, subtrees));
-
- cl_assert_equal_i(0, (int)git_reflog_entrycount(reflog));
-
- git_reflog_free(reflog);
- git_reference_free(subtrees);
- git_buf_free(&subtrees_log_path);
-}
-
-void test_refs_reflog_reflog__cannot_write_a_moved_reflog(void)
-{
- git_reference *master, *new_master;
- git_buf master_log_path = GIT_BUF_INIT, moved_log_path = GIT_BUF_INIT;
- git_reflog *reflog;
-
- cl_git_pass(git_reference_lookup(&master, g_repo, "refs/heads/master"));
- cl_git_pass(git_reflog_read(&reflog, master));
-
- cl_git_pass(git_reflog_write(reflog));
-
- cl_git_pass(git_reference_rename(&new_master, master, "refs/moved", 0));
- git_reference_free(master);
-
- cl_git_fail(git_reflog_write(reflog));
-
- git_reflog_free(reflog);
- git_reference_free(new_master);
- git_buf_free(&moved_log_path);
- git_buf_free(&master_log_path);
-}
-
-void test_refs_reflog_reflog__renaming_with_an_invalid_name_returns_EINVALIDSPEC(void)
-{
- git_reference *master;
-
- cl_git_pass(git_reference_lookup(&master, g_repo, "refs/heads/master"));
-
- cl_assert_equal_i(GIT_EINVALIDSPEC,
- git_reflog_rename(master, "refs/heads/Inv@{id"));
-
- git_reference_free(master);
-}
diff --git a/tests-clar/refs/revparse.c b/tests-clar/refs/revparse.c
deleted file mode 100644
index 69d92745c..000000000
--- a/tests-clar/refs/revparse.c
+++ /dev/null
@@ -1,741 +0,0 @@
-#include "clar_libgit2.h"
-
-#include "git2/revparse.h"
-#include "buffer.h"
-#include "refs.h"
-#include "path.h"
-
-static git_repository *g_repo;
-static git_object *g_obj;
-
-/* Helpers */
-static void test_object_and_ref_inrepo(
- const char *spec,
- const char *expected_oid,
- const char *expected_refname,
- git_repository *repo,
- bool assert_reference_retrieval)
-{
- char objstr[64] = {0};
- git_object *obj = NULL;
- git_reference *ref = NULL;
- int error;
-
- error = git_revparse_ext(&obj, &ref, repo, spec);
-
- if (expected_oid != NULL) {
- cl_assert_equal_i(0, error);
- git_oid_fmt(objstr, git_object_id(obj));
- cl_assert_equal_s(objstr, expected_oid);
- } else
- cl_assert_equal_i(GIT_ENOTFOUND, error);
-
- if (assert_reference_retrieval) {
- if (expected_refname == NULL)
- cl_assert(NULL == ref);
- else
- cl_assert_equal_s(expected_refname, git_reference_name(ref));
- }
-
- git_object_free(obj);
- git_reference_free(ref);
-}
-
-static void test_object_inrepo(const char *spec, const char *expected_oid, git_repository *repo)
-{
- test_object_and_ref_inrepo(spec, expected_oid, NULL, repo, false);
-}
-
-static void test_id_inrepo(
- const char *spec,
- const char *expected_left,
- const char *expected_right,
- git_revparse_mode_t expected_flags,
- git_repository *repo)
-{
- git_revspec revspec;
- int error = git_revparse(&revspec, repo, spec);
-
- if (expected_left) {
- char str[64] = {0};
- cl_assert_equal_i(0, error);
- git_oid_fmt(str, git_object_id(revspec.from));
- cl_assert_equal_s(str, expected_left);
- git_object_free(revspec.from);
- } else {
- cl_assert_equal_i(GIT_ENOTFOUND, error);
- }
-
- if (expected_right) {
- char str[64] = {0};
- git_oid_fmt(str, git_object_id(revspec.to));
- cl_assert_equal_s(str, expected_right);
- git_object_free(revspec.to);
- }
-
- if (expected_flags)
- cl_assert_equal_i(expected_flags, revspec.flags);
-}
-
-static void test_object(const char *spec, const char *expected_oid)
-{
- test_object_inrepo(spec, expected_oid, g_repo);
-}
-
-static void test_object_and_ref(const char *spec, const char *expected_oid, const char *expected_refname)
-{
- test_object_and_ref_inrepo(spec, expected_oid, expected_refname, g_repo, true);
-}
-
-static void test_rangelike(const char *rangelike,
- const char *expected_left,
- const char *expected_right,
- git_revparse_mode_t expected_revparseflags)
-{
- char objstr[64] = {0};
- git_revspec revspec;
- int error;
-
- error = git_revparse(&revspec, g_repo, rangelike);
-
- if (expected_left != NULL) {
- cl_assert_equal_i(0, error);
- cl_assert_equal_i(revspec.flags, expected_revparseflags);
- git_oid_fmt(objstr, git_object_id(revspec.from));
- cl_assert_equal_s(objstr, expected_left);
- git_oid_fmt(objstr, git_object_id(revspec.to));
- cl_assert_equal_s(objstr, expected_right);
- } else
- cl_assert(error != 0);
-
- git_object_free(revspec.from);
- git_object_free(revspec.to);
-}
-
-
-static void test_id(
- const char *spec,
- const char *expected_left,
- const char *expected_right,
- git_revparse_mode_t expected_flags)
-{
- test_id_inrepo(spec, expected_left, expected_right, expected_flags, g_repo);
-}
-
-void test_refs_revparse__initialize(void)
-{
- cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
-}
-
-void test_refs_revparse__cleanup(void)
-{
- git_repository_free(g_repo);
-}
-
-void test_refs_revparse__nonexistant_object(void)
-{
- test_object("this-does-not-exist", NULL);
- test_object("this-does-not-exist^1", NULL);
- test_object("this-does-not-exist~2", NULL);
-}
-
-static void assert_invalid_single_spec(const char *invalid_spec)
-{
- cl_assert_equal_i(
- GIT_EINVALIDSPEC, git_revparse_single(&g_obj, g_repo, invalid_spec));
-}
-
-void test_refs_revparse__invalid_reference_name(void)
-{
- assert_invalid_single_spec("this doesn't make sense");
- assert_invalid_single_spec("Inv@{id");
- assert_invalid_single_spec("");
-}
-
-void test_refs_revparse__shas(void)
-{
- test_object("c47800c7266a2be04c571c04d5a6614691ea99bd", "c47800c7266a2be04c571c04d5a6614691ea99bd");
- test_object("c47800c", "c47800c7266a2be04c571c04d5a6614691ea99bd");
-}
-
-void test_refs_revparse__head(void)
-{
- test_object("HEAD", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
- test_object("HEAD^0", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
- test_object("HEAD~0", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
- test_object("master", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
-}
-
-void test_refs_revparse__full_refs(void)
-{
- test_object("refs/heads/master", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
- test_object("refs/heads/test", "e90810b8df3e80c413d903f631643c716887138d");
- test_object("refs/tags/test", "b25fa35b38051e4ae45d4222e795f9df2e43f1d1");
-}
-
-void test_refs_revparse__partial_refs(void)
-{
- test_object("point_to_blob", "1385f264afb75a56a5bec74243be9b367ba4ca08");
- test_object("packed-test", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045");
- test_object("br2", "a4a7dce85cf63874e984719f4fdd239f5145052f");
-}
-
-void test_refs_revparse__describe_output(void)
-{
- test_object("blah-7-gc47800c", "c47800c7266a2be04c571c04d5a6614691ea99bd");
- test_object("not-good", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
-}
-
-void test_refs_revparse__nth_parent(void)
-{
- assert_invalid_single_spec("be3563a^-1");
- assert_invalid_single_spec("^");
- assert_invalid_single_spec("be3563a^{tree}^");
- assert_invalid_single_spec("point_to_blob^{blob}^");
- assert_invalid_single_spec("this doesn't make sense^1");
-
- test_object("be3563a^1", "9fd738e8f7967c078dceed8190330fc8648ee56a");
- test_object("be3563a^", "9fd738e8f7967c078dceed8190330fc8648ee56a");
- test_object("be3563a^2", "c47800c7266a2be04c571c04d5a6614691ea99bd");
- test_object("be3563a^1^1", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045");
- test_object("be3563a^^", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045");
- test_object("be3563a^2^1", "5b5b025afb0b4c913b4c338a42934a3863bf3644");
- test_object("be3563a^0", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
- test_object("be3563a^{commit}^", "9fd738e8f7967c078dceed8190330fc8648ee56a");
-
- test_object("be3563a^42", NULL);
-}
-
-void test_refs_revparse__not_tag(void)
-{
- test_object("point_to_blob^{}", "1385f264afb75a56a5bec74243be9b367ba4ca08");
- test_object("wrapped_tag^{}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
- test_object("master^{}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
- test_object("master^{tree}^{}", "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162");
- test_object("e90810b^{}", "e90810b8df3e80c413d903f631643c716887138d");
- test_object("tags/e90810b^{}", "e90810b8df3e80c413d903f631643c716887138d");
- test_object("e908^{}", "e90810b8df3e80c413d903f631643c716887138d");
-}
-
-void test_refs_revparse__to_type(void)
-{
- assert_invalid_single_spec("wrapped_tag^{trip}");
- test_object("point_to_blob^{commit}", NULL);
- cl_assert_equal_i(
- GIT_EAMBIGUOUS, git_revparse_single(&g_obj, g_repo, "wrapped_tag^{blob}"));
-
- test_object("wrapped_tag^{commit}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
- test_object("wrapped_tag^{tree}", "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162");
- test_object("point_to_blob^{blob}", "1385f264afb75a56a5bec74243be9b367ba4ca08");
- test_object("master^{commit}^{commit}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
-}
-
-void test_refs_revparse__linear_history(void)
-{
- assert_invalid_single_spec("~");
- test_object("foo~bar", NULL);
-
- assert_invalid_single_spec("master~bar");
- assert_invalid_single_spec("master~-1");
- assert_invalid_single_spec("master~0bar");
- assert_invalid_single_spec("this doesn't make sense~2");
- assert_invalid_single_spec("be3563a^{tree}~");
- assert_invalid_single_spec("point_to_blob^{blob}~");
-
- test_object("master~0", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
- test_object("master~1", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
- test_object("master~2", "9fd738e8f7967c078dceed8190330fc8648ee56a");
- test_object("master~1~1", "9fd738e8f7967c078dceed8190330fc8648ee56a");
- test_object("master~~", "9fd738e8f7967c078dceed8190330fc8648ee56a");
-}
-
-void test_refs_revparse__chaining(void)
-{
- assert_invalid_single_spec("master@{0}@{0}");
- assert_invalid_single_spec("@{u}@{-1}");
- assert_invalid_single_spec("@{-1}@{-1}");
- assert_invalid_single_spec("@{-3}@{0}");
-
- test_object("master@{0}~1^1", "9fd738e8f7967c078dceed8190330fc8648ee56a");
- test_object("@{u}@{0}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
- test_object("@{-1}@{0}", "a4a7dce85cf63874e984719f4fdd239f5145052f");
- test_object("@{-4}@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
- test_object("master~1^1", "9fd738e8f7967c078dceed8190330fc8648ee56a");
- test_object("master~1^2", "c47800c7266a2be04c571c04d5a6614691ea99bd");
- test_object("master^1^2~1", "5b5b025afb0b4c913b4c338a42934a3863bf3644");
- test_object("master^^2^", "5b5b025afb0b4c913b4c338a42934a3863bf3644");
- test_object("master^1^1^1^1^1", "8496071c1b46c854b31185ea97743be6a8774479");
- test_object("master^^1^2^1", NULL);
-}
-
-void test_refs_revparse__upstream(void)
-{
- assert_invalid_single_spec("e90810b@{u}");
- assert_invalid_single_spec("refs/tags/e90810b@{u}");
- test_object("refs/heads/e90810b@{u}", NULL);
-
- test_object("master@{upstream}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
- test_object("@{u}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
- test_object("master@{u}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
- test_object("heads/master@{u}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
- test_object("refs/heads/master@{u}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
-}
-
-void test_refs_revparse__ordinal(void)
-{
- assert_invalid_single_spec("master@{-2}");
-
- /* TODO: make the test below actually fail
- * cl_git_fail(git_revparse_single(&g_obj, g_repo, "master@{1a}"));
- */
-
- test_object("nope@{0}", NULL);
- test_object("master@{31415}", NULL);
- test_object("@{1000}", NULL);
- test_object("@{2}", NULL);
-
- test_object("@{0}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
- test_object("@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
-
- test_object("master@{0}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
- test_object("master@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
- test_object("heads/master@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
- test_object("refs/heads/master@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
-}
-
-void test_refs_revparse__previous_head(void)
-{
- assert_invalid_single_spec("@{-xyz}");
- assert_invalid_single_spec("@{-0}");
- assert_invalid_single_spec("@{-1b}");
-
- test_object("@{-42}", NULL);
-
- test_object("@{-2}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
- test_object("@{-1}", "a4a7dce85cf63874e984719f4fdd239f5145052f");
-}
-
-static void create_fake_stash_reference_and_reflog(git_repository *repo)
-{
- git_reference *master, *new_master;
- git_buf log_path = GIT_BUF_INIT;
-
- git_buf_joinpath(&log_path, git_repository_path(repo), "logs/refs/fakestash");
-
- cl_assert_equal_i(false, git_path_isfile(git_buf_cstr(&log_path)));
-
- cl_git_pass(git_reference_lookup(&master, repo, "refs/heads/master"));
- cl_git_pass(git_reference_rename(&new_master, master, "refs/fakestash", 0));
- git_reference_free(master);
-
- cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&log_path)));
-
- git_buf_free(&log_path);
- git_reference_free(new_master);
-}
-
-void test_refs_revparse__reflog_of_a_ref_under_refs(void)
-{
- git_repository *repo = cl_git_sandbox_init("testrepo.git");
-
- test_object_inrepo("refs/fakestash", NULL, repo);
-
- create_fake_stash_reference_and_reflog(repo);
-
- /*
- * $ git reflog -1 refs/fakestash
- * a65fedf refs/fakestash@{0}: commit: checking in
- *
- * $ git reflog -1 refs/fakestash@{0}
- * a65fedf refs/fakestash@{0}: commit: checking in
- *
- * $ git reflog -1 fakestash
- * a65fedf fakestash@{0}: commit: checking in
- *
- * $ git reflog -1 fakestash@{0}
- * a65fedf fakestash@{0}: commit: checking in
- */
- test_object_inrepo("refs/fakestash", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo);
- test_object_inrepo("refs/fakestash@{0}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo);
- test_object_inrepo("fakestash", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo);
- test_object_inrepo("fakestash@{0}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo);
-
- cl_git_sandbox_cleanup();
-}
-
-void test_refs_revparse__revwalk(void)
-{
- test_object("master^{/not found in any commit}", NULL);
- test_object("master^{/merge}", NULL);
- assert_invalid_single_spec("master^{/((}");
-
- test_object("master^{/anoth}", "5b5b025afb0b4c913b4c338a42934a3863bf3644");
- test_object("master^{/Merge}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
- test_object("br2^{/Merge}", "a4a7dce85cf63874e984719f4fdd239f5145052f");
- test_object("master^{/fo.rth}", "9fd738e8f7967c078dceed8190330fc8648ee56a");
-}
-
-void test_refs_revparse__date(void)
-{
- /*
- * $ git reflog HEAD --date=iso
- * a65fedf HEAD@{2012-04-30 08:23:41 -0900}: checkout: moving from br2 to master
- * a4a7dce HEAD@{2012-04-30 08:23:37 -0900}: commit: checking in
- * c47800c HEAD@{2012-04-30 08:23:28 -0900}: checkout: moving from master to br2
- * a65fedf HEAD@{2012-04-30 08:23:23 -0900}: commit:
- * be3563a HEAD@{2012-04-30 10:22:43 -0700}: clone: from /Users/ben/src/libgit2/tes
- *
- * $ git reflog HEAD --date=raw
- * a65fedf HEAD@{1335806621 -0900}: checkout: moving from br2 to master
- * a4a7dce HEAD@{1335806617 -0900}: commit: checking in
- * c47800c HEAD@{1335806608 -0900}: checkout: moving from master to br2
- * a65fedf HEAD@{1335806603 -0900}: commit:
- * be3563a HEAD@{1335806563 -0700}: clone: from /Users/ben/src/libgit2/tests/resour
- */
- test_object("HEAD@{10 years ago}", NULL);
-
- test_object("HEAD@{1 second}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
- test_object("HEAD@{1 second ago}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
- test_object("HEAD@{2 days ago}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
-
- /*
- * $ git reflog master --date=iso
- * a65fedf master@{2012-04-30 09:23:23 -0800}: commit: checking in
- * be3563a master@{2012-04-30 09:22:43 -0800}: clone: from /Users/ben/src...
- *
- * $ git reflog master --date=raw
- * a65fedf master@{1335806603 -0800}: commit: checking in
- * be3563a master@{1335806563 -0800}: clone: from /Users/ben/src/libgit2/tests/reso
- */
-
-
- /*
- * $ git reflog -1 "master@{2012-04-30 17:22:42 +0000}"
- * warning: Log for 'master' only goes back to Mon, 30 Apr 2012 09:22:43 -0800.
- */
- test_object("master@{2012-04-30 17:22:42 +0000}", NULL);
- test_object("master@{2012-04-30 09:22:42 -0800}", NULL);
-
- /*
- * $ git reflog -1 "master@{2012-04-30 17:22:43 +0000}"
- * be3563a master@{Mon Apr 30 09:22:43 2012 -0800}: clone: from /Users/ben/src/libg
- */
- test_object("master@{2012-04-30 17:22:43 +0000}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
- test_object("master@{2012-04-30 09:22:43 -0800}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
-
- /*
- * $ git reflog -1 "master@{2012-4-30 09:23:27 -0800}"
- * a65fedf master@{Mon Apr 30 09:23:23 2012 -0800}: commit: checking in
- */
- test_object("master@{2012-4-30 09:23:27 -0800}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
-
- /*
- * $ git reflog -1 master@{2012-05-03}
- * a65fedf master@{Mon Apr 30 09:23:23 2012 -0800}: commit: checking in
- */
- test_object("master@{2012-05-03}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
-
- /*
- * $ git reflog -1 "master@{1335806603}"
- * a65fedf
- *
- * $ git reflog -1 "master@{1335806602}"
- * be3563a
- */
- test_object("master@{1335806603}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
- test_object("master@{1335806602}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
-}
-
-void test_refs_revparse__colon(void)
-{
- assert_invalid_single_spec(":/");
- assert_invalid_single_spec("point_to_blob:readme.txt");
- cl_git_fail(git_revparse_single(&g_obj, g_repo, ":2:README")); /* Not implemented */
-
- test_object(":/not found in any commit", NULL);
- test_object("subtrees:ab/42.txt", NULL);
- test_object("subtrees:ab/4.txt/nope", NULL);
- test_object("subtrees:nope", NULL);
- test_object("test/master^1:branch_file.txt", NULL);
-
- /* From tags */
- test_object("test:readme.txt", "0266163a49e280c4f5ed1e08facd36a2bd716bcf");
- test_object("tags/test:readme.txt", "0266163a49e280c4f5ed1e08facd36a2bd716bcf");
- test_object("e90810b:readme.txt", "0266163a49e280c4f5ed1e08facd36a2bd716bcf");
- test_object("tags/e90810b:readme.txt", "0266163a49e280c4f5ed1e08facd36a2bd716bcf");
-
- /* From commits */
- test_object("a65f:branch_file.txt", "3697d64be941a53d4ae8f6a271e4e3fa56b022cc");
-
- /* From trees */
- test_object("a65f^{tree}:branch_file.txt", "3697d64be941a53d4ae8f6a271e4e3fa56b022cc");
- test_object("944c:branch_file.txt", "3697d64be941a53d4ae8f6a271e4e3fa56b022cc");
-
- /* Retrieving trees */
- test_object("master:", "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162");
- test_object("subtrees:", "ae90f12eea699729ed24555e40b9fd669da12a12");
- test_object("subtrees:ab", "f1425cef211cc08caa31e7b545ffb232acb098c3");
- test_object("subtrees:ab/", "f1425cef211cc08caa31e7b545ffb232acb098c3");
-
- /* Retrieving blobs */
- test_object("subtrees:ab/4.txt", "d6c93164c249c8000205dd4ec5cbca1b516d487f");
- test_object("subtrees:ab/de/fgh/1.txt", "1f67fc4386b2d171e0d21be1c447e12660561f9b");
- test_object("master:README", "a8233120f6ad708f843d861ce2b7228ec4e3dec6");
- test_object("master:new.txt", "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd");
- test_object(":/Merge", "a4a7dce85cf63874e984719f4fdd239f5145052f");
- test_object(":/one", "c47800c7266a2be04c571c04d5a6614691ea99bd");
- test_object(":/packed commit t", "41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9");
- test_object("test/master^2:branch_file.txt", "45b983be36b73c0788dc9cbcb76cbb80fc7bb057");
- test_object("test/master@{1}:branch_file.txt", "3697d64be941a53d4ae8f6a271e4e3fa56b022cc");
-}
-
-void test_refs_revparse__disambiguation(void)
-{
- /*
- * $ git show e90810b
- * tag e90810b
- * Tagger: Vicent Marti <tanoku@gmail.com>
- * Date: Thu Aug 12 03:59:17 2010 +0200
- *
- * This is a very simple tag.
- *
- * commit e90810b8df3e80c413d903f631643c716887138d
- * Author: Vicent Marti <tanoku@gmail.com>
- * Date: Thu Aug 5 18:42:20 2010 +0200
- *
- * Test commit 2
- *
- * diff --git a/readme.txt b/readme.txt
- * index 6336846..0266163 100644
- * --- a/readme.txt
- * +++ b/readme.txt
- * @@ -1 +1,2 @@
- * Testing a readme.txt
- * +Now we add a single line here
- *
- * $ git show-ref e90810b
- * 7b4384978d2493e851f9cca7858815fac9b10980 refs/tags/e90810b
- *
- */
- test_object("e90810b", "7b4384978d2493e851f9cca7858815fac9b10980");
-
- /*
- * $ git show e90810
- * commit e90810b8df3e80c413d903f631643c716887138d
- * Author: Vicent Marti <tanoku@gmail.com>
- * Date: Thu Aug 5 18:42:20 2010 +0200
- *
- * Test commit 2
- *
- * diff --git a/readme.txt b/readme.txt
- * index 6336846..0266163 100644
- * --- a/readme.txt
- * +++ b/readme.txt
- * @@ -1 +1,2 @@
- * Testing a readme.txt
- * +Now we add a single line here
- */
- test_object("e90810", "e90810b8df3e80c413d903f631643c716887138d");
-}
-
-void test_refs_revparse__a_too_short_objectid_returns_EAMBIGUOUS(void)
-{
- cl_assert_equal_i(
- GIT_EAMBIGUOUS, git_revparse_single(&g_obj, g_repo, "e90"));
-}
-
-void test_refs_revparse__issue_994(void)
-{
- git_repository *repo;
- git_reference *head, *with_at;
- git_object *target;
-
- repo = cl_git_sandbox_init("testrepo.git");
-
- cl_assert_equal_i(GIT_ENOTFOUND,
- git_revparse_single(&target, repo, "origin/bim_with_3d@11296"));
-
- cl_assert_equal_i(GIT_ENOTFOUND,
- git_revparse_single(&target, repo, "refs/remotes/origin/bim_with_3d@11296"));
-
-
- cl_git_pass(git_repository_head(&head, repo));
- cl_git_pass(git_reference_create(
- &with_at,
- repo,
- "refs/remotes/origin/bim_with_3d@11296",
- git_reference_target(head),
- 0));
-
- cl_git_pass(git_revparse_single(&target, repo, "origin/bim_with_3d@11296"));
- git_object_free(target);
-
- cl_git_pass(git_revparse_single(&target, repo, "refs/remotes/origin/bim_with_3d@11296"));
- git_object_free(target);
-
- git_reference_free(with_at);
- git_reference_free(head);
- cl_git_sandbox_cleanup();
-}
-
-/**
- * $ git rev-parse blah-7-gc47800c
- * c47800c7266a2be04c571c04d5a6614691ea99bd
- *
- * $ git rev-parse HEAD~3
- * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
- *
- * $ git branch blah-7-gc47800c HEAD~3
- *
- * $ git rev-parse blah-7-gc47800c
- * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
- */
-void test_refs_revparse__try_to_retrieve_branch_before_described_tag(void)
-{
- git_repository *repo;
- git_reference *branch;
- git_object *target;
- char sha[GIT_OID_HEXSZ + 1];
-
- repo = cl_git_sandbox_init("testrepo.git");
-
- test_object_inrepo("blah-7-gc47800c", "c47800c7266a2be04c571c04d5a6614691ea99bd", repo);
-
- cl_git_pass(git_revparse_single(&target, repo, "HEAD~3"));
- cl_git_pass(git_branch_create(&branch, repo, "blah-7-gc47800c", (git_commit *)target, 0));
-
- git_oid_tostr(sha, GIT_OID_HEXSZ + 1, git_object_id(target));
-
- test_object_inrepo("blah-7-gc47800c", sha, repo);
-
- git_reference_free(branch);
- git_object_free(target);
- cl_git_sandbox_cleanup();
-}
-
-/**
- * $ git rev-parse a65fedf39aefe402d3bb6e24df4d4f5fe4547750
- * a65fedf39aefe402d3bb6e24df4d4f5fe4547750
- *
- * $ git rev-parse HEAD~3
- * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
- *
- * $ git branch a65fedf39aefe402d3bb6e24df4d4f5fe4547750 HEAD~3
- *
- * $ git rev-parse a65fedf39aefe402d3bb6e24df4d4f5fe4547750
- * a65fedf39aefe402d3bb6e24df4d4f5fe4547750
- *
- * $ git rev-parse heads/a65fedf39aefe402d3bb6e24df4d4f5fe4547750
- * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
- */
-void test_refs_revparse__try_to_retrieve_sha_before_branch(void)
-{
- git_repository *repo;
- git_reference *branch;
- git_object *target;
- char sha[GIT_OID_HEXSZ + 1];
-
- repo = cl_git_sandbox_init("testrepo.git");
-
- test_object_inrepo("a65fedf39aefe402d3bb6e24df4d4f5fe4547750", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo);
-
- cl_git_pass(git_revparse_single(&target, repo, "HEAD~3"));
- cl_git_pass(git_branch_create(&branch, repo, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", (git_commit *)target, 0));
-
- git_oid_tostr(sha, GIT_OID_HEXSZ + 1, git_object_id(target));
-
- test_object_inrepo("a65fedf39aefe402d3bb6e24df4d4f5fe4547750", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo);
- test_object_inrepo("heads/a65fedf39aefe402d3bb6e24df4d4f5fe4547750", sha, repo);
-
- git_reference_free(branch);
- git_object_free(target);
- cl_git_sandbox_cleanup();
-}
-
-/**
- * $ git rev-parse c47800
- * c47800c7266a2be04c571c04d5a6614691ea99bd
- *
- * $ git rev-parse HEAD~3
- * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
- *
- * $ git branch c47800 HEAD~3
- *
- * $ git rev-parse c47800
- * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
- */
-void test_refs_revparse__try_to_retrieve_branch_before_abbrev_sha(void)
-{
- git_repository *repo;
- git_reference *branch;
- git_object *target;
- char sha[GIT_OID_HEXSZ + 1];
-
- repo = cl_git_sandbox_init("testrepo.git");
-
- test_object_inrepo("c47800", "c47800c7266a2be04c571c04d5a6614691ea99bd", repo);
-
- cl_git_pass(git_revparse_single(&target, repo, "HEAD~3"));
- cl_git_pass(git_branch_create(&branch, repo, "c47800", (git_commit *)target, 0));
-
- git_oid_tostr(sha, GIT_OID_HEXSZ + 1, git_object_id(target));
-
- test_object_inrepo("c47800", sha, repo);
-
- git_reference_free(branch);
- git_object_free(target);
- cl_git_sandbox_cleanup();
-}
-
-
-void test_refs_revparse__range(void)
-{
- assert_invalid_single_spec("be3563a^1..be3563a");
-
- test_rangelike("be3563a^1..be3563a",
- "9fd738e8f7967c078dceed8190330fc8648ee56a",
- "be3563ae3f795b2b4353bcce3a527ad0a4f7f644",
- GIT_REVPARSE_RANGE);
-
- test_rangelike("be3563a^1...be3563a",
- "9fd738e8f7967c078dceed8190330fc8648ee56a",
- "be3563ae3f795b2b4353bcce3a527ad0a4f7f644",
- GIT_REVPARSE_RANGE | GIT_REVPARSE_MERGE_BASE);
-
- test_rangelike("be3563a^1.be3563a", NULL, NULL, 0);
-}
-
-void test_refs_revparse__parses_range_operator(void)
-{
- test_id("HEAD", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", NULL, GIT_REVPARSE_SINGLE);
- test_id("HEAD~3..HEAD",
- "4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
- "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
- GIT_REVPARSE_RANGE);
-
- test_id("HEAD~3...HEAD",
- "4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
- "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
- GIT_REVPARSE_RANGE | GIT_REVPARSE_MERGE_BASE);
-}
-
-void test_refs_revparse__ext_retrieves_both_the_reference_and_its_target(void)
-{
- test_object_and_ref(
- "master@{upstream}",
- "be3563ae3f795b2b4353bcce3a527ad0a4f7f644",
- "refs/remotes/test/master");
-
- test_object_and_ref(
- "@{-1}",
- "a4a7dce85cf63874e984719f4fdd239f5145052f",
- "refs/heads/br2");
-}
-
-void test_refs_revparse__ext_can_expand_short_reference_names(void)
-{
- test_object_and_ref(
- "master",
- "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
- "refs/heads/master");
-}
diff --git a/tests-clar/refs/unicode.c b/tests-clar/refs/unicode.c
deleted file mode 100644
index 2ec103275..000000000
--- a/tests-clar/refs/unicode.c
+++ /dev/null
@@ -1,44 +0,0 @@
-#include "clar_libgit2.h"
-
-static git_repository *repo;
-
-void test_refs_unicode__initialize(void)
-{
- cl_fixture_sandbox("testrepo.git");
-
- cl_git_pass(git_repository_open(&repo, "testrepo.git"));
-}
-
-void test_refs_unicode__cleanup(void)
-{
- git_repository_free(repo);
- repo = NULL;
-
- cl_fixture_cleanup("testrepo.git");
-}
-
-void test_refs_unicode__create_and_lookup(void)
-{
- git_reference *ref0, *ref1, *ref2;
- git_repository *repo2;
-
- const char *REFNAME = "refs/heads/" "\305" "ngstr" "\366" "m";
- const char *master = "refs/heads/master";
-
- /* Create the reference */
- cl_git_pass(git_reference_lookup(&ref0, repo, master));
- cl_git_pass(git_reference_create(&ref1, repo, REFNAME, git_reference_target(ref0), 0));
- cl_assert_equal_s(REFNAME, git_reference_name(ref1));
-
- /* Lookup the reference in a different instance of the repository */
- cl_git_pass(git_repository_open(&repo2, "testrepo.git"));
- cl_git_pass(git_reference_lookup(&ref2, repo2, REFNAME));
-
- cl_assert(git_oid_cmp(git_reference_target(ref1), git_reference_target(ref2)) == 0);
- cl_assert_equal_s(REFNAME, git_reference_name(ref2));
-
- git_reference_free(ref0);
- git_reference_free(ref1);
- git_reference_free(ref2);
- git_repository_free(repo2);
-}
diff --git a/tests-clar/repo/config.c b/tests-clar/repo/config.c
deleted file mode 100644
index b8971bb6b..000000000
--- a/tests-clar/repo/config.c
+++ /dev/null
@@ -1,200 +0,0 @@
-#include "clar_libgit2.h"
-#include "fileops.h"
-#include <ctype.h>
-
-git_buf path = GIT_BUF_INIT;
-
-void test_repo_config__initialize(void)
-{
- cl_fixture_sandbox("empty_standard_repo");
- cl_git_pass(cl_rename("empty_standard_repo/.gitted", "empty_standard_repo/.git"));
-
- git_buf_clear(&path);
-
- cl_must_pass(p_mkdir("alternate", 0777));
- cl_git_pass(git_path_prettify(&path, "alternate", NULL));
-}
-
-void test_repo_config__cleanup(void)
-{
- cl_git_pass(git_path_prettify(&path, "alternate", NULL));
- cl_git_pass(git_futils_rmdir_r(path.ptr, NULL, GIT_RMDIR_REMOVE_FILES));
- git_buf_free(&path);
- cl_assert(!git_path_isdir("alternate"));
-
- cl_fixture_cleanup("empty_standard_repo");
-}
-
-void test_repo_config__open_missing_global(void)
-{
- git_repository *repo;
- git_config *config, *global;
-
- cl_git_pass(git_libgit2_opts(
- GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
- cl_git_pass(git_libgit2_opts(
- GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr));
- cl_git_pass(git_libgit2_opts(
- GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr));
-
- cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
- cl_git_pass(git_repository_config(&config, repo));
- cl_git_pass(git_config_open_level(&global, config, GIT_CONFIG_LEVEL_GLOBAL));
-
- cl_git_pass(git_config_set_string(global, "test.set", "42"));
-
- git_config_free(global);
- git_config_free(config);
- git_repository_free(repo);
-}
-
-void test_repo_config__open_missing_global_with_separators(void)
-{
- git_repository *repo;
- git_config *config, *global;
-
- cl_git_pass(git_buf_printf(&path, "%c%s", GIT_PATH_LIST_SEPARATOR, "dummy"));
-
- cl_git_pass(git_libgit2_opts(
- GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
- cl_git_pass(git_libgit2_opts(
- GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr));
- cl_git_pass(git_libgit2_opts(
- GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr));
-
- git_buf_free(&path);
-
- cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
- cl_git_pass(git_repository_config(&config, repo));
- cl_git_pass(git_config_open_level(&global, config, GIT_CONFIG_LEVEL_GLOBAL));
-
- cl_git_pass(git_config_set_string(global, "test.set", "42"));
-
- git_config_free(global);
- git_config_free(config);
- git_repository_free(repo);
-}
-
-#include "repository.h"
-
-void test_repo_config__read_no_configs(void)
-{
- git_repository *repo;
- int val;
-
- cl_git_pass(git_libgit2_opts(
- GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
- cl_git_pass(git_libgit2_opts(
- GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr));
- cl_git_pass(git_libgit2_opts(
- GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr));
-
- /* with none */
-
- cl_must_pass(p_unlink("empty_standard_repo/.git/config"));
- cl_assert(!git_path_isfile("empty_standard_repo/.git/config"));
-
- cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
- git_repository__cvar_cache_clear(repo);
- val = -1;
- cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV));
- cl_assert_equal_i(GIT_ABBREV_DEFAULT, val);
- git_repository_free(repo);
-
- /* with just system */
-
- cl_must_pass(p_mkdir("alternate/1", 0777));
- cl_git_pass(git_buf_joinpath(&path, path.ptr, "1"));
- cl_git_rewritefile("alternate/1/gitconfig", "[core]\n\tabbrev = 10\n");
- cl_git_pass(git_libgit2_opts(
- GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr));
-
- cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
- git_repository__cvar_cache_clear(repo);
- val = -1;
- cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV));
- cl_assert_equal_i(10, val);
- git_repository_free(repo);
-
- /* with xdg + system */
-
- cl_must_pass(p_mkdir("alternate/2", 0777));
- path.ptr[path.size - 1] = '2';
- cl_git_rewritefile("alternate/2/config", "[core]\n\tabbrev = 20\n");
- cl_git_pass(git_libgit2_opts(
- GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr));
-
- cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
- git_repository__cvar_cache_clear(repo);
- val = -1;
- cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV));
- cl_assert_equal_i(20, val);
- git_repository_free(repo);
-
- /* with global + xdg + system */
-
- cl_must_pass(p_mkdir("alternate/3", 0777));
- path.ptr[path.size - 1] = '3';
- cl_git_rewritefile("alternate/3/.gitconfig", "[core]\n\tabbrev = 30\n");
- cl_git_pass(git_libgit2_opts(
- GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
-
- cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
- git_repository__cvar_cache_clear(repo);
- val = -1;
- cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV));
- cl_assert_equal_i(30, val);
- git_repository_free(repo);
-
- /* with all configs */
-
- cl_git_rewritefile("empty_standard_repo/.git/config", "[core]\n\tabbrev = 40\n");
-
- cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
- git_repository__cvar_cache_clear(repo);
- val = -1;
- cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV));
- cl_assert_equal_i(40, val);
- git_repository_free(repo);
-
- /* with all configs but delete the files ? */
-
- cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
- git_repository__cvar_cache_clear(repo);
- val = -1;
- cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV));
- cl_assert_equal_i(40, val);
-
- cl_must_pass(p_unlink("empty_standard_repo/.git/config"));
- cl_assert(!git_path_isfile("empty_standard_repo/.git/config"));
-
- cl_must_pass(p_unlink("alternate/1/gitconfig"));
- cl_assert(!git_path_isfile("alternate/1/gitconfig"));
-
- cl_must_pass(p_unlink("alternate/2/config"));
- cl_assert(!git_path_isfile("alternate/2/config"));
-
- cl_must_pass(p_unlink("alternate/3/.gitconfig"));
- cl_assert(!git_path_isfile("alternate/3/.gitconfig"));
-
- git_repository__cvar_cache_clear(repo);
- val = -1;
- cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV));
- cl_assert_equal_i(40, val);
- git_repository_free(repo);
-
- /* reopen */
-
- cl_assert(!git_path_isfile("empty_standard_repo/.git/config"));
- cl_assert(!git_path_isfile("alternate/3/.gitconfig"));
-
- cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
- git_repository__cvar_cache_clear(repo);
- val = -1;
- cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV));
- cl_assert_equal_i(7, val);
- git_repository_free(repo);
-
- cl_assert(!git_path_exists("empty_standard_repo/.git/config"));
- cl_assert(!git_path_exists("alternate/3/.gitconfig"));
-}
diff --git a/tests-clar/repo/head.c b/tests-clar/repo/head.c
deleted file mode 100644
index a9f5cfc58..000000000
--- a/tests-clar/repo/head.c
+++ /dev/null
@@ -1,196 +0,0 @@
-#include "clar_libgit2.h"
-#include "refs.h"
-#include "repo_helpers.h"
-#include "posix.h"
-
-static git_repository *repo;
-
-void test_repo_head__initialize(void)
-{
- repo = cl_git_sandbox_init("testrepo.git");
-}
-
-void test_repo_head__cleanup(void)
-{
- cl_git_sandbox_cleanup();
-}
-
-void test_repo_head__head_detached(void)
-{
- git_reference *ref;
-
- cl_git_pass(git_repository_head_detached(repo));
-
- cl_git_pass(git_repository_detach_head(repo));
-
- cl_assert_equal_i(true, git_repository_head_detached(repo));
-
- /* take the reop back to it's original state */
- cl_git_pass(git_reference_symbolic_create(&ref, repo, "HEAD", "refs/heads/master", 1));
- git_reference_free(ref);
-
- cl_assert_equal_i(false, git_repository_head_detached(repo));
-}
-
-void test_repo_head__head_orphan(void)
-{
- git_reference *ref;
-
- cl_git_pass(git_repository_head_detached(repo));
-
- make_head_orphaned(repo, NON_EXISTING_HEAD);
-
- cl_assert(git_repository_head_orphan(repo) == 1);
-
-
- /* take the repo back to it's original state */
- cl_git_pass(git_reference_symbolic_create(&ref, repo, "HEAD", "refs/heads/master", 1));
- cl_assert(git_repository_head_orphan(repo) == 0);
-
- git_reference_free(ref);
-}
-
-void test_repo_head__set_head_Attaches_HEAD_to_un_unborn_branch_when_the_branch_doesnt_exist(void)
-{
- git_reference *head;
-
- cl_git_pass(git_repository_set_head(repo, "refs/heads/doesnt/exist/yet"));
-
- cl_assert_equal_i(false, git_repository_head_detached(repo));
-
- cl_assert_equal_i(GIT_EORPHANEDHEAD, git_repository_head(&head, repo));
-}
-
-void test_repo_head__set_head_Returns_ENOTFOUND_when_the_reference_doesnt_exist(void)
-{
- cl_assert_equal_i(GIT_ENOTFOUND, git_repository_set_head(repo, "refs/tags/doesnt/exist/yet"));
-}
-
-void test_repo_head__set_head_Fails_when_the_reference_points_to_a_non_commitish(void)
-{
- cl_git_fail(git_repository_set_head(repo, "refs/tags/point_to_blob"));
-}
-
-void test_repo_head__set_head_Attaches_HEAD_when_the_reference_points_to_a_branch(void)
-{
- git_reference *head;
-
- cl_git_pass(git_repository_set_head(repo, "refs/heads/br2"));
-
- cl_assert_equal_i(false, git_repository_head_detached(repo));
-
- cl_git_pass(git_repository_head(&head, repo));
- cl_assert_equal_s("refs/heads/br2", git_reference_name(head));
-
- git_reference_free(head);
-}
-
-static void assert_head_is_correctly_detached(void)
-{
- git_reference *head;
- git_object *commit;
-
- cl_assert_equal_i(true, git_repository_head_detached(repo));
-
- cl_git_pass(git_repository_head(&head, repo));
-
- cl_git_pass(git_object_lookup(&commit, repo, git_reference_target(head), GIT_OBJ_COMMIT));
-
- git_object_free(commit);
- git_reference_free(head);
-}
-
-void test_repo_head__set_head_Detaches_HEAD_when_the_reference_doesnt_point_to_a_branch(void)
-{
- cl_git_pass(git_repository_set_head(repo, "refs/tags/test"));
-
- cl_assert_equal_i(true, git_repository_head_detached(repo));
-
- assert_head_is_correctly_detached();
-}
-
-void test_repo_head__set_head_detached_Return_ENOTFOUND_when_the_object_doesnt_exist(void)
-{
- git_oid oid;
-
- cl_git_pass(git_oid_fromstr(&oid, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
-
- cl_assert_equal_i(GIT_ENOTFOUND, git_repository_set_head_detached(repo, &oid));
-}
-
-void test_repo_head__set_head_detached_Fails_when_the_object_isnt_a_commitish(void)
-{
- git_object *blob;
-
- cl_git_pass(git_revparse_single(&blob, repo, "point_to_blob"));
-
- cl_git_fail(git_repository_set_head_detached(repo, git_object_id(blob)));
-
- git_object_free(blob);
-}
-
-void test_repo_head__set_head_detached_Detaches_HEAD_and_make_it_point_to_the_peeled_commit(void)
-{
- git_object *tag;
-
- cl_git_pass(git_revparse_single(&tag, repo, "tags/test"));
- cl_assert_equal_i(GIT_OBJ_TAG, git_object_type(tag));
-
- cl_git_pass(git_repository_set_head_detached(repo, git_object_id(tag)));
-
- assert_head_is_correctly_detached();
-
- git_object_free(tag);
-}
-
-void test_repo_head__detach_head_Detaches_HEAD_and_make_it_point_to_the_peeled_commit(void)
-{
- cl_assert_equal_i(false, git_repository_head_detached(repo));
-
- cl_git_pass(git_repository_detach_head(repo));
-
- assert_head_is_correctly_detached();
-}
-
-void test_repo_head__detach_head_Fails_if_HEAD_and_point_to_a_non_commitish(void)
-{
- git_reference *head;
-
- cl_git_pass(git_reference_symbolic_create(&head, repo, GIT_HEAD_FILE, "refs/tags/point_to_blob", 1));
-
- cl_git_fail(git_repository_detach_head(repo));
-
- git_reference_free(head);
-}
-
-void test_repo_head__detaching_an_orphaned_head_returns_GIT_EORPHANEDHEAD(void)
-{
- make_head_orphaned(repo, NON_EXISTING_HEAD);
-
- cl_assert_equal_i(GIT_EORPHANEDHEAD, git_repository_detach_head(repo));
-}
-
-void test_repo_head__retrieving_an_orphaned_head_returns_GIT_EORPHANEDHEAD(void)
-{
- git_reference *head;
-
- make_head_orphaned(repo, NON_EXISTING_HEAD);
-
- cl_assert_equal_i(GIT_EORPHANEDHEAD, git_repository_head(&head, repo));
-}
-
-void test_repo_head__retrieving_a_missing_head_returns_GIT_ENOTFOUND(void)
-{
- git_reference *head;
-
- delete_head(repo);
-
- cl_assert_equal_i(GIT_ENOTFOUND, git_repository_head(&head, repo));
-}
-
-void test_repo_head__can_tell_if_an_orphaned_head_is_detached(void)
-{
- make_head_orphaned(repo, NON_EXISTING_HEAD);
-
- cl_assert_equal_i(false, git_repository_head_detached(repo));
-}
diff --git a/tests-clar/repo/headtree.c b/tests-clar/repo/headtree.c
deleted file mode 100644
index 0e7fe93e5..000000000
--- a/tests-clar/repo/headtree.c
+++ /dev/null
@@ -1,53 +0,0 @@
-#include "clar_libgit2.h"
-#include "repository.h"
-#include "repo_helpers.h"
-#include "posix.h"
-
-static git_repository *repo;
-static git_tree *tree;
-
-void test_repo_headtree__initialize(void)
-{
- repo = cl_git_sandbox_init("testrepo.git");
- tree = NULL;
-}
-
-void test_repo_headtree__cleanup(void)
-{
- git_tree_free(tree);
- cl_git_sandbox_cleanup();
-}
-
-void test_repo_headtree__can_retrieve_the_root_tree_from_a_detached_head(void)
-{
- cl_git_pass(git_repository_detach_head(repo));
-
- cl_git_pass(git_repository_head_tree(&tree, repo));
-
- cl_assert(git_oid_streq(git_tree_id(tree), "az"));
-}
-
-void test_repo_headtree__can_retrieve_the_root_tree_from_a_non_detached_head(void)
-{
- cl_assert_equal_i(false, git_repository_head_detached(repo));
-
- cl_git_pass(git_repository_head_tree(&tree, repo));
-
- cl_assert(git_oid_streq(git_tree_id(tree), "az"));
-}
-
-void test_repo_headtree__when_head_is_orphaned_returns_EORPHANEDHEAD(void)
-{
- make_head_orphaned(repo, NON_EXISTING_HEAD);
-
- cl_assert_equal_i(true, git_repository_head_orphan(repo));
-
- cl_assert_equal_i(GIT_EORPHANEDHEAD, git_repository_head_tree(&tree, repo));
-}
-
-void test_repo_headtree__when_head_is_missing_returns_ENOTFOUND(void)
-{
- delete_head(repo);
-
- cl_assert_equal_i(GIT_ENOTFOUND, git_repository_head_tree(&tree, repo));
-}
diff --git a/tests-clar/repo/init.c b/tests-clar/repo/init.c
deleted file mode 100644
index 8cf73795f..000000000
--- a/tests-clar/repo/init.c
+++ /dev/null
@@ -1,532 +0,0 @@
-#include "clar_libgit2.h"
-#include "fileops.h"
-#include "repository.h"
-#include "config.h"
-#include "path.h"
-
-enum repo_mode {
- STANDARD_REPOSITORY = 0,
- BARE_REPOSITORY = 1
-};
-
-static git_repository *_repo = NULL;
-
-void test_repo_init__initialize(void)
-{
- _repo = NULL;
-}
-
-static void cleanup_repository(void *path)
-{
- git_repository_free(_repo);
- _repo = NULL;
-
- cl_fixture_cleanup((const char *)path);
-}
-
-static void ensure_repository_init(
- const char *working_directory,
- int is_bare,
- const char *expected_path_repository,
- const char *expected_working_directory)
-{
- const char *workdir;
-
- cl_assert(!git_path_isdir(working_directory));
-
- cl_git_pass(git_repository_init(&_repo, working_directory, is_bare));
-
- workdir = git_repository_workdir(_repo);
- if (workdir != NULL || expected_working_directory != NULL) {
- cl_assert(
- git__suffixcmp(workdir, expected_working_directory) == 0
- );
- }
-
- cl_assert(
- git__suffixcmp(git_repository_path(_repo), expected_path_repository) == 0
- );
-
- cl_assert(git_repository_is_bare(_repo) == is_bare);
-
-#ifdef GIT_WIN32
- if (!is_bare) {
- DWORD fattrs = GetFileAttributes(git_repository_path(_repo));
- cl_assert((fattrs & FILE_ATTRIBUTE_HIDDEN) != 0);
- }
-#endif
-
- cl_assert(git_repository_is_empty(_repo));
-}
-
-void test_repo_init__standard_repo(void)
-{
- cl_set_cleanup(&cleanup_repository, "testrepo");
- ensure_repository_init("testrepo/", 0, "testrepo/.git/", "testrepo/");
-}
-
-void test_repo_init__standard_repo_noslash(void)
-{
- cl_set_cleanup(&cleanup_repository, "testrepo");
- ensure_repository_init("testrepo", 0, "testrepo/.git/", "testrepo/");
-}
-
-void test_repo_init__bare_repo(void)
-{
- cl_set_cleanup(&cleanup_repository, "testrepo.git");
- ensure_repository_init("testrepo.git/", 1, "testrepo.git/", NULL);
-}
-
-void test_repo_init__bare_repo_noslash(void)
-{
- cl_set_cleanup(&cleanup_repository, "testrepo.git");
- ensure_repository_init("testrepo.git", 1, "testrepo.git/", NULL);
-}
-
-void test_repo_init__bare_repo_escaping_current_workdir(void)
-{
- git_buf path_repository = GIT_BUF_INIT;
- git_buf path_current_workdir = GIT_BUF_INIT;
-
- cl_git_pass(git_path_prettify_dir(&path_current_workdir, ".", NULL));
-
- cl_git_pass(git_buf_joinpath(&path_repository, git_buf_cstr(&path_current_workdir), "a/b/c"));
- cl_git_pass(git_futils_mkdir_r(git_buf_cstr(&path_repository), NULL, GIT_DIR_MODE));
-
- /* Change the current working directory */
- cl_git_pass(chdir(git_buf_cstr(&path_repository)));
-
- /* Initialize a bare repo with a relative path escaping out of the current working directory */
- cl_git_pass(git_repository_init(&_repo, "../d/e.git", 1));
- cl_git_pass(git__suffixcmp(git_repository_path(_repo), "/a/b/d/e.git/"));
-
- git_repository_free(_repo);
-
- /* Open a bare repo with a relative path escaping out of the current working directory */
- cl_git_pass(git_repository_open(&_repo, "../d/e.git"));
-
- cl_git_pass(chdir(git_buf_cstr(&path_current_workdir)));
-
- git_buf_free(&path_current_workdir);
- git_buf_free(&path_repository);
-
- cleanup_repository("a");
-}
-
-void test_repo_init__reinit_bare_repo(void)
-{
- cl_set_cleanup(&cleanup_repository, "reinit.git");
-
- /* Initialize the repository */
- cl_git_pass(git_repository_init(&_repo, "reinit.git", 1));
- git_repository_free(_repo);
-
- /* Reinitialize the repository */
- cl_git_pass(git_repository_init(&_repo, "reinit.git", 1));
-}
-
-void test_repo_init__reinit_too_recent_bare_repo(void)
-{
- git_config *config;
-
- /* Initialize the repository */
- cl_git_pass(git_repository_init(&_repo, "reinit.git", 1));
- git_repository_config(&config, _repo);
-
- /*
- * Hack the config of the repository to make it look like it has
- * been created by a recenter version of git/libgit2
- */
- cl_git_pass(git_config_set_int32(config, "core.repositoryformatversion", 42));
-
- git_config_free(config);
- git_repository_free(_repo);
-
- /* Try to reinitialize the repository */
- cl_git_fail(git_repository_init(&_repo, "reinit.git", 1));
-
- cl_fixture_cleanup("reinit.git");
-}
-
-void test_repo_init__additional_templates(void)
-{
- git_buf path = GIT_BUF_INIT;
-
- cl_set_cleanup(&cleanup_repository, "tester");
-
- ensure_repository_init("tester", 0, "tester/.git/", "tester/");
-
- cl_git_pass(
- git_buf_joinpath(&path, git_repository_path(_repo), "description"));
- cl_assert(git_path_isfile(git_buf_cstr(&path)));
-
- cl_git_pass(
- git_buf_joinpath(&path, git_repository_path(_repo), "info/exclude"));
- cl_assert(git_path_isfile(git_buf_cstr(&path)));
-
- cl_git_pass(
- git_buf_joinpath(&path, git_repository_path(_repo), "hooks"));
- cl_assert(git_path_isdir(git_buf_cstr(&path)));
- /* won't confirm specific contents of hooks dir since it may vary */
-
- git_buf_free(&path);
-}
-
-static void assert_config_entry_on_init_bytype(const char *config_key, int expected_value, bool is_bare)
-{
- git_config *config;
- int current_value;
- git_buf repo_path = GIT_BUF_INIT;
-
- cl_set_cleanup(&cleanup_repository, "config_entry");
-
- cl_git_pass(git_buf_puts(&repo_path, "config_entry/test."));
-
- if (!is_bare)
- cl_git_pass(git_buf_puts(&repo_path, "non."));
-
- cl_git_pass(git_buf_puts(&repo_path, "bare.git"));
-
- cl_git_pass(git_repository_init(&_repo, git_buf_cstr(&repo_path), is_bare));
-
- git_buf_free(&repo_path);
-
- git_repository_config(&config, _repo);
-
- if (expected_value >= 0) {
- cl_git_pass(git_config_get_bool(&current_value, config, config_key));
-
- cl_assert_equal_i(expected_value, current_value);
- } else {
- int error = git_config_get_bool(&current_value, config, config_key);
-
- cl_assert_equal_i(expected_value, error);
- }
-
- git_config_free(config);
-}
-
-static void assert_config_entry_on_init(const char *config_key, int expected_value)
-{
- assert_config_entry_on_init_bytype(config_key, expected_value, true);
- git_repository_free(_repo);
-
- assert_config_entry_on_init_bytype(config_key, expected_value, false);
-}
-
-void test_repo_init__detect_filemode(void)
-{
-#ifdef GIT_WIN32
- assert_config_entry_on_init("core.filemode", false);
-#else
- assert_config_entry_on_init("core.filemode", true);
-#endif
-}
-
-#define CASE_INSENSITIVE_FILESYSTEM (defined GIT_WIN32 || defined __APPLE__)
-
-void test_repo_init__detect_ignorecase(void)
-{
-#if CASE_INSENSITIVE_FILESYSTEM
- assert_config_entry_on_init("core.ignorecase", true);
-#else
- assert_config_entry_on_init("core.ignorecase", GIT_ENOTFOUND);
-#endif
-}
-
-void test_repo_init__reinit_doesnot_overwrite_ignorecase(void)
-{
- git_config *config;
- int current_value;
-
- /* Init a new repo */
- cl_set_cleanup(&cleanup_repository, "not.overwrite.git");
- cl_git_pass(git_repository_init(&_repo, "not.overwrite.git", 1));
-
- /* Change the "core.ignorecase" config value to something unlikely */
- git_repository_config(&config, _repo);
- git_config_set_int32(config, "core.ignorecase", 42);
- git_config_free(config);
- git_repository_free(_repo);
- _repo = NULL;
-
- /* Reinit the repository */
- cl_git_pass(git_repository_init(&_repo, "not.overwrite.git", 1));
- git_repository_config(&config, _repo);
-
- /* Ensure the "core.ignorecase" config value hasn't been updated */
- cl_git_pass(git_config_get_int32(&current_value, config, "core.ignorecase"));
- cl_assert_equal_i(42, current_value);
-
- git_config_free(config);
-}
-
-void test_repo_init__reinit_overwrites_filemode(void)
-{
- git_config *config;
- int expected, current_value;
-
-#ifdef GIT_WIN32
- expected = false;
-#else
- expected = true;
-#endif
-
- /* Init a new repo */
- cl_set_cleanup(&cleanup_repository, "overwrite.git");
- cl_git_pass(git_repository_init(&_repo, "overwrite.git", 1));
-
- /* Change the "core.filemode" config value to something unlikely */
- cl_repo_set_bool(_repo, "core.filemode", !expected);
-
- git_repository_free(_repo);
- _repo = NULL;
-
- /* Reinit the repository */
- cl_git_pass(git_repository_init(&_repo, "overwrite.git", 1));
- git_repository_config(&config, _repo);
-
- /* Ensure the "core.filemode" config value has been reset */
- cl_git_pass(git_config_get_bool(&current_value, config, "core.filemode"));
- cl_assert_equal_i(expected, current_value);
-
- git_config_free(config);
-}
-
-void test_repo_init__sets_logAllRefUpdates_according_to_type_of_repository(void)
-{
- assert_config_entry_on_init_bytype("core.logallrefupdates", GIT_ENOTFOUND, true);
- git_repository_free(_repo);
- assert_config_entry_on_init_bytype("core.logallrefupdates", true, false);
-}
-
-void test_repo_init__extended_0(void)
-{
- git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
-
- /* without MKDIR this should fail */
- cl_git_fail(git_repository_init_ext(&_repo, "extended", &opts));
-
- /* make the directory first, then it should succeed */
- cl_git_pass(git_futils_mkdir("extended", NULL, 0775, 0));
- cl_git_pass(git_repository_init_ext(&_repo, "extended", &opts));
-
- cl_assert(!git__suffixcmp(git_repository_workdir(_repo), "/extended/"));
- cl_assert(!git__suffixcmp(git_repository_path(_repo), "/extended/.git/"));
- cl_assert(!git_repository_is_bare(_repo));
- cl_assert(git_repository_is_empty(_repo));
-
- cleanup_repository("extended");
-}
-
-void test_repo_init__extended_1(void)
-{
- git_reference *ref;
- git_remote *remote;
- struct stat st;
- git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
-
- opts.flags = GIT_REPOSITORY_INIT_MKPATH |
- GIT_REPOSITORY_INIT_NO_DOTGIT_DIR;
- opts.mode = GIT_REPOSITORY_INIT_SHARED_GROUP;
- opts.workdir_path = "../c_wd";
- opts.description = "Awesomest test repository evah";
- opts.initial_head = "development";
- opts.origin_url = "https://github.com/libgit2/libgit2.git";
-
- cl_git_pass(git_repository_init_ext(&_repo, "root/b/c.git", &opts));
-
- cl_assert(!git__suffixcmp(git_repository_workdir(_repo), "/c_wd/"));
- cl_assert(!git__suffixcmp(git_repository_path(_repo), "/c.git/"));
- cl_assert(git_path_isfile("root/b/c_wd/.git"));
- cl_assert(!git_repository_is_bare(_repo));
- /* repo will not be counted as empty because we set head to "development" */
- cl_assert(!git_repository_is_empty(_repo));
-
- cl_git_pass(git_path_lstat(git_repository_path(_repo), &st));
- cl_assert(S_ISDIR(st.st_mode));
- cl_assert((S_ISGID & st.st_mode) == S_ISGID);
-
- cl_git_pass(git_reference_lookup(&ref, _repo, "HEAD"));
- cl_assert(git_reference_type(ref) == GIT_REF_SYMBOLIC);
- cl_assert_equal_s("refs/heads/development", git_reference_symbolic_target(ref));
- git_reference_free(ref);
-
- cl_git_pass(git_remote_load(&remote, _repo, "origin"));
- cl_assert_equal_s("origin", git_remote_name(remote));
- cl_assert_equal_s(opts.origin_url, git_remote_url(remote));
- git_remote_free(remote);
-
- git_repository_free(_repo);
- cl_fixture_cleanup("root");
-}
-
-static void assert_hooks_match(
- const char *template_dir,
- const char *repo_dir,
- const char *hook_path,
- bool core_filemode)
-{
- git_buf expected = GIT_BUF_INIT;
- git_buf actual = GIT_BUF_INIT;
- struct stat expected_st, st;
-
- cl_git_pass(git_buf_joinpath(&expected, template_dir, hook_path));
- cl_git_pass(git_path_lstat(expected.ptr, &expected_st));
-
- cl_git_pass(git_buf_joinpath(&actual, repo_dir, hook_path));
- cl_git_pass(git_path_lstat(actual.ptr, &st));
-
- cl_assert(expected_st.st_size == st.st_size);
-
- if (!core_filemode) {
- expected_st.st_mode = expected_st.st_mode & ~0111;
- st.st_mode = st.st_mode & ~0111;
- }
-
- cl_assert_equal_i((int)expected_st.st_mode, (int)st.st_mode);
-
- git_buf_free(&expected);
- git_buf_free(&actual);
-}
-
-static void assert_mode_seems_okay(
- const char *base, const char *path,
- git_filemode_t expect_mode, bool expect_setgid, bool core_filemode)
-{
- git_buf full = GIT_BUF_INIT;
- struct stat st;
-
- cl_git_pass(git_buf_joinpath(&full, base, path));
- cl_git_pass(git_path_lstat(full.ptr, &st));
- git_buf_free(&full);
-
- if (!core_filemode) {
- expect_mode = expect_mode & ~0111;
- st.st_mode = st.st_mode & ~0111;
- expect_setgid = false;
- }
-
- if (S_ISGID != 0) {
- if (expect_setgid)
- cl_assert((st.st_mode & S_ISGID) != 0);
- else
- cl_assert((st.st_mode & S_ISGID) == 0);
- }
-
- if ((expect_mode & 0111) != 0)
- cl_assert((st.st_mode & 0111) != 0);
- else
- cl_assert((st.st_mode & 0111) == 0);
-
- cl_assert((expect_mode & 0170000) == (st.st_mode & 0170000));
-}
-
-void test_repo_init__extended_with_template(void)
-{
- git_buf expected = GIT_BUF_INIT;
- git_buf actual = GIT_BUF_INIT;
- git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
-
- cl_set_cleanup(&cleanup_repository, "templated.git");
-
- opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_BARE |
- GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
- opts.template_path = cl_fixture("template");
-
- cl_git_pass(git_repository_init_ext(&_repo, "templated.git", &opts));
-
- cl_assert(git_repository_is_bare(_repo));
-
- cl_assert(!git__suffixcmp(git_repository_path(_repo), "/templated.git/"));
-
- cl_git_pass(git_futils_readbuffer(
- &expected, cl_fixture("template/description")));
- cl_git_pass(git_futils_readbuffer(
- &actual, "templated.git/description"));
-
- cl_assert_equal_s(expected.ptr, actual.ptr);
-
- git_buf_free(&expected);
- git_buf_free(&actual);
-
- assert_hooks_match(
- cl_fixture("template"), git_repository_path(_repo),
- "hooks/update.sample", true);
-
- assert_hooks_match(
- cl_fixture("template"), git_repository_path(_repo),
- "hooks/link.sample", true);
-}
-
-void test_repo_init__extended_with_template_and_shared_mode(void)
-{
- git_buf expected = GIT_BUF_INIT;
- git_buf actual = GIT_BUF_INIT;
- git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
- git_config *config;
- int filemode = true;
- const char *repo_path = NULL;
-
- cl_set_cleanup(&cleanup_repository, "init_shared_from_tpl");
-
- opts.flags = GIT_REPOSITORY_INIT_MKPATH |
- GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
- opts.template_path = cl_fixture("template");
- opts.mode = GIT_REPOSITORY_INIT_SHARED_GROUP;
-
- cl_git_pass(git_repository_init_ext(&_repo, "init_shared_from_tpl", &opts));
-
- cl_assert(!git_repository_is_bare(_repo));
- cl_assert(!git__suffixcmp(git_repository_path(_repo), "/init_shared_from_tpl/.git/"));
-
- cl_git_pass(git_repository_config(&config, _repo));
- cl_git_pass(git_config_get_bool(&filemode, config, "core.filemode"));
- git_config_free(config);
-
- cl_git_pass(git_futils_readbuffer(
- &expected, cl_fixture("template/description")));
- cl_git_pass(git_futils_readbuffer(
- &actual, "init_shared_from_tpl/.git/description"));
-
- cl_assert_equal_s(expected.ptr, actual.ptr);
-
- git_buf_free(&expected);
- git_buf_free(&actual);
-
- repo_path = git_repository_path(_repo);
- assert_mode_seems_okay(repo_path, "hooks",
- GIT_FILEMODE_TREE | GIT_REPOSITORY_INIT_SHARED_GROUP, true, filemode);
- assert_mode_seems_okay(repo_path, "info",
- GIT_FILEMODE_TREE | GIT_REPOSITORY_INIT_SHARED_GROUP, true, filemode);
- assert_mode_seems_okay(repo_path, "description",
- GIT_FILEMODE_BLOB, false, filemode);
-
- /* for a non-symlinked hook, it should have shared permissions now */
- assert_hooks_match(
- cl_fixture("template"), git_repository_path(_repo),
- "hooks/update.sample", filemode);
-
- /* for a symlinked hook, the permissions still should match the
- * source link, not the GIT_REPOSITORY_INIT_SHARED_GROUP value
- */
- assert_hooks_match(
- cl_fixture("template"), git_repository_path(_repo),
- "hooks/link.sample", filemode);
-}
-
-void test_repo_init__can_reinit_an_initialized_repository(void)
-{
- git_repository *reinit;
-
- cl_set_cleanup(&cleanup_repository, "extended");
-
- cl_git_pass(git_futils_mkdir("extended", NULL, 0775, 0));
- cl_git_pass(git_repository_init(&_repo, "extended", false));
-
- cl_git_pass(git_repository_init(&reinit, "extended", false));
-
- cl_assert_equal_s(git_repository_path(_repo), git_repository_path(reinit));
-
- git_repository_free(reinit);
-}
diff --git a/tests-clar/repo/iterator.c b/tests-clar/repo/iterator.c
deleted file mode 100644
index 11a7d2a23..000000000
--- a/tests-clar/repo/iterator.c
+++ /dev/null
@@ -1,927 +0,0 @@
-#include "clar_libgit2.h"
-#include "iterator.h"
-#include "repository.h"
-#include "fileops.h"
-#include <stdarg.h>
-
-static git_repository *g_repo;
-
-void test_repo_iterator__initialize(void)
-{
-}
-
-void test_repo_iterator__cleanup(void)
-{
- cl_git_sandbox_cleanup();
- g_repo = NULL;
-}
-
-static void expect_iterator_items(
- git_iterator *i,
- int expected_flat,
- const char **expected_flat_paths,
- int expected_total,
- const char **expected_total_paths)
-{
- const git_index_entry *entry;
- int count, error;
- int no_trees = !(git_iterator_flags(i) & GIT_ITERATOR_INCLUDE_TREES);
- bool v = false;
-
- if (expected_flat < 0) { v = true; expected_flat = -expected_flat; }
- if (expected_total < 0) { v = true; expected_total = -expected_total; }
-
- if (v) fprintf(stderr, "== %s ==\n", no_trees ? "notrees" : "trees");
-
- count = 0;
-
- while (!git_iterator_advance(&entry, i)) {
- if (v) fprintf(stderr, " %s %07o\n", entry->path, (int)entry->mode);
-
- if (no_trees)
- cl_assert(entry->mode != GIT_FILEMODE_TREE);
-
- if (expected_flat_paths) {
- const char *expect_path = expected_flat_paths[count];
- size_t expect_len = strlen(expect_path);
-
- cl_assert_equal_s(expect_path, entry->path);
-
- if (expect_path[expect_len - 1] == '/')
- cl_assert_equal_i(GIT_FILEMODE_TREE, entry->mode);
- else
- cl_assert(entry->mode != GIT_FILEMODE_TREE);
- }
-
- if (++count > expected_flat)
- break;
- }
-
- cl_assert_equal_i(expected_flat, count);
-
- cl_git_pass(git_iterator_reset(i, NULL, NULL));
-
- count = 0;
- cl_git_pass(git_iterator_current(&entry, i));
-
- if (v) fprintf(stderr, "-- %s --\n", no_trees ? "notrees" : "trees");
-
- while (entry != NULL) {
- if (v) fprintf(stderr, " %s %07o\n", entry->path, (int)entry->mode);
-
- if (no_trees)
- cl_assert(entry->mode != GIT_FILEMODE_TREE);
-
- if (expected_total_paths) {
- const char *expect_path = expected_total_paths[count];
- size_t expect_len = strlen(expect_path);
-
- cl_assert_equal_s(expect_path, entry->path);
-
- if (expect_path[expect_len - 1] == '/')
- cl_assert_equal_i(GIT_FILEMODE_TREE, entry->mode);
- else
- cl_assert(entry->mode != GIT_FILEMODE_TREE);
- }
-
- if (entry->mode == GIT_FILEMODE_TREE) {
- error = git_iterator_advance_into(&entry, i);
-
- /* could return NOTFOUND if directory is empty */
- cl_assert(!error || error == GIT_ENOTFOUND);
-
- if (error == GIT_ENOTFOUND) {
- error = git_iterator_advance(&entry, i);
- cl_assert(!error || error == GIT_ITEROVER);
- }
- } else {
- error = git_iterator_advance(&entry, i);
- cl_assert(!error || error == GIT_ITEROVER);
- }
-
- if (++count > expected_total)
- break;
- }
-
- cl_assert_equal_i(expected_total, count);
-}
-
-/* Index contents (including pseudotrees):
- *
- * 0: a 5: F 10: k/ 16: L/
- * 1: B 6: g 11: k/1 17: L/1
- * 2: c 7: H 12: k/a 18: L/a
- * 3: D 8: i 13: k/B 19: L/B
- * 4: e 9: J 14: k/c 20: L/c
- * 15: k/D 21: L/D
- *
- * 0: B 5: L/ 11: a 16: k/
- * 1: D 6: L/1 12: c 17: k/1
- * 2: F 7: L/B 13: e 18: k/B
- * 3: H 8: L/D 14: g 19: k/D
- * 4: J 9: L/a 15: i 20: k/a
- * 10: L/c 21: k/c
- */
-
-void test_repo_iterator__index(void)
-{
- git_iterator *i;
- git_index *index;
-
- g_repo = cl_git_sandbox_init("icase");
-
- cl_git_pass(git_repository_index(&index, g_repo));
-
- /* autoexpand with no tree entries for index */
- cl_git_pass(git_iterator_for_index(&i, index, 0, NULL, NULL));
- expect_iterator_items(i, 20, NULL, 20, NULL);
- git_iterator_free(i);
-
- /* auto expand with tree entries */
- cl_git_pass(git_iterator_for_index(
- &i, index, GIT_ITERATOR_INCLUDE_TREES, NULL, NULL));
- expect_iterator_items(i, 22, NULL, 22, NULL);
- git_iterator_free(i);
-
- /* no auto expand (implies trees included) */
- cl_git_pass(git_iterator_for_index(
- &i, index, GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL));
- expect_iterator_items(i, 12, NULL, 22, NULL);
- git_iterator_free(i);
-
- git_index_free(index);
-}
-
-void test_repo_iterator__index_icase(void)
-{
- git_iterator *i;
- git_index *index;
- unsigned int caps;
-
- g_repo = cl_git_sandbox_init("icase");
-
- cl_git_pass(git_repository_index(&index, g_repo));
- caps = git_index_caps(index);
-
- /* force case sensitivity */
- cl_git_pass(git_index_set_caps(index, caps & ~GIT_INDEXCAP_IGNORE_CASE));
-
- /* autoexpand with no tree entries over range */
- cl_git_pass(git_iterator_for_index(&i, index, 0, "c", "k/D"));
- expect_iterator_items(i, 7, NULL, 7, NULL);
- git_iterator_free(i);
-
- cl_git_pass(git_iterator_for_index(&i, index, 0, "k", "k/Z"));
- expect_iterator_items(i, 3, NULL, 3, NULL);
- git_iterator_free(i);
-
- /* auto expand with tree entries */
- cl_git_pass(git_iterator_for_index(
- &i, index, GIT_ITERATOR_INCLUDE_TREES, "c", "k/D"));
- expect_iterator_items(i, 8, NULL, 8, NULL);
- git_iterator_free(i);
- cl_git_pass(git_iterator_for_index(
- &i, index, GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z"));
- expect_iterator_items(i, 4, NULL, 4, NULL);
- git_iterator_free(i);
-
- /* no auto expand (implies trees included) */
- cl_git_pass(git_iterator_for_index(
- &i, index, GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D"));
- expect_iterator_items(i, 5, NULL, 8, NULL);
- git_iterator_free(i);
-
- cl_git_pass(git_iterator_for_index(
- &i, index, GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z"));
- expect_iterator_items(i, 1, NULL, 4, NULL);
- git_iterator_free(i);
-
- /* force case insensitivity */
- cl_git_pass(git_index_set_caps(index, caps | GIT_INDEXCAP_IGNORE_CASE));
-
- /* autoexpand with no tree entries over range */
- cl_git_pass(git_iterator_for_index(&i, index, 0, "c", "k/D"));
- expect_iterator_items(i, 13, NULL, 13, NULL);
- git_iterator_free(i);
-
- cl_git_pass(git_iterator_for_index(&i, index, 0, "k", "k/Z"));
- expect_iterator_items(i, 5, NULL, 5, NULL);
- git_iterator_free(i);
-
- /* auto expand with tree entries */
- cl_git_pass(git_iterator_for_index(
- &i, index, GIT_ITERATOR_INCLUDE_TREES, "c", "k/D"));
- expect_iterator_items(i, 14, NULL, 14, NULL);
- git_iterator_free(i);
-
- cl_git_pass(git_iterator_for_index(
- &i, index, GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z"));
- expect_iterator_items(i, 6, NULL, 6, NULL);
- git_iterator_free(i);
-
- /* no auto expand (implies trees included) */
- cl_git_pass(git_iterator_for_index(
- &i, index, GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D"));
- expect_iterator_items(i, 9, NULL, 14, NULL);
- git_iterator_free(i);
-
- cl_git_pass(git_iterator_for_index(
- &i, index, GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z"));
- expect_iterator_items(i, 1, NULL, 6, NULL);
- git_iterator_free(i);
-
- cl_git_pass(git_index_set_caps(index, caps));
- git_index_free(index);
-}
-
-void test_repo_iterator__tree(void)
-{
- git_iterator *i;
- git_tree *head;
-
- g_repo = cl_git_sandbox_init("icase");
-
- cl_git_pass(git_repository_head_tree(&head, g_repo));
-
- /* auto expand with no tree entries */
- cl_git_pass(git_iterator_for_tree(&i, head, 0, NULL, NULL));
- expect_iterator_items(i, 20, NULL, 20, NULL);
- git_iterator_free(i);
-
- /* auto expand with tree entries */
- cl_git_pass(git_iterator_for_tree(
- &i, head, GIT_ITERATOR_INCLUDE_TREES, NULL, NULL));
- expect_iterator_items(i, 22, NULL, 22, NULL);
- git_iterator_free(i);
-
- /* no auto expand (implies trees included) */
- cl_git_pass(git_iterator_for_tree(
- &i, head, GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL));
- expect_iterator_items(i, 12, NULL, 22, NULL);
- git_iterator_free(i);
-
- git_tree_free(head);
-}
-
-void test_repo_iterator__tree_icase(void)
-{
- git_iterator *i;
- git_tree *head;
- git_iterator_flag_t flag;
-
- g_repo = cl_git_sandbox_init("icase");
-
- cl_git_pass(git_repository_head_tree(&head, g_repo));
-
- flag = GIT_ITERATOR_DONT_IGNORE_CASE;
-
- /* auto expand with no tree entries */
- cl_git_pass(git_iterator_for_tree(&i, head, flag, "c", "k/D"));
- expect_iterator_items(i, 7, NULL, 7, NULL);
- git_iterator_free(i);
-
- cl_git_pass(git_iterator_for_tree(&i, head, flag, "k", "k/Z"));
- expect_iterator_items(i, 3, NULL, 3, NULL);
- git_iterator_free(i);
-
- /* auto expand with tree entries */
- cl_git_pass(git_iterator_for_tree(
- &i, head, flag | GIT_ITERATOR_INCLUDE_TREES, "c", "k/D"));
- expect_iterator_items(i, 8, NULL, 8, NULL);
- git_iterator_free(i);
-
- cl_git_pass(git_iterator_for_tree(
- &i, head, flag | GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z"));
- expect_iterator_items(i, 4, NULL, 4, NULL);
- git_iterator_free(i);
-
- /* no auto expand (implies trees included) */
- cl_git_pass(git_iterator_for_tree(
- &i, head, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D"));
- expect_iterator_items(i, 5, NULL, 8, NULL);
- git_iterator_free(i);
-
- cl_git_pass(git_iterator_for_tree(
- &i, head, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z"));
- expect_iterator_items(i, 1, NULL, 4, NULL);
- git_iterator_free(i);
-
- flag = GIT_ITERATOR_IGNORE_CASE;
-
- /* auto expand with no tree entries */
- cl_git_pass(git_iterator_for_tree(&i, head, flag, "c", "k/D"));
- expect_iterator_items(i, 13, NULL, 13, NULL);
- git_iterator_free(i);
-
- cl_git_pass(git_iterator_for_tree(&i, head, flag, "k", "k/Z"));
- expect_iterator_items(i, 5, NULL, 5, NULL);
- git_iterator_free(i);
-
- /* auto expand with tree entries */
- cl_git_pass(git_iterator_for_tree(
- &i, head, flag | GIT_ITERATOR_INCLUDE_TREES, "c", "k/D"));
- expect_iterator_items(i, 14, NULL, 14, NULL);
- git_iterator_free(i);
-
- cl_git_pass(git_iterator_for_tree(
- &i, head, flag | GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z"));
- expect_iterator_items(i, 6, NULL, 6, NULL);
- git_iterator_free(i);
-
- /* no auto expand (implies trees included) */
- cl_git_pass(git_iterator_for_tree(
- &i, head, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D"));
- expect_iterator_items(i, 9, NULL, 14, NULL);
- git_iterator_free(i);
-
- cl_git_pass(git_iterator_for_tree(
- &i, head, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z"));
- expect_iterator_items(i, 1, NULL, 6, NULL);
- git_iterator_free(i);
-
- git_tree_free(head);
-}
-
-void test_repo_iterator__tree_more(void)
-{
- git_iterator *i;
- git_tree *head;
- static const char *expect_basic[] = {
- "current_file",
- "file_deleted",
- "modified_file",
- "staged_changes",
- "staged_changes_file_deleted",
- "staged_changes_modified_file",
- "staged_delete_file_deleted",
- "staged_delete_modified_file",
- "subdir.txt",
- "subdir/current_file",
- "subdir/deleted_file",
- "subdir/modified_file",
- NULL,
- };
- static const char *expect_trees[] = {
- "current_file",
- "file_deleted",
- "modified_file",
- "staged_changes",
- "staged_changes_file_deleted",
- "staged_changes_modified_file",
- "staged_delete_file_deleted",
- "staged_delete_modified_file",
- "subdir.txt",
- "subdir/",
- "subdir/current_file",
- "subdir/deleted_file",
- "subdir/modified_file",
- NULL,
- };
- static const char *expect_noauto[] = {
- "current_file",
- "file_deleted",
- "modified_file",
- "staged_changes",
- "staged_changes_file_deleted",
- "staged_changes_modified_file",
- "staged_delete_file_deleted",
- "staged_delete_modified_file",
- "subdir.txt",
- "subdir/",
- NULL
- };
-
- g_repo = cl_git_sandbox_init("status");
-
- cl_git_pass(git_repository_head_tree(&head, g_repo));
-
- /* auto expand with no tree entries */
- cl_git_pass(git_iterator_for_tree(&i, head, 0, NULL, NULL));
- expect_iterator_items(i, 12, expect_basic, 12, expect_basic);
- git_iterator_free(i);
-
- /* auto expand with tree entries */
- cl_git_pass(git_iterator_for_tree(
- &i, head, GIT_ITERATOR_INCLUDE_TREES, NULL, NULL));
- expect_iterator_items(i, 13, expect_trees, 13, expect_trees);
- git_iterator_free(i);
-
- /* no auto expand (implies trees included) */
- cl_git_pass(git_iterator_for_tree(
- &i, head, GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL));
- expect_iterator_items(i, 10, expect_noauto, 13, expect_trees);
- git_iterator_free(i);
-
- git_tree_free(head);
-}
-
-/* "b=name,t=name", blob_id, tree_id */
-static void build_test_tree(
- git_oid *out, git_repository *repo, const char *fmt, ...)
-{
- git_oid *id;
- git_treebuilder *builder;
- const char *scan = fmt, *next;
- char type, delimiter;
- git_filemode_t mode = GIT_FILEMODE_BLOB;
- git_buf name = GIT_BUF_INIT;
- va_list arglist;
-
- cl_git_pass(git_treebuilder_create(&builder, NULL)); /* start builder */
-
- va_start(arglist, fmt);
- while (*scan) {
- switch (type = *scan++) {
- case 't': case 'T': mode = GIT_FILEMODE_TREE; break;
- case 'b': case 'B': mode = GIT_FILEMODE_BLOB; break;
- default:
- cl_assert(type == 't' || type == 'T' || type == 'b' || type == 'B');
- }
-
- delimiter = *scan++; /* read and skip delimiter */
- for (next = scan; *next && *next != delimiter; ++next)
- /* seek end */;
- cl_git_pass(git_buf_set(&name, scan, (size_t)(next - scan)));
- for (scan = next; *scan && (*scan == delimiter || *scan == ','); ++scan)
- /* skip delimiter and optional comma */;
-
- id = va_arg(arglist, git_oid *);
-
- cl_git_pass(git_treebuilder_insert(NULL, builder, name.ptr, id, mode));
- }
- va_end(arglist);
-
- cl_git_pass(git_treebuilder_write(out, repo, builder));
-
- git_treebuilder_free(builder);
- git_buf_free(&name);
-}
-
-void test_repo_iterator__tree_case_conflicts_0(void)
-{
- const char *blob_sha = "d44e18fb93b7107b5cd1b95d601591d77869a1b6";
- git_tree *tree;
- git_oid blob_id, biga_id, littlea_id, tree_id;
- git_iterator *i;
- const char *expect_cs[] = {
- "A/1.file", "A/3.file", "a/2.file", "a/4.file" };
- const char *expect_ci[] = {
- "A/1.file", "a/2.file", "A/3.file", "a/4.file" };
- const char *expect_cs_trees[] = {
- "A/", "A/1.file", "A/3.file", "a/", "a/2.file", "a/4.file" };
- const char *expect_ci_trees[] = {
- "A/", "A/1.file", "a/2.file", "A/3.file", "a/4.file" };
-
- g_repo = cl_git_sandbox_init("icase");
-
- cl_git_pass(git_oid_fromstr(&blob_id, blob_sha)); /* lookup blob */
-
- /* create tree with: A/1.file, A/3.file, a/2.file, a/4.file */
- build_test_tree(
- &biga_id, g_repo, "b|1.file|,b|3.file|", &blob_id, &blob_id);
- build_test_tree(
- &littlea_id, g_repo, "b|2.file|,b|4.file|", &blob_id, &blob_id);
- build_test_tree(
- &tree_id, g_repo, "t|A|,t|a|", &biga_id, &littlea_id);
-
- cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
-
- cl_git_pass(git_iterator_for_tree(
- &i, tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL));
- expect_iterator_items(i, 4, expect_cs, 4, expect_cs);
- git_iterator_free(i);
-
- cl_git_pass(git_iterator_for_tree(
- &i, tree, GIT_ITERATOR_IGNORE_CASE, NULL, NULL));
- expect_iterator_items(i, 4, expect_ci, 4, expect_ci);
- git_iterator_free(i);
-
- cl_git_pass(git_iterator_for_tree(
- &i, tree, GIT_ITERATOR_DONT_IGNORE_CASE |
- GIT_ITERATOR_INCLUDE_TREES, NULL, NULL));
- expect_iterator_items(i, 6, expect_cs_trees, 6, expect_cs_trees);
- git_iterator_free(i);
-
- cl_git_pass(git_iterator_for_tree(
- &i, tree, GIT_ITERATOR_IGNORE_CASE |
- GIT_ITERATOR_INCLUDE_TREES, NULL, NULL));
- expect_iterator_items(i, 5, expect_ci_trees, 5, expect_ci_trees);
- git_iterator_free(i);
-
- git_tree_free(tree);
-}
-
-void test_repo_iterator__tree_case_conflicts_1(void)
-{
- const char *blob_sha = "d44e18fb93b7107b5cd1b95d601591d77869a1b6";
- git_tree *tree;
- git_oid blob_id, Ab_id, biga_id, littlea_id, tree_id;
- git_iterator *i;
- const char *expect_cs[] = {
- "A/a", "A/b/1", "A/c", "a/C", "a/a", "a/b" };
- const char *expect_ci[] = {
- "A/a", "a/b", "A/b/1", "A/c" };
- const char *expect_cs_trees[] = {
- "A/", "A/a", "A/b/", "A/b/1", "A/c", "a/", "a/C", "a/a", "a/b" };
- const char *expect_ci_trees[] = {
- "A/", "A/a", "a/b", "A/b/", "A/b/1", "A/c" };
-
- g_repo = cl_git_sandbox_init("icase");
-
- cl_git_pass(git_oid_fromstr(&blob_id, blob_sha)); /* lookup blob */
-
- /* create: A/a A/b/1 A/c a/a a/b a/C */
- build_test_tree(&Ab_id, g_repo, "b|1|", &blob_id);
- build_test_tree(
- &biga_id, g_repo, "b|a|,t|b|,b|c|", &blob_id, &Ab_id, &blob_id);
- build_test_tree(
- &littlea_id, g_repo, "b|a|,b|b|,b|C|", &blob_id, &blob_id, &blob_id);
- build_test_tree(
- &tree_id, g_repo, "t|A|,t|a|", &biga_id, &littlea_id);
-
- cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
-
- cl_git_pass(git_iterator_for_tree(
- &i, tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL));
- expect_iterator_items(i, 6, expect_cs, 6, expect_cs);
- git_iterator_free(i);
-
- cl_git_pass(git_iterator_for_tree(
- &i, tree, GIT_ITERATOR_IGNORE_CASE, NULL, NULL));
- expect_iterator_items(i, 4, expect_ci, 4, expect_ci);
- git_iterator_free(i);
-
- cl_git_pass(git_iterator_for_tree(
- &i, tree, GIT_ITERATOR_DONT_IGNORE_CASE |
- GIT_ITERATOR_INCLUDE_TREES, NULL, NULL));
- expect_iterator_items(i, 9, expect_cs_trees, 9, expect_cs_trees);
- git_iterator_free(i);
-
- cl_git_pass(git_iterator_for_tree(
- &i, tree, GIT_ITERATOR_IGNORE_CASE |
- GIT_ITERATOR_INCLUDE_TREES, NULL, NULL));
- expect_iterator_items(i, 6, expect_ci_trees, 6, expect_ci_trees);
- git_iterator_free(i);
-
- git_tree_free(tree);
-}
-
-void test_repo_iterator__tree_case_conflicts_2(void)
-{
- const char *blob_sha = "d44e18fb93b7107b5cd1b95d601591d77869a1b6";
- git_tree *tree;
- git_oid blob_id, d1, d2, c1, c2, b1, b2, a1, a2, tree_id;
- git_iterator *i;
- const char *expect_cs[] = {
- "A/B/C/D/16", "A/B/C/D/foo", "A/B/C/d/15", "A/B/C/d/FOO",
- "A/B/c/D/14", "A/B/c/D/foo", "A/B/c/d/13", "A/B/c/d/FOO",
- "A/b/C/D/12", "A/b/C/D/foo", "A/b/C/d/11", "A/b/C/d/FOO",
- "A/b/c/D/10", "A/b/c/D/foo", "A/b/c/d/09", "A/b/c/d/FOO",
- "a/B/C/D/08", "a/B/C/D/foo", "a/B/C/d/07", "a/B/C/d/FOO",
- "a/B/c/D/06", "a/B/c/D/foo", "a/B/c/d/05", "a/B/c/d/FOO",
- "a/b/C/D/04", "a/b/C/D/foo", "a/b/C/d/03", "a/b/C/d/FOO",
- "a/b/c/D/02", "a/b/c/D/foo", "a/b/c/d/01", "a/b/c/d/FOO", };
- const char *expect_ci[] = {
- "a/b/c/d/01", "a/b/c/D/02", "a/b/C/d/03", "a/b/C/D/04",
- "a/B/c/d/05", "a/B/c/D/06", "a/B/C/d/07", "a/B/C/D/08",
- "A/b/c/d/09", "A/b/c/D/10", "A/b/C/d/11", "A/b/C/D/12",
- "A/B/c/d/13", "A/B/c/D/14", "A/B/C/d/15", "A/B/C/D/16",
- "A/B/C/D/foo", };
- const char *expect_ci_trees[] = {
- "A/", "A/B/", "A/B/C/", "A/B/C/D/",
- "a/b/c/d/01", "a/b/c/D/02", "a/b/C/d/03", "a/b/C/D/04",
- "a/B/c/d/05", "a/B/c/D/06", "a/B/C/d/07", "a/B/C/D/08",
- "A/b/c/d/09", "A/b/c/D/10", "A/b/C/d/11", "A/b/C/D/12",
- "A/B/c/d/13", "A/B/c/D/14", "A/B/C/d/15", "A/B/C/D/16",
- "A/B/C/D/foo", };
-
- g_repo = cl_git_sandbox_init("icase");
-
- cl_git_pass(git_oid_fromstr(&blob_id, blob_sha)); /* lookup blob */
-
- build_test_tree(&d1, g_repo, "b|16|,b|foo|", &blob_id, &blob_id);
- build_test_tree(&d2, g_repo, "b|15|,b|FOO|", &blob_id, &blob_id);
- build_test_tree(&c1, g_repo, "t|D|,t|d|", &d1, &d2);
- build_test_tree(&d1, g_repo, "b|14|,b|foo|", &blob_id, &blob_id);
- build_test_tree(&d2, g_repo, "b|13|,b|FOO|", &blob_id, &blob_id);
- build_test_tree(&c2, g_repo, "t|D|,t|d|", &d1, &d2);
- build_test_tree(&b1, g_repo, "t|C|,t|c|", &c1, &c2);
-
- build_test_tree(&d1, g_repo, "b|12|,b|foo|", &blob_id, &blob_id);
- build_test_tree(&d2, g_repo, "b|11|,b|FOO|", &blob_id, &blob_id);
- build_test_tree(&c1, g_repo, "t|D|,t|d|", &d1, &d2);
- build_test_tree(&d1, g_repo, "b|10|,b|foo|", &blob_id, &blob_id);
- build_test_tree(&d2, g_repo, "b|09|,b|FOO|", &blob_id, &blob_id);
- build_test_tree(&c2, g_repo, "t|D|,t|d|", &d1, &d2);
- build_test_tree(&b2, g_repo, "t|C|,t|c|", &c1, &c2);
-
- build_test_tree(&a1, g_repo, "t|B|,t|b|", &b1, &b2);
-
- build_test_tree(&d1, g_repo, "b|08|,b|foo|", &blob_id, &blob_id);
- build_test_tree(&d2, g_repo, "b|07|,b|FOO|", &blob_id, &blob_id);
- build_test_tree(&c1, g_repo, "t|D|,t|d|", &d1, &d2);
- build_test_tree(&d1, g_repo, "b|06|,b|foo|", &blob_id, &blob_id);
- build_test_tree(&d2, g_repo, "b|05|,b|FOO|", &blob_id, &blob_id);
- build_test_tree(&c2, g_repo, "t|D|,t|d|", &d1, &d2);
- build_test_tree(&b1, g_repo, "t|C|,t|c|", &c1, &c2);
-
- build_test_tree(&d1, g_repo, "b|04|,b|foo|", &blob_id, &blob_id);
- build_test_tree(&d2, g_repo, "b|03|,b|FOO|", &blob_id, &blob_id);
- build_test_tree(&c1, g_repo, "t|D|,t|d|", &d1, &d2);
- build_test_tree(&d1, g_repo, "b|02|,b|foo|", &blob_id, &blob_id);
- build_test_tree(&d2, g_repo, "b|01|,b|FOO|", &blob_id, &blob_id);
- build_test_tree(&c2, g_repo, "t|D|,t|d|", &d1, &d2);
- build_test_tree(&b2, g_repo, "t|C|,t|c|", &c1, &c2);
-
- build_test_tree(&a2, g_repo, "t|B|,t|b|", &b1, &b2);
-
- build_test_tree(&tree_id, g_repo, "t/A/,t/a/", &a1, &a2);
-
- cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
-
- cl_git_pass(git_iterator_for_tree(
- &i, tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL));
- expect_iterator_items(i, 32, expect_cs, 32, expect_cs);
- git_iterator_free(i);
-
- cl_git_pass(git_iterator_for_tree(
- &i, tree, GIT_ITERATOR_IGNORE_CASE, NULL, NULL));
- expect_iterator_items(i, 17, expect_ci, 17, expect_ci);
- git_iterator_free(i);
-
- cl_git_pass(git_iterator_for_tree(
- &i, tree, GIT_ITERATOR_IGNORE_CASE |
- GIT_ITERATOR_INCLUDE_TREES, NULL, NULL));
- expect_iterator_items(i, 21, expect_ci_trees, 21, expect_ci_trees);
- git_iterator_free(i);
-
- git_tree_free(tree);
-}
-
-void test_repo_iterator__workdir(void)
-{
- git_iterator *i;
-
- g_repo = cl_git_sandbox_init("icase");
-
- /* auto expand with no tree entries */
- cl_git_pass(git_iterator_for_workdir(&i, g_repo, 0, NULL, NULL));
- expect_iterator_items(i, 20, NULL, 20, NULL);
- git_iterator_free(i);
-
- /* auto expand with tree entries */
- cl_git_pass(git_iterator_for_workdir(
- &i, g_repo, GIT_ITERATOR_INCLUDE_TREES, NULL, NULL));
- expect_iterator_items(i, 22, NULL, 22, NULL);
- git_iterator_free(i);
-
- /* no auto expand (implies trees included) */
- cl_git_pass(git_iterator_for_workdir(
- &i, g_repo, GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL));
- expect_iterator_items(i, 12, NULL, 22, NULL);
- git_iterator_free(i);
-}
-
-void test_repo_iterator__workdir_icase(void)
-{
- git_iterator *i;
- git_iterator_flag_t flag;
-
- g_repo = cl_git_sandbox_init("icase");
-
- flag = GIT_ITERATOR_DONT_IGNORE_CASE;
-
- /* auto expand with no tree entries */
- cl_git_pass(git_iterator_for_workdir(&i, g_repo, flag, "c", "k/D"));
- expect_iterator_items(i, 7, NULL, 7, NULL);
- git_iterator_free(i);
-
- cl_git_pass(git_iterator_for_workdir(&i, g_repo, flag, "k", "k/Z"));
- expect_iterator_items(i, 3, NULL, 3, NULL);
- git_iterator_free(i);
-
- /* auto expand with tree entries */
- cl_git_pass(git_iterator_for_workdir(
- &i, g_repo, flag | GIT_ITERATOR_INCLUDE_TREES, "c", "k/D"));
- expect_iterator_items(i, 8, NULL, 8, NULL);
- git_iterator_free(i);
-
- cl_git_pass(git_iterator_for_workdir(
- &i, g_repo, flag | GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z"));
- expect_iterator_items(i, 4, NULL, 4, NULL);
- git_iterator_free(i);
-
- /* no auto expand (implies trees included) */
- cl_git_pass(git_iterator_for_workdir(
- &i, g_repo, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D"));
- expect_iterator_items(i, 5, NULL, 8, NULL);
- git_iterator_free(i);
-
- cl_git_pass(git_iterator_for_workdir(
- &i, g_repo, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z"));
- expect_iterator_items(i, 1, NULL, 4, NULL);
- git_iterator_free(i);
-
- flag = GIT_ITERATOR_IGNORE_CASE;
-
- /* auto expand with no tree entries */
- cl_git_pass(git_iterator_for_workdir(&i, g_repo, flag, "c", "k/D"));
- expect_iterator_items(i, 13, NULL, 13, NULL);
- git_iterator_free(i);
-
- cl_git_pass(git_iterator_for_workdir(&i, g_repo, flag, "k", "k/Z"));
- expect_iterator_items(i, 5, NULL, 5, NULL);
- git_iterator_free(i);
-
- /* auto expand with tree entries */
- cl_git_pass(git_iterator_for_workdir(
- &i, g_repo, flag | GIT_ITERATOR_INCLUDE_TREES, "c", "k/D"));
- expect_iterator_items(i, 14, NULL, 14, NULL);
- git_iterator_free(i);
-
- cl_git_pass(git_iterator_for_workdir(
- &i, g_repo, flag | GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z"));
- expect_iterator_items(i, 6, NULL, 6, NULL);
- git_iterator_free(i);
-
- /* no auto expand (implies trees included) */
- cl_git_pass(git_iterator_for_workdir(
- &i, g_repo, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D"));
- expect_iterator_items(i, 9, NULL, 14, NULL);
- git_iterator_free(i);
-
- cl_git_pass(git_iterator_for_workdir(
- &i, g_repo, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z"));
- expect_iterator_items(i, 1, NULL, 6, NULL);
- git_iterator_free(i);
-}
-
-static void build_workdir_tree(const char *root, int dirs, int subs)
-{
- int i, j;
- char buf[64], sub[64];
-
- for (i = 0; i < dirs; ++i) {
- if (i % 2 == 0) {
- p_snprintf(buf, sizeof(buf), "%s/dir%02d", root, i);
- cl_git_pass(git_futils_mkdir(buf, NULL, 0775, GIT_MKDIR_PATH));
-
- p_snprintf(buf, sizeof(buf), "%s/dir%02d/file", root, i);
- cl_git_mkfile(buf, buf);
- buf[strlen(buf) - 5] = '\0';
- } else {
- p_snprintf(buf, sizeof(buf), "%s/DIR%02d", root, i);
- cl_git_pass(git_futils_mkdir(buf, NULL, 0775, GIT_MKDIR_PATH));
- }
-
- for (j = 0; j < subs; ++j) {
- switch (j % 4) {
- case 0: p_snprintf(sub, sizeof(sub), "%s/sub%02d", buf, j); break;
- case 1: p_snprintf(sub, sizeof(sub), "%s/sUB%02d", buf, j); break;
- case 2: p_snprintf(sub, sizeof(sub), "%s/Sub%02d", buf, j); break;
- case 3: p_snprintf(sub, sizeof(sub), "%s/SUB%02d", buf, j); break;
- }
- cl_git_pass(git_futils_mkdir(sub, NULL, 0775, GIT_MKDIR_PATH));
-
- if (j % 2 == 0) {
- size_t sublen = strlen(sub);
- memcpy(&sub[sublen], "/file", sizeof("/file"));
- cl_git_mkfile(sub, sub);
- sub[sublen] = '\0';
- }
- }
- }
-}
-
-void test_repo_iterator__workdir_depth(void)
-{
- git_iterator *iter;
-
- g_repo = cl_git_sandbox_init("icase");
-
- build_workdir_tree("icase", 10, 10);
- build_workdir_tree("icase/DIR01/sUB01", 50, 0);
- build_workdir_tree("icase/dir02/sUB01", 50, 0);
-
- /* auto expand with no tree entries */
- cl_git_pass(git_iterator_for_workdir(&iter, g_repo, 0, NULL, NULL));
- expect_iterator_items(iter, 125, NULL, 125, NULL);
- git_iterator_free(iter);
-
- /* auto expand with tree entries (empty dirs silently skipped) */
- cl_git_pass(git_iterator_for_workdir(
- &iter, g_repo, GIT_ITERATOR_INCLUDE_TREES, NULL, NULL));
- expect_iterator_items(iter, 337, NULL, 337, NULL);
- git_iterator_free(iter);
-}
-
-void test_repo_iterator__fs(void)
-{
- git_iterator *i;
- static const char *expect_base[] = {
- "DIR01/Sub02/file",
- "DIR01/sub00/file",
- "current_file",
- "dir00/Sub02/file",
- "dir00/file",
- "dir00/sub00/file",
- "modified_file",
- "new_file",
- NULL,
- };
- static const char *expect_trees[] = {
- "DIR01/",
- "DIR01/SUB03/",
- "DIR01/Sub02/",
- "DIR01/Sub02/file",
- "DIR01/sUB01/",
- "DIR01/sub00/",
- "DIR01/sub00/file",
- "current_file",
- "dir00/",
- "dir00/SUB03/",
- "dir00/Sub02/",
- "dir00/Sub02/file",
- "dir00/file",
- "dir00/sUB01/",
- "dir00/sub00/",
- "dir00/sub00/file",
- "modified_file",
- "new_file",
- NULL,
- };
- static const char *expect_noauto[] = {
- "DIR01/",
- "current_file",
- "dir00/",
- "modified_file",
- "new_file",
- NULL,
- };
-
- g_repo = cl_git_sandbox_init("status");
-
- build_workdir_tree("status/subdir", 2, 4);
-
- cl_git_pass(git_iterator_for_filesystem(
- &i, "status/subdir", 0, NULL, NULL));
- expect_iterator_items(i, 8, expect_base, 8, expect_base);
- git_iterator_free(i);
-
- cl_git_pass(git_iterator_for_filesystem(
- &i, "status/subdir", GIT_ITERATOR_INCLUDE_TREES, NULL, NULL));
- expect_iterator_items(i, 18, expect_trees, 18, expect_trees);
- git_iterator_free(i);
-
- cl_git_pass(git_iterator_for_filesystem(
- &i, "status/subdir", GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL));
- expect_iterator_items(i, 5, expect_noauto, 18, expect_trees);
- git_iterator_free(i);
-
- git__tsort((void **)expect_base, 8, (git__tsort_cmp)git__strcasecmp);
- git__tsort((void **)expect_trees, 18, (git__tsort_cmp)git__strcasecmp);
- git__tsort((void **)expect_noauto, 5, (git__tsort_cmp)git__strcasecmp);
-
- cl_git_pass(git_iterator_for_filesystem(
- &i, "status/subdir", GIT_ITERATOR_IGNORE_CASE, NULL, NULL));
- expect_iterator_items(i, 8, expect_base, 8, expect_base);
- git_iterator_free(i);
-
- cl_git_pass(git_iterator_for_filesystem(
- &i, "status/subdir", GIT_ITERATOR_IGNORE_CASE |
- GIT_ITERATOR_INCLUDE_TREES, NULL, NULL));
- expect_iterator_items(i, 18, expect_trees, 18, expect_trees);
- git_iterator_free(i);
-
- cl_git_pass(git_iterator_for_filesystem(
- &i, "status/subdir", GIT_ITERATOR_IGNORE_CASE |
- GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL));
- expect_iterator_items(i, 5, expect_noauto, 18, expect_trees);
- git_iterator_free(i);
-}
-
-void test_repo_iterator__fs2(void)
-{
- git_iterator *i;
- static const char *expect_base[] = {
- "heads/br2",
- "heads/dir",
- "heads/master",
- "heads/packed-test",
- "heads/subtrees",
- "heads/test",
- "tags/e90810b",
- "tags/foo/bar",
- "tags/foo/foo/bar",
- "tags/point_to_blob",
- "tags/test",
- NULL,
- };
-
- g_repo = cl_git_sandbox_init("testrepo");
-
- cl_git_pass(git_iterator_for_filesystem(
- &i, "testrepo/.git/refs", 0, NULL, NULL));
- expect_iterator_items(i, 11, expect_base, 11, expect_base);
- git_iterator_free(i);
-}
diff --git a/tests-clar/repo/open.c b/tests-clar/repo/open.c
deleted file mode 100644
index 840858586..000000000
--- a/tests-clar/repo/open.c
+++ /dev/null
@@ -1,317 +0,0 @@
-#include "clar_libgit2.h"
-#include "fileops.h"
-#include <ctype.h>
-
-void test_repo_open__cleanup(void)
-{
- cl_git_sandbox_cleanup();
-
- if (git_path_isdir("alternate"))
- git_futils_rmdir_r("alternate", NULL, GIT_RMDIR_REMOVE_FILES);
-}
-
-void test_repo_open__bare_empty_repo(void)
-{
- git_repository *repo = cl_git_sandbox_init("empty_bare.git");
-
- cl_assert(git_repository_path(repo) != NULL);
- cl_assert(git__suffixcmp(git_repository_path(repo), "/") == 0);
- cl_assert(git_repository_workdir(repo) == NULL);
-}
-
-void test_repo_open__standard_empty_repo_through_gitdir(void)
-{
- git_repository *repo;
-
- cl_git_pass(git_repository_open(&repo, cl_fixture("empty_standard_repo/.gitted")));
-
- cl_assert(git_repository_path(repo) != NULL);
- cl_assert(git__suffixcmp(git_repository_path(repo), "/") == 0);
-
- cl_assert(git_repository_workdir(repo) != NULL);
- cl_assert(git__suffixcmp(git_repository_workdir(repo), "/") == 0);
-
- git_repository_free(repo);
-}
-
-void test_repo_open__standard_empty_repo_through_workdir(void)
-{
- git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
-
- cl_assert(git_repository_path(repo) != NULL);
- cl_assert(git__suffixcmp(git_repository_path(repo), "/") == 0);
-
- cl_assert(git_repository_workdir(repo) != NULL);
- cl_assert(git__suffixcmp(git_repository_workdir(repo), "/") == 0);
-}
-
-
-void test_repo_open__open_with_discover(void)
-{
- static const char *variants[] = {
- "attr", "attr/", "attr/.git", "attr/.git/",
- "attr/sub", "attr/sub/", "attr/sub/sub", "attr/sub/sub/",
- NULL
- };
- git_repository *repo;
- const char **scan;
-
- cl_fixture_sandbox("attr");
- cl_git_pass(p_rename("attr/.gitted", "attr/.git"));
-
- for (scan = variants; *scan != NULL; scan++) {
- cl_git_pass(git_repository_open_ext(&repo, *scan, 0, NULL));
- cl_assert(git__suffixcmp(git_repository_path(repo), "attr/.git/") == 0);
- cl_assert(git__suffixcmp(git_repository_workdir(repo), "attr/") == 0);
- git_repository_free(repo);
- }
-
- cl_fixture_cleanup("attr");
-}
-
-void test_repo_open__gitlinked(void)
-{
- /* need to have both repo dir and workdir set up correctly */
- git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
- git_repository *repo2;
-
- cl_must_pass(p_mkdir("alternate", 0777));
- cl_git_mkfile("alternate/.git", "gitdir: ../empty_standard_repo/.git");
-
- cl_git_pass(git_repository_open(&repo2, "alternate"));
-
- cl_assert(git_repository_path(repo2) != NULL);
- cl_assert_(git__suffixcmp(git_repository_path(repo2), "empty_standard_repo/.git/") == 0, git_repository_path(repo2));
- cl_assert_equal_s(git_repository_path(repo), git_repository_path(repo2));
-
- cl_assert(git_repository_workdir(repo2) != NULL);
- cl_assert_(git__suffixcmp(git_repository_workdir(repo2), "alternate/") == 0, git_repository_workdir(repo2));
-
- git_repository_free(repo2);
-}
-
-void test_repo_open__from_git_new_workdir(void)
-{
- /* The git-new-workdir script that ships with git sets up a bunch of
- * symlinks to create a second workdir that shares the object db with
- * another checkout. Libgit2 can open a repo that has been configured
- * this way.
- */
- cl_git_sandbox_init("empty_standard_repo");
-
-#ifndef GIT_WIN32
- git_repository *repo2;
- git_buf link_tgt = GIT_BUF_INIT, link = GIT_BUF_INIT, body = GIT_BUF_INIT;
- const char **scan;
- int link_fd;
- static const char *links[] = {
- "config", "refs", "logs/refs", "objects", "info", "hooks",
- "packed-refs", "remotes", "rr-cache", "svn", NULL
- };
- static const char *copies[] = {
- "HEAD", NULL
- };
-
- cl_git_pass(p_mkdir("alternate", 0777));
- cl_git_pass(p_mkdir("alternate/.git", 0777));
-
- for (scan = links; *scan != NULL; scan++) {
- git_buf_joinpath(&link_tgt, "empty_standard_repo/.git", *scan);
- if (git_path_exists(link_tgt.ptr)) {
- git_buf_joinpath(&link_tgt, "../../empty_standard_repo/.git", *scan);
- git_buf_joinpath(&link, "alternate/.git", *scan);
- if (strchr(*scan, '/'))
- git_futils_mkpath2file(link.ptr, 0777);
- cl_assert_(symlink(link_tgt.ptr, link.ptr) == 0, strerror(errno));
- }
- }
- for (scan = copies; *scan != NULL; scan++) {
- git_buf_joinpath(&link_tgt, "empty_standard_repo/.git", *scan);
- if (git_path_exists(link_tgt.ptr)) {
- git_buf_joinpath(&link, "alternate/.git", *scan);
- cl_git_pass(git_futils_readbuffer(&body, link_tgt.ptr));
-
- cl_assert((link_fd = git_futils_creat_withpath(link.ptr, 0777, 0666)) >= 0);
- cl_must_pass(p_write(link_fd, body.ptr, body.size));
- p_close(link_fd);
- }
- }
-
- git_buf_free(&link_tgt);
- git_buf_free(&link);
- git_buf_free(&body);
-
-
- cl_git_pass(git_repository_open(&repo2, "alternate"));
-
- cl_assert(git_repository_path(repo2) != NULL);
- cl_assert_(git__suffixcmp(git_repository_path(repo2), "alternate/.git/") == 0, git_repository_path(repo2));
-
- cl_assert(git_repository_workdir(repo2) != NULL);
- cl_assert_(git__suffixcmp(git_repository_workdir(repo2), "alternate/") == 0, git_repository_workdir(repo2));
-
- git_repository_free(repo2);
-#endif
-}
-
-void test_repo_open__failures(void)
-{
- git_repository *base, *repo;
- git_buf ceiling = GIT_BUF_INIT;
-
- base = cl_git_sandbox_init("attr");
- cl_git_pass(git_buf_sets(&ceiling, git_repository_workdir(base)));
-
- /* fail with no searching */
- cl_git_fail(git_repository_open(&repo, "attr/sub"));
- cl_git_fail(git_repository_open_ext(
- &repo, "attr/sub", GIT_REPOSITORY_OPEN_NO_SEARCH, NULL));
-
- /* fail with ceiling too low */
- cl_git_pass(git_buf_joinpath(&ceiling, ceiling.ptr, "sub"));
- cl_git_fail(git_repository_open_ext(&repo, "attr/sub", 0, ceiling.ptr));
-
- /* fail with no repo */
- cl_git_pass(p_mkdir("alternate", 0777));
- cl_git_pass(p_mkdir("alternate/.git", 0777));
- cl_git_fail(git_repository_open_ext(&repo, "alternate", 0, NULL));
- cl_git_fail(git_repository_open_ext(&repo, "alternate/.git", 0, NULL));
-
- git_buf_free(&ceiling);
-}
-
-void test_repo_open__bad_gitlinks(void)
-{
- git_repository *repo;
- static const char *bad_links[] = {
- "garbage\n", "gitdir", "gitdir:\n", "gitdir: foobar",
- "gitdir: ../invalid", "gitdir: ../invalid2",
- "gitdir: ../attr/.git with extra stuff",
- NULL
- };
- const char **scan;
-
- cl_git_sandbox_init("attr");
-
- cl_git_pass(p_mkdir("alternate", 0777));
- cl_git_pass(p_mkdir("invalid", 0777));
- cl_git_pass(git_futils_mkdir_r("invalid2/.git", NULL, 0777));
-
- for (scan = bad_links; *scan != NULL; scan++) {
- cl_git_rewritefile("alternate/.git", *scan);
- cl_git_fail(git_repository_open_ext(&repo, "alternate", 0, NULL));
- }
-
- git_futils_rmdir_r("invalid", NULL, GIT_RMDIR_REMOVE_FILES);
- git_futils_rmdir_r("invalid2", NULL, GIT_RMDIR_REMOVE_FILES);
-}
-
-#ifdef GIT_WIN32
-static void unposix_path(git_buf *path)
-{
- char *src, *tgt;
-
- src = tgt = path->ptr;
-
- /* convert "/d/..." to "d:\..." */
- if (src[0] == '/' && isalpha(src[1]) && src[2] == '/') {
- *tgt++ = src[1];
- *tgt++ = ':';
- *tgt++ = '\\';
- src += 3;
- }
-
- while (*src) {
- *tgt++ = (*src == '/') ? '\\' : *src;
- src++;
- }
-
- *tgt = '\0';
-}
-#endif
-
-void test_repo_open__win32_path(void)
-{
-#ifdef GIT_WIN32
- git_repository *repo = cl_git_sandbox_init("empty_standard_repo"), *repo2;
- git_buf winpath = GIT_BUF_INIT;
- static const char *repo_path = "empty_standard_repo/.git/";
- static const char *repo_wd = "empty_standard_repo/";
-
- cl_assert(git__suffixcmp(git_repository_path(repo), repo_path) == 0);
- cl_assert(git__suffixcmp(git_repository_workdir(repo), repo_wd) == 0);
-
- cl_git_pass(git_buf_sets(&winpath, git_repository_path(repo)));
- unposix_path(&winpath);
- cl_git_pass(git_repository_open(&repo2, winpath.ptr));
- cl_assert(git__suffixcmp(git_repository_path(repo2), repo_path) == 0);
- cl_assert(git__suffixcmp(git_repository_workdir(repo2), repo_wd) == 0);
- git_repository_free(repo2);
-
- cl_git_pass(git_buf_sets(&winpath, git_repository_path(repo)));
- git_buf_truncate(&winpath, winpath.size - 1); /* remove trailing '/' */
- unposix_path(&winpath);
- cl_git_pass(git_repository_open(&repo2, winpath.ptr));
- cl_assert(git__suffixcmp(git_repository_path(repo2), repo_path) == 0);
- cl_assert(git__suffixcmp(git_repository_workdir(repo2), repo_wd) == 0);
- git_repository_free(repo2);
-
- cl_git_pass(git_buf_sets(&winpath, git_repository_workdir(repo)));
- unposix_path(&winpath);
- cl_git_pass(git_repository_open(&repo2, winpath.ptr));
- cl_assert(git__suffixcmp(git_repository_path(repo2), repo_path) == 0);
- cl_assert(git__suffixcmp(git_repository_workdir(repo2), repo_wd) == 0);
- git_repository_free(repo2);
-
- cl_git_pass(git_buf_sets(&winpath, git_repository_workdir(repo)));
- git_buf_truncate(&winpath, winpath.size - 1); /* remove trailing '/' */
- unposix_path(&winpath);
- cl_git_pass(git_repository_open(&repo2, winpath.ptr));
- cl_assert(git__suffixcmp(git_repository_path(repo2), repo_path) == 0);
- cl_assert(git__suffixcmp(git_repository_workdir(repo2), repo_wd) == 0);
- git_repository_free(repo2);
-
- git_buf_free(&winpath);
-#endif
-}
-
-void test_repo_open__opening_a_non_existing_repository_returns_ENOTFOUND(void)
-{
- git_repository *repo;
- cl_assert_equal_i(GIT_ENOTFOUND, git_repository_open(&repo, "i-do-not/exist"));
-}
-
-void test_repo_open__no_config(void)
-{
- git_buf path = GIT_BUF_INIT;
- git_repository *repo;
- git_config *config;
-
- cl_fixture_sandbox("empty_standard_repo");
- cl_git_pass(cl_rename("empty_standard_repo/.gitted", "empty_standard_repo/.git"));
-
- /* remove local config */
- cl_git_pass(git_futils_rmdir_r(
- "empty_standard_repo/.git/config", NULL, GIT_RMDIR_REMOVE_FILES));
-
- /* isolate from system level configs */
- cl_must_pass(p_mkdir("alternate", 0777));
- cl_git_pass(git_path_prettify(&path, "alternate", NULL));
- cl_git_pass(git_libgit2_opts(
- GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
- cl_git_pass(git_libgit2_opts(
- GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr));
- cl_git_pass(git_libgit2_opts(
- GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr));
-
- git_buf_free(&path);
-
- cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
- cl_git_pass(git_repository_config(&config, repo));
-
- cl_git_pass(git_config_set_string(config, "test.set", "42"));
-
- git_config_free(config);
- git_repository_free(repo);
- cl_fixture_cleanup("empty_standard_repo");
-}
diff --git a/tests-clar/repo/repo_helpers.c b/tests-clar/repo/repo_helpers.c
deleted file mode 100644
index 74902e439..000000000
--- a/tests-clar/repo/repo_helpers.c
+++ /dev/null
@@ -1,22 +0,0 @@
-#include "clar_libgit2.h"
-#include "refs.h"
-#include "repo_helpers.h"
-#include "posix.h"
-
-void make_head_orphaned(git_repository* repo, const char *target)
-{
- git_reference *head;
-
- cl_git_pass(git_reference_symbolic_create(&head, repo, GIT_HEAD_FILE, target, 1));
- git_reference_free(head);
-}
-
-void delete_head(git_repository* repo)
-{
- git_buf head_path = GIT_BUF_INIT;
-
- cl_git_pass(git_buf_joinpath(&head_path, git_repository_path(repo), GIT_HEAD_FILE));
- cl_git_pass(p_unlink(git_buf_cstr(&head_path)));
-
- git_buf_free(&head_path);
-}
diff --git a/tests-clar/repo/repo_helpers.h b/tests-clar/repo/repo_helpers.h
deleted file mode 100644
index 09b5cac84..000000000
--- a/tests-clar/repo/repo_helpers.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#include "common.h"
-
-#define NON_EXISTING_HEAD "refs/heads/hide/and/seek"
-
-extern void make_head_orphaned(git_repository* repo, const char *target);
-extern void delete_head(git_repository* repo);
diff --git a/tests-clar/reset/soft.c b/tests-clar/reset/soft.c
deleted file mode 100644
index 884697c91..000000000
--- a/tests-clar/reset/soft.c
+++ /dev/null
@@ -1,157 +0,0 @@
-#include "clar_libgit2.h"
-#include "posix.h"
-#include "reset_helpers.h"
-#include "path.h"
-#include "repo/repo_helpers.h"
-
-static git_repository *repo;
-static git_object *target;
-
-void test_reset_soft__initialize(void)
-{
- repo = cl_git_sandbox_init("testrepo.git");
-}
-
-void test_reset_soft__cleanup(void)
-{
- git_object_free(target);
- target = NULL;
-
- cl_git_sandbox_cleanup();
-}
-
-static void assert_reset_soft(bool should_be_detached)
-{
- git_oid oid;
-
- cl_git_pass(git_reference_name_to_id(&oid, repo, "HEAD"));
- cl_git_fail(git_oid_streq(&oid, KNOWN_COMMIT_IN_BARE_REPO));
-
- retrieve_target_from_oid(&target, repo, KNOWN_COMMIT_IN_BARE_REPO);
-
- cl_assert(git_repository_head_detached(repo) == should_be_detached);
-
- cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT));
-
- cl_assert(git_repository_head_detached(repo) == should_be_detached);
-
- cl_git_pass(git_reference_name_to_id(&oid, repo, "HEAD"));
- cl_git_pass(git_oid_streq(&oid, KNOWN_COMMIT_IN_BARE_REPO));
-}
-
-void test_reset_soft__can_reset_the_non_detached_Head_to_the_specified_commit(void)
-{
- assert_reset_soft(false);
-}
-
-void test_reset_soft__can_reset_the_detached_Head_to_the_specified_commit(void)
-{
- git_repository_detach_head(repo);
-
- assert_reset_soft(true);
-}
-
-void test_reset_soft__resetting_to_the_commit_pointed_at_by_the_Head_does_not_change_the_target_of_the_Head(void)
-{
- git_oid oid;
- char raw_head_oid[GIT_OID_HEXSZ + 1];
-
- cl_git_pass(git_reference_name_to_id(&oid, repo, "HEAD"));
- git_oid_fmt(raw_head_oid, &oid);
- raw_head_oid[GIT_OID_HEXSZ] = '\0';
-
- retrieve_target_from_oid(&target, repo, raw_head_oid);
-
- cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT));
-
- cl_git_pass(git_reference_name_to_id(&oid, repo, "HEAD"));
- cl_git_pass(git_oid_streq(&oid, raw_head_oid));
-}
-
-void test_reset_soft__resetting_to_a_tag_sets_the_Head_to_the_peeled_commit(void)
-{
- git_oid oid;
-
- /* b25fa35 is a tag, pointing to another tag which points to commit e90810b */
- retrieve_target_from_oid(&target, repo, "b25fa35b38051e4ae45d4222e795f9df2e43f1d1");
-
- cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT));
-
- cl_assert(git_repository_head_detached(repo) == false);
- cl_git_pass(git_reference_name_to_id(&oid, repo, "HEAD"));
- cl_git_pass(git_oid_streq(&oid, KNOWN_COMMIT_IN_BARE_REPO));
-}
-
-void test_reset_soft__cannot_reset_to_a_tag_not_pointing_at_a_commit(void)
-{
- /* 53fc32d is the tree of commit e90810b */
- retrieve_target_from_oid(&target, repo, "53fc32d17276939fc79ed05badaef2db09990016");
-
- cl_git_fail(git_reset(repo, target, GIT_RESET_SOFT));
- git_object_free(target);
-
- /* 521d87c is an annotated tag pointing to a blob */
- retrieve_target_from_oid(&target, repo, "521d87c1ec3aef9824daf6d96cc0ae3710766d91");
- cl_git_fail(git_reset(repo, target, GIT_RESET_SOFT));
-}
-
-void test_reset_soft__resetting_against_an_orphaned_head_repo_makes_the_head_no_longer_orphaned(void)
-{
- git_reference *head;
-
- retrieve_target_from_oid(&target, repo, KNOWN_COMMIT_IN_BARE_REPO);
-
- make_head_orphaned(repo, NON_EXISTING_HEAD);
-
- cl_assert_equal_i(true, git_repository_head_orphan(repo));
-
- cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT));
-
- cl_assert_equal_i(false, git_repository_head_orphan(repo));
-
- cl_git_pass(git_reference_lookup(&head, repo, NON_EXISTING_HEAD));
- cl_assert_equal_i(0, git_oid_streq(git_reference_target(head), KNOWN_COMMIT_IN_BARE_REPO));
-
- git_reference_free(head);
-}
-
-void test_reset_soft__fails_when_merging(void)
-{
- git_buf merge_head_path = GIT_BUF_INIT;
-
- cl_git_pass(git_repository_detach_head(repo));
- cl_git_pass(git_buf_joinpath(&merge_head_path, git_repository_path(repo), "MERGE_HEAD"));
- cl_git_mkfile(git_buf_cstr(&merge_head_path), "beefbeefbeefbeefbeefbeefbeefbeefbeefbeef\n");
-
- retrieve_target_from_oid(&target, repo, KNOWN_COMMIT_IN_BARE_REPO);
-
- cl_assert_equal_i(GIT_EUNMERGED, git_reset(repo, target, GIT_RESET_SOFT));
- cl_git_pass(p_unlink(git_buf_cstr(&merge_head_path)));
-
- git_buf_free(&merge_head_path);
-}
-
-void test_reset_soft__fails_when_index_contains_conflicts_independently_of_MERGE_HEAD_file_existence(void)
-{
- git_index *index;
- git_reference *head;
- git_buf merge_head_path = GIT_BUF_INIT;
-
- cl_git_sandbox_cleanup();
-
- repo = cl_git_sandbox_init("mergedrepo");
-
- cl_git_pass(git_buf_joinpath(&merge_head_path, git_repository_path(repo), "MERGE_HEAD"));
- cl_git_pass(p_unlink(git_buf_cstr(&merge_head_path)));
- git_buf_free(&merge_head_path);
-
- cl_git_pass(git_repository_index(&index, repo));
- cl_assert_equal_i(true, git_index_has_conflicts(index));
- git_index_free(index);
-
- cl_git_pass(git_repository_head(&head, repo));
- cl_git_pass(git_reference_peel(&target, head, GIT_OBJ_COMMIT));
- git_reference_free(head);
-
- cl_assert_equal_i(GIT_EUNMERGED, git_reset(repo, target, GIT_RESET_SOFT));
-}
diff --git a/tests-clar/resources/config/config11 b/tests-clar/resources/config/config11
deleted file mode 100644
index 7331862a5..000000000
--- a/tests-clar/resources/config/config11
+++ /dev/null
@@ -1,5 +0,0 @@
-[remote "fancy"]
- url = git://github.com/libgit2/libgit2
- url = git://git.example.com/libgit2
-
-
diff --git a/tests-clar/resources/duplicate.git/config b/tests-clar/resources/duplicate.git/config
deleted file mode 100644
index 515f48362..000000000
--- a/tests-clar/resources/duplicate.git/config
+++ /dev/null
@@ -1,5 +0,0 @@
-[core]
- repositoryformatversion = 0
- filemode = true
- bare = false
- logallrefupdates = true
diff --git a/tests-clar/resources/duplicate.git/objects/info/packs b/tests-clar/resources/duplicate.git/objects/info/packs
deleted file mode 100644
index 3696a7d36..000000000
--- a/tests-clar/resources/duplicate.git/objects/info/packs
+++ /dev/null
@@ -1,2 +0,0 @@
-P pack-e87994ad581c9af946de0eb890175c08cd005f38.pack
-
diff --git a/tests-clar/resources/submodules/gitmodules b/tests-clar/resources/submodules/gitmodules
deleted file mode 100644
index 1262f8bb0..000000000
--- a/tests-clar/resources/submodules/gitmodules
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "testrepo"]
- path = testrepo
- url = \ No newline at end of file
diff --git a/tests-clar/resources/testrepo.git/config b/tests-clar/resources/testrepo.git/config
deleted file mode 100644
index 904a4e3f3..000000000
--- a/tests-clar/resources/testrepo.git/config
+++ /dev/null
@@ -1,36 +0,0 @@
-[core]
- repositoryformatversion = 0
- filemode = true
- bare = true
- logallrefupdates = true
-[remote "test"]
- url = git://github.com/libgit2/libgit2
- fetch = +refs/heads/*:refs/remotes/test/*
-[remote "joshaber"]
- url = git://github.com/libgit2/libgit2
-[remote "empty-remote-url"]
- url =
-
-[remote "test_with_pushurl"]
- url = git://github.com/libgit2/fetchlibgit2
- pushurl = git://github.com/libgit2/pushlibgit2
- fetch = +refs/heads/*:refs/remotes/test_with_pushurl/*
-
-[branch "master"]
- remote = test
- merge = refs/heads/master
-[branch "track-local"]
- remote = .
- merge = refs/heads/master
-[branch "cannot-fetch"]
- remote = joshaber
- merge = refs/heads/cannot-fetch
-[branch "remoteless"]
- remote =
- merge = refs/heads/master
-[branch "mergeless"]
- remote = test
- merge =
-[branch "mergeandremoteless"]
- remote =
- merge =
diff --git a/tests-clar/revwalk/basic.c b/tests-clar/revwalk/basic.c
deleted file mode 100644
index e82776260..000000000
--- a/tests-clar/revwalk/basic.c
+++ /dev/null
@@ -1,206 +0,0 @@
-#include "clar_libgit2.h"
-
-/*
- * a4a7dce [0] Merge branch 'master' into br2
- |\
- | * 9fd738e [1] a fourth commit
- | * 4a202b3 [2] a third commit
- * | c47800c [3] branch commit one
- |/
- * 5b5b025 [5] another commit
- * 8496071 [4] testing
-*/
-static const char *commit_head = "a4a7dce85cf63874e984719f4fdd239f5145052f";
-
-static const char *commit_ids[] = {
- "a4a7dce85cf63874e984719f4fdd239f5145052f", /* 0 */
- "9fd738e8f7967c078dceed8190330fc8648ee56a", /* 1 */
- "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", /* 2 */
- "c47800c7266a2be04c571c04d5a6614691ea99bd", /* 3 */
- "8496071c1b46c854b31185ea97743be6a8774479", /* 4 */
- "5b5b025afb0b4c913b4c338a42934a3863bf3644", /* 5 */
-};
-
-/* Careful: there are two possible topological sorts */
-static const int commit_sorting_topo[][6] = {
- {0, 1, 2, 3, 5, 4}, {0, 3, 1, 2, 5, 4}
-};
-
-static const int commit_sorting_time[][6] = {
- {0, 3, 1, 2, 5, 4}
-};
-
-static const int commit_sorting_topo_reverse[][6] = {
- {4, 5, 3, 2, 1, 0}, {4, 5, 2, 1, 3, 0}
-};
-
-static const int commit_sorting_time_reverse[][6] = {
- {4, 5, 2, 1, 3, 0}
-};
-
-static const int commit_sorting_segment[][6] = {
- {1, 2, -1, -1, -1, -1}
-};
-
-#define commit_count 6
-static const int result_bytes = 24;
-
-
-static int get_commit_index(git_oid *raw_oid)
-{
- int i;
- char oid[40];
-
- git_oid_fmt(oid, raw_oid);
-
- for (i = 0; i < commit_count; ++i)
- if (memcmp(oid, commit_ids[i], 40) == 0)
- return i;
-
- return -1;
-}
-
-static int test_walk_only(git_revwalk *walk,
- const int possible_results[][commit_count], int results_count)
-{
- git_oid oid;
- int i;
- int result_array[commit_count];
-
- for (i = 0; i < commit_count; ++i)
- result_array[i] = -1;
-
- i = 0;
- while (git_revwalk_next(&oid, walk) == 0) {
- result_array[i++] = get_commit_index(&oid);
- /*{
- char str[41];
- git_oid_fmt(str, &oid);
- str[40] = 0;
- printf(" %d) %s\n", i, str);
- }*/
- }
-
- for (i = 0; i < results_count; ++i)
- if (memcmp(possible_results[i],
- result_array, result_bytes) == 0)
- return 0;
-
- return GIT_ERROR;
-}
-
-static int test_walk(git_revwalk *walk, const git_oid *root,
- int flags, const int possible_results[][6], int results_count)
-{
- git_revwalk_sorting(walk, flags);
- git_revwalk_push(walk, root);
-
- return test_walk_only(walk, possible_results, results_count);
-}
-
-static git_repository *_repo;
-static git_revwalk *_walk;
-
-void test_revwalk_basic__initialize(void)
-{
- cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git")));
- cl_git_pass(git_revwalk_new(&_walk, _repo));
-}
-
-void test_revwalk_basic__cleanup(void)
-{
- git_revwalk_free(_walk);
- _walk = NULL;
- git_repository_free(_repo);
- _repo = NULL;
-}
-
-void test_revwalk_basic__sorting_modes(void)
-{
- git_oid id;
-
- git_oid_fromstr(&id, commit_head);
-
- cl_git_pass(test_walk(_walk, &id, GIT_SORT_TIME, commit_sorting_time, 1));
- cl_git_pass(test_walk(_walk, &id, GIT_SORT_TOPOLOGICAL, commit_sorting_topo, 2));
- cl_git_pass(test_walk(_walk, &id, GIT_SORT_TIME | GIT_SORT_REVERSE, commit_sorting_time_reverse, 1));
- cl_git_pass(test_walk(_walk, &id, GIT_SORT_TOPOLOGICAL | GIT_SORT_REVERSE, commit_sorting_topo_reverse, 2));
-}
-
-void test_revwalk_basic__glob_heads(void)
-{
- int i = 0;
- git_oid oid;
-
- cl_git_pass(git_revwalk_push_glob(_walk, "heads"));
-
- while (git_revwalk_next(&oid, _walk) == 0) {
- i++;
- }
-
- /* git log --branches --oneline | wc -l => 14 */
- cl_assert(i == 14);
-}
-
-void test_revwalk_basic__push_head(void)
-{
- int i = 0;
- git_oid oid;
-
- cl_git_pass(git_revwalk_push_head(_walk));
-
- while (git_revwalk_next(&oid, _walk) == 0) {
- i++;
- }
-
- /* git log HEAD --oneline | wc -l => 7 */
- cl_assert(i == 7);
-}
-
-void test_revwalk_basic__push_head_hide_ref(void)
-{
- int i = 0;
- git_oid oid;
-
- cl_git_pass(git_revwalk_push_head(_walk));
- cl_git_pass(git_revwalk_hide_ref(_walk, "refs/heads/packed-test"));
-
- while (git_revwalk_next(&oid, _walk) == 0) {
- i++;
- }
-
- /* git log HEAD --oneline --not refs/heads/packed-test | wc -l => 4 */
- cl_assert(i == 4);
-}
-
-void test_revwalk_basic__push_head_hide_ref_nobase(void)
-{
- int i = 0;
- git_oid oid;
-
- cl_git_pass(git_revwalk_push_head(_walk));
- cl_git_pass(git_revwalk_hide_ref(_walk, "refs/heads/packed"));
-
- while (git_revwalk_next(&oid, _walk) == 0) {
- i++;
- }
-
- /* git log HEAD --oneline --not refs/heads/packed | wc -l => 7 */
- cl_assert(i == 7);
-}
-
-void test_revwalk_basic__disallow_non_commit(void)
-{
- git_oid oid;
-
- cl_git_pass(git_oid_fromstr(&oid, "521d87c1ec3aef9824daf6d96cc0ae3710766d91"));
- cl_git_fail(git_revwalk_push(_walk, &oid));
-}
-
-void test_revwalk_basic__push_range(void)
-{
- git_revwalk_reset(_walk);
- git_revwalk_sorting(_walk, 0);
- cl_git_pass(git_revwalk_push_range(_walk, "9fd738e~2..9fd738e"));
- cl_git_pass(test_walk_only(_walk, commit_sorting_segment, 1));
-}
diff --git a/tests-clar/revwalk/mergebase.c b/tests-clar/revwalk/mergebase.c
deleted file mode 100644
index e2617ab0e..000000000
--- a/tests-clar/revwalk/mergebase.c
+++ /dev/null
@@ -1,380 +0,0 @@
-#include "clar_libgit2.h"
-#include "vector.h"
-#include <stdarg.h>
-
-static git_repository *_repo;
-static git_repository *_repo2;
-
-void test_revwalk_mergebase__initialize(void)
-{
- cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git")));
- cl_git_pass(git_repository_open(&_repo2, cl_fixture("twowaymerge.git")));
-}
-
-void test_revwalk_mergebase__cleanup(void)
-{
- git_repository_free(_repo);
- _repo = NULL;
-
- git_repository_free(_repo2);
- _repo2 = NULL;
-}
-
-void test_revwalk_mergebase__single1(void)
-{
- git_oid result, one, two, expected;
- size_t ahead, behind;
-
- cl_git_pass(git_oid_fromstr(&one, "c47800c7266a2be04c571c04d5a6614691ea99bd "));
- cl_git_pass(git_oid_fromstr(&two, "9fd738e8f7967c078dceed8190330fc8648ee56a"));
- cl_git_pass(git_oid_fromstr(&expected, "5b5b025afb0b4c913b4c338a42934a3863bf3644"));
-
- cl_git_pass(git_merge_base(&result, _repo, &one, &two));
- cl_assert(git_oid_cmp(&result, &expected) == 0);
-
- cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, &one, &two));
- cl_assert_equal_sz(ahead, 2);
- cl_assert_equal_sz(behind, 1);
-
- cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, &two, &one));
- cl_assert_equal_sz(ahead, 1);
- cl_assert_equal_sz(behind, 2);
-}
-
-void test_revwalk_mergebase__single2(void)
-{
- git_oid result, one, two, expected;
- size_t ahead, behind;
-
- cl_git_pass(git_oid_fromstr(&one, "763d71aadf09a7951596c9746c024e7eece7c7af"));
- cl_git_pass(git_oid_fromstr(&two, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"));
- cl_git_pass(git_oid_fromstr(&expected, "c47800c7266a2be04c571c04d5a6614691ea99bd"));
-
- cl_git_pass(git_merge_base(&result, _repo, &one, &two));
- cl_assert(git_oid_cmp(&result, &expected) == 0);
-
- cl_git_pass(git_graph_ahead_behind( &ahead, &behind, _repo, &one, &two));
- cl_assert_equal_sz(ahead, 4);
- cl_assert_equal_sz(behind, 1);
-
- cl_git_pass(git_graph_ahead_behind( &ahead, &behind, _repo, &two, &one));
- cl_assert_equal_sz(ahead, 1);
- cl_assert_equal_sz(behind, 4);
-}
-
-void test_revwalk_mergebase__merged_branch(void)
-{
- git_oid result, one, two, expected;
- size_t ahead, behind;
-
- cl_git_pass(git_oid_fromstr(&one, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"));
- cl_git_pass(git_oid_fromstr(&two, "9fd738e8f7967c078dceed8190330fc8648ee56a"));
- cl_git_pass(git_oid_fromstr(&expected, "9fd738e8f7967c078dceed8190330fc8648ee56a"));
-
- cl_git_pass(git_merge_base(&result, _repo, &one, &two));
- cl_assert(git_oid_cmp(&result, &expected) == 0);
-
- cl_git_pass(git_merge_base(&result, _repo, &two, &one));
- cl_assert(git_oid_cmp(&result, &expected) == 0);
-
- cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, &one, &two));
- cl_assert_equal_sz(ahead, 0);
- cl_assert_equal_sz(behind, 3);
-
- cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, &two, &one));
- cl_assert_equal_sz(ahead, 3);
- cl_assert_equal_sz(behind, 0);
-}
-
-void test_revwalk_mergebase__two_way_merge(void)
-{
- git_oid one, two;
- size_t ahead, behind;
-
- cl_git_pass(git_oid_fromstr(&one, "9b219343610c88a1187c996d0dc58330b55cee28"));
- cl_git_pass(git_oid_fromstr(&two, "a953a018c5b10b20c86e69fef55ebc8ad4c5a417"));
- cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo2, &one, &two));
-
- cl_assert_equal_sz(ahead, 2);
- cl_assert_equal_sz(behind, 8);
-
- cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo2, &two, &one));
-
- cl_assert_equal_sz(ahead, 8);
- cl_assert_equal_sz(behind, 2);
-}
-
-void test_revwalk_mergebase__no_common_ancestor_returns_ENOTFOUND(void)
-{
- git_oid result, one, two;
- size_t ahead, behind;
- int error;
-
- cl_git_pass(git_oid_fromstr(&one, "763d71aadf09a7951596c9746c024e7eece7c7af"));
- cl_git_pass(git_oid_fromstr(&two, "e90810b8df3e80c413d903f631643c716887138d"));
-
- error = git_merge_base(&result, _repo, &one, &two);
- cl_git_fail(error);
-
- cl_assert_equal_i(GIT_ENOTFOUND, error);
-
- cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, &one, &two));
- cl_assert_equal_sz(2, ahead);
- cl_assert_equal_sz(4, behind);
-}
-
-void test_revwalk_mergebase__no_off_by_one_missing(void)
-{
- git_oid result, one, two;
-
- cl_git_pass(git_oid_fromstr(&one, "1a443023183e3f2bfbef8ac923cd81c1018a18fd"));
- cl_git_pass(git_oid_fromstr(&two, "9f13f7d0a9402c681f91dc590cf7b5470e6a77d2"));
- cl_git_pass(git_merge_base(&result, _repo, &one, &two));
-}
-
-static void assert_mergebase_many(const char *expected_sha, int count, ...)
-{
- va_list ap;
- int i;
- git_oid *oids;
- git_oid oid, expected;
- char *partial_oid;
- git_object *object;
-
- oids = git__malloc(count * sizeof(git_oid));
- cl_assert(oids != NULL);
-
- memset(oids, 0x0, count * sizeof(git_oid));
-
- va_start(ap, count);
-
- for (i = 0; i < count; ++i) {
- partial_oid = va_arg(ap, char *);
- cl_git_pass(git_oid_fromstrn(&oid, partial_oid, strlen(partial_oid)));
-
- cl_git_pass(git_object_lookup_prefix(&object, _repo, &oid, strlen(partial_oid), GIT_OBJ_COMMIT));
- git_oid_cpy(&oids[i], git_object_id(object));
- git_object_free(object);
- }
-
- va_end(ap);
-
- if (expected_sha == NULL)
- cl_assert_equal_i(GIT_ENOTFOUND, git_merge_base_many(&oid, _repo, oids, count));
- else {
- cl_git_pass(git_merge_base_many(&oid, _repo, oids, count));
- cl_git_pass(git_oid_fromstr(&expected, expected_sha));
-
- cl_assert(git_oid_cmp(&expected, &oid) == 0);
- }
-
- git__free(oids);
-}
-
-void test_revwalk_mergebase__many_no_common_ancestor_returns_ENOTFOUND(void)
-{
- assert_mergebase_many(NULL, 3, "41bc8c", "e90810", "a65fed");
- assert_mergebase_many(NULL, 3, "e90810", "41bc8c", "a65fed");
- assert_mergebase_many(NULL, 3, "e90810", "a65fed", "41bc8c");
- assert_mergebase_many(NULL, 3, "a65fed", "e90810", "41bc8c");
- assert_mergebase_many(NULL, 3, "a65fed", "e90810", "41bc8c");
- assert_mergebase_many(NULL, 3, "a65fed", "41bc8c", "e90810");
-
- assert_mergebase_many(NULL, 3, "e90810", "763d71", "a65fed");
-}
-
-void test_revwalk_mergebase__many_merge_branch(void)
-{
- assert_mergebase_many("c47800c7266a2be04c571c04d5a6614691ea99bd", 3, "a65fed", "763d71", "849607");
-
- assert_mergebase_many("c47800c7266a2be04c571c04d5a6614691ea99bd", 3, "763d71", "e90810", "a65fed");
- assert_mergebase_many("c47800c7266a2be04c571c04d5a6614691ea99bd", 3, "763d71", "a65fed", "e90810");
-
- assert_mergebase_many("c47800c7266a2be04c571c04d5a6614691ea99bd", 3, "a65fed", "763d71", "849607");
- assert_mergebase_many("c47800c7266a2be04c571c04d5a6614691ea99bd", 3, "a65fed", "849607", "763d71");
- assert_mergebase_many("8496071c1b46c854b31185ea97743be6a8774479", 3, "849607", "a65fed", "763d71");
-
- assert_mergebase_many("5b5b025afb0b4c913b4c338a42934a3863bf3644", 5, "5b5b02", "763d71", "a4a7dc", "a65fed", "41bc8c");
-}
-
-/*
- * testrepo.git $ git log --graph --all
- * * commit 763d71aadf09a7951596c9746c024e7eece7c7af
- * | Author: nulltoken <emeric.fermas@gmail.com>
- * | Date: Sun Oct 9 12:54:47 2011 +0200
- * |
- * | Add some files into subdirectories
- * |
- * | * commit a65fedf39aefe402d3bb6e24df4d4f5fe4547750
- * | | Author: Scott Chacon <schacon@gmail.com>
- * | | Date: Tue Aug 9 19:33:46 2011 -0700
- * | |
- * | * commit be3563ae3f795b2b4353bcce3a527ad0a4f7f644
- * | |\ Merge: 9fd738e c47800c
- * | |/ Author: Scott Chacon <schacon@gmail.com>
- * |/| Date: Tue May 25 11:58:27 2010 -0700
- * | |
- * | | Merge branch 'br2'
- * | |
- * | | * commit e90810b8df3e80c413d903f631643c716887138d
- * | | | Author: Vicent Marti <tanoku@gmail.com>
- * | | | Date: Thu Aug 5 18:42:20 2010 +0200
- * | | |
- * | | | Test commit 2
- * | | |
- * | | * commit 6dcf9bf7541ee10456529833502442f385010c3d
- * | | Author: Vicent Marti <tanoku@gmail.com>
- * | | Date: Thu Aug 5 18:41:33 2010 +0200
- * | |
- * | | Test commit 1
- * | |
- * | | * commit a4a7dce85cf63874e984719f4fdd239f5145052f
- * | | |\ Merge: c47800c 9fd738e
- * | |/ / Author: Scott Chacon <schacon@gmail.com>
- * |/| / Date: Tue May 25 12:00:23 2010 -0700
- * | |/
- * | | Merge branch 'master' into br2
- * | |
- * | * commit 9fd738e8f7967c078dceed8190330fc8648ee56a
- * | | Author: Scott Chacon <schacon@gmail.com>
- * | | Date: Mon May 24 10:19:19 2010 -0700
- * | |
- * | | a fourth commit
- * | |
- * | * commit 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
- * | | Author: Scott Chacon <schacon@gmail.com>
- * | | Date: Mon May 24 10:19:04 2010 -0700
- * | |
- * | | a third commit
- * | |
- * * | commit c47800c7266a2be04c571c04d5a6614691ea99bd
- * |/ Author: Scott Chacon <schacon@gmail.com>
- * | Date: Tue May 25 11:58:14 2010 -0700
- * |
- * | branch commit one
- * |
- * * commit 5b5b025afb0b4c913b4c338a42934a3863bf3644
- * | Author: Scott Chacon <schacon@gmail.com>
- * | Date: Tue May 11 13:38:42 2010 -0700
- * |
- * | another commit
- * |
- * * commit 8496071c1b46c854b31185ea97743be6a8774479
- * Author: Scott Chacon <schacon@gmail.com>
- * Date: Sat May 8 16:13:06 2010 -0700
- *
- * testing
- *
- * * commit 41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9
- * | Author: Scott Chacon <schacon@gmail.com>
- * | Date: Tue May 11 13:40:41 2010 -0700
- * |
- * | packed commit two
- * |
- * * commit 5001298e0c09ad9c34e4249bc5801c75e9754fa5
- * Author: Scott Chacon <schacon@gmail.com>
- * Date: Tue May 11 13:40:23 2010 -0700
- *
- * packed commit one
- */
-
-/*
- * twowaymerge.git $ git log --graph --all
- * * commit 9b219343610c88a1187c996d0dc58330b55cee28
- * |\ Merge: c37a783 2224e19
- * | | Author: Scott J. Goldman <scottjg@github.com>
- * | | Date: Tue Nov 27 20:31:04 2012 -0800
- * | |
- * | | Merge branch 'first-branch' into second-branch
- * | |
- * | * commit 2224e191514cb4bd8c566d80dac22dfcb1e9bb83
- * | | Author: Scott J. Goldman <scottjg@github.com>
- * | | Date: Tue Nov 27 20:28:51 2012 -0800
- * | |
- * | | j
- * | |
- * | * commit a41a49f8f5cd9b6cb14a076bf8394881ed0b4d19
- * | | Author: Scott J. Goldman <scottjg@github.com>
- * | | Date: Tue Nov 27 20:28:39 2012 -0800
- * | |
- * | | i
- * | |
- * | * commit 82bf9a1a10a4b25c1f14c9607b60970705e92545
- * | | Author: Scott J. Goldman <scottjg@github.com>
- * | | Date: Tue Nov 27 20:28:28 2012 -0800
- * | |
- * | | h
- * | |
- * * | commit c37a783c20d92ac92362a78a32860f7eebf938ef
- * | | Author: Scott J. Goldman <scottjg@github.com>
- * | | Date: Tue Nov 27 20:30:57 2012 -0800
- * | |
- * | | n
- * | |
- * * | commit 8b82fb1794cb1c8c7f172ec730a4c2db0ae3e650
- * | | Author: Scott J. Goldman <scottjg@github.com>
- * | | Date: Tue Nov 27 20:30:43 2012 -0800
- * | |
- * | | m
- * | |
- * * | commit 6ab5d28acbf3c3bdff276f7ccfdf29c1520e542f
- * | | Author: Scott J. Goldman <scottjg@github.com>
- * | | Date: Tue Nov 27 20:30:38 2012 -0800
- * | |
- * | | l
- * | |
- * * | commit 7b8c336c45fc6895c1c60827260fe5d798e5d247
- * | | Author: Scott J. Goldman <scottjg@github.com>
- * | | Date: Tue Nov 27 20:30:24 2012 -0800
- * | |
- * | | k
- * | |
- * | | * commit 1c30b88f5f3ee66d78df6520a7de9e89b890818b
- * | | | Author: Scott J. Goldman <scottjg@github.com>
- * | | | Date: Tue Nov 27 20:28:10 2012 -0800
- * | | |
- * | | | e
- * | | |
- * | | * commit 42b7311aa626e712891940c1ec5d5cba201946a4
- * | | | Author: Scott J. Goldman <scottjg@github.com>
- * | | | Date: Tue Nov 27 20:28:06 2012 -0800
- * | | |
- * | | | d
- * | | |
- * | | * commit a953a018c5b10b20c86e69fef55ebc8ad4c5a417
- * | | |\ Merge: bd1732c cdf97fd
- * | | |/ Author: Scott J. Goldman <scottjg@github.com>
- * | |/| Date: Tue Nov 27 20:26:43 2012 -0800
- * | | |
- * | | | Merge branch 'first-branch'
- * | | |
- * | * | commit cdf97fd3bb48eb3827638bb33d208f5fd32d0aa6
- * | | | Author: Scott J. Goldman <scottjg@github.com>
- * | | | Date: Tue Nov 27 20:24:46 2012 -0800
- * | | |
- * | | | g
- * | | |
- * | * | commit ef0488f0b722f0be8bcb90a7730ac7efafd1d694
- * | | | Author: Scott J. Goldman <scottjg@github.com>
- * | | | Date: Tue Nov 27 20:24:39 2012 -0800
- * | | |
- * | | | f
- * | | |
- * | | * commit bd1732c43c68d712ad09e1d872b9be6d4b9efdc4
- * | |/ Author: Scott J. Goldman <scottjg@github.com>
- * | | Date: Tue Nov 27 17:43:58 2012 -0800
- * | |
- * | | c
- * | |
- * | * commit 0c8a3f1f3d5f421cf83048c7c73ee3b55a5e0f29
- * |/ Author: Scott J. Goldman <scottjg@github.com>
- * | Date: Tue Nov 27 17:43:48 2012 -0800
- * |
- * | b
- * |
- * * commit 1f4c0311a24b63f6fc209a59a1e404942d4a5006
- * Author: Scott J. Goldman <scottjg@github.com>
- * Date: Tue Nov 27 17:43:41 2012 -0800
- *
- * a
- */
diff --git a/tests-clar/stash/drop.c b/tests-clar/stash/drop.c
deleted file mode 100644
index 60b3c72e0..000000000
--- a/tests-clar/stash/drop.c
+++ /dev/null
@@ -1,172 +0,0 @@
-#include "clar_libgit2.h"
-#include "fileops.h"
-#include "stash_helpers.h"
-#include "refs.h"
-
-static git_repository *repo;
-static git_signature *signature;
-
-void test_stash_drop__initialize(void)
-{
- cl_git_pass(git_repository_init(&repo, "stash", 0));
- cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60)); /* Wed Dec 14 08:29:03 2011 +0100 */
-}
-
-void test_stash_drop__cleanup(void)
-{
- git_signature_free(signature);
- signature = NULL;
-
- git_repository_free(repo);
- repo = NULL;
-
- cl_git_pass(git_futils_rmdir_r("stash", NULL, GIT_RMDIR_REMOVE_FILES));
-}
-
-void test_stash_drop__cannot_drop_from_an_empty_stash(void)
-{
- cl_git_fail_with(git_stash_drop(repo, 0), GIT_ENOTFOUND);
-}
-
-static void push_three_states(void)
-{
- git_oid oid;
- git_index *index;
-
- cl_git_mkfile("stash/zero.txt", "content\n");
- cl_git_pass(git_repository_index(&index, repo));
- cl_git_pass(git_index_add_bypath(index, "zero.txt"));
- commit_staged_files(&oid, index, signature);
- cl_assert(git_path_exists("stash/zero.txt"));
-
- cl_git_mkfile("stash/one.txt", "content\n");
- cl_git_pass(git_stash_save(&oid, repo, signature, "First", GIT_STASH_INCLUDE_UNTRACKED));
- cl_assert(!git_path_exists("stash/one.txt"));
- cl_assert(git_path_exists("stash/zero.txt"));
-
- cl_git_mkfile("stash/two.txt", "content\n");
- cl_git_pass(git_stash_save(&oid, repo, signature, "Second", GIT_STASH_INCLUDE_UNTRACKED));
- cl_assert(!git_path_exists("stash/two.txt"));
- cl_assert(git_path_exists("stash/zero.txt"));
-
- cl_git_mkfile("stash/three.txt", "content\n");
- cl_git_pass(git_stash_save(&oid, repo, signature, "Third", GIT_STASH_INCLUDE_UNTRACKED));
- cl_assert(!git_path_exists("stash/three.txt"));
- cl_assert(git_path_exists("stash/zero.txt"));
-
- git_index_free(index);
-}
-
-void test_stash_drop__cannot_drop_a_non_existing_stashed_state(void)
-{
- push_three_states();
-
- cl_git_fail_with(git_stash_drop(repo, 666), GIT_ENOTFOUND);
- cl_git_fail_with(git_stash_drop(repo, 42), GIT_ENOTFOUND);
- cl_git_fail_with(git_stash_drop(repo, 3), GIT_ENOTFOUND);
-}
-
-void test_stash_drop__can_purge_the_stash_from_the_top(void)
-{
- push_three_states();
-
- cl_git_pass(git_stash_drop(repo, 0));
- cl_git_pass(git_stash_drop(repo, 0));
- cl_git_pass(git_stash_drop(repo, 0));
-
- cl_git_fail_with(git_stash_drop(repo, 0), GIT_ENOTFOUND);
-}
-
-void test_stash_drop__can_purge_the_stash_from_the_bottom(void)
-{
- push_three_states();
-
- cl_git_pass(git_stash_drop(repo, 2));
- cl_git_pass(git_stash_drop(repo, 1));
- cl_git_pass(git_stash_drop(repo, 0));
-
- cl_git_fail_with(git_stash_drop(repo, 0), GIT_ENOTFOUND);
-}
-
-void test_stash_drop__dropping_an_entry_rewrites_reflog_history(void)
-{
- git_reference *stash;
- git_reflog *reflog;
- const git_reflog_entry *entry;
- git_oid oid;
- size_t count;
-
- push_three_states();
-
- cl_git_pass(git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE));
-
- cl_git_pass(git_reflog_read(&reflog, stash));
- entry = git_reflog_entry_byindex(reflog, 1);
-
- git_oid_cpy(&oid, git_reflog_entry_id_old(entry));
- count = git_reflog_entrycount(reflog);
-
- git_reflog_free(reflog);
-
- cl_git_pass(git_stash_drop(repo, 1));
-
- cl_git_pass(git_reflog_read(&reflog, stash));
- entry = git_reflog_entry_byindex(reflog, 0);
-
- cl_assert_equal_i(0, git_oid_cmp(&oid, git_reflog_entry_id_old(entry)));
- cl_assert_equal_sz(count - 1, git_reflog_entrycount(reflog));
-
- git_reflog_free(reflog);
-
- git_reference_free(stash);
-}
-
-void test_stash_drop__dropping_the_last_entry_removes_the_stash(void)
-{
- git_reference *stash;
-
- push_three_states();
-
- cl_git_pass(git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE));
- git_reference_free(stash);
-
- cl_git_pass(git_stash_drop(repo, 0));
- cl_git_pass(git_stash_drop(repo, 0));
- cl_git_pass(git_stash_drop(repo, 0));
-
- cl_git_fail_with(
- git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE), GIT_ENOTFOUND);
-}
-
-void retrieve_top_stash_id(git_oid *out)
-{
- git_object *top_stash;
-
- cl_git_pass(git_revparse_single(&top_stash, repo, "stash@{0}"));
- cl_git_pass(git_reference_name_to_id(out, repo, GIT_REFS_STASH_FILE));
-
- cl_assert_equal_i(true, git_oid_cmp(out, git_object_id(top_stash)) == 0);
-
- git_object_free(top_stash);
-}
-
-void test_stash_drop__dropping_the_top_stash_updates_the_stash_reference(void)
-{
- git_object *next_top_stash;
- git_oid oid;
-
- push_three_states();
-
- retrieve_top_stash_id(&oid);
-
- cl_git_pass(git_revparse_single(&next_top_stash, repo, "stash@{1}"));
- cl_assert_equal_i(false, git_oid_cmp(&oid, git_object_id(next_top_stash)) == 0);
-
- cl_git_pass(git_stash_drop(repo, 0));
-
- retrieve_top_stash_id(&oid);
-
- cl_git_pass(git_oid_cmp(&oid, git_object_id(next_top_stash)));
-
- git_object_free(next_top_stash);
-}
diff --git a/tests-clar/stash/save.c b/tests-clar/stash/save.c
deleted file mode 100644
index eae116ac5..000000000
--- a/tests-clar/stash/save.c
+++ /dev/null
@@ -1,373 +0,0 @@
-#include "clar_libgit2.h"
-#include "fileops.h"
-#include "stash_helpers.h"
-
-static git_repository *repo;
-static git_signature *signature;
-static git_oid stash_tip_oid;
-
-/*
- * Friendly reminder, in order to ease the reading of the following tests:
- *
- * "stash" points to the worktree commit
- * "stash^1" points to the base commit (HEAD when the stash was created)
- * "stash^2" points to the index commit
- * "stash^3" points to the untracked commit
- */
-
-void test_stash_save__initialize(void)
-{
- cl_git_pass(git_repository_init(&repo, "stash", 0));
- cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60)); /* Wed Dec 14 08:29:03 2011 +0100 */
-
- setup_stash(repo, signature);
-}
-
-void test_stash_save__cleanup(void)
-{
- git_signature_free(signature);
- signature = NULL;
-
- git_repository_free(repo);
- repo = NULL;
-
- cl_git_pass(git_futils_rmdir_r("stash", NULL, GIT_RMDIR_REMOVE_FILES));
- cl_fixture_cleanup("sorry-it-is-a-non-bare-only-party");
-}
-
-static void assert_object_oid(const char* revision, const char* expected_oid, git_otype type)
-{
- int result;
- git_object *obj;
-
- result = git_revparse_single(&obj, repo, revision);
-
- if (!expected_oid) {
- cl_assert_equal_i(GIT_ENOTFOUND, result);
- return;
- } else
- cl_assert_equal_i(0, result);
-
- cl_git_pass(git_oid_streq(git_object_id(obj), expected_oid));
- cl_assert_equal_i(type, git_object_type(obj));
- git_object_free(obj);
-}
-
-static void assert_blob_oid(const char* revision, const char* expected_oid)
-{
- assert_object_oid(revision, expected_oid, GIT_OBJ_BLOB);
-}
-
-void test_stash_save__does_not_keep_index_by_default(void)
-{
-/*
-$ git stash
-
-$ git show refs/stash:what
-see you later
-
-$ git show refs/stash:how
-not so small and
-
-$ git show refs/stash:who
-funky world
-
-$ git show refs/stash:when
-fatal: Path 'when' exists on disk, but not in 'stash'.
-
-$ git show refs/stash^2:what
-goodbye
-
-$ git show refs/stash^2:how
-not so small and
-
-$ git show refs/stash^2:who
-world
-
-$ git show refs/stash^2:when
-fatal: Path 'when' exists on disk, but not in 'stash^2'.
-
-$ git status --short
-?? when
-
-*/
- unsigned int status;
-
- cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
- cl_git_pass(git_status_file(&status, repo, "when"));
-
- assert_blob_oid("refs/stash:what", "bc99dc98b3eba0e9157e94769cd4d49cb49de449"); /* see you later */
- assert_blob_oid("refs/stash:how", "e6d64adb2c7f3eb8feb493b556cc8070dca379a3"); /* not so small and */
- assert_blob_oid("refs/stash:who", "a0400d4954659306a976567af43125a0b1aa8595"); /* funky world */
- assert_blob_oid("refs/stash:when", NULL);
- assert_blob_oid("refs/stash:just.ignore", NULL);
-
- assert_blob_oid("refs/stash^2:what", "dd7e1c6f0fefe118f0b63d9f10908c460aa317a6"); /* goodbye */
- assert_blob_oid("refs/stash^2:how", "e6d64adb2c7f3eb8feb493b556cc8070dca379a3"); /* not so small and */
- assert_blob_oid("refs/stash^2:who", "cc628ccd10742baea8241c5924df992b5c019f71"); /* world */
- assert_blob_oid("refs/stash^2:when", NULL);
- assert_blob_oid("refs/stash^2:just.ignore", NULL);
-
- assert_blob_oid("refs/stash^3", NULL);
-
- cl_assert_equal_i(GIT_STATUS_WT_NEW, status);
-}
-
-static void assert_status(
- const char *path,
- int status_flags)
-{
- unsigned int status;
- int error;
-
- error = git_status_file(&status, repo, path);
-
- if (status_flags < 0) {
- cl_assert_equal_i(status_flags, error);
- return;
- }
-
- cl_assert_equal_i(0, error);
- cl_assert_equal_i((unsigned int)status_flags, status);
-}
-
-void test_stash_save__can_keep_index(void)
-{
- cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_KEEP_INDEX));
-
- assert_status("what", GIT_STATUS_INDEX_MODIFIED);
- assert_status("how", GIT_STATUS_INDEX_MODIFIED);
- assert_status("who", GIT_STATUS_CURRENT);
- assert_status("when", GIT_STATUS_WT_NEW);
- assert_status("just.ignore", GIT_STATUS_IGNORED);
-}
-
-static void assert_commit_message_contains(const char *revision, const char *fragment)
-{
- git_commit *commit;
-
- cl_git_pass(git_revparse_single((git_object**)&commit, repo, revision));
-
- cl_assert(strstr(git_commit_message(commit), fragment) != NULL);
-
- git_commit_free(commit);
-}
-
-void test_stash_save__can_include_untracked_files(void)
-{
- cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
-
- assert_commit_message_contains("refs/stash^3", "untracked files on master: ");
-
- assert_blob_oid("refs/stash^3:what", NULL);
- assert_blob_oid("refs/stash^3:how", NULL);
- assert_blob_oid("refs/stash^3:who", NULL);
- assert_blob_oid("refs/stash^3:when", "b6ed15e81e2593d7bb6265eb4a991d29dc3e628b");
- assert_blob_oid("refs/stash^3:just.ignore", NULL);
-}
-
-void test_stash_save__can_include_untracked_and_ignored_files(void)
-{
- cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED | GIT_STASH_INCLUDE_IGNORED));
-
- assert_commit_message_contains("refs/stash^3", "untracked files on master: ");
-
- assert_blob_oid("refs/stash^3:what", NULL);
- assert_blob_oid("refs/stash^3:how", NULL);
- assert_blob_oid("refs/stash^3:who", NULL);
- assert_blob_oid("refs/stash^3:when", "b6ed15e81e2593d7bb6265eb4a991d29dc3e628b");
- assert_blob_oid("refs/stash^3:just.ignore", "78925fb1236b98b37a35e9723033e627f97aa88b");
-}
-
-#define MESSAGE "Look Ma! I'm on TV!"
-void test_stash_save__can_accept_a_message(void)
-{
- cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, MESSAGE, GIT_STASH_DEFAULT));
-
- assert_commit_message_contains("refs/stash^2", "index on master: ");
- assert_commit_message_contains("refs/stash", "On master: " MESSAGE);
-}
-
-void test_stash_save__cannot_stash_against_an_unborn_branch(void)
-{
- git_reference *head;
-
- cl_git_pass(git_reference_symbolic_create(&head, repo, "HEAD", "refs/heads/unborn", 1));
-
- cl_assert_equal_i(GIT_EORPHANEDHEAD,
- git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
-
- git_reference_free(head);
-}
-
-void test_stash_save__cannot_stash_against_a_bare_repository(void)
-{
- git_repository *local;
-
- cl_git_pass(git_repository_init(&local, "sorry-it-is-a-non-bare-only-party", 1));
-
- cl_assert_equal_i(GIT_EBAREREPO,
- git_stash_save(&stash_tip_oid, local, signature, NULL, GIT_STASH_DEFAULT));
-
- git_repository_free(local);
-}
-
-void test_stash_save__can_stash_against_a_detached_head(void)
-{
- git_repository_detach_head(repo);
-
- cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
-
- assert_commit_message_contains("refs/stash^2", "index on (no branch): ");
- assert_commit_message_contains("refs/stash", "WIP on (no branch): ");
-}
-
-void test_stash_save__stashing_updates_the_reflog(void)
-{
- char *sha;
-
- assert_object_oid("refs/stash@{0}", NULL, GIT_OBJ_COMMIT);
-
- cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
-
- sha = git_oid_allocfmt(&stash_tip_oid);
-
- assert_object_oid("refs/stash@{0}", sha, GIT_OBJ_COMMIT);
- assert_object_oid("refs/stash@{1}", NULL, GIT_OBJ_COMMIT);
-
- git__free(sha);
-}
-
-void test_stash_save__cannot_stash_when_there_are_no_local_change(void)
-{
- git_index *index;
- git_oid commit_oid, stash_tip_oid;
-
- cl_git_pass(git_repository_index(&index, repo));
-
- /*
- * 'what' and 'who' are being committed.
- * 'when' remain untracked.
- */
- cl_git_pass(git_index_add_bypath(index, "what"));
- cl_git_pass(git_index_add_bypath(index, "who"));
- cl_git_pass(git_index_write(index));
- commit_staged_files(&commit_oid, index, signature);
- git_index_free(index);
-
- cl_assert_equal_i(GIT_ENOTFOUND,
- git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
-
- p_unlink("stash/when");
- cl_assert_equal_i(GIT_ENOTFOUND,
- git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
-}
-
-void test_stash_save__can_stage_normal_then_stage_untracked(void)
-{
- /*
- * $ git ls-tree stash@{1}^0
- * 100644 blob ac4d88de61733173d9959e4b77c69b9f17a00980 .gitignore
- * 100644 blob e6d64adb2c7f3eb8feb493b556cc8070dca379a3 how
- * 100644 blob bc99dc98b3eba0e9157e94769cd4d49cb49de449 what
- * 100644 blob a0400d4954659306a976567af43125a0b1aa8595 who
- *
- * $ git ls-tree stash@{1}^1
- * 100644 blob ac4d88de61733173d9959e4b77c69b9f17a00980 .gitignore
- * 100644 blob ac790413e2d7a26c3767e78c57bb28716686eebc how
- * 100644 blob ce013625030ba8dba906f756967f9e9ca394464a what
- * 100644 blob cc628ccd10742baea8241c5924df992b5c019f71 who
- *
- * $ git ls-tree stash@{1}^2
- * 100644 blob ac4d88de61733173d9959e4b77c69b9f17a00980 .gitignore
- * 100644 blob e6d64adb2c7f3eb8feb493b556cc8070dca379a3 how
- * 100644 blob dd7e1c6f0fefe118f0b63d9f10908c460aa317a6 what
- * 100644 blob cc628ccd10742baea8241c5924df992b5c019f71 who
- *
- * $ git ls-tree stash@{1}^3
- * fatal: Not a valid object name stash@{1}^3
- *
- * $ git ls-tree stash@{0}^0
- * 100644 blob ac4d88de61733173d9959e4b77c69b9f17a00980 .gitignore
- * 100644 blob ac790413e2d7a26c3767e78c57bb28716686eebc how
- * 100644 blob ce013625030ba8dba906f756967f9e9ca394464a what
- * 100644 blob cc628ccd10742baea8241c5924df992b5c019f71 who
- *
- * $ git ls-tree stash@{0}^1
- * 100644 blob ac4d88de61733173d9959e4b77c69b9f17a00980 .gitignore
- * 100644 blob ac790413e2d7a26c3767e78c57bb28716686eebc how
- * 100644 blob ce013625030ba8dba906f756967f9e9ca394464a what
- * 100644 blob cc628ccd10742baea8241c5924df992b5c019f71 who
- *
- * $ git ls-tree stash@{0}^2
- * 100644 blob ac4d88de61733173d9959e4b77c69b9f17a00980 .gitignore
- * 100644 blob ac790413e2d7a26c3767e78c57bb28716686eebc how
- * 100644 blob ce013625030ba8dba906f756967f9e9ca394464a what
- * 100644 blob cc628ccd10742baea8241c5924df992b5c019f71 who
- *
- * $ git ls-tree stash@{0}^3
- * 100644 blob b6ed15e81e2593d7bb6265eb4a991d29dc3e628b when
- */
-
- assert_status("what", GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED);
- assert_status("how", GIT_STATUS_INDEX_MODIFIED);
- assert_status("who", GIT_STATUS_WT_MODIFIED);
- assert_status("when", GIT_STATUS_WT_NEW);
- assert_status("just.ignore", GIT_STATUS_IGNORED);
-
- cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
- assert_status("what", GIT_STATUS_CURRENT);
- assert_status("how", GIT_STATUS_CURRENT);
- assert_status("who", GIT_STATUS_CURRENT);
- assert_status("when", GIT_STATUS_WT_NEW);
- assert_status("just.ignore", GIT_STATUS_IGNORED);
-
- cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
- assert_status("what", GIT_STATUS_CURRENT);
- assert_status("how", GIT_STATUS_CURRENT);
- assert_status("who", GIT_STATUS_CURRENT);
- assert_status("when", GIT_ENOTFOUND);
- assert_status("just.ignore", GIT_STATUS_IGNORED);
-
-
- assert_blob_oid("stash@{1}^0:what", "bc99dc98b3eba0e9157e94769cd4d49cb49de449"); /* see you later */
- assert_blob_oid("stash@{1}^0:how", "e6d64adb2c7f3eb8feb493b556cc8070dca379a3"); /* not so small and */
- assert_blob_oid("stash@{1}^0:who", "a0400d4954659306a976567af43125a0b1aa8595"); /* funky world */
- assert_blob_oid("stash@{1}^0:when", NULL);
-
- assert_blob_oid("stash@{1}^2:what", "dd7e1c6f0fefe118f0b63d9f10908c460aa317a6"); /* goodbye */
- assert_blob_oid("stash@{1}^2:how", "e6d64adb2c7f3eb8feb493b556cc8070dca379a3"); /* not so small and */
- assert_blob_oid("stash@{1}^2:who", "cc628ccd10742baea8241c5924df992b5c019f71"); /* world */
- assert_blob_oid("stash@{1}^2:when", NULL);
-
- assert_object_oid("stash@{1}^3", NULL, GIT_OBJ_COMMIT);
-
- assert_blob_oid("stash@{0}^0:what", "ce013625030ba8dba906f756967f9e9ca394464a"); /* hello */
- assert_blob_oid("stash@{0}^0:how", "ac790413e2d7a26c3767e78c57bb28716686eebc"); /* small */
- assert_blob_oid("stash@{0}^0:who", "cc628ccd10742baea8241c5924df992b5c019f71"); /* world */
- assert_blob_oid("stash@{0}^0:when", NULL);
-
- assert_blob_oid("stash@{0}^2:what", "ce013625030ba8dba906f756967f9e9ca394464a"); /* hello */
- assert_blob_oid("stash@{0}^2:how", "ac790413e2d7a26c3767e78c57bb28716686eebc"); /* small */
- assert_blob_oid("stash@{0}^2:who", "cc628ccd10742baea8241c5924df992b5c019f71"); /* world */
- assert_blob_oid("stash@{0}^2:when", NULL);
-
- assert_blob_oid("stash@{0}^3:when", "b6ed15e81e2593d7bb6265eb4a991d29dc3e628b"); /* now */
-}
-
-#define EMPTY_TREE "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
-
-void test_stash_save__including_untracked_without_any_untracked_file_creates_an_empty_tree(void)
-{
- cl_git_pass(p_unlink("stash/when"));
-
- assert_status("what", GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED);
- assert_status("how", GIT_STATUS_INDEX_MODIFIED);
- assert_status("who", GIT_STATUS_WT_MODIFIED);
- assert_status("when", GIT_ENOTFOUND);
- assert_status("just.ignore", GIT_STATUS_IGNORED);
-
- cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
-
- assert_object_oid("stash^3^{tree}", EMPTY_TREE, GIT_OBJ_TREE);
-}
diff --git a/tests-clar/stash/stash_helpers.c b/tests-clar/stash/stash_helpers.c
deleted file mode 100644
index f462a1351..000000000
--- a/tests-clar/stash/stash_helpers.c
+++ /dev/null
@@ -1,68 +0,0 @@
-#include "clar_libgit2.h"
-#include "fileops.h"
-#include "stash_helpers.h"
-
-void commit_staged_files(
- git_oid *commit_oid,
- git_index *index,
- git_signature *signature)
-{
- git_tree *tree;
- git_oid tree_oid;
- git_repository *repo;
-
- repo = git_index_owner(index);
-
- cl_git_pass(git_index_write_tree(&tree_oid, index));
-
- cl_git_pass(git_tree_lookup(&tree, repo, &tree_oid));
-
- cl_git_pass(git_commit_create_v(
- commit_oid,
- repo,
- "HEAD",
- signature,
- signature,
- NULL,
- "Initial commit",
- tree,
- 0));
-
- git_tree_free(tree);
-}
-
-void setup_stash(git_repository *repo, git_signature *signature)
-{
- git_oid commit_oid;
- git_index *index;
-
- cl_git_pass(git_repository_index(&index, repo));
-
- cl_git_mkfile("stash/what", "hello\n"); /* ce013625030ba8dba906f756967f9e9ca394464a */
- cl_git_mkfile("stash/how", "small\n"); /* ac790413e2d7a26c3767e78c57bb28716686eebc */
- cl_git_mkfile("stash/who", "world\n"); /* cc628ccd10742baea8241c5924df992b5c019f71 */
- cl_git_mkfile("stash/when", "now\n"); /* b6ed15e81e2593d7bb6265eb4a991d29dc3e628b */
- cl_git_mkfile("stash/just.ignore", "me\n"); /* 78925fb1236b98b37a35e9723033e627f97aa88b */
-
- cl_git_mkfile("stash/.gitignore", "*.ignore\n");
-
- cl_git_pass(git_index_add_bypath(index, "what"));
- cl_git_pass(git_index_add_bypath(index, "how"));
- cl_git_pass(git_index_add_bypath(index, "who"));
- cl_git_pass(git_index_add_bypath(index, ".gitignore"));
- cl_git_pass(git_index_write(index));
-
- commit_staged_files(&commit_oid, index, signature);
-
- cl_git_rewritefile("stash/what", "goodbye\n"); /* dd7e1c6f0fefe118f0b63d9f10908c460aa317a6 */
- cl_git_rewritefile("stash/how", "not so small and\n"); /* e6d64adb2c7f3eb8feb493b556cc8070dca379a3 */
- cl_git_rewritefile("stash/who", "funky world\n"); /* a0400d4954659306a976567af43125a0b1aa8595 */
-
- cl_git_pass(git_index_add_bypath(index, "what"));
- cl_git_pass(git_index_add_bypath(index, "how"));
- cl_git_pass(git_index_write(index));
-
- cl_git_rewritefile("stash/what", "see you later\n"); /* bc99dc98b3eba0e9157e94769cd4d49cb49de449 */
-
- git_index_free(index);
-}
diff --git a/tests-clar/stash/stash_helpers.h b/tests-clar/stash/stash_helpers.h
deleted file mode 100644
index bb7fec4f5..000000000
--- a/tests-clar/stash/stash_helpers.h
+++ /dev/null
@@ -1,8 +0,0 @@
-void setup_stash(
- git_repository *repo,
- git_signature *signature);
-
-void commit_staged_files(
- git_oid *commit_oid,
- git_index *index,
- git_signature *signature); \ No newline at end of file
diff --git a/tests-clar/status/ignore.c b/tests-clar/status/ignore.c
deleted file mode 100644
index 4f6879cfc..000000000
--- a/tests-clar/status/ignore.c
+++ /dev/null
@@ -1,461 +0,0 @@
-#include "clar_libgit2.h"
-#include "fileops.h"
-#include "git2/attr.h"
-#include "ignore.h"
-#include "attr.h"
-#include "status_helpers.h"
-
-static git_repository *g_repo = NULL;
-
-void test_status_ignore__initialize(void)
-{
-}
-
-void test_status_ignore__cleanup(void)
-{
- cl_git_sandbox_cleanup();
-}
-
-void test_status_ignore__0(void)
-{
- struct {
- const char *path;
- int expected;
- } test_cases[] = {
- /* pattern "ign" from .gitignore */
- { "file", 0 },
- { "ign", 1 },
- { "sub", 0 },
- { "sub/file", 0 },
- { "sub/ign", 1 },
- { "sub/ign/file", 1 },
- { "sub/ign/sub", 1 },
- { "sub/ign/sub/file", 1 },
- { "sub/sub", 0 },
- { "sub/sub/file", 0 },
- { "sub/sub/ign", 1 },
- { "sub/sub/sub", 0 },
- /* pattern "dir/" from .gitignore */
- { "dir", 1 },
- { "dir/", 1 },
- { "sub/dir", 1 },
- { "sub/dir/", 1 },
- { "sub/dir/file", 1 }, /* contained in ignored parent */
- { "sub/sub/dir", 0 }, /* dir is not actually a dir, but a file */
- { NULL, 0 }
- }, *one_test;
-
- g_repo = cl_git_sandbox_init("attr");
-
- for (one_test = test_cases; one_test->path != NULL; one_test++) {
- int ignored;
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, one_test->path));
- cl_assert_(ignored == one_test->expected, one_test->path);
- }
-
- /* confirm that ignore files were cached */
- cl_assert(git_attr_cache__is_cached(g_repo, 0, ".git/info/exclude"));
- cl_assert(git_attr_cache__is_cached(g_repo, 0, ".gitignore"));
-}
-
-
-void test_status_ignore__1(void)
-{
- int ignored;
-
- g_repo = cl_git_sandbox_init("attr");
-
- cl_git_rewritefile("attr/.gitignore", "/*.txt\n/dir/\n");
- git_attr_cache_flush(g_repo);
-
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "root_test4.txt"));
- cl_assert(ignored);
-
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "sub/subdir_test2.txt"));
- cl_assert(!ignored);
-
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "dir"));
- cl_assert(ignored);
-
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "dir/"));
- cl_assert(ignored);
-
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "sub/dir"));
- cl_assert(!ignored);
-
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "sub/dir/"));
- cl_assert(!ignored);
-}
-
-
-void test_status_ignore__empty_repo_with_gitignore_rewrite(void)
-{
- status_entry_single st;
- int ignored;
-
- g_repo = cl_git_sandbox_init("empty_standard_repo");
-
- cl_git_mkfile(
- "empty_standard_repo/look-ma.txt", "I'm going to be ignored!");
-
- memset(&st, 0, sizeof(st));
- cl_git_pass(git_status_foreach(g_repo, cb_status__single, &st));
- cl_assert(st.count == 1);
- cl_assert(st.status == GIT_STATUS_WT_NEW);
-
- cl_git_pass(git_status_file(&st.status, g_repo, "look-ma.txt"));
- cl_assert(st.status == GIT_STATUS_WT_NEW);
-
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "look-ma.txt"));
- cl_assert(!ignored);
-
- cl_git_rewritefile("empty_standard_repo/.gitignore", "*.nomatch\n");
-
- memset(&st, 0, sizeof(st));
- cl_git_pass(git_status_foreach(g_repo, cb_status__single, &st));
- cl_assert(st.count == 2);
- cl_assert(st.status == GIT_STATUS_WT_NEW);
-
- cl_git_pass(git_status_file(&st.status, g_repo, "look-ma.txt"));
- cl_assert(st.status == GIT_STATUS_WT_NEW);
-
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "look-ma.txt"));
- cl_assert(!ignored);
-
- cl_git_rewritefile("empty_standard_repo/.gitignore", "*.txt\n");
-
- memset(&st, 0, sizeof(st));
- cl_git_pass(git_status_foreach(g_repo, cb_status__single, &st));
- cl_assert(st.count == 2);
- cl_assert(st.status == GIT_STATUS_IGNORED);
-
- cl_git_pass(git_status_file(&st.status, g_repo, "look-ma.txt"));
- cl_assert(st.status == GIT_STATUS_IGNORED);
-
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "look-ma.txt"));
- cl_assert(ignored);
-}
-
-void test_status_ignore__ignore_pattern_contains_space(void)
-{
- unsigned int flags;
- const mode_t mode = 0777;
-
- g_repo = cl_git_sandbox_init("empty_standard_repo");
- cl_git_rewritefile("empty_standard_repo/.gitignore", "foo bar.txt\n");
-
- cl_git_mkfile(
- "empty_standard_repo/foo bar.txt", "I'm going to be ignored!");
-
- cl_git_pass(git_status_file(&flags, g_repo, "foo bar.txt"));
- cl_assert(flags == GIT_STATUS_IGNORED);
-
- cl_git_pass(git_futils_mkdir_r("empty_standard_repo/foo", NULL, mode));
- cl_git_mkfile("empty_standard_repo/foo/look-ma.txt", "I'm not going to be ignored!");
-
- cl_git_pass(git_status_file(&flags, g_repo, "foo/look-ma.txt"));
- cl_assert(flags == GIT_STATUS_WT_NEW);
-}
-
-void test_status_ignore__ignore_pattern_ignorecase(void)
-{
- unsigned int flags;
- bool ignore_case;
- git_index *index;
-
- g_repo = cl_git_sandbox_init("empty_standard_repo");
- cl_git_rewritefile("empty_standard_repo/.gitignore", "a.txt\n");
-
- cl_git_mkfile("empty_standard_repo/A.txt", "Differs in case");
-
- cl_git_pass(git_repository_index(&index, g_repo));
- ignore_case = (git_index_caps(index) & GIT_INDEXCAP_IGNORE_CASE) != 0;
- git_index_free(index);
-
- cl_git_pass(git_status_file(&flags, g_repo, "A.txt"));
- cl_assert(flags == ignore_case ? GIT_STATUS_IGNORED : GIT_STATUS_WT_NEW);
-}
-
-void test_status_ignore__subdirectories(void)
-{
- status_entry_single st;
- int ignored;
-
- g_repo = cl_git_sandbox_init("empty_standard_repo");
-
- cl_git_mkfile(
- "empty_standard_repo/ignore_me", "I'm going to be ignored!");
-
- cl_git_rewritefile("empty_standard_repo/.gitignore", "ignore_me\n");
-
- memset(&st, 0, sizeof(st));
- cl_git_pass(git_status_foreach(g_repo, cb_status__single, &st));
- cl_assert_equal_i(2, st.count);
- cl_assert(st.status == GIT_STATUS_IGNORED);
-
- cl_git_pass(git_status_file(&st.status, g_repo, "ignore_me"));
- cl_assert(st.status == GIT_STATUS_IGNORED);
-
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "ignore_me"));
- cl_assert(ignored);
-
- /* I've changed libgit2 so that the behavior here now differs from
- * core git but seems to make more sense. In core git, the following
- * items are skipped completed, even if --ignored is passed to status.
- * It you mirror these steps and run "git status -uall --ignored" then
- * you will not see "test/ignore_me/" in the results.
- *
- * However, we had a couple reports of this as a bug, plus there is a
- * similar circumstance where we were differing for core git when you
- * used a rooted path for an ignore, so I changed this behavior.
- */
- cl_git_pass(git_futils_mkdir_r(
- "empty_standard_repo/test/ignore_me", NULL, 0775));
- cl_git_mkfile(
- "empty_standard_repo/test/ignore_me/file", "I'm going to be ignored!");
- cl_git_mkfile(
- "empty_standard_repo/test/ignore_me/file2", "Me, too!");
-
- memset(&st, 0, sizeof(st));
- cl_git_pass(git_status_foreach(g_repo, cb_status__single, &st));
- cl_assert_equal_i(3, st.count);
-
- cl_git_pass(git_status_file(&st.status, g_repo, "test/ignore_me/file"));
- cl_assert(st.status == GIT_STATUS_IGNORED);
-
- cl_git_pass(
- git_status_should_ignore(&ignored, g_repo, "test/ignore_me/file"));
- cl_assert(ignored);
-}
-
-void test_status_ignore__subdirectories_recursion(void)
-{
- /* Let's try again with recursing into ignored dirs turned on */
- git_status_options opts = GIT_STATUS_OPTIONS_INIT;
- status_entry_counts counts;
- static const char *paths_r[] = {
- ".gitignore",
- "ignore_also/file",
- "ignore_me",
- "test/ignore_me/and_me/file",
- "test/ignore_me/file",
- "test/ignore_me/file2",
- };
- static const unsigned int statuses_r[] = {
- GIT_STATUS_WT_NEW,
- GIT_STATUS_IGNORED,
- GIT_STATUS_IGNORED,
- GIT_STATUS_IGNORED,
- GIT_STATUS_IGNORED,
- GIT_STATUS_IGNORED,
- };
- static const char *paths_nr[] = {
- ".gitignore",
- "ignore_also/",
- "ignore_me",
- "test/ignore_me/",
- };
- static const unsigned int statuses_nr[] = {
- GIT_STATUS_WT_NEW,
- GIT_STATUS_IGNORED,
- GIT_STATUS_IGNORED,
- GIT_STATUS_IGNORED,
- };
-
- g_repo = cl_git_sandbox_init("empty_standard_repo");
-
- cl_git_rewritefile("empty_standard_repo/.gitignore", "ignore_me\n/ignore_also\n");
-
- cl_git_mkfile(
- "empty_standard_repo/ignore_me", "I'm going to be ignored!");
- cl_git_pass(git_futils_mkdir_r(
- "empty_standard_repo/test/ignore_me", NULL, 0775));
- cl_git_mkfile(
- "empty_standard_repo/test/ignore_me/file", "I'm going to be ignored!");
- cl_git_mkfile(
- "empty_standard_repo/test/ignore_me/file2", "Me, too!");
- cl_git_pass(git_futils_mkdir_r(
- "empty_standard_repo/test/ignore_me/and_me", NULL, 0775));
- cl_git_mkfile(
- "empty_standard_repo/test/ignore_me/and_me/file", "Deeply ignored");
- cl_git_pass(git_futils_mkdir_r(
- "empty_standard_repo/ignore_also", NULL, 0775));
- cl_git_mkfile(
- "empty_standard_repo/ignore_also/file", "I'm going to be ignored!");
-
- memset(&counts, 0x0, sizeof(status_entry_counts));
- counts.expected_entry_count = 6;
- counts.expected_paths = paths_r;
- counts.expected_statuses = statuses_r;
-
- opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_RECURSE_IGNORED_DIRS;
-
- cl_git_pass(git_status_foreach_ext(
- g_repo, &opts, cb_status__normal, &counts));
-
- cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
- cl_assert_equal_i(0, counts.wrong_status_flags_count);
- cl_assert_equal_i(0, counts.wrong_sorted_path);
-
-
- memset(&counts, 0x0, sizeof(status_entry_counts));
- counts.expected_entry_count = 4;
- counts.expected_paths = paths_nr;
- counts.expected_statuses = statuses_nr;
-
- opts.flags = GIT_STATUS_OPT_DEFAULTS;
-
- cl_git_pass(git_status_foreach_ext(
- g_repo, &opts, cb_status__normal, &counts));
-
- cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
- cl_assert_equal_i(0, counts.wrong_status_flags_count);
- cl_assert_equal_i(0, counts.wrong_sorted_path);
-}
-
-void test_status_ignore__adding_internal_ignores(void)
-{
- int ignored;
-
- g_repo = cl_git_sandbox_init("empty_standard_repo");
-
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "one.txt"));
- cl_assert(!ignored);
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "two.bar"));
- cl_assert(!ignored);
-
- cl_git_pass(git_ignore_add_rule(g_repo, "*.nomatch\n"));
-
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "one.txt"));
- cl_assert(!ignored);
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "two.bar"));
- cl_assert(!ignored);
-
- cl_git_pass(git_ignore_add_rule(g_repo, "*.txt\n"));
-
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "one.txt"));
- cl_assert(ignored);
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "two.bar"));
- cl_assert(!ignored);
-
- cl_git_pass(git_ignore_add_rule(g_repo, "*.bar\n"));
-
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "one.txt"));
- cl_assert(ignored);
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "two.bar"));
- cl_assert(ignored);
-
- cl_git_pass(git_ignore_clear_internal_rules(g_repo));
-
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "one.txt"));
- cl_assert(!ignored);
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "two.bar"));
- cl_assert(!ignored);
-
- cl_git_pass(git_ignore_add_rule(
- g_repo, "multiple\n*.rules\n# comment line\n*.bar\n"));
-
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "one.txt"));
- cl_assert(!ignored);
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "two.bar"));
- cl_assert(ignored);
-}
-
-void test_status_ignore__add_internal_as_first_thing(void)
-{
- int ignored;
- const char *add_me = "\n#################\n## Eclipse\n#################\n\n*.pydevproject\n.project\n.metadata\nbin/\ntmp/\n*.tmp\n\n";
-
- g_repo = cl_git_sandbox_init("empty_standard_repo");
-
- cl_git_pass(git_ignore_add_rule(g_repo, add_me));
-
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "one.tmp"));
- cl_assert(ignored);
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "two.bar"));
- cl_assert(!ignored);
-}
-
-void test_status_ignore__internal_ignores_inside_deep_paths(void)
-{
- int ignored;
- const char *add_me = "Debug\nthis/is/deep\npatterned*/dir\n";
-
- g_repo = cl_git_sandbox_init("empty_standard_repo");
-
- cl_git_pass(git_ignore_add_rule(g_repo, add_me));
-
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "Debug"));
- cl_assert(ignored);
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "and/Debug"));
- cl_assert(ignored);
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "really/Debug/this/file"));
- cl_assert(ignored);
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "Debug/what/I/say"));
- cl_assert(ignored);
-
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "and/NoDebug"));
- cl_assert(!ignored);
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "NoDebug/this"));
- cl_assert(!ignored);
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "please/NoDebug/this"));
- cl_assert(!ignored);
-
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "this/is/deep"));
- cl_assert(ignored);
- /* pattern containing slash gets FNM_PATHNAME so all slashes must match */
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "and/this/is/deep"));
- cl_assert(!ignored);
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "this/is/deep/too"));
- cl_assert(ignored);
- /* pattern containing slash gets FNM_PATHNAME so all slashes must match */
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "but/this/is/deep/and/ignored"));
- cl_assert(!ignored);
-
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "this/is/not/deep"));
- cl_assert(!ignored);
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "is/this/not/as/deep"));
- cl_assert(!ignored);
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "this/is/deepish"));
- cl_assert(!ignored);
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "xthis/is/deep"));
- cl_assert(!ignored);
-}
-
-void test_status_ignore__automatically_ignore_bad_files(void)
-{
- int ignored;
-
- g_repo = cl_git_sandbox_init("empty_standard_repo");
-
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, ".git"));
- cl_assert(ignored);
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "this/file/."));
- cl_assert(ignored);
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "path/../funky"));
- cl_assert(ignored);
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "path/whatever.c"));
- cl_assert(!ignored);
-
- cl_git_pass(git_ignore_add_rule(g_repo, "*.c\n"));
-
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, ".git"));
- cl_assert(ignored);
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "this/file/."));
- cl_assert(ignored);
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "path/../funky"));
- cl_assert(ignored);
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "path/whatever.c"));
- cl_assert(ignored);
-
- cl_git_pass(git_ignore_clear_internal_rules(g_repo));
-
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, ".git"));
- cl_assert(ignored);
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "this/file/."));
- cl_assert(ignored);
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "path/../funky"));
- cl_assert(ignored);
- cl_git_pass(git_status_should_ignore(&ignored, g_repo, "path/whatever.c"));
- cl_assert(!ignored);
-}
diff --git a/tests-clar/status/renames.c b/tests-clar/status/renames.c
deleted file mode 100644
index 80ff26020..000000000
--- a/tests-clar/status/renames.c
+++ /dev/null
@@ -1,385 +0,0 @@
-#include "clar_libgit2.h"
-#include "buffer.h"
-#include "path.h"
-#include "posix.h"
-#include "status_helpers.h"
-#include "util.h"
-#include "status.h"
-
-static git_repository *g_repo = NULL;
-
-void test_status_renames__initialize(void)
-{
- g_repo = cl_git_sandbox_init("renames");
-}
-
-void test_status_renames__cleanup(void)
-{
- cl_git_sandbox_cleanup();
-}
-
-static void rename_file(git_repository *repo, const char *oldname, const char *newname)
-{
- git_buf oldpath = GIT_BUF_INIT, newpath = GIT_BUF_INIT;
-
- git_buf_joinpath(&oldpath, git_repository_workdir(repo), oldname);
- git_buf_joinpath(&newpath, git_repository_workdir(repo), newname);
-
- cl_git_pass(p_rename(oldpath.ptr, newpath.ptr));
-
- git_buf_free(&oldpath);
- git_buf_free(&newpath);
-}
-
-static void rename_and_edit_file(git_repository *repo, const char *oldname, const char *newname)
-{
- git_buf oldpath = GIT_BUF_INIT, newpath = GIT_BUF_INIT;
-
- git_buf_joinpath(&oldpath, git_repository_workdir(repo), oldname);
- git_buf_joinpath(&newpath, git_repository_workdir(repo), newname);
-
- cl_git_pass(p_rename(oldpath.ptr, newpath.ptr));
- cl_git_append2file(newpath.ptr, "Added at the end to keep similarity!");
-
- git_buf_free(&oldpath);
- git_buf_free(&newpath);
-}
-
-struct status_entry {
- git_status_t status;
- const char *oldname;
- const char *newname;
-};
-
-static void test_status(
- git_status_list *status_list,
- struct status_entry *expected_list,
- size_t expected_len)
-{
- const git_status_entry *actual;
- const struct status_entry *expected;
- const char *oldname, *newname;
- size_t i;
-
- cl_assert_equal_sz(expected_len, git_status_list_entrycount(status_list));
-
- for (i = 0; i < expected_len; i++) {
- actual = git_status_byindex(status_list, i);
- expected = &expected_list[i];
-
- cl_assert_equal_i((int)expected->status, (int)actual->status);
-
- oldname = actual->head_to_index ? actual->head_to_index->old_file.path :
- actual->index_to_workdir ? actual->index_to_workdir->old_file.path : NULL;
-
- newname = actual->index_to_workdir ? actual->index_to_workdir->new_file.path :
- actual->head_to_index ? actual->head_to_index->new_file.path : NULL;
-
- if (oldname)
- cl_assert(git__strcmp(oldname, expected->oldname) == 0);
- else
- cl_assert(expected->oldname == NULL);
-
- if (newname)
- cl_assert(git__strcmp(newname, expected->newname) == 0);
- else
- cl_assert(expected->newname == NULL);
- }
-}
-
-void test_status_renames__head2index_one(void)
-{
- git_index *index;
- git_status_list *statuslist;
- git_status_options opts = GIT_STATUS_OPTIONS_INIT;
- struct status_entry expected[] = {
- { GIT_STATUS_INDEX_RENAMED, "ikeepsix.txt", "newname.txt" },
- };
-
- opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
-
- cl_git_pass(git_repository_index(&index, g_repo));
-
- rename_file(g_repo, "ikeepsix.txt", "newname.txt");
-
- cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
- cl_git_pass(git_index_add_bypath(index, "newname.txt"));
- cl_git_pass(git_index_write(index));
-
- cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
- test_status(statuslist, expected, 1);
- git_status_list_free(statuslist);
-
- git_index_free(index);
-}
-
-void test_status_renames__head2index_two(void)
-{
- git_index *index;
- git_status_list *statuslist;
- git_status_options opts = GIT_STATUS_OPTIONS_INIT;
- struct status_entry expected[] = {
- { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED,
- "sixserving.txt", "aaa.txt" },
- { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED,
- "untimely.txt", "bbb.txt" },
- { GIT_STATUS_INDEX_RENAMED, "songof7cities.txt", "ccc.txt" },
- { GIT_STATUS_INDEX_RENAMED, "ikeepsix.txt", "ddd.txt" },
- };
-
- opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
-
- cl_git_pass(git_repository_index(&index, g_repo));
-
- rename_file(g_repo, "ikeepsix.txt", "ddd.txt");
- rename_and_edit_file(g_repo, "sixserving.txt", "aaa.txt");
- rename_file(g_repo, "songof7cities.txt", "ccc.txt");
- rename_and_edit_file(g_repo, "untimely.txt", "bbb.txt");
-
- cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
- cl_git_pass(git_index_remove_bypath(index, "sixserving.txt"));
- cl_git_pass(git_index_remove_bypath(index, "songof7cities.txt"));
- cl_git_pass(git_index_remove_bypath(index, "untimely.txt"));
- cl_git_pass(git_index_add_bypath(index, "ddd.txt"));
- cl_git_pass(git_index_add_bypath(index, "aaa.txt"));
- cl_git_pass(git_index_add_bypath(index, "ccc.txt"));
- cl_git_pass(git_index_add_bypath(index, "bbb.txt"));
- cl_git_pass(git_index_write(index));
-
- cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
- test_status(statuslist, expected, 4);
- git_status_list_free(statuslist);
-
- git_index_free(index);
-}
-
-void test_status_renames__index2workdir_one(void)
-{
- git_status_list *statuslist;
- git_status_options opts = GIT_STATUS_OPTIONS_INIT;
- struct status_entry expected[] = {
- { GIT_STATUS_WT_RENAMED, "ikeepsix.txt", "newname.txt" },
- };
-
- opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
- opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
-
- rename_file(g_repo, "ikeepsix.txt", "newname.txt");
-
- cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
- test_status(statuslist, expected, 1);
- git_status_list_free(statuslist);
-}
-
-void test_status_renames__index2workdir_two(void)
-{
- git_status_list *statuslist;
- git_status_options opts = GIT_STATUS_OPTIONS_INIT;
- struct status_entry expected[] = {
- { GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
- "sixserving.txt", "aaa.txt" },
- { GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
- "untimely.txt", "bbb.txt" },
- { GIT_STATUS_WT_RENAMED, "songof7cities.txt", "ccc.txt" },
- { GIT_STATUS_WT_RENAMED, "ikeepsix.txt", "ddd.txt" },
- };
-
- opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
- opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
-
- rename_file(g_repo, "ikeepsix.txt", "ddd.txt");
- rename_and_edit_file(g_repo, "sixserving.txt", "aaa.txt");
- rename_file(g_repo, "songof7cities.txt", "ccc.txt");
- rename_and_edit_file(g_repo, "untimely.txt", "bbb.txt");
-
- cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
- test_status(statuslist, expected, 4);
- git_status_list_free(statuslist);
-}
-
-void test_status_renames__both_one(void)
-{
- git_index *index;
- git_status_list *statuslist;
- git_status_options opts = GIT_STATUS_OPTIONS_INIT;
- struct status_entry expected[] = {
- { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
- "ikeepsix.txt", "newname-workdir.txt" },
- };
-
- opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
- opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
- opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
-
- cl_git_pass(git_repository_index(&index, g_repo));
-
- rename_file(g_repo, "ikeepsix.txt", "newname-index.txt");
-
- cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
- cl_git_pass(git_index_add_bypath(index, "newname-index.txt"));
- cl_git_pass(git_index_write(index));
-
- rename_file(g_repo, "newname-index.txt", "newname-workdir.txt");
-
- cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
- test_status(statuslist, expected, 1);
- git_status_list_free(statuslist);
-
- git_index_free(index);
-}
-
-void test_status_renames__both_two(void)
-{
- git_index *index;
- git_status_list *statuslist;
- git_status_options opts = GIT_STATUS_OPTIONS_INIT;
- struct status_entry expected[] = {
- { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED |
- GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
- "ikeepsix.txt", "ikeepsix-both.txt" },
- { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED,
- "sixserving.txt", "sixserving-index.txt" },
- { GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
- "songof7cities.txt", "songof7cities-workdir.txt" },
- { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
- "untimely.txt", "untimely-both.txt" },
- };
-
- opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
- opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
- opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
-
- cl_git_pass(git_repository_index(&index, g_repo));
-
- rename_and_edit_file(g_repo, "ikeepsix.txt", "ikeepsix-index.txt");
- rename_and_edit_file(g_repo, "sixserving.txt", "sixserving-index.txt");
- rename_file(g_repo, "untimely.txt", "untimely-index.txt");
-
- cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
- cl_git_pass(git_index_remove_bypath(index, "sixserving.txt"));
- cl_git_pass(git_index_remove_bypath(index, "untimely.txt"));
- cl_git_pass(git_index_add_bypath(index, "ikeepsix-index.txt"));
- cl_git_pass(git_index_add_bypath(index, "sixserving-index.txt"));
- cl_git_pass(git_index_add_bypath(index, "untimely-index.txt"));
- cl_git_pass(git_index_write(index));
-
- rename_and_edit_file(g_repo, "ikeepsix-index.txt", "ikeepsix-both.txt");
- rename_and_edit_file(g_repo, "songof7cities.txt", "songof7cities-workdir.txt");
- rename_file(g_repo, "untimely-index.txt", "untimely-both.txt");
-
- cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
- test_status(statuslist, expected, 4);
- git_status_list_free(statuslist);
-
- git_index_free(index);
-}
-
-void test_status_renames__both_casechange_one(void)
-{
- git_index *index;
- git_status_list *statuslist;
- git_status_options opts = GIT_STATUS_OPTIONS_INIT;
- int index_caps;
- struct status_entry expected_icase[] = {
- { GIT_STATUS_INDEX_RENAMED,
- "ikeepsix.txt", "IKeepSix.txt" },
- };
- struct status_entry expected_case[] = {
- { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
- "ikeepsix.txt", "IKEEPSIX.txt" },
- };
-
- opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
- opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
- opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
-
- cl_git_pass(git_repository_index(&index, g_repo));
- index_caps = git_index_caps(index);
-
- rename_file(g_repo, "ikeepsix.txt", "IKeepSix.txt");
-
- cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
- cl_git_pass(git_index_add_bypath(index, "IKeepSix.txt"));
- cl_git_pass(git_index_write(index));
-
- /* on a case-insensitive file system, this change won't matter.
- * on a case-sensitive one, it will.
- */
- rename_file(g_repo, "IKeepSix.txt", "IKEEPSIX.txt");
-
- cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
-
- test_status(statuslist, (index_caps & GIT_INDEXCAP_IGNORE_CASE) ?
- expected_icase : expected_case, 1);
-
- git_status_list_free(statuslist);
-
- git_index_free(index);
-}
-
-void test_status_renames__both_casechange_two(void)
-{
- git_index *index;
- git_status_list *statuslist;
- git_status_options opts = GIT_STATUS_OPTIONS_INIT;
- int index_caps;
- struct status_entry expected_icase[] = {
- { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED |
- GIT_STATUS_WT_MODIFIED,
- "ikeepsix.txt", "IKeepSix.txt" },
- { GIT_STATUS_INDEX_MODIFIED,
- "sixserving.txt", "sixserving.txt" },
- { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_MODIFIED,
- "songof7cities.txt", "songof7.txt" },
- { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
- "untimely.txt", "untimeliest.txt" }
- };
- struct status_entry expected_case[] = {
- { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED |
- GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
- "ikeepsix.txt", "ikeepsix.txt" },
- { GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_RENAMED,
- "sixserving.txt", "SixServing.txt" },
- { GIT_STATUS_INDEX_RENAMED |
- GIT_STATUS_WT_MODIFIED | GIT_STATUS_WT_RENAMED,
- "songof7cities.txt", "SONGOF7.txt" },
- { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
- "untimely.txt", "untimeliest.txt" }
- };
-
- opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
- opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
- opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
-
- cl_git_pass(git_repository_index(&index, g_repo));
- index_caps = git_index_caps(index);
-
- rename_and_edit_file(g_repo, "ikeepsix.txt", "IKeepSix.txt");
- rename_and_edit_file(g_repo, "sixserving.txt", "sixserving.txt");
- rename_file(g_repo, "songof7cities.txt", "songof7.txt");
- rename_file(g_repo, "untimely.txt", "untimelier.txt");
-
- cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
- cl_git_pass(git_index_remove_bypath(index, "sixserving.txt"));
- cl_git_pass(git_index_remove_bypath(index, "songof7cities.txt"));
- cl_git_pass(git_index_remove_bypath(index, "untimely.txt"));
- cl_git_pass(git_index_add_bypath(index, "IKeepSix.txt"));
- cl_git_pass(git_index_add_bypath(index, "sixserving.txt"));
- cl_git_pass(git_index_add_bypath(index, "songof7.txt"));
- cl_git_pass(git_index_add_bypath(index, "untimelier.txt"));
- cl_git_pass(git_index_write(index));
-
- rename_and_edit_file(g_repo, "IKeepSix.txt", "ikeepsix.txt");
- rename_file(g_repo, "sixserving.txt", "SixServing.txt");
- rename_and_edit_file(g_repo, "songof7.txt", "SONGOF7.txt");
- rename_file(g_repo, "untimelier.txt", "untimeliest.txt");
-
- cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
-
- test_status(statuslist, (index_caps & GIT_INDEXCAP_IGNORE_CASE) ?
- expected_icase : expected_case, 4);
-
- git_status_list_free(statuslist);
-
- git_index_free(index);
-}
diff --git a/tests-clar/status/status_data.h b/tests-clar/status/status_data.h
deleted file mode 100644
index a41bde7c2..000000000
--- a/tests-clar/status/status_data.h
+++ /dev/null
@@ -1,252 +0,0 @@
-#include "status_helpers.h"
-
-/* entries for a plain copy of tests/resources/status */
-
-static const char *entry_paths0[] = {
- "file_deleted",
- "ignored_file",
- "modified_file",
- "new_file",
- "staged_changes",
- "staged_changes_file_deleted",
- "staged_changes_modified_file",
- "staged_delete_file_deleted",
- "staged_delete_modified_file",
- "staged_new_file",
- "staged_new_file_deleted_file",
- "staged_new_file_modified_file",
-
- "subdir/deleted_file",
- "subdir/modified_file",
- "subdir/new_file",
-
- "\xe8\xbf\x99",
-};
-
-static const unsigned int entry_statuses0[] = {
- GIT_STATUS_WT_DELETED,
- GIT_STATUS_IGNORED,
- GIT_STATUS_WT_MODIFIED,
- GIT_STATUS_WT_NEW,
- GIT_STATUS_INDEX_MODIFIED,
- GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_DELETED,
- GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_MODIFIED,
- GIT_STATUS_INDEX_DELETED,
- GIT_STATUS_INDEX_DELETED | GIT_STATUS_WT_NEW,
- GIT_STATUS_INDEX_NEW,
- GIT_STATUS_INDEX_NEW | GIT_STATUS_WT_DELETED,
- GIT_STATUS_INDEX_NEW | GIT_STATUS_WT_MODIFIED,
-
- GIT_STATUS_WT_DELETED,
- GIT_STATUS_WT_MODIFIED,
- GIT_STATUS_WT_NEW,
-
- GIT_STATUS_WT_NEW,
-};
-
-static const int entry_count0 = 16;
-
-/* entries for a copy of tests/resources/status with all content
- * deleted from the working directory
- */
-
-static const char *entry_paths2[] = {
- "current_file",
- "file_deleted",
- "modified_file",
- "staged_changes",
- "staged_changes_file_deleted",
- "staged_changes_modified_file",
- "staged_delete_file_deleted",
- "staged_delete_modified_file",
- "staged_new_file",
- "staged_new_file_deleted_file",
- "staged_new_file_modified_file",
- "subdir.txt",
- "subdir/current_file",
- "subdir/deleted_file",
- "subdir/modified_file",
-};
-
-static const unsigned int entry_statuses2[] = {
- GIT_STATUS_WT_DELETED,
- GIT_STATUS_WT_DELETED,
- GIT_STATUS_WT_DELETED,
- GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
- GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
- GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
- GIT_STATUS_INDEX_DELETED,
- GIT_STATUS_INDEX_DELETED,
- GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW,
- GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW,
- GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW,
- GIT_STATUS_WT_DELETED,
- GIT_STATUS_WT_DELETED,
- GIT_STATUS_WT_DELETED,
- GIT_STATUS_WT_DELETED,
-};
-
-static const int entry_count2 = 15;
-
-/* entries for a copy of tests/resources/status with some mods */
-
-static const char *entry_paths3_icase[] = {
- ".HEADER",
- "42-is-not-prime.sigh",
- "current_file",
- "current_file/",
- "file_deleted",
- "ignored_file",
- "modified_file",
- "new_file",
- "README.md",
- "staged_changes",
- "staged_changes_file_deleted",
- "staged_changes_modified_file",
- "staged_delete_file_deleted",
- "staged_delete_modified_file",
- "staged_new_file",
- "staged_new_file_deleted_file",
- "staged_new_file_modified_file",
- "subdir",
- "subdir/current_file",
- "subdir/deleted_file",
- "subdir/modified_file",
- "\xe8\xbf\x99",
-};
-
-static const unsigned int entry_statuses3_icase[] = {
- GIT_STATUS_WT_NEW,
- GIT_STATUS_WT_NEW,
- GIT_STATUS_WT_DELETED,
- GIT_STATUS_WT_NEW,
- GIT_STATUS_WT_DELETED,
- GIT_STATUS_IGNORED,
- GIT_STATUS_WT_MODIFIED,
- GIT_STATUS_WT_NEW,
- GIT_STATUS_WT_NEW,
- GIT_STATUS_INDEX_MODIFIED,
- GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
- GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED,
- GIT_STATUS_INDEX_DELETED,
- GIT_STATUS_WT_NEW | GIT_STATUS_INDEX_DELETED,
- GIT_STATUS_INDEX_NEW,
- GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW,
- GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_NEW,
- GIT_STATUS_WT_NEW,
- GIT_STATUS_WT_DELETED,
- GIT_STATUS_WT_DELETED,
- GIT_STATUS_WT_DELETED,
- GIT_STATUS_WT_NEW,
-};
-
-static const char *entry_paths3[] = {
- ".HEADER",
- "42-is-not-prime.sigh",
- "README.md",
- "current_file",
- "current_file/",
- "file_deleted",
- "ignored_file",
- "modified_file",
- "new_file",
- "staged_changes",
- "staged_changes_file_deleted",
- "staged_changes_modified_file",
- "staged_delete_file_deleted",
- "staged_delete_modified_file",
- "staged_new_file",
- "staged_new_file_deleted_file",
- "staged_new_file_modified_file",
- "subdir",
- "subdir/current_file",
- "subdir/deleted_file",
- "subdir/modified_file",
- "\xe8\xbf\x99",
-};
-
-static const unsigned int entry_statuses3[] = {
- GIT_STATUS_WT_NEW,
- GIT_STATUS_WT_NEW,
- GIT_STATUS_WT_NEW,
- GIT_STATUS_WT_DELETED,
- GIT_STATUS_WT_NEW,
- GIT_STATUS_WT_DELETED,
- GIT_STATUS_IGNORED,
- GIT_STATUS_WT_MODIFIED,
- GIT_STATUS_WT_NEW,
- GIT_STATUS_INDEX_MODIFIED,
- GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
- GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED,
- GIT_STATUS_INDEX_DELETED,
- GIT_STATUS_WT_NEW | GIT_STATUS_INDEX_DELETED,
- GIT_STATUS_INDEX_NEW,
- GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW,
- GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_NEW,
- GIT_STATUS_WT_NEW,
- GIT_STATUS_WT_DELETED,
- GIT_STATUS_WT_DELETED,
- GIT_STATUS_WT_DELETED,
- GIT_STATUS_WT_NEW,
-};
-
-static const int entry_count3 = 22;
-
-
-/* entries for a copy of tests/resources/status with some mods
- * and different options to the status call
- */
-
-static const char *entry_paths4[] = {
- ".new_file",
- "current_file",
- "current_file/current_file",
- "current_file/modified_file",
- "current_file/new_file",
- "file_deleted",
- "modified_file",
- "new_file",
- "staged_changes",
- "staged_changes_file_deleted",
- "staged_changes_modified_file",
- "staged_delete_file_deleted",
- "staged_delete_modified_file",
- "staged_new_file",
- "staged_new_file_deleted_file",
- "staged_new_file_modified_file",
- "subdir",
- "subdir/current_file",
- "subdir/deleted_file",
- "subdir/modified_file",
- "zzz_new_dir/new_file",
- "zzz_new_file",
- "\xe8\xbf\x99",
-};
-
-static const unsigned int entry_statuses4[] = {
- GIT_STATUS_WT_NEW,
- GIT_STATUS_WT_DELETED,
- GIT_STATUS_WT_NEW,
- GIT_STATUS_WT_NEW,
- GIT_STATUS_WT_NEW,
- GIT_STATUS_WT_DELETED,
- GIT_STATUS_WT_MODIFIED,
- GIT_STATUS_WT_NEW,
- GIT_STATUS_INDEX_MODIFIED,
- GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
- GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED,
- GIT_STATUS_INDEX_DELETED,
- GIT_STATUS_WT_NEW | GIT_STATUS_INDEX_DELETED,
- GIT_STATUS_INDEX_NEW,
- GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW,
- GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_NEW,
- GIT_STATUS_WT_NEW,
- GIT_STATUS_WT_DELETED,
- GIT_STATUS_WT_DELETED,
- GIT_STATUS_WT_DELETED,
- GIT_STATUS_WT_NEW,
- GIT_STATUS_WT_NEW,
- GIT_STATUS_WT_NEW,
-};
-
-static const int entry_count4 = 23;
diff --git a/tests-clar/status/submodules.c b/tests-clar/status/submodules.c
deleted file mode 100644
index af8707721..000000000
--- a/tests-clar/status/submodules.c
+++ /dev/null
@@ -1,221 +0,0 @@
-#include "clar_libgit2.h"
-#include "buffer.h"
-#include "path.h"
-#include "posix.h"
-#include "status_helpers.h"
-#include "../submodule/submodule_helpers.h"
-
-static git_repository *g_repo = NULL;
-
-void test_status_submodules__initialize(void)
-{
- g_repo = cl_git_sandbox_init("submodules");
-
- cl_fixture_sandbox("testrepo.git");
-
- rewrite_gitmodules(git_repository_workdir(g_repo));
-
- p_rename("submodules/testrepo/.gitted", "submodules/testrepo/.git");
-}
-
-void test_status_submodules__cleanup(void)
-{
- cl_git_sandbox_cleanup();
- cl_fixture_cleanup("testrepo.git");
-}
-
-void test_status_submodules__api(void)
-{
- git_submodule *sm;
-
- cl_assert(git_submodule_lookup(NULL, g_repo, "nonexistent") == GIT_ENOTFOUND);
-
- cl_assert(git_submodule_lookup(NULL, g_repo, "modified") == GIT_ENOTFOUND);
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
- cl_assert(sm != NULL);
- cl_assert_equal_s("testrepo", git_submodule_name(sm));
- cl_assert_equal_s("testrepo", git_submodule_path(sm));
-}
-
-void test_status_submodules__0(void)
-{
- int counts = 0;
-
- cl_assert(git_path_isdir("submodules/.git"));
- cl_assert(git_path_isdir("submodules/testrepo/.git"));
- cl_assert(git_path_isfile("submodules/.gitmodules"));
-
- cl_git_pass(
- git_status_foreach(g_repo, cb_status__count, &counts)
- );
-
- cl_assert_equal_i(6, counts);
-}
-
-static const char *expected_files[] = {
- ".gitmodules",
- "added",
- "deleted",
- "ignored",
- "modified",
- "untracked"
-};
-
-static unsigned int expected_status[] = {
- GIT_STATUS_WT_MODIFIED,
- GIT_STATUS_INDEX_NEW,
- GIT_STATUS_INDEX_DELETED,
- GIT_STATUS_IGNORED,
- GIT_STATUS_WT_MODIFIED,
- GIT_STATUS_WT_NEW
-};
-
-static int cb_status__match(const char *p, unsigned int s, void *payload)
-{
- status_entry_counts *counts = payload;
- int idx = counts->entry_count++;
-
- cl_assert_equal_s(counts->expected_paths[idx], p);
- cl_assert(counts->expected_statuses[idx] == s);
-
- return 0;
-}
-
-void test_status_submodules__1(void)
-{
- status_entry_counts counts;
-
- cl_assert(git_path_isdir("submodules/.git"));
- cl_assert(git_path_isdir("submodules/testrepo/.git"));
- cl_assert(git_path_isfile("submodules/.gitmodules"));
-
- memset(&counts, 0, sizeof(counts));
- counts.expected_paths = expected_files;
- counts.expected_statuses = expected_status;
-
- cl_git_pass(
- git_status_foreach(g_repo, cb_status__match, &counts)
- );
-
- cl_assert_equal_i(6, counts.entry_count);
-}
-
-void test_status_submodules__single_file(void)
-{
- unsigned int status = 0;
- cl_git_pass( git_status_file(&status, g_repo, "testrepo") );
- cl_assert(!status);
-}
-
-void test_status_submodules__moved_head(void)
-{
- git_submodule *sm;
- git_repository *smrepo;
- git_oid oid;
- git_status_options opts = GIT_STATUS_OPTIONS_INIT;
- status_entry_counts counts;
- static const char *expected_files_with_sub[] = {
- ".gitmodules",
- "added",
- "deleted",
- "ignored",
- "modified",
- "testrepo",
- "untracked"
- };
- static unsigned int expected_status_with_sub[] = {
- GIT_STATUS_WT_MODIFIED,
- GIT_STATUS_INDEX_NEW,
- GIT_STATUS_INDEX_DELETED,
- GIT_STATUS_IGNORED,
- GIT_STATUS_WT_MODIFIED,
- GIT_STATUS_WT_MODIFIED,
- GIT_STATUS_WT_NEW
- };
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
- cl_git_pass(git_submodule_open(&smrepo, sm));
-
- /* move submodule HEAD to c47800c7266a2be04c571c04d5a6614691ea99bd */
- cl_git_pass(
- git_oid_fromstr(&oid, "c47800c7266a2be04c571c04d5a6614691ea99bd"));
- cl_git_pass(git_repository_set_head_detached(smrepo, &oid));
-
- /* first do a normal status, which should now include the submodule */
-
- memset(&counts, 0, sizeof(counts));
- counts.expected_paths = expected_files_with_sub;
- counts.expected_statuses = expected_status_with_sub;
-
- opts.flags = GIT_STATUS_OPT_DEFAULTS;
-
- cl_git_pass(
- git_status_foreach_ext(g_repo, &opts, cb_status__match, &counts));
- cl_assert_equal_i(7, counts.entry_count);
-
- /* try again with EXCLUDE_SUBMODULES which should skip it */
-
- memset(&counts, 0, sizeof(counts));
- counts.expected_paths = expected_files;
- counts.expected_statuses = expected_status;
-
- opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
-
- cl_git_pass(
- git_status_foreach_ext(g_repo, &opts, cb_status__match, &counts));
- cl_assert_equal_i(6, counts.entry_count);
-
- git_repository_free(smrepo);
-}
-
-void test_status_submodules__dirty_workdir_only(void)
-{
- git_status_options opts = GIT_STATUS_OPTIONS_INIT;
- status_entry_counts counts;
- static const char *expected_files_with_sub[] = {
- ".gitmodules",
- "added",
- "deleted",
- "ignored",
- "modified",
- "testrepo",
- "untracked"
- };
- static unsigned int expected_status_with_sub[] = {
- GIT_STATUS_WT_MODIFIED,
- GIT_STATUS_INDEX_NEW,
- GIT_STATUS_INDEX_DELETED,
- GIT_STATUS_IGNORED,
- GIT_STATUS_WT_MODIFIED,
- GIT_STATUS_WT_MODIFIED,
- GIT_STATUS_WT_NEW
- };
-
- cl_git_rewritefile("submodules/testrepo/README", "heyheyhey");
- cl_git_mkfile("submodules/testrepo/all_new.txt", "never seen before");
-
- /* first do a normal status, which should now include the submodule */
-
- memset(&counts, 0, sizeof(counts));
- counts.expected_paths = expected_files_with_sub;
- counts.expected_statuses = expected_status_with_sub;
-
- opts.flags = GIT_STATUS_OPT_DEFAULTS;
-
- cl_git_pass(
- git_status_foreach_ext(g_repo, &opts, cb_status__match, &counts));
- cl_assert_equal_i(7, counts.entry_count);
-
- /* try again with EXCLUDE_SUBMODULES which should skip it */
-
- memset(&counts, 0, sizeof(counts));
- counts.expected_paths = expected_files;
- counts.expected_statuses = expected_status;
-
- opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
-
- cl_git_pass(
- git_status_foreach_ext(g_repo, &opts, cb_status__match, &counts));
- cl_assert_equal_i(6, counts.entry_count);
-}
diff --git a/tests-clar/status/worktree.c b/tests-clar/status/worktree.c
deleted file mode 100644
index 920671e13..000000000
--- a/tests-clar/status/worktree.c
+++ /dev/null
@@ -1,825 +0,0 @@
-#include "clar_libgit2.h"
-#include "fileops.h"
-#include "ignore.h"
-#include "status_data.h"
-#include "posix.h"
-#include "util.h"
-#include "path.h"
-
-/**
- * Cleanup
- *
- * This will be called once after each test finishes, even
- * if the test failed
- */
-void test_status_worktree__cleanup(void)
-{
- cl_git_sandbox_cleanup();
-}
-
-/**
- * Tests - Status determination on a working tree
- */
-/* this test is equivalent to t18-status.c:statuscb0 */
-void test_status_worktree__whole_repository(void)
-{
- status_entry_counts counts;
- git_repository *repo = cl_git_sandbox_init("status");
-
- memset(&counts, 0x0, sizeof(status_entry_counts));
- counts.expected_entry_count = entry_count0;
- counts.expected_paths = entry_paths0;
- counts.expected_statuses = entry_statuses0;
-
- cl_git_pass(
- git_status_foreach(repo, cb_status__normal, &counts)
- );
-
- cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
- cl_assert_equal_i(0, counts.wrong_status_flags_count);
- cl_assert_equal_i(0, counts.wrong_sorted_path);
-}
-
-/* this test is equivalent to t18-status.c:statuscb1 */
-void test_status_worktree__empty_repository(void)
-{
- int count = 0;
- git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
-
- cl_git_pass(git_status_foreach(repo, cb_status__count, &count));
-
- cl_assert_equal_i(0, count);
-}
-
-static int remove_file_cb(void *data, git_buf *file)
-{
- const char *filename = git_buf_cstr(file);
-
- GIT_UNUSED(data);
-
- if (git__suffixcmp(filename, ".git") == 0)
- return 0;
-
- if (git_path_isdir(filename))
- cl_git_pass(git_futils_rmdir_r(filename, NULL, GIT_RMDIR_REMOVE_FILES));
- else
- cl_git_pass(p_unlink(git_buf_cstr(file)));
-
- return 0;
-}
-
-/* this test is equivalent to t18-status.c:statuscb2 */
-void test_status_worktree__purged_worktree(void)
-{
- status_entry_counts counts;
- git_repository *repo = cl_git_sandbox_init("status");
- git_buf workdir = GIT_BUF_INIT;
-
- /* first purge the contents of the worktree */
- cl_git_pass(git_buf_sets(&workdir, git_repository_workdir(repo)));
- cl_git_pass(git_path_direach(&workdir, remove_file_cb, NULL));
- git_buf_free(&workdir);
-
- /* now get status */
- memset(&counts, 0x0, sizeof(status_entry_counts));
- counts.expected_entry_count = entry_count2;
- counts.expected_paths = entry_paths2;
- counts.expected_statuses = entry_statuses2;
-
- cl_git_pass(
- git_status_foreach(repo, cb_status__normal, &counts)
- );
-
- cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
- cl_assert_equal_i(0, counts.wrong_status_flags_count);
- cl_assert_equal_i(0, counts.wrong_sorted_path);
-}
-
-/* this test is similar to t18-status.c:statuscb3 */
-void test_status_worktree__swap_subdir_and_file(void)
-{
- status_entry_counts counts;
- git_repository *repo = cl_git_sandbox_init("status");
- git_index *index;
- git_status_options opts = GIT_STATUS_OPTIONS_INIT;
- bool ignore_case;
-
- cl_git_pass(git_repository_index(&index, repo));
- ignore_case = (git_index_caps(index) & GIT_INDEXCAP_IGNORE_CASE) != 0;
- git_index_free(index);
-
- /* first alter the contents of the worktree */
- cl_git_pass(p_rename("status/current_file", "status/swap"));
- cl_git_pass(p_rename("status/subdir", "status/current_file"));
- cl_git_pass(p_rename("status/swap", "status/subdir"));
-
- cl_git_mkfile("status/.HEADER", "dummy");
- cl_git_mkfile("status/42-is-not-prime.sigh", "dummy");
- cl_git_mkfile("status/README.md", "dummy");
-
- /* now get status */
- memset(&counts, 0x0, sizeof(status_entry_counts));
- counts.expected_entry_count = entry_count3;
- counts.expected_paths = ignore_case ? entry_paths3_icase : entry_paths3;
- counts.expected_statuses = ignore_case ? entry_statuses3_icase : entry_statuses3;
-
- opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
- GIT_STATUS_OPT_INCLUDE_IGNORED;
-
- cl_git_pass(
- git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)
- );
-
- cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
- cl_assert_equal_i(0, counts.wrong_status_flags_count);
- cl_assert_equal_i(0, counts.wrong_sorted_path);
-}
-
-void test_status_worktree__swap_subdir_with_recurse_and_pathspec(void)
-{
- status_entry_counts counts;
- git_repository *repo = cl_git_sandbox_init("status");
- git_status_options opts = GIT_STATUS_OPTIONS_INIT;
-
- /* first alter the contents of the worktree */
- cl_git_pass(p_rename("status/current_file", "status/swap"));
- cl_git_pass(p_rename("status/subdir", "status/current_file"));
- cl_git_pass(p_rename("status/swap", "status/subdir"));
- cl_git_mkfile("status/.new_file", "dummy");
- cl_git_pass(git_futils_mkdir_r("status/zzz_new_dir", NULL, 0777));
- cl_git_mkfile("status/zzz_new_dir/new_file", "dummy");
- cl_git_mkfile("status/zzz_new_file", "dummy");
-
- /* now get status */
- memset(&counts, 0x0, sizeof(status_entry_counts));
- counts.expected_entry_count = entry_count4;
- counts.expected_paths = entry_paths4;
- counts.expected_statuses = entry_statuses4;
-
- opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
- GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
- /* TODO: set pathspec to "current_file" eventually */
-
- cl_git_pass(
- git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)
- );
-
- cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
- cl_assert_equal_i(0, counts.wrong_status_flags_count);
- cl_assert_equal_i(0, counts.wrong_sorted_path);
-}
-
-/* this test is equivalent to t18-status.c:singlestatus0 */
-void test_status_worktree__single_file(void)
-{
- int i;
- unsigned int status_flags;
- git_repository *repo = cl_git_sandbox_init("status");
-
- for (i = 0; i < (int)entry_count0; i++) {
- cl_git_pass(
- git_status_file(&status_flags, repo, entry_paths0[i])
- );
- cl_assert(entry_statuses0[i] == status_flags);
- }
-}
-
-/* this test is equivalent to t18-status.c:singlestatus1 */
-void test_status_worktree__single_nonexistent_file(void)
-{
- int error;
- unsigned int status_flags;
- git_repository *repo = cl_git_sandbox_init("status");
-
- error = git_status_file(&status_flags, repo, "nonexistent");
- cl_git_fail(error);
- cl_assert(error == GIT_ENOTFOUND);
-}
-
-/* this test is equivalent to t18-status.c:singlestatus2 */
-void test_status_worktree__single_nonexistent_file_empty_repo(void)
-{
- int error;
- unsigned int status_flags;
- git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
-
- error = git_status_file(&status_flags, repo, "nonexistent");
- cl_git_fail(error);
- cl_assert(error == GIT_ENOTFOUND);
-}
-
-/* this test is equivalent to t18-status.c:singlestatus3 */
-void test_status_worktree__single_file_empty_repo(void)
-{
- unsigned int status_flags;
- git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
-
- cl_git_mkfile("empty_standard_repo/new_file", "new_file\n");
-
- cl_git_pass(git_status_file(&status_flags, repo, "new_file"));
- cl_assert(status_flags == GIT_STATUS_WT_NEW);
-}
-
-/* this test is equivalent to t18-status.c:singlestatus4 */
-void test_status_worktree__single_folder(void)
-{
- int error;
- unsigned int status_flags;
- git_repository *repo = cl_git_sandbox_init("status");
-
- error = git_status_file(&status_flags, repo, "subdir");
- cl_git_fail(error);
- cl_assert(error != GIT_ENOTFOUND);
-}
-
-
-void test_status_worktree__ignores(void)
-{
- int i, ignored;
- git_repository *repo = cl_git_sandbox_init("status");
-
- for (i = 0; i < (int)entry_count0; i++) {
- cl_git_pass(
- git_status_should_ignore(&ignored, repo, entry_paths0[i])
- );
- cl_assert(ignored == (entry_statuses0[i] == GIT_STATUS_IGNORED));
- }
-
- cl_git_pass(
- git_status_should_ignore(&ignored, repo, "nonexistent_file")
- );
- cl_assert(!ignored);
-
- cl_git_pass(
- git_status_should_ignore(&ignored, repo, "ignored_nonexistent_file")
- );
- cl_assert(ignored);
-}
-
-static int cb_status__check_592(const char *p, unsigned int s, void *payload)
-{
- if (s != GIT_STATUS_WT_DELETED ||
- (payload != NULL && strcmp(p, (const char *)payload) != 0))
- return -1;
-
- return 0;
-}
-
-void test_status_worktree__issue_592(void)
-{
- git_repository *repo;
- git_buf path = GIT_BUF_INIT;
-
- repo = cl_git_sandbox_init("issue_592");
- cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(repo), "l.txt"));
- cl_git_pass(p_unlink(git_buf_cstr(&path)));
- cl_assert(!git_path_exists("issue_592/l.txt"));
-
- cl_git_pass(git_status_foreach(repo, cb_status__check_592, "l.txt"));
-
- git_buf_free(&path);
-}
-
-void test_status_worktree__issue_592_2(void)
-{
- git_repository *repo;
- git_buf path = GIT_BUF_INIT;
-
- repo = cl_git_sandbox_init("issue_592");
- cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(repo), "c/a.txt"));
- cl_git_pass(p_unlink(git_buf_cstr(&path)));
- cl_assert(!git_path_exists("issue_592/c/a.txt"));
-
- cl_git_pass(git_status_foreach(repo, cb_status__check_592, "c/a.txt"));
-
- git_buf_free(&path);
-}
-
-void test_status_worktree__issue_592_3(void)
-{
- git_repository *repo;
- git_buf path = GIT_BUF_INIT;
-
- repo = cl_git_sandbox_init("issue_592");
-
- cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(repo), "c"));
- cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES));
- cl_assert(!git_path_exists("issue_592/c/a.txt"));
-
- cl_git_pass(git_status_foreach(repo, cb_status__check_592, "c/a.txt"));
-
- git_buf_free(&path);
-}
-
-void test_status_worktree__issue_592_4(void)
-{
- git_repository *repo;
- git_buf path = GIT_BUF_INIT;
-
- repo = cl_git_sandbox_init("issue_592");
-
- cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(repo), "t/b.txt"));
- cl_git_pass(p_unlink(git_buf_cstr(&path)));
-
- cl_git_pass(git_status_foreach(repo, cb_status__check_592, "t/b.txt"));
-
- git_buf_free(&path);
-}
-
-void test_status_worktree__issue_592_5(void)
-{
- git_repository *repo;
- git_buf path = GIT_BUF_INIT;
-
- repo = cl_git_sandbox_init("issue_592");
-
- cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(repo), "t"));
- cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES));
- cl_git_pass(p_mkdir(git_buf_cstr(&path), 0777));
-
- cl_git_pass(git_status_foreach(repo, cb_status__check_592, NULL));
-
- git_buf_free(&path);
-}
-
-void test_status_worktree__issue_592_ignores_0(void)
-{
- int count = 0;
- status_entry_single st;
- git_repository *repo = cl_git_sandbox_init("issue_592");
-
- cl_git_pass(git_status_foreach(repo, cb_status__count, &count));
- cl_assert_equal_i(0, count);
-
- cl_git_rewritefile("issue_592/.gitignore",
- ".gitignore\n*.txt\nc/\n[tT]*/\n");
-
- cl_git_pass(git_status_foreach(repo, cb_status__count, &count));
- cl_assert_equal_i(1, count);
-
- /* This is a situation where the behavior of libgit2 is
- * different from core git. Core git will show ignored.txt
- * in the list of ignored files, even though the directory
- * "t" is ignored and the file is untracked because we have
- * the explicit "*.txt" ignore rule. Libgit2 just excludes
- * all untracked files that are contained within ignored
- * directories without explicitly listing them.
- */
- cl_git_rewritefile("issue_592/t/ignored.txt", "ping");
-
- memset(&st, 0, sizeof(st));
- cl_git_pass(git_status_foreach(repo, cb_status__single, &st));
- cl_assert_equal_i(1, st.count);
- cl_assert(st.status == GIT_STATUS_IGNORED);
-
- cl_git_rewritefile("issue_592/c/ignored_by_dir", "ping");
-
- memset(&st, 0, sizeof(st));
- cl_git_pass(git_status_foreach(repo, cb_status__single, &st));
- cl_assert_equal_i(1, st.count);
- cl_assert(st.status == GIT_STATUS_IGNORED);
-
- cl_git_rewritefile("issue_592/t/ignored_by_dir_pattern", "ping");
-
- memset(&st, 0, sizeof(st));
- cl_git_pass(git_status_foreach(repo, cb_status__single, &st));
- cl_assert_equal_i(1, st.count);
- cl_assert(st.status == GIT_STATUS_IGNORED);
-}
-
-void test_status_worktree__issue_592_ignored_dirs_with_tracked_content(void)
-{
- int count = 0;
- git_repository *repo = cl_git_sandbox_init("issue_592b");
-
- cl_git_pass(git_status_foreach(repo, cb_status__count, &count));
- cl_assert_equal_i(1, count);
-
- /* if we are really mimicking core git, then only ignored1.txt
- * at the top level will show up in the ignores list here.
- * everything else will be unmodified or skipped completely.
- */
-}
-
-void test_status_worktree__conflict_with_diff3(void)
-{
- git_repository *repo = cl_git_sandbox_init("status");
- git_index *index;
- unsigned int status;
- git_index_entry ancestor_entry, our_entry, their_entry;
-
- memset(&ancestor_entry, 0x0, sizeof(git_index_entry));
- memset(&our_entry, 0x0, sizeof(git_index_entry));
- memset(&their_entry, 0x0, sizeof(git_index_entry));
-
- ancestor_entry.path = "modified_file";
- git_oid_fromstr(&ancestor_entry.oid,
- "452e4244b5d083ddf0460acf1ecc74db9dcfa11a");
-
- our_entry.path = "modified_file";
- git_oid_fromstr(&our_entry.oid,
- "452e4244b5d083ddf0460acf1ecc74db9dcfa11a");
-
- their_entry.path = "modified_file";
- git_oid_fromstr(&their_entry.oid,
- "452e4244b5d083ddf0460acf1ecc74db9dcfa11a");
-
- cl_git_pass(git_status_file(&status, repo, "modified_file"));
- cl_assert_equal_i(GIT_STATUS_WT_MODIFIED, status);
-
- cl_git_pass(git_repository_index(&index, repo));
-
- cl_git_pass(git_index_remove(index, "modified_file", 0));
- cl_git_pass(git_index_conflict_add(index, &ancestor_entry,
- &our_entry, &their_entry));
-
- cl_git_pass(git_status_file(&status, repo, "modified_file"));
-
- cl_assert_equal_i(GIT_STATUS_INDEX_DELETED | GIT_STATUS_WT_NEW, status);
-
- git_index_free(index);
-}
-
-static const char *filemode_paths[] = {
- "exec_off",
- "exec_off2on_staged",
- "exec_off2on_workdir",
- "exec_off_untracked",
- "exec_on",
- "exec_on2off_staged",
- "exec_on2off_workdir",
- "exec_on_untracked",
-};
-
-static unsigned int filemode_statuses[] = {
- GIT_STATUS_CURRENT,
- GIT_STATUS_INDEX_MODIFIED,
- GIT_STATUS_WT_MODIFIED,
- GIT_STATUS_WT_NEW,
- GIT_STATUS_CURRENT,
- GIT_STATUS_INDEX_MODIFIED,
- GIT_STATUS_WT_MODIFIED,
- GIT_STATUS_WT_NEW
-};
-
-static const int filemode_count = 8;
-
-void test_status_worktree__filemode_changes(void)
-{
- git_repository *repo = cl_git_sandbox_init("filemodes");
- status_entry_counts counts;
- git_status_options opts = GIT_STATUS_OPTIONS_INIT;
-
- /* overwrite stored filemode with platform appropriate value */
- if (cl_is_chmod_supported())
- cl_repo_set_bool(repo, "core.filemode", true);
- else {
- int i;
-
- cl_repo_set_bool(repo, "core.filemode", false);
-
- /* won't trust filesystem mode diffs, so these will appear unchanged */
- for (i = 0; i < filemode_count; ++i)
- if (filemode_statuses[i] == GIT_STATUS_WT_MODIFIED)
- filemode_statuses[i] = GIT_STATUS_CURRENT;
- }
-
- opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
- GIT_STATUS_OPT_INCLUDE_IGNORED |
- GIT_STATUS_OPT_INCLUDE_UNMODIFIED;
-
- memset(&counts, 0, sizeof(counts));
- counts.expected_entry_count = filemode_count;
- counts.expected_paths = filemode_paths;
- counts.expected_statuses = filemode_statuses;
-
- cl_git_pass(
- git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)
- );
-
- cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
- cl_assert_equal_i(0, counts.wrong_status_flags_count);
- cl_assert_equal_i(0, counts.wrong_sorted_path);
-}
-
-static int cb_status__interrupt(const char *p, unsigned int s, void *payload)
-{
- volatile int *count = (int *)payload;
-
- GIT_UNUSED(p);
- GIT_UNUSED(s);
-
- (*count)++;
-
- return (*count == 8);
-}
-
-void test_status_worktree__interruptable_foreach(void)
-{
- int count = 0;
- git_repository *repo = cl_git_sandbox_init("status");
-
- cl_assert_equal_i(
- GIT_EUSER, git_status_foreach(repo, cb_status__interrupt, &count)
- );
-
- cl_assert_equal_i(8, count);
-}
-
-void test_status_worktree__line_endings_dont_count_as_changes_with_autocrlf(void)
-{
- git_repository *repo = cl_git_sandbox_init("status");
- unsigned int status;
-
- cl_repo_set_bool(repo, "core.autocrlf", true);
-
- cl_git_rewritefile("status/current_file", "current_file\r\n");
-
- cl_git_pass(git_status_file(&status, repo, "current_file"));
-
- cl_assert_equal_i(GIT_STATUS_CURRENT, status);
-}
-
-void test_status_worktree__line_endings_dont_count_as_changes_with_autocrlf_issue_1397(void)
-{
- git_repository *repo = cl_git_sandbox_init("issue_1397");
- unsigned int status;
-
- cl_repo_set_bool(repo, "core.autocrlf", true);
-
- cl_git_pass(git_status_file(&status, repo, "crlf_file.txt"));
-
- cl_assert_equal_i(GIT_STATUS_CURRENT, status);
-}
-
-void test_status_worktree__conflicted_item(void)
-{
- git_repository *repo = cl_git_sandbox_init("status");
- git_index *index;
- unsigned int status;
- git_index_entry ancestor_entry, our_entry, their_entry;
-
- memset(&ancestor_entry, 0x0, sizeof(git_index_entry));
- memset(&our_entry, 0x0, sizeof(git_index_entry));
- memset(&their_entry, 0x0, sizeof(git_index_entry));
-
- ancestor_entry.path = "modified_file";
- git_oid_fromstr(&ancestor_entry.oid,
- "452e4244b5d083ddf0460acf1ecc74db9dcfa11a");
-
- our_entry.path = "modified_file";
- git_oid_fromstr(&our_entry.oid,
- "452e4244b5d083ddf0460acf1ecc74db9dcfa11a");
-
- their_entry.path = "modified_file";
- git_oid_fromstr(&their_entry.oid,
- "452e4244b5d083ddf0460acf1ecc74db9dcfa11a");
-
- cl_git_pass(git_status_file(&status, repo, "modified_file"));
- cl_assert_equal_i(GIT_STATUS_WT_MODIFIED, status);
-
- cl_git_pass(git_repository_index(&index, repo));
- cl_git_pass(git_index_conflict_add(index, &ancestor_entry,
- &our_entry, &their_entry));
-
- cl_git_pass(git_status_file(&status, repo, "modified_file"));
- cl_assert_equal_i(GIT_STATUS_WT_MODIFIED, status);
-
- git_index_free(index);
-}
-
-static void stage_and_commit(git_repository *repo, const char *path)
-{
- git_oid tree_oid, commit_oid;
- git_tree *tree;
- git_signature *signature;
- git_index *index;
-
- cl_git_pass(git_repository_index(&index, repo));
- cl_git_pass(git_index_add_bypath(index, path));
- cl_git_pass(git_index_write(index));
-
- cl_git_pass(git_index_write_tree(&tree_oid, index));
- git_index_free(index);
-
- cl_git_pass(git_tree_lookup(&tree, repo, &tree_oid));
-
- cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60));
-
- cl_git_pass(git_commit_create_v(
- &commit_oid,
- repo,
- "HEAD",
- signature,
- signature,
- NULL,
- "Initial commit\n\0",
- tree,
- 0));
-
- git_tree_free(tree);
- git_signature_free(signature);
-}
-
-static void assert_ignore_case(
- bool should_ignore_case,
- int expected_lower_cased_file_status,
- int expected_camel_cased_file_status)
-{
- unsigned int status;
- git_buf lower_case_path = GIT_BUF_INIT, camel_case_path = GIT_BUF_INIT;
- git_repository *repo, *repo2;
-
- repo = cl_git_sandbox_init("empty_standard_repo");
- cl_git_remove_placeholders(git_repository_path(repo), "dummy-marker.txt");
-
- cl_repo_set_bool(repo, "core.ignorecase", should_ignore_case);
-
- cl_git_pass(git_buf_joinpath(&lower_case_path,
- git_repository_workdir(repo), "plop"));
-
- cl_git_mkfile(git_buf_cstr(&lower_case_path), "");
-
- stage_and_commit(repo, "plop");
-
- cl_git_pass(git_repository_open(&repo2, "./empty_standard_repo"));
-
- cl_git_pass(git_status_file(&status, repo2, "plop"));
- cl_assert_equal_i(GIT_STATUS_CURRENT, status);
-
- cl_git_pass(git_buf_joinpath(&camel_case_path,
- git_repository_workdir(repo), "Plop"));
-
- cl_git_pass(p_rename(git_buf_cstr(&lower_case_path), git_buf_cstr(&camel_case_path)));
-
- cl_git_pass(git_status_file(&status, repo2, "plop"));
- cl_assert_equal_i(expected_lower_cased_file_status, status);
-
- cl_git_pass(git_status_file(&status, repo2, "Plop"));
- cl_assert_equal_i(expected_camel_cased_file_status, status);
-
- git_repository_free(repo2);
- git_buf_free(&lower_case_path);
- git_buf_free(&camel_case_path);
-}
-
-void test_status_worktree__file_status_honors_core_ignorecase_true(void)
-{
- assert_ignore_case(true, GIT_STATUS_CURRENT, GIT_STATUS_CURRENT);
-}
-
-void test_status_worktree__file_status_honors_core_ignorecase_false(void)
-{
- assert_ignore_case(false, GIT_STATUS_WT_DELETED, GIT_STATUS_WT_NEW);
-}
-
-void test_status_worktree__file_status_honors_case_ignorecase_regarding_untracked_files(void)
-{
- git_repository *repo = cl_git_sandbox_init("status");
- unsigned int status;
- git_index *index;
-
- cl_repo_set_bool(repo, "core.ignorecase", false);
-
- repo = cl_git_sandbox_reopen();
-
- /* Actually returns GIT_STATUS_IGNORED on Windows */
- cl_git_fail_with(git_status_file(&status, repo, "NEW_FILE"), GIT_ENOTFOUND);
-
- cl_git_pass(git_repository_index(&index, repo));
-
- cl_git_pass(git_index_add_bypath(index, "new_file"));
- cl_git_pass(git_index_write(index));
- git_index_free(index);
-
- /* Actually returns GIT_STATUS_IGNORED on Windows */
- cl_git_fail_with(git_status_file(&status, repo, "NEW_FILE"), GIT_ENOTFOUND);
-}
-
-void test_status_worktree__simple_delete(void)
-{
- git_repository *repo = cl_git_sandbox_init("renames");
- git_status_options opts = GIT_STATUS_OPTIONS_INIT;
- int count;
-
- opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
- GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH |
- GIT_STATUS_OPT_EXCLUDE_SUBMODULES |
- GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
-
- count = 0;
- cl_git_pass(
- git_status_foreach_ext(repo, &opts, cb_status__count, &count) );
- cl_assert_equal_i(0, count);
-
- cl_must_pass(p_unlink("renames/untimely.txt"));
-
- count = 0;
- cl_git_pass(
- git_status_foreach_ext(repo, &opts, cb_status__count, &count) );
- cl_assert_equal_i(1, count);
-}
-
-void test_status_worktree__simple_delete_indexed(void)
-{
- git_repository *repo = cl_git_sandbox_init("renames");
- git_status_options opts = GIT_STATUS_OPTIONS_INIT;
- git_status_list *status;
-
- opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
- GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH |
- GIT_STATUS_OPT_EXCLUDE_SUBMODULES |
- GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
-
- cl_git_pass(git_status_list_new(&status, repo, &opts));
- cl_assert_equal_sz(0, git_status_list_entrycount(status));
- git_status_list_free(status);
-
- cl_must_pass(p_unlink("renames/untimely.txt"));
-
- cl_git_pass(git_status_list_new(&status, repo, &opts));
- cl_assert_equal_sz(1, git_status_list_entrycount(status));
- cl_assert_equal_i(
- GIT_STATUS_WT_DELETED, git_status_byindex(status, 0)->status);
- git_status_list_free(status);
-}
-
-static const char *icase_paths[] = { "B", "c", "g", "H" };
-static unsigned int icase_statuses[] = {
- GIT_STATUS_WT_MODIFIED, GIT_STATUS_WT_DELETED,
- GIT_STATUS_WT_MODIFIED, GIT_STATUS_WT_DELETED,
-};
-
-static const char *case_paths[] = { "B", "H", "c", "g" };
-static unsigned int case_statuses[] = {
- GIT_STATUS_WT_MODIFIED, GIT_STATUS_WT_DELETED,
- GIT_STATUS_WT_DELETED, GIT_STATUS_WT_MODIFIED,
-};
-
-void test_status_worktree__sorting_by_case(void)
-{
- git_repository *repo = cl_git_sandbox_init("icase");
- git_index *index;
- git_status_options opts = GIT_STATUS_OPTIONS_INIT;
- bool native_ignore_case;
- status_entry_counts counts;
-
- cl_git_pass(git_repository_index(&index, repo));
- native_ignore_case =
- (git_index_caps(index) & GIT_INDEXCAP_IGNORE_CASE) != 0;
- git_index_free(index);
-
- memset(&counts, 0, sizeof(counts));
- counts.expected_entry_count = 0;
- counts.expected_paths = NULL;
- counts.expected_statuses = NULL;
- cl_git_pass(
- git_status_foreach_ext(repo, &opts, cb_status__normal, &counts));
- cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
- cl_assert_equal_i(0, counts.wrong_status_flags_count);
- cl_assert_equal_i(0, counts.wrong_sorted_path);
-
- cl_git_rewritefile("icase/B", "new stuff");
- cl_must_pass(p_unlink("icase/c"));
- cl_git_rewritefile("icase/g", "new stuff");
- cl_must_pass(p_unlink("icase/H"));
-
- memset(&counts, 0, sizeof(counts));
- counts.expected_entry_count = 4;
- if (native_ignore_case) {
- counts.expected_paths = icase_paths;
- counts.expected_statuses = icase_statuses;
- } else {
- counts.expected_paths = case_paths;
- counts.expected_statuses = case_statuses;
- }
- cl_git_pass(
- git_status_foreach_ext(repo, &opts, cb_status__normal, &counts));
- cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
- cl_assert_equal_i(0, counts.wrong_status_flags_count);
- cl_assert_equal_i(0, counts.wrong_sorted_path);
-
- opts.flags = GIT_STATUS_OPT_SORT_CASE_SENSITIVELY;
-
- memset(&counts, 0, sizeof(counts));
- counts.expected_entry_count = 4;
- counts.expected_paths = case_paths;
- counts.expected_statuses = case_statuses;
- cl_git_pass(
- git_status_foreach_ext(repo, &opts, cb_status__normal, &counts));
- cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
- cl_assert_equal_i(0, counts.wrong_status_flags_count);
- cl_assert_equal_i(0, counts.wrong_sorted_path);
-
- opts.flags = GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY;
-
- memset(&counts, 0, sizeof(counts));
- counts.expected_entry_count = 4;
- counts.expected_paths = icase_paths;
- counts.expected_statuses = icase_statuses;
- cl_git_pass(
- git_status_foreach_ext(repo, &opts, cb_status__normal, &counts));
- cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
- cl_assert_equal_i(0, counts.wrong_status_flags_count);
- cl_assert_equal_i(0, counts.wrong_sorted_path);
-}
diff --git a/tests-clar/submodule/lookup.c b/tests-clar/submodule/lookup.c
deleted file mode 100644
index acf8f6462..000000000
--- a/tests-clar/submodule/lookup.c
+++ /dev/null
@@ -1,114 +0,0 @@
-#include "clar_libgit2.h"
-#include "submodule_helpers.h"
-#include "posix.h"
-
-static git_repository *g_repo = NULL;
-
-void test_submodule_lookup__initialize(void)
-{
- g_repo = cl_git_sandbox_init("submod2");
-
- cl_fixture_sandbox("submod2_target");
- p_rename("submod2_target/.gitted", "submod2_target/.git");
-
- /* must create submod2_target before rewrite so prettify will work */
- rewrite_gitmodules(git_repository_workdir(g_repo));
- p_rename("submod2/not-submodule/.gitted", "submod2/not-submodule/.git");
-}
-
-void test_submodule_lookup__cleanup(void)
-{
- cl_git_sandbox_cleanup();
- cl_fixture_cleanup("submod2_target");
-}
-
-void test_submodule_lookup__simple_lookup(void)
-{
- git_submodule *sm;
-
- /* lookup existing */
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
- cl_assert(sm);
-
- /* lookup pending change in .gitmodules that is not in HEAD */
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited"));
- cl_assert(sm);
-
- /* lookup pending change in .gitmodules that is neither in HEAD nor index */
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_gitmodules_only"));
- cl_assert(sm);
-
- /* lookup git repo subdir that is not added as submodule */
- cl_assert(git_submodule_lookup(&sm, g_repo, "not-submodule") == GIT_EEXISTS);
-
- /* lookup existing directory that is not a submodule */
- cl_assert(git_submodule_lookup(&sm, g_repo, "just_a_dir") == GIT_ENOTFOUND);
-
- /* lookup existing file that is not a submodule */
- cl_assert(git_submodule_lookup(&sm, g_repo, "just_a_file") == GIT_ENOTFOUND);
-
- /* lookup non-existent item */
- cl_assert(git_submodule_lookup(&sm, g_repo, "no_such_file") == GIT_ENOTFOUND);
-}
-
-void test_submodule_lookup__accessors(void)
-{
- git_submodule *sm;
- const char *oid = "480095882d281ed676fe5b863569520e54a7d5c0";
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
- cl_assert(git_submodule_owner(sm) == g_repo);
- cl_assert_equal_s("sm_unchanged", git_submodule_name(sm));
- cl_assert(git__suffixcmp(git_submodule_path(sm), "sm_unchanged") == 0);
- cl_assert(git__suffixcmp(git_submodule_url(sm), "/submod2_target") == 0);
-
- cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
- cl_assert(git_oid_streq(git_submodule_head_id(sm), oid) == 0);
- cl_assert(git_oid_streq(git_submodule_wd_id(sm), oid) == 0);
-
- cl_assert(git_submodule_ignore(sm) == GIT_SUBMODULE_IGNORE_NONE);
- cl_assert(git_submodule_update(sm) == GIT_SUBMODULE_UPDATE_CHECKOUT);
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
- cl_assert_equal_s("sm_changed_head", git_submodule_name(sm));
-
- cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
- cl_assert(git_oid_streq(git_submodule_head_id(sm), oid) == 0);
- cl_assert(git_oid_streq(git_submodule_wd_id(sm),
- "3d9386c507f6b093471a3e324085657a3c2b4247") == 0);
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited"));
- cl_assert_equal_s("sm_added_and_uncommited", git_submodule_name(sm));
-
- cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
- cl_assert(git_submodule_head_id(sm) == NULL);
- cl_assert(git_oid_streq(git_submodule_wd_id(sm), oid) == 0);
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits"));
- cl_assert_equal_s("sm_missing_commits", git_submodule_name(sm));
-
- cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
- cl_assert(git_oid_streq(git_submodule_head_id(sm), oid) == 0);
- cl_assert(git_oid_streq(git_submodule_wd_id(sm),
- "5e4963595a9774b90524d35a807169049de8ccad") == 0);
-}
-
-typedef struct {
- int count;
-} sm_lookup_data;
-
-static int sm_lookup_cb(git_submodule *sm, const char *name, void *payload)
-{
- sm_lookup_data *data = payload;
- data->count += 1;
- cl_assert_equal_s(git_submodule_name(sm), name);
- return 0;
-}
-
-void test_submodule_lookup__foreach(void)
-{
- sm_lookup_data data;
- memset(&data, 0, sizeof(data));
- cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data));
- cl_assert_equal_i(8, data.count);
-}
diff --git a/tests-clar/submodule/modify.c b/tests-clar/submodule/modify.c
deleted file mode 100644
index 94eb3738a..000000000
--- a/tests-clar/submodule/modify.c
+++ /dev/null
@@ -1,266 +0,0 @@
-#include "clar_libgit2.h"
-#include "posix.h"
-#include "path.h"
-#include "submodule_helpers.h"
-
-static git_repository *g_repo = NULL;
-
-#define SM_LIBGIT2_URL "https://github.com/libgit2/libgit2.git"
-#define SM_LIBGIT2 "sm_libgit2"
-#define SM_LIBGIT2B "sm_libgit2b"
-
-void test_submodule_modify__initialize(void)
-{
- g_repo = cl_git_sandbox_init("submod2");
-
- cl_fixture_sandbox("submod2_target");
- p_rename("submod2_target/.gitted", "submod2_target/.git");
-
- /* must create submod2_target before rewrite so prettify will work */
- rewrite_gitmodules(git_repository_workdir(g_repo));
- p_rename("submod2/not-submodule/.gitted", "submod2/not-submodule/.git");
-}
-
-void test_submodule_modify__cleanup(void)
-{
- cl_git_sandbox_cleanup();
- cl_fixture_cleanup("submod2_target");
-}
-
-void test_submodule_modify__add(void)
-{
- git_submodule *sm;
- git_config *cfg;
- const char *s;
-
- /* re-add existing submodule */
- cl_assert(
- git_submodule_add_setup(NULL, g_repo, "whatever", "sm_unchanged", 1) ==
- GIT_EEXISTS );
-
- /* add a submodule using a gitlink */
-
- cl_git_pass(
- git_submodule_add_setup(&sm, g_repo, SM_LIBGIT2_URL, SM_LIBGIT2, 1)
- );
-
- cl_assert(git_path_isfile("submod2/" SM_LIBGIT2 "/.git"));
-
- cl_assert(git_path_isdir("submod2/.git/modules"));
- cl_assert(git_path_isdir("submod2/.git/modules/" SM_LIBGIT2));
- cl_assert(git_path_isfile("submod2/.git/modules/" SM_LIBGIT2 "/HEAD"));
-
- cl_git_pass(git_repository_config(&cfg, g_repo));
- cl_git_pass(
- git_config_get_string(&s, cfg, "submodule." SM_LIBGIT2 ".url"));
- cl_assert_equal_s(s, SM_LIBGIT2_URL);
- git_config_free(cfg);
-
- /* add a submodule not using a gitlink */
-
- cl_git_pass(
- git_submodule_add_setup(&sm, g_repo, SM_LIBGIT2_URL, SM_LIBGIT2B, 0)
- );
-
- cl_assert(git_path_isdir("submod2/" SM_LIBGIT2B "/.git"));
- cl_assert(git_path_isfile("submod2/" SM_LIBGIT2B "/.git/HEAD"));
- cl_assert(!git_path_exists("submod2/.git/modules/" SM_LIBGIT2B));
-
- cl_git_pass(git_repository_config(&cfg, g_repo));
- cl_git_pass(
- git_config_get_string(&s, cfg, "submodule." SM_LIBGIT2B ".url"));
- cl_assert_equal_s(s, SM_LIBGIT2_URL);
- git_config_free(cfg);
-}
-
-static int delete_one_config(const git_config_entry *entry, void *payload)
-{
- git_config *cfg = payload;
- return git_config_delete_entry(cfg, entry->name);
-}
-
-static int init_one_submodule(
- git_submodule *sm, const char *name, void *payload)
-{
- GIT_UNUSED(name);
- GIT_UNUSED(payload);
- return git_submodule_init(sm, false);
-}
-
-void test_submodule_modify__init(void)
-{
- git_config *cfg;
- const char *str;
-
- /* erase submodule data from .git/config */
- cl_git_pass(git_repository_config(&cfg, g_repo));
- cl_git_pass(
- git_config_foreach_match(cfg, "submodule\\..*", delete_one_config, cfg));
- git_config_free(cfg);
-
- /* confirm no submodule data in config */
- cl_git_pass(git_repository_config(&cfg, g_repo));
- cl_git_fail(git_config_get_string(&str, cfg, "submodule.sm_unchanged.url"));
- cl_git_fail(git_config_get_string(&str, cfg, "submodule.sm_changed_head.url"));
- cl_git_fail(git_config_get_string(&str, cfg, "submodule.sm_added_and_uncommited.url"));
- git_config_free(cfg);
-
- /* call init and see that settings are copied */
- cl_git_pass(git_submodule_foreach(g_repo, init_one_submodule, NULL));
-
- git_submodule_reload_all(g_repo);
-
- /* confirm submodule data in config */
- cl_git_pass(git_repository_config(&cfg, g_repo));
- cl_git_pass(git_config_get_string(&str, cfg, "submodule.sm_unchanged.url"));
- cl_assert(git__suffixcmp(str, "/submod2_target") == 0);
- cl_git_pass(git_config_get_string(&str, cfg, "submodule.sm_changed_head.url"));
- cl_assert(git__suffixcmp(str, "/submod2_target") == 0);
- cl_git_pass(git_config_get_string(&str, cfg, "submodule.sm_added_and_uncommited.url"));
- cl_assert(git__suffixcmp(str, "/submod2_target") == 0);
- git_config_free(cfg);
-}
-
-static int sync_one_submodule(
- git_submodule *sm, const char *name, void *payload)
-{
- GIT_UNUSED(name);
- GIT_UNUSED(payload);
- return git_submodule_sync(sm);
-}
-
-void test_submodule_modify__sync(void)
-{
- git_submodule *sm1, *sm2, *sm3;
- git_config *cfg;
- const char *str;
-
-#define SM1 "sm_unchanged"
-#define SM2 "sm_changed_head"
-#define SM3 "sm_added_and_uncommited"
-
- /* look up some submodules */
- cl_git_pass(git_submodule_lookup(&sm1, g_repo, SM1));
- cl_git_pass(git_submodule_lookup(&sm2, g_repo, SM2));
- cl_git_pass(git_submodule_lookup(&sm3, g_repo, SM3));
-
- /* At this point, the .git/config URLs for the submodules have
- * not be rewritten with the absolute paths (although the
- * .gitmodules have. Let's confirm that they DO NOT match
- * yet, then we can do a sync to make them match...
- */
-
- /* check submodule info does not match before sync */
- cl_git_pass(git_repository_config(&cfg, g_repo));
- cl_git_pass(git_config_get_string(&str, cfg, "submodule."SM1".url"));
- cl_assert(strcmp(git_submodule_url(sm1), str) != 0);
- cl_git_pass(git_config_get_string(&str, cfg, "submodule."SM2".url"));
- cl_assert(strcmp(git_submodule_url(sm2), str) != 0);
- cl_git_pass(git_config_get_string(&str, cfg, "submodule."SM3".url"));
- cl_assert(strcmp(git_submodule_url(sm3), str) != 0);
- git_config_free(cfg);
-
- /* sync all the submodules */
- cl_git_pass(git_submodule_foreach(g_repo, sync_one_submodule, NULL));
-
- /* check that submodule config is updated */
- cl_git_pass(git_repository_config(&cfg, g_repo));
- cl_git_pass(git_config_get_string(&str, cfg, "submodule."SM1".url"));
- cl_assert_equal_s(git_submodule_url(sm1), str);
- cl_git_pass(git_config_get_string(&str, cfg, "submodule."SM2".url"));
- cl_assert_equal_s(git_submodule_url(sm2), str);
- cl_git_pass(git_config_get_string(&str, cfg, "submodule."SM3".url"));
- cl_assert_equal_s(git_submodule_url(sm3), str);
- git_config_free(cfg);
-}
-
-void test_submodule_modify__edit_and_save(void)
-{
- git_submodule *sm1, *sm2;
- char *old_url;
- git_submodule_ignore_t old_ignore;
- git_submodule_update_t old_update;
- git_repository *r2;
- int old_fetchrecurse;
-
- cl_git_pass(git_submodule_lookup(&sm1, g_repo, "sm_changed_head"));
-
- old_url = git__strdup(git_submodule_url(sm1));
-
- /* modify properties of submodule */
- cl_git_pass(git_submodule_set_url(sm1, SM_LIBGIT2_URL));
- old_ignore = git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_UNTRACKED);
- old_update = git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_REBASE);
- old_fetchrecurse = git_submodule_set_fetch_recurse_submodules(sm1, 1);
-
- cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm1));
- cl_assert_equal_i(
- (int)GIT_SUBMODULE_IGNORE_UNTRACKED, (int)git_submodule_ignore(sm1));
- cl_assert_equal_i(
- (int)GIT_SUBMODULE_UPDATE_REBASE, (int)git_submodule_update(sm1));
- cl_assert_equal_i(1, git_submodule_fetch_recurse_submodules(sm1));
-
- /* revert without saving (and confirm setters return old value) */
- cl_git_pass(git_submodule_set_url(sm1, old_url));
- cl_assert_equal_i(
- (int)GIT_SUBMODULE_IGNORE_UNTRACKED,
- (int)git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_DEFAULT));
- cl_assert_equal_i(
- (int)GIT_SUBMODULE_UPDATE_REBASE,
- (int)git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_DEFAULT));
- cl_assert_equal_i(
- 1, git_submodule_set_fetch_recurse_submodules(sm1, old_fetchrecurse));
-
- /* check that revert was successful */
- cl_assert_equal_s(old_url, git_submodule_url(sm1));
- cl_assert_equal_i((int)old_ignore, (int)git_submodule_ignore(sm1));
- cl_assert_equal_i((int)old_update, (int)git_submodule_update(sm1));
- cl_assert_equal_i(
- old_fetchrecurse, git_submodule_fetch_recurse_submodules(sm1));
-
- /* modify properties of submodule (again) */
- cl_git_pass(git_submodule_set_url(sm1, SM_LIBGIT2_URL));
- git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_UNTRACKED);
- git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_REBASE);
- git_submodule_set_fetch_recurse_submodules(sm1, 1);
-
- /* call save */
- cl_git_pass(git_submodule_save(sm1));
-
- /* attempt to "revert" values */
- git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_DEFAULT);
- git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_DEFAULT);
-
- /* but ignore and update should NOT revert because the DEFAULT
- * should now be the newly saved value...
- */
- cl_assert_equal_i(
- (int)GIT_SUBMODULE_IGNORE_UNTRACKED, (int)git_submodule_ignore(sm1));
- cl_assert_equal_i(
- (int)GIT_SUBMODULE_UPDATE_REBASE, (int)git_submodule_update(sm1));
- cl_assert_equal_i(1, git_submodule_fetch_recurse_submodules(sm1));
-
- /* call reload and check that the new values are loaded */
- cl_git_pass(git_submodule_reload(sm1));
-
- cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm1));
- cl_assert_equal_i(
- (int)GIT_SUBMODULE_IGNORE_UNTRACKED, (int)git_submodule_ignore(sm1));
- cl_assert_equal_i(
- (int)GIT_SUBMODULE_UPDATE_REBASE, (int)git_submodule_update(sm1));
- cl_assert_equal_i(1, git_submodule_fetch_recurse_submodules(sm1));
-
- /* open a second copy of the repo and compare submodule */
- cl_git_pass(git_repository_open(&r2, "submod2"));
- cl_git_pass(git_submodule_lookup(&sm2, r2, "sm_changed_head"));
-
- cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm2));
- cl_assert_equal_i(
- (int)GIT_SUBMODULE_IGNORE_UNTRACKED, (int)git_submodule_ignore(sm2));
- cl_assert_equal_i(
- (int)GIT_SUBMODULE_UPDATE_REBASE, (int)git_submodule_update(sm2));
- cl_assert_equal_i(1, git_submodule_fetch_recurse_submodules(sm2));
-
- git_repository_free(r2);
- git__free(old_url);
-}
diff --git a/tests-clar/submodule/status.c b/tests-clar/submodule/status.c
deleted file mode 100644
index 68110bdd5..000000000
--- a/tests-clar/submodule/status.c
+++ /dev/null
@@ -1,414 +0,0 @@
-#include "clar_libgit2.h"
-#include "posix.h"
-#include "path.h"
-#include "submodule_helpers.h"
-#include "fileops.h"
-#include "iterator.h"
-
-static git_repository *g_repo = NULL;
-
-void test_submodule_status__initialize(void)
-{
- g_repo = cl_git_sandbox_init("submod2");
-
- cl_fixture_sandbox("submod2_target");
- p_rename("submod2_target/.gitted", "submod2_target/.git");
-
- /* must create submod2_target before rewrite so prettify will work */
- rewrite_gitmodules(git_repository_workdir(g_repo));
- p_rename("submod2/not-submodule/.gitted", "submod2/not-submodule/.git");
- p_rename("submod2/not/.gitted", "submod2/not/.git");
-}
-
-void test_submodule_status__cleanup(void)
-{
- cl_git_sandbox_cleanup();
- cl_fixture_cleanup("submod2_target");
-}
-
-void test_submodule_status__unchanged(void)
-{
- unsigned int status, expected;
- git_submodule *sm;
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
-
- expected = GIT_SUBMODULE_STATUS_IN_HEAD |
- GIT_SUBMODULE_STATUS_IN_INDEX |
- GIT_SUBMODULE_STATUS_IN_CONFIG |
- GIT_SUBMODULE_STATUS_IN_WD;
-
- cl_assert(status == expected);
-}
-
-/* 4 values of GIT_SUBMODULE_IGNORE to check */
-
-void test_submodule_status__ignore_none(void)
-{
- unsigned int status;
- git_submodule *sm;
- git_buf path = GIT_BUF_INIT;
-
- cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged"));
- cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES));
-
- cl_assert_equal_i(GIT_ENOTFOUND,
- git_submodule_lookup(&sm, g_repo, "just_a_dir"));
- cl_assert_equal_i(GIT_EEXISTS,
- git_submodule_lookup(&sm, g_repo, "not-submodule"));
- cl_assert_equal_i(GIT_EEXISTS,
- git_submodule_lookup(&sm, g_repo, "not"));
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_index"));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert((status & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED) != 0);
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_file"));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert((status & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED) != 0);
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_untracked_file"));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNTRACKED) != 0);
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits"));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited"));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_ADDED) != 0);
-
- /* removed sm_unchanged for deleted workdir */
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0);
-
- /* now mkdir sm_unchanged to test uninitialized */
- cl_git_pass(git_futils_mkdir(git_buf_cstr(&path), NULL, 0755, 0));
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
- cl_git_pass(git_submodule_reload(sm));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0);
-
- /* update sm_changed_head in index */
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
- cl_git_pass(git_submodule_add_to_index(sm, true));
- /* reload is not needed because add_to_index updates the submodule data */
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0);
-
- /* remove sm_changed_head from index */
- {
- git_index *index;
- size_t pos;
-
- cl_git_pass(git_repository_index(&index, g_repo));
- cl_assert(!git_index_find(&pos, index, "sm_changed_head"));
- cl_git_pass(git_index_remove(index, "sm_changed_head", 0));
- cl_git_pass(git_index_write(index));
-
- git_index_free(index);
- }
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
- cl_git_pass(git_submodule_reload(sm));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_DELETED) != 0);
-
- git_buf_free(&path);
-}
-
-static int set_sm_ignore(git_submodule *sm, const char *name, void *payload)
-{
- git_submodule_ignore_t ignore = *(git_submodule_ignore_t *)payload;
- GIT_UNUSED(name);
- git_submodule_set_ignore(sm, ignore);
- return 0;
-}
-
-void test_submodule_status__ignore_untracked(void)
-{
- unsigned int status;
- git_submodule *sm;
- git_buf path = GIT_BUF_INIT;
- git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_UNTRACKED;
-
- cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged"));
- cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES));
-
- cl_git_pass(git_submodule_foreach(g_repo, set_sm_ignore, &ign));
-
- cl_git_fail(git_submodule_lookup(&sm, g_repo, "not-submodule"));
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_index"));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert((status & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED) != 0);
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_file"));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert((status & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED) != 0);
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_untracked_file"));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits"));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited"));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_ADDED) != 0);
-
- /* removed sm_unchanged for deleted workdir */
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0);
-
- /* now mkdir sm_unchanged to test uninitialized */
- cl_git_pass(git_futils_mkdir(git_buf_cstr(&path), NULL, 0755, 0));
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
- cl_git_pass(git_submodule_reload(sm));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0);
-
- /* update sm_changed_head in index */
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
- cl_git_pass(git_submodule_add_to_index(sm, true));
- /* reload is not needed because add_to_index updates the submodule data */
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0);
-
- git_buf_free(&path);
-}
-
-void test_submodule_status__ignore_dirty(void)
-{
- unsigned int status;
- git_submodule *sm;
- git_buf path = GIT_BUF_INIT;
- git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_DIRTY;
-
- cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged"));
- cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES));
-
- cl_git_pass(git_submodule_foreach(g_repo, set_sm_ignore, &ign));
-
- cl_assert_equal_i(GIT_ENOTFOUND,
- git_submodule_lookup(&sm, g_repo, "just_a_dir"));
- cl_assert_equal_i(GIT_EEXISTS,
- git_submodule_lookup(&sm, g_repo, "not-submodule"));
- cl_assert_equal_i(GIT_EEXISTS,
- git_submodule_lookup(&sm, g_repo, "not"));
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_index"));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_file"));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_untracked_file"));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits"));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited"));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_ADDED) != 0);
-
- /* removed sm_unchanged for deleted workdir */
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0);
-
- /* now mkdir sm_unchanged to test uninitialized */
- cl_git_pass(git_futils_mkdir(git_buf_cstr(&path), NULL, 0755, 0));
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
- cl_git_pass(git_submodule_reload(sm));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0);
-
- /* update sm_changed_head in index */
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
- cl_git_pass(git_submodule_add_to_index(sm, true));
- /* reload is not needed because add_to_index updates the submodule data */
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0);
-
- git_buf_free(&path);
-}
-
-void test_submodule_status__ignore_all(void)
-{
- unsigned int status;
- git_submodule *sm;
- git_buf path = GIT_BUF_INIT;
- git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_ALL;
-
- cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged"));
- cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES));
-
- cl_git_pass(git_submodule_foreach(g_repo, set_sm_ignore, &ign));
-
- cl_assert_equal_i(GIT_ENOTFOUND,
- git_submodule_lookup(&sm, g_repo, "just_a_dir"));
- cl_assert_equal_i(GIT_EEXISTS,
- git_submodule_lookup(&sm, g_repo, "not-submodule"));
- cl_assert_equal_i(GIT_EEXISTS,
- git_submodule_lookup(&sm, g_repo, "not"));
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_index"));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_file"));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_untracked_file"));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits"));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited"));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
-
- /* removed sm_unchanged for deleted workdir */
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
-
- /* now mkdir sm_unchanged to test uninitialized */
- cl_git_pass(git_futils_mkdir(git_buf_cstr(&path), NULL, 0755, 0));
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
- cl_git_pass(git_submodule_reload(sm));
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
-
- /* update sm_changed_head in index */
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
- cl_git_pass(git_submodule_add_to_index(sm, true));
- /* reload is not needed because add_to_index updates the submodule data */
- cl_git_pass(git_submodule_status(&status, sm));
- cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
-
- git_buf_free(&path);
-}
-
-typedef struct {
- size_t counter;
- const char **paths;
-} submodule_expectations;
-
-static int confirm_submodule_status(
- const char *path, unsigned int status_flags, void *payload)
-{
- submodule_expectations *exp = payload;
-
- while (git__suffixcmp(exp->paths[exp->counter], "/") == 0)
- exp->counter++;
-
- cl_assert_equal_s(exp->paths[exp->counter++], path);
-
- GIT_UNUSED(status_flags);
-
- return 0;
-}
-
-void test_submodule_status__iterator(void)
-{
- git_iterator *iter;
- const git_index_entry *entry;
- size_t i;
- static const char *expected[] = {
- ".gitmodules",
- "just_a_dir/",
- "just_a_dir/contents",
- "just_a_file",
- "not",
- "not-submodule",
- "README.txt",
- "sm_added_and_uncommited",
- "sm_changed_file",
- "sm_changed_head",
- "sm_changed_index",
- "sm_changed_untracked_file",
- "sm_missing_commits",
- "sm_unchanged",
- NULL
- };
- submodule_expectations exp = { 0, expected };
- git_status_options opts = GIT_STATUS_OPTIONS_INIT;
-
- cl_git_pass(git_iterator_for_workdir(&iter, g_repo,
- GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES, NULL, NULL));
-
- for (i = 0; !git_iterator_advance(&entry, iter); ++i)
- cl_assert_equal_s(expected[i], entry->path);
-
- git_iterator_free(iter);
-
- opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
- GIT_STATUS_OPT_INCLUDE_UNMODIFIED |
- GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
-
- cl_git_pass(git_status_foreach_ext(
- g_repo, &opts, confirm_submodule_status, &exp));
-}
-
-void test_submodule_status__untracked_dirs_containing_ignored_files(void)
-{
- git_buf path = GIT_BUF_INIT;
- unsigned int status, expected;
- git_submodule *sm;
-
- cl_git_pass(git_buf_joinpath(&path, git_repository_path(g_repo), "modules/sm_unchanged/info/exclude"));
- cl_git_append2file(git_buf_cstr(&path), "\n*.ignored\n");
-
- cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged/directory"));
- cl_git_pass(git_futils_mkdir(git_buf_cstr(&path), NULL, 0755, 0));
- cl_git_pass(git_buf_joinpath(&path, git_buf_cstr(&path), "i_am.ignored"));
- cl_git_mkfile(git_buf_cstr(&path), "ignored this file, please\n");
-
- cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
- cl_git_pass(git_submodule_status(&status, sm));
-
- cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
-
- expected = GIT_SUBMODULE_STATUS_IN_HEAD |
- GIT_SUBMODULE_STATUS_IN_INDEX |
- GIT_SUBMODULE_STATUS_IN_CONFIG |
- GIT_SUBMODULE_STATUS_IN_WD;
-
- cl_assert(status == expected);
-
- git_buf_free(&path);
-}
diff --git a/tests-clar/submodule/submodule_helpers.c b/tests-clar/submodule/submodule_helpers.c
deleted file mode 100644
index 0c3e79f71..000000000
--- a/tests-clar/submodule/submodule_helpers.c
+++ /dev/null
@@ -1,84 +0,0 @@
-#include "clar_libgit2.h"
-#include "buffer.h"
-#include "path.h"
-#include "util.h"
-#include "posix.h"
-#include "submodule_helpers.h"
-
-/* rewrite gitmodules -> .gitmodules
- * rewrite the empty or relative urls inside each module
- * rename the .gitted directory inside any submodule to .git
- */
-void rewrite_gitmodules(const char *workdir)
-{
- git_buf in_f = GIT_BUF_INIT, out_f = GIT_BUF_INIT, path = GIT_BUF_INIT;
- FILE *in, *out;
- char line[256];
-
- cl_git_pass(git_buf_joinpath(&in_f, workdir, "gitmodules"));
- cl_git_pass(git_buf_joinpath(&out_f, workdir, ".gitmodules"));
-
- cl_assert((in = fopen(in_f.ptr, "r")) != NULL);
- cl_assert((out = fopen(out_f.ptr, "w")) != NULL);
-
- while (fgets(line, sizeof(line), in) != NULL) {
- char *scan = line;
-
- while (*scan == ' ' || *scan == '\t') scan++;
-
- /* rename .gitted -> .git in submodule directories */
- if (git__prefixcmp(scan, "path =") == 0) {
- scan += strlen("path =");
- while (*scan == ' ') scan++;
-
- git_buf_joinpath(&path, workdir, scan);
- git_buf_rtrim(&path);
- git_buf_joinpath(&path, path.ptr, ".gitted");
-
- if (!git_buf_oom(&path) && p_access(path.ptr, F_OK) == 0) {
- git_buf_joinpath(&out_f, workdir, scan);
- git_buf_rtrim(&out_f);
- git_buf_joinpath(&out_f, out_f.ptr, ".git");
-
- if (!git_buf_oom(&out_f))
- p_rename(path.ptr, out_f.ptr);
- }
- }
-
- /* copy non-"url =" lines verbatim */
- if (git__prefixcmp(scan, "url =") != 0) {
- fputs(line, out);
- continue;
- }
-
- /* convert relative URLs in "url =" lines */
- scan += strlen("url =");
- while (*scan == ' ') scan++;
-
- if (*scan == '.') {
- git_buf_joinpath(&path, workdir, scan);
- git_buf_rtrim(&path);
- } else if (!*scan || *scan == '\n') {
- git_buf_joinpath(&path, workdir, "../testrepo.git");
- } else {
- fputs(line, out);
- continue;
- }
-
- git_path_prettify(&path, path.ptr, NULL);
- git_buf_putc(&path, '\n');
- cl_assert(!git_buf_oom(&path));
-
- fwrite(line, scan - line, sizeof(char), out);
- fputs(path.ptr, out);
- }
-
- fclose(in);
- fclose(out);
-
- cl_must_pass(p_unlink(in_f.ptr));
-
- git_buf_free(&in_f);
- git_buf_free(&out_f);
- git_buf_free(&path);
-}
diff --git a/tests-clar/submodule/submodule_helpers.h b/tests-clar/submodule/submodule_helpers.h
deleted file mode 100644
index 6b76a832e..000000000
--- a/tests-clar/submodule/submodule_helpers.h
+++ /dev/null
@@ -1,2 +0,0 @@
-extern void rewrite_gitmodules(const char *workdir);
-
diff --git a/tests-clar/threads/basic.c b/tests-clar/threads/basic.c
deleted file mode 100644
index a15c53140..000000000
--- a/tests-clar/threads/basic.c
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "clar_libgit2.h"
-
-#include "cache.h"
-
-
-static git_repository *g_repo;
-
-void test_threads_basic__initialize(void)
-{
- g_repo = cl_git_sandbox_init("testrepo");
-}
-
-void test_threads_basic__cleanup(void)
-{
- cl_git_sandbox_cleanup();
-}
-
-
-void test_threads_basic__cache(void)
-{
- // run several threads polling the cache at the same time
- cl_assert(1 == 1);
-}
diff --git a/tests-clar/valgrind-supp-mac.txt b/tests-clar/valgrind-supp-mac.txt
deleted file mode 100644
index fcc7ede86..000000000
--- a/tests-clar/valgrind-supp-mac.txt
+++ /dev/null
@@ -1,156 +0,0 @@
-{
- libgit2-giterr-set-buffer
- Memcheck:Leak
- ...
- fun:git__realloc
- fun:git_buf_try_grow
- fun:git_buf_grow
- fun:git_buf_vprintf
- fun:giterr_set
-}
-{
- mac-setenv-leak-1
- Memcheck:Leak
- fun:malloc_zone_malloc
- fun:__setenv
- fun:setenv
-}
-{
- mac-setenv-leak-2
- Memcheck:Leak
- fun:malloc_zone_malloc
- fun:malloc_set_zone_name
- ...
- fun:init__zone0
- fun:setenv
-}
-{
- mac-dyld-initializer-leak
- Memcheck:Leak
- fun:malloc
- ...
- fun:dyld_register_image_state_change_handler
- fun:_dyld_initializer
-}
-{
- mac-tz-leak-1
- Memcheck:Leak
- ...
- fun:token_table_add
- fun:notify_register_check
- fun:notify_register_tz
-}
-{
- mac-tz-leak-2
- Memcheck:Leak
- fun:malloc
- fun:tzload
-}
-{
- mac-tz-leak-3
- Memcheck:Leak
- fun:malloc
- fun:tzsetwall_basic
-}
-{
- mac-tz-leak-4
- Memcheck:Leak
- fun:malloc
- fun:gmtsub
-}
-{
- mac-system-init-leak-1
- Memcheck:Leak
- ...
- fun:_libxpc_initializer
- fun:libSystem_initializer
-}
-{
- mac-system-init-leak-2
- Memcheck:Leak
- ...
- fun:__keymgr_initializer
- fun:libSystem_initializer
-}
-{
- mac-puts-leak
- Memcheck:Leak
- fun:malloc
- fun:__smakebuf
- ...
- fun:puts
-}
-{
- mac-ssl-uninitialized-1
- Memcheck:Cond
- obj:/usr/lib/libcrypto.0.9.8.dylib
- ...
- fun:ssl23_connect
-}
-{
- mac-ssl-uninitialized-2
- Memcheck:Cond
- ...
- obj:/usr/lib/libssl.0.9.8.dylib
- ...
- fun:ssl23_connect
-}
-{
- mac-ssl-uninitialized-3
- Memcheck:Value8
- obj:/usr/lib/libcrypto.0.9.8.dylib
- ...
- fun:ssl23_connect
-}
-{
- mac-ssl-uninitialized-4
- Memcheck:Param
- ...
- obj:/usr/lib/libcrypto.0.9.8.dylib
- ...
- fun:ssl23_connect
-}
-{
- mac-ssl-leak-1
- Memcheck:Leak
- ...
- fun:ERR_load_strings
-}
-{
- mac-ssl-leak-2
- Memcheck:Leak
- ...
- fun:SSL_library_init
-}
-{
- mac-ssl-leak-3
- Memcheck:Leak
- ...
- fun:si_module_with_name
- fun:getaddrinfo
-}
-{
- mac-ssl-leak-4
- Memcheck:Leak
- fun:malloc
- fun:CRYPTO_malloc
- ...
- fun:ssl3_get_server_certificate
-}
-{
- mac-ssl-leak-5
- Memcheck:Leak
- fun:malloc
- fun:CRYPTO_malloc
- ...
- fun:ERR_put_error
-}
-{
- clar-printf-buf
- Memcheck:Leak
- fun:malloc
- fun:__smakebuf
- ...
- fun:printf
- fun:clar_print_init
-}
diff --git a/tests-clar/README.md b/tests/README.md
index 3aeaaf464..3aeaaf464 100644
--- a/tests-clar/README.md
+++ b/tests/README.md
diff --git a/tests-clar/attr/attr_expect.h b/tests/attr/attr_expect.h
index 70f1ab4f5..70f1ab4f5 100644
--- a/tests-clar/attr/attr_expect.h
+++ b/tests/attr/attr_expect.h
diff --git a/tests/attr/file.c b/tests/attr/file.c
new file mode 100644
index 000000000..4eb1d22fe
--- /dev/null
+++ b/tests/attr/file.c
@@ -0,0 +1,224 @@
+#include "clar_libgit2.h"
+#include "attr_file.h"
+#include "attr_expect.h"
+
+#define get_rule(X) ((git_attr_rule *)git_vector_get(&file->rules,(X)))
+#define get_assign(R,Y) ((git_attr_assignment *)git_vector_get(&(R)->assigns,(Y)))
+
+void test_attr_file__simple_read(void)
+{
+ git_attr_file *file;
+ git_attr_assignment *assign;
+ git_attr_rule *rule;
+
+ cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr0")));
+
+ cl_assert_equal_s(cl_fixture("attr/attr0"), file->key + 2);
+ cl_assert(file->rules.length == 1);
+
+ rule = get_rule(0);
+ cl_assert(rule != NULL);
+ cl_assert_equal_s("*", rule->match.pattern);
+ cl_assert(rule->match.length == 1);
+ cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_HASWILD) != 0);
+
+ cl_assert(rule->assigns.length == 1);
+ assign = get_assign(rule, 0);
+ cl_assert(assign != NULL);
+ cl_assert_equal_s("binary", assign->name);
+ cl_assert(GIT_ATTR_TRUE(assign->value));
+
+ git_attr_file__free(file);
+}
+
+void test_attr_file__match_variants(void)
+{
+ git_attr_file *file;
+ git_attr_rule *rule;
+ git_attr_assignment *assign;
+
+ cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr1")));
+
+ cl_assert_equal_s(cl_fixture("attr/attr1"), file->key + 2);
+ cl_assert(file->rules.length == 10);
+
+ /* let's do a thorough check of this rule, then just verify
+ * the things that are unique for the later rules
+ */
+ rule = get_rule(0);
+ cl_assert(rule);
+ cl_assert_equal_s("pat0", rule->match.pattern);
+ cl_assert(rule->match.length == strlen("pat0"));
+ cl_assert(rule->assigns.length == 1);
+ assign = get_assign(rule,0);
+ cl_assert_equal_s("attr0", assign->name);
+ cl_assert(assign->name_hash == git_attr_file__name_hash(assign->name));
+ cl_assert(GIT_ATTR_TRUE(assign->value));
+
+ rule = get_rule(1);
+ cl_assert_equal_s("pat1", rule->match.pattern);
+ cl_assert(rule->match.length == strlen("pat1"));
+ cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0);
+
+ rule = get_rule(2);
+ cl_assert_equal_s("pat2", rule->match.pattern);
+ cl_assert(rule->match.length == strlen("pat2"));
+ cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_DIRECTORY) != 0);
+
+ rule = get_rule(3);
+ cl_assert_equal_s("pat3dir/pat3file", rule->match.pattern);
+ cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_FULLPATH) != 0);
+
+ rule = get_rule(4);
+ cl_assert_equal_s("pat4.*", rule->match.pattern);
+ cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_HASWILD) != 0);
+
+ rule = get_rule(5);
+ cl_assert_equal_s("*.pat5", rule->match.pattern);
+ cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_HASWILD) != 0);
+
+ rule = get_rule(7);
+ cl_assert_equal_s("pat7[a-e]??[xyz]", rule->match.pattern);
+ cl_assert(rule->assigns.length == 1);
+ cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_HASWILD) != 0);
+ assign = get_assign(rule,0);
+ cl_assert_equal_s("attr7", assign->name);
+ cl_assert(GIT_ATTR_TRUE(assign->value));
+
+ rule = get_rule(8);
+ cl_assert_equal_s("pat8 with spaces", rule->match.pattern);
+ cl_assert(rule->match.length == strlen("pat8 with spaces"));
+
+ rule = get_rule(9);
+ cl_assert_equal_s("pat9", rule->match.pattern);
+
+ git_attr_file__free(file);
+}
+
+static void check_one_assign(
+ git_attr_file *file,
+ int rule_idx,
+ int assign_idx,
+ const char *pattern,
+ const char *name,
+ enum attr_expect_t expected,
+ const char *expected_str)
+{
+ git_attr_rule *rule = get_rule(rule_idx);
+ git_attr_assignment *assign = get_assign(rule, assign_idx);
+
+ cl_assert_equal_s(pattern, rule->match.pattern);
+ cl_assert(rule->assigns.length == 1);
+ cl_assert_equal_s(name, assign->name);
+ cl_assert(assign->name_hash == git_attr_file__name_hash(assign->name));
+
+ attr_check_expected(expected, expected_str, assign->name, assign->value);
+}
+
+void test_attr_file__assign_variants(void)
+{
+ git_attr_file *file;
+ git_attr_rule *rule;
+ git_attr_assignment *assign;
+
+ cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr2")));
+
+ cl_assert_equal_s(cl_fixture("attr/attr2"), file->key + 2);
+ cl_assert(file->rules.length == 11);
+
+ check_one_assign(file, 0, 0, "pat0", "simple", EXPECT_TRUE, NULL);
+ check_one_assign(file, 1, 0, "pat1", "neg", EXPECT_FALSE, NULL);
+ check_one_assign(file, 2, 0, "*", "notundef", EXPECT_TRUE, NULL);
+ check_one_assign(file, 3, 0, "pat2", "notundef", EXPECT_UNDEFINED, NULL);
+ check_one_assign(file, 4, 0, "pat3", "assigned", EXPECT_STRING, "test-value");
+ check_one_assign(file, 5, 0, "pat4", "rule-with-more-chars", EXPECT_STRING, "value-with-more-chars");
+ check_one_assign(file, 6, 0, "pat5", "empty", EXPECT_TRUE, NULL);
+ check_one_assign(file, 7, 0, "pat6", "negempty", EXPECT_FALSE, NULL);
+
+ rule = get_rule(8);
+ cl_assert_equal_s("pat7", rule->match.pattern);
+ cl_assert(rule->assigns.length == 5);
+ /* assignments will be sorted by hash value, so we have to do
+ * lookups by search instead of by position
+ */
+ assign = git_attr_rule__lookup_assignment(rule, "multiple");
+ cl_assert(assign);
+ cl_assert_equal_s("multiple", assign->name);
+ cl_assert(GIT_ATTR_TRUE(assign->value));
+ assign = git_attr_rule__lookup_assignment(rule, "single");
+ cl_assert(assign);
+ cl_assert_equal_s("single", assign->name);
+ cl_assert(GIT_ATTR_FALSE(assign->value));
+ assign = git_attr_rule__lookup_assignment(rule, "values");
+ cl_assert(assign);
+ cl_assert_equal_s("values", assign->name);
+ cl_assert_equal_s("1", assign->value);
+ assign = git_attr_rule__lookup_assignment(rule, "also");
+ cl_assert(assign);
+ cl_assert_equal_s("also", assign->name);
+ cl_assert_equal_s("a-really-long-value/*", assign->value);
+ assign = git_attr_rule__lookup_assignment(rule, "happy");
+ cl_assert(assign);
+ cl_assert_equal_s("happy", assign->name);
+ cl_assert_equal_s("yes!", assign->value);
+ assign = git_attr_rule__lookup_assignment(rule, "other");
+ cl_assert(!assign);
+
+ rule = get_rule(9);
+ cl_assert_equal_s("pat8", rule->match.pattern);
+ cl_assert(rule->assigns.length == 2);
+ assign = git_attr_rule__lookup_assignment(rule, "again");
+ cl_assert(assign);
+ cl_assert_equal_s("again", assign->name);
+ cl_assert(GIT_ATTR_TRUE(assign->value));
+ assign = git_attr_rule__lookup_assignment(rule, "another");
+ cl_assert(assign);
+ cl_assert_equal_s("another", assign->name);
+ cl_assert_equal_s("12321", assign->value);
+
+ check_one_assign(file, 10, 0, "pat9", "at-eof", EXPECT_FALSE, NULL);
+
+ git_attr_file__free(file);
+}
+
+void test_attr_file__check_attr_examples(void)
+{
+ git_attr_file *file;
+ git_attr_rule *rule;
+ git_attr_assignment *assign;
+
+ cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr3")));
+ cl_assert_equal_s(cl_fixture("attr/attr3"), file->key + 2);
+ cl_assert(file->rules.length == 3);
+
+ rule = get_rule(0);
+ cl_assert_equal_s("*.java", rule->match.pattern);
+ cl_assert(rule->assigns.length == 3);
+ assign = git_attr_rule__lookup_assignment(rule, "diff");
+ cl_assert_equal_s("diff", assign->name);
+ cl_assert_equal_s("java", assign->value);
+ assign = git_attr_rule__lookup_assignment(rule, "crlf");
+ cl_assert_equal_s("crlf", assign->name);
+ cl_assert(GIT_ATTR_FALSE(assign->value));
+ assign = git_attr_rule__lookup_assignment(rule, "myAttr");
+ cl_assert_equal_s("myAttr", assign->name);
+ cl_assert(GIT_ATTR_TRUE(assign->value));
+ assign = git_attr_rule__lookup_assignment(rule, "missing");
+ cl_assert(assign == NULL);
+
+ rule = get_rule(1);
+ cl_assert_equal_s("NoMyAttr.java", rule->match.pattern);
+ cl_assert(rule->assigns.length == 1);
+ assign = get_assign(rule, 0);
+ cl_assert_equal_s("myAttr", assign->name);
+ cl_assert(GIT_ATTR_UNSPECIFIED(assign->value));
+
+ rule = get_rule(2);
+ cl_assert_equal_s("README", rule->match.pattern);
+ cl_assert(rule->assigns.length == 1);
+ assign = get_assign(rule, 0);
+ cl_assert_equal_s("caveat", assign->name);
+ cl_assert_equal_s("unspecified", assign->value);
+
+ git_attr_file__free(file);
+}
diff --git a/tests-clar/attr/flags.c b/tests/attr/flags.c
index 80c6e1171..80c6e1171 100644
--- a/tests-clar/attr/flags.c
+++ b/tests/attr/flags.c
diff --git a/tests-clar/attr/ignore.c b/tests/attr/ignore.c
index 0f945ebf6..0f945ebf6 100644
--- a/tests-clar/attr/ignore.c
+++ b/tests/attr/ignore.c
diff --git a/tests-clar/attr/lookup.c b/tests/attr/lookup.c
index 200bdd2c7..200bdd2c7 100644
--- a/tests-clar/attr/lookup.c
+++ b/tests/attr/lookup.c
diff --git a/tests/attr/repo.c b/tests/attr/repo.c
new file mode 100644
index 000000000..ef2ad5ce9
--- /dev/null
+++ b/tests/attr/repo.c
@@ -0,0 +1,310 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+#include "git2/attr.h"
+#include "attr.h"
+
+#include "attr_expect.h"
+
+static git_repository *g_repo = NULL;
+
+void test_attr_repo__initialize(void)
+{
+ /* Before each test, instantiate the attr repo from the fixtures and
+ * rename the .gitted to .git so it is a repo with a working dir.
+ * Also rename gitattributes to .gitattributes, because it contains
+ * macro definitions which are only allowed in the root.
+ */
+ g_repo = cl_git_sandbox_init("attr");
+}
+
+void test_attr_repo__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ g_repo = NULL;
+}
+
+void test_attr_repo__get_one(void)
+{
+ struct attr_expected test_cases[] = {
+ { "root_test1", "repoattr", EXPECT_TRUE, NULL },
+ { "root_test1", "rootattr", EXPECT_TRUE, NULL },
+ { "root_test1", "missingattr", EXPECT_UNDEFINED, NULL },
+ { "root_test1", "subattr", EXPECT_UNDEFINED, NULL },
+ { "root_test1", "negattr", EXPECT_UNDEFINED, NULL },
+ { "root_test2", "repoattr", EXPECT_TRUE, NULL },
+ { "root_test2", "rootattr", EXPECT_FALSE, NULL },
+ { "root_test2", "missingattr", EXPECT_UNDEFINED, NULL },
+ { "root_test2", "multiattr", EXPECT_FALSE, NULL },
+ { "root_test3", "repoattr", EXPECT_TRUE, NULL },
+ { "root_test3", "rootattr", EXPECT_UNDEFINED, NULL },
+ { "root_test3", "multiattr", EXPECT_STRING, "3" },
+ { "root_test3", "multi2", EXPECT_UNDEFINED, NULL },
+ { "sub/subdir_test1", "repoattr", EXPECT_TRUE, NULL },
+ { "sub/subdir_test1", "rootattr", EXPECT_TRUE, NULL },
+ { "sub/subdir_test1", "missingattr", EXPECT_UNDEFINED, NULL },
+ { "sub/subdir_test1", "subattr", EXPECT_STRING, "yes" },
+ { "sub/subdir_test1", "negattr", EXPECT_FALSE, NULL },
+ { "sub/subdir_test1", "another", EXPECT_UNDEFINED, NULL },
+ { "sub/subdir_test2.txt", "repoattr", EXPECT_TRUE, NULL },
+ { "sub/subdir_test2.txt", "rootattr", EXPECT_TRUE, NULL },
+ { "sub/subdir_test2.txt", "missingattr", EXPECT_UNDEFINED, NULL },
+ { "sub/subdir_test2.txt", "subattr", EXPECT_STRING, "yes" },
+ { "sub/subdir_test2.txt", "negattr", EXPECT_FALSE, NULL },
+ { "sub/subdir_test2.txt", "another", EXPECT_STRING, "zero" },
+ { "sub/subdir_test2.txt", "reposub", EXPECT_TRUE, NULL },
+ { "sub/sub/subdir.txt", "another", EXPECT_STRING, "one" },
+ { "sub/sub/subdir.txt", "reposubsub", EXPECT_TRUE, NULL },
+ { "sub/sub/subdir.txt", "reposub", EXPECT_UNDEFINED, NULL },
+ { "does-not-exist", "foo", EXPECT_STRING, "yes" },
+ { "sub/deep/file", "deepdeep", EXPECT_TRUE, NULL },
+ { "sub/sub/d/no", "test", EXPECT_STRING, "a/b/d/*" },
+ { "sub/sub/d/yes", "test", EXPECT_UNDEFINED, NULL },
+ { NULL, NULL, 0, NULL }
+ }, *scan;
+
+ for (scan = test_cases; scan->path != NULL; scan++) {
+ const char *value;
+ cl_git_pass(git_attr_get(&value, g_repo, 0, scan->path, scan->attr));
+ attr_check_expected(scan->expected, scan->expected_str, scan->attr, value);
+ }
+
+ cl_assert(git_attr_cache__is_cached(g_repo, 0, ".git/info/attributes"));
+ cl_assert(git_attr_cache__is_cached(g_repo, 0, ".gitattributes"));
+ cl_assert(git_attr_cache__is_cached(g_repo, 0, "sub/.gitattributes"));
+}
+
+void test_attr_repo__get_many(void)
+{
+ const char *names[4] = { "repoattr", "rootattr", "missingattr", "subattr" };
+ const char *values[4];
+
+ cl_git_pass(git_attr_get_many(values, g_repo, 0, "root_test1", 4, names));
+
+ cl_assert(GIT_ATTR_TRUE(values[0]));
+ cl_assert(GIT_ATTR_TRUE(values[1]));
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[2]));
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));
+
+ cl_git_pass(git_attr_get_many(values, g_repo, 0, "root_test2", 4, names));
+
+ cl_assert(GIT_ATTR_TRUE(values[0]));
+ cl_assert(GIT_ATTR_FALSE(values[1]));
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[2]));
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));
+
+ cl_git_pass(git_attr_get_many(values, g_repo, 0, "sub/subdir_test1", 4, names));
+
+ cl_assert(GIT_ATTR_TRUE(values[0]));
+ cl_assert(GIT_ATTR_TRUE(values[1]));
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[2]));
+ cl_assert_equal_s("yes", values[3]);
+}
+
+void test_attr_repo__get_many_in_place(void)
+{
+ const char *vals[4] = { "repoattr", "rootattr", "missingattr", "subattr" };
+
+ /* it should be legal to look up values into the same array that has
+ * the attribute names, overwriting each name as the value is found.
+ */
+
+ cl_git_pass(git_attr_get_many(vals, g_repo, 0, "sub/subdir_test1", 4, vals));
+
+ cl_assert(GIT_ATTR_TRUE(vals[0]));
+ cl_assert(GIT_ATTR_TRUE(vals[1]));
+ cl_assert(GIT_ATTR_UNSPECIFIED(vals[2]));
+ cl_assert_equal_s("yes", vals[3]);
+}
+
+static int count_attrs(
+ const char *name,
+ const char *value,
+ void *payload)
+{
+ GIT_UNUSED(name);
+ GIT_UNUSED(value);
+
+ *((int *)payload) += 1;
+
+ return 0;
+}
+
+static int cancel_iteration(
+ const char *name,
+ const char *value,
+ void *payload)
+{
+ GIT_UNUSED(name);
+ GIT_UNUSED(value);
+
+ *((int *)payload) -= 1;
+
+ if (*((int *)payload) < 0)
+ return -1;
+
+ return 0;
+}
+
+void test_attr_repo__foreach(void)
+{
+ int count;
+
+ count = 0;
+ cl_git_pass(git_attr_foreach(
+ g_repo, 0, "root_test1", &count_attrs, &count));
+ cl_assert(count == 2);
+
+ count = 0;
+ cl_git_pass(git_attr_foreach(g_repo, 0, "sub/subdir_test1",
+ &count_attrs, &count));
+ cl_assert(count == 4); /* repoattr, rootattr, subattr, negattr */
+
+ count = 0;
+ cl_git_pass(git_attr_foreach(g_repo, 0, "sub/subdir_test2.txt",
+ &count_attrs, &count));
+ cl_assert(count == 6); /* repoattr, rootattr, subattr, reposub, negattr, another */
+
+ count = 2;
+ cl_assert_equal_i(
+ GIT_EUSER, git_attr_foreach(
+ g_repo, 0, "sub/subdir_test1", &cancel_iteration, &count)
+ );
+}
+
+void test_attr_repo__manpage_example(void)
+{
+ const char *value;
+
+ cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "foo"));
+ cl_assert(GIT_ATTR_TRUE(value));
+
+ cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "bar"));
+ cl_assert(GIT_ATTR_UNSPECIFIED(value));
+
+ cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "baz"));
+ cl_assert(GIT_ATTR_FALSE(value));
+
+ cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "merge"));
+ cl_assert_equal_s("filfre", value);
+
+ cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "frotz"));
+ cl_assert(GIT_ATTR_UNSPECIFIED(value));
+}
+
+void test_attr_repo__macros(void)
+{
+ const char *names[5] = { "rootattr", "binary", "diff", "crlf", "frotz" };
+ const char *names2[5] = { "mymacro", "positive", "negative", "rootattr", "another" };
+ const char *names3[3] = { "macro2", "multi2", "multi3" };
+ const char *values[5];
+
+ cl_git_pass(git_attr_get_many(values, g_repo, 0, "binfile", 5, names));
+
+ cl_assert(GIT_ATTR_TRUE(values[0]));
+ cl_assert(GIT_ATTR_TRUE(values[1]));
+ cl_assert(GIT_ATTR_FALSE(values[2]));
+ cl_assert(GIT_ATTR_FALSE(values[3]));
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[4]));
+
+ cl_git_pass(git_attr_get_many(values, g_repo, 0, "macro_test", 5, names2));
+
+ cl_assert(GIT_ATTR_TRUE(values[0]));
+ cl_assert(GIT_ATTR_TRUE(values[1]));
+ cl_assert(GIT_ATTR_FALSE(values[2]));
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[3]));
+ cl_assert_equal_s("77", values[4]);
+
+ cl_git_pass(git_attr_get_many(values, g_repo, 0, "macro_test", 3, names3));
+
+ cl_assert(GIT_ATTR_TRUE(values[0]));
+ cl_assert(GIT_ATTR_FALSE(values[1]));
+ cl_assert_equal_s("answer", values[2]);
+}
+
+void test_attr_repo__bad_macros(void)
+{
+ const char *names[6] = { "rootattr", "positive", "negative",
+ "firstmacro", "secondmacro", "thirdmacro" };
+ const char *values[6];
+
+ cl_git_pass(git_attr_get_many(values, g_repo, 0, "macro_bad", 6, names));
+
+ /* these three just confirm that the "mymacro" rule ran */
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[0]));
+ cl_assert(GIT_ATTR_TRUE(values[1]));
+ cl_assert(GIT_ATTR_FALSE(values[2]));
+
+ /* file contains:
+ * # let's try some malicious macro defs
+ * [attr]firstmacro -thirdmacro -secondmacro
+ * [attr]secondmacro firstmacro -firstmacro
+ * [attr]thirdmacro secondmacro=hahaha -firstmacro
+ * macro_bad firstmacro secondmacro thirdmacro
+ *
+ * firstmacro assignment list ends up with:
+ * -thirdmacro -secondmacro
+ * secondmacro assignment list expands "firstmacro" and ends up with:
+ * -thirdmacro -secondmacro -firstmacro
+ * thirdmacro assignment don't expand so list ends up with:
+ * secondmacro="hahaha"
+ *
+ * macro_bad assignment list ends up with:
+ * -thirdmacro -secondmacro firstmacro &&
+ * -thirdmacro -secondmacro -firstmacro secondmacro &&
+ * secondmacro="hahaha" thirdmacro
+ *
+ * so summary results should be:
+ * -firstmacro secondmacro="hahaha" thirdmacro
+ */
+ cl_assert(GIT_ATTR_FALSE(values[3]));
+ cl_assert_equal_s("hahaha", values[4]);
+ cl_assert(GIT_ATTR_TRUE(values[5]));
+}
+
+#define CONTENT "I'm going to be dynamically processed\r\n" \
+ "And my line endings...\r\n" \
+ "...are going to be\n" \
+ "normalized!\r\n"
+
+#define GITATTR "* text=auto\n" \
+ "*.txt text\n" \
+ "*.data binary\n"
+
+static void add_to_workdir(const char *filename, const char *content)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_buf_joinpath(&buf, "attr", filename));
+ cl_git_rewritefile(git_buf_cstr(&buf), content);
+
+ git_buf_free(&buf);
+}
+
+static void assert_proper_normalization(git_index *index, const char *filename, const char *expected_sha)
+{
+ size_t index_pos;
+ const git_index_entry *entry;
+
+ add_to_workdir(filename, CONTENT);
+ cl_git_pass(git_index_add_bypath(index, filename));
+
+ cl_assert(!git_index_find(&index_pos, index, filename));
+
+ entry = git_index_get_byindex(index, index_pos);
+ cl_assert_equal_i(0, git_oid_streq(&entry->oid, expected_sha));
+}
+
+void test_attr_repo__staging_properly_normalizes_line_endings_according_to_gitattributes_directives(void)
+{
+ git_index* index;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ add_to_workdir(".gitattributes", GITATTR);
+
+ assert_proper_normalization(index, "text.txt", "22c74203bace3c2e950278c7ab08da0fca9f4e9b");
+ assert_proper_normalization(index, "huh.dunno", "22c74203bace3c2e950278c7ab08da0fca9f4e9b");
+ assert_proper_normalization(index, "binary.data", "66eeff1fcbacf589e6d70aa70edd3fce5be2b37c");
+
+ git_index_free(index);
+}
diff --git a/tests/blame/blame_helpers.c b/tests/blame/blame_helpers.c
new file mode 100644
index 000000000..d64bb5c4c
--- /dev/null
+++ b/tests/blame/blame_helpers.c
@@ -0,0 +1,64 @@
+#include "blame_helpers.h"
+
+void hunk_message(size_t idx, const git_blame_hunk *hunk, const char *fmt, ...)
+{
+ va_list arglist;
+
+ printf("Hunk %zd (line %d +%d): ", idx,
+ hunk->final_start_line_number, hunk->lines_in_hunk-1);
+
+ va_start(arglist, fmt);
+ vprintf(fmt, arglist);
+ va_end(arglist);
+
+ printf("\n");
+}
+
+void check_blame_hunk_index(git_repository *repo, git_blame *blame, int idx,
+ int start_line, int len, char boundary, const char *commit_id, const char *orig_path)
+{
+ char expected[41] = {0}, actual[41] = {0};
+ const git_blame_hunk *hunk = git_blame_get_hunk_byindex(blame, idx);
+ cl_assert(hunk);
+
+ if (!strncmp(commit_id, "0000", 4)) {
+ strcpy(expected, "0000000000000000000000000000000000000000");
+ } else {
+ git_object *obj;
+ cl_git_pass(git_revparse_single(&obj, repo, commit_id));
+ git_oid_fmt(expected, git_object_id(obj));
+ git_object_free(obj);
+ }
+
+ if (hunk->final_start_line_number != start_line) {
+ hunk_message(idx, hunk, "mismatched start line number: expected %d, got %d",
+ start_line, hunk->final_start_line_number);
+ }
+ cl_assert_equal_i(hunk->final_start_line_number, start_line);
+
+ if (hunk->lines_in_hunk != len) {
+ hunk_message(idx, hunk, "mismatched line count: expected %d, got %d",
+ len, hunk->lines_in_hunk);
+ }
+ cl_assert_equal_i(hunk->lines_in_hunk, len);
+
+ git_oid_fmt(actual, &hunk->final_commit_id);
+ if (strcmp(expected, actual)) {
+ hunk_message(idx, hunk, "has mismatched original id (got %s, expected %s)\n",
+ actual, expected);
+ }
+ cl_assert_equal_s(actual, expected);
+ if (strcmp(hunk->orig_path, orig_path)) {
+ hunk_message(idx, hunk, "has mismatched original path (got '%s', expected '%s')\n",
+ hunk->orig_path, orig_path);
+ }
+ cl_assert_equal_s(hunk->orig_path, orig_path);
+
+ if (hunk->boundary != boundary) {
+ hunk_message(idx, hunk, "doesn't match boundary flag (got %d, expected %d)\n",
+ hunk->boundary, boundary);
+ }
+ cl_assert_equal_i(boundary, hunk->boundary);
+}
+
+
diff --git a/tests/blame/blame_helpers.h b/tests/blame/blame_helpers.h
new file mode 100644
index 000000000..94321a5b5
--- /dev/null
+++ b/tests/blame/blame_helpers.h
@@ -0,0 +1,16 @@
+#include "clar_libgit2.h"
+#include "blame.h"
+
+void hunk_message(size_t idx, const git_blame_hunk *hunk, const char *fmt, ...);
+
+void check_blame_hunk_index(
+ git_repository *repo,
+ git_blame *blame,
+ int idx,
+ int start_line,
+ int len,
+ char boundary,
+ const char *commit_id,
+ const char *orig_path);
+
+
diff --git a/tests/blame/buffer.c b/tests/blame/buffer.c
new file mode 100644
index 000000000..912ee9846
--- /dev/null
+++ b/tests/blame/buffer.c
@@ -0,0 +1,166 @@
+#include "blame_helpers.h"
+
+git_repository *g_repo;
+git_blame *g_fileblame, *g_bufferblame;
+
+void test_blame_buffer__initialize(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
+ cl_git_pass(git_blame_file(&g_fileblame, g_repo, "b.txt", NULL));
+ g_bufferblame = NULL;
+}
+
+void test_blame_buffer__cleanup(void)
+{
+ git_blame_free(g_fileblame);
+ git_blame_free(g_bufferblame);
+ git_repository_free(g_repo);
+}
+
+void test_blame_buffer__added_line(void)
+{
+ const git_blame_hunk *hunk;
+
+ const char *buffer = "\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+\n\
+abcdefg\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\n";
+
+ cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
+ cl_assert_equal_i(5, git_blame_get_hunk_count(g_bufferblame));
+ check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 1, 0, "000000", "b.txt");
+
+ hunk = git_blame_get_hunk_byline(g_bufferblame, 16);
+ cl_assert(hunk);
+ cl_assert_equal_s("Ben Straub", hunk->final_signature->name);
+}
+
+void test_blame_buffer__deleted_line(void)
+{
+ const char *buffer = "\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\n";
+
+ cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
+ check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 3, 0, "63d671eb", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 3, 9, 1, 0, "63d671eb", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 4, 10, 5, 0, "aa06ecca", "b.txt");
+}
+
+void test_blame_buffer__add_splits_hunk(void)
+{
+ const char *buffer = "\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+abc\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\n";
+
+ cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
+ check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 2, 0, "63d671eb", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 3, 8, 1, 0, "00000000", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 4, 9, 3, 0, "63d671eb", "b.txt");
+}
+
+void test_blame_buffer__delete_crosses_hunk_boundary(void)
+{
+ const char *buffer = "\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\n";
+
+ cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
+ check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 1, 0, "63d671eb", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 3, 7, 2, 0, "aa06ecca", "b.txt");
+}
+
+void test_blame_buffer__replace_line(void)
+{
+ const char *buffer = "\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+abc\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\n";
+
+ cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
+ check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 1, 0, "63d671eb", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 3, 7, 1, 0, "00000000", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 4, 8, 3, 0, "63d671eb", "b.txt");
+}
+
+void test_blame_buffer__add_lines_at_end(void)
+{
+ const char *buffer = "\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+\n\
+abc\n\
+def\n";
+ cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
+
+ cl_assert_equal_i(5, git_blame_get_hunk_count(g_bufferblame));
+ check_blame_hunk_index(g_repo, g_bufferblame, 0, 1, 4, 0, "da237394", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 1, 5, 1, 1, "b99f7ac0", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 5, 0, "63d671eb", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 3, 11, 5, 0, "aa06ecca", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 4, 16, 2, 0, "00000000", "b.txt");
+}
diff --git a/tests/blame/getters.c b/tests/blame/getters.c
new file mode 100644
index 000000000..66eaeecf9
--- /dev/null
+++ b/tests/blame/getters.c
@@ -0,0 +1,56 @@
+#include "clar_libgit2.h"
+
+#include "blame.h"
+
+git_blame *g_blame;
+
+void test_blame_getters__initialize(void)
+{
+ size_t i;
+ git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+
+ git_blame_hunk hunks[] = {
+ { 3, {{0}}, 1, NULL, {{0}}, "a", 0},
+ { 3, {{0}}, 4, NULL, {{0}}, "b", 0},
+ { 3, {{0}}, 7, NULL, {{0}}, "c", 0},
+ { 3, {{0}}, 10, NULL, {{0}}, "d", 0},
+ { 3, {{0}}, 13, NULL, {{0}}, "e", 0},
+ };
+
+ g_blame = git_blame__alloc(NULL, opts, "");
+
+ for (i=0; i<5; i++) {
+ git_blame_hunk *h = git__calloc(1, sizeof(git_blame_hunk));
+ h->final_start_line_number = hunks[i].final_start_line_number;
+ h->orig_path = git__strdup(hunks[i].orig_path);
+ h->lines_in_hunk = hunks[i].lines_in_hunk;
+
+ git_vector_insert(&g_blame->hunks, h);
+ }
+}
+
+void test_blame_getters__cleanup(void)
+{
+ git_blame_free(g_blame);
+}
+
+
+void test_blame_getters__byindex(void)
+{
+ const git_blame_hunk *h = git_blame_get_hunk_byindex(g_blame, 2);
+ cl_assert(h);
+ cl_assert_equal_s(h->orig_path, "c");
+
+ h = git_blame_get_hunk_byindex(g_blame, 95);
+ cl_assert_equal_p(h, NULL);
+}
+
+void test_blame_getters__byline(void)
+{
+ const git_blame_hunk *h = git_blame_get_hunk_byline(g_blame, 5);
+ cl_assert(h);
+ cl_assert_equal_s(h->orig_path, "b");
+
+ h = git_blame_get_hunk_byline(g_blame, 95);
+ cl_assert_equal_p(h, NULL);
+}
diff --git a/tests/blame/harder.c b/tests/blame/harder.c
new file mode 100644
index 000000000..7c4dd4f74
--- /dev/null
+++ b/tests/blame/harder.c
@@ -0,0 +1,71 @@
+#include "clar_libgit2.h"
+
+#include "blame.h"
+
+
+/**
+ * The test repo has a history that looks like this:
+ *
+ * * (A) bc7c5ac
+ * |\
+ * | * (B) aa06ecc
+ * * | (C) 63d671e
+ * |/
+ * * (D) da23739
+ * * (E) b99f7ac
+ *
+ */
+
+static git_repository *g_repo = NULL;
+
+void test_blame_harder__initialize(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
+}
+
+void test_blame_harder__cleanup(void)
+{
+ git_repository_free(g_repo);
+ g_repo = NULL;
+}
+
+
+
+void test_blame_harder__m(void)
+{
+ /* TODO */
+ git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+
+ opts.flags = GIT_BLAME_TRACK_COPIES_SAME_FILE;
+}
+
+
+void test_blame_harder__c(void)
+{
+ git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+
+ /* Attribute the first hunk in b.txt to (E), since it was cut/pasted from
+ * a.txt in (D).
+ */
+ opts.flags = GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES;
+}
+
+void test_blame_harder__cc(void)
+{
+ git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+
+ /* Attribute the second hunk in b.txt to (E), since it was copy/pasted from
+ * a.txt in (C).
+ */
+ opts.flags = GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES;
+}
+
+void test_blame_harder__ccc(void)
+{
+ git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+
+ /* Attribute the third hunk in b.txt to (E). This hunk was deleted from
+ * a.txt in (D), but reintroduced in (B).
+ */
+ opts.flags = GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES;
+}
diff --git a/tests/blame/simple.c b/tests/blame/simple.c
new file mode 100644
index 000000000..79bd56b83
--- /dev/null
+++ b/tests/blame/simple.c
@@ -0,0 +1,305 @@
+#include "blame_helpers.h"
+
+static git_repository *g_repo;
+static git_blame *g_blame;
+
+void test_blame_simple__initialize(void)
+{
+ g_repo = NULL;
+ g_blame = NULL;
+}
+
+void test_blame_simple__cleanup(void)
+{
+ git_blame_free(g_blame);
+ git_repository_free(g_repo);
+}
+
+/*
+ * $ git blame -s branch_file.txt
+ * orig line no final line no
+ * commit V author timestamp V
+ * c47800c7 1 (Scott Chacon 2010-05-25 11:58:14 -0700 1
+ * a65fedf3 2 (Scott Chacon 2011-08-09 19:33:46 -0700 2
+ */
+void test_blame_simple__trivial_testrepo(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo/.gitted")));
+ cl_git_pass(git_blame_file(&g_blame, g_repo, "branch_file.txt", NULL));
+
+ cl_assert_equal_i(2, git_blame_get_hunk_count(g_blame));
+ check_blame_hunk_index(g_repo, g_blame, 0, 1, 1, 0, "c47800c7", "branch_file.txt");
+ check_blame_hunk_index(g_repo, g_blame, 1, 2, 1, 0, "a65fedf3", "branch_file.txt");
+}
+
+/*
+ * $ git blame -n b.txt
+ * orig line no final line no
+ * commit V author timestamp V
+ * da237394 1 (Ben Straub 2013-02-12 15:11:30 -0800 1
+ * da237394 2 (Ben Straub 2013-02-12 15:11:30 -0800 2
+ * da237394 3 (Ben Straub 2013-02-12 15:11:30 -0800 3
+ * da237394 4 (Ben Straub 2013-02-12 15:11:30 -0800 4
+ * ^b99f7ac 1 (Ben Straub 2013-02-12 15:10:12 -0800 5
+ * 63d671eb 6 (Ben Straub 2013-02-12 15:13:04 -0800 6
+ * 63d671eb 7 (Ben Straub 2013-02-12 15:13:04 -0800 7
+ * 63d671eb 8 (Ben Straub 2013-02-12 15:13:04 -0800 8
+ * 63d671eb 9 (Ben Straub 2013-02-12 15:13:04 -0800 9
+ * 63d671eb 10 (Ben Straub 2013-02-12 15:13:04 -0800 10
+ * aa06ecca 6 (Ben Straub 2013-02-12 15:14:46 -0800 11
+ * aa06ecca 7 (Ben Straub 2013-02-12 15:14:46 -0800 12
+ * aa06ecca 8 (Ben Straub 2013-02-12 15:14:46 -0800 13
+ * aa06ecca 9 (Ben Straub 2013-02-12 15:14:46 -0800 14
+ * aa06ecca 10 (Ben Straub 2013-02-12 15:14:46 -0800 15
+ */
+void test_blame_simple__trivial_blamerepo(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
+ cl_git_pass(git_blame_file(&g_blame, g_repo, "b.txt", NULL));
+
+ cl_assert_equal_i(4, git_blame_get_hunk_count(g_blame));
+ check_blame_hunk_index(g_repo, g_blame, 0, 1, 4, 0, "da237394", "b.txt");
+ check_blame_hunk_index(g_repo, g_blame, 1, 5, 1, 1, "b99f7ac0", "b.txt");
+ check_blame_hunk_index(g_repo, g_blame, 2, 6, 5, 0, "63d671eb", "b.txt");
+ check_blame_hunk_index(g_repo, g_blame, 3, 11, 5, 0, "aa06ecca", "b.txt");
+}
+
+
+/*
+ * $ git blame -n 359fc2d -- include/git2.h
+ * orig line no final line no
+ * commit orig path V author timestamp V
+ * d12299fe src/git.h 1 (Vicent Martí 2010-12-03 22:22:10 +0200 1
+ * 359fc2d2 include/git2.h 2 (Edward Thomson 2013-01-08 17:07:25 -0600 2
+ * d12299fe src/git.h 5 (Vicent Martí 2010-12-03 22:22:10 +0200 3
+ * bb742ede include/git2.h 4 (Vicent Martí 2011-09-19 01:54:32 +0300 4
+ * bb742ede include/git2.h 5 (Vicent Martí 2011-09-19 01:54:32 +0300 5
+ * d12299fe src/git.h 24 (Vicent Martí 2010-12-03 22:22:10 +0200 6
+ * d12299fe src/git.h 25 (Vicent Martí 2010-12-03 22:22:10 +0200 7
+ * d12299fe src/git.h 26 (Vicent Martí 2010-12-03 22:22:10 +0200 8
+ * d12299fe src/git.h 27 (Vicent Martí 2010-12-03 22:22:10 +0200 9
+ * d12299fe src/git.h 28 (Vicent Martí 2010-12-03 22:22:10 +0200 10
+ * 96fab093 include/git2.h 11 (Sven Strickroth 2011-10-09 18:37:41 +0200 11
+ * 9d1dcca2 src/git2.h 33 (Vicent Martí 2011-02-07 10:35:58 +0200 12
+ * 44908fe7 src/git2.h 29 (Vicent Martí 2010-12-06 23:03:16 +0200 13
+ * a15c550d include/git2.h 14 (Vicent Martí 2011-11-16 14:09:44 +0100 14
+ * 44908fe7 src/git2.h 30 (Vicent Martí 2010-12-06 23:03:16 +0200 15
+ * d12299fe src/git.h 32 (Vicent Martí 2010-12-03 22:22:10 +0200 16
+ * 44908fe7 src/git2.h 33 (Vicent Martí 2010-12-06 23:03:16 +0200 17
+ * d12299fe src/git.h 34 (Vicent Martí 2010-12-03 22:22:10 +0200 18
+ * 44908fe7 src/git2.h 35 (Vicent Martí 2010-12-06 23:03:16 +0200 19
+ * 638c2ca4 src/git2.h 36 (Vicent Martí 2010-12-18 02:10:25 +0200 20
+ * 44908fe7 src/git2.h 36 (Vicent Martí 2010-12-06 23:03:16 +0200 21
+ * d12299fe src/git.h 37 (Vicent Martí 2010-12-03 22:22:10 +0200 22
+ * 44908fe7 src/git2.h 38 (Vicent Martí 2010-12-06 23:03:16 +0200 23
+ * 44908fe7 src/git2.h 39 (Vicent Martí 2010-12-06 23:03:16 +0200 24
+ * bf787bd8 include/git2.h 25 (Carlos Martín Nieto 2012-04-08 18:56:50 +0200 25
+ * 0984c876 include/git2.h 26 (Scott J. Goldman 2012-11-28 18:27:43 -0800 26
+ * 2f8a8ab2 src/git2.h 41 (Vicent Martí 2011-01-29 01:56:25 +0200 27
+ * 27df4275 include/git2.h 47 (Michael Schubert 2011-06-28 14:13:12 +0200 28
+ * a346992f include/git2.h 28 (Ben Straub 2012-05-10 09:47:14 -0700 29
+ * d12299fe src/git.h 40 (Vicent Martí 2010-12-03 22:22:10 +0200 30
+ * 44908fe7 src/git2.h 41 (Vicent Martí 2010-12-06 23:03:16 +0200 31
+ * 44908fe7 src/git2.h 42 (Vicent Martí 2010-12-06 23:03:16 +0200 32
+ * 44908fe7 src/git2.h 43 (Vicent Martí 2010-12-06 23:03:16 +0200 33
+ * 44908fe7 src/git2.h 44 (Vicent Martí 2010-12-06 23:03:16 +0200 34
+ * 44908fe7 src/git2.h 45 (Vicent Martí 2010-12-06 23:03:16 +0200 35
+ * 65b09b1d include/git2.h 33 (Russell Belfer 2012-02-02 18:03:43 -0800 36
+ * d12299fe src/git.h 46 (Vicent Martí 2010-12-03 22:22:10 +0200 37
+ * 44908fe7 src/git2.h 47 (Vicent Martí 2010-12-06 23:03:16 +0200 38
+ * 5d4cd003 include/git2.h 55 (Carlos Martín Nieto 2011-03-28 17:02:45 +0200 39
+ * 41fb1ca0 include/git2.h 39 (Philip Kelley 2012-10-29 13:41:14 -0400 40
+ * 2dc31040 include/git2.h 56 (Carlos Martín Nieto 2011-06-20 18:58:57 +0200 41
+ * 764df57e include/git2.h 40 (Ben Straub 2012-06-15 13:14:43 -0700 42
+ * 5280f4e6 include/git2.h 41 (Ben Straub 2012-07-31 19:39:06 -0700 43
+ * 613d5eb9 include/git2.h 43 (Philip Kelley 2012-11-28 11:42:37 -0500 44
+ * d12299fe src/git.h 48 (Vicent Martí 2010-12-03 22:22:10 +0200 45
+ * 111ee3fe include/git2.h 41 (Vicent Martí 2012-07-11 14:37:26 +0200 46
+ * f004c4a8 include/git2.h 44 (Russell Belfer 2012-08-21 17:26:39 -0700 47
+ * 111ee3fe include/git2.h 42 (Vicent Martí 2012-07-11 14:37:26 +0200 48
+ * 9c82357b include/git2.h 58 (Carlos Martín Nieto 2011-06-17 18:13:14 +0200 49
+ * d6258deb include/git2.h 61 (Carlos Martín Nieto 2011-06-25 15:10:09 +0200 50
+ * b311e313 include/git2.h 63 (Julien Miotte 2011-07-27 18:31:13 +0200 51
+ * 3412391d include/git2.h 63 (Carlos Martín Nieto 2011-07-07 11:47:31 +0200 52
+ * bfc9ca59 include/git2.h 43 (Russell Belfer 2012-03-28 16:45:36 -0700 53
+ * bf477ed4 include/git2.h 44 (Michael Schubert 2012-02-15 00:33:38 +0100 54
+ * edebceff include/git2.h 46 (nulltoken 2012-05-01 13:57:45 +0200 55
+ * 743a4b3b include/git2.h 48 (nulltoken 2012-06-15 22:24:59 +0200 56
+ * 0a32dca5 include/git2.h 54 (Michael Schubert 2012-08-19 22:26:32 +0200 57
+ * 590fb68b include/git2.h 55 (nulltoken 2012-10-04 13:47:45 +0200 58
+ * bf477ed4 include/git2.h 45 (Michael Schubert 2012-02-15 00:33:38 +0100 59
+ * d12299fe src/git.h 49 (Vicent Martí 2010-12-03 22:22:10 +0200 60
+ */
+void test_blame_simple__trivial_libgit2(void)
+{
+ git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+ git_object *obj;
+
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("../..")));
+
+ /* This test can't work on a shallow clone */
+ if (git_repository_is_shallow(g_repo))
+ return;
+
+ cl_git_pass(git_revparse_single(&obj, g_repo, "359fc2d"));
+ git_oid_cpy(&opts.newest_commit, git_object_id(obj));
+ git_object_free(obj);
+
+ cl_git_pass(git_blame_file(&g_blame, g_repo, "include/git2.h", &opts));
+
+ check_blame_hunk_index(g_repo, g_blame, 0, 1, 1, 0, "d12299fe", "src/git.h");
+ check_blame_hunk_index(g_repo, g_blame, 1, 2, 1, 0, "359fc2d2", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 2, 3, 1, 0, "d12299fe", "src/git.h");
+ check_blame_hunk_index(g_repo, g_blame, 3, 4, 2, 0, "bb742ede", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 4, 6, 5, 0, "d12299fe", "src/git.h");
+ check_blame_hunk_index(g_repo, g_blame, 5, 11, 1, 0, "96fab093", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 6, 12, 1, 0, "9d1dcca2", "src/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 7, 13, 1, 0, "44908fe7", "src/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 8, 14, 1, 0, "a15c550d", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 9, 15, 1, 0, "44908fe7", "src/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 10, 16, 1, 0, "d12299fe", "src/git.h");
+ check_blame_hunk_index(g_repo, g_blame, 11, 17, 1, 0, "44908fe7", "src/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 12, 18, 1, 0, "d12299fe", "src/git.h");
+ check_blame_hunk_index(g_repo, g_blame, 13, 19, 1, 0, "44908fe7", "src/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 14, 20, 1, 0, "638c2ca4", "src/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 15, 21, 1, 0, "44908fe7", "src/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 16, 22, 1, 0, "d12299fe", "src/git.h");
+ check_blame_hunk_index(g_repo, g_blame, 17, 23, 2, 0, "44908fe7", "src/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 18, 25, 1, 0, "bf787bd8", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 19, 26, 1, 0, "0984c876", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 20, 27, 1, 0, "2f8a8ab2", "src/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 21, 28, 1, 0, "27df4275", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 22, 29, 1, 0, "a346992f", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 23, 30, 1, 0, "d12299fe", "src/git.h");
+ check_blame_hunk_index(g_repo, g_blame, 24, 31, 5, 0, "44908fe7", "src/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 25, 36, 1, 0, "65b09b1d", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 26, 37, 1, 0, "d12299fe", "src/git.h");
+ check_blame_hunk_index(g_repo, g_blame, 27, 38, 1, 0, "44908fe7", "src/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 28, 39, 1, 0, "5d4cd003", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 29, 40, 1, 0, "41fb1ca0", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 30, 41, 1, 0, "2dc31040", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 31, 42, 1, 0, "764df57e", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 32, 43, 1, 0, "5280f4e6", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 33, 44, 1, 0, "613d5eb9", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 34, 45, 1, 0, "d12299fe", "src/git.h");
+ check_blame_hunk_index(g_repo, g_blame, 35, 46, 1, 0, "111ee3fe", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 36, 47, 1, 0, "f004c4a8", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 37, 48, 1, 0, "111ee3fe", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 38, 49, 1, 0, "9c82357b", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 39, 50, 1, 0, "d6258deb", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 40, 51, 1, 0, "b311e313", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 41, 52, 1, 0, "3412391d", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 42, 53, 1, 0, "bfc9ca59", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 43, 54, 1, 0, "bf477ed4", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 44, 55, 1, 0, "edebceff", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 45, 56, 1, 0, "743a4b3b", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 46, 57, 1, 0, "0a32dca5", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 47, 58, 1, 0, "590fb68b", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 48, 59, 1, 0, "bf477ed4", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 49, 60, 1, 0, "d12299fe", "src/git.h");
+}
+
+
+/*
+ * $ git blame -n b.txt -L 8
+ * orig line no final line no
+ * commit V author timestamp V
+ * 63d671eb 8 (Ben Straub 2013-02-12 15:13:04 -0800 8
+ * 63d671eb 9 (Ben Straub 2013-02-12 15:13:04 -0800 9
+ * 63d671eb 10 (Ben Straub 2013-02-12 15:13:04 -0800 10
+ * aa06ecca 6 (Ben Straub 2013-02-12 15:14:46 -0800 11
+ * aa06ecca 7 (Ben Straub 2013-02-12 15:14:46 -0800 12
+ * aa06ecca 8 (Ben Straub 2013-02-12 15:14:46 -0800 13
+ * aa06ecca 9 (Ben Straub 2013-02-12 15:14:46 -0800 14
+ * aa06ecca 10 (Ben Straub 2013-02-12 15:14:46 -0800 15
+ */
+void test_blame_simple__can_restrict_lines_min(void)
+{
+ git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
+
+ opts.min_line = 8;
+ cl_git_pass(git_blame_file(&g_blame, g_repo, "b.txt", &opts));
+ cl_assert_equal_i(2, git_blame_get_hunk_count(g_blame));
+ check_blame_hunk_index(g_repo, g_blame, 0, 8, 3, 0, "63d671eb", "b.txt");
+ check_blame_hunk_index(g_repo, g_blame, 1, 11, 5, 0, "aa06ecca", "b.txt");
+}
+
+/*
+ * $ git blame -n b.txt -L ,6
+ * orig line no final line no
+ * commit V author timestamp V
+ * da237394 1 (Ben Straub 2013-02-12 15:11:30 -0800 1
+ * da237394 2 (Ben Straub 2013-02-12 15:11:30 -0800 2
+ * da237394 3 (Ben Straub 2013-02-12 15:11:30 -0800 3
+ * da237394 4 (Ben Straub 2013-02-12 15:11:30 -0800 4
+ * ^b99f7ac 1 (Ben Straub 2013-02-12 15:10:12 -0800 5
+ * 63d671eb 6 (Ben Straub 2013-02-12 15:13:04 -0800 6
+ */
+void test_blame_simple__can_restrict_lines_max(void)
+{
+ git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
+
+ opts.max_line = 6;
+ cl_git_pass(git_blame_file(&g_blame, g_repo, "b.txt", &opts));
+ cl_assert_equal_i(3, git_blame_get_hunk_count(g_blame));
+ check_blame_hunk_index(g_repo, g_blame, 0, 1, 4, 0, "da237394", "b.txt");
+ check_blame_hunk_index(g_repo, g_blame, 1, 5, 1, 1, "b99f7ac0", "b.txt");
+ check_blame_hunk_index(g_repo, g_blame, 2, 6, 1, 0, "63d671eb", "b.txt");
+}
+
+/*
+ * $ git blame -n b.txt -L 2,7
+ * orig line no final line no
+ * commit V author timestamp V
+ * da237394 2 (Ben Straub 2013-02-12 15:11:30 -0800 2
+ * da237394 3 (Ben Straub 2013-02-12 15:11:30 -0800 3
+ * da237394 4 (Ben Straub 2013-02-12 15:11:30 -0800 4
+ * ^b99f7ac 1 (Ben Straub 2013-02-12 15:10:12 -0800 5
+ * 63d671eb 6 (Ben Straub 2013-02-12 15:13:04 -0800 6
+ * 63d671eb 7 (Ben Straub 2013-02-12 15:13:04 -0800 7
+ */
+void test_blame_simple__can_restrict_lines_both(void)
+{
+ git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
+
+ opts.min_line = 2;
+ opts.max_line = 7;
+ cl_git_pass(git_blame_file(&g_blame, g_repo, "b.txt", &opts));
+ cl_assert_equal_i(3, git_blame_get_hunk_count(g_blame));
+ check_blame_hunk_index(g_repo, g_blame, 0, 2, 3, 0, "da237394", "b.txt");
+ check_blame_hunk_index(g_repo, g_blame, 1, 5, 1, 1, "b99f7ac0", "b.txt");
+ check_blame_hunk_index(g_repo, g_blame, 2, 6, 2, 0, "63d671eb", "b.txt");
+}
+
+/*
+ * $ git blame -n branch_file.txt be3563a..HEAD
+ * orig line no final line no
+ * commit V author timestamp V
+ * ^be3563a 1 (Scott Chacon 2010-05-25 11:58:27 -0700 1) hi
+ * a65fedf3 2 (Scott Chacon 2011-08-09 19:33:46 -0700 2) bye!
+ */
+void test_blame_simple__can_restrict_to_newish_commits(void)
+{
+ git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
+
+ {
+ git_object *obj;
+ cl_git_pass(git_revparse_single(&obj, g_repo, "be3563a"));
+ git_oid_cpy(&opts.oldest_commit, git_object_id(obj));
+ git_object_free(obj);
+ }
+
+ cl_git_pass(git_blame_file(&g_blame, g_repo, "branch_file.txt", &opts));
+
+ cl_assert_equal_i(2, git_blame_get_hunk_count(g_blame));
+ check_blame_hunk_index(g_repo, g_blame, 0, 1, 1, 1, "be3563a", "branch_file.txt");
+ check_blame_hunk_index(g_repo, g_blame, 1, 2, 1, 0, "a65fedf", "branch_file.txt");
+}
diff --git a/tests-clar/buf/basic.c b/tests/buf/basic.c
index d558757a9..d558757a9 100644
--- a/tests-clar/buf/basic.c
+++ b/tests/buf/basic.c
diff --git a/tests-clar/buf/splice.c b/tests/buf/splice.c
index e80c93105..e80c93105 100644
--- a/tests-clar/buf/splice.c
+++ b/tests/buf/splice.c
diff --git a/tests-clar/checkout/binaryunicode.c b/tests/checkout/binaryunicode.c
index 14ab9fdfa..14ab9fdfa 100644
--- a/tests-clar/checkout/binaryunicode.c
+++ b/tests/checkout/binaryunicode.c
diff --git a/tests/checkout/checkout_helpers.c b/tests/checkout/checkout_helpers.c
new file mode 100644
index 000000000..06b4e0682
--- /dev/null
+++ b/tests/checkout/checkout_helpers.c
@@ -0,0 +1,130 @@
+#include "clar_libgit2.h"
+#include "checkout_helpers.h"
+#include "refs.h"
+#include "fileops.h"
+
+void assert_on_branch(git_repository *repo, const char *branch)
+{
+ git_reference *head;
+ git_buf bname = GIT_BUF_INIT;
+
+ cl_git_pass(git_reference_lookup(&head, repo, GIT_HEAD_FILE));
+ cl_assert_(git_reference_type(head) == GIT_REF_SYMBOLIC, branch);
+
+ cl_git_pass(git_buf_joinpath(&bname, "refs/heads", branch));
+ cl_assert_equal_s(bname.ptr, git_reference_symbolic_target(head));
+
+ git_reference_free(head);
+ git_buf_free(&bname);
+}
+
+void reset_index_to_treeish(git_object *treeish)
+{
+ git_object *tree;
+ git_index *index;
+ git_repository *repo = git_object_owner(treeish);
+
+ cl_git_pass(git_object_peel(&tree, treeish, GIT_OBJ_TREE));
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_read_tree(index, (git_tree *)tree));
+ cl_git_pass(git_index_write(index));
+
+ git_object_free(tree);
+ git_index_free(index);
+}
+
+int checkout_count_callback(
+ git_checkout_notify_t why,
+ const char *path,
+ const git_diff_file *baseline,
+ const git_diff_file *target,
+ const git_diff_file *workdir,
+ void *payload)
+{
+ checkout_counts *ct = payload;
+
+ GIT_UNUSED(baseline); GIT_UNUSED(target); GIT_UNUSED(workdir);
+
+ if (why & GIT_CHECKOUT_NOTIFY_CONFLICT) {
+ ct->n_conflicts++;
+
+ if (ct->debug) {
+ if (workdir) {
+ if (baseline) {
+ if (target)
+ fprintf(stderr, "M %s (conflicts with M %s)\n",
+ workdir->path, target->path);
+ else
+ fprintf(stderr, "M %s (conflicts with D %s)\n",
+ workdir->path, baseline->path);
+ } else {
+ if (target)
+ fprintf(stderr, "Existing %s (conflicts with A %s)\n",
+ workdir->path, target->path);
+ else
+ fprintf(stderr, "How can an untracked file be a conflict (%s)\n", workdir->path);
+ }
+ } else {
+ if (baseline) {
+ if (target)
+ fprintf(stderr, "D %s (conflicts with M %s)\n",
+ target->path, baseline->path);
+ else
+ fprintf(stderr, "D %s (conflicts with D %s)\n",
+ baseline->path, baseline->path);
+ } else {
+ if (target)
+ fprintf(stderr, "How can an added file with no workdir be a conflict (%s)\n", target->path);
+ else
+ fprintf(stderr, "How can a nonexistent file be a conflict (%s)\n", path);
+ }
+ }
+ }
+ }
+
+ if (why & GIT_CHECKOUT_NOTIFY_DIRTY) {
+ ct->n_dirty++;
+
+ if (ct->debug) {
+ if (workdir)
+ fprintf(stderr, "M %s\n", workdir->path);
+ else
+ fprintf(stderr, "D %s\n", baseline->path);
+ }
+ }
+
+ if (why & GIT_CHECKOUT_NOTIFY_UPDATED) {
+ ct->n_updates++;
+
+ if (ct->debug) {
+ if (baseline) {
+ if (target)
+ fprintf(stderr, "update: M %s\n", path);
+ else
+ fprintf(stderr, "update: D %s\n", path);
+ } else {
+ if (target)
+ fprintf(stderr, "update: A %s\n", path);
+ else
+ fprintf(stderr, "update: this makes no sense %s\n", path);
+ }
+ }
+ }
+
+ if (why & GIT_CHECKOUT_NOTIFY_UNTRACKED) {
+ ct->n_untracked++;
+
+ if (ct->debug)
+ fprintf(stderr, "? %s\n", path);
+ }
+
+ if (why & GIT_CHECKOUT_NOTIFY_IGNORED) {
+ ct->n_ignored++;
+
+ if (ct->debug)
+ fprintf(stderr, "I %s\n", path);
+ }
+
+ return 0;
+}
diff --git a/tests/checkout/checkout_helpers.h b/tests/checkout/checkout_helpers.h
new file mode 100644
index 000000000..705ee903d
--- /dev/null
+++ b/tests/checkout/checkout_helpers.h
@@ -0,0 +1,29 @@
+#include "buffer.h"
+#include "git2/object.h"
+#include "git2/repository.h"
+
+extern void assert_on_branch(git_repository *repo, const char *branch);
+extern void reset_index_to_treeish(git_object *treeish);
+
+#define check_file_contents(PATH,EXP) \
+ cl_assert_equal_file(EXP,0,PATH)
+
+#define check_file_contents_nocr(PATH,EXP) \
+ cl_assert_equal_file_ignore_cr(EXP,0,PATH)
+
+typedef struct {
+ int n_conflicts;
+ int n_dirty;
+ int n_updates;
+ int n_untracked;
+ int n_ignored;
+ int debug;
+} checkout_counts;
+
+extern int checkout_count_callback(
+ git_checkout_notify_t why,
+ const char *path,
+ const git_diff_file *baseline,
+ const git_diff_file *target,
+ const git_diff_file *workdir,
+ void *payload);
diff --git a/tests/checkout/conflict.c b/tests/checkout/conflict.c
new file mode 100644
index 000000000..66965a89b
--- /dev/null
+++ b/tests/checkout/conflict.c
@@ -0,0 +1,1127 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/sys/index.h"
+#include "fileops.h"
+
+static git_repository *g_repo;
+static git_index *g_index;
+
+#define TEST_REPO_PATH "merge-resolve"
+
+#define CONFLICTING_ANCESTOR_OID "d427e0b2e138501a3d15cc376077a3631e15bd46"
+#define CONFLICTING_OURS_OID "4e886e602529caa9ab11d71f86634bd1b6e0de10"
+#define CONFLICTING_THEIRS_OID "2bd0a343aeef7a2cf0d158478966a6e587ff3863"
+
+#define AUTOMERGEABLE_ANCESTOR_OID "6212c31dab5e482247d7977e4f0dd3601decf13b"
+#define AUTOMERGEABLE_OURS_OID "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf"
+#define AUTOMERGEABLE_THEIRS_OID "058541fc37114bfc1dddf6bd6bffc7fae5c2e6fe"
+
+#define LINK_ANCESTOR_OID "1a010b1c0f081b2e8901d55307a15c29ff30af0e"
+#define LINK_OURS_OID "72ea499e108df5ff0a4a913e7655bbeeb1fb69f2"
+#define LINK_THEIRS_OID "8bfb012a6d809e499bd8d3e194a3929bc8995b93"
+
+#define LINK_ANCESTOR_TARGET "file"
+#define LINK_OURS_TARGET "other-file"
+#define LINK_THEIRS_TARGET "still-another-file"
+
+#define CONFLICTING_OURS_FILE \
+ "this file is changed in master and branch\n"
+#define CONFLICTING_THEIRS_FILE \
+ "this file is changed in branch and master\n"
+#define CONFLICTING_DIFF3_FILE \
+ "<<<<<<< ours\n" \
+ "this file is changed in master and branch\n" \
+ "=======\n" \
+ "this file is changed in branch and master\n" \
+ ">>>>>>> theirs\n"
+
+#define AUTOMERGEABLE_MERGED_FILE \
+ "this file is changed in master\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is changed in branch\n"
+
+struct checkout_index_entry {
+ uint16_t mode;
+ char oid_str[41];
+ int stage;
+ char path[128];
+};
+
+struct checkout_name_entry {
+ char ancestor[64];
+ char ours[64];
+ char theirs[64];
+};
+
+void test_checkout_conflict__initialize(void)
+{
+ g_repo = cl_git_sandbox_init(TEST_REPO_PATH);
+ git_repository_index(&g_index, g_repo);
+
+ cl_git_rewritefile(
+ TEST_REPO_PATH "/.gitattributes",
+ "* text eol=lf\n");
+}
+
+void test_checkout_conflict__cleanup(void)
+{
+ git_index_free(g_index);
+ cl_git_sandbox_cleanup();
+}
+
+static void create_index(struct checkout_index_entry *entries, size_t entries_len)
+{
+ git_buf path = GIT_BUF_INIT;
+ size_t i;
+
+ for (i = 0; i < entries_len; i++) {
+ git_buf_joinpath(&path, TEST_REPO_PATH, entries[i].path);
+
+ if (entries[i].stage == 3 && (i == 0 || strcmp(entries[i-1].path, entries[i].path) != 0 || entries[i-1].stage != 2))
+ p_unlink(git_buf_cstr(&path));
+
+ git_index_remove_bypath(g_index, entries[i].path);
+ }
+
+ for (i = 0; i < entries_len; i++) {
+ git_index_entry entry;
+
+ memset(&entry, 0x0, sizeof(git_index_entry));
+
+ entry.mode = entries[i].mode;
+ entry.flags = entries[i].stage << GIT_IDXENTRY_STAGESHIFT;
+ git_oid_fromstr(&entry.oid, entries[i].oid_str);
+ entry.path = entries[i].path;
+
+ cl_git_pass(git_index_add(g_index, &entry));
+ }
+
+ git_buf_free(&path);
+}
+
+static void create_index_names(struct checkout_name_entry *entries, size_t entries_len)
+{
+ size_t i;
+
+ for (i = 0; i < entries_len; i++) {
+ cl_git_pass(git_index_name_add(g_index,
+ strlen(entries[i].ancestor) == 0 ? NULL : entries[i].ancestor,
+ strlen(entries[i].ours) == 0 ? NULL : entries[i].ours,
+ strlen(entries[i].theirs) == 0 ? NULL : entries[i].theirs));
+ }
+}
+
+static void create_conflicting_index(void)
+{
+ struct checkout_index_entry checkout_index_entries[] = {
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "conflicting.txt" },
+ { 0100644, CONFLICTING_OURS_OID, 2, "conflicting.txt" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "conflicting.txt" },
+ };
+
+ create_index(checkout_index_entries, 3);
+ git_index_write(g_index);
+}
+
+static void ensure_workdir_contents(const char *path, const char *contents)
+{
+ git_buf fullpath = GIT_BUF_INIT, data_buf = GIT_BUF_INIT;
+
+ cl_git_pass(
+ git_buf_joinpath(&fullpath, git_repository_workdir(g_repo), path));
+
+ cl_git_pass(git_futils_readbuffer(&data_buf, git_buf_cstr(&fullpath)));
+ cl_assert(strcmp(git_buf_cstr(&data_buf), contents) == 0);
+
+ git_buf_free(&fullpath);
+ git_buf_free(&data_buf);
+}
+
+static void ensure_workdir_oid(const char *path, const char *oid_str)
+{
+ git_oid expected, actual;
+
+ cl_git_pass(git_oid_fromstr(&expected, oid_str));
+ cl_git_pass(git_repository_hashfile(&actual, g_repo, path, GIT_OBJ_BLOB, NULL));
+ cl_assert(git_oid_cmp(&expected, &actual) == 0);
+}
+
+static void ensure_workdir_mode(const char *path, int mode)
+{
+#ifndef GIT_WIN32
+ git_buf fullpath = GIT_BUF_INIT;
+ struct stat st;
+
+ cl_git_pass(
+ git_buf_joinpath(&fullpath, git_repository_workdir(g_repo), path));
+
+ cl_git_pass(p_stat(git_buf_cstr(&fullpath), &st));
+ cl_assert_equal_i(mode, st.st_mode);
+
+ git_buf_free(&fullpath);
+#endif
+}
+
+static void ensure_workdir(const char *path, int mode, const char *oid_str)
+{
+ ensure_workdir_mode(path, mode);
+ ensure_workdir_oid(path, oid_str);
+}
+
+static void ensure_workdir_link(const char *path, const char *target)
+{
+#ifdef GIT_WIN32
+ ensure_workdir_contents(path, target);
+#else
+ git_buf fullpath = GIT_BUF_INIT;
+ char actual[1024];
+ struct stat st;
+ int len;
+
+ cl_git_pass(
+ git_buf_joinpath(&fullpath, git_repository_workdir(g_repo), path));
+
+ cl_git_pass(p_lstat(git_buf_cstr(&fullpath), &st));
+ cl_assert(S_ISLNK(st.st_mode));
+
+ cl_assert((len = p_readlink(git_buf_cstr(&fullpath), actual, 1024)) > 0);
+ actual[len] = '\0';
+ cl_assert(strcmp(actual, target) == 0);
+
+ git_buf_free(&fullpath);
+#endif
+}
+
+void test_checkout_conflict__ignored(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ opts.checkout_strategy |= GIT_CHECKOUT_SKIP_UNMERGED;
+
+ create_conflicting_index();
+ cl_git_pass(p_unlink(TEST_REPO_PATH "/conflicting.txt"));
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ cl_assert(!git_path_exists(TEST_REPO_PATH "/conflicting.txt"));
+}
+
+void test_checkout_conflict__ours(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ opts.checkout_strategy |= GIT_CHECKOUT_USE_OURS;
+
+ create_conflicting_index();
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ ensure_workdir_contents("conflicting.txt", CONFLICTING_OURS_FILE);
+}
+
+void test_checkout_conflict__theirs(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ opts.checkout_strategy |= GIT_CHECKOUT_USE_THEIRS;
+
+ create_conflicting_index();
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ ensure_workdir_contents("conflicting.txt", CONFLICTING_THEIRS_FILE);
+
+}
+
+void test_checkout_conflict__diff3(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ create_conflicting_index();
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ ensure_workdir_contents("conflicting.txt", CONFLICTING_DIFF3_FILE);
+}
+
+void test_checkout_conflict__automerge(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ struct checkout_index_entry checkout_index_entries[] = {
+ { 0100644, AUTOMERGEABLE_ANCESTOR_OID, 1, "automergeable.txt" },
+ { 0100644, AUTOMERGEABLE_OURS_OID, 2, "automergeable.txt" },
+ { 0100644, AUTOMERGEABLE_THEIRS_OID, 3, "automergeable.txt" },
+ };
+
+ create_index(checkout_index_entries, 3);
+ git_index_write(g_index);
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ ensure_workdir_contents("automergeable.txt", AUTOMERGEABLE_MERGED_FILE);
+}
+
+void test_checkout_conflict__directory_file(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ struct checkout_index_entry checkout_index_entries[] = {
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "df-1" },
+ { 0100644, CONFLICTING_OURS_OID, 2, "df-1" },
+ { 0100644, CONFLICTING_THEIRS_OID, 0, "df-1/file" },
+
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "df-2" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "df-2" },
+ { 0100644, CONFLICTING_OURS_OID, 0, "df-2/file" },
+
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "df-3" },
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "df-3/file" },
+ { 0100644, CONFLICTING_OURS_OID, 2, "df-3/file" },
+
+ { 0100644, CONFLICTING_OURS_OID, 2, "df-4" },
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "df-4/file" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "df-4/file" },
+ };
+
+ opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+
+ create_index(checkout_index_entries, 12);
+ git_index_write(g_index);
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ ensure_workdir_oid("df-1/file", CONFLICTING_THEIRS_OID);
+ ensure_workdir_oid("df-1~ours", CONFLICTING_OURS_OID);
+ ensure_workdir_oid("df-2/file", CONFLICTING_OURS_OID);
+ ensure_workdir_oid("df-2~theirs", CONFLICTING_THEIRS_OID);
+ ensure_workdir_oid("df-3/file", CONFLICTING_OURS_OID);
+ ensure_workdir_oid("df-3~theirs", CONFLICTING_THEIRS_OID);
+ ensure_workdir_oid("df-4~ours", CONFLICTING_OURS_OID);
+ ensure_workdir_oid("df-4/file", CONFLICTING_THEIRS_OID);
+}
+
+void test_checkout_conflict__directory_file_with_custom_labels(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ struct checkout_index_entry checkout_index_entries[] = {
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "df-1" },
+ { 0100644, CONFLICTING_OURS_OID, 2, "df-1" },
+ { 0100644, CONFLICTING_THEIRS_OID, 0, "df-1/file" },
+
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "df-2" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "df-2" },
+ { 0100644, CONFLICTING_OURS_OID, 0, "df-2/file" },
+
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "df-3" },
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "df-3/file" },
+ { 0100644, CONFLICTING_OURS_OID, 2, "df-3/file" },
+
+ { 0100644, CONFLICTING_OURS_OID, 2, "df-4" },
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "df-4/file" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "df-4/file" },
+ };
+
+ opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+ opts.our_label = "HEAD";
+ opts.their_label = "branch";
+
+ create_index(checkout_index_entries, 12);
+ git_index_write(g_index);
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ ensure_workdir_oid("df-1/file", CONFLICTING_THEIRS_OID);
+ ensure_workdir_oid("df-1~HEAD", CONFLICTING_OURS_OID);
+ ensure_workdir_oid("df-2/file", CONFLICTING_OURS_OID);
+ ensure_workdir_oid("df-2~branch", CONFLICTING_THEIRS_OID);
+ ensure_workdir_oid("df-3/file", CONFLICTING_OURS_OID);
+ ensure_workdir_oid("df-3~branch", CONFLICTING_THEIRS_OID);
+ ensure_workdir_oid("df-4~HEAD", CONFLICTING_OURS_OID);
+ ensure_workdir_oid("df-4/file", CONFLICTING_THEIRS_OID);
+}
+
+void test_checkout_conflict__link_file(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ struct checkout_index_entry checkout_index_entries[] = {
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "link-1" },
+ { 0100644, CONFLICTING_OURS_OID, 2, "link-1" },
+ { 0120000, LINK_THEIRS_OID, 3, "link-1" },
+
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "link-2" },
+ { 0120000, LINK_OURS_OID, 2, "link-2" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "link-2" },
+
+ { 0120000, LINK_ANCESTOR_OID, 1, "link-3" },
+ { 0100644, CONFLICTING_OURS_OID, 2, "link-3" },
+ { 0120000, LINK_THEIRS_OID, 3, "link-3" },
+
+ { 0120000, LINK_ANCESTOR_OID, 1, "link-4" },
+ { 0120000, LINK_OURS_OID, 2, "link-4" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "link-4" },
+ };
+
+ opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+
+ create_index(checkout_index_entries, 12);
+ git_index_write(g_index);
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ /* Typechange conflicts always keep the file in the workdir */
+ ensure_workdir_oid("link-1", CONFLICTING_OURS_OID);
+ ensure_workdir_oid("link-2", CONFLICTING_THEIRS_OID);
+ ensure_workdir_oid("link-3", CONFLICTING_OURS_OID);
+ ensure_workdir_oid("link-4", CONFLICTING_THEIRS_OID);
+}
+
+void test_checkout_conflict__links(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ struct checkout_index_entry checkout_index_entries[] = {
+ { 0120000, LINK_ANCESTOR_OID, 1, "link-1" },
+ { 0120000, LINK_OURS_OID, 2, "link-1" },
+ { 0120000, LINK_THEIRS_OID, 3, "link-1" },
+
+ { 0120000, LINK_OURS_OID, 2, "link-2" },
+ { 0120000, LINK_THEIRS_OID, 3, "link-2" },
+ };
+
+ opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+
+ create_index(checkout_index_entries, 5);
+ git_index_write(g_index);
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ /* Conflicts with links always keep the ours side (even with -Xtheirs) */
+ ensure_workdir_link("link-1", LINK_OURS_TARGET);
+ ensure_workdir_link("link-2", LINK_OURS_TARGET);
+}
+
+void test_checkout_conflict__add_add(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ struct checkout_index_entry checkout_index_entries[] = {
+ { 0100644, CONFLICTING_OURS_OID, 2, "conflicting.txt" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "conflicting.txt" },
+ };
+
+ opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+
+ create_index(checkout_index_entries, 2);
+ git_index_write(g_index);
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ /* Add/add writes diff3 files */
+ ensure_workdir_contents("conflicting.txt", CONFLICTING_DIFF3_FILE);
+}
+
+void test_checkout_conflict__mode_change(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ struct checkout_index_entry checkout_index_entries[] = {
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "executable-1" },
+ { 0100755, CONFLICTING_ANCESTOR_OID, 2, "executable-1" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "executable-1" },
+
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "executable-2" },
+ { 0100644, CONFLICTING_OURS_OID, 2, "executable-2" },
+ { 0100755, CONFLICTING_ANCESTOR_OID, 3, "executable-2" },
+
+ { 0100755, CONFLICTING_ANCESTOR_OID, 1, "executable-3" },
+ { 0100644, CONFLICTING_ANCESTOR_OID, 2, "executable-3" },
+ { 0100755, CONFLICTING_THEIRS_OID, 3, "executable-3" },
+
+ { 0100755, CONFLICTING_ANCESTOR_OID, 1, "executable-4" },
+ { 0100755, CONFLICTING_OURS_OID, 2, "executable-4" },
+ { 0100644, CONFLICTING_ANCESTOR_OID, 3, "executable-4" },
+
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "executable-5" },
+ { 0100755, CONFLICTING_OURS_OID, 2, "executable-5" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "executable-5" },
+
+ { 0100755, CONFLICTING_ANCESTOR_OID, 1, "executable-6" },
+ { 0100644, CONFLICTING_OURS_OID, 2, "executable-6" },
+ { 0100755, CONFLICTING_THEIRS_OID, 3, "executable-6" },
+ };
+
+ opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+
+ create_index(checkout_index_entries, 18);
+ git_index_write(g_index);
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ /* Keep the modified mode */
+ ensure_workdir_oid("executable-1", CONFLICTING_THEIRS_OID);
+ ensure_workdir_mode("executable-1", 0100755);
+
+ ensure_workdir_oid("executable-2", CONFLICTING_OURS_OID);
+ ensure_workdir_mode("executable-2", 0100755);
+
+ ensure_workdir_oid("executable-3", CONFLICTING_THEIRS_OID);
+ ensure_workdir_mode("executable-3", 0100644);
+
+ ensure_workdir_oid("executable-4", CONFLICTING_OURS_OID);
+ ensure_workdir_mode("executable-4", 0100644);
+
+ ensure_workdir_contents("executable-5", CONFLICTING_DIFF3_FILE);
+ ensure_workdir_mode("executable-5", 0100755);
+
+ ensure_workdir_contents("executable-6", CONFLICTING_DIFF3_FILE);
+ ensure_workdir_mode("executable-6", 0100644);
+}
+
+void test_checkout_conflict__renames(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ struct checkout_index_entry checkout_index_entries[] = {
+ { 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" },
+ { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt" },
+ { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 1, "0b-rewritten-in-ours.txt" },
+ { 0100644, "e376fbdd06ebf021c92724da9f26f44212734e3e", 2, "0b-rewritten-in-ours.txt" },
+ { 0100644, "b2d399ae15224e1d58066e3c8df70ce37de7a656", 3, "0b-rewritten-in-ours.txt" },
+ { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt" },
+ { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 1, "0c-rewritten-in-theirs.txt" },
+ { 0100644, "efc9121fdedaf08ba180b53ebfbcf71bd488ed09", 2, "0c-rewritten-in-theirs.txt" },
+ { 0100644, "712ebba6669ea847d9829e4f1059d6c830c8b531", 3, "0c-rewritten-in-theirs.txt" },
+ { 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 0, "1a-newname-in-ours-edited-in-theirs.txt" },
+ { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt" },
+ { 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 0, "1b-newname-in-theirs-edited-in-ours.txt" },
+ { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt" },
+ { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" },
+ { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 2, "3a-newname-in-ours-deleted-in-theirs.txt" },
+ { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 1, "3a-renamed-in-ours-deleted-in-theirs.txt" },
+ { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 3, "3b-newname-in-theirs-deleted-in-ours.txt" },
+ { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 1, "3b-renamed-in-theirs-deleted-in-ours.txt" },
+ { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 2, "4a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a", 3, "4a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 1, "4a-renamed-in-ours-added-in-theirs.txt" },
+ { 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 2, "4b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 3, "4b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 1, "4b-renamed-in-theirs-added-in-ours.txt" },
+ { 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 2, "5a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "98ba4205fcf31f5dd93c916d35fe3f3b3d0e6714", 3, "5a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 1, "5a-renamed-in-ours-added-in-theirs.txt" },
+ { 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 3, "5a-renamed-in-ours-added-in-theirs.txt" },
+ { 0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced", 2, "5b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 3, "5b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 1, "5b-renamed-in-theirs-added-in-ours.txt" },
+ { 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 2, "5b-renamed-in-theirs-added-in-ours.txt" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 2, "6-both-renamed-1-to-2-ours.txt" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 3, "6-both-renamed-1-to-2-theirs.txt" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 1, "6-both-renamed-1-to-2.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 1, "7-both-renamed-side-1.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 3, "7-both-renamed-side-1.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 1, "7-both-renamed-side-2.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 2, "7-both-renamed-side-2.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 2, "7-both-renamed.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 3, "7-both-renamed.txt" }
+ };
+
+ struct checkout_name_entry checkout_name_entries[] = {
+ {
+ "3a-renamed-in-ours-deleted-in-theirs.txt",
+ "3a-newname-in-ours-deleted-in-theirs.txt",
+ ""
+ },
+
+ {
+ "3b-renamed-in-theirs-deleted-in-ours.txt",
+ "",
+ "3b-newname-in-theirs-deleted-in-ours.txt"
+ },
+
+ {
+ "4a-renamed-in-ours-added-in-theirs.txt",
+ "4a-newname-in-ours-added-in-theirs.txt",
+ ""
+ },
+
+ {
+ "4b-renamed-in-theirs-added-in-ours.txt",
+ "",
+ "4b-newname-in-theirs-added-in-ours.txt"
+ },
+
+ {
+ "5a-renamed-in-ours-added-in-theirs.txt",
+ "5a-newname-in-ours-added-in-theirs.txt",
+ "5a-renamed-in-ours-added-in-theirs.txt"
+ },
+
+ {
+ "5b-renamed-in-theirs-added-in-ours.txt",
+ "5b-renamed-in-theirs-added-in-ours.txt",
+ "5b-newname-in-theirs-added-in-ours.txt"
+ },
+
+ {
+ "6-both-renamed-1-to-2.txt",
+ "6-both-renamed-1-to-2-ours.txt",
+ "6-both-renamed-1-to-2-theirs.txt"
+ },
+
+ {
+ "7-both-renamed-side-1.txt",
+ "7-both-renamed.txt",
+ "7-both-renamed-side-1.txt"
+ },
+
+ {
+ "7-both-renamed-side-2.txt",
+ "7-both-renamed-side-2.txt",
+ "7-both-renamed.txt"
+ }
+ };
+
+ opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+
+ create_index(checkout_index_entries, 41);
+ create_index_names(checkout_name_entries, 9);
+ git_index_write(g_index);
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ ensure_workdir("0a-no-change.txt",
+ 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e");
+
+ ensure_workdir("0b-duplicated-in-ours.txt",
+ 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6");
+
+ ensure_workdir("0b-rewritten-in-ours.txt",
+ 0100644, "4c7e515d6d52d820496858f2f059ece69e99e2e3");
+
+ ensure_workdir("0c-duplicated-in-theirs.txt",
+ 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31");
+
+ ensure_workdir("0c-rewritten-in-theirs.txt",
+ 0100644, "4648d658682d1155c2a3db5b0c53305e26884ea5");
+
+ ensure_workdir("1a-newname-in-ours-edited-in-theirs.txt",
+ 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638");
+
+ ensure_workdir("1a-newname-in-ours.txt",
+ 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb");
+
+ ensure_workdir("1b-newname-in-theirs-edited-in-ours.txt",
+ 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a");
+
+ ensure_workdir("1b-newname-in-theirs.txt",
+ 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136");
+
+ ensure_workdir("2-newname-in-both.txt",
+ 0100644, "178940b450f238a56c0d75b7955cb57b38191982");
+
+ ensure_workdir("3a-newname-in-ours-deleted-in-theirs.txt",
+ 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9");
+
+ ensure_workdir("3b-newname-in-theirs-deleted-in-ours.txt",
+ 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495");
+
+ ensure_workdir("4a-newname-in-ours-added-in-theirs.txt~ours",
+ 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c");
+
+ ensure_workdir("4a-newname-in-ours-added-in-theirs.txt~theirs",
+ 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a");
+
+ ensure_workdir("4b-newname-in-theirs-added-in-ours.txt~ours",
+ 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9");
+
+ ensure_workdir("4b-newname-in-theirs-added-in-ours.txt~theirs",
+ 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db");
+
+ ensure_workdir("5a-newname-in-ours-added-in-theirs.txt~ours",
+ 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436");
+
+ ensure_workdir("5a-newname-in-ours-added-in-theirs.txt~theirs",
+ 0100644, "98ba4205fcf31f5dd93c916d35fe3f3b3d0e6714");
+
+ ensure_workdir("5b-newname-in-theirs-added-in-ours.txt~ours",
+ 0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced");
+
+ ensure_workdir("5b-newname-in-theirs-added-in-ours.txt~theirs",
+ 0100644, "63247125386de9ec90a27ad36169307bf8a11a38");
+
+ ensure_workdir("6-both-renamed-1-to-2-ours.txt",
+ 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450");
+
+ ensure_workdir("6-both-renamed-1-to-2-theirs.txt",
+ 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450");
+
+ ensure_workdir("7-both-renamed.txt~ours",
+ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11");
+
+ ensure_workdir("7-both-renamed.txt~theirs",
+ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07");
+}
+
+void test_checkout_conflict__rename_keep_ours(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ struct checkout_index_entry checkout_index_entries[] = {
+ { 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" },
+ { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt" },
+ { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 1, "0b-rewritten-in-ours.txt" },
+ { 0100644, "e376fbdd06ebf021c92724da9f26f44212734e3e", 2, "0b-rewritten-in-ours.txt" },
+ { 0100644, "b2d399ae15224e1d58066e3c8df70ce37de7a656", 3, "0b-rewritten-in-ours.txt" },
+ { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt" },
+ { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 1, "0c-rewritten-in-theirs.txt" },
+ { 0100644, "efc9121fdedaf08ba180b53ebfbcf71bd488ed09", 2, "0c-rewritten-in-theirs.txt" },
+ { 0100644, "712ebba6669ea847d9829e4f1059d6c830c8b531", 3, "0c-rewritten-in-theirs.txt" },
+ { 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 0, "1a-newname-in-ours-edited-in-theirs.txt" },
+ { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt" },
+ { 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 0, "1b-newname-in-theirs-edited-in-ours.txt" },
+ { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt" },
+ { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" },
+ { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 2, "3a-newname-in-ours-deleted-in-theirs.txt" },
+ { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 1, "3a-renamed-in-ours-deleted-in-theirs.txt" },
+ { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 3, "3b-newname-in-theirs-deleted-in-ours.txt" },
+ { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 1, "3b-renamed-in-theirs-deleted-in-ours.txt" },
+ { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 2, "4a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a", 3, "4a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 1, "4a-renamed-in-ours-added-in-theirs.txt" },
+ { 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 2, "4b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 3, "4b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 1, "4b-renamed-in-theirs-added-in-ours.txt" },
+ { 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 2, "5a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "98ba4205fcf31f5dd93c916d35fe3f3b3d0e6714", 3, "5a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 1, "5a-renamed-in-ours-added-in-theirs.txt" },
+ { 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 3, "5a-renamed-in-ours-added-in-theirs.txt" },
+ { 0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced", 2, "5b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 3, "5b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 1, "5b-renamed-in-theirs-added-in-ours.txt" },
+ { 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 2, "5b-renamed-in-theirs-added-in-ours.txt" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 2, "6-both-renamed-1-to-2-ours.txt" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 3, "6-both-renamed-1-to-2-theirs.txt" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 1, "6-both-renamed-1-to-2.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 1, "7-both-renamed-side-1.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 3, "7-both-renamed-side-1.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 1, "7-both-renamed-side-2.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 2, "7-both-renamed-side-2.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 2, "7-both-renamed.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 3, "7-both-renamed.txt" }
+ };
+
+ struct checkout_name_entry checkout_name_entries[] = {
+ {
+ "3a-renamed-in-ours-deleted-in-theirs.txt",
+ "3a-newname-in-ours-deleted-in-theirs.txt",
+ ""
+ },
+
+ {
+ "3b-renamed-in-theirs-deleted-in-ours.txt",
+ "",
+ "3b-newname-in-theirs-deleted-in-ours.txt"
+ },
+
+ {
+ "4a-renamed-in-ours-added-in-theirs.txt",
+ "4a-newname-in-ours-added-in-theirs.txt",
+ ""
+ },
+
+ {
+ "4b-renamed-in-theirs-added-in-ours.txt",
+ "",
+ "4b-newname-in-theirs-added-in-ours.txt"
+ },
+
+ {
+ "5a-renamed-in-ours-added-in-theirs.txt",
+ "5a-newname-in-ours-added-in-theirs.txt",
+ "5a-renamed-in-ours-added-in-theirs.txt"
+ },
+
+ {
+ "5b-renamed-in-theirs-added-in-ours.txt",
+ "5b-renamed-in-theirs-added-in-ours.txt",
+ "5b-newname-in-theirs-added-in-ours.txt"
+ },
+
+ {
+ "6-both-renamed-1-to-2.txt",
+ "6-both-renamed-1-to-2-ours.txt",
+ "6-both-renamed-1-to-2-theirs.txt"
+ },
+
+ {
+ "7-both-renamed-side-1.txt",
+ "7-both-renamed.txt",
+ "7-both-renamed-side-1.txt"
+ },
+
+ {
+ "7-both-renamed-side-2.txt",
+ "7-both-renamed-side-2.txt",
+ "7-both-renamed.txt"
+ }
+ };
+
+ opts.checkout_strategy |= GIT_CHECKOUT_SAFE | GIT_CHECKOUT_USE_OURS;
+
+ create_index(checkout_index_entries, 41);
+ create_index_names(checkout_name_entries, 9);
+ git_index_write(g_index);
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ ensure_workdir("0a-no-change.txt",
+ 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e");
+
+ ensure_workdir("0b-duplicated-in-ours.txt",
+ 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6");
+
+ ensure_workdir("0b-rewritten-in-ours.txt",
+ 0100644, "e376fbdd06ebf021c92724da9f26f44212734e3e");
+
+ ensure_workdir("0c-duplicated-in-theirs.txt",
+ 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31");
+
+ ensure_workdir("0c-rewritten-in-theirs.txt",
+ 0100644, "efc9121fdedaf08ba180b53ebfbcf71bd488ed09");
+
+ ensure_workdir("1a-newname-in-ours-edited-in-theirs.txt",
+ 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638");
+
+ ensure_workdir("1a-newname-in-ours.txt",
+ 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb");
+
+ ensure_workdir("1b-newname-in-theirs-edited-in-ours.txt",
+ 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a");
+
+ ensure_workdir("1b-newname-in-theirs.txt",
+ 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136");
+
+ ensure_workdir("2-newname-in-both.txt",
+ 0100644, "178940b450f238a56c0d75b7955cb57b38191982");
+
+ ensure_workdir("3a-newname-in-ours-deleted-in-theirs.txt",
+ 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9");
+
+ ensure_workdir("3b-newname-in-theirs-deleted-in-ours.txt",
+ 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495");
+
+ ensure_workdir("4a-newname-in-ours-added-in-theirs.txt",
+ 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c");
+
+ ensure_workdir("4b-newname-in-theirs-added-in-ours.txt",
+ 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9");
+
+ ensure_workdir("5a-newname-in-ours-added-in-theirs.txt",
+ 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436");
+
+ ensure_workdir("5b-newname-in-theirs-added-in-ours.txt",
+ 0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced");
+
+ ensure_workdir("6-both-renamed-1-to-2-ours.txt",
+ 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450");
+
+ ensure_workdir("7-both-renamed.txt",
+ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11");
+}
+
+void test_checkout_conflict__name_mangled_file_exists_in_workdir(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ struct checkout_index_entry checkout_index_entries[] = {
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 1, "test-one-side-one.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 3, "test-one-side-one.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 1, "test-one-side-two.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 2, "test-one-side-two.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 2, "test-one.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 3, "test-one.txt" },
+
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 1, "test-two-side-one.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 3, "test-two-side-one.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 1, "test-two-side-two.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 2, "test-two-side-two.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 2, "test-two.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 3, "test-two.txt" },
+
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 1, "test-three-side-one.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 3, "test-three-side-one.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 1, "test-three-side-two.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 2, "test-three-side-two.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 2, "test-three.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 3, "test-three.txt" },
+
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "directory_file-one" },
+ { 0100644, CONFLICTING_OURS_OID, 2, "directory_file-one" },
+ { 0100644, CONFLICTING_THEIRS_OID, 0, "directory_file-one/file" },
+
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "directory_file-two" },
+ { 0100644, CONFLICTING_OURS_OID, 0, "directory_file-two/file" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "directory_file-two" },
+ };
+
+ struct checkout_name_entry checkout_name_entries[] = {
+ {
+ "test-one-side-one.txt",
+ "test-one.txt",
+ "test-one-side-one.txt"
+ },
+ {
+ "test-one-side-two.txt",
+ "test-one-side-two.txt",
+ "test-one.txt"
+ },
+
+ {
+ "test-two-side-one.txt",
+ "test-two.txt",
+ "test-two-side-one.txt"
+ },
+ {
+ "test-two-side-two.txt",
+ "test-two-side-two.txt",
+ "test-two.txt"
+ },
+
+ {
+ "test-three-side-one.txt",
+ "test-three.txt",
+ "test-three-side-one.txt"
+ },
+ {
+ "test-three-side-two.txt",
+ "test-three-side-two.txt",
+ "test-three.txt"
+ }
+ };
+
+ opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+
+ create_index(checkout_index_entries, 24);
+ create_index_names(checkout_name_entries, 6);
+ git_index_write(g_index);
+
+ /* Add some files on disk that conflict with the names that would be chosen
+ * for the files written for each side. */
+
+ cl_git_rewritefile("merge-resolve/test-one.txt~ours",
+ "Expect index contents to be written to ~ours_0");
+ cl_git_rewritefile("merge-resolve/test-one.txt~theirs",
+ "Expect index contents to be written to ~theirs_0");
+
+ cl_git_rewritefile("merge-resolve/test-two.txt~ours",
+ "Expect index contents to be written to ~ours_3");
+ cl_git_rewritefile("merge-resolve/test-two.txt~theirs",
+ "Expect index contents to be written to ~theirs_3");
+ cl_git_rewritefile("merge-resolve/test-two.txt~ours_0",
+ "Expect index contents to be written to ~ours_3");
+ cl_git_rewritefile("merge-resolve/test-two.txt~theirs_0",
+ "Expect index contents to be written to ~theirs_3");
+ cl_git_rewritefile("merge-resolve/test-two.txt~ours_1",
+ "Expect index contents to be written to ~ours_3");
+ cl_git_rewritefile("merge-resolve/test-two.txt~theirs_1",
+ "Expect index contents to be written to ~theirs_3");
+ cl_git_rewritefile("merge-resolve/test-two.txt~ours_2",
+ "Expect index contents to be written to ~ours_3");
+ cl_git_rewritefile("merge-resolve/test-two.txt~theirs_2",
+ "Expect index contents to be written to ~theirs_3");
+
+ cl_git_rewritefile("merge-resolve/test-three.txt~Ours",
+ "Expect case insensitive filesystems to create ~ours_0");
+ cl_git_rewritefile("merge-resolve/test-three.txt~THEIRS",
+ "Expect case insensitive filesystems to create ~theirs_0");
+
+ cl_git_rewritefile("merge-resolve/directory_file-one~ours",
+ "Index contents written to ~ours_0 in this D/F conflict");
+ cl_git_rewritefile("merge-resolve/directory_file-two~theirs",
+ "Index contents written to ~theirs_0 in this D/F conflict");
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ ensure_workdir("test-one.txt~ours_0",
+ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11");
+ ensure_workdir("test-one.txt~theirs_0",
+ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07");
+
+ ensure_workdir("test-two.txt~ours_3",
+ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11");
+ ensure_workdir("test-two.txt~theirs_3",
+ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07");
+
+ /* Name is mangled on case insensitive only */
+#if defined(GIT_WIN32) || defined(__APPLE__)
+ ensure_workdir("test-three.txt~ours_0",
+ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11");
+ ensure_workdir("test-three.txt~theirs_0",
+ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07");
+#else
+ ensure_workdir("test-three.txt~ours",
+ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11");
+ ensure_workdir("test-three.txt~theirs",
+ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07");
+#endif
+
+ ensure_workdir("directory_file-one~ours_0", 0100644, CONFLICTING_OURS_OID);
+ ensure_workdir("directory_file-two~theirs_0", 0100644, CONFLICTING_THEIRS_OID);
+}
+
+void test_checkout_conflict__update_only(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ struct checkout_index_entry checkout_index_entries[] = {
+ { 0100644, AUTOMERGEABLE_ANCESTOR_OID, 1, "automergeable.txt" },
+ { 0100644, AUTOMERGEABLE_OURS_OID, 2, "automergeable.txt" },
+ { 0100644, AUTOMERGEABLE_THEIRS_OID, 3, "automergeable.txt" },
+
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "modify-delete" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "modify-delete" },
+
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "directory_file-one" },
+ { 0100644, CONFLICTING_OURS_OID, 2, "directory_file-one" },
+ { 0100644, CONFLICTING_THEIRS_OID, 0, "directory_file-one/file" },
+
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "directory_file-two" },
+ { 0100644, CONFLICTING_OURS_OID, 0, "directory_file-two/file" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "directory_file-two" },
+ };
+
+ opts.checkout_strategy |= GIT_CHECKOUT_UPDATE_ONLY;
+
+ create_index(checkout_index_entries, 3);
+ git_index_write(g_index);
+
+ cl_git_pass(p_mkdir("merge-resolve/directory_file-two", 0777));
+ cl_git_rewritefile("merge-resolve/directory_file-two/file", CONFLICTING_OURS_FILE);
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ ensure_workdir_contents("automergeable.txt", AUTOMERGEABLE_MERGED_FILE);
+ ensure_workdir("directory_file-two/file", 0100644, CONFLICTING_OURS_OID);
+
+ cl_assert(!git_path_exists("merge-resolve/modify-delete"));
+ cl_assert(!git_path_exists("merge-resolve/test-one.txt"));
+ cl_assert(!git_path_exists("merge-resolve/test-one-side-one.txt"));
+ cl_assert(!git_path_exists("merge-resolve/test-one-side-two.txt"));
+ cl_assert(!git_path_exists("merge-resolve/test-one.txt~ours"));
+ cl_assert(!git_path_exists("merge-resolve/test-one.txt~theirs"));
+ cl_assert(!git_path_exists("merge-resolve/directory_file-one/file"));
+ cl_assert(!git_path_exists("merge-resolve/directory_file-one~ours"));
+ cl_assert(!git_path_exists("merge-resolve/directory_file-two~theirs"));
+}
+
+void test_checkout_conflict__path_filters(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ char *paths[] = { "conflicting-1.txt", "conflicting-3.txt" };
+ git_strarray patharray = {0};
+
+ struct checkout_index_entry checkout_index_entries[] = {
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "conflicting-1.txt" },
+ { 0100644, CONFLICTING_OURS_OID, 2, "conflicting-1.txt" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "conflicting-1.txt" },
+
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "conflicting-2.txt" },
+ { 0100644, CONFLICTING_OURS_OID, 2, "conflicting-2.txt" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "conflicting-2.txt" },
+
+ { 0100644, AUTOMERGEABLE_ANCESTOR_OID, 1, "conflicting-3.txt" },
+ { 0100644, AUTOMERGEABLE_OURS_OID, 2, "conflicting-3.txt" },
+ { 0100644, AUTOMERGEABLE_THEIRS_OID, 3, "conflicting-3.txt" },
+
+ { 0100644, AUTOMERGEABLE_ANCESTOR_OID, 1, "conflicting-4.txt" },
+ { 0100644, AUTOMERGEABLE_OURS_OID, 2, "conflicting-4.txt" },
+ { 0100644, AUTOMERGEABLE_THEIRS_OID, 3, "conflicting-4.txt" },
+ };
+
+ patharray.count = 2;
+ patharray.strings = paths;
+
+ opts.paths = patharray;
+
+ create_index(checkout_index_entries, 12);
+ git_index_write(g_index);
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ ensure_workdir_contents("conflicting-1.txt", CONFLICTING_DIFF3_FILE);
+ cl_assert(!git_path_exists("merge-resolve/conflicting-2.txt"));
+ ensure_workdir_contents("conflicting-3.txt", AUTOMERGEABLE_MERGED_FILE);
+ cl_assert(!git_path_exists("merge-resolve/conflicting-4.txt"));
+}
+
+static void collect_progress(
+ const char *path,
+ size_t completed_steps,
+ size_t total_steps,
+ void *payload)
+{
+ git_vector *paths = payload;
+
+ (void)completed_steps;
+ (void)total_steps;
+
+ if (path == NULL)
+ return;
+
+ git_vector_insert(paths, strdup(path));
+}
+
+void test_checkout_conflict__report_progress(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ git_vector paths = GIT_VECTOR_INIT;
+ char *path;
+ size_t i;
+
+ struct checkout_index_entry checkout_index_entries[] = {
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "conflicting-1.txt" },
+ { 0100644, CONFLICTING_OURS_OID, 2, "conflicting-1.txt" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "conflicting-1.txt" },
+
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "conflicting-2.txt" },
+ { 0100644, CONFLICTING_OURS_OID, 2, "conflicting-2.txt" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "conflicting-2.txt" },
+
+ { 0100644, AUTOMERGEABLE_ANCESTOR_OID, 1, "conflicting-3.txt" },
+ { 0100644, AUTOMERGEABLE_OURS_OID, 2, "conflicting-3.txt" },
+ { 0100644, AUTOMERGEABLE_THEIRS_OID, 3, "conflicting-3.txt" },
+
+ { 0100644, AUTOMERGEABLE_ANCESTOR_OID, 1, "conflicting-4.txt" },
+ { 0100644, AUTOMERGEABLE_OURS_OID, 2, "conflicting-4.txt" },
+ { 0100644, AUTOMERGEABLE_THEIRS_OID, 3, "conflicting-4.txt" },
+ };
+
+ opts.progress_cb = collect_progress;
+ opts.progress_payload = &paths;
+
+
+ create_index(checkout_index_entries, 12);
+ git_index_write(g_index);
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ cl_assert_equal_i(4, git_vector_length(&paths));
+ cl_assert_equal_s("conflicting-1.txt", git_vector_get(&paths, 0));
+ cl_assert_equal_s("conflicting-2.txt", git_vector_get(&paths, 1));
+ cl_assert_equal_s("conflicting-3.txt", git_vector_get(&paths, 2));
+ cl_assert_equal_s("conflicting-4.txt", git_vector_get(&paths, 3));
+
+ git_vector_foreach(&paths, i, path)
+ git__free(path);
+
+ git_vector_free(&paths);
+}
diff --git a/tests/checkout/crlf.c b/tests/checkout/crlf.c
new file mode 100644
index 000000000..9a4cbd313
--- /dev/null
+++ b/tests/checkout/crlf.c
@@ -0,0 +1,231 @@
+#include "clar_libgit2.h"
+#include "checkout_helpers.h"
+#include "../filter/crlf.h"
+
+#include "git2/checkout.h"
+#include "repository.h"
+#include "posix.h"
+
+static git_repository *g_repo;
+
+void test_checkout_crlf__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("crlf");
+}
+
+void test_checkout_crlf__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_checkout_crlf__detect_crlf_autocrlf_false(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", false);
+
+ git_checkout_head(g_repo, &opts);
+
+ check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW);
+ check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
+}
+
+void test_checkout_crlf__autocrlf_false_index_size_is_unfiltered_size(void)
+{
+ git_index *index;
+ const git_index_entry *entry;
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", false);
+
+ git_checkout_head(g_repo, &opts);
+
+ git_repository_index(&index, g_repo);
+
+ cl_assert((entry = git_index_get_bypath(index, "all-lf", 0)) != NULL);
+ cl_assert(entry->file_size == strlen(ALL_LF_TEXT_RAW));
+
+ cl_assert((entry = git_index_get_bypath(index, "all-crlf", 0)) != NULL);
+ cl_assert(entry->file_size == strlen(ALL_CRLF_TEXT_RAW));
+
+ git_index_free(index);
+}
+
+void test_checkout_crlf__detect_crlf_autocrlf_true(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ git_checkout_head(g_repo, &opts);
+
+ if (GIT_EOL_NATIVE == GIT_EOL_LF)
+ check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW);
+ else
+ check_file_contents("./crlf/all-lf", ALL_LF_TEXT_AS_CRLF);
+
+ check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
+}
+
+void test_checkout_crlf__more_lf_autocrlf_true(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ git_checkout_head(g_repo, &opts);
+
+ if (GIT_EOL_NATIVE == GIT_EOL_LF)
+ check_file_contents("./crlf/more-lf", MORE_LF_TEXT_RAW);
+ else
+ check_file_contents("./crlf/more-lf", MORE_LF_TEXT_AS_CRLF);
+}
+
+void test_checkout_crlf__more_crlf_autocrlf_true(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ git_checkout_head(g_repo, &opts);
+
+ if (GIT_EOL_NATIVE == GIT_EOL_LF)
+ check_file_contents("./crlf/more-crlf", MORE_CRLF_TEXT_RAW);
+ else
+ check_file_contents("./crlf/more-crlf", MORE_CRLF_TEXT_AS_CRLF);
+}
+
+void test_checkout_crlf__all_crlf_autocrlf_true(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ git_checkout_head(g_repo, &opts);
+
+ check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
+}
+
+void test_checkout_crlf__autocrlf_true_index_size_is_filtered_size(void)
+{
+ git_index *index;
+ const git_index_entry *entry;
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ git_checkout_head(g_repo, &opts);
+
+ git_repository_index(&index, g_repo);
+
+ cl_assert((entry = git_index_get_bypath(index, "all-lf", 0)) != NULL);
+
+ if (GIT_EOL_NATIVE == GIT_EOL_LF)
+ cl_assert_equal_sz(strlen(ALL_LF_TEXT_RAW), entry->file_size);
+ else
+ cl_assert_equal_sz(strlen(ALL_LF_TEXT_AS_CRLF), entry->file_size);
+
+ cl_assert((entry = git_index_get_bypath(index, "all-crlf", 0)) != NULL);
+ cl_assert_equal_sz(strlen(ALL_CRLF_TEXT_RAW), entry->file_size);
+
+ git_index_free(index);
+}
+
+void test_checkout_crlf__with_ident(void)
+{
+ git_index *index;
+ git_blob *blob;
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+
+ cl_git_mkfile("crlf/.gitattributes",
+ "*.txt text\n*.bin binary\n"
+ "*.crlf text eol=crlf\n"
+ "*.lf text eol=lf\n"
+ "*.ident text ident\n"
+ "*.identcrlf ident text eol=crlf\n"
+ "*.identlf ident text eol=lf\n");
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ /* add files with $Id$ */
+
+ cl_git_mkfile("crlf/lf.ident", ALL_LF_TEXT_RAW "\n$Id: initial content$\n");
+ cl_git_mkfile("crlf/crlf.ident", ALL_CRLF_TEXT_RAW "\r\n$Id$\r\n\r\n");
+ cl_git_mkfile("crlf/more1.identlf", "$Id$\n" MORE_LF_TEXT_RAW);
+ cl_git_mkfile("crlf/more2.identcrlf", "\r\n$Id: $\r\n" MORE_CRLF_TEXT_RAW);
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_add_bypath(index, "lf.ident"));
+ cl_git_pass(git_index_add_bypath(index, "crlf.ident"));
+ cl_git_pass(git_index_add_bypath(index, "more1.identlf"));
+ cl_git_pass(git_index_add_bypath(index, "more2.identcrlf"));
+ cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "Some ident files\n");
+
+ git_checkout_head(g_repo, &opts);
+
+ /* check that blobs have $Id$ */
+
+ cl_git_pass(git_blob_lookup(&blob, g_repo,
+ & git_index_get_bypath(index, "lf.ident", 0)->oid));
+ cl_assert_equal_s(
+ ALL_LF_TEXT_RAW "\n$Id$\n", git_blob_rawcontent(blob));
+ git_blob_free(blob);
+
+ cl_git_pass(git_blob_lookup(&blob, g_repo,
+ & git_index_get_bypath(index, "more2.identcrlf", 0)->oid));
+ cl_assert_equal_s(
+ "\n$Id$\n" MORE_CRLF_TEXT_AS_LF, git_blob_rawcontent(blob));
+ git_blob_free(blob);
+
+ /* check that filesystem is initially untouched - matching core Git */
+
+ cl_assert_equal_file(
+ ALL_LF_TEXT_RAW "\n$Id: initial content$\n", 0, "crlf/lf.ident");
+
+ /* check that forced checkout rewrites correctly */
+
+ p_unlink("crlf/lf.ident");
+ p_unlink("crlf/crlf.ident");
+ p_unlink("crlf/more1.identlf");
+ p_unlink("crlf/more2.identcrlf");
+
+ git_checkout_head(g_repo, &opts);
+
+ if (GIT_EOL_NATIVE == GIT_EOL_LF) {
+ cl_assert_equal_file(
+ ALL_LF_TEXT_RAW
+ "\n$Id: fcf6d4d9c212dc66563b1171b1cd99953c756467$\n",
+ 0, "crlf/lf.ident");
+ cl_assert_equal_file(
+ ALL_CRLF_TEXT_AS_LF
+ "\n$Id: f2c66ad9b2b5a734d9bf00d5000cc10a62b8a857$\n\n",
+ 0, "crlf/crlf.ident");
+ } else {
+ cl_assert_equal_file(
+ ALL_LF_TEXT_AS_CRLF
+ "\r\n$Id: fcf6d4d9c212dc66563b1171b1cd99953c756467$\r\n",
+ 0, "crlf/lf.ident");
+ cl_assert_equal_file(
+ ALL_CRLF_TEXT_RAW
+ "\r\n$Id: f2c66ad9b2b5a734d9bf00d5000cc10a62b8a857$\r\n\r\n",
+ 0, "crlf/crlf.ident");
+ }
+
+ cl_assert_equal_file(
+ "$Id: f7830382dac1f1583422be5530fdfbd26289431b$\n"
+ MORE_LF_TEXT_AS_LF, 0, "crlf/more1.identlf");
+
+ cl_assert_equal_file(
+ "\r\n$Id: 74677a68413012ce8d7e7cfc3f12603df3a3eac4$\r\n"
+ MORE_CRLF_TEXT_AS_CRLF, 0, "crlf/more2.identcrlf");
+
+ git_index_free(index);
+}
diff --git a/tests/checkout/head.c b/tests/checkout/head.c
new file mode 100644
index 000000000..a7a7e9071
--- /dev/null
+++ b/tests/checkout/head.c
@@ -0,0 +1,62 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+#include "repo/repo_helpers.h"
+#include "path.h"
+#include "fileops.h"
+
+static git_repository *g_repo;
+
+void test_checkout_head__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_checkout_head__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_checkout_head__unborn_head_returns_GIT_EUNBORNBRANCH(void)
+{
+ make_head_unborn(g_repo, NON_EXISTING_HEAD);
+
+ cl_assert_equal_i(GIT_EUNBORNBRANCH, git_checkout_head(g_repo, NULL));
+}
+
+void test_checkout_head__with_index_only_tree(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ git_index *index;
+
+ /* let's start by getting things into a known state */
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ /* now let's stage some new stuff including a new directory */
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ p_mkdir("testrepo/newdir", 0777);
+ cl_git_mkfile("testrepo/newdir/newfile.txt", "new file\n");
+
+ cl_git_pass(git_index_add_bypath(index, "newdir/newfile.txt"));
+ cl_git_pass(git_index_write(index));
+
+ cl_assert(git_path_isfile("testrepo/newdir/newfile.txt"));
+ cl_assert(git_index_get_bypath(index, "newdir/newfile.txt", 0) != NULL);
+
+ git_index_free(index);
+
+ /* okay, so now we have staged this new file; let's see if we can remove */
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED;
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_assert(!git_path_isfile("testrepo/newdir/newfile.txt"));
+ cl_assert(git_index_get_bypath(index, "newdir/newfile.txt", 0) == NULL);
+
+ git_index_free(index);
+}
diff --git a/tests/checkout/index.c b/tests/checkout/index.c
new file mode 100644
index 000000000..48d6d79f9
--- /dev/null
+++ b/tests/checkout/index.c
@@ -0,0 +1,620 @@
+#include "clar_libgit2.h"
+#include "checkout_helpers.h"
+
+#include "git2/checkout.h"
+#include "fileops.h"
+#include "repository.h"
+
+static git_repository *g_repo;
+
+void test_checkout_index__initialize(void)
+{
+ git_tree *tree;
+
+ g_repo = cl_git_sandbox_init("testrepo");
+
+ cl_git_pass(git_repository_head_tree(&tree, g_repo));
+
+ reset_index_to_treeish((git_object *)tree);
+ git_tree_free(tree);
+
+ cl_git_rewritefile(
+ "./testrepo/.gitattributes",
+ "* text eol=lf\n");
+}
+
+void test_checkout_index__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+
+ /* try to remove alternative dir */
+ if (git_path_isdir("alternative"))
+ git_futils_rmdir_r("alternative", NULL, GIT_RMDIR_REMOVE_FILES);
+}
+
+void test_checkout_index__cannot_checkout_a_bare_repository(void)
+{
+ test_checkout_index__cleanup();
+
+ g_repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_git_fail(git_checkout_index(g_repo, NULL, NULL));
+}
+
+void test_checkout_index__can_create_missing_files(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ cl_assert_equal_i(false, git_path_isfile("./testrepo/README"));
+ cl_assert_equal_i(false, git_path_isfile("./testrepo/branch_file.txt"));
+ cl_assert_equal_i(false, git_path_isfile("./testrepo/new.txt"));
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ check_file_contents("./testrepo/README", "hey there\n");
+ check_file_contents("./testrepo/branch_file.txt", "hi\nbye!\n");
+ check_file_contents("./testrepo/new.txt", "my new file\n");
+}
+
+void test_checkout_index__can_remove_untracked_files(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ git_futils_mkdir("./testrepo/dir/subdir/subsubdir", NULL, 0755, GIT_MKDIR_PATH);
+ cl_git_mkfile("./testrepo/dir/one", "one\n");
+ cl_git_mkfile("./testrepo/dir/subdir/two", "two\n");
+
+ cl_assert_equal_i(true, git_path_isdir("./testrepo/dir/subdir/subsubdir"));
+
+ opts.checkout_strategy =
+ GIT_CHECKOUT_SAFE_CREATE | GIT_CHECKOUT_REMOVE_UNTRACKED;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ cl_assert_equal_i(false, git_path_isdir("./testrepo/dir"));
+}
+
+void test_checkout_index__honor_the_specified_pathspecs(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ char *entries[] = { "*.txt" };
+
+ opts.paths.strings = entries;
+ opts.paths.count = 1;
+
+ cl_assert_equal_i(false, git_path_isfile("./testrepo/README"));
+ cl_assert_equal_i(false, git_path_isfile("./testrepo/branch_file.txt"));
+ cl_assert_equal_i(false, git_path_isfile("./testrepo/new.txt"));
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ cl_assert_equal_i(false, git_path_isfile("./testrepo/README"));
+ check_file_contents("./testrepo/branch_file.txt", "hi\nbye!\n");
+ check_file_contents("./testrepo/new.txt", "my new file\n");
+}
+
+void test_checkout_index__honor_the_gitattributes_directives(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ const char *attributes =
+ "branch_file.txt text eol=crlf\n"
+ "new.txt text eol=lf\n";
+
+ cl_git_mkfile("./testrepo/.gitattributes", attributes);
+ cl_repo_set_bool(g_repo, "core.autocrlf", false);
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ check_file_contents("./testrepo/README", "hey there\n");
+ check_file_contents("./testrepo/new.txt", "my new file\n");
+ check_file_contents("./testrepo/branch_file.txt", "hi\r\nbye!\r\n");
+}
+
+void test_checkout_index__honor_coreautocrlf_setting_set_to_true(void)
+{
+#ifdef GIT_WIN32
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ const char *expected_readme_text = "hey there\r\n";
+
+ cl_git_pass(p_unlink("./testrepo/.gitattributes"));
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ check_file_contents("./testrepo/README", expected_readme_text);
+#endif
+}
+
+void test_checkout_index__honor_coresymlinks_setting_set_to_true(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ cl_repo_set_bool(g_repo, "core.symlinks", true);
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+#ifdef GIT_WIN32
+ check_file_contents("./testrepo/link_to_new.txt", "new.txt");
+#else
+ {
+ char link_data[1024];
+ size_t link_size = 1024;
+
+ link_size = p_readlink("./testrepo/link_to_new.txt", link_data, link_size);
+ link_data[link_size] = '\0';
+ cl_assert_equal_i(link_size, strlen("new.txt"));
+ cl_assert_equal_s(link_data, "new.txt");
+ check_file_contents("./testrepo/link_to_new.txt", "my new file\n");
+ }
+#endif
+}
+
+void test_checkout_index__honor_coresymlinks_setting_set_to_false(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ cl_repo_set_bool(g_repo, "core.symlinks", false);
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ check_file_contents("./testrepo/link_to_new.txt", "new.txt");
+}
+
+void test_checkout_index__donot_overwrite_modified_file_by_default(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ cl_git_mkfile("./testrepo/new.txt", "This isn't what's stored!");
+
+ /* set this up to not return an error code on conflicts, but it
+ * still will not have permission to overwrite anything...
+ */
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_ALLOW_CONFLICTS;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ check_file_contents("./testrepo/new.txt", "This isn't what's stored!");
+}
+
+void test_checkout_index__can_overwrite_modified_file(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ cl_git_mkfile("./testrepo/new.txt", "This isn't what's stored!");
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ check_file_contents("./testrepo/new.txt", "my new file\n");
+}
+
+void test_checkout_index__options_disable_filters(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ cl_git_mkfile("./testrepo/.gitattributes", "*.txt text eol=crlf\n");
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+ opts.disable_filters = false;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ check_file_contents("./testrepo/new.txt", "my new file\r\n");
+
+ p_unlink("./testrepo/new.txt");
+
+ opts.disable_filters = true;
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ check_file_contents("./testrepo/new.txt", "my new file\n");
+}
+
+void test_checkout_index__options_dir_modes(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ struct stat st;
+ git_oid oid;
+ git_commit *commit;
+ mode_t um;
+
+ if (!cl_is_chmod_supported())
+ return;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &oid));
+
+ reset_index_to_treeish((git_object *)commit);
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+ opts.dir_mode = 0701;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ /* umask will influence actual directory creation mode */
+ (void)p_umask(um = p_umask(022));
+
+ cl_git_pass(p_stat("./testrepo/a", &st));
+ cl_assert_equal_i_fmt(st.st_mode, (GIT_FILEMODE_TREE | 0701) & ~um, "%07o");
+
+ /* File-mode test, since we're on the 'dir' branch */
+ cl_git_pass(p_stat("./testrepo/a/b.txt", &st));
+ cl_assert_equal_i_fmt(st.st_mode, GIT_FILEMODE_BLOB_EXECUTABLE, "%07o");
+
+ git_commit_free(commit);
+}
+
+void test_checkout_index__options_override_file_modes(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ struct stat st;
+
+ if (!cl_is_chmod_supported())
+ return;
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+ opts.file_mode = 0700;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ cl_git_pass(p_stat("./testrepo/new.txt", &st));
+ cl_assert_equal_i_fmt(st.st_mode & GIT_MODE_PERMS_MASK, 0700, "%07o");
+}
+
+void test_checkout_index__options_open_flags(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ cl_git_mkfile("./testrepo/new.txt", "hi\n");
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+ opts.file_open_flags = O_CREAT | O_RDWR | O_APPEND;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ check_file_contents("./testrepo/new.txt", "hi\nmy new file\n");
+}
+
+struct notify_data {
+ const char *file;
+ const char *sha;
+};
+
+static int test_checkout_notify_cb(
+ git_checkout_notify_t why,
+ const char *path,
+ const git_diff_file *baseline,
+ const git_diff_file *target,
+ const git_diff_file *workdir,
+ void *payload)
+{
+ struct notify_data *expectations = (struct notify_data *)payload;
+
+ GIT_UNUSED(workdir);
+
+ cl_assert_equal_i(GIT_CHECKOUT_NOTIFY_CONFLICT, why);
+ cl_assert_equal_s(expectations->file, path);
+ cl_assert_equal_i(0, git_oid_streq(&baseline->oid, expectations->sha));
+ cl_assert_equal_i(0, git_oid_streq(&target->oid, expectations->sha));
+
+ return 0;
+}
+
+void test_checkout_index__can_notify_of_skipped_files(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ struct notify_data data;
+
+ cl_git_mkfile("./testrepo/new.txt", "This isn't what's stored!");
+
+ /*
+ * $ git ls-tree HEAD
+ * 100644 blob a8233120f6ad708f843d861ce2b7228ec4e3dec6 README
+ * 100644 blob 3697d64be941a53d4ae8f6a271e4e3fa56b022cc branch_file.txt
+ * 100644 blob a71586c1dfe8a71c6cbf6c129f404c5642ff31bd new.txt
+ */
+ data.file = "new.txt";
+ data.sha = "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd";
+
+ opts.checkout_strategy =
+ GIT_CHECKOUT_SAFE_CREATE | GIT_CHECKOUT_ALLOW_CONFLICTS;
+ opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT;
+ opts.notify_cb = test_checkout_notify_cb;
+ opts.notify_payload = &data;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+}
+
+static int dont_notify_cb(
+ git_checkout_notify_t why,
+ const char *path,
+ const git_diff_file *baseline,
+ const git_diff_file *target,
+ const git_diff_file *workdir,
+ void *payload)
+{
+ GIT_UNUSED(why);
+ GIT_UNUSED(path);
+ GIT_UNUSED(baseline);
+ GIT_UNUSED(target);
+ GIT_UNUSED(workdir);
+ GIT_UNUSED(payload);
+
+ cl_assert(false);
+
+ return 0;
+}
+
+void test_checkout_index__wont_notify_of_expected_line_ending_changes(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ cl_git_pass(p_unlink("./testrepo/.gitattributes"));
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ cl_git_mkfile("./testrepo/new.txt", "my new file\r\n");
+
+ opts.checkout_strategy =
+ GIT_CHECKOUT_SAFE_CREATE | GIT_CHECKOUT_ALLOW_CONFLICTS;
+ opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT;
+ opts.notify_cb = dont_notify_cb;
+ opts.notify_payload = NULL;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+}
+
+static void checkout_progress_counter(
+ const char *path, size_t cur, size_t tot, void *payload)
+{
+ GIT_UNUSED(path); GIT_UNUSED(cur); GIT_UNUSED(tot);
+ (*(int *)payload)++;
+}
+
+void test_checkout_index__calls_progress_callback(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ int calls = 0;
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+ opts.progress_cb = checkout_progress_counter;
+ opts.progress_payload = &calls;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+ cl_assert(calls > 0);
+}
+
+void test_checkout_index__can_overcome_name_clashes(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ git_index *index;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ git_index_clear(index);
+
+ cl_git_mkfile("./testrepo/path0", "content\r\n");
+ cl_git_pass(p_mkdir("./testrepo/path1", 0777));
+ cl_git_mkfile("./testrepo/path1/file1", "content\r\n");
+
+ cl_git_pass(git_index_add_bypath(index, "path0"));
+ cl_git_pass(git_index_add_bypath(index, "path1/file1"));
+
+ cl_git_pass(p_unlink("./testrepo/path0"));
+ cl_git_pass(git_futils_rmdir_r(
+ "./testrepo/path1", NULL, GIT_RMDIR_REMOVE_FILES));
+
+ cl_git_mkfile("./testrepo/path1", "content\r\n");
+ cl_git_pass(p_mkdir("./testrepo/path0", 0777));
+ cl_git_mkfile("./testrepo/path0/file0", "content\r\n");
+
+ cl_assert(git_path_isfile("./testrepo/path1"));
+ cl_assert(git_path_isfile("./testrepo/path0/file0"));
+
+ opts.checkout_strategy =
+ GIT_CHECKOUT_SAFE_CREATE | GIT_CHECKOUT_ALLOW_CONFLICTS;
+ cl_git_pass(git_checkout_index(g_repo, index, &opts));
+
+ cl_assert(git_path_isfile("./testrepo/path1"));
+ cl_assert(git_path_isfile("./testrepo/path0/file0"));
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+ cl_git_pass(git_checkout_index(g_repo, index, &opts));
+
+ cl_assert(git_path_isfile("./testrepo/path0"));
+ cl_assert(git_path_isfile("./testrepo/path1/file1"));
+
+ git_index_free(index);
+}
+
+void test_checkout_index__validates_struct_version(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ const git_error *err;
+
+ opts.version = 1024;
+ cl_git_fail(git_checkout_index(g_repo, NULL, &opts));
+
+ err = giterr_last();
+ cl_assert_equal_i(err->klass, GITERR_INVALID);
+
+ opts.version = 0;
+ giterr_clear();
+ cl_git_fail(git_checkout_index(g_repo, NULL, &opts));
+
+ err = giterr_last();
+ cl_assert_equal_i(err->klass, GITERR_INVALID);
+}
+
+void test_checkout_index__can_update_prefixed_files(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ cl_assert_equal_i(false, git_path_isfile("./testrepo/README"));
+ cl_assert_equal_i(false, git_path_isfile("./testrepo/branch_file.txt"));
+ cl_assert_equal_i(false, git_path_isfile("./testrepo/new.txt"));
+
+ cl_git_mkfile("./testrepo/READ", "content\n");
+ cl_git_mkfile("./testrepo/README.after", "content\n");
+ cl_git_pass(p_mkdir("./testrepo/branch_file", 0777));
+ cl_git_pass(p_mkdir("./testrepo/branch_file/contained_dir", 0777));
+ cl_git_mkfile("./testrepo/branch_file/contained_file", "content\n");
+ cl_git_pass(p_mkdir("./testrepo/branch_file.txt.after", 0777));
+
+ opts.checkout_strategy =
+ GIT_CHECKOUT_SAFE_CREATE | GIT_CHECKOUT_REMOVE_UNTRACKED;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ /* remove untracked will remove the .gitattributes file before the blobs
+ * were created, so they will have had crlf filtering applied on Windows
+ */
+ check_file_contents_nocr("./testrepo/README", "hey there\n");
+ check_file_contents_nocr("./testrepo/branch_file.txt", "hi\nbye!\n");
+ check_file_contents_nocr("./testrepo/new.txt", "my new file\n");
+
+ cl_assert(!git_path_exists("testrepo/READ"));
+ cl_assert(!git_path_exists("testrepo/README.after"));
+ cl_assert(!git_path_exists("testrepo/branch_file"));
+ cl_assert(!git_path_exists("testrepo/branch_file.txt.after"));
+}
+
+void test_checkout_index__can_checkout_a_newly_initialized_repository(void)
+{
+ test_checkout_index__cleanup();
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+ cl_git_remove_placeholders(git_repository_path(g_repo), "dummy-marker.txt");
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, NULL));
+}
+
+void test_checkout_index__issue_1397(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ test_checkout_index__cleanup();
+
+ g_repo = cl_git_sandbox_init("issue_1397");
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ check_file_contents("./issue_1397/crlf_file.txt", "first line\r\nsecond line\r\nboth with crlf");
+}
+
+void test_checkout_index__target_directory(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ checkout_counts cts;
+ memset(&cts, 0, sizeof(cts));
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+ opts.target_directory = "alternative";
+ cl_assert(!git_path_isdir("alternative"));
+
+ opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
+ opts.notify_cb = checkout_count_callback;
+ opts.notify_payload = &cts;
+
+ /* create some files that *would* conflict if we were using the wd */
+ cl_git_mkfile("testrepo/README", "I'm in the way!\n");
+ cl_git_mkfile("testrepo/new.txt", "my new file\n");
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ cl_assert_equal_i(0, cts.n_untracked);
+ cl_assert_equal_i(0, cts.n_ignored);
+ cl_assert_equal_i(4, cts.n_updates);
+
+ check_file_contents("./alternative/README", "hey there\n");
+ check_file_contents("./alternative/branch_file.txt", "hi\nbye!\n");
+ check_file_contents("./alternative/new.txt", "my new file\n");
+
+ cl_git_pass(git_futils_rmdir_r(
+ "alternative", NULL, GIT_RMDIR_REMOVE_FILES));
+}
+
+void test_checkout_index__target_directory_from_bare(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ git_index *index;
+ git_object *head = NULL;
+ checkout_counts cts;
+ memset(&cts, 0, sizeof(cts));
+
+ test_checkout_index__cleanup();
+
+ g_repo = cl_git_sandbox_init("testrepo.git");
+ cl_assert(git_repository_is_bare(g_repo));
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_revparse_single(&head, g_repo, "HEAD^{tree}"));
+ cl_git_pass(git_index_read_tree(index, (const git_tree *)head));
+ cl_git_pass(git_index_write(index));
+ git_index_free(index);
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+
+ opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
+ opts.notify_cb = checkout_count_callback;
+ opts.notify_payload = &cts;
+
+ /* fail to checkout a bare repo */
+ cl_git_fail(git_checkout_index(g_repo, NULL, &opts));
+
+ opts.target_directory = "alternative";
+ cl_assert(!git_path_isdir("alternative"));
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ cl_assert_equal_i(0, cts.n_untracked);
+ cl_assert_equal_i(0, cts.n_ignored);
+ cl_assert_equal_i(3, cts.n_updates);
+
+ /* files will have been filtered if needed, so strip CR */
+ check_file_contents_nocr("./alternative/README", "hey there\n");
+ check_file_contents_nocr("./alternative/branch_file.txt", "hi\nbye!\n");
+ check_file_contents_nocr("./alternative/new.txt", "my new file\n");
+
+ cl_git_pass(git_futils_rmdir_r(
+ "alternative", NULL, GIT_RMDIR_REMOVE_FILES));
+
+ git_object_free(head);
+}
+
+void test_checkout_index__can_get_repo_from_index(void)
+{
+ git_index *index;
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ cl_assert_equal_i(false, git_path_isfile("./testrepo/README"));
+ cl_assert_equal_i(false, git_path_isfile("./testrepo/branch_file.txt"));
+ cl_assert_equal_i(false, git_path_isfile("./testrepo/new.txt"));
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_pass(git_checkout_index(NULL, index, &opts));
+
+ check_file_contents("./testrepo/README", "hey there\n");
+ check_file_contents("./testrepo/branch_file.txt", "hi\nbye!\n");
+ check_file_contents("./testrepo/new.txt", "my new file\n");
+
+ git_index_free(index);
+}
diff --git a/tests/checkout/tree.c b/tests/checkout/tree.c
new file mode 100644
index 000000000..66b01bc7f
--- /dev/null
+++ b/tests/checkout/tree.c
@@ -0,0 +1,742 @@
+#include "clar_libgit2.h"
+#include "checkout_helpers.h"
+
+#include "git2/checkout.h"
+#include "repository.h"
+#include "buffer.h"
+#include "fileops.h"
+
+static git_repository *g_repo;
+static git_checkout_opts g_opts;
+static git_object *g_object;
+
+void test_checkout_tree__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+
+ GIT_INIT_STRUCTURE(&g_opts, GIT_CHECKOUT_OPTS_VERSION);
+ g_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+}
+
+void test_checkout_tree__cleanup(void)
+{
+ git_object_free(g_object);
+ g_object = NULL;
+
+ cl_git_sandbox_cleanup();
+
+ if (git_path_isdir("alternative"))
+ git_futils_rmdir_r("alternative", NULL, GIT_RMDIR_REMOVE_FILES);
+}
+
+void test_checkout_tree__cannot_checkout_a_non_treeish(void)
+{
+ /* blob */
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd"));
+ cl_git_fail(git_checkout_tree(g_repo, g_object, NULL));
+}
+
+void test_checkout_tree__can_checkout_a_subdirectory_from_a_commit(void)
+{
+ char *entries[] = { "ab/de/" };
+
+ g_opts.paths.strings = entries;
+ g_opts.paths.count = 1;
+
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "subtrees"));
+
+ cl_assert_equal_i(false, git_path_isdir("./testrepo/ab/"));
+
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+
+ cl_assert_equal_i(true, git_path_isfile("./testrepo/ab/de/2.txt"));
+ cl_assert_equal_i(true, git_path_isfile("./testrepo/ab/de/fgh/1.txt"));
+}
+
+void test_checkout_tree__can_checkout_and_remove_directory(void)
+{
+ cl_assert_equal_i(false, git_path_isdir("./testrepo/ab/"));
+
+ /* Checkout brach "subtrees" and update HEAD, so that HEAD matches the
+ * current working tree
+ */
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "subtrees"));
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees"));
+
+ cl_assert_equal_i(true, git_path_isdir("./testrepo/ab/"));
+ cl_assert_equal_i(true, git_path_isfile("./testrepo/ab/de/2.txt"));
+ cl_assert_equal_i(true, git_path_isfile("./testrepo/ab/de/fgh/1.txt"));
+
+ git_object_free(g_object);
+ g_object = NULL;
+
+ /* Checkout brach "master" and update HEAD, so that HEAD matches the
+ * current working tree
+ */
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "master"));
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master"));
+
+ /* This directory should no longer exist */
+ cl_assert_equal_i(false, git_path_isdir("./testrepo/ab/"));
+}
+
+void test_checkout_tree__can_checkout_a_subdirectory_from_a_subtree(void)
+{
+ char *entries[] = { "de/" };
+
+ g_opts.paths.strings = entries;
+ g_opts.paths.count = 1;
+
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "subtrees:ab"));
+
+ cl_assert_equal_i(false, git_path_isdir("./testrepo/de/"));
+
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+
+ cl_assert_equal_i(true, git_path_isfile("./testrepo/de/2.txt"));
+ cl_assert_equal_i(true, git_path_isfile("./testrepo/de/fgh/1.txt"));
+}
+
+static void progress(const char *path, size_t cur, size_t tot, void *payload)
+{
+ bool *was_called = (bool*)payload;
+ GIT_UNUSED(path); GIT_UNUSED(cur); GIT_UNUSED(tot);
+ *was_called = true;
+}
+
+void test_checkout_tree__calls_progress_callback(void)
+{
+ bool was_called = 0;
+
+ g_opts.progress_cb = progress;
+ g_opts.progress_payload = &was_called;
+
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "master"));
+
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+
+ cl_assert_equal_i(was_called, true);
+}
+
+void test_checkout_tree__doesnt_write_unrequested_files_to_worktree(void)
+{
+ git_oid master_oid;
+ git_oid chomped_oid;
+ git_commit* p_master_commit;
+ git_commit* p_chomped_commit;
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ git_oid_fromstr(&master_oid, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ git_oid_fromstr(&chomped_oid, "e90810b8df3e80c413d903f631643c716887138d");
+ cl_git_pass(git_commit_lookup(&p_master_commit, g_repo, &master_oid));
+ cl_git_pass(git_commit_lookup(&p_chomped_commit, g_repo, &chomped_oid));
+
+ /* GIT_CHECKOUT_NONE should not add any file to the working tree from the
+ * index as it is supposed to be a dry run.
+ */
+ opts.checkout_strategy = GIT_CHECKOUT_NONE;
+ git_checkout_tree(g_repo, (git_object*)p_chomped_commit, &opts);
+ cl_assert_equal_i(false, git_path_isfile("testrepo/readme.txt"));
+
+ git_commit_free(p_master_commit);
+ git_commit_free(p_chomped_commit);
+}
+
+void test_checkout_tree__can_switch_branches(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ git_oid oid;
+ git_object *obj = NULL;
+
+ assert_on_branch(g_repo, "master");
+
+ /* do first checkout with FORCE because we don't know if testrepo
+ * base data is clean for a checkout or not
+ */
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+
+ cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir"));
+
+ cl_assert(git_path_isfile("testrepo/README"));
+ cl_assert(git_path_isfile("testrepo/branch_file.txt"));
+ cl_assert(git_path_isfile("testrepo/new.txt"));
+ cl_assert(git_path_isfile("testrepo/a/b.txt"));
+
+ cl_assert(!git_path_isdir("testrepo/ab"));
+
+ assert_on_branch(g_repo, "dir");
+
+ git_object_free(obj);
+
+ /* do second checkout safe because we should be clean after first */
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/subtrees"));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+
+ cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees"));
+
+ cl_assert(git_path_isfile("testrepo/README"));
+ cl_assert(git_path_isfile("testrepo/branch_file.txt"));
+ cl_assert(git_path_isfile("testrepo/new.txt"));
+ cl_assert(git_path_isfile("testrepo/ab/4.txt"));
+ cl_assert(git_path_isfile("testrepo/ab/c/3.txt"));
+ cl_assert(git_path_isfile("testrepo/ab/de/2.txt"));
+ cl_assert(git_path_isfile("testrepo/ab/de/fgh/1.txt"));
+
+ cl_assert(!git_path_isdir("testrepo/a"));
+
+ assert_on_branch(g_repo, "subtrees");
+
+ git_object_free(obj);
+}
+
+void test_checkout_tree__can_remove_untracked(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_REMOVE_UNTRACKED;
+
+ cl_git_mkfile("testrepo/untracked_file", "as you wish");
+ cl_assert(git_path_isfile("testrepo/untracked_file"));
+
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ cl_assert(!git_path_isfile("testrepo/untracked_file"));
+}
+
+void test_checkout_tree__can_remove_ignored(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ int ignored = 0;
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_REMOVE_IGNORED;
+
+ cl_git_mkfile("testrepo/ignored_file", "as you wish");
+
+ cl_git_pass(git_ignore_add_rule(g_repo, "ignored_file\n"));
+
+ cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "ignored_file"));
+ cl_assert_equal_i(1, ignored);
+
+ cl_assert(git_path_isfile("testrepo/ignored_file"));
+
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ cl_assert(!git_path_isfile("testrepo/ignored_file"));
+}
+
+void test_checkout_tree__can_update_only(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ git_oid oid;
+ git_object *obj = NULL;
+
+ /* first let's get things into a known state - by checkout out the HEAD */
+
+ assert_on_branch(g_repo, "master");
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ cl_assert(!git_path_isdir("testrepo/a"));
+
+ check_file_contents_nocr("testrepo/branch_file.txt", "hi\nbye!\n");
+
+ /* now checkout branch but with update only */
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+
+ cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir"));
+
+ assert_on_branch(g_repo, "dir");
+
+ /* this normally would have been created (which was tested separately in
+ * the test_checkout_tree__can_switch_branches test), but with
+ * UPDATE_ONLY it will not have been created.
+ */
+ cl_assert(!git_path_isdir("testrepo/a"));
+
+ /* but this file still should have been updated */
+ check_file_contents_nocr("testrepo/branch_file.txt", "hi\n");
+
+ git_object_free(obj);
+}
+
+void test_checkout_tree__can_checkout_with_pattern(void)
+{
+ char *entries[] = { "[l-z]*.txt" };
+
+ /* reset to beginning of history (i.e. just a README file) */
+
+ g_opts.checkout_strategy =
+ GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED;
+
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "8496071c1b46c854b31185ea97743be6a8774479"));
+
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+ cl_git_pass(
+ git_repository_set_head_detached(g_repo, git_object_id(g_object)));
+
+ git_object_free(g_object);
+ g_object = NULL;
+
+ cl_assert(git_path_exists("testrepo/README"));
+ cl_assert(!git_path_exists("testrepo/branch_file.txt"));
+ cl_assert(!git_path_exists("testrepo/link_to_new.txt"));
+ cl_assert(!git_path_exists("testrepo/new.txt"));
+
+ /* now to a narrow patterned checkout */
+
+ g_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+ g_opts.paths.strings = entries;
+ g_opts.paths.count = 1;
+
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "refs/heads/master"));
+
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+
+ cl_assert(git_path_exists("testrepo/README"));
+ cl_assert(!git_path_exists("testrepo/branch_file.txt"));
+ cl_assert(git_path_exists("testrepo/link_to_new.txt"));
+ cl_assert(git_path_exists("testrepo/new.txt"));
+}
+
+void test_checkout_tree__can_disable_pattern_match(void)
+{
+ char *entries[] = { "b*.txt" };
+
+ /* reset to beginning of history (i.e. just a README file) */
+
+ g_opts.checkout_strategy =
+ GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED;
+
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "8496071c1b46c854b31185ea97743be6a8774479"));
+
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+ cl_git_pass(
+ git_repository_set_head_detached(g_repo, git_object_id(g_object)));
+
+ git_object_free(g_object);
+ g_object = NULL;
+
+ cl_assert(!git_path_isfile("testrepo/branch_file.txt"));
+
+ /* now to a narrow patterned checkout, but disable pattern */
+
+ g_opts.checkout_strategy =
+ GIT_CHECKOUT_SAFE_CREATE | GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH;
+ g_opts.paths.strings = entries;
+ g_opts.paths.count = 1;
+
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "refs/heads/master"));
+
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+
+ cl_assert(!git_path_isfile("testrepo/branch_file.txt"));
+
+ /* let's try that again, but allow the pattern match */
+
+ g_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+
+ cl_assert(git_path_isfile("testrepo/branch_file.txt"));
+}
+
+void assert_conflict(
+ const char *entry_path,
+ const char *new_content,
+ const char *parent_sha,
+ const char *commit_sha)
+{
+ git_index *index;
+ git_object *hack_tree;
+ git_reference *branch, *head;
+ git_buf file_path = GIT_BUF_INIT;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ /* Create a branch pointing at the parent */
+ cl_git_pass(git_revparse_single(&g_object, g_repo, parent_sha));
+ cl_git_pass(git_branch_create(&branch, g_repo,
+ "potential_conflict", (git_commit *)g_object, 0));
+
+ /* Make HEAD point to this branch */
+ cl_git_pass(git_reference_symbolic_create(
+ &head, g_repo, "HEAD", git_reference_name(branch), 1));
+ git_reference_free(head);
+ git_reference_free(branch);
+
+ /* Checkout the parent */
+ g_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+
+ /* Hack-ishy workaound to ensure *all* the index entries
+ * match the content of the tree
+ */
+ cl_git_pass(git_object_peel(&hack_tree, g_object, GIT_OBJ_TREE));
+ cl_git_pass(git_index_read_tree(index, (git_tree *)hack_tree));
+ git_object_free(hack_tree);
+ git_object_free(g_object);
+ g_object = NULL;
+
+ /* Create a conflicting file */
+ cl_git_pass(git_buf_joinpath(&file_path, "./testrepo", entry_path));
+ cl_git_mkfile(git_buf_cstr(&file_path), new_content);
+ git_buf_free(&file_path);
+
+ /* Trying to checkout the original commit */
+ cl_git_pass(git_revparse_single(&g_object, g_repo, commit_sha));
+
+ g_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+ cl_assert_equal_i(
+ GIT_EMERGECONFLICT, git_checkout_tree(g_repo, g_object, &g_opts));
+
+ /* Stage the conflicting change */
+ cl_git_pass(git_index_add_bypath(index, entry_path));
+ cl_git_pass(git_index_write(index));
+ git_index_free(index);
+
+ cl_assert_equal_i(
+ GIT_EMERGECONFLICT, git_checkout_tree(g_repo, g_object, &g_opts));
+}
+
+void test_checkout_tree__checking_out_a_conflicting_type_change_returns_EMERGECONFLICT(void)
+{
+ /*
+ * 099faba adds a symlink named 'link_to_new.txt'
+ * a65fedf is the parent of 099faba
+ */
+
+ assert_conflict("link_to_new.txt", "old.txt", "a65fedf", "099faba");
+}
+
+void test_checkout_tree__checking_out_a_conflicting_type_change_returns_EMERGECONFLICT_2(void)
+{
+ /*
+ * cf80f8d adds a directory named 'a/'
+ * a4a7dce is the parent of cf80f8d
+ */
+
+ assert_conflict("a", "hello\n", "a4a7dce", "cf80f8d");
+}
+
+void test_checkout_tree__checking_out_a_conflicting_content_change_returns_EMERGECONFLICT(void)
+{
+ /*
+ * c47800c adds a symlink named 'branch_file.txt'
+ * 5b5b025 is the parent of 763d71a
+ */
+
+ assert_conflict("branch_file.txt", "hello\n", "5b5b025", "c47800c");
+}
+
+void test_checkout_tree__donot_update_deleted_file_by_default(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ git_oid old_id, new_id;
+ git_commit *old_commit = NULL, *new_commit = NULL;
+ git_index *index = NULL;
+ checkout_counts ct;
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+
+ memset(&ct, 0, sizeof(ct));
+ opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
+ opts.notify_cb = checkout_count_callback;
+ opts.notify_payload = &ct;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_pass(git_oid_fromstr(&old_id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
+ cl_git_pass(git_commit_lookup(&old_commit, g_repo, &old_id));
+ cl_git_pass(git_reset(g_repo, (git_object *)old_commit, GIT_RESET_HARD));
+
+ cl_git_pass(p_unlink("testrepo/branch_file.txt"));
+ cl_git_pass(git_index_remove_bypath(index ,"branch_file.txt"));
+ cl_git_pass(git_index_write(index));
+
+ cl_assert(!git_path_exists("testrepo/branch_file.txt"));
+
+ cl_git_pass(git_oid_fromstr(&new_id, "099fabac3a9ea935598528c27f866e34089c2eff"));
+ cl_git_pass(git_commit_lookup(&new_commit, g_repo, &new_id));
+
+
+ cl_git_fail(git_checkout_tree(g_repo, (git_object *)new_commit, &opts));
+
+ cl_assert_equal_i(1, ct.n_conflicts);
+ cl_assert_equal_i(1, ct.n_updates);
+
+ git_commit_free(old_commit);
+ git_commit_free(new_commit);
+ git_index_free(index);
+}
+
+void test_checkout_tree__can_checkout_with_last_workdir_item_missing(void)
+{
+ git_index *index = NULL;
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ git_oid tree_id, commit_id;
+ git_tree *tree = NULL;
+ git_commit *commit = NULL;
+
+ git_repository_index(&index, g_repo);
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_reference_name_to_id(&commit_id, g_repo, "refs/heads/master"));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &commit_id));
+
+ cl_git_pass(git_checkout_tree(g_repo, (git_object *)commit, &opts));
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master"));
+
+ cl_git_pass(p_mkdir("./testrepo/this-is-dir", 0777));
+ cl_git_mkfile("./testrepo/this-is-dir/contained_file", "content\n");
+
+ cl_git_pass(git_index_add_bypath(index, "this-is-dir/contained_file"));
+ git_index_write_tree(&tree_id, index);
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
+
+ cl_git_pass(p_unlink("./testrepo/this-is-dir/contained_file"));
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+
+ opts.checkout_strategy = 1;
+ git_checkout_tree(g_repo, (git_object *)tree, &opts);
+
+ git_tree_free(tree);
+ git_commit_free(commit);
+ git_index_free(index);
+}
+
+void test_checkout_tree__issue_1397(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ const char *partial_oid = "8a7ef04";
+ git_object *tree = NULL;
+
+ test_checkout_tree__cleanup(); /* cleanup default checkout */
+
+ g_repo = cl_git_sandbox_init("issue_1397");
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ cl_git_pass(git_revparse_single(&tree, g_repo, partial_oid));
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_checkout_tree(g_repo, tree, &opts));
+
+ check_file_contents("./issue_1397/crlf_file.txt", "first line\r\nsecond line\r\nboth with crlf");
+
+ git_object_free(tree);
+}
+
+void test_checkout_tree__can_write_to_empty_dirs(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ git_oid oid;
+ git_object *obj = NULL;
+
+ assert_on_branch(g_repo, "master");
+
+ cl_git_pass(p_mkdir("testrepo/a", 0777));
+
+ /* do first checkout with FORCE because we don't know if testrepo
+ * base data is clean for a checkout or not
+ */
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+
+ cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+
+ cl_assert(git_path_isfile("testrepo/a/b.txt"));
+
+ git_object_free(obj);
+}
+
+void test_checkout_tree__fails_when_dir_in_use(void)
+{
+#ifdef GIT_WIN32
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ git_oid oid;
+ git_object *obj = NULL;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+
+ cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+
+ cl_assert(git_path_isfile("testrepo/a/b.txt"));
+
+ git_object_free(obj);
+
+ cl_git_pass(p_chdir("testrepo/a"));
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/master"));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+
+ cl_git_fail(git_checkout_tree(g_repo, obj, &opts));
+
+ cl_git_pass(p_chdir("../.."));
+
+ cl_assert(git_path_is_empty_dir("testrepo/a"));
+
+ git_object_free(obj);
+#endif
+}
+
+void test_checkout_tree__can_continue_when_dir_in_use(void)
+{
+#ifdef GIT_WIN32
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ git_oid oid;
+ git_object *obj = NULL;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE |
+ GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+
+ cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+
+ cl_assert(git_path_isfile("testrepo/a/b.txt"));
+
+ git_object_free(obj);
+
+ cl_git_pass(p_chdir("testrepo/a"));
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/master"));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+
+ cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+
+ cl_git_pass(p_chdir("../.."));
+
+ cl_assert(git_path_is_empty_dir("testrepo/a"));
+
+ git_object_free(obj);
+#endif
+}
+
+void test_checkout_tree__target_directory_from_bare(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ git_oid oid;
+ checkout_counts cts;
+ memset(&cts, 0, sizeof(cts));
+
+ test_checkout_tree__cleanup(); /* cleanup default checkout */
+
+ g_repo = cl_git_sandbox_init("testrepo.git");
+ cl_assert(git_repository_is_bare(g_repo));
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+
+ opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
+ opts.notify_cb = checkout_count_callback;
+ opts.notify_payload = &cts;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "HEAD"));
+ cl_git_pass(git_object_lookup(&g_object, g_repo, &oid, GIT_OBJ_ANY));
+
+ cl_git_fail(git_checkout_tree(g_repo, g_object, &opts));
+
+ opts.target_directory = "alternative";
+ cl_assert(!git_path_isdir("alternative"));
+
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &opts));
+
+ cl_assert_equal_i(0, cts.n_untracked);
+ cl_assert_equal_i(0, cts.n_ignored);
+ cl_assert_equal_i(3, cts.n_updates);
+
+ check_file_contents_nocr("./alternative/README", "hey there\n");
+ check_file_contents_nocr("./alternative/branch_file.txt", "hi\nbye!\n");
+ check_file_contents_nocr("./alternative/new.txt", "my new file\n");
+
+ cl_git_pass(git_futils_rmdir_r(
+ "alternative", NULL, GIT_RMDIR_REMOVE_FILES));
+}
+
+void test_checkout_tree__extremely_long_file_name(void)
+{
+ // A utf-8 string with 83 characters, but 249 bytes.
+ const char *longname = "\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97";
+ char path[1024];
+
+ g_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "long-file-name"));
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+
+ sprintf(path, "testrepo/%s.txt", longname);
+ cl_assert(git_path_exists(path));
+
+ git_object_free(g_object);
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "master"));
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+ cl_assert(!git_path_exists(path));
+}
+
+static void create_conflict(void)
+{
+ git_index *index;
+ git_index_entry entry;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ memset(&entry, 0x0, sizeof(git_index_entry));
+ entry.mode = 0100644;
+ entry.flags = 1 << GIT_IDXENTRY_STAGESHIFT;
+ git_oid_fromstr(&entry.oid, "d427e0b2e138501a3d15cc376077a3631e15bd46");
+ entry.path = "conflicts.txt";
+ cl_git_pass(git_index_add(index, &entry));
+
+ entry.flags = 2 << GIT_IDXENTRY_STAGESHIFT;
+ git_oid_fromstr(&entry.oid, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf");
+ cl_git_pass(git_index_add(index, &entry));
+
+ entry.flags = 3 << GIT_IDXENTRY_STAGESHIFT;
+ git_oid_fromstr(&entry.oid, "2bd0a343aeef7a2cf0d158478966a6e587ff3863");
+ cl_git_pass(git_index_add(index, &entry));
+
+ git_index_write(index);
+ git_index_free(index);
+}
+
+void test_checkout_tree__fails_when_conflicts_exist_in_index(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ git_oid oid;
+ git_object *obj = NULL;
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "HEAD"));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+
+ create_conflict();
+
+ cl_git_fail(git_checkout_tree(g_repo, obj, &opts));
+
+ git_object_free(obj);
+}
diff --git a/tests-clar/checkout/typechange.c b/tests/checkout/typechange.c
index 6cf99ac15..6cf99ac15 100644
--- a/tests-clar/checkout/typechange.c
+++ b/tests/checkout/typechange.c
diff --git a/tests/clar.c b/tests/clar.c
new file mode 100644
index 000000000..6c7399a54
--- /dev/null
+++ b/tests/clar.c
@@ -0,0 +1,511 @@
+/*
+ * Copyright (c) Vicent Marti. All rights reserved.
+ *
+ * This file is part of clar, distributed under the ISC license.
+ * For full terms see the included COPYING file.
+ */
+#include <assert.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <stdarg.h>
+
+/* required for sandboxing */
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef _WIN32
+# include <windows.h>
+# include <io.h>
+# include <shellapi.h>
+# include <direct.h>
+
+# define _MAIN_CC __cdecl
+
+# ifndef stat
+# define stat(path, st) _stat(path, st)
+# endif
+# ifndef mkdir
+# define mkdir(path, mode) _mkdir(path)
+# endif
+# ifndef chdir
+# define chdir(path) _chdir(path)
+# endif
+# ifndef access
+# define access(path, mode) _access(path, mode)
+# endif
+# ifndef strdup
+# define strdup(str) _strdup(str)
+# endif
+# ifndef strcasecmp
+# define strcasecmp(a,b) _stricmp(a,b)
+# endif
+
+# ifndef __MINGW32__
+# pragma comment(lib, "shell32")
+# ifndef strncpy
+# define strncpy(to, from, to_size) strncpy_s(to, to_size, from, _TRUNCATE)
+# endif
+# ifndef W_OK
+# define W_OK 02
+# endif
+# ifndef S_ISDIR
+# define S_ISDIR(x) ((x & _S_IFDIR) != 0)
+# endif
+# define p_snprintf(buf,sz,fmt,...) _snprintf_s(buf,sz,_TRUNCATE,fmt,__VA_ARGS__)
+# else
+# define p_snprintf snprintf
+# endif
+
+# ifndef PRIuZ
+# define PRIuZ "Iu"
+# endif
+# ifndef PRIxZ
+# define PRIxZ "Ix"
+# endif
+ typedef struct _stat STAT_T;
+#else
+# include <sys/wait.h> /* waitpid(2) */
+# include <unistd.h>
+# define _MAIN_CC
+# define p_snprintf snprintf
+# ifndef PRIuZ
+# define PRIuZ "zu"
+# endif
+# ifndef PRIxZ
+# define PRIxZ "zx"
+# endif
+ typedef struct stat STAT_T;
+#endif
+
+#include "clar.h"
+
+static void fs_rm(const char *_source);
+static void fs_copy(const char *_source, const char *dest);
+
+static const char *
+fixture_path(const char *base, const char *fixture_name);
+
+struct clar_error {
+ const char *test;
+ int test_number;
+ const char *suite;
+ const char *file;
+ int line_number;
+ const char *error_msg;
+ char *description;
+
+ struct clar_error *next;
+};
+
+static struct {
+ const char *active_test;
+ const char *active_suite;
+
+ int suite_errors;
+ int total_errors;
+
+ int tests_ran;
+ int suites_ran;
+
+ int report_errors_only;
+ int exit_on_error;
+ int report_suite_names;
+
+ struct clar_error *errors;
+ struct clar_error *last_error;
+
+ void (*local_cleanup)(void *);
+ void *local_cleanup_payload;
+
+ jmp_buf trampoline;
+ int trampoline_enabled;
+} _clar;
+
+struct clar_func {
+ const char *name;
+ void (*ptr)(void);
+};
+
+struct clar_suite {
+ const char *name;
+ struct clar_func initialize;
+ struct clar_func cleanup;
+ const struct clar_func *tests;
+ size_t test_count;
+ int enabled;
+};
+
+/* From clar_print_*.c */
+static void clar_print_init(int test_count, int suite_count, const char *suite_names);
+static void clar_print_shutdown(int test_count, int suite_count, int error_count);
+static void clar_print_error(int num, const struct clar_error *error);
+static void clar_print_ontest(const char *test_name, int test_number, int failed);
+static void clar_print_onsuite(const char *suite_name, int suite_index);
+static void clar_print_onabort(const char *msg, ...);
+
+/* From clar_sandbox.c */
+static void clar_unsandbox(void);
+static int clar_sandbox(void);
+
+/* Load the declarations for the test suite */
+#include "clar.suite"
+
+/* Core test functions */
+static void
+clar_report_errors(void)
+{
+ int i = 1;
+ struct clar_error *error, *next;
+
+ error = _clar.errors;
+ while (error != NULL) {
+ next = error->next;
+ clar_print_error(i++, error);
+ free(error->description);
+ free(error);
+ error = next;
+ }
+
+ _clar.errors = _clar.last_error = NULL;
+}
+
+static void
+clar_run_test(
+ const struct clar_func *test,
+ const struct clar_func *initialize,
+ const struct clar_func *cleanup)
+{
+ int error_st = _clar.suite_errors;
+
+ _clar.trampoline_enabled = 1;
+
+ if (setjmp(_clar.trampoline) == 0) {
+ if (initialize->ptr != NULL)
+ initialize->ptr();
+
+ test->ptr();
+ }
+
+ _clar.trampoline_enabled = 0;
+
+ if (_clar.local_cleanup != NULL)
+ _clar.local_cleanup(_clar.local_cleanup_payload);
+
+ if (cleanup->ptr != NULL)
+ cleanup->ptr();
+
+ _clar.tests_ran++;
+
+ /* remove any local-set cleanup methods */
+ _clar.local_cleanup = NULL;
+ _clar.local_cleanup_payload = NULL;
+
+ if (_clar.report_errors_only)
+ clar_report_errors();
+ else
+ clar_print_ontest(
+ test->name,
+ _clar.tests_ran,
+ (_clar.suite_errors > error_st)
+ );
+}
+
+static void
+clar_run_suite(const struct clar_suite *suite, const char *filter)
+{
+ const struct clar_func *test = suite->tests;
+ size_t i, matchlen;
+
+ if (!suite->enabled)
+ return;
+
+ if (_clar.exit_on_error && _clar.total_errors)
+ return;
+
+ if (!_clar.report_errors_only)
+ clar_print_onsuite(suite->name, ++_clar.suites_ran);
+
+ _clar.active_suite = suite->name;
+ _clar.suite_errors = 0;
+
+ if (filter) {
+ size_t suitelen = strlen(suite->name);
+ matchlen = strlen(filter);
+ if (matchlen <= suitelen) {
+ filter = NULL;
+ } else {
+ filter += suitelen;
+ while (*filter == ':')
+ ++filter;
+ matchlen = strlen(filter);
+ }
+ }
+
+ for (i = 0; i < suite->test_count; ++i) {
+ if (filter && strncmp(test[i].name, filter, matchlen))
+ continue;
+
+ _clar.active_test = test[i].name;
+ clar_run_test(&test[i], &suite->initialize, &suite->cleanup);
+
+ if (_clar.exit_on_error && _clar.total_errors)
+ return;
+ }
+}
+
+static void
+clar_usage(const char *arg)
+{
+ printf("Usage: %s [options]\n\n", arg);
+ printf("Options:\n");
+ printf(" -sname\tRun only the suite with `name` (can go to individual test name)\n");
+ printf(" -iname\tInclude the suite with `name`\n");
+ printf(" -xname\tExclude the suite with `name`\n");
+ printf(" -q \tOnly report tests that had an error\n");
+ printf(" -Q \tQuit as soon as a test fails\n");
+ printf(" -l \tPrint suite names\n");
+ exit(-1);
+}
+
+static void
+clar_parse_args(int argc, char **argv)
+{
+ int i;
+
+ for (i = 1; i < argc; ++i) {
+ char *argument = argv[i];
+
+ if (argument[0] != '-')
+ clar_usage(argv[0]);
+
+ switch (argument[1]) {
+ case 's':
+ case 'i':
+ case 'x': { /* given suite name */
+ int offset = (argument[2] == '=') ? 3 : 2, found = 0;
+ char action = argument[1];
+ size_t j, arglen, suitelen, cmplen;
+
+ argument += offset;
+ arglen = strlen(argument);
+
+ if (arglen == 0)
+ clar_usage(argv[0]);
+
+ for (j = 0; j < _clar_suite_count; ++j) {
+ suitelen = strlen(_clar_suites[j].name);
+ cmplen = (arglen < suitelen) ? arglen : suitelen;
+
+ if (strncmp(argument, _clar_suites[j].name, cmplen) == 0) {
+ int exact = (arglen >= suitelen);
+
+ ++found;
+
+ if (!exact)
+ _clar.report_suite_names = 1;
+
+ switch (action) {
+ case 's': clar_run_suite(&_clar_suites[j], argument); break;
+ case 'i': _clar_suites[j].enabled = 1; break;
+ case 'x': _clar_suites[j].enabled = 0; break;
+ }
+
+ if (exact)
+ break;
+ }
+ }
+
+ if (!found) {
+ clar_print_onabort("No suite matching '%s' found.\n", argument);
+ exit(-1);
+ }
+ break;
+ }
+
+ case 'q':
+ _clar.report_errors_only = 1;
+ break;
+
+ case 'Q':
+ _clar.exit_on_error = 1;
+ break;
+
+ case 'l': {
+ size_t j;
+ printf("Test suites (use -s<name> to run just one):\n");
+ for (j = 0; j < _clar_suite_count; ++j)
+ printf(" %3d: %s\n", (int)j, _clar_suites[j].name);
+
+ exit(0);
+ }
+
+ default:
+ clar_usage(argv[0]);
+ }
+ }
+}
+
+int
+clar_test(int argc, char **argv)
+{
+ clar_print_init(
+ (int)_clar_callback_count,
+ (int)_clar_suite_count,
+ ""
+ );
+
+ if (clar_sandbox() < 0) {
+ clar_print_onabort("Failed to sandbox the test runner.\n");
+ exit(-1);
+ }
+
+ if (argc > 1)
+ clar_parse_args(argc, argv);
+
+ if (!_clar.suites_ran) {
+ size_t i;
+ for (i = 0; i < _clar_suite_count; ++i)
+ clar_run_suite(&_clar_suites[i], NULL);
+ }
+
+ clar_print_shutdown(
+ _clar.tests_ran,
+ (int)_clar_suite_count,
+ _clar.total_errors
+ );
+
+ clar_unsandbox();
+ return _clar.total_errors;
+}
+
+void clar__fail(
+ const char *file,
+ int line,
+ const char *error_msg,
+ const char *description,
+ int should_abort)
+{
+ struct clar_error *error = calloc(1, sizeof(struct clar_error));
+
+ if (_clar.errors == NULL)
+ _clar.errors = error;
+
+ if (_clar.last_error != NULL)
+ _clar.last_error->next = error;
+
+ _clar.last_error = error;
+
+ error->test = _clar.active_test;
+ error->test_number = _clar.tests_ran;
+ error->suite = _clar.active_suite;
+ error->file = file;
+ error->line_number = line;
+ error->error_msg = error_msg;
+
+ if (description != NULL)
+ error->description = strdup(description);
+
+ _clar.suite_errors++;
+ _clar.total_errors++;
+
+ if (should_abort) {
+ if (!_clar.trampoline_enabled) {
+ clar_print_onabort(
+ "Fatal error: a cleanup method raised an exception.");
+ clar_report_errors();
+ exit(-1);
+ }
+
+ longjmp(_clar.trampoline, -1);
+ }
+}
+
+void clar__assert(
+ int condition,
+ const char *file,
+ int line,
+ const char *error_msg,
+ const char *description,
+ int should_abort)
+{
+ if (condition)
+ return;
+
+ clar__fail(file, line, error_msg, description, should_abort);
+}
+
+void clar__assert_equal(
+ const char *file,
+ int line,
+ const char *err,
+ int should_abort,
+ const char *fmt,
+ ...)
+{
+ va_list args;
+ char buf[4096];
+ int is_equal = 1;
+
+ va_start(args, fmt);
+
+ if (!strcmp("%s", fmt)) {
+ const char *s1 = va_arg(args, const char *);
+ const char *s2 = va_arg(args, const char *);
+ is_equal = (!s1 || !s2) ? (s1 == s2) : !strcmp(s1, s2);
+
+ if (!is_equal) {
+ if (s1 && s2) {
+ int pos;
+ for (pos = 0; s1[pos] == s2[pos] && s1[pos] && s2[pos]; ++pos)
+ /* find differing byte offset */;
+ p_snprintf(buf, sizeof(buf), "'%s' != '%s' (at byte %d)",
+ s1, s2, pos);
+ } else {
+ p_snprintf(buf, sizeof(buf), "'%s' != '%s'", s1, s2);
+ }
+ }
+ }
+ else if (!strcmp("%"PRIuZ, fmt) || !strcmp("%"PRIxZ, fmt)) {
+ size_t sz1 = va_arg(args, size_t), sz2 = va_arg(args, size_t);
+ is_equal = (sz1 == sz2);
+ if (!is_equal) {
+ int offset = p_snprintf(buf, sizeof(buf), fmt, sz1);
+ strncat(buf, " != ", sizeof(buf) - offset);
+ p_snprintf(buf + offset + 4, sizeof(buf) - offset - 4, fmt, sz2);
+ }
+ }
+ else if (!strcmp("%p", fmt)) {
+ void *p1 = va_arg(args, void *), *p2 = va_arg(args, void *);
+ is_equal = (p1 == p2);
+ if (!is_equal)
+ p_snprintf(buf, sizeof(buf), "%p != %p", p1, p2);
+ }
+ else {
+ int i1 = va_arg(args, int), i2 = va_arg(args, int);
+ is_equal = (i1 == i2);
+ if (!is_equal) {
+ int offset = p_snprintf(buf, sizeof(buf), fmt, i1);
+ strncat(buf, " != ", sizeof(buf) - offset);
+ p_snprintf(buf + offset + 4, sizeof(buf) - offset - 4, fmt, i2);
+ }
+ }
+
+ va_end(args);
+
+ if (!is_equal)
+ clar__fail(file, line, err, buf, should_abort);
+}
+
+void cl_set_cleanup(void (*cleanup)(void *), void *opaque)
+{
+ _clar.local_cleanup = cleanup;
+ _clar.local_cleanup_payload = opaque;
+}
+
+#include "clar/sandbox.h"
+#include "clar/fixtures.h"
+#include "clar/fs.h"
+#include "clar/print.h"
diff --git a/tests/clar.h b/tests/clar.h
new file mode 100644
index 000000000..e1f244eba
--- /dev/null
+++ b/tests/clar.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) Vicent Marti. All rights reserved.
+ *
+ * This file is part of clar, distributed under the ISC license.
+ * For full terms see the included COPYING file.
+ */
+#ifndef __CLAR_TEST_H__
+#define __CLAR_TEST_H__
+
+#include <stdlib.h>
+
+int clar_test(int argc, char *argv[]);
+
+void cl_set_cleanup(void (*cleanup)(void *), void *opaque);
+void cl_fs_cleanup(void);
+
+#ifdef CLAR_FIXTURE_PATH
+const char *cl_fixture(const char *fixture_name);
+void cl_fixture_sandbox(const char *fixture_name);
+void cl_fixture_cleanup(const char *fixture_name);
+#endif
+
+/**
+ * Assertion macros with explicit error message
+ */
+#define cl_must_pass_(expr, desc) clar__assert((expr) >= 0, __FILE__, __LINE__, "Function call failed: " #expr, desc, 1)
+#define cl_must_fail_(expr, desc) clar__assert((expr) < 0, __FILE__, __LINE__, "Expected function call to fail: " #expr, desc, 1)
+#define cl_assert_(expr, desc) clar__assert((expr) != 0, __FILE__, __LINE__, "Expression is not true: " #expr, desc, 1)
+
+/**
+ * Check macros with explicit error message
+ */
+#define cl_check_pass_(expr, desc) clar__assert((expr) >= 0, __FILE__, __LINE__, "Function call failed: " #expr, desc, 0)
+#define cl_check_fail_(expr, desc) clar__assert((expr) < 0, __FILE__, __LINE__, "Expected function call to fail: " #expr, desc, 0)
+#define cl_check_(expr, desc) clar__assert((expr) != 0, __FILE__, __LINE__, "Expression is not true: " #expr, desc, 0)
+
+/**
+ * Assertion macros with no error message
+ */
+#define cl_must_pass(expr) cl_must_pass_(expr, NULL)
+#define cl_must_fail(expr) cl_must_fail_(expr, NULL)
+#define cl_assert(expr) cl_assert_(expr, NULL)
+
+/**
+ * Check macros with no error message
+ */
+#define cl_check_pass(expr) cl_check_pass_(expr, NULL)
+#define cl_check_fail(expr) cl_check_fail_(expr, NULL)
+#define cl_check(expr) cl_check_(expr, NULL)
+
+/**
+ * Forced failure/warning
+ */
+#define cl_fail(desc) clar__fail(__FILE__, __LINE__, "Test failed.", desc, 1)
+#define cl_warning(desc) clar__fail(__FILE__, __LINE__, "Warning during test execution:", desc, 0)
+
+/**
+ * Typed assertion macros
+ */
+#define cl_assert_equal_s(s1,s2) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2, 1, "%s", (s1), (s2))
+#define cl_assert_equal_s_(s1,s2,note) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%s", (s1), (s2))
+
+#define cl_assert_equal_i(i1,i2) clar__assert_equal(__FILE__,__LINE__,#i1 " != " #i2, 1, "%d", (int)(i1), (int)(i2))
+#define cl_assert_equal_i_(i1,i2,note) clar__assert_equal(__FILE__,__LINE__,#i1 " != " #i2 " (" #note ")", 1, "%d", (i1), (i2))
+#define cl_assert_equal_i_fmt(i1,i2,fmt) clar__assert_equal(__FILE__,__LINE__,#i1 " != " #i2, 1, (fmt), (int)(i1), (int)(i2))
+
+#define cl_assert_equal_b(b1,b2) clar__assert_equal(__FILE__,__LINE__,#b1 " != " #b2, 1, "%d", (int)((b1) != 0),(int)((b2) != 0))
+
+#define cl_assert_equal_p(p1,p2) clar__assert_equal(__FILE__,__LINE__,"Pointer mismatch: " #p1 " != " #p2, 1, "%p", (p1), (p2))
+
+
+void clar__fail(
+ const char *file,
+ int line,
+ const char *error,
+ const char *description,
+ int should_abort);
+
+void clar__assert(
+ int condition,
+ const char *file,
+ int line,
+ const char *error,
+ const char *description,
+ int should_abort);
+
+void clar__assert_equal(
+ const char *file,
+ int line,
+ const char *err,
+ int should_abort,
+ const char *fmt,
+ ...);
+
+#endif
diff --git a/tests-clar/clar/fixtures.h b/tests/clar/fixtures.h
index 264cd7f4f..264cd7f4f 100644
--- a/tests-clar/clar/fixtures.h
+++ b/tests/clar/fixtures.h
diff --git a/tests-clar/clar/fs.h b/tests/clar/fs.h
index b7a1ff9d2..b7a1ff9d2 100644
--- a/tests-clar/clar/fs.h
+++ b/tests/clar/fs.h
diff --git a/tests-clar/clar/print.h b/tests/clar/print.h
index 368016f2f..368016f2f 100644
--- a/tests-clar/clar/print.h
+++ b/tests/clar/print.h
diff --git a/tests/clar/sandbox.h b/tests/clar/sandbox.h
new file mode 100644
index 000000000..ee7564148
--- /dev/null
+++ b/tests/clar/sandbox.h
@@ -0,0 +1,129 @@
+static char _clar_path[4096];
+
+static int
+is_valid_tmp_path(const char *path)
+{
+ STAT_T st;
+
+ if (stat(path, &st) != 0)
+ return 0;
+
+ if (!S_ISDIR(st.st_mode))
+ return 0;
+
+ return (access(path, W_OK) == 0);
+}
+
+static int
+find_tmp_path(char *buffer, size_t length)
+{
+#ifndef _WIN32
+ static const size_t var_count = 5;
+ static const char *env_vars[] = {
+ "CLAR_TMP", "TMPDIR", "TMP", "TEMP", "USERPROFILE"
+ };
+
+ size_t i;
+
+ for (i = 0; i < var_count; ++i) {
+ const char *env = getenv(env_vars[i]);
+ if (!env)
+ continue;
+
+ if (is_valid_tmp_path(env)) {
+ strncpy(buffer, env, length);
+ return 0;
+ }
+ }
+
+ /* If the environment doesn't say anything, try to use /tmp */
+ if (is_valid_tmp_path("/tmp")) {
+ strncpy(buffer, "/tmp", length);
+ return 0;
+ }
+
+#else
+ DWORD env_len = GetEnvironmentVariable("CLAR_TMP", buffer, (DWORD)length);
+ if (env_len > 0 && env_len < (DWORD)length)
+ return 0;
+
+ if (GetTempPath((DWORD)length, buffer))
+ return 0;
+#endif
+
+ /* This system doesn't like us, try to use the current directory */
+ if (is_valid_tmp_path(".")) {
+ strncpy(buffer, ".", length);
+ return 0;
+ }
+
+ return -1;
+}
+
+static void clar_unsandbox(void)
+{
+ if (_clar_path[0] == '\0')
+ return;
+
+ chdir("..");
+
+ fs_rm(_clar_path);
+}
+
+static int build_sandbox_path(void)
+{
+ const char path_tail[] = "clar_tmp_XXXXXX";
+ size_t len;
+
+ if (find_tmp_path(_clar_path, sizeof(_clar_path)) < 0)
+ return -1;
+
+ len = strlen(_clar_path);
+
+#ifdef _WIN32
+ { /* normalize path to POSIX forward slashes */
+ size_t i;
+ for (i = 0; i < len; ++i) {
+ if (_clar_path[i] == '\\')
+ _clar_path[i] = '/';
+ }
+ }
+#endif
+
+ if (_clar_path[len - 1] != '/') {
+ _clar_path[len++] = '/';
+ }
+
+ strncpy(_clar_path + len, path_tail, sizeof(_clar_path) - len);
+
+#if defined(__MINGW32__)
+ if (_mktemp(_clar_path) == NULL)
+ return -1;
+
+ if (mkdir(_clar_path, 0700) != 0)
+ return -1;
+#elif defined(_WIN32)
+ if (_mktemp_s(_clar_path, sizeof(_clar_path)) != 0)
+ return -1;
+
+ if (mkdir(_clar_path, 0700) != 0)
+ return -1;
+#else
+ if (mkdtemp(_clar_path) == NULL)
+ return -1;
+#endif
+
+ return 0;
+}
+
+static int clar_sandbox(void)
+{
+ if (_clar_path[0] == '\0' && build_sandbox_path() < 0)
+ return -1;
+
+ if (chdir(_clar_path) != 0)
+ return -1;
+
+ return 0;
+}
+
diff --git a/tests/clar_libgit2.c b/tests/clar_libgit2.c
new file mode 100644
index 000000000..50762cdb8
--- /dev/null
+++ b/tests/clar_libgit2.c
@@ -0,0 +1,483 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "path.h"
+#include "git2/sys/repository.h"
+
+void cl_git_report_failure(
+ int error, const char *file, int line, const char *fncall)
+{
+ char msg[4096];
+ const git_error *last = giterr_last();
+ p_snprintf(msg, 4096, "error %d - %s",
+ error, last ? last->message : "<no message>");
+ clar__assert(0, file, line, fncall, msg, 1);
+}
+
+void cl_git_mkfile(const char *filename, const char *content)
+{
+ int fd;
+
+ fd = p_creat(filename, 0666);
+ cl_assert(fd != 0);
+
+ if (content) {
+ cl_must_pass(p_write(fd, content, strlen(content)));
+ } else {
+ cl_must_pass(p_write(fd, filename, strlen(filename)));
+ cl_must_pass(p_write(fd, "\n", 1));
+ }
+
+ cl_must_pass(p_close(fd));
+}
+
+void cl_git_write2file(
+ const char *path, const char *content, size_t content_len,
+ int flags, unsigned int mode)
+{
+ int fd;
+ cl_assert(path && content);
+ cl_assert((fd = p_open(path, flags, mode)) >= 0);
+ if (!content_len)
+ content_len = strlen(content);
+ cl_must_pass(p_write(fd, content, content_len));
+ cl_must_pass(p_close(fd));
+}
+
+void cl_git_append2file(const char *path, const char *content)
+{
+ cl_git_write2file(path, content, 0, O_WRONLY | O_CREAT | O_APPEND, 0644);
+}
+
+void cl_git_rewritefile(const char *path, const char *content)
+{
+ cl_git_write2file(path, content, 0, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+}
+
+#ifdef GIT_WIN32
+
+#include "win32/utf-conv.h"
+
+char *cl_getenv(const char *name)
+{
+ git_win32_path name_utf16;
+ DWORD alloc_len;
+ wchar_t *value_utf16;
+ char *value_utf8;
+
+ git_win32_path_from_c(name_utf16, name);
+ alloc_len = GetEnvironmentVariableW(name_utf16, NULL, 0);
+ if (alloc_len <= 0)
+ return NULL;
+
+ cl_assert(value_utf16 = git__calloc(alloc_len, sizeof(wchar_t)));
+
+ GetEnvironmentVariableW(name_utf16, value_utf16, alloc_len);
+
+ alloc_len = alloc_len * 4 + 1; /* worst case UTF16->UTF8 growth */
+ cl_assert(value_utf8 = git__calloc(alloc_len, 1));
+
+ git__utf16_to_8(value_utf8, alloc_len, value_utf16);
+
+ git__free(value_utf16);
+
+ return value_utf8;
+}
+
+int cl_setenv(const char *name, const char *value)
+{
+ git_win32_path name_utf16;
+ git_win32_path value_utf16;
+
+ git_win32_path_from_c(name_utf16, name);
+
+ if (value) {
+ git_win32_path_from_c(value_utf16, value);
+ cl_assert(SetEnvironmentVariableW(name_utf16, value_utf16));
+ } else {
+ /* Windows XP returns 0 (failed) when passing NULL for lpValue when
+ * lpName does not exist in the environment block. This behavior
+ * seems to have changed in later versions. Don't check return value
+ * of SetEnvironmentVariable when passing NULL for lpValue.
+ */
+ SetEnvironmentVariableW(name_utf16, NULL);
+ }
+
+ return 0;
+}
+
+/* This function performs retries on calls to MoveFile in order
+ * to provide enhanced reliability in the face of antivirus
+ * agents that may be scanning the source (or in the case that
+ * the source is a directory, a child of the source). */
+int cl_rename(const char *source, const char *dest)
+{
+ git_win32_path source_utf16;
+ git_win32_path dest_utf16;
+ unsigned retries = 1;
+
+ git_win32_path_from_c(source_utf16, source);
+ git_win32_path_from_c(dest_utf16, dest);
+
+ while (!MoveFileW(source_utf16, dest_utf16)) {
+ /* Only retry if the error is ERROR_ACCESS_DENIED;
+ * this may indicate that an antivirus agent is
+ * preventing the rename from source to target */
+ if (retries > 5 ||
+ ERROR_ACCESS_DENIED != GetLastError())
+ return -1;
+
+ /* With 5 retries and a coefficient of 10ms, the maximum
+ * delay here is 550 ms */
+ Sleep(10 * retries * retries);
+ retries++;
+ }
+
+ return 0;
+}
+
+#else
+
+#include <stdlib.h>
+char *cl_getenv(const char *name)
+{
+ return getenv(name);
+}
+
+int cl_setenv(const char *name, const char *value)
+{
+ return (value == NULL) ? unsetenv(name) : setenv(name, value, 1);
+}
+
+int cl_rename(const char *source, const char *dest)
+{
+ return p_rename(source, dest);
+}
+
+#endif
+
+static const char *_cl_sandbox = NULL;
+static git_repository *_cl_repo = NULL;
+
+git_repository *cl_git_sandbox_init(const char *sandbox)
+{
+ /* Copy the whole sandbox folder from our fixtures to our test sandbox
+ * area. After this it can be accessed with `./sandbox`
+ */
+ cl_fixture_sandbox(sandbox);
+ _cl_sandbox = sandbox;
+
+ cl_git_pass(p_chdir(sandbox));
+
+ /* If this is not a bare repo, then rename `sandbox/.gitted` to
+ * `sandbox/.git` which must be done since we cannot store a folder
+ * named `.git` inside the fixtures folder of our libgit2 repo.
+ */
+ if (p_access(".gitted", F_OK) == 0)
+ cl_git_pass(cl_rename(".gitted", ".git"));
+
+ /* If we have `gitattributes`, rename to `.gitattributes`. This may
+ * be necessary if we don't want the attributes to be applied in the
+ * libgit2 repo, but just during testing.
+ */
+ if (p_access("gitattributes", F_OK) == 0)
+ cl_git_pass(cl_rename("gitattributes", ".gitattributes"));
+
+ /* As with `gitattributes`, we may need `gitignore` just for testing. */
+ if (p_access("gitignore", F_OK) == 0)
+ cl_git_pass(cl_rename("gitignore", ".gitignore"));
+
+ cl_git_pass(p_chdir(".."));
+
+ /* Now open the sandbox repository and make it available for tests */
+ cl_git_pass(git_repository_open(&_cl_repo, sandbox));
+
+ /* Adjust configs after copying to new filesystem */
+ cl_git_pass(git_repository_reinit_filesystem(_cl_repo, 0));
+
+ return _cl_repo;
+}
+
+git_repository *cl_git_sandbox_reopen(void)
+{
+ if (_cl_repo) {
+ git_repository_free(_cl_repo);
+ _cl_repo = NULL;
+
+ cl_git_pass(git_repository_open(&_cl_repo, _cl_sandbox));
+ }
+
+ return _cl_repo;
+}
+
+void cl_git_sandbox_cleanup(void)
+{
+ if (_cl_repo) {
+ git_repository_free(_cl_repo);
+ _cl_repo = NULL;
+ }
+ if (_cl_sandbox) {
+ cl_fixture_cleanup(_cl_sandbox);
+ _cl_sandbox = NULL;
+ }
+}
+
+bool cl_toggle_filemode(const char *filename)
+{
+ struct stat st1, st2;
+
+ cl_must_pass(p_stat(filename, &st1));
+ cl_must_pass(p_chmod(filename, st1.st_mode ^ 0100));
+ cl_must_pass(p_stat(filename, &st2));
+
+ return (st1.st_mode != st2.st_mode);
+}
+
+bool cl_is_chmod_supported(void)
+{
+ static int _is_supported = -1;
+
+ if (_is_supported < 0) {
+ cl_git_mkfile("filemode.t", "Test if filemode can be modified");
+ _is_supported = cl_toggle_filemode("filemode.t");
+ cl_must_pass(p_unlink("filemode.t"));
+ }
+
+ return _is_supported;
+}
+
+const char* cl_git_fixture_url(const char *fixturename)
+{
+ return cl_git_path_url(cl_fixture(fixturename));
+}
+
+const char* cl_git_path_url(const char *path)
+{
+ static char url[4096];
+
+ const char *in_buf;
+ git_buf path_buf = GIT_BUF_INIT;
+ git_buf url_buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_path_prettify_dir(&path_buf, path, NULL));
+ cl_git_pass(git_buf_puts(&url_buf, "file://"));
+
+#ifdef GIT_WIN32
+ /*
+ * A FILE uri matches the following format: file://[host]/path
+ * where "host" can be empty and "path" is an absolute path to the resource.
+ *
+ * In this test, no hostname is used, but we have to ensure the leading triple slashes:
+ *
+ * *nix: file:///usr/home/...
+ * Windows: file:///C:/Users/...
+ */
+ cl_git_pass(git_buf_putc(&url_buf, '/'));
+#endif
+
+ in_buf = git_buf_cstr(&path_buf);
+
+ /*
+ * A very hacky Url encoding that only takes care of escaping the spaces
+ */
+ while (*in_buf) {
+ if (*in_buf == ' ')
+ cl_git_pass(git_buf_puts(&url_buf, "%20"));
+ else
+ cl_git_pass(git_buf_putc(&url_buf, *in_buf));
+
+ in_buf++;
+ }
+
+ strncpy(url, git_buf_cstr(&url_buf), 4096);
+ git_buf_free(&url_buf);
+ git_buf_free(&path_buf);
+ return url;
+}
+
+typedef struct {
+ const char *filename;
+ size_t filename_len;
+} remove_data;
+
+static int remove_placeholders_recurs(void *_data, git_buf *path)
+{
+ remove_data *data = (remove_data *)_data;
+ size_t pathlen;
+
+ if (git_path_isdir(path->ptr) == true)
+ return git_path_direach(path, 0, remove_placeholders_recurs, data);
+
+ pathlen = path->size;
+
+ if (pathlen < data->filename_len)
+ return 0;
+
+ /* if path ends in '/'+filename (or equals filename) */
+ if (!strcmp(data->filename, path->ptr + pathlen - data->filename_len) &&
+ (pathlen == data->filename_len ||
+ path->ptr[pathlen - data->filename_len - 1] == '/'))
+ return p_unlink(path->ptr);
+
+ return 0;
+}
+
+int cl_git_remove_placeholders(const char *directory_path, const char *filename)
+{
+ int error;
+ remove_data data;
+ git_buf buffer = GIT_BUF_INIT;
+
+ if (git_path_isdir(directory_path) == false)
+ return -1;
+
+ if (git_buf_sets(&buffer, directory_path) < 0)
+ return -1;
+
+ data.filename = filename;
+ data.filename_len = strlen(filename);
+
+ error = remove_placeholders_recurs(&data, &buffer);
+
+ git_buf_free(&buffer);
+
+ return error;
+}
+
+#define CL_COMMIT_NAME "Libgit2 Tester"
+#define CL_COMMIT_EMAIL "libgit2-test@github.com"
+#define CL_COMMIT_MSG "Test commit of tree "
+
+void cl_repo_commit_from_index(
+ git_oid *out,
+ git_repository *repo,
+ git_signature *sig,
+ git_time_t time,
+ const char *msg)
+{
+ git_index *index;
+ git_oid commit_id, tree_id;
+ git_object *parent = NULL;
+ git_reference *ref = NULL;
+ git_tree *tree = NULL;
+ char buf[128];
+ int free_sig = (sig == NULL);
+
+ /* it is fine if looking up HEAD fails - we make this the first commit */
+ git_revparse_ext(&parent, &ref, repo, "HEAD");
+
+ /* write the index content as a tree */
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_write_tree(&tree_id, index));
+ cl_git_pass(git_index_write(index));
+ git_index_free(index);
+
+ cl_git_pass(git_tree_lookup(&tree, repo, &tree_id));
+
+ if (sig)
+ cl_assert(sig->name && sig->email);
+ else if (!time)
+ cl_git_pass(git_signature_now(&sig, CL_COMMIT_NAME, CL_COMMIT_EMAIL));
+ else
+ cl_git_pass(git_signature_new(
+ &sig, CL_COMMIT_NAME, CL_COMMIT_EMAIL, time, 0));
+
+ if (!msg) {
+ strcpy(buf, CL_COMMIT_MSG);
+ git_oid_tostr(buf + strlen(CL_COMMIT_MSG),
+ sizeof(buf) - strlen(CL_COMMIT_MSG), &tree_id);
+ msg = buf;
+ }
+
+ cl_git_pass(git_commit_create_v(
+ &commit_id, repo, ref ? git_reference_name(ref) : "HEAD",
+ sig, sig, NULL, msg, tree, parent ? 1 : 0, parent));
+
+ if (out)
+ git_oid_cpy(out, &commit_id);
+
+ git_object_free(parent);
+ git_reference_free(ref);
+ if (free_sig)
+ git_signature_free(sig);
+ git_tree_free(tree);
+}
+
+void cl_repo_set_bool(git_repository *repo, const char *cfg, int value)
+{
+ git_config *config;
+ cl_git_pass(git_repository_config(&config, repo));
+ cl_git_pass(git_config_set_bool(config, cfg, value != 0));
+ git_config_free(config);
+}
+
+int cl_repo_get_bool(git_repository *repo, const char *cfg)
+{
+ int val = 0;
+ git_config *config;
+ cl_git_pass(git_repository_config(&config, repo));
+ cl_git_pass(git_config_get_bool(&val, config, cfg));;
+ git_config_free(config);
+ return val;
+}
+
+/* this is essentially the code from git__unescape modified slightly */
+static size_t strip_cr_from_buf(char *start, size_t len)
+{
+ char *scan, *trail, *end = start + len;
+
+ for (scan = trail = start; scan < end; trail++, scan++) {
+ while (*scan == '\r')
+ scan++; /* skip '\r' */
+
+ if (trail != scan)
+ *trail = *scan;
+ }
+
+ *trail = '\0';
+
+ return (trail - start);
+}
+
+void clar__assert_equal_file(
+ const char *expected_data,
+ size_t expected_bytes,
+ int ignore_cr,
+ const char *path,
+ const char *file,
+ int line)
+{
+ char buf[4000];
+ ssize_t bytes, total_bytes = 0;
+ int fd = p_open(path, O_RDONLY | O_BINARY);
+ cl_assert(fd >= 0);
+
+ if (expected_data && !expected_bytes)
+ expected_bytes = strlen(expected_data);
+
+ while ((bytes = p_read(fd, buf, sizeof(buf))) != 0) {
+ clar__assert(
+ bytes > 0, file, line, "error reading from file", path, 1);
+
+ if (ignore_cr)
+ bytes = strip_cr_from_buf(buf, bytes);
+
+ if (memcmp(expected_data, buf, bytes) != 0) {
+ int pos;
+ for (pos = 0; pos < bytes && expected_data[pos] == buf[pos]; ++pos)
+ /* find differing byte offset */;
+ p_snprintf(
+ buf, sizeof(buf), "file content mismatch at byte %d",
+ (int)(total_bytes + pos));
+ clar__fail(file, line, buf, path, 1);
+ }
+
+ expected_data += bytes;
+ total_bytes += bytes;
+ }
+
+ p_close(fd);
+
+ clar__assert(!bytes, file, line, "error reading from file", path, 1);
+ clar__assert_equal(file, line, "mismatched file length", 1, "%"PRIuZ,
+ (size_t)expected_bytes, (size_t)total_bytes);
+}
diff --git a/tests/clar_libgit2.h b/tests/clar_libgit2.h
new file mode 100644
index 000000000..b9ef5627e
--- /dev/null
+++ b/tests/clar_libgit2.h
@@ -0,0 +1,119 @@
+#ifndef __CLAR_LIBGIT2__
+#define __CLAR_LIBGIT2__
+
+#include "clar.h"
+#include <git2.h>
+#include "common.h"
+
+/**
+ * Replace for `clar_must_pass` that passes the last library error as the
+ * test failure message.
+ *
+ * Use this wrapper around all `git_` library calls that return error codes!
+ */
+#define cl_git_pass(expr) do { \
+ int _lg2_error; \
+ giterr_clear(); \
+ if ((_lg2_error = (expr)) != 0) \
+ cl_git_report_failure(_lg2_error, __FILE__, __LINE__, "Function call failed: " #expr); \
+ } while (0)
+
+/**
+ * Wrapper for `clar_must_fail` -- this one is
+ * just for consistency. Use with `git_` library
+ * calls that are supposed to fail!
+ */
+#define cl_git_fail(expr) cl_must_fail(expr)
+
+#define cl_git_fail_with(expr, error) cl_assert_equal_i(error,expr)
+
+void cl_git_report_failure(int, const char *, int, const char *);
+
+#define cl_assert_at_line(expr,file,line) \
+ clar__assert((expr) != 0, file, line, "Expression is not true: " #expr, NULL, 1)
+
+GIT_INLINE(void) clar__assert_in_range(
+ int lo, int val, int hi,
+ const char *file, int line, const char *err, int should_abort)
+{
+ if (lo > val || hi < val) {
+ char buf[128];
+ snprintf(buf, sizeof(buf), "%d not in [%d,%d]", val, lo, hi);
+ clar__fail(file, line, err, buf, should_abort);
+ }
+}
+
+#define cl_assert_equal_sz(sz1,sz2) do { \
+ size_t __sz1 = (size_t)(sz1), __sz2 = (size_t)(sz2); \
+ clar__assert_equal(__FILE__,__LINE__,#sz1 " != " #sz2, 1, "%"PRIuZ, __sz1, __sz2); \
+} while (0)
+
+#define cl_assert_in_range(L,V,H) \
+ clar__assert_in_range((L),(V),(H),__FILE__,__LINE__,"Range check: " #V " in [" #L "," #H "]", 1)
+
+#define cl_assert_equal_file(DATA,SIZE,PATH) \
+ clar__assert_equal_file(DATA,SIZE,0,PATH,__FILE__,(int)__LINE__)
+
+#define cl_assert_equal_file_ignore_cr(DATA,SIZE,PATH) \
+ clar__assert_equal_file(DATA,SIZE,1,PATH,__FILE__,(int)__LINE__)
+
+void clar__assert_equal_file(
+ const char *expected_data,
+ size_t expected_size,
+ int ignore_cr,
+ const char *path,
+ const char *file,
+ int line);
+
+/*
+ * Some utility macros for building long strings
+ */
+#define REP4(STR) STR STR STR STR
+#define REP15(STR) REP4(STR) REP4(STR) REP4(STR) STR STR STR
+#define REP16(STR) REP4(REP4(STR))
+#define REP256(STR) REP16(REP16(STR))
+#define REP1024(STR) REP4(REP256(STR))
+
+/* Write the contents of a buffer to disk */
+void cl_git_mkfile(const char *filename, const char *content);
+void cl_git_append2file(const char *filename, const char *new_content);
+void cl_git_rewritefile(const char *filename, const char *new_content);
+void cl_git_write2file(const char *path, const char *data,
+ size_t datalen, int flags, unsigned int mode);
+
+bool cl_toggle_filemode(const char *filename);
+bool cl_is_chmod_supported(void);
+
+/* Environment wrappers */
+char *cl_getenv(const char *name);
+int cl_setenv(const char *name, const char *value);
+
+/* Reliable rename */
+int cl_rename(const char *source, const char *dest);
+
+/* Git sandbox setup helpers */
+
+git_repository *cl_git_sandbox_init(const char *sandbox);
+void cl_git_sandbox_cleanup(void);
+git_repository *cl_git_sandbox_reopen(void);
+
+/* Local-repo url helpers */
+const char* cl_git_fixture_url(const char *fixturename);
+const char* cl_git_path_url(const char *path);
+
+/* Test repository cleaner */
+int cl_git_remove_placeholders(const char *directory_path, const char *filename);
+
+/* commit creation helpers */
+void cl_repo_commit_from_index(
+ git_oid *out,
+ git_repository *repo,
+ git_signature *sig,
+ git_time_t time,
+ const char *msg);
+
+/* config setting helpers */
+void cl_repo_set_bool(git_repository *repo, const char *cfg, int value);
+int cl_repo_get_bool(git_repository *repo, const char *cfg);
+
+#endif
diff --git a/tests/clone/empty.c b/tests/clone/empty.c
new file mode 100644
index 000000000..6d19244cc
--- /dev/null
+++ b/tests/clone/empty.c
@@ -0,0 +1,85 @@
+#include "clar_libgit2.h"
+
+#include "git2/clone.h"
+#include "repository.h"
+
+static git_clone_options g_options;
+static git_repository *g_repo;
+static git_repository *g_repo_cloned;
+
+void test_clone_empty__initialize(void)
+{
+ git_repository *sandbox = cl_git_sandbox_init("empty_bare.git");
+ git_remote_callbacks dummy_callbacks = GIT_REMOTE_CALLBACKS_INIT;
+ cl_git_remove_placeholders(git_repository_path(sandbox), "dummy-marker.txt");
+
+ g_repo = NULL;
+
+ memset(&g_options, 0, sizeof(git_clone_options));
+ g_options.version = GIT_CLONE_OPTIONS_VERSION;
+ g_options.remote_callbacks = dummy_callbacks;
+}
+
+void test_clone_empty__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static void cleanup_repository(void *path)
+{
+ cl_fixture_cleanup((const char *)path);
+
+ git_repository_free(g_repo_cloned);
+ g_repo_cloned = NULL;
+}
+
+void test_clone_empty__can_clone_an_empty_local_repo_barely(void)
+{
+ char *local_name = "refs/heads/master";
+ const char *expected_tracked_branch_name = "refs/remotes/origin/master";
+ const char *expected_remote_name = "origin";
+ char buffer[1024];
+ git_reference *ref;
+
+ cl_set_cleanup(&cleanup_repository, "./empty");
+
+ g_options.bare = true;
+ cl_git_pass(git_clone(&g_repo_cloned, "./empty_bare.git", "./empty", &g_options));
+
+ /* Although the HEAD is unborn... */
+ cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&ref, g_repo_cloned, local_name));
+
+ /* ...one can still retrieve the name of the remote tracking reference */
+ cl_assert_equal_i((int)strlen(expected_tracked_branch_name) + 1,
+ git_branch_upstream_name(buffer, 1024, g_repo_cloned, local_name));
+
+ cl_assert_equal_s(expected_tracked_branch_name, buffer);
+
+ /* ...and the name of the remote... */
+ cl_assert_equal_i((int)strlen(expected_remote_name) + 1,
+ git_branch_remote_name(buffer, 1024, g_repo_cloned, expected_tracked_branch_name));
+
+ cl_assert_equal_s(expected_remote_name, buffer);
+
+ /* ...even when the remote HEAD is unborn as well */
+ cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&ref, g_repo_cloned,
+ expected_tracked_branch_name));
+}
+
+void test_clone_empty__can_clone_an_empty_local_repo(void)
+{
+ cl_set_cleanup(&cleanup_repository, "./empty");
+
+ cl_git_pass(git_clone(&g_repo_cloned, "./empty_bare.git", "./empty", &g_options));
+}
+
+void test_clone_empty__can_clone_an_empty_standard_repo(void)
+{
+ cl_git_sandbox_cleanup();
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+ cl_git_remove_placeholders(git_repository_path(g_repo), "dummy-marker.txt");
+
+ cl_set_cleanup(&cleanup_repository, "./empty");
+
+ cl_git_pass(git_clone(&g_repo_cloned, "./empty_standard_repo", "./empty", &g_options));
+}
diff --git a/tests/clone/nonetwork.c b/tests/clone/nonetwork.c
new file mode 100644
index 000000000..a286e2a8f
--- /dev/null
+++ b/tests/clone/nonetwork.c
@@ -0,0 +1,179 @@
+#include "clar_libgit2.h"
+
+#include "git2/clone.h"
+#include "remote.h"
+#include "fileops.h"
+#include "repository.h"
+
+#define LIVE_REPO_URL "git://github.com/libgit2/TestGitRepository"
+
+static git_clone_options g_options;
+static git_repository *g_repo;
+static git_reference* g_ref;
+static git_remote* g_remote;
+
+void test_clone_nonetwork__initialize(void)
+{
+ git_checkout_opts dummy_opts = GIT_CHECKOUT_OPTS_INIT;
+ git_remote_callbacks dummy_callbacks = GIT_REMOTE_CALLBACKS_INIT;
+
+ g_repo = NULL;
+
+ memset(&g_options, 0, sizeof(git_clone_options));
+ g_options.version = GIT_CLONE_OPTIONS_VERSION;
+ g_options.checkout_opts = dummy_opts;
+ g_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+ g_options.remote_callbacks = dummy_callbacks;
+}
+
+void test_clone_nonetwork__cleanup(void)
+{
+ if (g_repo) {
+ git_repository_free(g_repo);
+ g_repo = NULL;
+ }
+
+ if (g_ref) {
+ git_reference_free(g_ref);
+ g_ref = NULL;
+ }
+
+ if (g_remote) {
+ git_remote_free(g_remote);
+ g_remote = NULL;
+ }
+
+ cl_fixture_cleanup("./foo");
+}
+
+void test_clone_nonetwork__bad_urls(void)
+{
+ /* Clone should clean up the mess if the URL isn't a git repository */
+ cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
+ cl_assert(!git_path_exists("./foo"));
+ g_options.bare = true;
+ cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
+ cl_assert(!git_path_exists("./foo"));
+
+ cl_git_fail(git_clone(&g_repo, "git://example.com:asdf", "./foo", &g_options));
+ cl_git_fail(git_clone(&g_repo, "https://example.com:asdf/foo", "./foo", &g_options));
+ cl_git_fail(git_clone(&g_repo, "git://github.com/git://github.com/foo/bar.git.git",
+ "./foo", &g_options));
+ cl_git_fail(git_clone(&g_repo, "arrbee:my/bad:password@github.com:1111/strange:words.git",
+ "./foo", &g_options));
+}
+
+void test_clone_nonetwork__do_not_clean_existing_directory(void)
+{
+ /* Clone should not remove the directory if it already exists, but
+ * Should clean up entries it creates. */
+ p_mkdir("./foo", GIT_DIR_MODE);
+ cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
+ cl_assert(git_path_is_empty_dir("./foo"));
+
+ /* Try again with a bare repository. */
+ g_options.bare = true;
+ cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
+ cl_assert(git_path_is_empty_dir("./foo"));
+}
+
+void test_clone_nonetwork__local(void)
+{
+ cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
+}
+
+void test_clone_nonetwork__local_absolute_path(void)
+{
+ const char *local_src;
+ local_src = cl_fixture("testrepo.git");
+ cl_git_pass(git_clone(&g_repo, local_src, "./foo", &g_options));
+}
+
+void test_clone_nonetwork__local_bare(void)
+{
+ g_options.bare = true;
+ cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
+}
+
+void test_clone_nonetwork__fail_when_the_target_is_a_file(void)
+{
+ cl_git_mkfile("./foo", "Bar!");
+ cl_git_fail(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
+}
+
+void test_clone_nonetwork__fail_with_already_existing_but_non_empty_directory(void)
+{
+ p_mkdir("./foo", GIT_DIR_MODE);
+ cl_git_mkfile("./foo/bar", "Baz!");
+ cl_git_fail(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
+}
+
+void test_clone_nonetwork__custom_origin_name(void)
+{
+ g_options.remote_name = "my_origin";
+ cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
+
+ cl_git_pass(git_remote_load(&g_remote, g_repo, "my_origin"));
+}
+
+void test_clone_nonetwork__defaults(void)
+{
+ cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", NULL));
+ cl_assert(g_repo);
+ cl_git_pass(git_remote_load(&g_remote, g_repo, "origin"));
+}
+
+void test_clone_nonetwork__cope_with_already_existing_directory(void)
+{
+ p_mkdir("./foo", GIT_DIR_MODE);
+ cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
+}
+
+void test_clone_nonetwork__can_prevent_the_checkout_of_a_standard_repo(void)
+{
+ git_buf path = GIT_BUF_INIT;
+
+ g_options.checkout_opts.checkout_strategy = 0;
+ cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
+
+ cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "master.txt"));
+ cl_assert_equal_i(false, git_path_isfile(git_buf_cstr(&path)));
+
+ git_buf_free(&path);
+}
+
+void test_clone_nonetwork__can_checkout_given_branch(void)
+{
+ g_options.checkout_branch = "test";
+ cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
+
+ cl_assert_equal_i(0, git_repository_head_unborn(g_repo));
+
+ cl_git_pass(git_repository_head(&g_ref, g_repo));
+ cl_assert_equal_s(git_reference_name(g_ref), "refs/heads/test");
+}
+
+void test_clone_nonetwork__can_detached_head(void)
+{
+ git_object *obj;
+ git_repository *cloned;
+ git_reference *cloned_head;
+
+ cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
+
+ cl_git_pass(git_revparse_single(&obj, g_repo, "master~1"));
+ cl_git_pass(git_repository_set_head_detached(g_repo, git_object_id(obj)));
+
+ cl_git_pass(git_clone(&cloned, "./foo", "./foo1", &g_options));
+
+ cl_assert(git_repository_head_detached(cloned));
+
+ cl_git_pass(git_repository_head(&cloned_head, cloned));
+ cl_assert(!git_oid_cmp(git_object_id(obj), git_reference_target(cloned_head)));
+
+ git_object_free(obj);
+ git_reference_free(cloned_head);
+ git_repository_free(cloned);
+
+ cl_fixture_cleanup("./foo1");
+}
diff --git a/tests-clar/commit/commit.c b/tests/commit/commit.c
index 8f071ff94..8f071ff94 100644
--- a/tests-clar/commit/commit.c
+++ b/tests/commit/commit.c
diff --git a/tests-clar/commit/parent.c b/tests/commit/parent.c
index 18ce0bba6..18ce0bba6 100644
--- a/tests-clar/commit/parent.c
+++ b/tests/commit/parent.c
diff --git a/tests/commit/parse.c b/tests/commit/parse.c
new file mode 100644
index 000000000..41e162440
--- /dev/null
+++ b/tests/commit/parse.c
@@ -0,0 +1,413 @@
+#include "clar_libgit2.h"
+#include <git2/types.h>
+#include "commit.h"
+#include "signature.h"
+
+// Fixture setup
+static git_repository *g_repo;
+void test_commit_parse__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+void test_commit_parse__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+
+// Header parsing
+typedef struct {
+ const char *line;
+ const char *header;
+} parse_test_case;
+
+static parse_test_case passing_header_cases[] = {
+ { "parent 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "parent " },
+ { "tree 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree " },
+ { "random_heading 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "random_heading " },
+ { "stuck_heading05452d6349abcd67aa396dfb28660d765d8b2a36\n", "stuck_heading" },
+ { "tree 5F4BEFFC0759261D015AA63A3A85613FF2F235DE\n", "tree " },
+ { "tree 1A669B8AB81B5EB7D9DB69562D34952A38A9B504\n", "tree " },
+ { "tree 5B20DCC6110FCC75D31C6CEDEBD7F43ECA65B503\n", "tree " },
+ { "tree 173E7BF00EA5C33447E99E6C1255954A13026BE4\n", "tree " },
+ { NULL, NULL }
+};
+
+static parse_test_case failing_header_cases[] = {
+ { "parent 05452d6349abcd67aa396dfb28660d765d8b2a36", "parent " },
+ { "05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree " },
+ { "parent05452d6349abcd67aa396dfb28660d765d8b2a6a\n", "parent " },
+ { "parent 05452d6349abcd67aa396dfb280d765d8b2a6\n", "parent " },
+ { "tree 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree " },
+ { "parent 0545xd6349abcd67aa396dfb28660d765d8b2a36\n", "parent " },
+ { "parent 0545xd6349abcd67aa396dfb28660d765d8b2a36FF\n", "parent " },
+ { "", "tree " },
+ { "", "" },
+ { NULL, NULL }
+};
+
+void test_commit_parse__header(void)
+{
+ git_oid oid;
+
+ parse_test_case *testcase;
+ for (testcase = passing_header_cases; testcase->line != NULL; testcase++)
+ {
+ const char *line = testcase->line;
+ const char *line_end = line + strlen(line);
+
+ cl_git_pass(git_oid__parse(&oid, &line, line_end, testcase->header));
+ cl_assert(line == line_end);
+ }
+
+ for (testcase = failing_header_cases; testcase->line != NULL; testcase++)
+ {
+ const char *line = testcase->line;
+ const char *line_end = line + strlen(line);
+
+ cl_git_fail(git_oid__parse(&oid, &line, line_end, testcase->header));
+ }
+}
+
+
+// Signature parsing
+typedef struct {
+ const char *string;
+ const char *header;
+ const char *name;
+ const char *email;
+ git_time_t time;
+ int offset;
+} passing_signature_test_case;
+
+passing_signature_test_case passing_signature_cases[] = {
+ {"author Vicent Marti <tanoku@gmail.com> 12345 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 12345, 0},
+ {"author Vicent Marti <> 12345 \n", "author ", "Vicent Marti", "", 12345, 0},
+ {"author Vicent Marti <tanoku@gmail.com> 231301 +1020\n", "author ", "Vicent Marti", "tanoku@gmail.com", 231301, 620},
+ {"author Vicent Marti with an outrageously long name which will probably overflow the buffer <tanoku@gmail.com> 12345 \n", "author ", "Vicent Marti with an outrageously long name which will probably overflow the buffer", "tanoku@gmail.com", 12345, 0},
+ {"author Vicent Marti <tanokuwithaveryveryverylongemailwhichwillprobablyvoverflowtheemailbuffer@gmail.com> 12345 \n", "author ", "Vicent Marti", "tanokuwithaveryveryverylongemailwhichwillprobablyvoverflowtheemailbuffer@gmail.com", 12345, 0},
+ {"committer Vicent Marti <tanoku@gmail.com> 123456 +0000 \n", "committer ", "Vicent Marti", "tanoku@gmail.com", 123456, 0},
+ {"committer Vicent Marti <tanoku@gmail.com> 123456 +0100 \n", "committer ", "Vicent Marti", "tanoku@gmail.com", 123456, 60},
+ {"committer Vicent Marti <tanoku@gmail.com> 123456 -0100 \n", "committer ", "Vicent Marti", "tanoku@gmail.com", 123456, -60},
+ // Parse a signature without an author field
+ {"committer <tanoku@gmail.com> 123456 -0100 \n", "committer ", "", "tanoku@gmail.com", 123456, -60},
+ // Parse a signature without an author field
+ {"committer <tanoku@gmail.com> 123456 -0100 \n", "committer ", "", "tanoku@gmail.com", 123456, -60},
+ // Parse a signature with an empty author field
+ {"committer <tanoku@gmail.com> 123456 -0100 \n", "committer ", "", "tanoku@gmail.com", 123456, -60},
+ // Parse a signature with an empty email field
+ {"committer Vicent Marti <> 123456 -0100 \n", "committer ", "Vicent Marti", "", 123456, -60},
+ // Parse a signature with an empty email field
+ {"committer Vicent Marti < > 123456 -0100 \n", "committer ", "Vicent Marti", "", 123456, -60},
+ // Parse a signature with empty name and email
+ {"committer <> 123456 -0100 \n", "committer ", "", "", 123456, -60},
+ // Parse a signature with empty name and email
+ {"committer <> 123456 -0100 \n", "committer ", "", "", 123456, -60},
+ // Parse a signature with empty name and email
+ {"committer < > 123456 -0100 \n", "committer ", "", "", 123456, -60},
+ // Parse an obviously invalid signature
+ {"committer foo<@bar> 123456 -0100 \n", "committer ", "foo", "@bar", 123456, -60},
+ // Parse an obviously invalid signature
+ {"committer foo<@bar> 123456 -0100 \n", "committer ", "foo", "@bar", 123456, -60},
+ // Parse an obviously invalid signature
+ {"committer <>\n", "committer ", "", "", 0, 0},
+ {"committer Vicent Marti <tanoku@gmail.com> 123456 -1500 \n", "committer ", "Vicent Marti", "tanoku@gmail.com", 123456, 0},
+ {"committer Vicent Marti <tanoku@gmail.com> 123456 +0163 \n", "committer ", "Vicent Marti", "tanoku@gmail.com", 123456, 0},
+ {"author Vicent Marti <tanoku@gmail.com>\n", "author ", "Vicent Marti", "tanoku@gmail.com", 0, 0},
+ /* a variety of dates */
+ {"author Vicent Marti <tanoku@gmail.com> 0 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 0, 0},
+ {"author Vicent Marti <tanoku@gmail.com> 1234567890 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 1234567890, 0},
+ {"author Vicent Marti <tanoku@gmail.com> 2147483647 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 0x7fffffff, 0},
+ {"author Vicent Marti <tanoku@gmail.com> 4294967295 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 0xffffffff, 0},
+ {"author Vicent Marti <tanoku@gmail.com> 4294967296 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 4294967296, 0},
+ {"author Vicent Marti <tanoku@gmail.com> 8589934592 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 8589934592, 0},
+
+ {NULL,NULL,NULL,NULL,0,0}
+};
+
+typedef struct {
+ const char *string;
+ const char *header;
+} failing_signature_test_case;
+
+failing_signature_test_case failing_signature_cases[] = {
+ {"committer Vicent Marti tanoku@gmail.com> 123456 -0100 \n", "committer "},
+ {"author Vicent Marti <tanoku@gmail.com> 12345 \n", "author "},
+ {"author Vicent Marti <tanoku@gmail.com> 12345 \n", "committer "},
+ {"author Vicent Marti 12345 \n", "author "},
+ {"author Vicent Marti <broken@email 12345 \n", "author "},
+ {"committer Vicent Marti ><\n", "committer "},
+ {"author ", "author "},
+ {NULL, NULL,}
+};
+
+void test_commit_parse__signature(void)
+{
+ passing_signature_test_case *passcase;
+ failing_signature_test_case *failcase;
+
+ for (passcase = passing_signature_cases; passcase->string != NULL; passcase++)
+ {
+ const char *str = passcase->string;
+ size_t len = strlen(passcase->string);
+ struct git_signature person = {0};
+
+ cl_git_pass(git_signature__parse(&person, &str, str + len, passcase->header, '\n'));
+ cl_assert_equal_s(passcase->name, person.name);
+ cl_assert_equal_s(passcase->email, person.email);
+ cl_assert_equal_i((int)passcase->time, (int)person.when.time);
+ cl_assert_equal_i(passcase->offset, person.when.offset);
+ git__free(person.name); git__free(person.email);
+ }
+
+ for (failcase = failing_signature_cases; failcase->string != NULL; failcase++)
+ {
+ const char *str = failcase->string;
+ size_t len = strlen(failcase->string);
+ git_signature person = {0};
+ cl_git_fail(git_signature__parse(&person, &str, str + len, failcase->header, '\n'));
+ git__free(person.name); git__free(person.email);
+ }
+}
+
+
+
+static char *failing_commit_cases[] = {
+// empty commit
+"",
+// random garbage
+"asd97sa9du902e9a0jdsuusad09as9du098709aweu8987sd\n",
+// broken endlines 1
+"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\r\n\
+parent 05452d6349abcd67aa396dfb28660d765d8b2a36\r\n\
+author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\r\n\
+committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\r\n\
+\r\n\
+a test commit with broken endlines\r\n",
+// broken endlines 2
+"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\
+parent 05452d6349abcd67aa396dfb28660d765d8b2a36\
+author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\
+committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\
+\
+another test commit with broken endlines",
+// starting endlines
+"\ntree f6c0dad3c7b3481caa9d73db21f91964894a945b\n\
+parent 05452d6349abcd67aa396dfb28660d765d8b2a36\n\
+author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+\n\
+a test commit with a starting endline\n",
+// corrupted commit 1
+"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\n\
+parent 05452d6349abcd67aa396df",
+// corrupted commit 2
+"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\n\
+parent ",
+// corrupted commit 3
+"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\n\
+parent ",
+// corrupted commit 4
+"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\n\
+par",
+};
+
+
+static char *passing_commit_cases[] = {
+// simple commit with no message
+"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
+author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+\n",
+// simple commit, no parent
+"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
+author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+\n\
+a simple commit which works\n",
+// simple commit, no parent, no newline in message
+"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
+author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+\n\
+a simple commit which works",
+// simple commit, 1 parent
+"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
+parent e90810b8df3e80c413d903f631643c716887138d\n\
+author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+\n\
+a simple commit which works\n",
+/* simple commit with GPG signature */
+"tree 6b79e22d69bf46e289df0345a14ca059dfc9bdf6\n\
+parent 34734e478d6cf50c27c9d69026d93974d052c454\n\
+author Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
+committer Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
+gpgsig -----BEGIN PGP SIGNATURE-----\n\
+ Version: GnuPG v1.4.12 (Darwin)\n\
+ \n\
+ iQIcBAABAgAGBQJQ+FMIAAoJEH+LfPdZDSs1e3EQAJMjhqjWF+WkGLHju7pTw2al\n\
+ o6IoMAhv0Z/LHlWhzBd9e7JeCnanRt12bAU7yvYp9+Z+z+dbwqLwDoFp8LVuigl8\n\
+ JGLcnwiUW3rSvhjdCp9irdb4+bhKUnKUzSdsR2CK4/hC0N2i/HOvMYX+BRsvqweq\n\
+ AsAkA6dAWh+gAfedrBUkCTGhlNYoetjdakWqlGL1TiKAefEZrtA1TpPkGn92vbLq\n\
+ SphFRUY9hVn1ZBWrT3hEpvAIcZag3rTOiRVT1X1flj8B2vGCEr3RrcwOIZikpdaW\n\
+ who/X3xh/DGbI2RbuxmmJpxxP/8dsVchRJJzBwG+yhwU/iN3MlV2c5D69tls/Dok\n\
+ 6VbyU4lm/ae0y3yR83D9dUlkycOnmmlBAHKIZ9qUts9X7mWJf0+yy2QxJVpjaTGG\n\
+ cmnQKKPeNIhGJk2ENnnnzjEve7L7YJQF6itbx5VCOcsGh3Ocb3YR7DMdWjt7f8pu\n\
+ c6j+q1rP7EpE2afUN/geSlp5i3x8aXZPDj67jImbVCE/Q1X9voCtyzGJH7MXR0N9\n\
+ ZpRF8yzveRfMH8bwAJjSOGAFF5XkcR/RNY95o+J+QcgBLdX48h+ZdNmUf6jqlu3J\n\
+ 7KmTXXQcOVpN6dD3CmRFsbjq+x6RHwa8u1iGn+oIkX908r97ckfB/kHKH7ZdXIJc\n\
+ cpxtDQQMGYFpXK/71stq\n\
+ =ozeK\n\
+ -----END PGP SIGNATURE-----\n\
+\n\
+a simple commit which works\n",
+};
+
+static int parse_commit(git_commit **out, const char *buffer)
+{
+ git_commit *commit;
+ git_odb_object fake_odb_object;
+ int error;
+
+ commit = (git_commit*)git__malloc(sizeof(git_commit));
+ memset(commit, 0x0, sizeof(git_commit));
+ commit->object.repo = g_repo;
+
+ memset(&fake_odb_object, 0x0, sizeof(git_odb_object));
+ fake_odb_object.buffer = (char *)buffer;
+ fake_odb_object.cached.size = strlen(fake_odb_object.buffer);
+
+ error = git_commit__parse(commit, &fake_odb_object);
+
+ *out = commit;
+ return error;
+}
+
+void test_commit_parse__entire_commit(void)
+{
+ const int failing_commit_count = ARRAY_SIZE(failing_commit_cases);
+ const int passing_commit_count = ARRAY_SIZE(passing_commit_cases);
+ int i;
+ git_commit *commit;
+
+ for (i = 0; i < failing_commit_count; ++i) {
+ cl_git_fail(parse_commit(&commit, failing_commit_cases[i]));
+ git_commit__free(commit);
+ }
+
+ for (i = 0; i < passing_commit_count; ++i) {
+ cl_git_pass(parse_commit(&commit, passing_commit_cases[i]));
+
+ if (!i)
+ cl_assert_equal_s("", git_commit_message(commit));
+ else
+ cl_assert(git__prefixcmp(
+ git_commit_message(commit), "a simple commit which works") == 0);
+
+ git_commit__free(commit);
+ }
+}
+
+
+// query the details on a parsed commit
+void test_commit_parse__details0(void) {
+ static const char *commit_ids[] = {
+ "a4a7dce85cf63874e984719f4fdd239f5145052f", /* 0 */
+ "9fd738e8f7967c078dceed8190330fc8648ee56a", /* 1 */
+ "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", /* 2 */
+ "c47800c7266a2be04c571c04d5a6614691ea99bd", /* 3 */
+ "8496071c1b46c854b31185ea97743be6a8774479", /* 4 */
+ "5b5b025afb0b4c913b4c338a42934a3863bf3644", /* 5 */
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", /* 6 */
+ };
+ const size_t commit_count = sizeof(commit_ids) / sizeof(const char *);
+ unsigned int i;
+
+ for (i = 0; i < commit_count; ++i) {
+ git_oid id;
+ git_commit *commit;
+
+ const git_signature *author, *committer;
+ const char *message;
+ git_time_t commit_time;
+ unsigned int parents, p;
+ git_commit *parent = NULL, *old_parent = NULL;
+
+ git_oid_fromstr(&id, commit_ids[i]);
+
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &id));
+
+ message = git_commit_message(commit);
+ author = git_commit_author(commit);
+ committer = git_commit_committer(commit);
+ commit_time = git_commit_time(commit);
+ parents = git_commit_parentcount(commit);
+
+ cl_assert_equal_s("Scott Chacon", author->name);
+ cl_assert_equal_s("schacon@gmail.com", author->email);
+ cl_assert_equal_s("Scott Chacon", committer->name);
+ cl_assert_equal_s("schacon@gmail.com", committer->email);
+ cl_assert(message != NULL);
+ cl_assert(commit_time > 0);
+ cl_assert(parents <= 2);
+ for (p = 0;p < parents;p++) {
+ if (old_parent != NULL)
+ git_commit_free(old_parent);
+
+ old_parent = parent;
+ cl_git_pass(git_commit_parent(&parent, commit, p));
+ cl_assert(parent != NULL);
+ cl_assert(git_commit_author(parent) != NULL); // is it really a commit?
+ }
+ git_commit_free(old_parent);
+ git_commit_free(parent);
+
+ cl_git_fail(git_commit_parent(&parent, commit, parents));
+ git_commit_free(commit);
+ }
+}
+
+void test_commit_parse__leading_lf(void)
+{
+ git_commit *commit;
+ const char *buffer =
+"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
+parent e90810b8df3e80c413d903f631643c716887138d\n\
+author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+\n\
+\n\
+\n\
+This commit has a few LF at the start of the commit message";
+ const char *message =
+"This commit has a few LF at the start of the commit message";
+ const char *raw_message =
+"\n\
+\n\
+This commit has a few LF at the start of the commit message";
+ cl_git_pass(parse_commit(&commit, buffer));
+ cl_assert_equal_s(message, git_commit_message(commit));
+ cl_assert_equal_s(raw_message, git_commit_message_raw(commit));
+ git_commit__free(commit);
+}
+
+void test_commit_parse__only_lf(void)
+{
+ git_commit *commit;
+ const char *buffer =
+"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
+parent e90810b8df3e80c413d903f631643c716887138d\n\
+author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+\n\
+\n\
+\n";
+ const char *message = "";
+ const char *raw_message = "\n\n";
+
+ cl_git_pass(parse_commit(&commit, buffer));
+ cl_assert_equal_s(message, git_commit_message(commit));
+ cl_assert_equal_s(raw_message, git_commit_message_raw(commit));
+ git_commit__free(commit);
+}
diff --git a/tests-clar/commit/signature.c b/tests/commit/signature.c
index e9dcfab41..e9dcfab41 100644
--- a/tests-clar/commit/signature.c
+++ b/tests/commit/signature.c
diff --git a/tests-clar/commit/write.c b/tests/commit/write.c
index 73436b74b..73436b74b 100644
--- a/tests-clar/commit/write.c
+++ b/tests/commit/write.c
diff --git a/tests-clar/config/add.c b/tests/config/add.c
index 405f1e2c9..405f1e2c9 100644
--- a/tests-clar/config/add.c
+++ b/tests/config/add.c
diff --git a/tests-clar/config/backend.c b/tests/config/backend.c
index 3fd6eb114..3fd6eb114 100644
--- a/tests-clar/config/backend.c
+++ b/tests/config/backend.c
diff --git a/tests-clar/config/config_helpers.c b/tests/config/config_helpers.c
index 53bd945a0..53bd945a0 100644
--- a/tests-clar/config/config_helpers.c
+++ b/tests/config/config_helpers.c
diff --git a/tests-clar/config/config_helpers.h b/tests/config/config_helpers.h
index b887b3d38..b887b3d38 100644
--- a/tests-clar/config/config_helpers.h
+++ b/tests/config/config_helpers.h
diff --git a/tests-clar/config/configlevel.c b/tests/config/configlevel.c
index 1c22e8d9f..1c22e8d9f 100644
--- a/tests-clar/config/configlevel.c
+++ b/tests/config/configlevel.c
diff --git a/tests/config/global.c b/tests/config/global.c
new file mode 100644
index 000000000..d5f95f504
--- /dev/null
+++ b/tests/config/global.c
@@ -0,0 +1,72 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "fileops.h"
+
+void test_config_global__initialize(void)
+{
+ git_buf path = GIT_BUF_INIT;
+
+ cl_assert_equal_i(0, p_mkdir("home", 0777));
+ cl_git_pass(git_path_prettify(&path, "home", NULL));
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
+
+ cl_assert_equal_i(0, p_mkdir("xdg", 0777));
+ cl_assert_equal_i(0, p_mkdir("xdg/git", 0777));
+ cl_git_pass(git_path_prettify(&path, "xdg/git", NULL));
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr));
+
+ cl_assert_equal_i(0, p_mkdir("etc", 0777));
+ cl_git_pass(git_path_prettify(&path, "etc", NULL));
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr));
+
+ git_buf_free(&path);
+}
+
+void test_config_global__cleanup(void)
+{
+ cl_git_pass(git_futils_rmdir_r("home", NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_git_pass(git_futils_rmdir_r("xdg", NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_git_pass(git_futils_rmdir_r("etc", NULL, GIT_RMDIR_REMOVE_FILES));
+
+ git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, NULL);
+ git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, NULL);
+ git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, NULL);
+}
+
+void test_config_global__open_global(void)
+{
+ git_config *cfg, *global, *selected, *dummy;
+
+ cl_git_pass(git_config_open_default(&cfg));
+ cl_git_pass(git_config_open_level(&global, cfg, GIT_CONFIG_LEVEL_GLOBAL));
+ cl_git_fail(git_config_open_level(&dummy, cfg, GIT_CONFIG_LEVEL_XDG));
+ cl_git_pass(git_config_open_global(&selected, cfg));
+
+ git_config_free(selected);
+ git_config_free(global);
+ git_config_free(cfg);
+}
+
+void test_config_global__open_xdg(void)
+{
+ git_config *cfg, *xdg, *selected;
+ const char *val, *str = "teststring";
+ const char *key = "this.variable";
+
+ cl_git_mkfile("xdg/git/config", "# XDG config\n[core]\n test = 1\n");
+
+ cl_git_pass(git_config_open_default(&cfg));
+ cl_git_pass(git_config_open_level(&xdg, cfg, GIT_CONFIG_LEVEL_XDG));
+ cl_git_pass(git_config_open_global(&selected, cfg));
+
+ cl_git_pass(git_config_set_string(xdg, key, str));
+ cl_git_pass(git_config_get_string(&val, selected, key));
+ cl_assert_equal_s(str, val);
+
+ git_config_free(selected);
+ git_config_free(xdg);
+ git_config_free(cfg);
+}
diff --git a/tests/config/include.c b/tests/config/include.c
new file mode 100644
index 000000000..535573808
--- /dev/null
+++ b/tests/config/include.c
@@ -0,0 +1,109 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "fileops.h"
+
+void test_config_include__relative(void)
+{
+ git_config *cfg;
+ const char *str;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config-include")));
+
+ cl_git_pass(git_config_get_string(&str, cfg, "foo.bar.baz"));
+ cl_assert_equal_s(str, "huzzah");
+
+ git_config_free(cfg);
+}
+
+void test_config_include__absolute(void)
+{
+ git_config *cfg;
+ const char *str;
+ 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));
+ git_buf_free(&buf);
+ cl_git_pass(git_config_open_ondisk(&cfg, "config-include-absolute"));
+
+ cl_git_pass(git_config_get_string(&str, cfg, "foo.bar.baz"));
+ cl_assert_equal_s(str, "huzzah");
+
+ git_config_free(cfg);
+}
+
+void test_config_include__homedir(void)
+{
+ git_config *cfg;
+ const char *str;
+
+ 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");
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config-include-homedir"));
+
+ cl_git_pass(git_config_get_string(&str, cfg, "foo.bar.baz"));
+ cl_assert_equal_s(str, "huzzah");
+
+ git_config_free(cfg);
+}
+
+void test_config_include__refresh(void)
+{
+ git_config *cfg;
+ const char *str;
+
+ cl_fixture_sandbox("config");
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config-include"));
+
+ cl_git_pass(git_config_get_string(&str, cfg, "foo.bar.baz"));
+ cl_assert_equal_s(str, "huzzah");
+
+ /* Change the included file and see if we refresh */
+ cl_git_mkfile("config/config-included", "[foo \"bar\"]\nbaz = hurrah");
+ cl_git_pass(git_config_refresh(cfg));
+
+ cl_git_pass(git_config_get_string(&str, cfg, "foo.bar.baz"));
+ cl_assert_equal_s(str, "hurrah");
+
+ git_config_free(cfg);
+ cl_fixture_cleanup("config");
+}
+
+/* We need to pretend that the variables were defined where the file was included */
+void test_config_include__ordering(void)
+{
+ git_config *cfg;
+ const char *str;
+
+ cl_git_mkfile("included", "[foo \"bar\"]\nbaz = hurrah\nfrotz = hiya");
+ cl_git_mkfile("including",
+ "[foo \"bar\"]\nfrotz = hello\n"
+ "[include]\npath = included\n"
+ "[foo \"bar\"]\nbaz = huzzah\n");
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "including"));
+
+ cl_git_pass(git_config_get_string(&str, cfg, "foo.bar.frotz"));
+ cl_assert_equal_s(str, "hiya");
+ cl_git_pass(git_config_get_string(&str, cfg, "foo.bar.baz"));
+ cl_assert_equal_s(str, "huzzah");
+
+ 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");
+
+ cl_git_fail(git_config_open_ondisk(&cfg, "a"));
+
+ unlink("a");
+ unlink("b");
+}
diff --git a/tests/config/multivar.c b/tests/config/multivar.c
new file mode 100644
index 000000000..afdb1e5f4
--- /dev/null
+++ b/tests/config/multivar.c
@@ -0,0 +1,288 @@
+#include "clar_libgit2.h"
+
+static const char *_name = "remote.ab.url";
+
+void test_config_multivar__initialize(void)
+{
+ cl_fixture_sandbox("config");
+}
+
+void test_config_multivar__cleanup(void)
+{
+ cl_fixture_cleanup("config");
+}
+
+static int mv_read_cb(const git_config_entry *entry, void *data)
+{
+ int *n = (int *) data;
+
+ if (!strcmp(entry->name, _name))
+ (*n)++;
+
+ return 0;
+}
+
+void test_config_multivar__foreach(void)
+{
+ git_config *cfg;
+ int n = 0;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config11")));
+
+ cl_git_pass(git_config_foreach(cfg, mv_read_cb, &n));
+ cl_assert(n == 2);
+
+ git_config_free(cfg);
+}
+
+static int cb(const git_config_entry *entry, void *data)
+{
+ int *n = (int *) data;
+
+ GIT_UNUSED(entry);
+
+ (*n)++;
+
+ return 0;
+}
+
+static void check_get_multivar_foreach(
+ git_config *cfg, int expected, int expected_patterned)
+{
+ int n = 0;
+
+ if (expected > 0) {
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+ cl_assert_equal_i(expected, n);
+ } else {
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+ }
+
+ n = 0;
+
+ if (expected_patterned > 0) {
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "example", cb, &n));
+ cl_assert_equal_i(expected_patterned, n);
+ } else {
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_config_get_multivar_foreach(cfg, _name, "example", cb, &n));
+ }
+}
+
+static void check_get_multivar(git_config *cfg, int expected)
+{
+ git_config_iterator *iter;
+ git_config_entry *entry;
+ int n = 0;
+
+ cl_git_pass(git_config_multivar_iterator_new(&iter, cfg, _name, NULL));
+
+ while (git_config_next(&entry, iter) == 0)
+ n++;
+
+ cl_assert_equal_i(expected, n);
+ git_config_iterator_free(iter);
+
+}
+
+void test_config_multivar__get(void)
+{
+ git_config *cfg;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+ check_get_multivar_foreach(cfg, 2, 1);
+
+ /* add another that has the _name entry */
+ cl_git_pass(git_config_add_file_ondisk(cfg, "config/config9", GIT_CONFIG_LEVEL_SYSTEM, 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));
+ 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));
+ 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));
+ 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));
+ 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));
+ check_get_multivar_foreach(cfg, 2, 1);
+
+ check_get_multivar(cfg, 2);
+
+ git_config_free(cfg);
+}
+
+void test_config_multivar__add(void)
+{
+ git_config *cfg;
+ int n;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+ cl_git_pass(git_config_set_multivar(cfg, _name, "nonexistant", "git://git.otherplace.org/libgit2"));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+ cl_assert_equal_i(n, 3);
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n));
+ cl_assert_equal_i(n, 1);
+
+ git_config_free(cfg);
+
+ /* We know it works in memory, let's see if the file is written correctly */
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+ cl_assert_equal_i(n, 3);
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n));
+ cl_assert_equal_i(n, 1);
+
+ git_config_free(cfg);
+}
+
+void test_config_multivar__add_new(void)
+{
+ const char *var = "a.brand.new";
+ git_config *cfg;
+ int n;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+ cl_git_pass(git_config_set_multivar(cfg, var, "", "variable"));
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(cfg, var, NULL, cb, &n));
+ cl_assert_equal_i(n, 1);
+
+ git_config_free(cfg);
+}
+
+void test_config_multivar__replace(void)
+{
+ git_config *cfg;
+ int n;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+ cl_assert(n == 2);
+
+ cl_git_pass(git_config_set_multivar(cfg, _name, "github", "git://git.otherplace.org/libgit2"));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+ cl_assert(n == 2);
+
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+ cl_assert(n == 2);
+
+ git_config_free(cfg);
+}
+
+void test_config_multivar__replace_multiple(void)
+{
+ git_config *cfg;
+ int n;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+ cl_git_pass(git_config_set_multivar(cfg, _name, "git://", "git://git.otherplace.org/libgit2"));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n));
+ cl_assert_equal_i(n, 2);
+
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n));
+ cl_assert_equal_i(n, 2);
+
+ git_config_free(cfg);
+}
+
+void test_config_multivar__delete(void)
+{
+ git_config *cfg;
+ int n;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+ cl_assert(n == 2);
+
+ cl_git_pass(git_config_delete_multivar(cfg, _name, "github"));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+ cl_assert(n == 1);
+
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+ cl_assert(n == 1);
+
+ git_config_free(cfg);
+}
+
+void test_config_multivar__delete_multiple(void)
+{
+ git_config *cfg;
+ int n;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+ cl_assert(n == 2);
+
+ cl_git_pass(git_config_delete_multivar(cfg, _name, "git"));
+
+ n = 0;
+ cl_git_fail_with(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n), GIT_ENOTFOUND);
+
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+ n = 0;
+ cl_git_fail_with(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n), GIT_ENOTFOUND);
+
+ git_config_free(cfg);
+}
+
+void test_config_multivar__delete_notfound(void)
+{
+ git_config *cfg;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+ cl_git_fail_with(git_config_delete_multivar(cfg, "remote.ab.noturl", "git"), GIT_ENOTFOUND);
+
+ git_config_free(cfg);
+}
diff --git a/tests-clar/config/new.c b/tests/config/new.c
index dd6dbca9e..dd6dbca9e 100644
--- a/tests-clar/config/new.c
+++ b/tests/config/new.c
diff --git a/tests/config/read.c b/tests/config/read.c
new file mode 100644
index 000000000..abc088d59
--- /dev/null
+++ b/tests/config/read.c
@@ -0,0 +1,569 @@
+#include "clar_libgit2.h"
+
+void test_config_read__simple_read(void)
+{
+ git_config *cfg;
+ int32_t i;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config0")));
+
+ cl_git_pass(git_config_get_int32(&i, cfg, "core.repositoryformatversion"));
+ cl_assert(i == 0);
+ cl_git_pass(git_config_get_bool(&i, cfg, "core.filemode"));
+ cl_assert(i == 1);
+ cl_git_pass(git_config_get_bool(&i, cfg, "core.bare"));
+ cl_assert(i == 0);
+ cl_git_pass(git_config_get_bool(&i, cfg, "core.logallrefupdates"));
+ cl_assert(i == 1);
+
+ git_config_free(cfg);
+}
+
+void test_config_read__case_sensitive(void)
+{
+ git_config *cfg;
+ int i;
+ const char *str;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config1")));
+
+ cl_git_pass(git_config_get_string(&str, cfg, "this.that.other"));
+ cl_assert_equal_s(str, "true");
+ cl_git_pass(git_config_get_string(&str, cfg, "this.That.other"));
+ cl_assert_equal_s(str, "yes");
+
+ cl_git_pass(git_config_get_bool(&i, cfg, "this.that.other"));
+ cl_assert(i == 1);
+ cl_git_pass(git_config_get_bool(&i, cfg, "this.That.other"));
+ cl_assert(i == 1);
+
+ /* This one doesn't exist */
+ cl_must_fail(git_config_get_bool(&i, cfg, "this.thaT.other"));
+
+ git_config_free(cfg);
+}
+
+/*
+ * If \ is the last non-space character on the line, we read the next
+ * one, separating each line with SP.
+ */
+void test_config_read__multiline_value(void)
+{
+ git_config *cfg;
+ const char *str;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config2")));
+
+ cl_git_pass(git_config_get_string(&str, cfg, "this.That.and"));
+ cl_assert_equal_s(str, "one one one two two three three");
+
+ git_config_free(cfg);
+}
+
+/*
+ * This kind of subsection declaration is case-insensitive
+ */
+void test_config_read__subsection_header(void)
+{
+ git_config *cfg;
+ const char *str;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config3")));
+
+ cl_git_pass(git_config_get_string(&str, cfg, "section.subsection.var"));
+ cl_assert_equal_s(str, "hello");
+
+ /* The subsection is transformed to lower-case */
+ cl_must_fail(git_config_get_string(&str, cfg, "section.subSectIon.var"));
+
+ git_config_free(cfg);
+}
+
+void test_config_read__lone_variable(void)
+{
+ git_config *cfg;
+ const char *str;
+ int i;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config4")));
+
+ cl_git_fail(git_config_get_int32(&i, cfg, "some.section.variable"));
+
+ cl_git_pass(git_config_get_string(&str, cfg, "some.section.variable"));
+ cl_assert_equal_s(str, "");
+
+ cl_git_pass(git_config_get_bool(&i, cfg, "some.section.variable"));
+ cl_assert(i == 1);
+
+ cl_git_pass(git_config_get_string(&str, cfg, "some.section.variableeq"));
+ cl_assert_equal_s(str, "");
+
+ cl_git_pass(git_config_get_bool(&i, cfg, "some.section.variableeq"));
+ cl_assert(i == 0);
+
+ git_config_free(cfg);
+}
+
+void test_config_read__number_suffixes(void)
+{
+ git_config *cfg;
+ int64_t i;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config5")));
+
+ cl_git_pass(git_config_get_int64(&i, cfg, "number.simple"));
+ cl_assert(i == 1);
+
+ cl_git_pass(git_config_get_int64(&i, cfg, "number.k"));
+ cl_assert(i == 1 * 1024);
+
+ cl_git_pass(git_config_get_int64(&i, cfg, "number.kk"));
+ cl_assert(i == 1 * 1024);
+
+ cl_git_pass(git_config_get_int64(&i, cfg, "number.m"));
+ cl_assert(i == 1 * 1024 * 1024);
+
+ cl_git_pass(git_config_get_int64(&i, cfg, "number.mm"));
+ cl_assert(i == 1 * 1024 * 1024);
+
+ cl_git_pass(git_config_get_int64(&i, cfg, "number.g"));
+ cl_assert(i == 1 * 1024 * 1024 * 1024);
+
+ cl_git_pass(git_config_get_int64(&i, cfg, "number.gg"));
+ cl_assert(i == 1 * 1024 * 1024 * 1024);
+
+ git_config_free(cfg);
+}
+
+void test_config_read__blank_lines(void)
+{
+ git_config *cfg;
+ int i;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config6")));
+
+ cl_git_pass(git_config_get_bool(&i, cfg, "valid.subsection.something"));
+ cl_assert(i == 1);
+
+ cl_git_pass(git_config_get_bool(&i, cfg, "something.else.something"));
+ cl_assert(i == 0);
+
+ git_config_free(cfg);
+}
+
+void test_config_read__invalid_ext_headers(void)
+{
+ git_config *cfg;
+ cl_must_fail(git_config_open_ondisk(&cfg, cl_fixture("config/config7")));
+}
+
+void test_config_read__empty_files(void)
+{
+ git_config *cfg;
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config8")));
+ git_config_free(cfg);
+}
+
+void test_config_read__symbol_headers(void)
+{
+ git_config *cfg;
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config20")));
+ git_config_free(cfg);
+}
+
+void test_config_read__header_in_last_line(void)
+{
+ git_config *cfg;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config10")));
+ git_config_free(cfg);
+}
+
+void test_config_read__prefixes(void)
+{
+ git_config *cfg;
+ const char *str;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config9")));
+ cl_git_pass(git_config_get_string(&str, cfg, "remote.ab.url"));
+ cl_assert_equal_s(str, "http://example.com/git/ab");
+
+ cl_git_pass(git_config_get_string(&str, cfg, "remote.abba.url"));
+ cl_assert_equal_s(str, "http://example.com/git/abba");
+
+ git_config_free(cfg);
+}
+
+void test_config_read__escaping_quotes(void)
+{
+ git_config *cfg;
+ const char *str;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config13")));
+ cl_git_pass(git_config_get_string(&str, cfg, "core.editor"));
+ cl_assert_equal_s("\"C:/Program Files/Nonsense/bah.exe\" \"--some option\"", str);
+
+ git_config_free(cfg);
+}
+
+static int count_cfg_entries_and_compare_levels(
+ const git_config_entry *entry, void *payload)
+{
+ int *count = payload;
+
+ if (!strcmp(entry->value, "7") || !strcmp(entry->value, "17"))
+ cl_assert(entry->level == GIT_CONFIG_LEVEL_GLOBAL);
+ else
+ cl_assert(entry->level == GIT_CONFIG_LEVEL_SYSTEM);
+
+ (*count)++;
+ return 0;
+}
+
+static int cfg_callback_countdown(const git_config_entry *entry, void *payload)
+{
+ int *count = payload;
+ GIT_UNUSED(entry);
+ (*count)--;
+ if (*count == 0)
+ return -100;
+ return 0;
+}
+
+void test_config_read__foreach(void)
+{
+ git_config *cfg;
+ int count, ret;
+
+ cl_git_pass(git_config_new(&cfg));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
+ GIT_CONFIG_LEVEL_SYSTEM, 0));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
+ GIT_CONFIG_LEVEL_GLOBAL, 0));
+
+ count = 0;
+ cl_git_pass(git_config_foreach(cfg, count_cfg_entries_and_compare_levels, &count));
+ cl_assert_equal_i(7, count);
+
+ count = 3;
+ cl_git_fail(ret = git_config_foreach(cfg, cfg_callback_countdown, &count));
+ cl_assert_equal_i(GIT_EUSER, ret);
+
+ git_config_free(cfg);
+}
+
+void test_config_read__iterator(void)
+{
+ git_config *cfg;
+ git_config_iterator *iter;
+ git_config_entry *entry;
+ int count, ret;
+
+ cl_git_pass(git_config_new(&cfg));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
+ GIT_CONFIG_LEVEL_SYSTEM, 0));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
+ GIT_CONFIG_LEVEL_GLOBAL, 0));
+
+ count = 0;
+ cl_git_pass(git_config_iterator_new(&iter, cfg));
+
+ while ((ret = git_config_next(&entry, iter)) == 0) {
+ count++;
+ }
+
+ git_config_iterator_free(iter);
+ cl_assert_equal_i(GIT_ITEROVER, ret);
+ cl_assert_equal_i(7, count);
+
+ count = 3;
+ cl_git_pass(git_config_iterator_new(&iter, cfg));
+
+ git_config_iterator_free(iter);
+ git_config_free(cfg);
+}
+
+static int count_cfg_entries(const git_config_entry *entry, void *payload)
+{
+ int *count = payload;
+ GIT_UNUSED(entry);
+ (*count)++;
+ return 0;
+}
+
+void test_config_read__foreach_match(void)
+{
+ git_config *cfg;
+ int count;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config9")));
+
+ count = 0;
+ cl_git_pass(
+ git_config_foreach_match(cfg, "core.*", count_cfg_entries, &count));
+ cl_assert_equal_i(3, count);
+
+ count = 0;
+ cl_git_pass(
+ git_config_foreach_match(cfg, "remote\\.ab.*", count_cfg_entries, &count));
+ cl_assert_equal_i(2, count);
+
+ count = 0;
+ cl_git_pass(
+ git_config_foreach_match(cfg, ".*url$", count_cfg_entries, &count));
+ cl_assert_equal_i(2, count);
+
+ count = 0;
+ cl_git_pass(
+ git_config_foreach_match(cfg, ".*dummy.*", count_cfg_entries, &count));
+ cl_assert_equal_i(2, count);
+
+ count = 0;
+ cl_git_pass(
+ git_config_foreach_match(cfg, ".*nomatch.*", count_cfg_entries, &count));
+ cl_assert_equal_i(0, count);
+
+ git_config_free(cfg);
+}
+
+static void check_glob_iter(git_config *cfg, const char *regexp, int expected)
+{
+ git_config_iterator *iter;
+ git_config_entry *entry;
+ int count, error;
+
+ cl_git_pass(git_config_iterator_glob_new(&iter, cfg, regexp));
+
+ count = 0;
+ while ((error = git_config_next(&entry, iter)) == 0)
+ count++;
+
+ cl_assert_equal_i(GIT_ITEROVER, error);
+ cl_assert_equal_i(expected, count);
+ git_config_iterator_free(iter);
+}
+
+void test_config_read__iterator_glob(void)
+{
+ git_config *cfg;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config9")));
+
+ check_glob_iter(cfg, "core.*", 3);
+ check_glob_iter(cfg, "remote\\.ab.*", 2);
+ check_glob_iter(cfg, ".*url$", 2);
+ check_glob_iter(cfg, ".*dummy.*", 2);
+ check_glob_iter(cfg, ".*nomatch.*", 0);
+
+ git_config_free(cfg);
+}
+
+void test_config_read__whitespace_not_required_around_assignment(void)
+{
+ git_config *cfg;
+ const char *str;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config14")));
+
+ cl_git_pass(git_config_get_string(&str, cfg, "a.b"));
+ cl_assert_equal_s(str, "c");
+
+ cl_git_pass(git_config_get_string(&str, cfg, "d.e"));
+ cl_assert_equal_s(str, "f");
+
+ git_config_free(cfg);
+}
+
+void test_config_read__read_git_config_entry(void)
+{
+ git_config *cfg;
+ const git_config_entry *entry;
+
+ cl_git_pass(git_config_new(&cfg));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
+ GIT_CONFIG_LEVEL_SYSTEM, 0));
+
+ cl_git_pass(git_config_get_entry(&entry, cfg, "core.dummy2"));
+ cl_assert_equal_s("core.dummy2", entry->name);
+ cl_assert_equal_s("42", entry->value);
+ cl_assert_equal_i(GIT_CONFIG_LEVEL_SYSTEM, entry->level);
+
+ git_config_free(cfg);
+}
+
+/*
+ * At the beginning of the test:
+ * - config9 has: core.dummy2=42
+ * - config15 has: core.dummy2=7
+ * - config16 has: core.dummy2=28
+ */
+void test_config_read__local_config_overrides_global_config_overrides_system_config(void)
+{
+ git_config *cfg;
+ int32_t i;
+
+ cl_git_pass(git_config_new(&cfg));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
+ GIT_CONFIG_LEVEL_SYSTEM, 0));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
+ GIT_CONFIG_LEVEL_GLOBAL, 0));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config16"),
+ GIT_CONFIG_LEVEL_LOCAL, 0));
+
+ cl_git_pass(git_config_get_int32(&i, cfg, "core.dummy2"));
+ cl_assert_equal_i(28, i);
+
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_new(&cfg));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
+ GIT_CONFIG_LEVEL_SYSTEM, 0));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
+ GIT_CONFIG_LEVEL_GLOBAL, 0));
+
+ cl_git_pass(git_config_get_int32(&i, cfg, "core.dummy2"));
+ cl_assert_equal_i(7, i);
+
+ git_config_free(cfg);
+}
+
+/*
+ * At the beginning of the test:
+ * - config9 has: core.global does not exist
+ * - config15 has: core.global=17
+ * - config16 has: core.global=29
+ *
+ * And also:
+ * - config9 has: core.system does not exist
+ * - config15 has: core.system does not exist
+ * - config16 has: core.system=11
+ */
+void test_config_read__fallback_from_local_to_global_and_from_global_to_system(void)
+{
+ git_config *cfg;
+ int32_t i;
+
+ cl_git_pass(git_config_new(&cfg));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
+ GIT_CONFIG_LEVEL_SYSTEM, 0));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
+ GIT_CONFIG_LEVEL_GLOBAL, 0));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config16"),
+ GIT_CONFIG_LEVEL_LOCAL, 0));
+
+ cl_git_pass(git_config_get_int32(&i, cfg, "core.global"));
+ cl_assert_equal_i(17, i);
+ cl_git_pass(git_config_get_int32(&i, cfg, "core.system"));
+ cl_assert_equal_i(11, i);
+
+ git_config_free(cfg);
+}
+
+/*
+ * At the beginning of the test, config18 has:
+ * int32global = 28
+ * int64global = 9223372036854775803
+ * boolglobal = true
+ * stringglobal = I'm a global config value!
+ *
+ * And config19 has:
+ * int32global = -1
+ * int64global = -2
+ * boolglobal = false
+ * stringglobal = don't find me!
+ *
+ */
+void test_config_read__simple_read_from_specific_level(void)
+{
+ git_config *cfg, *cfg_specific;
+ int i;
+ int64_t l, expected = +9223372036854775803;
+ const char *s;
+
+ 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));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config19"),
+ GIT_CONFIG_LEVEL_SYSTEM, 0));
+
+ cl_git_pass(git_config_open_level(&cfg_specific, cfg, GIT_CONFIG_LEVEL_GLOBAL));
+
+ cl_git_pass(git_config_get_int32(&i, cfg_specific, "core.int32global"));
+ cl_assert_equal_i(28, i);
+ cl_git_pass(git_config_get_int64(&l, cfg_specific, "core.int64global"));
+ cl_assert(l == expected);
+ cl_git_pass(git_config_get_bool(&i, cfg_specific, "core.boolglobal"));
+ cl_assert_equal_b(true, i);
+ cl_git_pass(git_config_get_string(&s, cfg_specific, "core.stringglobal"));
+ cl_assert_equal_s("I'm a global config value!", s);
+
+ git_config_free(cfg_specific);
+ git_config_free(cfg);
+}
+
+static void clean_test_config(void *unused)
+{
+ GIT_UNUSED(unused);
+ cl_fixture_cleanup("./testconfig");
+}
+
+void test_config_read__can_load_and_parse_an_empty_config_file(void)
+{
+ git_config *cfg;
+ int i;
+
+ cl_set_cleanup(&clean_test_config, NULL);
+ cl_git_mkfile("./testconfig", "");
+ cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_config_get_int32(&i, cfg, "nope.neither"));
+
+ git_config_free(cfg);
+}
+
+void test_config_read__corrupt_header(void)
+{
+ git_config *cfg;
+
+ cl_set_cleanup(&clean_test_config, NULL);
+ cl_git_mkfile("./testconfig", "[sneaky ] \"quoted closing quote mark\\\"");
+ cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
+
+ git_config_free(cfg);
+}
+
+void test_config_read__corrupt_header2(void)
+{
+ git_config *cfg;
+
+ cl_set_cleanup(&clean_test_config, NULL);
+ cl_git_mkfile("./testconfig", "[unclosed \"bracket\"\n lib = git2\n");
+ cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
+
+ git_config_free(cfg);
+}
+
+void test_config_read__corrupt_header3(void)
+{
+ git_config *cfg;
+
+ cl_set_cleanup(&clean_test_config, NULL);
+ cl_git_mkfile("./testconfig", "[unclosed \"slash\\\"]\n lib = git2\n");
+ cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
+
+ git_config_free(cfg);
+}
+
+void test_config_read__override_variable(void)
+{
+ git_config *cfg;
+ const char *str;
+
+ cl_set_cleanup(&clean_test_config, NULL);
+ cl_git_mkfile("./testconfig", "[some] var = one\nvar = two");
+ cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
+
+ cl_git_pass(git_config_get_string(&str, cfg, "some.var"));
+ cl_assert_equal_s(str, "two");
+
+ git_config_free(cfg);
+}
diff --git a/tests-clar/config/refresh.c b/tests/config/refresh.c
index 99d677f0e..99d677f0e 100644
--- a/tests-clar/config/refresh.c
+++ b/tests/config/refresh.c
diff --git a/tests/config/stress.c b/tests/config/stress.c
new file mode 100644
index 000000000..eeca54ff4
--- /dev/null
+++ b/tests/config/stress.c
@@ -0,0 +1,92 @@
+#include "clar_libgit2.h"
+
+#include "filebuf.h"
+#include "fileops.h"
+#include "posix.h"
+
+#define TEST_CONFIG "git-test-config"
+
+void test_config_stress__initialize(void)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+
+ cl_git_pass(git_filebuf_open(&file, TEST_CONFIG, 0, 0666));
+
+ git_filebuf_printf(&file, "[color]\n\tui = auto\n");
+ git_filebuf_printf(&file, "[core]\n\teditor = \n");
+
+ cl_git_pass(git_filebuf_commit(&file));
+}
+
+void test_config_stress__cleanup(void)
+{
+ p_unlink(TEST_CONFIG);
+}
+
+void test_config_stress__dont_break_on_invalid_input(void)
+{
+ const char *editor, *color;
+ git_config *config;
+
+ cl_assert(git_path_exists(TEST_CONFIG));
+ cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
+
+ cl_git_pass(git_config_get_string(&color, config, "color.ui"));
+ cl_git_pass(git_config_get_string(&editor, config, "core.editor"));
+
+ git_config_free(config);
+}
+
+void test_config_stress__comments(void)
+{
+ git_config *config;
+ const char *str;
+
+ cl_git_pass(git_config_open_ondisk(&config, cl_fixture("config/config12")));
+
+ cl_git_pass(git_config_get_string(&str, config, "some.section.other"));
+ cl_assert_equal_s("hello! \" ; ; ; ", str);
+
+ cl_git_pass(git_config_get_string(&str, config, "some.section.multi"));
+ cl_assert_equal_s("hi, this is a ; multiline comment # with ;\n special chars and other stuff !@#", str);
+
+ cl_git_pass(git_config_get_string(&str, config, "some.section.back"));
+ cl_assert_equal_s("this is \ba phrase", str);
+
+ git_config_free(config);
+}
+
+void test_config_stress__escape_subsection_names(void)
+{
+ git_config *config;
+ const char *str;
+
+ cl_assert(git_path_exists("git-test-config"));
+ cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
+
+ cl_git_pass(git_config_set_string(config, "some.sec\\tion.other", "foo"));
+ git_config_free(config);
+
+ cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
+
+ cl_git_pass(git_config_get_string(&str, config, "some.sec\\tion.other"));
+ cl_assert_equal_s("foo", str);
+ git_config_free(config);
+}
+
+void test_config_stress__trailing_backslash(void)
+{
+ git_config *config;
+ const char *str;
+ const char *path = "C:\\iam\\some\\windows\\path\\";
+
+ cl_assert(git_path_exists("git-test-config"));
+ cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
+ cl_git_pass(git_config_set_string(config, "windows.path", path));
+ git_config_free(config);
+
+ cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
+ cl_git_pass(git_config_get_string(&str, config, "windows.path"));
+ cl_assert_equal_s(path, str);
+ git_config_free(config);
+}
diff --git a/tests/config/validkeyname.c b/tests/config/validkeyname.c
new file mode 100644
index 000000000..33699737b
--- /dev/null
+++ b/tests/config/validkeyname.c
@@ -0,0 +1,68 @@
+#include "clar_libgit2.h"
+
+#include "config.h"
+
+static git_config *cfg;
+static const char *value;
+
+void test_config_validkeyname__initialize(void)
+{
+ cl_fixture_sandbox("config/config10");
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config10"));
+}
+
+void test_config_validkeyname__cleanup(void)
+{
+ git_config_free(cfg);
+ cfg = NULL;
+
+ cl_fixture_cleanup("config10");
+}
+
+static void assert_invalid_config_key_name(const char *name)
+{
+ cl_git_fail_with(git_config_get_string(&value, cfg, name),
+ GIT_EINVALIDSPEC);
+ cl_git_fail_with(git_config_set_string(cfg, name, "42"),
+ GIT_EINVALIDSPEC);
+ cl_git_fail_with(git_config_delete_entry(cfg, name),
+ GIT_EINVALIDSPEC);
+ cl_git_fail_with(git_config_get_multivar_foreach(cfg, name, "*", NULL, NULL),
+ GIT_EINVALIDSPEC);
+ cl_git_fail_with(git_config_set_multivar(cfg, name, "*", "42"),
+ GIT_EINVALIDSPEC);
+}
+
+void test_config_validkeyname__accessing_requires_a_valid_name(void)
+{
+ assert_invalid_config_key_name("");
+ assert_invalid_config_key_name(".");
+ assert_invalid_config_key_name("..");
+ assert_invalid_config_key_name("core.");
+ assert_invalid_config_key_name("d#ff.dirstat.lines");
+ assert_invalid_config_key_name("diff.dirstat.lines#");
+ assert_invalid_config_key_name("dif\nf.dirstat.lines");
+ assert_invalid_config_key_name("dif.dir\nstat.lines");
+ assert_invalid_config_key_name("dif.dirstat.li\nes");
+}
+
+static void assert_invalid_config_section_name(git_repository *repo, const char *name)
+{
+ cl_git_fail_with(git_config_rename_section(repo, "branch.remoteless", name), GIT_EINVALIDSPEC);
+}
+
+void test_config_validkeyname__renaming_a_section_requires_a_valid_name(void)
+{
+ git_repository *repo;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+ assert_invalid_config_section_name(repo, "");
+ assert_invalid_config_section_name(repo, "bra\nch");
+ assert_invalid_config_section_name(repo, "branc#");
+ assert_invalid_config_section_name(repo, "bra\nch.duh");
+ assert_invalid_config_section_name(repo, "branc#.duh");
+
+ git_repository_free(repo);
+}
diff --git a/tests/config/write.c b/tests/config/write.c
new file mode 100644
index 000000000..15f750dc0
--- /dev/null
+++ b/tests/config/write.c
@@ -0,0 +1,305 @@
+#include "clar_libgit2.h"
+
+void test_config_write__initialize(void)
+{
+ cl_fixture_sandbox("config/config9");
+ cl_fixture_sandbox("config/config15");
+ cl_fixture_sandbox("config/config17");
+}
+
+void test_config_write__cleanup(void)
+{
+ cl_fixture_cleanup("config9");
+ cl_fixture_cleanup("config15");
+ cl_fixture_cleanup("config17");
+}
+
+void test_config_write__replace_value(void)
+{
+ git_config *cfg;
+ int i;
+ int64_t l, expected = +9223372036854775803;
+
+ /* By freeing the config, we make sure we flush the values */
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_set_int32(cfg, "core.dummy", 5));
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_get_int32(&i, cfg, "core.dummy"));
+ cl_assert(i == 5);
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_set_int32(cfg, "core.dummy", 1));
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_set_int64(cfg, "core.verylong", expected));
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_get_int64(&l, cfg, "core.verylong"));
+ cl_assert(l == expected);
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_must_fail(git_config_get_int32(&i, cfg, "core.verylong"));
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_set_int64(cfg, "core.verylong", 1));
+ git_config_free(cfg);
+}
+
+void test_config_write__delete_value(void)
+{
+ git_config *cfg;
+ int32_t i;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_set_int32(cfg, "core.dummy", 5));
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_delete_entry(cfg, "core.dummy"));
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_assert(git_config_get_int32(&i, cfg, "core.dummy") == GIT_ENOTFOUND);
+ cl_git_pass(git_config_set_int32(cfg, "core.dummy", 1));
+ git_config_free(cfg);
+}
+
+/*
+ * At the beginning of the test:
+ * - config9 has: core.dummy2=42
+ * - config15 has: core.dummy2=7
+ */
+void test_config_write__delete_value_at_specific_level(void)
+{
+ git_config *cfg, *cfg_specific;
+ int32_t i;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config15"));
+ cl_git_pass(git_config_get_int32(&i, cfg, "core.dummy2"));
+ cl_assert(i == 7);
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_new(&cfg));
+ cl_git_pass(git_config_add_file_ondisk(cfg, "config9",
+ GIT_CONFIG_LEVEL_LOCAL, 0));
+ cl_git_pass(git_config_add_file_ondisk(cfg, "config15",
+ GIT_CONFIG_LEVEL_GLOBAL, 0));
+
+ cl_git_pass(git_config_open_level(&cfg_specific, cfg, GIT_CONFIG_LEVEL_GLOBAL));
+
+ cl_git_pass(git_config_delete_entry(cfg_specific, "core.dummy2"));
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config15"));
+ cl_assert(git_config_get_int32(&i, cfg, "core.dummy2") == GIT_ENOTFOUND);
+ cl_git_pass(git_config_set_int32(cfg, "core.dummy2", 7));
+
+ git_config_free(cfg_specific);
+ git_config_free(cfg);
+}
+
+void test_config_write__write_subsection(void)
+{
+ git_config *cfg;
+ const char *str;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_set_string(cfg, "my.own.var", "works"));
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_get_string(&str, cfg, "my.own.var"));
+ cl_assert_equal_s("works", str);
+ git_config_free(cfg);
+}
+
+void test_config_write__delete_inexistent(void)
+{
+ git_config *cfg;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_assert(git_config_delete_entry(cfg, "core.imaginary") == GIT_ENOTFOUND);
+ git_config_free(cfg);
+}
+
+void test_config_write__value_containing_quotes(void)
+{
+ git_config *cfg;
+ const char* str;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_set_string(cfg, "core.somevar", "this \"has\" quotes"));
+ cl_git_pass(git_config_get_string(&str, cfg, "core.somevar"));
+ cl_assert_equal_s(str, "this \"has\" quotes");
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_get_string(&str, cfg, "core.somevar"));
+ cl_assert_equal_s(str, "this \"has\" quotes");
+ git_config_free(cfg);
+
+ /* The code path for values that already exist is different, check that one as well */
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_set_string(cfg, "core.somevar", "this also \"has\" quotes"));
+ cl_git_pass(git_config_get_string(&str, cfg, "core.somevar"));
+ cl_assert_equal_s(str, "this also \"has\" quotes");
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_get_string(&str, cfg, "core.somevar"));
+ cl_assert_equal_s(str, "this also \"has\" quotes");
+ git_config_free(cfg);
+}
+
+void test_config_write__escape_value(void)
+{
+ git_config *cfg;
+ const char* str;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_set_string(cfg, "core.somevar", "this \"has\" quotes and \t"));
+ cl_git_pass(git_config_get_string(&str, cfg, "core.somevar"));
+ cl_assert_equal_s(str, "this \"has\" quotes and \t");
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_get_string(&str, cfg, "core.somevar"));
+ cl_assert_equal_s(str, "this \"has\" quotes and \t");
+ git_config_free(cfg);
+}
+
+void test_config_write__add_value_at_specific_level(void)
+{
+ git_config *cfg, *cfg_specific;
+ int i;
+ int64_t l, expected = +9223372036854775803;
+ const char *s;
+
+ // 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));
+ cl_git_pass(git_config_add_file_ondisk(cfg, "config15",
+ GIT_CONFIG_LEVEL_GLOBAL, 0));
+
+ cl_git_pass(git_config_open_level(&cfg_specific, cfg, GIT_CONFIG_LEVEL_GLOBAL));
+
+ cl_git_pass(git_config_set_int32(cfg_specific, "core.int32global", 28));
+ cl_git_pass(git_config_set_int64(cfg_specific, "core.int64global", expected));
+ cl_git_pass(git_config_set_bool(cfg_specific, "core.boolglobal", true));
+ cl_git_pass(git_config_set_string(cfg_specific, "core.stringglobal", "I'm a global config value!"));
+ git_config_free(cfg_specific);
+ git_config_free(cfg);
+
+ // open config15 as local level config file
+ cl_git_pass(git_config_open_ondisk(&cfg, "config15"));
+
+ cl_git_pass(git_config_get_int32(&i, cfg, "core.int32global"));
+ cl_assert_equal_i(28, i);
+ cl_git_pass(git_config_get_int64(&l, cfg, "core.int64global"));
+ cl_assert(l == expected);
+ cl_git_pass(git_config_get_bool(&i, cfg, "core.boolglobal"));
+ cl_assert_equal_b(true, i);
+ cl_git_pass(git_config_get_string(&s, cfg, "core.stringglobal"));
+ cl_assert_equal_s("I'm a global config value!", s);
+
+ git_config_free(cfg);
+}
+
+void test_config_write__add_value_at_file_with_no_clrf_at_the_end(void)
+{
+ git_config *cfg;
+ int i;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config17"));
+ cl_git_pass(git_config_set_int32(cfg, "core.newline", 7));
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config17"));
+ cl_git_pass(git_config_get_int32(&i, cfg, "core.newline"));
+ cl_assert_equal_i(7, i);
+
+ git_config_free(cfg);
+}
+
+void test_config_write__add_value_which_needs_quotes(void)
+{
+ git_config *cfg;
+ const char* str1;
+ const char* str2;
+ const char* str3;
+ const char* str4;
+ const char* str5;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config17"));
+ cl_git_pass(git_config_set_string(cfg, "core.startwithspace", " Something"));
+ cl_git_pass(git_config_set_string(cfg, "core.endwithspace", "Something "));
+ cl_git_pass(git_config_set_string(cfg, "core.containscommentchar1", "some#thing"));
+ cl_git_pass(git_config_set_string(cfg, "core.containscommentchar2", "some;thing"));
+ cl_git_pass(git_config_set_string(cfg, "core.startwhithsapceandcontainsdoublequote", " some\"thing"));
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config17"));
+ cl_git_pass(git_config_get_string(&str1, cfg, "core.startwithspace"));
+ cl_assert_equal_s(" Something", str1);
+ cl_git_pass(git_config_get_string(&str2, cfg, "core.endwithspace"));
+ cl_assert_equal_s("Something ", str2);
+ cl_git_pass(git_config_get_string(&str3, cfg, "core.containscommentchar1"));
+ cl_assert_equal_s("some#thing", str3);
+ cl_git_pass(git_config_get_string(&str4, cfg, "core.containscommentchar2"));
+ cl_assert_equal_s("some;thing", str4);
+ cl_git_pass(git_config_get_string(&str5, cfg, "core.startwhithsapceandcontainsdoublequote"));
+ cl_assert_equal_s(" some\"thing", str5);
+ git_config_free(cfg);
+}
+
+void test_config_write__can_set_a_value_to_NULL(void)
+{
+ git_repository *repository;
+ git_config *config;
+
+ repository = cl_git_sandbox_init("testrepo.git");
+
+ cl_git_pass(git_repository_config(&config, repository));
+ cl_git_fail(git_config_set_string(config, "a.b.c", NULL));
+ git_config_free(config);
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_config_write__can_set_an_empty_value(void)
+{
+ git_repository *repository;
+ git_config *config;
+ const char * str;
+
+ repository = cl_git_sandbox_init("testrepo.git");
+ cl_git_pass(git_repository_config(&config, repository));
+
+ cl_git_pass(git_config_set_string(config, "core.somevar", ""));
+ cl_git_pass(git_config_get_string(&str, config, "core.somevar"));
+ cl_assert_equal_s(str, "");
+
+ git_config_free(config);
+ cl_git_sandbox_cleanup();
+}
+
+void test_config_write__updating_a_locked_config_file_returns_ELOCKED(void)
+{
+ git_config *cfg;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+
+ cl_git_mkfile("config9.lock", "[core]\n");
+
+ cl_git_fail_with(git_config_set_string(cfg, "core.dump", "boom"), GIT_ELOCKED);
+
+ git_config_free(cfg);
+}
diff --git a/tests/core/bitvec.c b/tests/core/bitvec.c
new file mode 100644
index 000000000..48d7b99f0
--- /dev/null
+++ b/tests/core/bitvec.c
@@ -0,0 +1,64 @@
+#include "clar_libgit2.h"
+#include "bitvec.h"
+
+#if 0
+static void print_bitvec(git_bitvec *bv)
+{
+ int b;
+
+ if (!bv->length) {
+ for (b = 63; b >= 0; --b)
+ fprintf(stderr, "%d", (bv->u.bits & (1ul << b)) ? 1 : 0);
+ } else {
+ for (b = bv->length * 8; b >= 0; --b)
+ fprintf(stderr, "%d", (bv->u.ptr[b >> 3] & (b & 0x0ff)) ? 1 : 0);
+ }
+ fprintf(stderr, "\n");
+}
+#endif
+
+static void set_some_bits(git_bitvec *bv, size_t length)
+{
+ size_t i;
+
+ for (i = 0; i < length; ++i) {
+ if (i % 3 == 0 || i % 7 == 0)
+ git_bitvec_set(bv, i, true);
+ }
+}
+
+static void check_some_bits(git_bitvec *bv, size_t length)
+{
+ size_t i;
+
+ for (i = 0; i < length; ++i)
+ cl_assert_equal_b(i % 3 == 0 || i % 7 == 0, git_bitvec_get(bv, i));
+}
+
+void test_core_bitvec__0(void)
+{
+ git_bitvec bv;
+
+ cl_git_pass(git_bitvec_init(&bv, 32));
+ set_some_bits(&bv, 16);
+ check_some_bits(&bv, 16);
+ git_bitvec_clear(&bv);
+ set_some_bits(&bv, 32);
+ check_some_bits(&bv, 32);
+ git_bitvec_clear(&bv);
+ set_some_bits(&bv, 64);
+ check_some_bits(&bv, 64);
+ git_bitvec_free(&bv);
+
+ cl_git_pass(git_bitvec_init(&bv, 128));
+ set_some_bits(&bv, 32);
+ check_some_bits(&bv, 32);
+ set_some_bits(&bv, 128);
+ check_some_bits(&bv, 128);
+ git_bitvec_free(&bv);
+
+ cl_git_pass(git_bitvec_init(&bv, 4000));
+ set_some_bits(&bv, 4000);
+ check_some_bits(&bv, 4000);
+ git_bitvec_free(&bv);
+}
diff --git a/tests/core/buffer.c b/tests/core/buffer.c
new file mode 100644
index 000000000..11d173d49
--- /dev/null
+++ b/tests/core/buffer.c
@@ -0,0 +1,1039 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "buf_text.h"
+#include "hashsig.h"
+#include "fileops.h"
+
+#define TESTSTR "Have you seen that? Have you seeeen that??"
+const char *test_string = TESTSTR;
+const char *test_string_x2 = TESTSTR TESTSTR;
+
+#define TESTSTR_4096 REP1024("1234")
+#define TESTSTR_8192 REP1024("12341234")
+const char *test_4096 = TESTSTR_4096;
+const char *test_8192 = TESTSTR_8192;
+
+/* test basic data concatenation */
+void test_core_buffer__0(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_assert(buf.size == 0);
+
+ git_buf_puts(&buf, test_string);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(test_string, git_buf_cstr(&buf));
+
+ git_buf_puts(&buf, test_string);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(test_string_x2, git_buf_cstr(&buf));
+
+ git_buf_free(&buf);
+}
+
+/* test git_buf_printf */
+void test_core_buffer__1(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ git_buf_printf(&buf, "%s %s %d ", "shoop", "da", 23);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s("shoop da 23 ", git_buf_cstr(&buf));
+
+ git_buf_printf(&buf, "%s %d", "woop", 42);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s("shoop da 23 woop 42", git_buf_cstr(&buf));
+
+ git_buf_free(&buf);
+}
+
+/* more thorough test of concatenation options */
+void test_core_buffer__2(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ int i;
+ char data[128];
+
+ cl_assert(buf.size == 0);
+
+ /* this must be safe to do */
+ git_buf_free(&buf);
+ cl_assert(buf.size == 0);
+ cl_assert(buf.asize == 0);
+
+ /* empty buffer should be empty string */
+ cl_assert_equal_s("", git_buf_cstr(&buf));
+ cl_assert(buf.size == 0);
+ /* cl_assert(buf.asize == 0); -- should not assume what git_buf does */
+
+ /* free should set us back to the beginning */
+ git_buf_free(&buf);
+ cl_assert(buf.size == 0);
+ cl_assert(buf.asize == 0);
+
+ /* add letter */
+ git_buf_putc(&buf, '+');
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s("+", git_buf_cstr(&buf));
+
+ /* add letter again */
+ git_buf_putc(&buf, '+');
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s("++", git_buf_cstr(&buf));
+
+ /* let's try that a few times */
+ for (i = 0; i < 16; ++i) {
+ git_buf_putc(&buf, '+');
+ cl_assert(git_buf_oom(&buf) == 0);
+ }
+ cl_assert_equal_s("++++++++++++++++++", git_buf_cstr(&buf));
+
+ git_buf_free(&buf);
+
+ /* add data */
+ git_buf_put(&buf, "xo", 2);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s("xo", git_buf_cstr(&buf));
+
+ /* add letter again */
+ git_buf_put(&buf, "xo", 2);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s("xoxo", git_buf_cstr(&buf));
+
+ /* let's try that a few times */
+ for (i = 0; i < 16; ++i) {
+ git_buf_put(&buf, "xo", 2);
+ cl_assert(git_buf_oom(&buf) == 0);
+ }
+ cl_assert_equal_s("xoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxo",
+ git_buf_cstr(&buf));
+
+ git_buf_free(&buf);
+
+ /* set to string */
+ git_buf_sets(&buf, test_string);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(test_string, git_buf_cstr(&buf));
+
+ /* append string */
+ git_buf_puts(&buf, test_string);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(test_string_x2, git_buf_cstr(&buf));
+
+ /* set to string again (should overwrite - not append) */
+ git_buf_sets(&buf, test_string);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(test_string, git_buf_cstr(&buf));
+
+ /* test clear */
+ git_buf_clear(&buf);
+ cl_assert_equal_s("", git_buf_cstr(&buf));
+
+ git_buf_free(&buf);
+
+ /* test extracting data into buffer */
+ git_buf_puts(&buf, REP4("0123456789"));
+ cl_assert(git_buf_oom(&buf) == 0);
+
+ git_buf_copy_cstr(data, sizeof(data), &buf);
+ cl_assert_equal_s(REP4("0123456789"), data);
+ git_buf_copy_cstr(data, 11, &buf);
+ cl_assert_equal_s("0123456789", data);
+ git_buf_copy_cstr(data, 3, &buf);
+ cl_assert_equal_s("01", data);
+ git_buf_copy_cstr(data, 1, &buf);
+ cl_assert_equal_s("", data);
+
+ git_buf_copy_cstr(data, sizeof(data), &buf);
+ cl_assert_equal_s(REP4("0123456789"), data);
+
+ git_buf_sets(&buf, REP256("x"));
+ git_buf_copy_cstr(data, sizeof(data), &buf);
+ /* since sizeof(data) == 128, only 127 bytes should be copied */
+ cl_assert_equal_s(REP4(REP16("x")) REP16("x") REP16("x")
+ REP16("x") "xxxxxxxxxxxxxxx", data);
+
+ git_buf_free(&buf);
+
+ git_buf_copy_cstr(data, sizeof(data), &buf);
+ cl_assert_equal_s("", data);
+}
+
+/* let's do some tests with larger buffers to push our limits */
+void test_core_buffer__3(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ /* set to string */
+ git_buf_set(&buf, test_4096, 4096);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(test_4096, git_buf_cstr(&buf));
+
+ /* append string */
+ git_buf_puts(&buf, test_4096);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(test_8192, git_buf_cstr(&buf));
+
+ /* set to string again (should overwrite - not append) */
+ git_buf_set(&buf, test_4096, 4096);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(test_4096, git_buf_cstr(&buf));
+
+ git_buf_free(&buf);
+}
+
+/* let's try some producer/consumer tests */
+void test_core_buffer__4(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ int i;
+
+ for (i = 0; i < 10; ++i) {
+ git_buf_puts(&buf, "1234"); /* add 4 */
+ cl_assert(git_buf_oom(&buf) == 0);
+ git_buf_consume(&buf, buf.ptr + 2); /* eat the first two */
+ cl_assert(strlen(git_buf_cstr(&buf)) == (size_t)((i + 1) * 2));
+ }
+ /* we have appended 1234 10x and removed the first 20 letters */
+ cl_assert_equal_s("12341234123412341234", git_buf_cstr(&buf));
+
+ git_buf_consume(&buf, NULL);
+ cl_assert_equal_s("12341234123412341234", git_buf_cstr(&buf));
+
+ git_buf_consume(&buf, "invalid pointer");
+ cl_assert_equal_s("12341234123412341234", git_buf_cstr(&buf));
+
+ git_buf_consume(&buf, buf.ptr);
+ cl_assert_equal_s("12341234123412341234", git_buf_cstr(&buf));
+
+ git_buf_consume(&buf, buf.ptr + 1);
+ cl_assert_equal_s("2341234123412341234", git_buf_cstr(&buf));
+
+ git_buf_consume(&buf, buf.ptr + buf.size);
+ cl_assert_equal_s("", git_buf_cstr(&buf));
+
+ git_buf_free(&buf);
+}
+
+
+static void
+check_buf_append(
+ const char* data_a,
+ const char* data_b,
+ const char* expected_data,
+ size_t expected_size,
+ size_t expected_asize)
+{
+ git_buf tgt = GIT_BUF_INIT;
+
+ git_buf_sets(&tgt, data_a);
+ cl_assert(git_buf_oom(&tgt) == 0);
+ git_buf_puts(&tgt, data_b);
+ cl_assert(git_buf_oom(&tgt) == 0);
+ cl_assert_equal_s(expected_data, git_buf_cstr(&tgt));
+ cl_assert(tgt.size == expected_size);
+ if (expected_asize > 0)
+ cl_assert(tgt.asize == expected_asize);
+
+ git_buf_free(&tgt);
+}
+
+static void
+check_buf_append_abc(
+ const char* buf_a,
+ const char* buf_b,
+ const char* buf_c,
+ const char* expected_ab,
+ const char* expected_abc,
+ const char* expected_abca,
+ const char* expected_abcab,
+ const char* expected_abcabc)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ git_buf_sets(&buf, buf_a);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(buf_a, git_buf_cstr(&buf));
+
+ git_buf_puts(&buf, buf_b);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(expected_ab, git_buf_cstr(&buf));
+
+ git_buf_puts(&buf, buf_c);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(expected_abc, git_buf_cstr(&buf));
+
+ git_buf_puts(&buf, buf_a);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(expected_abca, git_buf_cstr(&buf));
+
+ git_buf_puts(&buf, buf_b);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(expected_abcab, git_buf_cstr(&buf));
+
+ git_buf_puts(&buf, buf_c);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(expected_abcabc, git_buf_cstr(&buf));
+
+ git_buf_free(&buf);
+}
+
+/* more variations on append tests */
+void test_core_buffer__5(void)
+{
+ check_buf_append("", "", "", 0, 8);
+ check_buf_append("a", "", "a", 1, 8);
+ check_buf_append("", "a", "a", 1, 8);
+ check_buf_append("", "a", "a", 1, 8);
+ check_buf_append("a", "", "a", 1, 8);
+ check_buf_append("a", "b", "ab", 2, 8);
+ check_buf_append("", "abcdefgh", "abcdefgh", 8, 16);
+ check_buf_append("abcdefgh", "", "abcdefgh", 8, 16);
+
+ /* buffer with starting asize will grow to:
+ * 1 -> 2, 2 -> 3, 3 -> 5, 4 -> 6, 5 -> 8, 6 -> 9,
+ * 7 -> 11, 8 -> 12, 9 -> 14, 10 -> 15, 11 -> 17, 12 -> 18,
+ * 13 -> 20, 14 -> 21, 15 -> 23, 16 -> 24, 17 -> 26, 18 -> 27,
+ * 19 -> 29, 20 -> 30, 21 -> 32, 22 -> 33, 23 -> 35, 24 -> 36,
+ * ...
+ * follow sequence until value > target size,
+ * then round up to nearest multiple of 8.
+ */
+
+ check_buf_append("abcdefgh", "/", "abcdefgh/", 9, 16);
+ check_buf_append("abcdefgh", "ijklmno", "abcdefghijklmno", 15, 16);
+ check_buf_append("abcdefgh", "ijklmnop", "abcdefghijklmnop", 16, 24);
+ check_buf_append("0123456789", "0123456789",
+ "01234567890123456789", 20, 24);
+ check_buf_append(REP16("x"), REP16("o"),
+ REP16("x") REP16("o"), 32, 40);
+
+ check_buf_append(test_4096, "", test_4096, 4096, 4104);
+ check_buf_append(test_4096, test_4096, test_8192, 8192, 9240);
+
+ /* check sequences of appends */
+ check_buf_append_abc("a", "b", "c",
+ "ab", "abc", "abca", "abcab", "abcabc");
+ check_buf_append_abc("a1", "b2", "c3",
+ "a1b2", "a1b2c3", "a1b2c3a1",
+ "a1b2c3a1b2", "a1b2c3a1b2c3");
+ check_buf_append_abc("a1/", "b2/", "c3/",
+ "a1/b2/", "a1/b2/c3/", "a1/b2/c3/a1/",
+ "a1/b2/c3/a1/b2/", "a1/b2/c3/a1/b2/c3/");
+}
+
+/* test swap */
+void test_core_buffer__6(void)
+{
+ git_buf a = GIT_BUF_INIT;
+ git_buf b = GIT_BUF_INIT;
+
+ git_buf_sets(&a, "foo");
+ cl_assert(git_buf_oom(&a) == 0);
+ git_buf_sets(&b, "bar");
+ cl_assert(git_buf_oom(&b) == 0);
+
+ cl_assert_equal_s("foo", git_buf_cstr(&a));
+ cl_assert_equal_s("bar", git_buf_cstr(&b));
+
+ git_buf_swap(&a, &b);
+
+ cl_assert_equal_s("bar", git_buf_cstr(&a));
+ cl_assert_equal_s("foo", git_buf_cstr(&b));
+
+ git_buf_free(&a);
+ git_buf_free(&b);
+}
+
+
+/* test detach/attach data */
+void test_core_buffer__7(void)
+{
+ const char *fun = "This is fun";
+ git_buf a = GIT_BUF_INIT;
+ char *b = NULL;
+
+ git_buf_sets(&a, "foo");
+ cl_assert(git_buf_oom(&a) == 0);
+ cl_assert_equal_s("foo", git_buf_cstr(&a));
+
+ b = git_buf_detach(&a);
+
+ cl_assert_equal_s("foo", b);
+ cl_assert_equal_s("", a.ptr);
+ git__free(b);
+
+ b = git_buf_detach(&a);
+
+ cl_assert_equal_s(NULL, b);
+ cl_assert_equal_s("", a.ptr);
+
+ git_buf_free(&a);
+
+ b = git__strdup(fun);
+ git_buf_attach(&a, b, 0);
+
+ cl_assert_equal_s(fun, a.ptr);
+ cl_assert(a.size == strlen(fun));
+ cl_assert(a.asize == strlen(fun) + 1);
+
+ git_buf_free(&a);
+
+ b = git__strdup(fun);
+ git_buf_attach(&a, b, strlen(fun) + 1);
+
+ cl_assert_equal_s(fun, a.ptr);
+ cl_assert(a.size == strlen(fun));
+ cl_assert(a.asize == strlen(fun) + 1);
+
+ git_buf_free(&a);
+}
+
+
+static void
+check_joinbuf_2(
+ const char *a,
+ const char *b,
+ const char *expected)
+{
+ char sep = '/';
+ git_buf buf = GIT_BUF_INIT;
+
+ git_buf_join(&buf, sep, a, b);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(expected, git_buf_cstr(&buf));
+ git_buf_free(&buf);
+}
+
+static void
+check_joinbuf_n_2(
+ const char *a,
+ const char *b,
+ const char *expected)
+{
+ char sep = '/';
+ git_buf buf = GIT_BUF_INIT;
+
+ git_buf_sets(&buf, a);
+ cl_assert(git_buf_oom(&buf) == 0);
+
+ git_buf_join_n(&buf, sep, 1, b);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(expected, git_buf_cstr(&buf));
+
+ git_buf_free(&buf);
+}
+
+static void
+check_joinbuf_n_4(
+ const char *a,
+ const char *b,
+ const char *c,
+ const char *d,
+ const char *expected)
+{
+ char sep = ';';
+ git_buf buf = GIT_BUF_INIT;
+ git_buf_join_n(&buf, sep, 4, a, b, c, d);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_equal_s(expected, git_buf_cstr(&buf));
+ git_buf_free(&buf);
+}
+
+/* test join */
+void test_core_buffer__8(void)
+{
+ git_buf a = GIT_BUF_INIT;
+
+ git_buf_join_n(&a, '/', 1, "foo");
+ cl_assert(git_buf_oom(&a) == 0);
+ cl_assert_equal_s("foo", git_buf_cstr(&a));
+
+ git_buf_join_n(&a, '/', 1, "bar");
+ cl_assert(git_buf_oom(&a) == 0);
+ cl_assert_equal_s("foo/bar", git_buf_cstr(&a));
+
+ git_buf_join_n(&a, '/', 1, "baz");
+ cl_assert(git_buf_oom(&a) == 0);
+ cl_assert_equal_s("foo/bar/baz", git_buf_cstr(&a));
+
+ git_buf_free(&a);
+
+ check_joinbuf_2(NULL, "", "");
+ check_joinbuf_2(NULL, "a", "a");
+ check_joinbuf_2(NULL, "/a", "/a");
+ check_joinbuf_2("", "", "");
+ check_joinbuf_2("", "a", "a");
+ check_joinbuf_2("", "/a", "/a");
+ check_joinbuf_2("a", "", "a/");
+ check_joinbuf_2("a", "/", "a/");
+ check_joinbuf_2("a", "b", "a/b");
+ check_joinbuf_2("/", "a", "/a");
+ check_joinbuf_2("/", "", "/");
+ check_joinbuf_2("/a", "/b", "/a/b");
+ check_joinbuf_2("/a", "/b/", "/a/b/");
+ check_joinbuf_2("/a/", "b/", "/a/b/");
+ check_joinbuf_2("/a/", "/b/", "/a/b/");
+ check_joinbuf_2("/a/", "//b/", "/a/b/");
+ check_joinbuf_2("/abcd", "/defg", "/abcd/defg");
+ check_joinbuf_2("/abcd", "/defg/", "/abcd/defg/");
+ check_joinbuf_2("/abcd/", "defg/", "/abcd/defg/");
+ check_joinbuf_2("/abcd/", "/defg/", "/abcd/defg/");
+
+ check_joinbuf_n_2("", "", "");
+ check_joinbuf_n_2("", "a", "a");
+ check_joinbuf_n_2("", "/a", "/a");
+ check_joinbuf_n_2("a", "", "a/");
+ check_joinbuf_n_2("a", "/", "a/");
+ check_joinbuf_n_2("a", "b", "a/b");
+ check_joinbuf_n_2("/", "a", "/a");
+ check_joinbuf_n_2("/", "", "/");
+ check_joinbuf_n_2("/a", "/b", "/a/b");
+ check_joinbuf_n_2("/a", "/b/", "/a/b/");
+ check_joinbuf_n_2("/a/", "b/", "/a/b/");
+ check_joinbuf_n_2("/a/", "/b/", "/a/b/");
+ check_joinbuf_n_2("/abcd", "/defg", "/abcd/defg");
+ check_joinbuf_n_2("/abcd", "/defg/", "/abcd/defg/");
+ check_joinbuf_n_2("/abcd/", "defg/", "/abcd/defg/");
+ check_joinbuf_n_2("/abcd/", "/defg/", "/abcd/defg/");
+
+ check_joinbuf_n_4("", "", "", "", "");
+ check_joinbuf_n_4("", "a", "", "", "a;");
+ check_joinbuf_n_4("a", "", "", "", "a;");
+ check_joinbuf_n_4("", "", "", "a", "a");
+ check_joinbuf_n_4("a", "b", "", ";c;d;", "a;b;c;d;");
+ check_joinbuf_n_4("a", "b", "", ";c;d", "a;b;c;d");
+ check_joinbuf_n_4("abcd", "efgh", "ijkl", "mnop", "abcd;efgh;ijkl;mnop");
+ check_joinbuf_n_4("abcd;", "efgh;", "ijkl;", "mnop;", "abcd;efgh;ijkl;mnop;");
+ check_joinbuf_n_4(";abcd;", ";efgh;", ";ijkl;", ";mnop;", ";abcd;efgh;ijkl;mnop;");
+}
+
+void test_core_buffer__9(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ /* just some exhaustive tests of various separator placement */
+ char *a[] = { "", "-", "a-", "-a", "-a-" };
+ char *b[] = { "", "-", "b-", "-b", "-b-" };
+ char sep[] = { 0, '-', '/' };
+ char *expect_null[] = { "", "-", "a-", "-a", "-a-",
+ "-", "--", "a--", "-a-", "-a--",
+ "b-", "-b-", "a-b-", "-ab-", "-a-b-",
+ "-b", "--b", "a--b", "-a-b", "-a--b",
+ "-b-", "--b-", "a--b-", "-a-b-", "-a--b-" };
+ char *expect_dash[] = { "", "-", "a-", "-a-", "-a-",
+ "-", "-", "a-", "-a-", "-a-",
+ "b-", "-b-", "a-b-", "-a-b-", "-a-b-",
+ "-b", "-b", "a-b", "-a-b", "-a-b",
+ "-b-", "-b-", "a-b-", "-a-b-", "-a-b-" };
+ char *expect_slas[] = { "", "-/", "a-/", "-a/", "-a-/",
+ "-", "-/-", "a-/-", "-a/-", "-a-/-",
+ "b-", "-/b-", "a-/b-", "-a/b-", "-a-/b-",
+ "-b", "-/-b", "a-/-b", "-a/-b", "-a-/-b",
+ "-b-", "-/-b-", "a-/-b-", "-a/-b-", "-a-/-b-" };
+ char **expect_values[] = { expect_null, expect_dash, expect_slas };
+ char separator, **expect;
+ unsigned int s, i, j;
+
+ for (s = 0; s < sizeof(sep) / sizeof(char); ++s) {
+ separator = sep[s];
+ expect = expect_values[s];
+
+ for (j = 0; j < sizeof(b) / sizeof(char*); ++j) {
+ for (i = 0; i < sizeof(a) / sizeof(char*); ++i) {
+ git_buf_join(&buf, separator, a[i], b[j]);
+ cl_assert_equal_s(*expect, buf.ptr);
+ expect++;
+ }
+ }
+ }
+
+ git_buf_free(&buf);
+}
+
+void test_core_buffer__10(void)
+{
+ git_buf a = GIT_BUF_INIT;
+
+ cl_git_pass(git_buf_join_n(&a, '/', 1, "test"));
+ cl_assert_equal_s(a.ptr, "test");
+ cl_git_pass(git_buf_join_n(&a, '/', 1, "string"));
+ cl_assert_equal_s(a.ptr, "test/string");
+ git_buf_clear(&a);
+ cl_git_pass(git_buf_join_n(&a, '/', 3, "test", "string", "join"));
+ cl_assert_equal_s(a.ptr, "test/string/join");
+ cl_git_pass(git_buf_join_n(&a, '/', 2, a.ptr, "more"));
+ cl_assert_equal_s(a.ptr, "test/string/join/test/string/join/more");
+
+ git_buf_free(&a);
+}
+
+void test_core_buffer__11(void)
+{
+ git_buf a = GIT_BUF_INIT;
+ git_strarray t;
+ char *t1[] = { "nothing", "in", "common" };
+ char *t2[] = { "something", "something else", "some other" };
+ char *t3[] = { "something", "some fun", "no fun" };
+ char *t4[] = { "happy", "happier", "happiest" };
+ char *t5[] = { "happiest", "happier", "happy" };
+ char *t6[] = { "no", "nope", "" };
+ char *t7[] = { "", "doesn't matter" };
+
+ t.strings = t1;
+ t.count = 3;
+ cl_git_pass(git_buf_text_common_prefix(&a, &t));
+ cl_assert_equal_s(a.ptr, "");
+
+ t.strings = t2;
+ t.count = 3;
+ cl_git_pass(git_buf_text_common_prefix(&a, &t));
+ cl_assert_equal_s(a.ptr, "some");
+
+ t.strings = t3;
+ t.count = 3;
+ cl_git_pass(git_buf_text_common_prefix(&a, &t));
+ cl_assert_equal_s(a.ptr, "");
+
+ t.strings = t4;
+ t.count = 3;
+ cl_git_pass(git_buf_text_common_prefix(&a, &t));
+ cl_assert_equal_s(a.ptr, "happ");
+
+ t.strings = t5;
+ t.count = 3;
+ cl_git_pass(git_buf_text_common_prefix(&a, &t));
+ cl_assert_equal_s(a.ptr, "happ");
+
+ t.strings = t6;
+ t.count = 3;
+ cl_git_pass(git_buf_text_common_prefix(&a, &t));
+ cl_assert_equal_s(a.ptr, "");
+
+ t.strings = t7;
+ t.count = 3;
+ cl_git_pass(git_buf_text_common_prefix(&a, &t));
+ cl_assert_equal_s(a.ptr, "");
+
+ git_buf_free(&a);
+}
+
+void test_core_buffer__rfind_variants(void)
+{
+ git_buf a = GIT_BUF_INIT;
+ ssize_t len;
+
+ cl_git_pass(git_buf_sets(&a, "/this/is/it/"));
+
+ len = (ssize_t)git_buf_len(&a);
+
+ cl_assert(git_buf_rfind(&a, '/') == len - 1);
+ cl_assert(git_buf_rfind_next(&a, '/') == len - 4);
+
+ cl_assert(git_buf_rfind(&a, 'i') == len - 3);
+ cl_assert(git_buf_rfind_next(&a, 'i') == len - 3);
+
+ cl_assert(git_buf_rfind(&a, 'h') == 2);
+ cl_assert(git_buf_rfind_next(&a, 'h') == 2);
+
+ cl_assert(git_buf_rfind(&a, 'q') == -1);
+ cl_assert(git_buf_rfind_next(&a, 'q') == -1);
+
+ git_buf_free(&a);
+}
+
+void test_core_buffer__puts_escaped(void)
+{
+ git_buf a = GIT_BUF_INIT;
+
+ git_buf_clear(&a);
+ cl_git_pass(git_buf_text_puts_escaped(&a, "this is a test", "", ""));
+ cl_assert_equal_s("this is a test", a.ptr);
+
+ git_buf_clear(&a);
+ cl_git_pass(git_buf_text_puts_escaped(&a, "this is a test", "t", "\\"));
+ cl_assert_equal_s("\\this is a \\tes\\t", a.ptr);
+
+ git_buf_clear(&a);
+ cl_git_pass(git_buf_text_puts_escaped(&a, "this is a test", "i ", "__"));
+ cl_assert_equal_s("th__is__ __is__ a__ test", a.ptr);
+
+ git_buf_clear(&a);
+ cl_git_pass(git_buf_text_puts_escape_regex(&a, "^match\\s*[A-Z]+.*"));
+ cl_assert_equal_s("\\^match\\\\s\\*\\[A-Z\\]\\+\\.\\*", a.ptr);
+
+ git_buf_free(&a);
+}
+
+static void assert_unescape(char *expected, char *to_unescape) {
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_buf_sets(&buf, to_unescape));
+ git_buf_text_unescape(&buf);
+ cl_assert_equal_s(expected, buf.ptr);
+ cl_assert_equal_sz(strlen(expected), buf.size);
+
+ git_buf_free(&buf);
+}
+
+void test_core_buffer__unescape(void)
+{
+ assert_unescape("Escaped\\", "Es\\ca\\ped\\");
+ assert_unescape("Es\\caped\\", "Es\\\\ca\\ped\\\\");
+ assert_unescape("\\", "\\");
+ assert_unescape("\\", "\\\\");
+ assert_unescape("", "");
+}
+
+void test_core_buffer__base64(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ /* t h i s
+ * 0x 74 68 69 73
+ * 0b 01110100 01101000 01101001 01110011
+ * 0b 011101 000110 100001 101001 011100 110000
+ * 0x 1d 06 21 29 1c 30
+ * d G h p c w
+ */
+ cl_git_pass(git_buf_put_base64(&buf, "this", 4));
+ cl_assert_equal_s("dGhpcw==", buf.ptr);
+
+ git_buf_clear(&buf);
+ cl_git_pass(git_buf_put_base64(&buf, "this!", 5));
+ cl_assert_equal_s("dGhpcyE=", buf.ptr);
+
+ git_buf_clear(&buf);
+ cl_git_pass(git_buf_put_base64(&buf, "this!\n", 6));
+ cl_assert_equal_s("dGhpcyEK", buf.ptr);
+
+ git_buf_free(&buf);
+}
+
+void test_core_buffer__classify_with_utf8(void)
+{
+ char *data0 = "Simple text\n";
+ size_t data0len = 12;
+ char *data1 = "Is that UTF-8 data I see…\nYep!\n";
+ size_t data1len = 31;
+ char *data2 = "Internal NUL!!!\000\n\nI see you!\n";
+ size_t data2len = 29;
+ char *data3 = "\xef\xbb\xbfThis is UTF-8 with a BOM.\n";
+ size_t data3len = 20;
+ git_buf b;
+
+ b.ptr = data0; b.size = b.asize = data0len;
+ cl_assert(!git_buf_text_is_binary(&b));
+ cl_assert(!git_buf_text_contains_nul(&b));
+
+ b.ptr = data1; b.size = b.asize = data1len;
+ cl_assert(git_buf_text_is_binary(&b));
+ cl_assert(!git_buf_text_contains_nul(&b));
+
+ b.ptr = data2; b.size = b.asize = data2len;
+ cl_assert(git_buf_text_is_binary(&b));
+ cl_assert(git_buf_text_contains_nul(&b));
+
+ b.ptr = data3; b.size = b.asize = data3len;
+ cl_assert(!git_buf_text_is_binary(&b));
+ cl_assert(!git_buf_text_contains_nul(&b));
+}
+
+#define SIMILARITY_TEST_DATA_1 \
+ "000\n001\n002\n003\n004\n005\n006\n007\n008\n009\n" \
+ "010\n011\n012\n013\n014\n015\n016\n017\n018\n019\n" \
+ "020\n021\n022\n023\n024\n025\n026\n027\n028\n029\n" \
+ "030\n031\n032\n033\n034\n035\n036\n037\n038\n039\n" \
+ "040\n041\n042\n043\n044\n045\n046\n047\n048\n049\n"
+
+void test_core_buffer__similarity_metric(void)
+{
+ git_hashsig *a, *b;
+ git_buf buf = GIT_BUF_INIT;
+ int sim;
+
+ /* in the first case, we compare data to itself and expect 100% match */
+
+ cl_git_pass(git_buf_sets(&buf, SIMILARITY_TEST_DATA_1));
+ cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
+ cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
+
+ cl_assert_equal_i(100, git_hashsig_compare(a, b));
+
+ git_hashsig_free(a);
+ git_hashsig_free(b);
+
+ /* if we change just a single byte, how much does that change magnify? */
+
+ cl_git_pass(git_buf_sets(&buf, SIMILARITY_TEST_DATA_1));
+ cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
+ cl_git_pass(git_buf_sets(&buf,
+ "000\n001\n002\n003\n004\n005\n006\n007\n008\n009\n" \
+ "010\n011\n012\n013\n014\n015\n016\n017\n018\n019\n" \
+ "x020x\n021\n022\n023\n024\n025\n026\n027\n028\n029\n" \
+ "030\n031\n032\n033\n034\n035\n036\n037\n038\n039\n" \
+ "040\n041\n042\n043\n044\n045\n046\n047\n048\n049\n"
+ ));
+ cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
+
+ sim = git_hashsig_compare(a, b);
+
+ cl_assert_in_range(95, sim, 100); /* expect >95% similarity */
+
+ git_hashsig_free(a);
+ git_hashsig_free(b);
+
+ /* let's try comparing data to a superset of itself */
+
+ cl_git_pass(git_buf_sets(&buf, SIMILARITY_TEST_DATA_1));
+ cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
+ cl_git_pass(git_buf_sets(&buf, SIMILARITY_TEST_DATA_1
+ "050\n051\n052\n053\n054\n055\n056\n057\n058\n059\n"));
+ cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
+
+ sim = git_hashsig_compare(a, b);
+ /* 20% lines added ~= 10% lines changed */
+
+ cl_assert_in_range(85, sim, 95); /* expect similarity around 90% */
+
+ git_hashsig_free(a);
+ git_hashsig_free(b);
+
+ /* what if we keep about half the original data and add half new */
+
+ cl_git_pass(git_buf_sets(&buf, SIMILARITY_TEST_DATA_1));
+ cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
+ cl_git_pass(git_buf_sets(&buf,
+ "000\n001\n002\n003\n004\n005\n006\n007\n008\n009\n" \
+ "010\n011\n012\n013\n014\n015\n016\n017\n018\n019\n" \
+ "020x\n021\n022\n023\n024\n" \
+ "x25\nx26\nx27\nx28\nx29\n" \
+ "x30\nx31\nx32\nx33\nx34\nx35\nx36\nx37\nx38\nx39\n" \
+ "x40\nx41\nx42\nx43\nx44\nx45\nx46\nx47\nx48\nx49\n"
+ ));
+ cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
+
+ sim = git_hashsig_compare(a, b);
+ /* 50% lines changed */
+
+ cl_assert_in_range(40, sim, 60); /* expect in the 40-60% similarity range */
+
+ git_hashsig_free(a);
+ git_hashsig_free(b);
+
+ /* lastly, let's check that we can hash file content as well */
+
+ cl_git_pass(git_buf_sets(&buf, SIMILARITY_TEST_DATA_1));
+ cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
+
+ cl_git_pass(git_futils_mkdir("scratch", NULL, 0755, GIT_MKDIR_PATH));
+ cl_git_mkfile("scratch/testdata", SIMILARITY_TEST_DATA_1);
+ cl_git_pass(git_hashsig_create_fromfile(
+ &b, "scratch/testdata", GIT_HASHSIG_NORMAL));
+
+ cl_assert_equal_i(100, git_hashsig_compare(a, b));
+
+ git_hashsig_free(a);
+ git_hashsig_free(b);
+
+ git_buf_free(&buf);
+ git_futils_rmdir_r("scratch", NULL, GIT_RMDIR_REMOVE_FILES);
+}
+
+
+void test_core_buffer__similarity_metric_whitespace(void)
+{
+ git_hashsig *a, *b;
+ git_buf buf = GIT_BUF_INIT;
+ int sim, i, j;
+ git_hashsig_option_t opt;
+ const char *tabbed =
+ " for (s = 0; s < sizeof(sep) / sizeof(char); ++s) {\n"
+ " separator = sep[s];\n"
+ " expect = expect_values[s];\n"
+ "\n"
+ " for (j = 0; j < sizeof(b) / sizeof(char*); ++j) {\n"
+ " for (i = 0; i < sizeof(a) / sizeof(char*); ++i) {\n"
+ " git_buf_join(&buf, separator, a[i], b[j]);\n"
+ " cl_assert_equal_s(*expect, buf.ptr);\n"
+ " expect++;\n"
+ " }\n"
+ " }\n"
+ " }\n";
+ const char *spaced =
+ " for (s = 0; s < sizeof(sep) / sizeof(char); ++s) {\n"
+ " separator = sep[s];\n"
+ " expect = expect_values[s];\n"
+ "\n"
+ " for (j = 0; j < sizeof(b) / sizeof(char*); ++j) {\n"
+ " for (i = 0; i < sizeof(a) / sizeof(char*); ++i) {\n"
+ " git_buf_join(&buf, separator, a[i], b[j]);\n"
+ " cl_assert_equal_s(*expect, buf.ptr);\n"
+ " expect++;\n"
+ " }\n"
+ " }\n"
+ " }\n";
+ const char *crlf_spaced2 =
+ " for (s = 0; s < sizeof(sep) / sizeof(char); ++s) {\r\n"
+ " separator = sep[s];\r\n"
+ " expect = expect_values[s];\r\n"
+ "\r\n"
+ " for (j = 0; j < sizeof(b) / sizeof(char*); ++j) {\r\n"
+ " for (i = 0; i < sizeof(a) / sizeof(char*); ++i) {\r\n"
+ " git_buf_join(&buf, separator, a[i], b[j]);\r\n"
+ " cl_assert_equal_s(*expect, buf.ptr);\r\n"
+ " expect++;\r\n"
+ " }\r\n"
+ " }\r\n"
+ " }\r\n";
+ const char *text[3] = { tabbed, spaced, crlf_spaced2 };
+
+ /* let's try variations of our own code with whitespace changes */
+
+ for (opt = GIT_HASHSIG_NORMAL; opt <= GIT_HASHSIG_SMART_WHITESPACE; ++opt) {
+ for (i = 0; i < 3; ++i) {
+ for (j = 0; j < 3; ++j) {
+ cl_git_pass(git_buf_sets(&buf, text[i]));
+ cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, opt));
+
+ cl_git_pass(git_buf_sets(&buf, text[j]));
+ cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, opt));
+
+ sim = git_hashsig_compare(a, b);
+
+ if (opt == GIT_HASHSIG_NORMAL) {
+ if (i == j)
+ cl_assert_equal_i(100, sim);
+ else
+ cl_assert_in_range(0, sim, 30); /* pretty different */
+ } else {
+ cl_assert_equal_i(100, sim);
+ }
+
+ git_hashsig_free(a);
+ git_hashsig_free(b);
+ }
+ }
+ }
+
+ git_buf_free(&buf);
+}
+
+#include "../filter/crlf.h"
+
+#define check_buf(expected,buf) do { \
+ cl_assert_equal_s(expected, buf.ptr); \
+ cl_assert_equal_sz(strlen(expected), buf.size); } while (0)
+
+void test_core_buffer__lf_and_crlf_conversions(void)
+{
+ git_buf src = GIT_BUF_INIT, tgt = GIT_BUF_INIT;
+
+ /* LF source */
+
+ git_buf_sets(&src, "lf\nlf\nlf\nlf\n");
+
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf("lf\r\nlf\r\nlf\r\nlf\r\n", tgt);
+
+ cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ check_buf(src.ptr, tgt);
+
+ git_buf_sets(&src, "\nlf\nlf\nlf\nlf\nlf");
+
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf("\r\nlf\r\nlf\r\nlf\r\nlf\r\nlf", tgt);
+
+ cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ check_buf(src.ptr, tgt);
+
+ /* CRLF source */
+
+ git_buf_sets(&src, "crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n");
+
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf("crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n", tgt);
+ check_buf(src.ptr, tgt);
+
+ cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ check_buf("crlf\ncrlf\ncrlf\ncrlf\n", tgt);
+
+ git_buf_sets(&src, "\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf");
+
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf("\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf", tgt);
+ check_buf(src.ptr, tgt);
+
+ cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ check_buf("\ncrlf\ncrlf\ncrlf\ncrlf\ncrlf", tgt);
+
+ /* CRLF in LF text */
+
+ git_buf_sets(&src, "\nlf\nlf\ncrlf\r\nlf\nlf\ncrlf\r\n");
+
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf("\r\nlf\r\nlf\r\ncrlf\r\nlf\r\nlf\r\ncrlf\r\n", tgt);
+ cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ check_buf("\nlf\nlf\ncrlf\nlf\nlf\ncrlf\n", tgt);
+
+ /* LF in CRLF text */
+
+ git_buf_sets(&src, "\ncrlf\r\ncrlf\r\nlf\ncrlf\r\ncrlf");
+
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf("\r\ncrlf\r\ncrlf\r\nlf\r\ncrlf\r\ncrlf", tgt);
+ cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ check_buf("\ncrlf\ncrlf\nlf\ncrlf\ncrlf", tgt);
+
+ /* bare CR test */
+
+ git_buf_sets(&src, "\rcrlf\r\nlf\nlf\ncr\rcrlf\r\nlf\ncr\r");
+
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf("\rcrlf\r\nlf\r\nlf\r\ncr\rcrlf\r\nlf\r\ncr\r", tgt);
+ cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ check_buf("\rcrlf\nlf\nlf\ncr\rcrlf\nlf\ncr\r", tgt);
+
+ git_buf_sets(&src, "\rcr\r");
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf(src.ptr, tgt);
+ cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ check_buf("\rcr\r", tgt);
+
+ git_buf_free(&src);
+ git_buf_free(&tgt);
+
+ /* blob correspondence tests */
+
+ git_buf_sets(&src, ALL_CRLF_TEXT_RAW);
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf(ALL_CRLF_TEXT_AS_CRLF, tgt);
+ cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ check_buf(ALL_CRLF_TEXT_AS_LF, tgt);
+ git_buf_free(&src);
+ git_buf_free(&tgt);
+
+ git_buf_sets(&src, ALL_LF_TEXT_RAW);
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf(ALL_LF_TEXT_AS_CRLF, tgt);
+ cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ check_buf(ALL_LF_TEXT_AS_LF, tgt);
+ git_buf_free(&src);
+ git_buf_free(&tgt);
+
+ git_buf_sets(&src, MORE_CRLF_TEXT_RAW);
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf(MORE_CRLF_TEXT_AS_CRLF, tgt);
+ cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ check_buf(MORE_CRLF_TEXT_AS_LF, tgt);
+ git_buf_free(&src);
+ git_buf_free(&tgt);
+
+ git_buf_sets(&src, MORE_LF_TEXT_RAW);
+ cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+ check_buf(MORE_LF_TEXT_AS_CRLF, tgt);
+ cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+ check_buf(MORE_LF_TEXT_AS_LF, tgt);
+ git_buf_free(&src);
+ git_buf_free(&tgt);
+}
diff --git a/tests/core/caps.c b/tests/core/caps.c
new file mode 100644
index 000000000..68a518ed7
--- /dev/null
+++ b/tests/core/caps.c
@@ -0,0 +1,31 @@
+#include "clar_libgit2.h"
+
+void test_core_caps__0(void)
+{
+ int major, minor, rev, caps;
+
+ git_libgit2_version(&major, &minor, &rev);
+ cl_assert_equal_i(LIBGIT2_VER_MAJOR, major);
+ cl_assert_equal_i(LIBGIT2_VER_MINOR, minor);
+ cl_assert_equal_i(LIBGIT2_VER_REVISION, rev);
+
+ caps = git_libgit2_capabilities();
+
+#ifdef GIT_THREADS
+ cl_assert((caps & GIT_CAP_THREADS) != 0);
+#else
+ cl_assert((caps & GIT_CAP_THREADS) == 0);
+#endif
+
+#if defined(GIT_SSL) || defined(GIT_WINHTTP)
+ cl_assert((caps & GIT_CAP_HTTPS) != 0);
+#else
+ cl_assert((caps & GIT_CAP_HTTPS) == 0);
+#endif
+
+#if defined(GIT_SSH)
+ cl_assert((caps & GIT_CAP_SSH) != 0);
+#else
+ cl_assert((caps & GIT_CAP_SSH) == 0);
+#endif
+}
diff --git a/tests-clar/core/copy.c b/tests/core/copy.c
index c0c59c056..c0c59c056 100644
--- a/tests-clar/core/copy.c
+++ b/tests/core/copy.c
diff --git a/tests/core/dirent.c b/tests/core/dirent.c
new file mode 100644
index 000000000..f17260362
--- /dev/null
+++ b/tests/core/dirent.c
@@ -0,0 +1,236 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+
+typedef struct name_data {
+ int count; /* return count */
+ char *name; /* filename */
+} name_data;
+
+typedef struct walk_data {
+ char *sub; /* sub-directory name */
+ name_data *names; /* name state data */
+ git_buf path;
+} walk_data;
+
+
+static char *top_dir = "dir-walk";
+static walk_data *state_loc;
+
+static void setup(walk_data *d)
+{
+ name_data *n;
+
+ cl_must_pass(p_mkdir(top_dir, 0777));
+
+ cl_must_pass(p_chdir(top_dir));
+
+ if (strcmp(d->sub, ".") != 0)
+ cl_must_pass(p_mkdir(d->sub, 0777));
+
+ cl_git_pass(git_buf_sets(&d->path, d->sub));
+
+ state_loc = d;
+
+ for (n = d->names; n->name; n++) {
+ git_file fd = p_creat(n->name, 0666);
+ cl_assert(fd >= 0);
+ p_close(fd);
+ n->count = 0;
+ }
+}
+
+static void dirent_cleanup__cb(void *_d)
+{
+ walk_data *d = _d;
+ name_data *n;
+
+ for (n = d->names; n->name; n++) {
+ cl_must_pass(p_unlink(n->name));
+ }
+
+ if (strcmp(d->sub, ".") != 0)
+ cl_must_pass(p_rmdir(d->sub));
+
+ cl_must_pass(p_chdir(".."));
+
+ cl_must_pass(p_rmdir(top_dir));
+
+ git_buf_free(&d->path);
+}
+
+static void check_counts(walk_data *d)
+{
+ name_data *n;
+
+ for (n = d->names; n->name; n++) {
+ cl_assert(n->count == 1);
+ }
+}
+
+static int one_entry(void *state, git_buf *path)
+{
+ walk_data *d = (walk_data *) state;
+ name_data *n;
+
+ if (state != state_loc)
+ return GIT_ERROR;
+
+ if (path != &d->path)
+ return GIT_ERROR;
+
+ for (n = d->names; n->name; n++) {
+ if (!strcmp(n->name, path->ptr)) {
+ n->count++;
+ return 0;
+ }
+ }
+
+ return GIT_ERROR;
+}
+
+
+static name_data dot_names[] = {
+ { 0, "./a" },
+ { 0, "./asdf" },
+ { 0, "./pack-foo.pack" },
+ { 0, NULL }
+};
+static walk_data dot = {
+ ".",
+ dot_names,
+ GIT_BUF_INIT
+};
+
+/* make sure that the '.' folder is not traversed */
+void test_core_dirent__dont_traverse_dot(void)
+{
+ cl_set_cleanup(&dirent_cleanup__cb, &dot);
+ setup(&dot);
+
+ cl_git_pass(git_path_direach(&dot.path, 0, one_entry, &dot));
+
+ check_counts(&dot);
+}
+
+
+static name_data sub_names[] = {
+ { 0, "sub/a" },
+ { 0, "sub/asdf" },
+ { 0, "sub/pack-foo.pack" },
+ { 0, NULL }
+};
+static walk_data sub = {
+ "sub",
+ sub_names,
+ GIT_BUF_INIT
+};
+
+/* traverse a subfolder */
+void test_core_dirent__traverse_subfolder(void)
+{
+ cl_set_cleanup(&dirent_cleanup__cb, &sub);
+ setup(&sub);
+
+ cl_git_pass(git_path_direach(&sub.path, 0, one_entry, &sub));
+
+ check_counts(&sub);
+}
+
+
+static walk_data sub_slash = {
+ "sub/",
+ sub_names,
+ GIT_BUF_INIT
+};
+
+/* traverse a slash-terminated subfolder */
+void test_core_dirent__traverse_slash_terminated_folder(void)
+{
+ cl_set_cleanup(&dirent_cleanup__cb, &sub_slash);
+ setup(&sub_slash);
+
+ cl_git_pass(git_path_direach(&sub_slash.path, 0, one_entry, &sub_slash));
+
+ check_counts(&sub_slash);
+}
+
+
+static name_data empty_names[] = {
+ { 0, NULL }
+};
+static walk_data empty = {
+ "empty",
+ empty_names,
+ GIT_BUF_INIT
+};
+
+/* make sure that empty folders are not traversed */
+void test_core_dirent__dont_traverse_empty_folders(void)
+{
+ cl_set_cleanup(&dirent_cleanup__cb, &empty);
+ setup(&empty);
+
+ cl_git_pass(git_path_direach(&empty.path, 0, one_entry, &empty));
+
+ check_counts(&empty);
+
+ /* make sure callback not called */
+ cl_assert(git_path_is_empty_dir(empty.path.ptr));
+}
+
+static name_data odd_names[] = {
+ { 0, "odd/.a" },
+ { 0, "odd/..c" },
+ /* the following don't work on cygwin/win32 */
+ /* { 0, "odd/.b." }, */
+ /* { 0, "odd/..d.." }, */
+ { 0, NULL }
+};
+static walk_data odd = {
+ "odd",
+ odd_names,
+ GIT_BUF_INIT
+};
+
+/* make sure that strange looking filenames ('..c') are traversed */
+void test_core_dirent__traverse_weird_filenames(void)
+{
+ cl_set_cleanup(&dirent_cleanup__cb, &odd);
+ setup(&odd);
+
+ cl_git_pass(git_path_direach(&odd.path, 0, one_entry, &odd));
+
+ check_counts(&odd);
+}
+
+/* test filename length limits */
+void test_core_dirent__length_limits(void)
+{
+ char *big_filename = (char *)git__malloc(FILENAME_MAX + 1);
+ memset(big_filename, 'a', FILENAME_MAX + 1);
+ big_filename[FILENAME_MAX] = 0;
+
+ cl_must_fail(p_creat(big_filename, 0666));
+
+ git__free(big_filename);
+}
+
+void test_core_dirent__empty_dir(void)
+{
+ cl_must_pass(p_mkdir("empty_dir", 0777));
+ cl_assert(git_path_is_empty_dir("empty_dir"));
+
+ cl_git_mkfile("empty_dir/content", "whatever\n");
+ cl_assert(!git_path_is_empty_dir("empty_dir"));
+ cl_assert(!git_path_is_empty_dir("empty_dir/content"));
+
+ cl_must_pass(p_unlink("empty_dir/content"));
+
+ cl_must_pass(p_mkdir("empty_dir/content", 0777));
+ cl_assert(!git_path_is_empty_dir("empty_dir"));
+ cl_assert(git_path_is_empty_dir("empty_dir/content"));
+
+ cl_must_pass(p_rmdir("empty_dir/content"));
+
+ cl_must_pass(p_rmdir("empty_dir"));
+}
diff --git a/tests-clar/core/env.c b/tests/core/env.c
index 0fa6472d7..0fa6472d7 100644
--- a/tests-clar/core/env.c
+++ b/tests/core/env.c
diff --git a/tests-clar/core/errors.c b/tests/core/errors.c
index 512a4134d..512a4134d 100644
--- a/tests-clar/core/errors.c
+++ b/tests/core/errors.c
diff --git a/tests/core/filebuf.c b/tests/core/filebuf.c
new file mode 100644
index 000000000..5a3e7510f
--- /dev/null
+++ b/tests/core/filebuf.c
@@ -0,0 +1,126 @@
+#include "clar_libgit2.h"
+#include "filebuf.h"
+
+/* make sure git_filebuf_open doesn't delete an existing lock */
+void test_core_filebuf__0(void)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ int fd;
+ char test[] = "test", testlock[] = "test.lock";
+
+ fd = p_creat(testlock, 0744); //-V536
+
+ cl_must_pass(fd);
+ cl_must_pass(p_close(fd));
+
+ cl_git_fail(git_filebuf_open(&file, test, 0, 0666));
+ cl_assert(git_path_exists(testlock));
+
+ cl_must_pass(p_unlink(testlock));
+}
+
+
+/* make sure GIT_FILEBUF_APPEND works as expected */
+void test_core_filebuf__1(void)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ char test[] = "test";
+
+ cl_git_mkfile(test, "libgit2 rocks\n");
+
+ cl_git_pass(git_filebuf_open(&file, test, GIT_FILEBUF_APPEND, 0666));
+ cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
+ cl_git_pass(git_filebuf_commit(&file));
+
+ cl_assert_equal_file("libgit2 rocks\nlibgit2 rocks\n", 0, test);
+
+ cl_must_pass(p_unlink(test));
+}
+
+
+/* make sure git_filebuf_write writes large buffer correctly */
+void test_core_filebuf__2(void)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ char test[] = "test";
+ unsigned char buf[4096 * 4]; /* 2 * WRITE_BUFFER_SIZE */
+
+ memset(buf, 0xfe, sizeof(buf));
+
+ cl_git_pass(git_filebuf_open(&file, test, 0, 0666));
+ cl_git_pass(git_filebuf_write(&file, buf, sizeof(buf)));
+ cl_git_pass(git_filebuf_commit(&file));
+
+ cl_assert_equal_file((char *)buf, sizeof(buf), test);
+
+ cl_must_pass(p_unlink(test));
+}
+
+/* make sure git_filebuf_cleanup clears the buffer */
+void test_core_filebuf__4(void)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ char test[] = "test";
+
+ cl_assert(file.buffer == NULL);
+
+ cl_git_pass(git_filebuf_open(&file, test, 0, 0666));
+ cl_assert(file.buffer != NULL);
+
+ git_filebuf_cleanup(&file);
+ cl_assert(file.buffer == NULL);
+}
+
+
+/* make sure git_filebuf_commit clears the buffer */
+void test_core_filebuf__5(void)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ char test[] = "test";
+
+ cl_assert(file.buffer == NULL);
+
+ cl_git_pass(git_filebuf_open(&file, test, 0, 0666));
+ cl_assert(file.buffer != NULL);
+ cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
+ cl_assert(file.buffer != NULL);
+
+ cl_git_pass(git_filebuf_commit(&file));
+ cl_assert(file.buffer == NULL);
+
+ cl_must_pass(p_unlink(test));
+}
+
+
+/* make sure git_filebuf_commit takes umask into account */
+void test_core_filebuf__umask(void)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ char test[] = "test";
+ struct stat statbuf;
+ mode_t mask, os_mask;
+
+#ifdef GIT_WIN32
+ os_mask = 0600;
+#else
+ os_mask = 0777;
+#endif
+
+ p_umask(mask = p_umask(0));
+
+ cl_assert(file.buffer == NULL);
+
+ cl_git_pass(git_filebuf_open(&file, test, 0, 0666));
+ cl_assert(file.buffer != NULL);
+ cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
+ cl_assert(file.buffer != NULL);
+
+ cl_git_pass(git_filebuf_commit(&file));
+ cl_assert(file.buffer == NULL);
+
+ cl_must_pass(p_stat("test", &statbuf));
+ cl_assert_equal_i(statbuf.st_mode & os_mask, (0666 & ~mask) & os_mask);
+
+ cl_must_pass(p_unlink(test));
+}
+
diff --git a/tests-clar/core/hex.c b/tests/core/hex.c
index 930af1670..930af1670 100644
--- a/tests-clar/core/hex.c
+++ b/tests/core/hex.c
diff --git a/tests/core/iconv.c b/tests/core/iconv.c
new file mode 100644
index 000000000..8aedab206
--- /dev/null
+++ b/tests/core/iconv.c
@@ -0,0 +1,68 @@
+#include "clar_libgit2.h"
+#include "path.h"
+
+#ifdef GIT_USE_ICONV
+static git_path_iconv_t ic;
+static char *nfc = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D";
+static char *nfd = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D";
+#endif
+
+void test_core_iconv__initialize(void)
+{
+#ifdef GIT_USE_ICONV
+ cl_git_pass(git_path_iconv_init_precompose(&ic));
+#endif
+}
+
+void test_core_iconv__cleanup(void)
+{
+#ifdef GIT_USE_ICONV
+ git_path_iconv_clear(&ic);
+#endif
+}
+
+void test_core_iconv__unchanged(void)
+{
+#ifdef GIT_USE_ICONV
+ char *data = "Ascii data", *original = data;
+ size_t datalen = strlen(data);
+
+ cl_git_pass(git_path_iconv(&ic, &data, &datalen));
+ GIT_UNUSED(datalen);
+
+ /* There are no high bits set, so this should leave data untouched */
+ cl_assert(data == original);
+#endif
+}
+
+void test_core_iconv__decomposed_to_precomposed(void)
+{
+#ifdef GIT_USE_ICONV
+ char *data = nfd;
+ size_t datalen = strlen(nfd);
+
+ cl_git_pass(git_path_iconv(&ic, &data, &datalen));
+ GIT_UNUSED(datalen);
+
+ /* The decomposed nfd string should be transformed to the nfc form
+ * (on platforms where iconv is enabled, of course).
+ */
+ cl_assert_equal_s(nfc, data);
+#endif
+}
+
+void test_core_iconv__precomposed_is_unmodified(void)
+{
+#ifdef GIT_USE_ICONV
+ char *data = nfc;
+ size_t datalen = strlen(nfc);
+
+ cl_git_pass(git_path_iconv(&ic, &data, &datalen));
+ GIT_UNUSED(datalen);
+
+ /* data is already in precomposed form, so even though some bytes have
+ * the high-bit set, the iconv transform should result in no change.
+ */
+ cl_assert_equal_s(nfc, data);
+#endif
+}
diff --git a/tests/core/mkdir.c b/tests/core/mkdir.c
new file mode 100644
index 000000000..a8c5b10ae
--- /dev/null
+++ b/tests/core/mkdir.c
@@ -0,0 +1,188 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+#include "path.h"
+#include "posix.h"
+
+static void cleanup_basic_dirs(void *ref)
+{
+ GIT_UNUSED(ref);
+ git_futils_rmdir_r("d0", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
+ git_futils_rmdir_r("d1", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
+ git_futils_rmdir_r("d2", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
+ git_futils_rmdir_r("d3", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
+ git_futils_rmdir_r("d4", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
+}
+
+void test_core_mkdir__basic(void)
+{
+ cl_set_cleanup(cleanup_basic_dirs, NULL);
+
+ /* make a directory */
+ cl_assert(!git_path_isdir("d0"));
+ cl_git_pass(git_futils_mkdir("d0", NULL, 0755, 0));
+ cl_assert(git_path_isdir("d0"));
+
+ /* make a path */
+ cl_assert(!git_path_isdir("d1"));
+ cl_git_pass(git_futils_mkdir("d1/d1.1/d1.2", NULL, 0755, GIT_MKDIR_PATH));
+ cl_assert(git_path_isdir("d1"));
+ cl_assert(git_path_isdir("d1/d1.1"));
+ cl_assert(git_path_isdir("d1/d1.1/d1.2"));
+
+ /* make a dir exclusively */
+ cl_assert(!git_path_isdir("d2"));
+ cl_git_pass(git_futils_mkdir("d2", NULL, 0755, GIT_MKDIR_EXCL));
+ cl_assert(git_path_isdir("d2"));
+
+ /* make exclusive failure */
+ cl_git_fail(git_futils_mkdir("d2", NULL, 0755, GIT_MKDIR_EXCL));
+
+ /* make a path exclusively */
+ cl_assert(!git_path_isdir("d3"));
+ cl_git_pass(git_futils_mkdir("d3/d3.1/d3.2", NULL, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
+ cl_assert(git_path_isdir("d3"));
+ cl_assert(git_path_isdir("d3/d3.1/d3.2"));
+
+ /* make exclusive path failure */
+ cl_git_fail(git_futils_mkdir("d3/d3.1/d3.2", NULL, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
+ /* ??? Should EXCL only apply to the last item in the path? */
+
+ /* path with trailing slash? */
+ cl_assert(!git_path_isdir("d4"));
+ cl_git_pass(git_futils_mkdir("d4/d4.1/", NULL, 0755, GIT_MKDIR_PATH));
+ cl_assert(git_path_isdir("d4/d4.1"));
+}
+
+static void cleanup_basedir(void *ref)
+{
+ GIT_UNUSED(ref);
+ git_futils_rmdir_r("base", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
+}
+
+void test_core_mkdir__with_base(void)
+{
+#define BASEDIR "base/dir/here"
+
+ cl_set_cleanup(cleanup_basedir, NULL);
+
+ cl_git_pass(git_futils_mkdir(BASEDIR, NULL, 0755, GIT_MKDIR_PATH));
+
+ cl_git_pass(git_futils_mkdir("a", BASEDIR, 0755, 0));
+ cl_assert(git_path_isdir(BASEDIR "/a"));
+
+ cl_git_pass(git_futils_mkdir("b/b1/b2", BASEDIR, 0755, GIT_MKDIR_PATH));
+ cl_assert(git_path_isdir(BASEDIR "/b/b1/b2"));
+
+ /* exclusive with existing base */
+ cl_git_pass(git_futils_mkdir("c/c1/c2", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
+
+ /* fail: exclusive with duplicated suffix */
+ cl_git_fail(git_futils_mkdir("c/c1/c3", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
+
+ /* fail: exclusive with any duplicated component */
+ cl_git_fail(git_futils_mkdir("c/cz/cz", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
+
+ /* success: exclusive without path */
+ cl_git_pass(git_futils_mkdir("c/c1/c3", BASEDIR, 0755, GIT_MKDIR_EXCL));
+
+ /* path with shorter base and existing dirs */
+ cl_git_pass(git_futils_mkdir("dir/here/d/", "base", 0755, GIT_MKDIR_PATH));
+ cl_assert(git_path_isdir("base/dir/here/d"));
+
+ /* fail: path with shorter base and existing dirs */
+ cl_git_fail(git_futils_mkdir("dir/here/e/", "base", 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
+
+ /* fail: base with missing components */
+ cl_git_fail(git_futils_mkdir("f/", "base/missing", 0755, GIT_MKDIR_PATH));
+
+ /* success: shift missing component to path */
+ cl_git_pass(git_futils_mkdir("missing/f/", "base/", 0755, GIT_MKDIR_PATH));
+}
+
+static void cleanup_chmod_root(void *ref)
+{
+ mode_t *mode = ref;
+
+ if (*mode != 0) {
+ (void)p_umask(*mode);
+ git__free(mode);
+ }
+
+ git_futils_rmdir_r("r", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
+}
+
+#define check_mode(X,A) check_mode_at_line((X), (A), __FILE__, __LINE__)
+
+static void check_mode_at_line(
+ mode_t expected, mode_t actual, const char *file, int line)
+{
+ /* FAT filesystems don't support exec bit, nor group/world bits */
+ if (!cl_is_chmod_supported()) {
+ expected &= 0600;
+ actual &= 0600;
+ }
+
+ clar__assert_equal(
+ file, line, "expected_mode != actual_mode", 1,
+ "%07o", (int)expected, (int)(actual & 0777));
+}
+
+void test_core_mkdir__chmods(void)
+{
+ struct stat st;
+ mode_t *old = git__malloc(sizeof(mode_t));
+ *old = p_umask(022);
+
+ cl_set_cleanup(cleanup_chmod_root, old);
+
+ cl_git_pass(git_futils_mkdir("r", NULL, 0777, 0));
+
+ cl_git_pass(git_futils_mkdir("mode/is/important", "r", 0777, GIT_MKDIR_PATH));
+
+ cl_git_pass(git_path_lstat("r/mode", &st));
+ check_mode(0755, st.st_mode);
+ cl_git_pass(git_path_lstat("r/mode/is", &st));
+ check_mode(0755, st.st_mode);
+ cl_git_pass(git_path_lstat("r/mode/is/important", &st));
+ check_mode(0755, st.st_mode);
+
+ cl_git_pass(git_futils_mkdir("mode2/is2/important2", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD));
+
+ cl_git_pass(git_path_lstat("r/mode2", &st));
+ check_mode(0755, st.st_mode);
+ cl_git_pass(git_path_lstat("r/mode2/is2", &st));
+ check_mode(0755, st.st_mode);
+ cl_git_pass(git_path_lstat("r/mode2/is2/important2", &st));
+ check_mode(0777, st.st_mode);
+
+ cl_git_pass(git_futils_mkdir("mode3/is3/important3", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD_PATH));
+
+ cl_git_pass(git_path_lstat("r/mode3", &st));
+ check_mode(0777, st.st_mode);
+ cl_git_pass(git_path_lstat("r/mode3/is3", &st));
+ check_mode(0777, st.st_mode);
+ cl_git_pass(git_path_lstat("r/mode3/is3/important3", &st));
+ check_mode(0777, st.st_mode);
+
+ /* test that we chmod existing dir */
+
+ cl_git_pass(git_futils_mkdir("mode/is/important", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD));
+
+ cl_git_pass(git_path_lstat("r/mode", &st));
+ check_mode(0755, st.st_mode);
+ cl_git_pass(git_path_lstat("r/mode/is", &st));
+ check_mode(0755, st.st_mode);
+ cl_git_pass(git_path_lstat("r/mode/is/important", &st));
+ check_mode(0777, st.st_mode);
+
+ /* test that we chmod even existing dirs if CHMOD_PATH is set */
+
+ cl_git_pass(git_futils_mkdir("mode2/is2/important2.1", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD_PATH));
+
+ cl_git_pass(git_path_lstat("r/mode2", &st));
+ check_mode(0777, st.st_mode);
+ cl_git_pass(git_path_lstat("r/mode2/is2", &st));
+ check_mode(0777, st.st_mode);
+ cl_git_pass(git_path_lstat("r/mode2/is2/important2.1", &st));
+ check_mode(0777, st.st_mode);
+}
diff --git a/tests-clar/core/oid.c b/tests/core/oid.c
index 7ee6fb67d..7ee6fb67d 100644
--- a/tests-clar/core/oid.c
+++ b/tests/core/oid.c
diff --git a/tests-clar/core/oidmap.c b/tests/core/oidmap.c
index ec4b5e775..ec4b5e775 100644
--- a/tests-clar/core/oidmap.c
+++ b/tests/core/oidmap.c
diff --git a/tests-clar/core/opts.c b/tests/core/opts.c
index 3173c648b..3173c648b 100644
--- a/tests-clar/core/opts.c
+++ b/tests/core/opts.c
diff --git a/tests/core/path.c b/tests/core/path.c
new file mode 100644
index 000000000..cf2d5e944
--- /dev/null
+++ b/tests/core/path.c
@@ -0,0 +1,583 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+
+static void
+check_dirname(const char *A, const char *B)
+{
+ git_buf dir = GIT_BUF_INIT;
+ char *dir2;
+
+ cl_assert(git_path_dirname_r(&dir, A) >= 0);
+ cl_assert_equal_s(B, dir.ptr);
+ git_buf_free(&dir);
+
+ cl_assert((dir2 = git_path_dirname(A)) != NULL);
+ cl_assert_equal_s(B, dir2);
+ git__free(dir2);
+}
+
+static void
+check_basename(const char *A, const char *B)
+{
+ git_buf base = GIT_BUF_INIT;
+ char *base2;
+
+ cl_assert(git_path_basename_r(&base, A) >= 0);
+ cl_assert_equal_s(B, base.ptr);
+ git_buf_free(&base);
+
+ cl_assert((base2 = git_path_basename(A)) != NULL);
+ cl_assert_equal_s(B, base2);
+ git__free(base2);
+}
+
+static void
+check_topdir(const char *A, const char *B)
+{
+ const char *dir;
+
+ cl_assert((dir = git_path_topdir(A)) != NULL);
+ cl_assert_equal_s(B, dir);
+}
+
+static void
+check_joinpath(const char *path_a, const char *path_b, const char *expected_path)
+{
+ git_buf joined_path = GIT_BUF_INIT;
+
+ cl_git_pass(git_buf_joinpath(&joined_path, path_a, path_b));
+ cl_assert_equal_s(expected_path, joined_path.ptr);
+
+ git_buf_free(&joined_path);
+}
+
+static void
+check_joinpath_n(
+ const char *path_a,
+ const char *path_b,
+ const char *path_c,
+ const char *path_d,
+ const char *expected_path)
+{
+ git_buf joined_path = GIT_BUF_INIT;
+
+ cl_git_pass(git_buf_join_n(&joined_path, '/', 4,
+ path_a, path_b, path_c, path_d));
+ cl_assert_equal_s(expected_path, joined_path.ptr);
+
+ git_buf_free(&joined_path);
+}
+
+
+/* get the dirname of a path */
+void test_core_path__00_dirname(void)
+{
+ check_dirname(NULL, ".");
+ check_dirname("", ".");
+ check_dirname("a", ".");
+ check_dirname("/", "/");
+ check_dirname("/usr", "/");
+ check_dirname("/usr/", "/");
+ check_dirname("/usr/lib", "/usr");
+ check_dirname("/usr/lib/", "/usr");
+ check_dirname("/usr/lib//", "/usr");
+ check_dirname("usr/lib", "usr");
+ check_dirname("usr/lib/", "usr");
+ check_dirname("usr/lib//", "usr");
+ check_dirname(".git/", ".");
+
+ check_dirname(REP16("/abc"), REP15("/abc"));
+
+#ifdef GIT_WIN32
+ check_dirname("C:/path/", "C:/");
+ check_dirname("C:/path", "C:/");
+ check_dirname("//computername/path/", "//computername/");
+ check_dirname("//computername/path", "//computername/");
+ check_dirname("//computername/sub/path/", "//computername/sub");
+ check_dirname("//computername/sub/path", "//computername/sub");
+#endif
+}
+
+/* get the base name of a path */
+void test_core_path__01_basename(void)
+{
+ check_basename(NULL, ".");
+ check_basename("", ".");
+ check_basename("a", "a");
+ check_basename("/", "/");
+ check_basename("/usr", "usr");
+ check_basename("/usr/", "usr");
+ check_basename("/usr/lib", "lib");
+ check_basename("/usr/lib//", "lib");
+ check_basename("usr/lib", "lib");
+
+ check_basename(REP16("/abc"), "abc");
+ check_basename(REP1024("/abc"), "abc");
+}
+
+/* get the latest component in a path */
+void test_core_path__02_topdir(void)
+{
+ check_topdir(".git/", ".git/");
+ check_topdir("/.git/", ".git/");
+ check_topdir("usr/local/.git/", ".git/");
+ check_topdir("./.git/", ".git/");
+ check_topdir("/usr/.git/", ".git/");
+ check_topdir("/", "/");
+ check_topdir("a/", "a/");
+
+ cl_assert(git_path_topdir("/usr/.git") == NULL);
+ cl_assert(git_path_topdir(".") == NULL);
+ cl_assert(git_path_topdir("") == NULL);
+ cl_assert(git_path_topdir("a") == NULL);
+}
+
+/* properly join path components */
+void test_core_path__05_joins(void)
+{
+ check_joinpath("", "", "");
+ check_joinpath("", "a", "a");
+ check_joinpath("", "/a", "/a");
+ check_joinpath("a", "", "a/");
+ check_joinpath("a", "/", "a/");
+ check_joinpath("a", "b", "a/b");
+ check_joinpath("/", "a", "/a");
+ check_joinpath("/", "", "/");
+ check_joinpath("/a", "/b", "/a/b");
+ check_joinpath("/a", "/b/", "/a/b/");
+ check_joinpath("/a/", "b/", "/a/b/");
+ check_joinpath("/a/", "/b/", "/a/b/");
+
+ check_joinpath("/abcd", "/defg", "/abcd/defg");
+ check_joinpath("/abcd", "/defg/", "/abcd/defg/");
+ check_joinpath("/abcd/", "defg/", "/abcd/defg/");
+ check_joinpath("/abcd/", "/defg/", "/abcd/defg/");
+
+ check_joinpath("/abcdefgh", "/12345678", "/abcdefgh/12345678");
+ check_joinpath("/abcdefgh", "/12345678/", "/abcdefgh/12345678/");
+ check_joinpath("/abcdefgh/", "12345678/", "/abcdefgh/12345678/");
+
+ check_joinpath(REP1024("aaaa"), "", REP1024("aaaa") "/");
+ check_joinpath(REP1024("aaaa/"), "", REP1024("aaaa/"));
+ check_joinpath(REP1024("/aaaa"), "", REP1024("/aaaa") "/");
+
+ check_joinpath(REP1024("aaaa"), REP1024("bbbb"),
+ REP1024("aaaa") "/" REP1024("bbbb"));
+ check_joinpath(REP1024("/aaaa"), REP1024("/bbbb"),
+ REP1024("/aaaa") REP1024("/bbbb"));
+}
+
+/* properly join path components for more than one path */
+void test_core_path__06_long_joins(void)
+{
+ check_joinpath_n("", "", "", "", "");
+ check_joinpath_n("", "a", "", "", "a/");
+ check_joinpath_n("a", "", "", "", "a/");
+ check_joinpath_n("", "", "", "a", "a");
+ check_joinpath_n("a", "b", "", "/c/d/", "a/b/c/d/");
+ check_joinpath_n("a", "b", "", "/c/d", "a/b/c/d");
+ check_joinpath_n("abcd", "efgh", "ijkl", "mnop", "abcd/efgh/ijkl/mnop");
+ check_joinpath_n("abcd/", "efgh/", "ijkl/", "mnop/", "abcd/efgh/ijkl/mnop/");
+ check_joinpath_n("/abcd/", "/efgh/", "/ijkl/", "/mnop/", "/abcd/efgh/ijkl/mnop/");
+
+ check_joinpath_n(REP1024("a"), REP1024("b"), REP1024("c"), REP1024("d"),
+ REP1024("a") "/" REP1024("b") "/"
+ REP1024("c") "/" REP1024("d"));
+ check_joinpath_n(REP1024("/a"), REP1024("/b"), REP1024("/c"), REP1024("/d"),
+ REP1024("/a") REP1024("/b")
+ REP1024("/c") REP1024("/d"));
+}
+
+
+static void
+check_path_to_dir(
+ const char* path,
+ const char* expected)
+{
+ git_buf tgt = GIT_BUF_INIT;
+
+ git_buf_sets(&tgt, path);
+ cl_git_pass(git_path_to_dir(&tgt));
+ cl_assert_equal_s(expected, tgt.ptr);
+
+ git_buf_free(&tgt);
+}
+
+static void
+check_string_to_dir(
+ const char* path,
+ size_t maxlen,
+ const char* expected)
+{
+ size_t len = strlen(path);
+ char *buf = git__malloc(len + 2);
+ cl_assert(buf);
+
+ strncpy(buf, path, len + 2);
+
+ git_path_string_to_dir(buf, maxlen);
+
+ cl_assert_equal_s(expected, buf);
+
+ git__free(buf);
+}
+
+/* convert paths to dirs */
+void test_core_path__07_path_to_dir(void)
+{
+ check_path_to_dir("", "");
+ check_path_to_dir(".", "./");
+ check_path_to_dir("./", "./");
+ check_path_to_dir("a/", "a/");
+ check_path_to_dir("ab", "ab/");
+ /* make sure we try just under and just over an expansion that will
+ * require a realloc
+ */
+ check_path_to_dir("abcdef", "abcdef/");
+ check_path_to_dir("abcdefg", "abcdefg/");
+ check_path_to_dir("abcdefgh", "abcdefgh/");
+ check_path_to_dir("abcdefghi", "abcdefghi/");
+ check_path_to_dir(REP1024("abcd") "/", REP1024("abcd") "/");
+ check_path_to_dir(REP1024("abcd"), REP1024("abcd") "/");
+
+ check_string_to_dir("", 1, "");
+ check_string_to_dir(".", 1, ".");
+ check_string_to_dir(".", 2, "./");
+ check_string_to_dir(".", 3, "./");
+ check_string_to_dir("abcd", 3, "abcd");
+ check_string_to_dir("abcd", 4, "abcd");
+ check_string_to_dir("abcd", 5, "abcd/");
+ check_string_to_dir("abcd", 6, "abcd/");
+}
+
+/* join path to itself */
+void test_core_path__08_self_join(void)
+{
+ git_buf path = GIT_BUF_INIT;
+ size_t asize = 0;
+
+ asize = path.asize;
+ cl_git_pass(git_buf_sets(&path, "/foo"));
+ cl_assert_equal_s(path.ptr, "/foo");
+ cl_assert(asize < path.asize);
+
+ asize = path.asize;
+ cl_git_pass(git_buf_joinpath(&path, path.ptr, "this is a new string"));
+ cl_assert_equal_s(path.ptr, "/foo/this is a new string");
+ cl_assert(asize < path.asize);
+
+ asize = path.asize;
+ cl_git_pass(git_buf_joinpath(&path, path.ptr, "/grow the buffer, grow the buffer, grow the buffer"));
+ cl_assert_equal_s(path.ptr, "/foo/this is a new string/grow the buffer, grow the buffer, grow the buffer");
+ cl_assert(asize < path.asize);
+
+ git_buf_free(&path);
+ cl_git_pass(git_buf_sets(&path, "/foo/bar"));
+
+ cl_git_pass(git_buf_joinpath(&path, path.ptr + 4, "baz"));
+ cl_assert_equal_s(path.ptr, "/bar/baz");
+
+ asize = path.asize;
+ cl_git_pass(git_buf_joinpath(&path, path.ptr + 4, "somethinglongenoughtorealloc"));
+ cl_assert_equal_s(path.ptr, "/baz/somethinglongenoughtorealloc");
+ cl_assert(asize < path.asize);
+
+ git_buf_free(&path);
+}
+
+static void check_percent_decoding(const char *expected_result, const char *input)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git__percent_decode(&buf, input));
+ cl_assert_equal_s(expected_result, git_buf_cstr(&buf));
+
+ git_buf_free(&buf);
+}
+
+void test_core_path__09_percent_decode(void)
+{
+ check_percent_decoding("abcd", "abcd");
+ check_percent_decoding("a2%", "a2%");
+ check_percent_decoding("a2%3", "a2%3");
+ check_percent_decoding("a2%%3", "a2%%3");
+ check_percent_decoding("a2%3z", "a2%3z");
+ check_percent_decoding("a,", "a%2c");
+ check_percent_decoding("a21", "a2%31");
+ check_percent_decoding("a2%1", "a2%%31");
+ check_percent_decoding("a bc ", "a%20bc%20");
+ check_percent_decoding("Vicent Mart" "\355", "Vicent%20Mart%ED");
+}
+
+static void check_fromurl(const char *expected_result, const char *input, int should_fail)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ assert(should_fail || expected_result);
+
+ if (!should_fail) {
+ cl_git_pass(git_path_fromurl(&buf, input));
+ cl_assert_equal_s(expected_result, git_buf_cstr(&buf));
+ } else
+ cl_git_fail(git_path_fromurl(&buf, input));
+
+ git_buf_free(&buf);
+}
+
+#ifdef GIT_WIN32
+#define ABS_PATH_MARKER ""
+#else
+#define ABS_PATH_MARKER "/"
+#endif
+
+void test_core_path__10_fromurl(void)
+{
+ /* Failing cases */
+ check_fromurl(NULL, "a", 1);
+ check_fromurl(NULL, "http:///c:/Temp%20folder/note.txt", 1);
+ check_fromurl(NULL, "file://c:/Temp%20folder/note.txt", 1);
+ check_fromurl(NULL, "file:////c:/Temp%20folder/note.txt", 1);
+ check_fromurl(NULL, "file:///", 1);
+ check_fromurl(NULL, "file:////", 1);
+ check_fromurl(NULL, "file://servername/c:/Temp%20folder/note.txt", 1);
+
+ /* Passing cases */
+ check_fromurl(ABS_PATH_MARKER "c:/Temp folder/note.txt", "file:///c:/Temp%20folder/note.txt", 0);
+ check_fromurl(ABS_PATH_MARKER "c:/Temp folder/note.txt", "file://localhost/c:/Temp%20folder/note.txt", 0);
+ check_fromurl(ABS_PATH_MARKER "c:/Temp+folder/note.txt", "file:///c:/Temp+folder/note.txt", 0);
+ check_fromurl(ABS_PATH_MARKER "a", "file:///a", 0);
+}
+
+typedef struct {
+ int expect_idx;
+ char **expect;
+} check_walkup_info;
+
+static int check_one_walkup_step(void *ref, git_buf *path)
+{
+ check_walkup_info *info = (check_walkup_info *)ref;
+ cl_assert(info->expect[info->expect_idx] != NULL);
+ cl_assert_equal_s(info->expect[info->expect_idx], path->ptr);
+ info->expect_idx++;
+ return 0;
+}
+
+void test_core_path__11_walkup(void)
+{
+ git_buf p = GIT_BUF_INIT;
+ char *expect[] = {
+ "/a/b/c/d/e/", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
+ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
+ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
+ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
+ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", NULL,
+ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", NULL,
+ "this is a path", NULL,
+ "///a///b///c///d///e///", "///a///b///c///d///", "///a///b///c///", "///a///b///", "///a///", "///", NULL,
+ NULL
+ };
+ char *root[] = { NULL, NULL, "/", "", "/a/b", "/a/b/", NULL, NULL, NULL };
+ int i, j;
+ check_walkup_info info;
+
+ info.expect = expect;
+
+ for (i = 0, j = 0; expect[i] != NULL; i++, j++) {
+
+ git_buf_sets(&p, expect[i]);
+
+ info.expect_idx = i;
+ cl_git_pass(
+ git_path_walk_up(&p, root[j], check_one_walkup_step, &info)
+ );
+
+ cl_assert_equal_s(p.ptr, expect[i]);
+
+ /* skip to next run of expectations */
+ while (expect[i] != NULL) i++;
+ }
+
+ git_buf_free(&p);
+}
+
+void test_core_path__12_offset_to_path_root(void)
+{
+ cl_assert(git_path_root("non/rooted/path") == -1);
+ cl_assert(git_path_root("/rooted/path") == 0);
+
+#ifdef GIT_WIN32
+ /* Windows specific tests */
+ cl_assert(git_path_root("C:non/rooted/path") == -1);
+ cl_assert(git_path_root("C:/rooted/path") == 2);
+ cl_assert(git_path_root("//computername/sharefolder/resource") == 14);
+ cl_assert(git_path_root("//computername/sharefolder") == 14);
+ cl_assert(git_path_root("//computername") == -1);
+#endif
+}
+
+#define NON_EXISTING_FILEPATH "i_hope_i_do_not_exist"
+
+void test_core_path__13_cannot_prettify_a_non_existing_file(void)
+{
+ git_buf p = GIT_BUF_INIT;
+
+ cl_must_pass(git_path_exists(NON_EXISTING_FILEPATH) == false);
+ cl_assert_equal_i(GIT_ENOTFOUND, git_path_prettify(&p, NON_EXISTING_FILEPATH, NULL));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_path_prettify(&p, NON_EXISTING_FILEPATH "/so-do-i", NULL));
+
+ git_buf_free(&p);
+}
+
+void test_core_path__14_apply_relative(void)
+{
+ git_buf p = GIT_BUF_INIT;
+
+ cl_git_pass(git_buf_sets(&p, "/this/is/a/base"));
+
+ cl_git_pass(git_path_apply_relative(&p, "../test"));
+ cl_assert_equal_s("/this/is/a/test", p.ptr);
+
+ cl_git_pass(git_path_apply_relative(&p, "../../the/./end"));
+ cl_assert_equal_s("/this/is/the/end", p.ptr);
+
+ cl_git_pass(git_path_apply_relative(&p, "./of/this/../the/string"));
+ cl_assert_equal_s("/this/is/the/end/of/the/string", p.ptr);
+
+ cl_git_pass(git_path_apply_relative(&p, "../../../../../.."));
+ cl_assert_equal_s("/this/", p.ptr);
+
+ cl_git_pass(git_path_apply_relative(&p, "../"));
+ cl_assert_equal_s("/", p.ptr);
+
+ cl_git_fail(git_path_apply_relative(&p, "../../.."));
+
+
+ cl_git_pass(git_buf_sets(&p, "d:/another/test"));
+
+ cl_git_pass(git_path_apply_relative(&p, "../.."));
+ cl_assert_equal_s("d:/", p.ptr);
+
+ cl_git_pass(git_path_apply_relative(&p, "from/here/to/../and/./back/."));
+ cl_assert_equal_s("d:/from/here/and/back/", p.ptr);
+
+
+ cl_git_pass(git_buf_sets(&p, "https://my.url.com/test.git"));
+
+ cl_git_pass(git_path_apply_relative(&p, "../another.git"));
+ cl_assert_equal_s("https://my.url.com/another.git", p.ptr);
+
+ cl_git_pass(git_path_apply_relative(&p, "../full/path/url.patch"));
+ cl_assert_equal_s("https://my.url.com/full/path/url.patch", p.ptr);
+
+ cl_git_pass(git_path_apply_relative(&p, ".."));
+ cl_assert_equal_s("https://my.url.com/full/path/", p.ptr);
+
+ cl_git_pass(git_path_apply_relative(&p, "../../../"));
+ cl_assert_equal_s("https://", p.ptr);
+
+
+ cl_git_pass(git_buf_sets(&p, "../../this/is/relative"));
+
+ cl_git_pass(git_path_apply_relative(&p, "../../preserves/the/prefix"));
+ cl_assert_equal_s("../../this/preserves/the/prefix", p.ptr);
+
+ cl_git_pass(git_path_apply_relative(&p, "../../../../that"));
+ cl_assert_equal_s("../../that", p.ptr);
+
+ cl_git_pass(git_path_apply_relative(&p, "../there"));
+ cl_assert_equal_s("../../there", p.ptr);
+ git_buf_free(&p);
+}
+
+static void assert_resolve_relative(
+ git_buf *buf, const char *expected, const char *path)
+{
+ cl_git_pass(git_buf_sets(buf, path));
+ cl_git_pass(git_path_resolve_relative(buf, 0));
+ cl_assert_equal_s(expected, buf->ptr);
+}
+
+void test_core_path__15_resolve_relative(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ assert_resolve_relative(&buf, "", "");
+ assert_resolve_relative(&buf, "", ".");
+ assert_resolve_relative(&buf, "", "./");
+ assert_resolve_relative(&buf, "..", "..");
+ assert_resolve_relative(&buf, "../", "../");
+ assert_resolve_relative(&buf, "..", "./..");
+ assert_resolve_relative(&buf, "../", "./../");
+ assert_resolve_relative(&buf, "../", "../.");
+ assert_resolve_relative(&buf, "../", ".././");
+ assert_resolve_relative(&buf, "../..", "../..");
+ assert_resolve_relative(&buf, "../../", "../../");
+
+ assert_resolve_relative(&buf, "/", "/");
+ assert_resolve_relative(&buf, "/", "/.");
+
+ assert_resolve_relative(&buf, "", "a/..");
+ assert_resolve_relative(&buf, "", "a/../");
+ assert_resolve_relative(&buf, "", "a/../.");
+
+ assert_resolve_relative(&buf, "/a", "/a");
+ assert_resolve_relative(&buf, "/a/", "/a/.");
+ assert_resolve_relative(&buf, "/", "/a/../");
+ assert_resolve_relative(&buf, "/", "/a/../.");
+ assert_resolve_relative(&buf, "/", "/a/.././");
+
+ assert_resolve_relative(&buf, "a", "a");
+ assert_resolve_relative(&buf, "a/", "a/");
+ assert_resolve_relative(&buf, "a/", "a/.");
+ assert_resolve_relative(&buf, "a/", "a/./");
+
+ assert_resolve_relative(&buf, "a/b", "a//b");
+ assert_resolve_relative(&buf, "a/b/c", "a/b/c");
+ assert_resolve_relative(&buf, "b/c", "./b/c");
+ assert_resolve_relative(&buf, "a/c", "a/./c");
+ assert_resolve_relative(&buf, "a/b/", "a/b/.");
+
+ assert_resolve_relative(&buf, "/a/b/c", "///a/b/c");
+ assert_resolve_relative(&buf, "/", "////");
+ assert_resolve_relative(&buf, "/a", "///a");
+ assert_resolve_relative(&buf, "/", "///.");
+ assert_resolve_relative(&buf, "/", "///a/..");
+
+ assert_resolve_relative(&buf, "../../path", "../../test//../././path");
+ assert_resolve_relative(&buf, "../d", "a/b/../../../c/../d");
+
+ cl_git_pass(git_buf_sets(&buf, "/.."));
+ cl_git_fail(git_path_resolve_relative(&buf, 0));
+
+ cl_git_pass(git_buf_sets(&buf, "/./.."));
+ cl_git_fail(git_path_resolve_relative(&buf, 0));
+
+ cl_git_pass(git_buf_sets(&buf, "/.//.."));
+ cl_git_fail(git_path_resolve_relative(&buf, 0));
+
+ cl_git_pass(git_buf_sets(&buf, "/../."));
+ cl_git_fail(git_path_resolve_relative(&buf, 0));
+
+ cl_git_pass(git_buf_sets(&buf, "/../.././../a"));
+ cl_git_fail(git_path_resolve_relative(&buf, 0));
+
+ cl_git_pass(git_buf_sets(&buf, "////.."));
+ cl_git_fail(git_path_resolve_relative(&buf, 0));
+
+ /* things that start with Windows network paths */
+#ifdef GIT_WIN32
+ assert_resolve_relative(&buf, "//a/b/c", "//a/b/c");
+ assert_resolve_relative(&buf, "//a/", "//a/b/..");
+ assert_resolve_relative(&buf, "//a/b/c", "//a/Q/../b/x/y/../../c");
+
+ cl_git_pass(git_buf_sets(&buf, "//a/b/../.."));
+ cl_git_fail(git_path_resolve_relative(&buf, 0));
+#else
+ assert_resolve_relative(&buf, "/a/b/c", "//a/b/c");
+ assert_resolve_relative(&buf, "/a/", "//a/b/..");
+ assert_resolve_relative(&buf, "/a/b/c", "//a/Q/../b/x/y/../../c");
+ assert_resolve_relative(&buf, "/", "//a/b/../..");
+#endif
+
+ git_buf_free(&buf);
+}
diff --git a/tests-clar/core/pool.c b/tests/core/pool.c
index 3073c4a45..3073c4a45 100644
--- a/tests-clar/core/pool.c
+++ b/tests/core/pool.c
diff --git a/tests/core/posix.c b/tests/core/posix.c
new file mode 100644
index 000000000..1cef937cd
--- /dev/null
+++ b/tests/core/posix.c
@@ -0,0 +1,99 @@
+#ifndef _WIN32
+# include <arpa/inet.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+#else
+# include <ws2tcpip.h>
+# ifdef _MSC_VER
+# pragma comment(lib, "ws2_32")
+# endif
+#endif
+
+#include "clar_libgit2.h"
+#include "posix.h"
+
+void test_core_posix__initialize(void)
+{
+#ifdef GIT_WIN32
+ /* on win32, the WSA context needs to be initialized
+ * before any socket calls can be performed */
+ WSADATA wsd;
+
+ cl_git_pass(WSAStartup(MAKEWORD(2,2), &wsd));
+ cl_assert(LOBYTE(wsd.wVersion) == 2 && HIBYTE(wsd.wVersion) == 2);
+#endif
+}
+
+static bool supports_ipv6(void)
+{
+#ifdef GIT_WIN32
+ /* IPv6 is supported on Vista and newer */
+ return git_has_win32_version(6, 0, 0);
+#else
+ return 1;
+#endif
+}
+
+void test_core_posix__inet_pton(void)
+{
+ struct in_addr addr;
+ struct in6_addr addr6;
+ size_t i;
+
+ struct in_addr_data {
+ const char *p;
+ const uint8_t n[4];
+ };
+
+ struct in6_addr_data {
+ const char *p;
+ const uint8_t n[16];
+ };
+
+ static struct in_addr_data in_addr_data[] = {
+ { "0.0.0.0", { 0, 0, 0, 0 } },
+ { "10.42.101.8", { 10, 42, 101, 8 } },
+ { "127.0.0.1", { 127, 0, 0, 1 } },
+ { "140.177.10.12", { 140, 177, 10, 12 } },
+ { "204.232.175.90", { 204, 232, 175, 90 } },
+ { "255.255.255.255", { 255, 255, 255, 255 } },
+ };
+
+ static struct in6_addr_data in6_addr_data[] = {
+ { "::", { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { "::1", { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } },
+ { "0:0:0:0:0:0:0:1", { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } },
+ { "2001:db8:8714:3a90::12", { 0x20, 0x01, 0x0d, 0xb8, 0x87, 0x14, 0x3a, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12 } },
+ { "fe80::f8ba:c2d6:86be:3645", { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xba, 0xc2, 0xd6, 0x86, 0xbe, 0x36, 0x45 } },
+ { "::ffff:204.152.189.116", { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0x98, 0xbd, 0x74 } },
+ };
+
+ /* Test some ipv4 addresses */
+ for (i = 0; i < 6; i++) {
+ cl_assert(p_inet_pton(AF_INET, in_addr_data[i].p, &addr) == 1);
+ cl_assert(memcmp(&addr, in_addr_data[i].n, sizeof(struct in_addr)) == 0);
+ }
+
+ /* Test some ipv6 addresses */
+ if (supports_ipv6())
+ {
+ for (i = 0; i < 6; i++) {
+ cl_assert(p_inet_pton(AF_INET6, in6_addr_data[i].p, &addr6) == 1);
+ cl_assert(memcmp(&addr6, in6_addr_data[i].n, sizeof(struct in6_addr)) == 0);
+ }
+ }
+
+ /* Test some invalid strings */
+ cl_assert(p_inet_pton(AF_INET, "", &addr) == 0);
+ cl_assert(p_inet_pton(AF_INET, "foo", &addr) == 0);
+ cl_assert(p_inet_pton(AF_INET, " 127.0.0.1", &addr) == 0);
+ cl_assert(p_inet_pton(AF_INET, "bar", &addr) == 0);
+ cl_assert(p_inet_pton(AF_INET, "10.foo.bar.1", &addr) == 0);
+
+ /* Test unsupported address families */
+ cl_git_fail(p_inet_pton(12, "52.472", NULL)); /* AF_DECnet */
+ cl_assert_equal_i(EAFNOSUPPORT, errno);
+
+ cl_git_fail(p_inet_pton(5, "315.124", NULL)); /* AF_CHAOS */
+ cl_assert_equal_i(EAFNOSUPPORT, errno);
+}
diff --git a/tests-clar/core/rmdir.c b/tests/core/rmdir.c
index f0b0bfa42..f0b0bfa42 100644
--- a/tests-clar/core/rmdir.c
+++ b/tests/core/rmdir.c
diff --git a/tests/core/sortedcache.c b/tests/core/sortedcache.c
new file mode 100644
index 000000000..c1869bee0
--- /dev/null
+++ b/tests/core/sortedcache.c
@@ -0,0 +1,363 @@
+#include "clar_libgit2.h"
+#include "sortedcache.h"
+
+static int name_only_cmp(const void *a, const void *b)
+{
+ return strcmp(a, b);
+}
+
+void test_core_sortedcache__name_only(void)
+{
+ git_sortedcache *sc;
+ void *item;
+ size_t pos;
+
+ cl_git_pass(git_sortedcache_new(
+ &sc, 0, NULL, NULL, name_only_cmp, NULL));
+
+ cl_git_pass(git_sortedcache_wlock(sc));
+ cl_git_pass(git_sortedcache_upsert(&item, sc, "aaa"));
+ cl_git_pass(git_sortedcache_upsert(&item, sc, "bbb"));
+ cl_git_pass(git_sortedcache_upsert(&item, sc, "zzz"));
+ cl_git_pass(git_sortedcache_upsert(&item, sc, "mmm"));
+ cl_git_pass(git_sortedcache_upsert(&item, sc, "iii"));
+ git_sortedcache_wunlock(sc);
+
+ cl_assert_equal_sz(5, git_sortedcache_entrycount(sc));
+
+ cl_assert((item = git_sortedcache_lookup(sc, "aaa")) != NULL);
+ cl_assert_equal_s("aaa", item);
+ cl_assert((item = git_sortedcache_lookup(sc, "mmm")) != NULL);
+ cl_assert_equal_s("mmm", item);
+ cl_assert((item = git_sortedcache_lookup(sc, "zzz")) != NULL);
+ cl_assert_equal_s("zzz", item);
+ cl_assert(git_sortedcache_lookup(sc, "qqq") == NULL);
+
+ cl_assert((item = git_sortedcache_entry(sc, 0)) != NULL);
+ cl_assert_equal_s("aaa", item);
+ cl_assert((item = git_sortedcache_entry(sc, 1)) != NULL);
+ cl_assert_equal_s("bbb", item);
+ cl_assert((item = git_sortedcache_entry(sc, 2)) != NULL);
+ cl_assert_equal_s("iii", item);
+ cl_assert((item = git_sortedcache_entry(sc, 3)) != NULL);
+ cl_assert_equal_s("mmm", item);
+ cl_assert((item = git_sortedcache_entry(sc, 4)) != NULL);
+ cl_assert_equal_s("zzz", item);
+ cl_assert(git_sortedcache_entry(sc, 5) == NULL);
+
+ cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "aaa"));
+ cl_assert_equal_sz(0, pos);
+ cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "iii"));
+ cl_assert_equal_sz(2, pos);
+ cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "zzz"));
+ cl_assert_equal_sz(4, pos);
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "abc"));
+
+ git_sortedcache_clear(sc, true);
+
+ cl_assert_equal_sz(0, git_sortedcache_entrycount(sc));
+ cl_assert(git_sortedcache_entry(sc, 0) == NULL);
+ cl_assert(git_sortedcache_lookup(sc, "aaa") == NULL);
+ cl_assert(git_sortedcache_entry(sc, 0) == NULL);
+
+ git_sortedcache_free(sc);
+}
+
+typedef struct {
+ int value;
+ char smaller_value;
+ char path[GIT_FLEX_ARRAY];
+} sortedcache_test_struct;
+
+static int sortedcache_test_struct_cmp(const void *a_, const void *b_)
+{
+ const sortedcache_test_struct *a = a_, *b = b_;
+ return strcmp(a->path, b->path);
+}
+
+static void sortedcache_test_struct_free(void *payload, void *item_)
+{
+ sortedcache_test_struct *item = item_;
+ int *count = payload;
+ (*count)++;
+ item->smaller_value = 0;
+}
+
+void test_core_sortedcache__in_memory(void)
+{
+ git_sortedcache *sc;
+ sortedcache_test_struct *item;
+ int free_count = 0;
+
+ cl_git_pass(git_sortedcache_new(
+ &sc, offsetof(sortedcache_test_struct, path),
+ sortedcache_test_struct_free, &free_count,
+ sortedcache_test_struct_cmp, NULL));
+
+ cl_git_pass(git_sortedcache_wlock(sc));
+ cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "aaa"));
+ item->value = 10;
+ item->smaller_value = 1;
+ cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "bbb"));
+ item->value = 20;
+ item->smaller_value = 2;
+ cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "zzz"));
+ item->value = 30;
+ item->smaller_value = 26;
+ cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "mmm"));
+ item->value = 40;
+ item->smaller_value = 14;
+ cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "iii"));
+ item->value = 50;
+ item->smaller_value = 9;
+ git_sortedcache_wunlock(sc);
+
+ cl_assert_equal_sz(5, git_sortedcache_entrycount(sc));
+
+ cl_git_pass(git_sortedcache_rlock(sc));
+
+ cl_assert((item = git_sortedcache_lookup(sc, "aaa")) != NULL);
+ cl_assert_equal_s("aaa", item->path);
+ cl_assert_equal_i(10, item->value);
+ cl_assert((item = git_sortedcache_lookup(sc, "mmm")) != NULL);
+ cl_assert_equal_s("mmm", item->path);
+ cl_assert_equal_i(40, item->value);
+ cl_assert((item = git_sortedcache_lookup(sc, "zzz")) != NULL);
+ cl_assert_equal_s("zzz", item->path);
+ cl_assert_equal_i(30, item->value);
+ cl_assert(git_sortedcache_lookup(sc, "abc") == NULL);
+
+ /* not on Windows:
+ * cl_git_pass(git_sortedcache_rlock(sc)); -- grab more than one
+ */
+
+ cl_assert((item = git_sortedcache_entry(sc, 0)) != NULL);
+ cl_assert_equal_s("aaa", item->path);
+ cl_assert_equal_i(10, item->value);
+ cl_assert((item = git_sortedcache_entry(sc, 1)) != NULL);
+ cl_assert_equal_s("bbb", item->path);
+ cl_assert_equal_i(20, item->value);
+ cl_assert((item = git_sortedcache_entry(sc, 2)) != NULL);
+ cl_assert_equal_s("iii", item->path);
+ cl_assert_equal_i(50, item->value);
+ cl_assert((item = git_sortedcache_entry(sc, 3)) != NULL);
+ cl_assert_equal_s("mmm", item->path);
+ cl_assert_equal_i(40, item->value);
+ cl_assert((item = git_sortedcache_entry(sc, 4)) != NULL);
+ cl_assert_equal_s("zzz", item->path);
+ cl_assert_equal_i(30, item->value);
+ cl_assert(git_sortedcache_entry(sc, 5) == NULL);
+
+ git_sortedcache_runlock(sc);
+ /* git_sortedcache_runlock(sc); */
+
+ cl_assert_equal_i(0, free_count);
+
+ git_sortedcache_clear(sc, true);
+
+ cl_assert_equal_i(5, free_count);
+
+ cl_assert_equal_sz(0, git_sortedcache_entrycount(sc));
+ cl_assert(git_sortedcache_entry(sc, 0) == NULL);
+ cl_assert(git_sortedcache_lookup(sc, "aaa") == NULL);
+ cl_assert(git_sortedcache_entry(sc, 0) == NULL);
+
+ free_count = 0;
+
+ cl_git_pass(git_sortedcache_wlock(sc));
+ cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "testing"));
+ item->value = 10;
+ item->smaller_value = 3;
+ cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "again"));
+ item->value = 20;
+ item->smaller_value = 1;
+ cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "final"));
+ item->value = 30;
+ item->smaller_value = 2;
+ git_sortedcache_wunlock(sc);
+
+ cl_assert_equal_sz(3, git_sortedcache_entrycount(sc));
+
+ cl_assert((item = git_sortedcache_lookup(sc, "testing")) != NULL);
+ cl_assert_equal_s("testing", item->path);
+ cl_assert_equal_i(10, item->value);
+ cl_assert((item = git_sortedcache_lookup(sc, "again")) != NULL);
+ cl_assert_equal_s("again", item->path);
+ cl_assert_equal_i(20, item->value);
+ cl_assert((item = git_sortedcache_lookup(sc, "final")) != NULL);
+ cl_assert_equal_s("final", item->path);
+ cl_assert_equal_i(30, item->value);
+ cl_assert(git_sortedcache_lookup(sc, "zzz") == NULL);
+
+ cl_assert((item = git_sortedcache_entry(sc, 0)) != NULL);
+ cl_assert_equal_s("again", item->path);
+ cl_assert_equal_i(20, item->value);
+ cl_assert((item = git_sortedcache_entry(sc, 1)) != NULL);
+ cl_assert_equal_s("final", item->path);
+ cl_assert_equal_i(30, item->value);
+ cl_assert((item = git_sortedcache_entry(sc, 2)) != NULL);
+ cl_assert_equal_s("testing", item->path);
+ cl_assert_equal_i(10, item->value);
+ cl_assert(git_sortedcache_entry(sc, 3) == NULL);
+
+ {
+ size_t pos;
+
+ cl_git_pass(git_sortedcache_wlock(sc));
+
+ cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "again"));
+ cl_assert_equal_sz(0, pos);
+ cl_git_pass(git_sortedcache_remove(sc, pos));
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "again"));
+
+ cl_assert_equal_sz(2, git_sortedcache_entrycount(sc));
+
+ cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "testing"));
+ cl_assert_equal_sz(1, pos);
+ cl_git_pass(git_sortedcache_remove(sc, pos));
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "testing"));
+
+ cl_assert_equal_sz(1, git_sortedcache_entrycount(sc));
+
+ cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "final"));
+ cl_assert_equal_sz(0, pos);
+ cl_git_pass(git_sortedcache_remove(sc, pos));
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "final"));
+
+ cl_assert_equal_sz(0, git_sortedcache_entrycount(sc));
+
+ git_sortedcache_wunlock(sc);
+ }
+
+ git_sortedcache_free(sc);
+
+ cl_assert_equal_i(3, free_count);
+}
+
+static void sortedcache_test_reload(git_sortedcache *sc)
+{
+ int count = 0;
+ git_buf buf = GIT_BUF_INIT;
+ char *scan, *after;
+ sortedcache_test_struct *item;
+
+ cl_assert(git_sortedcache_lockandload(sc, &buf) > 0);
+
+ git_sortedcache_clear(sc, false); /* clear once we already have lock */
+
+ for (scan = buf.ptr; *scan; scan = after + 1) {
+ int val = strtol(scan, &after, 0);
+ cl_assert(after > scan);
+ scan = after;
+
+ for (scan = after; git__isspace(*scan); ++scan) /* find start */;
+ for (after = scan; *after && *after != '\n'; ++after) /* find eol */;
+ *after = '\0';
+
+ cl_git_pass(git_sortedcache_upsert((void **)&item, sc, scan));
+
+ item->value = val;
+ item->smaller_value = (char)(count++);
+ }
+
+ git_sortedcache_wunlock(sc);
+
+ git_buf_free(&buf);
+}
+
+void test_core_sortedcache__on_disk(void)
+{
+ git_sortedcache *sc;
+ sortedcache_test_struct *item;
+ int free_count = 0;
+ size_t pos;
+
+ cl_git_mkfile("cacheitems.txt", "10 abc\n20 bcd\n30 cde\n");
+
+ cl_git_pass(git_sortedcache_new(
+ &sc, offsetof(sortedcache_test_struct, path),
+ sortedcache_test_struct_free, &free_count,
+ sortedcache_test_struct_cmp, "cacheitems.txt"));
+
+ /* should need to reload the first time */
+
+ sortedcache_test_reload(sc);
+
+ /* test what we loaded */
+
+ cl_assert_equal_sz(3, git_sortedcache_entrycount(sc));
+
+ cl_assert((item = git_sortedcache_lookup(sc, "abc")) != NULL);
+ cl_assert_equal_s("abc", item->path);
+ cl_assert_equal_i(10, item->value);
+ cl_assert((item = git_sortedcache_lookup(sc, "cde")) != NULL);
+ cl_assert_equal_s("cde", item->path);
+ cl_assert_equal_i(30, item->value);
+ cl_assert(git_sortedcache_lookup(sc, "aaa") == NULL);
+
+ cl_assert((item = git_sortedcache_entry(sc, 0)) != NULL);
+ cl_assert_equal_s("abc", item->path);
+ cl_assert_equal_i(10, item->value);
+ cl_assert((item = git_sortedcache_entry(sc, 1)) != NULL);
+ cl_assert_equal_s("bcd", item->path);
+ cl_assert_equal_i(20, item->value);
+ cl_assert(git_sortedcache_entry(sc, 3) == NULL);
+
+ /* should not need to reload this time */
+
+ cl_assert_equal_i(0, git_sortedcache_lockandload(sc, NULL));
+
+ /* rewrite ondisk file and reload */
+
+ cl_assert_equal_i(0, free_count);
+
+ cl_git_rewritefile(
+ "cacheitems.txt", "100 abc\n200 zzz\n500 aaa\n10 final\n");
+ sortedcache_test_reload(sc);
+
+ cl_assert_equal_i(3, free_count);
+
+ /* test what we loaded */
+
+ cl_assert_equal_sz(4, git_sortedcache_entrycount(sc));
+
+ cl_assert((item = git_sortedcache_lookup(sc, "abc")) != NULL);
+ cl_assert_equal_s("abc", item->path);
+ cl_assert_equal_i(100, item->value);
+ cl_assert((item = git_sortedcache_lookup(sc, "final")) != NULL);
+ cl_assert_equal_s("final", item->path);
+ cl_assert_equal_i(10, item->value);
+ cl_assert(git_sortedcache_lookup(sc, "cde") == NULL);
+
+ cl_assert((item = git_sortedcache_entry(sc, 0)) != NULL);
+ cl_assert_equal_s("aaa", item->path);
+ cl_assert_equal_i(500, item->value);
+ cl_assert((item = git_sortedcache_entry(sc, 2)) != NULL);
+ cl_assert_equal_s("final", item->path);
+ cl_assert_equal_i(10, item->value);
+ cl_assert((item = git_sortedcache_entry(sc, 3)) != NULL);
+ cl_assert_equal_s("zzz", item->path);
+ cl_assert_equal_i(200, item->value);
+
+ cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "aaa"));
+ cl_assert_equal_sz(0, pos);
+ cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "abc"));
+ cl_assert_equal_sz(1, pos);
+ cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "final"));
+ cl_assert_equal_sz(2, pos);
+ cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "zzz"));
+ cl_assert_equal_sz(3, pos);
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "missing"));
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "cde"));
+
+ git_sortedcache_free(sc);
+
+ cl_assert_equal_i(7, free_count);
+}
+
diff --git a/tests-clar/core/stat.c b/tests/core/stat.c
index 2e4abfb79..2e4abfb79 100644
--- a/tests-clar/core/stat.c
+++ b/tests/core/stat.c
diff --git a/tests-clar/core/string.c b/tests/core/string.c
index ec9575685..ec9575685 100644
--- a/tests-clar/core/string.c
+++ b/tests/core/string.c
diff --git a/tests-clar/core/strmap.c b/tests/core/strmap.c
index f34a4f89f..f34a4f89f 100644
--- a/tests-clar/core/strmap.c
+++ b/tests/core/strmap.c
diff --git a/tests-clar/core/strtol.c b/tests/core/strtol.c
index 8765e042b..8765e042b 100644
--- a/tests-clar/core/strtol.c
+++ b/tests/core/strtol.c
diff --git a/tests/core/vector.c b/tests/core/vector.c
new file mode 100644
index 000000000..db52c004f
--- /dev/null
+++ b/tests/core/vector.c
@@ -0,0 +1,275 @@
+#include "clar_libgit2.h"
+#include "vector.h"
+
+/* initial size of 1 would cause writing past array bounds */
+void test_core_vector__0(void)
+{
+ git_vector x;
+ int i;
+ git_vector_init(&x, 1, NULL);
+ for (i = 0; i < 10; ++i) {
+ git_vector_insert(&x, (void*) 0xabc);
+ }
+ git_vector_free(&x);
+}
+
+
+/* don't read past array bounds on remove() */
+void test_core_vector__1(void)
+{
+ git_vector x;
+ // make initial capacity exact for our insertions.
+ git_vector_init(&x, 3, NULL);
+ git_vector_insert(&x, (void*) 0xabc);
+ git_vector_insert(&x, (void*) 0xdef);
+ git_vector_insert(&x, (void*) 0x123);
+
+ git_vector_remove(&x, 0); // used to read past array bounds.
+ git_vector_free(&x);
+}
+
+
+static int test_cmp(const void *a, const void *b)
+{
+ return *(const int *)a - *(const int *)b;
+}
+
+/* remove duplicates */
+void test_core_vector__2(void)
+{
+ git_vector x;
+ int *ptrs[2];
+
+ ptrs[0] = git__malloc(sizeof(int));
+ ptrs[1] = git__malloc(sizeof(int));
+
+ *ptrs[0] = 2;
+ *ptrs[1] = 1;
+
+ cl_git_pass(git_vector_init(&x, 5, test_cmp));
+ cl_git_pass(git_vector_insert(&x, ptrs[0]));
+ cl_git_pass(git_vector_insert(&x, ptrs[1]));
+ cl_git_pass(git_vector_insert(&x, ptrs[1]));
+ cl_git_pass(git_vector_insert(&x, ptrs[0]));
+ cl_git_pass(git_vector_insert(&x, ptrs[1]));
+ cl_assert(x.length == 5);
+
+ git_vector_uniq(&x, NULL);
+ cl_assert(x.length == 2);
+
+ git_vector_free(&x);
+
+ git__free(ptrs[0]);
+ git__free(ptrs[1]);
+}
+
+
+static int compare_them(const void *a, const void *b)
+{
+ return (int)((long)a - (long)b);
+}
+
+/* insert_sorted */
+void test_core_vector__3(void)
+{
+ git_vector x;
+ long i;
+ git_vector_init(&x, 1, &compare_them);
+
+ for (i = 0; i < 10; i += 2) {
+ git_vector_insert_sorted(&x, (void*)(i + 1), NULL);
+ }
+
+ for (i = 9; i > 0; i -= 2) {
+ git_vector_insert_sorted(&x, (void*)(i + 1), NULL);
+ }
+
+ cl_assert(x.length == 10);
+ for (i = 0; i < 10; ++i) {
+ cl_assert(git_vector_get(&x, i) == (void*)(i + 1));
+ }
+
+ git_vector_free(&x);
+}
+
+/* insert_sorted with duplicates */
+void test_core_vector__4(void)
+{
+ git_vector x;
+ long i;
+ git_vector_init(&x, 1, &compare_them);
+
+ for (i = 0; i < 10; i += 2) {
+ git_vector_insert_sorted(&x, (void*)(i + 1), NULL);
+ }
+
+ for (i = 9; i > 0; i -= 2) {
+ git_vector_insert_sorted(&x, (void*)(i + 1), NULL);
+ }
+
+ for (i = 0; i < 10; i += 2) {
+ git_vector_insert_sorted(&x, (void*)(i + 1), NULL);
+ }
+
+ for (i = 9; i > 0; i -= 2) {
+ git_vector_insert_sorted(&x, (void*)(i + 1), NULL);
+ }
+
+ cl_assert(x.length == 20);
+ for (i = 0; i < 20; ++i) {
+ cl_assert(git_vector_get(&x, i) == (void*)(i / 2 + 1));
+ }
+
+ git_vector_free(&x);
+}
+
+typedef struct {
+ int content;
+ int count;
+} my_struct;
+
+static int _struct_count = 0;
+
+static int compare_structs(const void *a, const void *b)
+{
+ return ((const my_struct *)a)->content -
+ ((const my_struct *)b)->content;
+}
+
+static int merge_structs(void **old_raw, void *new)
+{
+ my_struct *old = *(my_struct **)old_raw;
+ cl_assert(((my_struct *)old)->content == ((my_struct *)new)->content);
+ ((my_struct *)old)->count += 1;
+ git__free(new);
+ _struct_count--;
+ return GIT_EEXISTS;
+}
+
+static my_struct *alloc_struct(int value)
+{
+ my_struct *st = git__malloc(sizeof(my_struct));
+ st->content = value;
+ st->count = 0;
+ _struct_count++;
+ return st;
+}
+
+/* insert_sorted with duplicates and special handling */
+void test_core_vector__5(void)
+{
+ git_vector x;
+ int i;
+
+ git_vector_init(&x, 1, &compare_structs);
+
+ for (i = 0; i < 10; i += 2)
+ git_vector_insert_sorted(&x, alloc_struct(i), &merge_structs);
+
+ for (i = 9; i > 0; i -= 2)
+ git_vector_insert_sorted(&x, alloc_struct(i), &merge_structs);
+
+ cl_assert(x.length == 10);
+ cl_assert(_struct_count == 10);
+
+ for (i = 0; i < 10; i += 2)
+ git_vector_insert_sorted(&x, alloc_struct(i), &merge_structs);
+
+ for (i = 9; i > 0; i -= 2)
+ git_vector_insert_sorted(&x, alloc_struct(i), &merge_structs);
+
+ cl_assert(x.length == 10);
+ cl_assert(_struct_count == 10);
+
+ for (i = 0; i < 10; ++i) {
+ cl_assert(((my_struct *)git_vector_get(&x, i))->content == i);
+ git__free(git_vector_get(&x, i));
+ _struct_count--;
+ }
+
+ git_vector_free(&x);
+}
+
+static int remove_ones(const git_vector *v, size_t idx)
+{
+ return (git_vector_get(v, idx) == (void *)0x001);
+}
+
+/* Test removal based on callback */
+void test_core_vector__remove_matching(void)
+{
+ git_vector x;
+ size_t i;
+ void *compare;
+
+ git_vector_init(&x, 1, NULL);
+ git_vector_insert(&x, (void*) 0x001);
+
+ cl_assert(x.length == 1);
+ git_vector_remove_matching(&x, remove_ones);
+ cl_assert(x.length == 0);
+
+ git_vector_insert(&x, (void*) 0x001);
+ git_vector_insert(&x, (void*) 0x001);
+ git_vector_insert(&x, (void*) 0x001);
+
+ cl_assert(x.length == 3);
+ git_vector_remove_matching(&x, remove_ones);
+ cl_assert(x.length == 0);
+
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x001);
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x001);
+
+ cl_assert(x.length == 4);
+ git_vector_remove_matching(&x, remove_ones);
+ cl_assert(x.length == 2);
+
+ git_vector_foreach(&x, i, compare) {
+ cl_assert(compare != (void *)0x001);
+ }
+
+ git_vector_clear(&x);
+
+ git_vector_insert(&x, (void*) 0x001);
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x001);
+
+ cl_assert(x.length == 4);
+ git_vector_remove_matching(&x, remove_ones);
+ cl_assert(x.length == 2);
+
+ git_vector_foreach(&x, i, compare) {
+ cl_assert(compare != (void *)0x001);
+ }
+
+ git_vector_clear(&x);
+
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x001);
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x001);
+
+ cl_assert(x.length == 4);
+ git_vector_remove_matching(&x, remove_ones);
+ cl_assert(x.length == 2);
+
+ git_vector_foreach(&x, i, compare) {
+ cl_assert(compare != (void *)0x001);
+ }
+
+ git_vector_clear(&x);
+
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x003);
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x003);
+
+ cl_assert(x.length == 4);
+ git_vector_remove_matching(&x, remove_ones);
+ cl_assert(x.length == 4);
+
+ git_vector_free(&x);
+}
diff --git a/tests-clar/date/date.c b/tests/date/date.c
index 88881d1e1..88881d1e1 100644
--- a/tests-clar/date/date.c
+++ b/tests/date/date.c
diff --git a/tests/diff/blob.c b/tests/diff/blob.c
new file mode 100644
index 000000000..93f20711c
--- /dev/null
+++ b/tests/diff/blob.c
@@ -0,0 +1,1067 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+
+static git_repository *g_repo = NULL;
+static diff_expects expected;
+static git_diff_options opts;
+static git_blob *d, *alien;
+
+static void quick_diff_blob_to_str(
+ const git_blob *blob, const char *blob_path,
+ const char *str, size_t len, const char *str_path)
+{
+ memset(&expected, 0, sizeof(expected));
+
+ if (str && !len)
+ len = strlen(str);
+
+ cl_git_pass(git_diff_blob_to_buffer(
+ blob, blob_path, str, len, str_path,
+ &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
+}
+
+void test_diff_blob__initialize(void)
+{
+ git_oid oid;
+
+ g_repo = cl_git_sandbox_init("attr");
+
+ cl_git_pass(git_diff_options_init(&opts, GIT_DIFF_OPTIONS_VERSION));
+ opts.context_lines = 1;
+
+ memset(&expected, 0, sizeof(expected));
+
+ /* tests/resources/attr/root_test4.txt */
+ cl_git_pass(git_oid_fromstrn(&oid, "a0f7217a", 8));
+ cl_git_pass(git_blob_lookup_prefix(&d, g_repo, &oid, 4));
+
+ /* alien.png */
+ cl_git_pass(git_oid_fromstrn(&oid, "edf3dcee", 8));
+ cl_git_pass(git_blob_lookup_prefix(&alien, g_repo, &oid, 4));
+}
+
+void test_diff_blob__cleanup(void)
+{
+ git_blob_free(d);
+ d = NULL;
+
+ git_blob_free(alien);
+ alien = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_diff_blob__can_compare_text_blobs(void)
+{
+ git_blob *a, *b, *c;
+ git_oid a_oid, b_oid, c_oid;
+
+ /* tests/resources/attr/root_test1 */
+ cl_git_pass(git_oid_fromstrn(&a_oid, "45141a79", 8));
+ 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));
+
+ /* tests/resources/attr/root_test3 */
+ cl_git_pass(git_oid_fromstrn(&c_oid, "c96bbb2c2557a832", 16));
+ cl_git_pass(git_blob_lookup_prefix(&c, g_repo, &c_oid, 8));
+
+ /* Doing the equivalent of a `git diff -U1` on these files */
+
+ /* diff on tests/resources/attr/root_test1 */
+ cl_git_pass(git_diff_blobs(
+ a, NULL, b, NULL, &opts,
+ diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ cl_assert_equal_i(1, expected.files);
+ cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, expected.files_binary);
+
+ cl_assert_equal_i(1, expected.hunks);
+ cl_assert_equal_i(6, expected.lines);
+ cl_assert_equal_i(1, expected.line_ctxt);
+ cl_assert_equal_i(5, expected.line_adds);
+ cl_assert_equal_i(0, expected.line_dels);
+
+ /* diff on tests/resources/attr/root_test2 */
+ memset(&expected, 0, sizeof(expected));
+ cl_git_pass(git_diff_blobs(
+ b, NULL, c, NULL, &opts,
+ diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ cl_assert_equal_i(1, expected.files);
+ cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, expected.files_binary);
+
+ cl_assert_equal_i(1, expected.hunks);
+ cl_assert_equal_i(15, expected.lines);
+ cl_assert_equal_i(3, expected.line_ctxt);
+ cl_assert_equal_i(9, expected.line_adds);
+ cl_assert_equal_i(3, expected.line_dels);
+
+ /* diff on tests/resources/attr/root_test3 */
+ memset(&expected, 0, sizeof(expected));
+ cl_git_pass(git_diff_blobs(
+ a, NULL, c, NULL, &opts,
+ diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ cl_assert_equal_i(1, expected.files);
+ cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, expected.files_binary);
+
+ cl_assert_equal_i(1, expected.hunks);
+ cl_assert_equal_i(13, expected.lines);
+ cl_assert_equal_i(0, expected.line_ctxt);
+ cl_assert_equal_i(12, expected.line_adds);
+ cl_assert_equal_i(1, expected.line_dels);
+
+ memset(&expected, 0, sizeof(expected));
+ cl_git_pass(git_diff_blobs(
+ c, NULL, d, NULL, &opts,
+ diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ cl_assert_equal_i(1, expected.files);
+ cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, expected.files_binary);
+
+ cl_assert_equal_i(2, expected.hunks);
+ cl_assert_equal_i(14, expected.lines);
+ cl_assert_equal_i(4, expected.line_ctxt);
+ cl_assert_equal_i(6, expected.line_adds);
+ cl_assert_equal_i(4, expected.line_dels);
+
+ git_blob_free(a);
+ git_blob_free(b);
+ git_blob_free(c);
+}
+
+void test_diff_blob__can_compare_text_blobs_with_patch(void)
+{
+ git_blob *a, *b, *c;
+ git_oid a_oid, b_oid, c_oid;
+ git_patch *p;
+ const git_diff_delta *delta;
+ size_t tc, ta, td;
+
+ /* 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));
+
+ /* tests/resources/attr/root_test3 */
+ cl_git_pass(git_oid_fromstrn(&c_oid, "c96bbb2c2557a832", 16));
+ cl_git_pass(git_blob_lookup_prefix(&c, g_repo, &c_oid, 8));
+
+ /* Doing the equivalent of a `git diff -U1` on these files */
+
+ /* diff on tests/resources/attr/root_test1 */
+ cl_git_pass(git_patch_from_blobs(&p, a, NULL, b, NULL, &opts));
+
+ cl_assert(p != NULL);
+
+ delta = git_patch_get_delta(p);
+ cl_assert(delta != NULL);
+ cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status);
+ cl_assert(git_oid_equal(git_blob_id(a), &delta->old_file.oid));
+ cl_assert_equal_sz(git_blob_rawsize(a), delta->old_file.size);
+ cl_assert(git_oid_equal(git_blob_id(b), &delta->new_file.oid));
+ cl_assert_equal_sz(git_blob_rawsize(b), delta->new_file.size);
+
+ cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
+ cl_assert_equal_i(6, git_patch_num_lines_in_hunk(p, 0));
+
+ cl_git_pass(git_patch_line_stats(&tc, &ta, &td, p));
+ cl_assert_equal_i(1, (int)tc);
+ cl_assert_equal_i(5, (int)ta);
+ cl_assert_equal_i(0, (int)td);
+
+ git_patch_free(p);
+
+ /* diff on tests/resources/attr/root_test2 */
+ cl_git_pass(git_patch_from_blobs(&p, b, NULL, c, NULL, &opts));
+
+ cl_assert(p != NULL);
+
+ delta = git_patch_get_delta(p);
+ cl_assert(delta != NULL);
+ cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status);
+ cl_assert(git_oid_equal(git_blob_id(b), &delta->old_file.oid));
+ cl_assert_equal_sz(git_blob_rawsize(b), delta->old_file.size);
+ cl_assert(git_oid_equal(git_blob_id(c), &delta->new_file.oid));
+ cl_assert_equal_sz(git_blob_rawsize(c), delta->new_file.size);
+
+ cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
+ cl_assert_equal_i(15, git_patch_num_lines_in_hunk(p, 0));
+
+ cl_git_pass(git_patch_line_stats(&tc, &ta, &td, p));
+ cl_assert_equal_i(3, (int)tc);
+ cl_assert_equal_i(9, (int)ta);
+ cl_assert_equal_i(3, (int)td);
+
+ git_patch_free(p);
+
+ /* diff on tests/resources/attr/root_test3 */
+ cl_git_pass(git_patch_from_blobs(&p, a, NULL, c, NULL, &opts));
+
+ cl_assert(p != NULL);
+
+ delta = git_patch_get_delta(p);
+ cl_assert(delta != NULL);
+ cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status);
+ cl_assert(git_oid_equal(git_blob_id(a), &delta->old_file.oid));
+ cl_assert_equal_sz(git_blob_rawsize(a), delta->old_file.size);
+ cl_assert(git_oid_equal(git_blob_id(c), &delta->new_file.oid));
+ cl_assert_equal_sz(git_blob_rawsize(c), delta->new_file.size);
+
+ cl_git_pass(git_patch_line_stats(&tc, &ta, &td, p));
+ cl_assert_equal_i(0, (int)tc);
+ cl_assert_equal_i(12, (int)ta);
+ cl_assert_equal_i(1, (int)td);
+
+ git_patch_free(p);
+
+ /* one more */
+ cl_git_pass(git_patch_from_blobs(&p, c, NULL, d, NULL, &opts));
+
+ cl_assert(p != NULL);
+
+ delta = git_patch_get_delta(p);
+ cl_assert(delta != NULL);
+ cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status);
+ cl_assert(git_oid_equal(git_blob_id(c), &delta->old_file.oid));
+ cl_assert_equal_sz(git_blob_rawsize(c), delta->old_file.size);
+ cl_assert(git_oid_equal(git_blob_id(d), &delta->new_file.oid));
+ cl_assert_equal_sz(git_blob_rawsize(d), delta->new_file.size);
+
+ cl_assert_equal_i(2, (int)git_patch_num_hunks(p));
+ cl_assert_equal_i(5, git_patch_num_lines_in_hunk(p, 0));
+ cl_assert_equal_i(9, git_patch_num_lines_in_hunk(p, 1));
+
+ cl_git_pass(git_patch_line_stats(&tc, &ta, &td, p));
+ cl_assert_equal_i(4, (int)tc);
+ cl_assert_equal_i(6, (int)ta);
+ cl_assert_equal_i(4, (int)td);
+
+ git_patch_free(p);
+
+ git_blob_free(a);
+ git_blob_free(b);
+ git_blob_free(c);
+}
+
+void test_diff_blob__can_compare_against_null_blobs(void)
+{
+ git_blob *e = NULL;
+
+ cl_git_pass(git_diff_blobs(
+ d, NULL, e, NULL, &opts,
+ diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ cl_assert_equal_i(1, expected.files);
+ cl_assert_equal_i(1, expected.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(0, expected.files_binary);
+
+ cl_assert_equal_i(1, expected.hunks);
+ cl_assert_equal_i(14, expected.hunk_old_lines);
+ cl_assert_equal_i(14, expected.lines);
+ cl_assert_equal_i(14, expected.line_dels);
+
+ opts.flags |= GIT_DIFF_REVERSE;
+ memset(&expected, 0, sizeof(expected));
+
+ cl_git_pass(git_diff_blobs(
+ d, NULL, e, NULL, &opts,
+ diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ cl_assert_equal_i(1, expected.files);
+ cl_assert_equal_i(1, expected.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, expected.files_binary);
+
+ cl_assert_equal_i(1, expected.hunks);
+ cl_assert_equal_i(14, expected.hunk_new_lines);
+ cl_assert_equal_i(14, expected.lines);
+ cl_assert_equal_i(14, expected.line_adds);
+
+ opts.flags ^= GIT_DIFF_REVERSE;
+ memset(&expected, 0, sizeof(expected));
+
+ cl_git_pass(git_diff_blobs(
+ alien, NULL, NULL, NULL, &opts,
+ diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ cl_assert_equal_i(1, expected.files);
+ cl_assert_equal_i(1, expected.files_binary);
+ cl_assert_equal_i(1, expected.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(0, expected.hunks);
+ cl_assert_equal_i(0, expected.lines);
+
+ memset(&expected, 0, sizeof(expected));
+
+ cl_git_pass(git_diff_blobs(
+ NULL, NULL, alien, NULL, &opts,
+ diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ cl_assert_equal_i(1, expected.files);
+ cl_assert_equal_i(1, expected.files_binary);
+ cl_assert_equal_i(1, expected.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, expected.hunks);
+ cl_assert_equal_i(0, expected.lines);
+}
+
+void test_diff_blob__can_compare_against_null_blobs_with_patch(void)
+{
+ git_blob *e = NULL;
+ git_patch *p;
+ const git_diff_delta *delta;
+ const git_diff_line *line;
+ int l, max_l;
+
+ cl_git_pass(git_patch_from_blobs(&p, d, NULL, e, NULL, &opts));
+
+ cl_assert(p != NULL);
+
+ delta = git_patch_get_delta(p);
+ cl_assert(delta != NULL);
+ cl_assert_equal_i(GIT_DELTA_DELETED, delta->status);
+ cl_assert(git_oid_equal(git_blob_id(d), &delta->old_file.oid));
+ cl_assert_equal_sz(git_blob_rawsize(d), delta->old_file.size);
+ cl_assert(git_oid_iszero(&delta->new_file.oid));
+ cl_assert_equal_sz(0, delta->new_file.size);
+
+ cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
+ cl_assert_equal_i(14, git_patch_num_lines_in_hunk(p, 0));
+
+ max_l = git_patch_num_lines_in_hunk(p, 0);
+ for (l = 0; l < max_l; ++l) {
+ cl_git_pass(git_patch_get_line_in_hunk(&line, p, 0, l));
+ cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)line->origin);
+ }
+
+ git_patch_free(p);
+
+ opts.flags |= GIT_DIFF_REVERSE;
+
+ cl_git_pass(git_patch_from_blobs(&p, d, NULL, e, NULL, &opts));
+
+ cl_assert(p != NULL);
+
+ delta = git_patch_get_delta(p);
+ cl_assert(delta != NULL);
+ cl_assert_equal_i(GIT_DELTA_ADDED, delta->status);
+ cl_assert(git_oid_iszero(&delta->old_file.oid));
+ cl_assert_equal_sz(0, delta->old_file.size);
+ cl_assert(git_oid_equal(git_blob_id(d), &delta->new_file.oid));
+ cl_assert_equal_sz(git_blob_rawsize(d), delta->new_file.size);
+
+ cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
+ cl_assert_equal_i(14, git_patch_num_lines_in_hunk(p, 0));
+
+ max_l = git_patch_num_lines_in_hunk(p, 0);
+ for (l = 0; l < max_l; ++l) {
+ cl_git_pass(git_patch_get_line_in_hunk(&line, p, 0, l));
+ cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)line->origin);
+ }
+
+ git_patch_free(p);
+
+ opts.flags ^= GIT_DIFF_REVERSE;
+
+ cl_git_pass(git_patch_from_blobs(&p, alien, NULL, NULL, NULL, &opts));
+
+ cl_assert(p != NULL);
+
+ delta = git_patch_get_delta(p);
+ cl_assert(delta != NULL);
+ cl_assert_equal_i(GIT_DELTA_DELETED, delta->status);
+ cl_assert((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
+
+ cl_assert_equal_i(0, (int)git_patch_num_hunks(p));
+
+ git_patch_free(p);
+
+ cl_git_pass(git_patch_from_blobs(&p, NULL, NULL, alien, NULL, &opts));
+
+ cl_assert(p != NULL);
+
+ delta = git_patch_get_delta(p);
+ cl_assert(delta != NULL);
+ cl_assert_equal_i(GIT_DELTA_ADDED, delta->status);
+ cl_assert((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
+
+ cl_assert_equal_i(0, (int)git_patch_num_hunks(p));
+
+ git_patch_free(p);
+}
+
+static void assert_identical_blobs_comparison(diff_expects *expected)
+{
+ cl_assert_equal_i(1, expected->files);
+ cl_assert_equal_i(1, expected->file_status[GIT_DELTA_UNMODIFIED]);
+ cl_assert_equal_i(0, expected->hunks);
+ cl_assert_equal_i(0, expected->lines);
+}
+
+void test_diff_blob__can_compare_identical_blobs(void)
+{
+ opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
+
+ cl_git_pass(git_diff_blobs(
+ d, NULL, d, NULL, &opts,
+ diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ assert_identical_blobs_comparison(&expected);
+ cl_assert_equal_i(0, expected.files_binary);
+
+ memset(&expected, 0, sizeof(expected));
+ cl_git_pass(git_diff_blobs(
+ NULL, NULL, NULL, NULL, &opts,
+ diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ assert_identical_blobs_comparison(&expected);
+ cl_assert_equal_i(0, expected.files_binary);
+
+ memset(&expected, 0, sizeof(expected));
+ cl_git_pass(git_diff_blobs(
+ alien, NULL, alien, NULL, &opts,
+ diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ assert_identical_blobs_comparison(&expected);
+ cl_assert(expected.files_binary > 0);
+}
+
+void test_diff_blob__can_compare_identical_blobs_with_patch(void)
+{
+ git_patch *p;
+ const git_diff_delta *delta;
+
+ cl_git_pass(git_patch_from_blobs(&p, d, NULL, d, NULL, &opts));
+ cl_assert(p != NULL);
+
+ delta = git_patch_get_delta(p);
+ cl_assert(delta != NULL);
+ cl_assert_equal_i(GIT_DELTA_UNMODIFIED, delta->status);
+ cl_assert_equal_sz(delta->old_file.size, git_blob_rawsize(d));
+ cl_assert(git_oid_equal(git_blob_id(d), &delta->old_file.oid));
+ cl_assert_equal_sz(delta->new_file.size, git_blob_rawsize(d));
+ cl_assert(git_oid_equal(git_blob_id(d), &delta->new_file.oid));
+
+ cl_assert_equal_i(0, (int)git_patch_num_hunks(p));
+ git_patch_free(p);
+
+ cl_git_pass(git_patch_from_blobs(&p, NULL, NULL, NULL, NULL, &opts));
+ cl_assert(p != NULL);
+
+ delta = git_patch_get_delta(p);
+ cl_assert(delta != NULL);
+ cl_assert_equal_i(GIT_DELTA_UNMODIFIED, delta->status);
+ cl_assert_equal_sz(0, delta->old_file.size);
+ cl_assert(git_oid_iszero(&delta->old_file.oid));
+ cl_assert_equal_sz(0, delta->new_file.size);
+ cl_assert(git_oid_iszero(&delta->new_file.oid));
+
+ cl_assert_equal_i(0, (int)git_patch_num_hunks(p));
+ git_patch_free(p);
+
+ cl_git_pass(git_patch_from_blobs(&p, alien, NULL, alien, NULL, &opts));
+ cl_assert(p != NULL);
+ cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_patch_get_delta(p)->status);
+ cl_assert_equal_i(0, (int)git_patch_num_hunks(p));
+ git_patch_free(p);
+}
+
+static void assert_binary_blobs_comparison(diff_expects *expected)
+{
+ cl_assert(expected->files_binary > 0);
+
+ cl_assert_equal_i(1, expected->files);
+ cl_assert_equal_i(1, expected->file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, expected->hunks);
+ cl_assert_equal_i(0, expected->lines);
+}
+
+void test_diff_blob__can_compare_two_binary_blobs(void)
+{
+ git_blob *heart;
+ git_oid h_oid;
+
+ /* heart.png */
+ cl_git_pass(git_oid_fromstrn(&h_oid, "de863bff", 8));
+ cl_git_pass(git_blob_lookup_prefix(&heart, g_repo, &h_oid, 4));
+
+ cl_git_pass(git_diff_blobs(
+ alien, NULL, heart, NULL, &opts,
+ diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ assert_binary_blobs_comparison(&expected);
+
+ memset(&expected, 0, sizeof(expected));
+
+ cl_git_pass(git_diff_blobs(
+ heart, NULL, alien, NULL, &opts,
+ diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ assert_binary_blobs_comparison(&expected);
+
+ git_blob_free(heart);
+}
+
+void test_diff_blob__can_compare_a_binary_blob_and_a_text_blob(void)
+{
+ cl_git_pass(git_diff_blobs(
+ alien, NULL, d, NULL, &opts,
+ diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ assert_binary_blobs_comparison(&expected);
+
+ memset(&expected, 0, sizeof(expected));
+
+ cl_git_pass(git_diff_blobs(
+ d, NULL, alien, NULL, &opts,
+ diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ assert_binary_blobs_comparison(&expected);
+}
+
+/*
+ * $ git diff fe773770 a0f7217
+ * diff --git a/fe773770 b/a0f7217
+ * index fe77377..a0f7217 100644
+ * --- a/fe773770
+ * +++ b/a0f7217
+ * @@ -1,6 +1,6 @@
+ * Here is some stuff at the start
+ *
+ * -This should go in one hunk
+ * +This should go in one hunk (first)
+ *
+ * Some additional lines
+ *
+ * @@ -8,7 +8,7 @@ Down here below the other lines
+ *
+ * With even more at the end
+ *
+ * -Followed by a second hunk of stuff
+ * +Followed by a second hunk of stuff (second)
+ *
+ * That happens down here
+ */
+void test_diff_blob__comparing_two_text_blobs_honors_interhunkcontext(void)
+{
+ git_blob *old_d;
+ git_oid old_d_oid;
+
+ opts.context_lines = 3;
+
+ /* tests/resources/attr/root_test1 from commit f5b0af1 */
+ cl_git_pass(git_oid_fromstrn(&old_d_oid, "fe773770", 8));
+ cl_git_pass(git_blob_lookup_prefix(&old_d, g_repo, &old_d_oid, 4));
+
+ /* Test with default inter-hunk-context (not set) => default is 0 */
+ cl_git_pass(git_diff_blobs(
+ old_d, NULL, d, NULL, &opts,
+ diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ cl_assert_equal_i(2, expected.hunks);
+
+ /* Test with inter-hunk-context explicitly set to 0 */
+ opts.interhunk_lines = 0;
+ memset(&expected, 0, sizeof(expected));
+ cl_git_pass(git_diff_blobs(
+ old_d, NULL, d, NULL, &opts,
+ diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ cl_assert_equal_i(2, expected.hunks);
+
+ /* Test with inter-hunk-context explicitly set to 1 */
+ opts.interhunk_lines = 1;
+ memset(&expected, 0, sizeof(expected));
+ cl_git_pass(git_diff_blobs(
+ old_d, NULL, d, NULL, &opts,
+ diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ cl_assert_equal_i(1, expected.hunks);
+
+ git_blob_free(old_d);
+}
+
+void test_diff_blob__checks_options_version_too_low(void)
+{
+ const git_error *err;
+
+ opts.version = 0;
+ cl_git_fail(git_diff_blobs(
+ d, NULL, alien, NULL, &opts,
+ diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
+ err = giterr_last();
+ cl_assert_equal_i(GITERR_INVALID, err->klass);
+}
+
+void test_diff_blob__checks_options_version_too_high(void)
+{
+ const git_error *err;
+
+ opts.version = 1024;
+ cl_git_fail(git_diff_blobs(
+ d, NULL, alien, NULL, &opts,
+ diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
+ err = giterr_last();
+ cl_assert_equal_i(GITERR_INVALID, err->klass);
+}
+
+void test_diff_blob__can_correctly_detect_a_binary_blob_as_binary(void)
+{
+ /* alien.png */
+ cl_assert_equal_i(true, git_blob_is_binary(alien));
+}
+
+void test_diff_blob__can_correctly_detect_a_textual_blob_as_non_binary(void)
+{
+ /* tests/resources/attr/root_test4.txt */
+ cl_assert_equal_i(false, git_blob_is_binary(d));
+}
+
+/*
+ * git_diff_blob_to_buffer tests
+ */
+
+static void assert_changed_single_one_line_file(
+ diff_expects *expected, git_delta_t mod)
+{
+ cl_assert_equal_i(1, expected->files);
+ cl_assert_equal_i(1, expected->file_status[mod]);
+ cl_assert_equal_i(1, expected->hunks);
+ cl_assert_equal_i(1, expected->lines);
+
+ if (mod == GIT_DELTA_ADDED)
+ cl_assert_equal_i(1, expected->line_adds);
+ else if (mod == GIT_DELTA_DELETED)
+ cl_assert_equal_i(1, expected->line_dels);
+}
+
+void test_diff_blob__can_compare_blob_to_buffer(void)
+{
+ git_blob *a;
+ git_oid a_oid;
+ const char *a_content = "Hello from the root\n";
+ const char *b_content = "Hello from the root\n\nSome additional lines\n\nDown here below\n\n";
+
+ /* tests/resources/attr/root_test1 */
+ cl_git_pass(git_oid_fromstrn(&a_oid, "45141a79", 8));
+ cl_git_pass(git_blob_lookup_prefix(&a, g_repo, &a_oid, 4));
+
+ /* diff from blob a to content of b */
+ quick_diff_blob_to_str(a, NULL, b_content, 0, NULL);
+ cl_assert_equal_i(1, expected.files);
+ cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, expected.files_binary);
+ cl_assert_equal_i(1, expected.hunks);
+ cl_assert_equal_i(6, expected.lines);
+ cl_assert_equal_i(1, expected.line_ctxt);
+ cl_assert_equal_i(5, expected.line_adds);
+ cl_assert_equal_i(0, expected.line_dels);
+
+ /* diff from blob a to content of a */
+ opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
+ quick_diff_blob_to_str(a, NULL, a_content, 0, NULL);
+ assert_identical_blobs_comparison(&expected);
+
+ /* diff from NULL blob to content of a */
+ memset(&expected, 0, sizeof(expected));
+ quick_diff_blob_to_str(NULL, NULL, a_content, 0, NULL);
+ assert_changed_single_one_line_file(&expected, GIT_DELTA_ADDED);
+
+ /* diff from blob a to NULL buffer */
+ memset(&expected, 0, sizeof(expected));
+ quick_diff_blob_to_str(a, NULL, NULL, 0, NULL);
+ assert_changed_single_one_line_file(&expected, GIT_DELTA_DELETED);
+
+ /* diff with reverse */
+ opts.flags ^= GIT_DIFF_REVERSE;
+
+ memset(&expected, 0, sizeof(expected));
+ quick_diff_blob_to_str(a, NULL, NULL, 0, NULL);
+ assert_changed_single_one_line_file(&expected, GIT_DELTA_ADDED);
+
+ git_blob_free(a);
+}
+
+void test_diff_blob__can_compare_blob_to_buffer_with_patch(void)
+{
+ git_patch *p;
+ git_blob *a;
+ git_oid a_oid;
+ const char *a_content = "Hello from the root\n";
+ const char *b_content = "Hello from the root\n\nSome additional lines\n\nDown here below\n\n";
+ size_t tc, ta, td;
+
+ /* tests/resources/attr/root_test1 */
+ cl_git_pass(git_oid_fromstrn(&a_oid, "45141a79", 8));
+ cl_git_pass(git_blob_lookup_prefix(&a, g_repo, &a_oid, 4));
+
+ /* diff from blob a to content of b */
+ cl_git_pass(git_patch_from_blob_and_buffer(
+ &p, a, NULL, b_content, strlen(b_content), NULL, &opts));
+
+ cl_assert(p != NULL);
+ cl_assert_equal_i(GIT_DELTA_MODIFIED, git_patch_get_delta(p)->status);
+ cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
+ cl_assert_equal_i(6, git_patch_num_lines_in_hunk(p, 0));
+
+ cl_git_pass(git_patch_line_stats(&tc, &ta, &td, p));
+ cl_assert_equal_i(1, (int)tc);
+ cl_assert_equal_i(5, (int)ta);
+ cl_assert_equal_i(0, (int)td);
+
+ git_patch_free(p);
+
+ /* diff from blob a to content of a */
+ opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
+ cl_git_pass(git_patch_from_blob_and_buffer(
+ &p, a, NULL, a_content, strlen(a_content), NULL, &opts));
+ cl_assert(p != NULL);
+ cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_patch_get_delta(p)->status);
+ cl_assert_equal_i(0, (int)git_patch_num_hunks(p));
+ git_patch_free(p);
+
+ /* diff from NULL blob to content of a */
+ cl_git_pass(git_patch_from_blob_and_buffer(
+ &p, NULL, NULL, a_content, strlen(a_content), NULL, &opts));
+ cl_assert(p != NULL);
+ cl_assert_equal_i(GIT_DELTA_ADDED, git_patch_get_delta(p)->status);
+ cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
+ cl_assert_equal_i(1, git_patch_num_lines_in_hunk(p, 0));
+ git_patch_free(p);
+
+ /* diff from blob a to NULL buffer */
+ cl_git_pass(git_patch_from_blob_and_buffer(
+ &p, a, NULL, NULL, 0, NULL, &opts));
+ cl_assert(p != NULL);
+ cl_assert_equal_i(GIT_DELTA_DELETED, git_patch_get_delta(p)->status);
+ cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
+ cl_assert_equal_i(1, git_patch_num_lines_in_hunk(p, 0));
+ git_patch_free(p);
+
+ /* diff with reverse */
+ opts.flags ^= GIT_DIFF_REVERSE;
+
+ cl_git_pass(git_patch_from_blob_and_buffer(
+ &p, a, NULL, NULL, 0, NULL, &opts));
+ cl_assert(p != NULL);
+ cl_assert_equal_i(GIT_DELTA_ADDED, git_patch_get_delta(p)->status);
+ cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
+ cl_assert_equal_i(1, git_patch_num_lines_in_hunk(p, 0));
+ git_patch_free(p);
+
+ git_blob_free(a);
+}
+
+static void assert_one_modified_with_lines(diff_expects *expected, int lines)
+{
+ cl_assert_equal_i(1, expected->files);
+ cl_assert_equal_i(1, expected->file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, expected->files_binary);
+ cl_assert_equal_i(lines, expected->lines);
+}
+
+void test_diff_blob__binary_data_comparisons(void)
+{
+ git_blob *bin, *nonbin;
+ git_oid oid;
+ const char *nonbin_content = "Hello from the root\n";
+ size_t nonbin_len = 20;
+ const char *bin_content = "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\n0123456789\n";
+ size_t bin_len = 33;
+
+ opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
+
+ cl_git_pass(git_oid_fromstrn(&oid, "45141a79", 8));
+ cl_git_pass(git_blob_lookup_prefix(&nonbin, g_repo, &oid, 4));
+
+ cl_git_pass(git_oid_fromstrn(&oid, "b435cd56", 8));
+ cl_git_pass(git_blob_lookup_prefix(&bin, g_repo, &oid, 4));
+
+ /* non-binary to reference content */
+
+ quick_diff_blob_to_str(nonbin, NULL, nonbin_content, nonbin_len, NULL);
+ assert_identical_blobs_comparison(&expected);
+ cl_assert_equal_i(0, expected.files_binary);
+
+ /* binary to reference content */
+
+ quick_diff_blob_to_str(bin, NULL, bin_content, bin_len, NULL);
+ assert_identical_blobs_comparison(&expected);
+
+ cl_assert_equal_i(1, expected.files_binary);
+
+ /* non-binary to binary content */
+
+ quick_diff_blob_to_str(nonbin, NULL, bin_content, bin_len, NULL);
+ assert_binary_blobs_comparison(&expected);
+
+ /* binary to non-binary content */
+
+ quick_diff_blob_to_str(bin, NULL, nonbin_content, nonbin_len, NULL);
+ assert_binary_blobs_comparison(&expected);
+
+ /* non-binary to binary blob */
+
+ memset(&expected, 0, sizeof(expected));
+ cl_git_pass(git_diff_blobs(
+ bin, NULL, nonbin, NULL, &opts,
+ diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
+ assert_binary_blobs_comparison(&expected);
+
+ /*
+ * repeat with FORCE_TEXT
+ */
+
+ opts.flags |= GIT_DIFF_FORCE_TEXT;
+
+ quick_diff_blob_to_str(bin, NULL, bin_content, bin_len, NULL);
+ assert_identical_blobs_comparison(&expected);
+
+ quick_diff_blob_to_str(nonbin, NULL, bin_content, bin_len, NULL);
+ assert_one_modified_with_lines(&expected, 4);
+
+ quick_diff_blob_to_str(bin, NULL, nonbin_content, nonbin_len, NULL);
+ assert_one_modified_with_lines(&expected, 4);
+
+ memset(&expected, 0, sizeof(expected));
+ cl_git_pass(git_diff_blobs(
+ bin, NULL, nonbin, NULL, &opts,
+ diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
+ assert_one_modified_with_lines(&expected, 4);
+
+ /* cleanup */
+ git_blob_free(bin);
+ git_blob_free(nonbin);
+}
+
+void test_diff_blob__using_path_and_attributes(void)
+{
+ git_config *cfg;
+ git_blob *bin, *nonbin;
+ git_oid oid;
+ const char *nonbin_content = "Hello from the root\n";
+ const char *bin_content =
+ "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\n0123456789\n";
+ size_t bin_len = 33;
+ const char *changed;
+ git_patch *p;
+ char *pout;
+
+ /* set up custom diff drivers and 'diff' attribute mappings for them */
+
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(git_config_set_bool(cfg, "diff.iam_binary.binary", 1));
+ cl_git_pass(git_config_set_bool(cfg, "diff.iam_text.binary", 0));
+ cl_git_pass(git_config_set_string(
+ cfg, "diff.iam_alphactx.xfuncname", "^[A-Za-z]"));
+ cl_git_pass(git_config_set_bool(cfg, "diff.iam_textalpha.binary", 0));
+ cl_git_pass(git_config_set_string(
+ cfg, "diff.iam_textalpha.xfuncname", "^[A-Za-z]"));
+ cl_git_pass(git_config_set_string(
+ cfg, "diff.iam_numctx.funcname", "^[0-9]"));
+ cl_git_pass(git_config_set_bool(cfg, "diff.iam_textnum.binary", 0));
+ cl_git_pass(git_config_set_string(
+ cfg, "diff.iam_textnum.funcname", "^[0-9]"));
+ git_config_free(cfg);
+
+ cl_git_append2file(
+ "attr/.gitattributes",
+ "\n\n# test_diff_blob__using_path_and_attributes extra\n\n"
+ "*.binary diff=iam_binary\n"
+ "*.textary diff=iam_text\n"
+ "*.alphary diff=iam_alphactx\n"
+ "*.textalphary diff=iam_textalpha\n"
+ "*.textnumary diff=iam_textnum\n"
+ "*.numary diff=iam_numctx\n\n");
+
+ opts.context_lines = 0;
+ opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
+
+ cl_git_pass(git_oid_fromstrn(&oid, "45141a79", 8));
+ cl_git_pass(git_blob_lookup_prefix(&nonbin, g_repo, &oid, 4));
+ /* 20b: "Hello from the root\n" */
+
+ cl_git_pass(git_oid_fromstrn(&oid, "b435cd56", 8));
+ cl_git_pass(git_blob_lookup_prefix(&bin, g_repo, &oid, 4));
+ /* 33b: "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\n0123456789\n" */
+
+ /* non-binary to reference content */
+
+ quick_diff_blob_to_str(nonbin, NULL, nonbin_content, 0, NULL);
+ assert_identical_blobs_comparison(&expected);
+ cl_assert_equal_i(0, expected.files_binary);
+
+ /* binary to reference content */
+
+ quick_diff_blob_to_str(bin, NULL, bin_content, bin_len, NULL);
+ assert_identical_blobs_comparison(&expected);
+ cl_assert_equal_i(1, expected.files_binary);
+
+ /* add some text */
+
+ changed = "Hello from the root\nMore lines\nAnd more\nGo here\n";
+
+ quick_diff_blob_to_str(nonbin, NULL, changed, 0, NULL);
+ cl_assert_equal_i(1, expected.files);
+ cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, expected.files_binary);
+ cl_assert_equal_i(1, expected.hunks);
+ cl_assert_equal_i(3, expected.lines);
+ cl_assert_equal_i(0, expected.line_ctxt);
+ cl_assert_equal_i(3, expected.line_adds);
+ cl_assert_equal_i(0, expected.line_dels);
+
+ quick_diff_blob_to_str(nonbin, "foo/bar.binary", changed, 0, NULL);
+ cl_assert_equal_i(1, expected.files);
+ cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, expected.files_binary);
+ cl_assert_equal_i(0, expected.hunks);
+ cl_assert_equal_i(0, expected.lines);
+ cl_assert_equal_i(0, expected.line_ctxt);
+ cl_assert_equal_i(0, expected.line_adds);
+ cl_assert_equal_i(0, expected.line_dels);
+
+ quick_diff_blob_to_str(nonbin, "foo/bar.textary", changed, 0, NULL);
+ cl_assert_equal_i(1, expected.files);
+ cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, expected.files_binary);
+ cl_assert_equal_i(1, expected.hunks);
+ cl_assert_equal_i(3, expected.lines);
+ cl_assert_equal_i(0, expected.line_ctxt);
+ cl_assert_equal_i(3, expected.line_adds);
+ cl_assert_equal_i(0, expected.line_dels);
+
+ quick_diff_blob_to_str(nonbin, "foo/bar.alphary", changed, 0, NULL);
+ cl_assert_equal_i(1, expected.files);
+ cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, expected.files_binary);
+ cl_assert_equal_i(1, expected.hunks);
+ cl_assert_equal_i(3, expected.lines);
+ cl_assert_equal_i(0, expected.line_ctxt);
+ cl_assert_equal_i(3, expected.line_adds);
+ cl_assert_equal_i(0, expected.line_dels);
+
+ cl_git_pass(git_patch_from_blob_and_buffer(
+ &p, nonbin, "zzz.normal", changed, strlen(changed), NULL, &opts));
+ cl_git_pass(git_patch_to_str(&pout, p));
+ cl_assert_equal_s(
+ "diff --git a/zzz.normal b/zzz.normal\n"
+ "index 45141a7..75b0dbb 100644\n"
+ "--- a/zzz.normal\n"
+ "+++ b/zzz.normal\n"
+ "@@ -1,0 +2,3 @@ Hello from the root\n"
+ "+More lines\n"
+ "+And more\n"
+ "+Go here\n", pout);
+ git__free(pout);
+ git_patch_free(p);
+
+ cl_git_pass(git_patch_from_blob_and_buffer(
+ &p, nonbin, "zzz.binary", changed, strlen(changed), NULL, &opts));
+ cl_git_pass(git_patch_to_str(&pout, p));
+ cl_assert_equal_s(
+ "diff --git a/zzz.binary b/zzz.binary\n"
+ "index 45141a7..75b0dbb 100644\n"
+ "Binary files a/zzz.binary and b/zzz.binary differ\n", pout);
+ git__free(pout);
+ git_patch_free(p);
+
+ cl_git_pass(git_patch_from_blob_and_buffer(
+ &p, nonbin, "zzz.alphary", changed, strlen(changed), NULL, &opts));
+ cl_git_pass(git_patch_to_str(&pout, p));
+ cl_assert_equal_s(
+ "diff --git a/zzz.alphary b/zzz.alphary\n"
+ "index 45141a7..75b0dbb 100644\n"
+ "--- a/zzz.alphary\n"
+ "+++ b/zzz.alphary\n"
+ "@@ -1,0 +2,3 @@ Hello from the root\n"
+ "+More lines\n"
+ "+And more\n"
+ "+Go here\n", pout);
+ git__free(pout);
+ git_patch_free(p);
+
+ cl_git_pass(git_patch_from_blob_and_buffer(
+ &p, nonbin, "zzz.numary", changed, strlen(changed), NULL, &opts));
+ cl_git_pass(git_patch_to_str(&pout, p));
+ cl_assert_equal_s(
+ "diff --git a/zzz.numary b/zzz.numary\n"
+ "index 45141a7..75b0dbb 100644\n"
+ "--- a/zzz.numary\n"
+ "+++ b/zzz.numary\n"
+ "@@ -1,0 +2,3 @@\n"
+ "+More lines\n"
+ "+And more\n"
+ "+Go here\n", pout);
+ git__free(pout);
+ git_patch_free(p);
+
+ /* "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\n0123456789\n"
+ * 33 bytes
+ */
+
+ changed = "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\nreplace a line\n";
+
+ cl_git_pass(git_patch_from_blob_and_buffer(
+ &p, bin, "zzz.normal", changed, 37, NULL, &opts));
+ cl_git_pass(git_patch_to_str(&pout, p));
+ cl_assert_equal_s(
+ "diff --git a/zzz.normal b/zzz.normal\n"
+ "index b435cd5..1604519 100644\n"
+ "Binary files a/zzz.normal and b/zzz.normal differ\n", pout);
+ git__free(pout);
+ git_patch_free(p);
+
+ cl_git_pass(git_patch_from_blob_and_buffer(
+ &p, bin, "zzz.textary", changed, 37, NULL, &opts));
+ cl_git_pass(git_patch_to_str(&pout, p));
+ cl_assert_equal_s(
+ "diff --git a/zzz.textary b/zzz.textary\n"
+ "index b435cd5..1604519 100644\n"
+ "--- a/zzz.textary\n"
+ "+++ b/zzz.textary\n"
+ "@@ -3 +3 @@\n"
+ "-0123456789\n"
+ "+replace a line\n", pout);
+ git__free(pout);
+ git_patch_free(p);
+
+ cl_git_pass(git_patch_from_blob_and_buffer(
+ &p, bin, "zzz.textalphary", changed, 37, NULL, &opts));
+ cl_git_pass(git_patch_to_str(&pout, p));
+ cl_assert_equal_s(
+ "diff --git a/zzz.textalphary b/zzz.textalphary\n"
+ "index b435cd5..1604519 100644\n"
+ "--- a/zzz.textalphary\n"
+ "+++ b/zzz.textalphary\n"
+ "@@ -3 +3 @@\n"
+ "-0123456789\n"
+ "+replace a line\n", pout);
+ git__free(pout);
+ git_patch_free(p);
+
+ cl_git_pass(git_patch_from_blob_and_buffer(
+ &p, bin, "zzz.textnumary", changed, 37, NULL, &opts));
+ cl_git_pass(git_patch_to_str(&pout, p));
+ cl_assert_equal_s(
+ "diff --git a/zzz.textnumary b/zzz.textnumary\n"
+ "index b435cd5..1604519 100644\n"
+ "--- a/zzz.textnumary\n"
+ "+++ b/zzz.textnumary\n"
+ "@@ -3 +3 @@ 0123456789\n"
+ "-0123456789\n"
+ "+replace a line\n", pout);
+ git__free(pout);
+ git_patch_free(p);
+
+ git_blob_free(nonbin);
+ git_blob_free(bin);
+}
diff --git a/tests/diff/diff_helpers.c b/tests/diff/diff_helpers.c
new file mode 100644
index 000000000..33bb561f6
--- /dev/null
+++ b/tests/diff/diff_helpers.c
@@ -0,0 +1,246 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+
+git_tree *resolve_commit_oid_to_tree(
+ git_repository *repo,
+ const char *partial_oid)
+{
+ size_t len = strlen(partial_oid);
+ git_oid oid;
+ git_object *obj = NULL;
+ git_tree *tree = NULL;
+
+ if (git_oid_fromstrn(&oid, partial_oid, len) == 0)
+ git_object_lookup_prefix(&obj, repo, &oid, len, GIT_OBJ_ANY);
+ cl_assert(obj);
+ if (git_object_type(obj) == GIT_OBJ_TREE)
+ return (git_tree *)obj;
+ cl_assert(git_object_type(obj) == GIT_OBJ_COMMIT);
+ cl_git_pass(git_commit_tree(&tree, (git_commit *)obj));
+ git_object_free(obj);
+ return tree;
+}
+
+static char diff_pick_suffix(int mode)
+{
+ if (S_ISDIR(mode))
+ return '/';
+ else if (GIT_PERMS_IS_EXEC(mode))
+ return '*';
+ else
+ return ' ';
+}
+
+static void fprintf_delta(FILE *fp, const git_diff_delta *delta, float progress)
+{
+ char code = git_diff_status_char(delta->status);
+ char old_suffix = diff_pick_suffix(delta->old_file.mode);
+ char new_suffix = diff_pick_suffix(delta->new_file.mode);
+
+ fprintf(fp, "%c\t%s", code, delta->old_file.path);
+
+ if ((delta->old_file.path != delta->new_file.path &&
+ strcmp(delta->old_file.path, delta->new_file.path) != 0) ||
+ (delta->old_file.mode != delta->new_file.mode &&
+ delta->old_file.mode != 0 && delta->new_file.mode != 0))
+ fprintf(fp, "%c %s%c", old_suffix, delta->new_file.path, new_suffix);
+ else if (old_suffix != ' ')
+ fprintf(fp, "%c", old_suffix);
+
+ fprintf(fp, "\t[%.2f]\n", progress);
+}
+
+int diff_file_cb(
+ const git_diff_delta *delta,
+ float progress,
+ void *payload)
+{
+ diff_expects *e = payload;
+
+ if (e->debug)
+ fprintf_delta(stderr, delta, progress);
+
+ if (e->names)
+ cl_assert_equal_s(e->names[e->files], delta->old_file.path);
+ if (e->statuses)
+ cl_assert_equal_i(e->statuses[e->files], (int)delta->status);
+
+ e->files++;
+
+ if ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0)
+ e->files_binary++;
+
+ cl_assert(delta->status <= GIT_DELTA_TYPECHANGE);
+
+ e->file_status[delta->status] += 1;
+
+ return 0;
+}
+
+int diff_print_file_cb(
+ const git_diff_delta *delta,
+ float progress,
+ void *payload)
+{
+ if (!payload) {
+ fprintf_delta(stderr, delta, progress);
+ return 0;
+ }
+
+ if (!((diff_expects *)payload)->debug)
+ fprintf_delta(stderr, delta, progress);
+
+ return diff_file_cb(delta, progress, payload);
+}
+
+int diff_hunk_cb(
+ const git_diff_delta *delta,
+ const git_diff_hunk *hunk,
+ void *payload)
+{
+ diff_expects *e = payload;
+ const char *scan = hunk->header, *scan_end = scan + hunk->header_len;
+
+ GIT_UNUSED(delta);
+
+ /* confirm no NUL bytes in header text */
+ while (scan < scan_end)
+ cl_assert('\0' != *scan++);
+
+ e->hunks++;
+ e->hunk_old_lines += hunk->old_lines;
+ e->hunk_new_lines += hunk->new_lines;
+ return 0;
+}
+
+int diff_line_cb(
+ const git_diff_delta *delta,
+ const git_diff_hunk *hunk,
+ const git_diff_line *line,
+ void *payload)
+{
+ diff_expects *e = payload;
+
+ GIT_UNUSED(delta);
+ GIT_UNUSED(hunk);
+
+ e->lines++;
+ switch (line->origin) {
+ case GIT_DIFF_LINE_CONTEXT:
+ case GIT_DIFF_LINE_CONTEXT_EOFNL: /* techically not a line */
+ e->line_ctxt++;
+ break;
+ case GIT_DIFF_LINE_ADDITION:
+ case GIT_DIFF_LINE_ADD_EOFNL: /* technically not a line add */
+ e->line_adds++;
+ break;
+ case GIT_DIFF_LINE_DELETION:
+ case GIT_DIFF_LINE_DEL_EOFNL: /* technically not a line delete */
+ e->line_dels++;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+int diff_foreach_via_iterator(
+ git_diff *diff,
+ git_diff_file_cb file_cb,
+ git_diff_hunk_cb hunk_cb,
+ git_diff_line_cb line_cb,
+ void *data)
+{
+ size_t d, num_d = git_diff_num_deltas(diff);
+
+ for (d = 0; d < num_d; ++d) {
+ git_patch *patch;
+ const git_diff_delta *delta;
+ size_t h, num_h;
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, d));
+ cl_assert((delta = git_patch_get_delta(patch)) != NULL);
+
+ /* call file_cb for this file */
+ if (file_cb != NULL && file_cb(delta, (float)d / num_d, data) != 0) {
+ git_patch_free(patch);
+ goto abort;
+ }
+
+ /* if there are no changes, then the patch will be NULL */
+ if (!patch) {
+ cl_assert(delta->status == GIT_DELTA_UNMODIFIED ||
+ (delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
+ continue;
+ }
+
+ if (!hunk_cb && !line_cb) {
+ git_patch_free(patch);
+ continue;
+ }
+
+ num_h = git_patch_num_hunks(patch);
+
+ for (h = 0; h < num_h; h++) {
+ const git_diff_hunk *hunk;
+ size_t l, num_l;
+
+ cl_git_pass(git_patch_get_hunk(&hunk, &num_l, patch, h));
+
+ if (hunk_cb && hunk_cb(delta, hunk, data) != 0) {
+ git_patch_free(patch);
+ goto abort;
+ }
+
+ for (l = 0; l < num_l; ++l) {
+ const git_diff_line *line;
+
+ cl_git_pass(git_patch_get_line_in_hunk(&line, patch, h, l));
+
+ if (line_cb &&
+ line_cb(delta, hunk, line, data) != 0) {
+ git_patch_free(patch);
+ goto abort;
+ }
+ }
+ }
+
+ git_patch_free(patch);
+ }
+
+ return 0;
+
+abort:
+ giterr_clear();
+ return GIT_EUSER;
+}
+
+static int diff_print_cb(
+ const git_diff_delta *delta,
+ const git_diff_hunk *hunk,
+ const git_diff_line *line,
+ void *payload)
+{
+ FILE *fp = payload;
+
+ GIT_UNUSED(delta); GIT_UNUSED(hunk);
+
+ if (line->origin == GIT_DIFF_LINE_CONTEXT ||
+ line->origin == GIT_DIFF_LINE_ADDITION ||
+ line->origin == GIT_DIFF_LINE_DELETION)
+ fputc(line->origin, fp);
+ fwrite(line->content, 1, line->content_len, fp);
+ return 0;
+}
+
+void diff_print(FILE *fp, git_diff *diff)
+{
+ cl_git_pass(git_diff_print(
+ diff, GIT_DIFF_FORMAT_PATCH, diff_print_cb, fp ? fp : stderr));
+}
+
+void diff_print_raw(FILE *fp, git_diff *diff)
+{
+ cl_git_pass(git_diff_print(
+ diff, GIT_DIFF_FORMAT_RAW, diff_print_cb, fp ? fp : stderr));
+}
diff --git a/tests/diff/diff_helpers.h b/tests/diff/diff_helpers.h
new file mode 100644
index 000000000..bf21f4b1f
--- /dev/null
+++ b/tests/diff/diff_helpers.h
@@ -0,0 +1,64 @@
+#include "fileops.h"
+#include "git2/diff.h"
+
+extern git_tree *resolve_commit_oid_to_tree(
+ git_repository *repo, const char *partial_oid);
+
+typedef struct {
+ int files;
+ int files_binary;
+
+ int file_status[10]; /* indexed by git_delta_t value */
+
+ int hunks;
+ int hunk_new_lines;
+ int hunk_old_lines;
+
+ int lines;
+ int line_ctxt;
+ int line_adds;
+ int line_dels;
+
+ /* optional arrays of expected specific values */
+ const char **names;
+ int *statuses;
+
+ int debug;
+
+} diff_expects;
+
+typedef struct {
+ const char *path;
+ const char *matched_pathspec;
+} notify_expected;
+
+extern int diff_file_cb(
+ const git_diff_delta *delta,
+ float progress,
+ void *cb_data);
+
+extern int diff_print_file_cb(
+ const git_diff_delta *delta,
+ float progress,
+ void *cb_data);
+
+extern int diff_hunk_cb(
+ const git_diff_delta *delta,
+ const git_diff_hunk *hunk,
+ void *cb_data);
+
+extern int diff_line_cb(
+ const git_diff_delta *delta,
+ const git_diff_hunk *hunk,
+ const git_diff_line *line,
+ void *cb_data);
+
+extern int diff_foreach_via_iterator(
+ git_diff *diff,
+ git_diff_file_cb file_cb,
+ git_diff_hunk_cb hunk_cb,
+ git_diff_line_cb line_cb,
+ void *data);
+
+extern void diff_print(FILE *fp, git_diff *diff);
+extern void diff_print_raw(FILE *fp, git_diff *diff);
diff --git a/tests/diff/diffiter.c b/tests/diff/diffiter.c
new file mode 100644
index 000000000..f886e1baa
--- /dev/null
+++ b/tests/diff/diffiter.c
@@ -0,0 +1,453 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+
+void test_diff_diffiter__initialize(void)
+{
+}
+
+void test_diff_diffiter__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_diff_diffiter__create(void)
+{
+ git_repository *repo = cl_git_sandbox_init("attr");
+ git_diff *diff;
+ size_t d, num_d;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
+
+ num_d = git_diff_num_deltas(diff);
+ for (d = 0; d < num_d; ++d) {
+ const git_diff_delta *delta = git_diff_get_delta(diff, d);
+ cl_assert(delta != NULL);
+ }
+
+ cl_assert(!git_diff_get_delta(diff, num_d));
+
+ git_diff_free(diff);
+}
+
+void test_diff_diffiter__iterate_files_1(void)
+{
+ git_repository *repo = cl_git_sandbox_init("attr");
+ git_diff *diff;
+ size_t d, num_d;
+ diff_expects exp = { 0 };
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
+
+ num_d = git_diff_num_deltas(diff);
+
+ for (d = 0; d < num_d; ++d) {
+ const git_diff_delta *delta = git_diff_get_delta(diff, d);
+ cl_assert(delta != NULL);
+
+ diff_file_cb(delta, (float)d / (float)num_d, &exp);
+ }
+ cl_assert_equal_sz(6, exp.files);
+
+ git_diff_free(diff);
+}
+
+void test_diff_diffiter__iterate_files_2(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_diff *diff;
+ size_t d, num_d;
+ int count = 0;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
+
+ num_d = git_diff_num_deltas(diff);
+ cl_assert_equal_i(8, (int)num_d);
+
+ for (d = 0; d < num_d; ++d) {
+ const git_diff_delta *delta = git_diff_get_delta(diff, d);
+ cl_assert(delta != NULL);
+ count++;
+ }
+ cl_assert_equal_i(8, count);
+
+ git_diff_free(diff);
+}
+
+void test_diff_diffiter__iterate_files_and_hunks(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ size_t d, num_d;
+ int file_count = 0, hunk_count = 0;
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
+
+ num_d = git_diff_num_deltas(diff);
+
+ for (d = 0; d < num_d; ++d) {
+ git_patch *patch;
+ size_t h, num_h;
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, d));
+ cl_assert(patch);
+
+ file_count++;
+
+ num_h = git_patch_num_hunks(patch);
+
+ for (h = 0; h < num_h; h++) {
+ const git_diff_hunk *hunk;
+
+ cl_git_pass(git_patch_get_hunk(&hunk, NULL, patch, h));
+ cl_assert(hunk);
+
+ hunk_count++;
+ }
+
+ git_patch_free(patch);
+ }
+
+ cl_assert_equal_i(13, file_count);
+ cl_assert_equal_i(8, hunk_count);
+
+ git_diff_free(diff);
+}
+
+void test_diff_diffiter__max_size_threshold(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ int file_count = 0, binary_count = 0, hunk_count = 0;
+ size_t d, num_d;
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
+ num_d = git_diff_num_deltas(diff);
+
+ for (d = 0; d < num_d; ++d) {
+ git_patch *patch;
+ const git_diff_delta *delta;
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, d));
+ cl_assert(patch);
+ delta = git_patch_get_delta(patch);
+ cl_assert(delta);
+
+ file_count++;
+ hunk_count += (int)git_patch_num_hunks(patch);
+
+ assert((delta->flags & (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY)) != 0);
+ binary_count += ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
+
+ git_patch_free(patch);
+ }
+
+ cl_assert_equal_i(13, file_count);
+ cl_assert_equal_i(0, binary_count);
+ cl_assert_equal_i(8, hunk_count);
+
+ git_diff_free(diff);
+
+ /* try again with low file size threshold */
+
+ file_count = binary_count = hunk_count = 0;
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+ opts.max_size = 50; /* treat anything over 50 bytes as binary! */
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
+ num_d = git_diff_num_deltas(diff);
+
+ for (d = 0; d < num_d; ++d) {
+ git_patch *patch;
+ const git_diff_delta *delta;
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, d));
+ delta = git_patch_get_delta(patch);
+
+ file_count++;
+ hunk_count += (int)git_patch_num_hunks(patch);
+
+ assert((delta->flags & (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY)) != 0);
+ binary_count += ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
+
+ git_patch_free(patch);
+ }
+
+ cl_assert_equal_i(13, file_count);
+ /* Three files are over the 50 byte threshold:
+ * - staged_changes_file_deleted
+ * - staged_changes_modified_file
+ * - staged_new_file_modified_file
+ */
+ cl_assert_equal_i(3, binary_count);
+ cl_assert_equal_i(5, hunk_count);
+
+ git_diff_free(diff);
+}
+
+
+void test_diff_diffiter__iterate_all(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ diff_expects exp = {0};
+ size_t d, num_d;
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
+
+ num_d = git_diff_num_deltas(diff);
+ for (d = 0; d < num_d; ++d) {
+ git_patch *patch;
+ size_t h, num_h;
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, d));
+ cl_assert(patch);
+ exp.files++;
+
+ num_h = git_patch_num_hunks(patch);
+ for (h = 0; h < num_h; h++) {
+ const git_diff_hunk *range;
+ size_t l, num_l;
+
+ cl_git_pass(git_patch_get_hunk(&range, &num_l, patch, h));
+ cl_assert(range);
+ exp.hunks++;
+
+ for (l = 0; l < num_l; ++l) {
+ const git_diff_line *line;
+
+ cl_git_pass(git_patch_get_line_in_hunk(&line, patch, h, l));
+ cl_assert(line && line->content);
+ exp.lines++;
+ }
+ }
+
+ git_patch_free(patch);
+ }
+
+ cl_assert_equal_i(13, exp.files);
+ cl_assert_equal_i(8, exp.hunks);
+ cl_assert_equal_i(14, exp.lines);
+
+ git_diff_free(diff);
+}
+
+static void iterate_over_patch(git_patch *patch, diff_expects *exp)
+{
+ size_t h, num_h = git_patch_num_hunks(patch), num_l;
+
+ exp->files++;
+ exp->hunks += (int)num_h;
+
+ /* let's iterate in reverse, just because we can! */
+ for (h = 1, num_l = 0; h <= num_h; ++h)
+ num_l += git_patch_num_lines_in_hunk(patch, num_h - h);
+
+ exp->lines += (int)num_l;
+}
+
+#define PATCH_CACHE 5
+
+void test_diff_diffiter__iterate_randomly_while_saving_state(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ diff_expects exp = {0};
+ git_patch *patches[PATCH_CACHE];
+ size_t p, d, num_d;
+
+ memset(patches, 0, sizeof(patches));
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
+
+ num_d = git_diff_num_deltas(diff);
+
+ /* To make sure that references counts work for diff and patch objects,
+ * this generates patches and randomly caches them. Only when the patch
+ * is removed from the cache are hunks and lines counted. At the end,
+ * there are still patches in the cache, so free the diff and try to
+ * process remaining patches after the diff is freed.
+ */
+
+ srand(121212);
+ p = rand() % PATCH_CACHE;
+
+ for (d = 0; d < num_d; ++d) {
+ /* take old patch */
+ git_patch *patch = patches[p];
+ patches[p] = NULL;
+
+ /* cache new patch */
+ cl_git_pass(git_patch_from_diff(&patches[p], diff, d));
+ cl_assert(patches[p] != NULL);
+
+ /* process old patch if non-NULL */
+ if (patch != NULL) {
+ iterate_over_patch(patch, &exp);
+ git_patch_free(patch);
+ }
+
+ p = rand() % PATCH_CACHE;
+ }
+
+ /* free diff list now - refcounts should keep things safe */
+ git_diff_free(diff);
+
+ /* process remaining unprocessed patches */
+ for (p = 0; p < PATCH_CACHE; p++) {
+ git_patch *patch = patches[p];
+
+ if (patch != NULL) {
+ iterate_over_patch(patch, &exp);
+ git_patch_free(patch);
+ }
+ }
+
+ /* hopefully it all still added up right */
+ cl_assert_equal_i(13, exp.files);
+ cl_assert_equal_i(8, exp.hunks);
+ cl_assert_equal_i(14, exp.lines);
+}
+
+/* This output is taken directly from `git diff` on the status test data */
+static const char *expected_patch_text[8] = {
+ /* 0 */
+ "diff --git a/file_deleted b/file_deleted\n"
+ "deleted file mode 100644\n"
+ "index 5452d32..0000000\n"
+ "--- a/file_deleted\n"
+ "+++ /dev/null\n"
+ "@@ -1 +0,0 @@\n"
+ "-file_deleted\n",
+ /* 1 */
+ "diff --git a/modified_file b/modified_file\n"
+ "index 452e424..0a53963 100644\n"
+ "--- a/modified_file\n"
+ "+++ b/modified_file\n"
+ "@@ -1 +1,2 @@\n"
+ " modified_file\n"
+ "+modified_file\n",
+ /* 2 */
+ "diff --git a/staged_changes_file_deleted b/staged_changes_file_deleted\n"
+ "deleted file mode 100644\n"
+ "index a6be623..0000000\n"
+ "--- a/staged_changes_file_deleted\n"
+ "+++ /dev/null\n"
+ "@@ -1,2 +0,0 @@\n"
+ "-staged_changes_file_deleted\n"
+ "-staged_changes_file_deleted\n",
+ /* 3 */
+ "diff --git a/staged_changes_modified_file b/staged_changes_modified_file\n"
+ "index 906ee77..011c344 100644\n"
+ "--- a/staged_changes_modified_file\n"
+ "+++ b/staged_changes_modified_file\n"
+ "@@ -1,2 +1,3 @@\n"
+ " staged_changes_modified_file\n"
+ " staged_changes_modified_file\n"
+ "+staged_changes_modified_file\n",
+ /* 4 */
+ "diff --git a/staged_new_file_deleted_file b/staged_new_file_deleted_file\n"
+ "deleted file mode 100644\n"
+ "index 90b8c29..0000000\n"
+ "--- a/staged_new_file_deleted_file\n"
+ "+++ /dev/null\n"
+ "@@ -1 +0,0 @@\n"
+ "-staged_new_file_deleted_file\n",
+ /* 5 */
+ "diff --git a/staged_new_file_modified_file b/staged_new_file_modified_file\n"
+ "index ed06290..8b090c0 100644\n"
+ "--- a/staged_new_file_modified_file\n"
+ "+++ b/staged_new_file_modified_file\n"
+ "@@ -1 +1,2 @@\n"
+ " staged_new_file_modified_file\n"
+ "+staged_new_file_modified_file\n",
+ /* 6 */
+ "diff --git a/subdir/deleted_file b/subdir/deleted_file\n"
+ "deleted file mode 100644\n"
+ "index 1888c80..0000000\n"
+ "--- a/subdir/deleted_file\n"
+ "+++ /dev/null\n"
+ "@@ -1 +0,0 @@\n"
+ "-subdir/deleted_file\n",
+ /* 7 */
+ "diff --git a/subdir/modified_file b/subdir/modified_file\n"
+ "index a619198..57274b7 100644\n"
+ "--- a/subdir/modified_file\n"
+ "+++ b/subdir/modified_file\n"
+ "@@ -1 +1,2 @@\n"
+ " subdir/modified_file\n"
+ "+subdir/modified_file\n"
+};
+
+void test_diff_diffiter__iterate_and_generate_patch_text(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_diff *diff;
+ size_t d, num_d;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
+
+ num_d = git_diff_num_deltas(diff);
+ cl_assert_equal_i(8, (int)num_d);
+
+ for (d = 0; d < num_d; ++d) {
+ git_patch *patch;
+ char *text;
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, d));
+ cl_assert(patch != NULL);
+
+ cl_git_pass(git_patch_to_str(&text, patch));
+
+ cl_assert_equal_s(expected_patch_text[d], text);
+
+ git__free(text);
+ git_patch_free(patch);
+ }
+
+ git_diff_free(diff);
+}
+
+void test_diff_diffiter__checks_options_version(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ const git_error *err;
+
+ opts.version = 0;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+
+ cl_git_fail(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
+ err = giterr_last();
+ cl_assert_equal_i(GITERR_INVALID, err->klass);
+
+ giterr_clear();
+ opts.version = 1024;
+ cl_git_fail(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
+ err = giterr_last();
+ cl_assert_equal_i(GITERR_INVALID, err->klass);
+}
+
diff --git a/tests/diff/drivers.c b/tests/diff/drivers.c
new file mode 100644
index 000000000..fbd1dff81
--- /dev/null
+++ b/tests/diff/drivers.c
@@ -0,0 +1,163 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+#include "repository.h"
+#include "diff_driver.h"
+
+static git_repository *g_repo = NULL;
+
+void test_diff_drivers__initialize(void)
+{
+}
+
+void test_diff_drivers__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ g_repo = NULL;
+}
+
+void test_diff_drivers__patterns(void)
+{
+ git_config *cfg;
+ const char *one_sha = "19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13";
+ git_tree *one;
+ git_diff *diff;
+ git_patch *patch;
+ char *text;
+ const char *expected0 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\n--- a/untimely.txt\n+++ b/untimely.txt\n@@ -22,3 +22,5 @@ Comes through the blood of the vanguards who\n dreamed--too soon--it had sounded.\r\n \r\n -- Rudyard Kipling\r\n+\r\n+Some new stuff\r\n";
+ const char *expected1 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\nBinary files a/untimely.txt and b/untimely.txt differ\n";
+ const char *expected2 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\n--- a/untimely.txt\n+++ b/untimely.txt\n@@ -22,3 +22,5 @@ Heaven delivers on earth the Hour that cannot be\n dreamed--too soon--it had sounded.\r\n \r\n -- Rudyard Kipling\r\n+\r\n+Some new stuff\r\n";
+
+ g_repo = cl_git_sandbox_init("renames");
+
+ one = resolve_commit_oid_to_tree(g_repo, one_sha);
+
+ /* no diff */
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
+ cl_assert_equal_i(0, (int)git_diff_num_deltas(diff));
+ git_diff_free(diff);
+
+ /* default diff */
+
+ cl_git_append2file("renames/untimely.txt", "\r\nSome new stuff\r\n");
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
+ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_str(&text, patch));
+ cl_assert_equal_s(expected0, text);
+
+ git__free(text);
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+ /* attribute diff set to false */
+
+ cl_git_rewritefile("renames/.gitattributes", "untimely.txt -diff\n");
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
+ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_str(&text, patch));
+ cl_assert_equal_s(expected1, text);
+
+ git__free(text);
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+ /* attribute diff set to unconfigured value (should use default) */
+
+ cl_git_rewritefile("renames/.gitattributes", "untimely.txt diff=kipling0\n");
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
+ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_str(&text, patch));
+ cl_assert_equal_s(expected0, text);
+
+ git__free(text);
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+ /* let's define that driver */
+
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(git_config_set_bool(cfg, "diff.kipling0.binary", 1));
+ git_config_free(cfg);
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
+ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_str(&text, patch));
+ cl_assert_equal_s(expected1, text);
+
+ git__free(text);
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+ /* let's use a real driver with some regular expressions */
+
+ git_diff_driver_registry_free(g_repo->diff_drivers);
+ g_repo->diff_drivers = NULL;
+
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(git_config_set_bool(cfg, "diff.kipling0.binary", 0));
+ cl_git_pass(git_config_set_string(cfg, "diff.kipling0.xfuncname", "^H"));
+ git_config_free(cfg);
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
+ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_str(&text, patch));
+ cl_assert_equal_s(expected2, text);
+
+ git__free(text);
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+ git_tree_free(one);
+}
+
+void test_diff_drivers__long_lines(void)
+{
+ const char *base = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non nisi ligula. Ut viverra enim sed lobortis suscipit.\nPhasellus eget erat odio. Praesent at est iaculis, ultricies augue vel, dignissim risus. Suspendisse at nisi quis turpis fringilla rutrum id sit amet nulla.\nNam eget dolor fermentum, aliquet nisl at, convallis tellus. Pellentesque rhoncus erat enim, id porttitor elit euismod quis.\nMauris sollicitudin magna odio, non egestas libero vehicula ut. Etiam et quam velit. Fusce eget libero rhoncus, ultricies felis sit amet, egestas purus.\nAliquam in semper tellus. Pellentesque adipiscing rutrum velit, quis malesuada lacus consequat eget.\n";
+ git_index *idx;
+ git_diff *diff;
+ git_patch *patch;
+ char *actual;
+ const char *expected = "diff --git a/longlines.txt b/longlines.txt\nindex c1ce6ef..0134431 100644\n--- a/longlines.txt\n+++ b/longlines.txt\n@@ -3,3 +3,5 @@ Phasellus eget erat odio. Praesent at est iaculis, ultricies augue vel, dignissi\n Nam eget dolor fermentum, aliquet nisl at, convallis tellus. Pellentesque rhoncus erat enim, id porttitor elit euismod quis.\n Mauris sollicitudin magna odio, non egestas libero vehicula ut. Etiam et quam velit. Fusce eget libero rhoncus, ultricies felis sit amet, egestas purus.\n Aliquam in semper tellus. Pellentesque adipiscing rutrum velit, quis malesuada lacus consequat eget.\n+newline\n+newline\n";
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_mkfile("empty_standard_repo/longlines.txt", base);
+ cl_git_pass(git_repository_index(&idx, g_repo));
+ cl_git_pass(git_index_add_bypath(idx, "longlines.txt"));
+ cl_git_pass(git_index_write(idx));
+ git_index_free(idx);
+
+ cl_git_append2file("empty_standard_repo/longlines.txt", "newline\nnewline\n");
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
+ cl_assert_equal_sz(1, git_diff_num_deltas(diff));
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_str(&actual, patch));
+
+ /* if chmod not supported, overwrite mode bits since anything is possible */
+ if (!cl_is_chmod_supported()) {
+ size_t actual_len = strlen(actual);
+ if (actual_len > 72 && memcmp(&actual[66], "100644", 6) != 0)
+ memcpy(&actual[66], "100644", 6);
+ }
+
+ cl_assert_equal_s(expected, actual);
+
+ free(actual);
+ git_patch_free(patch);
+ git_diff_free(diff);
+}
+
diff --git a/tests/diff/index.c b/tests/diff/index.c
new file mode 100644
index 000000000..8f4567137
--- /dev/null
+++ b/tests/diff/index.c
@@ -0,0 +1,167 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+
+static git_repository *g_repo = NULL;
+
+void test_diff_index__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("status");
+}
+
+void test_diff_index__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_diff_index__0(void)
+{
+ /* grabbed a couple of commit oids from the history of the attr repo */
+ const char *a_commit = "26a125ee1bf"; /* the current HEAD */
+ const char *b_commit = "0017bd4ab1ec3"; /* the start */
+ git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
+ git_tree *b = resolve_commit_oid_to_tree(g_repo, b_commit);
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ diff_expects exp;
+
+ cl_assert(a);
+ cl_assert(b);
+
+ opts.context_lines = 1;
+ opts.interhunk_lines = 1;
+
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ /* to generate these values:
+ * - cd to tests/resources/status,
+ * - mv .gitted .git
+ * - git diff --name-status --cached 26a125ee1bf
+ * - git diff -U1 --cached 26a125ee1bf
+ * - mv .git .gitted
+ */
+ cl_assert_equal_i(8, exp.files);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
+
+ cl_assert_equal_i(8, exp.hunks);
+
+ cl_assert_equal_i(11, exp.lines);
+ cl_assert_equal_i(3, exp.line_ctxt);
+ cl_assert_equal_i(6, exp.line_adds);
+ cl_assert_equal_i(2, exp.line_dels);
+
+ git_diff_free(diff);
+ diff = NULL;
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, b, NULL, &opts));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ /* to generate these values:
+ * - cd to tests/resources/status,
+ * - mv .gitted .git
+ * - git diff --name-status --cached 0017bd4ab1ec3
+ * - git diff -U1 --cached 0017bd4ab1ec3
+ * - mv .git .gitted
+ */
+ cl_assert_equal_i(12, exp.files);
+ cl_assert_equal_i(7, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
+
+ cl_assert_equal_i(12, exp.hunks);
+
+ cl_assert_equal_i(16, exp.lines);
+ cl_assert_equal_i(3, exp.line_ctxt);
+ cl_assert_equal_i(11, exp.line_adds);
+ cl_assert_equal_i(2, exp.line_dels);
+
+ git_diff_free(diff);
+ diff = NULL;
+
+ git_tree_free(a);
+ git_tree_free(b);
+}
+
+static int diff_stop_after_2_files(
+ const git_diff_delta *delta,
+ float progress,
+ void *payload)
+{
+ diff_expects *e = payload;
+
+ GIT_UNUSED(progress);
+ GIT_UNUSED(delta);
+
+ e->files++;
+
+ return (e->files == 2);
+}
+
+void test_diff_index__1(void)
+{
+ /* grabbed a couple of commit oids from the history of the attr repo */
+ const char *a_commit = "26a125ee1bf"; /* the current HEAD */
+ const char *b_commit = "0017bd4ab1ec3"; /* the start */
+ git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
+ git_tree *b = resolve_commit_oid_to_tree(g_repo, b_commit);
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ diff_expects exp;
+
+ cl_assert(a);
+ cl_assert(b);
+
+ opts.context_lines = 1;
+ opts.interhunk_lines = 1;
+
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
+
+ cl_assert_equal_i(
+ GIT_EUSER,
+ git_diff_foreach(diff, diff_stop_after_2_files, NULL, NULL, &exp)
+ );
+
+ cl_assert_equal_i(2, exp.files);
+
+ git_diff_free(diff);
+ diff = NULL;
+
+ git_tree_free(a);
+ git_tree_free(b);
+}
+
+void test_diff_index__checks_options_version(void)
+{
+ const char *a_commit = "26a125ee1bf";
+ git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ const git_error *err;
+
+ opts.version = 0;
+ cl_git_fail(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
+ err = giterr_last();
+ cl_assert_equal_i(GITERR_INVALID, err->klass);
+ cl_assert_equal_p(diff, NULL);
+
+ giterr_clear();
+ opts.version = 1024;
+ cl_git_fail(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
+ err = giterr_last();
+ cl_assert_equal_i(GITERR_INVALID, err->klass);
+ cl_assert_equal_p(diff, NULL);
+
+ git_tree_free(a);
+}
+
diff --git a/tests-clar/diff/iterator.c b/tests/diff/iterator.c
index bbdae8ad1..bbdae8ad1 100644
--- a/tests-clar/diff/iterator.c
+++ b/tests/diff/iterator.c
diff --git a/tests/diff/notify.c b/tests/diff/notify.c
new file mode 100644
index 000000000..cc33cb71c
--- /dev/null
+++ b/tests/diff/notify.c
@@ -0,0 +1,228 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+
+static git_repository *g_repo = NULL;
+
+void test_diff_notify__initialize(void)
+{
+}
+
+void test_diff_notify__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static int assert_called_notifications(
+ const git_diff *diff_so_far,
+ const git_diff_delta *delta_to_add,
+ const char *matched_pathspec,
+ void *payload)
+{
+ bool found = false;
+ notify_expected *exp = (notify_expected*)payload;
+ notify_expected *e;;
+
+ GIT_UNUSED(diff_so_far);
+
+ for (e = exp; e->path != NULL; e++) {
+ if (strcmp(e->path, delta_to_add->new_file.path))
+ continue;
+
+ cl_assert_equal_s(e->matched_pathspec, matched_pathspec);
+
+ found = true;
+ break;
+ }
+
+ cl_assert(found);
+ return 0;
+}
+
+static void test_notify(
+ char **searched_pathspecs,
+ int pathspecs_count,
+ notify_expected *expected_matched_pathspecs,
+ int expected_diffed_files_count)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ diff_expects exp;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+ opts.notify_cb = assert_called_notifications;
+ opts.pathspec.strings = searched_pathspecs;
+ opts.pathspec.count = pathspecs_count;
+
+ opts.notify_payload = expected_matched_pathspecs;
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
+
+ cl_assert_equal_i(expected_diffed_files_count, exp.files);
+
+ git_diff_free(diff);
+}
+
+void test_diff_notify__notify_single_pathspec(void)
+{
+ char *searched_pathspecs[] = {
+ "*_deleted",
+ };
+ notify_expected expected_matched_pathspecs[] = {
+ { "file_deleted", "*_deleted" },
+ { "staged_changes_file_deleted", "*_deleted" },
+ { NULL, NULL }
+ };
+
+ test_notify(searched_pathspecs, 1, expected_matched_pathspecs, 2);
+}
+
+void test_diff_notify__notify_multiple_pathspec(void)
+{
+ char *searched_pathspecs[] = {
+ "staged_changes_cant_find_me",
+ "subdir/modified_cant_find_me",
+ "subdir/*",
+ "staged*"
+ };
+ notify_expected expected_matched_pathspecs[] = {
+ { "staged_changes_file_deleted", "staged*" },
+ { "staged_changes_modified_file", "staged*" },
+ { "staged_delete_modified_file", "staged*" },
+ { "staged_new_file_deleted_file", "staged*" },
+ { "staged_new_file_modified_file", "staged*" },
+ { "subdir/deleted_file", "subdir/*" },
+ { "subdir/modified_file", "subdir/*" },
+ { "subdir/new_file", "subdir/*" },
+ { NULL, NULL }
+ };
+
+ test_notify(searched_pathspecs, 4, expected_matched_pathspecs, 8);
+}
+
+void test_diff_notify__notify_catchall_with_empty_pathspecs(void)
+{
+ char *searched_pathspecs[] = {
+ "",
+ ""
+ };
+ notify_expected expected_matched_pathspecs[] = {
+ { "file_deleted", NULL },
+ { "ignored_file", NULL },
+ { "modified_file", NULL },
+ { "new_file", NULL },
+ { "\xe8\xbf\x99", NULL },
+ { "staged_changes_file_deleted", NULL },
+ { "staged_changes_modified_file", NULL },
+ { "staged_delete_modified_file", NULL },
+ { "staged_new_file_deleted_file", NULL },
+ { "staged_new_file_modified_file", NULL },
+ { "subdir/deleted_file", NULL },
+ { "subdir/modified_file", NULL },
+ { "subdir/new_file", NULL },
+ { NULL, NULL }
+ };
+
+ test_notify(searched_pathspecs, 1, expected_matched_pathspecs, 13);
+}
+
+void test_diff_notify__notify_catchall(void)
+{
+ char *searched_pathspecs[] = {
+ "*",
+ };
+ notify_expected expected_matched_pathspecs[] = {
+ { "file_deleted", "*" },
+ { "ignored_file", "*" },
+ { "modified_file", "*" },
+ { "new_file", "*" },
+ { "\xe8\xbf\x99", "*" },
+ { "staged_changes_file_deleted", "*" },
+ { "staged_changes_modified_file", "*" },
+ { "staged_delete_modified_file", "*" },
+ { "staged_new_file_deleted_file", "*" },
+ { "staged_new_file_modified_file", "*" },
+ { "subdir/deleted_file", "*" },
+ { "subdir/modified_file", "*" },
+ { "subdir/new_file", "*" },
+ { NULL, NULL }
+ };
+
+ test_notify(searched_pathspecs, 1, expected_matched_pathspecs, 13);
+}
+
+static int abort_diff(
+ const git_diff *diff_so_far,
+ const git_diff_delta *delta_to_add,
+ const char *matched_pathspec,
+ void *payload)
+{
+ GIT_UNUSED(diff_so_far);
+ GIT_UNUSED(delta_to_add);
+ GIT_UNUSED(matched_pathspec);
+ GIT_UNUSED(payload);
+
+ return -42;
+}
+
+void test_diff_notify__notify_cb_can_abort_diff(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ char *pathspec = NULL;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+ opts.notify_cb = abort_diff;
+ opts.pathspec.strings = &pathspec;
+ opts.pathspec.count = 1;
+
+ pathspec = "file_deleted";
+ cl_git_fail(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ pathspec = "staged_changes_modified_file";
+ cl_git_fail(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+}
+
+static int filter_all(
+ const git_diff *diff_so_far,
+ const git_diff_delta *delta_to_add,
+ const char *matched_pathspec,
+ void *payload)
+{
+ GIT_UNUSED(diff_so_far);
+ GIT_UNUSED(delta_to_add);
+ GIT_UNUSED(matched_pathspec);
+ GIT_UNUSED(payload);
+
+ return 42;
+}
+
+void test_diff_notify__notify_cb_can_be_used_as_filtering_function(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ char *pathspec = NULL;
+ diff_expects exp;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+ opts.notify_cb = filter_all;
+ opts.pathspec.strings = &pathspec;
+ opts.pathspec.count = 1;
+
+ pathspec = "*_deleted";
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
+
+ cl_assert_equal_i(0, exp.files);
+
+ git_diff_free(diff);
+}
diff --git a/tests/diff/patch.c b/tests/diff/patch.c
new file mode 100644
index 000000000..bd1598b21
--- /dev/null
+++ b/tests/diff/patch.c
@@ -0,0 +1,577 @@
+#include "clar_libgit2.h"
+#include "git2/sys/repository.h"
+
+#include "diff_helpers.h"
+#include "repository.h"
+#include "buf_text.h"
+
+static git_repository *g_repo = NULL;
+
+void test_diff_patch__initialize(void)
+{
+}
+
+void test_diff_patch__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+#define EXPECTED_HEADER "diff --git a/subdir.txt b/subdir.txt\n" \
+ "deleted file mode 100644\n" \
+ "index e8ee89e..0000000\n" \
+ "--- a/subdir.txt\n" \
+ "+++ /dev/null\n"
+
+#define EXPECTED_HUNK "@@ -1,2 +0,0 @@\n"
+
+static int check_removal_cb(
+ const git_diff_delta *delta,
+ const git_diff_hunk *hunk,
+ const git_diff_line *line,
+ void *payload)
+{
+ GIT_UNUSED(payload);
+
+ switch (line->origin) {
+ case GIT_DIFF_LINE_FILE_HDR:
+ cl_assert_equal_s(EXPECTED_HEADER, line->content);
+ cl_assert(hunk == NULL);
+ goto check_delta;
+
+ case GIT_DIFF_LINE_HUNK_HDR:
+ cl_assert_equal_s(EXPECTED_HUNK, line->content);
+ /* Fall through */
+
+ case GIT_DIFF_LINE_CONTEXT:
+ case GIT_DIFF_LINE_DELETION:
+ goto check_hunk;
+
+ default:
+ /* unexpected code path */
+ return -1;
+ }
+
+check_hunk:
+ cl_assert(hunk != NULL);
+ cl_assert_equal_i(1, hunk->old_start);
+ cl_assert_equal_i(2, hunk->old_lines);
+ cl_assert_equal_i(0, hunk->new_start);
+ cl_assert_equal_i(0, hunk->new_lines);
+
+check_delta:
+ cl_assert_equal_s("subdir.txt", delta->old_file.path);
+ cl_assert_equal_s("subdir.txt", delta->new_file.path);
+ cl_assert_equal_i(GIT_DELTA_DELETED, delta->status);
+
+ return 0;
+}
+
+void test_diff_patch__can_properly_display_the_removal_of_a_file(void)
+{
+ /*
+ * $ git diff 26a125e..735b6a2
+ * diff --git a/subdir.txt b/subdir.txt
+ * deleted file mode 100644
+ * index e8ee89e..0000000
+ * --- a/subdir.txt
+ * +++ /dev/null
+ * @@ -1,2 +0,0 @@
+ * -Is it a bird?
+ * -Is it a plane?
+ */
+
+ const char *one_sha = "26a125e";
+ const char *another_sha = "735b6a2";
+ git_tree *one, *another;
+ git_diff *diff;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ one = resolve_commit_oid_to_tree(g_repo, one_sha);
+ another = resolve_commit_oid_to_tree(g_repo, another_sha);
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, one, another, NULL));
+
+ cl_git_pass(git_diff_print(
+ diff, GIT_DIFF_FORMAT_PATCH, check_removal_cb, NULL));
+
+ git_diff_free(diff);
+
+ git_tree_free(another);
+ git_tree_free(one);
+}
+
+void test_diff_patch__to_string(void)
+{
+ const char *one_sha = "26a125e";
+ const char *another_sha = "735b6a2";
+ git_tree *one, *another;
+ git_diff *diff;
+ git_patch *patch;
+ char *text;
+ const char *expected = "diff --git a/subdir.txt b/subdir.txt\ndeleted file mode 100644\nindex e8ee89e..0000000\n--- a/subdir.txt\n+++ /dev/null\n@@ -1,2 +0,0 @@\n-Is it a bird?\n-Is it a plane?\n";
+
+ g_repo = cl_git_sandbox_init("status");
+
+ one = resolve_commit_oid_to_tree(g_repo, one_sha);
+ another = resolve_commit_oid_to_tree(g_repo, another_sha);
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, one, another, NULL));
+
+ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+
+ cl_git_pass(git_patch_to_str(&text, patch));
+
+ cl_assert_equal_s(expected, text);
+
+ cl_assert_equal_sz(31, git_patch_size(patch, 0, 0, 0));
+ cl_assert_equal_sz(31, git_patch_size(patch, 1, 0, 0));
+ cl_assert_equal_sz(31 + 16, git_patch_size(patch, 1, 1, 0));
+ cl_assert_equal_sz(strlen(expected), git_patch_size(patch, 1, 1, 1));
+
+ git__free(text);
+ git_patch_free(patch);
+ git_diff_free(diff);
+ git_tree_free(another);
+ git_tree_free(one);
+}
+
+void test_diff_patch__config_options(void)
+{
+ const char *one_sha = "26a125e"; /* current HEAD */
+ git_tree *one;
+ git_config *cfg;
+ git_diff *diff;
+ git_patch *patch;
+ char *text;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ char *onefile = "staged_changes_modified_file";
+ const char *expected1 = "diff --git c/staged_changes_modified_file i/staged_changes_modified_file\nindex 70bd944..906ee77 100644\n--- c/staged_changes_modified_file\n+++ i/staged_changes_modified_file\n@@ -1 +1,2 @@\n staged_changes_modified_file\n+staged_changes_modified_file\n";
+ const char *expected2 = "diff --git i/staged_changes_modified_file w/staged_changes_modified_file\nindex 906ee77..011c344 100644\n--- i/staged_changes_modified_file\n+++ w/staged_changes_modified_file\n@@ -1,2 +1,3 @@\n staged_changes_modified_file\n staged_changes_modified_file\n+staged_changes_modified_file\n";
+ const char *expected3 = "diff --git staged_changes_modified_file staged_changes_modified_file\nindex 906ee77..011c344 100644\n--- staged_changes_modified_file\n+++ staged_changes_modified_file\n@@ -1,2 +1,3 @@\n staged_changes_modified_file\n staged_changes_modified_file\n+staged_changes_modified_file\n";
+ const char *expected4 = "diff --git staged_changes_modified_file staged_changes_modified_file\nindex 70bd9443ada0..906ee7711f4f 100644\n--- staged_changes_modified_file\n+++ staged_changes_modified_file\n@@ -1 +1,2 @@\n staged_changes_modified_file\n+staged_changes_modified_file\n";
+
+ g_repo = cl_git_sandbox_init("status");
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ one = resolve_commit_oid_to_tree(g_repo, one_sha);
+ opts.pathspec.count = 1;
+ opts.pathspec.strings = &onefile;
+
+
+ cl_git_pass(git_config_set_string(cfg, "diff.mnemonicprefix", "true"));
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, one, NULL, &opts));
+
+ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_str(&text, patch));
+ cl_assert_equal_s(expected1, text);
+
+ git__free(text);
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_str(&text, patch));
+ cl_assert_equal_s(expected2, text);
+
+ git__free(text);
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+
+ cl_git_pass(git_config_set_string(cfg, "diff.noprefix", "true"));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_str(&text, patch));
+ cl_assert_equal_s(expected3, text);
+
+ git__free(text);
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+
+ cl_git_pass(git_config_set_int32(cfg, "core.abbrev", 12));
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, one, NULL, &opts));
+
+ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_str(&text, patch));
+ cl_assert_equal_s(expected4, text);
+
+ git__free(text);
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+ git_tree_free(one);
+ git_config_free(cfg);
+}
+
+void test_diff_patch__hunks_have_correct_line_numbers(void)
+{
+ git_config *cfg;
+ git_tree *head;
+ git_diff_options opt = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff;
+ git_patch *patch;
+ const git_diff_delta *delta;
+ const git_diff_hunk *hunk;
+ const git_diff_line *line;
+ size_t hunklen;
+ git_buf old_content = GIT_BUF_INIT, actual = GIT_BUF_INIT;
+ const char *new_content = "The Song of Seven Cities\n------------------------\n\nI WAS Lord of Cities very sumptuously builded.\nSeven roaring Cities paid me tribute from afar.\nIvory their outposts were--the guardrooms of them gilded,\nAnd garrisoned with Amazons invincible in war.\n\nThis is some new text;\nNot as good as the old text;\nBut here it is.\n\nSo they warred and trafficked only yesterday, my Cities.\nTo-day there is no mark or mound of where my Cities stood.\nFor the River rose at midnight and it washed away my Cities.\nThey are evened with Atlantis and the towns before the Flood.\n\nRain on rain-gorged channels raised the water-levels round them,\nFreshet backed on freshet swelled and swept their world from sight,\nTill the emboldened floods linked arms and, flashing forward, drowned them--\nDrowned my Seven Cities and their peoples in one night!\n\nLow among the alders lie their derelict foundations,\nThe beams wherein they trusted and the plinths whereon they built--\nMy rulers and their treasure and their unborn populations,\nDead, destroyed, aborted, and defiled with mud and silt!\n\nAnother replacement;\nBreaking up the poem;\nGenerating some hunks.\n\nTo the sound of trumpets shall their seed restore my Cities\nWealthy and well-weaponed, that once more may I behold\nAll the world go softly when it walks before my Cities,\nAnd the horses and the chariots fleeing from them as of old!\n\n -- Rudyard Kipling\n";
+
+ g_repo = cl_git_sandbox_init("renames");
+
+ cl_git_pass(git_config_new(&cfg));
+ git_repository_set_config(g_repo, cfg);
+ git_config_free(cfg);
+
+ git_repository_reinit_filesystem(g_repo, false);
+
+ cl_git_pass(
+ git_futils_readbuffer(&old_content, "renames/songof7cities.txt"));
+
+ cl_git_rewritefile("renames/songof7cities.txt", new_content);
+
+ cl_git_pass(git_repository_head_tree(&head, g_repo));
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, head, &opt));
+
+ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_assert((delta = git_patch_get_delta(patch)) != NULL);
+
+ cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
+ cl_assert_equal_i(2, (int)git_patch_num_hunks(patch));
+
+ /* check hunk 0 */
+
+ cl_git_pass(
+ git_patch_get_hunk(&hunk, &hunklen, patch, 0));
+
+ cl_assert_equal_i(18, (int)hunklen);
+
+ cl_assert_equal_i(6, (int)hunk->old_start);
+ cl_assert_equal_i(15, (int)hunk->old_lines);
+ cl_assert_equal_i(6, (int)hunk->new_start);
+ cl_assert_equal_i(9, (int)hunk->new_lines);
+
+ cl_assert_equal_i(18, (int)git_patch_num_lines_in_hunk(patch, 0));
+
+ cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 0));
+ cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)line->origin);
+ cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
+ cl_assert_equal_s("Ivory their outposts were--the guardrooms of them gilded,\n", actual.ptr);
+ cl_assert_equal_i(6, line->old_lineno);
+ cl_assert_equal_i(6, line->new_lineno);
+ cl_assert_equal_i(-1, line->content_offset);
+
+ cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 3));
+ cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)line->origin);
+ cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
+ cl_assert_equal_s("All the world went softly when it walked before my Cities--\n", actual.ptr);
+ cl_assert_equal_i(9, line->old_lineno);
+ cl_assert_equal_i(-1, line->new_lineno);
+ cl_assert_equal_i(252, line->content_offset);
+
+ cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 12));
+ cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)line->origin);
+ cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
+ cl_assert_equal_s("This is some new text;\n", actual.ptr);
+ cl_assert_equal_i(-1, line->old_lineno);
+ cl_assert_equal_i(9, line->new_lineno);
+ cl_assert_equal_i(252, line->content_offset);
+
+ /* check hunk 1 */
+
+ cl_git_pass(git_patch_get_hunk(&hunk, &hunklen, patch, 1));
+
+ cl_assert_equal_i(18, (int)hunklen);
+
+ cl_assert_equal_i(31, (int)hunk->old_start);
+ cl_assert_equal_i(15, (int)hunk->old_lines);
+ cl_assert_equal_i(25, (int)hunk->new_start);
+ cl_assert_equal_i(9, (int)hunk->new_lines);
+
+ cl_assert_equal_i(18, (int)git_patch_num_lines_in_hunk(patch, 1));
+
+ cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 1, 0));
+ cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)line->origin);
+ cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
+ cl_assert_equal_s("My rulers and their treasure and their unborn populations,\n", actual.ptr);
+ cl_assert_equal_i(31, line->old_lineno);
+ cl_assert_equal_i(25, line->new_lineno);
+ cl_assert_equal_i(-1, line->content_offset);
+
+ cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 1, 3));
+ cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)line->origin);
+ cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
+ cl_assert_equal_s("The Daughters of the Palace whom they cherished in my Cities,\n", actual.ptr);
+ cl_assert_equal_i(34, line->old_lineno);
+ cl_assert_equal_i(-1, line->new_lineno);
+ cl_assert_equal_i(1468, line->content_offset);
+
+ cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 1, 12));
+ cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)line->origin);
+ cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
+ cl_assert_equal_s("Another replacement;\n", actual.ptr);
+ cl_assert_equal_i(-1, line->old_lineno);
+ cl_assert_equal_i(28, line->new_lineno);
+ cl_assert_equal_i(1066, line->content_offset);
+
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+ /* Let's check line numbers when there is no newline */
+
+ git_buf_rtrim(&old_content);
+ cl_git_rewritefile("renames/songof7cities.txt", old_content.ptr);
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, head, &opt));
+
+ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_assert((delta = git_patch_get_delta(patch)) != NULL);
+
+ cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
+ cl_assert_equal_i(1, (int)git_patch_num_hunks(patch));
+
+ /* check hunk 0 */
+
+ cl_git_pass(git_patch_get_hunk(&hunk, &hunklen, patch, 0));
+
+ cl_assert_equal_i(6, (int)hunklen);
+
+ cl_assert_equal_i(46, (int)hunk->old_start);
+ cl_assert_equal_i(4, (int)hunk->old_lines);
+ cl_assert_equal_i(46, (int)hunk->new_start);
+ cl_assert_equal_i(4, (int)hunk->new_lines);
+
+ cl_assert_equal_i(6, (int)git_patch_num_lines_in_hunk(patch, 0));
+
+ cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 1));
+ cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)line->origin);
+ cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
+ cl_assert_equal_s("And the horses and the chariots fleeing from them as of old!\n", actual.ptr);
+ cl_assert_equal_i(47, line->old_lineno);
+ cl_assert_equal_i(47, line->new_lineno);
+
+ cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 2));
+ cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)line->origin);
+ cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
+ cl_assert_equal_s("\n", actual.ptr);
+ cl_assert_equal_i(48, line->old_lineno);
+ cl_assert_equal_i(48, line->new_lineno);
+
+ cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 3));
+ cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)line->origin);
+ cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
+ cl_assert_equal_s(" -- Rudyard Kipling\n", actual.ptr);
+ cl_assert_equal_i(49, line->old_lineno);
+ cl_assert_equal_i(-1, line->new_lineno);
+
+ cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 4));
+ cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)line->origin);
+ cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
+ cl_assert_equal_s(" -- Rudyard Kipling", actual.ptr);
+ cl_assert_equal_i(-1, line->old_lineno);
+ cl_assert_equal_i(49, line->new_lineno);
+
+ cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 5));
+ cl_assert_equal_i(GIT_DIFF_LINE_DEL_EOFNL, (int)line->origin);
+ cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
+ cl_assert_equal_s("\n\\ No newline at end of file\n", actual.ptr);
+ cl_assert_equal_i(-1, line->old_lineno);
+ cl_assert_equal_i(49, line->new_lineno);
+
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+ git_buf_free(&actual);
+ git_buf_free(&old_content);
+ git_tree_free(head);
+}
+
+static void check_single_patch_stats(
+ git_repository *repo, size_t hunks,
+ size_t adds, size_t dels, size_t ctxt, size_t *sizes,
+ const char *expected)
+{
+ git_diff *diff;
+ git_patch *patch;
+ const git_diff_delta *delta;
+ size_t actual_ctxt, actual_adds, actual_dels;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
+
+ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_assert((delta = git_patch_get_delta(patch)) != NULL);
+ cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
+
+ cl_assert_equal_i((int)hunks, (int)git_patch_num_hunks(patch));
+
+ cl_git_pass( git_patch_line_stats(
+ &actual_ctxt, &actual_adds, &actual_dels, patch) );
+
+ cl_assert_equal_sz(ctxt, actual_ctxt);
+ cl_assert_equal_sz(adds, actual_adds);
+ cl_assert_equal_sz(dels, actual_dels);
+
+ if (expected != NULL) {
+ char *text;
+ cl_git_pass(git_patch_to_str(&text, patch));
+ cl_assert_equal_s(expected, text);
+ git__free(text);
+
+ cl_assert_equal_sz(
+ strlen(expected), git_patch_size(patch, 1, 1, 1));
+ }
+
+ if (sizes) {
+ if (sizes[0])
+ cl_assert_equal_sz(sizes[0], git_patch_size(patch, 0, 0, 0));
+ if (sizes[1])
+ cl_assert_equal_sz(sizes[1], git_patch_size(patch, 1, 0, 0));
+ if (sizes[2])
+ cl_assert_equal_sz(sizes[2], git_patch_size(patch, 1, 1, 0));
+ }
+
+ /* walk lines in hunk with basic sanity checks */
+ for (; hunks > 0; --hunks) {
+ size_t i, max_i;
+ const git_diff_line *line;
+ int last_new_lineno = -1, last_old_lineno = -1;
+
+ max_i = git_patch_num_lines_in_hunk(patch, hunks - 1);
+
+ for (i = 0; i < max_i; ++i) {
+ int expected = 1;
+
+ cl_git_pass(
+ git_patch_get_line_in_hunk(&line, patch, hunks - 1, i));
+
+ if (line->origin == GIT_DIFF_LINE_ADD_EOFNL ||
+ line->origin == GIT_DIFF_LINE_DEL_EOFNL ||
+ line->origin == GIT_DIFF_LINE_CONTEXT_EOFNL)
+ expected = 0;
+
+ if (line->old_lineno >= 0) {
+ if (last_old_lineno >= 0)
+ cl_assert_equal_i(
+ expected, line->old_lineno - last_old_lineno);
+ last_old_lineno = line->old_lineno;
+ }
+
+ if (line->new_lineno >= 0) {
+ if (last_new_lineno >= 0)
+ cl_assert_equal_i(
+ expected, line->new_lineno - last_new_lineno);
+ last_new_lineno = line->new_lineno;
+ }
+ }
+ }
+
+ git_patch_free(patch);
+ git_diff_free(diff);
+}
+
+void test_diff_patch__line_counts_with_eofnl(void)
+{
+ git_config *cfg;
+ git_buf content = GIT_BUF_INIT;
+ const char *end;
+ git_index *index;
+ const char *expected =
+ /* below is pasted output of 'git diff' with fn context removed */
+ "diff --git a/songof7cities.txt b/songof7cities.txt\n"
+ "index 378a7d9..3d0154e 100644\n"
+ "--- a/songof7cities.txt\n"
+ "+++ b/songof7cities.txt\n"
+ "@@ -42,7 +42,7 @@ With peoples undefeated of the dark, enduring blood.\n"
+ " \n"
+ " To the sound of trumpets shall their seed restore my Cities\n"
+ " Wealthy and well-weaponed, that once more may I behold\n"
+ "-All the world go softly when it walks before my Cities,\n"
+ "+#All the world go softly when it walks before my Cities,\n"
+ " And the horses and the chariots fleeing from them as of old!\n"
+ " \n"
+ " -- Rudyard Kipling\n"
+ "\\ No newline at end of file\n";
+ size_t expected_sizes[3] = { 115, 119 + 115 + 114, 119 + 115 + 114 + 71 };
+
+ g_repo = cl_git_sandbox_init("renames");
+
+ cl_git_pass(git_config_new(&cfg));
+ git_repository_set_config(g_repo, cfg);
+ git_config_free(cfg);
+
+ git_repository_reinit_filesystem(g_repo, false);
+
+ cl_git_pass(git_futils_readbuffer(&content, "renames/songof7cities.txt"));
+
+ /* remove first line */
+
+ end = git_buf_cstr(&content) + git_buf_find(&content, '\n') + 1;
+ git_buf_consume(&content, end);
+ cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
+
+ check_single_patch_stats(g_repo, 1, 0, 1, 3, NULL, NULL);
+
+ /* remove trailing whitespace */
+
+ git_buf_rtrim(&content);
+ cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
+
+ check_single_patch_stats(g_repo, 2, 1, 2, 6, NULL, NULL);
+
+ /* add trailing whitespace */
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_add_bypath(index, "songof7cities.txt"));
+ cl_git_pass(git_index_write(index));
+ git_index_free(index);
+
+ cl_git_pass(git_buf_putc(&content, '\n'));
+ cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
+
+ check_single_patch_stats(g_repo, 1, 1, 1, 3, NULL, NULL);
+
+ /* no trailing whitespace as context line */
+
+ {
+ /* walk back a couple lines, make space and insert char */
+ char *scan = content.ptr + content.size;
+ int i;
+
+ for (i = 0; i < 5; ++i) {
+ for (--scan; scan > content.ptr && *scan != '\n'; --scan)
+ /* seek to prev \n */;
+ }
+ cl_assert(scan > content.ptr);
+
+ /* overwrite trailing \n with right-shifted content */
+ memmove(scan + 1, scan, content.size - (scan - content.ptr) - 1);
+ /* insert '#' char into space we created */
+ scan[1] = '#';
+ }
+ cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
+
+ check_single_patch_stats(
+ g_repo, 1, 1, 1, 6, expected_sizes, expected);
+
+ git_buf_free(&content);
+}
diff --git a/tests/diff/pathspec.c b/tests/diff/pathspec.c
new file mode 100644
index 000000000..5761d2d2b
--- /dev/null
+++ b/tests/diff/pathspec.c
@@ -0,0 +1,93 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+
+static git_repository *g_repo = NULL;
+
+void test_diff_pathspec__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("status");
+}
+
+void test_diff_pathspec__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_diff_pathspec__0(void)
+{
+ const char *a_commit = "26a125ee"; /* the current HEAD */
+ const char *b_commit = "0017bd4a"; /* the start */
+ git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
+ git_tree *b = resolve_commit_oid_to_tree(g_repo, b_commit);
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ git_strarray paths = { NULL, 1 };
+ char *path;
+ git_pathspec *ps;
+ git_pathspec_match_list *matches;
+
+ cl_assert(a);
+ cl_assert(b);
+
+ path = "*_file";
+ paths.strings = &path;
+ cl_git_pass(git_pathspec_new(&ps, &paths));
+
+ cl_git_pass(git_pathspec_match_tree(&matches, a, GIT_PATHSPEC_DEFAULT, ps));
+ cl_assert_equal_i(7, (int)git_pathspec_match_list_entrycount(matches));
+ cl_assert_equal_s("current_file", git_pathspec_match_list_entry(matches,0));
+ cl_assert(git_pathspec_match_list_diff_entry(matches,0) == NULL);
+ git_pathspec_match_list_free(matches);
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, NULL, a, &opts));
+
+ cl_git_pass(git_pathspec_match_diff(
+ &matches, diff, GIT_PATHSPEC_DEFAULT, ps));
+ cl_assert_equal_i(7, (int)git_pathspec_match_list_entrycount(matches));
+ cl_assert(git_pathspec_match_list_diff_entry(matches, 0) != NULL);
+ cl_assert(git_pathspec_match_list_entry(matches, 0) == NULL);
+ cl_assert_equal_s("current_file",
+ git_pathspec_match_list_diff_entry(matches,0)->new_file.path);
+ cl_assert_equal_i(GIT_DELTA_ADDED,
+ (int)git_pathspec_match_list_diff_entry(matches,0)->status);
+ git_pathspec_match_list_free(matches);
+
+ git_diff_free(diff);
+ diff = NULL;
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
+
+ cl_git_pass(git_pathspec_match_diff(
+ &matches, diff, GIT_PATHSPEC_DEFAULT, ps));
+ cl_assert_equal_i(3, (int)git_pathspec_match_list_entrycount(matches));
+ cl_assert(git_pathspec_match_list_diff_entry(matches, 0) != NULL);
+ cl_assert(git_pathspec_match_list_entry(matches, 0) == NULL);
+ cl_assert_equal_s("subdir/current_file",
+ git_pathspec_match_list_diff_entry(matches,0)->new_file.path);
+ cl_assert_equal_i(GIT_DELTA_DELETED,
+ (int)git_pathspec_match_list_diff_entry(matches,0)->status);
+ git_pathspec_match_list_free(matches);
+
+ git_diff_free(diff);
+ diff = NULL;
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts));
+
+ cl_git_pass(git_pathspec_match_diff(
+ &matches, diff, GIT_PATHSPEC_DEFAULT, ps));
+ cl_assert_equal_i(4, (int)git_pathspec_match_list_entrycount(matches));
+ cl_assert(git_pathspec_match_list_diff_entry(matches, 0) != NULL);
+ cl_assert(git_pathspec_match_list_entry(matches, 0) == NULL);
+ cl_assert_equal_s("modified_file",
+ git_pathspec_match_list_diff_entry(matches,0)->new_file.path);
+ cl_assert_equal_i(GIT_DELTA_MODIFIED,
+ (int)git_pathspec_match_list_diff_entry(matches,0)->status);
+ git_pathspec_match_list_free(matches);
+
+ git_diff_free(diff);
+ diff = NULL;
+
+ git_tree_free(a);
+ git_tree_free(b);
+ git_pathspec_free(ps);
+}
diff --git a/tests/diff/rename.c b/tests/diff/rename.c
new file mode 100644
index 000000000..42bb65aa8
--- /dev/null
+++ b/tests/diff/rename.c
@@ -0,0 +1,1286 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+#include "buf_text.h"
+
+static git_repository *g_repo = NULL;
+
+void test_diff_rename__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("renames");
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", false);
+}
+
+void test_diff_rename__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+/*
+ * Renames repo has:
+ *
+ * commit 31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 -
+ * serving.txt (25 lines)
+ * sevencities.txt (50 lines)
+ * commit 2bc7f351d20b53f1c72c16c4b036e491c478c49a -
+ * serving.txt -> sixserving.txt (rename, no change, 100% match)
+ * sevencities.txt -> sevencities.txt (no change)
+ * sevencities.txt -> songofseven.txt (copy, no change, 100% match)
+ * commit 1c068dee5790ef1580cfc4cd670915b48d790084
+ * songofseven.txt -> songofseven.txt (major rewrite, <20% match - split)
+ * sixserving.txt -> sixserving.txt (indentation change)
+ * sixserving.txt -> ikeepsix.txt (copy, add title, >80% match)
+ * sevencities.txt (no change)
+ * commit 19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13
+ * songofseven.txt -> untimely.txt (rename, convert to crlf)
+ * ikeepsix.txt -> ikeepsix.txt (reorder sections in file)
+ * sixserving.txt -> sixserving.txt (whitespace change - not just indent)
+ * sevencities.txt -> songof7cities.txt (rename, small text changes)
+ */
+
+void test_diff_rename__match_oid(void)
+{
+ const char *old_sha = "31e47d8c1fa36d7f8d537b96158e3f024de0a9f2";
+ const char *new_sha = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
+ git_tree *old_tree, *new_tree;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ diff_expects exp;
+
+ old_tree = resolve_commit_oid_to_tree(g_repo, old_sha);
+ new_tree = resolve_commit_oid_to_tree(g_repo, new_sha);
+
+ /* Must pass GIT_DIFF_INCLUDE_UNMODIFIED if you expect to emulate
+ * --find-copies-harder during rename transformion...
+ */
+ diffopts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
+
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, old_tree, new_tree, &diffopts));
+
+ /* git diff --no-renames \
+ * 31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 \
+ * 2bc7f351d20b53f1c72c16c4b036e491c478c49a
+ */
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+
+ /* git diff 31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 \
+ * 2bc7f351d20b53f1c72c16c4b036e491c478c49a
+ * don't use NULL opts to avoid config `diff.renames` contamination
+ */
+ opts.flags = GIT_DIFF_FIND_RENAMES;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(3, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
+
+ git_diff_free(diff);
+
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, old_tree, new_tree, &diffopts));
+
+ /* git diff --find-copies-harder \
+ * 31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 \
+ * 2bc7f351d20b53f1c72c16c4b036e491c478c49a
+ */
+ opts.flags = GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(3, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_COPIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
+
+ git_diff_free(diff);
+
+ git_tree_free(old_tree);
+ git_tree_free(new_tree);
+}
+
+void test_diff_rename__checks_options_version(void)
+{
+ const char *old_sha = "31e47d8c1fa36d7f8d537b96158e3f024de0a9f2";
+ const char *new_sha = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
+ git_tree *old_tree, *new_tree;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ const git_error *err;
+
+ old_tree = resolve_commit_oid_to_tree(g_repo, old_sha);
+ new_tree = resolve_commit_oid_to_tree(g_repo, new_sha);
+ diffopts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, old_tree, new_tree, &diffopts));
+
+ opts.version = 0;
+ cl_git_fail(git_diff_find_similar(diff, &opts));
+ err = giterr_last();
+ cl_assert_equal_i(GITERR_INVALID, err->klass);
+
+ giterr_clear();
+ opts.version = 1024;
+ cl_git_fail(git_diff_find_similar(diff, &opts));
+ err = giterr_last();
+ cl_assert_equal_i(GITERR_INVALID, err->klass);
+
+ git_diff_free(diff);
+ git_tree_free(old_tree);
+ git_tree_free(new_tree);
+}
+
+void test_diff_rename__not_exact_match(void)
+{
+ const char *sha0 = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
+ const char *sha1 = "1c068dee5790ef1580cfc4cd670915b48d790084";
+ const char *sha2 = "19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13";
+ git_tree *old_tree, *new_tree;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ diff_expects exp;
+
+ /* == Changes =====================================================
+ * songofseven.txt -> songofseven.txt (major rewrite, <20% match - split)
+ * sixserving.txt -> sixserving.txt (indentation change)
+ * sixserving.txt -> ikeepsix.txt (copy, add title, >80% match)
+ * sevencities.txt (no change)
+ */
+
+ old_tree = resolve_commit_oid_to_tree(g_repo, sha0);
+ new_tree = resolve_commit_oid_to_tree(g_repo, sha1);
+
+ /* Must pass GIT_DIFF_INCLUDE_UNMODIFIED if you expect to emulate
+ * --find-copies-harder during rename transformion...
+ */
+ diffopts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
+
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, old_tree, new_tree, &diffopts));
+
+ /* git diff --no-renames \
+ * 2bc7f351d20b53f1c72c16c4b036e491c478c49a \
+ * 1c068dee5790ef1580cfc4cd670915b48d790084
+ */
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
+
+ /* git diff -M 2bc7f351d20b53f1c72c16c4b036e491c478c49a \
+ * 1c068dee5790ef1580cfc4cd670915b48d790084
+ *
+ * must not pass NULL for opts because it will pick up environment
+ * values for "diff.renames" and test won't be consistent.
+ */
+ opts.flags = GIT_DIFF_FIND_RENAMES;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
+
+ git_diff_free(diff);
+
+ /* git diff -M -C \
+ * 2bc7f351d20b53f1c72c16c4b036e491c478c49a \
+ * 1c068dee5790ef1580cfc4cd670915b48d790084
+ */
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, old_tree, new_tree, &diffopts));
+
+ opts.flags = GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_COPIED]);
+
+ git_diff_free(diff);
+
+ /* git diff -M -C --find-copies-harder --break-rewrites \
+ * 2bc7f351d20b53f1c72c16c4b036e491c478c49a \
+ * 1c068dee5790ef1580cfc4cd670915b48d790084
+ */
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, old_tree, new_tree, &diffopts));
+
+ opts.flags = GIT_DIFF_FIND_ALL;
+ opts.break_rewrite_threshold = 70;
+
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(5, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_COPIED]);
+
+ git_diff_free(diff);
+
+ /* == Changes =====================================================
+ * songofseven.txt -> untimely.txt (rename, convert to crlf)
+ * ikeepsix.txt -> ikeepsix.txt (reorder sections in file)
+ * sixserving.txt -> sixserving.txt (whitespace - not just indent)
+ * sevencities.txt -> songof7cities.txt (rename, small text changes)
+ */
+
+ git_tree_free(old_tree);
+ old_tree = new_tree;
+ new_tree = resolve_commit_oid_to_tree(g_repo, sha2);
+
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, old_tree, new_tree, &diffopts));
+
+ /* git diff --no-renames \
+ * 1c068dee5790ef1580cfc4cd670915b48d790084 \
+ * 19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13
+ */
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(6, exp.files);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
+ git_diff_free(diff);
+
+ /* git diff -M -C \
+ * 1c068dee5790ef1580cfc4cd670915b48d790084 \
+ * 19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13
+ */
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, old_tree, new_tree, &diffopts));
+
+ opts.flags = GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_RENAMED]);
+
+ git_diff_free(diff);
+
+ /* git diff -M -C --find-copies-harder --break-rewrites \
+ * 1c068dee5790ef1580cfc4cd670915b48d790084 \
+ * 19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13
+ * with libgit2 default similarity comparison...
+ */
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, old_tree, new_tree, &diffopts));
+
+ opts.flags = GIT_DIFF_FIND_ALL;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ /* the default match algorithm is going to find the internal
+ * whitespace differences in the lines of sixserving.txt to be
+ * significant enough that this will decide to split it into an ADD
+ * and a DELETE
+ */
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(5, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_RENAMED]);
+
+ git_diff_free(diff);
+
+ /* git diff -M -C --find-copies-harder --break-rewrites \
+ * 1c068dee5790ef1580cfc4cd670915b48d790084 \
+ * 19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13
+ * with ignore_space whitespace comparision
+ */
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, old_tree, new_tree, &diffopts));
+
+ opts.flags = GIT_DIFF_FIND_ALL | GIT_DIFF_FIND_IGNORE_WHITESPACE;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ /* Ignoring whitespace, this should no longer split sixserver.txt */
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_RENAMED]);
+
+ git_diff_free(diff);
+
+ git_tree_free(old_tree);
+ git_tree_free(new_tree);
+}
+
+void test_diff_rename__handles_small_files(void)
+{
+ const char *tree_sha = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
+ git_index *index;
+ git_tree *tree;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ tree = resolve_commit_oid_to_tree(g_repo, tree_sha);
+
+ cl_git_rewritefile("renames/songof7cities.txt", "single line\n");
+ cl_git_pass(git_index_add_bypath(index, "songof7cities.txt"));
+
+ cl_git_rewritefile("renames/untimely.txt", "untimely\n");
+ cl_git_pass(git_index_add_bypath(index, "untimely.txt"));
+
+ /* Tests that we can invoke find_similar on small files
+ * and that the GIT_EBUFS (too small) error code is not
+ * propagated to the caller.
+ */
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
+
+ opts.flags = GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES |
+ GIT_DIFF_FIND_AND_BREAK_REWRITES;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_index_free(index);
+}
+
+void test_diff_rename__working_directory_changes(void)
+{
+ const char *sha0 = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
+ const char *blobsha = "66311f5cfbe7836c27510a3ba2f43e282e2c8bba";
+ git_oid id;
+ git_tree *tree;
+ git_blob *blob;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ diff_expects exp;
+ git_buf old_content = GIT_BUF_INIT, content = GIT_BUF_INIT;;
+
+ tree = resolve_commit_oid_to_tree(g_repo, sha0);
+ diffopts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED | GIT_DIFF_INCLUDE_UNTRACKED;
+
+ /*
+ $ git cat-file -p 2bc7f351d20b53f1c72c16c4b036e491c478c49a^{tree}
+
+ 100644 blob 66311f5cfbe7836c27510a3ba2f43e282e2c8bba sevencities.txt
+ 100644 blob ad0a8e55a104ac54a8a29ed4b84b49e76837a113 sixserving.txt
+ 100644 blob 66311f5cfbe7836c27510a3ba2f43e282e2c8bba songofseven.txt
+
+ $ for f in *.txt; do
+ echo `git hash-object -t blob $f` $f
+ done
+
+ eaf4a3e3bfe68585e90cada20736ace491cd100b ikeepsix.txt
+ f90d4fc20ecddf21eebe6a37e9225d244339d2b5 sixserving.txt
+ 4210ffd5c390b21dd5483375e75288dea9ede512 songof7cities.txt
+ 9a69d960ae94b060f56c2a8702545e2bb1abb935 untimely.txt
+ */
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &diffopts));
+
+ /* git diff --no-renames 2bc7f351d20b53f1c72c16c4b036e491c478c49a */
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(6, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ /* git diff -M 2bc7f351d20b53f1c72c16c4b036e491c478c49a */
+ opts.flags = GIT_DIFF_FIND_ALL;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(5, exp.files);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_RENAMED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ git_diff_free(diff);
+
+ /* rewrite files in the working directory with / without CRLF changes */
+
+ cl_git_pass(
+ git_futils_readbuffer(&old_content, "renames/songof7cities.txt"));
+ cl_git_pass(
+ git_buf_text_lf_to_crlf(&content, &old_content));
+ cl_git_pass(
+ git_futils_writebuffer(&content, "renames/songof7cities.txt", 0, 0));
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &diffopts));
+
+ /* git diff -M 2bc7f351d20b53f1c72c16c4b036e491c478c49a */
+ opts.flags = GIT_DIFF_FIND_ALL;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(5, exp.files);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_RENAMED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ git_diff_free(diff);
+
+ /* try a different whitespace option */
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &diffopts));
+
+ opts.flags = GIT_DIFF_FIND_ALL | GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE;
+ opts.rename_threshold = 70;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(6, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ git_diff_free(diff);
+
+ /* try a different matching option */
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &diffopts));
+
+ opts.flags = GIT_DIFF_FIND_ALL | GIT_DIFF_FIND_EXACT_MATCH_ONLY;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(6, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
+
+ git_diff_free(diff);
+
+ /* again with exact match blob */
+
+ cl_git_pass(git_oid_fromstr(&id, blobsha));
+ cl_git_pass(git_blob_lookup(&blob, g_repo, &id));
+ cl_git_pass(git_buf_set(
+ &content, git_blob_rawcontent(blob), (size_t)git_blob_rawsize(blob)));
+ cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
+ git_blob_free(blob);
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &diffopts));
+
+ opts.flags = GIT_DIFF_FIND_ALL | GIT_DIFF_FIND_EXACT_MATCH_ONLY;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ /*
+ fprintf(stderr, "\n\n");
+ diff_print_raw(stderr, diff);
+ */
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(5, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ git_diff_free(diff);
+
+ git_tree_free(tree);
+ git_buf_free(&content);
+ git_buf_free(&old_content);
+}
+
+void test_diff_rename__patch(void)
+{
+ const char *sha0 = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
+ const char *sha1 = "1c068dee5790ef1580cfc4cd670915b48d790084";
+ git_tree *old_tree, *new_tree;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ git_patch *patch;
+ const git_diff_delta *delta;
+ char *text;
+ const char *expected = "diff --git a/sixserving.txt b/ikeepsix.txt\nindex ad0a8e5..36020db 100644\n--- a/sixserving.txt\n+++ b/ikeepsix.txt\n@@ -1,3 +1,6 @@\n+I Keep Six Honest Serving-Men\n+=============================\n+\n I KEEP six honest serving-men\n (They taught me all I knew);\n Their names are What and Why and When\n@@ -21,4 +24,4 @@ She sends'em abroad on her own affairs,\n One million Hows, two million Wheres,\n And seven million Whys!\n \n- -- Rudyard Kipling\n+ -- Rudyard Kipling\n";
+
+ old_tree = resolve_commit_oid_to_tree(g_repo, sha0);
+ new_tree = resolve_commit_oid_to_tree(g_repo, sha1);
+
+ diffopts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, old_tree, new_tree, &diffopts));
+
+ opts.flags = GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ /* == Changes =====================================================
+ * sixserving.txt -> ikeepsix.txt (copy, add title, >80% match)
+ * sevencities.txt (no change)
+ * sixserving.txt -> sixserving.txt (indentation change)
+ * songofseven.txt -> songofseven.txt (major rewrite, <20% match - split)
+ */
+
+ cl_assert_equal_i(4, (int)git_diff_num_deltas(diff));
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_assert((delta = git_patch_get_delta(patch)) != NULL);
+ cl_assert_equal_i(GIT_DELTA_COPIED, (int)delta->status);
+
+ cl_git_pass(git_patch_to_str(&text, patch));
+ cl_assert_equal_s(expected, text);
+ git__free(text);
+
+ git_patch_free(patch);
+
+ cl_assert((delta = git_diff_get_delta(diff, 1)) != NULL);
+ cl_assert_equal_i(GIT_DELTA_UNMODIFIED, (int)delta->status);
+
+ cl_assert((delta = git_diff_get_delta(diff, 2)) != NULL);
+ cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
+
+ cl_assert((delta = git_diff_get_delta(diff, 3)) != NULL);
+ cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
+
+ git_diff_free(diff);
+ git_tree_free(old_tree);
+ git_tree_free(new_tree);
+}
+
+void test_diff_rename__file_exchange(void)
+{
+ git_buf c1 = GIT_BUF_INIT, c2 = GIT_BUF_INIT;
+ git_index *index;
+ git_tree *tree;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ diff_expects exp;
+
+ cl_git_pass(git_futils_readbuffer(&c1, "renames/untimely.txt"));
+ cl_git_pass(git_futils_readbuffer(&c2, "renames/songof7cities.txt"));
+ cl_git_pass(git_futils_writebuffer(&c1, "renames/songof7cities.txt", 0, 0));
+ cl_git_pass(git_futils_writebuffer(&c2, "renames/untimely.txt", 0, 0));
+
+ cl_git_pass(
+ git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_read_tree(index, tree));
+ cl_git_pass(git_index_add_bypath(index, "songof7cities.txt"));
+ cl_git_pass(git_index_add_bypath(index, "untimely.txt"));
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(2, exp.files);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
+
+ opts.flags = GIT_DIFF_FIND_ALL;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(2, exp.files);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_RENAMED]);
+
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_index_free(index);
+
+ git_buf_free(&c1);
+ git_buf_free(&c2);
+}
+
+void test_diff_rename__file_exchange_three(void)
+{
+ git_buf c1 = GIT_BUF_INIT, c2 = GIT_BUF_INIT, c3 = GIT_BUF_INIT;
+ git_index *index;
+ git_tree *tree;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ diff_expects exp;
+
+ cl_git_pass(git_futils_readbuffer(&c1, "renames/untimely.txt"));
+ cl_git_pass(git_futils_readbuffer(&c2, "renames/songof7cities.txt"));
+ cl_git_pass(git_futils_readbuffer(&c3, "renames/ikeepsix.txt"));
+
+ cl_git_pass(git_futils_writebuffer(&c1, "renames/ikeepsix.txt", 0, 0));
+ cl_git_pass(git_futils_writebuffer(&c2, "renames/untimely.txt", 0, 0));
+ cl_git_pass(git_futils_writebuffer(&c3, "renames/songof7cities.txt", 0, 0));
+
+ cl_git_pass(
+ git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_read_tree(index, tree));
+ cl_git_pass(git_index_add_bypath(index, "songof7cities.txt"));
+ cl_git_pass(git_index_add_bypath(index, "untimely.txt"));
+ cl_git_pass(git_index_add_bypath(index, "ikeepsix.txt"));
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(3, exp.files);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
+
+ opts.flags = GIT_DIFF_FIND_ALL;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(3, exp.files);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_RENAMED]);
+
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_index_free(index);
+
+ git_buf_free(&c1);
+ git_buf_free(&c2);
+ git_buf_free(&c3);
+}
+
+void test_diff_rename__file_partial_exchange(void)
+{
+ git_buf c1 = GIT_BUF_INIT, c2 = GIT_BUF_INIT;
+ git_index *index;
+ git_tree *tree;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ diff_expects exp;
+ int i;
+
+ cl_git_pass(git_futils_readbuffer(&c1, "renames/untimely.txt"));
+ cl_git_pass(git_futils_writebuffer(&c1, "renames/songof7cities.txt", 0, 0));
+ for (i = 0; i < 100; ++i)
+ cl_git_pass(git_buf_puts(&c2, "this is not the content you are looking for\n"));
+ cl_git_pass(git_futils_writebuffer(&c2, "renames/untimely.txt", 0, 0));
+
+ cl_git_pass(
+ git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_read_tree(index, tree));
+ cl_git_pass(git_index_add_bypath(index, "songof7cities.txt"));
+ cl_git_pass(git_index_add_bypath(index, "untimely.txt"));
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(2, exp.files);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
+
+ opts.flags = GIT_DIFF_FIND_ALL;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(3, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_index_free(index);
+
+ git_buf_free(&c1);
+ git_buf_free(&c2);
+}
+
+void test_diff_rename__rename_and_copy_from_same_source(void)
+{
+ git_buf c1 = GIT_BUF_INIT, c2 = GIT_BUF_INIT;
+ git_index *index;
+ git_tree *tree;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ diff_expects exp;
+
+ /* put the first 2/3 of file into one new place
+ * and the second 2/3 of file into another new place
+ */
+ cl_git_pass(git_futils_readbuffer(&c1, "renames/songof7cities.txt"));
+ cl_git_pass(git_buf_set(&c2, c1.ptr, c1.size));
+ git_buf_truncate(&c1, c1.size * 2 / 3);
+ git_buf_consume(&c2, ((char *)c2.ptr) + (c2.size / 3));
+ cl_git_pass(git_futils_writebuffer(&c1, "renames/song_a.txt", 0, 0));
+ cl_git_pass(git_futils_writebuffer(&c2, "renames/song_b.txt", 0, 0));
+
+ cl_git_pass(
+ git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_read_tree(index, tree));
+ cl_git_pass(git_index_add_bypath(index, "song_a.txt"));
+ cl_git_pass(git_index_add_bypath(index, "song_b.txt"));
+
+ diffopts.flags = GIT_DIFF_INCLUDE_UNMODIFIED;
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(6, exp.files);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNMODIFIED]);
+
+ opts.flags = GIT_DIFF_FIND_ALL;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(6, exp.files);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_COPIED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNMODIFIED]);
+
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_index_free(index);
+
+ git_buf_free(&c1);
+ git_buf_free(&c2);
+}
+
+void test_diff_rename__from_deleted_to_split(void)
+{
+ git_buf c1 = GIT_BUF_INIT;
+ git_index *index;
+ git_tree *tree;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ diff_expects exp;
+
+ /* old file is missing, new file is actually old file renamed */
+
+ cl_git_pass(git_futils_readbuffer(&c1, "renames/songof7cities.txt"));
+ cl_git_pass(git_futils_writebuffer(&c1, "renames/untimely.txt", 0, 0));
+
+ cl_git_pass(
+ git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_read_tree(index, tree));
+ cl_git_pass(git_index_remove_bypath(index, "songof7cities.txt"));
+ cl_git_pass(git_index_add_bypath(index, "untimely.txt"));
+
+ diffopts.flags = GIT_DIFF_INCLUDE_UNMODIFIED;
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNMODIFIED]);
+
+ opts.flags = GIT_DIFF_FIND_ALL;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNMODIFIED]);
+
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_index_free(index);
+
+ git_buf_free(&c1);
+}
+
+struct rename_expected
+{
+ size_t len;
+
+ unsigned int *status;
+ const char **sources;
+ const char **targets;
+
+ size_t idx;
+};
+
+int test_names_expected(const git_diff_delta *delta, float progress, void *p)
+{
+ struct rename_expected *expected = p;
+
+ GIT_UNUSED(progress);
+
+ cl_assert(expected->idx < expected->len);
+
+ cl_assert_equal_i(delta->status, expected->status[expected->idx]);
+
+ cl_assert(git__strcmp(expected->sources[expected->idx],
+ delta->old_file.path) == 0);
+ cl_assert(git__strcmp(expected->targets[expected->idx],
+ delta->new_file.path) == 0);
+
+ expected->idx++;
+
+ return 0;
+}
+
+void test_diff_rename__rejected_match_can_match_others(void)
+{
+ git_reference *head, *selfsimilar;
+ git_index *index;
+ git_tree *tree;
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
+ git_buf one = GIT_BUF_INIT, two = GIT_BUF_INIT;
+ unsigned int status[] = { GIT_DELTA_RENAMED, GIT_DELTA_RENAMED };
+ const char *sources[] = { "Class1.cs", "Class2.cs" };
+ const char *targets[] = { "ClassA.cs", "ClassB.cs" };
+ struct rename_expected expect = { 2, status, sources, targets };
+ char *ptr;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
+ cl_git_pass(git_reference_symbolic_set_target(
+ &selfsimilar, head, "refs/heads/renames_similar"));
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_pass(git_futils_readbuffer(&one, "renames/Class1.cs"));
+ cl_git_pass(git_futils_readbuffer(&two, "renames/Class2.cs"));
+
+ cl_git_pass(p_unlink("renames/Class1.cs"));
+ cl_git_pass(p_unlink("renames/Class2.cs"));
+
+ cl_git_pass(git_index_remove_bypath(index, "Class1.cs"));
+ cl_git_pass(git_index_remove_bypath(index, "Class2.cs"));
+
+ cl_assert(ptr = strstr(one.ptr, "Class1"));
+ ptr[5] = 'A';
+
+ cl_assert(ptr = strstr(two.ptr, "Class2"));
+ ptr[5] = 'B';
+
+ cl_git_pass(
+ git_futils_writebuffer(&one, "renames/ClassA.cs", O_RDWR|O_CREAT, 0777));
+ cl_git_pass(
+ git_futils_writebuffer(&two, "renames/ClassB.cs", O_RDWR|O_CREAT, 0777));
+
+ cl_git_pass(git_index_add_bypath(index, "ClassA.cs"));
+ cl_git_pass(git_index_add_bypath(index, "ClassB.cs"));
+
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(
+ git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(
+ git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
+
+ cl_git_pass(git_diff_find_similar(diff, &findopts));
+
+ cl_git_pass(
+ git_diff_foreach(diff, test_names_expected, NULL, NULL, &expect));
+
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_index_free(index);
+ git_reference_free(head);
+ git_reference_free(selfsimilar);
+ git_buf_free(&one);
+ git_buf_free(&two);
+}
+
+static void write_similarity_file_two(const char *filename, size_t b_lines)
+{
+ git_buf contents = GIT_BUF_INIT;
+ size_t i;
+
+ for (i = 0; i < b_lines; i++)
+ git_buf_printf(&contents, "%02d - bbbbb\r\n", (int)(i+1));
+
+ for (i = b_lines; i < 50; i++)
+ git_buf_printf(&contents, "%02d - aaaaa%s", (int)(i+1), (i == 49 ? "" : "\r\n"));
+
+ cl_git_pass(
+ git_futils_writebuffer(&contents, filename, O_RDWR|O_CREAT, 0777));
+
+ git_buf_free(&contents);
+}
+
+void test_diff_rename__rejected_match_can_match_others_two(void)
+{
+ git_reference *head, *selfsimilar;
+ git_index *index;
+ git_tree *tree;
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
+ unsigned int status[] = { GIT_DELTA_RENAMED, GIT_DELTA_RENAMED };
+ const char *sources[] = { "a.txt", "b.txt" };
+ const char *targets[] = { "c.txt", "d.txt" };
+ struct rename_expected expect = { 2, status, sources, targets };
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
+ cl_git_pass(git_reference_symbolic_set_target(
+ &selfsimilar, head, "refs/heads/renames_similar_two"));
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_pass(p_unlink("renames/a.txt"));
+ cl_git_pass(p_unlink("renames/b.txt"));
+
+ cl_git_pass(git_index_remove_bypath(index, "a.txt"));
+ cl_git_pass(git_index_remove_bypath(index, "b.txt"));
+
+ write_similarity_file_two("renames/c.txt", 7);
+ write_similarity_file_two("renames/d.txt", 8);
+
+ cl_git_pass(git_index_add_bypath(index, "c.txt"));
+ cl_git_pass(git_index_add_bypath(index, "d.txt"));
+
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(
+ git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(
+ git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
+
+ cl_git_pass(git_diff_find_similar(diff, &findopts));
+
+ cl_git_pass(
+ git_diff_foreach(diff, test_names_expected, NULL, NULL, &expect));
+ cl_assert(expect.idx > 0);
+
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_index_free(index);
+ git_reference_free(head);
+ git_reference_free(selfsimilar);
+}
+
+void test_diff_rename__rejected_match_can_match_others_three(void)
+{
+ git_reference *head, *selfsimilar;
+ git_index *index;
+ git_tree *tree;
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
+
+ /* Both cannot be renames from a.txt */
+ unsigned int status[] = { GIT_DELTA_ADDED, GIT_DELTA_RENAMED };
+ const char *sources[] = { "0001.txt", "a.txt" };
+ const char *targets[] = { "0001.txt", "0002.txt" };
+ struct rename_expected expect = { 2, status, sources, targets };
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
+ cl_git_pass(git_reference_symbolic_set_target(
+ &selfsimilar, head, "refs/heads/renames_similar_two"));
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_pass(p_unlink("renames/a.txt"));
+
+ cl_git_pass(git_index_remove_bypath(index, "a.txt"));
+
+ write_similarity_file_two("renames/0001.txt", 7);
+ write_similarity_file_two("renames/0002.txt", 0);
+
+ cl_git_pass(git_index_add_bypath(index, "0001.txt"));
+ cl_git_pass(git_index_add_bypath(index, "0002.txt"));
+
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(
+ git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(
+ git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
+
+ cl_git_pass(git_diff_find_similar(diff, &findopts));
+
+ cl_git_pass(
+ git_diff_foreach(diff, test_names_expected, NULL, NULL, &expect));
+
+ cl_assert(expect.idx == expect.len);
+
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_index_free(index);
+ git_reference_free(head);
+ git_reference_free(selfsimilar);
+}
+
+void test_diff_rename__can_rename_from_rewrite(void)
+{
+ git_index *index;
+ git_tree *tree;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
+
+ unsigned int status[] = { GIT_DELTA_RENAMED, GIT_DELTA_RENAMED };
+ const char *sources[] = { "ikeepsix.txt", "songof7cities.txt" };
+ const char *targets[] = { "songof7cities.txt", "this-is-a-rename.txt" };
+ struct rename_expected expect = { 2, status, sources, targets };
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_pass(p_rename("renames/songof7cities.txt", "renames/this-is-a-rename.txt"));
+ cl_git_pass(p_rename("renames/ikeepsix.txt", "renames/songof7cities.txt"));
+
+ cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
+
+ cl_git_pass(git_index_add_bypath(index, "songof7cities.txt"));
+ cl_git_pass(git_index_add_bypath(index, "this-is-a-rename.txt"));
+
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(
+ git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(
+ git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
+
+ findopts.flags |= GIT_DIFF_FIND_AND_BREAK_REWRITES |
+ GIT_DIFF_FIND_REWRITES |
+ GIT_DIFF_FIND_RENAMES_FROM_REWRITES;
+
+ cl_git_pass(git_diff_find_similar(diff, &findopts));
+
+ cl_git_pass(
+ git_diff_foreach(diff, test_names_expected, NULL, NULL, &expect));
+
+ cl_assert(expect.idx == expect.len);
+
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_index_free(index);
+}
+
+void test_diff_rename__case_changes_are_split(void)
+{
+ git_index *index;
+ git_tree *tree;
+ git_diff *diff = NULL;
+ diff_expects exp;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_pass(
+ git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(p_rename("renames/ikeepsix.txt", "renames/IKEEPSIX.txt"));
+
+ cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
+ cl_git_pass(git_index_add_bypath(index, "IKEEPSIX.txt"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, NULL));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(2, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
+
+ opts.flags = GIT_DIFF_FIND_ALL;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
+
+ git_diff_free(diff);
+ git_index_free(index);
+ git_tree_free(tree);
+}
+
+void test_diff_rename__unmodified_can_be_renamed(void)
+{
+ git_index *index;
+ git_tree *tree;
+ git_diff *diff = NULL;
+ diff_expects exp;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(
+ git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(p_rename("renames/ikeepsix.txt", "renames/ikeepsix2.txt"));
+
+ cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
+ cl_git_pass(git_index_add_bypath(index, "ikeepsix2.txt"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(2, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
+
+ opts.flags = GIT_DIFF_FIND_ALL;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
+
+ git_diff_free(diff);
+ git_index_free(index);
+ git_tree_free(tree);
+}
+
+void test_diff_rename__rewrite_on_single_file(void)
+{
+ git_index *index;
+ git_diff *diff = NULL;
+ diff_expects exp;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
+
+ diffopts.flags = GIT_DIFF_INCLUDE_UNTRACKED;
+
+ findopts.flags = GIT_DIFF_FIND_FOR_UNTRACKED |
+ GIT_DIFF_FIND_AND_BREAK_REWRITES |
+ GIT_DIFF_FIND_RENAMES_FROM_REWRITES;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_rewritefile("renames/ikeepsix.txt",
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n");
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, &diffopts));
+ cl_git_pass(git_diff_find_similar(diff, &findopts));
+
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(2, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ git_diff_free(diff);
+ git_index_free(index);
+}
diff --git a/tests/diff/submodules.c b/tests/diff/submodules.c
new file mode 100644
index 000000000..24545b2c7
--- /dev/null
+++ b/tests/diff/submodules.c
@@ -0,0 +1,423 @@
+#include "clar_libgit2.h"
+#include "repository.h"
+#include "posix.h"
+#include "../submodule/submodule_helpers.h"
+
+static git_repository *g_repo = NULL;
+
+void test_diff_submodules__initialize(void)
+{
+}
+
+void test_diff_submodules__cleanup(void)
+{
+}
+
+static void check_diff_patches_at_line(
+ git_diff *diff, const char **expected, const char *file, int line)
+{
+ const git_diff_delta *delta;
+ git_patch *patch = NULL;
+ size_t d, num_d = git_diff_num_deltas(diff);
+ char *patch_text;
+
+ for (d = 0; d < num_d; ++d, git_patch_free(patch)) {
+ cl_git_pass(git_patch_from_diff(&patch, diff, d));
+ cl_assert((delta = git_patch_get_delta(patch)) != NULL);
+
+ if (delta->status == GIT_DELTA_UNMODIFIED) {
+ cl_assert_at_line(expected[d] == NULL, file, line);
+ continue;
+ }
+
+ if (expected[d] && !strcmp(expected[d], "<SKIP>"))
+ continue;
+ if (expected[d] && !strcmp(expected[d], "<END>")) {
+ cl_git_pass(git_patch_to_str(&patch_text, patch));
+ cl_assert_at_line(!strcmp(expected[d], "<END>"), file, line);
+ }
+
+ cl_git_pass(git_patch_to_str(&patch_text, patch));
+
+ clar__assert_equal(
+ file, line, "expected diff did not match actual diff", 1,
+ "%s", expected[d], patch_text);
+ git__free(patch_text);
+ }
+
+ cl_assert_at_line(expected[d] && !strcmp(expected[d], "<END>"), file, line);
+}
+
+#define check_diff_patches(diff, exp) \
+ check_diff_patches_at_line(diff, exp, __FILE__, __LINE__)
+
+void test_diff_submodules__unmodified_submodule(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ static const char *expected[] = {
+ "<SKIP>", /* .gitmodules */
+ NULL, /* added */
+ NULL, /* ignored */
+ "diff --git a/modified b/modified\nindex 092bfb9..452216e 100644\n--- a/modified\n+++ b/modified\n@@ -1 +1,2 @@\n-yo\n+changed\n+\n", /* modified */
+ NULL, /* testrepo.git */
+ NULL, /* unmodified */
+ NULL, /* untracked */
+ "<END>"
+ };
+
+ g_repo = setup_fixture_submodules();
+
+ opts.flags = GIT_DIFF_INCLUDE_IGNORED |
+ GIT_DIFF_INCLUDE_UNTRACKED |
+ GIT_DIFF_INCLUDE_UNMODIFIED;
+ opts.old_prefix = "a"; opts.new_prefix = "b";
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected);
+ git_diff_free(diff);
+}
+
+void test_diff_submodules__dirty_submodule(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ static const char *expected[] = {
+ "<SKIP>", /* .gitmodules */
+ NULL, /* added */
+ NULL, /* ignored */
+ "diff --git a/modified b/modified\nindex 092bfb9..452216e 100644\n--- a/modified\n+++ b/modified\n@@ -1 +1,2 @@\n-yo\n+changed\n+\n", /* modified */
+ "diff --git a/testrepo b/testrepo\nindex a65fedf..a65fedf 160000\n--- a/testrepo\n+++ b/testrepo\n@@ -1 +1 @@\n-Subproject commit a65fedf39aefe402d3bb6e24df4d4f5fe4547750\n+Subproject commit a65fedf39aefe402d3bb6e24df4d4f5fe4547750-dirty\n", /* testrepo.git */
+ NULL, /* unmodified */
+ NULL, /* untracked */
+ "<END>"
+ };
+
+ g_repo = setup_fixture_submodules();
+
+ cl_git_rewritefile("submodules/testrepo/README", "heyheyhey");
+ cl_git_mkfile("submodules/testrepo/all_new.txt", "never seen before");
+
+ opts.flags = GIT_DIFF_INCLUDE_IGNORED |
+ GIT_DIFF_INCLUDE_UNTRACKED |
+ GIT_DIFF_INCLUDE_UNMODIFIED;
+ opts.old_prefix = "a"; opts.new_prefix = "b";
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected);
+ git_diff_free(diff);
+}
+
+void test_diff_submodules__dirty_submodule_2(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL, *diff2 = NULL;
+ char *smpath = "testrepo";
+ static const char *expected_none[] = { "<END>" };
+ static const char *expected_dirty[] = {
+ "diff --git a/testrepo b/testrepo\nindex a65fedf..a65fedf 160000\n--- a/testrepo\n+++ b/testrepo\n@@ -1 +1 @@\n-Subproject commit a65fedf39aefe402d3bb6e24df4d4f5fe4547750\n+Subproject commit a65fedf39aefe402d3bb6e24df4d4f5fe4547750-dirty\n", /* testrepo.git */
+ "<END>"
+ };
+
+ g_repo = setup_fixture_submodules();
+
+ cl_git_pass(git_submodule_reload_all(g_repo));
+
+ opts.flags = GIT_DIFF_INCLUDE_UNTRACKED |
+ GIT_DIFF_SHOW_UNTRACKED_CONTENT |
+ GIT_DIFF_RECURSE_UNTRACKED_DIRS |
+ GIT_DIFF_DISABLE_PATHSPEC_MATCH;
+ opts.old_prefix = "a"; opts.new_prefix = "b";
+ opts.pathspec.count = 1;
+ opts.pathspec.strings = &smpath;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_none);
+ git_diff_free(diff);
+
+ cl_git_rewritefile("submodules/testrepo/README", "heyheyhey");
+ cl_git_mkfile("submodules/testrepo/all_new.txt", "never seen before");
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_dirty);
+
+ {
+ git_tree *head;
+
+ cl_git_pass(git_repository_head_tree(&head, g_repo));
+ cl_git_pass(git_diff_tree_to_index(&diff2, g_repo, head, NULL, &opts));
+ cl_git_pass(git_diff_merge(diff, diff2));
+ git_diff_free(diff2);
+ git_tree_free(head);
+
+ check_diff_patches(diff, expected_dirty);
+ }
+
+ git_diff_free(diff);
+
+ cl_git_pass(git_submodule_reload_all(g_repo));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_dirty);
+ git_diff_free(diff);
+}
+
+void test_diff_submodules__submod2_index_to_wd(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ static const char *expected[] = {
+ "<SKIP>", /* .gitmodules */
+ "diff --git a/sm_changed_file b/sm_changed_file\nindex 4800958..4800958 160000\n--- a/sm_changed_file\n+++ b/sm_changed_file\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_file */
+ "diff --git a/sm_changed_head b/sm_changed_head\nindex 4800958..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n", /* sm_changed_head */
+ "diff --git a/sm_changed_index b/sm_changed_index\nindex 4800958..4800958 160000\n--- a/sm_changed_index\n+++ b/sm_changed_index\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_index */
+ "diff --git a/sm_changed_untracked_file b/sm_changed_untracked_file\nindex 4800958..4800958 160000\n--- a/sm_changed_untracked_file\n+++ b/sm_changed_untracked_file\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_untracked_file */
+ "diff --git a/sm_missing_commits b/sm_missing_commits\nindex 4800958..5e49635 160000\n--- a/sm_missing_commits\n+++ b/sm_missing_commits\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 5e4963595a9774b90524d35a807169049de8ccad\n", /* sm_missing_commits */
+ "<END>"
+ };
+
+ g_repo = setup_fixture_submod2();
+
+ opts.flags = GIT_DIFF_INCLUDE_UNTRACKED;
+ opts.old_prefix = "a"; opts.new_prefix = "b";
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected);
+ git_diff_free(diff);
+}
+
+void test_diff_submodules__submod2_head_to_index(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_tree *head;
+ git_diff *diff = NULL;
+ static const char *expected[] = {
+ "<SKIP>", /* .gitmodules */
+ "diff --git a/sm_added_and_uncommited b/sm_added_and_uncommited\nnew file mode 160000\nindex 0000000..4800958\n--- /dev/null\n+++ b/sm_added_and_uncommited\n@@ -0,0 +1 @@\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n", /* sm_added_and_uncommited */
+ "<END>"
+ };
+
+ g_repo = setup_fixture_submod2();
+
+ cl_git_pass(git_repository_head_tree(&head, g_repo));
+
+ opts.flags = GIT_DIFF_INCLUDE_UNTRACKED;
+ opts.old_prefix = "a"; opts.new_prefix = "b";
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, head, NULL, &opts));
+ check_diff_patches(diff, expected);
+ git_diff_free(diff);
+
+ git_tree_free(head);
+}
+
+void test_diff_submodules__invalid_cache(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ git_submodule *sm;
+ char *smpath = "sm_changed_head";
+ git_repository *smrepo;
+ git_index *smindex;
+ static const char *expected_baseline[] = {
+ "diff --git a/sm_changed_head b/sm_changed_head\nindex 4800958..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n", /* sm_changed_head */
+ "<END>"
+ };
+ static const char *expected_unchanged[] = { "<END>" };
+ static const char *expected_dirty[] = {
+ "diff --git a/sm_changed_head b/sm_changed_head\nindex 3d9386c..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247-dirty\n",
+ "<END>"
+ };
+ static const char *expected_moved[] = {
+ "diff --git a/sm_changed_head b/sm_changed_head\nindex 3d9386c..7002348 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n+Subproject commit 700234833f6ccc20d744b238612646be071acaae\n",
+ "<END>"
+ };
+ static const char *expected_moved_dirty[] = {
+ "diff --git a/sm_changed_head b/sm_changed_head\nindex 3d9386c..7002348 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n+Subproject commit 700234833f6ccc20d744b238612646be071acaae-dirty\n",
+ "<END>"
+ };
+
+ g_repo = setup_fixture_submod2();
+
+ opts.flags = GIT_DIFF_INCLUDE_UNTRACKED;
+ opts.old_prefix = "a"; opts.new_prefix = "b";
+ opts.pathspec.count = 1;
+ opts.pathspec.strings = &smpath;
+
+ /* baseline */
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_baseline);
+ git_diff_free(diff);
+
+ /* update index with new HEAD */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, smpath));
+ cl_git_pass(git_submodule_add_to_index(sm, 1));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_unchanged);
+ git_diff_free(diff);
+
+ /* create untracked file in submodule working directory */
+ cl_git_mkfile("submod2/sm_changed_head/new_around_here", "hello");
+ git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_NONE);
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_dirty);
+ git_diff_free(diff);
+
+ git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_UNTRACKED);
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_unchanged);
+ git_diff_free(diff);
+
+ /* modify tracked file in submodule working directory */
+ cl_git_append2file(
+ "submod2/sm_changed_head/file_to_modify", "\nmore stuff\n");
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_dirty);
+ git_diff_free(diff);
+
+ cl_git_pass(git_submodule_reload_all(g_repo));
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, smpath));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_dirty);
+ git_diff_free(diff);
+
+ git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_DIRTY);
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_unchanged);
+ git_diff_free(diff);
+
+ /* add file to index in submodule */
+ cl_git_pass(git_submodule_open(&smrepo, sm));
+ cl_git_pass(git_repository_index(&smindex, smrepo));
+ cl_git_pass(git_index_add_bypath(smindex, "file_to_modify"));
+
+ git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_UNTRACKED);
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_dirty);
+ git_diff_free(diff);
+
+ git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_DIRTY);
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_unchanged);
+ git_diff_free(diff);
+
+ /* commit changed index of submodule */
+ cl_repo_commit_from_index(NULL, smrepo, NULL, 1372350000, "Move it");
+
+ git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_DIRTY);
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_moved);
+ git_diff_free(diff);
+
+ git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_ALL);
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_unchanged);
+ git_diff_free(diff);
+
+ git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_NONE);
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_moved_dirty);
+ git_diff_free(diff);
+
+ p_unlink("submod2/sm_changed_head/new_around_here");
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_moved);
+ git_diff_free(diff);
+
+ git_index_free(smindex);
+ git_repository_free(smrepo);
+}
+
+void test_diff_submodules__diff_ignore_options(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ git_config *cfg;
+ static const char *expected_normal[] = {
+ "<SKIP>", /* .gitmodules */
+ "diff --git a/sm_changed_file b/sm_changed_file\nindex 4800958..4800958 160000\n--- a/sm_changed_file\n+++ b/sm_changed_file\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_file */
+ "diff --git a/sm_changed_head b/sm_changed_head\nindex 4800958..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n", /* sm_changed_head */
+ "diff --git a/sm_changed_index b/sm_changed_index\nindex 4800958..4800958 160000\n--- a/sm_changed_index\n+++ b/sm_changed_index\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_index */
+ "diff --git a/sm_changed_untracked_file b/sm_changed_untracked_file\nindex 4800958..4800958 160000\n--- a/sm_changed_untracked_file\n+++ b/sm_changed_untracked_file\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_untracked_file */
+ "diff --git a/sm_missing_commits b/sm_missing_commits\nindex 4800958..5e49635 160000\n--- a/sm_missing_commits\n+++ b/sm_missing_commits\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 5e4963595a9774b90524d35a807169049de8ccad\n", /* sm_missing_commits */
+ "<END>"
+ };
+ static const char *expected_ignore_all[] = {
+ "<SKIP>", /* .gitmodules */
+ "<END>"
+ };
+ static const char *expected_ignore_dirty[] = {
+ "<SKIP>", /* .gitmodules */
+ "diff --git a/sm_changed_head b/sm_changed_head\nindex 4800958..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n", /* sm_changed_head */
+ "diff --git a/sm_missing_commits b/sm_missing_commits\nindex 4800958..5e49635 160000\n--- a/sm_missing_commits\n+++ b/sm_missing_commits\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 5e4963595a9774b90524d35a807169049de8ccad\n", /* sm_missing_commits */
+ "<END>"
+ };
+
+ g_repo = setup_fixture_submod2();
+
+ opts.flags = GIT_DIFF_INCLUDE_UNTRACKED;
+ opts.old_prefix = "a"; opts.new_prefix = "b";
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_normal);
+ git_diff_free(diff);
+
+ opts.flags |= GIT_DIFF_IGNORE_SUBMODULES;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_ignore_all);
+ git_diff_free(diff);
+
+ opts.flags &= ~GIT_DIFF_IGNORE_SUBMODULES;
+ opts.ignore_submodules = GIT_SUBMODULE_IGNORE_ALL;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_ignore_all);
+ git_diff_free(diff);
+
+ opts.ignore_submodules = GIT_SUBMODULE_IGNORE_DIRTY;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_ignore_dirty);
+ git_diff_free(diff);
+
+ opts.ignore_submodules = 0;
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(git_config_set_bool(cfg, "diff.ignoreSubmodules", false));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_normal);
+ git_diff_free(diff);
+
+ cl_git_pass(git_config_set_bool(cfg, "diff.ignoreSubmodules", true));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_ignore_all);
+ git_diff_free(diff);
+
+ cl_git_pass(git_config_set_string(cfg, "diff.ignoreSubmodules", "none"));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_normal);
+ git_diff_free(diff);
+
+ cl_git_pass(git_config_set_string(cfg, "diff.ignoreSubmodules", "dirty"));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_ignore_dirty);
+ git_diff_free(diff);
+
+ git_config_free(cfg);
+}
diff --git a/tests/diff/tree.c b/tests/diff/tree.c
new file mode 100644
index 000000000..582174b8b
--- /dev/null
+++ b/tests/diff/tree.c
@@ -0,0 +1,526 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+
+static git_repository *g_repo = NULL;
+static git_diff_options opts;
+static git_diff *diff;
+static git_tree *a, *b;
+static diff_expects expect;
+
+void test_diff_tree__initialize(void)
+{
+ cl_git_pass(git_diff_options_init(&opts, GIT_DIFF_OPTIONS_VERSION));
+
+ memset(&expect, 0, sizeof(expect));
+
+ diff = NULL;
+ a = NULL;
+ b = NULL;
+}
+
+void test_diff_tree__cleanup(void)
+{
+ git_diff_free(diff);
+ git_tree_free(a);
+ git_tree_free(b);
+
+ cl_git_sandbox_cleanup();
+
+}
+
+void test_diff_tree__0(void)
+{
+ /* grabbed a couple of commit oids from the history of the attr repo */
+ const char *a_commit = "605812a";
+ const char *b_commit = "370fe9ec22";
+ const char *c_commit = "f5b0af1fb4f5c";
+ git_tree *c;
+
+ g_repo = cl_git_sandbox_init("attr");
+
+ cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
+ cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
+ cl_assert((c = resolve_commit_oid_to_tree(g_repo, c_commit)) != NULL);
+
+ opts.context_lines = 1;
+ opts.interhunk_lines = 1;
+
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &expect));
+
+ cl_assert_equal_i(5, expect.files);
+ cl_assert_equal_i(2, expect.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, expect.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(2, expect.file_status[GIT_DELTA_MODIFIED]);
+
+ cl_assert_equal_i(5, expect.hunks);
+
+ cl_assert_equal_i(7 + 24 + 1 + 6 + 6, expect.lines);
+ cl_assert_equal_i(1, expect.line_ctxt);
+ cl_assert_equal_i(24 + 1 + 5 + 5, expect.line_adds);
+ cl_assert_equal_i(7 + 1, expect.line_dels);
+
+ git_diff_free(diff);
+ diff = NULL;
+
+ memset(&expect, 0, sizeof(expect));
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, c, b, &opts));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &expect));
+
+ cl_assert_equal_i(2, expect.files);
+ cl_assert_equal_i(0, expect.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, expect.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(2, expect.file_status[GIT_DELTA_MODIFIED]);
+
+ cl_assert_equal_i(2, expect.hunks);
+
+ cl_assert_equal_i(8 + 15, expect.lines);
+ cl_assert_equal_i(1, expect.line_ctxt);
+ cl_assert_equal_i(1, expect.line_adds);
+ cl_assert_equal_i(7 + 14, expect.line_dels);
+
+ git_tree_free(c);
+}
+
+#define DIFF_OPTS(FLAGS, CTXT) \
+ {GIT_DIFF_OPTIONS_VERSION, (FLAGS), GIT_SUBMODULE_IGNORE_DEFAULT, \
+ {NULL,0}, NULL, NULL, (CTXT), 1}
+
+void test_diff_tree__options(void)
+{
+ /* grabbed a couple of commit oids from the history of the attr repo */
+ const char *a_commit = "6bab5c79cd5140d0";
+ const char *b_commit = "605812ab7fe421fdd";
+ const char *c_commit = "f5b0af1fb4f5";
+ const char *d_commit = "a97cc019851";
+ git_tree *c, *d;
+ diff_expects actual;
+ int test_ab_or_cd[] = { 0, 0, 0, 0, 1, 1, 1, 1, 1 };
+ git_diff_options test_options[] = {
+ /* a vs b tests */
+ DIFF_OPTS(GIT_DIFF_NORMAL, 1),
+ DIFF_OPTS(GIT_DIFF_NORMAL, 3),
+ DIFF_OPTS(GIT_DIFF_REVERSE, 2),
+ DIFF_OPTS(GIT_DIFF_FORCE_TEXT, 2),
+ /* c vs d tests */
+ DIFF_OPTS(GIT_DIFF_NORMAL, 3),
+ DIFF_OPTS(GIT_DIFF_IGNORE_WHITESPACE, 3),
+ DIFF_OPTS(GIT_DIFF_IGNORE_WHITESPACE_CHANGE, 3),
+ DIFF_OPTS(GIT_DIFF_IGNORE_WHITESPACE_EOL, 3),
+ DIFF_OPTS(GIT_DIFF_IGNORE_WHITESPACE | GIT_DIFF_REVERSE, 1),
+ };
+
+ /* to generate these values:
+ * - cd to tests/resources/attr,
+ * - mv .gitted .git
+ * - git diff [options] 6bab5c79cd5140d0 605812ab7fe421fdd
+ * - mv .git .gitted
+ */
+#define EXPECT_STATUS_ADM(ADDS,DELS,MODS) { 0, ADDS, DELS, MODS, 0, 0, 0, 0, 0 }
+
+ diff_expects test_expects[] = {
+ /* a vs b tests */
+ { 5, 0, EXPECT_STATUS_ADM(3, 0, 2), 4, 0, 0, 51, 2, 46, 3 },
+ { 5, 0, EXPECT_STATUS_ADM(3, 0, 2), 4, 0, 0, 53, 4, 46, 3 },
+ { 5, 0, EXPECT_STATUS_ADM(0, 3, 2), 4, 0, 0, 52, 3, 3, 46 },
+ { 5, 0, EXPECT_STATUS_ADM(3, 0, 2), 5, 0, 0, 54, 3, 47, 4 },
+ /* c vs d tests */
+ { 1, 0, EXPECT_STATUS_ADM(0, 0, 1), 1, 0, 0, 22, 9, 10, 3 },
+ { 1, 0, EXPECT_STATUS_ADM(0, 0, 1), 1, 0, 0, 19, 12, 7, 0 },
+ { 1, 0, EXPECT_STATUS_ADM(0, 0, 1), 1, 0, 0, 20, 11, 8, 1 },
+ { 1, 0, EXPECT_STATUS_ADM(0, 0, 1), 1, 0, 0, 20, 11, 8, 1 },
+ { 1, 0, EXPECT_STATUS_ADM(0, 0, 1), 1, 0, 0, 18, 11, 0, 7 },
+ { 0 },
+ };
+ diff_expects *expected;
+ int i, j;
+
+ g_repo = cl_git_sandbox_init("attr");
+
+ cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
+ cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
+ cl_assert((c = resolve_commit_oid_to_tree(g_repo, c_commit)) != NULL);
+ cl_assert((d = resolve_commit_oid_to_tree(g_repo, d_commit)) != NULL);
+
+ for (i = 0; test_expects[i].files > 0; i++) {
+ memset(&actual, 0, sizeof(actual)); /* clear accumulator */
+ opts = test_options[i];
+
+ if (test_ab_or_cd[i] == 0)
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
+ else
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, c, d, &opts));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &actual));
+
+ expected = &test_expects[i];
+ cl_assert_equal_i(actual.files, expected->files);
+ for (j = GIT_DELTA_UNMODIFIED; j <= GIT_DELTA_TYPECHANGE; ++j)
+ cl_assert_equal_i(expected->file_status[j], actual.file_status[j]);
+ cl_assert_equal_i(actual.hunks, expected->hunks);
+ cl_assert_equal_i(actual.lines, expected->lines);
+ cl_assert_equal_i(actual.line_ctxt, expected->line_ctxt);
+ cl_assert_equal_i(actual.line_adds, expected->line_adds);
+ cl_assert_equal_i(actual.line_dels, expected->line_dels);
+
+ git_diff_free(diff);
+ diff = NULL;
+ }
+
+ git_tree_free(c);
+ git_tree_free(d);
+}
+
+void test_diff_tree__bare(void)
+{
+ const char *a_commit = "8496071c1b46c85";
+ const char *b_commit = "be3563ae3f79";
+
+ g_repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
+ cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
+
+ opts.context_lines = 1;
+ opts.interhunk_lines = 1;
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &expect));
+
+ cl_assert_equal_i(3, expect.files);
+ cl_assert_equal_i(2, expect.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, expect.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, expect.file_status[GIT_DELTA_MODIFIED]);
+
+ cl_assert_equal_i(3, expect.hunks);
+
+ cl_assert_equal_i(4, expect.lines);
+ cl_assert_equal_i(0, expect.line_ctxt);
+ cl_assert_equal_i(3, expect.line_adds);
+ cl_assert_equal_i(1, expect.line_dels);
+}
+
+void test_diff_tree__merge(void)
+{
+ /* grabbed a couple of commit oids from the history of the attr repo */
+ const char *a_commit = "605812a";
+ const char *b_commit = "370fe9ec22";
+ const char *c_commit = "f5b0af1fb4f5c";
+ git_tree *c;
+ git_diff *diff1 = NULL, *diff2 = NULL;
+
+ g_repo = cl_git_sandbox_init("attr");
+
+ cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
+ cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
+ cl_assert((c = resolve_commit_oid_to_tree(g_repo, c_commit)) != NULL);
+
+ cl_git_pass(git_diff_tree_to_tree(&diff1, g_repo, a, b, NULL));
+
+ cl_git_pass(git_diff_tree_to_tree(&diff2, g_repo, c, b, NULL));
+
+ git_tree_free(c);
+
+ cl_git_pass(git_diff_merge(diff1, diff2));
+
+ git_diff_free(diff2);
+
+ cl_git_pass(git_diff_foreach(
+ diff1, diff_file_cb, diff_hunk_cb, diff_line_cb, &expect));
+
+ cl_assert_equal_i(6, expect.files);
+ cl_assert_equal_i(2, expect.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, expect.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(3, expect.file_status[GIT_DELTA_MODIFIED]);
+
+ cl_assert_equal_i(6, expect.hunks);
+
+ cl_assert_equal_i(59, expect.lines);
+ cl_assert_equal_i(1, expect.line_ctxt);
+ cl_assert_equal_i(36, expect.line_adds);
+ cl_assert_equal_i(22, expect.line_dels);
+
+ git_diff_free(diff1);
+}
+
+void test_diff_tree__larger_hunks(void)
+{
+ const char *a_commit = "d70d245ed97ed2aa596dd1af6536e4bfdb047b69";
+ const char *b_commit = "7a9e0b02e63179929fed24f0a3e0f19168114d10";
+ size_t d, num_d, h, num_h, l, num_l;
+ git_patch *patch;
+ const git_diff_hunk *hunk;
+ const git_diff_line *line;
+
+ g_repo = cl_git_sandbox_init("diff");
+
+ cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
+ cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
+
+ opts.context_lines = 1;
+ opts.interhunk_lines = 0;
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
+
+ num_d = git_diff_num_deltas(diff);
+ for (d = 0; d < num_d; ++d) {
+ cl_git_pass(git_patch_from_diff(&patch, diff, d));
+ cl_assert(patch);
+
+ num_h = git_patch_num_hunks(patch);
+ for (h = 0; h < num_h; h++) {
+ cl_git_pass(git_patch_get_hunk(&hunk, &num_l, patch, h));
+
+ for (l = 0; l < num_l; ++l) {
+ cl_git_pass(git_patch_get_line_in_hunk(&line, patch, h, l));
+ cl_assert(line);
+ }
+
+ cl_git_fail(git_patch_get_line_in_hunk(&line, patch, h, num_l));
+ }
+
+ cl_git_fail(git_patch_get_hunk(&hunk, &num_l, patch, num_h));
+
+ git_patch_free(patch);
+ }
+
+ cl_git_fail(git_patch_from_diff(&patch, diff, num_d));
+
+ cl_assert_equal_i(2, (int)num_d);
+}
+
+void test_diff_tree__checks_options_version(void)
+{
+ const char *a_commit = "8496071c1b46c85";
+ const char *b_commit = "be3563ae3f79";
+ const git_error *err;
+
+ g_repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
+ cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
+
+ opts.version = 0;
+ cl_git_fail(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
+ err = giterr_last();
+ cl_assert_equal_i(GITERR_INVALID, err->klass);
+
+ giterr_clear();
+ opts.version = 1024;
+ cl_git_fail(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
+ err = giterr_last();
+}
+
+void process_tree_to_tree_diffing(
+ const char *old_commit,
+ const char *new_commit)
+{
+ g_repo = cl_git_sandbox_init("unsymlinked.git");
+
+ cl_assert((a = resolve_commit_oid_to_tree(g_repo, old_commit)) != NULL);
+ cl_assert((b = resolve_commit_oid_to_tree(g_repo, new_commit)) != NULL);
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, NULL, NULL, &expect));
+}
+
+void test_diff_tree__symlink_blob_mode_changed_to_regular_file(void)
+{
+ /*
+ * $ git diff 7fccd7..806999
+ * diff --git a/include/Nu/Nu.h b/include/Nu/Nu.h
+ * deleted file mode 120000
+ * index 19bf568..0000000
+ * --- a/include/Nu/Nu.h
+ * +++ /dev/null
+ * @@ -1 +0,0 @@
+ * -../../objc/Nu.h
+ * \ No newline at end of file
+ * diff --git a/include/Nu/Nu.h b/include/Nu/Nu.h
+ * new file mode 100644
+ * index 0000000..f9e6561
+ * --- /dev/null
+ * +++ b/include/Nu/Nu.h
+ * @@ -0,0 +1 @@
+ * +awesome content
+ * diff --git a/objc/Nu.h b/objc/Nu.h
+ * deleted file mode 100644
+ * index f9e6561..0000000
+ * --- a/objc/Nu.h
+ * +++ /dev/null
+ * @@ -1 +0,0 @@
+ * -awesome content
+ */
+
+ process_tree_to_tree_diffing("7fccd7", "806999");
+
+ cl_assert_equal_i(3, expect.files);
+ cl_assert_equal_i(2, expect.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(0, expect.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, expect.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, expect.file_status[GIT_DELTA_TYPECHANGE]);
+}
+
+void test_diff_tree__symlink_blob_mode_changed_to_regular_file_as_typechange(void)
+{
+ /*
+ * $ git diff 7fccd7..a8595c
+ * diff --git a/include/Nu/Nu.h b/include/Nu/Nu.h
+ * deleted file mode 120000
+ * index 19bf568..0000000
+ * --- a/include/Nu/Nu.h
+ * +++ /dev/null
+ * @@ -1 +0,0 @@
+ * -../../objc/Nu.h
+ * \ No newline at end of file
+ * diff --git a/include/Nu/Nu.h b/include/Nu/Nu.h
+ * new file mode 100755
+ * index 0000000..f9e6561
+ * --- /dev/null
+ * +++ b/include/Nu/Nu.h
+ * @@ -0,0 +1 @@
+ * +awesome content
+ * diff --git a/objc/Nu.h b/objc/Nu.h
+ * deleted file mode 100644
+ * index f9e6561..0000000
+ * --- a/objc/Nu.h
+ * +++ /dev/null
+ * @@ -1 +0,0 @@
+ * -awesome content
+ */
+
+ opts.flags = GIT_DIFF_INCLUDE_TYPECHANGE;
+ process_tree_to_tree_diffing("7fccd7", "a8595c");
+
+ cl_assert_equal_i(2, expect.files);
+ cl_assert_equal_i(1, expect.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(0, expect.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, expect.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, expect.file_status[GIT_DELTA_TYPECHANGE]);
+}
+
+void test_diff_tree__regular_blob_mode_changed_to_executable_file(void)
+{
+ /*
+ * $ git diff 806999..a8595c
+ * diff --git a/include/Nu/Nu.h b/include/Nu/Nu.h
+ * old mode 100644
+ * new mode 100755
+ */
+
+ process_tree_to_tree_diffing("806999", "a8595c");
+
+ cl_assert_equal_i(1, expect.files);
+ cl_assert_equal_i(0, expect.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, expect.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, expect.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, expect.file_status[GIT_DELTA_TYPECHANGE]);
+}
+
+void test_diff_tree__issue_1397(void)
+{
+ /* this test shows that it is not needed */
+
+ g_repo = cl_git_sandbox_init("issue_1397");
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ cl_assert((a = resolve_commit_oid_to_tree(g_repo, "8a7ef04")) != NULL);
+ cl_assert((b = resolve_commit_oid_to_tree(g_repo, "7f483a7")) != NULL);
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &expect));
+
+ cl_assert_equal_i(1, expect.files);
+ cl_assert_equal_i(0, expect.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, expect.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, expect.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, expect.file_status[GIT_DELTA_TYPECHANGE]);
+}
+
+static void set_config_int(git_repository *repo, const char *name, int value)
+{
+ git_config *cfg;
+
+ cl_git_pass(git_repository_config(&cfg, repo));
+ cl_git_pass(git_config_set_int32(cfg, name, value));
+ git_config_free(cfg);
+}
+
+void test_diff_tree__diff_configs(void)
+{
+ const char *a_commit = "d70d245e";
+ const char *b_commit = "7a9e0b02";
+
+ g_repo = cl_git_sandbox_init("diff");
+
+ cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
+ cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, NULL));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &expect));
+
+ cl_assert_equal_i(2, expect.files);
+ cl_assert_equal_i(2, expect.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(6, expect.hunks);
+ cl_assert_equal_i(55, expect.lines);
+ cl_assert_equal_i(33, expect.line_ctxt);
+ cl_assert_equal_i(7, expect.line_adds);
+ cl_assert_equal_i(15, expect.line_dels);
+
+ git_diff_free(diff);
+ diff = NULL;
+
+ set_config_int(g_repo, "diff.context", 1);
+
+ memset(&expect, 0, sizeof(expect));
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, NULL));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &expect));
+
+ cl_assert_equal_i(2, expect.files);
+ cl_assert_equal_i(2, expect.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(7, expect.hunks);
+ cl_assert_equal_i(34, expect.lines);
+ cl_assert_equal_i(12, expect.line_ctxt);
+ cl_assert_equal_i(7, expect.line_adds);
+ cl_assert_equal_i(15, expect.line_dels);
+
+ git_diff_free(diff);
+ diff = NULL;
+
+ set_config_int(g_repo, "diff.context", 0);
+ set_config_int(g_repo, "diff.noprefix", 1);
+
+ memset(&expect, 0, sizeof(expect));
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, NULL));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &expect));
+
+ cl_assert_equal_i(2, expect.files);
+ cl_assert_equal_i(2, expect.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(7, expect.hunks);
+ cl_assert_equal_i(22, expect.lines);
+ cl_assert_equal_i(0, expect.line_ctxt);
+ cl_assert_equal_i(7, expect.line_adds);
+ cl_assert_equal_i(15, expect.line_dels);
+}
diff --git a/tests/diff/workdir.c b/tests/diff/workdir.c
new file mode 100644
index 000000000..7cc032232
--- /dev/null
+++ b/tests/diff/workdir.c
@@ -0,0 +1,1490 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+#include "repository.h"
+
+static git_repository *g_repo = NULL;
+
+void test_diff_workdir__initialize(void)
+{
+}
+
+void test_diff_workdir__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_diff_workdir__to_index(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ diff_expects exp;
+ int use_iterator;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ /* to generate these values:
+ * - cd to tests/resources/status,
+ * - mv .gitted .git
+ * - git diff --name-status
+ * - git diff
+ * - mv .git .gitted
+ */
+ cl_assert_equal_i(13, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ cl_assert_equal_i(8, exp.hunks);
+
+ cl_assert_equal_i(14, exp.lines);
+ cl_assert_equal_i(5, exp.line_ctxt);
+ cl_assert_equal_i(4, exp.line_adds);
+ cl_assert_equal_i(5, exp.line_dels);
+ }
+
+ git_diff_free(diff);
+}
+
+void test_diff_workdir__to_index_with_assume_unchanged(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ git_index *idx = NULL;
+ diff_expects exp;
+ const git_index_entry *iep;
+ git_index_entry ie;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ /* do initial diff */
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(8, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
+ git_diff_free(diff);
+
+ /* mark a couple of entries with ASSUME_UNCHANGED */
+
+ cl_git_pass(git_repository_index(&idx, g_repo));
+
+ cl_assert((iep = git_index_get_bypath(idx, "modified_file", 0)) != NULL);
+ memcpy(&ie, iep, sizeof(ie));
+ ie.flags |= GIT_IDXENTRY_VALID;
+ cl_git_pass(git_index_add(idx, &ie));
+
+ cl_assert((iep = git_index_get_bypath(idx, "file_deleted", 0)) != NULL);
+ memcpy(&ie, iep, sizeof(ie));
+ ie.flags |= GIT_IDXENTRY_VALID;
+ cl_git_pass(git_index_add(idx, &ie));
+
+ cl_git_pass(git_index_write(idx));
+ git_index_free(idx);
+
+ /* redo diff and see that entries are skipped */
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(6, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
+ git_diff_free(diff);
+
+}
+
+void test_diff_workdir__to_tree(void)
+{
+ /* grabbed a couple of commit oids from the history of the attr repo */
+ const char *a_commit = "26a125ee1bf"; /* the current HEAD */
+ const char *b_commit = "0017bd4ab1ec3"; /* the start */
+ git_tree *a, *b;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ git_diff *diff2 = NULL;
+ diff_expects exp;
+ int use_iterator;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ a = resolve_commit_oid_to_tree(g_repo, a_commit);
+ b = resolve_commit_oid_to_tree(g_repo, b_commit);
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+
+ /* You can't really generate the equivalent of git_diff_tree_to_workdir()
+ * using C git. It really wants to interpose the index into the diff.
+ *
+ * To validate the following results with command line git, I ran the
+ * following:
+ * - git ls-tree 26a125
+ * - find . ! -path ./.git/\* -a -type f | git hash-object --stdin-paths
+ * The results are documented at the bottom of this file in the
+ * long comment entitled "PREPARATION OF TEST DATA".
+ */
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(14, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(5, exp.file_status[GIT_DELTA_UNTRACKED]);
+ }
+
+ /* Since there is no git diff equivalent, let's just assume that the
+ * text diffs produced by git_diff_foreach are accurate here. We will
+ * do more apples-to-apples test comparison below.
+ */
+
+ git_diff_free(diff);
+ diff = NULL;
+ memset(&exp, 0, sizeof(exp));
+
+ /* This is a compatible emulation of "git diff <sha>" which looks like
+ * a workdir to tree diff (even though it is not really). This is what
+ * you would get from "git diff --name-status 26a125ee1bf"
+ */
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
+ cl_git_pass(git_diff_index_to_workdir(&diff2, g_repo, NULL, &opts));
+ cl_git_pass(git_diff_merge(diff, diff2));
+ git_diff_free(diff2);
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(15, exp.files);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(5, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ cl_assert_equal_i(11, exp.hunks);
+
+ cl_assert_equal_i(17, exp.lines);
+ cl_assert_equal_i(4, exp.line_ctxt);
+ cl_assert_equal_i(8, exp.line_adds);
+ cl_assert_equal_i(5, exp.line_dels);
+ }
+
+ git_diff_free(diff);
+ diff = NULL;
+ memset(&exp, 0, sizeof(exp));
+
+ /* Again, emulating "git diff <sha>" for testing purposes using
+ * "git diff --name-status 0017bd4ab1ec3" instead.
+ */
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, b, NULL, &opts));
+ cl_git_pass(git_diff_index_to_workdir(&diff2, g_repo, NULL, &opts));
+ cl_git_pass(git_diff_merge(diff, diff2));
+ git_diff_free(diff2);
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(16, exp.files);
+ cl_assert_equal_i(5, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ cl_assert_equal_i(12, exp.hunks);
+
+ cl_assert_equal_i(19, exp.lines);
+ cl_assert_equal_i(3, exp.line_ctxt);
+ cl_assert_equal_i(12, exp.line_adds);
+ cl_assert_equal_i(4, exp.line_dels);
+ }
+
+ git_diff_free(diff);
+
+ /* Let's try that once more with a reversed diff */
+
+ opts.flags |= GIT_DIFF_REVERSE;
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, b, NULL, &opts));
+ cl_git_pass(git_diff_index_to_workdir(&diff2, g_repo, NULL, &opts));
+ cl_git_pass(git_diff_merge(diff, diff2));
+ git_diff_free(diff2);
+
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(16, exp.files);
+ cl_assert_equal_i(5, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ cl_assert_equal_i(12, exp.hunks);
+
+ cl_assert_equal_i(19, exp.lines);
+ cl_assert_equal_i(3, exp.line_ctxt);
+ cl_assert_equal_i(12, exp.line_dels);
+ cl_assert_equal_i(4, exp.line_adds);
+
+ git_diff_free(diff);
+
+ /* all done now */
+
+ git_tree_free(a);
+ git_tree_free(b);
+}
+
+void test_diff_workdir__to_index_with_pathspec(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ diff_expects exp;
+ char *pathspec = NULL;
+ int use_iterator;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+ opts.pathspec.strings = &pathspec;
+ opts.pathspec.count = 1;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, NULL, NULL, &exp));
+ else
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
+
+ cl_assert_equal_i(13, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNTRACKED]);
+ }
+
+ git_diff_free(diff);
+
+ pathspec = "modified_file";
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, NULL, NULL, &exp));
+ else
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
+
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_UNTRACKED]);
+ }
+
+ git_diff_free(diff);
+
+ pathspec = "subdir";
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, NULL, NULL, &exp));
+ else
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
+
+ cl_assert_equal_i(3, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
+ }
+
+ git_diff_free(diff);
+
+ pathspec = "*_deleted";
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, NULL, NULL, &exp));
+ else
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
+
+ cl_assert_equal_i(2, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_UNTRACKED]);
+ }
+
+ git_diff_free(diff);
+}
+
+void test_diff_workdir__filemode_changes(void)
+{
+ git_diff *diff = NULL;
+ diff_expects exp;
+ int use_iterator;
+
+ if (!cl_is_chmod_supported())
+ return;
+
+ g_repo = cl_git_sandbox_init("issue_592");
+
+ cl_repo_set_bool(g_repo, "core.filemode", true);
+
+ /* test once with no mods */
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(0, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.hunks);
+ }
+
+ git_diff_free(diff);
+
+ /* chmod file and test again */
+
+ cl_assert(cl_toggle_filemode("issue_592/a.txt"));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.hunks);
+ }
+
+ git_diff_free(diff);
+
+ cl_assert(cl_toggle_filemode("issue_592/a.txt"));
+}
+
+void test_diff_workdir__filemode_changes_with_filemode_false(void)
+{
+ git_diff *diff = NULL;
+ diff_expects exp;
+
+ if (!cl_is_chmod_supported())
+ return;
+
+ g_repo = cl_git_sandbox_init("issue_592");
+
+ cl_repo_set_bool(g_repo, "core.filemode", false);
+
+ /* test once with no mods */
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(0, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.hunks);
+
+ git_diff_free(diff);
+
+ /* chmod file and test again */
+
+ cl_assert(cl_toggle_filemode("issue_592/a.txt"));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(0, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.hunks);
+
+ git_diff_free(diff);
+
+ cl_assert(cl_toggle_filemode("issue_592/a.txt"));
+}
+
+void test_diff_workdir__head_index_and_workdir_all_differ(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff_i2t = NULL, *diff_w2i = NULL;
+ diff_expects exp;
+ char *pathspec = "staged_changes_modified_file";
+ git_tree *tree;
+ int use_iterator;
+
+ /* For this file,
+ * - head->index diff has 1 line of context, 1 line of diff
+ * - index->workdir diff has 2 lines of context, 1 line of diff
+ * but
+ * - head->workdir diff has 1 line of context, 2 lines of diff
+ * Let's make sure the right one is returned from each fn.
+ */
+
+ g_repo = cl_git_sandbox_init("status");
+
+ tree = resolve_commit_oid_to_tree(g_repo, "26a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f");
+
+ opts.pathspec.strings = &pathspec;
+ opts.pathspec.count = 1;
+
+ cl_git_pass(git_diff_tree_to_index(&diff_i2t, g_repo, tree, NULL, &opts));
+ cl_git_pass(git_diff_index_to_workdir(&diff_w2i, g_repo, NULL, &opts));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff_i2t, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff_i2t, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.hunks);
+ cl_assert_equal_i(2, exp.lines);
+ cl_assert_equal_i(1, exp.line_ctxt);
+ cl_assert_equal_i(1, exp.line_adds);
+ cl_assert_equal_i(0, exp.line_dels);
+ }
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff_w2i, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff_w2i, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.hunks);
+ cl_assert_equal_i(3, exp.lines);
+ cl_assert_equal_i(2, exp.line_ctxt);
+ cl_assert_equal_i(1, exp.line_adds);
+ cl_assert_equal_i(0, exp.line_dels);
+ }
+
+ cl_git_pass(git_diff_merge(diff_i2t, diff_w2i));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff_i2t, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff_i2t, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.hunks);
+ cl_assert_equal_i(3, exp.lines);
+ cl_assert_equal_i(1, exp.line_ctxt);
+ cl_assert_equal_i(2, exp.line_adds);
+ cl_assert_equal_i(0, exp.line_dels);
+ }
+
+ git_diff_free(diff_i2t);
+ git_diff_free(diff_w2i);
+
+ git_tree_free(tree);
+}
+
+void test_diff_workdir__eof_newline_changes(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ diff_expects exp;
+ char *pathspec = "current_file";
+ int use_iterator;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ opts.pathspec.strings = &pathspec;
+ opts.pathspec.count = 1;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(0, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.hunks);
+ cl_assert_equal_i(0, exp.lines);
+ cl_assert_equal_i(0, exp.line_ctxt);
+ cl_assert_equal_i(0, exp.line_adds);
+ cl_assert_equal_i(0, exp.line_dels);
+ }
+
+ git_diff_free(diff);
+
+ cl_git_append2file("status/current_file", "\n");
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.hunks);
+ cl_assert_equal_i(2, exp.lines);
+ cl_assert_equal_i(1, exp.line_ctxt);
+ cl_assert_equal_i(1, exp.line_adds);
+ cl_assert_equal_i(0, exp.line_dels);
+ }
+
+ git_diff_free(diff);
+
+ cl_git_rewritefile("status/current_file", "current_file");
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.hunks);
+ cl_assert_equal_i(3, exp.lines);
+ cl_assert_equal_i(0, exp.line_ctxt);
+ cl_assert_equal_i(1, exp.line_adds);
+ cl_assert_equal_i(2, exp.line_dels);
+ }
+
+ git_diff_free(diff);
+}
+
+/* PREPARATION OF TEST DATA
+ *
+ * Since there is no command line equivalent of git_diff_tree_to_workdir,
+ * it was a bit of a pain to confirm that I was getting the expected
+ * results in the first part of this tests. Here is what I ended up
+ * doing to set my expectation for the file counts and results:
+ *
+ * Running "git ls-tree 26a125" and "git ls-tree aa27a6" shows:
+ *
+ * A a0de7e0ac200c489c41c59dfa910154a70264e6e current_file
+ * B 5452d32f1dd538eb0405e8a83cc185f79e25e80f file_deleted
+ * C 452e4244b5d083ddf0460acf1ecc74db9dcfa11a modified_file
+ * D 32504b727382542f9f089e24fddac5e78533e96c staged_changes
+ * E 061d42a44cacde5726057b67558821d95db96f19 staged_changes_file_deleted
+ * F 70bd9443ada07063e7fbf0b3ff5c13f7494d89c2 staged_changes_modified_file
+ * G e9b9107f290627c04d097733a10055af941f6bca staged_delete_file_deleted
+ * H dabc8af9bd6e9f5bbe96a176f1a24baf3d1f8916 staged_delete_modified_file
+ * I 53ace0d1cc1145a5f4fe4f78a186a60263190733 subdir/current_file
+ * J 1888c805345ba265b0ee9449b8877b6064592058 subdir/deleted_file
+ * K a6191982709b746d5650e93c2acf34ef74e11504 subdir/modified_file
+ * L e8ee89e15bbe9b20137715232387b3de5b28972e subdir.txt
+ *
+ * --------
+ *
+ * find . ! -path ./.git/\* -a -type f | git hash-object --stdin-paths
+ *
+ * A a0de7e0ac200c489c41c59dfa910154a70264e6e current_file
+ * M 6a79f808a9c6bc9531ac726c184bbcd9351ccf11 ignored_file
+ * C 0a539630525aca2e7bc84975958f92f10a64c9b6 modified_file
+ * N d4fa8600b4f37d7516bef4816ae2c64dbf029e3a new_file
+ * D 55d316c9ba708999f1918e9677d01dfcae69c6b9 staged_changes
+ * F 011c3440d5c596e21d836aa6d7b10eb581f68c49 staged_changes_modified_file
+ * H dabc8af9bd6e9f5bbe96a176f1a24baf3d1f8916 staged_delete_modified_file
+ * O 529a16e8e762d4acb7b9636ff540a00831f9155a staged_new_file
+ * P 8b090c06d14ffa09c4e880088ebad33893f921d1 staged_new_file_modified_file
+ * I 53ace0d1cc1145a5f4fe4f78a186a60263190733 subdir/current_file
+ * K 57274b75eeb5f36fd55527806d567b2240a20c57 subdir/modified_file
+ * Q 80a86a6931b91bc01c2dbf5ca55bdd24ad1ef466 subdir/new_file
+ * L e8ee89e15bbe9b20137715232387b3de5b28972e subdir.txt
+ *
+ * --------
+ *
+ * A - current_file (UNMODIFIED) -> not in results
+ * B D file_deleted
+ * M I ignored_file (IGNORED)
+ * C M modified_file
+ * N U new_file (UNTRACKED)
+ * D M staged_changes
+ * E D staged_changes_file_deleted
+ * F M staged_changes_modified_file
+ * G D staged_delete_file_deleted
+ * H - staged_delete_modified_file (UNMODIFIED) -> not in results
+ * O U staged_new_file
+ * P U staged_new_file_modified_file
+ * I - subdir/current_file (UNMODIFIED) -> not in results
+ * J D subdir/deleted_file
+ * K M subdir/modified_file
+ * Q U subdir/new_file
+ * L - subdir.txt (UNMODIFIED) -> not in results
+ *
+ * Expect 13 files, 0 ADD, 4 DEL, 4 MOD, 1 IGN, 4 UNTR
+ */
+
+
+void test_diff_workdir__larger_hunks(void)
+{
+ const char *a_commit = "d70d245ed97ed2aa596dd1af6536e4bfdb047b69";
+ const char *b_commit = "7a9e0b02e63179929fed24f0a3e0f19168114d10";
+ git_tree *a, *b;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ size_t i, d, num_d, h, num_h, l, num_l;
+
+ g_repo = cl_git_sandbox_init("diff");
+
+ cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
+ cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
+
+ opts.context_lines = 1;
+ opts.interhunk_lines = 0;
+
+ for (i = 0; i <= 2; ++i) {
+ git_diff *diff = NULL;
+ git_patch *patch;
+ const git_diff_hunk *hunk;
+ const git_diff_line *line;
+
+ /* okay, this is a bit silly, but oh well */
+ switch (i) {
+ case 0:
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ break;
+ case 1:
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts));
+ break;
+ case 2:
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, b, &opts));
+ break;
+ }
+
+ num_d = git_diff_num_deltas(diff);
+ cl_assert_equal_i(2, (int)num_d);
+
+ for (d = 0; d < num_d; ++d) {
+ cl_git_pass(git_patch_from_diff(&patch, diff, d));
+ cl_assert(patch);
+
+ num_h = git_patch_num_hunks(patch);
+ for (h = 0; h < num_h; h++) {
+ cl_git_pass(git_patch_get_hunk(&hunk, &num_l, patch, h));
+
+ for (l = 0; l < num_l; ++l) {
+ cl_git_pass(
+ git_patch_get_line_in_hunk(&line, patch, h, l));
+ cl_assert(line);
+ }
+
+ /* confirm fail after the last item */
+ cl_git_fail(
+ git_patch_get_line_in_hunk(&line, patch, h, num_l));
+ }
+
+ /* confirm fail after the last item */
+ cl_git_fail(git_patch_get_hunk(&hunk, &num_l, patch, num_h));
+
+ git_patch_free(patch);
+ }
+
+ git_diff_free(diff);
+ }
+
+ git_tree_free(a);
+ git_tree_free(b);
+}
+
+/* Set up a test that exercises this code. The easiest test using existing
+ * test data is probably to create a sandbox of submod2 and then run a
+ * git_diff_tree_to_workdir against tree
+ * 873585b94bdeabccea991ea5e3ec1a277895b698. As for what you should actually
+ * test, you can start by just checking that the number of lines of diff
+ * content matches the actual output of git diff. That will at least
+ * demonstrate that the submodule content is being used to generate somewhat
+ * comparable outputs. It is a test that would fail without this code and
+ * will succeed with it.
+ */
+
+#include "../submodule/submodule_helpers.h"
+
+void test_diff_workdir__submodules(void)
+{
+ const char *a_commit = "873585b94bdeabccea991ea5e3ec1a277895b698";
+ git_tree *a;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ diff_expects exp;
+
+ g_repo = setup_fixture_submod2();
+
+ a = resolve_commit_oid_to_tree(g_repo, a_commit);
+
+ opts.flags =
+ GIT_DIFF_INCLUDE_UNTRACKED |
+ GIT_DIFF_INCLUDE_IGNORED |
+ GIT_DIFF_RECURSE_UNTRACKED_DIRS |
+ GIT_DIFF_SHOW_UNTRACKED_CONTENT;
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts));
+
+ /* diff_print(stderr, diff); */
+
+ /* essentially doing: git diff 873585b94bdeabccea991ea5e3ec1a277895b698 */
+
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ /* so "git diff 873585" returns:
+ * M .gitmodules
+ * A just_a_dir/contents
+ * A just_a_file
+ * A sm_added_and_uncommited
+ * A sm_changed_file
+ * A sm_changed_head
+ * A sm_changed_index
+ * A sm_changed_untracked_file
+ * M sm_missing_commits
+ * A sm_unchanged
+ * which is a little deceptive because of the difference between the
+ * "git diff <treeish>" results from "git_diff_tree_to_workdir". The
+ * only significant difference is that those Added items will show up
+ * as Untracked items in the pure libgit2 diff.
+ *
+ * Then add in the two extra ignored items "not" and "not-submodule"
+ * to get the 12 files reported here.
+ */
+
+ cl_assert_equal_i(12, exp.files);
+
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(8, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ /* the following numbers match "git diff 873585" exactly */
+
+ cl_assert_equal_i(9, exp.hunks);
+
+ cl_assert_equal_i(33, exp.lines);
+ cl_assert_equal_i(2, exp.line_ctxt);
+ cl_assert_equal_i(30, exp.line_adds);
+ cl_assert_equal_i(1, exp.line_dels);
+
+ git_diff_free(diff);
+ git_tree_free(a);
+}
+
+void test_diff_workdir__cannot_diff_against_a_bare_repository(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ git_tree *tree;
+
+ g_repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_assert_equal_i(
+ GIT_EBAREREPO, git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ cl_git_pass(git_repository_head_tree(&tree, g_repo));
+
+ cl_assert_equal_i(
+ GIT_EBAREREPO, git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
+
+ git_tree_free(tree);
+}
+
+void test_diff_workdir__to_null_tree(void)
+{
+ git_diff *diff;
+ diff_expects exp;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+
+ opts.flags = GIT_DIFF_INCLUDE_UNTRACKED |
+ GIT_DIFF_RECURSE_UNTRACKED_DIRS;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, NULL, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(exp.files, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ git_diff_free(diff);
+}
+
+void test_diff_workdir__checks_options_version(void)
+{
+ git_diff *diff;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ const git_error *err;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ opts.version = 0;
+ cl_git_fail(git_diff_tree_to_workdir(&diff, g_repo, NULL, &opts));
+ err = giterr_last();
+ cl_assert_equal_i(GITERR_INVALID, err->klass);
+
+ giterr_clear();
+ opts.version = 1024;
+ cl_git_fail(git_diff_tree_to_workdir(&diff, g_repo, NULL, &opts));
+ err = giterr_last();
+ cl_assert_equal_i(GITERR_INVALID, err->klass);
+}
+
+void test_diff_workdir__can_diff_empty_file(void)
+{
+ git_diff *diff;
+ git_tree *tree;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ struct stat st;
+ git_patch *patch;
+
+ g_repo = cl_git_sandbox_init("attr_index");
+
+ tree = resolve_commit_oid_to_tree(g_repo, "3812cfef3661"); /* HEAD */
+
+ /* baseline - make sure there are no outstanding diffs */
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
+ cl_assert_equal_i(2, (int)git_diff_num_deltas(diff));
+ git_diff_free(diff);
+
+ /* empty contents of file */
+
+ cl_git_rewritefile("attr_index/README.txt", "");
+ cl_git_pass(git_path_lstat("attr_index/README.txt", &st));
+ cl_assert_equal_i(0, (int)st.st_size);
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
+ cl_assert_equal_i(3, (int)git_diff_num_deltas(diff));
+ /* diffs are: .gitattributes, README.txt, sub/sub/.gitattributes */
+ cl_git_pass(git_patch_from_diff(&patch, diff, 1));
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+ /* remove a file altogether */
+
+ cl_git_pass(p_unlink("attr_index/README.txt"));
+ cl_assert(!git_path_exists("attr_index/README.txt"));
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
+ cl_assert_equal_i(3, (int)git_diff_num_deltas(diff));
+ cl_git_pass(git_patch_from_diff(&patch, diff, 1));
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+ git_tree_free(tree);
+}
+
+void test_diff_workdir__to_index_issue_1397(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ diff_expects exp;
+
+ g_repo = cl_git_sandbox_init("issue_1397");
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(0, exp.files);
+ cl_assert_equal_i(0, exp.hunks);
+ cl_assert_equal_i(0, exp.lines);
+
+ git_diff_free(diff);
+ diff = NULL;
+
+ cl_git_rewritefile("issue_1397/crlf_file.txt",
+ "first line\r\nsecond line modified\r\nboth with crlf");
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+
+ cl_assert_equal_i(1, exp.hunks);
+
+ cl_assert_equal_i(5, exp.lines);
+ cl_assert_equal_i(3, exp.line_ctxt);
+ cl_assert_equal_i(1, exp.line_adds);
+ cl_assert_equal_i(1, exp.line_dels);
+
+ git_diff_free(diff);
+}
+
+void test_diff_workdir__to_tree_issue_1397(void)
+{
+ const char *a_commit = "7f483a738"; /* the current HEAD */
+ git_tree *a;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ git_diff *diff2 = NULL;
+ diff_expects exp;
+
+ g_repo = cl_git_sandbox_init("issue_1397");
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ a = resolve_commit_oid_to_tree(g_repo, a_commit);
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(0, exp.files);
+ cl_assert_equal_i(0, exp.hunks);
+ cl_assert_equal_i(0, exp.lines);
+
+ git_diff_free(diff);
+ diff = NULL;
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
+ cl_git_pass(git_diff_index_to_workdir(&diff2, g_repo, NULL, &opts));
+ cl_git_pass(git_diff_merge(diff, diff2));
+ git_diff_free(diff2);
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(0, exp.files);
+ cl_assert_equal_i(0, exp.hunks);
+ cl_assert_equal_i(0, exp.lines);
+
+ git_diff_free(diff);
+ git_tree_free(a);
+}
+
+void test_diff_workdir__untracked_directory_scenarios(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ diff_expects exp;
+ char *pathspec = NULL;
+ static const char *files0[] = {
+ "subdir/deleted_file",
+ "subdir/modified_file",
+ "subdir/new_file",
+ NULL
+ };
+ static const char *files1[] = {
+ "subdir/deleted_file",
+ "subdir/directory/",
+ "subdir/modified_file",
+ "subdir/new_file",
+ NULL
+ };
+ static const char *files2[] = {
+ "subdir/deleted_file",
+ "subdir/directory/more/notignored",
+ "subdir/modified_file",
+ "subdir/new_file",
+ NULL
+ };
+
+ g_repo = cl_git_sandbox_init("status");
+ cl_git_mkfile("status/.gitignore", "ignored\n");
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+ opts.pathspec.strings = &pathspec;
+ opts.pathspec.count = 1;
+ pathspec = "subdir";
+
+ /* baseline for "subdir" pathspec */
+
+ memset(&exp, 0, sizeof(exp));
+ exp.names = files0;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
+
+ cl_assert_equal_i(3, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ git_diff_free(diff);
+
+ /* empty directory */
+
+ cl_git_pass(p_mkdir("status/subdir/directory", 0777));
+
+ memset(&exp, 0, sizeof(exp));
+ exp.names = files1;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
+
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ git_diff_free(diff);
+
+ /* empty directory in empty directory */
+
+ cl_git_pass(p_mkdir("status/subdir/directory/empty", 0777));
+
+ memset(&exp, 0, sizeof(exp));
+ exp.names = files1;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
+
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ git_diff_free(diff);
+
+ /* directory with only ignored files */
+
+ cl_git_pass(p_mkdir("status/subdir/directory/deeper", 0777));
+ cl_git_mkfile("status/subdir/directory/deeper/ignored", "ignore me\n");
+
+ cl_git_pass(p_mkdir("status/subdir/directory/another", 0777));
+ cl_git_mkfile("status/subdir/directory/another/ignored", "ignore me\n");
+
+ memset(&exp, 0, sizeof(exp));
+ exp.names = files1;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
+
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ git_diff_free(diff);
+
+ /* directory with ignored directory (contents irrelevant) */
+
+ cl_git_pass(p_mkdir("status/subdir/directory/more", 0777));
+ cl_git_pass(p_mkdir("status/subdir/directory/more/ignored", 0777));
+ cl_git_mkfile("status/subdir/directory/more/ignored/notignored",
+ "inside ignored dir\n");
+
+ memset(&exp, 0, sizeof(exp));
+ exp.names = files1;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
+
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ git_diff_free(diff);
+
+ /* quick version avoids directory scan */
+
+ opts.flags = opts.flags | GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS;
+
+ memset(&exp, 0, sizeof(exp));
+ exp.names = files1;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
+
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ git_diff_free(diff);
+
+ /* directory with nested non-ignored content */
+
+ opts.flags = opts.flags & ~GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS;
+
+ cl_git_mkfile("status/subdir/directory/more/notignored",
+ "not ignored deep under untracked\n");
+
+ memset(&exp, 0, sizeof(exp));
+ exp.names = files1;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
+
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ git_diff_free(diff);
+
+ /* use RECURSE_UNTRACKED_DIRS to get actual untracked files (no ignores) */
+
+ opts.flags = opts.flags & ~GIT_DIFF_INCLUDE_IGNORED;
+ opts.flags = opts.flags | GIT_DIFF_RECURSE_UNTRACKED_DIRS;
+
+ memset(&exp, 0, sizeof(exp));
+ exp.names = files2;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, &exp));
+
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ git_diff_free(diff);
+}
+
+
+void test_diff_workdir__untracked_directory_comes_last(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+
+ g_repo = cl_git_sandbox_init("renames");
+
+ cl_git_mkfile("renames/.gitignore", "*.ign\n");
+ cl_git_pass(p_mkdir("renames/zzz_untracked", 0777));
+ cl_git_mkfile("renames/zzz_untracked/an.ign", "ignore me please");
+ cl_git_mkfile("renames/zzz_untracked/skip.ign", "ignore me really");
+ cl_git_mkfile("renames/zzz_untracked/test.ign", "ignore me now");
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ cl_assert(diff != NULL);
+
+ git_diff_free(diff);
+}
+
+void test_diff_workdir__untracked_with_bom(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ const git_diff_delta *delta;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ cl_git_write2file("empty_standard_repo/bom.txt",
+ "\xFF\xFE\x31\x00\x32\x00\x33\x00\x34\x00", 10, O_WRONLY|O_CREAT, 0664);
+
+ opts.flags =
+ GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_SHOW_UNTRACKED_CONTENT;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ cl_assert_equal_i(1, git_diff_num_deltas(diff));
+ cl_assert((delta = git_diff_get_delta(diff, 0)) != NULL);
+ cl_assert_equal_i(GIT_DELTA_UNTRACKED, delta->status);
+
+ /* not known at this point
+ * cl_assert((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
+ */
+
+ git_diff_free(diff);
+}
+
+void test_diff_workdir__patience_diff(void)
+{
+ git_index *index;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ git_patch *patch = NULL;
+ char *as_str = NULL;
+ const char *expected_normal = "diff --git a/test.txt b/test.txt\nindex 34a5acc..d52725f 100644\n--- a/test.txt\n+++ b/test.txt\n@@ -1,10 +1,7 @@\n When I wrote this\n I did not know\n-how to create\n-a patience diff\n I did not know\n how to create\n+a patience diff\n another problem\n-I did not know\n-how to create\n a minimal diff\n";
+ const char *expected_patience = "diff --git a/test.txt b/test.txt\nindex 34a5acc..d52725f 100644\n--- a/test.txt\n+++ b/test.txt\n@@ -1,10 +1,7 @@\n When I wrote this\n I did not know\n+I did not know\n how to create\n a patience diff\n-I did not know\n-how to create\n another problem\n-I did not know\n-how to create\n a minimal diff\n";
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_mkfile(
+ "empty_standard_repo/test.txt",
+ "When I wrote this\nI did not know\nhow to create\na patience diff\nI did not know\nhow to create\nanother problem\nI did not know\nhow to create\na minimal diff\n");
+ cl_git_pass(git_index_add_bypath(index, "test.txt"));
+ cl_repo_commit_from_index(NULL, g_repo, NULL, 1372350000, "Base");
+ git_index_free(index);
+
+ cl_git_rewritefile(
+ "empty_standard_repo/test.txt",
+ "When I wrote this\nI did not know\nI did not know\nhow to create\na patience diff\nanother problem\na minimal diff\n");
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ cl_assert_equal_i(1, git_diff_num_deltas(diff));
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_str(&as_str, patch));
+
+ cl_assert_equal_s(expected_normal, as_str);
+ git__free(as_str);
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+ opts.flags |= GIT_DIFF_PATIENCE;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ cl_assert_equal_i(1, git_diff_num_deltas(diff));
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_str(&as_str, patch));
+
+ cl_assert_equal_s(expected_patience, as_str);
+ git__free(as_str);
+ git_patch_free(patch);
+ git_diff_free(diff);
+}
+
+void test_diff_workdir__with_stale_index(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ git_index *idx = NULL;
+ diff_expects exp;
+
+ g_repo = cl_git_sandbox_init("status");
+ cl_git_pass(git_repository_index(&idx, g_repo));
+
+ /* make the in-memory index invalid */
+ {
+ git_repository *r2;
+ git_index *idx2;
+ cl_git_pass(git_repository_open(&r2, "status"));
+ cl_git_pass(git_repository_index(&idx2, r2));
+ cl_git_pass(git_index_add_bypath(idx2, "new_file"));
+ cl_git_pass(git_index_add_bypath(idx2, "subdir/new_file"));
+ cl_git_pass(git_index_remove_bypath(idx2, "staged_new_file"));
+ cl_git_pass(git_index_remove_bypath(idx2, "staged_changes_file_deleted"));
+ cl_git_pass(git_index_write(idx2));
+ git_index_free(idx2);
+ git_repository_free(r2);
+ }
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_INCLUDE_UNMODIFIED;
+
+ /* first try with index pointer which should prevent reload */
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, idx, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(17, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNTRACKED]);
+ cl_assert_equal_i(5, exp.file_status[GIT_DELTA_UNMODIFIED]);
+
+ git_diff_free(diff);
+
+ /* now let's try without the index pointer which should trigger reload */
+
+ /* two files that were UNTRACKED should have become UNMODIFIED */
+ /* one file that was UNMODIFIED should now have become UNTRACKED */
+ /* one file that was DELETED should now be gone completely */
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ git_diff_free(diff);
+
+ cl_assert_equal_i(16, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
+ cl_assert_equal_i(6, exp.file_status[GIT_DELTA_UNMODIFIED]);
+
+ git_index_free(idx);
+}
diff --git a/tests-clar/fetchhead/fetchhead_data.h b/tests/fetchhead/fetchhead_data.h
index 294c9fb01..294c9fb01 100644
--- a/tests-clar/fetchhead/fetchhead_data.h
+++ b/tests/fetchhead/fetchhead_data.h
diff --git a/tests-clar/fetchhead/nonetwork.c b/tests/fetchhead/nonetwork.c
index a68ebb0b7..a68ebb0b7 100644
--- a/tests-clar/fetchhead/nonetwork.c
+++ b/tests/fetchhead/nonetwork.c
diff --git a/tests/filter/blob.c b/tests/filter/blob.c
new file mode 100644
index 000000000..9600a9779
--- /dev/null
+++ b/tests/filter/blob.c
@@ -0,0 +1,84 @@
+#include "clar_libgit2.h"
+#include "crlf.h"
+
+static git_repository *g_repo = NULL;
+
+void test_filter_blob__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("crlf");
+ cl_git_mkfile("crlf/.gitattributes",
+ "*.txt text\n*.bin binary\n"
+ "*.crlf text eol=crlf\n"
+ "*.lf text eol=lf\n"
+ "*.ident text ident\n"
+ "*.identcrlf ident text eol=crlf\n"
+ "*.identlf ident text eol=lf\n");
+}
+
+void test_filter_blob__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_filter_blob__all_crlf(void)
+{
+ git_blob *blob;
+ git_buf buf = { 0 };
+
+ cl_git_pass(git_revparse_single(
+ (git_object **)&blob, g_repo, "a9a2e891")); /* all-crlf */
+
+ cl_assert_equal_s(ALL_CRLF_TEXT_RAW, git_blob_rawcontent(blob));
+
+ cl_git_pass(git_blob_filtered_content(&buf, blob, "file.bin", 1));
+
+ cl_assert_equal_s(ALL_CRLF_TEXT_RAW, buf.ptr);
+
+ cl_git_pass(git_blob_filtered_content(&buf, blob, "file.crlf", 1));
+
+ /* in this case, raw content has crlf in it already */
+ cl_assert_equal_s(ALL_CRLF_TEXT_AS_CRLF, buf.ptr);
+
+ cl_git_pass(git_blob_filtered_content(&buf, blob, "file.lf", 1));
+
+ cl_assert_equal_s(ALL_CRLF_TEXT_AS_LF, buf.ptr);
+
+ git_buf_free(&buf);
+ git_blob_free(blob);
+}
+
+void test_filter_blob__ident(void)
+{
+ git_oid id;
+ git_blob *blob;
+ git_buf buf = { 0 };
+
+ cl_git_mkfile("crlf/test.ident", "Some text\n$Id$\nGoes there\n");
+ cl_git_pass(git_blob_create_fromworkdir(&id, g_repo, "test.ident"));
+ cl_git_pass(git_blob_lookup(&blob, g_repo, &id));
+ cl_assert_equal_s(
+ "Some text\n$Id$\nGoes there\n", git_blob_rawcontent(blob));
+ git_blob_free(blob);
+
+ cl_git_mkfile("crlf/test.ident", "Some text\n$Id: Any old just you want$\nGoes there\n");
+ cl_git_pass(git_blob_create_fromworkdir(&id, g_repo, "test.ident"));
+ cl_git_pass(git_blob_lookup(&blob, g_repo, &id));
+ cl_assert_equal_s(
+ "Some text\n$Id$\nGoes there\n", git_blob_rawcontent(blob));
+
+ cl_git_pass(git_blob_filtered_content(&buf, blob, "filter.bin", 1));
+ cl_assert_equal_s(
+ "Some text\n$Id$\nGoes there\n", buf.ptr);
+
+ cl_git_pass(git_blob_filtered_content(&buf, blob, "filter.identcrlf", 1));
+ cl_assert_equal_s(
+ "Some text\r\n$Id: 3164f585d548ac68027d22b104f2d8100b2b6845$\r\nGoes there\r\n", buf.ptr);
+
+ cl_git_pass(git_blob_filtered_content(&buf, blob, "filter.identlf", 1));
+ cl_assert_equal_s(
+ "Some text\n$Id: 3164f585d548ac68027d22b104f2d8100b2b6845$\nGoes there\n", buf.ptr);
+
+ git_buf_free(&buf);
+ git_blob_free(blob);
+
+}
diff --git a/tests/filter/crlf.c b/tests/filter/crlf.c
new file mode 100644
index 000000000..c9fb9cd7f
--- /dev/null
+++ b/tests/filter/crlf.c
@@ -0,0 +1,71 @@
+#include "clar_libgit2.h"
+#include "git2/sys/filter.h"
+
+static git_repository *g_repo = NULL;
+
+void test_filter_crlf__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("crlf");
+
+ cl_git_mkfile("crlf/.gitattributes",
+ "*.txt text\n*.bin binary\n*.crlf text eol=crlf\n*.lf text eol=lf\n");
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+}
+
+void test_filter_crlf__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_filter_crlf__to_worktree(void)
+{
+ git_filter_list *fl;
+ git_filter *crlf;
+ git_buf in = { 0 }, out = { 0 };
+
+ cl_git_pass(git_filter_list_new(&fl, g_repo, GIT_FILTER_TO_WORKTREE));
+
+ crlf = git_filter_lookup(GIT_FILTER_CRLF);
+ cl_assert(crlf != NULL);
+
+ cl_git_pass(git_filter_list_push(fl, crlf, NULL));
+
+ in.ptr = "Some text\nRight here\n";
+ in.size = strlen(in.ptr);
+
+ cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+
+#ifdef GIT_WIN32
+ cl_assert_equal_s("Some text\r\nRight here\r\n", out.ptr);
+#else
+ cl_assert_equal_s("Some text\nRight here\n", out.ptr);
+#endif
+
+ git_filter_list_free(fl);
+ git_buf_free(&out);
+}
+
+void test_filter_crlf__to_odb(void)
+{
+ git_filter_list *fl;
+ git_filter *crlf;
+ git_buf in = { 0 }, out = { 0 };
+
+ cl_git_pass(git_filter_list_new(&fl, g_repo, GIT_FILTER_TO_ODB));
+
+ crlf = git_filter_lookup(GIT_FILTER_CRLF);
+ cl_assert(crlf != NULL);
+
+ cl_git_pass(git_filter_list_push(fl, crlf, NULL));
+
+ in.ptr = "Some text\r\nRight here\r\n";
+ in.size = strlen(in.ptr);
+
+ cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+
+ cl_assert_equal_s("Some text\nRight here\n", out.ptr);
+
+ git_filter_list_free(fl);
+ git_buf_free(&out);
+}
diff --git a/tests/filter/crlf.h b/tests/filter/crlf.h
new file mode 100644
index 000000000..9cb98ad4c
--- /dev/null
+++ b/tests/filter/crlf.h
@@ -0,0 +1,25 @@
+#ifndef INCLUDE_filter_crlf_h__
+#define INCLUDE_filter_crlf_h__
+
+/*
+ * file content for files in the resources/crlf repository
+ */
+
+#define UTF8_BOM "\xEF\xBB\xBF"
+
+#define ALL_CRLF_TEXT_RAW "crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n"
+#define ALL_LF_TEXT_RAW "lf\nlf\nlf\nlf\nlf\n"
+#define MORE_CRLF_TEXT_RAW "crlf\r\ncrlf\r\nlf\ncrlf\r\ncrlf\r\n"
+#define MORE_LF_TEXT_RAW "lf\nlf\ncrlf\r\nlf\nlf\n"
+
+#define ALL_CRLF_TEXT_AS_CRLF ALL_CRLF_TEXT_RAW
+#define ALL_LF_TEXT_AS_CRLF "lf\r\nlf\r\nlf\r\nlf\r\nlf\r\n"
+#define MORE_CRLF_TEXT_AS_CRLF "crlf\r\ncrlf\r\nlf\r\ncrlf\r\ncrlf\r\n"
+#define MORE_LF_TEXT_AS_CRLF "lf\r\nlf\r\ncrlf\r\nlf\r\nlf\r\n"
+
+#define ALL_CRLF_TEXT_AS_LF "crlf\ncrlf\ncrlf\ncrlf\n"
+#define ALL_LF_TEXT_AS_LF ALL_LF_TEXT_RAW
+#define MORE_CRLF_TEXT_AS_LF "crlf\ncrlf\nlf\ncrlf\ncrlf\n"
+#define MORE_LF_TEXT_AS_LF "lf\nlf\ncrlf\nlf\nlf\n"
+
+#endif
diff --git a/tests/filter/custom.c b/tests/filter/custom.c
new file mode 100644
index 000000000..a81885c28
--- /dev/null
+++ b/tests/filter/custom.c
@@ -0,0 +1,337 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "blob.h"
+#include "filter.h"
+#include "buf_text.h"
+#include "git2/sys/filter.h"
+#include "git2/sys/repository.h"
+
+/* going TO_WORKDIR, filters are executed low to high
+ * going TO_ODB, filters are executed high to low
+ */
+#define BITFLIP_FILTER_PRIORITY -1
+#define REVERSE_FILTER_PRIORITY -2
+
+#define VERY_SECURE_ENCRYPTION(b) ((b) ^ 0xff)
+
+#ifdef GIT_WIN32
+# define NEWLINE "\r\n"
+#else
+# define NEWLINE "\n"
+#endif
+
+static char workdir_data[] =
+ "some simple" NEWLINE
+ "data" NEWLINE
+ "that will be" NEWLINE
+ "trivially" NEWLINE
+ "scrambled." NEWLINE;
+
+/* Represents the data above scrambled (bits flipped) after \r\n -> \n
+ * conversion, then bytewise reversed
+ */
+static unsigned char bitflipped_and_reversed_data[] =
+ { 0xf5, 0xd1, 0x9b, 0x9a, 0x93, 0x9d, 0x92, 0x9e, 0x8d, 0x9c, 0x8c,
+ 0xf5, 0x86, 0x93, 0x93, 0x9e, 0x96, 0x89, 0x96, 0x8d, 0x8b, 0xf5,
+ 0x9a, 0x9d, 0xdf, 0x93, 0x93, 0x96, 0x88, 0xdf, 0x8b, 0x9e, 0x97,
+ 0x8b, 0xf5, 0x9e, 0x8b, 0x9e, 0x9b, 0xf5, 0x9a, 0x93, 0x8f, 0x92,
+ 0x96, 0x8c, 0xdf, 0x9a, 0x92, 0x90, 0x8c };
+
+#define BITFLIPPED_AND_REVERSED_DATA_LEN 51
+
+static git_repository *g_repo = NULL;
+
+static void register_custom_filters(void);
+
+void test_filter_custom__initialize(void)
+{
+ register_custom_filters();
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_mkfile(
+ "empty_standard_repo/.gitattributes",
+ "hero* bitflip reverse\n"
+ "herofile text\n"
+ "heroflip -reverse binary\n"
+ "*.bin binary\n");
+}
+
+void test_filter_custom__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ g_repo = NULL;
+}
+
+static int bitflip_filter_apply(
+ git_filter *self,
+ void **payload,
+ git_buf *to,
+ const git_buf *from,
+ const git_filter_source *source)
+{
+ const unsigned char *src = (const unsigned char *)from->ptr;
+ unsigned char *dst;
+ size_t i;
+
+ GIT_UNUSED(self); GIT_UNUSED(payload);
+
+ /* verify that attribute path match worked as expected */
+ cl_assert_equal_i(
+ 0, git__strncmp("hero", git_filter_source_path(source), 4));
+
+ if (!from->size)
+ return 0;
+
+ cl_git_pass(git_buf_grow(to, from->size));
+
+ dst = (unsigned char *)to->ptr;
+
+ for (i = 0; i < from->size; i++)
+ dst[i] = VERY_SECURE_ENCRYPTION(src[i]);
+
+ to->size = from->size;
+
+ return 0;
+}
+
+static void bitflip_filter_free(git_filter *f)
+{
+ git__free(f);
+}
+
+static git_filter *create_bitflip_filter(void)
+{
+ git_filter *filter = git__calloc(1, sizeof(git_filter));
+ cl_assert(filter);
+
+ filter->version = GIT_FILTER_VERSION;
+ filter->attributes = "+bitflip";
+ filter->shutdown = bitflip_filter_free;
+ filter->apply = bitflip_filter_apply;
+
+ return filter;
+}
+
+
+static int reverse_filter_apply(
+ git_filter *self,
+ void **payload,
+ git_buf *to,
+ const git_buf *from,
+ const git_filter_source *source)
+{
+ const unsigned char *src = (const unsigned char *)from->ptr;
+ const unsigned char *end = src + from->size;
+ unsigned char *dst;
+
+ GIT_UNUSED(self); GIT_UNUSED(payload); GIT_UNUSED(source);
+
+ /* verify that attribute path match worked as expected */
+ cl_assert_equal_i(
+ 0, git__strncmp("hero", git_filter_source_path(source), 4));
+
+ if (!from->size)
+ return 0;
+
+ cl_git_pass(git_buf_grow(to, from->size));
+
+ dst = (unsigned char *)to->ptr + from->size - 1;
+
+ while (src < end)
+ *dst-- = *src++;
+
+ to->size = from->size;
+
+ return 0;
+}
+
+static void reverse_filter_free(git_filter *f)
+{
+ git__free(f);
+}
+
+static git_filter *create_reverse_filter(const char *attrs)
+{
+ git_filter *filter = git__calloc(1, sizeof(git_filter));
+ cl_assert(filter);
+
+ filter->version = GIT_FILTER_VERSION;
+ filter->attributes = attrs;
+ filter->shutdown = reverse_filter_free;
+ filter->apply = reverse_filter_apply;
+
+ return filter;
+}
+
+static void register_custom_filters(void)
+{
+ static int filters_registered = 0;
+
+ if (!filters_registered) {
+ cl_git_pass(git_filter_register(
+ "bitflip", create_bitflip_filter(), BITFLIP_FILTER_PRIORITY));
+
+ cl_git_pass(git_filter_register(
+ "reverse", create_reverse_filter("+reverse"),
+ REVERSE_FILTER_PRIORITY));
+
+ /* re-register reverse filter with standard filter=xyz priority */
+ cl_git_pass(git_filter_register(
+ "pre-reverse",
+ create_reverse_filter("+prereverse"),
+ GIT_FILTER_DRIVER_PRIORITY));
+
+ filters_registered = 1;
+ }
+}
+
+
+void test_filter_custom__to_odb(void)
+{
+ git_filter_list *fl;
+ git_buf out = { 0 };
+ git_buf in = GIT_BUF_INIT_CONST(workdir_data, strlen(workdir_data));
+
+ cl_git_pass(git_filter_list_load(
+ &fl, g_repo, NULL, "herofile", GIT_FILTER_TO_ODB));
+
+ cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+
+ cl_assert_equal_i(BITFLIPPED_AND_REVERSED_DATA_LEN, out.size);
+
+ cl_assert_equal_i(
+ 0, memcmp(bitflipped_and_reversed_data, out.ptr, out.size));
+
+ git_filter_list_free(fl);
+ git_buf_free(&out);
+}
+
+void test_filter_custom__to_workdir(void)
+{
+ git_filter_list *fl;
+ git_buf out = { 0 };
+ git_buf in = GIT_BUF_INIT_CONST(
+ bitflipped_and_reversed_data, BITFLIPPED_AND_REVERSED_DATA_LEN);
+
+ cl_git_pass(git_filter_list_load(
+ &fl, g_repo, NULL, "herofile", GIT_FILTER_TO_WORKTREE));
+
+ cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+
+ cl_assert_equal_i(strlen(workdir_data), out.size);
+
+ cl_assert_equal_i(
+ 0, memcmp(workdir_data, out.ptr, out.size));
+
+ git_filter_list_free(fl);
+ git_buf_free(&out);
+}
+
+void test_filter_custom__can_register_a_custom_filter_in_the_repository(void)
+{
+ git_filter_list *fl;
+
+ cl_git_pass(git_filter_list_load(
+ &fl, g_repo, NULL, "herofile", GIT_FILTER_TO_WORKTREE));
+ /* expect: bitflip, reverse, crlf */
+ cl_assert_equal_sz(3, git_filter_list_length(fl));
+ git_filter_list_free(fl);
+
+ cl_git_pass(git_filter_list_load(
+ &fl, g_repo, NULL, "herocorp", GIT_FILTER_TO_WORKTREE));
+ /* expect: bitflip, reverse - possibly crlf depending on global config */
+ {
+ size_t flen = git_filter_list_length(fl);
+ cl_assert(flen == 2 || flen == 3);
+ }
+ git_filter_list_free(fl);
+
+ cl_git_pass(git_filter_list_load(
+ &fl, g_repo, NULL, "hero.bin", GIT_FILTER_TO_WORKTREE));
+ /* expect: bitflip, reverse */
+ cl_assert_equal_sz(2, git_filter_list_length(fl));
+ git_filter_list_free(fl);
+
+ cl_git_pass(git_filter_list_load(
+ &fl, g_repo, NULL, "heroflip", GIT_FILTER_TO_WORKTREE));
+ /* expect: bitflip (because of -reverse) */
+ cl_assert_equal_sz(1, git_filter_list_length(fl));
+ git_filter_list_free(fl);
+
+ cl_git_pass(git_filter_list_load(
+ &fl, g_repo, NULL, "doesntapplytome.bin", GIT_FILTER_TO_WORKTREE));
+ /* expect: none */
+ cl_assert_equal_sz(0, git_filter_list_length(fl));
+ git_filter_list_free(fl);
+}
+
+void test_filter_custom__order_dependency(void)
+{
+ git_index *index;
+ git_blob *blob;
+ git_buf buf = { 0 };
+
+ /* so if ident and reverse are used together, an interesting thing
+ * happens - a reversed "$Id$" string is no longer going to trigger
+ * ident correctly. When checking out, the filters should be applied
+ * in order CLRF, then ident, then reverse, so ident expansion should
+ * work correctly. On check in, the content should be reversed, then
+ * ident, then CRLF filtered. Let's make sure that works...
+ */
+
+ cl_git_mkfile(
+ "empty_standard_repo/.gitattributes",
+ "hero.*.rev-ident text ident prereverse eol=lf\n");
+
+ cl_git_mkfile(
+ "empty_standard_repo/hero.1.rev-ident",
+ "This is a test\n$Id$\nHave fun!\n");
+
+ cl_git_mkfile(
+ "empty_standard_repo/hero.2.rev-ident",
+ "Another test\n$dI$\nCrazy!\n");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_add_bypath(index, "hero.1.rev-ident"));
+ cl_git_pass(git_index_add_bypath(index, "hero.2.rev-ident"));
+ cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "Filter chains\n");
+ git_index_free(index);
+
+ cl_git_pass(git_blob_lookup(&blob, g_repo,
+ & git_index_get_bypath(index, "hero.1.rev-ident", 0)->oid));
+ cl_assert_equal_s(
+ "\n!nuf evaH\n$dI$\ntset a si sihT", git_blob_rawcontent(blob));
+ cl_git_pass(git_blob_filtered_content(&buf, blob, "hero.1.rev-ident", 0));
+ /* no expansion because id was reversed at checkin and now at ident
+ * time, reverse is not applied yet */
+ cl_assert_equal_s(
+ "This is a test\n$Id$\nHave fun!\n", buf.ptr);
+ git_blob_free(blob);
+
+ cl_git_pass(git_blob_lookup(&blob, g_repo,
+ & git_index_get_bypath(index, "hero.2.rev-ident", 0)->oid));
+ cl_assert_equal_s(
+ "\n!yzarC\n$Id$\ntset rehtonA", git_blob_rawcontent(blob));
+ cl_git_pass(git_blob_filtered_content(&buf, blob, "hero.2.rev-ident", 0));
+ /* expansion because reverse was applied at checkin and at ident time,
+ * reverse is not applied yet */
+ cl_assert_equal_s(
+ "Another test\n$59001fe193103b1016b27027c0c827d036fd0ac8 :dI$\nCrazy!\n", buf.ptr);
+ cl_assert_equal_i(0, git_oid_strcmp(
+ git_blob_id(blob), "8ca0df630d728c0c72072b6101b301391ef10095"));
+ git_blob_free(blob);
+
+ git_buf_free(&buf);
+}
+
+void test_filter_custom__filter_registry_failure_cases(void)
+{
+ git_filter fake = { GIT_FILTER_VERSION, 0 };
+
+ cl_assert_equal_i(GIT_EEXISTS, git_filter_register("bitflip", &fake, 0));
+
+ cl_git_fail(git_filter_unregister(GIT_FILTER_CRLF));
+ cl_git_fail(git_filter_unregister(GIT_FILTER_IDENT));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_filter_unregister("not-a-filter"));
+}
diff --git a/tests/filter/ident.c b/tests/filter/ident.c
new file mode 100644
index 000000000..2c8e6abea
--- /dev/null
+++ b/tests/filter/ident.c
@@ -0,0 +1,131 @@
+#include "clar_libgit2.h"
+#include "git2/sys/filter.h"
+
+static git_repository *g_repo = NULL;
+
+void test_filter_ident__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("crlf");
+}
+
+void test_filter_ident__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static void add_blob_and_filter(
+ const char *data,
+ git_filter_list *fl,
+ const char *expected)
+{
+ git_oid id;
+ git_blob *blob;
+ git_buf out = { 0 };
+
+ cl_git_mkfile("crlf/identtest", data);
+ cl_git_pass(git_blob_create_fromworkdir(&id, g_repo, "identtest"));
+ cl_git_pass(git_blob_lookup(&blob, g_repo, &id));
+
+ cl_git_pass(git_filter_list_apply_to_blob(&out, fl, blob));
+
+ cl_assert_equal_s(expected, out.ptr);
+
+ git_blob_free(blob);
+ git_buf_free(&out);
+}
+
+void test_filter_ident__to_worktree(void)
+{
+ git_filter_list *fl;
+ git_filter *ident;
+
+ cl_git_pass(git_filter_list_new(&fl, g_repo, GIT_FILTER_TO_WORKTREE));
+
+ ident = git_filter_lookup(GIT_FILTER_IDENT);
+ cl_assert(ident != NULL);
+
+ cl_git_pass(git_filter_list_push(fl, ident, NULL));
+
+ add_blob_and_filter(
+ "Hello\n$Id$\nFun stuff\n", fl,
+ "Hello\n$Id: b69e2387aafcaf73c4de5b9ab59abe27fdadee30$\nFun stuff\n");
+ add_blob_and_filter(
+ "Hello\n$Id: Junky$\nFun stuff\n", fl,
+ "Hello\n$Id: 45cd107a7102911cb2a7df08404674327fa050b9$\nFun stuff\n");
+ add_blob_and_filter(
+ "$Id$\nAt the start\n", fl,
+ "$Id: b13415c767abc196fb95bd17070e8c1113e32160$\nAt the start\n");
+ add_blob_and_filter(
+ "At the end\n$Id$", fl,
+ "At the end\n$Id: 1344925c6bc65b34c5a7b50f86bf688e48e9a272$");
+ add_blob_and_filter(
+ "$Id$", fl,
+ "$Id: b3f5ebfb5843bc43ceecff6d4f26bb37c615beb1$");
+ add_blob_and_filter(
+ "$Id: Some sort of junk goes here$", fl,
+ "$Id: ab2dd3853c7c9a4bff55aca2bea077a73c32ac06$");
+
+ add_blob_and_filter("$Id: ", fl, "$Id: ");
+ add_blob_and_filter("$Id", fl, "$Id");
+ add_blob_and_filter("$I", fl, "$I");
+ add_blob_and_filter("Id$", fl, "Id$");
+
+ git_filter_list_free(fl);
+}
+
+void test_filter_ident__to_odb(void)
+{
+ git_filter_list *fl;
+ git_filter *ident;
+
+ cl_git_pass(git_filter_list_new(&fl, g_repo, GIT_FILTER_TO_ODB));
+
+ ident = git_filter_lookup(GIT_FILTER_IDENT);
+ cl_assert(ident != NULL);
+
+ cl_git_pass(git_filter_list_push(fl, ident, NULL));
+
+ add_blob_and_filter(
+ "Hello\n$Id$\nFun stuff\n",
+ fl, "Hello\n$Id$\nFun stuff\n");
+ add_blob_and_filter(
+ "Hello\n$Id: b69e2387aafcaf73c4de5b9ab59abe27fdadee30$\nFun stuff\n",
+ fl, "Hello\n$Id$\nFun stuff\n");
+ add_blob_and_filter(
+ "Hello\n$Id: Any junk you may have left here$\nFun stuff\n",
+ fl, "Hello\n$Id$\nFun stuff\n");
+ add_blob_and_filter(
+ "Hello\n$Id:$\nFun stuff\n",
+ fl, "Hello\n$Id$\nFun stuff\n");
+ add_blob_and_filter(
+ "Hello\n$Id:x$\nFun stuff\n",
+ fl, "Hello\n$Id$\nFun stuff\n");
+
+ add_blob_and_filter(
+ "$Id$\nAt the start\n", fl, "$Id$\nAt the start\n");
+ add_blob_and_filter(
+ "$Id: lots of random text that should be removed from here$\nAt the start\n", fl, "$Id$\nAt the start\n");
+ add_blob_and_filter(
+ "$Id: lots of random text that should not be removed without a terminator\nAt the start\n", fl, "$Id: lots of random text that should not be removed without a terminator\nAt the start\n");
+
+ add_blob_and_filter(
+ "At the end\n$Id$", fl, "At the end\n$Id$");
+ add_blob_and_filter(
+ "At the end\n$Id:$", fl, "At the end\n$Id$");
+ add_blob_and_filter(
+ "At the end\n$Id:asdfasdf$", fl, "At the end\n$Id$");
+ add_blob_and_filter(
+ "At the end\n$Id", fl, "At the end\n$Id");
+ add_blob_and_filter(
+ "At the end\n$IddI", fl, "At the end\n$IddI");
+
+ add_blob_and_filter("$Id$", fl, "$Id$");
+ add_blob_and_filter("$Id: any$", fl, "$Id$");
+ add_blob_and_filter("$Id: any long stuff goes here you see$", fl, "$Id$");
+ add_blob_and_filter("$Id: ", fl, "$Id: ");
+ add_blob_and_filter("$Id", fl, "$Id");
+ add_blob_and_filter("$I", fl, "$I");
+ add_blob_and_filter("Id$", fl, "Id$");
+
+ git_filter_list_free(fl);
+}
diff --git a/tests-clar/generate.py b/tests/generate.py
index d4fe8f2a3..d4fe8f2a3 100644
--- a/tests-clar/generate.py
+++ b/tests/generate.py
diff --git a/tests/index/addall.c b/tests/index/addall.c
new file mode 100644
index 000000000..44c51279d
--- /dev/null
+++ b/tests/index/addall.c
@@ -0,0 +1,258 @@
+#include "clar_libgit2.h"
+#include "../status/status_helpers.h"
+#include "posix.h"
+#include "fileops.h"
+
+git_repository *g_repo = NULL;
+
+void test_index_addall__initialize(void)
+{
+}
+
+void test_index_addall__cleanup(void)
+{
+ git_repository_free(g_repo);
+ g_repo = NULL;
+}
+
+#define STATUS_INDEX_FLAGS \
+ (GIT_STATUS_INDEX_NEW | GIT_STATUS_INDEX_MODIFIED | \
+ GIT_STATUS_INDEX_DELETED | GIT_STATUS_INDEX_RENAMED | \
+ GIT_STATUS_INDEX_TYPECHANGE)
+
+#define STATUS_WT_FLAGS \
+ (GIT_STATUS_WT_NEW | GIT_STATUS_WT_MODIFIED | \
+ GIT_STATUS_WT_DELETED | GIT_STATUS_WT_TYPECHANGE | \
+ GIT_STATUS_WT_RENAMED)
+
+typedef struct {
+ size_t index_adds;
+ size_t index_dels;
+ size_t index_mods;
+ size_t wt_adds;
+ size_t wt_dels;
+ size_t wt_mods;
+ size_t ignores;
+} index_status_counts;
+
+static int index_status_cb(
+ const char *path, unsigned int status_flags, void *payload)
+{
+ index_status_counts *vals = payload;
+
+ /* cb_status__print(path, status_flags, NULL); */
+
+ GIT_UNUSED(path);
+
+ if (status_flags & GIT_STATUS_INDEX_NEW)
+ vals->index_adds++;
+ if (status_flags & GIT_STATUS_INDEX_MODIFIED)
+ vals->index_mods++;
+ if (status_flags & GIT_STATUS_INDEX_DELETED)
+ vals->index_dels++;
+ if (status_flags & GIT_STATUS_INDEX_TYPECHANGE)
+ vals->index_mods++;
+
+ if (status_flags & GIT_STATUS_WT_NEW)
+ vals->wt_adds++;
+ if (status_flags & GIT_STATUS_WT_MODIFIED)
+ vals->wt_mods++;
+ if (status_flags & GIT_STATUS_WT_DELETED)
+ vals->wt_dels++;
+ if (status_flags & GIT_STATUS_WT_TYPECHANGE)
+ vals->wt_mods++;
+
+ if (status_flags & GIT_STATUS_IGNORED)
+ vals->ignores++;
+
+ return 0;
+}
+
+static void check_status_at_line(
+ git_repository *repo,
+ size_t index_adds, size_t index_dels, size_t index_mods,
+ size_t wt_adds, size_t wt_dels, size_t wt_mods, size_t ignores,
+ const char *file, int line)
+{
+ index_status_counts vals;
+
+ memset(&vals, 0, sizeof(vals));
+
+ cl_git_pass(git_status_foreach(repo, index_status_cb, &vals));
+
+ clar__assert_equal(
+ file,line,"wrong index adds", 1, "%"PRIuZ, index_adds, vals.index_adds);
+ clar__assert_equal(
+ file,line,"wrong index dels", 1, "%"PRIuZ, index_dels, vals.index_dels);
+ clar__assert_equal(
+ file,line,"wrong index mods", 1, "%"PRIuZ, index_mods, vals.index_mods);
+ clar__assert_equal(
+ file,line,"wrong workdir adds", 1, "%"PRIuZ, wt_adds, vals.wt_adds);
+ clar__assert_equal(
+ file,line,"wrong workdir dels", 1, "%"PRIuZ, wt_dels, vals.wt_dels);
+ clar__assert_equal(
+ file,line,"wrong workdir mods", 1, "%"PRIuZ, wt_mods, vals.wt_mods);
+ clar__assert_equal(
+ file,line,"wrong ignores", 1, "%"PRIuZ, ignores, vals.ignores);
+}
+
+#define check_status(R,IA,ID,IM,WA,WD,WM,IG) \
+ check_status_at_line(R,IA,ID,IM,WA,WD,WM,IG,__FILE__,__LINE__)
+
+static void check_stat_data(git_index *index, const char *path, bool match)
+{
+ const git_index_entry *entry;
+ struct stat st;
+
+ cl_must_pass(p_lstat(path, &st));
+
+ /* skip repo base dir name */
+ while (*path != '/')
+ ++path;
+ ++path;
+
+ entry = git_index_get_bypath(index, path, 0);
+ cl_assert(entry);
+
+ if (match) {
+ cl_assert(st.st_ctime == entry->ctime.seconds);
+ cl_assert(st.st_mtime == entry->mtime.seconds);
+ cl_assert(st.st_size == entry->file_size);
+ cl_assert(st.st_uid == entry->uid);
+ cl_assert(st.st_gid == entry->gid);
+ cl_assert_equal_i_fmt(
+ GIT_MODE_TYPE(st.st_mode), GIT_MODE_TYPE(entry->mode), "%07o");
+ if (cl_is_chmod_supported())
+ cl_assert_equal_b(
+ GIT_PERMS_IS_EXEC(st.st_mode), GIT_PERMS_IS_EXEC(entry->mode));
+ } else {
+ /* most things will still match */
+ cl_assert(st.st_size != entry->file_size);
+ /* would check mtime, but with second resolution it won't work :( */
+ }
+}
+
+void test_index_addall__repo_lifecycle(void)
+{
+ int error;
+ git_index *index;
+ git_strarray paths = { NULL, 0 };
+ char *strs[1];
+
+ cl_git_pass(git_repository_init(&g_repo, "addall", false));
+ check_status(g_repo, 0, 0, 0, 0, 0, 0, 0);
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_mkfile("addall/file.foo", "a file");
+ check_status(g_repo, 0, 0, 0, 1, 0, 0, 0);
+
+ cl_git_mkfile("addall/.gitignore", "*.foo\n");
+ check_status(g_repo, 0, 0, 0, 1, 0, 0, 1);
+
+ cl_git_mkfile("addall/file.bar", "another file");
+ check_status(g_repo, 0, 0, 0, 2, 0, 0, 1);
+
+ strs[0] = "file.*";
+ paths.strings = strs;
+ paths.count = 1;
+
+ cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL));
+ check_stat_data(index, "addall/file.bar", true);
+ check_status(g_repo, 1, 0, 0, 1, 0, 0, 1);
+
+ cl_git_rewritefile("addall/file.bar", "new content for file");
+ check_stat_data(index, "addall/file.bar", false);
+ check_status(g_repo, 1, 0, 0, 1, 0, 1, 1);
+
+ cl_git_mkfile("addall/file.zzz", "yet another one");
+ cl_git_mkfile("addall/other.zzz", "yet another one");
+ cl_git_mkfile("addall/more.zzz", "yet another one");
+ check_status(g_repo, 1, 0, 0, 4, 0, 1, 1);
+
+ cl_git_pass(git_index_update_all(index, NULL, NULL, NULL));
+ check_stat_data(index, "addall/file.bar", true);
+ check_status(g_repo, 1, 0, 0, 4, 0, 0, 1);
+
+ cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL));
+ check_stat_data(index, "addall/file.zzz", true);
+ check_status(g_repo, 2, 0, 0, 3, 0, 0, 1);
+
+ cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "first commit");
+ check_status(g_repo, 0, 0, 0, 3, 0, 0, 1);
+
+ /* attempt to add an ignored file - does nothing */
+ strs[0] = "file.foo";
+ cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL));
+ check_status(g_repo, 0, 0, 0, 3, 0, 0, 1);
+
+ /* add with check - should generate error */
+ error = git_index_add_all(
+ index, &paths, GIT_INDEX_ADD_CHECK_PATHSPEC, NULL, NULL);
+ cl_assert_equal_i(GIT_EINVALIDSPEC, error);
+ check_status(g_repo, 0, 0, 0, 3, 0, 0, 1);
+
+ /* add with force - should allow */
+ cl_git_pass(git_index_add_all(
+ index, &paths, GIT_INDEX_ADD_FORCE, NULL, NULL));
+ check_stat_data(index, "addall/file.foo", true);
+ check_status(g_repo, 1, 0, 0, 3, 0, 0, 0);
+
+ /* now it's in the index, so regular add should work */
+ cl_git_rewritefile("addall/file.foo", "new content for file");
+ check_stat_data(index, "addall/file.foo", false);
+ check_status(g_repo, 1, 0, 0, 3, 0, 1, 0);
+
+ cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL));
+ check_stat_data(index, "addall/file.foo", true);
+ check_status(g_repo, 1, 0, 0, 3, 0, 0, 0);
+
+ cl_git_pass(git_index_add_bypath(index, "more.zzz"));
+ check_stat_data(index, "addall/more.zzz", true);
+ check_status(g_repo, 2, 0, 0, 2, 0, 0, 0);
+
+ cl_git_rewritefile("addall/file.zzz", "new content for file");
+ check_status(g_repo, 2, 0, 0, 2, 0, 1, 0);
+
+ cl_git_pass(git_index_add_bypath(index, "file.zzz"));
+ check_stat_data(index, "addall/file.zzz", true);
+ check_status(g_repo, 2, 0, 1, 2, 0, 0, 0);
+
+ strs[0] = "*.zzz";
+ cl_git_pass(git_index_remove_all(index, &paths, NULL, NULL));
+ check_status(g_repo, 1, 1, 0, 4, 0, 0, 0);
+
+ cl_git_pass(git_index_add_bypath(index, "file.zzz"));
+ check_status(g_repo, 1, 0, 1, 3, 0, 0, 0);
+
+ cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "second commit");
+ check_status(g_repo, 0, 0, 0, 3, 0, 0, 0);
+
+ cl_must_pass(p_unlink("addall/file.zzz"));
+ check_status(g_repo, 0, 0, 0, 3, 1, 0, 0);
+
+ /* update_all should be able to remove entries */
+ cl_git_pass(git_index_update_all(index, NULL, NULL, NULL));
+ check_status(g_repo, 0, 1, 0, 3, 0, 0, 0);
+
+ strs[0] = "*";
+ cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL));
+ check_status(g_repo, 3, 1, 0, 0, 0, 0, 0);
+
+ /* must be able to remove at any position while still updating other files */
+ cl_must_pass(p_unlink("addall/.gitignore"));
+ cl_git_rewritefile("addall/file.zzz", "reconstructed file");
+ cl_git_rewritefile("addall/more.zzz", "altered file reality");
+ check_status(g_repo, 3, 1, 0, 1, 1, 1, 0);
+
+ cl_git_pass(git_index_update_all(index, NULL, NULL, NULL));
+ check_status(g_repo, 2, 1, 0, 1, 0, 0, 0);
+ /* this behavior actually matches 'git add -u' where "file.zzz" has
+ * been removed from the index, so when you go to update, even though
+ * it exists in the HEAD, it is not re-added to the index, leaving it
+ * as a DELETE when comparing HEAD to index and as an ADD comparing
+ * index to worktree
+ */
+
+ git_index_free(index);
+}
diff --git a/tests-clar/index/conflicts.c b/tests/index/conflicts.c
index 6311b3a75..6311b3a75 100644
--- a/tests-clar/index/conflicts.c
+++ b/tests/index/conflicts.c
diff --git a/tests/index/filemodes.c b/tests/index/filemodes.c
new file mode 100644
index 000000000..013932696
--- /dev/null
+++ b/tests/index/filemodes.c
@@ -0,0 +1,154 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "posix.h"
+#include "index.h"
+
+static git_repository *g_repo = NULL;
+
+void test_index_filemodes__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("filemodes");
+}
+
+void test_index_filemodes__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_index_filemodes__read(void)
+{
+ git_index *index;
+ unsigned int i;
+ static bool expected[6] = { 0, 1, 0, 1, 0, 1 };
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_assert_equal_i(6, (int)git_index_entrycount(index));
+
+ for (i = 0; i < 6; ++i) {
+ const git_index_entry *entry = git_index_get_byindex(index, i);
+ cl_assert(entry != NULL);
+ cl_assert(((entry->mode & 0100) ? 1 : 0) == expected[i]);
+ }
+
+ git_index_free(index);
+}
+
+static void replace_file_with_mode(
+ const char *filename, const char *backup, unsigned int create_mode)
+{
+ git_buf path = GIT_BUF_INIT, content = GIT_BUF_INIT;
+
+ cl_git_pass(git_buf_joinpath(&path, "filemodes", filename));
+ cl_git_pass(git_buf_printf(&content, "%s as %08u (%d)",
+ filename, create_mode, rand()));
+
+ cl_git_pass(p_rename(path.ptr, backup));
+ cl_git_write2file(
+ path.ptr, content.ptr, content.size,
+ O_WRONLY|O_CREAT|O_TRUNC, create_mode);
+
+ git_buf_free(&path);
+ git_buf_free(&content);
+}
+
+#define add_and_check_mode(I,F,X) add_and_check_mode_(I,F,X,__FILE__,__LINE__)
+
+static void add_and_check_mode_(
+ git_index *index, const char *filename, unsigned int expect_mode,
+ const char *file, int line)
+{
+ size_t pos;
+ const git_index_entry *entry;
+
+ cl_git_pass(git_index_add_bypath(index, filename));
+
+ clar__assert(!git_index_find(&pos, index, filename),
+ file, line, "Cannot find index entry", NULL, 1);
+
+ entry = git_index_get_byindex(index, pos);
+
+ clar__assert_equal(file, line, "Expected mode does not match index",
+ 1, "%07o", (unsigned int)entry->mode, (unsigned int)expect_mode);
+}
+
+void test_index_filemodes__untrusted(void)
+{
+ git_index *index;
+
+ cl_repo_set_bool(g_repo, "core.filemode", false);
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_assert((git_index_caps(index) & GIT_INDEXCAP_NO_FILEMODE) != 0);
+
+ /* 1 - add 0644 over existing 0644 -> expect 0644 */
+ replace_file_with_mode("exec_off", "filemodes/exec_off.0", 0644);
+ add_and_check_mode(index, "exec_off", GIT_FILEMODE_BLOB);
+
+ /* 2 - add 0644 over existing 0755 -> expect 0755 */
+ replace_file_with_mode("exec_on", "filemodes/exec_on.0", 0644);
+ add_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB_EXECUTABLE);
+
+ /* 3 - add 0755 over existing 0644 -> expect 0644 */
+ replace_file_with_mode("exec_off", "filemodes/exec_off.1", 0755);
+ add_and_check_mode(index, "exec_off", GIT_FILEMODE_BLOB);
+
+ /* 4 - add 0755 over existing 0755 -> expect 0755 */
+ replace_file_with_mode("exec_on", "filemodes/exec_on.1", 0755);
+ add_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB_EXECUTABLE);
+
+ /* 5 - add new 0644 -> expect 0644 */
+ cl_git_write2file("filemodes/new_off", "blah", 0,
+ O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ add_and_check_mode(index, "new_off", GIT_FILEMODE_BLOB);
+
+ /* 6 - add new 0755 -> expect 0644 if core.filemode == false */
+ cl_git_write2file("filemodes/new_on", "blah", 0,
+ O_WRONLY | O_CREAT | O_TRUNC, 0755);
+ add_and_check_mode(index, "new_on", GIT_FILEMODE_BLOB);
+
+ git_index_free(index);
+}
+
+void test_index_filemodes__trusted(void)
+{
+ git_index *index;
+
+ /* Only run these tests on platforms where I can actually
+ * chmod a file and get the stat results I expect!
+ */
+ if (!cl_is_chmod_supported())
+ return;
+
+ cl_repo_set_bool(g_repo, "core.filemode", true);
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_assert((git_index_caps(index) & GIT_INDEXCAP_NO_FILEMODE) == 0);
+
+ /* 1 - add 0644 over existing 0644 -> expect 0644 */
+ replace_file_with_mode("exec_off", "filemodes/exec_off.0", 0644);
+ add_and_check_mode(index, "exec_off", GIT_FILEMODE_BLOB);
+
+ /* 2 - add 0644 over existing 0755 -> expect 0644 */
+ replace_file_with_mode("exec_on", "filemodes/exec_on.0", 0644);
+ add_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB);
+
+ /* 3 - add 0755 over existing 0644 -> expect 0755 */
+ replace_file_with_mode("exec_off", "filemodes/exec_off.1", 0755);
+ add_and_check_mode(index, "exec_off", GIT_FILEMODE_BLOB_EXECUTABLE);
+
+ /* 4 - add 0755 over existing 0755 -> expect 0755 */
+ replace_file_with_mode("exec_on", "filemodes/exec_on.1", 0755);
+ add_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB_EXECUTABLE);
+
+ /* 5 - add new 0644 -> expect 0644 */
+ cl_git_write2file("filemodes/new_off", "blah", 0,
+ O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ add_and_check_mode(index, "new_off", GIT_FILEMODE_BLOB);
+
+ /* 6 - add 0755 -> expect 0755 */
+ cl_git_write2file("filemodes/new_on", "blah", 0,
+ O_WRONLY | O_CREAT | O_TRUNC, 0755);
+ add_and_check_mode(index, "new_on", GIT_FILEMODE_BLOB_EXECUTABLE);
+
+ git_index_free(index);
+}
diff --git a/tests-clar/index/inmemory.c b/tests/index/inmemory.c
index 38e91e0fd..38e91e0fd 100644
--- a/tests-clar/index/inmemory.c
+++ b/tests/index/inmemory.c
diff --git a/tests/index/names.c b/tests/index/names.c
new file mode 100644
index 000000000..9007b1b15
--- /dev/null
+++ b/tests/index/names.c
@@ -0,0 +1,148 @@
+#include "clar_libgit2.h"
+#include "index.h"
+#include "git2/sys/index.h"
+#include "git2/repository.h"
+#include "../reset/reset_helpers.h"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+#define TEST_REPO_PATH "mergedrepo"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+// Fixture setup and teardown
+void test_index_names__initialize(void)
+{
+ repo = cl_git_sandbox_init("mergedrepo");
+ git_repository_index(&repo_index, repo);
+}
+
+void test_index_names__cleanup(void)
+{
+ git_index_free(repo_index);
+ repo_index = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_index_names__add(void)
+{
+ const git_index_name_entry *conflict_name;
+
+ cl_git_pass(git_index_name_add(repo_index, "ancestor", "ours", "theirs"));
+ cl_git_pass(git_index_name_add(repo_index, "ancestor2", "ours2", NULL));
+ cl_git_pass(git_index_name_add(repo_index, "ancestor3", NULL, "theirs3"));
+
+ cl_assert(git_index_name_entrycount(repo_index) == 3);
+
+ conflict_name = git_index_name_get_byindex(repo_index, 0);
+ cl_assert(strcmp(conflict_name->ancestor, "ancestor") == 0);
+ cl_assert(strcmp(conflict_name->ours, "ours") == 0);
+ cl_assert(strcmp(conflict_name->theirs, "theirs") == 0);
+
+ conflict_name = git_index_name_get_byindex(repo_index, 1);
+ cl_assert(strcmp(conflict_name->ancestor, "ancestor2") == 0);
+ cl_assert(strcmp(conflict_name->ours, "ours2") == 0);
+ cl_assert(conflict_name->theirs == NULL);
+
+ conflict_name = git_index_name_get_byindex(repo_index, 2);
+ cl_assert(strcmp(conflict_name->ancestor, "ancestor3") == 0);
+ cl_assert(conflict_name->ours == NULL);
+ cl_assert(strcmp(conflict_name->theirs, "theirs3") == 0);
+}
+
+void test_index_names__roundtrip(void)
+{
+ const git_index_name_entry *conflict_name;
+
+ cl_git_pass(git_index_name_add(repo_index, "ancestor", "ours", "theirs"));
+ cl_git_pass(git_index_name_add(repo_index, "ancestor2", "ours2", NULL));
+ cl_git_pass(git_index_name_add(repo_index, "ancestor3", NULL, "theirs3"));
+
+ cl_git_pass(git_index_write(repo_index));
+ git_index_clear(repo_index);
+ cl_assert(git_index_name_entrycount(repo_index) == 0);
+
+ cl_git_pass(git_index_read(repo_index, true));
+ cl_assert(git_index_name_entrycount(repo_index) == 3);
+
+ conflict_name = git_index_name_get_byindex(repo_index, 0);
+ cl_assert(strcmp(conflict_name->ancestor, "ancestor") == 0);
+ cl_assert(strcmp(conflict_name->ours, "ours") == 0);
+ cl_assert(strcmp(conflict_name->theirs, "theirs") == 0);
+
+ conflict_name = git_index_name_get_byindex(repo_index, 1);
+ cl_assert(strcmp(conflict_name->ancestor, "ancestor2") == 0);
+ cl_assert(strcmp(conflict_name->ours, "ours2") == 0);
+ cl_assert(conflict_name->theirs == NULL);
+
+ conflict_name = git_index_name_get_byindex(repo_index, 2);
+ cl_assert(strcmp(conflict_name->ancestor, "ancestor3") == 0);
+ cl_assert(conflict_name->ours == NULL);
+ cl_assert(strcmp(conflict_name->theirs, "theirs3") == 0);
+}
+
+void test_index_names__cleaned_on_reset_hard(void)
+{
+ git_object *target;
+
+ retrieve_target_from_oid(&target, repo, "3a34580a35add43a4cf361e8e9a30060a905c876");
+
+ test_index_names__add();
+ cl_git_pass(git_reset(repo, target, GIT_RESET_HARD));
+ cl_assert(git_index_name_entrycount(repo_index) == 0);
+
+ git_object_free(target);
+}
+
+void test_index_names__cleaned_on_reset_mixed(void)
+{
+ git_object *target;
+
+ retrieve_target_from_oid(&target, repo, "3a34580a35add43a4cf361e8e9a30060a905c876");
+
+ test_index_names__add();
+ cl_git_pass(git_reset(repo, target, GIT_RESET_MIXED));
+ cl_assert(git_index_name_entrycount(repo_index) == 0);
+
+ git_object_free(target);
+}
+
+void test_index_names__cleaned_on_checkout_tree(void)
+{
+ git_oid oid;
+ git_object *obj;
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY;
+
+ test_index_names__add();
+ git_reference_name_to_id(&oid, repo, "refs/heads/master");
+ git_object_lookup(&obj, repo, &oid, GIT_OBJ_ANY);
+ git_checkout_tree(repo, obj, &opts);
+ cl_assert_equal_sz(0, git_index_name_entrycount(repo_index));
+
+ git_object_free(obj);
+}
+
+void test_index_names__cleaned_on_checkout_head(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY;
+
+ test_index_names__add();
+ git_checkout_head(repo, &opts);
+ cl_assert_equal_sz(0, git_index_name_entrycount(repo_index));
+}
+
+void test_index_names__retained_on_checkout_index(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY;
+
+ test_index_names__add();
+ git_checkout_index(repo, repo_index, &opts);
+ cl_assert(git_index_name_entrycount(repo_index) > 0);
+}
diff --git a/tests-clar/index/read_tree.c b/tests/index/read_tree.c
index 6c6b40121..6c6b40121 100644
--- a/tests-clar/index/read_tree.c
+++ b/tests/index/read_tree.c
diff --git a/tests-clar/index/rename.c b/tests/index/rename.c
index 4deef1332..4deef1332 100644
--- a/tests-clar/index/rename.c
+++ b/tests/index/rename.c
diff --git a/tests/index/reuc.c b/tests/index/reuc.c
new file mode 100644
index 000000000..a18d5602e
--- /dev/null
+++ b/tests/index/reuc.c
@@ -0,0 +1,372 @@
+#include "clar_libgit2.h"
+#include "index.h"
+#include "git2/sys/index.h"
+#include "git2/repository.h"
+#include "../reset/reset_helpers.h"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+#define TEST_REPO_PATH "mergedrepo"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+#define ONE_ANCESTOR_OID "478871385b9cd03908c5383acfd568bef023c6b3"
+#define ONE_OUR_OID "4458b8bc9e72b6c8755ae456f60e9844d0538d8c"
+#define ONE_THEIR_OID "8b72416545c7e761b64cecad4f1686eae4078aa8"
+
+#define TWO_ANCESTOR_OID "9d81f82fccc7dcd7de7a1ffead1815294c2e092c"
+#define TWO_OUR_OID "8f3c06cff9a83757cec40c80bc9bf31a2582bde9"
+#define TWO_THEIR_OID "887b153b165d32409c70163e0f734c090f12f673"
+
+// Fixture setup and teardown
+void test_index_reuc__initialize(void)
+{
+ repo = cl_git_sandbox_init("mergedrepo");
+ git_repository_index(&repo_index, repo);
+}
+
+void test_index_reuc__cleanup(void)
+{
+ git_index_free(repo_index);
+ repo_index = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_index_reuc__add(void)
+{
+ git_oid ancestor_oid, our_oid, their_oid;
+ const git_index_reuc_entry *reuc;
+
+ git_oid_fromstr(&ancestor_oid, ONE_ANCESTOR_OID);
+ git_oid_fromstr(&our_oid, ONE_OUR_OID);
+ git_oid_fromstr(&their_oid, ONE_THEIR_OID);
+
+ cl_git_pass(git_index_reuc_add(repo_index, "newfile.txt",
+ 0100644, &ancestor_oid,
+ 0100644, &our_oid,
+ 0100644, &their_oid));
+
+ cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "newfile.txt"));
+
+ cl_assert_equal_s("newfile.txt", reuc->path);
+ cl_assert(reuc->mode[0] == 0100644);
+ cl_assert(reuc->mode[1] == 0100644);
+ cl_assert(reuc->mode[2] == 0100644);
+ cl_assert(git_oid_cmp(&reuc->oid[0], &ancestor_oid) == 0);
+ cl_assert(git_oid_cmp(&reuc->oid[1], &our_oid) == 0);
+ cl_assert(git_oid_cmp(&reuc->oid[2], &their_oid) == 0);
+}
+
+void test_index_reuc__add_no_ancestor(void)
+{
+ git_oid ancestor_oid, our_oid, their_oid;
+ const git_index_reuc_entry *reuc;
+
+ memset(&ancestor_oid, 0x0, sizeof(git_oid));
+ git_oid_fromstr(&our_oid, ONE_OUR_OID);
+ git_oid_fromstr(&their_oid, ONE_THEIR_OID);
+
+ cl_git_pass(git_index_reuc_add(repo_index, "newfile.txt",
+ 0, NULL,
+ 0100644, &our_oid,
+ 0100644, &their_oid));
+
+ cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "newfile.txt"));
+
+ cl_assert_equal_s("newfile.txt", reuc->path);
+ cl_assert(reuc->mode[0] == 0);
+ cl_assert(reuc->mode[1] == 0100644);
+ cl_assert(reuc->mode[2] == 0100644);
+ cl_assert(git_oid_cmp(&reuc->oid[0], &ancestor_oid) == 0);
+ cl_assert(git_oid_cmp(&reuc->oid[1], &our_oid) == 0);
+ cl_assert(git_oid_cmp(&reuc->oid[2], &their_oid) == 0);
+}
+
+void test_index_reuc__read_bypath(void)
+{
+ const git_index_reuc_entry *reuc;
+ git_oid oid;
+
+ cl_assert_equal_i(2, git_index_reuc_entrycount(repo_index));
+
+ cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "two.txt"));
+
+ cl_assert_equal_s("two.txt", reuc->path);
+ cl_assert(reuc->mode[0] == 0100644);
+ cl_assert(reuc->mode[1] == 0100644);
+ cl_assert(reuc->mode[2] == 0100644);
+ git_oid_fromstr(&oid, TWO_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
+ git_oid_fromstr(&oid, TWO_OUR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
+ git_oid_fromstr(&oid, TWO_THEIR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
+
+ cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "one.txt"));
+
+ cl_assert_equal_s("one.txt", reuc->path);
+ cl_assert(reuc->mode[0] == 0100644);
+ cl_assert(reuc->mode[1] == 0100644);
+ cl_assert(reuc->mode[2] == 0100644);
+ git_oid_fromstr(&oid, ONE_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
+ git_oid_fromstr(&oid, ONE_OUR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
+ git_oid_fromstr(&oid, ONE_THEIR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
+}
+
+void test_index_reuc__ignore_case(void)
+{
+ const git_index_reuc_entry *reuc;
+ git_oid oid;
+ int index_caps;
+
+ index_caps = git_index_caps(repo_index);
+
+ index_caps &= ~GIT_INDEXCAP_IGNORE_CASE;
+ cl_git_pass(git_index_set_caps(repo_index, index_caps));
+
+ cl_assert(!git_index_reuc_get_bypath(repo_index, "TWO.txt"));
+
+ index_caps |= GIT_INDEXCAP_IGNORE_CASE;
+ cl_git_pass(git_index_set_caps(repo_index, index_caps));
+
+ cl_assert_equal_i(2, git_index_reuc_entrycount(repo_index));
+
+ cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "TWO.txt"));
+
+ cl_assert_equal_s("two.txt", reuc->path);
+ cl_assert(reuc->mode[0] == 0100644);
+ cl_assert(reuc->mode[1] == 0100644);
+ cl_assert(reuc->mode[2] == 0100644);
+ git_oid_fromstr(&oid, TWO_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
+ git_oid_fromstr(&oid, TWO_OUR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
+ git_oid_fromstr(&oid, TWO_THEIR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
+}
+
+void test_index_reuc__read_byindex(void)
+{
+ const git_index_reuc_entry *reuc;
+ git_oid oid;
+
+ cl_assert_equal_i(2, git_index_reuc_entrycount(repo_index));
+
+ cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 0));
+
+ cl_assert_equal_s("one.txt", reuc->path);
+ cl_assert(reuc->mode[0] == 0100644);
+ cl_assert(reuc->mode[1] == 0100644);
+ cl_assert(reuc->mode[2] == 0100644);
+ git_oid_fromstr(&oid, ONE_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
+ git_oid_fromstr(&oid, ONE_OUR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
+ git_oid_fromstr(&oid, ONE_THEIR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
+
+ cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 1));
+
+ cl_assert_equal_s("two.txt", reuc->path);
+ cl_assert(reuc->mode[0] == 0100644);
+ cl_assert(reuc->mode[1] == 0100644);
+ cl_assert(reuc->mode[2] == 0100644);
+ git_oid_fromstr(&oid, TWO_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
+ git_oid_fromstr(&oid, TWO_OUR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
+ git_oid_fromstr(&oid, TWO_THEIR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
+}
+
+void test_index_reuc__updates_existing(void)
+{
+ const git_index_reuc_entry *reuc;
+ git_oid ancestor_oid, our_oid, their_oid, oid;
+ int index_caps;
+
+ git_index_clear(repo_index);
+
+ index_caps = git_index_caps(repo_index);
+
+ index_caps |= GIT_INDEXCAP_IGNORE_CASE;
+ cl_git_pass(git_index_set_caps(repo_index, index_caps));
+
+ git_oid_fromstr(&ancestor_oid, TWO_ANCESTOR_OID);
+ git_oid_fromstr(&our_oid, TWO_OUR_OID);
+ git_oid_fromstr(&their_oid, TWO_THEIR_OID);
+
+ cl_git_pass(git_index_reuc_add(repo_index, "two.txt",
+ 0100644, &ancestor_oid,
+ 0100644, &our_oid,
+ 0100644, &their_oid));
+
+ cl_git_pass(git_index_reuc_add(repo_index, "TWO.txt",
+ 0100644, &our_oid,
+ 0100644, &their_oid,
+ 0100644, &ancestor_oid));
+
+ cl_assert_equal_i(1, git_index_reuc_entrycount(repo_index));
+
+ cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 0));
+
+ cl_assert_equal_s("TWO.txt", reuc->path);
+ git_oid_fromstr(&oid, TWO_OUR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
+ git_oid_fromstr(&oid, TWO_THEIR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
+ git_oid_fromstr(&oid, TWO_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
+}
+
+void test_index_reuc__remove(void)
+{
+ git_oid oid;
+ const git_index_reuc_entry *reuc;
+
+ cl_assert_equal_i(2, git_index_reuc_entrycount(repo_index));
+
+ cl_git_pass(git_index_reuc_remove(repo_index, 0));
+ cl_git_fail(git_index_reuc_remove(repo_index, 1));
+
+ cl_assert_equal_i(1, git_index_reuc_entrycount(repo_index));
+
+ cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 0));
+
+ cl_assert_equal_s("two.txt", reuc->path);
+ cl_assert(reuc->mode[0] == 0100644);
+ cl_assert(reuc->mode[1] == 0100644);
+ cl_assert(reuc->mode[2] == 0100644);
+ git_oid_fromstr(&oid, TWO_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0);
+ git_oid_fromstr(&oid, TWO_OUR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0);
+ git_oid_fromstr(&oid, TWO_THEIR_OID);
+ cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0);
+}
+
+void test_index_reuc__write(void)
+{
+ git_oid ancestor_oid, our_oid, their_oid;
+ const git_index_reuc_entry *reuc;
+
+ git_index_clear(repo_index);
+
+ /* Write out of order to ensure sorting is correct */
+ git_oid_fromstr(&ancestor_oid, TWO_ANCESTOR_OID);
+ git_oid_fromstr(&our_oid, TWO_OUR_OID);
+ git_oid_fromstr(&their_oid, TWO_THEIR_OID);
+
+ cl_git_pass(git_index_reuc_add(repo_index, "two.txt",
+ 0100644, &ancestor_oid,
+ 0100644, &our_oid,
+ 0100644, &their_oid));
+
+ git_oid_fromstr(&ancestor_oid, ONE_ANCESTOR_OID);
+ git_oid_fromstr(&our_oid, ONE_OUR_OID);
+ git_oid_fromstr(&their_oid, ONE_THEIR_OID);
+
+ cl_git_pass(git_index_reuc_add(repo_index, "one.txt",
+ 0100644, &ancestor_oid,
+ 0100644, &our_oid,
+ 0100644, &their_oid));
+
+ cl_git_pass(git_index_write(repo_index));
+ cl_assert_equal_i(2, git_index_reuc_entrycount(repo_index));
+
+ /* ensure sort order was round-tripped correct */
+ cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 0));
+ cl_assert_equal_s("one.txt", reuc->path);
+
+ cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 1));
+ cl_assert_equal_s("two.txt", reuc->path);
+}
+
+static int reuc_entry_exists(void)
+{
+ return (git_index_reuc_get_bypath(repo_index, "newfile.txt") != NULL);
+}
+
+void test_index_reuc__cleaned_on_reset_hard(void)
+{
+ git_object *target;
+
+ retrieve_target_from_oid(&target, repo, "3a34580a35add43a4cf361e8e9a30060a905c876");
+
+ test_index_reuc__add();
+ cl_git_pass(git_reset(repo, target, GIT_RESET_HARD));
+ cl_assert(reuc_entry_exists() == false);
+
+ git_object_free(target);
+}
+
+void test_index_reuc__cleaned_on_reset_mixed(void)
+{
+ git_object *target;
+
+ retrieve_target_from_oid(&target, repo, "3a34580a35add43a4cf361e8e9a30060a905c876");
+
+ test_index_reuc__add();
+ cl_git_pass(git_reset(repo, target, GIT_RESET_MIXED));
+ cl_assert(reuc_entry_exists() == false);
+
+ git_object_free(target);
+}
+
+void test_index_reuc__retained_on_reset_soft(void)
+{
+ git_object *target;
+
+ retrieve_target_from_oid(&target, repo, "3a34580a35add43a4cf361e8e9a30060a905c876");
+
+ git_reset(repo, target, GIT_RESET_HARD);
+
+ test_index_reuc__add();
+ cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT));
+ cl_assert(reuc_entry_exists() == true);
+
+ git_object_free(target);
+}
+
+void test_index_reuc__cleaned_on_checkout_tree(void)
+{
+ git_oid oid;
+ git_object *obj;
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY;
+
+ test_index_reuc__add();
+ git_reference_name_to_id(&oid, repo, "refs/heads/master");
+ git_object_lookup(&obj, repo, &oid, GIT_OBJ_ANY);
+ git_checkout_tree(repo, obj, &opts);
+ cl_assert(reuc_entry_exists() == false);
+
+ git_object_free(obj);
+}
+
+void test_index_reuc__cleaned_on_checkout_head(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY;
+
+ test_index_reuc__add();
+ git_checkout_head(repo, &opts);
+ cl_assert(reuc_entry_exists() == false);
+}
+
+void test_index_reuc__retained_on_checkout_index(void)
+{
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY;
+
+ test_index_reuc__add();
+ git_checkout_index(repo, repo_index, &opts);
+ cl_assert(reuc_entry_exists() == true);
+}
diff --git a/tests-clar/index/stage.c b/tests/index/stage.c
index 58dc1fb5e..58dc1fb5e 100644
--- a/tests-clar/index/stage.c
+++ b/tests/index/stage.c
diff --git a/tests/index/tests.c b/tests/index/tests.c
new file mode 100644
index 000000000..5a05bd8a9
--- /dev/null
+++ b/tests/index/tests.c
@@ -0,0 +1,545 @@
+#include "clar_libgit2.h"
+#include "index.h"
+
+static const size_t index_entry_count = 109;
+static const size_t index_entry_count_2 = 1437;
+#define TEST_INDEX_PATH cl_fixture("testrepo.git/index")
+#define TEST_INDEX2_PATH cl_fixture("gitgit.index")
+#define TEST_INDEXBIG_PATH cl_fixture("big.index")
+#define TEST_INDEXBAD_PATH cl_fixture("bad.index")
+
+
+/* Suite data */
+struct test_entry {
+ size_t index;
+ char path[128];
+ git_off_t file_size;
+ git_time_t mtime;
+};
+
+static struct test_entry test_entries[] = {
+ {4, "Makefile", 5064, 0x4C3F7F33},
+ {62, "tests/Makefile", 2631, 0x4C3F7F33},
+ {36, "src/index.c", 10014, 0x4C43368D},
+ {6, "git.git-authors", 2709, 0x4C3F7F33},
+ {48, "src/revobject.h", 1448, 0x4C3F7FE2}
+};
+
+/* Helpers */
+static void copy_file(const char *src, const char *dst)
+{
+ git_buf source_buf = GIT_BUF_INIT;
+ git_file dst_fd;
+
+ cl_git_pass(git_futils_readbuffer(&source_buf, src));
+
+ dst_fd = git_futils_creat_withpath(dst, 0777, 0666); /* -V536 */
+ if (dst_fd < 0)
+ goto cleanup;
+
+ cl_git_pass(p_write(dst_fd, source_buf.ptr, source_buf.size));
+
+cleanup:
+ git_buf_free(&source_buf);
+ p_close(dst_fd);
+}
+
+static void files_are_equal(const char *a, const char *b)
+{
+ git_buf buf_a = GIT_BUF_INIT;
+ git_buf buf_b = GIT_BUF_INIT;
+ int pass;
+
+ if (git_futils_readbuffer(&buf_a, a) < 0)
+ cl_assert(0);
+
+ if (git_futils_readbuffer(&buf_b, b) < 0) {
+ git_buf_free(&buf_a);
+ cl_assert(0);
+ }
+
+ pass = (buf_a.size == buf_b.size && !memcmp(buf_a.ptr, buf_b.ptr, buf_a.size));
+
+ git_buf_free(&buf_a);
+ git_buf_free(&buf_b);
+
+ cl_assert(pass);
+}
+
+
+/* Fixture setup and teardown */
+void test_index_tests__initialize(void)
+{
+}
+
+void test_index_tests__empty_index(void)
+{
+ git_index *index;
+
+ cl_git_pass(git_index_open(&index, "in-memory-index"));
+ cl_assert(index->on_disk == 0);
+
+ cl_assert(git_index_entrycount(index) == 0);
+ cl_assert(index->entries.sorted);
+
+ git_index_free(index);
+}
+
+void test_index_tests__default_test_index(void)
+{
+ git_index *index;
+ unsigned int i;
+ git_index_entry **entries;
+
+ cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
+ cl_assert(index->on_disk);
+
+ cl_assert(git_index_entrycount(index) == index_entry_count);
+ cl_assert(index->entries.sorted);
+
+ entries = (git_index_entry **)index->entries.contents;
+
+ for (i = 0; i < ARRAY_SIZE(test_entries); ++i) {
+ git_index_entry *e = entries[test_entries[i].index];
+
+ cl_assert_equal_s(e->path, test_entries[i].path);
+ cl_assert(e->mtime.seconds == test_entries[i].mtime);
+ cl_assert(e->file_size == test_entries[i].file_size);
+ }
+
+ git_index_free(index);
+}
+
+void test_index_tests__gitgit_index(void)
+{
+ git_index *index;
+
+ cl_git_pass(git_index_open(&index, TEST_INDEX2_PATH));
+ cl_assert(index->on_disk);
+
+ cl_assert(git_index_entrycount(index) == index_entry_count_2);
+ cl_assert(index->entries.sorted);
+ cl_assert(index->tree != NULL);
+
+ git_index_free(index);
+}
+
+void test_index_tests__find_in_existing(void)
+{
+ git_index *index;
+ unsigned int i;
+
+ cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
+
+ for (i = 0; i < ARRAY_SIZE(test_entries); ++i) {
+ size_t idx;
+
+ cl_assert(!git_index_find(&idx, index, test_entries[i].path));
+ cl_assert(idx == test_entries[i].index);
+ }
+
+ git_index_free(index);
+}
+
+void test_index_tests__find_in_empty(void)
+{
+ git_index *index;
+ unsigned int i;
+
+ cl_git_pass(git_index_open(&index, "fake-index"));
+
+ for (i = 0; i < ARRAY_SIZE(test_entries); ++i) {
+ cl_assert(GIT_ENOTFOUND == git_index_find(NULL, index, test_entries[i].path));
+ }
+
+ git_index_free(index);
+}
+
+void test_index_tests__write(void)
+{
+ git_index *index;
+
+ copy_file(TEST_INDEXBIG_PATH, "index_rewrite");
+
+ cl_git_pass(git_index_open(&index, "index_rewrite"));
+ cl_assert(index->on_disk);
+
+ cl_git_pass(git_index_write(index));
+ files_are_equal(TEST_INDEXBIG_PATH, "index_rewrite");
+
+ git_index_free(index);
+
+ p_unlink("index_rewrite");
+}
+
+void test_index_tests__sort0(void)
+{
+ /* sort the entires in an index */
+
+ /*
+ * TODO: This no longer applies:
+ * index sorting in Git uses some specific changes to the way
+ * directories are sorted.
+ *
+ * We need to specificially check for this by creating a new
+ * index, adding entries in random order and then
+ * checking for consistency
+ */
+}
+
+void test_index_tests__sort1(void)
+{
+ /* sort the entires in an empty index */
+ git_index *index;
+
+ cl_git_pass(git_index_open(&index, "fake-index"));
+
+ /* FIXME: this test is slightly dumb */
+ cl_assert(index->entries.sorted);
+
+ git_index_free(index);
+}
+
+static void cleanup_myrepo(void *opaque)
+{
+ GIT_UNUSED(opaque);
+ cl_fixture_cleanup("myrepo");
+}
+
+void test_index_tests__add(void)
+{
+ git_index *index;
+ git_filebuf file = GIT_FILEBUF_INIT;
+ git_repository *repo;
+ const git_index_entry *entry;
+ git_oid id1;
+
+ cl_set_cleanup(&cleanup_myrepo, NULL);
+
+ /* Intialize a new repository */
+ cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
+
+ /* Ensure we're the only guy in the room */
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(git_index_entrycount(index) == 0);
+
+ /* Create a new file in the working directory */
+ cl_git_pass(git_futils_mkpath2file("myrepo/test.txt", 0777));
+ cl_git_pass(git_filebuf_open(&file, "myrepo/test.txt", 0, 0666));
+ cl_git_pass(git_filebuf_write(&file, "hey there\n", 10));
+ cl_git_pass(git_filebuf_commit(&file));
+
+ /* Store the expected hash of the file/blob
+ * This has been generated by executing the following
+ * $ echo "hey there" | git hash-object --stdin
+ */
+ cl_git_pass(git_oid_fromstr(&id1, "a8233120f6ad708f843d861ce2b7228ec4e3dec6"));
+
+ /* Add the new file to the index */
+ cl_git_pass(git_index_add_bypath(index, "test.txt"));
+
+ /* Wow... it worked! */
+ cl_assert(git_index_entrycount(index) == 1);
+ entry = git_index_get_byindex(index, 0);
+
+ /* And the built-in hashing mechanism worked as expected */
+ cl_assert(git_oid_cmp(&id1, &entry->oid) == 0);
+
+ /* Test access by path instead of index */
+ cl_assert((entry = git_index_get_bypath(index, "test.txt", 0)) != NULL);
+ cl_assert(git_oid_cmp(&id1, &entry->oid) == 0);
+
+ git_index_free(index);
+ git_repository_free(repo);
+}
+
+static void cleanup_1397(void *opaque)
+{
+ GIT_UNUSED(opaque);
+ cl_git_sandbox_cleanup();
+}
+
+void test_index_tests__add_issue_1397(void)
+{
+ git_index *index;
+ git_repository *repo;
+ const git_index_entry *entry;
+ git_oid id1;
+
+ cl_set_cleanup(&cleanup_1397, NULL);
+
+ repo = cl_git_sandbox_init("issue_1397");
+
+ cl_repo_set_bool(repo, "core.autocrlf", true);
+
+ /* Ensure we're the only guy in the room */
+ cl_git_pass(git_repository_index(&index, repo));
+
+ /* Store the expected hash of the file/blob
+ * This has been generated by executing the following
+ * $ git hash-object crlf_file.txt
+ */
+ cl_git_pass(git_oid_fromstr(&id1, "8312e0889a9cbab77c732b6bc39b51a683e3a318"));
+
+ /* Make sure the initial SHA-1 is correct */
+ cl_assert((entry = git_index_get_bypath(index, "crlf_file.txt", 0)) != NULL);
+ cl_assert_(git_oid_cmp(&id1, &entry->oid) == 0, "first oid check");
+
+ /* Update the index */
+ cl_git_pass(git_index_add_bypath(index, "crlf_file.txt"));
+
+ /* Check the new SHA-1 */
+ cl_assert((entry = git_index_get_bypath(index, "crlf_file.txt", 0)) != NULL);
+ cl_assert_(git_oid_cmp(&id1, &entry->oid) == 0, "second oid check");
+
+ git_index_free(index);
+}
+
+void test_index_tests__add_bypath_to_a_bare_repository_returns_EBAREPO(void)
+{
+ git_repository *bare_repo;
+ git_index *index;
+
+ cl_git_pass(git_repository_open(&bare_repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_repository_index(&index, bare_repo));
+
+ cl_assert_equal_i(GIT_EBAREREPO, git_index_add_bypath(index, "test.txt"));
+
+ git_index_free(index);
+ git_repository_free(bare_repo);
+}
+
+/* Test that writing an invalid filename fails */
+void test_index_tests__write_invalid_filename(void)
+{
+ git_repository *repo;
+ git_index *index;
+ git_oid expected;
+
+ p_mkdir("read_tree", 0700);
+
+ cl_git_pass(git_repository_init(&repo, "./read_tree", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+
+ cl_assert(git_index_entrycount(index) == 0);
+
+ cl_git_mkfile("./read_tree/.git/hello", NULL);
+
+ cl_git_pass(git_index_add_bypath(index, ".git/hello"));
+
+ /* write-tree */
+ cl_git_fail(git_index_write_tree(&expected, index));
+
+ git_index_free(index);
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("read_tree");
+}
+
+void test_index_tests__remove_entry(void)
+{
+ git_repository *repo;
+ git_index *index;
+
+ p_mkdir("index_test", 0770);
+
+ cl_git_pass(git_repository_init(&repo, "index_test", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(git_index_entrycount(index) == 0);
+
+ cl_git_mkfile("index_test/hello", NULL);
+ cl_git_pass(git_index_add_bypath(index, "hello"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_index_read(index, true)); /* reload */
+ cl_assert(git_index_entrycount(index) == 1);
+ cl_assert(git_index_get_bypath(index, "hello", 0) != NULL);
+
+ cl_git_pass(git_index_remove(index, "hello", 0));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_index_read(index, true)); /* reload */
+ cl_assert(git_index_entrycount(index) == 0);
+ cl_assert(git_index_get_bypath(index, "hello", 0) == NULL);
+
+ git_index_free(index);
+ git_repository_free(repo);
+ cl_fixture_cleanup("index_test");
+}
+
+void test_index_tests__remove_directory(void)
+{
+ git_repository *repo;
+ git_index *index;
+
+ p_mkdir("index_test", 0770);
+
+ cl_git_pass(git_repository_init(&repo, "index_test", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert_equal_i(0, (int)git_index_entrycount(index));
+
+ p_mkdir("index_test/a", 0770);
+ cl_git_mkfile("index_test/a/1.txt", NULL);
+ cl_git_mkfile("index_test/a/2.txt", NULL);
+ cl_git_mkfile("index_test/a/3.txt", NULL);
+ cl_git_mkfile("index_test/b.txt", NULL);
+
+ cl_git_pass(git_index_add_bypath(index, "a/1.txt"));
+ cl_git_pass(git_index_add_bypath(index, "a/2.txt"));
+ cl_git_pass(git_index_add_bypath(index, "a/3.txt"));
+ cl_git_pass(git_index_add_bypath(index, "b.txt"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_index_read(index, true)); /* reload */
+ cl_assert_equal_i(4, (int)git_index_entrycount(index));
+ cl_assert(git_index_get_bypath(index, "a/1.txt", 0) != NULL);
+ cl_assert(git_index_get_bypath(index, "a/2.txt", 0) != NULL);
+ cl_assert(git_index_get_bypath(index, "b.txt", 0) != NULL);
+
+ cl_git_pass(git_index_remove(index, "a/1.txt", 0));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_index_read(index, true)); /* reload */
+ cl_assert_equal_i(3, (int)git_index_entrycount(index));
+ cl_assert(git_index_get_bypath(index, "a/1.txt", 0) == NULL);
+ cl_assert(git_index_get_bypath(index, "a/2.txt", 0) != NULL);
+ cl_assert(git_index_get_bypath(index, "b.txt", 0) != NULL);
+
+ cl_git_pass(git_index_remove_directory(index, "a", 0));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_index_read(index, true)); /* reload */
+ cl_assert_equal_i(1, (int)git_index_entrycount(index));
+ cl_assert(git_index_get_bypath(index, "a/1.txt", 0) == NULL);
+ cl_assert(git_index_get_bypath(index, "a/2.txt", 0) == NULL);
+ cl_assert(git_index_get_bypath(index, "b.txt", 0) != NULL);
+
+ git_index_free(index);
+ git_repository_free(repo);
+ cl_fixture_cleanup("index_test");
+}
+
+void test_index_tests__preserves_case(void)
+{
+ git_repository *repo;
+ git_index *index;
+ const git_index_entry *entry;
+ int index_caps;
+
+ cl_set_cleanup(&cleanup_myrepo, NULL);
+
+ cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+
+ index_caps = git_index_caps(index);
+
+ cl_git_rewritefile("myrepo/test.txt", "hey there\n");
+ cl_git_pass(git_index_add_bypath(index, "test.txt"));
+
+ cl_git_pass(p_rename("myrepo/test.txt", "myrepo/TEST.txt"));
+ cl_git_rewritefile("myrepo/TEST.txt", "hello again\n");
+ cl_git_pass(git_index_add_bypath(index, "TEST.txt"));
+
+ if (index_caps & GIT_INDEXCAP_IGNORE_CASE)
+ cl_assert_equal_i(1, (int)git_index_entrycount(index));
+ else
+ cl_assert_equal_i(2, (int)git_index_entrycount(index));
+
+ /* Test access by path instead of index */
+ cl_assert((entry = git_index_get_bypath(index, "test.txt", 0)) != NULL);
+ /* The path should *not* have changed without an explicit remove */
+ cl_assert(git__strcmp(entry->path, "test.txt") == 0);
+
+ cl_assert((entry = git_index_get_bypath(index, "TEST.txt", 0)) != NULL);
+ if (index_caps & GIT_INDEXCAP_IGNORE_CASE)
+ /* The path should *not* have changed without an explicit remove */
+ cl_assert(git__strcmp(entry->path, "test.txt") == 0);
+ else
+ cl_assert(git__strcmp(entry->path, "TEST.txt") == 0);
+
+ git_index_free(index);
+ git_repository_free(repo);
+}
+
+void test_index_tests__elocked(void)
+{
+ git_repository *repo;
+ git_index *index;
+ git_filebuf file = GIT_FILEBUF_INIT;
+ const git_error *err;
+ int error;
+
+ cl_set_cleanup(&cleanup_myrepo, NULL);
+
+ cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+
+ /* Lock the index file so we fail to lock it */
+ cl_git_pass(git_filebuf_open(&file, index->index_file_path, 0, 0666));
+ error = git_index_write(index);
+ cl_assert_equal_i(GIT_ELOCKED, error);
+
+ err = giterr_last();
+ cl_assert_equal_i(err->klass, GITERR_INDEX);
+
+ git_filebuf_cleanup(&file);
+ git_index_free(index);
+ git_repository_free(repo);
+}
+
+void test_index_tests__reload_from_disk(void)
+{
+ git_repository *repo;
+ git_index *read_index;
+ git_index *write_index;
+
+ cl_set_cleanup(&cleanup_myrepo, NULL);
+
+ cl_git_pass(git_futils_mkdir("./myrepo", NULL, 0777, GIT_MKDIR_PATH));
+ cl_git_mkfile("./myrepo/a.txt", "a\n");
+ cl_git_mkfile("./myrepo/b.txt", "b\n");
+
+ cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
+ cl_git_pass(git_repository_index(&write_index, repo));
+ cl_assert_equal_i(false, write_index->on_disk);
+
+ cl_git_pass(git_index_open(&read_index, write_index->index_file_path));
+ cl_assert_equal_i(false, read_index->on_disk);
+
+ /* Stage two new files agaisnt the write_index */
+ cl_git_pass(git_index_add_bypath(write_index, "a.txt"));
+ cl_git_pass(git_index_add_bypath(write_index, "b.txt"));
+
+ cl_assert_equal_sz(2, git_index_entrycount(write_index));
+
+ /* Persist the index changes to disk */
+ cl_git_pass(git_index_write(write_index));
+ cl_assert_equal_i(true, write_index->on_disk);
+
+ /* Sync the changes back into the read_index */
+ cl_assert_equal_sz(0, git_index_entrycount(read_index));
+
+ cl_git_pass(git_index_read(read_index, true));
+ cl_assert_equal_i(true, read_index->on_disk);
+
+ cl_assert_equal_sz(2, git_index_entrycount(read_index));
+
+ /* Remove the index file from the filesystem */
+ cl_git_pass(p_unlink(write_index->index_file_path));
+
+ /* Sync the changes back into the read_index */
+ cl_git_pass(git_index_read(read_index, true));
+ cl_assert_equal_i(false, read_index->on_disk);
+ cl_assert_equal_sz(0, git_index_entrycount(read_index));
+
+ git_index_free(read_index);
+ git_index_free(write_index);
+ git_repository_free(repo);
+}
+
+void test_index_tests__corrupted_extension(void)
+{
+ git_index *index;
+
+ cl_git_fail_with(git_index_open(&index, TEST_INDEXBAD_PATH), GIT_ERROR);
+}
diff --git a/tests-clar/main.c b/tests/main.c
index 6b498939d..6b498939d 100644
--- a/tests-clar/main.c
+++ b/tests/main.c
diff --git a/tests/merge/merge_helpers.c b/tests/merge/merge_helpers.c
new file mode 100644
index 000000000..43619be0d
--- /dev/null
+++ b/tests/merge/merge_helpers.c
@@ -0,0 +1,333 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+#include "refs.h"
+#include "tree.h"
+#include "merge_helpers.h"
+#include "merge.h"
+#include "git2/merge.h"
+#include "git2/sys/index.h"
+
+int merge_trees_from_branches(
+ git_index **index, git_repository *repo,
+ const char *ours_name, const char *theirs_name,
+ git_merge_tree_opts *opts)
+{
+ git_commit *our_commit, *their_commit, *ancestor_commit = NULL;
+ git_tree *our_tree, *their_tree, *ancestor_tree = NULL;
+ git_oid our_oid, their_oid, ancestor_oid;
+ git_buf branch_buf = GIT_BUF_INIT;
+ int error;
+
+ git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, ours_name);
+ cl_git_pass(git_reference_name_to_id(&our_oid, repo, branch_buf.ptr));
+ cl_git_pass(git_commit_lookup(&our_commit, repo, &our_oid));
+
+ git_buf_clear(&branch_buf);
+ git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, theirs_name);
+ cl_git_pass(git_reference_name_to_id(&their_oid, repo, branch_buf.ptr));
+ cl_git_pass(git_commit_lookup(&their_commit, repo, &their_oid));
+
+ error = git_merge_base(&ancestor_oid, repo, git_commit_id(our_commit), git_commit_id(their_commit));
+
+ if (error != GIT_ENOTFOUND) {
+ cl_git_pass(error);
+
+ cl_git_pass(git_commit_lookup(&ancestor_commit, repo, &ancestor_oid));
+ cl_git_pass(git_commit_tree(&ancestor_tree, ancestor_commit));
+ }
+
+ cl_git_pass(git_commit_tree(&our_tree, our_commit));
+ cl_git_pass(git_commit_tree(&their_tree, their_commit));
+
+ cl_git_pass(git_merge_trees(index, repo, ancestor_tree, our_tree, their_tree, opts));
+
+ git_buf_free(&branch_buf);
+ git_tree_free(our_tree);
+ git_tree_free(their_tree);
+ git_tree_free(ancestor_tree);
+ git_commit_free(our_commit);
+ git_commit_free(their_commit);
+ git_commit_free(ancestor_commit);
+
+ return 0;
+}
+
+int merge_branches(git_merge_result **result, git_repository *repo, const char *ours_branch, const char *theirs_branch, git_merge_opts *opts)
+{
+ git_reference *head_ref, *theirs_ref;
+ git_merge_head *theirs_head;
+ git_checkout_opts head_checkout_opts = GIT_CHECKOUT_OPTS_INIT;
+
+ head_checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_reference_symbolic_create(&head_ref, repo, "HEAD", ours_branch, 1));
+ cl_git_pass(git_checkout_head(repo, &head_checkout_opts));
+
+ cl_git_pass(git_reference_lookup(&theirs_ref, repo, theirs_branch));
+ cl_git_pass(git_merge_head_from_ref(&theirs_head, repo, theirs_ref));
+
+ cl_git_pass(git_merge(result, repo, (const git_merge_head **)&theirs_head, 1, opts));
+
+ git_reference_free(head_ref);
+ git_reference_free(theirs_ref);
+ git_merge_head_free(theirs_head);
+
+ return 0;
+}
+
+void merge__dump_index_entries(git_vector *index_entries)
+{
+ size_t i;
+ const git_index_entry *index_entry;
+
+ printf ("\nINDEX [%d]:\n", (int)index_entries->length);
+ for (i = 0; i < index_entries->length; i++) {
+ index_entry = index_entries->contents[i];
+
+ printf("%o ", index_entry->mode);
+ printf("%s ", git_oid_allocfmt(&index_entry->oid));
+ printf("%d ", git_index_entry_stage(index_entry));
+ printf("%s ", index_entry->path);
+ printf("\n");
+ }
+ printf("\n");
+}
+
+void merge__dump_names(git_index *index)
+{
+ size_t i;
+ const git_index_name_entry *conflict_name;
+
+ for (i = 0; i < git_index_name_entrycount(index); i++) {
+ conflict_name = git_index_name_get_byindex(index, i);
+
+ printf("%s %s %s\n", conflict_name->ancestor, conflict_name->ours, conflict_name->theirs);
+ }
+ printf("\n");
+}
+
+void merge__dump_reuc(git_index *index)
+{
+ size_t i;
+ const git_index_reuc_entry *reuc;
+
+ printf ("\nREUC:\n");
+ for (i = 0; i < git_index_reuc_entrycount(index); i++) {
+ reuc = git_index_reuc_get_byindex(index, i);
+
+ printf("%s ", reuc->path);
+ printf("%o ", reuc->mode[0]);
+ printf("%s\n", git_oid_allocfmt(&reuc->oid[0]));
+ printf(" %o ", reuc->mode[1]);
+ printf(" %s\n", git_oid_allocfmt(&reuc->oid[1]));
+ printf(" %o ", reuc->mode[2]);
+ printf(" %s ", git_oid_allocfmt(&reuc->oid[2]));
+ printf("\n");
+ }
+ printf("\n");
+}
+
+static int index_entry_eq_merge_index_entry(const struct merge_index_entry *expected, const git_index_entry *actual)
+{
+ git_oid expected_oid;
+ bool test_oid;
+
+ if (strlen(expected->oid_str) != 0) {
+ cl_git_pass(git_oid_fromstr(&expected_oid, expected->oid_str));
+ test_oid = 1;
+ } else
+ test_oid = 0;
+
+ if (actual->mode != expected->mode ||
+ (test_oid && git_oid_cmp(&actual->oid, &expected_oid) != 0) ||
+ git_index_entry_stage(actual) != expected->stage)
+ return 0;
+
+ if (actual->mode == 0 && (actual->path != NULL || strlen(expected->path) > 0))
+ return 0;
+
+ if (actual->mode != 0 && (strcmp(actual->path, expected->path) != 0))
+ return 0;
+
+ return 1;
+}
+
+static int name_entry_eq(const char *expected, const char *actual)
+{
+ if (strlen(expected) == 0)
+ return (actual == NULL) ? 1 : 0;
+
+ return (strcmp(expected, actual) == 0) ? 1 : 0;
+}
+
+static int name_entry_eq_merge_name_entry(const struct merge_name_entry *expected, const git_index_name_entry *actual)
+{
+ if (name_entry_eq(expected->ancestor_path, actual->ancestor) == 0 ||
+ name_entry_eq(expected->our_path, actual->ours) == 0 ||
+ name_entry_eq(expected->their_path, actual->theirs) == 0)
+ return 0;
+
+ return 1;
+}
+
+static int index_conflict_data_eq_merge_diff(const struct merge_index_conflict_data *expected, git_merge_diff *actual)
+{
+ if (!index_entry_eq_merge_index_entry(&expected->ancestor.entry, &actual->ancestor_entry) ||
+ !index_entry_eq_merge_index_entry(&expected->ours.entry, &actual->our_entry) ||
+ !index_entry_eq_merge_index_entry(&expected->theirs.entry, &actual->their_entry))
+ return 0;
+
+ if (expected->ours.status != actual->our_status ||
+ expected->theirs.status != actual->their_status)
+ return 0;
+
+ return 1;
+}
+
+int merge_test_merge_conflicts(git_vector *conflicts, const struct merge_index_conflict_data expected[], size_t expected_len)
+{
+ git_merge_diff *actual;
+ size_t i;
+
+ if (conflicts->length != expected_len)
+ return 0;
+
+ for (i = 0; i < expected_len; i++) {
+ actual = conflicts->contents[i];
+
+ if (!index_conflict_data_eq_merge_diff(&expected[i], actual))
+ return 0;
+ }
+
+ return 1;
+}
+
+int merge_test_index(git_index *index, const struct merge_index_entry expected[], size_t expected_len)
+{
+ size_t i;
+ const git_index_entry *index_entry;
+
+ /*
+ dump_index_entries(&index->entries);
+ */
+
+ if (git_index_entrycount(index) != expected_len)
+ return 0;
+
+ for (i = 0; i < expected_len; i++) {
+ if ((index_entry = git_index_get_byindex(index, i)) == NULL)
+ return 0;
+
+ if (!index_entry_eq_merge_index_entry(&expected[i], index_entry))
+ return 0;
+ }
+
+ return 1;
+}
+
+int merge_test_names(git_index *index, const struct merge_name_entry expected[], size_t expected_len)
+{
+ size_t i;
+ const git_index_name_entry *name_entry;
+
+ /*
+ dump_names(index);
+ */
+
+ if (git_index_name_entrycount(index) != expected_len)
+ return 0;
+
+ for (i = 0; i < expected_len; i++) {
+ if ((name_entry = git_index_name_get_byindex(index, i)) == NULL)
+ return 0;
+
+ if (! name_entry_eq_merge_name_entry(&expected[i], name_entry))
+ return 0;
+ }
+
+ return 1;
+}
+
+int merge_test_reuc(git_index *index, const struct merge_reuc_entry expected[], size_t expected_len)
+{
+ size_t i;
+ const git_index_reuc_entry *reuc_entry;
+ git_oid expected_oid;
+
+ /*
+ dump_reuc(index);
+ */
+
+ if (git_index_reuc_entrycount(index) != expected_len)
+ return 0;
+
+ for (i = 0; i < expected_len; i++) {
+ if ((reuc_entry = git_index_reuc_get_byindex(index, i)) == NULL)
+ return 0;
+
+ if (strcmp(reuc_entry->path, expected[i].path) != 0 ||
+ reuc_entry->mode[0] != expected[i].ancestor_mode ||
+ reuc_entry->mode[1] != expected[i].our_mode ||
+ reuc_entry->mode[2] != expected[i].their_mode)
+ return 0;
+
+ if (expected[i].ancestor_mode > 0) {
+ cl_git_pass(git_oid_fromstr(&expected_oid, expected[i].ancestor_oid_str));
+
+ if (git_oid_cmp(&reuc_entry->oid[0], &expected_oid) != 0)
+ return 0;
+ }
+
+ if (expected[i].our_mode > 0) {
+ cl_git_pass(git_oid_fromstr(&expected_oid, expected[i].our_oid_str));
+
+ if (git_oid_cmp(&reuc_entry->oid[1], &expected_oid) != 0)
+ return 0;
+ }
+
+ if (expected[i].their_mode > 0) {
+ cl_git_pass(git_oid_fromstr(&expected_oid, expected[i].their_oid_str));
+
+ if (git_oid_cmp(&reuc_entry->oid[2], &expected_oid) != 0)
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+int dircount(void *payload, git_buf *pathbuf)
+{
+ int *entries = payload;
+ size_t len = git_buf_len(pathbuf);
+
+ if (len < 5 || strcmp(pathbuf->ptr + (git_buf_len(pathbuf) - 5), "/.git") != 0)
+ (*entries)++;
+
+ return 0;
+}
+
+int merge_test_workdir(git_repository *repo, const struct merge_index_entry expected[], size_t expected_len)
+{
+ size_t actual_len = 0, i;
+ git_oid actual_oid, expected_oid;
+ git_buf wd = GIT_BUF_INIT;
+
+ git_buf_puts(&wd, repo->workdir);
+ git_path_direach(&wd, 0, dircount, &actual_len);
+
+ if (actual_len != expected_len)
+ return 0;
+
+ for (i = 0; i < expected_len; i++) {
+ git_blob_create_fromworkdir(&actual_oid, repo, expected[i].path);
+ git_oid_fromstr(&expected_oid, expected[i].oid_str);
+
+ if (git_oid_cmp(&actual_oid, &expected_oid) != 0)
+ return 0;
+ }
+
+ git_buf_free(&wd);
+
+ return 1;
+}
diff --git a/tests/merge/merge_helpers.h b/tests/merge/merge_helpers.h
new file mode 100644
index 000000000..ae3274437
--- /dev/null
+++ b/tests/merge/merge_helpers.h
@@ -0,0 +1,62 @@
+#ifndef INCLUDE_cl_merge_helpers_h__
+#define INCLUDE_cl_merge_helpers_h__
+
+#include "merge.h"
+#include "git2/merge.h"
+
+struct merge_index_entry {
+ uint16_t mode;
+ char oid_str[41];
+ int stage;
+ char path[128];
+};
+
+struct merge_name_entry {
+ char ancestor_path[128];
+ char our_path[128];
+ char their_path[128];
+};
+
+struct merge_index_with_status {
+ struct merge_index_entry entry;
+ unsigned int status;
+};
+
+struct merge_reuc_entry {
+ char path[128];
+ unsigned int ancestor_mode;
+ unsigned int our_mode;
+ unsigned int their_mode;
+ char ancestor_oid_str[41];
+ char our_oid_str[41];
+ char their_oid_str[41];
+};
+
+struct merge_index_conflict_data {
+ struct merge_index_with_status ancestor;
+ struct merge_index_with_status ours;
+ struct merge_index_with_status theirs;
+ git_merge_diff_type_t change_type;
+};
+
+int merge_trees_from_branches(
+ git_index **index, git_repository *repo,
+ const char *ours_name, const char *theirs_name,
+ git_merge_tree_opts *opts);
+
+int merge_branches(git_merge_result **result, git_repository *repo,
+ const char *ours_branch, const char *theirs_branch, git_merge_opts *opts);
+
+int merge_test_diff_list(git_merge_diff_list *diff_list, const struct merge_index_entry expected[], size_t expected_len);
+
+int merge_test_merge_conflicts(git_vector *conflicts, const struct merge_index_conflict_data expected[], size_t expected_len);
+
+int merge_test_index(git_index *index, const struct merge_index_entry expected[], size_t expected_len);
+
+int merge_test_names(git_index *index, const struct merge_name_entry expected[], size_t expected_len);
+
+int merge_test_reuc(git_index *index, const struct merge_reuc_entry expected[], size_t expected_len);
+
+int merge_test_workdir(git_repository *repo, const struct merge_index_entry expected[], size_t expected_len);
+
+#endif
diff --git a/tests/merge/trees/automerge.c b/tests/merge/trees/automerge.c
new file mode 100644
index 000000000..746ce5068
--- /dev/null
+++ b/tests/merge/trees/automerge.c
@@ -0,0 +1,217 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/merge.h"
+#include "buffer.h"
+#include "merge.h"
+#include "../merge_helpers.h"
+#include "fileops.h"
+
+static git_repository *repo;
+
+#define TEST_REPO_PATH "merge-resolve"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+#define THEIRS_AUTOMERGE_BRANCH "branch"
+
+#define THEIRS_UNRELATED_BRANCH "unrelated"
+#define THEIRS_UNRELATED_OID "55b4e4687e7a0d9ca367016ed930f385d4022e6f"
+#define THEIRS_UNRELATED_PARENT "d6cf6c7741b3316826af1314042550c97ded1d50"
+
+#define OURS_DIRECTORY_FILE "df_side1"
+#define THEIRS_DIRECTORY_FILE "df_side2"
+
+/* Non-conflicting files, index entries are common to every merge operation */
+#define ADDED_IN_MASTER_INDEX_ENTRY \
+ { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" }
+#define AUTOMERGEABLE_INDEX_ENTRY \
+ { 0100644, "f2e1550a0c9e53d5811175864a29536642ae3821", 0, "automergeable.txt" }
+#define CHANGED_IN_BRANCH_INDEX_ENTRY \
+ { 0100644, "4eb04c9e79e88f6640d01ff5b25ca2a60764f216", 0, "changed-in-branch.txt" }
+#define CHANGED_IN_MASTER_INDEX_ENTRY \
+ { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" }
+#define UNCHANGED_INDEX_ENTRY \
+ { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" }
+
+/* Expected REUC entries */
+#define AUTOMERGEABLE_REUC_ENTRY \
+ { "automergeable.txt", 0100644, 0100644, 0100644, \
+ "6212c31dab5e482247d7977e4f0dd3601decf13b", \
+ "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", \
+ "058541fc37114bfc1dddf6bd6bffc7fae5c2e6fe" }
+#define CONFLICTING_REUC_ENTRY \
+ { "conflicting.txt", 0100644, 0100644, 0100644, \
+ "d427e0b2e138501a3d15cc376077a3631e15bd46", \
+ "4e886e602529caa9ab11d71f86634bd1b6e0de10", \
+ "2bd0a343aeef7a2cf0d158478966a6e587ff3863" }
+#define REMOVED_IN_BRANCH_REUC_ENTRY \
+ { "removed-in-branch.txt", 0100644, 0100644, 0, \
+ "dfe3f22baa1f6fce5447901c3086bae368de6bdd", \
+ "dfe3f22baa1f6fce5447901c3086bae368de6bdd", \
+ "" }
+#define REMOVED_IN_MASTER_REUC_ENTRY \
+ { "removed-in-master.txt", 0100644, 0, 0100644, \
+ "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", \
+ "", \
+ "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5" }
+
+#define AUTOMERGEABLE_MERGED_FILE \
+ "this file is changed in master\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is changed in branch\n"
+
+#define AUTOMERGEABLE_MERGED_FILE_CRLF \
+ "this file is changed in master\r\n" \
+ "this file is automergeable\r\n" \
+ "this file is automergeable\r\n" \
+ "this file is automergeable\r\n" \
+ "this file is automergeable\r\n" \
+ "this file is automergeable\r\n" \
+ "this file is automergeable\r\n" \
+ "this file is automergeable\r\n" \
+ "this file is changed in branch\r\n"
+
+// Fixture setup and teardown
+void test_merge_trees_automerge__initialize(void)
+{
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+}
+
+void test_merge_trees_automerge__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_merge_trees_automerge__automerge(void)
+{
+ git_index *index;
+ const git_index_entry *entry;
+ git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT;
+ git_blob *blob;
+
+ struct merge_index_entry merge_index_entries[] = {
+ ADDED_IN_MASTER_INDEX_ENTRY,
+ AUTOMERGEABLE_INDEX_ENTRY,
+ CHANGED_IN_BRANCH_INDEX_ENTRY,
+ CHANGED_IN_MASTER_INDEX_ENTRY,
+
+ { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" },
+ { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
+ { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" },
+
+ UNCHANGED_INDEX_ENTRY,
+ };
+
+ struct merge_reuc_entry merge_reuc_entries[] = {
+ AUTOMERGEABLE_REUC_ENTRY,
+ REMOVED_IN_BRANCH_REUC_ENTRY,
+ REMOVED_IN_MASTER_REUC_ENTRY
+ };
+
+ cl_git_pass(merge_trees_from_branches(&index, repo, "master", THEIRS_AUTOMERGE_BRANCH, &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 8));
+ cl_assert(merge_test_reuc(index, merge_reuc_entries, 3));
+
+ cl_assert((entry = git_index_get_bypath(index, "automergeable.txt", 0)) != NULL);
+ cl_assert(entry->file_size == strlen(AUTOMERGEABLE_MERGED_FILE));
+
+ cl_git_pass(git_object_lookup((git_object **)&blob, repo, &entry->oid, GIT_OBJ_BLOB));
+ cl_assert(memcmp(git_blob_rawcontent(blob), AUTOMERGEABLE_MERGED_FILE, (size_t)entry->file_size) == 0);
+
+ git_index_free(index);
+ git_blob_free(blob);
+}
+
+void test_merge_trees_automerge__favor_ours(void)
+{
+ git_index *index;
+ git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ ADDED_IN_MASTER_INDEX_ENTRY,
+ AUTOMERGEABLE_INDEX_ENTRY,
+ CHANGED_IN_BRANCH_INDEX_ENTRY,
+ CHANGED_IN_MASTER_INDEX_ENTRY,
+ { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt" },
+ UNCHANGED_INDEX_ENTRY,
+ };
+
+ struct merge_reuc_entry merge_reuc_entries[] = {
+ AUTOMERGEABLE_REUC_ENTRY,
+ CONFLICTING_REUC_ENTRY,
+ REMOVED_IN_BRANCH_REUC_ENTRY,
+ REMOVED_IN_MASTER_REUC_ENTRY,
+ };
+
+ opts.automerge_flags = GIT_MERGE_AUTOMERGE_FAVOR_OURS;
+
+ cl_git_pass(merge_trees_from_branches(&index, repo, "master", THEIRS_AUTOMERGE_BRANCH, &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 6));
+ cl_assert(merge_test_reuc(index, merge_reuc_entries, 4));
+
+ git_index_free(index);
+}
+
+void test_merge_trees_automerge__favor_theirs(void)
+{
+ git_index *index;
+ git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ ADDED_IN_MASTER_INDEX_ENTRY,
+ AUTOMERGEABLE_INDEX_ENTRY,
+ CHANGED_IN_BRANCH_INDEX_ENTRY,
+ CHANGED_IN_MASTER_INDEX_ENTRY,
+ { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 0, "conflicting.txt" },
+ UNCHANGED_INDEX_ENTRY,
+ };
+
+ struct merge_reuc_entry merge_reuc_entries[] = {
+ AUTOMERGEABLE_REUC_ENTRY,
+ CONFLICTING_REUC_ENTRY,
+ REMOVED_IN_BRANCH_REUC_ENTRY,
+ REMOVED_IN_MASTER_REUC_ENTRY,
+ };
+
+ opts.automerge_flags = GIT_MERGE_AUTOMERGE_FAVOR_THEIRS;
+
+ cl_git_pass(merge_trees_from_branches(&index, repo, "master", THEIRS_AUTOMERGE_BRANCH, &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 6));
+ cl_assert(merge_test_reuc(index, merge_reuc_entries, 4));
+
+ git_index_free(index);
+}
+
+void test_merge_trees_automerge__unrelated(void)
+{
+ git_index *index;
+ git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" },
+ { 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 2, "automergeable.txt" },
+ { 0100644, "d07ec190c306ec690bac349e87d01c4358e49bb2", 3, "automergeable.txt" },
+ { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt" },
+ { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" },
+ { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
+ { 0100644, "4b253da36a0ae8bfce63aeabd8c5b58429925594", 3, "conflicting.txt" },
+ { 0100644, "ef58fdd8086c243bdc81f99e379acacfd21d32d6", 0, "new-in-unrelated1.txt" },
+ { 0100644, "948ba6e701c1edab0c2d394fb7c5538334129793", 0, "new-in-unrelated2.txt" },
+ { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" },
+ { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" },
+ };
+
+ cl_git_pass(merge_trees_from_branches(&index, repo, "master", THEIRS_UNRELATED_BRANCH, &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 11));
+
+ git_index_free(index);
+}
diff --git a/tests-clar/merge/trees/modeconflict.c b/tests/merge/trees/modeconflict.c
index d858b8f66..d858b8f66 100644
--- a/tests-clar/merge/trees/modeconflict.c
+++ b/tests/merge/trees/modeconflict.c
diff --git a/tests-clar/merge/trees/renames.c b/tests/merge/trees/renames.c
index 427b6bd8f..427b6bd8f 100644
--- a/tests-clar/merge/trees/renames.c
+++ b/tests/merge/trees/renames.c
diff --git a/tests-clar/merge/trees/treediff.c b/tests/merge/trees/treediff.c
index 357859df3..357859df3 100644
--- a/tests-clar/merge/trees/treediff.c
+++ b/tests/merge/trees/treediff.c
diff --git a/tests-clar/merge/trees/trivial.c b/tests/merge/trees/trivial.c
index bfd5dfed3..bfd5dfed3 100644
--- a/tests-clar/merge/trees/trivial.c
+++ b/tests/merge/trees/trivial.c
diff --git a/tests/merge/workdir/fastforward.c b/tests/merge/workdir/fastforward.c
new file mode 100644
index 000000000..861f38354
--- /dev/null
+++ b/tests/merge/workdir/fastforward.c
@@ -0,0 +1,148 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/merge.h"
+#include "git2/sys/index.h"
+#include "merge.h"
+#include "../merge_helpers.h"
+#include "refs.h"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+#define TEST_REPO_PATH "merge-resolve"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+#define THEIRS_FASTFORWARD_BRANCH "ff_branch"
+#define THEIRS_FASTFORWARD_OID "fd89f8cffb663ac89095a0f9764902e93ceaca6a"
+
+#define THEIRS_NOFASTFORWARD_BRANCH "branch"
+#define THEIRS_NOFASTFORWARD_OID "7cb63eed597130ba4abb87b3e544b85021905520"
+
+
+// Fixture setup and teardown
+void test_merge_workdir_fastforward__initialize(void)
+{
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+ git_repository_index(&repo_index, repo);
+}
+
+void test_merge_workdir_fastforward__cleanup(void)
+{
+ git_index_free(repo_index);
+ cl_git_sandbox_cleanup();
+}
+
+static git_merge_result *merge_fastforward_branch(int flags)
+{
+ git_reference *their_ref;
+ git_merge_head *their_heads[1];
+ git_merge_result *result;
+ git_merge_opts opts = GIT_MERGE_OPTS_INIT;
+
+ opts.merge_flags = flags;
+
+ cl_git_pass(git_reference_lookup(&their_ref, repo, GIT_REFS_HEADS_DIR THEIRS_FASTFORWARD_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, their_ref));
+
+ cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, &opts));
+
+ git_merge_head_free(their_heads[0]);
+ git_reference_free(their_ref);
+
+ return result;
+}
+
+void test_merge_workdir_fastforward__fastforward(void)
+{
+ git_merge_result *result;
+ git_oid expected, ff_oid;
+
+ cl_git_pass(git_oid_fromstr(&expected, THEIRS_FASTFORWARD_OID));
+
+ cl_assert(result = merge_fastforward_branch(0));
+ cl_assert(git_merge_result_is_fastforward(result));
+ cl_git_pass(git_merge_result_fastforward_oid(&ff_oid, result));
+ cl_assert(git_oid_cmp(&ff_oid, &expected) == 0);
+
+ git_merge_result_free(result);
+}
+
+void test_merge_workdir_fastforward__fastforward_only(void)
+{
+ git_merge_result *result;
+ git_merge_opts opts = GIT_MERGE_OPTS_INIT;
+ git_reference *their_ref;
+ git_merge_head *their_head;
+ int error;
+
+ opts.merge_flags = GIT_MERGE_FASTFORWARD_ONLY;
+
+ cl_git_pass(git_reference_lookup(&their_ref, repo, GIT_REFS_HEADS_DIR THEIRS_NOFASTFORWARD_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_head, repo, their_ref));
+
+ cl_git_fail((error = git_merge(&result, repo, (const git_merge_head **)&their_head, 1, &opts)));
+ cl_assert(error == GIT_ENONFASTFORWARD);
+
+ git_merge_head_free(their_head);
+ git_reference_free(their_ref);
+}
+
+void test_merge_workdir_fastforward__no_fastforward(void)
+{
+ git_merge_result *result;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" },
+ { 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 0, "automergeable.txt" },
+ { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt" },
+ { 0100644, "bd9cb4cd0a770cb9adcb5fce212142ef40ea1c35", 0, "changed-in-master.txt" },
+ { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt" },
+ { 0100644, "364bbe4ce80c7bd31e6307dce77d46e3e1759fb3", 0, "new-in-ff.txt" },
+ { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" },
+ { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" },
+ };
+
+ cl_assert(result = merge_fastforward_branch(GIT_MERGE_NO_FASTFORWARD));
+ cl_assert(!git_merge_result_is_fastforward(result));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 8));
+ cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+
+ git_merge_result_free(result);
+}
+
+void test_merge_workdir_fastforward__uptodate(void)
+{
+ git_reference *their_ref;
+ git_merge_head *their_heads[1];
+ git_merge_result *result;
+
+ cl_git_pass(git_reference_lookup(&their_ref, repo, GIT_HEAD_FILE));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, their_ref));
+
+ cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, NULL));
+
+ cl_assert(git_merge_result_is_uptodate(result));
+
+ git_merge_head_free(their_heads[0]);
+ git_reference_free(their_ref);
+ git_merge_result_free(result);
+}
+
+void test_merge_workdir_fastforward__uptodate_merging_prev_commit(void)
+{
+ git_oid their_oid;
+ git_merge_head *their_heads[1];
+ git_merge_result *result;
+
+ cl_git_pass(git_oid_fromstr(&their_oid, "c607fc30883e335def28cd686b51f6cfa02b06ec"));
+ cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &their_oid));
+
+ cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, NULL));
+
+ cl_assert(git_merge_result_is_uptodate(result));
+
+ git_merge_head_free(their_heads[0]);
+ git_merge_result_free(result);
+}
+
diff --git a/tests/merge/workdir/renames.c b/tests/merge/workdir/renames.c
new file mode 100644
index 000000000..d38397983
--- /dev/null
+++ b/tests/merge/workdir/renames.c
@@ -0,0 +1,156 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/merge.h"
+#include "buffer.h"
+#include "merge.h"
+#include "../merge_helpers.h"
+#include "fileops.h"
+#include "refs.h"
+
+static git_repository *repo;
+
+#define TEST_REPO_PATH "merge-resolve"
+
+#define BRANCH_RENAME_OURS "rename_conflict_ours"
+#define BRANCH_RENAME_THEIRS "rename_conflict_theirs"
+
+// Fixture setup and teardown
+void test_merge_workdir_renames__initialize(void)
+{
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+}
+
+void test_merge_workdir_renames__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_merge_workdir_renames__renames(void)
+{
+ git_merge_result *result;
+ git_merge_opts opts = GIT_MERGE_OPTS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" },
+ { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt" },
+ { 0100644, "8aac75de2a34b4d340bf62a6e58197269cb55797", 0, "0b-rewritten-in-ours.txt" },
+ { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt" },
+ { 0100644, "7edc726325da726751a4195e434e4377b0f67f9a", 0, "0c-rewritten-in-theirs.txt" },
+ { 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 0, "1a-newname-in-ours-edited-in-theirs.txt" },
+ { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt" },
+ { 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 0, "1b-newname-in-theirs-edited-in-ours.txt" },
+ { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt" },
+ { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" },
+ { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 0, "3a-newname-in-ours-deleted-in-theirs.txt" },
+ { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 0, "3b-newname-in-theirs-deleted-in-ours.txt" },
+ { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 0, "4a-newname-in-ours-added-in-theirs.txt~HEAD" },
+ { 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a", 0, "4a-newname-in-ours-added-in-theirs.txt~rename_conflict_theirs" },
+ { 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 0, "4b-newname-in-theirs-added-in-ours.txt~HEAD" },
+ { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 0, "4b-newname-in-theirs-added-in-ours.txt~rename_conflict_theirs" },
+ { 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 0, "5a-newname-in-ours-added-in-theirs.txt~HEAD" },
+ { 0100644, "98ba4205fcf31f5dd93c916d35fe3f3b3d0e6714", 0, "5a-newname-in-ours-added-in-theirs.txt~rename_conflict_theirs" },
+ { 0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced", 0, "5b-newname-in-theirs-added-in-ours.txt~HEAD" },
+ { 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 0, "5b-newname-in-theirs-added-in-ours.txt~rename_conflict_theirs" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "6-both-renamed-1-to-2-ours.txt" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "6-both-renamed-1-to-2-theirs.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "7-both-renamed.txt~HEAD" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "7-both-renamed.txt~rename_conflict_theirs" },
+ };
+
+ opts.merge_tree_opts.flags |= GIT_MERGE_TREE_FIND_RENAMES;
+ opts.merge_tree_opts.rename_threshold = 50;
+
+ cl_git_pass(merge_branches(&result, repo, GIT_REFS_HEADS_DIR BRANCH_RENAME_OURS, GIT_REFS_HEADS_DIR BRANCH_RENAME_THEIRS, &opts));
+ cl_assert(merge_test_workdir(repo, merge_index_entries, 24));
+
+ git_merge_result_free(result);
+}
+
+void test_merge_workdir_renames__ours(void)
+{
+ git_index *index;
+ git_merge_result *result;
+ git_merge_opts opts = GIT_MERGE_OPTS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" },
+ { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt" },
+ { 0100644, "e376fbdd06ebf021c92724da9f26f44212734e3e", 0, "0b-rewritten-in-ours.txt" },
+ { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt" },
+ { 0100644, "efc9121fdedaf08ba180b53ebfbcf71bd488ed09", 0, "0c-rewritten-in-theirs.txt" },
+ { 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 0, "1a-newname-in-ours-edited-in-theirs.txt" },
+ { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt" },
+ { 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 0, "1b-newname-in-theirs-edited-in-ours.txt" },
+ { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt" },
+ { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" },
+ { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 0, "3a-newname-in-ours-deleted-in-theirs.txt" },
+ { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 0, "3b-newname-in-theirs-deleted-in-ours.txt" },
+ { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 0, "4a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 0, "4b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 0, "5a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced", 0, "5b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 0, "5b-renamed-in-theirs-added-in-ours.txt" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "6-both-renamed-1-to-2-ours.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "7-both-renamed-side-2.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "7-both-renamed.txt" },
+ };
+
+ opts.merge_tree_opts.flags |= GIT_MERGE_TREE_FIND_RENAMES;
+ opts.merge_tree_opts.rename_threshold = 50;
+ opts.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_USE_OURS;
+
+ cl_git_pass(merge_branches(&result, repo, GIT_REFS_HEADS_DIR BRANCH_RENAME_OURS, GIT_REFS_HEADS_DIR BRANCH_RENAME_THEIRS, &opts));
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_write(index));
+ cl_assert(merge_test_workdir(repo, merge_index_entries, 20));
+
+ git_merge_result_free(result);
+ git_index_free(index);
+}
+
+void test_merge_workdir_renames__similar(void)
+{
+ git_merge_result *result;
+ git_merge_opts opts = GIT_MERGE_OPTS_INIT;
+
+ /*
+ * Note: this differs slightly from the core git merge result - there, 4a is
+ * tracked as a rename/delete instead of a rename/add and the theirs side
+ * is not placed in workdir in any form.
+ */
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" },
+ { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt" },
+ { 0100644, "8aac75de2a34b4d340bf62a6e58197269cb55797", 0, "0b-rewritten-in-ours.txt" },
+ { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt" },
+ { 0100644, "7edc726325da726751a4195e434e4377b0f67f9a", 0, "0c-rewritten-in-theirs.txt" },
+ { 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 0, "1a-newname-in-ours-edited-in-theirs.txt" },
+ { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt" },
+ { 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 0, "1b-newname-in-theirs-edited-in-ours.txt" },
+ { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt" },
+ { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" },
+ { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 0, "3a-newname-in-ours-deleted-in-theirs.txt" },
+ { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 0, "3b-newname-in-theirs-deleted-in-ours.txt" },
+ { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 0, "4a-newname-in-ours-added-in-theirs.txt~HEAD" },
+ { 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a", 0, "4a-newname-in-ours-added-in-theirs.txt~rename_conflict_theirs" },
+ { 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 0, "4b-newname-in-theirs-added-in-ours.txt~HEAD" },
+ { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 0, "4b-newname-in-theirs-added-in-ours.txt~rename_conflict_theirs" },
+ { 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 0, "5a-newname-in-ours-added-in-theirs.txt~HEAD" },
+ { 0100644, "98ba4205fcf31f5dd93c916d35fe3f3b3d0e6714", 0, "5a-newname-in-ours-added-in-theirs.txt~rename_conflict_theirs" },
+ { 0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced", 0, "5b-newname-in-theirs-added-in-ours.txt~HEAD" },
+ { 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 0, "5b-newname-in-theirs-added-in-ours.txt~rename_conflict_theirs" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "6-both-renamed-1-to-2-ours.txt" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "6-both-renamed-1-to-2-theirs.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "7-both-renamed.txt~HEAD" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "7-both-renamed.txt~rename_conflict_theirs" },
+ };
+
+ opts.merge_tree_opts.flags |= GIT_MERGE_TREE_FIND_RENAMES;
+ opts.merge_tree_opts.rename_threshold = 50;
+
+ cl_git_pass(merge_branches(&result, repo, GIT_REFS_HEADS_DIR BRANCH_RENAME_OURS, GIT_REFS_HEADS_DIR BRANCH_RENAME_THEIRS, &opts));
+ cl_assert(merge_test_workdir(repo, merge_index_entries, 24));
+
+ git_merge_result_free(result);
+}
+
diff --git a/tests/merge/workdir/setup.c b/tests/merge/workdir/setup.c
new file mode 100644
index 000000000..870d55ef2
--- /dev/null
+++ b/tests/merge/workdir/setup.c
@@ -0,0 +1,1057 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/merge.h"
+#include "merge.h"
+#include "refs.h"
+#include "fileops.h"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+#define TEST_REPO_PATH "merge-resolve"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+#define ORIG_HEAD "bd593285fc7fe4ca18ccdbabf027f5d689101452"
+
+#define THEIRS_SIMPLE_BRANCH "branch"
+#define THEIRS_SIMPLE_OID "7cb63eed597130ba4abb87b3e544b85021905520"
+
+#define OCTO1_BRANCH "octo1"
+#define OCTO1_OID "16f825815cfd20a07a75c71554e82d8eede0b061"
+
+#define OCTO2_BRANCH "octo2"
+#define OCTO2_OID "158dc7bedb202f5b26502bf3574faa7f4238d56c"
+
+#define OCTO3_BRANCH "octo3"
+#define OCTO3_OID "50ce7d7d01217679e26c55939eef119e0c93e272"
+
+#define OCTO4_BRANCH "octo4"
+#define OCTO4_OID "54269b3f6ec3d7d4ede24dd350dd5d605495c3ae"
+
+#define OCTO5_BRANCH "octo5"
+#define OCTO5_OID "e4f618a2c3ed0669308735727df5ebf2447f022f"
+
+// Fixture setup and teardown
+void test_merge_workdir_setup__initialize(void)
+{
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+ git_repository_index(&repo_index, repo);
+}
+
+void test_merge_workdir_setup__cleanup(void)
+{
+ git_index_free(repo_index);
+ cl_git_sandbox_cleanup();
+}
+
+static bool test_file_contents(const char *filename, const char *expected)
+{
+ git_buf file_path_buf = GIT_BUF_INIT, file_buf = GIT_BUF_INIT;
+ bool equals;
+
+ git_buf_printf(&file_path_buf, "%s/%s", git_repository_path(repo), filename);
+
+ cl_git_pass(git_futils_readbuffer(&file_buf, file_path_buf.ptr));
+ equals = (strcmp(file_buf.ptr, expected) == 0);
+
+ git_buf_free(&file_path_buf);
+ git_buf_free(&file_buf);
+
+ return equals;
+}
+
+static void write_file_contents(const char *filename, const char *output)
+{
+ git_buf file_path_buf = GIT_BUF_INIT;
+
+ git_buf_printf(&file_path_buf, "%s/%s", git_repository_path(repo),
+ filename);
+ cl_git_rewritefile(file_path_buf.ptr, output);
+
+ git_buf_free(&file_path_buf);
+}
+
+/* git merge octo1 */
+void test_merge_workdir_setup__one_branch(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_merge_head *our_head, *their_heads[1];
+
+ cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
+ cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 1, 0));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO1_BRANCH "'\n"));
+
+ git_reference_free(octo1_ref);
+
+ git_merge_head_free(our_head);
+ git_merge_head_free(their_heads[0]);
+}
+
+/* git merge --no-ff octo1 */
+void test_merge_workdir_setup__no_fastforward(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_merge_head *our_head, *their_heads[1];
+
+ cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
+ cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 1, GIT_MERGE_NO_FASTFORWARD));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff"));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO1_BRANCH "'\n"));
+
+ git_reference_free(octo1_ref);
+
+ git_merge_head_free(our_head);
+ git_merge_head_free(their_heads[0]);
+}
+
+/* git merge 16f825815cfd20a07a75c71554e82d8eede0b061 */
+void test_merge_workdir_setup__one_oid(void)
+{
+ git_oid our_oid;
+ git_oid octo1_oid;
+ git_merge_head *our_head, *their_heads[1];
+
+ cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
+ cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID));
+ cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &octo1_oid));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 1, 0));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge commit '" OCTO1_OID "'\n"));
+
+ git_merge_head_free(our_head);
+ git_merge_head_free(their_heads[0]);
+}
+
+/* git merge octo1 octo2 */
+void test_merge_workdir_setup__two_branches(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_reference *octo2_ref;
+ git_merge_head *our_head, *their_heads[2];
+
+ cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
+ cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
+
+ cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 2, 0));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "' and '" OCTO2_BRANCH "'\n"));
+
+ git_reference_free(octo1_ref);
+ git_reference_free(octo2_ref);
+
+ git_merge_head_free(our_head);
+ git_merge_head_free(their_heads[0]);
+ git_merge_head_free(their_heads[1]);
+}
+
+/* git merge octo1 octo2 octo3 */
+void test_merge_workdir_setup__three_branches(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_reference *octo2_ref;
+ git_reference *octo3_ref;
+ git_merge_head *our_head, *their_heads[3];
+
+ cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
+ cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
+
+ cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref));
+
+ cl_git_pass(git_reference_lookup(&octo3_ref, repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[2], repo, octo3_ref));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 3, 0));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "', '" OCTO2_BRANCH "' and '" OCTO3_BRANCH "'\n"));
+
+ git_reference_free(octo1_ref);
+ git_reference_free(octo2_ref);
+ git_reference_free(octo3_ref);
+
+ git_merge_head_free(our_head);
+ git_merge_head_free(their_heads[0]);
+ git_merge_head_free(their_heads[1]);
+ git_merge_head_free(their_heads[2]);
+}
+
+/* git merge 16f825815cfd20a07a75c71554e82d8eede0b061 158dc7bedb202f5b26502bf3574faa7f4238d56c 50ce7d7d01217679e26c55939eef119e0c93e272 */
+void test_merge_workdir_setup__three_oids(void)
+{
+ git_oid our_oid;
+ git_oid octo1_oid;
+ git_oid octo2_oid;
+ git_oid octo3_oid;
+ git_merge_head *our_head, *their_heads[3];
+
+ cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
+ cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID));
+ cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &octo1_oid));
+
+ cl_git_pass(git_oid_fromstr(&octo2_oid, OCTO2_OID));
+ cl_git_pass(git_merge_head_from_oid(&their_heads[1], repo, &octo2_oid));
+
+ cl_git_pass(git_oid_fromstr(&octo3_oid, OCTO3_OID));
+ cl_git_pass(git_merge_head_from_oid(&their_heads[2], repo, &octo3_oid));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 3, 0));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge commit '" OCTO1_OID "'; commit '" OCTO2_OID "'; commit '" OCTO3_OID "'\n"));
+
+ git_merge_head_free(our_head);
+ git_merge_head_free(their_heads[0]);
+ git_merge_head_free(their_heads[1]);
+ git_merge_head_free(their_heads[2]);
+}
+
+/* git merge octo1 158dc7bedb202f5b26502bf3574faa7f4238d56c */
+void test_merge_workdir_setup__branches_and_oids_1(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_oid octo2_oid;
+ git_merge_head *our_head, *their_heads[2];
+
+ cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
+ cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
+
+ cl_git_pass(git_oid_fromstr(&octo2_oid, OCTO2_OID));
+ cl_git_pass(git_merge_head_from_oid(&their_heads[1], repo, &octo2_oid));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 2, 0));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO1_BRANCH "'; commit '" OCTO2_OID "'\n"));
+
+ git_reference_free(octo1_ref);
+
+ git_merge_head_free(our_head);
+ git_merge_head_free(their_heads[0]);
+ git_merge_head_free(their_heads[1]);
+}
+
+/* git merge octo1 158dc7bedb202f5b26502bf3574faa7f4238d56c octo3 54269b3f6ec3d7d4ede24dd350dd5d605495c3ae */
+void test_merge_workdir_setup__branches_and_oids_2(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_oid octo2_oid;
+ git_reference *octo3_ref;
+ git_oid octo4_oid;
+ git_merge_head *our_head, *their_heads[4];
+
+ cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
+ cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
+
+ cl_git_pass(git_oid_fromstr(&octo2_oid, OCTO2_OID));
+ cl_git_pass(git_merge_head_from_oid(&their_heads[1], repo, &octo2_oid));
+
+ cl_git_pass(git_reference_lookup(&octo3_ref, repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[2], repo, octo3_ref));
+
+ cl_git_pass(git_oid_fromstr(&octo4_oid, OCTO4_OID));
+ cl_git_pass(git_merge_head_from_oid(&their_heads[3], repo, &octo4_oid));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 4, 0));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n" OCTO4_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "' and '" OCTO3_BRANCH "'; commit '" OCTO2_OID "'; commit '" OCTO4_OID "'\n"));
+
+ git_reference_free(octo1_ref);
+ git_reference_free(octo3_ref);
+
+ git_merge_head_free(our_head);
+ git_merge_head_free(their_heads[0]);
+ git_merge_head_free(their_heads[1]);
+ git_merge_head_free(their_heads[2]);
+ git_merge_head_free(their_heads[3]);
+}
+
+/* git merge 16f825815cfd20a07a75c71554e82d8eede0b061 octo2 50ce7d7d01217679e26c55939eef119e0c93e272 octo4 */
+void test_merge_workdir_setup__branches_and_oids_3(void)
+{
+ git_oid our_oid;
+ git_oid octo1_oid;
+ git_reference *octo2_ref;
+ git_oid octo3_oid;
+ git_reference *octo4_ref;
+ git_merge_head *our_head, *their_heads[4];
+
+ cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
+ cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID));
+ cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &octo1_oid));
+
+ cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref));
+
+ cl_git_pass(git_oid_fromstr(&octo3_oid, OCTO3_OID));
+ cl_git_pass(git_merge_head_from_oid(&their_heads[2], repo, &octo3_oid));
+
+ cl_git_pass(git_reference_lookup(&octo4_ref, repo, GIT_REFS_HEADS_DIR OCTO4_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[3], repo, octo4_ref));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 4, 0));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n" OCTO4_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge commit '" OCTO1_OID "'; branches '" OCTO2_BRANCH "' and '" OCTO4_BRANCH "'; commit '" OCTO3_OID "'\n"));
+
+ git_reference_free(octo2_ref);
+ git_reference_free(octo4_ref);
+
+ git_merge_head_free(our_head);
+ git_merge_head_free(their_heads[0]);
+ git_merge_head_free(their_heads[1]);
+ git_merge_head_free(their_heads[2]);
+ git_merge_head_free(their_heads[3]);
+}
+
+/* git merge 16f825815cfd20a07a75c71554e82d8eede0b061 octo2 50ce7d7d01217679e26c55939eef119e0c93e272 octo4 octo5 */
+void test_merge_workdir_setup__branches_and_oids_4(void)
+{
+ git_oid our_oid;
+ git_oid octo1_oid;
+ git_reference *octo2_ref;
+ git_oid octo3_oid;
+ git_reference *octo4_ref;
+ git_reference *octo5_ref;
+ git_merge_head *our_head, *their_heads[5];
+
+ cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
+ cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID));
+ cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &octo1_oid));
+
+ cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref));
+
+ cl_git_pass(git_oid_fromstr(&octo3_oid, OCTO3_OID));
+ cl_git_pass(git_merge_head_from_oid(&their_heads[2], repo, &octo3_oid));
+
+ cl_git_pass(git_reference_lookup(&octo4_ref, repo, GIT_REFS_HEADS_DIR OCTO4_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[3], repo, octo4_ref));
+
+ cl_git_pass(git_reference_lookup(&octo5_ref, repo, GIT_REFS_HEADS_DIR OCTO5_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[4], repo, octo5_ref));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 5, 0));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n" OCTO4_OID "\n" OCTO5_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge commit '" OCTO1_OID "'; branches '" OCTO2_BRANCH "', '" OCTO4_BRANCH "' and '" OCTO5_BRANCH "'; commit '" OCTO3_OID "'\n"));
+
+ git_reference_free(octo2_ref);
+ git_reference_free(octo4_ref);
+ git_reference_free(octo5_ref);
+
+ git_merge_head_free(our_head);
+ git_merge_head_free(their_heads[0]);
+ git_merge_head_free(their_heads[1]);
+ git_merge_head_free(their_heads[2]);
+ git_merge_head_free(their_heads[3]);
+ git_merge_head_free(their_heads[4]);
+}
+
+/* git merge octo1 octo1 octo1 */
+void test_merge_workdir_setup__three_same_branches(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_1_ref;
+ git_reference *octo1_2_ref;
+ git_reference *octo1_3_ref;
+ git_merge_head *our_head, *their_heads[3];
+
+ cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
+ cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_1_ref));
+
+ cl_git_pass(git_reference_lookup(&octo1_2_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo1_2_ref));
+
+ cl_git_pass(git_reference_lookup(&octo1_3_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[2], repo, octo1_3_ref));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 3, 0));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO1_OID "\n" OCTO1_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "', '" OCTO1_BRANCH "' and '" OCTO1_BRANCH "'\n"));
+
+ git_reference_free(octo1_1_ref);
+ git_reference_free(octo1_2_ref);
+ git_reference_free(octo1_3_ref);
+
+ git_merge_head_free(our_head);
+ git_merge_head_free(their_heads[0]);
+ git_merge_head_free(their_heads[1]);
+ git_merge_head_free(their_heads[2]);
+}
+
+/* git merge 16f825815cfd20a07a75c71554e82d8eede0b061 16f825815cfd20a07a75c71554e82d8eede0b061 16f825815cfd20a07a75c71554e82d8eede0b061 */
+void test_merge_workdir_setup__three_same_oids(void)
+{
+ git_oid our_oid;
+ git_oid octo1_1_oid;
+ git_oid octo1_2_oid;
+ git_oid octo1_3_oid;
+ git_merge_head *our_head, *their_heads[3];
+
+ cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
+ cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_oid_fromstr(&octo1_1_oid, OCTO1_OID));
+ cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &octo1_1_oid));
+
+ cl_git_pass(git_oid_fromstr(&octo1_2_oid, OCTO1_OID));
+ cl_git_pass(git_merge_head_from_oid(&their_heads[1], repo, &octo1_2_oid));
+
+ cl_git_pass(git_oid_fromstr(&octo1_3_oid, OCTO1_OID));
+ cl_git_pass(git_merge_head_from_oid(&their_heads[2], repo, &octo1_3_oid));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 3, 0));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO1_OID "\n" OCTO1_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge commit '" OCTO1_OID "'; commit '" OCTO1_OID "'; commit '" OCTO1_OID "'\n"));
+
+ git_merge_head_free(our_head);
+ git_merge_head_free(their_heads[0]);
+ git_merge_head_free(their_heads[1]);
+ git_merge_head_free(their_heads[2]);
+}
+
+static int create_remote_tracking_branch(const char *branch_name, const char *oid_str)
+{
+ int error = 0;
+
+ git_buf remotes_path = GIT_BUF_INIT,
+ origin_path = GIT_BUF_INIT,
+ filename = GIT_BUF_INIT,
+ data = GIT_BUF_INIT;
+
+ if ((error = git_buf_puts(&remotes_path, git_repository_path(repo))) < 0 ||
+ (error = git_buf_puts(&remotes_path, GIT_REFS_REMOTES_DIR)) < 0)
+ goto done;
+
+ if (!git_path_exists(git_buf_cstr(&remotes_path)) &&
+ (error = p_mkdir(git_buf_cstr(&remotes_path), 0777)) < 0)
+ goto done;
+
+ if ((error = git_buf_puts(&origin_path, git_buf_cstr(&remotes_path))) < 0 ||
+ (error = git_buf_puts(&origin_path, "origin")) < 0)
+ goto done;
+
+ if (!git_path_exists(git_buf_cstr(&origin_path)) &&
+ (error = p_mkdir(git_buf_cstr(&origin_path), 0777)) < 0)
+ goto done;
+
+ if ((error = git_buf_puts(&filename, git_buf_cstr(&origin_path))) < 0 ||
+ (error = git_buf_puts(&filename, "/")) < 0 ||
+ (error = git_buf_puts(&filename, branch_name)) < 0 ||
+ (error = git_buf_puts(&data, oid_str)) < 0 ||
+ (error = git_buf_puts(&data, "\n")) < 0)
+ goto done;
+
+ cl_git_rewritefile(git_buf_cstr(&filename), git_buf_cstr(&data));
+
+done:
+ git_buf_free(&remotes_path);
+ git_buf_free(&origin_path);
+ git_buf_free(&filename);
+ git_buf_free(&data);
+
+ return error;
+}
+
+/* git merge refs/remotes/origin/octo1 */
+void test_merge_workdir_setup__remote_tracking_one_branch(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_merge_head *our_head, *their_heads[1];
+
+ cl_git_pass(create_remote_tracking_branch(OCTO1_BRANCH, OCTO1_OID));
+
+ cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
+ cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO1_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 1, 0));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge remote-tracking branch 'refs/remotes/origin/" OCTO1_BRANCH "'\n"));
+
+ git_reference_free(octo1_ref);
+
+ git_merge_head_free(our_head);
+ git_merge_head_free(their_heads[0]);
+}
+
+/* git merge refs/remotes/origin/octo1 refs/remotes/origin/octo2 */
+void test_merge_workdir_setup__remote_tracking_two_branches(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_reference *octo2_ref;
+ git_merge_head *our_head, *their_heads[2];
+
+ cl_git_pass(create_remote_tracking_branch(OCTO1_BRANCH, OCTO1_OID));
+ cl_git_pass(create_remote_tracking_branch(OCTO2_BRANCH, OCTO2_OID));
+
+ cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
+ cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO1_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
+
+ cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO2_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 2, 0));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge remote-tracking branches 'refs/remotes/origin/" OCTO1_BRANCH "' and 'refs/remotes/origin/" OCTO2_BRANCH "'\n"));
+
+ git_reference_free(octo1_ref);
+ git_reference_free(octo2_ref);
+
+ git_merge_head_free(our_head);
+ git_merge_head_free(their_heads[0]);
+ git_merge_head_free(their_heads[1]);
+}
+
+/* git merge refs/remotes/origin/octo1 refs/remotes/origin/octo2 refs/remotes/origin/octo3 */
+void test_merge_workdir_setup__remote_tracking_three_branches(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_reference *octo2_ref;
+ git_reference *octo3_ref;
+ git_merge_head *our_head, *their_heads[3];
+
+ cl_git_pass(create_remote_tracking_branch(OCTO1_BRANCH, OCTO1_OID));
+ cl_git_pass(create_remote_tracking_branch(OCTO2_BRANCH, OCTO2_OID));
+ cl_git_pass(create_remote_tracking_branch(OCTO3_BRANCH, OCTO3_OID));
+
+ cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
+ cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO1_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
+
+ cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO2_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref));
+
+ cl_git_pass(git_reference_lookup(&octo3_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO3_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[2], repo, octo3_ref));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 3, 0));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge remote-tracking branches 'refs/remotes/origin/" OCTO1_BRANCH "', 'refs/remotes/origin/" OCTO2_BRANCH "' and 'refs/remotes/origin/" OCTO3_BRANCH "'\n"));
+
+ git_reference_free(octo1_ref);
+ git_reference_free(octo2_ref);
+ git_reference_free(octo3_ref);
+
+ git_merge_head_free(our_head);
+ git_merge_head_free(their_heads[0]);
+ git_merge_head_free(their_heads[1]);
+ git_merge_head_free(their_heads[2]);
+}
+
+/* git merge octo1 refs/remotes/origin/octo2 */
+void test_merge_workdir_setup__normal_branch_and_remote_tracking_branch(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_reference *octo2_ref;
+ git_merge_head *our_head, *their_heads[2];
+
+ cl_git_pass(create_remote_tracking_branch(OCTO2_BRANCH, OCTO2_OID));
+
+ cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
+ cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
+
+ cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO2_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 2, 0));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO1_BRANCH "', remote-tracking branch 'refs/remotes/origin/" OCTO2_BRANCH "'\n"));
+
+ git_reference_free(octo1_ref);
+ git_reference_free(octo2_ref);
+
+ git_merge_head_free(our_head);
+ git_merge_head_free(their_heads[0]);
+ git_merge_head_free(their_heads[1]);
+}
+
+/* git merge refs/remotes/origin/octo1 octo2 */
+void test_merge_workdir_setup__remote_tracking_branch_and_normal_branch(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_reference *octo2_ref;
+ git_merge_head *our_head, *their_heads[2];
+
+ cl_git_pass(create_remote_tracking_branch(OCTO1_BRANCH, OCTO1_OID));
+
+ cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
+ cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO1_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
+
+ cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 2, 0));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO2_BRANCH "', remote-tracking branch 'refs/remotes/origin/" OCTO1_BRANCH "'\n"));
+
+ git_reference_free(octo1_ref);
+ git_reference_free(octo2_ref);
+
+ git_merge_head_free(our_head);
+ git_merge_head_free(their_heads[0]);
+ git_merge_head_free(their_heads[1]);
+}
+
+/* git merge octo1 refs/remotes/origin/octo2 octo3 refs/remotes/origin/octo4 */
+void test_merge_workdir_setup__two_remote_tracking_branch_and_two_normal_branches(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_reference *octo2_ref;
+ git_reference *octo3_ref;
+ git_reference *octo4_ref;
+ git_merge_head *our_head, *their_heads[4];
+
+ cl_git_pass(create_remote_tracking_branch(OCTO2_BRANCH, OCTO2_OID));
+ cl_git_pass(create_remote_tracking_branch(OCTO4_BRANCH, OCTO4_OID));
+
+ cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
+ cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
+
+ cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO2_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref));
+
+ cl_git_pass(git_reference_lookup(&octo3_ref, repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[2], repo, octo3_ref));
+
+ cl_git_pass(git_reference_lookup(&octo4_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO4_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[3], repo, octo4_ref));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 4, 0));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n" OCTO4_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "' and '" OCTO3_BRANCH "', remote-tracking branches 'refs/remotes/origin/" OCTO2_BRANCH "' and 'refs/remotes/origin/" OCTO4_BRANCH "'\n"));
+
+ git_reference_free(octo1_ref);
+ git_reference_free(octo2_ref);
+ git_reference_free(octo3_ref);
+ git_reference_free(octo4_ref);
+
+ git_merge_head_free(our_head);
+ git_merge_head_free(their_heads[0]);
+ git_merge_head_free(their_heads[1]);
+ git_merge_head_free(their_heads[2]);
+ git_merge_head_free(their_heads[3]);
+}
+
+/* git pull origin branch octo1 */
+void test_merge_workdir_setup__pull_one(void)
+{
+ git_oid our_oid;
+ git_oid octo1_1_oid;
+ git_merge_head *our_head, *their_heads[1];
+
+ cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
+ cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_oid_fromstr(&octo1_1_oid, OCTO1_OID));
+ cl_git_pass(git_merge_head_from_fetchhead(&their_heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.url/repo.git", &octo1_1_oid));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 1, 0));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch 'octo1' of http://remote.url/repo.git\n"));
+
+ git_merge_head_free(our_head);
+ git_merge_head_free(their_heads[0]);
+}
+
+/* git pull origin octo1 octo2 */
+void test_merge_workdir_setup__pull_two(void)
+{
+ git_oid our_oid;
+ git_oid octo1_oid;
+ git_oid octo2_oid;
+ git_merge_head *our_head, *their_heads[2];
+
+ cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
+ cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID));
+ cl_git_pass(git_merge_head_from_fetchhead(&their_heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.url/repo.git", &octo1_oid));
+
+ cl_git_pass(git_oid_fromstr(&octo2_oid, OCTO2_OID));
+ cl_git_pass(git_merge_head_from_fetchhead(&their_heads[1], repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH, "http://remote.url/repo.git", &octo2_oid));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 2, 0));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "' and '" OCTO2_BRANCH "' of http://remote.url/repo.git\n"));
+
+ git_merge_head_free(our_head);
+ git_merge_head_free(their_heads[0]);
+ git_merge_head_free(their_heads[1]);
+}
+
+/* git pull origin octo1 octo2 octo3 */
+void test_merge_workdir_setup__pull_three(void)
+{
+ git_oid our_oid;
+ git_oid octo1_oid;
+ git_oid octo2_oid;
+ git_oid octo3_oid;
+ git_merge_head *our_head, *their_heads[3];
+
+ cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
+ cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID));
+ cl_git_pass(git_merge_head_from_fetchhead(&their_heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.url/repo.git", &octo1_oid));
+
+ cl_git_pass(git_oid_fromstr(&octo2_oid, OCTO2_OID));
+ cl_git_pass(git_merge_head_from_fetchhead(&their_heads[1], repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH, "http://remote.url/repo.git", &octo2_oid));
+
+ cl_git_pass(git_oid_fromstr(&octo3_oid, OCTO3_OID));
+ cl_git_pass(git_merge_head_from_fetchhead(&their_heads[2], repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH, "http://remote.url/repo.git", &octo3_oid));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 3, 0));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "', '" OCTO2_BRANCH "' and '" OCTO3_BRANCH "' of http://remote.url/repo.git\n"));
+
+ git_merge_head_free(our_head);
+ git_merge_head_free(their_heads[0]);
+ git_merge_head_free(their_heads[1]);
+ git_merge_head_free(their_heads[2]);
+}
+
+void test_merge_workdir_setup__three_remotes(void)
+{
+ git_oid our_oid;
+ git_oid octo1_oid;
+ git_oid octo2_oid;
+ git_oid octo3_oid;
+ git_merge_head *our_head, *their_heads[3];
+
+ cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
+ cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID));
+ cl_git_pass(git_merge_head_from_fetchhead(&their_heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.first/repo.git", &octo1_oid));
+
+ cl_git_pass(git_oid_fromstr(&octo2_oid, OCTO2_OID));
+ cl_git_pass(git_merge_head_from_fetchhead(&their_heads[1], repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH, "http://remote.second/repo.git", &octo2_oid));
+
+ cl_git_pass(git_oid_fromstr(&octo3_oid, OCTO3_OID));
+ cl_git_pass(git_merge_head_from_fetchhead(&their_heads[2], repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH, "http://remote.third/repo.git", &octo3_oid));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 3, 0));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO1_BRANCH "' of http://remote.first/repo.git, branch '" OCTO2_BRANCH "' of http://remote.second/repo.git, branch '" OCTO3_BRANCH "' of http://remote.third/repo.git\n"));
+
+ git_merge_head_free(our_head);
+ git_merge_head_free(their_heads[0]);
+ git_merge_head_free(their_heads[1]);
+ git_merge_head_free(their_heads[2]);
+}
+
+void test_merge_workdir_setup__two_remotes(void)
+{
+ git_oid our_oid;
+ git_oid octo1_oid;
+ git_oid octo2_oid;
+ git_oid octo3_oid;
+ git_oid octo4_oid;
+ git_merge_head *our_head, *their_heads[4];
+
+ cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
+ cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID));
+ cl_git_pass(git_merge_head_from_fetchhead(&their_heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.first/repo.git", &octo1_oid));
+
+ cl_git_pass(git_oid_fromstr(&octo2_oid, OCTO2_OID));
+ cl_git_pass(git_merge_head_from_fetchhead(&their_heads[1], repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH, "http://remote.second/repo.git", &octo2_oid));
+
+ cl_git_pass(git_oid_fromstr(&octo3_oid, OCTO3_OID));
+ cl_git_pass(git_merge_head_from_fetchhead(&their_heads[2], repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH, "http://remote.first/repo.git", &octo3_oid));
+
+ cl_git_pass(git_oid_fromstr(&octo4_oid, OCTO4_OID));
+ cl_git_pass(git_merge_head_from_fetchhead(&their_heads[3], repo, GIT_REFS_HEADS_DIR OCTO4_BRANCH, "http://remote.second/repo.git", &octo4_oid));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 4, 0));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n" OCTO4_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, ""));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "' and '" OCTO3_BRANCH "' of http://remote.first/repo.git, branches '" OCTO2_BRANCH "' and '" OCTO4_BRANCH "' of http://remote.second/repo.git\n"));
+
+ git_merge_head_free(our_head);
+ git_merge_head_free(their_heads[0]);
+ git_merge_head_free(their_heads[1]);
+ git_merge_head_free(their_heads[2]);
+ git_merge_head_free(their_heads[3]);
+}
+
+struct merge_head_cb_data {
+ const char **oid_str;
+ unsigned int len;
+
+ unsigned int i;
+};
+
+static int merge_head_foreach_cb(const git_oid *oid, void *payload)
+{
+ git_oid expected_oid;
+ struct merge_head_cb_data *cb_data = payload;
+
+ git_oid_fromstr(&expected_oid, cb_data->oid_str[cb_data->i]);
+ cl_assert(git_oid_cmp(&expected_oid, oid) == 0);
+ cb_data->i++;
+ return 0;
+}
+
+void test_merge_workdir_setup__head_notfound(void)
+{
+ int error;
+
+ cl_git_fail((error = git_repository_mergehead_foreach(repo,
+ merge_head_foreach_cb, NULL)));
+ cl_assert(error == GIT_ENOTFOUND);
+}
+
+void test_merge_workdir_setup__head_invalid_oid(void)
+{
+ int error;
+
+ write_file_contents(GIT_MERGE_HEAD_FILE, "invalid-oid\n");
+
+ cl_git_fail((error = git_repository_mergehead_foreach(repo,
+ merge_head_foreach_cb, NULL)));
+ cl_assert(error == -1);
+}
+
+void test_merge_workdir_setup__head_foreach_nonewline(void)
+{
+ int error;
+
+ write_file_contents(GIT_MERGE_HEAD_FILE, THEIRS_SIMPLE_OID);
+
+ cl_git_fail((error = git_repository_mergehead_foreach(repo,
+ merge_head_foreach_cb, NULL)));
+ cl_assert(error == -1);
+}
+
+void test_merge_workdir_setup__head_foreach_one(void)
+{
+ const char *expected = THEIRS_SIMPLE_OID;
+
+ struct merge_head_cb_data cb_data = { &expected, 1 };
+
+ write_file_contents(GIT_MERGE_HEAD_FILE, THEIRS_SIMPLE_OID "\n");
+
+ cl_git_pass(git_repository_mergehead_foreach(repo,
+ merge_head_foreach_cb, &cb_data));
+
+ cl_assert(cb_data.i == cb_data.len);
+}
+
+void test_merge_workdir_setup__head_foreach_octopus(void)
+{
+ const char *expected[] = { THEIRS_SIMPLE_OID,
+ OCTO1_OID, OCTO2_OID, OCTO3_OID, OCTO4_OID, OCTO5_OID };
+
+ struct merge_head_cb_data cb_data = { expected, 6 };
+
+ write_file_contents(GIT_MERGE_HEAD_FILE,
+ THEIRS_SIMPLE_OID "\n"
+ OCTO1_OID "\n"
+ OCTO2_OID "\n"
+ OCTO3_OID "\n"
+ OCTO4_OID "\n"
+ OCTO5_OID "\n");
+
+ cl_git_pass(git_repository_mergehead_foreach(repo,
+ merge_head_foreach_cb, &cb_data));
+
+ cl_assert(cb_data.i == cb_data.len);
+}
+
+void test_merge_workdir_setup__retained_after_success(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_merge_head *our_head, *their_heads[1];
+ git_merge_result *result;
+ git_merge_opts opts = GIT_MERGE_OPTS_INIT;
+
+ opts.merge_flags |= GIT_MERGE_NO_FASTFORWARD;
+
+ cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
+ cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+
+ cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
+
+ cl_git_pass(git_merge(&result, repo, (const git_merge_head **)&their_heads[0], 1, &opts));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff"));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO1_BRANCH "'\n"));
+
+ git_reference_free(octo1_ref);
+
+ git_merge_head_free(our_head);
+ git_merge_head_free(their_heads[0]);
+ git_merge_result_free(result);
+}
+
+void test_merge_workdir_setup__removed_after_failure(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_merge_head *our_head, *their_heads[1];
+ git_merge_result *result;
+ git_merge_opts opts = GIT_MERGE_OPTS_INIT;
+
+ opts.merge_flags |= GIT_MERGE_NO_FASTFORWARD;
+
+ cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
+ cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
+
+ cl_git_rewritefile("merge-resolve/new-in-octo1.txt",
+ "Conflicting file!\n\nMerge will fail!\n");
+
+ cl_git_fail(git_merge(
+ &result, repo, (const git_merge_head **)&their_heads[0], 1, &opts));
+
+ cl_assert(!git_path_exists("merge-resolve/" GIT_MERGE_HEAD_FILE));
+ cl_assert(!git_path_exists("merge-resolve/" GIT_ORIG_HEAD_FILE));
+ cl_assert(!git_path_exists("merge-resolve/" GIT_MERGE_MODE_FILE));
+ cl_assert(!git_path_exists("merge-resolve/" GIT_MERGE_MSG_FILE));
+
+ git_reference_free(octo1_ref);
+
+ git_merge_head_free(our_head);
+ git_merge_head_free(their_heads[0]);
+ git_merge_result_free(result);
+}
diff --git a/tests/merge/workdir/simple.c b/tests/merge/workdir/simple.c
new file mode 100644
index 000000000..4a3b86ee4
--- /dev/null
+++ b/tests/merge/workdir/simple.c
@@ -0,0 +1,491 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/merge.h"
+#include "buffer.h"
+#include "merge.h"
+#include "../merge_helpers.h"
+#include "refs.h"
+#include "fileops.h"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+#define TEST_REPO_PATH "merge-resolve"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+#define THEIRS_SIMPLE_BRANCH "branch"
+#define THEIRS_SIMPLE_OID "7cb63eed597130ba4abb87b3e544b85021905520"
+
+#define THEIRS_UNRELATED_BRANCH "unrelated"
+#define THEIRS_UNRELATED_OID "55b4e4687e7a0d9ca367016ed930f385d4022e6f"
+#define THEIRS_UNRELATED_PARENT "d6cf6c7741b3316826af1314042550c97ded1d50"
+
+#define OURS_DIRECTORY_FILE "df_side1"
+#define THEIRS_DIRECTORY_FILE "fc90237dc4891fa6c69827fc465632225e391618"
+
+
+/* Non-conflicting files, index entries are common to every merge operation */
+#define ADDED_IN_MASTER_INDEX_ENTRY \
+ { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, \
+ "added-in-master.txt" }
+#define AUTOMERGEABLE_INDEX_ENTRY \
+ { 0100644, "f2e1550a0c9e53d5811175864a29536642ae3821", 0, \
+ "automergeable.txt" }
+#define CHANGED_IN_BRANCH_INDEX_ENTRY \
+ { 0100644, "4eb04c9e79e88f6640d01ff5b25ca2a60764f216", 0, \
+ "changed-in-branch.txt" }
+#define CHANGED_IN_MASTER_INDEX_ENTRY \
+ { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, \
+ "changed-in-master.txt" }
+#define UNCHANGED_INDEX_ENTRY \
+ { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, \
+ "unchanged.txt" }
+
+/* Unrelated files */
+#define UNRELATED_NEW1 \
+ { 0100644, "ef58fdd8086c243bdc81f99e379acacfd21d32d6", 0, \
+ "new-in-unrelated1.txt" }
+#define UNRELATED_NEW2 \
+ { 0100644, "948ba6e701c1edab0c2d394fb7c5538334129793", 0, \
+ "new-in-unrelated2.txt" }
+
+/* Expected REUC entries */
+#define AUTOMERGEABLE_REUC_ENTRY \
+ { "automergeable.txt", 0100644, 0100644, 0100644, \
+ "6212c31dab5e482247d7977e4f0dd3601decf13b", \
+ "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", \
+ "058541fc37114bfc1dddf6bd6bffc7fae5c2e6fe" }
+#define CONFLICTING_REUC_ENTRY \
+ { "conflicting.txt", 0100644, 0100644, 0100644, \
+ "d427e0b2e138501a3d15cc376077a3631e15bd46", \
+ "4e886e602529caa9ab11d71f86634bd1b6e0de10", \
+ "2bd0a343aeef7a2cf0d158478966a6e587ff3863" }
+#define REMOVED_IN_BRANCH_REUC_ENTRY \
+ { "removed-in-branch.txt", 0100644, 0100644, 0, \
+ "dfe3f22baa1f6fce5447901c3086bae368de6bdd", \
+ "dfe3f22baa1f6fce5447901c3086bae368de6bdd", \
+ "" }
+#define REMOVED_IN_MASTER_REUC_ENTRY \
+ { "removed-in-master.txt", 0100644, 0, 0100644, \
+ "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", \
+ "", \
+ "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5" }
+
+#define AUTOMERGEABLE_MERGED_FILE \
+ "this file is changed in master\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is changed in branch\n"
+
+#define AUTOMERGEABLE_MERGED_FILE_CRLF \
+ "this file is changed in master\r\n" \
+ "this file is automergeable\r\n" \
+ "this file is automergeable\r\n" \
+ "this file is automergeable\r\n" \
+ "this file is automergeable\r\n" \
+ "this file is automergeable\r\n" \
+ "this file is automergeable\r\n" \
+ "this file is automergeable\r\n" \
+ "this file is changed in branch\r\n"
+
+#define CONFLICTING_DIFF3_FILE \
+ "<<<<<<< HEAD\n" \
+ "this file is changed in master and branch\n" \
+ "=======\n" \
+ "this file is changed in branch and master\n" \
+ ">>>>>>> 7cb63eed597130ba4abb87b3e544b85021905520\n"
+
+// Fixture setup and teardown
+void test_merge_workdir_simple__initialize(void)
+{
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+ git_repository_index(&repo_index, repo);
+}
+
+void test_merge_workdir_simple__cleanup(void)
+{
+ git_index_free(repo_index);
+ cl_git_sandbox_cleanup();
+}
+
+static git_merge_result *merge_simple_branch(int automerge_flags, int checkout_strategy)
+{
+ git_oid their_oids[1];
+ git_merge_head *their_heads[1];
+ git_merge_result *result;
+ git_merge_opts opts = GIT_MERGE_OPTS_INIT;
+
+ cl_git_pass(git_oid_fromstr(&their_oids[0], THEIRS_SIMPLE_OID));
+ cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &their_oids[0]));
+
+ opts.merge_tree_opts.automerge_flags = automerge_flags;
+ opts.checkout_opts.checkout_strategy = checkout_strategy;
+ cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, &opts));
+
+ git_merge_head_free(their_heads[0]);
+
+ return result;
+}
+
+static void set_core_autocrlf_to(git_repository *repo, bool value)
+{
+ git_config *cfg;
+
+ cl_git_pass(git_repository_config(&cfg, repo));
+ cl_git_pass(git_config_set_bool(cfg, "core.autocrlf", value));
+
+ git_config_free(cfg);
+}
+
+void test_merge_workdir_simple__automerge(void)
+{
+ git_index *index;
+ const git_index_entry *entry;
+ git_merge_result *result;
+ git_buf automergeable_buf = GIT_BUF_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ ADDED_IN_MASTER_INDEX_ENTRY,
+ AUTOMERGEABLE_INDEX_ENTRY,
+ CHANGED_IN_BRANCH_INDEX_ENTRY,
+ CHANGED_IN_MASTER_INDEX_ENTRY,
+
+ { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" },
+ { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
+ { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" },
+
+ UNCHANGED_INDEX_ENTRY,
+ };
+
+ struct merge_reuc_entry merge_reuc_entries[] = {
+ AUTOMERGEABLE_REUC_ENTRY,
+ REMOVED_IN_BRANCH_REUC_ENTRY,
+ REMOVED_IN_MASTER_REUC_ENTRY
+ };
+
+
+ set_core_autocrlf_to(repo, false);
+
+ cl_assert(result = merge_simple_branch(0, 0));
+ cl_assert(!git_merge_result_is_fastforward(result));
+
+ cl_git_pass(git_futils_readbuffer(&automergeable_buf,
+ TEST_REPO_PATH "/automergeable.txt"));
+ cl_assert(strcmp(automergeable_buf.ptr, AUTOMERGEABLE_MERGED_FILE) == 0);
+ git_buf_free(&automergeable_buf);
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 8));
+ cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3));
+
+ git_merge_result_free(result);
+
+ git_repository_index(&index, repo);
+
+ cl_assert((entry = git_index_get_bypath(index, "automergeable.txt", 0)) != NULL);
+ cl_assert(entry->file_size == strlen(AUTOMERGEABLE_MERGED_FILE));
+
+ git_index_free(index);
+}
+
+void test_merge_workdir_simple__automerge_crlf(void)
+{
+#ifdef GIT_WIN32
+ git_index *index;
+ const git_index_entry *entry;
+
+ git_merge_result *result;
+ git_buf automergeable_buf = GIT_BUF_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ ADDED_IN_MASTER_INDEX_ENTRY,
+ AUTOMERGEABLE_INDEX_ENTRY,
+ CHANGED_IN_BRANCH_INDEX_ENTRY,
+ CHANGED_IN_MASTER_INDEX_ENTRY,
+
+ { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" },
+ { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
+ { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" },
+
+ UNCHANGED_INDEX_ENTRY,
+ };
+
+ struct merge_reuc_entry merge_reuc_entries[] = {
+ AUTOMERGEABLE_REUC_ENTRY,
+ REMOVED_IN_BRANCH_REUC_ENTRY,
+ REMOVED_IN_MASTER_REUC_ENTRY
+ };
+
+ set_core_autocrlf_to(repo, true);
+
+ cl_assert(result = merge_simple_branch(0, 0));
+ cl_assert(!git_merge_result_is_fastforward(result));
+
+ cl_git_pass(git_futils_readbuffer(&automergeable_buf,
+ TEST_REPO_PATH "/automergeable.txt"));
+ cl_assert(strcmp(automergeable_buf.ptr, AUTOMERGEABLE_MERGED_FILE_CRLF) == 0);
+ git_buf_free(&automergeable_buf);
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 8));
+ cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3));
+
+ git_merge_result_free(result);
+
+ git_repository_index(&index, repo);
+
+ cl_assert((entry = git_index_get_bypath(index, "automergeable.txt", 0)) != NULL);
+ cl_assert(entry->file_size == strlen(AUTOMERGEABLE_MERGED_FILE_CRLF));
+
+ git_index_free(index);
+#endif /* GIT_WIN32 */
+}
+
+void test_merge_workdir_simple__diff3(void)
+{
+ git_merge_result *result;
+ git_buf conflicting_buf = GIT_BUF_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ ADDED_IN_MASTER_INDEX_ENTRY,
+ AUTOMERGEABLE_INDEX_ENTRY,
+ CHANGED_IN_BRANCH_INDEX_ENTRY,
+ CHANGED_IN_MASTER_INDEX_ENTRY,
+
+ { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" },
+ { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
+ { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" },
+
+ UNCHANGED_INDEX_ENTRY,
+ };
+
+ struct merge_reuc_entry merge_reuc_entries[] = {
+ AUTOMERGEABLE_REUC_ENTRY,
+ REMOVED_IN_BRANCH_REUC_ENTRY,
+ REMOVED_IN_MASTER_REUC_ENTRY
+ };
+
+ cl_assert(result = merge_simple_branch(0, 0));
+ cl_assert(!git_merge_result_is_fastforward(result));
+
+ cl_git_pass(git_futils_readbuffer(&conflicting_buf,
+ TEST_REPO_PATH "/conflicting.txt"));
+ cl_assert(strcmp(conflicting_buf.ptr, CONFLICTING_DIFF3_FILE) == 0);
+ git_buf_free(&conflicting_buf);
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 8));
+ cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3));
+
+ git_merge_result_free(result);
+}
+
+void test_merge_workdir_simple__checkout_ours(void)
+{
+ git_merge_result *result;
+
+ struct merge_index_entry merge_index_entries[] = {
+ ADDED_IN_MASTER_INDEX_ENTRY,
+ AUTOMERGEABLE_INDEX_ENTRY,
+ CHANGED_IN_BRANCH_INDEX_ENTRY,
+ CHANGED_IN_MASTER_INDEX_ENTRY,
+
+ { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" },
+ { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
+ { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" },
+
+ UNCHANGED_INDEX_ENTRY,
+ };
+
+ struct merge_reuc_entry merge_reuc_entries[] = {
+ AUTOMERGEABLE_REUC_ENTRY,
+ REMOVED_IN_BRANCH_REUC_ENTRY,
+ REMOVED_IN_MASTER_REUC_ENTRY
+ };
+
+ cl_assert(result = merge_simple_branch(0, GIT_CHECKOUT_SAFE | GIT_CHECKOUT_USE_OURS));
+ cl_assert(!git_merge_result_is_fastforward(result));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 8));
+ cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3));
+
+ cl_assert(git_path_exists(TEST_REPO_PATH "/conflicting.txt"));
+
+ git_merge_result_free(result);
+}
+
+void test_merge_workdir_simple__favor_ours(void)
+{
+ git_merge_result *result;
+
+ struct merge_index_entry merge_index_entries[] = {
+ ADDED_IN_MASTER_INDEX_ENTRY,
+ AUTOMERGEABLE_INDEX_ENTRY,
+ CHANGED_IN_BRANCH_INDEX_ENTRY,
+ CHANGED_IN_MASTER_INDEX_ENTRY,
+ { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt" },
+ UNCHANGED_INDEX_ENTRY,
+ };
+
+ struct merge_reuc_entry merge_reuc_entries[] = {
+ AUTOMERGEABLE_REUC_ENTRY,
+ CONFLICTING_REUC_ENTRY,
+ REMOVED_IN_BRANCH_REUC_ENTRY,
+ REMOVED_IN_MASTER_REUC_ENTRY,
+ };
+
+ cl_assert(result = merge_simple_branch(GIT_MERGE_AUTOMERGE_FAVOR_OURS, 0));
+ cl_assert(!git_merge_result_is_fastforward(result));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 6));
+ cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 4));
+
+ git_merge_result_free(result);
+}
+
+void test_merge_workdir_simple__favor_theirs(void)
+{
+ git_merge_result *result;
+
+ struct merge_index_entry merge_index_entries[] = {
+ ADDED_IN_MASTER_INDEX_ENTRY,
+ AUTOMERGEABLE_INDEX_ENTRY,
+ CHANGED_IN_BRANCH_INDEX_ENTRY,
+ CHANGED_IN_MASTER_INDEX_ENTRY,
+ { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 0, "conflicting.txt" },
+ UNCHANGED_INDEX_ENTRY,
+ };
+
+ struct merge_reuc_entry merge_reuc_entries[] = {
+ AUTOMERGEABLE_REUC_ENTRY,
+ CONFLICTING_REUC_ENTRY,
+ REMOVED_IN_BRANCH_REUC_ENTRY,
+ REMOVED_IN_MASTER_REUC_ENTRY,
+ };
+
+ cl_assert(result = merge_simple_branch(GIT_MERGE_AUTOMERGE_FAVOR_THEIRS, 0));
+ cl_assert(!git_merge_result_is_fastforward(result));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 6));
+ cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 4));
+
+ git_merge_result_free(result);
+}
+
+void test_merge_workdir_simple__directory_file(void)
+{
+ git_reference *head;
+ git_oid their_oids[1], head_commit_id;
+ git_merge_head *their_heads[1];
+ git_merge_result *result;
+ git_merge_opts opts = GIT_MERGE_OPTS_INIT;
+ git_commit *head_commit;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "49130a28ef567af9a6a6104c38773fedfa5f9742", 2, "dir-10" },
+ { 0100644, "6c06dcd163587c2cc18be44857e0b71116382aeb", 3, "dir-10" },
+ { 0100644, "43aafd43bea779ec74317dc361f45ae3f532a505", 0, "dir-6" },
+ { 0100644, "a031a28ae70e33a641ce4b8a8f6317f1ab79dee4", 3, "dir-7" },
+ { 0100644, "5012fd565b1393bdfda1805d4ec38ce6619e1fd1", 1, "dir-7/file.txt" },
+ { 0100644, "a5563304ddf6caba25cb50323a2ea6f7dbfcadca", 2, "dir-7/file.txt" },
+ { 0100644, "e9ad6ec3e38364a3d07feda7c4197d4d845c53b5", 0, "dir-8" },
+ { 0100644, "3ef4d30382ca33fdeba9fda895a99e0891ba37aa", 2, "dir-9" },
+ { 0100644, "fc4c636d6515e9e261f9260dbcf3cc6eca97ea08", 1, "dir-9/file.txt" },
+ { 0100644, "76ab0e2868197ec158ddd6c78d8a0d2fd73d38f9", 3, "dir-9/file.txt" },
+ { 0100644, "5c2411f8075f48a6b2fdb85ebc0d371747c4df15", 0, "file-1/new" },
+ { 0100644, "a39a620dae5bc8b4e771cd4d251b7d080401a21e", 1, "file-2" },
+ { 0100644, "d963979c237d08b6ba39062ee7bf64c7d34a27f8", 2, "file-2" },
+ { 0100644, "5c341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d", 0, "file-2/new" },
+ { 0100644, "9efe7723802d4305142eee177e018fee1572c4f4", 0, "file-3/new" },
+ { 0100644, "bacac9b3493509aa15e1730e1545fc0919d1dae0", 1, "file-4" },
+ { 0100644, "7663fce0130db092936b137cabd693ec234eb060", 3, "file-4" },
+ { 0100644, "e49f917b448d1340b31d76e54ba388268fd4c922", 0, "file-4/new" },
+ { 0100644, "cab2cf23998b40f1af2d9d9a756dc9e285a8df4b", 2, "file-5/new" },
+ { 0100644, "f5504f36e6f4eb797a56fc5bac6c6c7f32969bf2", 3, "file-5/new" },
+ };
+
+ cl_git_pass(git_reference_symbolic_create(&head, repo, GIT_HEAD_FILE, GIT_REFS_HEADS_DIR OURS_DIRECTORY_FILE, 1));
+ cl_git_pass(git_reference_name_to_id(&head_commit_id, repo, GIT_HEAD_FILE));
+ cl_git_pass(git_commit_lookup(&head_commit, repo, &head_commit_id));
+ cl_git_pass(git_reset(repo, (git_object *)head_commit, GIT_RESET_HARD));
+
+ cl_git_pass(git_oid_fromstr(&their_oids[0], THEIRS_DIRECTORY_FILE));
+ cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &their_oids[0]));
+
+ opts.merge_tree_opts.automerge_flags = 0;
+ cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, &opts));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 20));
+
+ git_reference_free(head);
+ git_commit_free(head_commit);
+ git_merge_head_free(their_heads[0]);
+ git_merge_result_free(result);
+}
+
+void test_merge_workdir_simple__unrelated(void)
+{
+ git_oid their_oids[1];
+ git_merge_head *their_heads[1];
+ git_merge_result *result;
+ git_merge_opts opts = GIT_MERGE_OPTS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" },
+ { 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 0, "automergeable.txt" },
+ { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt" },
+ { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" },
+ { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt" },
+ { 0100644, "ef58fdd8086c243bdc81f99e379acacfd21d32d6", 0, "new-in-unrelated1.txt" },
+ { 0100644, "948ba6e701c1edab0c2d394fb7c5538334129793", 0, "new-in-unrelated2.txt" },
+ { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" },
+ { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" },
+ };
+
+ cl_git_pass(git_oid_fromstr(&their_oids[0], THEIRS_UNRELATED_PARENT));
+ cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &their_oids[0]));
+
+ opts.merge_tree_opts.automerge_flags = 0;
+ cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, &opts));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 9));
+
+ git_merge_head_free(their_heads[0]);
+ git_merge_result_free(result);
+}
+
+void test_merge_workdir_simple__unrelated_with_conflicts(void)
+{
+ git_oid their_oids[1];
+ git_merge_head *their_heads[1];
+ git_merge_result *result;
+ git_merge_opts opts = GIT_MERGE_OPTS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" },
+ { 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 2, "automergeable.txt" },
+ { 0100644, "d07ec190c306ec690bac349e87d01c4358e49bb2", 3, "automergeable.txt" },
+ { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt" },
+ { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" },
+ { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
+ { 0100644, "4b253da36a0ae8bfce63aeabd8c5b58429925594", 3, "conflicting.txt" },
+ { 0100644, "ef58fdd8086c243bdc81f99e379acacfd21d32d6", 0, "new-in-unrelated1.txt" },
+ { 0100644, "948ba6e701c1edab0c2d394fb7c5538334129793", 0, "new-in-unrelated2.txt" },
+ { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" },
+ { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" },
+ };
+
+ cl_git_pass(git_oid_fromstr(&their_oids[0], THEIRS_UNRELATED_OID));
+ cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &their_oids[0]));
+
+ opts.merge_tree_opts.automerge_flags = 0;
+ cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, &opts));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 11));
+
+ git_merge_head_free(their_heads[0]);
+ git_merge_result_free(result);
+}
+
diff --git a/tests/merge/workdir/trivial.c b/tests/merge/workdir/trivial.c
new file mode 100644
index 000000000..9f9566243
--- /dev/null
+++ b/tests/merge/workdir/trivial.c
@@ -0,0 +1,341 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/merge.h"
+#include "git2/sys/index.h"
+#include "merge.h"
+#include "../merge_helpers.h"
+#include "refs.h"
+#include "fileops.h"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+#define TEST_REPO_PATH "merge-resolve"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+
+// Fixture setup and teardown
+void test_merge_workdir_trivial__initialize(void)
+{
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+ git_repository_index(&repo_index, repo);
+}
+
+void test_merge_workdir_trivial__cleanup(void)
+{
+ git_index_free(repo_index);
+ cl_git_sandbox_cleanup();
+}
+
+
+static int merge_trivial(const char *ours, const char *theirs, bool automerge)
+{
+ git_buf branch_buf = GIT_BUF_INIT;
+ git_checkout_opts checkout_opts = GIT_CHECKOUT_OPTS_INIT;
+ git_reference *our_ref, *their_ref;
+ git_merge_head *their_heads[1];
+ git_merge_opts opts = GIT_MERGE_OPTS_INIT;
+ git_merge_result *result;
+
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ opts.merge_tree_opts.automerge_flags |= automerge ? 0 : GIT_MERGE_AUTOMERGE_NONE;
+
+ git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, ours);
+ cl_git_pass(git_reference_symbolic_create(&our_ref, repo, "HEAD", branch_buf.ptr, 1));
+
+ cl_git_pass(git_checkout_head(repo, &checkout_opts));
+
+ git_buf_clear(&branch_buf);
+ git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, theirs);
+ cl_git_pass(git_reference_lookup(&their_ref, repo, branch_buf.ptr));
+ cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, their_ref));
+
+ cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, &opts));
+
+ git_buf_free(&branch_buf);
+ git_reference_free(our_ref);
+ git_reference_free(their_ref);
+ git_merge_head_free(their_heads[0]);
+ git_merge_result_free(result);
+
+ return 0;
+}
+
+static size_t merge_trivial_conflict_entrycount(void)
+{
+ const git_index_entry *entry;
+ size_t count = 0;
+ size_t i;
+
+ for (i = 0; i < git_index_entrycount(repo_index); i++) {
+ cl_assert(entry = git_index_get_byindex(repo_index, i));
+
+ if (git_index_entry_stage(entry) > 0)
+ count++;
+ }
+
+ return count;
+}
+
+/* 2ALT: ancest:(empty)+, head:*empty*, remote:remote = result:remote */
+void test_merge_workdir_trivial__2alt(void)
+{
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial("trivial-2alt", "trivial-2alt-branch", 0));
+
+ cl_assert(entry = git_index_get_bypath(repo_index, "new-in-branch.txt", 0));
+ cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+ cl_assert(merge_trivial_conflict_entrycount() == 0);
+}
+
+/* 3ALT: ancest:(empty)+, head:head, remote:*empty* = result:head */
+void test_merge_workdir_trivial__3alt(void)
+{
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial("trivial-3alt", "trivial-3alt-branch", 0));
+
+ cl_assert(entry = git_index_get_bypath(repo_index, "new-in-3alt.txt", 0));
+ cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+ cl_assert(merge_trivial_conflict_entrycount() == 0);
+}
+
+/* 4: ancest:(empty)^, head:head, remote:remote = result:no merge */
+void test_merge_workdir_trivial__4(void)
+{
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial("trivial-4", "trivial-4-branch", 0));
+
+ cl_assert((entry = git_index_get_bypath(repo_index, "new-and-different.txt", 0)) == NULL);
+ cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+
+ cl_assert(merge_trivial_conflict_entrycount() == 2);
+ cl_assert(entry = git_index_get_bypath(repo_index, "new-and-different.txt", 2));
+ cl_assert(entry = git_index_get_bypath(repo_index, "new-and-different.txt", 3));
+}
+
+/* 5ALT: ancest:*, head:head, remote:head = result:head */
+void test_merge_workdir_trivial__5alt_1(void)
+{
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial("trivial-5alt-1", "trivial-5alt-1-branch", 0));
+
+ cl_assert(entry = git_index_get_bypath(repo_index, "new-and-same.txt", 0));
+ cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+ cl_assert(merge_trivial_conflict_entrycount() == 0);
+}
+
+/* 5ALT: ancest:*, head:head, remote:head = result:head */
+void test_merge_workdir_trivial__5alt_2(void)
+{
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial("trivial-5alt-2", "trivial-5alt-2-branch", 0));
+
+ cl_assert(entry = git_index_get_bypath(repo_index, "modified-to-same.txt", 0));
+ cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+ cl_assert(merge_trivial_conflict_entrycount() == 0);
+}
+
+/* 6: ancest:ancest+, head:(empty), remote:(empty) = result:no merge */
+void test_merge_workdir_trivial__6(void)
+{
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial("trivial-6", "trivial-6-branch", 0));
+
+ cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-both.txt", 0)) == NULL);
+ cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+
+ cl_assert(merge_trivial_conflict_entrycount() == 1);
+ cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-both.txt", 1));
+}
+
+/* 6: ancest:ancest+, head:(empty), remote:(empty) = result:no merge */
+void test_merge_workdir_trivial__6_automerge(void)
+{
+ const git_index_entry *entry;
+ const git_index_reuc_entry *reuc;
+
+ cl_git_pass(merge_trivial("trivial-6", "trivial-6-branch", 1));
+
+ cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-both.txt", 0)) == NULL);
+ cl_assert(git_index_reuc_entrycount(repo_index) == 1);
+ cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "removed-in-both.txt"));
+
+ cl_assert(merge_trivial_conflict_entrycount() == 0);
+}
+
+/* 8: ancest:ancest^, head:(empty), remote:ancest = result:no merge */
+void test_merge_workdir_trivial__8(void)
+{
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial("trivial-8", "trivial-8-branch", 0));
+
+ cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-8.txt", 0)) == NULL);
+ cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+
+ cl_assert(merge_trivial_conflict_entrycount() == 2);
+ cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-8.txt", 1));
+ cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-8.txt", 3));
+}
+
+/* 8: ancest:ancest^, head:(empty), remote:ancest = result:no merge */
+void test_merge_workdir_trivial__8_automerge(void)
+{
+ const git_index_entry *entry;
+ const git_index_reuc_entry *reuc;
+
+ cl_git_pass(merge_trivial("trivial-8", "trivial-8-branch", 1));
+
+ cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-8.txt", 0)) == NULL);
+
+ cl_assert(git_index_reuc_entrycount(repo_index) == 1);
+ cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "removed-in-8.txt"));
+
+ cl_assert(merge_trivial_conflict_entrycount() == 0);
+}
+
+/* 7: ancest:ancest+, head:(empty), remote:remote = result:no merge */
+void test_merge_workdir_trivial__7(void)
+{
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial("trivial-7", "trivial-7-branch", 0));
+
+ cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-7.txt", 0)) == NULL);
+ cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+
+ cl_assert(merge_trivial_conflict_entrycount() == 2);
+ cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-7.txt", 1));
+ cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-7.txt", 3));
+}
+
+/* 7: ancest:ancest+, head:(empty), remote:remote = result:no merge */
+void test_merge_workdir_trivial__7_automerge(void)
+{
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial("trivial-7", "trivial-7-branch", 0));
+
+ cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-7.txt", 0)) == NULL);
+ cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+
+ cl_assert(merge_trivial_conflict_entrycount() == 2);
+ cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-7.txt", 1));
+ cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-7.txt", 3));
+}
+
+/* 10: ancest:ancest^, head:ancest, remote:(empty) = result:no merge */
+void test_merge_workdir_trivial__10(void)
+{
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial("trivial-10", "trivial-10-branch", 0));
+
+ cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-10-branch.txt", 0)) == NULL);
+ cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+
+ cl_assert(merge_trivial_conflict_entrycount() == 2);
+ cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-10-branch.txt", 1));
+ cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-10-branch.txt", 2));
+}
+
+/* 10: ancest:ancest^, head:ancest, remote:(empty) = result:no merge */
+void test_merge_workdir_trivial__10_automerge(void)
+{
+ const git_index_entry *entry;
+ const git_index_reuc_entry *reuc;
+
+ cl_git_pass(merge_trivial("trivial-10", "trivial-10-branch", 1));
+
+ cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-10-branch.txt", 0)) == NULL);
+
+ cl_assert(git_index_reuc_entrycount(repo_index) == 1);
+ cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "removed-in-10-branch.txt"));
+
+ cl_assert(merge_trivial_conflict_entrycount() == 0);
+}
+
+/* 9: ancest:ancest+, head:head, remote:(empty) = result:no merge */
+void test_merge_workdir_trivial__9(void)
+{
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial("trivial-9", "trivial-9-branch", 0));
+
+ cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-9-branch.txt", 0)) == NULL);
+ cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+
+ cl_assert(merge_trivial_conflict_entrycount() == 2);
+ cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-9-branch.txt", 1));
+ cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-9-branch.txt", 2));
+}
+
+/* 9: ancest:ancest+, head:head, remote:(empty) = result:no merge */
+void test_merge_workdir_trivial__9_automerge(void)
+{
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial("trivial-9", "trivial-9-branch", 1));
+
+ cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-9-branch.txt", 0)) == NULL);
+ cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+
+ cl_assert(merge_trivial_conflict_entrycount() == 2);
+ cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-9-branch.txt", 1));
+ cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-9-branch.txt", 2));
+}
+
+/* 13: ancest:ancest+, head:head, remote:ancest = result:head */
+void test_merge_workdir_trivial__13(void)
+{
+ const git_index_entry *entry;
+ git_oid expected_oid;
+
+ cl_git_pass(merge_trivial("trivial-13", "trivial-13-branch", 0));
+
+ cl_assert(entry = git_index_get_bypath(repo_index, "modified-in-13.txt", 0));
+ cl_git_pass(git_oid_fromstr(&expected_oid, "1cff9ec6a47a537380dedfdd17c9e76d74259a2b"));
+ cl_assert(git_oid_cmp(&entry->oid, &expected_oid) == 0);
+
+ cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+ cl_assert(merge_trivial_conflict_entrycount() == 0);
+}
+
+/* 14: ancest:ancest+, head:ancest, remote:remote = result:remote */
+void test_merge_workdir_trivial__14(void)
+{
+ const git_index_entry *entry;
+ git_oid expected_oid;
+
+ cl_git_pass(merge_trivial("trivial-14", "trivial-14-branch", 0));
+
+ cl_assert(entry = git_index_get_bypath(repo_index, "modified-in-14-branch.txt", 0));
+ cl_git_pass(git_oid_fromstr(&expected_oid, "26153a3ff3649b6c2bb652d3f06878c6e0a172f9"));
+ cl_assert(git_oid_cmp(&entry->oid, &expected_oid) == 0);
+
+ cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+ cl_assert(merge_trivial_conflict_entrycount() == 0);
+}
+
+/* 11: ancest:ancest+, head:head, remote:remote = result:no merge */
+void test_merge_workdir_trivial__11(void)
+{
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial("trivial-11", "trivial-11-branch", 0));
+
+ cl_assert((entry = git_index_get_bypath(repo_index, "modified-in-both.txt", 0)) == NULL);
+ cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+
+ cl_assert(merge_trivial_conflict_entrycount() == 3);
+ cl_assert(entry = git_index_get_bypath(repo_index, "modified-in-both.txt", 1));
+ cl_assert(entry = git_index_get_bypath(repo_index, "modified-in-both.txt", 2));
+ cl_assert(entry = git_index_get_bypath(repo_index, "modified-in-both.txt", 3));
+}
diff --git a/tests-clar/network/cred.c b/tests/network/cred.c
index 6994cc0c3..6994cc0c3 100644
--- a/tests-clar/network/cred.c
+++ b/tests/network/cred.c
diff --git a/tests/network/fetchlocal.c b/tests/network/fetchlocal.c
new file mode 100644
index 000000000..28c7115bf
--- /dev/null
+++ b/tests/network/fetchlocal.c
@@ -0,0 +1,88 @@
+#include "clar_libgit2.h"
+
+#include "buffer.h"
+#include "path.h"
+#include "remote.h"
+
+static int transfer_cb(const git_transfer_progress *stats, void *payload)
+{
+ int *callcount = (int*)payload;
+ GIT_UNUSED(stats);
+ (*callcount)++;
+ return 0;
+}
+
+static void cleanup_local_repo(void *path)
+{
+ cl_fixture_cleanup((char *)path);
+}
+
+void test_network_fetchlocal__complete(void)
+{
+ git_repository *repo;
+ git_remote *origin;
+ int callcount = 0;
+ git_strarray refnames = {0};
+
+ const char *url = cl_git_fixture_url("testrepo.git");
+ git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
+
+ callbacks.transfer_progress = transfer_cb;
+ callbacks.payload = &callcount;
+
+ cl_set_cleanup(&cleanup_local_repo, "foo");
+ cl_git_pass(git_repository_init(&repo, "foo", true));
+
+ cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url));
+ git_remote_set_callbacks(origin, &callbacks);
+ cl_git_pass(git_remote_connect(origin, GIT_DIRECTION_FETCH));
+ cl_git_pass(git_remote_download(origin));
+ cl_git_pass(git_remote_update_tips(origin));
+
+ cl_git_pass(git_reference_list(&refnames, repo));
+ cl_assert_equal_i(19, (int)refnames.count);
+ cl_assert(callcount > 0);
+
+ git_strarray_free(&refnames);
+ git_remote_free(origin);
+ 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");
+ git_remote *origin;
+ int callcount = 0;
+ git_strarray refnames = {0};
+ const char *url;
+ git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
+
+ callbacks.transfer_progress = transfer_cb;
+ 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);
+
+ url = cl_git_fixture_url("testrepo.git");
+ cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url));
+ git_remote_set_callbacks(origin, &callbacks);
+ cl_git_pass(git_remote_connect(origin, GIT_DIRECTION_FETCH));
+ cl_git_pass(git_remote_download(origin));
+ cl_git_pass(git_remote_update_tips(origin));
+
+ git_strarray_free(&refnames);
+
+ cl_git_pass(git_reference_list(&refnames, repo));
+ cl_assert_equal_i(20, (int)refnames.count); /* 18 remote + 1 local */
+ cl_assert(callcount > 0);
+
+ git_strarray_free(&refnames);
+ git_remote_free(origin);
+}
diff --git a/tests-clar/network/refspecs.c b/tests/network/refspecs.c
index 676a1fa99..676a1fa99 100644
--- a/tests-clar/network/refspecs.c
+++ b/tests/network/refspecs.c
diff --git a/tests-clar/network/remote/createthenload.c b/tests/network/remote/createthenload.c
index ac6cfccd3..ac6cfccd3 100644
--- a/tests-clar/network/remote/createthenload.c
+++ b/tests/network/remote/createthenload.c
diff --git a/tests-clar/network/remote/isvalidname.c b/tests/network/remote/isvalidname.c
index c26fbd0a5..c26fbd0a5 100644
--- a/tests-clar/network/remote/isvalidname.c
+++ b/tests/network/remote/isvalidname.c
diff --git a/tests/network/remote/local.c b/tests/network/remote/local.c
new file mode 100644
index 000000000..309142925
--- /dev/null
+++ b/tests/network/remote/local.c
@@ -0,0 +1,234 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "path.h"
+#include "posix.h"
+
+static git_repository *repo;
+static git_buf file_path_buf = GIT_BUF_INIT;
+static git_remote *remote;
+
+void test_network_remote_local__initialize(void)
+{
+ cl_git_pass(git_repository_init(&repo, "remotelocal/", 0));
+ cl_assert(repo != NULL);
+}
+
+void test_network_remote_local__cleanup(void)
+{
+ git_buf_free(&file_path_buf);
+
+ git_remote_free(remote);
+ remote = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+
+ cl_fixture_cleanup("remotelocal");
+}
+
+static void connect_to_local_repository(const char *local_repository)
+{
+ git_buf_sets(&file_path_buf, cl_git_path_url(local_repository));
+
+ cl_git_pass(git_remote_create_inmemory(&remote, repo, NULL, git_buf_cstr(&file_path_buf)));
+ cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
+}
+
+void test_network_remote_local__connected(void)
+{
+ connect_to_local_repository(cl_fixture("testrepo.git"));
+ cl_assert(git_remote_connected(remote));
+
+ git_remote_disconnect(remote);
+ cl_assert(!git_remote_connected(remote));
+}
+
+void test_network_remote_local__retrieve_advertised_references(void)
+{
+ const git_remote_head **refs;
+ size_t refs_len;
+
+ connect_to_local_repository(cl_fixture("testrepo.git"));
+
+ cl_git_pass(git_remote_ls(&refs, &refs_len, remote));
+
+ cl_assert_equal_i(refs_len, 28);
+}
+
+void test_network_remote_local__retrieve_advertised_references_after_disconnect(void)
+{
+ const git_remote_head **refs;
+ size_t refs_len;
+
+ connect_to_local_repository(cl_fixture("testrepo.git"));
+ git_remote_disconnect(remote);
+
+ cl_git_pass(git_remote_ls(&refs, &refs_len, remote));
+
+ cl_assert_equal_i(refs_len, 28);
+}
+
+void test_network_remote_local__retrieve_advertised_references_from_spaced_repository(void)
+{
+ const git_remote_head **refs;
+ size_t refs_len;
+
+ cl_fixture_sandbox("testrepo.git");
+ cl_git_pass(p_rename("testrepo.git", "spaced testrepo.git"));
+
+ connect_to_local_repository("spaced testrepo.git");
+
+ cl_git_pass(git_remote_ls(&refs, &refs_len, remote));
+
+ cl_assert_equal_i(refs_len, 28);
+
+ git_remote_free(remote); /* Disconnect from the "spaced repo" before the cleanup */
+ remote = NULL;
+
+ cl_fixture_cleanup("spaced testrepo.git");
+}
+
+void test_network_remote_local__nested_tags_are_completely_peeled(void)
+{
+ const git_remote_head **refs;
+ size_t refs_len, i;
+
+ connect_to_local_repository(cl_fixture("testrepo.git"));
+
+ cl_git_pass(git_remote_ls(&refs, &refs_len, remote));
+
+ for (i = 0; i < refs_len; i++) {
+ if (!strcmp(refs[i]->name, "refs/tags/test^{}"))
+ cl_git_pass(git_oid_streq(&refs[i]->oid, "e90810b8df3e80c413d903f631643c716887138d"));
+ }
+}
+
+void test_network_remote_local__shorthand_fetch_refspec0(void)
+{
+ const char *refspec = "master:remotes/sloppy/master";
+ const char *refspec2 = "master:boh/sloppy/master";
+
+ git_reference *ref;
+
+ connect_to_local_repository(cl_fixture("testrepo.git"));
+ cl_git_pass(git_remote_add_fetch(remote, refspec));
+ cl_git_pass(git_remote_add_fetch(remote, refspec2));
+
+ cl_git_pass(git_remote_download(remote));
+ cl_git_pass(git_remote_update_tips(remote));
+
+ cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/sloppy/master"));
+ git_reference_free(ref);
+
+ cl_git_pass(git_reference_lookup(&ref, repo, "refs/heads/boh/sloppy/master"));
+ git_reference_free(ref);
+}
+
+void test_network_remote_local__shorthand_fetch_refspec1(void)
+{
+ const char *refspec = "master";
+ const char *refspec2 = "hard_tag";
+
+ git_reference *ref;
+
+ connect_to_local_repository(cl_fixture("testrepo.git"));
+ git_remote_clear_refspecs(remote);
+ cl_git_pass(git_remote_add_fetch(remote, refspec));
+ cl_git_pass(git_remote_add_fetch(remote, refspec2));
+
+ cl_git_pass(git_remote_download(remote));
+ cl_git_pass(git_remote_update_tips(remote));
+
+ cl_git_fail(git_reference_lookup(&ref, repo, "refs/remotes/master"));
+
+ cl_git_fail(git_reference_lookup(&ref, repo, "refs/tags/hard_tag"));
+}
+
+void test_network_remote_local__tagopt(void)
+{
+ git_reference *ref;
+
+ connect_to_local_repository(cl_fixture("testrepo.git"));
+ git_remote_set_autotag(remote, GIT_REMOTE_DOWNLOAD_TAGS_ALL);
+
+ cl_git_pass(git_remote_download(remote));
+ cl_git_pass(git_remote_update_tips(remote));
+
+
+ cl_git_fail(git_reference_lookup(&ref, repo, "refs/remotes/master"));
+
+ cl_git_pass(git_reference_lookup(&ref, repo, "refs/tags/hard_tag"));
+ git_reference_free(ref);
+}
+
+void test_network_remote_local__push_to_bare_remote(void)
+{
+ /* Should be able to push to a bare remote */
+ git_remote *localremote;
+ git_push *push;
+
+ /* Get some commits */
+ connect_to_local_repository(cl_fixture("testrepo.git"));
+ cl_git_pass(git_remote_add_fetch(remote, "master:master"));
+ cl_git_pass(git_remote_download(remote));
+ cl_git_pass(git_remote_update_tips(remote));
+ git_remote_disconnect(remote);
+
+ /* Set up an empty bare repo to push into */
+ {
+ git_repository *localbarerepo;
+ cl_git_pass(git_repository_init(&localbarerepo, "./localbare.git", 1));
+ git_repository_free(localbarerepo);
+ }
+
+ /* Connect to the bare repo */
+ cl_git_pass(git_remote_create_inmemory(&localremote, repo, NULL, "./localbare.git"));
+ cl_git_pass(git_remote_connect(localremote, GIT_DIRECTION_PUSH));
+
+ /* Try to push */
+ cl_git_pass(git_push_new(&push, localremote));
+ cl_git_pass(git_push_add_refspec(push, "refs/heads/master:"));
+ cl_git_pass(git_push_finish(push));
+ cl_assert(git_push_unpack_ok(push));
+
+ /* Clean up */
+ git_push_free(push);
+ git_remote_free(localremote);
+ cl_fixture_cleanup("localbare.git");
+}
+
+void test_network_remote_local__push_to_non_bare_remote(void)
+{
+ /* Shouldn't be able to push to a non-bare remote */
+ git_remote *localremote;
+ git_push *push;
+
+ /* Get some commits */
+ connect_to_local_repository(cl_fixture("testrepo.git"));
+ cl_git_pass(git_remote_add_fetch(remote, "master:master"));
+ cl_git_pass(git_remote_download(remote));
+ cl_git_pass(git_remote_update_tips(remote));
+ git_remote_disconnect(remote);
+
+ /* Set up an empty non-bare repo to push into */
+ {
+ git_repository *remoterepo = NULL;
+ cl_git_pass(git_repository_init(&remoterepo, "localnonbare", 0));
+ git_repository_free(remoterepo);
+ }
+
+ /* Connect to the bare repo */
+ cl_git_pass(git_remote_create_inmemory(&localremote, repo, NULL, "./localnonbare"));
+ cl_git_pass(git_remote_connect(localremote, GIT_DIRECTION_PUSH));
+
+ /* Try to push */
+ cl_git_pass(git_push_new(&push, localremote));
+ cl_git_pass(git_push_add_refspec(push, "refs/heads/master:"));
+ cl_git_fail_with(git_push_finish(push), GIT_EBAREREPO);
+ cl_assert_equal_i(0, git_push_unpack_ok(push));
+
+ /* Clean up */
+ git_push_free(push);
+ git_remote_free(localremote);
+ cl_fixture_cleanup("localbare.git");
+}
diff --git a/tests/network/remote/remotes.c b/tests/network/remote/remotes.c
new file mode 100644
index 000000000..954ded82c
--- /dev/null
+++ b/tests/network/remote/remotes.c
@@ -0,0 +1,510 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "refspec.h"
+#include "remote.h"
+
+static git_remote *_remote;
+static git_repository *_repo;
+static const git_refspec *_refspec;
+
+void test_network_remote_remotes__initialize(void)
+{
+ _repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_git_pass(git_remote_load(&_remote, _repo, "test"));
+
+ _refspec = git_remote_get_refspec(_remote, 0);
+ cl_assert(_refspec != NULL);
+}
+
+void test_network_remote_remotes__cleanup(void)
+{
+ git_remote_free(_remote);
+ _remote = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_network_remote_remotes__parsing(void)
+{
+ git_remote *_remote2 = NULL;
+
+ cl_assert_equal_s(git_remote_name(_remote), "test");
+ cl_assert_equal_s(git_remote_url(_remote), "git://github.com/libgit2/libgit2");
+ cl_assert(git_remote_pushurl(_remote) == NULL);
+
+ cl_assert_equal_s(git_remote__urlfordirection(_remote, GIT_DIRECTION_FETCH),
+ "git://github.com/libgit2/libgit2");
+ cl_assert_equal_s(git_remote__urlfordirection(_remote, GIT_DIRECTION_PUSH),
+ "git://github.com/libgit2/libgit2");
+
+ cl_git_pass(git_remote_load(&_remote2, _repo, "test_with_pushurl"));
+ cl_assert_equal_s(git_remote_name(_remote2), "test_with_pushurl");
+ cl_assert_equal_s(git_remote_url(_remote2), "git://github.com/libgit2/fetchlibgit2");
+ cl_assert_equal_s(git_remote_pushurl(_remote2), "git://github.com/libgit2/pushlibgit2");
+
+ cl_assert_equal_s(git_remote__urlfordirection(_remote2, GIT_DIRECTION_FETCH),
+ "git://github.com/libgit2/fetchlibgit2");
+ cl_assert_equal_s(git_remote__urlfordirection(_remote2, GIT_DIRECTION_PUSH),
+ "git://github.com/libgit2/pushlibgit2");
+
+ git_remote_free(_remote2);
+}
+
+void test_network_remote_remotes__pushurl(void)
+{
+ cl_git_pass(git_remote_set_pushurl(_remote, "git://github.com/libgit2/notlibgit2"));
+ cl_assert_equal_s(git_remote_pushurl(_remote), "git://github.com/libgit2/notlibgit2");
+
+ cl_git_pass(git_remote_set_pushurl(_remote, NULL));
+ cl_assert(git_remote_pushurl(_remote) == NULL);
+}
+
+void test_network_remote_remotes__error_when_no_push_available(void)
+{
+ git_remote *r;
+ git_transport *t;
+ git_push *p;
+
+ cl_git_pass(git_remote_create_inmemory(&r, _repo, NULL, cl_fixture("testrepo.git")));
+
+ cl_git_pass(git_transport_local(&t,r,NULL));
+
+ /* Make sure that push is really not available */
+ t->push = NULL;
+ cl_git_pass(git_remote_set_transport(r, t));
+
+ cl_git_pass(git_remote_connect(r, GIT_DIRECTION_PUSH));
+ cl_git_pass(git_push_new(&p, r));
+ cl_git_pass(git_push_add_refspec(p, "refs/heads/master"));
+ cl_git_fail_with(git_push_finish(p), GIT_ERROR);
+
+ git_push_free(p);
+ git_remote_free(r);
+}
+
+void test_network_remote_remotes__parsing_ssh_remote(void)
+{
+ cl_assert( git_remote_valid_url("git@github.com:libgit2/libgit2.git") );
+}
+
+void test_network_remote_remotes__parsing_local_path_fails_if_path_not_found(void)
+{
+ cl_assert( !git_remote_valid_url("/home/git/repos/libgit2.git") );
+}
+
+void test_network_remote_remotes__supported_transport_methods_are_supported(void)
+{
+ cl_assert( git_remote_supported_url("git://github.com/libgit2/libgit2") );
+}
+
+void test_network_remote_remotes__unsupported_transport_methods_are_unsupported(void)
+{
+#ifndef GIT_SSH
+ cl_assert( !git_remote_supported_url("git@github.com:libgit2/libgit2.git") );
+#endif
+}
+
+void test_network_remote_remotes__refspec_parsing(void)
+{
+ cl_assert_equal_s(git_refspec_src(_refspec), "refs/heads/*");
+ cl_assert_equal_s(git_refspec_dst(_refspec), "refs/remotes/test/*");
+}
+
+void test_network_remote_remotes__add_fetchspec(void)
+{
+ size_t size;
+
+ size = git_remote_refspec_count(_remote);
+
+ cl_git_pass(git_remote_add_fetch(_remote, "refs/*:refs/*"));
+
+ size++;
+ cl_assert_equal_i((int)size, (int)git_remote_refspec_count(_remote));
+
+ _refspec = git_remote_get_refspec(_remote, size - 1);
+ cl_assert_equal_s(git_refspec_src(_refspec), "refs/*");
+ cl_assert_equal_s(git_refspec_dst(_refspec), "refs/*");
+ cl_assert_equal_s(git_refspec_string(_refspec), "refs/*:refs/*");
+ cl_assert_equal_b(_refspec->push, false);
+}
+
+void test_network_remote_remotes__add_pushspec(void)
+{
+ size_t size;
+
+ size = git_remote_refspec_count(_remote);
+
+ cl_git_pass(git_remote_add_push(_remote, "refs/*:refs/*"));
+ size++;
+ cl_assert_equal_i((int)size, (int)git_remote_refspec_count(_remote));
+
+ _refspec = git_remote_get_refspec(_remote, size - 1);
+ cl_assert_equal_s(git_refspec_src(_refspec), "refs/*");
+ cl_assert_equal_s(git_refspec_dst(_refspec), "refs/*");
+ cl_assert_equal_s(git_refspec_string(_refspec), "refs/*:refs/*");
+
+ cl_assert_equal_b(_refspec->push, true);
+}
+
+void test_network_remote_remotes__save(void)
+{
+ git_strarray array;
+ const char *fetch_refspec1 = "refs/heads/ns1/*:refs/remotes/upstream/ns1/*";
+ const char *fetch_refspec2 = "refs/heads/ns2/*:refs/remotes/upstream/ns2/*";
+ const char *push_refspec1 = "refs/heads/ns1/*:refs/heads/ns1/*";
+ const char *push_refspec2 = "refs/heads/ns2/*:refs/heads/ns2/*";
+
+ git_remote_free(_remote);
+ _remote = NULL;
+
+ /* Set up the remote and save it to config */
+ cl_git_pass(git_remote_create(&_remote, _repo, "upstream", "git://github.com/libgit2/libgit2"));
+ git_remote_clear_refspecs(_remote);
+
+ cl_git_pass(git_remote_add_fetch(_remote, fetch_refspec1));
+ cl_git_pass(git_remote_add_fetch(_remote, fetch_refspec2));
+ cl_git_pass(git_remote_add_push(_remote, push_refspec1));
+ cl_git_pass(git_remote_add_push(_remote, push_refspec2));
+ cl_git_pass(git_remote_set_pushurl(_remote, "git://github.com/libgit2/libgit2_push"));
+ cl_git_pass(git_remote_save(_remote));
+ git_remote_free(_remote);
+ _remote = NULL;
+
+ /* Load it from config and make sure everything matches */
+ cl_git_pass(git_remote_load(&_remote, _repo, "upstream"));
+
+ cl_git_pass(git_remote_get_fetch_refspecs(&array, _remote));
+ cl_assert_equal_i(2, (int)array.count);
+ cl_assert_equal_s(fetch_refspec1, array.strings[0]);
+ cl_assert_equal_s(fetch_refspec2, array.strings[1]);
+ git_strarray_free(&array);
+
+ cl_git_pass(git_remote_get_push_refspecs(&array, _remote));
+ cl_assert_equal_i(2, (int)array.count);
+ cl_assert_equal_s(push_refspec1, array.strings[0]);
+ cl_assert_equal_s(push_refspec2, array.strings[1]);
+ git_strarray_free(&array);
+
+ cl_assert_equal_s(git_remote_url(_remote), "git://github.com/libgit2/libgit2");
+ cl_assert_equal_s(git_remote_pushurl(_remote), "git://github.com/libgit2/libgit2_push");
+
+ /* remove the pushurl again and see if we can save that too */
+ cl_git_pass(git_remote_set_pushurl(_remote, NULL));
+ cl_git_pass(git_remote_save(_remote));
+ git_remote_free(_remote);
+ _remote = NULL;
+
+ cl_git_pass(git_remote_load(&_remote, _repo, "upstream"));
+ cl_assert(git_remote_pushurl(_remote) == NULL);
+}
+
+void test_network_remote_remotes__fnmatch(void)
+{
+ cl_assert(git_refspec_src_matches(_refspec, "refs/heads/master"));
+ cl_assert(git_refspec_src_matches(_refspec, "refs/heads/multi/level/branch"));
+}
+
+void test_network_remote_remotes__transform(void)
+{
+ char ref[1024] = {0};
+
+ cl_git_pass(git_refspec_transform(ref, sizeof(ref), _refspec, "refs/heads/master"));
+ cl_assert_equal_s(ref, "refs/remotes/test/master");
+}
+
+void test_network_remote_remotes__transform_destination_to_source(void)
+{
+ char ref[1024] = {0};
+
+ cl_git_pass(git_refspec_rtransform(ref, sizeof(ref), _refspec, "refs/remotes/test/master"));
+ cl_assert_equal_s(ref, "refs/heads/master");
+}
+
+void test_network_remote_remotes__transform_r(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_refspec_transform_r(&buf, _refspec, "refs/heads/master"));
+ cl_assert_equal_s(git_buf_cstr(&buf), "refs/remotes/test/master");
+ git_buf_free(&buf);
+}
+
+void test_network_remote_remotes__missing_refspecs(void)
+{
+ git_config *cfg;
+
+ git_remote_free(_remote);
+ _remote = NULL;
+
+ cl_git_pass(git_repository_config(&cfg, _repo));
+ cl_git_pass(git_config_set_string(cfg, "remote.specless.url", "http://example.com"));
+ cl_git_pass(git_remote_load(&_remote, _repo, "specless"));
+
+ git_config_free(cfg);
+}
+
+void test_network_remote_remotes__list(void)
+{
+ git_strarray list;
+ git_config *cfg;
+
+ cl_git_pass(git_remote_list(&list, _repo));
+ cl_assert(list.count == 5);
+ git_strarray_free(&list);
+
+ cl_git_pass(git_repository_config(&cfg, _repo));
+
+ /* Create a new remote */
+ cl_git_pass(git_config_set_string(cfg, "remote.specless.url", "http://example.com"));
+
+ /* Update a remote (previously without any url/pushurl entry) */
+ cl_git_pass(git_config_set_string(cfg, "remote.no-remote-url.pushurl", "http://example.com"));
+
+ cl_git_pass(git_remote_list(&list, _repo));
+ cl_assert(list.count == 7);
+ git_strarray_free(&list);
+
+ git_config_free(cfg);
+}
+
+void test_network_remote_remotes__loading_a_missing_remote_returns_ENOTFOUND(void)
+{
+ git_remote_free(_remote);
+ _remote = NULL;
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_remote_load(&_remote, _repo, "just-left-few-minutes-ago"));
+}
+
+void test_network_remote_remotes__loading_with_an_invalid_name_returns_EINVALIDSPEC(void)
+{
+ git_remote_free(_remote);
+ _remote = NULL;
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC, git_remote_load(&_remote, _repo, "Inv@{id"));
+}
+
+/*
+ * $ git remote add addtest http://github.com/libgit2/libgit2
+ *
+ * $ cat .git/config
+ * [...]
+ * [remote "addtest"]
+ * url = http://github.com/libgit2/libgit2
+ * fetch = +refs/heads/\*:refs/remotes/addtest/\*
+ */
+void test_network_remote_remotes__add(void)
+{
+ git_remote_free(_remote);
+ _remote = NULL;
+
+ cl_git_pass(git_remote_create(&_remote, _repo, "addtest", "http://github.com/libgit2/libgit2"));
+ cl_assert_equal_i(GIT_REMOTE_DOWNLOAD_TAGS_AUTO, git_remote_autotag(_remote));
+
+ git_remote_free(_remote);
+ _remote = NULL;
+
+ cl_git_pass(git_remote_load(&_remote, _repo, "addtest"));
+ cl_assert_equal_i(GIT_REMOTE_DOWNLOAD_TAGS_AUTO, git_remote_autotag(_remote));
+
+ _refspec = git_vector_get(&_remote->refspecs, 0);
+ cl_assert_equal_s("refs/heads/*", git_refspec_src(_refspec));
+ cl_assert(git_refspec_force(_refspec) == 1);
+ cl_assert_equal_s("refs/remotes/addtest/*", git_refspec_dst(_refspec));
+ cl_assert_equal_s(git_remote_url(_remote), "http://github.com/libgit2/libgit2");
+}
+
+void test_network_remote_remotes__cannot_add_a_nameless_remote(void)
+{
+ git_remote *remote;
+
+ cl_assert_equal_i(
+ GIT_EINVALIDSPEC,
+ git_remote_create(&remote, _repo, NULL, "git://github.com/libgit2/libgit2"));
+}
+
+void test_network_remote_remotes__cannot_save_an_inmemory_remote(void)
+{
+ git_remote *remote;
+
+ cl_git_pass(git_remote_create_inmemory(&remote, _repo, NULL, "git://github.com/libgit2/libgit2"));
+
+ cl_assert_equal_p(NULL, git_remote_name(remote));
+
+ cl_git_fail(git_remote_save(remote));
+ git_remote_free(remote);
+}
+
+void test_network_remote_remotes__cannot_add_a_remote_with_an_invalid_name(void)
+{
+ git_remote *remote = NULL;
+
+ cl_assert_equal_i(
+ GIT_EINVALIDSPEC,
+ git_remote_create(&remote, _repo, "Inv@{id", "git://github.com/libgit2/libgit2"));
+ cl_assert_equal_p(remote, NULL);
+
+ cl_assert_equal_i(
+ GIT_EINVALIDSPEC,
+ git_remote_create(&remote, _repo, "", "git://github.com/libgit2/libgit2"));
+ cl_assert_equal_p(remote, NULL);
+}
+
+void test_network_remote_remotes__tagopt(void)
+{
+ const char *opt;
+ git_config *cfg;
+
+ cl_git_pass(git_repository_config(&cfg, _repo));
+
+ git_remote_set_autotag(_remote, GIT_REMOTE_DOWNLOAD_TAGS_ALL);
+ cl_git_pass(git_remote_save(_remote));
+ cl_git_pass(git_config_get_string(&opt, cfg, "remote.test.tagopt"));
+ cl_assert_equal_s("--tags", opt);
+
+ git_remote_set_autotag(_remote, GIT_REMOTE_DOWNLOAD_TAGS_NONE);
+ cl_git_pass(git_remote_save(_remote));
+ cl_git_pass(git_config_get_string(&opt, cfg, "remote.test.tagopt"));
+ cl_assert_equal_s("--no-tags", opt);
+
+ git_remote_set_autotag(_remote, GIT_REMOTE_DOWNLOAD_TAGS_AUTO);
+ cl_git_pass(git_remote_save(_remote));
+ cl_assert(git_config_get_string(&opt, cfg, "remote.test.tagopt") == GIT_ENOTFOUND);
+
+ git_config_free(cfg);
+}
+
+void test_network_remote_remotes__can_load_with_an_empty_url(void)
+{
+ git_remote *remote = NULL;
+
+ cl_git_pass(git_remote_load(&remote, _repo, "empty-remote-url"));
+
+ cl_assert(remote->url == NULL);
+ cl_assert(remote->pushurl == NULL);
+
+ cl_git_fail(git_remote_connect(remote, GIT_DIRECTION_FETCH));
+
+ cl_assert(giterr_last() != NULL);
+ cl_assert(giterr_last()->klass == GITERR_INVALID);
+
+ git_remote_free(remote);
+}
+
+void test_network_remote_remotes__can_load_with_only_an_empty_pushurl(void)
+{
+ git_remote *remote = NULL;
+
+ cl_git_pass(git_remote_load(&remote, _repo, "empty-remote-pushurl"));
+
+ cl_assert(remote->url == NULL);
+ cl_assert(remote->pushurl == NULL);
+
+ cl_git_fail(git_remote_connect(remote, GIT_DIRECTION_FETCH));
+
+ git_remote_free(remote);
+}
+
+void test_network_remote_remotes__returns_ENOTFOUND_when_neither_url_nor_pushurl(void)
+{
+ git_remote *remote = NULL;
+
+ cl_git_fail_with(
+ git_remote_load(&remote, _repo, "no-remote-url"), GIT_ENOTFOUND);
+}
+
+void test_network_remote_remotes__check_structure_version(void)
+{
+ git_transport transport = GIT_TRANSPORT_INIT;
+ const git_error *err;
+
+ git_remote_free(_remote);
+ _remote = NULL;
+ cl_git_pass(git_remote_create_inmemory(&_remote, _repo, NULL, "test-protocol://localhost"));
+
+ transport.version = 0;
+ cl_git_fail(git_remote_set_transport(_remote, &transport));
+ err = giterr_last();
+ cl_assert_equal_i(GITERR_INVALID, err->klass);
+
+ giterr_clear();
+ transport.version = 1024;
+ cl_git_fail(git_remote_set_transport(_remote, &transport));
+ err = giterr_last();
+ cl_assert_equal_i(GITERR_INVALID, err->klass);
+}
+
+void assert_cannot_create_remote(const char *name, int expected_error)
+{
+ git_remote *remote = NULL;
+
+ cl_git_fail_with(
+ git_remote_create(&remote, _repo, name, "git://github.com/libgit2/libgit2"),
+ expected_error);
+
+ cl_assert_equal_p(remote, NULL);
+}
+
+void test_network_remote_remotes__cannot_create_a_remote_which_name_conflicts_with_an_existing_remote(void)
+{
+ assert_cannot_create_remote("test", GIT_EEXISTS);
+}
+
+void test_network_remote_remotes__cannot_create_a_remote_which_name_is_invalid(void)
+{
+ assert_cannot_create_remote("/", GIT_EINVALIDSPEC);
+ assert_cannot_create_remote("//", GIT_EINVALIDSPEC);
+ assert_cannot_create_remote(".lock", GIT_EINVALIDSPEC);
+ assert_cannot_create_remote("a.lock", GIT_EINVALIDSPEC);
+}
+
+void test_network_remote_remote__git_remote_create_with_fetchspec(void)
+{
+ git_remote *remote;
+ git_strarray array;
+
+ cl_git_pass(git_remote_create_with_fetchspec(&remote, _repo, "test-new", "git://github.com/libgit2/libgit2", "+refs/*:refs/*"));
+ git_remote_get_fetch_refspecs(&array, remote);
+ cl_assert_equal_s("+refs/*:refs/*", array.strings[0]);
+ git_remote_free(remote);
+}
+
+static const char *fetch_refspecs[] = {
+ "+refs/heads/*:refs/remotes/origin/*",
+ "refs/tags/*:refs/tags/*",
+ "+refs/pull/*:refs/pull/*",
+};
+
+static const char *push_refspecs[] = {
+ "refs/heads/*:refs/heads/*",
+ "refs/tags/*:refs/tags/*",
+ "refs/notes/*:refs/notes/*",
+};
+
+void test_network_remote_remotes__query_refspecs(void)
+{
+ git_remote *remote;
+ git_strarray array;
+ int i;
+
+ cl_git_pass(git_remote_create_inmemory(&remote, _repo, NULL, "git://github.com/libgit2/libgit2"));
+
+ for (i = 0; i < 3; i++) {
+ cl_git_pass(git_remote_add_fetch(remote, fetch_refspecs[i]));
+ cl_git_pass(git_remote_add_push(remote, push_refspecs[i]));
+ }
+
+ cl_git_pass(git_remote_get_fetch_refspecs(&array, remote));
+ for (i = 0; i < 3; i++) {
+ cl_assert_equal_s(fetch_refspecs[i], array.strings[i]);
+ }
+ git_strarray_free(&array);
+
+ cl_git_pass(git_remote_get_push_refspecs(&array, remote));
+ for (i = 0; i < 3; i++) {
+ cl_assert_equal_s(push_refspecs[i], array.strings[i]);
+ }
+ git_strarray_free(&array);
+
+ git_remote_free(remote);
+}
diff --git a/tests-clar/network/remote/rename.c b/tests/network/remote/rename.c
index ed98ee811..ed98ee811 100644
--- a/tests-clar/network/remote/rename.c
+++ b/tests/network/remote/rename.c
diff --git a/tests/network/urlparse.c b/tests/network/urlparse.c
new file mode 100644
index 000000000..2a9c2f69f
--- /dev/null
+++ b/tests/network/urlparse.c
@@ -0,0 +1,193 @@
+#include "clar_libgit2.h"
+#include "netops.h"
+
+static char *host, *port, *path, *user, *pass;
+static gitno_connection_data conndata;
+
+void test_network_urlparse__initialize(void)
+{
+ host = port = path = user = pass = NULL;
+ memset(&conndata, 0, sizeof(conndata));
+}
+
+void test_network_urlparse__cleanup(void)
+{
+#define FREE_AND_NULL(x) if (x) { git__free(x); x = NULL; }
+ FREE_AND_NULL(host);
+ FREE_AND_NULL(port);
+ FREE_AND_NULL(path);
+ FREE_AND_NULL(user);
+ FREE_AND_NULL(pass);
+
+ gitno_connection_data_free_ptrs(&conndata);
+}
+
+void test_network_urlparse__trivial(void)
+{
+ cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
+ "http://example.com/resource", "8080"));
+ cl_assert_equal_s(host, "example.com");
+ cl_assert_equal_s(port, "8080");
+ cl_assert_equal_s(path, "/resource");
+ cl_assert_equal_p(user, NULL);
+ cl_assert_equal_p(pass, NULL);
+}
+
+void test_network_urlparse__encoded_password(void)
+{
+ cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
+ "https://user:pass%2fis%40bad@hostname.com:1234/", "1"));
+ cl_assert_equal_s(host, "hostname.com");
+ cl_assert_equal_s(port, "1234");
+ cl_assert_equal_s(path, "/");
+ cl_assert_equal_s(user, "user");
+ cl_assert_equal_s(pass, "pass/is@bad");
+}
+
+void test_network_urlparse__user(void)
+{
+ cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
+ "https://user@example.com/resource", "8080"));
+ cl_assert_equal_s(host, "example.com");
+ cl_assert_equal_s(port, "8080");
+ cl_assert_equal_s(path, "/resource");
+ cl_assert_equal_s(user, "user");
+ cl_assert_equal_p(pass, NULL);
+}
+
+void test_network_urlparse__user_pass(void)
+{
+ /* user:pass@hostname.tld/resource */
+ cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
+ "https://user:pass@example.com/resource", "8080"));
+ cl_assert_equal_s(host, "example.com");
+ cl_assert_equal_s(port, "8080");
+ cl_assert_equal_s(path, "/resource");
+ cl_assert_equal_s(user, "user");
+ cl_assert_equal_s(pass, "pass");
+}
+
+void test_network_urlparse__port(void)
+{
+ /* hostname.tld:port/resource */
+ cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
+ "https://example.com:9191/resource", "8080"));
+ cl_assert_equal_s(host, "example.com");
+ cl_assert_equal_s(port, "9191");
+ cl_assert_equal_s(path, "/resource");
+ cl_assert_equal_p(user, NULL);
+ cl_assert_equal_p(pass, NULL);
+}
+
+void test_network_urlparse__user_port(void)
+{
+ /* user@hostname.tld:port/resource */
+ cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
+ "https://user@example.com:9191/resource", "8080"));
+ cl_assert_equal_s(host, "example.com");
+ cl_assert_equal_s(port, "9191");
+ cl_assert_equal_s(path, "/resource");
+ cl_assert_equal_s(user, "user");
+ cl_assert_equal_p(pass, NULL);
+}
+
+void test_network_urlparse__user_pass_port(void)
+{
+ /* user:pass@hostname.tld:port/resource */
+ cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
+ "https://user:pass@example.com:9191/resource", "8080"));
+ cl_assert_equal_s(host, "example.com");
+ cl_assert_equal_s(port, "9191");
+ cl_assert_equal_s(path, "/resource");
+ cl_assert_equal_s(user, "user");
+ cl_assert_equal_s(pass, "pass");
+}
+
+void test_network_urlparse__connection_data_http(void)
+{
+ cl_git_pass(gitno_connection_data_from_url(&conndata,
+ "http://example.com/foo/bar/baz", "bar/baz"));
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/foo/");
+ cl_assert_equal_p(conndata.user, NULL);
+ cl_assert_equal_p(conndata.pass, NULL);
+ cl_assert_equal_i(conndata.use_ssl, false);
+}
+
+void test_network_urlparse__connection_data_ssl(void)
+{
+ cl_git_pass(gitno_connection_data_from_url(&conndata,
+ "https://example.com/foo/bar/baz", "bar/baz"));
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/foo/");
+ cl_assert_equal_p(conndata.user, NULL);
+ cl_assert_equal_p(conndata.pass, NULL);
+ cl_assert_equal_i(conndata.use_ssl, true);
+}
+
+void test_network_urlparse__encoded_username_password(void)
+{
+ cl_git_pass(gitno_connection_data_from_url(&conndata,
+ "https://user%2fname:pass%40word%zyx%v@example.com/foo/bar/baz", "bar/baz"));
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/foo/");
+ cl_assert_equal_s(conndata.user, "user/name");
+ cl_assert_equal_s(conndata.pass, "pass@word%zyx%v");
+ cl_assert_equal_i(conndata.use_ssl, true);
+}
+
+void test_network_urlparse__connection_data_cross_host_redirect(void)
+{
+ conndata.host = git__strdup("bar.com");
+ cl_git_fail_with(gitno_connection_data_from_url(&conndata,
+ "https://foo.com/bar/baz", NULL),
+ -1);
+}
+
+void test_network_urlparse__connection_data_http_downgrade(void)
+{
+ conndata.use_ssl = true;
+ cl_git_fail_with(gitno_connection_data_from_url(&conndata,
+ "http://foo.com/bar/baz", NULL),
+ -1);
+}
+
+void test_network_urlparse__connection_data_relative_redirect(void)
+{
+ cl_git_pass(gitno_connection_data_from_url(&conndata,
+ "http://foo.com/bar/baz/biff", NULL));
+ cl_git_pass(gitno_connection_data_from_url(&conndata,
+ "/zap/baz/biff?bam", NULL));
+ cl_assert_equal_s(conndata.host, "foo.com");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/zap/baz/biff?bam");
+ cl_assert_equal_p(conndata.user, NULL);
+ cl_assert_equal_p(conndata.pass, NULL);
+ cl_assert_equal_i(conndata.use_ssl, false);
+}
+
+void test_network_urlparse__connection_data_relative_redirect_ssl(void)
+{
+ cl_git_pass(gitno_connection_data_from_url(&conndata,
+ "https://foo.com/bar/baz/biff", NULL));
+ cl_git_pass(gitno_connection_data_from_url(&conndata,
+ "/zap/baz/biff?bam", NULL));
+ cl_assert_equal_s(conndata.host, "foo.com");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/zap/baz/biff?bam");
+ cl_assert_equal_p(conndata.user, NULL);
+ cl_assert_equal_p(conndata.pass, NULL);
+ cl_assert_equal_i(conndata.use_ssl, true);
+}
+
+/* Run this under valgrind */
+void test_network_urlparse__connection_data_cleanup(void)
+{
+ cl_git_pass(gitno_connection_data_from_url(&conndata,
+ "http://foo.com/bar/baz/biff", "baz/biff"));
+ cl_git_pass(gitno_connection_data_from_url(&conndata,
+ "https://foo.com/bar/baz/biff", "baz/biff"));
+}
diff --git a/tests-clar/notes/notes.c b/tests/notes/notes.c
index 82dcaf8ca..82dcaf8ca 100644
--- a/tests-clar/notes/notes.c
+++ b/tests/notes/notes.c
diff --git a/tests-clar/notes/notesref.c b/tests/notes/notesref.c
index c89b71ba5..c89b71ba5 100644
--- a/tests-clar/notes/notesref.c
+++ b/tests/notes/notesref.c
diff --git a/tests/object/blob/filter.c b/tests/object/blob/filter.c
new file mode 100644
index 000000000..0b2d6bf9e
--- /dev/null
+++ b/tests/object/blob/filter.c
@@ -0,0 +1,143 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "blob.h"
+#include "buf_text.h"
+
+static git_repository *g_repo = NULL;
+
+#define CRLF_NUM_TEST_OBJECTS 9
+
+static const char *g_crlf_raw[CRLF_NUM_TEST_OBJECTS] = {
+ "",
+ "foo\nbar\n",
+ "foo\rbar\r",
+ "foo\r\nbar\r\n",
+ "foo\nbar\rboth\r\nreversed\n\ragain\nproblems\r",
+ "123\n\000\001\002\003\004abc\255\254\253\r\n",
+ "\xEF\xBB\xBFThis is UTF-8\n",
+ "\xEF\xBB\xBF\xE3\x81\xBB\xE3\x81\x92\xE3\x81\xBB\xE3\x81\x92\r\n\xE3\x81\xBB\xE3\x81\x92\xE3\x81\xBB\xE3\x81\x92\r\n",
+ "\xFE\xFF\x00T\x00h\x00i\x00s\x00!"
+};
+
+static git_off_t g_crlf_raw_len[CRLF_NUM_TEST_OBJECTS] = {
+ -1, -1, -1, -1, -1, 17, -1, -1, 12
+};
+
+static git_oid g_crlf_oids[CRLF_NUM_TEST_OBJECTS];
+
+static git_buf g_crlf_filtered[CRLF_NUM_TEST_OBJECTS] = {
+ { "", 0, 0 },
+ { "foo\nbar\n", 0, 8 },
+ { "foo\rbar\r", 0, 8 },
+ { "foo\nbar\n", 0, 8 },
+ { "foo\nbar\rboth\nreversed\n\ragain\nproblems\r", 0, 38 },
+ { "123\n\000\001\002\003\004abc\255\254\253\n", 0, 16 },
+ { "\xEF\xBB\xBFThis is UTF-8\n", 0, 17 },
+ { "\xEF\xBB\xBF\xE3\x81\xBB\xE3\x81\x92\xE3\x81\xBB\xE3\x81\x92\n\xE3\x81\xBB\xE3\x81\x92\xE3\x81\xBB\xE3\x81\x92\n", 0, 29 },
+ { "\xFE\xFF\x00T\x00h\x00i\x00s\x00!", 0, 12 }
+};
+
+static git_buf_text_stats g_crlf_filtered_stats[CRLF_NUM_TEST_OBJECTS] = {
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 2, 0, 6, 0 },
+ { 0, 0, 2, 0, 0, 6, 0 },
+ { 0, 0, 2, 2, 2, 6, 0 },
+ { 0, 0, 4, 4, 1, 31, 0 },
+ { 0, 1, 1, 2, 1, 9, 5 },
+ { GIT_BOM_UTF8, 0, 0, 1, 0, 16, 0 },
+ { GIT_BOM_UTF8, 0, 2, 2, 2, 27, 0 },
+ { GIT_BOM_UTF16_BE, 5, 0, 0, 0, 7, 5 },
+};
+
+void test_object_blob_filter__initialize(void)
+{
+ int i;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ for (i = 0; i < CRLF_NUM_TEST_OBJECTS; i++) {
+ if (g_crlf_raw_len[i] < 0)
+ g_crlf_raw_len[i] = strlen(g_crlf_raw[i]);
+
+ cl_git_pass(git_blob_create_frombuffer(
+ &g_crlf_oids[i], g_repo, g_crlf_raw[i], (size_t)g_crlf_raw_len[i]));
+ }
+}
+
+void test_object_blob_filter__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_object_blob_filter__unfiltered(void)
+{
+ int i;
+ git_blob *blob;
+
+ for (i = 0; i < CRLF_NUM_TEST_OBJECTS; i++) {
+ size_t raw_len = (size_t)g_crlf_raw_len[i];
+
+ cl_git_pass(git_blob_lookup(&blob, g_repo, &g_crlf_oids[i]));
+
+ cl_assert_equal_sz(raw_len, (size_t)git_blob_rawsize(blob));
+ cl_assert_equal_i(
+ 0, memcmp(g_crlf_raw[i], git_blob_rawcontent(blob), raw_len));
+
+ git_blob_free(blob);
+ }
+}
+
+void test_object_blob_filter__stats(void)
+{
+ int i;
+ git_blob *blob;
+ git_buf buf = GIT_BUF_INIT;
+ git_buf_text_stats stats;
+
+ for (i = 0; i < CRLF_NUM_TEST_OBJECTS; i++) {
+ cl_git_pass(git_blob_lookup(&blob, g_repo, &g_crlf_oids[i]));
+ cl_git_pass(git_blob__getbuf(&buf, blob));
+ git_buf_text_gather_stats(&stats, &buf, false);
+ cl_assert_equal_i(
+ 0, memcmp(&g_crlf_filtered_stats[i], &stats, sizeof(stats)));
+ git_blob_free(blob);
+ }
+
+ git_buf_free(&buf);
+}
+
+void test_object_blob_filter__to_odb(void)
+{
+ git_filter_list *fl = NULL;
+ git_config *cfg;
+ int i;
+ git_blob *blob;
+ git_buf out = GIT_BUF_INIT;
+
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_assert(cfg);
+
+ git_attr_cache_flush(g_repo);
+ cl_git_append2file("empty_standard_repo/.gitattributes", "*.txt text\n");
+
+ cl_git_pass(git_filter_list_load(
+ &fl, g_repo, NULL, "filename.txt", GIT_FILTER_TO_ODB));
+ cl_assert(fl != NULL);
+
+ for (i = 0; i < CRLF_NUM_TEST_OBJECTS; i++) {
+ cl_git_pass(git_blob_lookup(&blob, g_repo, &g_crlf_oids[i]));
+
+ cl_git_pass(git_filter_list_apply_to_blob(&out, fl, blob));
+
+ cl_assert_equal_sz(g_crlf_filtered[i].size, out.size);
+
+ cl_assert_equal_i(
+ 0, memcmp(out.ptr, g_crlf_filtered[i].ptr, out.size));
+
+ git_blob_free(blob);
+ }
+
+ git_filter_list_free(fl);
+ git_buf_free(&out);
+ git_config_free(cfg);
+}
diff --git a/tests/object/blob/fromchunks.c b/tests/object/blob/fromchunks.c
new file mode 100644
index 000000000..03ed4efb4
--- /dev/null
+++ b/tests/object/blob/fromchunks.c
@@ -0,0 +1,119 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "posix.h"
+#include "path.h"
+#include "fileops.h"
+
+static git_repository *repo;
+static char textual_content[] = "libgit2\n\r\n\0";
+
+void test_object_blob_fromchunks__initialize(void)
+{
+ repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_object_blob_fromchunks__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static int text_chunked_source_cb(char *content, size_t max_length, void *payload)
+{
+ int *count;
+
+ GIT_UNUSED(max_length);
+
+ count = (int *)payload;
+ (*count)--;
+
+ if (*count == 0)
+ return 0;
+
+ strcpy(content, textual_content);
+ return (int)strlen(textual_content);
+}
+
+void test_object_blob_fromchunks__can_create_a_blob_from_a_in_memory_chunk_provider(void)
+{
+ git_oid expected_oid, oid;
+ git_object *blob;
+ int howmany = 7;
+
+ cl_git_pass(git_oid_fromstr(&expected_oid, "321cbdf08803c744082332332838df6bd160f8f9"));
+
+ cl_git_fail_with(
+ git_object_lookup(&blob, repo, &expected_oid, GIT_OBJ_ANY),
+ GIT_ENOTFOUND);
+
+ cl_git_pass(git_blob_create_fromchunks(&oid, repo, NULL, text_chunked_source_cb, &howmany));
+
+ cl_git_pass(git_object_lookup(&blob, repo, &expected_oid, GIT_OBJ_ANY));
+ cl_assert(git_oid_cmp(&expected_oid, git_object_id(blob)) == 0);
+
+ git_object_free(blob);
+}
+
+void test_object_blob_fromchunks__doesnot_overwrite_an_already_existing_object(void)
+{
+ git_buf path = GIT_BUF_INIT;
+ git_buf content = GIT_BUF_INIT;
+ git_oid expected_oid, oid;
+ int howmany = 7;
+
+ cl_git_pass(git_oid_fromstr(&expected_oid, "321cbdf08803c744082332332838df6bd160f8f9"));
+
+ cl_git_pass(git_blob_create_fromchunks(&oid, repo, NULL, text_chunked_source_cb, &howmany));
+
+ /* Let's replace the content of the blob file storage with something else... */
+ cl_git_pass(git_buf_joinpath(&path, git_repository_path(repo), "objects/32/1cbdf08803c744082332332838df6bd160f8f9"));
+ cl_git_pass(p_unlink(git_buf_cstr(&path)));
+ cl_git_mkfile(git_buf_cstr(&path), "boom");
+
+ /* ...request a creation of the same blob... */
+ howmany = 7;
+ cl_git_pass(git_blob_create_fromchunks(&oid, repo, NULL, text_chunked_source_cb, &howmany));
+
+ /* ...and ensure the content of the faked blob file hasn't been altered */
+ cl_git_pass(git_futils_readbuffer(&content, git_buf_cstr(&path)));
+ cl_assert(!git__strcmp("boom", git_buf_cstr(&content)));
+
+ git_buf_free(&path);
+ git_buf_free(&content);
+}
+
+#define GITATTR "* text=auto\n" \
+ "*.txt text\n" \
+ "*.data binary\n"
+
+static void write_attributes(git_repository *repo)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_buf_joinpath(&buf, git_repository_path(repo), "info"));
+ cl_git_pass(git_buf_joinpath(&buf, git_buf_cstr(&buf), "attributes"));
+
+ cl_git_pass(git_futils_mkpath2file(git_buf_cstr(&buf), 0777));
+ cl_git_rewritefile(git_buf_cstr(&buf), GITATTR);
+
+ git_buf_free(&buf);
+}
+
+static void assert_named_chunked_blob(const char *expected_sha, const char *fake_name)
+{
+ git_oid expected_oid, oid;
+ int howmany = 7;
+
+ cl_git_pass(git_oid_fromstr(&expected_oid, expected_sha));
+
+ cl_git_pass(git_blob_create_fromchunks(&oid, repo, fake_name, text_chunked_source_cb, &howmany));
+ cl_assert(git_oid_cmp(&expected_oid, &oid) == 0);
+}
+
+void test_object_blob_fromchunks__creating_a_blob_from_chunks_honors_the_attributes_directives(void)
+{
+ write_attributes(repo);
+
+ assert_named_chunked_blob("321cbdf08803c744082332332838df6bd160f8f9", "dummy.data");
+ assert_named_chunked_blob("e9671e138a780833cb689753570fd10a55be84fb", "dummy.txt");
+ assert_named_chunked_blob("e9671e138a780833cb689753570fd10a55be84fb", "dummy.dunno");
+}
diff --git a/tests-clar/object/blob/write.c b/tests/object/blob/write.c
index 203bc67c1..203bc67c1 100644
--- a/tests-clar/object/blob/write.c
+++ b/tests/object/blob/write.c
diff --git a/tests-clar/object/cache.c b/tests/object/cache.c
index b927b2514..b927b2514 100644
--- a/tests-clar/object/cache.c
+++ b/tests/object/cache.c
diff --git a/tests-clar/object/commit/commitstagedfile.c b/tests/object/commit/commitstagedfile.c
index 9867ab418..9867ab418 100644
--- a/tests-clar/object/commit/commitstagedfile.c
+++ b/tests/object/commit/commitstagedfile.c
diff --git a/tests-clar/object/lookup.c b/tests/object/lookup.c
index cfa6d4678..cfa6d4678 100644
--- a/tests-clar/object/lookup.c
+++ b/tests/object/lookup.c
diff --git a/tests/object/lookupbypath.c b/tests/object/lookupbypath.c
new file mode 100644
index 000000000..31aac7647
--- /dev/null
+++ b/tests/object/lookupbypath.c
@@ -0,0 +1,83 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+
+static git_repository *g_repo;
+static git_tree *g_root_tree;
+static git_commit *g_head_commit;
+static git_object *g_expectedobject,
+ *g_actualobject;
+
+void test_object_lookupbypath__initialize(void)
+{
+ git_reference *head;
+ git_tree_entry *tree_entry;
+
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("attr/.gitted")));
+
+ cl_git_pass(git_repository_head(&head, g_repo));
+ cl_git_pass(git_reference_peel((git_object**)&g_head_commit, head, GIT_OBJ_COMMIT));
+ cl_git_pass(git_commit_tree(&g_root_tree, g_head_commit));
+ cl_git_pass(git_tree_entry_bypath(&tree_entry, g_root_tree, "subdir/subdir_test2.txt"));
+ cl_git_pass(git_object_lookup(&g_expectedobject, g_repo, git_tree_entry_id(tree_entry),
+ GIT_OBJ_ANY));
+
+ git_tree_entry_free(tree_entry);
+ git_reference_free(head);
+
+ g_actualobject = NULL;
+}
+void test_object_lookupbypath__cleanup(void)
+{
+ git_object_free(g_actualobject);
+ git_object_free(g_expectedobject);
+ git_tree_free(g_root_tree);
+ git_commit_free(g_head_commit);
+ g_expectedobject = NULL;
+ git_repository_free(g_repo);
+ g_repo = NULL;
+}
+
+void test_object_lookupbypath__errors(void)
+{
+ cl_assert_equal_i(GIT_EINVALIDSPEC,
+ git_object_lookup_bypath(&g_actualobject, (git_object*)g_root_tree,
+ "subdir/subdir_test2.txt", GIT_OBJ_TREE)); // It's not a tree
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_object_lookup_bypath(&g_actualobject, (git_object*)g_root_tree,
+ "file/doesnt/exist", GIT_OBJ_ANY));
+}
+
+void test_object_lookupbypath__from_root_tree(void)
+{
+ cl_git_pass(git_object_lookup_bypath(&g_actualobject, (git_object*)g_root_tree,
+ "subdir/subdir_test2.txt", GIT_OBJ_BLOB));
+ cl_assert_equal_i(0, git_oid_cmp(git_object_id(g_expectedobject),
+ git_object_id(g_actualobject)));
+}
+
+void test_object_lookupbypath__from_head_commit(void)
+{
+ cl_git_pass(git_object_lookup_bypath(&g_actualobject, (git_object*)g_head_commit,
+ "subdir/subdir_test2.txt", GIT_OBJ_BLOB));
+ cl_assert_equal_i(0, git_oid_cmp(git_object_id(g_expectedobject),
+ git_object_id(g_actualobject)));
+}
+
+void test_object_lookupbypath__from_subdir_tree(void)
+{
+ git_tree_entry *entry = NULL;
+ git_tree *tree = NULL;
+
+ cl_git_pass(git_tree_entry_bypath(&entry, g_root_tree, "subdir"));
+ cl_git_pass(git_tree_lookup(&tree, g_repo, git_tree_entry_id(entry)));
+
+ cl_git_pass(git_object_lookup_bypath(&g_actualobject, (git_object*)tree,
+ "subdir_test2.txt", GIT_OBJ_BLOB));
+ cl_assert_equal_i(0, git_oid_cmp(git_object_id(g_expectedobject),
+ git_object_id(g_actualobject)));
+
+ git_tree_entry_free(entry);
+ git_tree_free(tree);
+}
+
diff --git a/tests-clar/object/message.c b/tests/object/message.c
index 7ef6374b3..7ef6374b3 100644
--- a/tests-clar/object/message.c
+++ b/tests/object/message.c
diff --git a/tests-clar/object/peel.c b/tests/object/peel.c
index b6c9c7a3b..b6c9c7a3b 100644
--- a/tests-clar/object/peel.c
+++ b/tests/object/peel.c
diff --git a/tests-clar/object/raw/chars.c b/tests/object/raw/chars.c
index 206bf7119..206bf7119 100644
--- a/tests-clar/object/raw/chars.c
+++ b/tests/object/raw/chars.c
diff --git a/tests-clar/object/raw/compare.c b/tests/object/raw/compare.c
index 1c9ce4b81..1c9ce4b81 100644
--- a/tests-clar/object/raw/compare.c
+++ b/tests/object/raw/compare.c
diff --git a/tests-clar/object/raw/convert.c b/tests/object/raw/convert.c
index 88b1380a4..88b1380a4 100644
--- a/tests-clar/object/raw/convert.c
+++ b/tests/object/raw/convert.c
diff --git a/tests-clar/object/raw/data.h b/tests/object/raw/data.h
index cf23819f1..cf23819f1 100644
--- a/tests-clar/object/raw/data.h
+++ b/tests/object/raw/data.h
diff --git a/tests-clar/object/raw/fromstr.c b/tests/object/raw/fromstr.c
index 8c11c105f..8c11c105f 100644
--- a/tests-clar/object/raw/fromstr.c
+++ b/tests/object/raw/fromstr.c
diff --git a/tests-clar/object/raw/hash.c b/tests/object/raw/hash.c
index ede31e145..ede31e145 100644
--- a/tests-clar/object/raw/hash.c
+++ b/tests/object/raw/hash.c
diff --git a/tests-clar/object/raw/short.c b/tests/object/raw/short.c
index 813cd86b6..813cd86b6 100644
--- a/tests-clar/object/raw/short.c
+++ b/tests/object/raw/short.c
diff --git a/tests-clar/object/raw/size.c b/tests/object/raw/size.c
index 930c6de23..930c6de23 100644
--- a/tests-clar/object/raw/size.c
+++ b/tests/object/raw/size.c
diff --git a/tests-clar/object/raw/type2string.c b/tests/object/raw/type2string.c
index a3585487f..a3585487f 100644
--- a/tests-clar/object/raw/type2string.c
+++ b/tests/object/raw/type2string.c
diff --git a/tests/object/raw/write.c b/tests/object/raw/write.c
new file mode 100644
index 000000000..273f08f2c
--- /dev/null
+++ b/tests/object/raw/write.c
@@ -0,0 +1,462 @@
+#include "clar_libgit2.h"
+#include "git2/odb_backend.h"
+
+#include "fileops.h"
+#include "odb.h"
+
+typedef struct object_data {
+ char *id; /* object id (sha1) */
+ char *dir; /* object store (fan-out) directory name */
+ char *file; /* object store filename */
+} object_data;
+
+static const char *odb_dir = "test-objects";
+
+void test_body(object_data *d, git_rawobj *o);
+
+
+
+// Helpers
+static void remove_object_files(object_data *d)
+{
+ cl_git_pass(p_unlink(d->file));
+ cl_git_pass(p_rmdir(d->dir));
+ cl_assert(errno != ENOTEMPTY);
+ cl_git_pass(p_rmdir(odb_dir) < 0);
+}
+
+static void streaming_write(git_oid *oid, git_odb *odb, git_rawobj *raw)
+{
+ git_odb_stream *stream;
+ int error;
+
+ cl_git_pass(git_odb_open_wstream(&stream, odb, raw->len, raw->type));
+ git_odb_stream_write(stream, raw->data, raw->len);
+ error = git_odb_stream_finalize_write(oid, stream);
+ git_odb_stream_free(stream);
+ cl_git_pass(error);
+}
+
+static void check_object_files(object_data *d)
+{
+ cl_assert(git_path_exists(d->dir));
+ cl_assert(git_path_exists(d->file));
+}
+
+static void cmp_objects(git_rawobj *o1, git_rawobj *o2)
+{
+ cl_assert(o1->type == o2->type);
+ cl_assert(o1->len == o2->len);
+ if (o1->len > 0)
+ cl_assert(memcmp(o1->data, o2->data, o1->len) == 0);
+}
+
+static void make_odb_dir(void)
+{
+ cl_git_pass(p_mkdir(odb_dir, GIT_OBJECT_DIR_MODE));
+}
+
+
+// Standard test form
+void test_body(object_data *d, git_rawobj *o)
+{
+ git_odb *db;
+ git_oid id1, id2;
+ git_odb_object *obj;
+ git_rawobj tmp;
+
+ make_odb_dir();
+ cl_git_pass(git_odb_open(&db, odb_dir));
+ cl_git_pass(git_oid_fromstr(&id1, d->id));
+
+ streaming_write(&id2, db, o);
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+ check_object_files(d);
+
+ cl_git_pass(git_odb_read(&obj, db, &id1));
+
+ tmp.data = obj->buffer;
+ tmp.len = obj->cached.size;
+ tmp.type = obj->cached.type;
+
+ cmp_objects(&tmp, o);
+
+ git_odb_object_free(obj);
+ git_odb_free(db);
+ remove_object_files(d);
+}
+
+
+void test_object_raw_write__loose_object(void)
+{
+ object_data commit = {
+ "3d7f8a6af076c8c3f20071a8935cdbe8228594d1",
+ "test-objects/3d",
+ "test-objects/3d/7f8a6af076c8c3f20071a8935cdbe8228594d1",
+ };
+
+ unsigned char commit_data[] = {
+ 0x74, 0x72, 0x65, 0x65, 0x20, 0x64, 0x66, 0x66,
+ 0x32, 0x64, 0x61, 0x39, 0x30, 0x62, 0x32, 0x35,
+ 0x34, 0x65, 0x31, 0x62, 0x65, 0x62, 0x38, 0x38,
+ 0x39, 0x64, 0x31, 0x66, 0x31, 0x66, 0x31, 0x32,
+ 0x38, 0x38, 0x62, 0x65, 0x31, 0x38, 0x30, 0x33,
+ 0x37, 0x38, 0x32, 0x64, 0x66, 0x0a, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x20, 0x41, 0x20, 0x55,
+ 0x20, 0x54, 0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x40, 0x65, 0x78,
+ 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x3e, 0x20, 0x31, 0x32, 0x32, 0x37, 0x38,
+ 0x31, 0x34, 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30,
+ 0x30, 0x30, 0x30, 0x0a, 0x63, 0x6f, 0x6d, 0x6d,
+ 0x69, 0x74, 0x74, 0x65, 0x72, 0x20, 0x43, 0x20,
+ 0x4f, 0x20, 0x4d, 0x69, 0x74, 0x74, 0x65, 0x72,
+ 0x20, 0x3c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+ 0x74, 0x65, 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d,
+ 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e,
+ 0x20, 0x31, 0x32, 0x32, 0x37, 0x38, 0x31, 0x34,
+ 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30, 0x30, 0x30,
+ 0x30, 0x0a, 0x0a, 0x41, 0x20, 0x6f, 0x6e, 0x65,
+ 0x2d, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x63, 0x6f,
+ 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x73, 0x75, 0x6d,
+ 0x6d, 0x61, 0x72, 0x79, 0x0a, 0x0a, 0x54, 0x68,
+ 0x65, 0x20, 0x62, 0x6f, 0x64, 0x79, 0x20, 0x6f,
+ 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f,
+ 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x6d, 0x65, 0x73,
+ 0x73, 0x61, 0x67, 0x65, 0x2c, 0x20, 0x63, 0x6f,
+ 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67,
+ 0x20, 0x66, 0x75, 0x72, 0x74, 0x68, 0x65, 0x72,
+ 0x20, 0x65, 0x78, 0x70, 0x6c, 0x61, 0x6e, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x6f, 0x66, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x70, 0x75, 0x72, 0x70,
+ 0x6f, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67,
+ 0x65, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x72, 0x6f,
+ 0x64, 0x75, 0x63, 0x65, 0x64, 0x20, 0x62, 0x79,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d,
+ 0x6d, 0x69, 0x74, 0x2e, 0x0a, 0x0a, 0x53, 0x69,
+ 0x67, 0x6e, 0x65, 0x64, 0x2d, 0x6f, 0x66, 0x2d,
+ 0x62, 0x79, 0x3a, 0x20, 0x41, 0x20, 0x55, 0x20,
+ 0x54, 0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x40, 0x65, 0x78, 0x61,
+ 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x3e, 0x0a,
+ };
+
+ git_rawobj commit_obj = {
+ commit_data,
+ sizeof(commit_data),
+ GIT_OBJ_COMMIT
+ };
+
+ test_body(&commit, &commit_obj);
+}
+
+void test_object_raw_write__loose_tree(void)
+{
+ static object_data tree = {
+ "dff2da90b254e1beb889d1f1f1288be1803782df",
+ "test-objects/df",
+ "test-objects/df/f2da90b254e1beb889d1f1f1288be1803782df",
+ };
+
+ static unsigned char tree_data[] = {
+ 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x6f,
+ 0x6e, 0x65, 0x00, 0x8b, 0x13, 0x78, 0x91, 0x79,
+ 0x1f, 0xe9, 0x69, 0x27, 0xad, 0x78, 0xe6, 0x4b,
+ 0x0a, 0xad, 0x7b, 0xde, 0xd0, 0x8b, 0xdc, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x73, 0x6f,
+ 0x6d, 0x65, 0x00, 0xfd, 0x84, 0x30, 0xbc, 0x86,
+ 0x4c, 0xfc, 0xd5, 0xf1, 0x0e, 0x55, 0x90, 0xf8,
+ 0xa4, 0x47, 0xe0, 0x1b, 0x94, 0x2b, 0xfe, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x77,
+ 0x6f, 0x00, 0x78, 0x98, 0x19, 0x22, 0x61, 0x3b,
+ 0x2a, 0xfb, 0x60, 0x25, 0x04, 0x2f, 0xf6, 0xbd,
+ 0x87, 0x8a, 0xc1, 0x99, 0x4e, 0x85, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x7a, 0x65, 0x72,
+ 0x6f, 0x00, 0xe6, 0x9d, 0xe2, 0x9b, 0xb2, 0xd1,
+ 0xd6, 0x43, 0x4b, 0x8b, 0x29, 0xae, 0x77, 0x5a,
+ 0xd8, 0xc2, 0xe4, 0x8c, 0x53, 0x91,
+ };
+
+ static git_rawobj tree_obj = {
+ tree_data,
+ sizeof(tree_data),
+ GIT_OBJ_TREE
+ };
+
+ test_body(&tree, &tree_obj);
+}
+
+void test_object_raw_write__loose_tag(void)
+{
+ static object_data tag = {
+ "09d373e1dfdc16b129ceec6dd649739911541e05",
+ "test-objects/09",
+ "test-objects/09/d373e1dfdc16b129ceec6dd649739911541e05",
+ };
+
+ static unsigned char tag_data[] = {
+ 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x33,
+ 0x64, 0x37, 0x66, 0x38, 0x61, 0x36, 0x61, 0x66,
+ 0x30, 0x37, 0x36, 0x63, 0x38, 0x63, 0x33, 0x66,
+ 0x32, 0x30, 0x30, 0x37, 0x31, 0x61, 0x38, 0x39,
+ 0x33, 0x35, 0x63, 0x64, 0x62, 0x65, 0x38, 0x32,
+ 0x32, 0x38, 0x35, 0x39, 0x34, 0x64, 0x31, 0x0a,
+ 0x74, 0x79, 0x70, 0x65, 0x20, 0x63, 0x6f, 0x6d,
+ 0x6d, 0x69, 0x74, 0x0a, 0x74, 0x61, 0x67, 0x20,
+ 0x76, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x0a, 0x74,
+ 0x61, 0x67, 0x67, 0x65, 0x72, 0x20, 0x43, 0x20,
+ 0x4f, 0x20, 0x4d, 0x69, 0x74, 0x74, 0x65, 0x72,
+ 0x20, 0x3c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+ 0x74, 0x65, 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d,
+ 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e,
+ 0x20, 0x31, 0x32, 0x32, 0x37, 0x38, 0x31, 0x34,
+ 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30, 0x30, 0x30,
+ 0x30, 0x0a, 0x0a, 0x54, 0x68, 0x69, 0x73, 0x20,
+ 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74,
+ 0x61, 0x67, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63,
+ 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x72, 0x65,
+ 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20, 0x76, 0x30,
+ 0x2e, 0x30, 0x2e, 0x31, 0x0a,
+ };
+
+ static git_rawobj tag_obj = {
+ tag_data,
+ sizeof(tag_data),
+ GIT_OBJ_TAG
+ };
+
+
+ test_body(&tag, &tag_obj);
+}
+
+void test_object_raw_write__zero_length(void)
+{
+ static object_data zero = {
+ "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
+ "test-objects/e6",
+ "test-objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391",
+ };
+
+ static unsigned char zero_data[] = {
+ 0x00 /* dummy data */
+ };
+
+ static git_rawobj zero_obj = {
+ zero_data,
+ 0,
+ GIT_OBJ_BLOB
+ };
+
+ test_body(&zero, &zero_obj);
+}
+
+void test_object_raw_write__one_byte(void)
+{
+ static object_data one = {
+ "8b137891791fe96927ad78e64b0aad7bded08bdc",
+ "test-objects/8b",
+ "test-objects/8b/137891791fe96927ad78e64b0aad7bded08bdc",
+ };
+
+ static unsigned char one_data[] = {
+ 0x0a,
+ };
+
+ static git_rawobj one_obj = {
+ one_data,
+ sizeof(one_data),
+ GIT_OBJ_BLOB
+ };
+
+ test_body(&one, &one_obj);
+}
+
+void test_object_raw_write__two_byte(void)
+{
+ static object_data two = {
+ "78981922613b2afb6025042ff6bd878ac1994e85",
+ "test-objects/78",
+ "test-objects/78/981922613b2afb6025042ff6bd878ac1994e85",
+ };
+
+ static unsigned char two_data[] = {
+ 0x61, 0x0a,
+ };
+
+ static git_rawobj two_obj = {
+ two_data,
+ sizeof(two_data),
+ GIT_OBJ_BLOB
+ };
+
+ test_body(&two, &two_obj);
+}
+
+void test_object_raw_write__several_bytes(void)
+{
+ static object_data some = {
+ "fd8430bc864cfcd5f10e5590f8a447e01b942bfe",
+ "test-objects/fd",
+ "test-objects/fd/8430bc864cfcd5f10e5590f8a447e01b942bfe",
+ };
+
+ static unsigned char some_data[] = {
+ 0x2f, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x54, 0x68,
+ 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20,
+ 0x69, 0x73, 0x20, 0x66, 0x72, 0x65, 0x65, 0x20,
+ 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
+ 0x3b, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x63, 0x61,
+ 0x6e, 0x20, 0x72, 0x65, 0x64, 0x69, 0x73, 0x74,
+ 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x69,
+ 0x74, 0x20, 0x61, 0x6e, 0x64, 0x2f, 0x6f, 0x72,
+ 0x20, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0a,
+ 0x20, 0x2a, 0x20, 0x69, 0x74, 0x20, 0x75, 0x6e,
+ 0x64, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x74, 0x65, 0x72, 0x6d, 0x73, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x47, 0x4e, 0x55,
+ 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
+ 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x2c,
+ 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+ 0x20, 0x32, 0x2c, 0x0a, 0x20, 0x2a, 0x20, 0x61,
+ 0x73, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73,
+ 0x68, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x46, 0x72, 0x65, 0x65, 0x20,
+ 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
+ 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x2a, 0x0a,
+ 0x20, 0x2a, 0x20, 0x49, 0x6e, 0x20, 0x61, 0x64,
+ 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74,
+ 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x65,
+ 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e,
+ 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x47, 0x4e, 0x55, 0x20, 0x47, 0x65, 0x6e,
+ 0x65, 0x72, 0x61, 0x6c, 0x20, 0x50, 0x75, 0x62,
+ 0x6c, 0x69, 0x63, 0x20, 0x4c, 0x69, 0x63, 0x65,
+ 0x6e, 0x73, 0x65, 0x2c, 0x0a, 0x20, 0x2a, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x61, 0x75, 0x74, 0x68,
+ 0x6f, 0x72, 0x73, 0x20, 0x67, 0x69, 0x76, 0x65,
+ 0x20, 0x79, 0x6f, 0x75, 0x20, 0x75, 0x6e, 0x6c,
+ 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x20, 0x70,
+ 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f,
+ 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x6c, 0x69, 0x6e,
+ 0x6b, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f,
+ 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x64, 0x0a, 0x20,
+ 0x2a, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
+ 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69,
+ 0x73, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x69,
+ 0x6e, 0x74, 0x6f, 0x20, 0x63, 0x6f, 0x6d, 0x62,
+ 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6f, 0x74,
+ 0x68, 0x65, 0x72, 0x20, 0x70, 0x72, 0x6f, 0x67,
+ 0x72, 0x61, 0x6d, 0x73, 0x2c, 0x0a, 0x20, 0x2a,
+ 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x6f, 0x20,
+ 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75,
+ 0x74, 0x65, 0x20, 0x74, 0x68, 0x6f, 0x73, 0x65,
+ 0x20, 0x63, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x77, 0x69,
+ 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x61, 0x6e,
+ 0x79, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x2a,
+ 0x20, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x20,
+ 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c,
+ 0x65, 0x2e, 0x20, 0x20, 0x28, 0x54, 0x68, 0x65,
+ 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
+ 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x0a,
+ 0x20, 0x2a, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72,
+ 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20,
+ 0x64, 0x6f, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x79,
+ 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x74, 0x68, 0x65,
+ 0x72, 0x20, 0x72, 0x65, 0x73, 0x70, 0x65, 0x63,
+ 0x74, 0x73, 0x3b, 0x20, 0x66, 0x6f, 0x72, 0x20,
+ 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c,
+ 0x20, 0x74, 0x68, 0x65, 0x79, 0x20, 0x63, 0x6f,
+ 0x76, 0x65, 0x72, 0x0a, 0x20, 0x2a, 0x20, 0x6d,
+ 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x2c,
+ 0x20, 0x61, 0x6e, 0x64, 0x20, 0x64, 0x69, 0x73,
+ 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x6e,
+ 0x6f, 0x74, 0x20, 0x6c, 0x69, 0x6e, 0x6b, 0x65,
+ 0x64, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x0a, 0x20,
+ 0x2a, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x62,
+ 0x69, 0x6e, 0x65, 0x64, 0x20, 0x65, 0x78, 0x65,
+ 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e,
+ 0x29, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20,
+ 0x54, 0x68, 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c,
+ 0x65, 0x20, 0x69, 0x73, 0x20, 0x64, 0x69, 0x73,
+ 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x64,
+ 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x68, 0x6f, 0x70, 0x65, 0x20, 0x74, 0x68, 0x61,
+ 0x74, 0x20, 0x69, 0x74, 0x20, 0x77, 0x69, 0x6c,
+ 0x6c, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65,
+ 0x66, 0x75, 0x6c, 0x2c, 0x20, 0x62, 0x75, 0x74,
+ 0x0a, 0x20, 0x2a, 0x20, 0x57, 0x49, 0x54, 0x48,
+ 0x4f, 0x55, 0x54, 0x20, 0x41, 0x4e, 0x59, 0x20,
+ 0x57, 0x41, 0x52, 0x52, 0x41, 0x4e, 0x54, 0x59,
+ 0x3b, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75,
+ 0x74, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x69,
+ 0x65, 0x64, 0x20, 0x77, 0x61, 0x72, 0x72, 0x61,
+ 0x6e, 0x74, 0x79, 0x20, 0x6f, 0x66, 0x0a, 0x20,
+ 0x2a, 0x20, 0x4d, 0x45, 0x52, 0x43, 0x48, 0x41,
+ 0x4e, 0x54, 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54,
+ 0x59, 0x20, 0x6f, 0x72, 0x20, 0x46, 0x49, 0x54,
+ 0x4e, 0x45, 0x53, 0x53, 0x20, 0x46, 0x4f, 0x52,
+ 0x20, 0x41, 0x20, 0x50, 0x41, 0x52, 0x54, 0x49,
+ 0x43, 0x55, 0x4c, 0x41, 0x52, 0x20, 0x50, 0x55,
+ 0x52, 0x50, 0x4f, 0x53, 0x45, 0x2e, 0x20, 0x20,
+ 0x53, 0x65, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x47, 0x4e, 0x55, 0x0a, 0x20, 0x2a, 0x20, 0x47,
+ 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x20, 0x50,
+ 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x4c, 0x69,
+ 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x66, 0x6f,
+ 0x72, 0x20, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x64,
+ 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x0a,
+ 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x59, 0x6f,
+ 0x75, 0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64,
+ 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x72, 0x65,
+ 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x20, 0x61,
+ 0x20, 0x63, 0x6f, 0x70, 0x79, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x47, 0x4e, 0x55,
+ 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
+ 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x0a,
+ 0x20, 0x2a, 0x20, 0x61, 0x6c, 0x6f, 0x6e, 0x67,
+ 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68,
+ 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72,
+ 0x61, 0x6d, 0x3b, 0x20, 0x73, 0x65, 0x65, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x65,
+ 0x20, 0x43, 0x4f, 0x50, 0x59, 0x49, 0x4e, 0x47,
+ 0x2e, 0x20, 0x20, 0x49, 0x66, 0x20, 0x6e, 0x6f,
+ 0x74, 0x2c, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65,
+ 0x20, 0x74, 0x6f, 0x0a, 0x20, 0x2a, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x46, 0x72, 0x65, 0x65, 0x20,
+ 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
+ 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x35, 0x31, 0x20,
+ 0x46, 0x72, 0x61, 0x6e, 0x6b, 0x6c, 0x69, 0x6e,
+ 0x20, 0x53, 0x74, 0x72, 0x65, 0x65, 0x74, 0x2c,
+ 0x20, 0x46, 0x69, 0x66, 0x74, 0x68, 0x20, 0x46,
+ 0x6c, 0x6f, 0x6f, 0x72, 0x2c, 0x0a, 0x20, 0x2a,
+ 0x20, 0x42, 0x6f, 0x73, 0x74, 0x6f, 0x6e, 0x2c,
+ 0x20, 0x4d, 0x41, 0x20, 0x30, 0x32, 0x31, 0x31,
+ 0x30, 0x2d, 0x31, 0x33, 0x30, 0x31, 0x2c, 0x20,
+ 0x55, 0x53, 0x41, 0x2e, 0x0a, 0x20, 0x2a, 0x2f,
+ 0x0a,
+ };
+
+ static git_rawobj some_obj = {
+ some_data,
+ sizeof(some_data),
+ GIT_OBJ_BLOB
+ };
+
+ test_body(&some, &some_obj);
+}
diff --git a/tests-clar/object/tag/list.c b/tests/object/tag/list.c
index 6d5a24347..6d5a24347 100644
--- a/tests-clar/object/tag/list.c
+++ b/tests/object/tag/list.c
diff --git a/tests-clar/object/tag/peel.c b/tests/object/tag/peel.c
index e2cd8d6a8..e2cd8d6a8 100644
--- a/tests-clar/object/tag/peel.c
+++ b/tests/object/tag/peel.c
diff --git a/tests-clar/object/tag/read.c b/tests/object/tag/read.c
index c9787a413..c9787a413 100644
--- a/tests-clar/object/tag/read.c
+++ b/tests/object/tag/read.c
diff --git a/tests-clar/object/tag/write.c b/tests/object/tag/write.c
index 68e4b6c61..68e4b6c61 100644
--- a/tests-clar/object/tag/write.c
+++ b/tests/object/tag/write.c
diff --git a/tests/object/tree/attributes.c b/tests/object/tree/attributes.c
new file mode 100644
index 000000000..85216cd1b
--- /dev/null
+++ b/tests/object/tree/attributes.c
@@ -0,0 +1,115 @@
+#include "clar_libgit2.h"
+#include "tree.h"
+
+static const char *blob_oid = "3d0970ec547fc41ef8a5882dde99c6adce65b021";
+static const char *tree_oid = "1b05fdaa881ee45b48cbaa5e9b037d667a47745e";
+
+void test_object_tree_attributes__ensure_correctness_of_attributes_on_insertion(void)
+{
+ git_treebuilder *builder;
+ git_oid oid;
+
+ cl_git_pass(git_oid_fromstr(&oid, blob_oid));
+
+ cl_git_pass(git_treebuilder_create(&builder, NULL));
+
+ cl_git_fail(git_treebuilder_insert(NULL, builder, "one.txt", &oid, (git_filemode_t)0777777));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, "one.txt", &oid, (git_filemode_t)0100666));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, "one.txt", &oid, (git_filemode_t)0000001));
+
+ git_treebuilder_free(builder);
+}
+
+void test_object_tree_attributes__group_writable_tree_entries_created_with_an_antique_git_version_can_still_be_accessed(void)
+{
+ git_repository *repo;
+ git_oid tid;
+ git_tree *tree;
+ const git_tree_entry *entry;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("deprecated-mode.git")));
+
+ cl_git_pass(git_oid_fromstr(&tid, tree_oid));
+ cl_git_pass(git_tree_lookup(&tree, repo, &tid));
+
+ entry = git_tree_entry_byname(tree, "old_mode.txt");
+ cl_assert_equal_i(
+ GIT_FILEMODE_BLOB,
+ git_tree_entry_filemode(entry));
+
+ git_tree_free(tree);
+ git_repository_free(repo);
+}
+
+void test_object_tree_attributes__treebuilder_reject_invalid_filemode(void)
+{
+ git_treebuilder *builder;
+ git_oid bid;
+ const git_tree_entry *entry;
+
+ cl_git_pass(git_oid_fromstr(&bid, blob_oid));
+ cl_git_pass(git_treebuilder_create(&builder, NULL));
+
+ cl_git_fail(git_treebuilder_insert(
+ &entry,
+ builder,
+ "normalized.txt",
+ &bid,
+ GIT_FILEMODE_BLOB_GROUP_WRITABLE));
+
+ git_treebuilder_free(builder);
+}
+
+void test_object_tree_attributes__normalize_attributes_when_creating_a_tree_from_an_existing_one(void)
+{
+ git_repository *repo;
+ git_treebuilder *builder;
+ git_oid tid, tid2;
+ git_tree *tree;
+ const git_tree_entry *entry;
+
+ repo = cl_git_sandbox_init("deprecated-mode.git");
+
+ cl_git_pass(git_oid_fromstr(&tid, tree_oid));
+ cl_git_pass(git_tree_lookup(&tree, repo, &tid));
+
+ cl_git_pass(git_treebuilder_create(&builder, tree));
+
+ entry = git_treebuilder_get(builder, "old_mode.txt");
+ cl_assert_equal_i(
+ GIT_FILEMODE_BLOB,
+ git_tree_entry_filemode(entry));
+
+ cl_git_pass(git_treebuilder_write(&tid2, repo, builder));
+ git_treebuilder_free(builder);
+ git_tree_free(tree);
+
+ cl_git_pass(git_tree_lookup(&tree, repo, &tid2));
+ entry = git_tree_entry_byname(tree, "old_mode.txt");
+ cl_assert_equal_i(
+ GIT_FILEMODE_BLOB,
+ git_tree_entry_filemode(entry));
+
+ git_tree_free(tree);
+ cl_git_sandbox_cleanup();
+}
+
+void test_object_tree_attributes__normalize_600(void)
+{
+ git_oid id;
+ git_tree *tree;
+ git_repository *repo;
+ const git_tree_entry *entry;
+
+ repo = cl_git_sandbox_init("deprecated-mode.git");
+
+ git_oid_fromstr(&id, "0810fb7818088ff5ac41ee49199b51473b1bd6c7");
+ cl_git_pass(git_tree_lookup(&tree, repo, &id));
+
+ entry = git_tree_entry_byname(tree, "ListaTeste.xml");
+ cl_assert_equal_i(git_tree_entry_filemode(entry), GIT_FILEMODE_BLOB);
+ cl_assert_equal_i(git_tree_entry_filemode_raw(entry), 0100600);
+
+ git_tree_free(tree);
+ cl_git_sandbox_cleanup();
+}
diff --git a/tests-clar/object/tree/duplicateentries.c b/tests/object/tree/duplicateentries.c
index 9262f9a1a..9262f9a1a 100644
--- a/tests-clar/object/tree/duplicateentries.c
+++ b/tests/object/tree/duplicateentries.c
diff --git a/tests-clar/object/tree/frompath.c b/tests/object/tree/frompath.c
index 86ca47e94..86ca47e94 100644
--- a/tests-clar/object/tree/frompath.c
+++ b/tests/object/tree/frompath.c
diff --git a/tests-clar/object/tree/read.c b/tests/object/tree/read.c
index 59a809bf1..59a809bf1 100644
--- a/tests-clar/object/tree/read.c
+++ b/tests/object/tree/read.c
diff --git a/tests/object/tree/walk.c b/tests/object/tree/walk.c
new file mode 100644
index 000000000..1207e864c
--- /dev/null
+++ b/tests/object/tree/walk.c
@@ -0,0 +1,177 @@
+#include "clar_libgit2.h"
+#include "tree.h"
+
+static const char *tree_oid = "1810dff58d8a660512d4832e740f692884338ccd";
+static git_repository *g_repo;
+
+void test_object_tree_walk__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_object_tree_walk__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static int treewalk_count_cb(
+ const char *root, const git_tree_entry *entry, void *payload)
+{
+ int *count = payload;
+
+ GIT_UNUSED(root);
+ GIT_UNUSED(entry);
+
+ (*count) += 1;
+
+ return 0;
+}
+
+void test_object_tree_walk__0(void)
+{
+ git_oid id;
+ git_tree *tree;
+ int ct;
+
+ git_oid_fromstr(&id, tree_oid);
+
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+
+ ct = 0;
+ cl_git_pass(git_tree_walk(tree, GIT_TREEWALK_PRE, treewalk_count_cb, &ct));
+ cl_assert_equal_i(3, ct);
+
+ ct = 0;
+ cl_git_pass(git_tree_walk(tree, GIT_TREEWALK_POST, treewalk_count_cb, &ct));
+ cl_assert_equal_i(3, ct);
+
+ git_tree_free(tree);
+}
+
+
+static int treewalk_stop_cb(
+ const char *root, const git_tree_entry *entry, void *payload)
+{
+ int *count = payload;
+
+ GIT_UNUSED(root);
+ GIT_UNUSED(entry);
+
+ (*count) += 1;
+
+ return (*count == 2) ? -1 : 0;
+}
+
+static int treewalk_stop_immediately_cb(
+ const char *root, const git_tree_entry *entry, void *payload)
+{
+ GIT_UNUSED(root);
+ GIT_UNUSED(entry);
+ GIT_UNUSED(payload);
+ return -100;
+}
+
+void test_object_tree_walk__1(void)
+{
+ git_oid id;
+ git_tree *tree;
+ int ct;
+
+ git_oid_fromstr(&id, tree_oid);
+
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+
+ ct = 0;
+ cl_assert_equal_i(
+ GIT_EUSER, git_tree_walk(tree, GIT_TREEWALK_PRE, treewalk_stop_cb, &ct));
+ cl_assert_equal_i(2, ct);
+
+ ct = 0;
+ cl_assert_equal_i(
+ GIT_EUSER, git_tree_walk(tree, GIT_TREEWALK_POST, treewalk_stop_cb, &ct));
+ cl_assert_equal_i(2, ct);
+
+ cl_assert_equal_i(
+ GIT_EUSER, git_tree_walk(
+ tree, GIT_TREEWALK_PRE, treewalk_stop_immediately_cb, NULL));
+
+ cl_assert_equal_i(
+ GIT_EUSER, git_tree_walk(
+ tree, GIT_TREEWALK_POST, treewalk_stop_immediately_cb, NULL));
+
+ git_tree_free(tree);
+}
+
+
+struct treewalk_skip_data {
+ int files;
+ int dirs;
+ const char *skip;
+ const char *stop;
+};
+
+static int treewalk_skip_de_cb(
+ const char *root, const git_tree_entry *entry, void *payload)
+{
+ struct treewalk_skip_data *data = payload;
+ const char *name = git_tree_entry_name(entry);
+
+ GIT_UNUSED(root);
+
+ if (git_tree_entry_type(entry) == GIT_OBJ_TREE)
+ data->dirs++;
+ else
+ data->files++;
+
+ if (data->skip && !strcmp(name, data->skip))
+ return 1;
+ else if (data->stop && !strcmp(name, data->stop))
+ return -1;
+ else
+ return 0;
+}
+
+void test_object_tree_walk__2(void)
+{
+ git_oid id;
+ git_tree *tree;
+ struct treewalk_skip_data data;
+
+ /* look up a deep tree */
+ git_oid_fromstr(&id, "ae90f12eea699729ed24555e40b9fd669da12a12");
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+
+ memset(&data, 0, sizeof(data));
+ data.skip = "de";
+
+ cl_assert_equal_i(0, git_tree_walk(
+ tree, GIT_TREEWALK_PRE, treewalk_skip_de_cb, &data));
+ cl_assert_equal_i(5, data.files);
+ cl_assert_equal_i(3, data.dirs);
+
+ memset(&data, 0, sizeof(data));
+ data.stop = "3.txt";
+
+ cl_assert_equal_i(GIT_EUSER, git_tree_walk(
+ tree, GIT_TREEWALK_PRE, treewalk_skip_de_cb, &data));
+ cl_assert_equal_i(3, data.files);
+ cl_assert_equal_i(2, data.dirs);
+
+ memset(&data, 0, sizeof(data));
+ data.skip = "new.txt";
+
+ cl_assert_equal_i(0, git_tree_walk(
+ tree, GIT_TREEWALK_PRE, treewalk_skip_de_cb, &data));
+ cl_assert_equal_i(7, data.files);
+ cl_assert_equal_i(4, data.dirs);
+
+ memset(&data, 0, sizeof(data));
+ data.stop = "new.txt";
+
+ cl_assert_equal_i(GIT_EUSER, git_tree_walk(
+ tree, GIT_TREEWALK_PRE, treewalk_skip_de_cb, &data));
+ cl_assert_equal_i(7, data.files);
+ cl_assert_equal_i(4, data.dirs);
+
+ git_tree_free(tree);
+}
diff --git a/tests-clar/object/tree/write.c b/tests/object/tree/write.c
index 468c0ccd1..468c0ccd1 100644
--- a/tests-clar/object/tree/write.c
+++ b/tests/object/tree/write.c
diff --git a/tests/odb/alternates.c b/tests/odb/alternates.c
new file mode 100644
index 000000000..c75f6feaa
--- /dev/null
+++ b/tests/odb/alternates.c
@@ -0,0 +1,80 @@
+#include "clar_libgit2.h"
+#include "odb.h"
+#include "filebuf.h"
+
+static git_buf destpath, filepath;
+static const char *paths[] = {
+ "A.git", "B.git", "C.git", "D.git", "E.git", "F.git", "G.git"
+};
+static git_filebuf file;
+static git_repository *repo;
+
+void test_odb_alternates__cleanup(void)
+{
+ size_t i;
+
+ git_buf_free(&destpath);
+ git_buf_free(&filepath);
+
+ for (i = 0; i < ARRAY_SIZE(paths); i++)
+ cl_fixture_cleanup(paths[i]);
+}
+
+static void init_linked_repo(const char *path, const char *alternate)
+{
+ git_buf_clear(&destpath);
+ git_buf_clear(&filepath);
+
+ cl_git_pass(git_repository_init(&repo, path, 1));
+ cl_git_pass(git_path_prettify(&destpath, alternate, NULL));
+ cl_git_pass(git_buf_joinpath(&destpath, destpath.ptr, "objects"));
+ cl_git_pass(git_buf_joinpath(&filepath, git_repository_path(repo), "objects/info"));
+ cl_git_pass(git_futils_mkdir(filepath.ptr, NULL, 0755, GIT_MKDIR_PATH));
+ cl_git_pass(git_buf_joinpath(&filepath, filepath.ptr , "alternates"));
+
+ cl_git_pass(git_filebuf_open(&file, git_buf_cstr(&filepath), 0, 0666));
+ git_filebuf_printf(&file, "%s\n", git_buf_cstr(&destpath));
+ cl_git_pass(git_filebuf_commit(&file));
+
+ git_repository_free(repo);
+}
+
+void test_odb_alternates__chained(void)
+{
+ git_commit *commit;
+ git_oid oid;
+
+ /* Set the alternate A -> testrepo.git */
+ init_linked_repo(paths[0], cl_fixture("testrepo.git"));
+
+ /* Set the alternate B -> A */
+ init_linked_repo(paths[1], paths[0]);
+
+ /* Now load B and see if we can find an object from testrepo.git */
+ cl_git_pass(git_repository_open(&repo, paths[1]));
+ git_oid_fromstr(&oid, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+ git_commit_free(commit);
+ git_repository_free(repo);
+}
+
+void test_odb_alternates__long_chain(void)
+{
+ git_commit *commit;
+ git_oid oid;
+ size_t i;
+
+ /* Set the alternate A -> testrepo.git */
+ init_linked_repo(paths[0], cl_fixture("testrepo.git"));
+
+ /* Set up the five-element chain */
+ for (i = 1; i < ARRAY_SIZE(paths); i++) {
+ init_linked_repo(paths[i], paths[i-1]);
+ }
+
+ /* Now load the last one and see if we can find an object from testrepo.git */
+ cl_git_pass(git_repository_open(&repo, paths[ARRAY_SIZE(paths)-1]));
+ git_oid_fromstr(&oid, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ cl_git_fail(git_commit_lookup(&commit, repo, &oid));
+ git_repository_free(repo);
+}
diff --git a/tests/odb/backend/nonrefreshing.c b/tests/odb/backend/nonrefreshing.c
new file mode 100644
index 000000000..b43529479
--- /dev/null
+++ b/tests/odb/backend/nonrefreshing.c
@@ -0,0 +1,274 @@
+#include "clar_libgit2.h"
+#include "git2/sys/odb_backend.h"
+#include "repository.h"
+
+typedef struct fake_backend {
+ git_odb_backend parent;
+
+ git_error_code error_code;
+
+ int exists_calls;
+ int read_calls;
+ int read_header_calls;
+ int read_prefix_calls;
+} fake_backend;
+
+static git_repository *_repo;
+static fake_backend *_fake;
+static git_oid _oid;
+
+static int fake_backend__exists(git_odb_backend *backend, const git_oid *oid)
+{
+ fake_backend *fake;
+
+ GIT_UNUSED(oid);
+
+ fake = (fake_backend *)backend;
+
+ fake->exists_calls++;
+
+ return (fake->error_code == GIT_OK);
+}
+
+static int fake_backend__read(
+ void **buffer_p, size_t *len_p, git_otype *type_p,
+ git_odb_backend *backend, const git_oid *oid)
+{
+ fake_backend *fake;
+
+ GIT_UNUSED(buffer_p);
+ GIT_UNUSED(len_p);
+ GIT_UNUSED(type_p);
+ GIT_UNUSED(oid);
+
+ fake = (fake_backend *)backend;
+
+ fake->read_calls++;
+
+ *len_p = 0;
+ *buffer_p = NULL;
+ *type_p = GIT_OBJ_BLOB;
+
+ return fake->error_code;
+}
+
+static int fake_backend__read_header(
+ size_t *len_p, git_otype *type_p,
+ git_odb_backend *backend, const git_oid *oid)
+{
+ fake_backend *fake;
+
+ GIT_UNUSED(len_p);
+ GIT_UNUSED(type_p);
+ GIT_UNUSED(oid);
+
+ fake = (fake_backend *)backend;
+
+ fake->read_header_calls++;
+
+ *len_p = 0;
+ *type_p = GIT_OBJ_BLOB;
+
+ return fake->error_code;
+}
+
+static int fake_backend__read_prefix(
+ git_oid *out_oid, void **buffer_p, size_t *len_p, git_otype *type_p,
+ git_odb_backend *backend, const git_oid *short_oid, size_t len)
+{
+ fake_backend *fake;
+
+ GIT_UNUSED(out_oid);
+ GIT_UNUSED(buffer_p);
+ GIT_UNUSED(len_p);
+ GIT_UNUSED(type_p);
+ GIT_UNUSED(short_oid);
+ GIT_UNUSED(len);
+
+ fake = (fake_backend *)backend;
+
+ fake->read_prefix_calls++;
+
+ *len_p = 0;
+ *buffer_p = NULL;
+ *type_p = GIT_OBJ_BLOB;
+
+ return fake->error_code;
+}
+
+static void fake_backend__free(git_odb_backend *_backend)
+{
+ fake_backend *backend;
+
+ backend = (fake_backend *)_backend;
+
+ git__free(backend);
+}
+
+static int build_fake_backend(
+ git_odb_backend **out,
+ git_error_code error_code)
+{
+ fake_backend *backend;
+
+ backend = git__calloc(1, sizeof(fake_backend));
+ GITERR_CHECK_ALLOC(backend);
+
+ backend->parent.version = GIT_ODB_BACKEND_VERSION;
+
+ backend->parent.refresh = NULL;
+ backend->error_code = error_code;
+
+ backend->parent.read = fake_backend__read;
+ backend->parent.read_prefix = fake_backend__read_prefix;
+ backend->parent.read_header = fake_backend__read_header;
+ backend->parent.exists = fake_backend__exists;
+ backend->parent.free = &fake_backend__free;
+
+ *out = (git_odb_backend *)backend;
+
+ return 0;
+}
+
+static void setup_repository_and_backend(git_error_code error_code)
+{
+ git_odb *odb = NULL;
+ git_odb_backend *backend = NULL;
+
+ _repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_git_pass(build_fake_backend(&backend, error_code));
+
+ cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+ cl_git_pass(git_odb_add_backend(odb, backend, 10));
+
+ _fake = (fake_backend *)backend;
+
+ cl_git_pass(git_oid_fromstr(&_oid, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
+}
+
+void test_odb_backend_nonrefreshing__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_odb_backend_nonrefreshing__exists_is_invoked_once_on_failure(void)
+{
+ git_odb *odb;
+
+ setup_repository_and_backend(GIT_ENOTFOUND);
+
+ cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+ cl_assert_equal_b(false, git_odb_exists(odb, &_oid));
+
+ cl_assert_equal_i(1, _fake->exists_calls);
+}
+
+void test_odb_backend_nonrefreshing__read_is_invoked_once_on_failure(void)
+{
+ git_object *obj;
+
+ setup_repository_and_backend(GIT_ENOTFOUND);
+
+ cl_git_fail_with(
+ git_object_lookup(&obj, _repo, &_oid, GIT_OBJ_ANY),
+ GIT_ENOTFOUND);
+
+ cl_assert_equal_i(1, _fake->read_calls);
+}
+
+void test_odb_backend_nonrefreshing__readprefix_is_invoked_once_on_failure(void)
+{
+ git_object *obj;
+
+ setup_repository_and_backend(GIT_ENOTFOUND);
+
+ cl_git_fail_with(
+ git_object_lookup_prefix(&obj, _repo, &_oid, 7, GIT_OBJ_ANY),
+ GIT_ENOTFOUND);
+
+ cl_assert_equal_i(1, _fake->read_prefix_calls);
+}
+
+void test_odb_backend_nonrefreshing__readheader_is_invoked_once_on_failure(void)
+{
+ git_odb *odb;
+ size_t len;
+ git_otype type;
+
+ setup_repository_and_backend(GIT_ENOTFOUND);
+
+ cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+
+ cl_git_fail_with(
+ git_odb_read_header(&len, &type, odb, &_oid),
+ GIT_ENOTFOUND);
+
+ cl_assert_equal_i(1, _fake->read_header_calls);
+}
+
+void test_odb_backend_nonrefreshing__exists_is_invoked_once_on_success(void)
+{
+ git_odb *odb;
+
+ setup_repository_and_backend(GIT_OK);
+
+ cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+ cl_assert_equal_b(true, git_odb_exists(odb, &_oid));
+
+ cl_assert_equal_i(1, _fake->exists_calls);
+}
+
+void test_odb_backend_nonrefreshing__read_is_invoked_once_on_success(void)
+{
+ git_object *obj;
+
+ setup_repository_and_backend(GIT_OK);
+
+ cl_git_pass(git_object_lookup(&obj, _repo, &_oid, GIT_OBJ_ANY));
+
+ cl_assert_equal_i(1, _fake->read_calls);
+
+ git_object_free(obj);
+}
+
+void test_odb_backend_nonrefreshing__readprefix_is_invoked_once_on_success(void)
+{
+ git_object *obj;
+
+ setup_repository_and_backend(GIT_OK);
+
+ cl_git_pass(git_object_lookup_prefix(&obj, _repo, &_oid, 7, GIT_OBJ_ANY));
+
+ cl_assert_equal_i(1, _fake->read_prefix_calls);
+
+ git_object_free(obj);
+}
+
+void test_odb_backend_nonrefreshing__readheader_is_invoked_once_on_success(void)
+{
+ git_odb *odb;
+ size_t len;
+ git_otype type;
+
+ setup_repository_and_backend(GIT_OK);
+
+ cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+
+ cl_git_pass(git_odb_read_header(&len, &type, odb, &_oid));
+
+ cl_assert_equal_i(1, _fake->read_header_calls);
+}
+
+void test_odb_backend_nonrefreshing__read_is_invoked_once_when_revparsing_a_full_oid(void)
+{
+ git_object *obj;
+
+ setup_repository_and_backend(GIT_ENOTFOUND);
+
+ cl_git_fail_with(
+ git_revparse_single(&obj, _repo, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"),
+ GIT_ENOTFOUND);
+
+ cl_assert_equal_i(1, _fake->read_calls);
+}
diff --git a/tests/odb/foreach.c b/tests/odb/foreach.c
new file mode 100644
index 000000000..ebb8866f7
--- /dev/null
+++ b/tests/odb/foreach.c
@@ -0,0 +1,80 @@
+#include "clar_libgit2.h"
+#include "odb.h"
+#include "git2/odb_backend.h"
+#include "pack.h"
+
+static git_odb *_odb;
+static git_repository *_repo;
+static int nobj;
+
+void test_odb_foreach__cleanup(void)
+{
+ git_odb_free(_odb);
+ git_repository_free(_repo);
+
+ _odb = NULL;
+ _repo = NULL;
+}
+
+static int foreach_cb(const git_oid *oid, void *data)
+{
+ GIT_UNUSED(data);
+ GIT_UNUSED(oid);
+
+ nobj++;
+
+ return 0;
+}
+
+/*
+ * $ git --git-dir tests/resources/testrepo.git count-objects --verbose
+ * count: 47
+ * size: 4
+ * in-pack: 1640
+ * packs: 3
+ * size-pack: 425
+ * prune-packable: 0
+ * garbage: 0
+ */
+void test_odb_foreach__foreach(void)
+{
+ cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git")));
+ git_repository_odb(&_odb, _repo);
+
+ cl_git_pass(git_odb_foreach(_odb, foreach_cb, NULL));
+ cl_assert_equal_i(47 + 1640, nobj); /* count + in-pack */
+}
+
+void test_odb_foreach__one_pack(void)
+{
+ git_odb_backend *backend = NULL;
+
+ cl_git_pass(git_odb_new(&_odb));
+ cl_git_pass(git_odb_backend_one_pack(&backend, cl_fixture("testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx")));
+ cl_git_pass(git_odb_add_backend(_odb, backend, 1));
+ _repo = NULL;
+
+ nobj = 0;
+ cl_git_pass(git_odb_foreach(_odb, foreach_cb, NULL));
+ cl_assert(nobj == 1628);
+}
+
+static int foreach_stop_cb(const git_oid *oid, void *data)
+{
+ GIT_UNUSED(data);
+ GIT_UNUSED(oid);
+
+ nobj++;
+
+ return (nobj == 1000);
+}
+
+void test_odb_foreach__interrupt_foreach(void)
+{
+ nobj = 0;
+ cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git")));
+ git_repository_odb(&_odb, _repo);
+
+ cl_assert_equal_i(GIT_EUSER, git_odb_foreach(_odb, foreach_stop_cb, NULL));
+ cl_assert(nobj == 1000);
+}
diff --git a/tests/odb/loose.c b/tests/odb/loose.c
new file mode 100644
index 000000000..a85f1430d
--- /dev/null
+++ b/tests/odb/loose.c
@@ -0,0 +1,146 @@
+#include "clar_libgit2.h"
+#include "odb.h"
+#include "git2/odb_backend.h"
+#include "posix.h"
+#include "loose_data.h"
+
+#ifdef __ANDROID_API__
+# define S_IREAD S_IRUSR
+# define S_IWRITE S_IWUSR
+#endif
+
+static void write_object_files(object_data *d)
+{
+ int fd;
+
+ if (p_mkdir(d->dir, GIT_OBJECT_DIR_MODE) < 0)
+ cl_assert(errno == EEXIST);
+
+ cl_assert((fd = p_creat(d->file, S_IREAD | S_IWRITE)) >= 0);
+ cl_must_pass(p_write(fd, d->bytes, d->blen));
+
+ p_close(fd);
+}
+
+static void cmp_objects(git_rawobj *o, object_data *d)
+{
+ cl_assert(o->type == git_object_string2type(d->type));
+ cl_assert(o->len == d->dlen);
+
+ if (o->len > 0)
+ cl_assert(memcmp(o->data, d->data, o->len) == 0);
+}
+
+static void test_read_object(object_data *data)
+{
+ git_oid id;
+ git_odb_object *obj;
+ git_odb *odb;
+ git_rawobj tmp;
+
+ 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(&obj, odb, &id));
+
+ tmp.data = obj->buffer;
+ tmp.len = obj->cached.size;
+ tmp.type = obj->cached.type;
+
+ cmp_objects(&tmp, data);
+
+ git_odb_object_free(obj);
+ git_odb_free(odb);
+}
+
+void test_odb_loose__initialize(void)
+{
+ cl_must_pass(p_mkdir("test-objects", GIT_OBJECT_DIR_MODE));
+}
+
+void test_odb_loose__cleanup(void)
+{
+ cl_fixture_cleanup("test-objects");
+}
+
+void test_odb_loose__exists(void)
+{
+ git_oid id, id2;
+ git_odb *odb;
+
+ write_object_files(&one);
+ cl_git_pass(git_odb_open(&odb, "test-objects"));
+
+ cl_git_pass(git_oid_fromstr(&id, one.id));
+
+ cl_assert(git_odb_exists(odb, &id));
+
+ /* Test for a non-existant object */
+ cl_git_pass(git_oid_fromstr(&id2, "8b137891791fe96927ad78e64b0aad7bded08baa"));
+ cl_assert(!git_odb_exists(odb, &id2));
+
+ git_odb_free(odb);
+}
+
+void test_odb_loose__simple_reads(void)
+{
+ test_read_object(&commit);
+ test_read_object(&tree);
+ test_read_object(&tag);
+ test_read_object(&zero);
+ test_read_object(&one);
+ test_read_object(&two);
+ test_read_object(&some);
+}
+
+void test_write_object_permission(
+ mode_t dir_mode, mode_t file_mode,
+ mode_t expected_dir_mode, mode_t expected_file_mode)
+{
+ git_odb *odb;
+ git_odb_backend *backend;
+ git_oid oid;
+ struct stat statbuf;
+ mode_t mask, os_mask;
+
+ /* Windows does not return group/user bits from stat,
+ * files are never executable.
+ */
+#ifdef GIT_WIN32
+ os_mask = 0600;
+#else
+ os_mask = 0777;
+#endif
+
+ mask = p_umask(0);
+ p_umask(mask);
+
+ cl_git_pass(git_odb_new(&odb));
+ cl_git_pass(git_odb_backend_loose(&backend, "test-objects", -1, 0, dir_mode, file_mode));
+ cl_git_pass(git_odb_add_backend(odb, backend, 1));
+ cl_git_pass(git_odb_write(&oid, odb, "Test data\n", 10, GIT_OBJ_BLOB));
+
+ cl_git_pass(p_stat("test-objects/67", &statbuf));
+ cl_assert_equal_i(statbuf.st_mode & os_mask, (expected_dir_mode & ~mask) & os_mask);
+
+ cl_git_pass(p_stat("test-objects/67/b808feb36201507a77f85e6d898f0a2836e4a5", &statbuf));
+ cl_assert_equal_i(statbuf.st_mode & os_mask, (expected_file_mode & ~mask) & os_mask);
+
+ git_odb_free(odb);
+}
+
+void test_odb_loose__permissions_standard(void)
+{
+ test_write_object_permission(0, 0, GIT_OBJECT_DIR_MODE, GIT_OBJECT_FILE_MODE);
+}
+
+void test_odb_loose_permissions_readonly(void)
+{
+ test_write_object_permission(0777, 0444, 0777, 0444);
+}
+
+void test_odb_loose__permissions_readwrite(void)
+{
+ test_write_object_permission(0777, 0666, 0777, 0666);
+}
diff --git a/tests-clar/odb/loose_data.h b/tests/odb/loose_data.h
index c10c9bc7f..c10c9bc7f 100644
--- a/tests-clar/odb/loose_data.h
+++ b/tests/odb/loose_data.h
diff --git a/tests/odb/mixed.c b/tests/odb/mixed.c
new file mode 100644
index 000000000..51970ceec
--- /dev/null
+++ b/tests/odb/mixed.c
@@ -0,0 +1,93 @@
+#include "clar_libgit2.h"
+#include "odb.h"
+
+static git_odb *_odb;
+
+void test_odb_mixed__initialize(void)
+{
+ cl_git_pass(git_odb_open(&_odb, cl_fixture("duplicate.git/objects")));
+}
+
+void test_odb_mixed__cleanup(void)
+{
+ git_odb_free(_odb);
+ _odb = NULL;
+}
+
+void test_odb_mixed__dup_oid(void) {
+ const char hex[] = "ce013625030ba8dba906f756967f9e9ca394464a";
+ const char short_hex[] = "ce01362";
+ git_oid oid;
+ git_odb_object *obj;
+
+ cl_git_pass(git_oid_fromstr(&oid, hex));
+ cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, GIT_OID_HEXSZ));
+ git_odb_object_free(obj);
+ cl_git_pass(git_oid_fromstrn(&oid, short_hex, sizeof(short_hex) - 1));
+ cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, sizeof(short_hex) - 1));
+ git_odb_object_free(obj);
+}
+
+/* some known sha collisions of file content:
+ * 'aabqhq' and 'aaazvc' with prefix 'dea509d0' (+ '9' and + 'b')
+ * 'aaeufo' and 'aaaohs' with prefix '81b5bff5' (+ 'f' and + 'b')
+ * 'aafewy' and 'aaepta' with prefix '739e3c4c'
+ * 'aahsyn' and 'aadrjg' with prefix '0ddeaded' (+ '9' and + 'e')
+ */
+
+void test_odb_mixed__dup_oid_prefix_0(void) {
+ char hex[10];
+ git_oid oid;
+ git_odb_object *obj;
+
+ /* ambiguous in the same pack file */
+
+ strncpy(hex, "dea509d0", sizeof(hex));
+ cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex)));
+ cl_assert_equal_i(
+ GIT_EAMBIGUOUS, git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
+
+ strncpy(hex, "dea509d09", sizeof(hex));
+ cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex)));
+ cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
+ git_odb_object_free(obj);
+
+ strncpy(hex, "dea509d0b", sizeof(hex));
+ cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex)));
+ cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
+ git_odb_object_free(obj);
+
+ /* ambiguous in different pack files */
+
+ strncpy(hex, "81b5bff5", sizeof(hex));
+ cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex)));
+ cl_assert_equal_i(
+ GIT_EAMBIGUOUS, git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
+
+ strncpy(hex, "81b5bff5b", sizeof(hex));
+ cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex)));
+ cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
+ git_odb_object_free(obj);
+
+ strncpy(hex, "81b5bff5f", sizeof(hex));
+ cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex)));
+ cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
+ git_odb_object_free(obj);
+
+ /* ambiguous in pack file and loose */
+
+ strncpy(hex, "0ddeaded", sizeof(hex));
+ cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex)));
+ cl_assert_equal_i(
+ GIT_EAMBIGUOUS, git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
+
+ strncpy(hex, "0ddeaded9", sizeof(hex));
+ cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex)));
+ cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
+ git_odb_object_free(obj);
+
+ strncpy(hex, "0ddeadede", sizeof(hex));
+ cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex)));
+ cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
+ git_odb_object_free(obj);
+}
diff --git a/tests-clar/odb/pack_data.h b/tests/odb/pack_data.h
index e6371beb1..e6371beb1 100644
--- a/tests-clar/odb/pack_data.h
+++ b/tests/odb/pack_data.h
diff --git a/tests-clar/odb/pack_data_one.h b/tests/odb/pack_data_one.h
index 13570ba78..13570ba78 100644
--- a/tests-clar/odb/pack_data_one.h
+++ b/tests/odb/pack_data_one.h
diff --git a/tests-clar/odb/packed.c b/tests/odb/packed.c
index b4f549b58..b4f549b58 100644
--- a/tests-clar/odb/packed.c
+++ b/tests/odb/packed.c
diff --git a/tests-clar/odb/packed_one.c b/tests/odb/packed_one.c
index 0c6ed387b..0c6ed387b 100644
--- a/tests-clar/odb/packed_one.c
+++ b/tests/odb/packed_one.c
diff --git a/tests-clar/odb/sorting.c b/tests/odb/sorting.c
index 147a160c8..147a160c8 100644
--- a/tests-clar/odb/sorting.c
+++ b/tests/odb/sorting.c
diff --git a/tests/odb/streamwrite.c b/tests/odb/streamwrite.c
new file mode 100644
index 000000000..591a20040
--- /dev/null
+++ b/tests/odb/streamwrite.c
@@ -0,0 +1,56 @@
+#include "clar_libgit2.h"
+#include "git2/odb_backend.h"
+
+static git_repository *repo;
+static git_odb *odb;
+static git_odb_stream *stream;
+
+void test_odb_streamwrite__initialize(void)
+{
+ repo = cl_git_sandbox_init("testrepo.git");
+ cl_git_pass(git_repository_odb(&odb, repo));
+
+ cl_git_pass(git_odb_open_wstream(&stream, odb, 14, GIT_OBJ_BLOB));
+ cl_assert_equal_sz(14, stream->declared_size);
+}
+
+void test_odb_streamwrite__cleanup(void)
+{
+ git_odb_stream_free(stream);
+ git_odb_free(odb);
+ cl_git_sandbox_cleanup();
+}
+
+void test_odb_streamwrite__can_accept_chunks(void)
+{
+ git_oid oid;
+
+ cl_git_pass(git_odb_stream_write(stream, "deadbeef", 8));
+ cl_assert_equal_sz(8, stream->received_bytes);
+
+ cl_git_pass(git_odb_stream_write(stream, "deadbeef", 6));
+ cl_assert_equal_sz(8 + 6, stream->received_bytes);
+
+ cl_git_pass(git_odb_stream_finalize_write(&oid, stream));
+}
+
+void test_odb_streamwrite__can_detect_missing_bytes(void)
+{
+ git_oid oid;
+
+ cl_git_pass(git_odb_stream_write(stream, "deadbeef", 8));
+ cl_assert_equal_sz(8, stream->received_bytes);
+
+ cl_git_pass(git_odb_stream_write(stream, "deadbeef", 4));
+ cl_assert_equal_sz(8 + 4, stream->received_bytes);
+
+ cl_git_fail(git_odb_stream_finalize_write(&oid, stream));
+}
+
+void test_odb_streamwrite__can_detect_additional_bytes(void)
+{
+ cl_git_pass(git_odb_stream_write(stream, "deadbeef", 8));
+ cl_assert_equal_sz(8, stream->received_bytes);
+
+ cl_git_fail(git_odb_stream_write(stream, "deadbeef", 7));
+}
diff --git a/tests/online/clone.c b/tests/online/clone.c
new file mode 100644
index 000000000..efc76d958
--- /dev/null
+++ b/tests/online/clone.c
@@ -0,0 +1,291 @@
+#include "clar_libgit2.h"
+
+#include "git2/clone.h"
+#include "git2/cred_helpers.h"
+#include "remote.h"
+#include "fileops.h"
+#include "refs.h"
+
+#define LIVE_REPO_URL "http://github.com/libgit2/TestGitRepository"
+#define LIVE_EMPTYREPO_URL "http://github.com/libgit2/TestEmptyRepository"
+#define BB_REPO_URL "https://libgit2@bitbucket.org/libgit2/testgitrepository.git"
+#define BB_REPO_URL_WITH_PASS "https://libgit2:libgit2@bitbucket.org/libgit2/testgitrepository.git"
+#define BB_REPO_URL_WITH_WRONG_PASS "https://libgit2:wrong@bitbucket.org/libgit2/testgitrepository.git"
+#define ASSEMBLA_REPO_URL "https://libgit2:_Libgit2@git.assembla.com/libgit2-test-repos.git"
+
+static git_repository *g_repo;
+static git_clone_options g_options;
+
+void test_online_clone__initialize(void)
+{
+ git_checkout_opts dummy_opts = GIT_CHECKOUT_OPTS_INIT;
+ git_remote_callbacks dummy_callbacks = GIT_REMOTE_CALLBACKS_INIT;
+
+ g_repo = NULL;
+
+ memset(&g_options, 0, sizeof(git_clone_options));
+ g_options.version = GIT_CLONE_OPTIONS_VERSION;
+ g_options.checkout_opts = dummy_opts;
+ g_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+ g_options.remote_callbacks = dummy_callbacks;
+}
+
+void test_online_clone__cleanup(void)
+{
+ if (g_repo) {
+ git_repository_free(g_repo);
+ g_repo = NULL;
+ }
+ cl_fixture_cleanup("./foo");
+}
+
+void test_online_clone__network_full(void)
+{
+ git_remote *origin;
+
+ cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
+ cl_assert(!git_repository_is_bare(g_repo));
+ cl_git_pass(git_remote_load(&origin, g_repo, "origin"));
+
+ cl_assert_equal_i(GIT_REMOTE_DOWNLOAD_TAGS_AUTO, origin->download_tags);
+
+ git_remote_free(origin);
+}
+
+void test_online_clone__network_bare(void)
+{
+ git_remote *origin;
+
+ g_options.bare = true;
+
+ cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
+ cl_assert(git_repository_is_bare(g_repo));
+ cl_git_pass(git_remote_load(&origin, g_repo, "origin"));
+
+ git_remote_free(origin);
+}
+
+void test_online_clone__empty_repository(void)
+{
+ git_reference *head;
+
+ cl_git_pass(git_clone(&g_repo, LIVE_EMPTYREPO_URL, "./foo", &g_options));
+
+ cl_assert_equal_i(true, git_repository_is_empty(g_repo));
+ cl_assert_equal_i(true, git_repository_head_unborn(g_repo));
+
+ cl_git_pass(git_reference_lookup(&head, g_repo, GIT_HEAD_FILE));
+ cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head));
+ cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
+
+ git_reference_free(head);
+}
+
+static void checkout_progress(const char *path, size_t cur, size_t tot, void *payload)
+{
+ bool *was_called = (bool*)payload;
+ GIT_UNUSED(path); GIT_UNUSED(cur); GIT_UNUSED(tot);
+ (*was_called) = true;
+}
+
+static int fetch_progress(const git_transfer_progress *stats, void *payload)
+{
+ bool *was_called = (bool*)payload;
+ GIT_UNUSED(stats);
+ (*was_called) = true;
+ return 0;
+}
+
+void test_online_clone__can_checkout_a_cloned_repo(void)
+{
+ git_buf path = GIT_BUF_INIT;
+ git_reference *head;
+ bool checkout_progress_cb_was_called = false,
+ fetch_progress_cb_was_called = false;
+
+ g_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+ g_options.checkout_opts.progress_cb = &checkout_progress;
+ g_options.checkout_opts.progress_payload = &checkout_progress_cb_was_called;
+ g_options.remote_callbacks.transfer_progress = &fetch_progress;
+ g_options.remote_callbacks.payload = &fetch_progress_cb_was_called;
+
+ cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
+
+ cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "master.txt"));
+ cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&path)));
+
+ cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
+ cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head));
+ cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
+
+ cl_assert_equal_i(true, checkout_progress_cb_was_called);
+ cl_assert_equal_i(true, fetch_progress_cb_was_called);
+
+ git_reference_free(head);
+ git_buf_free(&path);
+}
+
+void test_online_clone__clone_into(void)
+{
+ git_buf path = GIT_BUF_INIT;
+ git_remote *remote;
+ git_reference *head;
+ git_checkout_opts checkout_opts = GIT_CHECKOUT_OPTS_INIT;
+ git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
+
+ bool checkout_progress_cb_was_called = false,
+ fetch_progress_cb_was_called = false;
+
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+ checkout_opts.progress_cb = &checkout_progress;
+ checkout_opts.progress_payload = &checkout_progress_cb_was_called;
+
+ cl_git_pass(git_repository_init(&g_repo, "./foo", false));
+ cl_git_pass(git_remote_create(&remote, g_repo, "origin", LIVE_REPO_URL));
+
+ callbacks.transfer_progress = &fetch_progress;
+ callbacks.payload = &fetch_progress_cb_was_called;
+ git_remote_set_callbacks(remote, &callbacks);
+
+ cl_git_pass(git_clone_into(g_repo, remote, &checkout_opts, NULL));
+
+ cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "master.txt"));
+ cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&path)));
+
+ cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
+ cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head));
+ cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
+
+ cl_assert_equal_i(true, checkout_progress_cb_was_called);
+ cl_assert_equal_i(true, fetch_progress_cb_was_called);
+
+ git_remote_free(remote);
+ git_reference_free(head);
+ git_buf_free(&path);
+}
+
+static int update_tips(const char *refname, const git_oid *a, const git_oid *b, void *payload)
+{
+ int *callcount = (int*)payload;
+ GIT_UNUSED(refname); GIT_UNUSED(a); GIT_UNUSED(b);
+ *callcount = *callcount + 1;
+ return 0;
+}
+
+void test_online_clone__custom_remote_callbacks(void)
+{
+ int callcount = 0;
+
+ g_options.remote_callbacks.update_tips = update_tips;
+ g_options.remote_callbacks.payload = &callcount;
+
+ cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
+ cl_assert(callcount > 0);
+}
+
+static int cred_failure_cb(
+ git_cred **cred,
+ const char *url,
+ const char *username_from_url,
+ unsigned int allowed_types,
+ void *data)
+{
+ GIT_UNUSED(cred); GIT_UNUSED(url); GIT_UNUSED(username_from_url);
+ GIT_UNUSED(allowed_types); GIT_UNUSED(data);
+ return -1;
+}
+
+void test_online_clone__cred_callback_failure_is_euser(void)
+{
+ const char *remote_url = cl_getenv("GITTEST_REMOTE_URL");
+ const char *remote_user = cl_getenv("GITTEST_REMOTE_USER");
+ const char *remote_default = cl_getenv("GITTEST_REMOTE_DEFAULT");
+ int error;
+
+ if (!remote_url) {
+ printf("GITTEST_REMOTE_URL unset; skipping clone test\n");
+ return;
+ }
+
+ if (!remote_user && !remote_default) {
+ printf("GITTEST_REMOTE_USER and GITTEST_REMOTE_DEFAULT unset; skipping clone test\n");
+ return;
+ }
+
+ g_options.remote_callbacks.credentials = cred_failure_cb;
+
+ cl_git_fail(error = git_clone(&g_repo, remote_url, "./foo", &g_options));
+ cl_assert_equal_i(error, GIT_EUSER);
+}
+
+void test_online_clone__credentials(void)
+{
+ /* Remote URL environment variable must be set. User and password are optional. */
+ const char *remote_url = cl_getenv("GITTEST_REMOTE_URL");
+ git_cred_userpass_payload user_pass = {
+ cl_getenv("GITTEST_REMOTE_USER"),
+ cl_getenv("GITTEST_REMOTE_PASS")
+ };
+
+ if (!remote_url) return;
+
+ g_options.remote_callbacks.credentials = git_cred_userpass;
+ g_options.remote_callbacks.payload = &user_pass;
+
+ cl_git_pass(git_clone(&g_repo, remote_url, "./foo", &g_options));
+ git_repository_free(g_repo); g_repo = NULL;
+ cl_fixture_cleanup("./foo");
+}
+
+void test_online_clone__bitbucket_style(void)
+{
+ git_cred_userpass_payload user_pass = {
+ "libgit2", "libgit2"
+ };
+
+ g_options.remote_callbacks.credentials = git_cred_userpass;
+ g_options.remote_callbacks.payload = &user_pass;
+
+ cl_git_pass(git_clone(&g_repo, BB_REPO_URL, "./foo", &g_options));
+ git_repository_free(g_repo); g_repo = NULL;
+ cl_fixture_cleanup("./foo");
+
+ /* User and pass from URL */
+ user_pass.password = "wrong";
+ 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";
+ cl_git_pass(git_clone(&g_repo, BB_REPO_URL_WITH_WRONG_PASS, "./foo", &g_options));
+ git_repository_free(g_repo); g_repo = NULL;
+ cl_fixture_cleanup("./foo");
+}
+
+void test_online_clone__assembla_style(void)
+{
+ cl_git_pass(git_clone(&g_repo, ASSEMBLA_REPO_URL, "./foo", NULL));
+}
+
+static int cancel_at_half(const git_transfer_progress *stats, void *payload)
+{
+ GIT_UNUSED(payload);
+
+ if (stats->received_objects > (stats->total_objects/2))
+ return 1;
+ return 0;
+}
+
+void test_online_clone__can_cancel(void)
+{
+ g_options.remote_callbacks.transfer_progress = cancel_at_half;
+
+ cl_git_fail_with(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options), GIT_EUSER);
+}
+
+
+
+
+
+
diff --git a/tests/online/fetch.c b/tests/online/fetch.c
new file mode 100644
index 000000000..5153a7ae0
--- /dev/null
+++ b/tests/online/fetch.c
@@ -0,0 +1,171 @@
+#include "clar_libgit2.h"
+
+static git_repository *_repo;
+static int counter;
+
+void test_online_fetch__initialize(void)
+{
+ cl_git_pass(git_repository_init(&_repo, "./fetch", 0));
+}
+
+void test_online_fetch__cleanup(void)
+{
+ git_repository_free(_repo);
+ _repo = NULL;
+
+ cl_fixture_cleanup("./fetch");
+}
+
+static int update_tips(const char *refname, const git_oid *a, const git_oid *b, void *data)
+{
+ GIT_UNUSED(refname); GIT_UNUSED(a); GIT_UNUSED(b); GIT_UNUSED(data);
+
+ ++counter;
+
+ return 0;
+}
+
+static int progress(const git_transfer_progress *stats, void *payload)
+{
+ size_t *bytes_received = (size_t *)payload;
+ *bytes_received = stats->received_bytes;
+ return 0;
+}
+
+static void do_fetch(const char *url, git_remote_autotag_option_t flag, int n)
+{
+ git_remote *remote;
+ git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
+ size_t bytes_received = 0;
+
+ callbacks.transfer_progress = progress;
+ callbacks.update_tips = update_tips;
+ callbacks.payload = &bytes_received;
+ counter = 0;
+
+ cl_git_pass(git_remote_create(&remote, _repo, "test", url));
+ git_remote_set_callbacks(remote, &callbacks);
+ git_remote_set_autotag(remote, flag);
+ cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
+ cl_git_pass(git_remote_download(remote));
+ cl_git_pass(git_remote_update_tips(remote));
+ git_remote_disconnect(remote);
+ cl_assert_equal_i(counter, n);
+ cl_assert(bytes_received > 0);
+
+ git_remote_free(remote);
+}
+
+void test_online_fetch__default_git(void)
+{
+ do_fetch("git://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_AUTO, 6);
+}
+
+void test_online_fetch__default_http(void)
+{
+ do_fetch("http://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_AUTO, 6);
+}
+
+void test_online_fetch__default_https(void)
+{
+ do_fetch("https://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_AUTO, 6);
+}
+
+void test_online_fetch__no_tags_git(void)
+{
+ do_fetch("git://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_NONE, 3);
+}
+
+void test_online_fetch__no_tags_http(void)
+{
+ do_fetch("http://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_NONE, 3);
+}
+
+static int transferProgressCallback(const git_transfer_progress *stats, void *payload)
+{
+ bool *invoked = (bool *)payload;
+
+ GIT_UNUSED(stats);
+ *invoked = true;
+ return 0;
+}
+
+void test_online_fetch__doesnt_retrieve_a_pack_when_the_repository_is_up_to_date(void)
+{
+ git_repository *_repository;
+ bool invoked = false;
+ git_remote *remote;
+ git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
+ git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+ opts.bare = true;
+
+ cl_git_pass(git_clone(&_repository, "https://github.com/libgit2/TestGitRepository.git",
+ "./fetch/lg2", &opts));
+ git_repository_free(_repository);
+
+ cl_git_pass(git_repository_open(&_repository, "./fetch/lg2"));
+
+ cl_git_pass(git_remote_load(&remote, _repository, "origin"));
+ cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
+
+ cl_assert_equal_i(false, invoked);
+
+ callbacks.transfer_progress = &transferProgressCallback;
+ callbacks.payload = &invoked;
+ git_remote_set_callbacks(remote, &callbacks);
+ cl_git_pass(git_remote_download(remote));
+
+ cl_assert_equal_i(false, invoked);
+
+ cl_git_pass(git_remote_update_tips(remote));
+ git_remote_disconnect(remote);
+
+ git_remote_free(remote);
+ git_repository_free(_repository);
+}
+
+static int cancel_at_half(const git_transfer_progress *stats, void *payload)
+{
+ GIT_UNUSED(payload);
+
+ if (stats->received_objects > (stats->total_objects/2))
+ return -1;
+ return 0;
+}
+
+void test_online_fetch__can_cancel(void)
+{
+ git_remote *remote;
+ size_t bytes_received = 0;
+ git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
+
+ cl_git_pass(git_remote_create(&remote, _repo, "test",
+ "http://github.com/libgit2/TestGitRepository.git"));
+
+ callbacks.transfer_progress = cancel_at_half;
+ callbacks.payload = &bytes_received;
+ git_remote_set_callbacks(remote, &callbacks);
+
+ cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
+ cl_git_fail_with(git_remote_download(remote), GIT_EUSER);
+ git_remote_disconnect(remote);
+ git_remote_free(remote);
+}
+
+void test_online_fetch__ls_disconnected(void)
+{
+ const git_remote_head **refs;
+ size_t refs_len_before, refs_len_after;
+ git_remote *remote;
+
+ cl_git_pass(git_remote_create(&remote, _repo, "test",
+ "http://github.com/libgit2/TestGitRepository.git"));
+ cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
+ cl_git_pass(git_remote_ls(&refs, &refs_len_before, remote));
+ git_remote_disconnect(remote);
+ cl_git_pass(git_remote_ls(&refs, &refs_len_after, remote));
+
+ cl_assert_equal_i(refs_len_before, refs_len_after);
+
+ git_remote_free(remote);
+}
diff --git a/tests/online/fetchhead.c b/tests/online/fetchhead.c
new file mode 100644
index 000000000..57b183f88
--- /dev/null
+++ b/tests/online/fetchhead.c
@@ -0,0 +1,91 @@
+#include "clar_libgit2.h"
+
+#include "fileops.h"
+#include "fetchhead.h"
+#include "../fetchhead/fetchhead_data.h"
+#include "git2/clone.h"
+
+#define LIVE_REPO_URL "git://github.com/libgit2/TestGitRepository"
+
+static git_repository *g_repo;
+static git_clone_options g_options;
+
+void test_online_fetchhead__initialize(void)
+{
+ git_remote_callbacks dummy_callbacks = GIT_REMOTE_CALLBACKS_INIT;
+ g_repo = NULL;
+
+ memset(&g_options, 0, sizeof(git_clone_options));
+ g_options.version = GIT_CLONE_OPTIONS_VERSION;
+ g_options.remote_callbacks = dummy_callbacks;
+}
+
+void test_online_fetchhead__cleanup(void)
+{
+ if (g_repo) {
+ git_repository_free(g_repo);
+ g_repo = NULL;
+ }
+
+ cl_fixture_cleanup("./foo");
+}
+
+static void fetchhead_test_clone(void)
+{
+ cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
+}
+
+static void fetchhead_test_fetch(const char *fetchspec, const char *expected_fetchhead)
+{
+ git_remote *remote;
+ git_buf fetchhead_buf = GIT_BUF_INIT;
+ int equals = 0;
+
+ cl_git_pass(git_remote_load(&remote, g_repo, "origin"));
+ git_remote_set_autotag(remote, GIT_REMOTE_DOWNLOAD_TAGS_AUTO);
+
+ if(fetchspec != NULL) {
+ git_remote_clear_refspecs(remote);
+ git_remote_add_fetch(remote, fetchspec);
+ }
+
+ cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
+ cl_git_pass(git_remote_download(remote));
+ cl_git_pass(git_remote_update_tips(remote));
+ git_remote_disconnect(remote);
+ git_remote_free(remote);
+
+ cl_git_pass(git_futils_readbuffer(&fetchhead_buf, "./foo/.git/FETCH_HEAD"));
+
+ equals = (strcmp(fetchhead_buf.ptr, expected_fetchhead) == 0);
+
+ git_buf_free(&fetchhead_buf);
+
+ cl_assert(equals);
+}
+
+void test_online_fetchhead__wildcard_spec(void)
+{
+ fetchhead_test_clone();
+ fetchhead_test_fetch(NULL, FETCH_HEAD_WILDCARD_DATA);
+}
+
+void test_online_fetchhead__explicit_spec(void)
+{
+ fetchhead_test_clone();
+ fetchhead_test_fetch("refs/heads/first-merge:refs/remotes/origin/first-merge", FETCH_HEAD_EXPLICIT_DATA);
+}
+
+void test_online_fetchhead__no_merges(void)
+{
+ git_config *config;
+
+ fetchhead_test_clone();
+
+ cl_git_pass(git_repository_config(&config, g_repo));
+ cl_git_pass(git_config_delete_entry(config, "branch.master.remote"));
+ cl_git_pass(git_config_delete_entry(config, "branch.master.merge"));
+ git_config_free(config);
+
+ fetchhead_test_fetch(NULL, FETCH_HEAD_NO_MERGE_DATA);
+}
diff --git a/tests/online/push.c b/tests/online/push.c
new file mode 100644
index 000000000..be505c3a1
--- /dev/null
+++ b/tests/online/push.c
@@ -0,0 +1,784 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "posix.h"
+#include "vector.h"
+#include "../submodule/submodule_helpers.h"
+#include "push_util.h"
+#include "refspec.h"
+#include "remote.h"
+
+static git_repository *_repo;
+
+static char *_remote_url;
+
+static char *_remote_ssh_key;
+static char *_remote_ssh_pubkey;
+static char *_remote_ssh_passphrase;
+
+static char *_remote_user;
+static char *_remote_pass;
+
+static char *_remote_default;
+
+static int cred_acquire_cb(git_cred **, const char *, const char *, unsigned int, void *);
+
+static git_remote *_remote;
+static record_callbacks_data _record_cbs_data = {{ 0 }};
+static git_remote_callbacks _record_cbs = RECORD_CALLBACKS_INIT(&_record_cbs_data);
+
+static git_oid _oid_b6;
+static git_oid _oid_b5;
+static git_oid _oid_b4;
+static git_oid _oid_b3;
+static git_oid _oid_b2;
+static git_oid _oid_b1;
+
+static git_oid _tag_commit;
+static git_oid _tag_tree;
+static git_oid _tag_blob;
+static git_oid _tag_lightweight;
+static git_oid _tag_tag;
+
+static int cred_acquire_cb(
+ git_cred **cred,
+ const char *url,
+ const char *user_from_url,
+ unsigned int allowed_types,
+ void *payload)
+{
+ GIT_UNUSED(url);
+ GIT_UNUSED(user_from_url);
+ GIT_UNUSED(payload);
+
+ if (GIT_CREDTYPE_DEFAULT & allowed_types) {
+ if (!_remote_default) {
+ printf("GITTEST_REMOTE_DEFAULT must be set to use NTLM/Negotiate credentials\n");
+ return -1;
+ }
+
+ return git_cred_default_new(cred);
+ }
+
+ if (GIT_CREDTYPE_SSH_KEY & allowed_types) {
+ if (!_remote_user || !_remote_ssh_pubkey || !_remote_ssh_key || !_remote_ssh_passphrase) {
+ printf("GITTEST_REMOTE_USER, GITTEST_REMOTE_SSH_PUBKEY, GITTEST_REMOTE_SSH_KEY and GITTEST_REMOTE_SSH_PASSPHRASE must be set\n");
+ return -1;
+ }
+
+ return git_cred_ssh_key_new(cred, _remote_user, _remote_ssh_pubkey, _remote_ssh_key, _remote_ssh_passphrase);
+ }
+
+ if (GIT_CREDTYPE_USERPASS_PLAINTEXT & allowed_types) {
+ if (!_remote_user || !_remote_pass) {
+ printf("GITTEST_REMOTE_USER and GITTEST_REMOTE_PASS must be set\n");
+ return -1;
+ }
+
+ return git_cred_userpass_plaintext_new(cred, _remote_user, _remote_pass);
+ }
+
+ return -1;
+}
+
+/* the results of a push status. when used for expected values, msg may be NULL
+ * to indicate that it should not be matched. */
+typedef struct {
+ const char *ref;
+ int success;
+ const char *msg;
+} push_status;
+
+/**
+ * git_push_status_foreach callback that records status entries.
+ * @param data (git_vector *) of push_status instances
+ */
+static int record_push_status_cb(const char *ref, const char *msg, void *data)
+{
+ git_vector *statuses = (git_vector *)data;
+ push_status *s;
+
+ cl_assert(s = git__malloc(sizeof(*s)));
+ s->ref = ref;
+ s->success = (msg == NULL);
+ s->msg = msg;
+
+ git_vector_insert(statuses, s);
+
+ return 0;
+}
+
+static void do_verify_push_status(git_push *push, const push_status expected[], const size_t expected_len)
+{
+ git_vector actual = GIT_VECTOR_INIT;
+ push_status *iter;
+ bool failed = false;
+ size_t i;
+
+ git_push_status_foreach(push, record_push_status_cb, &actual);
+
+ if (expected_len != actual.length)
+ failed = true;
+ else
+ git_vector_foreach(&actual, i, iter)
+ if (strcmp(expected[i].ref, iter->ref) ||
+ (expected[i].success != iter->success) ||
+ (expected[i].msg && (!iter->msg || strcmp(expected[i].msg, iter->msg)))) {
+ failed = true;
+ break;
+ }
+
+ if (failed) {
+ git_buf msg = GIT_BUF_INIT;
+
+ git_buf_puts(&msg, "Expected and actual push statuses differ:\nEXPECTED:\n");
+
+ for(i = 0; i < expected_len; i++) {
+ git_buf_printf(&msg, "%s: %s\n",
+ expected[i].ref,
+ expected[i].success ? "success" : "failed");
+ }
+
+ git_buf_puts(&msg, "\nACTUAL:\n");
+
+ git_vector_foreach(&actual, i, iter) {
+ if (iter->success)
+ git_buf_printf(&msg, "%s: success\n", iter->ref);
+ else
+ git_buf_printf(&msg, "%s: failed with message: %s", iter->ref, iter->msg);
+ }
+
+ cl_fail(git_buf_cstr(&msg));
+
+ git_buf_free(&msg);
+ }
+
+ git_vector_foreach(&actual, i, iter)
+ git__free(iter);
+
+ git_vector_free(&actual);
+}
+
+/**
+ * Verifies that after git_push_finish(), refs on a remote have the expected
+ * names, oids, and order.
+ *
+ * @param remote remote to verify
+ * @param expected_refs expected remote refs after push
+ * @param expected_refs_len length of expected_refs
+ */
+static void verify_refs(git_remote *remote, expected_ref expected_refs[], size_t expected_refs_len)
+{
+ const git_remote_head **actual_refs;
+ size_t actual_refs_len;
+
+ git_remote_ls(&actual_refs, &actual_refs_len, remote);
+ verify_remote_refs(actual_refs, actual_refs_len, expected_refs, expected_refs_len);
+}
+
+/**
+ * Verifies that after git_push_update_tips(), remote tracking branches have the expected
+ * names and oids.
+ *
+ * @param remote remote to verify
+ * @param expected_refs expected remote refs after push
+ * @param expected_refs_len length of expected_refs
+ */
+static void verify_tracking_branches(git_remote *remote, expected_ref expected_refs[], size_t expected_refs_len)
+{
+ git_refspec *fetch_spec;
+ size_t i, j;
+ git_buf msg = GIT_BUF_INIT;
+ git_buf ref_name = GIT_BUF_INIT;
+ git_vector actual_refs = GIT_VECTOR_INIT;
+ git_branch_iterator *iter;
+ char *actual_ref;
+ git_oid oid;
+ int failed = 0, error;
+ git_branch_t branch_type;
+ git_reference *ref;
+
+ /* Get current remote branches */
+ cl_git_pass(git_branch_iterator_new(&iter, remote->repo, GIT_BRANCH_REMOTE));
+
+ while ((error = git_branch_next(&ref, &branch_type, iter)) == 0) {
+ cl_assert_equal_i(branch_type, GIT_BRANCH_REMOTE);
+
+ cl_git_pass(git_vector_insert(&actual_refs, git__strdup(git_reference_name(ref))));
+ }
+
+ cl_assert_equal_i(error, GIT_ITEROVER);
+
+ /* Loop through expected refs, make sure they exist */
+ for (i = 0; i < expected_refs_len; i++) {
+
+ /* Convert remote reference name into tracking branch name.
+ * If the spec is not under refs/heads/, then skip.
+ */
+ fetch_spec = git_remote__matching_refspec(remote, expected_refs[i].name);
+ if (!fetch_spec)
+ continue;
+
+ cl_git_pass(git_refspec_transform_r(&ref_name, fetch_spec, expected_refs[i].name));
+
+ /* Find matching remote branch */
+ git_vector_foreach(&actual_refs, j, actual_ref) {
+ if (!strcmp(git_buf_cstr(&ref_name), actual_ref))
+ break;
+ }
+
+ if (j == actual_refs.length) {
+ git_buf_printf(&msg, "Did not find expected tracking branch '%s'.", git_buf_cstr(&ref_name));
+ failed = 1;
+ goto failed;
+ }
+
+ /* Make sure tracking branch is at expected commit ID */
+ cl_git_pass(git_reference_name_to_id(&oid, remote->repo, actual_ref));
+
+ if (git_oid_cmp(expected_refs[i].oid, &oid) != 0) {
+ git_buf_puts(&msg, "Tracking branch commit does not match expected ID.");
+ failed = 1;
+ goto failed;
+ }
+
+ git__free(actual_ref);
+ cl_git_pass(git_vector_remove(&actual_refs, j));
+ }
+
+ /* Make sure there are no extra branches */
+ if (actual_refs.length > 0) {
+ git_buf_puts(&msg, "Unexpected remote tracking branches exist.");
+ failed = 1;
+ goto failed;
+ }
+
+failed:
+
+ if(failed)
+ cl_fail(git_buf_cstr(&msg));
+
+ git_vector_foreach(&actual_refs, i, actual_ref)
+ git__free(actual_ref);
+
+ git_vector_free(&actual_refs);
+ git_buf_free(&msg);
+ git_buf_free(&ref_name);
+ return;
+}
+
+void test_online_push__initialize(void)
+{
+ git_vector delete_specs = GIT_VECTOR_INIT;
+ const git_remote_head **heads;
+ size_t i, heads_len;
+ char *curr_del_spec;
+
+ _repo = cl_git_sandbox_init("push_src");
+
+ cl_fixture_sandbox("testrepo.git");
+ cl_rename("push_src/submodule/.gitted", "push_src/submodule/.git");
+
+ rewrite_gitmodules(git_repository_workdir(_repo));
+
+ /* git log --format=oneline --decorate --graph
+ * *-. 951bbbb90e2259a4c8950db78946784fb53fcbce (HEAD, b6) merge b3, b4, and b5 to b6
+ * |\ \
+ * | | * fa38b91f199934685819bea316186d8b008c52a2 (b5) added submodule named 'submodule' pointing to '../testrepo.git'
+ * | * | 27b7ce66243eb1403862d05f958c002312df173d (b4) edited fold\b.txt
+ * | |/
+ * * | d9b63a88223d8367516f50bd131a5f7349b7f3e4 (b3) edited a.txt
+ * |/
+ * * a78705c3b2725f931d3ee05348d83cc26700f247 (b2, b1) added fold and fold/b.txt
+ * * 5c0bb3d1b9449d1cc69d7519fd05166f01840915 added a.txt
+ */
+ git_oid_fromstr(&_oid_b6, "951bbbb90e2259a4c8950db78946784fb53fcbce");
+ git_oid_fromstr(&_oid_b5, "fa38b91f199934685819bea316186d8b008c52a2");
+ git_oid_fromstr(&_oid_b4, "27b7ce66243eb1403862d05f958c002312df173d");
+ git_oid_fromstr(&_oid_b3, "d9b63a88223d8367516f50bd131a5f7349b7f3e4");
+ git_oid_fromstr(&_oid_b2, "a78705c3b2725f931d3ee05348d83cc26700f247");
+ git_oid_fromstr(&_oid_b1, "a78705c3b2725f931d3ee05348d83cc26700f247");
+
+ git_oid_fromstr(&_tag_commit, "805c54522e614f29f70d2413a0470247d8b424ac");
+ git_oid_fromstr(&_tag_tree, "ff83aa4c5e5d28e3bcba2f5c6e2adc61286a4e5e");
+ git_oid_fromstr(&_tag_blob, "b483ae7ba66decee9aee971f501221dea84b1498");
+ git_oid_fromstr(&_tag_lightweight, "951bbbb90e2259a4c8950db78946784fb53fcbce");
+ git_oid_fromstr(&_tag_tag, "eea4f2705eeec2db3813f2430829afce99cd00b5");
+
+ /* Remote URL environment variable must be set. User and password are optional. */
+ _remote_url = cl_getenv("GITTEST_REMOTE_URL");
+ _remote_user = cl_getenv("GITTEST_REMOTE_USER");
+ _remote_pass = cl_getenv("GITTEST_REMOTE_PASS");
+ _remote_ssh_key = cl_getenv("GITTEST_REMOTE_SSH_KEY");
+ _remote_ssh_pubkey = cl_getenv("GITTEST_REMOTE_SSH_PUBKEY");
+ _remote_ssh_passphrase = cl_getenv("GITTEST_REMOTE_SSH_PASSPHRASE");
+ _remote_default = cl_getenv("GITTEST_REMOTE_DEFAULT");
+ _remote = NULL;
+
+ if (_remote_url) {
+ cl_git_pass(git_remote_create(&_remote, _repo, "test", _remote_url));
+
+ record_callbacks_data_clear(&_record_cbs_data);
+ git_remote_set_callbacks(_remote, &_record_cbs);
+
+ cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH));
+
+ /* Clean up previously pushed branches. Fails if receive.denyDeletes is
+ * set on the remote. Also, on Git 1.7.0 and newer, you must run
+ * 'git config receive.denyDeleteCurrent ignore' in the remote repo in
+ * order to delete the remote branch pointed to by HEAD (usually master).
+ * See: https://raw.github.com/git/git/master/Documentation/RelNotes/1.7.0.txt
+ */
+ cl_git_pass(git_remote_ls(&heads, &heads_len, _remote));
+ cl_git_pass(create_deletion_refspecs(&delete_specs, heads, heads_len));
+ if (delete_specs.length) {
+ git_push *push;
+
+ cl_git_pass(git_push_new(&push, _remote));
+
+ git_vector_foreach(&delete_specs, i, curr_del_spec) {
+ git_push_add_refspec(push, curr_del_spec);
+ git__free(curr_del_spec);
+ }
+
+ cl_git_pass(git_push_finish(push));
+ git_push_free(push);
+ }
+
+ git_remote_disconnect(_remote);
+ git_vector_free(&delete_specs);
+
+ /* Now that we've deleted everything, fetch from the remote */
+ cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_FETCH));
+ cl_git_pass(git_remote_download(_remote));
+ cl_git_pass(git_remote_update_tips(_remote));
+ git_remote_disconnect(_remote);
+ } else
+ printf("GITTEST_REMOTE_URL unset; skipping push test\n");
+}
+
+void test_online_push__cleanup(void)
+{
+ if (_remote)
+ git_remote_free(_remote);
+ _remote = NULL;
+
+ /* Freed by cl_git_sandbox_cleanup */
+ _repo = NULL;
+
+ record_callbacks_data_clear(&_record_cbs_data);
+
+ cl_fixture_cleanup("testrepo.git");
+ cl_git_sandbox_cleanup();
+}
+
+static int push_pack_progress_cb(int stage, unsigned int current, unsigned int total, void* payload)
+{
+ int *was_called = (int *) payload;
+ GIT_UNUSED(stage); GIT_UNUSED(current); GIT_UNUSED(total);
+ *was_called = 1;
+ return 0;
+}
+
+static int push_transfer_progress_cb(unsigned int current, unsigned int total, size_t bytes, void* payload)
+{
+ int *was_called = (int *) payload;
+ GIT_UNUSED(current); GIT_UNUSED(total); GIT_UNUSED(bytes);
+ *was_called = 1;
+ return 0;
+}
+
+/**
+ * Calls push and relists refs on remote to verify success.
+ *
+ * @param refspecs refspecs to push
+ * @param refspecs_len length of refspecs
+ * @param expected_refs expected remote refs after push
+ * @param expected_refs_len length of expected_refs
+ * @param expected_ret expected return value from git_push_finish()
+ * @param check_progress_cb Check that the push progress callbacks are called
+ */
+static void do_push(const char *refspecs[], size_t refspecs_len,
+ push_status expected_statuses[], size_t expected_statuses_len,
+ expected_ref expected_refs[], size_t expected_refs_len, int expected_ret, int check_progress_cb)
+{
+ git_push *push;
+ git_push_options opts = GIT_PUSH_OPTIONS_INIT;
+ size_t i;
+ int ret;
+ int pack_progress_called = 0, transfer_progress_called = 0;
+
+ if (_remote) {
+ /* Auto-detect the number of threads to use */
+ opts.pb_parallelism = 0;
+
+ cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH));
+
+ cl_git_pass(git_push_new(&push, _remote));
+ cl_git_pass(git_push_set_options(push, &opts));
+
+ if (check_progress_cb)
+ cl_git_pass(git_push_set_callbacks(push, push_pack_progress_cb, &pack_progress_called, push_transfer_progress_cb, &transfer_progress_called));
+
+ for (i = 0; i < refspecs_len; i++)
+ cl_git_pass(git_push_add_refspec(push, refspecs[i]));
+
+ if (expected_ret < 0) {
+ cl_git_fail(ret = git_push_finish(push));
+ cl_assert_equal_i(0, git_push_unpack_ok(push));
+ }
+ else {
+ cl_git_pass(ret = git_push_finish(push));
+ cl_assert_equal_i(1, git_push_unpack_ok(push));
+ }
+
+ if (check_progress_cb) {
+ cl_assert_equal_i(1, pack_progress_called);
+ cl_assert_equal_i(1, transfer_progress_called);
+ }
+
+ do_verify_push_status(push, expected_statuses, expected_statuses_len);
+
+ cl_assert_equal_i(expected_ret, ret);
+
+ verify_refs(_remote, expected_refs, expected_refs_len);
+
+ cl_git_pass(git_push_update_tips(push));
+ verify_tracking_branches(_remote, expected_refs, expected_refs_len);
+
+ git_push_free(push);
+
+ git_remote_disconnect(_remote);
+ }
+}
+
+/* Call push_finish() without ever calling git_push_add_refspec() */
+void test_online_push__noop(void)
+{
+ do_push(NULL, 0, NULL, 0, NULL, 0, 0, 0);
+}
+
+void test_online_push__b1(void)
+{
+ const char *specs[] = { "refs/heads/b1:refs/heads/b1" };
+ push_status exp_stats[] = { { "refs/heads/b1", 1 } };
+ expected_ref exp_refs[] = { { "refs/heads/b1", &_oid_b1 } };
+ do_push(specs, ARRAY_SIZE(specs),
+ exp_stats, ARRAY_SIZE(exp_stats),
+ exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
+}
+
+void test_online_push__b2(void)
+{
+ const char *specs[] = { "refs/heads/b2:refs/heads/b2" };
+ push_status exp_stats[] = { { "refs/heads/b2", 1 } };
+ expected_ref exp_refs[] = { { "refs/heads/b2", &_oid_b2 } };
+ do_push(specs, ARRAY_SIZE(specs),
+ exp_stats, ARRAY_SIZE(exp_stats),
+ exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
+}
+
+void test_online_push__b3(void)
+{
+ const char *specs[] = { "refs/heads/b3:refs/heads/b3" };
+ push_status exp_stats[] = { { "refs/heads/b3", 1 } };
+ expected_ref exp_refs[] = { { "refs/heads/b3", &_oid_b3 } };
+ do_push(specs, ARRAY_SIZE(specs),
+ exp_stats, ARRAY_SIZE(exp_stats),
+ exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
+}
+
+void test_online_push__b4(void)
+{
+ const char *specs[] = { "refs/heads/b4:refs/heads/b4" };
+ push_status exp_stats[] = { { "refs/heads/b4", 1 } };
+ expected_ref exp_refs[] = { { "refs/heads/b4", &_oid_b4 } };
+ do_push(specs, ARRAY_SIZE(specs),
+ exp_stats, ARRAY_SIZE(exp_stats),
+ exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
+}
+
+void test_online_push__b5(void)
+{
+ const char *specs[] = { "refs/heads/b5:refs/heads/b5" };
+ push_status exp_stats[] = { { "refs/heads/b5", 1 } };
+ expected_ref exp_refs[] = { { "refs/heads/b5", &_oid_b5 } };
+ do_push(specs, ARRAY_SIZE(specs),
+ exp_stats, ARRAY_SIZE(exp_stats),
+ exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
+}
+
+void test_online_push__multi(void)
+{
+ const char *specs[] = {
+ "refs/heads/b1:refs/heads/b1",
+ "refs/heads/b2:refs/heads/b2",
+ "refs/heads/b3:refs/heads/b3",
+ "refs/heads/b4:refs/heads/b4",
+ "refs/heads/b5:refs/heads/b5"
+ };
+ push_status exp_stats[] = {
+ { "refs/heads/b1", 1 },
+ { "refs/heads/b2", 1 },
+ { "refs/heads/b3", 1 },
+ { "refs/heads/b4", 1 },
+ { "refs/heads/b5", 1 }
+ };
+ expected_ref exp_refs[] = {
+ { "refs/heads/b1", &_oid_b1 },
+ { "refs/heads/b2", &_oid_b2 },
+ { "refs/heads/b3", &_oid_b3 },
+ { "refs/heads/b4", &_oid_b4 },
+ { "refs/heads/b5", &_oid_b5 }
+ };
+ do_push(specs, ARRAY_SIZE(specs),
+ exp_stats, ARRAY_SIZE(exp_stats),
+ exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
+}
+
+void test_online_push__implicit_tgt(void)
+{
+ const char *specs1[] = { "refs/heads/b1:" };
+ push_status exp_stats1[] = { { "refs/heads/b1", 1 } };
+ expected_ref exp_refs1[] = { { "refs/heads/b1", &_oid_b1 } };
+
+ const char *specs2[] = { "refs/heads/b2:" };
+ push_status exp_stats2[] = { { "refs/heads/b2", 1 } };
+ expected_ref exp_refs2[] = {
+ { "refs/heads/b1", &_oid_b1 },
+ { "refs/heads/b2", &_oid_b2 }
+ };
+
+ do_push(specs1, ARRAY_SIZE(specs1),
+ exp_stats1, ARRAY_SIZE(exp_stats1),
+ exp_refs1, ARRAY_SIZE(exp_refs1), 0, 1);
+ do_push(specs2, ARRAY_SIZE(specs2),
+ exp_stats2, ARRAY_SIZE(exp_stats2),
+ exp_refs2, ARRAY_SIZE(exp_refs2), 0, 0);
+}
+
+void test_online_push__fast_fwd(void)
+{
+ /* Fast forward b1 in tgt from _oid_b1 to _oid_b6. */
+
+ const char *specs_init[] = { "refs/heads/b1:refs/heads/b1" };
+ push_status exp_stats_init[] = { { "refs/heads/b1", 1 } };
+ expected_ref exp_refs_init[] = { { "refs/heads/b1", &_oid_b1 } };
+
+ const char *specs_ff[] = { "refs/heads/b6:refs/heads/b1" };
+ push_status exp_stats_ff[] = { { "refs/heads/b1", 1 } };
+ expected_ref exp_refs_ff[] = { { "refs/heads/b1", &_oid_b6 } };
+
+ /* Do a force push to reset b1 in target back to _oid_b1 */
+ const char *specs_reset[] = { "+refs/heads/b1:refs/heads/b1" };
+ /* Force should have no effect on a fast forward push */
+ const char *specs_ff_force[] = { "+refs/heads/b6:refs/heads/b1" };
+
+ do_push(specs_init, ARRAY_SIZE(specs_init),
+ exp_stats_init, ARRAY_SIZE(exp_stats_init),
+ exp_refs_init, ARRAY_SIZE(exp_refs_init), 0, 1);
+
+ do_push(specs_ff, ARRAY_SIZE(specs_ff),
+ exp_stats_ff, ARRAY_SIZE(exp_stats_ff),
+ exp_refs_ff, ARRAY_SIZE(exp_refs_ff), 0, 0);
+
+ do_push(specs_reset, ARRAY_SIZE(specs_reset),
+ exp_stats_init, ARRAY_SIZE(exp_stats_init),
+ exp_refs_init, ARRAY_SIZE(exp_refs_init), 0, 0);
+
+ do_push(specs_ff_force, ARRAY_SIZE(specs_ff_force),
+ exp_stats_ff, ARRAY_SIZE(exp_stats_ff),
+ exp_refs_ff, ARRAY_SIZE(exp_refs_ff), 0, 0);
+}
+
+void test_online_push__tag_commit(void)
+{
+ const char *specs[] = { "refs/tags/tag-commit:refs/tags/tag-commit" };
+ push_status exp_stats[] = { { "refs/tags/tag-commit", 1 } };
+ expected_ref exp_refs[] = { { "refs/tags/tag-commit", &_tag_commit } };
+ do_push(specs, ARRAY_SIZE(specs),
+ exp_stats, ARRAY_SIZE(exp_stats),
+ exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
+}
+
+void test_online_push__tag_tree(void)
+{
+ const char *specs[] = { "refs/tags/tag-tree:refs/tags/tag-tree" };
+ push_status exp_stats[] = { { "refs/tags/tag-tree", 1 } };
+ expected_ref exp_refs[] = { { "refs/tags/tag-tree", &_tag_tree } };
+ do_push(specs, ARRAY_SIZE(specs),
+ exp_stats, ARRAY_SIZE(exp_stats),
+ exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
+}
+
+void test_online_push__tag_blob(void)
+{
+ const char *specs[] = { "refs/tags/tag-blob:refs/tags/tag-blob" };
+ push_status exp_stats[] = { { "refs/tags/tag-blob", 1 } };
+ expected_ref exp_refs[] = { { "refs/tags/tag-blob", &_tag_blob } };
+ do_push(specs, ARRAY_SIZE(specs),
+ exp_stats, ARRAY_SIZE(exp_stats),
+ exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
+}
+
+void test_online_push__tag_lightweight(void)
+{
+ const char *specs[] = { "refs/tags/tag-lightweight:refs/tags/tag-lightweight" };
+ push_status exp_stats[] = { { "refs/tags/tag-lightweight", 1 } };
+ expected_ref exp_refs[] = { { "refs/tags/tag-lightweight", &_tag_lightweight } };
+ do_push(specs, ARRAY_SIZE(specs),
+ exp_stats, ARRAY_SIZE(exp_stats),
+ exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
+}
+
+void test_online_push__tag_to_tag(void)
+{
+ const char *specs[] = { "refs/tags/tag-tag:refs/tags/tag-tag" };
+ push_status exp_stats[] = { { "refs/tags/tag-tag", 1 } };
+ expected_ref exp_refs[] = { { "refs/tags/tag-tag", &_tag_tag } };
+ do_push(specs, ARRAY_SIZE(specs),
+ exp_stats, ARRAY_SIZE(exp_stats),
+ exp_refs, ARRAY_SIZE(exp_refs), 0, 0);
+}
+
+void test_online_push__force(void)
+{
+ const char *specs1[] = {"refs/heads/b3:refs/heads/tgt"};
+ push_status exp_stats1[] = { { "refs/heads/tgt", 1 } };
+ expected_ref exp_refs1[] = { { "refs/heads/tgt", &_oid_b3 } };
+
+ const char *specs2[] = {"refs/heads/b4:refs/heads/tgt"};
+
+ const char *specs2_force[] = {"+refs/heads/b4:refs/heads/tgt"};
+ push_status exp_stats2_force[] = { { "refs/heads/tgt", 1 } };
+ expected_ref exp_refs2_force[] = { { "refs/heads/tgt", &_oid_b4 } };
+
+ do_push(specs1, ARRAY_SIZE(specs1),
+ exp_stats1, ARRAY_SIZE(exp_stats1),
+ exp_refs1, ARRAY_SIZE(exp_refs1), 0, 1);
+
+ do_push(specs2, ARRAY_SIZE(specs2),
+ NULL, 0,
+ exp_refs1, ARRAY_SIZE(exp_refs1), GIT_ENONFASTFORWARD, 0);
+
+ /* Non-fast-forward update with force should pass. */
+ do_push(specs2_force, ARRAY_SIZE(specs2_force),
+ exp_stats2_force, ARRAY_SIZE(exp_stats2_force),
+ exp_refs2_force, ARRAY_SIZE(exp_refs2_force), 0, 1);
+}
+
+void test_online_push__delete(void)
+{
+ const char *specs1[] = {
+ "refs/heads/b1:refs/heads/tgt1",
+ "refs/heads/b1:refs/heads/tgt2"
+ };
+ push_status exp_stats1[] = {
+ { "refs/heads/tgt1", 1 },
+ { "refs/heads/tgt2", 1 }
+ };
+ expected_ref exp_refs1[] = {
+ { "refs/heads/tgt1", &_oid_b1 },
+ { "refs/heads/tgt2", &_oid_b1 }
+ };
+
+ const char *specs_del_fake[] = { ":refs/heads/fake" };
+ /* Force has no effect for delete. */
+ const char *specs_del_fake_force[] = { "+:refs/heads/fake" };
+ push_status exp_stats_fake[] = { { "refs/heads/fake", 1 } };
+
+ const char *specs_delete[] = { ":refs/heads/tgt1" };
+ push_status exp_stats_delete[] = { { "refs/heads/tgt1", 1 } };
+ expected_ref exp_refs_delete[] = { { "refs/heads/tgt2", &_oid_b1 } };
+ /* Force has no effect for delete. */
+ const char *specs_delete_force[] = { "+:refs/heads/tgt1" };
+
+ do_push(specs1, ARRAY_SIZE(specs1),
+ exp_stats1, ARRAY_SIZE(exp_stats1),
+ exp_refs1, ARRAY_SIZE(exp_refs1), 0, 1);
+
+ /* When deleting a non-existent branch, the git client sends zero for both
+ * the old and new commit id. This should succeed on the server with the
+ * same status report as if the branch were actually deleted. The server
+ * returns a warning on the side-band iff the side-band is supported.
+ * Since libgit2 doesn't support the side-band yet, there are no warnings.
+ */
+ do_push(specs_del_fake, ARRAY_SIZE(specs_del_fake),
+ exp_stats_fake, 1,
+ exp_refs1, ARRAY_SIZE(exp_refs1), 0, 0);
+ do_push(specs_del_fake_force, ARRAY_SIZE(specs_del_fake_force),
+ exp_stats_fake, 1,
+ exp_refs1, ARRAY_SIZE(exp_refs1), 0, 0);
+
+ /* Delete one of the pushed branches. */
+ do_push(specs_delete, ARRAY_SIZE(specs_delete),
+ exp_stats_delete, ARRAY_SIZE(exp_stats_delete),
+ exp_refs_delete, ARRAY_SIZE(exp_refs_delete), 0, 0);
+
+ /* Re-push branches and retry delete with force. */
+ do_push(specs1, ARRAY_SIZE(specs1),
+ exp_stats1, ARRAY_SIZE(exp_stats1),
+ exp_refs1, ARRAY_SIZE(exp_refs1), 0, 0);
+ do_push(specs_delete_force, ARRAY_SIZE(specs_delete_force),
+ exp_stats_delete, ARRAY_SIZE(exp_stats_delete),
+ exp_refs_delete, ARRAY_SIZE(exp_refs_delete), 0, 0);
+}
+
+void test_online_push__bad_refspecs(void)
+{
+ /* All classes of refspecs that should be rejected by
+ * git_push_add_refspec() should go in this test.
+ */
+ git_push *push;
+
+ if (_remote) {
+// cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH));
+ cl_git_pass(git_push_new(&push, _remote));
+
+ /* Unexpanded branch names not supported */
+ cl_git_fail(git_push_add_refspec(push, "b6:b6"));
+
+ git_push_free(push);
+ }
+}
+
+void test_online_push__expressions(void)
+{
+ /* TODO: Expressions in refspecs doesn't actually work yet */
+ const char *specs_left_expr[] = { "refs/heads/b2~1:refs/heads/b2" };
+
+ /* expect not NULL to indicate failure (core git replies "funny refname",
+ * other servers may be less pithy. */
+ const char *specs_right_expr[] = { "refs/heads/b2:refs/heads/b2~1" };
+ push_status exp_stats_right_expr[] = { { "refs/heads/b2~1", 0 } };
+
+ /* TODO: Find a more precise way of checking errors than a exit code of -1. */
+ do_push(specs_left_expr, ARRAY_SIZE(specs_left_expr),
+ NULL, 0,
+ NULL, 0, -1, 0);
+
+ do_push(specs_right_expr, ARRAY_SIZE(specs_right_expr),
+ exp_stats_right_expr, ARRAY_SIZE(exp_stats_right_expr),
+ NULL, 0, 0, 1);
+}
+
+void test_online_push__notes(void)
+{
+ git_oid note_oid, *target_oid, expected_oid;
+ git_signature *signature;
+ const char *specs[] = { "refs/notes/commits:refs/notes/commits" };
+ push_status exp_stats[] = { { "refs/notes/commits", 1 } };
+ expected_ref exp_refs[] = { { "refs/notes/commits", &expected_oid } };
+ git_oid_fromstr(&expected_oid, "8461a99b27b7043e58ff6e1f5d2cf07d282534fb");
+
+ target_oid = &_oid_b6;
+
+ /* Create note to push */
+ cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60)); /* Wed Dec 14 08:29:03 2011 +0100 */
+ cl_git_pass(git_note_create(&note_oid, _repo, signature, signature, NULL, target_oid, "hello world\n", 0));
+
+ do_push(specs, ARRAY_SIZE(specs),
+ exp_stats, ARRAY_SIZE(exp_stats),
+ exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
+
+ git_signature_free(signature);
+}
diff --git a/tests/online/push_util.c b/tests/online/push_util.c
new file mode 100644
index 000000000..038c144db
--- /dev/null
+++ b/tests/online/push_util.c
@@ -0,0 +1,132 @@
+
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "vector.h"
+#include "push_util.h"
+
+const git_oid OID_ZERO = {{ 0 }};
+
+void updated_tip_free(updated_tip *t)
+{
+ git__free(t->name);
+ git__free(t->old_oid);
+ git__free(t->new_oid);
+ git__free(t);
+}
+
+void record_callbacks_data_clear(record_callbacks_data *data)
+{
+ size_t i;
+ updated_tip *tip;
+
+ git_vector_foreach(&data->updated_tips, i, tip)
+ updated_tip_free(tip);
+
+ git_vector_free(&data->updated_tips);
+}
+
+int record_update_tips_cb(const char *refname, const git_oid *a, const git_oid *b, void *data)
+{
+ updated_tip *t;
+ record_callbacks_data *record_data = (record_callbacks_data *)data;
+
+ cl_assert(t = git__malloc(sizeof(*t)));
+
+ cl_assert(t->name = git__strdup(refname));
+ cl_assert(t->old_oid = git__malloc(sizeof(*t->old_oid)));
+ git_oid_cpy(t->old_oid, a);
+
+ cl_assert(t->new_oid = git__malloc(sizeof(*t->new_oid)));
+ git_oid_cpy(t->new_oid, b);
+
+ git_vector_insert(&record_data->updated_tips, t);
+
+ return 0;
+}
+
+int create_deletion_refspecs(git_vector *out, const git_remote_head **heads, size_t heads_len)
+{
+ git_buf del_spec = GIT_BUF_INIT;
+ size_t i;
+
+ for (i = 0; i < heads_len; i++) {
+ const git_remote_head *head = heads[i];
+ /* Ignore malformed ref names (which also saves us from tag^{} */
+ if (!git_reference_is_valid_name(head->name))
+ return 0;
+
+ /* Create a refspec that deletes a branch in the remote */
+ if (strcmp(head->name, "refs/heads/master")) {
+ cl_git_pass(git_buf_putc(&del_spec, ':'));
+ cl_git_pass(git_buf_puts(&del_spec, head->name));
+ cl_git_pass(git_vector_insert(out, git_buf_detach(&del_spec)));
+ }
+ }
+
+ return 0;
+}
+
+int record_ref_cb(git_remote_head *head, void *payload)
+{
+ git_vector *refs = (git_vector *) payload;
+ return git_vector_insert(refs, head);
+}
+
+void verify_remote_refs(const git_remote_head *actual_refs[], size_t actual_refs_len, const expected_ref expected_refs[], size_t expected_refs_len)
+{
+ size_t i, j = 0;
+ git_buf msg = GIT_BUF_INIT;
+ const git_remote_head *actual;
+ char *oid_str;
+ bool master_present = false;
+
+ /* We don't care whether "master" is present on the other end or not */
+ for (i = 0; i < actual_refs_len; i++) {
+ actual = actual_refs[i];
+ if (!strcmp(actual->name, "refs/heads/master")) {
+ master_present = true;
+ break;
+ }
+ }
+
+ if (expected_refs_len + (master_present ? 1 : 0) != actual_refs_len)
+ goto failed;
+
+ for (i = 0; i < actual_refs_len; i++) {
+ actual = actual_refs[i];
+ if (master_present && !strcmp(actual->name, "refs/heads/master"))
+ continue;
+
+ if (strcmp(expected_refs[j].name, actual->name) ||
+ git_oid_cmp(expected_refs[j].oid, &actual->oid))
+ goto failed;
+
+ j++;
+ }
+
+ return;
+
+failed:
+ git_buf_puts(&msg, "Expected and actual refs differ:\nEXPECTED:\n");
+
+ for(i = 0; i < expected_refs_len; i++) {
+ cl_assert(oid_str = git_oid_allocfmt(expected_refs[i].oid));
+ cl_git_pass(git_buf_printf(&msg, "%s = %s\n", expected_refs[i].name, oid_str));
+ git__free(oid_str);
+ }
+
+ git_buf_puts(&msg, "\nACTUAL:\n");
+ for (i = 0; i < actual_refs_len; i++) {
+ actual = actual_refs[i];
+ if (master_present && !strcmp(actual->name, "refs/heads/master"))
+ continue;
+
+ cl_assert(oid_str = git_oid_allocfmt(&actual->oid));
+ cl_git_pass(git_buf_printf(&msg, "%s = %s\n", actual->name, oid_str));
+ git__free(oid_str);
+ }
+
+ cl_fail(git_buf_cstr(&msg));
+
+ git_buf_free(&msg);
+}
diff --git a/tests/online/push_util.h b/tests/online/push_util.h
new file mode 100644
index 000000000..a7207c49e
--- /dev/null
+++ b/tests/online/push_util.h
@@ -0,0 +1,71 @@
+#ifndef INCLUDE_cl_push_util_h__
+#define INCLUDE_cl_push_util_h__
+
+#include "git2/oid.h"
+
+/* Constant for zero oid */
+extern const git_oid OID_ZERO;
+
+/**
+ * Macro for initializing git_remote_callbacks to use test helpers that
+ * record data in a record_callbacks_data instance.
+ * @param data pointer to a record_callbacks_data instance
+ */
+#define RECORD_CALLBACKS_INIT(data) \
+ { GIT_REMOTE_CALLBACKS_VERSION, NULL, NULL, cred_acquire_cb, NULL, record_update_tips_cb, data }
+
+typedef struct {
+ char *name;
+ git_oid *old_oid;
+ git_oid *new_oid;
+} updated_tip;
+
+typedef struct {
+ git_vector updated_tips;
+} record_callbacks_data;
+
+typedef struct {
+ const char *name;
+ const git_oid *oid;
+} expected_ref;
+
+void updated_tip_free(updated_tip *t);
+
+void record_callbacks_data_clear(record_callbacks_data *data);
+
+/**
+ * Callback for git_remote_update_tips that records updates
+ *
+ * @param data (git_vector *) of updated_tip instances
+ */
+int record_update_tips_cb(const char *refname, const git_oid *a, const git_oid *b, void *data);
+
+/**
+ * Create a set of refspecs that deletes each of the inputs
+ *
+ * @param out the vector in which to store the refspecs
+ * @param heads the remote heads
+ * @param heads_len the size of the array
+ */
+int create_deletion_refspecs(git_vector *out, const git_remote_head **heads, size_t heads_len);
+
+/**
+ * Callback for git_remote_list that adds refspecs to vector
+ *
+ * @param head a ref on the remote
+ * @param payload (git_vector *) of git_remote_head instances
+ */
+int record_ref_cb(git_remote_head *head, void *payload);
+
+/**
+ * Verifies that refs on remote stored by record_ref_cb match the expected
+ * names, oids, and order.
+ *
+ * @param actual_refs actual refs in the remote
+ * @param actual_refs_len length of actual_refs
+ * @param expected_refs expected remote refs
+ * @param expected_refs_len length of expected_refs
+ */
+void verify_remote_refs(const git_remote_head *actual_refs[], size_t actual_refs_len, const expected_ref expected_refs[], size_t expected_refs_len);
+
+#endif /* INCLUDE_cl_push_util_h__ */
diff --git a/tests/pack/indexer.c b/tests/pack/indexer.c
new file mode 100644
index 000000000..07963a9e7
--- /dev/null
+++ b/tests/pack/indexer.c
@@ -0,0 +1,126 @@
+#include "clar_libgit2.h"
+#include <git2.h>
+#include "fileops.h"
+#include "hash.h"
+#include "iterator.h"
+#include "vector.h"
+#include "posix.h"
+
+
+/*
+ * This is a packfile with three objects. The second is a delta which
+ * depends on the third, which is also a delta.
+ */
+unsigned char out_of_order_pack[] = {
+ 0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03,
+ 0x32, 0x78, 0x9c, 0x63, 0x67, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x76,
+ 0xe6, 0x8f, 0xe8, 0x12, 0x9b, 0x54, 0x6b, 0x10, 0x1a, 0xee, 0x95, 0x10,
+ 0xc5, 0x32, 0x8e, 0x7f, 0x21, 0xca, 0x1d, 0x18, 0x78, 0x9c, 0x63, 0x62,
+ 0x66, 0x4e, 0xcb, 0xcf, 0x07, 0x00, 0x02, 0xac, 0x01, 0x4d, 0x75, 0x01,
+ 0xd7, 0x71, 0x36, 0x66, 0xf4, 0xde, 0x82, 0x27, 0x76, 0xc7, 0x62, 0x2c,
+ 0x10, 0xf1, 0xb0, 0x7d, 0xe2, 0x80, 0xdc, 0x78, 0x9c, 0x63, 0x62, 0x62,
+ 0x62, 0xb7, 0x03, 0x00, 0x00, 0x69, 0x00, 0x4c, 0xde, 0x7d, 0xaa, 0xe4,
+ 0x19, 0x87, 0x58, 0x80, 0x61, 0x09, 0x9a, 0x33, 0xca, 0x7a, 0x31, 0x92,
+ 0x6f, 0xae, 0x66, 0x75
+};
+unsigned int out_of_order_pack_len = 112;
+
+/*
+ * Packfile with two objects. The second is a delta against an object
+ * which is not in the packfile
+ */
+unsigned char thin_pack[] = {
+ 0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02,
+ 0x32, 0x78, 0x9c, 0x63, 0x67, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x76,
+ 0xe6, 0x8f, 0xe8, 0x12, 0x9b, 0x54, 0x6b, 0x10, 0x1a, 0xee, 0x95, 0x10,
+ 0xc5, 0x32, 0x8e, 0x7f, 0x21, 0xca, 0x1d, 0x18, 0x78, 0x9c, 0x63, 0x62,
+ 0x66, 0x4e, 0xcb, 0xcf, 0x07, 0x00, 0x02, 0xac, 0x01, 0x4d, 0x42, 0x52,
+ 0x3a, 0x6f, 0x39, 0xd1, 0xfe, 0x66, 0x68, 0x6b, 0xa5, 0xe5, 0xe2, 0x97,
+ 0xac, 0x94, 0x6c, 0x76, 0x0b, 0x04
+};
+unsigned int thin_pack_len = 78;
+
+unsigned char base_obj[] = { 07, 076 };
+unsigned int base_obj_len = 2;
+
+void test_pack_indexer__out_of_order(void)
+{
+ git_indexer *idx;
+ git_transfer_progress stats;
+
+ cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL));
+ cl_git_pass(git_indexer_append(idx, out_of_order_pack, out_of_order_pack_len, &stats));
+ cl_git_pass(git_indexer_commit(idx, &stats));
+
+ cl_assert_equal_i(stats.total_objects, 3);
+ cl_assert_equal_i(stats.received_objects, 3);
+ cl_assert_equal_i(stats.indexed_objects, 3);
+
+ git_indexer_free(idx);
+}
+
+void test_pack_indexer__fix_thin(void)
+{
+ git_indexer *idx;
+ git_transfer_progress stats;
+ 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(!git_oid_cmp(&id, &should_id));
+
+ cl_git_pass(git_indexer_new(&idx, ".", 0, odb, NULL, NULL));
+ cl_git_pass(git_indexer_append(idx, thin_pack, thin_pack_len, &stats));
+ cl_git_pass(git_indexer_commit(idx, &stats));
+
+ cl_assert_equal_i(stats.total_objects, 2);
+ cl_assert_equal_i(stats.received_objects, 2);
+ cl_assert_equal_i(stats.indexed_objects, 2);
+ cl_assert_equal_i(stats.local_objects, 1);
+
+ git_oid_fromstr(&should_id, "11f0f69b334728fdd8bc86b80499f22f29d85b15");
+ cl_assert(!git_oid_cmp(git_indexer_hash(idx), &should_id));
+
+ git_indexer_free(idx);
+ git_odb_free(odb);
+ git_repository_free(repo);
+
+ /*
+ * The pack's name/hash only tells us what objects there are,
+ * so we need to go through the packfile again in order to
+ * figure out whether we calculated the trailer correctly.
+ */
+ {
+ unsigned char buffer[128];
+ int fd;
+ ssize_t read;
+ struct stat st;
+ const char *name = "pack-11f0f69b334728fdd8bc86b80499f22f29d85b15.pack";
+
+ fd = p_open(name, O_RDONLY);
+ cl_assert(fd != -1);
+
+ cl_git_pass(p_stat(name, &st));
+
+ cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL));
+ read = p_read(fd, buffer, sizeof(buffer));
+ cl_assert(read != -1);
+ p_close(fd);
+
+ cl_git_pass(git_indexer_append(idx, buffer, read, &stats));
+ cl_git_pass(git_indexer_commit(idx, &stats));
+
+ cl_assert_equal_i(stats.total_objects, 3);
+ cl_assert_equal_i(stats.received_objects, 3);
+ cl_assert_equal_i(stats.indexed_objects, 3);
+ cl_assert_equal_i(stats.local_objects, 0);
+
+ git_indexer_free(idx);
+ }
+}
diff --git a/tests/pack/packbuilder.c b/tests/pack/packbuilder.c
new file mode 100644
index 000000000..1ae2322a5
--- /dev/null
+++ b/tests/pack/packbuilder.c
@@ -0,0 +1,204 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+#include "pack.h"
+#include "hash.h"
+#include "iterator.h"
+#include "vector.h"
+#include "posix.h"
+
+static git_repository *_repo;
+static git_revwalk *_revwalker;
+static git_packbuilder *_packbuilder;
+static git_indexer *_indexer;
+static git_vector _commits;
+static int _commits_is_initialized;
+
+void test_pack_packbuilder__initialize(void)
+{
+ _repo = cl_git_sandbox_init("testrepo.git");
+ cl_git_pass(git_revwalk_new(&_revwalker, _repo));
+ cl_git_pass(git_packbuilder_new(&_packbuilder, _repo));
+ cl_git_pass(git_vector_init(&_commits, 0, NULL));
+ _commits_is_initialized = 1;
+}
+
+void test_pack_packbuilder__cleanup(void)
+{
+ git_oid *o;
+ unsigned int i;
+
+ if (_commits_is_initialized) {
+ _commits_is_initialized = 0;
+ git_vector_foreach(&_commits, i, o) {
+ git__free(o);
+ }
+ git_vector_free(&_commits);
+ }
+
+ git_packbuilder_free(_packbuilder);
+ _packbuilder = NULL;
+
+ git_revwalk_free(_revwalker);
+ _revwalker = NULL;
+
+ git_indexer_free(_indexer);
+ _indexer = NULL;
+
+ cl_git_sandbox_cleanup();
+ _repo = NULL;
+}
+
+static void seed_packbuilder(void)
+{
+ git_oid oid, *o;
+ unsigned int i;
+
+ git_revwalk_sorting(_revwalker, GIT_SORT_TIME);
+ cl_git_pass(git_revwalk_push_ref(_revwalker, "HEAD"));
+
+ while (git_revwalk_next(&oid, _revwalker) == 0) {
+ o = git__malloc(GIT_OID_RAWSZ);
+ cl_assert(o != NULL);
+ git_oid_cpy(o, &oid);
+ cl_git_pass(git_vector_insert(&_commits, o));
+ }
+
+ git_vector_foreach(&_commits, i, o) {
+ cl_git_pass(git_packbuilder_insert(_packbuilder, o, NULL));
+ }
+
+ git_vector_foreach(&_commits, i, o) {
+ git_object *obj;
+ cl_git_pass(git_object_lookup(&obj, _repo, o, GIT_OBJ_COMMIT));
+ cl_git_pass(git_packbuilder_insert_tree(_packbuilder,
+ git_commit_tree_id((git_commit *)obj)));
+ git_object_free(obj);
+ }
+}
+
+static int feed_indexer(void *ptr, size_t len, void *payload)
+{
+ git_transfer_progress *stats = (git_transfer_progress *)payload;
+
+ return git_indexer_append(_indexer, ptr, len, stats);
+}
+
+void test_pack_packbuilder__create_pack(void)
+{
+ git_transfer_progress stats;
+ git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
+ git_hash_ctx ctx;
+ git_oid hash;
+ char hex[41]; hex[40] = '\0';
+
+ seed_packbuilder();
+
+ cl_git_pass(git_indexer_new(&_indexer, ".", 0, NULL, NULL, NULL));
+ cl_git_pass(git_packbuilder_foreach(_packbuilder, feed_indexer, &stats));
+ cl_git_pass(git_indexer_commit(_indexer, &stats));
+
+ git_oid_fmt(hex, git_indexer_hash(_indexer));
+ git_buf_printf(&path, "pack-%s.pack", hex);
+
+ /*
+ * By default, packfiles are created with only one thread.
+ * Therefore we can predict the object ordering and make sure
+ * we create exactly the same pack as git.git does when *not*
+ * reusing existing deltas (as libgit2).
+ *
+ * $ cd tests/resources/testrepo.git
+ * $ git rev-list --objects HEAD | \
+ * git pack-objects -q --no-reuse-delta --threads=1 pack
+ * $ sha1sum git-80e61eb315239ef3c53033e37fee43b744d57122.pack
+ * 5d410bdf97cf896f9007681b92868471d636954b
+ *
+ */
+
+ cl_git_pass(git_futils_readbuffer(&buf, git_buf_cstr(&path)));
+
+ cl_git_pass(git_hash_ctx_init(&ctx));
+ cl_git_pass(git_hash_update(&ctx, buf.ptr, buf.size));
+ cl_git_pass(git_hash_final(&hash, &ctx));
+ git_hash_ctx_cleanup(&ctx);
+
+ git_buf_free(&path);
+ git_buf_free(&buf);
+
+ git_oid_fmt(hex, &hash);
+
+ cl_assert_equal_s(hex, "5d410bdf97cf896f9007681b92868471d636954b");
+}
+
+void test_pack_packbuilder__get_hash(void)
+{
+ char hex[41]; hex[40] = '\0';
+
+ seed_packbuilder();
+
+ git_packbuilder_write(_packbuilder, ".", 0, NULL, NULL);
+ git_oid_fmt(hex, git_packbuilder_hash(_packbuilder));
+
+ cl_assert_equal_s(hex, "80e61eb315239ef3c53033e37fee43b744d57122");
+}
+
+static void test_write_pack_permission(mode_t given, mode_t expected)
+{
+ struct stat statbuf;
+ mode_t mask, os_mask;
+
+ seed_packbuilder();
+
+ git_packbuilder_write(_packbuilder, ".", given, NULL, NULL);
+
+ /* Windows does not return group/user bits from stat,
+ * files are never executable.
+ */
+#ifdef GIT_WIN32
+ os_mask = 0600;
+#else
+ os_mask = 0777;
+#endif
+
+ mask = p_umask(0);
+ p_umask(mask);
+
+ cl_git_pass(p_stat("pack-80e61eb315239ef3c53033e37fee43b744d57122.idx", &statbuf));
+ cl_assert_equal_i(statbuf.st_mode & os_mask, (expected & ~mask) & os_mask);
+
+ cl_git_pass(p_stat("pack-80e61eb315239ef3c53033e37fee43b744d57122.pack", &statbuf));
+ cl_assert_equal_i(statbuf.st_mode & os_mask, (expected & ~mask) & os_mask);
+}
+
+void test_pack_packbuilder__permissions_standard(void)
+{
+ test_write_pack_permission(0, GIT_PACK_FILE_MODE);
+}
+
+void test_pack_packbuilder__permissions_readonly(void)
+{
+ test_write_pack_permission(0444, 0444);
+}
+
+void test_pack_packbuilder__permissions_readwrite(void)
+{
+ test_write_pack_permission(0666, 0666);
+}
+
+static git_transfer_progress stats;
+static int foreach_cb(void *buf, size_t len, void *payload)
+{
+ git_indexer *idx = (git_indexer *) payload;
+ cl_git_pass(git_indexer_append(idx, buf, len, &stats));
+ return 0;
+}
+
+void test_pack_packbuilder__foreach(void)
+{
+ git_indexer *idx;
+
+ seed_packbuilder();
+ cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL));
+ cl_git_pass(git_packbuilder_foreach(_packbuilder, foreach_cb, idx));
+ cl_git_pass(git_indexer_commit(idx, &stats));
+ git_indexer_free(idx);
+}
diff --git a/tests-clar/refs/branches/create.c b/tests/refs/branches/create.c
index 693a592a3..693a592a3 100644
--- a/tests-clar/refs/branches/create.c
+++ b/tests/refs/branches/create.c
diff --git a/tests/refs/branches/delete.c b/tests/refs/branches/delete.c
new file mode 100644
index 000000000..de90cb734
--- /dev/null
+++ b/tests/refs/branches/delete.c
@@ -0,0 +1,117 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+#include "repo/repo_helpers.h"
+#include "config/config_helpers.h"
+
+static git_repository *repo;
+static git_reference *fake_remote;
+
+void test_refs_branches_delete__initialize(void)
+{
+ git_oid id;
+
+ cl_fixture_sandbox("testrepo.git");
+ cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+
+ cl_git_pass(git_oid_fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
+ cl_git_pass(git_reference_create(&fake_remote, repo, "refs/remotes/nulltoken/master", &id, 0));
+}
+
+void test_refs_branches_delete__cleanup(void)
+{
+ git_reference_free(fake_remote);
+ fake_remote = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+
+ cl_fixture_cleanup("testrepo.git");
+}
+
+void test_refs_branches_delete__can_not_delete_a_branch_pointed_at_by_HEAD(void)
+{
+ git_reference *head;
+ git_reference *branch;
+
+ /* Ensure HEAD targets the local master branch */
+ cl_git_pass(git_reference_lookup(&head, repo, GIT_HEAD_FILE));
+ cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
+ git_reference_free(head);
+
+ cl_git_pass(git_branch_lookup(&branch, repo, "master", GIT_BRANCH_LOCAL));
+ cl_git_fail(git_branch_delete(branch));
+ git_reference_free(branch);
+}
+
+void test_refs_branches_delete__can_delete_a_branch_even_if_HEAD_is_missing(void)
+{
+ git_reference *head;
+ git_reference *branch;
+
+ cl_git_pass(git_reference_lookup(&head, repo, GIT_HEAD_FILE));
+ git_reference_delete(head);
+ git_reference_free(head);
+
+ cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_LOCAL));
+ cl_git_pass(git_branch_delete(branch));
+ git_reference_free(branch);
+}
+
+void test_refs_branches_delete__can_delete_a_branch_when_HEAD_is_unborn(void)
+{
+ git_reference *branch;
+
+ make_head_unborn(repo, NON_EXISTING_HEAD);
+
+ cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_LOCAL));
+ cl_git_pass(git_branch_delete(branch));
+ git_reference_free(branch);
+}
+
+void test_refs_branches_delete__can_delete_a_branch_pointed_at_by_detached_HEAD(void)
+{
+ git_reference *head, *branch;
+
+ cl_git_pass(git_reference_lookup(&head, repo, GIT_HEAD_FILE));
+ cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head));
+ cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
+ git_reference_free(head);
+
+ /* Detach HEAD and make it target the commit that "master" points to */
+ git_repository_detach_head(repo);
+
+ cl_git_pass(git_branch_lookup(&branch, repo, "master", GIT_BRANCH_LOCAL));
+ cl_git_pass(git_branch_delete(branch));
+ git_reference_free(branch);
+}
+
+void test_refs_branches_delete__can_delete_a_local_branch(void)
+{
+ git_reference *branch;
+ cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_LOCAL));
+ cl_git_pass(git_branch_delete(branch));
+ git_reference_free(branch);
+}
+
+void test_refs_branches_delete__can_delete_a_remote_branch(void)
+{
+ git_reference *branch;
+ cl_git_pass(git_branch_lookup(&branch, repo, "nulltoken/master", GIT_BRANCH_REMOTE));
+ cl_git_pass(git_branch_delete(branch));
+ git_reference_free(branch);
+}
+
+void test_refs_branches_delete__deleting_a_branch_removes_related_configuration_data(void)
+{
+ git_reference *branch;
+
+ assert_config_entry_existence(repo, "branch.track-local.remote", true);
+ assert_config_entry_existence(repo, "branch.track-local.merge", true);
+
+ cl_git_pass(git_branch_lookup(&branch, repo, "track-local", GIT_BRANCH_LOCAL));
+ cl_git_pass(git_branch_delete(branch));
+ git_reference_free(branch);
+
+ assert_config_entry_existence(repo, "branch.track-local.remote", false);
+ assert_config_entry_existence(repo, "branch.track-local.merge", false);
+}
diff --git a/tests/refs/branches/ishead.c b/tests/refs/branches/ishead.c
new file mode 100644
index 000000000..b1ad09c3e
--- /dev/null
+++ b/tests/refs/branches/ishead.c
@@ -0,0 +1,116 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+#include "repo/repo_helpers.h"
+
+static git_repository *repo;
+static git_reference *branch;
+
+void test_refs_branches_ishead__initialize(void)
+{
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+}
+
+void test_refs_branches_ishead__cleanup(void)
+{
+ git_reference_free(branch);
+ branch = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+}
+
+void test_refs_branches_ishead__can_tell_if_a_branch_is_pointed_at_by_HEAD(void)
+{
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master"));
+
+ cl_assert_equal_i(true, git_branch_is_head(branch));
+}
+
+void test_refs_branches_ishead__can_properly_handle_unborn_HEAD(void)
+{
+ git_repository_free(repo);
+
+ repo = cl_git_sandbox_init("testrepo.git");
+
+ make_head_unborn(repo, NON_EXISTING_HEAD);
+
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master"));
+
+ cl_assert_equal_i(false, git_branch_is_head(branch));
+
+ cl_git_sandbox_cleanup();
+ repo = NULL;
+}
+
+void test_refs_branches_ishead__can_properly_handle_missing_HEAD(void)
+{
+ git_repository_free(repo);
+
+ repo = cl_git_sandbox_init("testrepo.git");
+
+ delete_head(repo);
+
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master"));
+
+ cl_assert_equal_i(false, git_branch_is_head(branch));
+
+ cl_git_sandbox_cleanup();
+ repo = NULL;
+}
+
+void test_refs_branches_ishead__can_tell_if_a_branch_is_not_pointed_at_by_HEAD(void)
+{
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/br2"));
+
+ cl_assert_equal_i(false, git_branch_is_head(branch));
+}
+
+void test_refs_branches_ishead__wont_be_fooled_by_a_non_branch(void)
+{
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/tags/e90810b"));
+
+ cl_assert_equal_i(false, git_branch_is_head(branch));
+}
+
+/*
+ * $ git init .
+ * Initialized empty Git repository in d:/temp/tempee/.git/
+ *
+ * $ touch a && git add a
+ * $ git commit -m" boom"
+ * [master (root-commit) b47b758] boom
+ * 0 files changed
+ * create mode 100644 a
+ *
+ * $ echo "ref: refs/heads/master" > .git/refs/heads/linked
+ * $ echo "ref: refs/heads/linked" > .git/refs/heads/super
+ * $ echo "ref: refs/heads/super" > .git/HEAD
+ *
+ * $ git branch
+ * linked -> master
+ * * master
+ * super -> master
+ */
+void test_refs_branches_ishead__only_direct_references_are_considered(void)
+{
+ git_reference *linked, *super, *head;
+
+ git_repository_free(repo);
+ repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_git_pass(git_reference_symbolic_create(&linked, repo, "refs/heads/linked", "refs/heads/master", 0));
+ cl_git_pass(git_reference_symbolic_create(&super, repo, "refs/heads/super", "refs/heads/linked", 0));
+ cl_git_pass(git_reference_symbolic_create(&head, repo, GIT_HEAD_FILE, "refs/heads/super", 1));
+
+ cl_assert_equal_i(false, git_branch_is_head(linked));
+ cl_assert_equal_i(false, git_branch_is_head(super));
+
+ cl_git_pass(git_repository_head(&branch, repo));
+ cl_assert_equal_s("refs/heads/master", git_reference_name(branch));
+
+ git_reference_free(linked);
+ git_reference_free(super);
+ git_reference_free(head);
+ cl_git_sandbox_cleanup();
+ repo = NULL;
+}
diff --git a/tests/refs/branches/iterator.c b/tests/refs/branches/iterator.c
new file mode 100644
index 000000000..904c6a146
--- /dev/null
+++ b/tests/refs/branches/iterator.c
@@ -0,0 +1,151 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+
+static git_repository *repo;
+static git_reference *fake_remote;
+
+void test_refs_branches_iterator__initialize(void)
+{
+ git_oid id;
+
+ cl_fixture_sandbox("testrepo.git");
+ cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+
+ cl_git_pass(git_oid_fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
+ cl_git_pass(git_reference_create(&fake_remote, repo, "refs/remotes/nulltoken/master", &id, 0));
+}
+
+void test_refs_branches_iterator__cleanup(void)
+{
+ git_reference_free(fake_remote);
+ fake_remote = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+
+ cl_fixture_cleanup("testrepo.git");
+
+ cl_git_sandbox_cleanup();
+}
+
+static void assert_retrieval(unsigned int flags, unsigned int expected_count)
+{
+ git_branch_iterator *iter;
+ git_reference *ref;
+ int count = 0, error;
+ git_branch_t type;
+
+ cl_git_pass(git_branch_iterator_new(&iter, repo, flags));
+ while ((error = git_branch_next(&ref, &type, iter)) == 0) {
+ count++;
+ git_reference_free(ref);
+ }
+
+ git_branch_iterator_free(iter);
+ cl_assert_equal_i(error, GIT_ITEROVER);
+ cl_assert_equal_i(expected_count, count);
+}
+
+void test_refs_branches_iterator__retrieve_all_branches(void)
+{
+ assert_retrieval(GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE, 14);
+}
+
+void test_refs_branches_iterator__retrieve_remote_branches(void)
+{
+ assert_retrieval(GIT_BRANCH_REMOTE, 2);
+}
+
+void test_refs_branches_iterator__retrieve_local_branches(void)
+{
+ assert_retrieval(GIT_BRANCH_LOCAL, 12);
+}
+
+struct expectations {
+ const char *branch_name;
+ int encounters;
+};
+
+static void assert_branch_has_been_found(struct expectations *findings, const char* expected_branch_name)
+{
+ int pos = 0;
+
+ for (pos = 0; findings[pos].branch_name; ++pos) {
+ if (strcmp(expected_branch_name, findings[pos].branch_name) == 0) {
+ cl_assert_equal_i(1, findings[pos].encounters);
+ return;
+ }
+ }
+
+ cl_fail("expected branch not found in list.");
+}
+
+static void contains_branches(struct expectations exp[], git_branch_iterator *iter)
+{
+ git_reference *ref;
+ git_branch_t type;
+ int error, pos = 0;
+
+ while ((error = git_branch_next(&ref, &type, iter)) == 0) {
+ for (pos = 0; exp[pos].branch_name; ++pos) {
+ if (strcmp(git_reference_shorthand(ref), exp[pos].branch_name) == 0)
+ exp[pos].encounters++;
+ }
+
+ git_reference_free(ref);
+ }
+
+ cl_assert_equal_i(error, GIT_ITEROVER);
+}
+
+/*
+ * $ git branch -r
+ * nulltoken/HEAD -> nulltoken/master
+ * nulltoken/master
+ */
+void test_refs_branches_iterator__retrieve_remote_symbolic_HEAD_when_present(void)
+{
+ git_branch_iterator *iter;
+ struct expectations exp[] = {
+ { "nulltoken/HEAD", 0 },
+ { "nulltoken/master", 0 },
+ { NULL, 0 }
+ };
+
+ git_reference_free(fake_remote);
+ cl_git_pass(git_reference_symbolic_create(&fake_remote, repo, "refs/remotes/nulltoken/HEAD", "refs/remotes/nulltoken/master", 0));
+
+ assert_retrieval(GIT_BRANCH_REMOTE, 3);
+
+ cl_git_pass(git_branch_iterator_new(&iter, repo, GIT_BRANCH_REMOTE));
+ contains_branches(exp, iter);
+ git_branch_iterator_free(iter);
+
+ assert_branch_has_been_found(exp, "nulltoken/HEAD");
+ assert_branch_has_been_found(exp, "nulltoken/master");
+}
+
+void test_refs_branches_iterator__mix_of_packed_and_loose(void)
+{
+ git_branch_iterator *iter;
+ struct expectations exp[] = {
+ { "master", 0 },
+ { "origin/HEAD", 0 },
+ { "origin/master", 0 },
+ { "origin/packed", 0 },
+ { NULL, 0 }
+ };
+ git_repository *r2;
+
+ r2 = cl_git_sandbox_init("testrepo2");
+
+ cl_git_pass(git_branch_iterator_new(&iter, r2, GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE));
+ contains_branches(exp, iter);
+
+ git_branch_iterator_free(iter);
+
+ assert_branch_has_been_found(exp, "master");
+ assert_branch_has_been_found(exp, "origin/HEAD");
+ assert_branch_has_been_found(exp, "origin/master");
+ assert_branch_has_been_found(exp, "origin/packed");
+}
diff --git a/tests-clar/refs/branches/lookup.c b/tests/refs/branches/lookup.c
index 95d49a4b3..95d49a4b3 100644
--- a/tests-clar/refs/branches/lookup.c
+++ b/tests/refs/branches/lookup.c
diff --git a/tests-clar/refs/branches/move.c b/tests/refs/branches/move.c
index ecf14e006..ecf14e006 100644
--- a/tests-clar/refs/branches/move.c
+++ b/tests/refs/branches/move.c
diff --git a/tests-clar/refs/branches/name.c b/tests/refs/branches/name.c
index 176f836a4..176f836a4 100644
--- a/tests-clar/refs/branches/name.c
+++ b/tests/refs/branches/name.c
diff --git a/tests-clar/refs/branches/remote.c b/tests/refs/branches/remote.c
index c110adb33..c110adb33 100644
--- a/tests-clar/refs/branches/remote.c
+++ b/tests/refs/branches/remote.c
diff --git a/tests-clar/refs/branches/upstream.c b/tests/refs/branches/upstream.c
index 69e55a0c5..69e55a0c5 100644
--- a/tests-clar/refs/branches/upstream.c
+++ b/tests/refs/branches/upstream.c
diff --git a/tests-clar/refs/branches/upstreamname.c b/tests/refs/branches/upstreamname.c
index f05607d44..f05607d44 100644
--- a/tests-clar/refs/branches/upstreamname.c
+++ b/tests/refs/branches/upstreamname.c
diff --git a/tests-clar/refs/crashes.c b/tests/refs/crashes.c
index 5a1885a7a..5a1885a7a 100644
--- a/tests-clar/refs/crashes.c
+++ b/tests/refs/crashes.c
diff --git a/tests-clar/refs/create.c b/tests/refs/create.c
index 85ff05aa9..85ff05aa9 100644
--- a/tests-clar/refs/create.c
+++ b/tests/refs/create.c
diff --git a/tests-clar/refs/delete.c b/tests/refs/delete.c
index 973768aeb..973768aeb 100644
--- a/tests-clar/refs/delete.c
+++ b/tests/refs/delete.c
diff --git a/tests-clar/refs/foreachglob.c b/tests/refs/foreachglob.c
index 2c458082f..2c458082f 100644
--- a/tests-clar/refs/foreachglob.c
+++ b/tests/refs/foreachglob.c
diff --git a/tests-clar/refs/isvalidname.c b/tests/refs/isvalidname.c
index 65c70ba4d..65c70ba4d 100644
--- a/tests-clar/refs/isvalidname.c
+++ b/tests/refs/isvalidname.c
diff --git a/tests-clar/refs/iterator.c b/tests/refs/iterator.c
index 266410fdf..266410fdf 100644
--- a/tests-clar/refs/iterator.c
+++ b/tests/refs/iterator.c
diff --git a/tests/refs/list.c b/tests/refs/list.c
new file mode 100644
index 000000000..de5c0fd3d
--- /dev/null
+++ b/tests/refs/list.c
@@ -0,0 +1,57 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "git2/reflog.h"
+#include "reflog.h"
+
+static git_repository *g_repo;
+
+
+
+void test_refs_list__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_refs_list__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+
+
+void test_refs_list__all(void)
+{
+ // try to list all the references in our test repo
+ git_strarray ref_list;
+
+ cl_git_pass(git_reference_list(&ref_list, g_repo));
+
+ /*{
+ unsigned short i;
+ for (i = 0; i < ref_list.count; ++i)
+ printf("# %s\n", ref_list.strings[i]);
+ }*/
+
+ /* We have exactly 12 refs in total if we include the packed ones:
+ * there is a reference that exists both in the packfile and as
+ * loose, but we only list it once */
+ cl_assert_equal_i((int)ref_list.count, 14);
+
+ git_strarray_free(&ref_list);
+}
+
+void test_refs_list__do_not_retrieve_references_which_name_end_with_a_lock_extension(void)
+{
+ git_strarray ref_list;
+
+ /* Create a fake locked reference */
+ cl_git_mkfile(
+ "./testrepo/.git/refs/heads/hanwen.lock",
+ "144344043ba4d4a405da03de3844aa829ae8be0e\n");
+
+ cl_git_pass(git_reference_list(&ref_list, g_repo));
+ cl_assert_equal_i((int)ref_list.count, 14);
+
+ git_strarray_free(&ref_list);
+}
diff --git a/tests-clar/refs/listall.c b/tests/refs/listall.c
index c696fbb2e..c696fbb2e 100644
--- a/tests-clar/refs/listall.c
+++ b/tests/refs/listall.c
diff --git a/tests/refs/lookup.c b/tests/refs/lookup.c
new file mode 100644
index 000000000..2e31cf0f6
--- /dev/null
+++ b/tests/refs/lookup.c
@@ -0,0 +1,60 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+
+static git_repository *g_repo;
+
+void test_refs_lookup__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_refs_lookup__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_lookup__with_resolve(void)
+{
+ git_reference *a, *b, *temp;
+
+ cl_git_pass(git_reference_lookup(&temp, g_repo, "HEAD"));
+ cl_git_pass(git_reference_resolve(&a, temp));
+ git_reference_free(temp);
+
+ cl_git_pass(git_reference_lookup_resolved(&b, g_repo, "HEAD", 5));
+ cl_assert(git_reference_cmp(a, b) == 0);
+ git_reference_free(b);
+
+ cl_git_pass(git_reference_lookup_resolved(&b, g_repo, "HEAD_TRACKER", 5));
+ cl_assert(git_reference_cmp(a, b) == 0);
+ git_reference_free(b);
+
+ git_reference_free(a);
+}
+
+void test_refs_lookup__invalid_name(void)
+{
+ git_oid oid;
+ cl_git_fail(git_reference_name_to_id(&oid, g_repo, "/refs/tags/point_to_blob"));
+}
+
+void test_refs_lookup__oid(void)
+{
+ git_oid tag, expected;
+
+ cl_git_pass(git_reference_name_to_id(&tag, g_repo, "refs/tags/point_to_blob"));
+ cl_git_pass(git_oid_fromstr(&expected, "1385f264afb75a56a5bec74243be9b367ba4ca08"));
+ cl_assert(git_oid_cmp(&tag, &expected) == 0);
+}
+
+void test_refs_lookup__namespace(void)
+{
+ int error;
+ git_reference *ref;
+
+ error = git_reference_lookup(&ref, g_repo, "refs/heads");
+ cl_assert_equal_i(error, GIT_ENOTFOUND);
+
+ error = git_reference_lookup(&ref, g_repo, "refs/heads/");
+ cl_assert_equal_i(error, GIT_EINVALIDSPEC);
+}
diff --git a/tests-clar/refs/normalize.c b/tests/refs/normalize.c
index 7f313ef38..7f313ef38 100644
--- a/tests-clar/refs/normalize.c
+++ b/tests/refs/normalize.c
diff --git a/tests-clar/refs/overwrite.c b/tests/refs/overwrite.c
index ebe72069c..ebe72069c 100644
--- a/tests-clar/refs/overwrite.c
+++ b/tests/refs/overwrite.c
diff --git a/tests/refs/pack.c b/tests/refs/pack.c
new file mode 100644
index 000000000..849a052aa
--- /dev/null
+++ b/tests/refs/pack.c
@@ -0,0 +1,105 @@
+#include "clar_libgit2.h"
+
+#include "fileops.h"
+#include "git2/reflog.h"
+#include "git2/refdb.h"
+#include "reflog.h"
+#include "refs.h"
+#include "ref_helpers.h"
+
+static const char *loose_tag_ref_name = "refs/tags/e90810b";
+
+static git_repository *g_repo;
+
+void test_refs_pack__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_refs_pack__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static void packall(void)
+{
+ git_refdb *refdb;
+
+ cl_git_pass(git_repository_refdb(&refdb, g_repo));
+ cl_git_pass(git_refdb_compress(refdb));
+ git_refdb_free(refdb);
+}
+
+void test_refs_pack__empty(void)
+{
+ /* create a packfile for an empty folder */
+ git_buf temp_path = GIT_BUF_INIT;
+
+ cl_git_pass(git_buf_join_n(&temp_path, '/', 3, git_repository_path(g_repo), GIT_REFS_HEADS_DIR, "empty_dir"));
+ cl_git_pass(git_futils_mkdir_r(temp_path.ptr, NULL, GIT_REFS_DIR_MODE));
+ git_buf_free(&temp_path);
+
+ packall();
+}
+
+void test_refs_pack__loose(void)
+{
+ /* create a packfile from all the loose refs in a repo */
+ git_reference *reference;
+ git_buf temp_path = GIT_BUF_INIT;
+
+ /* Ensure a known loose ref can be looked up */
+ cl_git_pass(git_reference_lookup(&reference, g_repo, loose_tag_ref_name));
+ cl_assert(reference_is_packed(reference) == 0);
+ cl_assert_equal_s(reference->name, loose_tag_ref_name);
+ git_reference_free(reference);
+
+ /*
+ * We are now trying to pack also a loose reference
+ * called `points_to_blob`, to make sure we can properly
+ * pack weak tags
+ */
+ packall();
+
+ /* Ensure the packed-refs file exists */
+ cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), GIT_PACKEDREFS_FILE));
+ cl_assert(git_path_exists(temp_path.ptr));
+
+ /* Ensure the known ref can still be looked up but is now packed */
+ cl_git_pass(git_reference_lookup(&reference, g_repo, loose_tag_ref_name));
+ cl_assert(reference_is_packed(reference));
+ cl_assert_equal_s(reference->name, loose_tag_ref_name);
+
+ /* Ensure the known ref has been removed from the loose folder structure */
+ cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), loose_tag_ref_name));
+ cl_assert(!git_path_exists(temp_path.ptr));
+
+ git_reference_free(reference);
+ git_buf_free(&temp_path);
+}
+
+void test_refs_pack__symbolic(void)
+{
+ /* create a packfile from loose refs skipping symbolic refs */
+ int i;
+ git_oid head;
+ git_reference *ref;
+ char name[128];
+
+ cl_git_pass(git_reference_name_to_id(&head, g_repo, "HEAD"));
+
+ /* make a bunch of references */
+
+ for (i = 0; i < 100; ++i) {
+ snprintf(name, sizeof(name), "refs/heads/symbolic-%03d", i);
+ cl_git_pass(git_reference_symbolic_create(
+ &ref, g_repo, name, "refs/heads/master", 0));
+ git_reference_free(ref);
+
+ snprintf(name, sizeof(name), "refs/heads/direct-%03d", i);
+ cl_git_pass(git_reference_create(&ref, g_repo, name, &head, 0));
+ git_reference_free(ref);
+ }
+
+ packall();
+}
diff --git a/tests-clar/refs/peel.c b/tests/refs/peel.c
index f2fb6e259..f2fb6e259 100644
--- a/tests-clar/refs/peel.c
+++ b/tests/refs/peel.c
diff --git a/tests/refs/read.c b/tests/refs/read.c
new file mode 100644
index 000000000..35cf17e9e
--- /dev/null
+++ b/tests/refs/read.c
@@ -0,0 +1,284 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "git2/reflog.h"
+#include "reflog.h"
+#include "ref_helpers.h"
+
+static const char *loose_tag_ref_name = "refs/tags/e90810b";
+static const char *non_existing_tag_ref_name = "refs/tags/i-do-not-exist";
+static const char *head_tracker_sym_ref_name = "HEAD_TRACKER";
+static const char *current_head_target = "refs/heads/master";
+static const char *current_master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750";
+static const char *packed_head_name = "refs/heads/packed";
+static const char *packed_test_head_name = "refs/heads/packed-test";
+
+static git_repository *g_repo;
+
+void test_refs_read__initialize(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
+}
+
+void test_refs_read__cleanup(void)
+{
+ git_repository_free(g_repo);
+ g_repo = NULL;
+}
+
+void test_refs_read__loose_tag(void)
+{
+ // lookup a loose tag reference
+ git_reference *reference;
+ git_object *object;
+ git_buf ref_name_from_tag_name = GIT_BUF_INIT;
+
+ cl_git_pass(git_reference_lookup(&reference, g_repo, loose_tag_ref_name));
+ cl_assert(git_reference_type(reference) & GIT_REF_OID);
+ cl_assert(reference_is_packed(reference) == 0);
+ cl_assert_equal_s(reference->name, loose_tag_ref_name);
+
+ cl_git_pass(git_object_lookup(&object, g_repo, git_reference_target(reference), GIT_OBJ_ANY));
+ cl_assert(object != NULL);
+ cl_assert(git_object_type(object) == GIT_OBJ_TAG);
+
+ /* Ensure the name of the tag matches the name of the reference */
+ cl_git_pass(git_buf_joinpath(&ref_name_from_tag_name, GIT_REFS_TAGS_DIR, git_tag_name((git_tag *)object)));
+ cl_assert_equal_s(ref_name_from_tag_name.ptr, loose_tag_ref_name);
+ git_buf_free(&ref_name_from_tag_name);
+
+ git_object_free(object);
+
+ git_reference_free(reference);
+}
+
+void test_refs_read__nonexisting_tag(void)
+{
+ // lookup a loose tag reference that doesn't exist
+ git_reference *reference;
+
+ cl_git_fail(git_reference_lookup(&reference, g_repo, non_existing_tag_ref_name));
+
+ git_reference_free(reference);
+}
+
+
+void test_refs_read__symbolic(void)
+{
+ // lookup a symbolic reference
+ git_reference *reference, *resolved_ref;
+ git_object *object;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&reference, g_repo, GIT_HEAD_FILE));
+ cl_assert(git_reference_type(reference) & GIT_REF_SYMBOLIC);
+ cl_assert(reference_is_packed(reference) == 0);
+ cl_assert_equal_s(reference->name, GIT_HEAD_FILE);
+
+ cl_git_pass(git_reference_resolve(&resolved_ref, reference));
+ cl_assert(git_reference_type(resolved_ref) == GIT_REF_OID);
+
+ cl_git_pass(git_object_lookup(&object, g_repo, git_reference_target(resolved_ref), GIT_OBJ_ANY));
+ cl_assert(object != NULL);
+ cl_assert(git_object_type(object) == GIT_OBJ_COMMIT);
+
+ git_oid_fromstr(&id, current_master_tip);
+ cl_assert(git_oid_cmp(&id, git_object_id(object)) == 0);
+
+ git_object_free(object);
+
+ git_reference_free(reference);
+ git_reference_free(resolved_ref);
+}
+
+void test_refs_read__nested_symbolic(void)
+{
+ // lookup a nested symbolic reference
+ git_reference *reference, *resolved_ref;
+ git_object *object;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&reference, g_repo, head_tracker_sym_ref_name));
+ cl_assert(git_reference_type(reference) & GIT_REF_SYMBOLIC);
+ cl_assert(reference_is_packed(reference) == 0);
+ cl_assert_equal_s(reference->name, head_tracker_sym_ref_name);
+
+ cl_git_pass(git_reference_resolve(&resolved_ref, reference));
+ cl_assert(git_reference_type(resolved_ref) == GIT_REF_OID);
+
+ cl_git_pass(git_object_lookup(&object, g_repo, git_reference_target(resolved_ref), GIT_OBJ_ANY));
+ cl_assert(object != NULL);
+ cl_assert(git_object_type(object) == GIT_OBJ_COMMIT);
+
+ git_oid_fromstr(&id, current_master_tip);
+ cl_assert(git_oid_cmp(&id, git_object_id(object)) == 0);
+
+ git_object_free(object);
+
+ git_reference_free(reference);
+ git_reference_free(resolved_ref);
+}
+
+void test_refs_read__head_then_master(void)
+{
+ // lookup the HEAD and resolve the master branch
+ git_reference *reference, *resolved_ref, *comp_base_ref;
+
+ cl_git_pass(git_reference_lookup(&reference, g_repo, head_tracker_sym_ref_name));
+ cl_git_pass(git_reference_resolve(&comp_base_ref, reference));
+ git_reference_free(reference);
+
+ cl_git_pass(git_reference_lookup(&reference, g_repo, GIT_HEAD_FILE));
+ cl_git_pass(git_reference_resolve(&resolved_ref, reference));
+ cl_git_pass(git_oid_cmp(git_reference_target(comp_base_ref), git_reference_target(resolved_ref)));
+ git_reference_free(reference);
+ git_reference_free(resolved_ref);
+
+ cl_git_pass(git_reference_lookup(&reference, g_repo, current_head_target));
+ cl_git_pass(git_reference_resolve(&resolved_ref, reference));
+ cl_git_pass(git_oid_cmp(git_reference_target(comp_base_ref), git_reference_target(resolved_ref)));
+ git_reference_free(reference);
+ git_reference_free(resolved_ref);
+
+ git_reference_free(comp_base_ref);
+}
+
+void test_refs_read__master_then_head(void)
+{
+ // lookup the master branch and then the HEAD
+ git_reference *reference, *master_ref, *resolved_ref;
+
+ cl_git_pass(git_reference_lookup(&master_ref, g_repo, current_head_target));
+ cl_git_pass(git_reference_lookup(&reference, g_repo, GIT_HEAD_FILE));
+
+ cl_git_pass(git_reference_resolve(&resolved_ref, reference));
+ cl_git_pass(git_oid_cmp(git_reference_target(master_ref), git_reference_target(resolved_ref)));
+
+ git_reference_free(reference);
+ git_reference_free(resolved_ref);
+ git_reference_free(master_ref);
+}
+
+
+void test_refs_read__packed(void)
+{
+ // lookup a packed reference
+ git_reference *reference;
+ git_object *object;
+
+ cl_git_pass(git_reference_lookup(&reference, g_repo, packed_head_name));
+ cl_assert(git_reference_type(reference) & GIT_REF_OID);
+ cl_assert(reference_is_packed(reference));
+ cl_assert_equal_s(reference->name, packed_head_name);
+
+ cl_git_pass(git_object_lookup(&object, g_repo, git_reference_target(reference), GIT_OBJ_ANY));
+ cl_assert(object != NULL);
+ cl_assert(git_object_type(object) == GIT_OBJ_COMMIT);
+
+ git_object_free(object);
+
+ git_reference_free(reference);
+}
+
+void test_refs_read__loose_first(void)
+{
+ // assure that a loose reference is looked up before a packed reference
+ git_reference *reference;
+
+ cl_git_pass(git_reference_lookup(&reference, g_repo, packed_head_name));
+ git_reference_free(reference);
+ cl_git_pass(git_reference_lookup(&reference, g_repo, packed_test_head_name));
+ cl_assert(git_reference_type(reference) & GIT_REF_OID);
+ cl_assert(reference_is_packed(reference) == 0);
+ cl_assert_equal_s(reference->name, packed_test_head_name);
+
+ git_reference_free(reference);
+}
+
+void test_refs_read__chomped(void)
+{
+ git_reference *test, *chomped;
+
+ cl_git_pass(git_reference_lookup(&test, g_repo, "refs/heads/test"));
+ cl_git_pass(git_reference_lookup(&chomped, g_repo, "refs/heads/chomped"));
+ cl_git_pass(git_oid_cmp(git_reference_target(test), git_reference_target(chomped)));
+
+ git_reference_free(test);
+ git_reference_free(chomped);
+}
+
+void test_refs_read__trailing(void)
+{
+ git_reference *test, *trailing;
+
+ cl_git_pass(git_reference_lookup(&test, g_repo, "refs/heads/test"));
+ cl_git_pass(git_reference_lookup(&trailing, g_repo, "refs/heads/trailing"));
+ cl_git_pass(git_oid_cmp(git_reference_target(test), git_reference_target(trailing)));
+ git_reference_free(trailing);
+ cl_git_pass(git_reference_lookup(&trailing, g_repo, "FETCH_HEAD"));
+
+ git_reference_free(test);
+ git_reference_free(trailing);
+}
+
+void test_refs_read__unfound_return_ENOTFOUND(void)
+{
+ git_reference *reference;
+ git_oid id;
+
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_reference_lookup(&reference, g_repo, "TEST_MASTER"));
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_reference_lookup(&reference, g_repo, "refs/test/master"));
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_reference_lookup(&reference, g_repo, "refs/tags/test/master"));
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_reference_lookup(&reference, g_repo, "refs/tags/test/farther/master"));
+
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_reference_name_to_id(&id, g_repo, "refs/tags/test/farther/master"));
+}
+
+static void assert_is_branch(const char *name, bool expected_branchness)
+{
+ git_reference *reference;
+ cl_git_pass(git_reference_lookup(&reference, g_repo, name));
+ cl_assert_equal_i(expected_branchness, git_reference_is_branch(reference));
+ git_reference_free(reference);
+}
+
+void test_refs_read__can_determine_if_a_reference_is_a_local_branch(void)
+{
+ assert_is_branch("refs/heads/master", true);
+ assert_is_branch("refs/heads/packed", true);
+ assert_is_branch("refs/remotes/test/master", false);
+ assert_is_branch("refs/tags/e90810b", false);
+}
+
+static void assert_is_tag(const char *name, bool expected_tagness)
+{
+ git_reference *reference;
+ cl_git_pass(git_reference_lookup(&reference, g_repo, name));
+ cl_assert_equal_i(expected_tagness, git_reference_is_tag(reference));
+ git_reference_free(reference);
+}
+
+void test_refs_read__can_determine_if_a_reference_is_a_tag(void)
+{
+ assert_is_tag("refs/tags/e90810b", true);
+ assert_is_tag("refs/tags/test", true);
+ assert_is_tag("refs/heads/packed", false);
+ assert_is_tag("refs/remotes/test/master", false);
+}
+
+void test_refs_read__invalid_name_returns_EINVALIDSPEC(void)
+{
+ git_reference *reference;
+ git_oid id;
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC,
+ git_reference_lookup(&reference, g_repo, "refs/heads/Inv@{id"));
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC,
+ git_reference_name_to_id(&id, g_repo, "refs/heads/Inv@{id"));
+}
diff --git a/tests-clar/refs/ref_helpers.c b/tests/refs/ref_helpers.c
index 7676e65a7..7676e65a7 100644
--- a/tests-clar/refs/ref_helpers.c
+++ b/tests/refs/ref_helpers.c
diff --git a/tests-clar/refs/ref_helpers.h b/tests/refs/ref_helpers.h
index 0ef55bfce..0ef55bfce 100644
--- a/tests-clar/refs/ref_helpers.h
+++ b/tests/refs/ref_helpers.h
diff --git a/tests/refs/reflog/drop.c b/tests/refs/reflog/drop.c
new file mode 100644
index 000000000..916bd9933
--- /dev/null
+++ b/tests/refs/reflog/drop.c
@@ -0,0 +1,115 @@
+#include "clar_libgit2.h"
+
+#include "reflog.h"
+
+static git_repository *g_repo;
+static git_reflog *g_reflog;
+static size_t entrycount;
+
+void test_refs_reflog_drop__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+
+ git_reflog_read(&g_reflog, g_repo, "HEAD");
+ entrycount = git_reflog_entrycount(g_reflog);
+}
+
+void test_refs_reflog_drop__cleanup(void)
+{
+ git_reflog_free(g_reflog);
+ g_reflog = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_reflog_drop__dropping_a_non_exisiting_entry_from_the_log_returns_ENOTFOUND(void)
+{
+ cl_assert_equal_i(GIT_ENOTFOUND, git_reflog_drop(g_reflog, entrycount, 0));
+
+ cl_assert_equal_sz(entrycount, git_reflog_entrycount(g_reflog));
+}
+
+void test_refs_reflog_drop__can_drop_an_entry(void)
+{
+ cl_assert(entrycount > 4);
+
+ cl_git_pass(git_reflog_drop(g_reflog, 2, 0));
+ cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog));
+}
+
+void test_refs_reflog_drop__can_drop_an_entry_and_rewrite_the_log_history(void)
+{
+ const git_reflog_entry *before_current;
+ const git_reflog_entry *after_current;
+ git_oid before_current_old_oid, before_current_cur_oid;
+
+ cl_assert(entrycount > 4);
+
+ before_current = git_reflog_entry_byindex(g_reflog, 1);
+
+ git_oid_cpy(&before_current_old_oid, &before_current->oid_old);
+ git_oid_cpy(&before_current_cur_oid, &before_current->oid_cur);
+
+ cl_git_pass(git_reflog_drop(g_reflog, 1, 1));
+
+ cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog));
+
+ after_current = git_reflog_entry_byindex(g_reflog, 0);
+
+ cl_assert_equal_i(0, git_oid_cmp(&before_current_old_oid, &after_current->oid_old));
+ cl_assert(0 != git_oid_cmp(&before_current_cur_oid, &after_current->oid_cur));
+}
+
+void test_refs_reflog_drop__can_drop_the_oldest_entry(void)
+{
+ const git_reflog_entry *entry;
+
+ cl_assert(entrycount > 2);
+
+ cl_git_pass(git_reflog_drop(g_reflog, entrycount - 1, 0));
+ cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog));
+
+ entry = git_reflog_entry_byindex(g_reflog, entrycount - 2);
+ cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) != 0);
+}
+
+void test_refs_reflog_drop__can_drop_the_oldest_entry_and_rewrite_the_log_history(void)
+{
+ const git_reflog_entry *entry;
+
+ cl_assert(entrycount > 2);
+
+ cl_git_pass(git_reflog_drop(g_reflog, entrycount - 1, 1));
+ cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog));
+
+ entry = git_reflog_entry_byindex(g_reflog, entrycount - 2);
+ cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) == 0);
+}
+
+void test_refs_reflog_drop__can_drop_all_the_entries(void)
+{
+ cl_assert(--entrycount > 0);
+
+ do {
+ cl_git_pass(git_reflog_drop(g_reflog, 0, 1));
+ } while (--entrycount > 0);
+
+ cl_git_pass(git_reflog_drop(g_reflog, 0, 1));
+
+ cl_assert_equal_i(0, (int)git_reflog_entrycount(g_reflog));
+}
+
+void test_refs_reflog_drop__can_persist_deletion_on_disk(void)
+{
+ cl_assert(entrycount > 2);
+
+ cl_git_pass(git_reflog_drop(g_reflog, 0, 1));
+ cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog));
+ cl_git_pass(git_reflog_write(g_reflog));
+
+ git_reflog_free(g_reflog);
+
+ git_reflog_read(&g_reflog, g_repo, "HEAD");
+
+ cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog));
+}
diff --git a/tests/refs/reflog/reflog.c b/tests/refs/reflog/reflog.c
new file mode 100644
index 000000000..bcd224270
--- /dev/null
+++ b/tests/refs/reflog/reflog.c
@@ -0,0 +1,209 @@
+#include "clar_libgit2.h"
+
+#include "fileops.h"
+#include "git2/reflog.h"
+#include "reflog.h"
+
+
+static const char *new_ref = "refs/heads/test-reflog";
+static const char *current_master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750";
+#define commit_msg "commit: bla bla"
+
+static git_repository *g_repo;
+
+
+// helpers
+static void assert_signature(const git_signature *expected, const git_signature *actual)
+{
+ cl_assert(actual);
+ cl_assert_equal_s(expected->name, actual->name);
+ cl_assert_equal_s(expected->email, actual->email);
+ cl_assert(expected->when.offset == actual->when.offset);
+ cl_assert(expected->when.time == actual->when.time);
+}
+
+
+// Fixture setup and teardown
+void test_refs_reflog_reflog__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_refs_reflog_reflog__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static void assert_appends(const git_signature *committer, const git_oid *oid)
+{
+ git_repository *repo2;
+ git_reference *lookedup_ref;
+ git_reflog *reflog;
+ const git_reflog_entry *entry;
+
+ /* Reopen a new instance of the repository */
+ cl_git_pass(git_repository_open(&repo2, "testrepo.git"));
+
+ /* Lookup the previously created branch */
+ cl_git_pass(git_reference_lookup(&lookedup_ref, repo2, new_ref));
+
+ /* Read and parse the reflog for this branch */
+ cl_git_pass(git_reflog_read(&reflog, repo2, new_ref));
+ cl_assert_equal_i(2, (int)git_reflog_entrycount(reflog));
+
+ entry = git_reflog_entry_byindex(reflog, 1);
+ assert_signature(committer, entry->committer);
+ cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) == 0);
+ cl_assert(git_oid_cmp(oid, &entry->oid_cur) == 0);
+ cl_assert(entry->msg == NULL);
+
+ entry = git_reflog_entry_byindex(reflog, 0);
+ assert_signature(committer, entry->committer);
+ cl_assert(git_oid_cmp(oid, &entry->oid_old) == 0);
+ cl_assert(git_oid_cmp(oid, &entry->oid_cur) == 0);
+ cl_assert_equal_s(commit_msg, entry->msg);
+
+ git_reflog_free(reflog);
+ git_repository_free(repo2);
+
+ git_reference_free(lookedup_ref);
+}
+
+void test_refs_reflog_reflog__append_then_read(void)
+{
+ /* write a reflog for a given reference and ensure it can be read back */
+ git_reference *ref;
+ git_oid oid;
+ git_signature *committer;
+ git_reflog *reflog;
+
+ /* Create a new branch pointing at the HEAD */
+ git_oid_fromstr(&oid, current_master_tip);
+ cl_git_pass(git_reference_create(&ref, g_repo, new_ref, &oid, 0));
+ git_reference_free(ref);
+
+ cl_git_pass(git_signature_now(&committer, "foo", "foo@bar"));
+
+ cl_git_pass(git_reflog_read(&reflog, g_repo, new_ref));
+
+ cl_git_fail(git_reflog_append(reflog, &oid, committer, "no inner\nnewline"));
+ cl_git_pass(git_reflog_append(reflog, &oid, committer, NULL));
+ cl_git_pass(git_reflog_append(reflog, &oid, committer, commit_msg "\n"));
+ cl_git_pass(git_reflog_write(reflog));
+ git_reflog_free(reflog);
+
+ assert_appends(committer, &oid);
+
+ git_signature_free(committer);
+}
+
+void test_refs_reflog_reflog__append_to_then_read(void)
+{
+ /* write a reflog for a given reference and ensure it can be read back */
+ git_reference *ref;
+ git_oid oid;
+ git_signature *committer;
+
+ /* Create a new branch pointing at the HEAD */
+ git_oid_fromstr(&oid, current_master_tip);
+ cl_git_pass(git_reference_create(&ref, g_repo, new_ref, &oid, 0));
+ git_reference_free(ref);
+
+ cl_git_pass(git_signature_now(&committer, "foo", "foo@bar"));
+
+ cl_git_fail(git_reflog_append_to(g_repo, new_ref, &oid, committer, "no inner\nnewline"));
+ cl_git_pass(git_reflog_append_to(g_repo, new_ref, &oid, committer, NULL));
+ cl_git_pass(git_reflog_append_to(g_repo, new_ref, &oid, committer, commit_msg "\n"));
+
+ assert_appends(committer, &oid);
+
+ git_signature_free(committer);
+}
+
+void test_refs_reflog_reflog__renaming_the_reference_moves_the_reflog(void)
+{
+ git_reference *master, *new_master;
+ git_buf master_log_path = GIT_BUF_INIT, moved_log_path = GIT_BUF_INIT;
+
+ git_buf_joinpath(&master_log_path, git_repository_path(g_repo), GIT_REFLOG_DIR);
+ git_buf_puts(&moved_log_path, git_buf_cstr(&master_log_path));
+ git_buf_joinpath(&master_log_path, git_buf_cstr(&master_log_path), "refs/heads/master");
+ git_buf_joinpath(&moved_log_path, git_buf_cstr(&moved_log_path), "refs/moved");
+
+ cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&master_log_path)));
+ cl_assert_equal_i(false, git_path_isfile(git_buf_cstr(&moved_log_path)));
+
+ cl_git_pass(git_reference_lookup(&master, g_repo, "refs/heads/master"));
+ cl_git_pass(git_reference_rename(&new_master, master, "refs/moved", 0));
+ git_reference_free(master);
+
+ cl_assert_equal_i(false, git_path_isfile(git_buf_cstr(&master_log_path)));
+ cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&moved_log_path)));
+
+ git_reference_free(new_master);
+ git_buf_free(&moved_log_path);
+ git_buf_free(&master_log_path);
+}
+
+static void assert_has_reflog(bool expected_result, const char *name)
+{
+ git_reference *ref;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, name));
+
+ cl_assert_equal_i(expected_result, git_reference_has_log(ref));
+
+ git_reference_free(ref);
+}
+
+void test_refs_reflog_reflog__reference_has_reflog(void)
+{
+ assert_has_reflog(true, "HEAD");
+ assert_has_reflog(true, "refs/heads/master");
+ assert_has_reflog(false, "refs/heads/subtrees");
+}
+
+void test_refs_reflog_reflog__reading_the_reflog_from_a_reference_with_no_log_returns_an_empty_one(void)
+{
+ git_reflog *reflog;
+ const char *refname = "refs/heads/subtrees";
+ git_buf subtrees_log_path = GIT_BUF_INIT;
+
+ git_buf_join_n(&subtrees_log_path, '/', 3, git_repository_path(g_repo), GIT_REFLOG_DIR, refname);
+ cl_assert_equal_i(false, git_path_isfile(git_buf_cstr(&subtrees_log_path)));
+
+ cl_git_pass(git_reflog_read(&reflog, g_repo, refname));
+
+ cl_assert_equal_i(0, (int)git_reflog_entrycount(reflog));
+
+ git_reflog_free(reflog);
+ git_buf_free(&subtrees_log_path);
+}
+
+void test_refs_reflog_reflog__cannot_write_a_moved_reflog(void)
+{
+ git_reference *master, *new_master;
+ git_buf master_log_path = GIT_BUF_INIT, moved_log_path = GIT_BUF_INIT;
+ git_reflog *reflog;
+
+ cl_git_pass(git_reference_lookup(&master, g_repo, "refs/heads/master"));
+ cl_git_pass(git_reflog_read(&reflog, g_repo, "refs/heads/master"));
+
+ cl_git_pass(git_reflog_write(reflog));
+
+ cl_git_pass(git_reference_rename(&new_master, master, "refs/moved", 0));
+ git_reference_free(master);
+
+ cl_git_fail(git_reflog_write(reflog));
+
+ git_reflog_free(reflog);
+ git_reference_free(new_master);
+ git_buf_free(&moved_log_path);
+ git_buf_free(&master_log_path);
+}
+
+void test_refs_reflog_reflog__renaming_with_an_invalid_name_returns_EINVALIDSPEC(void)
+{
+ cl_assert_equal_i(GIT_EINVALIDSPEC,
+ git_reflog_rename(g_repo, "refs/heads/master", "refs/heads/Inv@{id"));
+}
diff --git a/tests-clar/refs/rename.c b/tests/refs/rename.c
index 543bc4d62..543bc4d62 100644
--- a/tests-clar/refs/rename.c
+++ b/tests/refs/rename.c
diff --git a/tests/refs/revparse.c b/tests/refs/revparse.c
new file mode 100644
index 000000000..37d3981bb
--- /dev/null
+++ b/tests/refs/revparse.c
@@ -0,0 +1,813 @@
+#include "clar_libgit2.h"
+
+#include "git2/revparse.h"
+#include "buffer.h"
+#include "refs.h"
+#include "path.h"
+
+static git_repository *g_repo;
+static git_object *g_obj;
+
+/* Helpers */
+static void test_object_and_ref_inrepo(
+ const char *spec,
+ const char *expected_oid,
+ const char *expected_refname,
+ git_repository *repo,
+ bool assert_reference_retrieval)
+{
+ char objstr[64] = {0};
+ git_object *obj = NULL;
+ git_reference *ref = NULL;
+ int error;
+
+ error = git_revparse_ext(&obj, &ref, repo, spec);
+
+ if (expected_oid != NULL) {
+ cl_assert_equal_i(0, error);
+ git_oid_fmt(objstr, git_object_id(obj));
+ cl_assert_equal_s(objstr, expected_oid);
+ } else
+ cl_assert_equal_i(GIT_ENOTFOUND, error);
+
+ if (assert_reference_retrieval) {
+ if (expected_refname == NULL)
+ cl_assert(NULL == ref);
+ else
+ cl_assert_equal_s(expected_refname, git_reference_name(ref));
+ }
+
+ git_object_free(obj);
+ git_reference_free(ref);
+}
+
+static void test_object_inrepo(const char *spec, const char *expected_oid, git_repository *repo)
+{
+ test_object_and_ref_inrepo(spec, expected_oid, NULL, repo, false);
+}
+
+static void test_id_inrepo(
+ const char *spec,
+ const char *expected_left,
+ const char *expected_right,
+ git_revparse_mode_t expected_flags,
+ git_repository *repo)
+{
+ git_revspec revspec;
+ int error = git_revparse(&revspec, repo, spec);
+
+ if (expected_left) {
+ char str[64] = {0};
+ cl_assert_equal_i(0, error);
+ git_oid_fmt(str, git_object_id(revspec.from));
+ cl_assert_equal_s(str, expected_left);
+ git_object_free(revspec.from);
+ } else {
+ cl_assert_equal_i(GIT_ENOTFOUND, error);
+ }
+
+ if (expected_right) {
+ char str[64] = {0};
+ git_oid_fmt(str, git_object_id(revspec.to));
+ cl_assert_equal_s(str, expected_right);
+ git_object_free(revspec.to);
+ }
+
+ if (expected_flags)
+ cl_assert_equal_i(expected_flags, revspec.flags);
+}
+
+static void test_object(const char *spec, const char *expected_oid)
+{
+ test_object_inrepo(spec, expected_oid, g_repo);
+}
+
+static void test_object_and_ref(const char *spec, const char *expected_oid, const char *expected_refname)
+{
+ test_object_and_ref_inrepo(spec, expected_oid, expected_refname, g_repo, true);
+}
+
+static void test_rangelike(const char *rangelike,
+ const char *expected_left,
+ const char *expected_right,
+ git_revparse_mode_t expected_revparseflags)
+{
+ char objstr[64] = {0};
+ git_revspec revspec;
+ int error;
+
+ error = git_revparse(&revspec, g_repo, rangelike);
+
+ if (expected_left != NULL) {
+ cl_assert_equal_i(0, error);
+ cl_assert_equal_i(revspec.flags, expected_revparseflags);
+ git_oid_fmt(objstr, git_object_id(revspec.from));
+ cl_assert_equal_s(objstr, expected_left);
+ git_oid_fmt(objstr, git_object_id(revspec.to));
+ cl_assert_equal_s(objstr, expected_right);
+ } else
+ cl_assert(error != 0);
+
+ git_object_free(revspec.from);
+ git_object_free(revspec.to);
+}
+
+
+static void test_id(
+ const char *spec,
+ const char *expected_left,
+ const char *expected_right,
+ git_revparse_mode_t expected_flags)
+{
+ test_id_inrepo(spec, expected_left, expected_right, expected_flags, g_repo);
+}
+
+void test_refs_revparse__initialize(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
+}
+
+void test_refs_revparse__cleanup(void)
+{
+ git_repository_free(g_repo);
+}
+
+void test_refs_revparse__nonexistant_object(void)
+{
+ test_object("this-does-not-exist", NULL);
+ test_object("this-does-not-exist^1", NULL);
+ test_object("this-does-not-exist~2", NULL);
+}
+
+static void assert_invalid_single_spec(const char *invalid_spec)
+{
+ cl_assert_equal_i(
+ GIT_EINVALIDSPEC, git_revparse_single(&g_obj, g_repo, invalid_spec));
+}
+
+void test_refs_revparse__invalid_reference_name(void)
+{
+ assert_invalid_single_spec("this doesn't make sense");
+ assert_invalid_single_spec("Inv@{id");
+ assert_invalid_single_spec("");
+}
+
+void test_refs_revparse__shas(void)
+{
+ test_object("c47800c7266a2be04c571c04d5a6614691ea99bd", "c47800c7266a2be04c571c04d5a6614691ea99bd");
+ test_object("c47800c", "c47800c7266a2be04c571c04d5a6614691ea99bd");
+}
+
+void test_refs_revparse__head(void)
+{
+ test_object("HEAD", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("HEAD^0", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("HEAD~0", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("master", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+}
+
+void test_refs_revparse__full_refs(void)
+{
+ test_object("refs/heads/master", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("refs/heads/test", "e90810b8df3e80c413d903f631643c716887138d");
+ test_object("refs/tags/test", "b25fa35b38051e4ae45d4222e795f9df2e43f1d1");
+}
+
+void test_refs_revparse__partial_refs(void)
+{
+ test_object("point_to_blob", "1385f264afb75a56a5bec74243be9b367ba4ca08");
+ test_object("packed-test", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045");
+ test_object("br2", "a4a7dce85cf63874e984719f4fdd239f5145052f");
+}
+
+void test_refs_revparse__describe_output(void)
+{
+ test_object("blah-7-gc47800c", "c47800c7266a2be04c571c04d5a6614691ea99bd");
+ test_object("not-good", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+}
+
+void test_refs_revparse__nth_parent(void)
+{
+ assert_invalid_single_spec("be3563a^-1");
+ assert_invalid_single_spec("^");
+ assert_invalid_single_spec("be3563a^{tree}^");
+ assert_invalid_single_spec("point_to_blob^{blob}^");
+ assert_invalid_single_spec("this doesn't make sense^1");
+
+ test_object("be3563a^1", "9fd738e8f7967c078dceed8190330fc8648ee56a");
+ test_object("be3563a^", "9fd738e8f7967c078dceed8190330fc8648ee56a");
+ test_object("be3563a^2", "c47800c7266a2be04c571c04d5a6614691ea99bd");
+ test_object("be3563a^1^1", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045");
+ test_object("be3563a^^", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045");
+ test_object("be3563a^2^1", "5b5b025afb0b4c913b4c338a42934a3863bf3644");
+ test_object("be3563a^0", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("be3563a^{commit}^", "9fd738e8f7967c078dceed8190330fc8648ee56a");
+
+ test_object("be3563a^42", NULL);
+}
+
+void test_refs_revparse__not_tag(void)
+{
+ test_object("point_to_blob^{}", "1385f264afb75a56a5bec74243be9b367ba4ca08");
+ test_object("wrapped_tag^{}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("master^{}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("master^{tree}^{}", "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162");
+ test_object("e90810b^{}", "e90810b8df3e80c413d903f631643c716887138d");
+ test_object("tags/e90810b^{}", "e90810b8df3e80c413d903f631643c716887138d");
+ test_object("e908^{}", "e90810b8df3e80c413d903f631643c716887138d");
+}
+
+void test_refs_revparse__to_type(void)
+{
+ assert_invalid_single_spec("wrapped_tag^{trip}");
+ test_object("point_to_blob^{commit}", NULL);
+ cl_assert_equal_i(
+ GIT_EAMBIGUOUS, git_revparse_single(&g_obj, g_repo, "wrapped_tag^{blob}"));
+
+ test_object("wrapped_tag^{commit}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("wrapped_tag^{tree}", "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162");
+ test_object("point_to_blob^{blob}", "1385f264afb75a56a5bec74243be9b367ba4ca08");
+ test_object("master^{commit}^{commit}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+}
+
+void test_refs_revparse__linear_history(void)
+{
+ assert_invalid_single_spec("~");
+ test_object("foo~bar", NULL);
+
+ assert_invalid_single_spec("master~bar");
+ assert_invalid_single_spec("master~-1");
+ assert_invalid_single_spec("master~0bar");
+ assert_invalid_single_spec("this doesn't make sense~2");
+ assert_invalid_single_spec("be3563a^{tree}~");
+ assert_invalid_single_spec("point_to_blob^{blob}~");
+
+ test_object("master~0", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("master~1", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("master~2", "9fd738e8f7967c078dceed8190330fc8648ee56a");
+ test_object("master~1~1", "9fd738e8f7967c078dceed8190330fc8648ee56a");
+ test_object("master~~", "9fd738e8f7967c078dceed8190330fc8648ee56a");
+}
+
+void test_refs_revparse__chaining(void)
+{
+ assert_invalid_single_spec("master@{0}@{0}");
+ assert_invalid_single_spec("@{u}@{-1}");
+ assert_invalid_single_spec("@{-1}@{-1}");
+ assert_invalid_single_spec("@{-3}@{0}");
+
+ test_object("master@{0}~1^1", "9fd738e8f7967c078dceed8190330fc8648ee56a");
+ test_object("@{u}@{0}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("@{-1}@{0}", "a4a7dce85cf63874e984719f4fdd239f5145052f");
+ test_object("@{-4}@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("master~1^1", "9fd738e8f7967c078dceed8190330fc8648ee56a");
+ test_object("master~1^2", "c47800c7266a2be04c571c04d5a6614691ea99bd");
+ test_object("master^1^2~1", "5b5b025afb0b4c913b4c338a42934a3863bf3644");
+ test_object("master^^2^", "5b5b025afb0b4c913b4c338a42934a3863bf3644");
+ test_object("master^1^1^1^1^1", "8496071c1b46c854b31185ea97743be6a8774479");
+ test_object("master^^1^2^1", NULL);
+}
+
+void test_refs_revparse__upstream(void)
+{
+ assert_invalid_single_spec("e90810b@{u}");
+ assert_invalid_single_spec("refs/tags/e90810b@{u}");
+ test_object("refs/heads/e90810b@{u}", NULL);
+
+ test_object("master@{upstream}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("@{u}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("master@{u}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("heads/master@{u}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("refs/heads/master@{u}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+}
+
+void test_refs_revparse__ordinal(void)
+{
+ assert_invalid_single_spec("master@{-2}");
+
+ /* TODO: make the test below actually fail
+ * cl_git_fail(git_revparse_single(&g_obj, g_repo, "master@{1a}"));
+ */
+
+ test_object("nope@{0}", NULL);
+ test_object("master@{31415}", NULL);
+ test_object("@{1000}", NULL);
+ test_object("@{2}", NULL);
+
+ test_object("@{0}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+
+ test_object("master@{0}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("master@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("heads/master@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("refs/heads/master@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+}
+
+void test_refs_revparse__previous_head(void)
+{
+ assert_invalid_single_spec("@{-xyz}");
+ assert_invalid_single_spec("@{-0}");
+ assert_invalid_single_spec("@{-1b}");
+
+ test_object("@{-42}", NULL);
+
+ test_object("@{-2}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("@{-1}", "a4a7dce85cf63874e984719f4fdd239f5145052f");
+}
+
+static void create_fake_stash_reference_and_reflog(git_repository *repo)
+{
+ git_reference *master, *new_master;
+ git_buf log_path = GIT_BUF_INIT;
+
+ git_buf_joinpath(&log_path, git_repository_path(repo), "logs/refs/fakestash");
+
+ cl_assert_equal_i(false, git_path_isfile(git_buf_cstr(&log_path)));
+
+ cl_git_pass(git_reference_lookup(&master, repo, "refs/heads/master"));
+ cl_git_pass(git_reference_rename(&new_master, master, "refs/fakestash", 0));
+ git_reference_free(master);
+
+ cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&log_path)));
+
+ git_buf_free(&log_path);
+ git_reference_free(new_master);
+}
+
+void test_refs_revparse__reflog_of_a_ref_under_refs(void)
+{
+ git_repository *repo = cl_git_sandbox_init("testrepo.git");
+
+ test_object_inrepo("refs/fakestash", NULL, repo);
+
+ create_fake_stash_reference_and_reflog(repo);
+
+ /*
+ * $ git reflog -1 refs/fakestash
+ * a65fedf refs/fakestash@{0}: commit: checking in
+ *
+ * $ git reflog -1 refs/fakestash@{0}
+ * a65fedf refs/fakestash@{0}: commit: checking in
+ *
+ * $ git reflog -1 fakestash
+ * a65fedf fakestash@{0}: commit: checking in
+ *
+ * $ git reflog -1 fakestash@{0}
+ * a65fedf fakestash@{0}: commit: checking in
+ */
+ test_object_inrepo("refs/fakestash", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo);
+ test_object_inrepo("refs/fakestash@{0}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo);
+ test_object_inrepo("fakestash", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo);
+ test_object_inrepo("fakestash@{0}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo);
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_revparse__revwalk(void)
+{
+ test_object("master^{/not found in any commit}", NULL);
+ test_object("master^{/merge}", NULL);
+ assert_invalid_single_spec("master^{/((}");
+
+ test_object("master^{/anoth}", "5b5b025afb0b4c913b4c338a42934a3863bf3644");
+ test_object("master^{/Merge}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("br2^{/Merge}", "a4a7dce85cf63874e984719f4fdd239f5145052f");
+ test_object("master^{/fo.rth}", "9fd738e8f7967c078dceed8190330fc8648ee56a");
+}
+
+void test_refs_revparse__date(void)
+{
+ /*
+ * $ git reflog HEAD --date=iso
+ * a65fedf HEAD@{2012-04-30 08:23:41 -0900}: checkout: moving from br2 to master
+ * a4a7dce HEAD@{2012-04-30 08:23:37 -0900}: commit: checking in
+ * c47800c HEAD@{2012-04-30 08:23:28 -0900}: checkout: moving from master to br2
+ * a65fedf HEAD@{2012-04-30 08:23:23 -0900}: commit:
+ * be3563a HEAD@{2012-04-30 10:22:43 -0700}: clone: from /Users/ben/src/libgit2/tes
+ *
+ * $ git reflog HEAD --date=raw
+ * a65fedf HEAD@{1335806621 -0900}: checkout: moving from br2 to master
+ * a4a7dce HEAD@{1335806617 -0900}: commit: checking in
+ * c47800c HEAD@{1335806608 -0900}: checkout: moving from master to br2
+ * a65fedf HEAD@{1335806603 -0900}: commit:
+ * be3563a HEAD@{1335806563 -0700}: clone: from /Users/ben/src/libgit2/tests/resour
+ */
+ test_object("HEAD@{10 years ago}", NULL);
+
+ test_object("HEAD@{1 second}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("HEAD@{1 second ago}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("HEAD@{2 days ago}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+
+ /*
+ * $ git reflog master --date=iso
+ * a65fedf master@{2012-04-30 09:23:23 -0800}: commit: checking in
+ * be3563a master@{2012-04-30 09:22:43 -0800}: clone: from /Users/ben/src...
+ *
+ * $ git reflog master --date=raw
+ * a65fedf master@{1335806603 -0800}: commit: checking in
+ * be3563a master@{1335806563 -0800}: clone: from /Users/ben/src/libgit2/tests/reso
+ */
+
+
+ /*
+ * $ git reflog -1 "master@{2012-04-30 17:22:42 +0000}"
+ * warning: Log for 'master' only goes back to Mon, 30 Apr 2012 09:22:43 -0800.
+ */
+ test_object("master@{2012-04-30 17:22:42 +0000}", NULL);
+ test_object("master@{2012-04-30 09:22:42 -0800}", NULL);
+
+ /*
+ * $ git reflog -1 "master@{2012-04-30 17:22:43 +0000}"
+ * be3563a master@{Mon Apr 30 09:22:43 2012 -0800}: clone: from /Users/ben/src/libg
+ */
+ test_object("master@{2012-04-30 17:22:43 +0000}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("master@{2012-04-30 09:22:43 -0800}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+
+ /*
+ * $ git reflog -1 "master@{2012-4-30 09:23:27 -0800}"
+ * a65fedf master@{Mon Apr 30 09:23:23 2012 -0800}: commit: checking in
+ */
+ test_object("master@{2012-4-30 09:23:27 -0800}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+
+ /*
+ * $ git reflog -1 master@{2012-05-03}
+ * a65fedf master@{Mon Apr 30 09:23:23 2012 -0800}: commit: checking in
+ */
+ test_object("master@{2012-05-03}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+
+ /*
+ * $ git reflog -1 "master@{1335806603}"
+ * a65fedf
+ *
+ * $ git reflog -1 "master@{1335806602}"
+ * be3563a
+ */
+ test_object("master@{1335806603}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("master@{1335806602}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+}
+
+void test_refs_revparse__colon(void)
+{
+ assert_invalid_single_spec(":/");
+ assert_invalid_single_spec("point_to_blob:readme.txt");
+ cl_git_fail(git_revparse_single(&g_obj, g_repo, ":2:README")); /* Not implemented */
+
+ test_object(":/not found in any commit", NULL);
+ test_object("subtrees:ab/42.txt", NULL);
+ test_object("subtrees:ab/4.txt/nope", NULL);
+ test_object("subtrees:nope", NULL);
+ test_object("test/master^1:branch_file.txt", NULL);
+
+ /* From tags */
+ test_object("test:readme.txt", "0266163a49e280c4f5ed1e08facd36a2bd716bcf");
+ test_object("tags/test:readme.txt", "0266163a49e280c4f5ed1e08facd36a2bd716bcf");
+ test_object("e90810b:readme.txt", "0266163a49e280c4f5ed1e08facd36a2bd716bcf");
+ test_object("tags/e90810b:readme.txt", "0266163a49e280c4f5ed1e08facd36a2bd716bcf");
+
+ /* From commits */
+ test_object("a65f:branch_file.txt", "3697d64be941a53d4ae8f6a271e4e3fa56b022cc");
+
+ /* From trees */
+ test_object("a65f^{tree}:branch_file.txt", "3697d64be941a53d4ae8f6a271e4e3fa56b022cc");
+ test_object("944c:branch_file.txt", "3697d64be941a53d4ae8f6a271e4e3fa56b022cc");
+
+ /* Retrieving trees */
+ test_object("master:", "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162");
+ test_object("subtrees:", "ae90f12eea699729ed24555e40b9fd669da12a12");
+ test_object("subtrees:ab", "f1425cef211cc08caa31e7b545ffb232acb098c3");
+ test_object("subtrees:ab/", "f1425cef211cc08caa31e7b545ffb232acb098c3");
+
+ /* Retrieving blobs */
+ test_object("subtrees:ab/4.txt", "d6c93164c249c8000205dd4ec5cbca1b516d487f");
+ test_object("subtrees:ab/de/fgh/1.txt", "1f67fc4386b2d171e0d21be1c447e12660561f9b");
+ test_object("master:README", "a8233120f6ad708f843d861ce2b7228ec4e3dec6");
+ test_object("master:new.txt", "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd");
+ test_object(":/Merge", "a4a7dce85cf63874e984719f4fdd239f5145052f");
+ test_object(":/one", "c47800c7266a2be04c571c04d5a6614691ea99bd");
+ test_object(":/packed commit t", "41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9");
+ test_object("test/master^2:branch_file.txt", "45b983be36b73c0788dc9cbcb76cbb80fc7bb057");
+ test_object("test/master@{1}:branch_file.txt", "3697d64be941a53d4ae8f6a271e4e3fa56b022cc");
+}
+
+void test_refs_revparse__disambiguation(void)
+{
+ /*
+ * $ git show e90810b
+ * tag e90810b
+ * Tagger: Vicent Marti <tanoku@gmail.com>
+ * Date: Thu Aug 12 03:59:17 2010 +0200
+ *
+ * This is a very simple tag.
+ *
+ * commit e90810b8df3e80c413d903f631643c716887138d
+ * Author: Vicent Marti <tanoku@gmail.com>
+ * Date: Thu Aug 5 18:42:20 2010 +0200
+ *
+ * Test commit 2
+ *
+ * diff --git a/readme.txt b/readme.txt
+ * index 6336846..0266163 100644
+ * --- a/readme.txt
+ * +++ b/readme.txt
+ * @@ -1 +1,2 @@
+ * Testing a readme.txt
+ * +Now we add a single line here
+ *
+ * $ git show-ref e90810b
+ * 7b4384978d2493e851f9cca7858815fac9b10980 refs/tags/e90810b
+ *
+ */
+ test_object("e90810b", "7b4384978d2493e851f9cca7858815fac9b10980");
+
+ /*
+ * $ git show e90810
+ * commit e90810b8df3e80c413d903f631643c716887138d
+ * Author: Vicent Marti <tanoku@gmail.com>
+ * Date: Thu Aug 5 18:42:20 2010 +0200
+ *
+ * Test commit 2
+ *
+ * diff --git a/readme.txt b/readme.txt
+ * index 6336846..0266163 100644
+ * --- a/readme.txt
+ * +++ b/readme.txt
+ * @@ -1 +1,2 @@
+ * Testing a readme.txt
+ * +Now we add a single line here
+ */
+ test_object("e90810", "e90810b8df3e80c413d903f631643c716887138d");
+}
+
+void test_refs_revparse__a_too_short_objectid_returns_EAMBIGUOUS(void)
+{
+ cl_assert_equal_i(
+ GIT_EAMBIGUOUS, git_revparse_single(&g_obj, g_repo, "e90"));
+}
+
+/*
+ * $ echo "aabqhq" | git hash-object -t blob --stdin
+ * dea509d0b3cb8ee0650f6ca210bc83f4678851ba
+ *
+ * $ echo "aaazvc" | git hash-object -t blob --stdin
+ * dea509d097ce692e167dfc6a48a7a280cc5e877e
+ */
+void test_refs_revparse__a_not_precise_enough_objectid_returns_EAMBIGUOUS(void)
+{
+ git_repository *repo;
+ git_index *index;
+ git_object *obj;
+
+ repo = cl_git_sandbox_init("testrepo");
+
+ cl_git_mkfile("testrepo/one.txt", "aabqhq\n");
+ cl_git_mkfile("testrepo/two.txt", "aaazvc\n");
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_add_bypath(index, "one.txt"));
+ cl_git_pass(git_index_add_bypath(index, "two.txt"));
+
+ cl_git_fail_with(git_revparse_single(&obj, repo, "dea509d0"), GIT_EAMBIGUOUS);
+
+ cl_git_pass(git_revparse_single(&obj, repo, "dea509d09"));
+
+ git_object_free(obj);
+ git_index_free(index);
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_revparse__issue_994(void)
+{
+ git_repository *repo;
+ git_reference *head, *with_at;
+ git_object *target;
+
+ repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_revparse_single(&target, repo, "origin/bim_with_3d@11296"));
+
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_revparse_single(&target, repo, "refs/remotes/origin/bim_with_3d@11296"));
+
+
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_create(
+ &with_at,
+ repo,
+ "refs/remotes/origin/bim_with_3d@11296",
+ git_reference_target(head),
+ 0));
+
+ cl_git_pass(git_revparse_single(&target, repo, "origin/bim_with_3d@11296"));
+ git_object_free(target);
+
+ cl_git_pass(git_revparse_single(&target, repo, "refs/remotes/origin/bim_with_3d@11296"));
+ git_object_free(target);
+
+ git_reference_free(with_at);
+ git_reference_free(head);
+ cl_git_sandbox_cleanup();
+}
+
+/**
+ * $ git rev-parse blah-7-gc47800c
+ * c47800c7266a2be04c571c04d5a6614691ea99bd
+ *
+ * $ git rev-parse HEAD~3
+ * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+ *
+ * $ git branch blah-7-gc47800c HEAD~3
+ *
+ * $ git rev-parse blah-7-gc47800c
+ * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+ */
+void test_refs_revparse__try_to_retrieve_branch_before_described_tag(void)
+{
+ git_repository *repo;
+ git_reference *branch;
+ git_object *target;
+ char sha[GIT_OID_HEXSZ + 1];
+
+ repo = cl_git_sandbox_init("testrepo.git");
+
+ test_object_inrepo("blah-7-gc47800c", "c47800c7266a2be04c571c04d5a6614691ea99bd", repo);
+
+ cl_git_pass(git_revparse_single(&target, repo, "HEAD~3"));
+ cl_git_pass(git_branch_create(&branch, repo, "blah-7-gc47800c", (git_commit *)target, 0));
+
+ git_oid_tostr(sha, GIT_OID_HEXSZ + 1, git_object_id(target));
+
+ test_object_inrepo("blah-7-gc47800c", sha, repo);
+
+ git_reference_free(branch);
+ git_object_free(target);
+ cl_git_sandbox_cleanup();
+}
+
+/**
+ * $ git rev-parse a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+ * a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+ *
+ * $ git rev-parse HEAD~3
+ * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+ *
+ * $ git branch a65fedf39aefe402d3bb6e24df4d4f5fe4547750 HEAD~3
+ *
+ * $ git rev-parse a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+ * a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+ *
+ * $ git rev-parse heads/a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+ * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+ */
+void test_refs_revparse__try_to_retrieve_sha_before_branch(void)
+{
+ git_repository *repo;
+ git_reference *branch;
+ git_object *target;
+ char sha[GIT_OID_HEXSZ + 1];
+
+ repo = cl_git_sandbox_init("testrepo.git");
+
+ test_object_inrepo("a65fedf39aefe402d3bb6e24df4d4f5fe4547750", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo);
+
+ cl_git_pass(git_revparse_single(&target, repo, "HEAD~3"));
+ cl_git_pass(git_branch_create(&branch, repo, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", (git_commit *)target, 0));
+
+ git_oid_tostr(sha, GIT_OID_HEXSZ + 1, git_object_id(target));
+
+ test_object_inrepo("a65fedf39aefe402d3bb6e24df4d4f5fe4547750", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo);
+ test_object_inrepo("heads/a65fedf39aefe402d3bb6e24df4d4f5fe4547750", sha, repo);
+
+ git_reference_free(branch);
+ git_object_free(target);
+ cl_git_sandbox_cleanup();
+}
+
+/**
+ * $ git rev-parse c47800
+ * c47800c7266a2be04c571c04d5a6614691ea99bd
+ *
+ * $ git rev-parse HEAD~3
+ * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+ *
+ * $ git branch c47800 HEAD~3
+ *
+ * $ git rev-parse c47800
+ * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+ */
+void test_refs_revparse__try_to_retrieve_branch_before_abbrev_sha(void)
+{
+ git_repository *repo;
+ git_reference *branch;
+ git_object *target;
+ char sha[GIT_OID_HEXSZ + 1];
+
+ repo = cl_git_sandbox_init("testrepo.git");
+
+ test_object_inrepo("c47800", "c47800c7266a2be04c571c04d5a6614691ea99bd", repo);
+
+ cl_git_pass(git_revparse_single(&target, repo, "HEAD~3"));
+ cl_git_pass(git_branch_create(&branch, repo, "c47800", (git_commit *)target, 0));
+
+ git_oid_tostr(sha, GIT_OID_HEXSZ + 1, git_object_id(target));
+
+ test_object_inrepo("c47800", sha, repo);
+
+ git_reference_free(branch);
+ git_object_free(target);
+ cl_git_sandbox_cleanup();
+}
+
+
+void test_refs_revparse__range(void)
+{
+ assert_invalid_single_spec("be3563a^1..be3563a");
+
+ test_rangelike("be3563a^1..be3563a",
+ "9fd738e8f7967c078dceed8190330fc8648ee56a",
+ "be3563ae3f795b2b4353bcce3a527ad0a4f7f644",
+ GIT_REVPARSE_RANGE);
+
+ test_rangelike("be3563a^1...be3563a",
+ "9fd738e8f7967c078dceed8190330fc8648ee56a",
+ "be3563ae3f795b2b4353bcce3a527ad0a4f7f644",
+ GIT_REVPARSE_RANGE | GIT_REVPARSE_MERGE_BASE);
+
+ test_rangelike("be3563a^1.be3563a", NULL, NULL, 0);
+}
+
+void test_refs_revparse__parses_range_operator(void)
+{
+ test_id("HEAD", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", NULL, GIT_REVPARSE_SINGLE);
+ test_id("HEAD~3..HEAD",
+ "4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ GIT_REVPARSE_RANGE);
+
+ test_id("HEAD~3...HEAD",
+ "4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ GIT_REVPARSE_RANGE | GIT_REVPARSE_MERGE_BASE);
+}
+
+void test_refs_revparse__ext_retrieves_both_the_reference_and_its_target(void)
+{
+ test_object_and_ref(
+ "master@{upstream}",
+ "be3563ae3f795b2b4353bcce3a527ad0a4f7f644",
+ "refs/remotes/test/master");
+
+ test_object_and_ref(
+ "@{-1}",
+ "a4a7dce85cf63874e984719f4fdd239f5145052f",
+ "refs/heads/br2");
+}
+
+void test_refs_revparse__ext_can_expand_short_reference_names(void)
+{
+ test_object_and_ref(
+ "master",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ "refs/heads/master");
+
+ test_object_and_ref(
+ "HEAD",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ "refs/heads/master");
+
+ test_object_and_ref(
+ "tags/test",
+ "b25fa35b38051e4ae45d4222e795f9df2e43f1d1",
+ "refs/tags/test");
+}
+
+void test_refs_revparse__ext_returns_NULL_reference_when_expression_points_at_a_revision(void)
+{
+ test_object_and_ref(
+ "HEAD~3",
+ "4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
+ NULL);
+
+ test_object_and_ref(
+ "HEAD~0",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ NULL);
+
+ test_object_and_ref(
+ "HEAD^0",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ NULL);
+
+ test_object_and_ref(
+ "@{-1}@{0}",
+ "a4a7dce85cf63874e984719f4fdd239f5145052f",
+ NULL);
+}
+
+void test_refs_revparse__ext_returns_NULL_reference_when_expression_points_at_a_tree_content(void)
+{
+ test_object_and_ref(
+ "tags/test:readme.txt",
+ "0266163a49e280c4f5ed1e08facd36a2bd716bcf",
+ NULL);
+}
diff --git a/tests-clar/refs/setter.c b/tests/refs/setter.c
index 6d875f9b6..6d875f9b6 100644
--- a/tests-clar/refs/setter.c
+++ b/tests/refs/setter.c
diff --git a/tests-clar/refs/shorthand.c b/tests/refs/shorthand.c
index f995d26ca..f995d26ca 100644
--- a/tests-clar/refs/shorthand.c
+++ b/tests/refs/shorthand.c
diff --git a/tests/refs/unicode.c b/tests/refs/unicode.c
new file mode 100644
index 000000000..b56012869
--- /dev/null
+++ b/tests/refs/unicode.c
@@ -0,0 +1,56 @@
+#include "clar_libgit2.h"
+
+static git_repository *repo;
+
+void test_refs_unicode__initialize(void)
+{
+ repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_refs_unicode__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ repo = NULL;
+}
+
+void test_refs_unicode__create_and_lookup(void)
+{
+ git_reference *ref0, *ref1, *ref2;
+ git_repository *repo2;
+
+ const char *REFNAME = "refs/heads/" "\303\205" "ngstr" "\303\266" "m";
+ const char *master = "refs/heads/master";
+
+ /* Create the reference */
+ cl_git_pass(git_reference_lookup(&ref0, repo, master));
+ cl_git_pass(git_reference_create(
+ &ref1, repo, REFNAME, git_reference_target(ref0), 0));
+ cl_assert_equal_s(REFNAME, git_reference_name(ref1));
+ git_reference_free(ref0);
+
+ /* Lookup the reference in a different instance of the repository */
+ cl_git_pass(git_repository_open(&repo2, "testrepo.git"));
+
+ cl_git_pass(git_reference_lookup(&ref2, repo2, REFNAME));
+ cl_assert_equal_i(
+ 0, git_oid_cmp(git_reference_target(ref1), git_reference_target(ref2)));
+ cl_assert_equal_s(REFNAME, git_reference_name(ref2));
+ git_reference_free(ref2);
+
+#if GIT_USE_ICONV
+ /* Lookup reference by decomposed unicode name */
+
+#define REFNAME_DECOMPOSED "refs/heads/" "A" "\314\212" "ngstro" "\314\210" "m"
+
+ cl_git_pass(git_reference_lookup(&ref2, repo2, REFNAME_DECOMPOSED));
+ cl_assert_equal_i(
+ 0, git_oid_cmp(git_reference_target(ref1), git_reference_target(ref2)));
+ cl_assert_equal_s(REFNAME, git_reference_name(ref2));
+ git_reference_free(ref2);
+#endif
+
+ /* Cleanup */
+
+ git_reference_free(ref1);
+ git_repository_free(repo2);
+}
diff --git a/tests-clar/refs/update.c b/tests/refs/update.c
index 205b526a2..205b526a2 100644
--- a/tests-clar/refs/update.c
+++ b/tests/refs/update.c
diff --git a/tests/repo/config.c b/tests/repo/config.c
new file mode 100644
index 000000000..e77acc8c5
--- /dev/null
+++ b/tests/repo/config.c
@@ -0,0 +1,208 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+#include <ctype.h>
+
+static git_buf path = GIT_BUF_INIT;
+
+void test_repo_config__initialize(void)
+{
+ cl_fixture_sandbox("empty_standard_repo");
+ cl_git_pass(cl_rename("empty_standard_repo/.gitted", "empty_standard_repo/.git"));
+
+ git_buf_clear(&path);
+
+ cl_must_pass(p_mkdir("alternate", 0777));
+ cl_git_pass(git_path_prettify(&path, "alternate", NULL));
+}
+
+void test_repo_config__cleanup(void)
+{
+ cl_git_pass(git_path_prettify(&path, "alternate", NULL));
+ cl_git_pass(git_futils_rmdir_r(path.ptr, NULL, GIT_RMDIR_REMOVE_FILES));
+ git_buf_free(&path);
+ cl_assert(!git_path_isdir("alternate"));
+
+ cl_fixture_cleanup("empty_standard_repo");
+}
+
+void test_repo_config__open_missing_global(void)
+{
+ git_repository *repo;
+ git_config *config, *global;
+
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr));
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr));
+
+ cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ cl_git_pass(git_repository_config(&config, repo));
+ cl_git_pass(git_config_open_level(&global, config, GIT_CONFIG_LEVEL_GLOBAL));
+
+ cl_git_pass(git_config_set_string(global, "test.set", "42"));
+
+ git_config_free(global);
+ git_config_free(config);
+ git_repository_free(repo);
+
+ git_futils_dirs_global_shutdown();
+}
+
+void test_repo_config__open_missing_global_with_separators(void)
+{
+ git_repository *repo;
+ git_config *config, *global;
+
+ cl_git_pass(git_buf_printf(&path, "%c%s", GIT_PATH_LIST_SEPARATOR, "dummy"));
+
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr));
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr));
+
+ git_buf_free(&path);
+
+ cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ cl_git_pass(git_repository_config(&config, repo));
+ cl_git_pass(git_config_open_level(&global, config, GIT_CONFIG_LEVEL_GLOBAL));
+
+ cl_git_pass(git_config_set_string(global, "test.set", "42"));
+
+ git_config_free(global);
+ git_config_free(config);
+ git_repository_free(repo);
+
+ git_futils_dirs_global_shutdown();
+}
+
+#include "repository.h"
+
+void test_repo_config__read_no_configs(void)
+{
+ git_repository *repo;
+ int val;
+
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr));
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr));
+
+ /* with none */
+
+ cl_must_pass(p_unlink("empty_standard_repo/.git/config"));
+ cl_assert(!git_path_isfile("empty_standard_repo/.git/config"));
+
+ cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ git_repository__cvar_cache_clear(repo);
+ val = -1;
+ cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV));
+ cl_assert_equal_i(GIT_ABBREV_DEFAULT, val);
+ git_repository_free(repo);
+
+ git_futils_dirs_global_shutdown();
+
+ /* with just system */
+
+ cl_must_pass(p_mkdir("alternate/1", 0777));
+ cl_git_pass(git_buf_joinpath(&path, path.ptr, "1"));
+ cl_git_rewritefile("alternate/1/gitconfig", "[core]\n\tabbrev = 10\n");
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr));
+
+ cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ git_repository__cvar_cache_clear(repo);
+ val = -1;
+ cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV));
+ cl_assert_equal_i(10, val);
+ git_repository_free(repo);
+
+ /* with xdg + system */
+
+ cl_must_pass(p_mkdir("alternate/2", 0777));
+ path.ptr[path.size - 1] = '2';
+ cl_git_rewritefile("alternate/2/config", "[core]\n\tabbrev = 20\n");
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr));
+
+ cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ git_repository__cvar_cache_clear(repo);
+ val = -1;
+ cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV));
+ cl_assert_equal_i(20, val);
+ git_repository_free(repo);
+
+ /* with global + xdg + system */
+
+ cl_must_pass(p_mkdir("alternate/3", 0777));
+ path.ptr[path.size - 1] = '3';
+ cl_git_rewritefile("alternate/3/.gitconfig", "[core]\n\tabbrev = 30\n");
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
+
+ cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ git_repository__cvar_cache_clear(repo);
+ val = -1;
+ cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV));
+ cl_assert_equal_i(30, val);
+ git_repository_free(repo);
+
+ /* with all configs */
+
+ cl_git_rewritefile("empty_standard_repo/.git/config", "[core]\n\tabbrev = 40\n");
+
+ cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ git_repository__cvar_cache_clear(repo);
+ val = -1;
+ cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV));
+ cl_assert_equal_i(40, val);
+ git_repository_free(repo);
+
+ /* with all configs but delete the files ? */
+
+ cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ git_repository__cvar_cache_clear(repo);
+ val = -1;
+ cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV));
+ cl_assert_equal_i(40, val);
+
+ cl_must_pass(p_unlink("empty_standard_repo/.git/config"));
+ cl_assert(!git_path_isfile("empty_standard_repo/.git/config"));
+
+ cl_must_pass(p_unlink("alternate/1/gitconfig"));
+ cl_assert(!git_path_isfile("alternate/1/gitconfig"));
+
+ cl_must_pass(p_unlink("alternate/2/config"));
+ cl_assert(!git_path_isfile("alternate/2/config"));
+
+ cl_must_pass(p_unlink("alternate/3/.gitconfig"));
+ cl_assert(!git_path_isfile("alternate/3/.gitconfig"));
+
+ git_repository__cvar_cache_clear(repo);
+ val = -1;
+ cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV));
+ cl_assert_equal_i(40, val);
+ git_repository_free(repo);
+
+ /* reopen */
+
+ cl_assert(!git_path_isfile("empty_standard_repo/.git/config"));
+ cl_assert(!git_path_isfile("alternate/3/.gitconfig"));
+
+ cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ git_repository__cvar_cache_clear(repo);
+ val = -1;
+ cl_git_pass(git_repository__cvar(&val, repo, GIT_CVAR_ABBREV));
+ cl_assert_equal_i(7, val);
+ git_repository_free(repo);
+
+ cl_assert(!git_path_exists("empty_standard_repo/.git/config"));
+ cl_assert(!git_path_exists("alternate/3/.gitconfig"));
+
+ git_futils_dirs_global_shutdown();
+}
diff --git a/tests-clar/repo/discover.c b/tests/repo/discover.c
index f93ff2462..f93ff2462 100644
--- a/tests-clar/repo/discover.c
+++ b/tests/repo/discover.c
diff --git a/tests-clar/repo/getters.c b/tests/repo/getters.c
index b8ede126c..b8ede126c 100644
--- a/tests-clar/repo/getters.c
+++ b/tests/repo/getters.c
diff --git a/tests-clar/repo/hashfile.c b/tests/repo/hashfile.c
index 4cc9f18b4..4cc9f18b4 100644
--- a/tests-clar/repo/hashfile.c
+++ b/tests/repo/hashfile.c
diff --git a/tests/repo/head.c b/tests/repo/head.c
new file mode 100644
index 000000000..5a55984bd
--- /dev/null
+++ b/tests/repo/head.c
@@ -0,0 +1,196 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+#include "repo_helpers.h"
+#include "posix.h"
+
+static git_repository *repo;
+
+void test_repo_head__initialize(void)
+{
+ repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_repo_head__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_repo_head__head_detached(void)
+{
+ git_reference *ref;
+
+ cl_git_pass(git_repository_head_detached(repo));
+
+ cl_git_pass(git_repository_detach_head(repo));
+
+ cl_assert_equal_i(true, git_repository_head_detached(repo));
+
+ /* take the reop back to it's original state */
+ cl_git_pass(git_reference_symbolic_create(&ref, repo, "HEAD", "refs/heads/master", 1));
+ git_reference_free(ref);
+
+ cl_assert_equal_i(false, git_repository_head_detached(repo));
+}
+
+void test_repo_head__unborn_head(void)
+{
+ git_reference *ref;
+
+ cl_git_pass(git_repository_head_detached(repo));
+
+ make_head_unborn(repo, NON_EXISTING_HEAD);
+
+ cl_assert(git_repository_head_unborn(repo) == 1);
+
+
+ /* take the repo back to it's original state */
+ cl_git_pass(git_reference_symbolic_create(&ref, repo, "HEAD", "refs/heads/master", 1));
+ cl_assert(git_repository_head_unborn(repo) == 0);
+
+ git_reference_free(ref);
+}
+
+void test_repo_head__set_head_Attaches_HEAD_to_un_unborn_branch_when_the_branch_doesnt_exist(void)
+{
+ git_reference *head;
+
+ cl_git_pass(git_repository_set_head(repo, "refs/heads/doesnt/exist/yet"));
+
+ cl_assert_equal_i(false, git_repository_head_detached(repo));
+
+ cl_assert_equal_i(GIT_EUNBORNBRANCH, git_repository_head(&head, repo));
+}
+
+void test_repo_head__set_head_Returns_ENOTFOUND_when_the_reference_doesnt_exist(void)
+{
+ cl_assert_equal_i(GIT_ENOTFOUND, git_repository_set_head(repo, "refs/tags/doesnt/exist/yet"));
+}
+
+void test_repo_head__set_head_Fails_when_the_reference_points_to_a_non_commitish(void)
+{
+ cl_git_fail(git_repository_set_head(repo, "refs/tags/point_to_blob"));
+}
+
+void test_repo_head__set_head_Attaches_HEAD_when_the_reference_points_to_a_branch(void)
+{
+ git_reference *head;
+
+ cl_git_pass(git_repository_set_head(repo, "refs/heads/br2"));
+
+ cl_assert_equal_i(false, git_repository_head_detached(repo));
+
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_assert_equal_s("refs/heads/br2", git_reference_name(head));
+
+ git_reference_free(head);
+}
+
+static void assert_head_is_correctly_detached(void)
+{
+ git_reference *head;
+ git_object *commit;
+
+ cl_assert_equal_i(true, git_repository_head_detached(repo));
+
+ cl_git_pass(git_repository_head(&head, repo));
+
+ cl_git_pass(git_object_lookup(&commit, repo, git_reference_target(head), GIT_OBJ_COMMIT));
+
+ git_object_free(commit);
+ git_reference_free(head);
+}
+
+void test_repo_head__set_head_Detaches_HEAD_when_the_reference_doesnt_point_to_a_branch(void)
+{
+ cl_git_pass(git_repository_set_head(repo, "refs/tags/test"));
+
+ cl_assert_equal_i(true, git_repository_head_detached(repo));
+
+ assert_head_is_correctly_detached();
+}
+
+void test_repo_head__set_head_detached_Return_ENOTFOUND_when_the_object_doesnt_exist(void)
+{
+ git_oid oid;
+
+ cl_git_pass(git_oid_fromstr(&oid, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_repository_set_head_detached(repo, &oid));
+}
+
+void test_repo_head__set_head_detached_Fails_when_the_object_isnt_a_commitish(void)
+{
+ git_object *blob;
+
+ cl_git_pass(git_revparse_single(&blob, repo, "point_to_blob"));
+
+ cl_git_fail(git_repository_set_head_detached(repo, git_object_id(blob)));
+
+ git_object_free(blob);
+}
+
+void test_repo_head__set_head_detached_Detaches_HEAD_and_make_it_point_to_the_peeled_commit(void)
+{
+ git_object *tag;
+
+ cl_git_pass(git_revparse_single(&tag, repo, "tags/test"));
+ cl_assert_equal_i(GIT_OBJ_TAG, git_object_type(tag));
+
+ cl_git_pass(git_repository_set_head_detached(repo, git_object_id(tag)));
+
+ assert_head_is_correctly_detached();
+
+ git_object_free(tag);
+}
+
+void test_repo_head__detach_head_Detaches_HEAD_and_make_it_point_to_the_peeled_commit(void)
+{
+ cl_assert_equal_i(false, git_repository_head_detached(repo));
+
+ cl_git_pass(git_repository_detach_head(repo));
+
+ assert_head_is_correctly_detached();
+}
+
+void test_repo_head__detach_head_Fails_if_HEAD_and_point_to_a_non_commitish(void)
+{
+ git_reference *head;
+
+ cl_git_pass(git_reference_symbolic_create(&head, repo, GIT_HEAD_FILE, "refs/tags/point_to_blob", 1));
+
+ cl_git_fail(git_repository_detach_head(repo));
+
+ git_reference_free(head);
+}
+
+void test_repo_head__detaching_an_unborn_branch_returns_GIT_EUNBORNBRANCH(void)
+{
+ make_head_unborn(repo, NON_EXISTING_HEAD);
+
+ cl_assert_equal_i(GIT_EUNBORNBRANCH, git_repository_detach_head(repo));
+}
+
+void test_repo_head__retrieving_an_unborn_branch_returns_GIT_EUNBORNBRANCH(void)
+{
+ git_reference *head;
+
+ make_head_unborn(repo, NON_EXISTING_HEAD);
+
+ cl_assert_equal_i(GIT_EUNBORNBRANCH, git_repository_head(&head, repo));
+}
+
+void test_repo_head__retrieving_a_missing_head_returns_GIT_ENOTFOUND(void)
+{
+ git_reference *head;
+
+ delete_head(repo);
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_repository_head(&head, repo));
+}
+
+void test_repo_head__can_tell_if_an_unborn_head_is_detached(void)
+{
+ make_head_unborn(repo, NON_EXISTING_HEAD);
+
+ cl_assert_equal_i(false, git_repository_head_detached(repo));
+}
diff --git a/tests/repo/headtree.c b/tests/repo/headtree.c
new file mode 100644
index 000000000..e899ac399
--- /dev/null
+++ b/tests/repo/headtree.c
@@ -0,0 +1,53 @@
+#include "clar_libgit2.h"
+#include "repository.h"
+#include "repo_helpers.h"
+#include "posix.h"
+
+static git_repository *repo;
+static git_tree *tree;
+
+void test_repo_headtree__initialize(void)
+{
+ repo = cl_git_sandbox_init("testrepo.git");
+ tree = NULL;
+}
+
+void test_repo_headtree__cleanup(void)
+{
+ git_tree_free(tree);
+ cl_git_sandbox_cleanup();
+}
+
+void test_repo_headtree__can_retrieve_the_root_tree_from_a_detached_head(void)
+{
+ cl_git_pass(git_repository_detach_head(repo));
+
+ cl_git_pass(git_repository_head_tree(&tree, repo));
+
+ cl_assert(git_oid_streq(git_tree_id(tree), "az"));
+}
+
+void test_repo_headtree__can_retrieve_the_root_tree_from_a_non_detached_head(void)
+{
+ cl_assert_equal_i(false, git_repository_head_detached(repo));
+
+ cl_git_pass(git_repository_head_tree(&tree, repo));
+
+ cl_assert(git_oid_streq(git_tree_id(tree), "az"));
+}
+
+void test_repo_headtree__when_head_is_unborn_returns_EUNBORNBRANCH(void)
+{
+ make_head_unborn(repo, NON_EXISTING_HEAD);
+
+ cl_assert_equal_i(true, git_repository_head_unborn(repo));
+
+ cl_assert_equal_i(GIT_EUNBORNBRANCH, git_repository_head_tree(&tree, repo));
+}
+
+void test_repo_headtree__when_head_is_missing_returns_ENOTFOUND(void)
+{
+ delete_head(repo);
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_repository_head_tree(&tree, repo));
+}
diff --git a/tests/repo/init.c b/tests/repo/init.c
new file mode 100644
index 000000000..aea383c64
--- /dev/null
+++ b/tests/repo/init.c
@@ -0,0 +1,606 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+#include "repository.h"
+#include "config.h"
+#include "path.h"
+
+enum repo_mode {
+ STANDARD_REPOSITORY = 0,
+ BARE_REPOSITORY = 1
+};
+
+static git_repository *_repo = NULL;
+static mode_t g_umask = 0;
+
+void test_repo_init__initialize(void)
+{
+ _repo = NULL;
+
+ /* load umask if not already loaded */
+ if (!g_umask) {
+ g_umask = p_umask(022);
+ (void)p_umask(g_umask);
+ }
+}
+
+static void cleanup_repository(void *path)
+{
+ git_repository_free(_repo);
+ _repo = NULL;
+
+ cl_fixture_cleanup((const char *)path);
+}
+
+static void ensure_repository_init(
+ const char *working_directory,
+ int is_bare,
+ const char *expected_path_repository,
+ const char *expected_working_directory)
+{
+ const char *workdir;
+
+ cl_assert(!git_path_isdir(working_directory));
+
+ cl_git_pass(git_repository_init(&_repo, working_directory, is_bare));
+
+ workdir = git_repository_workdir(_repo);
+ if (workdir != NULL || expected_working_directory != NULL) {
+ cl_assert(
+ git__suffixcmp(workdir, expected_working_directory) == 0
+ );
+ }
+
+ cl_assert(
+ git__suffixcmp(git_repository_path(_repo), expected_path_repository) == 0
+ );
+
+ cl_assert(git_repository_is_bare(_repo) == is_bare);
+
+#ifdef GIT_WIN32
+ if (!is_bare) {
+ DWORD fattrs = GetFileAttributes(git_repository_path(_repo));
+ cl_assert((fattrs & FILE_ATTRIBUTE_HIDDEN) != 0);
+ }
+#endif
+
+ cl_assert(git_repository_is_empty(_repo));
+}
+
+void test_repo_init__standard_repo(void)
+{
+ cl_set_cleanup(&cleanup_repository, "testrepo");
+ ensure_repository_init("testrepo/", 0, "testrepo/.git/", "testrepo/");
+}
+
+void test_repo_init__standard_repo_noslash(void)
+{
+ cl_set_cleanup(&cleanup_repository, "testrepo");
+ ensure_repository_init("testrepo", 0, "testrepo/.git/", "testrepo/");
+}
+
+void test_repo_init__bare_repo(void)
+{
+ cl_set_cleanup(&cleanup_repository, "testrepo.git");
+ ensure_repository_init("testrepo.git/", 1, "testrepo.git/", NULL);
+}
+
+void test_repo_init__bare_repo_noslash(void)
+{
+ cl_set_cleanup(&cleanup_repository, "testrepo.git");
+ ensure_repository_init("testrepo.git", 1, "testrepo.git/", NULL);
+}
+
+void test_repo_init__bare_repo_escaping_current_workdir(void)
+{
+ git_buf path_repository = GIT_BUF_INIT;
+ git_buf path_current_workdir = GIT_BUF_INIT;
+
+ cl_git_pass(git_path_prettify_dir(&path_current_workdir, ".", NULL));
+
+ cl_git_pass(git_buf_joinpath(&path_repository, git_buf_cstr(&path_current_workdir), "a/b/c"));
+ cl_git_pass(git_futils_mkdir_r(git_buf_cstr(&path_repository), NULL, GIT_DIR_MODE));
+
+ /* Change the current working directory */
+ cl_git_pass(chdir(git_buf_cstr(&path_repository)));
+
+ /* Initialize a bare repo with a relative path escaping out of the current working directory */
+ cl_git_pass(git_repository_init(&_repo, "../d/e.git", 1));
+ cl_git_pass(git__suffixcmp(git_repository_path(_repo), "/a/b/d/e.git/"));
+
+ git_repository_free(_repo);
+
+ /* Open a bare repo with a relative path escaping out of the current working directory */
+ cl_git_pass(git_repository_open(&_repo, "../d/e.git"));
+
+ cl_git_pass(chdir(git_buf_cstr(&path_current_workdir)));
+
+ git_buf_free(&path_current_workdir);
+ git_buf_free(&path_repository);
+
+ cleanup_repository("a");
+}
+
+void test_repo_init__reinit_bare_repo(void)
+{
+ cl_set_cleanup(&cleanup_repository, "reinit.git");
+
+ /* Initialize the repository */
+ cl_git_pass(git_repository_init(&_repo, "reinit.git", 1));
+ git_repository_free(_repo);
+
+ /* Reinitialize the repository */
+ cl_git_pass(git_repository_init(&_repo, "reinit.git", 1));
+}
+
+void test_repo_init__reinit_too_recent_bare_repo(void)
+{
+ git_config *config;
+
+ /* Initialize the repository */
+ cl_git_pass(git_repository_init(&_repo, "reinit.git", 1));
+ git_repository_config(&config, _repo);
+
+ /*
+ * Hack the config of the repository to make it look like it has
+ * been created by a recenter version of git/libgit2
+ */
+ cl_git_pass(git_config_set_int32(config, "core.repositoryformatversion", 42));
+
+ git_config_free(config);
+ git_repository_free(_repo);
+
+ /* Try to reinitialize the repository */
+ cl_git_fail(git_repository_init(&_repo, "reinit.git", 1));
+
+ cl_fixture_cleanup("reinit.git");
+}
+
+void test_repo_init__additional_templates(void)
+{
+ git_buf path = GIT_BUF_INIT;
+
+ cl_set_cleanup(&cleanup_repository, "tester");
+
+ ensure_repository_init("tester", 0, "tester/.git/", "tester/");
+
+ cl_git_pass(
+ git_buf_joinpath(&path, git_repository_path(_repo), "description"));
+ cl_assert(git_path_isfile(git_buf_cstr(&path)));
+
+ cl_git_pass(
+ git_buf_joinpath(&path, git_repository_path(_repo), "info/exclude"));
+ cl_assert(git_path_isfile(git_buf_cstr(&path)));
+
+ cl_git_pass(
+ git_buf_joinpath(&path, git_repository_path(_repo), "hooks"));
+ cl_assert(git_path_isdir(git_buf_cstr(&path)));
+ /* won't confirm specific contents of hooks dir since it may vary */
+
+ git_buf_free(&path);
+}
+
+static void assert_config_entry_on_init_bytype(
+ const char *config_key, int expected_value, bool is_bare)
+{
+ git_config *config;
+ int error, current_value;
+ const char *repo_path = is_bare ?
+ "config_entry/test.bare.git" : "config_entry/test.non.bare.git";
+
+ cl_set_cleanup(&cleanup_repository, "config_entry");
+
+ cl_git_pass(git_repository_init(&_repo, repo_path, is_bare));
+
+ cl_git_pass(git_repository_config(&config, _repo));
+ error = git_config_get_bool(&current_value, config, config_key);
+ git_config_free(config);
+
+ if (expected_value >= 0) {
+ cl_assert_equal_i(0, error);
+ cl_assert_equal_i(expected_value, current_value);
+ } else {
+ cl_assert_equal_i(expected_value, error);
+ }
+}
+
+static void assert_config_entry_on_init(
+ const char *config_key, int expected_value)
+{
+ assert_config_entry_on_init_bytype(config_key, expected_value, true);
+ git_repository_free(_repo);
+
+ assert_config_entry_on_init_bytype(config_key, expected_value, false);
+}
+
+void test_repo_init__detect_filemode(void)
+{
+ assert_config_entry_on_init("core.filemode", cl_is_chmod_supported());
+}
+
+void test_repo_init__detect_ignorecase(void)
+{
+ struct stat st;
+ bool found_without_match;
+
+ cl_git_write2file("testCAPS", "whatever\n", 0, O_CREAT | O_WRONLY, 0666);
+ found_without_match = (p_stat("Testcaps", &st) == 0);
+ cl_must_pass(p_unlink("testCAPS"));
+
+ assert_config_entry_on_init(
+ "core.ignorecase", found_without_match ? true : GIT_ENOTFOUND);
+}
+
+void test_repo_init__detect_precompose_unicode_required(void)
+{
+#ifdef GIT_USE_ICONV
+ char *composed = "ḱṷṓn", *decomposed = "kÌuÌ­oÌ„Ìn";
+ struct stat st;
+ bool found_with_nfd;
+
+ cl_git_write2file(composed, "whatever\n", 0, O_CREAT | O_WRONLY, 0666);
+ found_with_nfd = (p_stat(decomposed, &st) == 0);
+ cl_must_pass(p_unlink(composed));
+
+ assert_config_entry_on_init("core.precomposeunicode", found_with_nfd);
+#else
+ assert_config_entry_on_init("core.precomposeunicode", GIT_ENOTFOUND);
+#endif
+}
+
+void test_repo_init__reinit_doesnot_overwrite_ignorecase(void)
+{
+ git_config *config;
+ int current_value;
+
+ /* Init a new repo */
+ cl_set_cleanup(&cleanup_repository, "not.overwrite.git");
+ cl_git_pass(git_repository_init(&_repo, "not.overwrite.git", 1));
+
+ /* Change the "core.ignorecase" config value to something unlikely */
+ git_repository_config(&config, _repo);
+ git_config_set_int32(config, "core.ignorecase", 42);
+ git_config_free(config);
+ git_repository_free(_repo);
+ _repo = NULL;
+
+ /* Reinit the repository */
+ cl_git_pass(git_repository_init(&_repo, "not.overwrite.git", 1));
+ git_repository_config(&config, _repo);
+
+ /* Ensure the "core.ignorecase" config value hasn't been updated */
+ cl_git_pass(git_config_get_int32(&current_value, config, "core.ignorecase"));
+ cl_assert_equal_i(42, current_value);
+
+ git_config_free(config);
+}
+
+void test_repo_init__reinit_overwrites_filemode(void)
+{
+ int expected = cl_is_chmod_supported(), current_value;
+
+ /* Init a new repo */
+ cl_set_cleanup(&cleanup_repository, "overwrite.git");
+ cl_git_pass(git_repository_init(&_repo, "overwrite.git", 1));
+
+ /* Change the "core.filemode" config value to something unlikely */
+ cl_repo_set_bool(_repo, "core.filemode", !expected);
+
+ git_repository_free(_repo);
+ _repo = NULL;
+
+ /* Reinit the repository */
+ cl_git_pass(git_repository_init(&_repo, "overwrite.git", 1));
+
+ /* Ensure the "core.filemode" config value has been reset */
+ current_value = cl_repo_get_bool(_repo, "core.filemode");
+ cl_assert_equal_i(expected, current_value);
+}
+
+void test_repo_init__sets_logAllRefUpdates_according_to_type_of_repository(void)
+{
+ assert_config_entry_on_init_bytype("core.logallrefupdates", GIT_ENOTFOUND, true);
+ git_repository_free(_repo);
+ assert_config_entry_on_init_bytype("core.logallrefupdates", true, false);
+}
+
+void test_repo_init__extended_0(void)
+{
+ git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+
+ /* without MKDIR this should fail */
+ cl_git_fail(git_repository_init_ext(&_repo, "extended", &opts));
+
+ /* make the directory first, then it should succeed */
+ cl_git_pass(git_futils_mkdir("extended", NULL, 0775, 0));
+ cl_git_pass(git_repository_init_ext(&_repo, "extended", &opts));
+
+ cl_assert(!git__suffixcmp(git_repository_workdir(_repo), "/extended/"));
+ cl_assert(!git__suffixcmp(git_repository_path(_repo), "/extended/.git/"));
+ cl_assert(!git_repository_is_bare(_repo));
+ cl_assert(git_repository_is_empty(_repo));
+
+ cleanup_repository("extended");
+}
+
+void test_repo_init__extended_1(void)
+{
+ git_reference *ref;
+ git_remote *remote;
+ struct stat st;
+ git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+
+ opts.flags = GIT_REPOSITORY_INIT_MKPATH |
+ GIT_REPOSITORY_INIT_NO_DOTGIT_DIR;
+ opts.mode = GIT_REPOSITORY_INIT_SHARED_GROUP;
+ opts.workdir_path = "../c_wd";
+ opts.description = "Awesomest test repository evah";
+ opts.initial_head = "development";
+ opts.origin_url = "https://github.com/libgit2/libgit2.git";
+
+ cl_git_pass(git_repository_init_ext(&_repo, "root/b/c.git", &opts));
+
+ cl_assert(!git__suffixcmp(git_repository_workdir(_repo), "/c_wd/"));
+ cl_assert(!git__suffixcmp(git_repository_path(_repo), "/c.git/"));
+ cl_assert(git_path_isfile("root/b/c_wd/.git"));
+ cl_assert(!git_repository_is_bare(_repo));
+ /* repo will not be counted as empty because we set head to "development" */
+ cl_assert(!git_repository_is_empty(_repo));
+
+ cl_git_pass(git_path_lstat(git_repository_path(_repo), &st));
+ cl_assert(S_ISDIR(st.st_mode));
+ if (cl_is_chmod_supported())
+ cl_assert((S_ISGID & st.st_mode) == S_ISGID);
+ else
+ cl_assert((S_ISGID & st.st_mode) == 0);
+
+ cl_git_pass(git_reference_lookup(&ref, _repo, "HEAD"));
+ cl_assert(git_reference_type(ref) == GIT_REF_SYMBOLIC);
+ cl_assert_equal_s("refs/heads/development", git_reference_symbolic_target(ref));
+ git_reference_free(ref);
+
+ cl_git_pass(git_remote_load(&remote, _repo, "origin"));
+ cl_assert_equal_s("origin", git_remote_name(remote));
+ cl_assert_equal_s(opts.origin_url, git_remote_url(remote));
+ git_remote_free(remote);
+
+ git_repository_free(_repo);
+ cl_fixture_cleanup("root");
+}
+
+#define CLEAR_FOR_CORE_FILEMODE(M) ((M) &= ~0177)
+
+static void assert_hooks_match(
+ const char *template_dir,
+ const char *repo_dir,
+ const char *hook_path,
+ bool core_filemode)
+{
+ git_buf expected = GIT_BUF_INIT;
+ git_buf actual = GIT_BUF_INIT;
+ struct stat expected_st, st;
+
+ cl_git_pass(git_buf_joinpath(&expected, template_dir, hook_path));
+ cl_git_pass(git_path_lstat(expected.ptr, &expected_st));
+
+ cl_git_pass(git_buf_joinpath(&actual, repo_dir, hook_path));
+ cl_git_pass(git_path_lstat(actual.ptr, &st));
+
+ cl_assert(expected_st.st_size == st.st_size);
+
+ if (GIT_MODE_TYPE(expected_st.st_mode) != GIT_FILEMODE_LINK) {
+ mode_t expected_mode =
+ GIT_MODE_TYPE(expected_st.st_mode) |
+ (GIT_PERMS_FOR_WRITE(expected_st.st_mode) & ~g_umask);
+
+ if (!core_filemode) {
+ CLEAR_FOR_CORE_FILEMODE(expected_mode);
+ CLEAR_FOR_CORE_FILEMODE(st.st_mode);
+ }
+
+ cl_assert_equal_i_fmt(expected_mode, st.st_mode, "%07o");
+ }
+
+ git_buf_free(&expected);
+ git_buf_free(&actual);
+}
+
+static void assert_mode_seems_okay(
+ const char *base, const char *path,
+ git_filemode_t expect_mode, bool expect_setgid, bool core_filemode)
+{
+ git_buf full = GIT_BUF_INIT;
+ struct stat st;
+
+ cl_git_pass(git_buf_joinpath(&full, base, path));
+ cl_git_pass(git_path_lstat(full.ptr, &st));
+ git_buf_free(&full);
+
+ if (!core_filemode) {
+ CLEAR_FOR_CORE_FILEMODE(expect_mode);
+ CLEAR_FOR_CORE_FILEMODE(st.st_mode);
+ expect_setgid = false;
+ }
+
+ if (S_ISGID != 0)
+ cl_assert_equal_b(expect_setgid, (st.st_mode & S_ISGID) != 0);
+
+ cl_assert_equal_b(
+ GIT_PERMS_IS_EXEC(expect_mode), GIT_PERMS_IS_EXEC(st.st_mode));
+
+ cl_assert_equal_i_fmt(
+ GIT_MODE_TYPE(expect_mode), GIT_MODE_TYPE(st.st_mode), "%07o");
+}
+
+void test_repo_init__extended_with_template(void)
+{
+ git_buf expected = GIT_BUF_INIT;
+ git_buf actual = GIT_BUF_INIT;
+ git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+ int filemode;
+
+ cl_set_cleanup(&cleanup_repository, "templated.git");
+
+ opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_BARE |
+ GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
+ opts.template_path = cl_fixture("template");
+
+ cl_git_pass(git_repository_init_ext(&_repo, "templated.git", &opts));
+
+ cl_assert(git_repository_is_bare(_repo));
+
+ cl_assert(!git__suffixcmp(git_repository_path(_repo), "/templated.git/"));
+
+ cl_git_pass(git_futils_readbuffer(
+ &expected, cl_fixture("template/description")));
+ cl_git_pass(git_futils_readbuffer(
+ &actual, "templated.git/description"));
+
+ cl_assert_equal_s(expected.ptr, actual.ptr);
+
+ git_buf_free(&expected);
+ git_buf_free(&actual);
+
+ filemode = cl_repo_get_bool(_repo, "core.filemode");
+
+ assert_hooks_match(
+ cl_fixture("template"), git_repository_path(_repo),
+ "hooks/update.sample", filemode);
+
+ assert_hooks_match(
+ cl_fixture("template"), git_repository_path(_repo),
+ "hooks/link.sample", filemode);
+}
+
+void test_repo_init__extended_with_template_and_shared_mode(void)
+{
+ git_buf expected = GIT_BUF_INIT;
+ git_buf actual = GIT_BUF_INIT;
+ git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+ int filemode = true;
+ const char *repo_path = NULL;
+
+ cl_set_cleanup(&cleanup_repository, "init_shared_from_tpl");
+
+ opts.flags = GIT_REPOSITORY_INIT_MKPATH |
+ GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
+ opts.template_path = cl_fixture("template");
+ opts.mode = GIT_REPOSITORY_INIT_SHARED_GROUP;
+
+ cl_git_pass(git_repository_init_ext(&_repo, "init_shared_from_tpl", &opts));
+
+ cl_assert(!git_repository_is_bare(_repo));
+ cl_assert(!git__suffixcmp(git_repository_path(_repo), "/init_shared_from_tpl/.git/"));
+
+ filemode = cl_repo_get_bool(_repo, "core.filemode");
+
+ cl_git_pass(git_futils_readbuffer(
+ &expected, cl_fixture("template/description")));
+ cl_git_pass(git_futils_readbuffer(
+ &actual, "init_shared_from_tpl/.git/description"));
+
+ cl_assert_equal_s(expected.ptr, actual.ptr);
+
+ git_buf_free(&expected);
+ git_buf_free(&actual);
+
+ repo_path = git_repository_path(_repo);
+ assert_mode_seems_okay(repo_path, "hooks",
+ GIT_FILEMODE_TREE | GIT_REPOSITORY_INIT_SHARED_GROUP, true, filemode);
+ assert_mode_seems_okay(repo_path, "info",
+ GIT_FILEMODE_TREE | GIT_REPOSITORY_INIT_SHARED_GROUP, true, filemode);
+ assert_mode_seems_okay(repo_path, "description",
+ GIT_FILEMODE_BLOB, false, filemode);
+
+ /* for a non-symlinked hook, it should have shared permissions now */
+ assert_hooks_match(
+ cl_fixture("template"), git_repository_path(_repo),
+ "hooks/update.sample", filemode);
+
+ /* for a symlinked hook, the permissions still should match the
+ * source link, not the GIT_REPOSITORY_INIT_SHARED_GROUP value
+ */
+ assert_hooks_match(
+ cl_fixture("template"), git_repository_path(_repo),
+ "hooks/link.sample", filemode);
+}
+
+void test_repo_init__can_reinit_an_initialized_repository(void)
+{
+ git_repository *reinit;
+
+ cl_set_cleanup(&cleanup_repository, "extended");
+
+ cl_git_pass(git_futils_mkdir("extended", NULL, 0775, 0));
+ cl_git_pass(git_repository_init(&_repo, "extended", false));
+
+ cl_git_pass(git_repository_init(&reinit, "extended", false));
+
+ cl_assert_equal_s(git_repository_path(_repo), git_repository_path(reinit));
+
+ git_repository_free(reinit);
+}
+
+void test_repo_init__init_with_initial_commit(void)
+{
+ git_index *index;
+
+ cl_set_cleanup(&cleanup_repository, "committed");
+
+ /* Initialize the repository */
+ cl_git_pass(git_repository_init(&_repo, "committed", 0));
+
+ /* Init 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
+ *
+ * If you are writing code outside the test suite, you can create this
+ * file any way that you like, such as:
+ * FILE *fp = fopen("committed/file.txt", "w");
+ * fputs("some stuff\n", fp);
+ * fclose(fp);
+ * We like to use the help functions because they do error detection
+ * in a way that's easily compatible with our test suite.
+ */
+ cl_git_mkfile("committed/file.txt", "some stuff\n");
+
+ /* Add file to the index */
+ cl_git_pass(git_index_add_bypath(index, "file.txt"));
+ cl_git_pass(git_index_write(index));
+
+ /* Intentionally not using cl_repo_commit_from_index here so this code
+ * can be used as an example of how an initial commit is typically
+ * made to a repository...
+ */
+
+ /* Make sure we're ready to use git_signature_default :-) */
+ {
+ git_config *cfg, *local;
+ cl_git_pass(git_repository_config(&cfg, _repo));
+ cl_git_pass(git_config_open_level(&local, cfg, GIT_CONFIG_LEVEL_LOCAL));
+ cl_git_pass(git_config_set_string(local, "user.name", "Test User"));
+ cl_git_pass(git_config_set_string(local, "user.email", "t@example.com"));
+ git_config_free(local);
+ git_config_free(cfg);
+ }
+
+ /* Create a commit with the new contents of the index */
+ {
+ git_signature *sig;
+ git_oid tree_id, commit_id;
+ git_tree *tree;
+
+ cl_git_pass(git_signature_default(&sig, _repo));
+ cl_git_pass(git_index_write_tree(&tree_id, index));
+ cl_git_pass(git_tree_lookup(&tree, _repo, &tree_id));
+
+ cl_git_pass(git_commit_create_v(
+ &commit_id, _repo, "HEAD", sig, sig,
+ NULL, "First", tree, 0));
+
+ git_tree_free(tree);
+ git_signature_free(sig);
+ }
+
+ git_index_free(index);
+}
diff --git a/tests/repo/iterator.c b/tests/repo/iterator.c
new file mode 100644
index 000000000..56b51852c
--- /dev/null
+++ b/tests/repo/iterator.c
@@ -0,0 +1,962 @@
+#include "clar_libgit2.h"
+#include "iterator.h"
+#include "repository.h"
+#include "fileops.h"
+#include <stdarg.h>
+
+static git_repository *g_repo;
+
+void test_repo_iterator__initialize(void)
+{
+}
+
+void test_repo_iterator__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ g_repo = NULL;
+}
+
+static void expect_iterator_items(
+ git_iterator *i,
+ int expected_flat,
+ const char **expected_flat_paths,
+ int expected_total,
+ const char **expected_total_paths)
+{
+ const git_index_entry *entry;
+ int count, error;
+ int no_trees = !(git_iterator_flags(i) & GIT_ITERATOR_INCLUDE_TREES);
+ bool v = false;
+
+ if (expected_flat < 0) { v = true; expected_flat = -expected_flat; }
+ if (expected_total < 0) { v = true; expected_total = -expected_total; }
+
+ if (v) fprintf(stderr, "== %s ==\n", no_trees ? "notrees" : "trees");
+
+ count = 0;
+
+ while (!git_iterator_advance(&entry, i)) {
+ if (v) fprintf(stderr, " %s %07o\n", entry->path, (int)entry->mode);
+
+ if (no_trees)
+ cl_assert(entry->mode != GIT_FILEMODE_TREE);
+
+ if (expected_flat_paths) {
+ const char *expect_path = expected_flat_paths[count];
+ size_t expect_len = strlen(expect_path);
+
+ cl_assert_equal_s(expect_path, entry->path);
+
+ if (expect_path[expect_len - 1] == '/')
+ cl_assert_equal_i(GIT_FILEMODE_TREE, entry->mode);
+ else
+ cl_assert(entry->mode != GIT_FILEMODE_TREE);
+ }
+
+ if (++count > expected_flat)
+ break;
+ }
+
+ cl_assert_equal_i(expected_flat, count);
+
+ cl_git_pass(git_iterator_reset(i, NULL, NULL));
+
+ count = 0;
+ cl_git_pass(git_iterator_current(&entry, i));
+
+ if (v) fprintf(stderr, "-- %s --\n", no_trees ? "notrees" : "trees");
+
+ while (entry != NULL) {
+ if (v) fprintf(stderr, " %s %07o\n", entry->path, (int)entry->mode);
+
+ if (no_trees)
+ cl_assert(entry->mode != GIT_FILEMODE_TREE);
+
+ if (expected_total_paths) {
+ const char *expect_path = expected_total_paths[count];
+ size_t expect_len = strlen(expect_path);
+
+ cl_assert_equal_s(expect_path, entry->path);
+
+ if (expect_path[expect_len - 1] == '/')
+ cl_assert_equal_i(GIT_FILEMODE_TREE, entry->mode);
+ else
+ cl_assert(entry->mode != GIT_FILEMODE_TREE);
+ }
+
+ if (entry->mode == GIT_FILEMODE_TREE) {
+ error = git_iterator_advance_into(&entry, i);
+
+ /* could return NOTFOUND if directory is empty */
+ cl_assert(!error || error == GIT_ENOTFOUND);
+
+ if (error == GIT_ENOTFOUND) {
+ error = git_iterator_advance(&entry, i);
+ cl_assert(!error || error == GIT_ITEROVER);
+ }
+ } else {
+ error = git_iterator_advance(&entry, i);
+ cl_assert(!error || error == GIT_ITEROVER);
+ }
+
+ if (++count > expected_total)
+ break;
+ }
+
+ cl_assert_equal_i(expected_total, count);
+}
+
+/* Index contents (including pseudotrees):
+ *
+ * 0: a 5: F 10: k/ 16: L/
+ * 1: B 6: g 11: k/1 17: L/1
+ * 2: c 7: H 12: k/a 18: L/a
+ * 3: D 8: i 13: k/B 19: L/B
+ * 4: e 9: J 14: k/c 20: L/c
+ * 15: k/D 21: L/D
+ *
+ * 0: B 5: L/ 11: a 16: k/
+ * 1: D 6: L/1 12: c 17: k/1
+ * 2: F 7: L/B 13: e 18: k/B
+ * 3: H 8: L/D 14: g 19: k/D
+ * 4: J 9: L/a 15: i 20: k/a
+ * 10: L/c 21: k/c
+ */
+
+void test_repo_iterator__index(void)
+{
+ git_iterator *i;
+ git_index *index;
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ /* autoexpand with no tree entries for index */
+ cl_git_pass(git_iterator_for_index(&i, index, 0, NULL, NULL));
+ expect_iterator_items(i, 20, NULL, 20, NULL);
+ git_iterator_free(i);
+
+ /* auto expand with tree entries */
+ cl_git_pass(git_iterator_for_index(
+ &i, index, GIT_ITERATOR_INCLUDE_TREES, NULL, NULL));
+ expect_iterator_items(i, 22, NULL, 22, NULL);
+ git_iterator_free(i);
+
+ /* no auto expand (implies trees included) */
+ cl_git_pass(git_iterator_for_index(
+ &i, index, GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL));
+ expect_iterator_items(i, 12, NULL, 22, NULL);
+ git_iterator_free(i);
+
+ git_index_free(index);
+}
+
+void test_repo_iterator__index_icase(void)
+{
+ git_iterator *i;
+ git_index *index;
+ unsigned int caps;
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ caps = git_index_caps(index);
+
+ /* force case sensitivity */
+ cl_git_pass(git_index_set_caps(index, caps & ~GIT_INDEXCAP_IGNORE_CASE));
+
+ /* autoexpand with no tree entries over range */
+ cl_git_pass(git_iterator_for_index(&i, index, 0, "c", "k/D"));
+ expect_iterator_items(i, 7, NULL, 7, NULL);
+ git_iterator_free(i);
+
+ cl_git_pass(git_iterator_for_index(&i, index, 0, "k", "k/Z"));
+ expect_iterator_items(i, 3, NULL, 3, NULL);
+ git_iterator_free(i);
+
+ /* auto expand with tree entries */
+ cl_git_pass(git_iterator_for_index(
+ &i, index, GIT_ITERATOR_INCLUDE_TREES, "c", "k/D"));
+ expect_iterator_items(i, 8, NULL, 8, NULL);
+ git_iterator_free(i);
+ cl_git_pass(git_iterator_for_index(
+ &i, index, GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z"));
+ expect_iterator_items(i, 4, NULL, 4, NULL);
+ git_iterator_free(i);
+
+ /* no auto expand (implies trees included) */
+ cl_git_pass(git_iterator_for_index(
+ &i, index, GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D"));
+ expect_iterator_items(i, 5, NULL, 8, NULL);
+ git_iterator_free(i);
+
+ cl_git_pass(git_iterator_for_index(
+ &i, index, GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z"));
+ expect_iterator_items(i, 1, NULL, 4, NULL);
+ git_iterator_free(i);
+
+ /* force case insensitivity */
+ cl_git_pass(git_index_set_caps(index, caps | GIT_INDEXCAP_IGNORE_CASE));
+
+ /* autoexpand with no tree entries over range */
+ cl_git_pass(git_iterator_for_index(&i, index, 0, "c", "k/D"));
+ expect_iterator_items(i, 13, NULL, 13, NULL);
+ git_iterator_free(i);
+
+ cl_git_pass(git_iterator_for_index(&i, index, 0, "k", "k/Z"));
+ expect_iterator_items(i, 5, NULL, 5, NULL);
+ git_iterator_free(i);
+
+ /* auto expand with tree entries */
+ cl_git_pass(git_iterator_for_index(
+ &i, index, GIT_ITERATOR_INCLUDE_TREES, "c", "k/D"));
+ expect_iterator_items(i, 14, NULL, 14, NULL);
+ git_iterator_free(i);
+
+ cl_git_pass(git_iterator_for_index(
+ &i, index, GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z"));
+ expect_iterator_items(i, 6, NULL, 6, NULL);
+ git_iterator_free(i);
+
+ /* no auto expand (implies trees included) */
+ cl_git_pass(git_iterator_for_index(
+ &i, index, GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D"));
+ expect_iterator_items(i, 9, NULL, 14, NULL);
+ git_iterator_free(i);
+
+ cl_git_pass(git_iterator_for_index(
+ &i, index, GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z"));
+ expect_iterator_items(i, 1, NULL, 6, NULL);
+ git_iterator_free(i);
+
+ cl_git_pass(git_index_set_caps(index, caps));
+ git_index_free(index);
+}
+
+void test_repo_iterator__tree(void)
+{
+ git_iterator *i;
+ git_tree *head;
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ cl_git_pass(git_repository_head_tree(&head, g_repo));
+
+ /* auto expand with no tree entries */
+ cl_git_pass(git_iterator_for_tree(&i, head, 0, NULL, NULL));
+ expect_iterator_items(i, 20, NULL, 20, NULL);
+ git_iterator_free(i);
+
+ /* auto expand with tree entries */
+ cl_git_pass(git_iterator_for_tree(
+ &i, head, GIT_ITERATOR_INCLUDE_TREES, NULL, NULL));
+ expect_iterator_items(i, 22, NULL, 22, NULL);
+ git_iterator_free(i);
+
+ /* no auto expand (implies trees included) */
+ cl_git_pass(git_iterator_for_tree(
+ &i, head, GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL));
+ expect_iterator_items(i, 12, NULL, 22, NULL);
+ git_iterator_free(i);
+
+ git_tree_free(head);
+}
+
+void test_repo_iterator__tree_icase(void)
+{
+ git_iterator *i;
+ git_tree *head;
+ git_iterator_flag_t flag;
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ cl_git_pass(git_repository_head_tree(&head, g_repo));
+
+ flag = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ /* auto expand with no tree entries */
+ cl_git_pass(git_iterator_for_tree(&i, head, flag, "c", "k/D"));
+ expect_iterator_items(i, 7, NULL, 7, NULL);
+ git_iterator_free(i);
+
+ cl_git_pass(git_iterator_for_tree(&i, head, flag, "k", "k/Z"));
+ expect_iterator_items(i, 3, NULL, 3, NULL);
+ git_iterator_free(i);
+
+ /* auto expand with tree entries */
+ cl_git_pass(git_iterator_for_tree(
+ &i, head, flag | GIT_ITERATOR_INCLUDE_TREES, "c", "k/D"));
+ expect_iterator_items(i, 8, NULL, 8, NULL);
+ git_iterator_free(i);
+
+ cl_git_pass(git_iterator_for_tree(
+ &i, head, flag | GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z"));
+ expect_iterator_items(i, 4, NULL, 4, NULL);
+ git_iterator_free(i);
+
+ /* no auto expand (implies trees included) */
+ cl_git_pass(git_iterator_for_tree(
+ &i, head, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D"));
+ expect_iterator_items(i, 5, NULL, 8, NULL);
+ git_iterator_free(i);
+
+ cl_git_pass(git_iterator_for_tree(
+ &i, head, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z"));
+ expect_iterator_items(i, 1, NULL, 4, NULL);
+ git_iterator_free(i);
+
+ flag = GIT_ITERATOR_IGNORE_CASE;
+
+ /* auto expand with no tree entries */
+ cl_git_pass(git_iterator_for_tree(&i, head, flag, "c", "k/D"));
+ expect_iterator_items(i, 13, NULL, 13, NULL);
+ git_iterator_free(i);
+
+ cl_git_pass(git_iterator_for_tree(&i, head, flag, "k", "k/Z"));
+ expect_iterator_items(i, 5, NULL, 5, NULL);
+ git_iterator_free(i);
+
+ /* auto expand with tree entries */
+ cl_git_pass(git_iterator_for_tree(
+ &i, head, flag | GIT_ITERATOR_INCLUDE_TREES, "c", "k/D"));
+ expect_iterator_items(i, 14, NULL, 14, NULL);
+ git_iterator_free(i);
+
+ cl_git_pass(git_iterator_for_tree(
+ &i, head, flag | GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z"));
+ expect_iterator_items(i, 6, NULL, 6, NULL);
+ git_iterator_free(i);
+
+ /* no auto expand (implies trees included) */
+ cl_git_pass(git_iterator_for_tree(
+ &i, head, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D"));
+ expect_iterator_items(i, 9, NULL, 14, NULL);
+ git_iterator_free(i);
+
+ cl_git_pass(git_iterator_for_tree(
+ &i, head, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z"));
+ expect_iterator_items(i, 1, NULL, 6, NULL);
+ git_iterator_free(i);
+
+ git_tree_free(head);
+}
+
+void test_repo_iterator__tree_more(void)
+{
+ git_iterator *i;
+ git_tree *head;
+ static const char *expect_basic[] = {
+ "current_file",
+ "file_deleted",
+ "modified_file",
+ "staged_changes",
+ "staged_changes_file_deleted",
+ "staged_changes_modified_file",
+ "staged_delete_file_deleted",
+ "staged_delete_modified_file",
+ "subdir.txt",
+ "subdir/current_file",
+ "subdir/deleted_file",
+ "subdir/modified_file",
+ NULL,
+ };
+ static const char *expect_trees[] = {
+ "current_file",
+ "file_deleted",
+ "modified_file",
+ "staged_changes",
+ "staged_changes_file_deleted",
+ "staged_changes_modified_file",
+ "staged_delete_file_deleted",
+ "staged_delete_modified_file",
+ "subdir.txt",
+ "subdir/",
+ "subdir/current_file",
+ "subdir/deleted_file",
+ "subdir/modified_file",
+ NULL,
+ };
+ static const char *expect_noauto[] = {
+ "current_file",
+ "file_deleted",
+ "modified_file",
+ "staged_changes",
+ "staged_changes_file_deleted",
+ "staged_changes_modified_file",
+ "staged_delete_file_deleted",
+ "staged_delete_modified_file",
+ "subdir.txt",
+ "subdir/",
+ NULL
+ };
+
+ g_repo = cl_git_sandbox_init("status");
+
+ cl_git_pass(git_repository_head_tree(&head, g_repo));
+
+ /* auto expand with no tree entries */
+ cl_git_pass(git_iterator_for_tree(&i, head, 0, NULL, NULL));
+ expect_iterator_items(i, 12, expect_basic, 12, expect_basic);
+ git_iterator_free(i);
+
+ /* auto expand with tree entries */
+ cl_git_pass(git_iterator_for_tree(
+ &i, head, GIT_ITERATOR_INCLUDE_TREES, NULL, NULL));
+ expect_iterator_items(i, 13, expect_trees, 13, expect_trees);
+ git_iterator_free(i);
+
+ /* no auto expand (implies trees included) */
+ cl_git_pass(git_iterator_for_tree(
+ &i, head, GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL));
+ expect_iterator_items(i, 10, expect_noauto, 13, expect_trees);
+ git_iterator_free(i);
+
+ git_tree_free(head);
+}
+
+/* "b=name,t=name", blob_id, tree_id */
+static void build_test_tree(
+ git_oid *out, git_repository *repo, const char *fmt, ...)
+{
+ git_oid *id;
+ git_treebuilder *builder;
+ const char *scan = fmt, *next;
+ char type, delimiter;
+ git_filemode_t mode = GIT_FILEMODE_BLOB;
+ git_buf name = GIT_BUF_INIT;
+ va_list arglist;
+
+ cl_git_pass(git_treebuilder_create(&builder, NULL)); /* start builder */
+
+ va_start(arglist, fmt);
+ while (*scan) {
+ switch (type = *scan++) {
+ case 't': case 'T': mode = GIT_FILEMODE_TREE; break;
+ case 'b': case 'B': mode = GIT_FILEMODE_BLOB; break;
+ default:
+ cl_assert(type == 't' || type == 'T' || type == 'b' || type == 'B');
+ }
+
+ delimiter = *scan++; /* read and skip delimiter */
+ for (next = scan; *next && *next != delimiter; ++next)
+ /* seek end */;
+ cl_git_pass(git_buf_set(&name, scan, (size_t)(next - scan)));
+ for (scan = next; *scan && (*scan == delimiter || *scan == ','); ++scan)
+ /* skip delimiter and optional comma */;
+
+ id = va_arg(arglist, git_oid *);
+
+ cl_git_pass(git_treebuilder_insert(NULL, builder, name.ptr, id, mode));
+ }
+ va_end(arglist);
+
+ cl_git_pass(git_treebuilder_write(out, repo, builder));
+
+ git_treebuilder_free(builder);
+ git_buf_free(&name);
+}
+
+void test_repo_iterator__tree_case_conflicts_0(void)
+{
+ const char *blob_sha = "d44e18fb93b7107b5cd1b95d601591d77869a1b6";
+ git_tree *tree;
+ git_oid blob_id, biga_id, littlea_id, tree_id;
+ git_iterator *i;
+ const char *expect_cs[] = {
+ "A/1.file", "A/3.file", "a/2.file", "a/4.file" };
+ const char *expect_ci[] = {
+ "A/1.file", "a/2.file", "A/3.file", "a/4.file" };
+ const char *expect_cs_trees[] = {
+ "A/", "A/1.file", "A/3.file", "a/", "a/2.file", "a/4.file" };
+ const char *expect_ci_trees[] = {
+ "A/", "A/1.file", "a/2.file", "A/3.file", "a/4.file" };
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ cl_git_pass(git_oid_fromstr(&blob_id, blob_sha)); /* lookup blob */
+
+ /* create tree with: A/1.file, A/3.file, a/2.file, a/4.file */
+ build_test_tree(
+ &biga_id, g_repo, "b|1.file|,b|3.file|", &blob_id, &blob_id);
+ build_test_tree(
+ &littlea_id, g_repo, "b|2.file|,b|4.file|", &blob_id, &blob_id);
+ build_test_tree(
+ &tree_id, g_repo, "t|A|,t|a|", &biga_id, &littlea_id);
+
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
+
+ cl_git_pass(git_iterator_for_tree(
+ &i, tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL));
+ expect_iterator_items(i, 4, expect_cs, 4, expect_cs);
+ git_iterator_free(i);
+
+ cl_git_pass(git_iterator_for_tree(
+ &i, tree, GIT_ITERATOR_IGNORE_CASE, NULL, NULL));
+ expect_iterator_items(i, 4, expect_ci, 4, expect_ci);
+ git_iterator_free(i);
+
+ cl_git_pass(git_iterator_for_tree(
+ &i, tree, GIT_ITERATOR_DONT_IGNORE_CASE |
+ GIT_ITERATOR_INCLUDE_TREES, NULL, NULL));
+ expect_iterator_items(i, 6, expect_cs_trees, 6, expect_cs_trees);
+ git_iterator_free(i);
+
+ cl_git_pass(git_iterator_for_tree(
+ &i, tree, GIT_ITERATOR_IGNORE_CASE |
+ GIT_ITERATOR_INCLUDE_TREES, NULL, NULL));
+ expect_iterator_items(i, 5, expect_ci_trees, 5, expect_ci_trees);
+ git_iterator_free(i);
+
+ git_tree_free(tree);
+}
+
+void test_repo_iterator__tree_case_conflicts_1(void)
+{
+ const char *blob_sha = "d44e18fb93b7107b5cd1b95d601591d77869a1b6";
+ git_tree *tree;
+ git_oid blob_id, Ab_id, biga_id, littlea_id, tree_id;
+ git_iterator *i;
+ const char *expect_cs[] = {
+ "A/a", "A/b/1", "A/c", "a/C", "a/a", "a/b" };
+ const char *expect_ci[] = {
+ "A/a", "a/b", "A/b/1", "A/c" };
+ const char *expect_cs_trees[] = {
+ "A/", "A/a", "A/b/", "A/b/1", "A/c", "a/", "a/C", "a/a", "a/b" };
+ const char *expect_ci_trees[] = {
+ "A/", "A/a", "a/b", "A/b/", "A/b/1", "A/c" };
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ cl_git_pass(git_oid_fromstr(&blob_id, blob_sha)); /* lookup blob */
+
+ /* create: A/a A/b/1 A/c a/a a/b a/C */
+ build_test_tree(&Ab_id, g_repo, "b|1|", &blob_id);
+ build_test_tree(
+ &biga_id, g_repo, "b|a|,t|b|,b|c|", &blob_id, &Ab_id, &blob_id);
+ build_test_tree(
+ &littlea_id, g_repo, "b|a|,b|b|,b|C|", &blob_id, &blob_id, &blob_id);
+ build_test_tree(
+ &tree_id, g_repo, "t|A|,t|a|", &biga_id, &littlea_id);
+
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
+
+ cl_git_pass(git_iterator_for_tree(
+ &i, tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL));
+ expect_iterator_items(i, 6, expect_cs, 6, expect_cs);
+ git_iterator_free(i);
+
+ cl_git_pass(git_iterator_for_tree(
+ &i, tree, GIT_ITERATOR_IGNORE_CASE, NULL, NULL));
+ expect_iterator_items(i, 4, expect_ci, 4, expect_ci);
+ git_iterator_free(i);
+
+ cl_git_pass(git_iterator_for_tree(
+ &i, tree, GIT_ITERATOR_DONT_IGNORE_CASE |
+ GIT_ITERATOR_INCLUDE_TREES, NULL, NULL));
+ expect_iterator_items(i, 9, expect_cs_trees, 9, expect_cs_trees);
+ git_iterator_free(i);
+
+ cl_git_pass(git_iterator_for_tree(
+ &i, tree, GIT_ITERATOR_IGNORE_CASE |
+ GIT_ITERATOR_INCLUDE_TREES, NULL, NULL));
+ expect_iterator_items(i, 6, expect_ci_trees, 6, expect_ci_trees);
+ git_iterator_free(i);
+
+ git_tree_free(tree);
+}
+
+void test_repo_iterator__tree_case_conflicts_2(void)
+{
+ const char *blob_sha = "d44e18fb93b7107b5cd1b95d601591d77869a1b6";
+ git_tree *tree;
+ git_oid blob_id, d1, d2, c1, c2, b1, b2, a1, a2, tree_id;
+ git_iterator *i;
+ const char *expect_cs[] = {
+ "A/B/C/D/16", "A/B/C/D/foo", "A/B/C/d/15", "A/B/C/d/FOO",
+ "A/B/c/D/14", "A/B/c/D/foo", "A/B/c/d/13", "A/B/c/d/FOO",
+ "A/b/C/D/12", "A/b/C/D/foo", "A/b/C/d/11", "A/b/C/d/FOO",
+ "A/b/c/D/10", "A/b/c/D/foo", "A/b/c/d/09", "A/b/c/d/FOO",
+ "a/B/C/D/08", "a/B/C/D/foo", "a/B/C/d/07", "a/B/C/d/FOO",
+ "a/B/c/D/06", "a/B/c/D/foo", "a/B/c/d/05", "a/B/c/d/FOO",
+ "a/b/C/D/04", "a/b/C/D/foo", "a/b/C/d/03", "a/b/C/d/FOO",
+ "a/b/c/D/02", "a/b/c/D/foo", "a/b/c/d/01", "a/b/c/d/FOO", };
+ const char *expect_ci[] = {
+ "a/b/c/d/01", "a/b/c/D/02", "a/b/C/d/03", "a/b/C/D/04",
+ "a/B/c/d/05", "a/B/c/D/06", "a/B/C/d/07", "a/B/C/D/08",
+ "A/b/c/d/09", "A/b/c/D/10", "A/b/C/d/11", "A/b/C/D/12",
+ "A/B/c/d/13", "A/B/c/D/14", "A/B/C/d/15", "A/B/C/D/16",
+ "A/B/C/D/foo", };
+ const char *expect_ci_trees[] = {
+ "A/", "A/B/", "A/B/C/", "A/B/C/D/",
+ "a/b/c/d/01", "a/b/c/D/02", "a/b/C/d/03", "a/b/C/D/04",
+ "a/B/c/d/05", "a/B/c/D/06", "a/B/C/d/07", "a/B/C/D/08",
+ "A/b/c/d/09", "A/b/c/D/10", "A/b/C/d/11", "A/b/C/D/12",
+ "A/B/c/d/13", "A/B/c/D/14", "A/B/C/d/15", "A/B/C/D/16",
+ "A/B/C/D/foo", };
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ cl_git_pass(git_oid_fromstr(&blob_id, blob_sha)); /* lookup blob */
+
+ build_test_tree(&d1, g_repo, "b|16|,b|foo|", &blob_id, &blob_id);
+ build_test_tree(&d2, g_repo, "b|15|,b|FOO|", &blob_id, &blob_id);
+ build_test_tree(&c1, g_repo, "t|D|,t|d|", &d1, &d2);
+ build_test_tree(&d1, g_repo, "b|14|,b|foo|", &blob_id, &blob_id);
+ build_test_tree(&d2, g_repo, "b|13|,b|FOO|", &blob_id, &blob_id);
+ build_test_tree(&c2, g_repo, "t|D|,t|d|", &d1, &d2);
+ build_test_tree(&b1, g_repo, "t|C|,t|c|", &c1, &c2);
+
+ build_test_tree(&d1, g_repo, "b|12|,b|foo|", &blob_id, &blob_id);
+ build_test_tree(&d2, g_repo, "b|11|,b|FOO|", &blob_id, &blob_id);
+ build_test_tree(&c1, g_repo, "t|D|,t|d|", &d1, &d2);
+ build_test_tree(&d1, g_repo, "b|10|,b|foo|", &blob_id, &blob_id);
+ build_test_tree(&d2, g_repo, "b|09|,b|FOO|", &blob_id, &blob_id);
+ build_test_tree(&c2, g_repo, "t|D|,t|d|", &d1, &d2);
+ build_test_tree(&b2, g_repo, "t|C|,t|c|", &c1, &c2);
+
+ build_test_tree(&a1, g_repo, "t|B|,t|b|", &b1, &b2);
+
+ build_test_tree(&d1, g_repo, "b|08|,b|foo|", &blob_id, &blob_id);
+ build_test_tree(&d2, g_repo, "b|07|,b|FOO|", &blob_id, &blob_id);
+ build_test_tree(&c1, g_repo, "t|D|,t|d|", &d1, &d2);
+ build_test_tree(&d1, g_repo, "b|06|,b|foo|", &blob_id, &blob_id);
+ build_test_tree(&d2, g_repo, "b|05|,b|FOO|", &blob_id, &blob_id);
+ build_test_tree(&c2, g_repo, "t|D|,t|d|", &d1, &d2);
+ build_test_tree(&b1, g_repo, "t|C|,t|c|", &c1, &c2);
+
+ build_test_tree(&d1, g_repo, "b|04|,b|foo|", &blob_id, &blob_id);
+ build_test_tree(&d2, g_repo, "b|03|,b|FOO|", &blob_id, &blob_id);
+ build_test_tree(&c1, g_repo, "t|D|,t|d|", &d1, &d2);
+ build_test_tree(&d1, g_repo, "b|02|,b|foo|", &blob_id, &blob_id);
+ build_test_tree(&d2, g_repo, "b|01|,b|FOO|", &blob_id, &blob_id);
+ build_test_tree(&c2, g_repo, "t|D|,t|d|", &d1, &d2);
+ build_test_tree(&b2, g_repo, "t|C|,t|c|", &c1, &c2);
+
+ build_test_tree(&a2, g_repo, "t|B|,t|b|", &b1, &b2);
+
+ build_test_tree(&tree_id, g_repo, "t/A/,t/a/", &a1, &a2);
+
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
+
+ cl_git_pass(git_iterator_for_tree(
+ &i, tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL));
+ expect_iterator_items(i, 32, expect_cs, 32, expect_cs);
+ git_iterator_free(i);
+
+ cl_git_pass(git_iterator_for_tree(
+ &i, tree, GIT_ITERATOR_IGNORE_CASE, NULL, NULL));
+ expect_iterator_items(i, 17, expect_ci, 17, expect_ci);
+ git_iterator_free(i);
+
+ cl_git_pass(git_iterator_for_tree(
+ &i, tree, GIT_ITERATOR_IGNORE_CASE |
+ GIT_ITERATOR_INCLUDE_TREES, NULL, NULL));
+ expect_iterator_items(i, 21, expect_ci_trees, 21, expect_ci_trees);
+ git_iterator_free(i);
+
+ git_tree_free(tree);
+}
+
+void test_repo_iterator__workdir(void)
+{
+ git_iterator *i;
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ /* auto expand with no tree entries */
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, 0, NULL, NULL));
+ expect_iterator_items(i, 20, NULL, 20, NULL);
+ git_iterator_free(i);
+
+ /* auto expand with tree entries */
+ cl_git_pass(git_iterator_for_workdir(
+ &i, g_repo, GIT_ITERATOR_INCLUDE_TREES, NULL, NULL));
+ expect_iterator_items(i, 22, NULL, 22, NULL);
+ git_iterator_free(i);
+
+ /* no auto expand (implies trees included) */
+ cl_git_pass(git_iterator_for_workdir(
+ &i, g_repo, GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL));
+ expect_iterator_items(i, 12, NULL, 22, NULL);
+ git_iterator_free(i);
+}
+
+void test_repo_iterator__workdir_icase(void)
+{
+ git_iterator *i;
+ git_iterator_flag_t flag;
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ flag = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ /* auto expand with no tree entries */
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, flag, "c", "k/D"));
+ expect_iterator_items(i, 7, NULL, 7, NULL);
+ git_iterator_free(i);
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, flag, "k", "k/Z"));
+ expect_iterator_items(i, 3, NULL, 3, NULL);
+ git_iterator_free(i);
+
+ /* auto expand with tree entries */
+ cl_git_pass(git_iterator_for_workdir(
+ &i, g_repo, flag | GIT_ITERATOR_INCLUDE_TREES, "c", "k/D"));
+ expect_iterator_items(i, 8, NULL, 8, NULL);
+ git_iterator_free(i);
+
+ cl_git_pass(git_iterator_for_workdir(
+ &i, g_repo, flag | GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z"));
+ expect_iterator_items(i, 4, NULL, 4, NULL);
+ git_iterator_free(i);
+
+ /* no auto expand (implies trees included) */
+ cl_git_pass(git_iterator_for_workdir(
+ &i, g_repo, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D"));
+ expect_iterator_items(i, 5, NULL, 8, NULL);
+ git_iterator_free(i);
+
+ cl_git_pass(git_iterator_for_workdir(
+ &i, g_repo, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z"));
+ expect_iterator_items(i, 1, NULL, 4, NULL);
+ git_iterator_free(i);
+
+ flag = GIT_ITERATOR_IGNORE_CASE;
+
+ /* auto expand with no tree entries */
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, flag, "c", "k/D"));
+ expect_iterator_items(i, 13, NULL, 13, NULL);
+ git_iterator_free(i);
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, flag, "k", "k/Z"));
+ expect_iterator_items(i, 5, NULL, 5, NULL);
+ git_iterator_free(i);
+
+ /* auto expand with tree entries */
+ cl_git_pass(git_iterator_for_workdir(
+ &i, g_repo, flag | GIT_ITERATOR_INCLUDE_TREES, "c", "k/D"));
+ expect_iterator_items(i, 14, NULL, 14, NULL);
+ git_iterator_free(i);
+
+ cl_git_pass(git_iterator_for_workdir(
+ &i, g_repo, flag | GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z"));
+ expect_iterator_items(i, 6, NULL, 6, NULL);
+ git_iterator_free(i);
+
+ /* no auto expand (implies trees included) */
+ cl_git_pass(git_iterator_for_workdir(
+ &i, g_repo, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D"));
+ expect_iterator_items(i, 9, NULL, 14, NULL);
+ git_iterator_free(i);
+
+ cl_git_pass(git_iterator_for_workdir(
+ &i, g_repo, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z"));
+ expect_iterator_items(i, 1, NULL, 6, NULL);
+ git_iterator_free(i);
+}
+
+static void build_workdir_tree(const char *root, int dirs, int subs)
+{
+ int i, j;
+ char buf[64], sub[64];
+
+ for (i = 0; i < dirs; ++i) {
+ if (i % 2 == 0) {
+ p_snprintf(buf, sizeof(buf), "%s/dir%02d", root, i);
+ cl_git_pass(git_futils_mkdir(buf, NULL, 0775, GIT_MKDIR_PATH));
+
+ p_snprintf(buf, sizeof(buf), "%s/dir%02d/file", root, i);
+ cl_git_mkfile(buf, buf);
+ buf[strlen(buf) - 5] = '\0';
+ } else {
+ p_snprintf(buf, sizeof(buf), "%s/DIR%02d", root, i);
+ cl_git_pass(git_futils_mkdir(buf, NULL, 0775, GIT_MKDIR_PATH));
+ }
+
+ for (j = 0; j < subs; ++j) {
+ switch (j % 4) {
+ case 0: p_snprintf(sub, sizeof(sub), "%s/sub%02d", buf, j); break;
+ case 1: p_snprintf(sub, sizeof(sub), "%s/sUB%02d", buf, j); break;
+ case 2: p_snprintf(sub, sizeof(sub), "%s/Sub%02d", buf, j); break;
+ case 3: p_snprintf(sub, sizeof(sub), "%s/SUB%02d", buf, j); break;
+ }
+ cl_git_pass(git_futils_mkdir(sub, NULL, 0775, GIT_MKDIR_PATH));
+
+ if (j % 2 == 0) {
+ size_t sublen = strlen(sub);
+ memcpy(&sub[sublen], "/file", sizeof("/file"));
+ cl_git_mkfile(sub, sub);
+ sub[sublen] = '\0';
+ }
+ }
+ }
+}
+
+void test_repo_iterator__workdir_depth(void)
+{
+ git_iterator *iter;
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ build_workdir_tree("icase", 10, 10);
+ build_workdir_tree("icase/DIR01/sUB01", 50, 0);
+ build_workdir_tree("icase/dir02/sUB01", 50, 0);
+
+ /* auto expand with no tree entries */
+ cl_git_pass(git_iterator_for_workdir(&iter, g_repo, 0, NULL, NULL));
+ expect_iterator_items(iter, 125, NULL, 125, NULL);
+ git_iterator_free(iter);
+
+ /* auto expand with tree entries (empty dirs silently skipped) */
+ cl_git_pass(git_iterator_for_workdir(
+ &iter, g_repo, GIT_ITERATOR_INCLUDE_TREES, NULL, NULL));
+ expect_iterator_items(iter, 337, NULL, 337, NULL);
+ git_iterator_free(iter);
+}
+
+void test_repo_iterator__fs(void)
+{
+ git_iterator *i;
+ static const char *expect_base[] = {
+ "DIR01/Sub02/file",
+ "DIR01/sub00/file",
+ "current_file",
+ "dir00/Sub02/file",
+ "dir00/file",
+ "dir00/sub00/file",
+ "modified_file",
+ "new_file",
+ NULL,
+ };
+ static const char *expect_trees[] = {
+ "DIR01/",
+ "DIR01/SUB03/",
+ "DIR01/Sub02/",
+ "DIR01/Sub02/file",
+ "DIR01/sUB01/",
+ "DIR01/sub00/",
+ "DIR01/sub00/file",
+ "current_file",
+ "dir00/",
+ "dir00/SUB03/",
+ "dir00/Sub02/",
+ "dir00/Sub02/file",
+ "dir00/file",
+ "dir00/sUB01/",
+ "dir00/sub00/",
+ "dir00/sub00/file",
+ "modified_file",
+ "new_file",
+ NULL,
+ };
+ static const char *expect_noauto[] = {
+ "DIR01/",
+ "current_file",
+ "dir00/",
+ "modified_file",
+ "new_file",
+ NULL,
+ };
+
+ g_repo = cl_git_sandbox_init("status");
+
+ build_workdir_tree("status/subdir", 2, 4);
+
+ cl_git_pass(git_iterator_for_filesystem(
+ &i, "status/subdir", 0, NULL, NULL));
+ expect_iterator_items(i, 8, expect_base, 8, expect_base);
+ git_iterator_free(i);
+
+ cl_git_pass(git_iterator_for_filesystem(
+ &i, "status/subdir", GIT_ITERATOR_INCLUDE_TREES, NULL, NULL));
+ expect_iterator_items(i, 18, expect_trees, 18, expect_trees);
+ git_iterator_free(i);
+
+ cl_git_pass(git_iterator_for_filesystem(
+ &i, "status/subdir", GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL));
+ expect_iterator_items(i, 5, expect_noauto, 18, expect_trees);
+ git_iterator_free(i);
+
+ git__tsort((void **)expect_base, 8, (git__tsort_cmp)git__strcasecmp);
+ git__tsort((void **)expect_trees, 18, (git__tsort_cmp)git__strcasecmp);
+ git__tsort((void **)expect_noauto, 5, (git__tsort_cmp)git__strcasecmp);
+
+ cl_git_pass(git_iterator_for_filesystem(
+ &i, "status/subdir", GIT_ITERATOR_IGNORE_CASE, NULL, NULL));
+ expect_iterator_items(i, 8, expect_base, 8, expect_base);
+ git_iterator_free(i);
+
+ cl_git_pass(git_iterator_for_filesystem(
+ &i, "status/subdir", GIT_ITERATOR_IGNORE_CASE |
+ GIT_ITERATOR_INCLUDE_TREES, NULL, NULL));
+ expect_iterator_items(i, 18, expect_trees, 18, expect_trees);
+ git_iterator_free(i);
+
+ cl_git_pass(git_iterator_for_filesystem(
+ &i, "status/subdir", GIT_ITERATOR_IGNORE_CASE |
+ GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL));
+ expect_iterator_items(i, 5, expect_noauto, 18, expect_trees);
+ git_iterator_free(i);
+}
+
+void test_repo_iterator__fs2(void)
+{
+ git_iterator *i;
+ static const char *expect_base[] = {
+ "heads/br2",
+ "heads/dir",
+ "heads/long-file-name",
+ "heads/master",
+ "heads/packed-test",
+ "heads/subtrees",
+ "heads/test",
+ "tags/e90810b",
+ "tags/foo/bar",
+ "tags/foo/foo/bar",
+ "tags/point_to_blob",
+ "tags/test",
+ NULL,
+ };
+
+ g_repo = cl_git_sandbox_init("testrepo");
+
+ cl_git_pass(git_iterator_for_filesystem(
+ &i, "testrepo/.git/refs", 0, NULL, NULL));
+ expect_iterator_items(i, 12, expect_base, 12, expect_base);
+ git_iterator_free(i);
+}
+
+void test_repo_iterator__fs_preserves_error(void)
+{
+ git_iterator *i;
+ const git_index_entry *e;
+
+ if (!cl_is_chmod_supported())
+ return;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_must_pass(p_mkdir("empty_standard_repo/r", 0777));
+ cl_git_mkfile("empty_standard_repo/r/a", "hello");
+ cl_must_pass(p_mkdir("empty_standard_repo/r/b", 0777));
+ cl_git_mkfile("empty_standard_repo/r/b/problem", "not me");
+ cl_must_pass(p_chmod("empty_standard_repo/r/b", 0000));
+ cl_must_pass(p_mkdir("empty_standard_repo/r/c", 0777));
+ cl_git_mkfile("empty_standard_repo/r/d", "final");
+
+ cl_git_pass(git_iterator_for_filesystem(
+ &i, "empty_standard_repo/r", 0, NULL, NULL));
+
+ cl_git_pass(git_iterator_advance(&e, i)); /* a */
+ cl_git_fail(git_iterator_advance(&e, i)); /* b */
+ cl_assert(giterr_last());
+ cl_assert(giterr_last()->message != NULL);
+ /* skip 'c/' empty directory */
+ cl_git_pass(git_iterator_advance(&e, i)); /* d */
+ cl_assert_equal_i(GIT_ITEROVER, git_iterator_advance(&e, i));
+
+ cl_must_pass(p_chmod("empty_standard_repo/r/b", 0777));
+
+ git_iterator_free(i);
+}
diff --git a/tests-clar/repo/message.c b/tests/repo/message.c
index 629d40c12..629d40c12 100644
--- a/tests-clar/repo/message.c
+++ b/tests/repo/message.c
diff --git a/tests/repo/open.c b/tests/repo/open.c
new file mode 100644
index 000000000..7cfe041c2
--- /dev/null
+++ b/tests/repo/open.c
@@ -0,0 +1,376 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+#include <ctype.h>
+
+void test_repo_open__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+
+ if (git_path_isdir("alternate"))
+ git_futils_rmdir_r("alternate", NULL, GIT_RMDIR_REMOVE_FILES);
+}
+
+void test_repo_open__bare_empty_repo(void)
+{
+ git_repository *repo = cl_git_sandbox_init("empty_bare.git");
+
+ cl_assert(git_repository_path(repo) != NULL);
+ cl_assert(git__suffixcmp(git_repository_path(repo), "/") == 0);
+ cl_assert(git_repository_workdir(repo) == NULL);
+}
+
+void test_repo_open__standard_empty_repo_through_gitdir(void)
+{
+ git_repository *repo;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("empty_standard_repo/.gitted")));
+
+ cl_assert(git_repository_path(repo) != NULL);
+ cl_assert(git__suffixcmp(git_repository_path(repo), "/") == 0);
+
+ cl_assert(git_repository_workdir(repo) != NULL);
+ cl_assert(git__suffixcmp(git_repository_workdir(repo), "/") == 0);
+
+ git_repository_free(repo);
+}
+
+void test_repo_open__standard_empty_repo_through_workdir(void)
+{
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_assert(git_repository_path(repo) != NULL);
+ cl_assert(git__suffixcmp(git_repository_path(repo), "/") == 0);
+
+ cl_assert(git_repository_workdir(repo) != NULL);
+ cl_assert(git__suffixcmp(git_repository_workdir(repo), "/") == 0);
+}
+
+
+void test_repo_open__open_with_discover(void)
+{
+ static const char *variants[] = {
+ "attr", "attr/", "attr/.git", "attr/.git/",
+ "attr/sub", "attr/sub/", "attr/sub/sub", "attr/sub/sub/",
+ NULL
+ };
+ git_repository *repo;
+ const char **scan;
+
+ cl_fixture_sandbox("attr");
+ cl_git_pass(p_rename("attr/.gitted", "attr/.git"));
+
+ for (scan = variants; *scan != NULL; scan++) {
+ cl_git_pass(git_repository_open_ext(&repo, *scan, 0, NULL));
+ cl_assert(git__suffixcmp(git_repository_path(repo), "attr/.git/") == 0);
+ cl_assert(git__suffixcmp(git_repository_workdir(repo), "attr/") == 0);
+ git_repository_free(repo);
+ }
+
+ cl_fixture_cleanup("attr");
+}
+
+static void make_gitlink_dir(const char *dir, const char *linktext)
+{
+ git_buf path = GIT_BUF_INIT;
+
+ cl_git_pass(git_futils_mkdir(dir, NULL, 0777, GIT_MKDIR_VERIFY_DIR));
+ cl_git_pass(git_buf_joinpath(&path, dir, ".git"));
+ cl_git_rewritefile(path.ptr, linktext);
+ git_buf_free(&path);
+}
+
+void test_repo_open__gitlinked(void)
+{
+ /* need to have both repo dir and workdir set up correctly */
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+ git_repository *repo2;
+
+ make_gitlink_dir("alternate", "gitdir: ../empty_standard_repo/.git");
+
+ cl_git_pass(git_repository_open(&repo2, "alternate"));
+
+ cl_assert(git_repository_path(repo2) != NULL);
+ cl_assert_(git__suffixcmp(git_repository_path(repo2), "empty_standard_repo/.git/") == 0, git_repository_path(repo2));
+ cl_assert_equal_s(git_repository_path(repo), git_repository_path(repo2));
+
+ cl_assert(git_repository_workdir(repo2) != NULL);
+ cl_assert_(git__suffixcmp(git_repository_workdir(repo2), "alternate/") == 0, git_repository_workdir(repo2));
+
+ git_repository_free(repo2);
+}
+
+void test_repo_open__from_git_new_workdir(void)
+{
+ /* The git-new-workdir script that ships with git sets up a bunch of
+ * symlinks to create a second workdir that shares the object db with
+ * another checkout. Libgit2 can open a repo that has been configured
+ * this way.
+ */
+ cl_git_sandbox_init("empty_standard_repo");
+
+#ifndef GIT_WIN32
+ git_repository *repo2;
+ git_buf link_tgt = GIT_BUF_INIT, link = GIT_BUF_INIT, body = GIT_BUF_INIT;
+ const char **scan;
+ int link_fd;
+ static const char *links[] = {
+ "config", "refs", "logs/refs", "objects", "info", "hooks",
+ "packed-refs", "remotes", "rr-cache", "svn", NULL
+ };
+ static const char *copies[] = {
+ "HEAD", NULL
+ };
+
+ cl_git_pass(p_mkdir("alternate", 0777));
+ cl_git_pass(p_mkdir("alternate/.git", 0777));
+
+ for (scan = links; *scan != NULL; scan++) {
+ git_buf_joinpath(&link_tgt, "empty_standard_repo/.git", *scan);
+ if (git_path_exists(link_tgt.ptr)) {
+ git_buf_joinpath(&link_tgt, "../../empty_standard_repo/.git", *scan);
+ git_buf_joinpath(&link, "alternate/.git", *scan);
+ if (strchr(*scan, '/'))
+ git_futils_mkpath2file(link.ptr, 0777);
+ cl_assert_(symlink(link_tgt.ptr, link.ptr) == 0, strerror(errno));
+ }
+ }
+ for (scan = copies; *scan != NULL; scan++) {
+ git_buf_joinpath(&link_tgt, "empty_standard_repo/.git", *scan);
+ if (git_path_exists(link_tgt.ptr)) {
+ git_buf_joinpath(&link, "alternate/.git", *scan);
+ cl_git_pass(git_futils_readbuffer(&body, link_tgt.ptr));
+
+ cl_assert((link_fd = git_futils_creat_withpath(link.ptr, 0777, 0666)) >= 0);
+ cl_must_pass(p_write(link_fd, body.ptr, body.size));
+ p_close(link_fd);
+ }
+ }
+
+ git_buf_free(&link_tgt);
+ git_buf_free(&link);
+ git_buf_free(&body);
+
+
+ cl_git_pass(git_repository_open(&repo2, "alternate"));
+
+ cl_assert(git_repository_path(repo2) != NULL);
+ cl_assert_(git__suffixcmp(git_repository_path(repo2), "alternate/.git/") == 0, git_repository_path(repo2));
+
+ cl_assert(git_repository_workdir(repo2) != NULL);
+ cl_assert_(git__suffixcmp(git_repository_workdir(repo2), "alternate/") == 0, git_repository_workdir(repo2));
+
+ git_repository_free(repo2);
+#endif
+}
+
+void test_repo_open__failures(void)
+{
+ git_repository *base, *repo;
+ git_buf ceiling = GIT_BUF_INIT;
+
+ base = cl_git_sandbox_init("attr");
+ cl_git_pass(git_buf_sets(&ceiling, git_repository_workdir(base)));
+
+ /* fail with no searching */
+ cl_git_fail(git_repository_open(&repo, "attr/sub"));
+ cl_git_fail(git_repository_open_ext(
+ &repo, "attr/sub", GIT_REPOSITORY_OPEN_NO_SEARCH, NULL));
+
+ /* fail with ceiling too low */
+ cl_git_pass(git_buf_joinpath(&ceiling, ceiling.ptr, "sub"));
+ cl_git_fail(git_repository_open_ext(&repo, "attr/sub", 0, ceiling.ptr));
+
+ /* fail with no repo */
+ cl_git_pass(p_mkdir("alternate", 0777));
+ cl_git_pass(p_mkdir("alternate/.git", 0777));
+ cl_git_fail(git_repository_open_ext(&repo, "alternate", 0, NULL));
+ cl_git_fail(git_repository_open_ext(&repo, "alternate/.git", 0, NULL));
+
+ git_buf_free(&ceiling);
+}
+
+void test_repo_open__bad_gitlinks(void)
+{
+ git_repository *repo;
+ static const char *bad_links[] = {
+ "garbage\n", "gitdir", "gitdir:\n", "gitdir: foobar",
+ "gitdir: ../invalid", "gitdir: ../invalid2",
+ "gitdir: ../attr/.git with extra stuff",
+ NULL
+ };
+ const char **scan;
+
+ cl_git_sandbox_init("attr");
+
+ cl_git_pass(p_mkdir("invalid", 0777));
+ cl_git_pass(git_futils_mkdir_r("invalid2/.git", NULL, 0777));
+
+ for (scan = bad_links; *scan != NULL; scan++) {
+ make_gitlink_dir("alternate", *scan);
+ cl_git_fail(git_repository_open_ext(&repo, "alternate", 0, NULL));
+ }
+
+ git_futils_rmdir_r("invalid", NULL, GIT_RMDIR_REMOVE_FILES);
+ git_futils_rmdir_r("invalid2", NULL, GIT_RMDIR_REMOVE_FILES);
+}
+
+#ifdef GIT_WIN32
+static void unposix_path(git_buf *path)
+{
+ char *src, *tgt;
+
+ src = tgt = path->ptr;
+
+ /* convert "/d/..." to "d:\..." */
+ if (src[0] == '/' && isalpha(src[1]) && src[2] == '/') {
+ *tgt++ = src[1];
+ *tgt++ = ':';
+ *tgt++ = '\\';
+ src += 3;
+ }
+
+ while (*src) {
+ *tgt++ = (*src == '/') ? '\\' : *src;
+ src++;
+ }
+
+ *tgt = '\0';
+}
+#endif
+
+void test_repo_open__win32_path(void)
+{
+#ifdef GIT_WIN32
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo"), *repo2;
+ git_buf winpath = GIT_BUF_INIT;
+ static const char *repo_path = "empty_standard_repo/.git/";
+ static const char *repo_wd = "empty_standard_repo/";
+
+ cl_assert(git__suffixcmp(git_repository_path(repo), repo_path) == 0);
+ cl_assert(git__suffixcmp(git_repository_workdir(repo), repo_wd) == 0);
+
+ cl_git_pass(git_buf_sets(&winpath, git_repository_path(repo)));
+ unposix_path(&winpath);
+ cl_git_pass(git_repository_open(&repo2, winpath.ptr));
+ cl_assert(git__suffixcmp(git_repository_path(repo2), repo_path) == 0);
+ cl_assert(git__suffixcmp(git_repository_workdir(repo2), repo_wd) == 0);
+ git_repository_free(repo2);
+
+ cl_git_pass(git_buf_sets(&winpath, git_repository_path(repo)));
+ git_buf_truncate(&winpath, winpath.size - 1); /* remove trailing '/' */
+ unposix_path(&winpath);
+ cl_git_pass(git_repository_open(&repo2, winpath.ptr));
+ cl_assert(git__suffixcmp(git_repository_path(repo2), repo_path) == 0);
+ cl_assert(git__suffixcmp(git_repository_workdir(repo2), repo_wd) == 0);
+ git_repository_free(repo2);
+
+ cl_git_pass(git_buf_sets(&winpath, git_repository_workdir(repo)));
+ unposix_path(&winpath);
+ cl_git_pass(git_repository_open(&repo2, winpath.ptr));
+ cl_assert(git__suffixcmp(git_repository_path(repo2), repo_path) == 0);
+ cl_assert(git__suffixcmp(git_repository_workdir(repo2), repo_wd) == 0);
+ git_repository_free(repo2);
+
+ cl_git_pass(git_buf_sets(&winpath, git_repository_workdir(repo)));
+ git_buf_truncate(&winpath, winpath.size - 1); /* remove trailing '/' */
+ unposix_path(&winpath);
+ cl_git_pass(git_repository_open(&repo2, winpath.ptr));
+ cl_assert(git__suffixcmp(git_repository_path(repo2), repo_path) == 0);
+ cl_assert(git__suffixcmp(git_repository_workdir(repo2), repo_wd) == 0);
+ git_repository_free(repo2);
+
+ git_buf_free(&winpath);
+#endif
+}
+
+void test_repo_open__opening_a_non_existing_repository_returns_ENOTFOUND(void)
+{
+ git_repository *repo;
+ cl_assert_equal_i(GIT_ENOTFOUND, git_repository_open(&repo, "i-do-not/exist"));
+}
+
+void test_repo_open__no_config(void)
+{
+ git_buf path = GIT_BUF_INIT;
+ git_repository *repo;
+ git_config *config;
+
+ cl_fixture_sandbox("empty_standard_repo");
+ cl_git_pass(cl_rename("empty_standard_repo/.gitted", "empty_standard_repo/.git"));
+
+ /* remove local config */
+ cl_git_pass(git_futils_rmdir_r(
+ "empty_standard_repo/.git/config", NULL, GIT_RMDIR_REMOVE_FILES));
+
+ /* isolate from system level configs */
+ cl_must_pass(p_mkdir("alternate", 0777));
+ cl_git_pass(git_path_prettify(&path, "alternate", NULL));
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr));
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr));
+
+ git_buf_free(&path);
+
+ cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ cl_git_pass(git_repository_config(&config, repo));
+
+ cl_git_pass(git_config_set_string(config, "test.set", "42"));
+
+ git_config_free(config);
+ git_repository_free(repo);
+ cl_fixture_cleanup("empty_standard_repo");
+
+ git_futils_dirs_global_shutdown();
+}
+
+void test_repo_open__force_bare(void)
+{
+ /* need to have both repo dir and workdir set up correctly */
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+ git_repository *barerepo;
+
+ make_gitlink_dir("alternate", "gitdir: ../empty_standard_repo/.git");
+
+ cl_assert(!git_repository_is_bare(repo));
+
+ cl_git_pass(git_repository_open(&barerepo, "alternate"));
+ cl_assert(!git_repository_is_bare(barerepo));
+ git_repository_free(barerepo);
+
+ cl_git_pass(git_repository_open_bare(
+ &barerepo, "empty_standard_repo/.git"));
+ cl_assert(git_repository_is_bare(barerepo));
+ git_repository_free(barerepo);
+
+ cl_git_fail(git_repository_open_bare(&barerepo, "alternate/.git"));
+
+ cl_git_pass(git_repository_open_ext(
+ &barerepo, "alternate/.git", GIT_REPOSITORY_OPEN_BARE, NULL));
+ cl_assert(git_repository_is_bare(barerepo));
+ git_repository_free(barerepo);
+
+ cl_git_pass(p_mkdir("empty_standard_repo/subdir", 0777));
+ cl_git_mkfile("empty_standard_repo/subdir/something.txt", "something");
+
+ cl_git_fail(git_repository_open_bare(
+ &barerepo, "empty_standard_repo/subdir"));
+
+ cl_git_pass(git_repository_open_ext(
+ &barerepo, "empty_standard_repo/subdir", GIT_REPOSITORY_OPEN_BARE, NULL));
+ cl_assert(git_repository_is_bare(barerepo));
+ git_repository_free(barerepo);
+
+ cl_git_pass(p_mkdir("alternate/subdir", 0777));
+ cl_git_pass(p_mkdir("alternate/subdir/sub2", 0777));
+ cl_git_mkfile("alternate/subdir/sub2/something.txt", "something");
+
+ cl_git_fail(git_repository_open_bare(&barerepo, "alternate/subdir/sub2"));
+
+ cl_git_pass(git_repository_open_ext(
+ &barerepo, "alternate/subdir/sub2", GIT_REPOSITORY_OPEN_BARE, NULL));
+ cl_assert(git_repository_is_bare(barerepo));
+ git_repository_free(barerepo);
+}
diff --git a/tests/repo/pathspec.c b/tests/repo/pathspec.c
new file mode 100644
index 000000000..334066b67
--- /dev/null
+++ b/tests/repo/pathspec.c
@@ -0,0 +1,385 @@
+#include "clar_libgit2.h"
+#include "git2/pathspec.h"
+
+static git_repository *g_repo;
+
+void test_repo_pathspec__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("status");
+}
+
+void test_repo_pathspec__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ g_repo = NULL;
+}
+
+static char *str0[] = { "*_file", "new_file", "garbage" };
+static char *str1[] = { "*_FILE", "NEW_FILE", "GARBAGE" };
+static char *str2[] = { "staged_*" };
+static char *str3[] = { "!subdir", "*_file", "new_file" };
+static char *str4[] = { "*" };
+static char *str5[] = { "S*" };
+
+void test_repo_pathspec__workdir0(void)
+{
+ git_strarray s;
+ git_pathspec *ps;
+ git_pathspec_match_list *m;
+
+ /* { "*_file", "new_file", "garbage" } */
+ s.strings = str0; s.count = ARRAY_SIZE(str0);
+ cl_git_pass(git_pathspec_new(&ps, &s));
+
+ cl_git_pass(git_pathspec_match_workdir(&m, g_repo, 0, ps));
+ cl_assert_equal_sz(10, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ cl_git_pass(git_pathspec_match_workdir(&m, g_repo,
+ GIT_PATHSPEC_FIND_FAILURES, ps));
+ cl_assert_equal_sz(10, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_sz(1, git_pathspec_match_list_failed_entrycount(m));
+ cl_assert_equal_s("garbage", git_pathspec_match_list_failed_entry(m, 0));
+ git_pathspec_match_list_free(m);
+
+ cl_git_pass(git_pathspec_match_workdir(&m, g_repo,
+ GIT_PATHSPEC_FIND_FAILURES | GIT_PATHSPEC_FAILURES_ONLY, ps));
+ cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_sz(1, git_pathspec_match_list_failed_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ git_pathspec_free(ps);
+}
+
+void test_repo_pathspec__workdir1(void)
+{
+ git_strarray s;
+ git_pathspec *ps;
+ git_pathspec_match_list *m;
+
+ /* { "*_FILE", "NEW_FILE", "GARBAGE" } */
+ s.strings = str1; s.count = ARRAY_SIZE(str1);
+ cl_git_pass(git_pathspec_new(&ps, &s));
+
+ cl_git_pass(git_pathspec_match_workdir(&m, g_repo,
+ GIT_PATHSPEC_IGNORE_CASE, ps));
+ cl_assert_equal_sz(10, git_pathspec_match_list_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ cl_git_pass(git_pathspec_match_workdir(&m, g_repo,
+ GIT_PATHSPEC_USE_CASE, ps));
+ cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ cl_git_fail(git_pathspec_match_workdir(&m, g_repo,
+ GIT_PATHSPEC_USE_CASE | GIT_PATHSPEC_NO_MATCH_ERROR, ps));
+
+ cl_git_pass(git_pathspec_match_workdir(&m, g_repo,
+ GIT_PATHSPEC_IGNORE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps));
+ cl_assert_equal_sz(10, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_sz(1, git_pathspec_match_list_failed_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ cl_git_pass(git_pathspec_match_workdir(&m, g_repo,
+ GIT_PATHSPEC_USE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps));
+ cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_sz(3, git_pathspec_match_list_failed_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ git_pathspec_free(ps);
+}
+
+void test_repo_pathspec__workdir2(void)
+{
+ git_strarray s;
+ git_pathspec *ps;
+ git_pathspec_match_list *m;
+
+ /* { "staged_*" } */
+ s.strings = str2; s.count = ARRAY_SIZE(str2);
+ cl_git_pass(git_pathspec_new(&ps, &s));
+
+ cl_git_pass(git_pathspec_match_workdir(&m, g_repo, 0, ps));
+ cl_assert_equal_sz(5, git_pathspec_match_list_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ cl_git_pass(git_pathspec_match_workdir(&m, g_repo,
+ GIT_PATHSPEC_FIND_FAILURES, ps));
+ cl_assert_equal_sz(5, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ cl_git_fail(git_pathspec_match_workdir(&m, g_repo,
+ GIT_PATHSPEC_NO_GLOB | GIT_PATHSPEC_NO_MATCH_ERROR, ps));
+
+ cl_git_pass(git_pathspec_match_workdir(&m, g_repo,
+ GIT_PATHSPEC_NO_GLOB | GIT_PATHSPEC_FIND_FAILURES, ps));
+ cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_sz(1, git_pathspec_match_list_failed_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ git_pathspec_free(ps);
+}
+
+void test_repo_pathspec__workdir3(void)
+{
+ git_strarray s;
+ git_pathspec *ps;
+ git_pathspec_match_list *m;
+
+ /* { "!subdir", "*_file", "new_file" } */
+ s.strings = str3; s.count = ARRAY_SIZE(str3);
+ cl_git_pass(git_pathspec_new(&ps, &s));
+
+ cl_git_pass(git_pathspec_match_workdir(&m, g_repo, 0, ps));
+ cl_assert_equal_sz(7, git_pathspec_match_list_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ cl_git_pass(git_pathspec_match_workdir(&m, g_repo,
+ GIT_PATHSPEC_FIND_FAILURES, ps));
+ cl_assert_equal_sz(7, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m));
+
+ cl_assert_equal_s("current_file", git_pathspec_match_list_entry(m, 0));
+ cl_assert_equal_s("modified_file", git_pathspec_match_list_entry(m, 1));
+ cl_assert_equal_s("new_file", git_pathspec_match_list_entry(m, 2));
+ cl_assert_equal_s("staged_changes_modified_file", git_pathspec_match_list_entry(m, 3));
+ cl_assert_equal_s("staged_delete_modified_file", git_pathspec_match_list_entry(m, 4));
+ cl_assert_equal_s("staged_new_file", git_pathspec_match_list_entry(m, 5));
+ cl_assert_equal_s("staged_new_file_modified_file", git_pathspec_match_list_entry(m, 6));
+ cl_assert_equal_s(NULL, git_pathspec_match_list_entry(m, 7));
+
+ git_pathspec_match_list_free(m);
+
+ git_pathspec_free(ps);
+}
+
+void test_repo_pathspec__workdir4(void)
+{
+ git_strarray s;
+ git_pathspec *ps;
+ git_pathspec_match_list *m;
+
+ /* { "*" } */
+ s.strings = str4; s.count = ARRAY_SIZE(str4);
+ cl_git_pass(git_pathspec_new(&ps, &s));
+
+ cl_git_pass(git_pathspec_match_workdir(&m, g_repo, 0, ps));
+ cl_assert_equal_sz(13, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_s("è¿™", git_pathspec_match_list_entry(m, 12));
+ git_pathspec_match_list_free(m);
+
+ git_pathspec_free(ps);
+}
+
+
+void test_repo_pathspec__index0(void)
+{
+ git_index *idx;
+ git_strarray s;
+ git_pathspec *ps;
+ git_pathspec_match_list *m;
+
+ cl_git_pass(git_repository_index(&idx, g_repo));
+
+ /* { "*_file", "new_file", "garbage" } */
+ s.strings = str0; s.count = ARRAY_SIZE(str0);
+ cl_git_pass(git_pathspec_new(&ps, &s));
+
+ cl_git_pass(git_pathspec_match_index(&m, idx, 0, ps));
+ cl_assert_equal_sz(9, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m));
+ cl_assert_equal_s("current_file", git_pathspec_match_list_entry(m, 0));
+ cl_assert_equal_s("modified_file", git_pathspec_match_list_entry(m, 1));
+ cl_assert_equal_s("staged_changes_modified_file", git_pathspec_match_list_entry(m, 2));
+ cl_assert_equal_s("staged_new_file", git_pathspec_match_list_entry(m, 3));
+ cl_assert_equal_s("staged_new_file_deleted_file", git_pathspec_match_list_entry(m, 4));
+ cl_assert_equal_s("staged_new_file_modified_file", git_pathspec_match_list_entry(m, 5));
+ cl_assert_equal_s("subdir/current_file", git_pathspec_match_list_entry(m, 6));
+ cl_assert_equal_s("subdir/deleted_file", git_pathspec_match_list_entry(m, 7));
+ cl_assert_equal_s("subdir/modified_file", git_pathspec_match_list_entry(m, 8));
+ cl_assert_equal_s(NULL, git_pathspec_match_list_entry(m, 9));
+ git_pathspec_match_list_free(m);
+
+ cl_git_pass(git_pathspec_match_index(&m, idx,
+ GIT_PATHSPEC_FIND_FAILURES, ps));
+ cl_assert_equal_sz(9, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_sz(2, git_pathspec_match_list_failed_entrycount(m));
+ cl_assert_equal_s("new_file", git_pathspec_match_list_failed_entry(m, 0));
+ cl_assert_equal_s("garbage", git_pathspec_match_list_failed_entry(m, 1));
+ cl_assert_equal_s(NULL, git_pathspec_match_list_failed_entry(m, 2));
+ git_pathspec_match_list_free(m);
+
+ git_pathspec_free(ps);
+ git_index_free(idx);
+}
+
+void test_repo_pathspec__index1(void)
+{
+ /* Currently the USE_CASE and IGNORE_CASE flags don't work on the
+ * index because the index sort order for the index iterator is
+ * set by the index itself. I think the correct fix is for the
+ * index not to embed a global sort order but to support traversal
+ * in either case sensitive or insensitive order in a stateless
+ * manner.
+ *
+ * Anyhow, as it is, there is no point in doing this test.
+ */
+#if 0
+ git_index *idx;
+ git_strarray s;
+ git_pathspec *ps;
+ git_pathspec_match_list *m;
+
+ cl_git_pass(git_repository_index(&idx, g_repo));
+
+ /* { "*_FILE", "NEW_FILE", "GARBAGE" } */
+ s.strings = str1; s.count = ARRAY_SIZE(str1);
+ cl_git_pass(git_pathspec_new(&ps, &s));
+
+ cl_git_pass(git_pathspec_match_index(&m, idx,
+ GIT_PATHSPEC_USE_CASE, ps));
+ cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ cl_git_pass(git_pathspec_match_index(&m, idx,
+ GIT_PATHSPEC_USE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps));
+ cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_sz(3, git_pathspec_match_list_failed_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ cl_git_pass(git_pathspec_match_index(&m, idx,
+ GIT_PATHSPEC_IGNORE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps));
+ cl_assert_equal_sz(10, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_sz(2, git_pathspec_match_list_failed_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ git_pathspec_free(ps);
+ git_index_free(idx);
+#endif
+}
+
+void test_repo_pathspec__tree0(void)
+{
+ git_object *tree;
+ git_strarray s;
+ git_pathspec *ps;
+ git_pathspec_match_list *m;
+
+ /* { "*_file", "new_file", "garbage" } */
+ s.strings = str0; s.count = ARRAY_SIZE(str0);
+ cl_git_pass(git_pathspec_new(&ps, &s));
+
+ cl_git_pass(git_revparse_single(&tree, g_repo, "HEAD~2^{tree}"));
+
+ cl_git_pass(git_pathspec_match_tree(&m, (git_tree *)tree,
+ GIT_PATHSPEC_FIND_FAILURES, ps));
+ cl_assert_equal_sz(4, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_s("current_file", git_pathspec_match_list_entry(m, 0));
+ cl_assert_equal_s("modified_file", git_pathspec_match_list_entry(m, 1));
+ cl_assert_equal_s("staged_changes_modified_file", git_pathspec_match_list_entry(m, 2));
+ cl_assert_equal_s("staged_delete_modified_file", git_pathspec_match_list_entry(m, 3));
+ cl_assert_equal_s(NULL, git_pathspec_match_list_entry(m, 4));
+ cl_assert_equal_sz(2, git_pathspec_match_list_failed_entrycount(m));
+ cl_assert_equal_s("new_file", git_pathspec_match_list_failed_entry(m, 0));
+ cl_assert_equal_s("garbage", git_pathspec_match_list_failed_entry(m, 1));
+ cl_assert_equal_s(NULL, git_pathspec_match_list_failed_entry(m, 2));
+ git_pathspec_match_list_free(m);
+
+ git_object_free(tree);
+
+ cl_git_pass(git_revparse_single(&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(git_pathspec_match_tree(&m, (git_tree *)tree,
+ GIT_PATHSPEC_FIND_FAILURES, ps));
+ cl_assert_equal_sz(7, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_s("current_file", git_pathspec_match_list_entry(m, 0));
+ cl_assert_equal_s("modified_file", git_pathspec_match_list_entry(m, 1));
+ cl_assert_equal_s("staged_changes_modified_file", git_pathspec_match_list_entry(m, 2));
+ cl_assert_equal_s("staged_delete_modified_file", git_pathspec_match_list_entry(m, 3));
+ cl_assert_equal_s("subdir/current_file", git_pathspec_match_list_entry(m, 4));
+ cl_assert_equal_s("subdir/deleted_file", git_pathspec_match_list_entry(m, 5));
+ cl_assert_equal_s("subdir/modified_file", git_pathspec_match_list_entry(m, 6));
+ cl_assert_equal_s(NULL, git_pathspec_match_list_entry(m, 7));
+ cl_assert_equal_sz(2, git_pathspec_match_list_failed_entrycount(m));
+ cl_assert_equal_s("new_file", git_pathspec_match_list_failed_entry(m, 0));
+ cl_assert_equal_s("garbage", git_pathspec_match_list_failed_entry(m, 1));
+ cl_assert_equal_s(NULL, git_pathspec_match_list_failed_entry(m, 2));
+ git_pathspec_match_list_free(m);
+
+ git_object_free(tree);
+
+ git_pathspec_free(ps);
+}
+
+void test_repo_pathspec__tree5(void)
+{
+ git_object *tree;
+ git_strarray s;
+ git_pathspec *ps;
+ git_pathspec_match_list *m;
+
+ /* { "S*" } */
+ s.strings = str5; s.count = ARRAY_SIZE(str5);
+ cl_git_pass(git_pathspec_new(&ps, &s));
+
+ cl_git_pass(git_revparse_single(&tree, g_repo, "HEAD~2^{tree}"));
+
+ cl_git_pass(git_pathspec_match_tree(&m, (git_tree *)tree,
+ GIT_PATHSPEC_USE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps));
+ cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_sz(1, git_pathspec_match_list_failed_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ cl_git_pass(git_pathspec_match_tree(&m, (git_tree *)tree,
+ GIT_PATHSPEC_IGNORE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps));
+ cl_assert_equal_sz(5, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_s("staged_changes", git_pathspec_match_list_entry(m, 0));
+ cl_assert_equal_s("staged_delete_modified_file", git_pathspec_match_list_entry(m, 4));
+ cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ git_object_free(tree);
+
+ cl_git_pass(git_revparse_single(&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(git_pathspec_match_tree(&m, (git_tree *)tree,
+ GIT_PATHSPEC_IGNORE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps));
+ cl_assert_equal_sz(9, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_s("staged_changes", git_pathspec_match_list_entry(m, 0));
+ cl_assert_equal_s("subdir.txt", git_pathspec_match_list_entry(m, 5));
+ cl_assert_equal_s("subdir/current_file", git_pathspec_match_list_entry(m, 6));
+ cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ git_object_free(tree);
+
+ git_pathspec_free(ps);
+}
+
+void test_repo_pathspec__in_memory(void)
+{
+ static char *strings[] = { "one", "two*", "!three*", "*four" };
+ git_strarray s = { strings, ARRAY_SIZE(strings) };
+ git_pathspec *ps;
+
+ cl_git_pass(git_pathspec_new(&ps, &s));
+
+ cl_assert(git_pathspec_matches_path(ps, 0, "one"));
+ cl_assert(!git_pathspec_matches_path(ps, 0, "ONE"));
+ cl_assert(git_pathspec_matches_path(ps, GIT_PATHSPEC_IGNORE_CASE, "ONE"));
+ cl_assert(git_pathspec_matches_path(ps, 0, "two"));
+ cl_assert(git_pathspec_matches_path(ps, 0, "two.txt"));
+ cl_assert(!git_pathspec_matches_path(ps, 0, "three.txt"));
+ cl_assert(git_pathspec_matches_path(ps, 0, "anything.four"));
+ cl_assert(!git_pathspec_matches_path(ps, 0, "three.four"));
+ cl_assert(!git_pathspec_matches_path(ps, 0, "nomatch"));
+ cl_assert(!git_pathspec_matches_path(ps, GIT_PATHSPEC_NO_GLOB, "two"));
+ cl_assert(git_pathspec_matches_path(ps, GIT_PATHSPEC_NO_GLOB, "two*"));
+ cl_assert(!git_pathspec_matches_path(ps, GIT_PATHSPEC_NO_GLOB, "anyfour"));
+ cl_assert(git_pathspec_matches_path(ps, GIT_PATHSPEC_NO_GLOB, "*four"));
+
+ git_pathspec_free(ps);
+}
diff --git a/tests/repo/repo_helpers.c b/tests/repo/repo_helpers.c
new file mode 100644
index 000000000..3d477ff42
--- /dev/null
+++ b/tests/repo/repo_helpers.c
@@ -0,0 +1,22 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+#include "repo_helpers.h"
+#include "posix.h"
+
+void make_head_unborn(git_repository* repo, const char *target)
+{
+ git_reference *head;
+
+ cl_git_pass(git_reference_symbolic_create(&head, repo, GIT_HEAD_FILE, target, 1));
+ git_reference_free(head);
+}
+
+void delete_head(git_repository* repo)
+{
+ git_buf head_path = GIT_BUF_INIT;
+
+ cl_git_pass(git_buf_joinpath(&head_path, git_repository_path(repo), GIT_HEAD_FILE));
+ cl_git_pass(p_unlink(git_buf_cstr(&head_path)));
+
+ git_buf_free(&head_path);
+}
diff --git a/tests/repo/repo_helpers.h b/tests/repo/repo_helpers.h
new file mode 100644
index 000000000..6783d5701
--- /dev/null
+++ b/tests/repo/repo_helpers.h
@@ -0,0 +1,6 @@
+#include "common.h"
+
+#define NON_EXISTING_HEAD "refs/heads/hide/and/seek"
+
+extern void make_head_unborn(git_repository* repo, const char *target);
+extern void delete_head(git_repository* repo);
diff --git a/tests-clar/repo/setters.c b/tests/repo/setters.c
index f34f1e471..f34f1e471 100644
--- a/tests-clar/repo/setters.c
+++ b/tests/repo/setters.c
diff --git a/tests-clar/repo/shallow.c b/tests/repo/shallow.c
index 1cc66ae40..1cc66ae40 100644
--- a/tests-clar/repo/shallow.c
+++ b/tests/repo/shallow.c
diff --git a/tests-clar/repo/state.c b/tests/repo/state.c
index 5a0a5f360..5a0a5f360 100644
--- a/tests-clar/repo/state.c
+++ b/tests/repo/state.c
diff --git a/tests-clar/reset/default.c b/tests/reset/default.c
index e29e63550..e29e63550 100644
--- a/tests-clar/reset/default.c
+++ b/tests/reset/default.c
diff --git a/tests-clar/reset/hard.c b/tests/reset/hard.c
index 1c0c84135..1c0c84135 100644
--- a/tests-clar/reset/hard.c
+++ b/tests/reset/hard.c
diff --git a/tests-clar/reset/mixed.c b/tests/reset/mixed.c
index 7b90c23f1..7b90c23f1 100644
--- a/tests-clar/reset/mixed.c
+++ b/tests/reset/mixed.c
diff --git a/tests-clar/reset/reset_helpers.c b/tests/reset/reset_helpers.c
index 17edca4e9..17edca4e9 100644
--- a/tests-clar/reset/reset_helpers.c
+++ b/tests/reset/reset_helpers.c
diff --git a/tests-clar/reset/reset_helpers.h b/tests/reset/reset_helpers.h
index 5dbe9d2c7..5dbe9d2c7 100644
--- a/tests-clar/reset/reset_helpers.h
+++ b/tests/reset/reset_helpers.h
diff --git a/tests/reset/soft.c b/tests/reset/soft.c
new file mode 100644
index 000000000..bd6fcc205
--- /dev/null
+++ b/tests/reset/soft.c
@@ -0,0 +1,157 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "reset_helpers.h"
+#include "path.h"
+#include "repo/repo_helpers.h"
+
+static git_repository *repo;
+static git_object *target;
+
+void test_reset_soft__initialize(void)
+{
+ repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_reset_soft__cleanup(void)
+{
+ git_object_free(target);
+ target = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+static void assert_reset_soft(bool should_be_detached)
+{
+ git_oid oid;
+
+ cl_git_pass(git_reference_name_to_id(&oid, repo, "HEAD"));
+ cl_git_fail(git_oid_streq(&oid, KNOWN_COMMIT_IN_BARE_REPO));
+
+ retrieve_target_from_oid(&target, repo, KNOWN_COMMIT_IN_BARE_REPO);
+
+ cl_assert(git_repository_head_detached(repo) == should_be_detached);
+
+ cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT));
+
+ cl_assert(git_repository_head_detached(repo) == should_be_detached);
+
+ cl_git_pass(git_reference_name_to_id(&oid, repo, "HEAD"));
+ cl_git_pass(git_oid_streq(&oid, KNOWN_COMMIT_IN_BARE_REPO));
+}
+
+void test_reset_soft__can_reset_the_non_detached_Head_to_the_specified_commit(void)
+{
+ assert_reset_soft(false);
+}
+
+void test_reset_soft__can_reset_the_detached_Head_to_the_specified_commit(void)
+{
+ git_repository_detach_head(repo);
+
+ assert_reset_soft(true);
+}
+
+void test_reset_soft__resetting_to_the_commit_pointed_at_by_the_Head_does_not_change_the_target_of_the_Head(void)
+{
+ git_oid oid;
+ char raw_head_oid[GIT_OID_HEXSZ + 1];
+
+ cl_git_pass(git_reference_name_to_id(&oid, repo, "HEAD"));
+ git_oid_fmt(raw_head_oid, &oid);
+ raw_head_oid[GIT_OID_HEXSZ] = '\0';
+
+ retrieve_target_from_oid(&target, repo, raw_head_oid);
+
+ cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT));
+
+ cl_git_pass(git_reference_name_to_id(&oid, repo, "HEAD"));
+ cl_git_pass(git_oid_streq(&oid, raw_head_oid));
+}
+
+void test_reset_soft__resetting_to_a_tag_sets_the_Head_to_the_peeled_commit(void)
+{
+ git_oid oid;
+
+ /* b25fa35 is a tag, pointing to another tag which points to commit e90810b */
+ retrieve_target_from_oid(&target, repo, "b25fa35b38051e4ae45d4222e795f9df2e43f1d1");
+
+ cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT));
+
+ cl_assert(git_repository_head_detached(repo) == false);
+ cl_git_pass(git_reference_name_to_id(&oid, repo, "HEAD"));
+ cl_git_pass(git_oid_streq(&oid, KNOWN_COMMIT_IN_BARE_REPO));
+}
+
+void test_reset_soft__cannot_reset_to_a_tag_not_pointing_at_a_commit(void)
+{
+ /* 53fc32d is the tree of commit e90810b */
+ retrieve_target_from_oid(&target, repo, "53fc32d17276939fc79ed05badaef2db09990016");
+
+ cl_git_fail(git_reset(repo, target, GIT_RESET_SOFT));
+ git_object_free(target);
+
+ /* 521d87c is an annotated tag pointing to a blob */
+ retrieve_target_from_oid(&target, repo, "521d87c1ec3aef9824daf6d96cc0ae3710766d91");
+ cl_git_fail(git_reset(repo, target, GIT_RESET_SOFT));
+}
+
+void test_reset_soft__resetting_against_an_unborn_head_repo_makes_the_head_no_longer_unborn(void)
+{
+ git_reference *head;
+
+ retrieve_target_from_oid(&target, repo, KNOWN_COMMIT_IN_BARE_REPO);
+
+ make_head_unborn(repo, NON_EXISTING_HEAD);
+
+ cl_assert_equal_i(true, git_repository_head_unborn(repo));
+
+ cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT));
+
+ cl_assert_equal_i(false, git_repository_head_unborn(repo));
+
+ cl_git_pass(git_reference_lookup(&head, repo, NON_EXISTING_HEAD));
+ cl_assert_equal_i(0, git_oid_streq(git_reference_target(head), KNOWN_COMMIT_IN_BARE_REPO));
+
+ git_reference_free(head);
+}
+
+void test_reset_soft__fails_when_merging(void)
+{
+ git_buf merge_head_path = GIT_BUF_INIT;
+
+ cl_git_pass(git_repository_detach_head(repo));
+ cl_git_pass(git_buf_joinpath(&merge_head_path, git_repository_path(repo), "MERGE_HEAD"));
+ cl_git_mkfile(git_buf_cstr(&merge_head_path), "beefbeefbeefbeefbeefbeefbeefbeefbeefbeef\n");
+
+ retrieve_target_from_oid(&target, repo, KNOWN_COMMIT_IN_BARE_REPO);
+
+ cl_assert_equal_i(GIT_EUNMERGED, git_reset(repo, target, GIT_RESET_SOFT));
+ cl_git_pass(p_unlink(git_buf_cstr(&merge_head_path)));
+
+ git_buf_free(&merge_head_path);
+}
+
+void test_reset_soft__fails_when_index_contains_conflicts_independently_of_MERGE_HEAD_file_existence(void)
+{
+ git_index *index;
+ git_reference *head;
+ git_buf merge_head_path = GIT_BUF_INIT;
+
+ cl_git_sandbox_cleanup();
+
+ repo = cl_git_sandbox_init("mergedrepo");
+
+ cl_git_pass(git_buf_joinpath(&merge_head_path, git_repository_path(repo), "MERGE_HEAD"));
+ cl_git_pass(p_unlink(git_buf_cstr(&merge_head_path)));
+ git_buf_free(&merge_head_path);
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert_equal_i(true, git_index_has_conflicts(index));
+ git_index_free(index);
+
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_peel(&target, head, GIT_OBJ_COMMIT));
+ git_reference_free(head);
+
+ cl_assert_equal_i(GIT_EUNMERGED, git_reset(repo, target, GIT_RESET_SOFT));
+}
diff --git a/tests-clar/resources/.gitattributes b/tests/resources/.gitattributes
index 556f8c827..556f8c827 100644
--- a/tests-clar/resources/.gitattributes
+++ b/tests/resources/.gitattributes
diff --git a/tests-clar/resources/.gitignore b/tests/resources/.gitignore
index 43a19cc9d..43a19cc9d 100644
--- a/tests-clar/resources/.gitignore
+++ b/tests/resources/.gitignore
diff --git a/tests-clar/resources/attr/.gitted/HEAD b/tests/resources/attr/.gitted/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/attr/.gitted/HEAD
+++ b/tests/resources/attr/.gitted/HEAD
diff --git a/tests-clar/resources/attr/.gitted/config b/tests/resources/attr/.gitted/config
index af107929f..af107929f 100644
--- a/tests-clar/resources/attr/.gitted/config
+++ b/tests/resources/attr/.gitted/config
diff --git a/tests-clar/resources/attr/.gitted/description b/tests/resources/attr/.gitted/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/attr/.gitted/description
+++ b/tests/resources/attr/.gitted/description
diff --git a/tests-clar/resources/attr/.gitted/index b/tests/resources/attr/.gitted/index
index 439ffb151..439ffb151 100644
--- a/tests-clar/resources/attr/.gitted/index
+++ b/tests/resources/attr/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/info/attributes b/tests/resources/attr/.gitted/info/attributes
index 5fe62a37a..5fe62a37a 100644
--- a/tests-clar/resources/attr/.gitted/info/attributes
+++ b/tests/resources/attr/.gitted/info/attributes
diff --git a/tests-clar/resources/attr/.gitted/info/exclude b/tests/resources/attr/.gitted/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/attr/.gitted/info/exclude
+++ b/tests/resources/attr/.gitted/info/exclude
diff --git a/tests-clar/resources/attr/.gitted/logs/HEAD b/tests/resources/attr/.gitted/logs/HEAD
index 8ece39f37..8ece39f37 100644
--- a/tests-clar/resources/attr/.gitted/logs/HEAD
+++ b/tests/resources/attr/.gitted/logs/HEAD
diff --git a/tests-clar/resources/attr/.gitted/logs/refs/heads/master b/tests/resources/attr/.gitted/logs/refs/heads/master
index 8ece39f37..8ece39f37 100644
--- a/tests-clar/resources/attr/.gitted/logs/refs/heads/master
+++ b/tests/resources/attr/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/attr/.gitted/objects/10/8bb4e7fd7b16490dc33ff7d972151e73d7166e b/tests/resources/attr/.gitted/objects/10/8bb4e7fd7b16490dc33ff7d972151e73d7166e
index edcf7520c..edcf7520c 100644
--- a/tests-clar/resources/attr/.gitted/objects/10/8bb4e7fd7b16490dc33ff7d972151e73d7166e
+++ b/tests/resources/attr/.gitted/objects/10/8bb4e7fd7b16490dc33ff7d972151e73d7166e
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/16/983da6643656bb44c43965ecb6855c6d574512 b/tests/resources/attr/.gitted/objects/16/983da6643656bb44c43965ecb6855c6d574512
index e49c94acd..e49c94acd 100644
--- a/tests-clar/resources/attr/.gitted/objects/16/983da6643656bb44c43965ecb6855c6d574512
+++ b/tests/resources/attr/.gitted/objects/16/983da6643656bb44c43965ecb6855c6d574512
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/21/7878ab49e1314388ea2e32dc6fdb58a1b969e0 b/tests/resources/attr/.gitted/objects/21/7878ab49e1314388ea2e32dc6fdb58a1b969e0
index b537899f2..b537899f2 100644
--- a/tests-clar/resources/attr/.gitted/objects/21/7878ab49e1314388ea2e32dc6fdb58a1b969e0
+++ b/tests/resources/attr/.gitted/objects/21/7878ab49e1314388ea2e32dc6fdb58a1b969e0
diff --git a/tests-clar/resources/attr/.gitted/objects/24/fa9a9fc4e202313e24b648087495441dab432b b/tests/resources/attr/.gitted/objects/24/fa9a9fc4e202313e24b648087495441dab432b
index e7099bbaa..e7099bbaa 100644
--- a/tests-clar/resources/attr/.gitted/objects/24/fa9a9fc4e202313e24b648087495441dab432b
+++ b/tests/resources/attr/.gitted/objects/24/fa9a9fc4e202313e24b648087495441dab432b
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/29/29de282ce999e95183aedac6451d3384559c4b b/tests/resources/attr/.gitted/objects/29/29de282ce999e95183aedac6451d3384559c4b
index ad84f0854..ad84f0854 100644
--- a/tests-clar/resources/attr/.gitted/objects/29/29de282ce999e95183aedac6451d3384559c4b
+++ b/tests/resources/attr/.gitted/objects/29/29de282ce999e95183aedac6451d3384559c4b
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/2b/40c5aca159b04ea8d20ffe36cdf8b09369b14a b/tests/resources/attr/.gitted/objects/2b/40c5aca159b04ea8d20ffe36cdf8b09369b14a
index 0e2368069..0e2368069 100644
--- a/tests-clar/resources/attr/.gitted/objects/2b/40c5aca159b04ea8d20ffe36cdf8b09369b14a
+++ b/tests/resources/attr/.gitted/objects/2b/40c5aca159b04ea8d20ffe36cdf8b09369b14a
diff --git a/tests-clar/resources/attr/.gitted/objects/2c/66e14f77196ea763fb1e41612c1aa2bc2d8ed2 b/tests/resources/attr/.gitted/objects/2c/66e14f77196ea763fb1e41612c1aa2bc2d8ed2
index 4b75d50eb..4b75d50eb 100644
--- a/tests-clar/resources/attr/.gitted/objects/2c/66e14f77196ea763fb1e41612c1aa2bc2d8ed2
+++ b/tests/resources/attr/.gitted/objects/2c/66e14f77196ea763fb1e41612c1aa2bc2d8ed2
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/2d/e7dfe3588f3c7e9ad59e7d50ba90e3329df9d9 b/tests/resources/attr/.gitted/objects/2d/e7dfe3588f3c7e9ad59e7d50ba90e3329df9d9
index e0fd0468e..e0fd0468e 100644
--- a/tests-clar/resources/attr/.gitted/objects/2d/e7dfe3588f3c7e9ad59e7d50ba90e3329df9d9
+++ b/tests/resources/attr/.gitted/objects/2d/e7dfe3588f3c7e9ad59e7d50ba90e3329df9d9
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/37/0fe9ec224ce33e71f9e5ec2bd1142ce9937a6a b/tests/resources/attr/.gitted/objects/37/0fe9ec224ce33e71f9e5ec2bd1142ce9937a6a
index 9c37c5946..9c37c5946 100644
--- a/tests-clar/resources/attr/.gitted/objects/37/0fe9ec224ce33e71f9e5ec2bd1142ce9937a6a
+++ b/tests/resources/attr/.gitted/objects/37/0fe9ec224ce33e71f9e5ec2bd1142ce9937a6a
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/3a/6df026462ebafe455af9867d27eda20a9e0974 b/tests/resources/attr/.gitted/objects/3a/6df026462ebafe455af9867d27eda20a9e0974
index c74add826..c74add826 100644
--- a/tests-clar/resources/attr/.gitted/objects/3a/6df026462ebafe455af9867d27eda20a9e0974
+++ b/tests/resources/attr/.gitted/objects/3a/6df026462ebafe455af9867d27eda20a9e0974
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/3b/74db7ab381105dc0d28f8295a77f6a82989292 b/tests/resources/attr/.gitted/objects/3b/74db7ab381105dc0d28f8295a77f6a82989292
index e5cef35fa..e5cef35fa 100644
--- a/tests-clar/resources/attr/.gitted/objects/3b/74db7ab381105dc0d28f8295a77f6a82989292
+++ b/tests/resources/attr/.gitted/objects/3b/74db7ab381105dc0d28f8295a77f6a82989292
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/3e/42ffc54a663f9401cc25843d6c0e71a33e4249 b/tests/resources/attr/.gitted/objects/3e/42ffc54a663f9401cc25843d6c0e71a33e4249
index 091d79b14..091d79b14 100644
--- a/tests-clar/resources/attr/.gitted/objects/3e/42ffc54a663f9401cc25843d6c0e71a33e4249
+++ b/tests/resources/attr/.gitted/objects/3e/42ffc54a663f9401cc25843d6c0e71a33e4249
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/45/141a79a77842c59a63229403220a4e4be74e3d b/tests/resources/attr/.gitted/objects/45/141a79a77842c59a63229403220a4e4be74e3d
index 5b58ef024..5b58ef024 100644
--- a/tests-clar/resources/attr/.gitted/objects/45/141a79a77842c59a63229403220a4e4be74e3d
+++ b/tests/resources/attr/.gitted/objects/45/141a79a77842c59a63229403220a4e4be74e3d
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/45/5a314fa848d52ae1f11d254da4f60858fc97f4 b/tests/resources/attr/.gitted/objects/45/5a314fa848d52ae1f11d254da4f60858fc97f4
index f90f0d79c..f90f0d79c 100644
--- a/tests-clar/resources/attr/.gitted/objects/45/5a314fa848d52ae1f11d254da4f60858fc97f4
+++ b/tests/resources/attr/.gitted/objects/45/5a314fa848d52ae1f11d254da4f60858fc97f4
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/attr/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
index 7ca4ceed5..7ca4ceed5 100644
--- a/tests-clar/resources/attr/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
+++ b/tests/resources/attr/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/4d/713dc48e6b1bd75b0d61ad078ba9ca3a56745d b/tests/resources/attr/.gitted/objects/4d/713dc48e6b1bd75b0d61ad078ba9ca3a56745d
index eb1e8d0c5..eb1e8d0c5 100644
--- a/tests-clar/resources/attr/.gitted/objects/4d/713dc48e6b1bd75b0d61ad078ba9ca3a56745d
+++ b/tests/resources/attr/.gitted/objects/4d/713dc48e6b1bd75b0d61ad078ba9ca3a56745d
diff --git a/tests-clar/resources/attr/.gitted/objects/4e/49ba8c5b6c32ff28cd9dcb60be34df50fcc485 b/tests/resources/attr/.gitted/objects/4e/49ba8c5b6c32ff28cd9dcb60be34df50fcc485
index 6fcc549b4..6fcc549b4 100644
--- a/tests-clar/resources/attr/.gitted/objects/4e/49ba8c5b6c32ff28cd9dcb60be34df50fcc485
+++ b/tests/resources/attr/.gitted/objects/4e/49ba8c5b6c32ff28cd9dcb60be34df50fcc485
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/55/6f8c827b8e4a02ad5cab77dca2bcb3e226b0b3 b/tests/resources/attr/.gitted/objects/55/6f8c827b8e4a02ad5cab77dca2bcb3e226b0b3
index 4bcff1faa..4bcff1faa 100644
--- a/tests-clar/resources/attr/.gitted/objects/55/6f8c827b8e4a02ad5cab77dca2bcb3e226b0b3
+++ b/tests/resources/attr/.gitted/objects/55/6f8c827b8e4a02ad5cab77dca2bcb3e226b0b3
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/58/19a185d77b03325aaf87cafc771db36f6ddca7 b/tests/resources/attr/.gitted/objects/58/19a185d77b03325aaf87cafc771db36f6ddca7
index fe34eb63a..fe34eb63a 100644
--- a/tests-clar/resources/attr/.gitted/objects/58/19a185d77b03325aaf87cafc771db36f6ddca7
+++ b/tests/resources/attr/.gitted/objects/58/19a185d77b03325aaf87cafc771db36f6ddca7
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/60/5812ab7fe421fdd325a935d35cb06a9234a7d7 b/tests/resources/attr/.gitted/objects/60/5812ab7fe421fdd325a935d35cb06a9234a7d7
index b0cc51ee6..b0cc51ee6 100644
--- a/tests-clar/resources/attr/.gitted/objects/60/5812ab7fe421fdd325a935d35cb06a9234a7d7
+++ b/tests/resources/attr/.gitted/objects/60/5812ab7fe421fdd325a935d35cb06a9234a7d7
diff --git a/tests-clar/resources/attr/.gitted/objects/6b/ab5c79cd5140d0f800917f550eb2a3dc32b0da b/tests/resources/attr/.gitted/objects/6b/ab5c79cd5140d0f800917f550eb2a3dc32b0da
index f51e11ccc..f51e11ccc 100644
--- a/tests-clar/resources/attr/.gitted/objects/6b/ab5c79cd5140d0f800917f550eb2a3dc32b0da
+++ b/tests/resources/attr/.gitted/objects/6b/ab5c79cd5140d0f800917f550eb2a3dc32b0da
diff --git a/tests-clar/resources/attr/.gitted/objects/6d/968d62c89c7d9ea23a4c9a7b665d017c3d8ffd b/tests/resources/attr/.gitted/objects/6d/968d62c89c7d9ea23a4c9a7b665d017c3d8ffd
index e832241c9..e832241c9 100644
--- a/tests-clar/resources/attr/.gitted/objects/6d/968d62c89c7d9ea23a4c9a7b665d017c3d8ffd
+++ b/tests/resources/attr/.gitted/objects/6d/968d62c89c7d9ea23a4c9a7b665d017c3d8ffd
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/71/7fc31f6b84f9d6fc3a4edbca259d7fc92beee2 b/tests/resources/attr/.gitted/objects/71/7fc31f6b84f9d6fc3a4edbca259d7fc92beee2
index a80265cac..a80265cac 100644
--- a/tests-clar/resources/attr/.gitted/objects/71/7fc31f6b84f9d6fc3a4edbca259d7fc92beee2
+++ b/tests/resources/attr/.gitted/objects/71/7fc31f6b84f9d6fc3a4edbca259d7fc92beee2
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/8d/0b9df9bd30be7910ddda60548d485bc302b911 b/tests/resources/attr/.gitted/objects/8d/0b9df9bd30be7910ddda60548d485bc302b911
index 3dcf088e4..3dcf088e4 100644
--- a/tests-clar/resources/attr/.gitted/objects/8d/0b9df9bd30be7910ddda60548d485bc302b911
+++ b/tests/resources/attr/.gitted/objects/8d/0b9df9bd30be7910ddda60548d485bc302b911
diff --git a/tests-clar/resources/attr/.gitted/objects/93/61f40bb97239cf55811892e14de2e344168ba1 b/tests/resources/attr/.gitted/objects/93/61f40bb97239cf55811892e14de2e344168ba1
index 4b57836cd..4b57836cd 100644
--- a/tests-clar/resources/attr/.gitted/objects/93/61f40bb97239cf55811892e14de2e344168ba1
+++ b/tests/resources/attr/.gitted/objects/93/61f40bb97239cf55811892e14de2e344168ba1
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/94/da4faa0a6bfb8ee6ccf7153801a69202b31857 b/tests/resources/attr/.gitted/objects/94/da4faa0a6bfb8ee6ccf7153801a69202b31857
index a9ddf5d20..a9ddf5d20 100644
--- a/tests-clar/resources/attr/.gitted/objects/94/da4faa0a6bfb8ee6ccf7153801a69202b31857
+++ b/tests/resources/attr/.gitted/objects/94/da4faa0a6bfb8ee6ccf7153801a69202b31857
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/96/089fd31ce1d3ee2afb0ba09ba063066932f027 b/tests/resources/attr/.gitted/objects/96/089fd31ce1d3ee2afb0ba09ba063066932f027
index efa62f912..efa62f912 100644
--- a/tests-clar/resources/attr/.gitted/objects/96/089fd31ce1d3ee2afb0ba09ba063066932f027
+++ b/tests/resources/attr/.gitted/objects/96/089fd31ce1d3ee2afb0ba09ba063066932f027
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9 b/tests/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9
index 8f5acc70a..8f5acc70a 100644
--- a/tests-clar/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9
+++ b/tests/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/9e/5bdc47d6a80f2be0ea3049ad74231b94609242 b/tests/resources/attr/.gitted/objects/9e/5bdc47d6a80f2be0ea3049ad74231b94609242
index d6385ec8d..d6385ec8d 100644
--- a/tests-clar/resources/attr/.gitted/objects/9e/5bdc47d6a80f2be0ea3049ad74231b94609242
+++ b/tests/resources/attr/.gitted/objects/9e/5bdc47d6a80f2be0ea3049ad74231b94609242
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9 b/tests/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9
index 7663ad0ad..7663ad0ad 100644
--- a/tests-clar/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9
+++ b/tests/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/a0/f7217ae99f5ac3e88534f5cea267febc5fa85b b/tests/resources/attr/.gitted/objects/a0/f7217ae99f5ac3e88534f5cea267febc5fa85b
index 985c2e281..985c2e281 100644
--- a/tests-clar/resources/attr/.gitted/objects/a0/f7217ae99f5ac3e88534f5cea267febc5fa85b
+++ b/tests/resources/attr/.gitted/objects/a0/f7217ae99f5ac3e88534f5cea267febc5fa85b
diff --git a/tests-clar/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320 b/tests/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320
index d898ae9b8..d898ae9b8 100644
--- a/tests-clar/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320
+++ b/tests/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/a5/d76cad53f66f1312bd995909a5bab3c0820770 b/tests/resources/attr/.gitted/objects/a5/d76cad53f66f1312bd995909a5bab3c0820770
index cd6a389f9..cd6a389f9 100644
--- a/tests-clar/resources/attr/.gitted/objects/a5/d76cad53f66f1312bd995909a5bab3c0820770
+++ b/tests/resources/attr/.gitted/objects/a5/d76cad53f66f1312bd995909a5bab3c0820770
diff --git a/tests-clar/resources/attr/.gitted/objects/a9/7cc019851d401a4f1d091cb91a15890a0dd1ba b/tests/resources/attr/.gitted/objects/a9/7cc019851d401a4f1d091cb91a15890a0dd1ba
index 1a7ec0c55..1a7ec0c55 100644
--- a/tests-clar/resources/attr/.gitted/objects/a9/7cc019851d401a4f1d091cb91a15890a0dd1ba
+++ b/tests/resources/attr/.gitted/objects/a9/7cc019851d401a4f1d091cb91a15890a0dd1ba
diff --git a/tests-clar/resources/attr/.gitted/objects/b4/35cd5689a0fb54afbeda4ac20368aa480e8f04 b/tests/resources/attr/.gitted/objects/b4/35cd5689a0fb54afbeda4ac20368aa480e8f04
index ffe3473f4..ffe3473f4 100644
--- a/tests-clar/resources/attr/.gitted/objects/b4/35cd5689a0fb54afbeda4ac20368aa480e8f04
+++ b/tests/resources/attr/.gitted/objects/b4/35cd5689a0fb54afbeda4ac20368aa480e8f04
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/c0/091889c0c77142b87a1fa5123a6398a61d33e7 b/tests/resources/attr/.gitted/objects/c0/091889c0c77142b87a1fa5123a6398a61d33e7
index 11dc63c79..11dc63c79 100644
--- a/tests-clar/resources/attr/.gitted/objects/c0/091889c0c77142b87a1fa5123a6398a61d33e7
+++ b/tests/resources/attr/.gitted/objects/c0/091889c0c77142b87a1fa5123a6398a61d33e7
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/c4/85abe35abd4aa6fd83b076a78bbea9e2e7e06c b/tests/resources/attr/.gitted/objects/c4/85abe35abd4aa6fd83b076a78bbea9e2e7e06c
index 58569ca0e..58569ca0e 100644
--- a/tests-clar/resources/attr/.gitted/objects/c4/85abe35abd4aa6fd83b076a78bbea9e2e7e06c
+++ b/tests/resources/attr/.gitted/objects/c4/85abe35abd4aa6fd83b076a78bbea9e2e7e06c
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/c7/aadd770d5907a8475c29e9ee21a27b88bf675d b/tests/resources/attr/.gitted/objects/c7/aadd770d5907a8475c29e9ee21a27b88bf675d
index 39aedb7d9..39aedb7d9 100644
--- a/tests-clar/resources/attr/.gitted/objects/c7/aadd770d5907a8475c29e9ee21a27b88bf675d
+++ b/tests/resources/attr/.gitted/objects/c7/aadd770d5907a8475c29e9ee21a27b88bf675d
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/c9/6bbb2c2557a8325ae1559e3ba79cdcecb23076 b/tests/resources/attr/.gitted/objects/c9/6bbb2c2557a8325ae1559e3ba79cdcecb23076
index 589f9ad31..589f9ad31 100644
--- a/tests-clar/resources/attr/.gitted/objects/c9/6bbb2c2557a8325ae1559e3ba79cdcecb23076
+++ b/tests/resources/attr/.gitted/objects/c9/6bbb2c2557a8325ae1559e3ba79cdcecb23076
diff --git a/tests-clar/resources/attr/.gitted/objects/ce/39a97a7fb1fa90bcf5e711249c1e507476ae0e b/tests/resources/attr/.gitted/objects/ce/39a97a7fb1fa90bcf5e711249c1e507476ae0e
index 1005f944a..1005f944a 100644
--- a/tests-clar/resources/attr/.gitted/objects/ce/39a97a7fb1fa90bcf5e711249c1e507476ae0e
+++ b/tests/resources/attr/.gitted/objects/ce/39a97a7fb1fa90bcf5e711249c1e507476ae0e
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/d5/7da33c16b14326ecb05d19bbea908f5e4c47d9 b/tests/resources/attr/.gitted/objects/d5/7da33c16b14326ecb05d19bbea908f5e4c47d9
index b96d40c24..b96d40c24 100644
--- a/tests-clar/resources/attr/.gitted/objects/d5/7da33c16b14326ecb05d19bbea908f5e4c47d9
+++ b/tests/resources/attr/.gitted/objects/d5/7da33c16b14326ecb05d19bbea908f5e4c47d9
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2 b/tests/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2
index 83f3b726d..83f3b726d 100644
--- a/tests-clar/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2
+++ b/tests/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/dc/cada462d3df8ac6de596fb8c896aba9344f941 b/tests/resources/attr/.gitted/objects/dc/cada462d3df8ac6de596fb8c896aba9344f941
index ef62f8b9d..ef62f8b9d 100644
--- a/tests-clar/resources/attr/.gitted/objects/dc/cada462d3df8ac6de596fb8c896aba9344f941
+++ b/tests/resources/attr/.gitted/objects/dc/cada462d3df8ac6de596fb8c896aba9344f941
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/de/863bff4976c9ed7e17a4da0fd524908dc84049 b/tests/resources/attr/.gitted/objects/de/863bff4976c9ed7e17a4da0fd524908dc84049
index 7d9b8551f..7d9b8551f 100644
--- a/tests-clar/resources/attr/.gitted/objects/de/863bff4976c9ed7e17a4da0fd524908dc84049
+++ b/tests/resources/attr/.gitted/objects/de/863bff4976c9ed7e17a4da0fd524908dc84049
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/e5/63cf4758f0d646f1b14b76016aa17fa9e549a4 b/tests/resources/attr/.gitted/objects/e5/63cf4758f0d646f1b14b76016aa17fa9e549a4
index 1bc1f0f0b..1bc1f0f0b 100644
--- a/tests-clar/resources/attr/.gitted/objects/e5/63cf4758f0d646f1b14b76016aa17fa9e549a4
+++ b/tests/resources/attr/.gitted/objects/e5/63cf4758f0d646f1b14b76016aa17fa9e549a4
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/ec/b97df2a174987475ac816e3847fc8e9f6c596b b/tests/resources/attr/.gitted/objects/ec/b97df2a174987475ac816e3847fc8e9f6c596b
index 44d703b2e..44d703b2e 100644
--- a/tests-clar/resources/attr/.gitted/objects/ec/b97df2a174987475ac816e3847fc8e9f6c596b
+++ b/tests/resources/attr/.gitted/objects/ec/b97df2a174987475ac816e3847fc8e9f6c596b
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/ed/f3dcee4003d71f139777898882ccd097e34c53 b/tests/resources/attr/.gitted/objects/ed/f3dcee4003d71f139777898882ccd097e34c53
index d28184670..d28184670 100644
--- a/tests-clar/resources/attr/.gitted/objects/ed/f3dcee4003d71f139777898882ccd097e34c53
+++ b/tests/resources/attr/.gitted/objects/ed/f3dcee4003d71f139777898882ccd097e34c53
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/f2/c6d717cf4a5a3e6b02684155ab07b766982165 b/tests/resources/attr/.gitted/objects/f2/c6d717cf4a5a3e6b02684155ab07b766982165
index 27a25dc86..27a25dc86 100644
--- a/tests-clar/resources/attr/.gitted/objects/f2/c6d717cf4a5a3e6b02684155ab07b766982165
+++ b/tests/resources/attr/.gitted/objects/f2/c6d717cf4a5a3e6b02684155ab07b766982165
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/f5/b0af1fb4f5c0cd7aad880711d368a07333c307 b/tests/resources/attr/.gitted/objects/f5/b0af1fb4f5c0cd7aad880711d368a07333c307
index 21faeb8a2..21faeb8a2 100644
--- a/tests-clar/resources/attr/.gitted/objects/f5/b0af1fb4f5c0cd7aad880711d368a07333c307
+++ b/tests/resources/attr/.gitted/objects/f5/b0af1fb4f5c0cd7aad880711d368a07333c307
diff --git a/tests-clar/resources/attr/.gitted/objects/fb/5067b1aef3ac1ada4b379dbcb7d17255df7d78 b/tests/resources/attr/.gitted/objects/fb/5067b1aef3ac1ada4b379dbcb7d17255df7d78
index 6c8ff837e..6c8ff837e 100644
--- a/tests-clar/resources/attr/.gitted/objects/fb/5067b1aef3ac1ada4b379dbcb7d17255df7d78
+++ b/tests/resources/attr/.gitted/objects/fb/5067b1aef3ac1ada4b379dbcb7d17255df7d78
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/fe/773770c5a6cc7185580c9204b1ff18a33ff3fc b/tests/resources/attr/.gitted/objects/fe/773770c5a6cc7185580c9204b1ff18a33ff3fc
index e6fcbc0b3..e6fcbc0b3 100644
--- a/tests-clar/resources/attr/.gitted/objects/fe/773770c5a6cc7185580c9204b1ff18a33ff3fc
+++ b/tests/resources/attr/.gitted/objects/fe/773770c5a6cc7185580c9204b1ff18a33ff3fc
diff --git a/tests-clar/resources/attr/.gitted/objects/ff/69f8639ce2e6010b3f33a74160aad98b48da2b b/tests/resources/attr/.gitted/objects/ff/69f8639ce2e6010b3f33a74160aad98b48da2b
index b736c0b2b..b736c0b2b 100644
--- a/tests-clar/resources/attr/.gitted/objects/ff/69f8639ce2e6010b3f33a74160aad98b48da2b
+++ b/tests/resources/attr/.gitted/objects/ff/69f8639ce2e6010b3f33a74160aad98b48da2b
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/refs/heads/master b/tests/resources/attr/.gitted/refs/heads/master
index b3abfff7d..b3abfff7d 100644
--- a/tests-clar/resources/attr/.gitted/refs/heads/master
+++ b/tests/resources/attr/.gitted/refs/heads/master
diff --git a/tests-clar/resources/attr/attr0 b/tests/resources/attr/attr0
index 556f8c827..556f8c827 100644
--- a/tests-clar/resources/attr/attr0
+++ b/tests/resources/attr/attr0
diff --git a/tests-clar/resources/attr/attr1 b/tests/resources/attr/attr1
index 3b74db7ab..3b74db7ab 100644
--- a/tests-clar/resources/attr/attr1
+++ b/tests/resources/attr/attr1
diff --git a/tests-clar/resources/attr/attr2 b/tests/resources/attr/attr2
index 2c66e14f7..2c66e14f7 100644
--- a/tests-clar/resources/attr/attr2
+++ b/tests/resources/attr/attr2
diff --git a/tests-clar/resources/attr/attr3 b/tests/resources/attr/attr3
index c485abe35..c485abe35 100644
--- a/tests-clar/resources/attr/attr3
+++ b/tests/resources/attr/attr3
diff --git a/tests-clar/resources/attr/binfile b/tests/resources/attr/binfile
index d800886d9..d800886d9 100644
--- a/tests-clar/resources/attr/binfile
+++ b/tests/resources/attr/binfile
diff --git a/tests-clar/resources/attr/dir/file b/tests/resources/attr/dir/file
index e69de29bb..e69de29bb 100644
--- a/tests-clar/resources/attr/dir/file
+++ b/tests/resources/attr/dir/file
diff --git a/tests-clar/resources/attr/file b/tests/resources/attr/file
index 45b983be3..45b983be3 100644
--- a/tests-clar/resources/attr/file
+++ b/tests/resources/attr/file
diff --git a/tests-clar/resources/attr/gitattributes b/tests/resources/attr/gitattributes
index e038983ec..e038983ec 100644
--- a/tests-clar/resources/attr/gitattributes
+++ b/tests/resources/attr/gitattributes
diff --git a/tests-clar/resources/attr/gitignore b/tests/resources/attr/gitignore
index 192967012..192967012 100644
--- a/tests-clar/resources/attr/gitignore
+++ b/tests/resources/attr/gitignore
diff --git a/tests-clar/resources/attr/ign b/tests/resources/attr/ign
index 592fd2594..592fd2594 100644
--- a/tests-clar/resources/attr/ign
+++ b/tests/resources/attr/ign
diff --git a/tests-clar/resources/attr/macro_bad b/tests/resources/attr/macro_bad
index 5819a185d..5819a185d 100644
--- a/tests-clar/resources/attr/macro_bad
+++ b/tests/resources/attr/macro_bad
diff --git a/tests-clar/resources/attr/macro_test b/tests/resources/attr/macro_test
index ff69f8639..ff69f8639 100644
--- a/tests-clar/resources/attr/macro_test
+++ b/tests/resources/attr/macro_test
diff --git a/tests-clar/resources/attr/root_test1 b/tests/resources/attr/root_test1
index 45141a79a..45141a79a 100644
--- a/tests-clar/resources/attr/root_test1
+++ b/tests/resources/attr/root_test1
diff --git a/tests-clar/resources/attr/root_test2 b/tests/resources/attr/root_test2
index 4d713dc48..4d713dc48 100644
--- a/tests-clar/resources/attr/root_test2
+++ b/tests/resources/attr/root_test2
diff --git a/tests-clar/resources/attr/root_test3 b/tests/resources/attr/root_test3
index 108bb4e7f..108bb4e7f 100644
--- a/tests-clar/resources/attr/root_test3
+++ b/tests/resources/attr/root_test3
diff --git a/tests-clar/resources/attr/root_test4.txt b/tests/resources/attr/root_test4.txt
index a0f7217ae..a0f7217ae 100644
--- a/tests-clar/resources/attr/root_test4.txt
+++ b/tests/resources/attr/root_test4.txt
diff --git a/tests-clar/resources/attr/sub/.gitattributes b/tests/resources/attr/sub/.gitattributes
index 329c1c5b8..329c1c5b8 100644
--- a/tests-clar/resources/attr/sub/.gitattributes
+++ b/tests/resources/attr/sub/.gitattributes
diff --git a/tests-clar/resources/attr/sub/abc b/tests/resources/attr/sub/abc
index 3e42ffc54..3e42ffc54 100644
--- a/tests-clar/resources/attr/sub/abc
+++ b/tests/resources/attr/sub/abc
diff --git a/tests-clar/resources/attr/sub/dir/file b/tests/resources/attr/sub/dir/file
index e69de29bb..e69de29bb 100644
--- a/tests-clar/resources/attr/sub/dir/file
+++ b/tests/resources/attr/sub/dir/file
diff --git a/tests-clar/resources/attr/sub/file b/tests/resources/attr/sub/file
index 45b983be3..45b983be3 100644
--- a/tests-clar/resources/attr/sub/file
+++ b/tests/resources/attr/sub/file
diff --git a/tests-clar/resources/attr/sub/ign/file b/tests/resources/attr/sub/ign/file
index 4dcd992e1..4dcd992e1 100644
--- a/tests-clar/resources/attr/sub/ign/file
+++ b/tests/resources/attr/sub/ign/file
diff --git a/tests-clar/resources/attr/sub/ign/sub/file b/tests/resources/attr/sub/ign/sub/file
index 88aca0164..88aca0164 100644
--- a/tests-clar/resources/attr/sub/ign/sub/file
+++ b/tests/resources/attr/sub/ign/sub/file
diff --git a/tests-clar/resources/attr/sub/sub/.gitattributes b/tests/resources/attr/sub/sub/.gitattributes
index 55225e4d6..55225e4d6 100644
--- a/tests-clar/resources/attr/sub/sub/.gitattributes
+++ b/tests/resources/attr/sub/sub/.gitattributes
diff --git a/tests-clar/resources/attr/sub/sub/dir b/tests/resources/attr/sub/sub/dir
index e69de29bb..e69de29bb 100644
--- a/tests-clar/resources/attr/sub/sub/dir
+++ b/tests/resources/attr/sub/sub/dir
diff --git a/tests-clar/resources/attr/sub/sub/file b/tests/resources/attr/sub/sub/file
index 45b983be3..45b983be3 100644
--- a/tests-clar/resources/attr/sub/sub/file
+++ b/tests/resources/attr/sub/sub/file
diff --git a/tests-clar/resources/attr/sub/sub/subsub.txt b/tests/resources/attr/sub/sub/subsub.txt
index 9e5bdc47d..9e5bdc47d 100644
--- a/tests-clar/resources/attr/sub/sub/subsub.txt
+++ b/tests/resources/attr/sub/sub/subsub.txt
diff --git a/tests-clar/resources/attr/sub/subdir_test1 b/tests/resources/attr/sub/subdir_test1
index e563cf475..e563cf475 100644
--- a/tests-clar/resources/attr/sub/subdir_test1
+++ b/tests/resources/attr/sub/subdir_test1
diff --git a/tests-clar/resources/attr/sub/subdir_test2.txt b/tests/resources/attr/sub/subdir_test2.txt
index fb5067b1a..fb5067b1a 100644
--- a/tests-clar/resources/attr/sub/subdir_test2.txt
+++ b/tests/resources/attr/sub/subdir_test2.txt
diff --git a/tests-clar/resources/attr_index/.gitted/HEAD b/tests/resources/attr_index/.gitted/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/attr_index/.gitted/HEAD
+++ b/tests/resources/attr_index/.gitted/HEAD
diff --git a/tests-clar/resources/attr_index/.gitted/config b/tests/resources/attr_index/.gitted/config
index af107929f..af107929f 100644
--- a/tests-clar/resources/attr_index/.gitted/config
+++ b/tests/resources/attr_index/.gitted/config
diff --git a/tests-clar/resources/attr_index/.gitted/description b/tests/resources/attr_index/.gitted/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/attr_index/.gitted/description
+++ b/tests/resources/attr_index/.gitted/description
diff --git a/tests-clar/resources/attr_index/.gitted/index b/tests/resources/attr_index/.gitted/index
index d87480332..d87480332 100644
--- a/tests-clar/resources/attr_index/.gitted/index
+++ b/tests/resources/attr_index/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/attr_index/.gitted/info/exclude b/tests/resources/attr_index/.gitted/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/attr_index/.gitted/info/exclude
+++ b/tests/resources/attr_index/.gitted/info/exclude
diff --git a/tests-clar/resources/attr_index/.gitted/info/refs b/tests/resources/attr_index/.gitted/info/refs
index 60feca293..60feca293 100644
--- a/tests-clar/resources/attr_index/.gitted/info/refs
+++ b/tests/resources/attr_index/.gitted/info/refs
diff --git a/tests-clar/resources/attr_index/.gitted/logs/HEAD b/tests/resources/attr_index/.gitted/logs/HEAD
index ffd298c04..ffd298c04 100644
--- a/tests-clar/resources/attr_index/.gitted/logs/HEAD
+++ b/tests/resources/attr_index/.gitted/logs/HEAD
diff --git a/tests-clar/resources/attr_index/.gitted/logs/refs/heads/master b/tests/resources/attr_index/.gitted/logs/refs/heads/master
index ffd298c04..ffd298c04 100644
--- a/tests-clar/resources/attr_index/.gitted/logs/refs/heads/master
+++ b/tests/resources/attr_index/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/attr_index/.gitted/objects/38/12cfef36615db1788d4e63f90028007e17a348 b/tests/resources/attr_index/.gitted/objects/38/12cfef36615db1788d4e63f90028007e17a348
index ee2991571..ee2991571 100644
--- a/tests-clar/resources/attr_index/.gitted/objects/38/12cfef36615db1788d4e63f90028007e17a348
+++ b/tests/resources/attr_index/.gitted/objects/38/12cfef36615db1788d4e63f90028007e17a348
diff --git a/tests-clar/resources/attr_index/.gitted/objects/59/d942b8be2784bc96db9b22202c10815c9a077b b/tests/resources/attr_index/.gitted/objects/59/d942b8be2784bc96db9b22202c10815c9a077b
index ff33737db..ff33737db 100644
--- a/tests-clar/resources/attr_index/.gitted/objects/59/d942b8be2784bc96db9b22202c10815c9a077b
+++ b/tests/resources/attr_index/.gitted/objects/59/d942b8be2784bc96db9b22202c10815c9a077b
diff --git a/tests-clar/resources/attr_index/.gitted/objects/cd/f17ea3fe625ef812f4dce7f423f4f299287505 b/tests/resources/attr_index/.gitted/objects/cd/f17ea3fe625ef812f4dce7f423f4f299287505
index 2a410057e..2a410057e 100644
--- a/tests-clar/resources/attr_index/.gitted/objects/cd/f17ea3fe625ef812f4dce7f423f4f299287505
+++ b/tests/resources/attr_index/.gitted/objects/cd/f17ea3fe625ef812f4dce7f423f4f299287505
Binary files differ
diff --git a/tests-clar/resources/attr_index/.gitted/objects/f7/2502ddd01412bb20796ff812af56fd53b82b52 b/tests/resources/attr_index/.gitted/objects/f7/2502ddd01412bb20796ff812af56fd53b82b52
index 048928000..048928000 100644
--- a/tests-clar/resources/attr_index/.gitted/objects/f7/2502ddd01412bb20796ff812af56fd53b82b52
+++ b/tests/resources/attr_index/.gitted/objects/f7/2502ddd01412bb20796ff812af56fd53b82b52
Binary files differ
diff --git a/tests-clar/resources/attr_index/.gitted/objects/info/packs b/tests/resources/attr_index/.gitted/objects/info/packs
index 559dc741c..559dc741c 100644
--- a/tests-clar/resources/attr_index/.gitted/objects/info/packs
+++ b/tests/resources/attr_index/.gitted/objects/info/packs
diff --git a/tests-clar/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.idx b/tests/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.idx
index fbef4aa1d..fbef4aa1d 100644
--- a/tests-clar/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.idx
+++ b/tests/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.idx
Binary files differ
diff --git a/tests-clar/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.pack b/tests/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.pack
index 09c9e06d9..09c9e06d9 100644
--- a/tests-clar/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.pack
+++ b/tests/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.pack
Binary files differ
diff --git a/tests-clar/resources/attr_index/.gitted/packed-refs b/tests/resources/attr_index/.gitted/packed-refs
index 6b3e4decf..6b3e4decf 100644
--- a/tests-clar/resources/attr_index/.gitted/packed-refs
+++ b/tests/resources/attr_index/.gitted/packed-refs
diff --git a/tests-clar/resources/attr_index/.gitted/refs/heads/master b/tests/resources/attr_index/.gitted/refs/heads/master
index 9b7562931..9b7562931 100644
--- a/tests-clar/resources/attr_index/.gitted/refs/heads/master
+++ b/tests/resources/attr_index/.gitted/refs/heads/master
diff --git a/tests-clar/resources/attr_index/README.md b/tests/resources/attr_index/README.md
index 59d942b8b..59d942b8b 100644
--- a/tests-clar/resources/attr_index/README.md
+++ b/tests/resources/attr_index/README.md
diff --git a/tests-clar/resources/attr_index/README.txt b/tests/resources/attr_index/README.txt
index 874c12b79..874c12b79 100644
--- a/tests-clar/resources/attr_index/README.txt
+++ b/tests/resources/attr_index/README.txt
diff --git a/tests-clar/resources/attr_index/gitattributes b/tests/resources/attr_index/gitattributes
index cdf17ea3f..cdf17ea3f 100644
--- a/tests-clar/resources/attr_index/gitattributes
+++ b/tests/resources/attr_index/gitattributes
diff --git a/tests-clar/resources/attr_index/sub/sub/.gitattributes b/tests/resources/attr_index/sub/sub/.gitattributes
index 060c9a261..060c9a261 100644
--- a/tests-clar/resources/attr_index/sub/sub/.gitattributes
+++ b/tests/resources/attr_index/sub/sub/.gitattributes
diff --git a/tests-clar/resources/attr_index/sub/sub/README.md b/tests/resources/attr_index/sub/sub/README.md
index 59652e349..59652e349 100644
--- a/tests-clar/resources/attr_index/sub/sub/README.md
+++ b/tests/resources/attr_index/sub/sub/README.md
diff --git a/tests-clar/resources/attr_index/sub/sub/README.txt b/tests/resources/attr_index/sub/sub/README.txt
index 59652e349..59652e349 100644
--- a/tests-clar/resources/attr_index/sub/sub/README.txt
+++ b/tests/resources/attr_index/sub/sub/README.txt
diff --git a/tests/resources/bad.index b/tests/resources/bad.index
new file mode 100644
index 000000000..53746549f
--- /dev/null
+++ b/tests/resources/bad.index
Binary files differ
diff --git a/tests-clar/resources/bad_tag.git/HEAD b/tests/resources/bad_tag.git/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/bad_tag.git/HEAD
+++ b/tests/resources/bad_tag.git/HEAD
diff --git a/tests-clar/resources/bad_tag.git/config b/tests/resources/bad_tag.git/config
index 2f8958058..2f8958058 100644
--- a/tests-clar/resources/bad_tag.git/config
+++ b/tests/resources/bad_tag.git/config
diff --git a/tests-clar/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.idx b/tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.idx
index c404aa15b..c404aa15b 100644
--- a/tests-clar/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.idx
+++ b/tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.idx
Binary files differ
diff --git a/tests-clar/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.pack b/tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.pack
index 90eac5032..90eac5032 100644
--- a/tests-clar/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.pack
+++ b/tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.pack
Binary files differ
diff --git a/tests-clar/resources/bad_tag.git/packed-refs b/tests/resources/bad_tag.git/packed-refs
index 9da16459b..9da16459b 100644
--- a/tests-clar/resources/bad_tag.git/packed-refs
+++ b/tests/resources/bad_tag.git/packed-refs
diff --git a/tests-clar/resources/bad_tag.git/refs/dummy-marker.txt b/tests/resources/bad_tag.git/refs/dummy-marker.txt
index e69de29bb..e69de29bb 100644
--- a/tests-clar/resources/bad_tag.git/refs/dummy-marker.txt
+++ b/tests/resources/bad_tag.git/refs/dummy-marker.txt
diff --git a/tests-clar/resources/big.index b/tests/resources/big.index
index 66932f14b..66932f14b 100644
--- a/tests-clar/resources/big.index
+++ b/tests/resources/big.index
Binary files differ
diff --git a/tests-clar/resources/binaryunicode/.gitted/HEAD b/tests/resources/binaryunicode/.gitted/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/binaryunicode/.gitted/HEAD
+++ b/tests/resources/binaryunicode/.gitted/HEAD
diff --git a/tests-clar/resources/binaryunicode/.gitted/config b/tests/resources/binaryunicode/.gitted/config
index f9845fe7e..f9845fe7e 100644
--- a/tests-clar/resources/binaryunicode/.gitted/config
+++ b/tests/resources/binaryunicode/.gitted/config
diff --git a/tests-clar/resources/binaryunicode/.gitted/description b/tests/resources/binaryunicode/.gitted/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/binaryunicode/.gitted/description
+++ b/tests/resources/binaryunicode/.gitted/description
diff --git a/tests-clar/resources/binaryunicode/.gitted/index b/tests/resources/binaryunicode/.gitted/index
index a216d2219..a216d2219 100644
--- a/tests-clar/resources/binaryunicode/.gitted/index
+++ b/tests/resources/binaryunicode/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/binaryunicode/.gitted/info/exclude b/tests/resources/binaryunicode/.gitted/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/binaryunicode/.gitted/info/exclude
+++ b/tests/resources/binaryunicode/.gitted/info/exclude
diff --git a/tests-clar/resources/binaryunicode/.gitted/info/refs b/tests/resources/binaryunicode/.gitted/info/refs
index 128eea7c9..128eea7c9 100644
--- a/tests-clar/resources/binaryunicode/.gitted/info/refs
+++ b/tests/resources/binaryunicode/.gitted/info/refs
diff --git a/tests-clar/resources/binaryunicode/.gitted/objects/info/packs b/tests/resources/binaryunicode/.gitted/objects/info/packs
index c2de8f5cb..c2de8f5cb 100644
--- a/tests-clar/resources/binaryunicode/.gitted/objects/info/packs
+++ b/tests/resources/binaryunicode/.gitted/objects/info/packs
diff --git a/tests-clar/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.idx b/tests/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.idx
index 8a05b2beb..8a05b2beb 100644
--- a/tests-clar/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.idx
+++ b/tests/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.idx
Binary files differ
diff --git a/tests-clar/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.pack b/tests/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.pack
index 6b5ddc414..6b5ddc414 100644
--- a/tests-clar/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.pack
+++ b/tests/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.pack
Binary files differ
diff --git a/tests-clar/resources/binaryunicode/.gitted/refs/heads/branch1 b/tests/resources/binaryunicode/.gitted/refs/heads/branch1
index 0595fbd31..0595fbd31 100644
--- a/tests-clar/resources/binaryunicode/.gitted/refs/heads/branch1
+++ b/tests/resources/binaryunicode/.gitted/refs/heads/branch1
diff --git a/tests-clar/resources/binaryunicode/.gitted/refs/heads/branch2 b/tests/resources/binaryunicode/.gitted/refs/heads/branch2
index d86856687..d86856687 100644
--- a/tests-clar/resources/binaryunicode/.gitted/refs/heads/branch2
+++ b/tests/resources/binaryunicode/.gitted/refs/heads/branch2
diff --git a/tests-clar/resources/binaryunicode/.gitted/refs/heads/master b/tests/resources/binaryunicode/.gitted/refs/heads/master
index 552d166da..552d166da 100644
--- a/tests-clar/resources/binaryunicode/.gitted/refs/heads/master
+++ b/tests/resources/binaryunicode/.gitted/refs/heads/master
diff --git a/tests-clar/resources/binaryunicode/file.txt b/tests/resources/binaryunicode/file.txt
index 2255035d4..2255035d4 100644
--- a/tests-clar/resources/binaryunicode/file.txt
+++ b/tests/resources/binaryunicode/file.txt
diff --git a/tests-clar/resources/crlf/.gitted/HEAD b/tests/resources/blametest.git/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/crlf/.gitted/HEAD
+++ b/tests/resources/blametest.git/HEAD
diff --git a/tests-clar/resources/twowaymerge.git/config b/tests/resources/blametest.git/config
index c53d818dd..c53d818dd 100644
--- a/tests-clar/resources/twowaymerge.git/config
+++ b/tests/resources/blametest.git/config
diff --git a/tests-clar/resources/deprecated-mode.git/description b/tests/resources/blametest.git/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/deprecated-mode.git/description
+++ b/tests/resources/blametest.git/description
diff --git a/tests/resources/blametest.git/objects/0c/bab4d45fd61e55a1c9697f9f9cb07a12e15448 b/tests/resources/blametest.git/objects/0c/bab4d45fd61e55a1c9697f9f9cb07a12e15448
new file mode 100644
index 000000000..90331cef9
--- /dev/null
+++ b/tests/resources/blametest.git/objects/0c/bab4d45fd61e55a1c9697f9f9cb07a12e15448
Binary files differ
diff --git a/tests/resources/blametest.git/objects/1a/ac69ae5d96461afc4d81d0066cb12f5b05a35b b/tests/resources/blametest.git/objects/1a/ac69ae5d96461afc4d81d0066cb12f5b05a35b
new file mode 100644
index 000000000..71890274a
--- /dev/null
+++ b/tests/resources/blametest.git/objects/1a/ac69ae5d96461afc4d81d0066cb12f5b05a35b
Binary files differ
diff --git a/tests/resources/blametest.git/objects/1b/5f0775af166331c854bd8d1bca3450eaf2532a b/tests/resources/blametest.git/objects/1b/5f0775af166331c854bd8d1bca3450eaf2532a
new file mode 100644
index 000000000..e66430637
--- /dev/null
+++ b/tests/resources/blametest.git/objects/1b/5f0775af166331c854bd8d1bca3450eaf2532a
Binary files differ
diff --git a/tests/resources/blametest.git/objects/48/2f2c370e35c2c314fc1f96db2beb33f955a26a b/tests/resources/blametest.git/objects/48/2f2c370e35c2c314fc1f96db2beb33f955a26a
new file mode 100644
index 000000000..7da4cf5d4
--- /dev/null
+++ b/tests/resources/blametest.git/objects/48/2f2c370e35c2c314fc1f96db2beb33f955a26a
Binary files differ
diff --git a/tests/resources/blametest.git/objects/63/d671eb32d250e4a83766ebbc60e818c1e1e93a b/tests/resources/blametest.git/objects/63/d671eb32d250e4a83766ebbc60e818c1e1e93a
new file mode 100644
index 000000000..5f41f2936
--- /dev/null
+++ b/tests/resources/blametest.git/objects/63/d671eb32d250e4a83766ebbc60e818c1e1e93a
@@ -0,0 +1,3 @@
+x•A E]sŠÙu¥Ê@bŒ±GðÀP뢭Azk<ù’—¼Ÿ×y~60­–I³“1S´’=“-6÷FfC.†ìcp½õŠµ, $b
+¶8MF
+ᨹO!1eïÈò]TqkÓZáV¸··çô¾>žmÚÒ)¯ó49dì-Ñ#ªîm­üg©áw©ºTK@Î \ No newline at end of file
diff --git a/tests/resources/blametest.git/objects/63/eb57322e363e18d460da5ea8284f3cd2340b36 b/tests/resources/blametest.git/objects/63/eb57322e363e18d460da5ea8284f3cd2340b36
new file mode 100644
index 000000000..c6c285eeb
--- /dev/null
+++ b/tests/resources/blametest.git/objects/63/eb57322e363e18d460da5ea8284f3cd2340b36
Binary files differ
diff --git a/tests/resources/blametest.git/objects/8b/137891791fe96927ad78e64b0aad7bded08bdc b/tests/resources/blametest.git/objects/8b/137891791fe96927ad78e64b0aad7bded08bdc
new file mode 100644
index 000000000..9d8f60531
--- /dev/null
+++ b/tests/resources/blametest.git/objects/8b/137891791fe96927ad78e64b0aad7bded08bdc
Binary files differ
diff --git a/tests/resources/blametest.git/objects/96/679d59cf9f74d69b3c920f258559b5e8c9a18a b/tests/resources/blametest.git/objects/96/679d59cf9f74d69b3c920f258559b5e8c9a18a
new file mode 100644
index 000000000..d716e3b03
--- /dev/null
+++ b/tests/resources/blametest.git/objects/96/679d59cf9f74d69b3c920f258559b5e8c9a18a
Binary files differ
diff --git a/tests/resources/blametest.git/objects/98/89d6e5557761aa8e3607e80c874a6dc51ada7c b/tests/resources/blametest.git/objects/98/89d6e5557761aa8e3607e80c874a6dc51ada7c
new file mode 100644
index 000000000..12407f662
--- /dev/null
+++ b/tests/resources/blametest.git/objects/98/89d6e5557761aa8e3607e80c874a6dc51ada7c
Binary files differ
diff --git a/tests/resources/blametest.git/objects/aa/06ecca6c4ad6432ab9313e556ca92ba4bcf9e9 b/tests/resources/blametest.git/objects/aa/06ecca6c4ad6432ab9313e556ca92ba4bcf9e9
new file mode 100644
index 000000000..bc13badb1
--- /dev/null
+++ b/tests/resources/blametest.git/objects/aa/06ecca6c4ad6432ab9313e556ca92ba4bcf9e9
@@ -0,0 +1 @@
+x•KÂ0 DYçÞu²ã6I%„P9'ÈÇ¡]ô£ÞŸ"NÀÌîI#½‰ë<O4ᩈYPè(O.SÛçÔÚÜ yÉÉ­ FV›/²TH^³å¾C¬“ÆĘÉv¡–£3ìrúÆ+¿×q-0ÈÏZüàÞ÷×TÇ=\â:߀ؠ%tÎÀ¢:èáVå¿•zü.5CóªŠA@ \ No newline at end of file
diff --git a/tests/resources/blametest.git/objects/b1/76dfc3a4dc8734e4c579f77236a9c8d0a965d2 b/tests/resources/blametest.git/objects/b1/76dfc3a4dc8734e4c579f77236a9c8d0a965d2
new file mode 100644
index 000000000..caf5cc1e3
--- /dev/null
+++ b/tests/resources/blametest.git/objects/b1/76dfc3a4dc8734e4c579f77236a9c8d0a965d2
Binary files differ
diff --git a/tests/resources/blametest.git/objects/b9/0bb887b7c03750ae6b352ffe76ab9d2e86ee7d b/tests/resources/blametest.git/objects/b9/0bb887b7c03750ae6b352ffe76ab9d2e86ee7d
new file mode 100644
index 000000000..d4f9ccaca
--- /dev/null
+++ b/tests/resources/blametest.git/objects/b9/0bb887b7c03750ae6b352ffe76ab9d2e86ee7d
Binary files differ
diff --git a/tests/resources/blametest.git/objects/b9/9f7ac0b88909253d829554c14af488c3b0f3a5 b/tests/resources/blametest.git/objects/b9/9f7ac0b88909253d829554c14af488c3b0f3a5
new file mode 100644
index 000000000..a428fd63b
--- /dev/null
+++ b/tests/resources/blametest.git/objects/b9/9f7ac0b88909253d829554c14af488c3b0f3a5
@@ -0,0 +1,2 @@
+x•ŽK
+1]ç½›•ÒIg2Yˆˆâ <A>­3‹L vîoÀ¸{õR-eÐΤ1ƒ#ŽóBÆ0©}¶s˜9xãí‹R6d1’S¡ËZÜx‡§´Ð#œãçúÞdíñ”j¹€&‡‹F§ Ñ#ªAGKø?KݧÇôgõ2r \ No newline at end of file
diff --git a/tests/resources/blametest.git/objects/bc/7c5ac2bafe828a68e9d1d460343718d6fbe136 b/tests/resources/blametest.git/objects/bc/7c5ac2bafe828a68e9d1d460343718d6fbe136
new file mode 100644
index 000000000..4e6ad159e
--- /dev/null
+++ b/tests/resources/blametest.git/objects/bc/7c5ac2bafe828a68e9d1d460343718d6fbe136
@@ -0,0 +1,3 @@
+x•AnÃ0 {Ö+tË))J´AÛ<!/ eºÉÁqàÊÿ¯¢Èu±³Àl[æùÞc*òÖW÷èÅ26t-DS¨V5C2ÀŠN#§Lá©«?zdy@7Jc*àY+ ÌnÖ¼bݧЅô¿¯
+ì­)·¬#gJjBH^
+7•dš­Mâtë·e_þˆ×¾êfñd?ß÷~Ûì½-ó9"1 BPžî.Ý_£Âåï‚Ãç!ü&íO \ No newline at end of file
diff --git a/tests/resources/blametest.git/objects/cf/e0e1e1e3ba18f149fd47f5e1aef6016b2260c3 b/tests/resources/blametest.git/objects/cf/e0e1e1e3ba18f149fd47f5e1aef6016b2260c3
new file mode 100644
index 000000000..2048e81eb
--- /dev/null
+++ b/tests/resources/blametest.git/objects/cf/e0e1e1e3ba18f149fd47f5e1aef6016b2260c3
Binary files differ
diff --git a/tests/resources/blametest.git/objects/d0/67729932057cdb7527a833d6799c4ddc520640 b/tests/resources/blametest.git/objects/d0/67729932057cdb7527a833d6799c4ddc520640
new file mode 100644
index 000000000..926c4bbb0
--- /dev/null
+++ b/tests/resources/blametest.git/objects/d0/67729932057cdb7527a833d6799c4ddc520640
@@ -0,0 +1 @@
+x+)JMU03c040031QHÔ+©(a˜Ñyíihyâª>3ö<í^¹G¥nÕ@$H­É\;ÍMêo㶜úѬ‹£Ƥ \ No newline at end of file
diff --git a/tests/resources/blametest.git/objects/da/237394e6132d20d30f175b9b73c8638fddddda b/tests/resources/blametest.git/objects/da/237394e6132d20d30f175b9b73c8638fddddda
new file mode 100644
index 000000000..e9e13833f
--- /dev/null
+++ b/tests/resources/blametest.git/objects/da/237394e6132d20d30f175b9b73c8638fddddda
@@ -0,0 +1,4 @@
+x•K
+Â0@]÷³ëJ™|'"¢ÞÀ$“ÔvÑVbz žÀíƒïÉ:ÏS­ðÐj)Ñif£Ñ‘äDNS ÆdOÌbs§Ñ[ìÞ±–¥Ab(
+¦Y;“ƒfç¬(‚˜„ƒ‰®‹[×
+·²À³Õ¸%8§Ïõ5µqK'Yç (ã‘zF8b@ìvº·µòŸÕÝKý£ÿc–?S \ No newline at end of file
diff --git a/tests/resources/blametest.git/objects/e5/b41c1ea533f87388ab69b13baf0b5a562d6243 b/tests/resources/blametest.git/objects/e5/b41c1ea533f87388ab69b13baf0b5a562d6243
new file mode 100644
index 000000000..7e5586c2b
--- /dev/null
+++ b/tests/resources/blametest.git/objects/e5/b41c1ea533f87388ab69b13baf0b5a562d6243
Binary files differ
diff --git a/tests/resources/blametest.git/objects/ef/32df4d259143933715c74951f932d9892364d1 b/tests/resources/blametest.git/objects/ef/32df4d259143933715c74951f932d9892364d1
new file mode 100644
index 000000000..d021ccfde
--- /dev/null
+++ b/tests/resources/blametest.git/objects/ef/32df4d259143933715c74951f932d9892364d1
Binary files differ
diff --git a/tests/resources/blametest.git/refs/heads/master b/tests/resources/blametest.git/refs/heads/master
new file mode 100644
index 000000000..b763025d8
--- /dev/null
+++ b/tests/resources/blametest.git/refs/heads/master
@@ -0,0 +1 @@
+bc7c5ac2bafe828a68e9d1d460343718d6fbe136
diff --git a/tests-clar/resources/config/.gitconfig b/tests/resources/config/.gitconfig
index fa72bddfc..fa72bddfc 100644
--- a/tests-clar/resources/config/.gitconfig
+++ b/tests/resources/config/.gitconfig
diff --git a/tests/resources/config/config-include b/tests/resources/config/config-include
new file mode 100644
index 000000000..6b5e79de7
--- /dev/null
+++ b/tests/resources/config/config-include
@@ -0,0 +1,2 @@
+[include]
+ path = config-included
diff --git a/tests/resources/config/config-included b/tests/resources/config/config-included
new file mode 100644
index 000000000..089ca08a7
--- /dev/null
+++ b/tests/resources/config/config-included
@@ -0,0 +1,2 @@
+[foo "bar"]
+ baz = huzzah
diff --git a/tests-clar/resources/config/config0 b/tests/resources/config/config0
index 85235c501..85235c501 100644
--- a/tests-clar/resources/config/config0
+++ b/tests/resources/config/config0
diff --git a/tests-clar/resources/config/config1 b/tests/resources/config/config1
index 211dc9e7d..211dc9e7d 100644
--- a/tests-clar/resources/config/config1
+++ b/tests/resources/config/config1
diff --git a/tests-clar/resources/config/config10 b/tests/resources/config/config10
index dde17911b..dde17911b 100644
--- a/tests-clar/resources/config/config10
+++ b/tests/resources/config/config10
diff --git a/tests/resources/config/config11 b/tests/resources/config/config11
new file mode 100644
index 000000000..1d8a74470
--- /dev/null
+++ b/tests/resources/config/config11
@@ -0,0 +1,5 @@
+[remote "ab"]
+ url = git://github.com/libgit2/libgit2
+ url = git://git.example.com/libgit2
+
+
diff --git a/tests-clar/resources/config/config12 b/tests/resources/config/config12
index b57a81b08..b57a81b08 100644
--- a/tests-clar/resources/config/config12
+++ b/tests/resources/config/config12
diff --git a/tests-clar/resources/config/config13 b/tests/resources/config/config13
index c1e0c5647..c1e0c5647 100644
--- a/tests-clar/resources/config/config13
+++ b/tests/resources/config/config13
diff --git a/tests-clar/resources/config/config14 b/tests/resources/config/config14
index ef2198c45..ef2198c45 100644
--- a/tests-clar/resources/config/config14
+++ b/tests/resources/config/config14
diff --git a/tests-clar/resources/config/config15 b/tests/resources/config/config15
index 6d34f817b..6d34f817b 100644
--- a/tests-clar/resources/config/config15
+++ b/tests/resources/config/config15
diff --git a/tests-clar/resources/config/config16 b/tests/resources/config/config16
index f25cdb728..f25cdb728 100644
--- a/tests-clar/resources/config/config16
+++ b/tests/resources/config/config16
diff --git a/tests-clar/resources/config/config17 b/tests/resources/config/config17
index ca25a86af..ca25a86af 100644
--- a/tests-clar/resources/config/config17
+++ b/tests/resources/config/config17
diff --git a/tests-clar/resources/config/config18 b/tests/resources/config/config18
index cb6fd5ebc..cb6fd5ebc 100644
--- a/tests-clar/resources/config/config18
+++ b/tests/resources/config/config18
diff --git a/tests-clar/resources/config/config19 b/tests/resources/config/config19
index f3ae5a640..f3ae5a640 100644
--- a/tests-clar/resources/config/config19
+++ b/tests/resources/config/config19
diff --git a/tests-clar/resources/config/config2 b/tests/resources/config/config2
index 60a389827..60a389827 100644
--- a/tests-clar/resources/config/config2
+++ b/tests/resources/config/config2
diff --git a/tests/resources/config/config20 b/tests/resources/config/config20
new file mode 100644
index 000000000..8f0f12c4c
--- /dev/null
+++ b/tests/resources/config/config20
@@ -0,0 +1,11 @@
+[valid "[subsection]"]
+ something = a
+; we don't allow anything after closing "
+[sec "[subsec]/child"]
+ parent = grand
+[sec2 "[subsec2]/child2"]
+ type = dvcs
+[sec3 "escape\"quote"]
+ vcs = git
+[sec4 "escaping\\slash"]
+ lib = git2
diff --git a/tests-clar/resources/config/config3 b/tests/resources/config/config3
index 44a5e50ea..44a5e50ea 100644
--- a/tests-clar/resources/config/config3
+++ b/tests/resources/config/config3
diff --git a/tests-clar/resources/config/config4 b/tests/resources/config/config4
index 9dd40419e..9dd40419e 100644
--- a/tests-clar/resources/config/config4
+++ b/tests/resources/config/config4
diff --git a/tests-clar/resources/config/config5 b/tests/resources/config/config5
index 8ab60ccec..8ab60ccec 100644
--- a/tests-clar/resources/config/config5
+++ b/tests/resources/config/config5
diff --git a/tests-clar/resources/config/config6 b/tests/resources/config/config6
index 0f8f90ac9..0f8f90ac9 100644
--- a/tests-clar/resources/config/config6
+++ b/tests/resources/config/config6
diff --git a/tests-clar/resources/config/config7 b/tests/resources/config/config7
index 6af6fcf25..6af6fcf25 100644
--- a/tests-clar/resources/config/config7
+++ b/tests/resources/config/config7
diff --git a/tests-clar/resources/config/config8 b/tests/resources/config/config8
index e69de29bb..e69de29bb 100644
--- a/tests-clar/resources/config/config8
+++ b/tests/resources/config/config8
diff --git a/tests-clar/resources/config/config9 b/tests/resources/config/config9
index fcaac424e..fcaac424e 100644
--- a/tests-clar/resources/config/config9
+++ b/tests/resources/config/config9
diff --git a/tests-clar/resources/deprecated-mode.git/HEAD b/tests/resources/crlf/.gitted/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/deprecated-mode.git/HEAD
+++ b/tests/resources/crlf/.gitted/HEAD
diff --git a/tests-clar/resources/crlf/.gitted/config b/tests/resources/crlf/.gitted/config
index e69de29bb..e69de29bb 100644
--- a/tests-clar/resources/crlf/.gitted/config
+++ b/tests/resources/crlf/.gitted/config
diff --git a/tests-clar/resources/crlf/.gitted/objects/04/de00b358f13389948756732158eaaaefa1448c b/tests/resources/crlf/.gitted/objects/04/de00b358f13389948756732158eaaaefa1448c
index c3b7598c0..c3b7598c0 100644
--- a/tests-clar/resources/crlf/.gitted/objects/04/de00b358f13389948756732158eaaaefa1448c
+++ b/tests/resources/crlf/.gitted/objects/04/de00b358f13389948756732158eaaaefa1448c
Binary files differ
diff --git a/tests-clar/resources/crlf/.gitted/objects/0a/a76e474d259bd7c13eb726a1396c381db55c88 b/tests/resources/crlf/.gitted/objects/0a/a76e474d259bd7c13eb726a1396c381db55c88
index e118d6656..e118d6656 100644
--- a/tests-clar/resources/crlf/.gitted/objects/0a/a76e474d259bd7c13eb726a1396c381db55c88
+++ b/tests/resources/crlf/.gitted/objects/0a/a76e474d259bd7c13eb726a1396c381db55c88
Binary files differ
diff --git a/tests-clar/resources/crlf/.gitted/objects/0d/06894e14df22e066763ae906e0ed3eb79c205f b/tests/resources/crlf/.gitted/objects/0d/06894e14df22e066763ae906e0ed3eb79c205f
index b7a1f3290..b7a1f3290 100644
--- a/tests-clar/resources/crlf/.gitted/objects/0d/06894e14df22e066763ae906e0ed3eb79c205f
+++ b/tests/resources/crlf/.gitted/objects/0d/06894e14df22e066763ae906e0ed3eb79c205f
Binary files differ
diff --git a/tests-clar/resources/crlf/.gitted/objects/0f/f5a53f19bfd2b5eea1ba550295c47515678987 b/tests/resources/crlf/.gitted/objects/0f/f5a53f19bfd2b5eea1ba550295c47515678987
index 5366acd8c..5366acd8c 100644
--- a/tests-clar/resources/crlf/.gitted/objects/0f/f5a53f19bfd2b5eea1ba550295c47515678987
+++ b/tests/resources/crlf/.gitted/objects/0f/f5a53f19bfd2b5eea1ba550295c47515678987
Binary files differ
diff --git a/tests-clar/resources/crlf/.gitted/objects/12/faf3c1ea55f572473cec9052fca468c3584ccb b/tests/resources/crlf/.gitted/objects/12/faf3c1ea55f572473cec9052fca468c3584ccb
index 96d5b2f91..96d5b2f91 100644
--- a/tests-clar/resources/crlf/.gitted/objects/12/faf3c1ea55f572473cec9052fca468c3584ccb
+++ b/tests/resources/crlf/.gitted/objects/12/faf3c1ea55f572473cec9052fca468c3584ccb
diff --git a/tests-clar/resources/crlf/.gitted/objects/38/1cfe630df902bc29271a202d3277981180e4a6 b/tests/resources/crlf/.gitted/objects/38/1cfe630df902bc29271a202d3277981180e4a6
index 0cf707296..0cf707296 100644
--- a/tests-clar/resources/crlf/.gitted/objects/38/1cfe630df902bc29271a202d3277981180e4a6
+++ b/tests/resources/crlf/.gitted/objects/38/1cfe630df902bc29271a202d3277981180e4a6
Binary files differ
diff --git a/tests-clar/resources/crlf/.gitted/objects/79/9770d1cff46753a57db7a066159b5610da6e3a b/tests/resources/crlf/.gitted/objects/79/9770d1cff46753a57db7a066159b5610da6e3a
index 5c701b867..5c701b867 100644
--- a/tests-clar/resources/crlf/.gitted/objects/79/9770d1cff46753a57db7a066159b5610da6e3a
+++ b/tests/resources/crlf/.gitted/objects/79/9770d1cff46753a57db7a066159b5610da6e3a
Binary files differ
diff --git a/tests-clar/resources/crlf/.gitted/objects/7c/ce67e58173e2b01f7db124ceaabe3183d19c49 b/tests/resources/crlf/.gitted/objects/7c/ce67e58173e2b01f7db124ceaabe3183d19c49
index 8e836aba1..8e836aba1 100644
--- a/tests-clar/resources/crlf/.gitted/objects/7c/ce67e58173e2b01f7db124ceaabe3183d19c49
+++ b/tests/resources/crlf/.gitted/objects/7c/ce67e58173e2b01f7db124ceaabe3183d19c49
Binary files differ
diff --git a/tests-clar/resources/crlf/.gitted/objects/a9/a2e8913c1dbe2812fac5e6b4e0a4bd5d0d5966 b/tests/resources/crlf/.gitted/objects/a9/a2e8913c1dbe2812fac5e6b4e0a4bd5d0d5966
index 33d59f1f1..33d59f1f1 100644
--- a/tests-clar/resources/crlf/.gitted/objects/a9/a2e8913c1dbe2812fac5e6b4e0a4bd5d0d5966
+++ b/tests/resources/crlf/.gitted/objects/a9/a2e8913c1dbe2812fac5e6b4e0a4bd5d0d5966
diff --git a/tests-clar/resources/crlf/.gitted/objects/ba/aa042ab2976f8264e467988e6112ee518ec62e b/tests/resources/crlf/.gitted/objects/ba/aa042ab2976f8264e467988e6112ee518ec62e
index 4c544d5ef..4c544d5ef 100644
--- a/tests-clar/resources/crlf/.gitted/objects/ba/aa042ab2976f8264e467988e6112ee518ec62e
+++ b/tests/resources/crlf/.gitted/objects/ba/aa042ab2976f8264e467988e6112ee518ec62e
Binary files differ
diff --git a/tests-clar/resources/crlf/.gitted/objects/dc/88e3b917de821e25962bea7ec1f55c4ce2112c b/tests/resources/crlf/.gitted/objects/dc/88e3b917de821e25962bea7ec1f55c4ce2112c
index 3db13aa79..3db13aa79 100644
--- a/tests-clar/resources/crlf/.gitted/objects/dc/88e3b917de821e25962bea7ec1f55c4ce2112c
+++ b/tests/resources/crlf/.gitted/objects/dc/88e3b917de821e25962bea7ec1f55c4ce2112c
Binary files differ
diff --git a/tests-clar/resources/crlf/.gitted/objects/ea/030d3c6cec212069eca698cabaa5b4550f1511 b/tests/resources/crlf/.gitted/objects/ea/030d3c6cec212069eca698cabaa5b4550f1511
index 117dc725a..117dc725a 100644
--- a/tests-clar/resources/crlf/.gitted/objects/ea/030d3c6cec212069eca698cabaa5b4550f1511
+++ b/tests/resources/crlf/.gitted/objects/ea/030d3c6cec212069eca698cabaa5b4550f1511
Binary files differ
diff --git a/tests-clar/resources/crlf/.gitted/objects/fe/085d9ace90cc675b87df15e1aeed0c3a31407f b/tests/resources/crlf/.gitted/objects/fe/085d9ace90cc675b87df15e1aeed0c3a31407f
index 2e8d10b76..2e8d10b76 100644
--- a/tests-clar/resources/crlf/.gitted/objects/fe/085d9ace90cc675b87df15e1aeed0c3a31407f
+++ b/tests/resources/crlf/.gitted/objects/fe/085d9ace90cc675b87df15e1aeed0c3a31407f
Binary files differ
diff --git a/tests-clar/resources/crlf/.gitted/refs/heads/master b/tests/resources/crlf/.gitted/refs/heads/master
index a2dbe0c2d..a2dbe0c2d 100644
--- a/tests-clar/resources/crlf/.gitted/refs/heads/master
+++ b/tests/resources/crlf/.gitted/refs/heads/master
diff --git a/tests-clar/resources/crlf/.gitted/refs/heads/utf8 b/tests/resources/crlf/.gitted/refs/heads/utf8
index 4b32f7f91..4b32f7f91 100644
--- a/tests-clar/resources/crlf/.gitted/refs/heads/utf8
+++ b/tests/resources/crlf/.gitted/refs/heads/utf8
diff --git a/tests-clar/resources/diff/.gitted/HEAD b/tests/resources/deprecated-mode.git/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/diff/.gitted/HEAD
+++ b/tests/resources/deprecated-mode.git/HEAD
diff --git a/tests-clar/resources/deprecated-mode.git/config b/tests/resources/deprecated-mode.git/config
index f57351fd5..f57351fd5 100644
--- a/tests-clar/resources/deprecated-mode.git/config
+++ b/tests/resources/deprecated-mode.git/config
diff --git a/tests-clar/resources/diff/.gitted/description b/tests/resources/deprecated-mode.git/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/diff/.gitted/description
+++ b/tests/resources/deprecated-mode.git/description
diff --git a/tests-clar/resources/deprecated-mode.git/index b/tests/resources/deprecated-mode.git/index
index 682740603..682740603 100644
--- a/tests-clar/resources/deprecated-mode.git/index
+++ b/tests/resources/deprecated-mode.git/index
Binary files differ
diff --git a/tests-clar/resources/deprecated-mode.git/info/exclude b/tests/resources/deprecated-mode.git/info/exclude
index 6d05881d3..6d05881d3 100644
--- a/tests-clar/resources/deprecated-mode.git/info/exclude
+++ b/tests/resources/deprecated-mode.git/info/exclude
diff --git a/tests-clar/resources/deprecated-mode.git/objects/06/262edc257418e9987caf999f9a7a3e1547adff b/tests/resources/deprecated-mode.git/objects/06/262edc257418e9987caf999f9a7a3e1547adff
index a030cf7a7..a030cf7a7 100644
--- a/tests-clar/resources/deprecated-mode.git/objects/06/262edc257418e9987caf999f9a7a3e1547adff
+++ b/tests/resources/deprecated-mode.git/objects/06/262edc257418e9987caf999f9a7a3e1547adff
Binary files differ
diff --git a/tests-clar/resources/deprecated-mode.git/objects/08/10fb7818088ff5ac41ee49199b51473b1bd6c7 b/tests/resources/deprecated-mode.git/objects/08/10fb7818088ff5ac41ee49199b51473b1bd6c7
index 52d56936a..52d56936a 100644
--- a/tests-clar/resources/deprecated-mode.git/objects/08/10fb7818088ff5ac41ee49199b51473b1bd6c7
+++ b/tests/resources/deprecated-mode.git/objects/08/10fb7818088ff5ac41ee49199b51473b1bd6c7
Binary files differ
diff --git a/tests-clar/resources/deprecated-mode.git/objects/1b/05fdaa881ee45b48cbaa5e9b037d667a47745e b/tests/resources/deprecated-mode.git/objects/1b/05fdaa881ee45b48cbaa5e9b037d667a47745e
index ae7765a70..ae7765a70 100644
--- a/tests-clar/resources/deprecated-mode.git/objects/1b/05fdaa881ee45b48cbaa5e9b037d667a47745e
+++ b/tests/resources/deprecated-mode.git/objects/1b/05fdaa881ee45b48cbaa5e9b037d667a47745e
Binary files differ
diff --git a/tests-clar/resources/deprecated-mode.git/objects/3d/0970ec547fc41ef8a5882dde99c6adce65b021 b/tests/resources/deprecated-mode.git/objects/3d/0970ec547fc41ef8a5882dde99c6adce65b021
index 595283e89..595283e89 100644
--- a/tests-clar/resources/deprecated-mode.git/objects/3d/0970ec547fc41ef8a5882dde99c6adce65b021
+++ b/tests/resources/deprecated-mode.git/objects/3d/0970ec547fc41ef8a5882dde99c6adce65b021
Binary files differ
diff --git a/tests-clar/resources/deprecated-mode.git/refs/heads/master b/tests/resources/deprecated-mode.git/refs/heads/master
index 1e106dfa4..1e106dfa4 100644
--- a/tests-clar/resources/deprecated-mode.git/refs/heads/master
+++ b/tests/resources/deprecated-mode.git/refs/heads/master
diff --git a/tests-clar/resources/duplicate.git/HEAD b/tests/resources/diff/.gitted/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/duplicate.git/HEAD
+++ b/tests/resources/diff/.gitted/HEAD
diff --git a/tests-clar/resources/diff/.gitted/config b/tests/resources/diff/.gitted/config
index 77a27ef1d..77a27ef1d 100644
--- a/tests-clar/resources/diff/.gitted/config
+++ b/tests/resources/diff/.gitted/config
diff --git a/tests-clar/resources/duplicate.git/description b/tests/resources/diff/.gitted/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/duplicate.git/description
+++ b/tests/resources/diff/.gitted/description
diff --git a/tests-clar/resources/diff/.gitted/index b/tests/resources/diff/.gitted/index
index e1071874e..e1071874e 100644
--- a/tests-clar/resources/diff/.gitted/index
+++ b/tests/resources/diff/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/diff/.gitted/info/exclude b/tests/resources/diff/.gitted/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/diff/.gitted/info/exclude
+++ b/tests/resources/diff/.gitted/info/exclude
diff --git a/tests-clar/resources/diff/.gitted/logs/HEAD b/tests/resources/diff/.gitted/logs/HEAD
index 8c6f6fd18..8c6f6fd18 100644
--- a/tests-clar/resources/diff/.gitted/logs/HEAD
+++ b/tests/resources/diff/.gitted/logs/HEAD
diff --git a/tests-clar/resources/diff/.gitted/logs/refs/heads/master b/tests/resources/diff/.gitted/logs/refs/heads/master
index 8c6f6fd18..8c6f6fd18 100644
--- a/tests-clar/resources/diff/.gitted/logs/refs/heads/master
+++ b/tests/resources/diff/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/diff/.gitted/objects/29/ab7053bb4dde0298e03e2c179e890b7dd465a7 b/tests/resources/diff/.gitted/objects/29/ab7053bb4dde0298e03e2c179e890b7dd465a7
index 94f9a676d..94f9a676d 100644
--- a/tests-clar/resources/diff/.gitted/objects/29/ab7053bb4dde0298e03e2c179e890b7dd465a7
+++ b/tests/resources/diff/.gitted/objects/29/ab7053bb4dde0298e03e2c179e890b7dd465a7
Binary files differ
diff --git a/tests-clar/resources/diff/.gitted/objects/3e/5bcbad2a68e5bc60a53b8388eea53a1a7ab847 b/tests/resources/diff/.gitted/objects/3e/5bcbad2a68e5bc60a53b8388eea53a1a7ab847
index 9fed523dc..9fed523dc 100644
--- a/tests-clar/resources/diff/.gitted/objects/3e/5bcbad2a68e5bc60a53b8388eea53a1a7ab847
+++ b/tests/resources/diff/.gitted/objects/3e/5bcbad2a68e5bc60a53b8388eea53a1a7ab847
Binary files differ
diff --git a/tests-clar/resources/diff/.gitted/objects/54/6c735f16a3b44d9784075c2c0dab2ac9bf1989 b/tests/resources/diff/.gitted/objects/54/6c735f16a3b44d9784075c2c0dab2ac9bf1989
index d7df4d6a1..d7df4d6a1 100644
--- a/tests-clar/resources/diff/.gitted/objects/54/6c735f16a3b44d9784075c2c0dab2ac9bf1989
+++ b/tests/resources/diff/.gitted/objects/54/6c735f16a3b44d9784075c2c0dab2ac9bf1989
Binary files differ
diff --git a/tests-clar/resources/diff/.gitted/objects/7a/9e0b02e63179929fed24f0a3e0f19168114d10 b/tests/resources/diff/.gitted/objects/7a/9e0b02e63179929fed24f0a3e0f19168114d10
index 9bc25eb34..9bc25eb34 100644
--- a/tests-clar/resources/diff/.gitted/objects/7a/9e0b02e63179929fed24f0a3e0f19168114d10
+++ b/tests/resources/diff/.gitted/objects/7a/9e0b02e63179929fed24f0a3e0f19168114d10
Binary files differ
diff --git a/tests-clar/resources/diff/.gitted/objects/7b/808f723a8ca90df319682c221187235af76693 b/tests/resources/diff/.gitted/objects/7b/808f723a8ca90df319682c221187235af76693
index 2fd266be6..2fd266be6 100644
--- a/tests-clar/resources/diff/.gitted/objects/7b/808f723a8ca90df319682c221187235af76693
+++ b/tests/resources/diff/.gitted/objects/7b/808f723a8ca90df319682c221187235af76693
Binary files differ
diff --git a/tests-clar/resources/diff/.gitted/objects/88/789109439c1e1c3cd45224001edee5304ed53c b/tests/resources/diff/.gitted/objects/88/789109439c1e1c3cd45224001edee5304ed53c
index 7598b5914..7598b5914 100644
--- a/tests-clar/resources/diff/.gitted/objects/88/789109439c1e1c3cd45224001edee5304ed53c
+++ b/tests/resources/diff/.gitted/objects/88/789109439c1e1c3cd45224001edee5304ed53c
diff --git a/tests-clar/resources/diff/.gitted/objects/cb/8294e696339863df760b2ff5d1e275bee72455 b/tests/resources/diff/.gitted/objects/cb/8294e696339863df760b2ff5d1e275bee72455
index 86ebe04fe..86ebe04fe 100644
--- a/tests-clar/resources/diff/.gitted/objects/cb/8294e696339863df760b2ff5d1e275bee72455
+++ b/tests/resources/diff/.gitted/objects/cb/8294e696339863df760b2ff5d1e275bee72455
Binary files differ
diff --git a/tests-clar/resources/diff/.gitted/objects/d7/0d245ed97ed2aa596dd1af6536e4bfdb047b69 b/tests/resources/diff/.gitted/objects/d7/0d245ed97ed2aa596dd1af6536e4bfdb047b69
index 99304c4aa..99304c4aa 100644
--- a/tests-clar/resources/diff/.gitted/objects/d7/0d245ed97ed2aa596dd1af6536e4bfdb047b69
+++ b/tests/resources/diff/.gitted/objects/d7/0d245ed97ed2aa596dd1af6536e4bfdb047b69
diff --git a/tests-clar/resources/diff/.gitted/refs/heads/master b/tests/resources/diff/.gitted/refs/heads/master
index a83afc38b..a83afc38b 100644
--- a/tests-clar/resources/diff/.gitted/refs/heads/master
+++ b/tests/resources/diff/.gitted/refs/heads/master
diff --git a/tests-clar/resources/diff/another.txt b/tests/resources/diff/another.txt
index d0e0bae4d..d0e0bae4d 100644
--- a/tests-clar/resources/diff/another.txt
+++ b/tests/resources/diff/another.txt
diff --git a/tests-clar/resources/diff/readme.txt b/tests/resources/diff/readme.txt
index beedf288d..beedf288d 100644
--- a/tests-clar/resources/diff/readme.txt
+++ b/tests/resources/diff/readme.txt
diff --git a/tests-clar/resources/duplicate.git/COMMIT_EDITMSG b/tests/resources/duplicate.git/COMMIT_EDITMSG
index 01f9a2aac..01f9a2aac 100644
--- a/tests-clar/resources/duplicate.git/COMMIT_EDITMSG
+++ b/tests/resources/duplicate.git/COMMIT_EDITMSG
diff --git a/tests-clar/resources/empty_bare.git/HEAD b/tests/resources/duplicate.git/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/empty_bare.git/HEAD
+++ b/tests/resources/duplicate.git/HEAD
diff --git a/tests-clar/resources/short_tag.git/config b/tests/resources/duplicate.git/config
index a4ef456cb..a4ef456cb 100644
--- a/tests-clar/resources/short_tag.git/config
+++ b/tests/resources/duplicate.git/config
diff --git a/tests-clar/resources/empty_bare.git/description b/tests/resources/duplicate.git/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/empty_bare.git/description
+++ b/tests/resources/duplicate.git/description
diff --git a/tests-clar/resources/duplicate.git/index b/tests/resources/duplicate.git/index
index a61e1c5ca..a61e1c5ca 100644
--- a/tests-clar/resources/duplicate.git/index
+++ b/tests/resources/duplicate.git/index
Binary files differ
diff --git a/tests-clar/resources/duplicate.git/info/exclude b/tests/resources/duplicate.git/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/duplicate.git/info/exclude
+++ b/tests/resources/duplicate.git/info/exclude
diff --git a/tests-clar/resources/duplicate.git/info/refs b/tests/resources/duplicate.git/info/refs
index 3b5bb03be..3b5bb03be 100644
--- a/tests-clar/resources/duplicate.git/info/refs
+++ b/tests/resources/duplicate.git/info/refs
diff --git a/tests-clar/resources/duplicate.git/logs/HEAD b/tests/resources/duplicate.git/logs/HEAD
index be9b4c6cb..be9b4c6cb 100644
--- a/tests-clar/resources/duplicate.git/logs/HEAD
+++ b/tests/resources/duplicate.git/logs/HEAD
diff --git a/tests-clar/resources/duplicate.git/logs/refs/heads/master b/tests/resources/duplicate.git/logs/refs/heads/master
index be9b4c6cb..be9b4c6cb 100644
--- a/tests-clar/resources/duplicate.git/logs/refs/heads/master
+++ b/tests/resources/duplicate.git/logs/refs/heads/master
diff --git a/tests/resources/duplicate.git/objects/0d/deadede9e6d6ccddce0ee1e5749eed0485e5ea b/tests/resources/duplicate.git/objects/0d/deadede9e6d6ccddce0ee1e5749eed0485e5ea
new file mode 100644
index 000000000..47c2a631a
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/0d/deadede9e6d6ccddce0ee1e5749eed0485e5ea
Binary files differ
diff --git a/tests-clar/resources/duplicate.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a b/tests/resources/duplicate.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a
index 6802d4949..6802d4949 100644
--- a/tests-clar/resources/duplicate.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a
+++ b/tests/resources/duplicate.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a
Binary files differ
diff --git a/tests/resources/duplicate.git/objects/info/packs b/tests/resources/duplicate.git/objects/info/packs
new file mode 100644
index 000000000..d0fdf905e
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/info/packs
@@ -0,0 +1,3 @@
+P pack-e87994ad581c9af946de0eb890175c08cd005f38.pack
+P pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.pack
+
diff --git a/tests/resources/duplicate.git/objects/pack/pack-29a4896f0a0b9c9947b0927c57a5c03dcae052e3.idx b/tests/resources/duplicate.git/objects/pack/pack-29a4896f0a0b9c9947b0927c57a5c03dcae052e3.idx
new file mode 100644
index 000000000..acbed82b6
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/pack/pack-29a4896f0a0b9c9947b0927c57a5c03dcae052e3.idx
Binary files differ
diff --git a/tests/resources/duplicate.git/objects/pack/pack-29a4896f0a0b9c9947b0927c57a5c03dcae052e3.pack b/tests/resources/duplicate.git/objects/pack/pack-29a4896f0a0b9c9947b0927c57a5c03dcae052e3.pack
new file mode 100644
index 000000000..652b0c91f
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/pack/pack-29a4896f0a0b9c9947b0927c57a5c03dcae052e3.pack
Binary files differ
diff --git a/tests/resources/duplicate.git/objects/pack/pack-b18eeacbd65cbd30a365d7564b45a468e8bd43d6.idx b/tests/resources/duplicate.git/objects/pack/pack-b18eeacbd65cbd30a365d7564b45a468e8bd43d6.idx
new file mode 100644
index 000000000..fff685554
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/pack/pack-b18eeacbd65cbd30a365d7564b45a468e8bd43d6.idx
Binary files differ
diff --git a/tests/resources/duplicate.git/objects/pack/pack-b18eeacbd65cbd30a365d7564b45a468e8bd43d6.pack b/tests/resources/duplicate.git/objects/pack/pack-b18eeacbd65cbd30a365d7564b45a468e8bd43d6.pack
new file mode 100644
index 000000000..e3e5f0e09
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/pack/pack-b18eeacbd65cbd30a365d7564b45a468e8bd43d6.pack
Binary files differ
diff --git a/tests-clar/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.idx b/tests/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.idx
index fd8abee98..fd8abee98 100644
--- a/tests-clar/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.idx
+++ b/tests/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.idx
Binary files differ
diff --git a/tests-clar/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.pack b/tests/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.pack
index 9879e0869..9879e0869 100644
--- a/tests-clar/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.pack
+++ b/tests/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.pack
Binary files differ
diff --git a/tests/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.idx b/tests/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.idx
new file mode 100644
index 000000000..9f78f6e0f
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.idx
Binary files differ
diff --git a/tests/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.pack b/tests/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.pack
new file mode 100644
index 000000000..d1dd3b61a
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.pack
Binary files differ
diff --git a/tests-clar/resources/duplicate.git/packed-refs b/tests/resources/duplicate.git/packed-refs
index 9f0d4e434..9f0d4e434 100644
--- a/tests-clar/resources/duplicate.git/packed-refs
+++ b/tests/resources/duplicate.git/packed-refs
diff --git a/tests/resources/duplicate.git/refs/heads/dummy-marker.txt b/tests/resources/duplicate.git/refs/heads/dummy-marker.txt
new file mode 100644
index 000000000..421376db9
--- /dev/null
+++ b/tests/resources/duplicate.git/refs/heads/dummy-marker.txt
@@ -0,0 +1 @@
+dummy
diff --git a/tests-clar/resources/empty_standard_repo/.gitted/HEAD b/tests/resources/empty_bare.git/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/empty_standard_repo/.gitted/HEAD
+++ b/tests/resources/empty_bare.git/HEAD
diff --git a/tests-clar/resources/empty_bare.git/config b/tests/resources/empty_bare.git/config
index 90e16477b..90e16477b 100644
--- a/tests-clar/resources/empty_bare.git/config
+++ b/tests/resources/empty_bare.git/config
diff --git a/tests-clar/resources/empty_standard_repo/.gitted/description b/tests/resources/empty_bare.git/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/empty_standard_repo/.gitted/description
+++ b/tests/resources/empty_bare.git/description
diff --git a/tests-clar/resources/empty_bare.git/info/exclude b/tests/resources/empty_bare.git/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/empty_bare.git/info/exclude
+++ b/tests/resources/empty_bare.git/info/exclude
diff --git a/tests-clar/resources/empty_bare.git/objects/info/dummy-marker.txt b/tests/resources/empty_bare.git/objects/info/dummy-marker.txt
index e69de29bb..e69de29bb 100644
--- a/tests-clar/resources/empty_bare.git/objects/info/dummy-marker.txt
+++ b/tests/resources/empty_bare.git/objects/info/dummy-marker.txt
diff --git a/tests-clar/resources/empty_bare.git/objects/pack/dummy-marker.txt b/tests/resources/empty_bare.git/objects/pack/dummy-marker.txt
index e69de29bb..e69de29bb 100644
--- a/tests-clar/resources/empty_bare.git/objects/pack/dummy-marker.txt
+++ b/tests/resources/empty_bare.git/objects/pack/dummy-marker.txt
diff --git a/tests-clar/resources/empty_bare.git/refs/heads/dummy-marker.txt b/tests/resources/empty_bare.git/refs/heads/dummy-marker.txt
index e69de29bb..e69de29bb 100644
--- a/tests-clar/resources/empty_bare.git/refs/heads/dummy-marker.txt
+++ b/tests/resources/empty_bare.git/refs/heads/dummy-marker.txt
diff --git a/tests-clar/resources/empty_bare.git/refs/tags/dummy-marker.txt b/tests/resources/empty_bare.git/refs/tags/dummy-marker.txt
index e69de29bb..e69de29bb 100644
--- a/tests-clar/resources/empty_bare.git/refs/tags/dummy-marker.txt
+++ b/tests/resources/empty_bare.git/refs/tags/dummy-marker.txt
diff --git a/tests-clar/resources/filemodes/.gitted/HEAD b/tests/resources/empty_standard_repo/.gitted/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/filemodes/.gitted/HEAD
+++ b/tests/resources/empty_standard_repo/.gitted/HEAD
diff --git a/tests-clar/resources/empty_standard_repo/.gitted/config b/tests/resources/empty_standard_repo/.gitted/config
index 78387c50b..78387c50b 100644
--- a/tests-clar/resources/empty_standard_repo/.gitted/config
+++ b/tests/resources/empty_standard_repo/.gitted/config
diff --git a/tests-clar/resources/filemodes/.gitted/description b/tests/resources/empty_standard_repo/.gitted/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/filemodes/.gitted/description
+++ b/tests/resources/empty_standard_repo/.gitted/description
diff --git a/tests-clar/resources/empty_standard_repo/.gitted/info/exclude b/tests/resources/empty_standard_repo/.gitted/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/empty_standard_repo/.gitted/info/exclude
+++ b/tests/resources/empty_standard_repo/.gitted/info/exclude
diff --git a/tests-clar/resources/empty_standard_repo/.gitted/objects/info/dummy-marker.txt b/tests/resources/empty_standard_repo/.gitted/objects/info/dummy-marker.txt
index e69de29bb..e69de29bb 100644
--- a/tests-clar/resources/empty_standard_repo/.gitted/objects/info/dummy-marker.txt
+++ b/tests/resources/empty_standard_repo/.gitted/objects/info/dummy-marker.txt
diff --git a/tests-clar/resources/empty_standard_repo/.gitted/objects/pack/dummy-marker.txt b/tests/resources/empty_standard_repo/.gitted/objects/pack/dummy-marker.txt
index e69de29bb..e69de29bb 100644
--- a/tests-clar/resources/empty_standard_repo/.gitted/objects/pack/dummy-marker.txt
+++ b/tests/resources/empty_standard_repo/.gitted/objects/pack/dummy-marker.txt
diff --git a/tests-clar/resources/empty_standard_repo/.gitted/refs/heads/dummy-marker.txt b/tests/resources/empty_standard_repo/.gitted/refs/heads/dummy-marker.txt
index e69de29bb..e69de29bb 100644
--- a/tests-clar/resources/empty_standard_repo/.gitted/refs/heads/dummy-marker.txt
+++ b/tests/resources/empty_standard_repo/.gitted/refs/heads/dummy-marker.txt
diff --git a/tests-clar/resources/empty_standard_repo/.gitted/refs/tags/dummy-marker.txt b/tests/resources/empty_standard_repo/.gitted/refs/tags/dummy-marker.txt
index e69de29bb..e69de29bb 100644
--- a/tests-clar/resources/empty_standard_repo/.gitted/refs/tags/dummy-marker.txt
+++ b/tests/resources/empty_standard_repo/.gitted/refs/tags/dummy-marker.txt
diff --git a/tests-clar/resources/icase/.gitted/HEAD b/tests/resources/filemodes/.gitted/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/icase/.gitted/HEAD
+++ b/tests/resources/filemodes/.gitted/HEAD
diff --git a/tests-clar/resources/filemodes/.gitted/config b/tests/resources/filemodes/.gitted/config
index af107929f..af107929f 100644
--- a/tests-clar/resources/filemodes/.gitted/config
+++ b/tests/resources/filemodes/.gitted/config
diff --git a/tests-clar/resources/icase/.gitted/description b/tests/resources/filemodes/.gitted/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/icase/.gitted/description
+++ b/tests/resources/filemodes/.gitted/description
diff --git a/tests-clar/resources/filemodes/.gitted/index b/tests/resources/filemodes/.gitted/index
index b1b175a9b..b1b175a9b 100644
--- a/tests-clar/resources/filemodes/.gitted/index
+++ b/tests/resources/filemodes/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/filemodes/.gitted/info/exclude b/tests/resources/filemodes/.gitted/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/filemodes/.gitted/info/exclude
+++ b/tests/resources/filemodes/.gitted/info/exclude
diff --git a/tests-clar/resources/filemodes/.gitted/logs/HEAD b/tests/resources/filemodes/.gitted/logs/HEAD
index 1cb6a84c1..1cb6a84c1 100644
--- a/tests-clar/resources/filemodes/.gitted/logs/HEAD
+++ b/tests/resources/filemodes/.gitted/logs/HEAD
diff --git a/tests-clar/resources/filemodes/.gitted/logs/refs/heads/master b/tests/resources/filemodes/.gitted/logs/refs/heads/master
index 1cb6a84c1..1cb6a84c1 100644
--- a/tests-clar/resources/filemodes/.gitted/logs/refs/heads/master
+++ b/tests/resources/filemodes/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/filemodes/.gitted/objects/99/62c8453ba6f0cf8dac7c5dcc2fa2897fa9964a b/tests/resources/filemodes/.gitted/objects/99/62c8453ba6f0cf8dac7c5dcc2fa2897fa9964a
index cbd2b557a..cbd2b557a 100644
--- a/tests-clar/resources/filemodes/.gitted/objects/99/62c8453ba6f0cf8dac7c5dcc2fa2897fa9964a
+++ b/tests/resources/filemodes/.gitted/objects/99/62c8453ba6f0cf8dac7c5dcc2fa2897fa9964a
Binary files differ
diff --git a/tests-clar/resources/filemodes/.gitted/objects/a5/c5dd0fc6c313159a69b1d19d7f61a9f978e8f1 b/tests/resources/filemodes/.gitted/objects/a5/c5dd0fc6c313159a69b1d19d7f61a9f978e8f1
index a9eaf2cba..a9eaf2cba 100644
--- a/tests-clar/resources/filemodes/.gitted/objects/a5/c5dd0fc6c313159a69b1d19d7f61a9f978e8f1
+++ b/tests/resources/filemodes/.gitted/objects/a5/c5dd0fc6c313159a69b1d19d7f61a9f978e8f1
Binary files differ
diff --git a/tests-clar/resources/filemodes/.gitted/objects/e7/48d196331bcb20267eaaee4ff3326cb73b8182 b/tests/resources/filemodes/.gitted/objects/e7/48d196331bcb20267eaaee4ff3326cb73b8182
index 98066029c..98066029c 100644
--- a/tests-clar/resources/filemodes/.gitted/objects/e7/48d196331bcb20267eaaee4ff3326cb73b8182
+++ b/tests/resources/filemodes/.gitted/objects/e7/48d196331bcb20267eaaee4ff3326cb73b8182
Binary files differ
diff --git a/tests-clar/resources/filemodes/.gitted/refs/heads/master b/tests/resources/filemodes/.gitted/refs/heads/master
index 9822d2d3f..9822d2d3f 100644
--- a/tests-clar/resources/filemodes/.gitted/refs/heads/master
+++ b/tests/resources/filemodes/.gitted/refs/heads/master
diff --git a/tests-clar/resources/filemodes/exec_off b/tests/resources/filemodes/exec_off
index a5c5dd0fc..a5c5dd0fc 100644
--- a/tests-clar/resources/filemodes/exec_off
+++ b/tests/resources/filemodes/exec_off
diff --git a/tests-clar/resources/filemodes/exec_off2on_staged b/tests/resources/filemodes/exec_off2on_staged
index a5c5dd0fc..a5c5dd0fc 100755
--- a/tests-clar/resources/filemodes/exec_off2on_staged
+++ b/tests/resources/filemodes/exec_off2on_staged
diff --git a/tests-clar/resources/filemodes/exec_off2on_workdir b/tests/resources/filemodes/exec_off2on_workdir
index a5c5dd0fc..a5c5dd0fc 100755
--- a/tests-clar/resources/filemodes/exec_off2on_workdir
+++ b/tests/resources/filemodes/exec_off2on_workdir
diff --git a/tests-clar/resources/filemodes/exec_off_untracked b/tests/resources/filemodes/exec_off_untracked
index a5c5dd0fc..a5c5dd0fc 100644
--- a/tests-clar/resources/filemodes/exec_off_untracked
+++ b/tests/resources/filemodes/exec_off_untracked
diff --git a/tests-clar/resources/filemodes/exec_on b/tests/resources/filemodes/exec_on
index a5c5dd0fc..a5c5dd0fc 100755
--- a/tests-clar/resources/filemodes/exec_on
+++ b/tests/resources/filemodes/exec_on
diff --git a/tests-clar/resources/filemodes/exec_on2off_staged b/tests/resources/filemodes/exec_on2off_staged
index a5c5dd0fc..a5c5dd0fc 100644
--- a/tests-clar/resources/filemodes/exec_on2off_staged
+++ b/tests/resources/filemodes/exec_on2off_staged
diff --git a/tests-clar/resources/filemodes/exec_on2off_workdir b/tests/resources/filemodes/exec_on2off_workdir
index a5c5dd0fc..a5c5dd0fc 100644
--- a/tests-clar/resources/filemodes/exec_on2off_workdir
+++ b/tests/resources/filemodes/exec_on2off_workdir
diff --git a/tests-clar/resources/filemodes/exec_on_untracked b/tests/resources/filemodes/exec_on_untracked
index a5c5dd0fc..a5c5dd0fc 100755
--- a/tests-clar/resources/filemodes/exec_on_untracked
+++ b/tests/resources/filemodes/exec_on_untracked
diff --git a/tests-clar/resources/gitgit.index b/tests/resources/gitgit.index
index 215da649e..215da649e 100644
--- a/tests-clar/resources/gitgit.index
+++ b/tests/resources/gitgit.index
Binary files differ
diff --git a/tests-clar/resources/issue_1397/.gitted/HEAD b/tests/resources/icase/.gitted/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/issue_1397/.gitted/HEAD
+++ b/tests/resources/icase/.gitted/HEAD
diff --git a/tests-clar/resources/icase/.gitted/config b/tests/resources/icase/.gitted/config
index bb4d11c1f..bb4d11c1f 100644
--- a/tests-clar/resources/icase/.gitted/config
+++ b/tests/resources/icase/.gitted/config
diff --git a/tests-clar/resources/issue_592b/.gitted/description b/tests/resources/icase/.gitted/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/issue_592b/.gitted/description
+++ b/tests/resources/icase/.gitted/description
diff --git a/tests-clar/resources/icase/.gitted/index b/tests/resources/icase/.gitted/index
index f8288ec13..f8288ec13 100644
--- a/tests-clar/resources/icase/.gitted/index
+++ b/tests/resources/icase/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/icase/.gitted/info/exclude b/tests/resources/icase/.gitted/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/icase/.gitted/info/exclude
+++ b/tests/resources/icase/.gitted/info/exclude
diff --git a/tests-clar/resources/icase/.gitted/logs/HEAD b/tests/resources/icase/.gitted/logs/HEAD
index 3b16bd163..3b16bd163 100644
--- a/tests-clar/resources/icase/.gitted/logs/HEAD
+++ b/tests/resources/icase/.gitted/logs/HEAD
diff --git a/tests-clar/resources/icase/.gitted/logs/refs/heads/master b/tests/resources/icase/.gitted/logs/refs/heads/master
index 3b16bd163..3b16bd163 100644
--- a/tests-clar/resources/icase/.gitted/logs/refs/heads/master
+++ b/tests/resources/icase/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/icase/.gitted/objects/3e/257c57f136a1cb8f2b8e9a2e5bc8ec0258bdce b/tests/resources/icase/.gitted/objects/3e/257c57f136a1cb8f2b8e9a2e5bc8ec0258bdce
index 10691c788..10691c788 100644
--- a/tests-clar/resources/icase/.gitted/objects/3e/257c57f136a1cb8f2b8e9a2e5bc8ec0258bdce
+++ b/tests/resources/icase/.gitted/objects/3e/257c57f136a1cb8f2b8e9a2e5bc8ec0258bdce
Binary files differ
diff --git a/tests-clar/resources/icase/.gitted/objects/4d/d6027d083575c7431396dc2a3174afeb393c93 b/tests/resources/icase/.gitted/objects/4d/d6027d083575c7431396dc2a3174afeb393c93
index 8ca70df17..8ca70df17 100644
--- a/tests-clar/resources/icase/.gitted/objects/4d/d6027d083575c7431396dc2a3174afeb393c93
+++ b/tests/resources/icase/.gitted/objects/4d/d6027d083575c7431396dc2a3174afeb393c93
Binary files differ
diff --git a/tests-clar/resources/icase/.gitted/objects/62/e0af52c199ec731fe4ad230041cd3286192d49 b/tests/resources/icase/.gitted/objects/62/e0af52c199ec731fe4ad230041cd3286192d49
index e264aeab3..e264aeab3 100644
--- a/tests-clar/resources/icase/.gitted/objects/62/e0af52c199ec731fe4ad230041cd3286192d49
+++ b/tests/resources/icase/.gitted/objects/62/e0af52c199ec731fe4ad230041cd3286192d49
Binary files differ
diff --git a/tests-clar/resources/icase/.gitted/objects/76/d6e1d231b1085fcce151427e9899335de74be6 b/tests/resources/icase/.gitted/objects/76/d6e1d231b1085fcce151427e9899335de74be6
index 24a4b3ee3..24a4b3ee3 100644
--- a/tests-clar/resources/icase/.gitted/objects/76/d6e1d231b1085fcce151427e9899335de74be6
+++ b/tests/resources/icase/.gitted/objects/76/d6e1d231b1085fcce151427e9899335de74be6
diff --git a/tests-clar/resources/icase/.gitted/objects/d4/4e18fb93b7107b5cd1b95d601591d77869a1b6 b/tests/resources/icase/.gitted/objects/d4/4e18fb93b7107b5cd1b95d601591d77869a1b6
index 32d8c499f..32d8c499f 100644
--- a/tests-clar/resources/icase/.gitted/objects/d4/4e18fb93b7107b5cd1b95d601591d77869a1b6
+++ b/tests/resources/icase/.gitted/objects/d4/4e18fb93b7107b5cd1b95d601591d77869a1b6
Binary files differ
diff --git a/tests-clar/resources/icase/.gitted/refs/heads/master b/tests/resources/icase/.gitted/refs/heads/master
index 37410ec2a..37410ec2a 100644
--- a/tests-clar/resources/icase/.gitted/refs/heads/master
+++ b/tests/resources/icase/.gitted/refs/heads/master
diff --git a/tests-clar/resources/icase/B b/tests/resources/icase/B
index d44e18fb9..d44e18fb9 100644
--- a/tests-clar/resources/icase/B
+++ b/tests/resources/icase/B
diff --git a/tests-clar/resources/icase/D b/tests/resources/icase/D
index d44e18fb9..d44e18fb9 100644
--- a/tests-clar/resources/icase/D
+++ b/tests/resources/icase/D
diff --git a/tests-clar/resources/icase/F b/tests/resources/icase/F
index d44e18fb9..d44e18fb9 100644
--- a/tests-clar/resources/icase/F
+++ b/tests/resources/icase/F
diff --git a/tests-clar/resources/icase/H b/tests/resources/icase/H
index d44e18fb9..d44e18fb9 100644
--- a/tests-clar/resources/icase/H
+++ b/tests/resources/icase/H
diff --git a/tests-clar/resources/icase/J b/tests/resources/icase/J
index d44e18fb9..d44e18fb9 100644
--- a/tests-clar/resources/icase/J
+++ b/tests/resources/icase/J
diff --git a/tests-clar/resources/icase/L/1 b/tests/resources/icase/L/1
index 62e0af52c..62e0af52c 100644
--- a/tests-clar/resources/icase/L/1
+++ b/tests/resources/icase/L/1
diff --git a/tests-clar/resources/icase/L/B b/tests/resources/icase/L/B
index 62e0af52c..62e0af52c 100644
--- a/tests-clar/resources/icase/L/B
+++ b/tests/resources/icase/L/B
diff --git a/tests-clar/resources/icase/L/D b/tests/resources/icase/L/D
index 62e0af52c..62e0af52c 100644
--- a/tests-clar/resources/icase/L/D
+++ b/tests/resources/icase/L/D
diff --git a/tests-clar/resources/icase/L/a b/tests/resources/icase/L/a
index 62e0af52c..62e0af52c 100644
--- a/tests-clar/resources/icase/L/a
+++ b/tests/resources/icase/L/a
diff --git a/tests-clar/resources/icase/L/c b/tests/resources/icase/L/c
index 62e0af52c..62e0af52c 100644
--- a/tests-clar/resources/icase/L/c
+++ b/tests/resources/icase/L/c
diff --git a/tests-clar/resources/icase/a b/tests/resources/icase/a
index d44e18fb9..d44e18fb9 100644
--- a/tests-clar/resources/icase/a
+++ b/tests/resources/icase/a
diff --git a/tests-clar/resources/icase/c b/tests/resources/icase/c
index d44e18fb9..d44e18fb9 100644
--- a/tests-clar/resources/icase/c
+++ b/tests/resources/icase/c
diff --git a/tests-clar/resources/icase/e b/tests/resources/icase/e
index d44e18fb9..d44e18fb9 100644
--- a/tests-clar/resources/icase/e
+++ b/tests/resources/icase/e
diff --git a/tests-clar/resources/icase/g b/tests/resources/icase/g
index d44e18fb9..d44e18fb9 100644
--- a/tests-clar/resources/icase/g
+++ b/tests/resources/icase/g
diff --git a/tests-clar/resources/icase/i b/tests/resources/icase/i
index d44e18fb9..d44e18fb9 100644
--- a/tests-clar/resources/icase/i
+++ b/tests/resources/icase/i
diff --git a/tests-clar/resources/icase/k/1 b/tests/resources/icase/k/1
index 62e0af52c..62e0af52c 100644
--- a/tests-clar/resources/icase/k/1
+++ b/tests/resources/icase/k/1
diff --git a/tests-clar/resources/icase/k/B b/tests/resources/icase/k/B
index 62e0af52c..62e0af52c 100644
--- a/tests-clar/resources/icase/k/B
+++ b/tests/resources/icase/k/B
diff --git a/tests-clar/resources/icase/k/D b/tests/resources/icase/k/D
index 62e0af52c..62e0af52c 100644
--- a/tests-clar/resources/icase/k/D
+++ b/tests/resources/icase/k/D
diff --git a/tests-clar/resources/icase/k/a b/tests/resources/icase/k/a
index 62e0af52c..62e0af52c 100644
--- a/tests-clar/resources/icase/k/a
+++ b/tests/resources/icase/k/a
diff --git a/tests-clar/resources/icase/k/c b/tests/resources/icase/k/c
index 62e0af52c..62e0af52c 100644
--- a/tests-clar/resources/icase/k/c
+++ b/tests/resources/icase/k/c
diff --git a/tests-clar/resources/issue_592/.gitted/HEAD b/tests/resources/issue_1397/.gitted/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/issue_592/.gitted/HEAD
+++ b/tests/resources/issue_1397/.gitted/HEAD
diff --git a/tests-clar/resources/issue_1397/.gitted/config b/tests/resources/issue_1397/.gitted/config
index ba5bbde24..ba5bbde24 100644
--- a/tests-clar/resources/issue_1397/.gitted/config
+++ b/tests/resources/issue_1397/.gitted/config
diff --git a/tests-clar/resources/issue_1397/.gitted/index b/tests/resources/issue_1397/.gitted/index
index fa0f541d6..fa0f541d6 100644
--- a/tests-clar/resources/issue_1397/.gitted/index
+++ b/tests/resources/issue_1397/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/issue_1397/.gitted/objects/7f/483a738f867e5b21c8f377d70311f011eb48b5 b/tests/resources/issue_1397/.gitted/objects/7f/483a738f867e5b21c8f377d70311f011eb48b5
index 63bcb5d76..63bcb5d76 100644
--- a/tests-clar/resources/issue_1397/.gitted/objects/7f/483a738f867e5b21c8f377d70311f011eb48b5
+++ b/tests/resources/issue_1397/.gitted/objects/7f/483a738f867e5b21c8f377d70311f011eb48b5
diff --git a/tests-clar/resources/issue_1397/.gitted/objects/83/12e0889a9cbab77c732b6bc39b51a683e3a318 b/tests/resources/issue_1397/.gitted/objects/83/12e0889a9cbab77c732b6bc39b51a683e3a318
index 06b59fede..06b59fede 100644
--- a/tests-clar/resources/issue_1397/.gitted/objects/83/12e0889a9cbab77c732b6bc39b51a683e3a318
+++ b/tests/resources/issue_1397/.gitted/objects/83/12e0889a9cbab77c732b6bc39b51a683e3a318
Binary files differ
diff --git a/tests-clar/resources/issue_1397/.gitted/objects/8a/7ef047fc933edb62e84e7977b0612ec3f6f283 b/tests/resources/issue_1397/.gitted/objects/8a/7ef047fc933edb62e84e7977b0612ec3f6f283
index 19cfbeae2..19cfbeae2 100644
--- a/tests-clar/resources/issue_1397/.gitted/objects/8a/7ef047fc933edb62e84e7977b0612ec3f6f283
+++ b/tests/resources/issue_1397/.gitted/objects/8a/7ef047fc933edb62e84e7977b0612ec3f6f283
Binary files differ
diff --git a/tests-clar/resources/issue_1397/.gitted/objects/8e/8f80088a9274fd23584992f587083ca1bcbbac b/tests/resources/issue_1397/.gitted/objects/8e/8f80088a9274fd23584992f587083ca1bcbbac
index f5c776b17..f5c776b17 100644
--- a/tests-clar/resources/issue_1397/.gitted/objects/8e/8f80088a9274fd23584992f587083ca1bcbbac
+++ b/tests/resources/issue_1397/.gitted/objects/8e/8f80088a9274fd23584992f587083ca1bcbbac
Binary files differ
diff --git a/tests-clar/resources/issue_1397/.gitted/objects/f2/c62dea0372a0578e053697d5c1ba1ac05e774a b/tests/resources/issue_1397/.gitted/objects/f2/c62dea0372a0578e053697d5c1ba1ac05e774a
index f932f3618..f932f3618 100644
--- a/tests-clar/resources/issue_1397/.gitted/objects/f2/c62dea0372a0578e053697d5c1ba1ac05e774a
+++ b/tests/resources/issue_1397/.gitted/objects/f2/c62dea0372a0578e053697d5c1ba1ac05e774a
Binary files differ
diff --git a/tests-clar/resources/issue_1397/.gitted/objects/ff/3578d64d199d5b48d92bbb569e0a273e411741 b/tests/resources/issue_1397/.gitted/objects/ff/3578d64d199d5b48d92bbb569e0a273e411741
index fbd731727..fbd731727 100644
--- a/tests-clar/resources/issue_1397/.gitted/objects/ff/3578d64d199d5b48d92bbb569e0a273e411741
+++ b/tests/resources/issue_1397/.gitted/objects/ff/3578d64d199d5b48d92bbb569e0a273e411741
Binary files differ
diff --git a/tests-clar/resources/issue_1397/.gitted/refs/heads/master b/tests/resources/issue_1397/.gitted/refs/heads/master
index 285bc08ff..285bc08ff 100644
--- a/tests-clar/resources/issue_1397/.gitted/refs/heads/master
+++ b/tests/resources/issue_1397/.gitted/refs/heads/master
diff --git a/tests-clar/resources/issue_1397/crlf_file.txt b/tests/resources/issue_1397/crlf_file.txt
index 8312e0889..8312e0889 100644
--- a/tests-clar/resources/issue_1397/crlf_file.txt
+++ b/tests/resources/issue_1397/crlf_file.txt
diff --git a/tests-clar/resources/issue_1397/some_other_crlf_file.txt b/tests/resources/issue_1397/some_other_crlf_file.txt
index 8e8f80088..8e8f80088 100644
--- a/tests-clar/resources/issue_1397/some_other_crlf_file.txt
+++ b/tests/resources/issue_1397/some_other_crlf_file.txt
diff --git a/tests-clar/resources/issue_592/.gitted/COMMIT_EDITMSG b/tests/resources/issue_592/.gitted/COMMIT_EDITMSG
index 5852f4463..5852f4463 100644
--- a/tests-clar/resources/issue_592/.gitted/COMMIT_EDITMSG
+++ b/tests/resources/issue_592/.gitted/COMMIT_EDITMSG
diff --git a/tests-clar/resources/issue_592b/.gitted/HEAD b/tests/resources/issue_592/.gitted/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/issue_592b/.gitted/HEAD
+++ b/tests/resources/issue_592/.gitted/HEAD
diff --git a/tests-clar/resources/issue_592/.gitted/config b/tests/resources/issue_592/.gitted/config
index 78387c50b..78387c50b 100644
--- a/tests-clar/resources/issue_592/.gitted/config
+++ b/tests/resources/issue_592/.gitted/config
diff --git a/tests-clar/resources/issue_592/.gitted/index b/tests/resources/issue_592/.gitted/index
index be7a29d99..be7a29d99 100644
--- a/tests-clar/resources/issue_592/.gitted/index
+++ b/tests/resources/issue_592/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/issue_592/.gitted/info/exclude b/tests/resources/issue_592/.gitted/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/issue_592/.gitted/info/exclude
+++ b/tests/resources/issue_592/.gitted/info/exclude
diff --git a/tests-clar/resources/issue_592/.gitted/logs/HEAD b/tests/resources/issue_592/.gitted/logs/HEAD
index f19fe35a6..f19fe35a6 100644
--- a/tests-clar/resources/issue_592/.gitted/logs/HEAD
+++ b/tests/resources/issue_592/.gitted/logs/HEAD
diff --git a/tests-clar/resources/issue_592/.gitted/logs/refs/heads/master b/tests/resources/issue_592/.gitted/logs/refs/heads/master
index f19fe35a6..f19fe35a6 100644
--- a/tests-clar/resources/issue_592/.gitted/logs/refs/heads/master
+++ b/tests/resources/issue_592/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/issue_592/.gitted/objects/06/07ee9d4ccce8e4c4fa13c2c7d727e7faba4e0e b/tests/resources/issue_592/.gitted/objects/06/07ee9d4ccce8e4c4fa13c2c7d727e7faba4e0e
index 05dec10f7..05dec10f7 100644
--- a/tests-clar/resources/issue_592/.gitted/objects/06/07ee9d4ccce8e4c4fa13c2c7d727e7faba4e0e
+++ b/tests/resources/issue_592/.gitted/objects/06/07ee9d4ccce8e4c4fa13c2c7d727e7faba4e0e
Binary files differ
diff --git a/tests-clar/resources/issue_592/.gitted/objects/49/363a72a90d9424240258cd3759f23788ecf1d8 b/tests/resources/issue_592/.gitted/objects/49/363a72a90d9424240258cd3759f23788ecf1d8
index e997e1b49..e997e1b49 100644
--- a/tests-clar/resources/issue_592/.gitted/objects/49/363a72a90d9424240258cd3759f23788ecf1d8
+++ b/tests/resources/issue_592/.gitted/objects/49/363a72a90d9424240258cd3759f23788ecf1d8
Binary files differ
diff --git a/tests-clar/resources/issue_592/.gitted/objects/4d/383e87f0371ba8fa353f3912db6862b2625e85 b/tests/resources/issue_592/.gitted/objects/4d/383e87f0371ba8fa353f3912db6862b2625e85
index c49a8be58..c49a8be58 100644
--- a/tests-clar/resources/issue_592/.gitted/objects/4d/383e87f0371ba8fa353f3912db6862b2625e85
+++ b/tests/resources/issue_592/.gitted/objects/4d/383e87f0371ba8fa353f3912db6862b2625e85
diff --git a/tests-clar/resources/issue_592/.gitted/objects/71/44be264b61825fbff68046fe999bdfe96a1792 b/tests/resources/issue_592/.gitted/objects/71/44be264b61825fbff68046fe999bdfe96a1792
index 25d44d938..25d44d938 100644
--- a/tests-clar/resources/issue_592/.gitted/objects/71/44be264b61825fbff68046fe999bdfe96a1792
+++ b/tests/resources/issue_592/.gitted/objects/71/44be264b61825fbff68046fe999bdfe96a1792
Binary files differ
diff --git a/tests-clar/resources/issue_592/.gitted/objects/be/de83ee10b5b3f00239660b00acec2d55fd0b84 b/tests/resources/issue_592/.gitted/objects/be/de83ee10b5b3f00239660b00acec2d55fd0b84
index 1d6e38d37..1d6e38d37 100644
--- a/tests-clar/resources/issue_592/.gitted/objects/be/de83ee10b5b3f00239660b00acec2d55fd0b84
+++ b/tests/resources/issue_592/.gitted/objects/be/de83ee10b5b3f00239660b00acec2d55fd0b84
Binary files differ
diff --git a/tests-clar/resources/issue_592/.gitted/objects/e3/8fcc7a6060f5eb5b876e836b52ae4769363f21 b/tests/resources/issue_592/.gitted/objects/e3/8fcc7a6060f5eb5b876e836b52ae4769363f21
index 36c5b9aab..36c5b9aab 100644
--- a/tests-clar/resources/issue_592/.gitted/objects/e3/8fcc7a6060f5eb5b876e836b52ae4769363f21
+++ b/tests/resources/issue_592/.gitted/objects/e3/8fcc7a6060f5eb5b876e836b52ae4769363f21
Binary files differ
diff --git a/tests-clar/resources/issue_592/.gitted/objects/f1/adef63cb08891a0942b76fc4b9c50c6c494bc7 b/tests/resources/issue_592/.gitted/objects/f1/adef63cb08891a0942b76fc4b9c50c6c494bc7
index c08ecd5ed..c08ecd5ed 100644
--- a/tests-clar/resources/issue_592/.gitted/objects/f1/adef63cb08891a0942b76fc4b9c50c6c494bc7
+++ b/tests/resources/issue_592/.gitted/objects/f1/adef63cb08891a0942b76fc4b9c50c6c494bc7
Binary files differ
diff --git a/tests-clar/resources/issue_592/.gitted/refs/heads/master b/tests/resources/issue_592/.gitted/refs/heads/master
index 1f6669628..1f6669628 100644
--- a/tests-clar/resources/issue_592/.gitted/refs/heads/master
+++ b/tests/resources/issue_592/.gitted/refs/heads/master
diff --git a/tests-clar/resources/issue_592/a.txt b/tests/resources/issue_592/a.txt
index f1adef63c..f1adef63c 100644
--- a/tests-clar/resources/issue_592/a.txt
+++ b/tests/resources/issue_592/a.txt
diff --git a/tests-clar/resources/issue_592/c/a.txt b/tests/resources/issue_592/c/a.txt
index f1adef63c..f1adef63c 100644
--- a/tests-clar/resources/issue_592/c/a.txt
+++ b/tests/resources/issue_592/c/a.txt
diff --git a/tests-clar/resources/issue_592/l.txt b/tests/resources/issue_592/l.txt
index f1adef63c..f1adef63c 100644
--- a/tests-clar/resources/issue_592/l.txt
+++ b/tests/resources/issue_592/l.txt
diff --git a/tests-clar/resources/issue_592/t/a.txt b/tests/resources/issue_592/t/a.txt
index f1adef63c..f1adef63c 100644
--- a/tests-clar/resources/issue_592/t/a.txt
+++ b/tests/resources/issue_592/t/a.txt
diff --git a/tests-clar/resources/issue_592/t/b.txt b/tests/resources/issue_592/t/b.txt
index f1adef63c..f1adef63c 100644
--- a/tests-clar/resources/issue_592/t/b.txt
+++ b/tests/resources/issue_592/t/b.txt
diff --git a/tests-clar/resources/merge-resolve/.gitted/HEAD b/tests/resources/issue_592b/.gitted/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/merge-resolve/.gitted/HEAD
+++ b/tests/resources/issue_592b/.gitted/HEAD
diff --git a/tests-clar/resources/issue_592b/.gitted/config b/tests/resources/issue_592b/.gitted/config
index af107929f..af107929f 100644
--- a/tests-clar/resources/issue_592b/.gitted/config
+++ b/tests/resources/issue_592b/.gitted/config
diff --git a/tests-clar/resources/merge-resolve/.gitted/description b/tests/resources/issue_592b/.gitted/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/merge-resolve/.gitted/description
+++ b/tests/resources/issue_592b/.gitted/description
diff --git a/tests-clar/resources/issue_592b/.gitted/index b/tests/resources/issue_592b/.gitted/index
index 596438216..596438216 100644
--- a/tests-clar/resources/issue_592b/.gitted/index
+++ b/tests/resources/issue_592b/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/issue_592b/.gitted/info/exclude b/tests/resources/issue_592b/.gitted/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/issue_592b/.gitted/info/exclude
+++ b/tests/resources/issue_592b/.gitted/info/exclude
diff --git a/tests-clar/resources/issue_592b/.gitted/logs/HEAD b/tests/resources/issue_592b/.gitted/logs/HEAD
index 6f3ba90cc..6f3ba90cc 100644
--- a/tests-clar/resources/issue_592b/.gitted/logs/HEAD
+++ b/tests/resources/issue_592b/.gitted/logs/HEAD
diff --git a/tests-clar/resources/issue_592b/.gitted/logs/refs/heads/master b/tests/resources/issue_592b/.gitted/logs/refs/heads/master
index 6f3ba90cc..6f3ba90cc 100644
--- a/tests-clar/resources/issue_592b/.gitted/logs/refs/heads/master
+++ b/tests/resources/issue_592b/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/issue_592b/.gitted/objects/3f/bf1852f72fd268e36457b13a18cdd9a4c9ea35 b/tests/resources/issue_592b/.gitted/objects/3f/bf1852f72fd268e36457b13a18cdd9a4c9ea35
index 6eaf64b46..6eaf64b46 100644
--- a/tests-clar/resources/issue_592b/.gitted/objects/3f/bf1852f72fd268e36457b13a18cdd9a4c9ea35
+++ b/tests/resources/issue_592b/.gitted/objects/3f/bf1852f72fd268e36457b13a18cdd9a4c9ea35
diff --git a/tests-clar/resources/issue_592b/.gitted/objects/6f/a891d3e578c83e1c03bdb9e0fdd8e6e934157f b/tests/resources/issue_592b/.gitted/objects/6f/a891d3e578c83e1c03bdb9e0fdd8e6e934157f
index c4becfe2f..c4becfe2f 100644
--- a/tests-clar/resources/issue_592b/.gitted/objects/6f/a891d3e578c83e1c03bdb9e0fdd8e6e934157f
+++ b/tests/resources/issue_592b/.gitted/objects/6f/a891d3e578c83e1c03bdb9e0fdd8e6e934157f
Binary files differ
diff --git a/tests-clar/resources/issue_592b/.gitted/objects/80/07d41d5794e6ce4d4d2c97e370d5a9aa6d5213 b/tests/resources/issue_592b/.gitted/objects/80/07d41d5794e6ce4d4d2c97e370d5a9aa6d5213
index aea14f2af..aea14f2af 100644
--- a/tests-clar/resources/issue_592b/.gitted/objects/80/07d41d5794e6ce4d4d2c97e370d5a9aa6d5213
+++ b/tests/resources/issue_592b/.gitted/objects/80/07d41d5794e6ce4d4d2c97e370d5a9aa6d5213
Binary files differ
diff --git a/tests-clar/resources/issue_592b/.gitted/objects/a6/5fb6583a7c425284142f285bc359a2d6565513 b/tests/resources/issue_592b/.gitted/objects/a6/5fb6583a7c425284142f285bc359a2d6565513
index 9b7407221..9b7407221 100644
--- a/tests-clar/resources/issue_592b/.gitted/objects/a6/5fb6583a7c425284142f285bc359a2d6565513
+++ b/tests/resources/issue_592b/.gitted/objects/a6/5fb6583a7c425284142f285bc359a2d6565513
Binary files differ
diff --git a/tests-clar/resources/issue_592b/.gitted/objects/ae/be7a55922c7097ef91ca3a7bc327a901d87c2c b/tests/resources/issue_592b/.gitted/objects/ae/be7a55922c7097ef91ca3a7bc327a901d87c2c
index 1494ed822..1494ed822 100644
--- a/tests-clar/resources/issue_592b/.gitted/objects/ae/be7a55922c7097ef91ca3a7bc327a901d87c2c
+++ b/tests/resources/issue_592b/.gitted/objects/ae/be7a55922c7097ef91ca3a7bc327a901d87c2c
Binary files differ
diff --git a/tests-clar/resources/issue_592b/.gitted/objects/b3/44b055867fcdc1f01eaa75056a43e868eb4fbc b/tests/resources/issue_592b/.gitted/objects/b3/44b055867fcdc1f01eaa75056a43e868eb4fbc
index 7a6626636..7a6626636 100644
--- a/tests-clar/resources/issue_592b/.gitted/objects/b3/44b055867fcdc1f01eaa75056a43e868eb4fbc
+++ b/tests/resources/issue_592b/.gitted/objects/b3/44b055867fcdc1f01eaa75056a43e868eb4fbc
Binary files differ
diff --git a/tests-clar/resources/issue_592b/.gitted/objects/f7/d75fbfad8b1d2e307ced287ea78aad403cdce3 b/tests/resources/issue_592b/.gitted/objects/f7/d75fbfad8b1d2e307ced287ea78aad403cdce3
index 65a1fd0d0..65a1fd0d0 100644
--- a/tests-clar/resources/issue_592b/.gitted/objects/f7/d75fbfad8b1d2e307ced287ea78aad403cdce3
+++ b/tests/resources/issue_592b/.gitted/objects/f7/d75fbfad8b1d2e307ced287ea78aad403cdce3
Binary files differ
diff --git a/tests-clar/resources/issue_592b/.gitted/refs/heads/master b/tests/resources/issue_592b/.gitted/refs/heads/master
index c0a9ab495..c0a9ab495 100644
--- a/tests-clar/resources/issue_592b/.gitted/refs/heads/master
+++ b/tests/resources/issue_592b/.gitted/refs/heads/master
diff --git a/tests-clar/resources/issue_592b/gitignore b/tests/resources/issue_592b/gitignore
index 8007d41d5..8007d41d5 100644
--- a/tests-clar/resources/issue_592b/gitignore
+++ b/tests/resources/issue_592b/gitignore
diff --git a/tests-clar/resources/issue_592b/ignored/contained/ignored3.txt b/tests/resources/issue_592b/ignored/contained/ignored3.txt
index b5dc7b073..b5dc7b073 100644
--- a/tests-clar/resources/issue_592b/ignored/contained/ignored3.txt
+++ b/tests/resources/issue_592b/ignored/contained/ignored3.txt
diff --git a/tests-clar/resources/issue_592b/ignored/contained/tracked3.txt b/tests/resources/issue_592b/ignored/contained/tracked3.txt
index b344b0558..b344b0558 100644
--- a/tests-clar/resources/issue_592b/ignored/contained/tracked3.txt
+++ b/tests/resources/issue_592b/ignored/contained/tracked3.txt
diff --git a/tests-clar/resources/issue_592b/ignored/ignored2.txt b/tests/resources/issue_592b/ignored/ignored2.txt
index b5dc7b073..b5dc7b073 100644
--- a/tests-clar/resources/issue_592b/ignored/ignored2.txt
+++ b/tests/resources/issue_592b/ignored/ignored2.txt
diff --git a/tests-clar/resources/issue_592b/ignored/tracked2.txt b/tests/resources/issue_592b/ignored/tracked2.txt
index 6fa891d3e..6fa891d3e 100644
--- a/tests-clar/resources/issue_592b/ignored/tracked2.txt
+++ b/tests/resources/issue_592b/ignored/tracked2.txt
diff --git a/tests-clar/resources/issue_592b/ignored1.txt b/tests/resources/issue_592b/ignored1.txt
index b5dc7b073..b5dc7b073 100644
--- a/tests-clar/resources/issue_592b/ignored1.txt
+++ b/tests/resources/issue_592b/ignored1.txt
diff --git a/tests-clar/resources/issue_592b/tracked1.txt b/tests/resources/issue_592b/tracked1.txt
index 6fa891d3e..6fa891d3e 100644
--- a/tests-clar/resources/issue_592b/tracked1.txt
+++ b/tests/resources/issue_592b/tracked1.txt
diff --git a/tests-clar/resources/merge-resolve/.gitted/COMMIT_EDITMSG b/tests/resources/merge-resolve/.gitted/COMMIT_EDITMSG
index 245b18a2c..245b18a2c 100644
--- a/tests-clar/resources/merge-resolve/.gitted/COMMIT_EDITMSG
+++ b/tests/resources/merge-resolve/.gitted/COMMIT_EDITMSG
diff --git a/tests-clar/resources/mergedrepo/.gitted/HEAD b/tests/resources/merge-resolve/.gitted/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/mergedrepo/.gitted/HEAD
+++ b/tests/resources/merge-resolve/.gitted/HEAD
diff --git a/tests-clar/resources/merge-resolve/.gitted/ORIG_HEAD b/tests/resources/merge-resolve/.gitted/ORIG_HEAD
index 4092d428f..4092d428f 100644
--- a/tests-clar/resources/merge-resolve/.gitted/ORIG_HEAD
+++ b/tests/resources/merge-resolve/.gitted/ORIG_HEAD
diff --git a/tests-clar/resources/merge-resolve/.gitted/config b/tests/resources/merge-resolve/.gitted/config
index af107929f..af107929f 100644
--- a/tests-clar/resources/merge-resolve/.gitted/config
+++ b/tests/resources/merge-resolve/.gitted/config
diff --git a/tests-clar/resources/mergedrepo/.gitted/description b/tests/resources/merge-resolve/.gitted/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/mergedrepo/.gitted/description
+++ b/tests/resources/merge-resolve/.gitted/description
diff --git a/tests-clar/resources/merge-resolve/.gitted/index b/tests/resources/merge-resolve/.gitted/index
index 230eba9eb..230eba9eb 100644
--- a/tests-clar/resources/merge-resolve/.gitted/index
+++ b/tests/resources/merge-resolve/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/HEAD b/tests/resources/merge-resolve/.gitted/logs/HEAD
index 96cdb337e..96cdb337e 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/HEAD
+++ b/tests/resources/merge-resolve/.gitted/logs/HEAD
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/branch
index 8b0acb702..8b0acb702 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/branch
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/df_ancestor b/tests/resources/merge-resolve/.gitted/logs/refs/heads/df_ancestor
index df7695a66..df7695a66 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/df_ancestor
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/df_ancestor
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/df_side1 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/df_side1
index a504ad610..a504ad610 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/df_side1
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/df_side1
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/df_side2 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/df_side2
index 27d833eda..27d833eda 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/df_side2
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/df_side2
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/ff_branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/ff_branch
index c4706175d..c4706175d 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/ff_branch
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/ff_branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/master b/tests/resources/merge-resolve/.gitted/logs/refs/heads/master
index 60475992a..60475992a 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/master
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo1 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo1
index 0b6c9214a..0b6c9214a 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo1
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo1
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo2 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo2
index 5392a4f86..5392a4f86 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo2
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo2
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo3 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo3
index 7db5617c8..7db5617c8 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo3
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo3
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo4 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo4
index b0f9e42ef..b0f9e42ef 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo4
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo4
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo5 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo5
index 614563edf..614563edf 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo5
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo5
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo6 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo6
index 4c812eacc..4c812eacc 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo6
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo6
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/renames1 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/renames1
index 58a7e0565..58a7e0565 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/renames1
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/renames1
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/renames2 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/renames2
index 5645ecee7..5645ecee7 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/renames2
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/renames2
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10
index b6bd247e7..b6bd247e7 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10-branch
index 14ce9e545..14ce9e545 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10-branch
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11
index 3e6b77437..3e6b77437 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11-branch
index 30d5ec7a3..30d5ec7a3 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11-branch
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13
index 3a7302dea..3a7302dea 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13-branch
index bb2604244..bb2604244 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13-branch
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14
index 4b70d2898..4b70d2898 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14-branch
index 8e491ca68..8e491ca68 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14-branch
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt
index a2a28d401..a2a28d401 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt-branch
index a0a48ae35..a0a48ae35 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt-branch
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt
index 4374d3888..4374d3888 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt-branch
index 7a2e6f822..7a2e6f822 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt-branch
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4
index 3ee6d2503..3ee6d2503 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4-branch
index 51f8a9290..51f8a9290 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4-branch
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1
index 14497029a..14497029a 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1-branch
index 4cff83526..4cff83526 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1-branch
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2
index 3ca077b29..3ca077b29 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2-branch
index e7bb901f2..e7bb901f2 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2-branch
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6
index 7c717a210..7c717a210 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6-branch
index 715f3ae1c..715f3ae1c 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6-branch
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7
index a014f1722..a014f1722 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7-branch
index 22331d78c..22331d78c 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7-branch
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8
index 7670c3506..7670c3506 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8-branch
index c4d68edcf..c4d68edcf 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8-branch
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9
index 09a343bdb..09a343bdb 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9-branch
index 1b126fb7b..1b126fb7b 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9-branch
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/unrelated b/tests/resources/merge-resolve/.gitted/logs/refs/heads/unrelated
index a83ffc26a..a83ffc26a 100644
--- a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/unrelated
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/unrelated
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/00/5b6fcc8fec71d2550bef8462d169b3c26aa14b b/tests/resources/merge-resolve/.gitted/objects/00/5b6fcc8fec71d2550bef8462d169b3c26aa14b
index 82a8da597..82a8da597 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/00/5b6fcc8fec71d2550bef8462d169b3c26aa14b
+++ b/tests/resources/merge-resolve/.gitted/objects/00/5b6fcc8fec71d2550bef8462d169b3c26aa14b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/00/9b9cab6fdac02915a88ecd078b7a792ed802d8 b/tests/resources/merge-resolve/.gitted/objects/00/9b9cab6fdac02915a88ecd078b7a792ed802d8
index f663a3c51..f663a3c51 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/00/9b9cab6fdac02915a88ecd078b7a792ed802d8
+++ b/tests/resources/merge-resolve/.gitted/objects/00/9b9cab6fdac02915a88ecd078b7a792ed802d8
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/00/c7d33f1ffa79d19c2272b370fcaeaadba49c08 b/tests/resources/merge-resolve/.gitted/objects/00/c7d33f1ffa79d19c2272b370fcaeaadba49c08
index 72698dc3d..72698dc3d 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/00/c7d33f1ffa79d19c2272b370fcaeaadba49c08
+++ b/tests/resources/merge-resolve/.gitted/objects/00/c7d33f1ffa79d19c2272b370fcaeaadba49c08
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/01/f149e1b8f84bd8896aaff6d6b22af88459ded0 b/tests/resources/merge-resolve/.gitted/objects/01/f149e1b8f84bd8896aaff6d6b22af88459ded0
index aa6336d3f..aa6336d3f 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/01/f149e1b8f84bd8896aaff6d6b22af88459ded0
+++ b/tests/resources/merge-resolve/.gitted/objects/01/f149e1b8f84bd8896aaff6d6b22af88459ded0
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/02/04a84f822acbf6386b36d33f1f6bc68bbbf858 b/tests/resources/merge-resolve/.gitted/objects/02/04a84f822acbf6386b36d33f1f6bc68bbbf858
index 2f0a0e1bb..2f0a0e1bb 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/02/04a84f822acbf6386b36d33f1f6bc68bbbf858
+++ b/tests/resources/merge-resolve/.gitted/objects/02/04a84f822acbf6386b36d33f1f6bc68bbbf858
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/02/251f990ca8e92e7ae61d3426163fa821c64001 b/tests/resources/merge-resolve/.gitted/objects/02/251f990ca8e92e7ae61d3426163fa821c64001
index d623117c5..d623117c5 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/02/251f990ca8e92e7ae61d3426163fa821c64001
+++ b/tests/resources/merge-resolve/.gitted/objects/02/251f990ca8e92e7ae61d3426163fa821c64001
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/03/21415405cb906c46869919af56d51dbbe5e85c b/tests/resources/merge-resolve/.gitted/objects/03/21415405cb906c46869919af56d51dbbe5e85c
index 277bdcff5..277bdcff5 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/03/21415405cb906c46869919af56d51dbbe5e85c
+++ b/tests/resources/merge-resolve/.gitted/objects/03/21415405cb906c46869919af56d51dbbe5e85c
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/03/2ebc5ab85d9553bb187d3cd40875ff23a63ed0 b/tests/resources/merge-resolve/.gitted/objects/03/2ebc5ab85d9553bb187d3cd40875ff23a63ed0
index e5404d838..e5404d838 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/03/2ebc5ab85d9553bb187d3cd40875ff23a63ed0
+++ b/tests/resources/merge-resolve/.gitted/objects/03/2ebc5ab85d9553bb187d3cd40875ff23a63ed0
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/03/b87706555accbf874ccd410dbda01e8e70a67f b/tests/resources/merge-resolve/.gitted/objects/03/b87706555accbf874ccd410dbda01e8e70a67f
index 0befcd735..0befcd735 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/03/b87706555accbf874ccd410dbda01e8e70a67f
+++ b/tests/resources/merge-resolve/.gitted/objects/03/b87706555accbf874ccd410dbda01e8e70a67f
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/03/dad1005e5d06d418f50b12e0bcd48ff2306a03 b/tests/resources/merge-resolve/.gitted/objects/03/dad1005e5d06d418f50b12e0bcd48ff2306a03
index 04011a2ce..04011a2ce 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/03/dad1005e5d06d418f50b12e0bcd48ff2306a03
+++ b/tests/resources/merge-resolve/.gitted/objects/03/dad1005e5d06d418f50b12e0bcd48ff2306a03
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/05/1ffd7901a442faf56b226161649074f15c7c47 b/tests/resources/merge-resolve/.gitted/objects/05/1ffd7901a442faf56b226161649074f15c7c47
index 65fa6894f..65fa6894f 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/05/1ffd7901a442faf56b226161649074f15c7c47
+++ b/tests/resources/merge-resolve/.gitted/objects/05/1ffd7901a442faf56b226161649074f15c7c47
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/05/8541fc37114bfc1dddf6bd6bffc7fae5c2e6fe b/tests/resources/merge-resolve/.gitted/objects/05/8541fc37114bfc1dddf6bd6bffc7fae5c2e6fe
index d79dc30ba..d79dc30ba 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/05/8541fc37114bfc1dddf6bd6bffc7fae5c2e6fe
+++ b/tests/resources/merge-resolve/.gitted/objects/05/8541fc37114bfc1dddf6bd6bffc7fae5c2e6fe
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/05/f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c b/tests/resources/merge-resolve/.gitted/objects/05/f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c
index 7b4b152f3..7b4b152f3 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/05/f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c
+++ b/tests/resources/merge-resolve/.gitted/objects/05/f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/07/a759da919f737221791d542f176ab49c88837f b/tests/resources/merge-resolve/.gitted/objects/07/a759da919f737221791d542f176ab49c88837f
index a34b6c235..a34b6c235 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/07/a759da919f737221791d542f176ab49c88837f
+++ b/tests/resources/merge-resolve/.gitted/objects/07/a759da919f737221791d542f176ab49c88837f
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/07/c514b04698e068892b31c8d352b85813b99c6e b/tests/resources/merge-resolve/.gitted/objects/07/c514b04698e068892b31c8d352b85813b99c6e
index 23ab92171..23ab92171 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/07/c514b04698e068892b31c8d352b85813b99c6e
+++ b/tests/resources/merge-resolve/.gitted/objects/07/c514b04698e068892b31c8d352b85813b99c6e
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/09/055301463b7f2f8ee5d368f8ed5c0a40ad8515 b/tests/resources/merge-resolve/.gitted/objects/09/055301463b7f2f8ee5d368f8ed5c0a40ad8515
index bf5b0fcc5..bf5b0fcc5 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/09/055301463b7f2f8ee5d368f8ed5c0a40ad8515
+++ b/tests/resources/merge-resolve/.gitted/objects/09/055301463b7f2f8ee5d368f8ed5c0a40ad8515
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/09/17bb159596aea4d295f4857da77e8f96b3c7dc b/tests/resources/merge-resolve/.gitted/objects/09/17bb159596aea4d295f4857da77e8f96b3c7dc
index 9fb640dd5..9fb640dd5 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/09/17bb159596aea4d295f4857da77e8f96b3c7dc
+++ b/tests/resources/merge-resolve/.gitted/objects/09/17bb159596aea4d295f4857da77e8f96b3c7dc
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/09/2ce8682d7f3a2a3a769a6daca58950168ba5c4 b/tests/resources/merge-resolve/.gitted/objects/09/2ce8682d7f3a2a3a769a6daca58950168ba5c4
index b709cf461..b709cf461 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/09/2ce8682d7f3a2a3a769a6daca58950168ba5c4
+++ b/tests/resources/merge-resolve/.gitted/objects/09/2ce8682d7f3a2a3a769a6daca58950168ba5c4
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/09/3bebf072dd4bbba88833667d6ffe454df199e1 b/tests/resources/merge-resolve/.gitted/objects/09/3bebf072dd4bbba88833667d6ffe454df199e1
index ae13207d7..ae13207d7 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/09/3bebf072dd4bbba88833667d6ffe454df199e1
+++ b/tests/resources/merge-resolve/.gitted/objects/09/3bebf072dd4bbba88833667d6ffe454df199e1
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/09/768bed22680cdb0859683fa9677ccc8d5a25c1 b/tests/resources/merge-resolve/.gitted/objects/09/768bed22680cdb0859683fa9677ccc8d5a25c1
index 5f4b4dab1..5f4b4dab1 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/09/768bed22680cdb0859683fa9677ccc8d5a25c1
+++ b/tests/resources/merge-resolve/.gitted/objects/09/768bed22680cdb0859683fa9677ccc8d5a25c1
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/0a/75d9aac1dc84fb5aa51f7325c0ab53242ddef7 b/tests/resources/merge-resolve/.gitted/objects/0a/75d9aac1dc84fb5aa51f7325c0ab53242ddef7
index d5377341a..d5377341a 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/0a/75d9aac1dc84fb5aa51f7325c0ab53242ddef7
+++ b/tests/resources/merge-resolve/.gitted/objects/0a/75d9aac1dc84fb5aa51f7325c0ab53242ddef7
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/0c/fd6c54ef6532d862408f562309dc9c74a401e8 b/tests/resources/merge-resolve/.gitted/objects/0c/fd6c54ef6532d862408f562309dc9c74a401e8
index 40f628f89..40f628f89 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/0c/fd6c54ef6532d862408f562309dc9c74a401e8
+++ b/tests/resources/merge-resolve/.gitted/objects/0c/fd6c54ef6532d862408f562309dc9c74a401e8
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/0d/52e3a556e189ba0948ae56780918011c1b167d b/tests/resources/merge-resolve/.gitted/objects/0d/52e3a556e189ba0948ae56780918011c1b167d
index 4b633e504..4b633e504 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/0d/52e3a556e189ba0948ae56780918011c1b167d
+++ b/tests/resources/merge-resolve/.gitted/objects/0d/52e3a556e189ba0948ae56780918011c1b167d
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/0d/872f8e871a30208305978ecbf9e66d864f1638 b/tests/resources/merge-resolve/.gitted/objects/0d/872f8e871a30208305978ecbf9e66d864f1638
index 4cbc18e84..4cbc18e84 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/0d/872f8e871a30208305978ecbf9e66d864f1638
+++ b/tests/resources/merge-resolve/.gitted/objects/0d/872f8e871a30208305978ecbf9e66d864f1638
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/0e/c5f433959cd46177f745903353efb5be08d151 b/tests/resources/merge-resolve/.gitted/objects/0e/c5f433959cd46177f745903353efb5be08d151
index 1bee56c14..1bee56c14 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/0e/c5f433959cd46177f745903353efb5be08d151
+++ b/tests/resources/merge-resolve/.gitted/objects/0e/c5f433959cd46177f745903353efb5be08d151
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/11/deab00b2d3a6f5a3073988ac050c2d7b6655e2 b/tests/resources/merge-resolve/.gitted/objects/11/deab00b2d3a6f5a3073988ac050c2d7b6655e2
index 857b23686..857b23686 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/11/deab00b2d3a6f5a3073988ac050c2d7b6655e2
+++ b/tests/resources/merge-resolve/.gitted/objects/11/deab00b2d3a6f5a3073988ac050c2d7b6655e2
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/11/f4f3c08b737f5fd896cbefa1425ee63b21b2fa b/tests/resources/merge-resolve/.gitted/objects/11/f4f3c08b737f5fd896cbefa1425ee63b21b2fa
index 6555194cb..6555194cb 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/11/f4f3c08b737f5fd896cbefa1425ee63b21b2fa
+++ b/tests/resources/merge-resolve/.gitted/objects/11/f4f3c08b737f5fd896cbefa1425ee63b21b2fa
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/13/d1be4ea52a6ced1d7a1d832f0ee3c399348e5e b/tests/resources/merge-resolve/.gitted/objects/13/d1be4ea52a6ced1d7a1d832f0ee3c399348e5e
index 4e4e175e8..4e4e175e8 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/13/d1be4ea52a6ced1d7a1d832f0ee3c399348e5e
+++ b/tests/resources/merge-resolve/.gitted/objects/13/d1be4ea52a6ced1d7a1d832f0ee3c399348e5e
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/14/39088f509b79b1535b64193137d3ce4b240734 b/tests/resources/merge-resolve/.gitted/objects/14/39088f509b79b1535b64193137d3ce4b240734
index 51ddf6dcb..51ddf6dcb 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/14/39088f509b79b1535b64193137d3ce4b240734
+++ b/tests/resources/merge-resolve/.gitted/objects/14/39088f509b79b1535b64193137d3ce4b240734
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/15/8dc7bedb202f5b26502bf3574faa7f4238d56c b/tests/resources/merge-resolve/.gitted/objects/15/8dc7bedb202f5b26502bf3574faa7f4238d56c
index 064423d0c..064423d0c 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/15/8dc7bedb202f5b26502bf3574faa7f4238d56c
+++ b/tests/resources/merge-resolve/.gitted/objects/15/8dc7bedb202f5b26502bf3574faa7f4238d56c
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/16/f825815cfd20a07a75c71554e82d8eede0b061 b/tests/resources/merge-resolve/.gitted/objects/16/f825815cfd20a07a75c71554e82d8eede0b061
index 82d65253b..82d65253b 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/16/f825815cfd20a07a75c71554e82d8eede0b061
+++ b/tests/resources/merge-resolve/.gitted/objects/16/f825815cfd20a07a75c71554e82d8eede0b061
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/17/8940b450f238a56c0d75b7955cb57b38191982 b/tests/resources/merge-resolve/.gitted/objects/17/8940b450f238a56c0d75b7955cb57b38191982
index 94e571e65..94e571e65 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/17/8940b450f238a56c0d75b7955cb57b38191982
+++ b/tests/resources/merge-resolve/.gitted/objects/17/8940b450f238a56c0d75b7955cb57b38191982
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/18/3310e30fb1499af8c619108ffea4d300b5e778 b/tests/resources/merge-resolve/.gitted/objects/18/3310e30fb1499af8c619108ffea4d300b5e778
index 1c4010d04..1c4010d04 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/18/3310e30fb1499af8c619108ffea4d300b5e778
+++ b/tests/resources/merge-resolve/.gitted/objects/18/3310e30fb1499af8c619108ffea4d300b5e778
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/18/cb316b1cefa0f8a6946f0e201a8e1a6f845ab9 b/tests/resources/merge-resolve/.gitted/objects/18/cb316b1cefa0f8a6946f0e201a8e1a6f845ab9
index 30f3110f1..30f3110f1 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/18/cb316b1cefa0f8a6946f0e201a8e1a6f845ab9
+++ b/tests/resources/merge-resolve/.gitted/objects/18/cb316b1cefa0f8a6946f0e201a8e1a6f845ab9
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/19/b7ac485269b672a101060894de3ba9c2a24dd1 b/tests/resources/merge-resolve/.gitted/objects/19/b7ac485269b672a101060894de3ba9c2a24dd1
index e34ccb855..e34ccb855 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/19/b7ac485269b672a101060894de3ba9c2a24dd1
+++ b/tests/resources/merge-resolve/.gitted/objects/19/b7ac485269b672a101060894de3ba9c2a24dd1
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/1a/010b1c0f081b2e8901d55307a15c29ff30af0e b/tests/resources/merge-resolve/.gitted/objects/1a/010b1c0f081b2e8901d55307a15c29ff30af0e
new file mode 100644
index 000000000..6039df00e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/1a/010b1c0f081b2e8901d55307a15c29ff30af0e
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/1c/ff9ec6a47a537380dedfdd17c9e76d74259a2b b/tests/resources/merge-resolve/.gitted/objects/1c/ff9ec6a47a537380dedfdd17c9e76d74259a2b
index 30802bcec..30802bcec 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/1c/ff9ec6a47a537380dedfdd17c9e76d74259a2b
+++ b/tests/resources/merge-resolve/.gitted/objects/1c/ff9ec6a47a537380dedfdd17c9e76d74259a2b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/1e/4ff029aee68d0d69ef9eb6efa6cbf1ec732f99 b/tests/resources/merge-resolve/.gitted/objects/1e/4ff029aee68d0d69ef9eb6efa6cbf1ec732f99
index 5183b8360..5183b8360 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/1e/4ff029aee68d0d69ef9eb6efa6cbf1ec732f99
+++ b/tests/resources/merge-resolve/.gitted/objects/1e/4ff029aee68d0d69ef9eb6efa6cbf1ec732f99
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/1f/81433e3161efbf250576c58fede7f6b836f3d3 b/tests/resources/merge-resolve/.gitted/objects/1f/81433e3161efbf250576c58fede7f6b836f3d3
index 970855675..970855675 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/1f/81433e3161efbf250576c58fede7f6b836f3d3
+++ b/tests/resources/merge-resolve/.gitted/objects/1f/81433e3161efbf250576c58fede7f6b836f3d3
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/20/91d94c8bd3eb0835dc5220de5e8bb310fa1513 b/tests/resources/merge-resolve/.gitted/objects/20/91d94c8bd3eb0835dc5220de5e8bb310fa1513
index a843890c0..a843890c0 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/20/91d94c8bd3eb0835dc5220de5e8bb310fa1513
+++ b/tests/resources/merge-resolve/.gitted/objects/20/91d94c8bd3eb0835dc5220de5e8bb310fa1513
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/21/671e290278286fb2ce4c63d01699b67adce331 b/tests/resources/merge-resolve/.gitted/objects/21/671e290278286fb2ce4c63d01699b67adce331
index b656d0001..b656d0001 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/21/671e290278286fb2ce4c63d01699b67adce331
+++ b/tests/resources/merge-resolve/.gitted/objects/21/671e290278286fb2ce4c63d01699b67adce331
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/22/7792b52aaa0b238bea00ec7e509b02623f168c b/tests/resources/merge-resolve/.gitted/objects/22/7792b52aaa0b238bea00ec7e509b02623f168c
index 3bb19bb77..3bb19bb77 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/22/7792b52aaa0b238bea00ec7e509b02623f168c
+++ b/tests/resources/merge-resolve/.gitted/objects/22/7792b52aaa0b238bea00ec7e509b02623f168c
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/23/3c0919c998ed110a4b6ff36f353aec8b713487 b/tests/resources/merge-resolve/.gitted/objects/23/3c0919c998ed110a4b6ff36f353aec8b713487
index d0c8c9e1d..d0c8c9e1d 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/23/3c0919c998ed110a4b6ff36f353aec8b713487
+++ b/tests/resources/merge-resolve/.gitted/objects/23/3c0919c998ed110a4b6ff36f353aec8b713487
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/23/92a2dacc9efb562b8635d6579fb458751c7c5b b/tests/resources/merge-resolve/.gitted/objects/23/92a2dacc9efb562b8635d6579fb458751c7c5b
index 86127a344..86127a344 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/23/92a2dacc9efb562b8635d6579fb458751c7c5b
+++ b/tests/resources/merge-resolve/.gitted/objects/23/92a2dacc9efb562b8635d6579fb458751c7c5b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/24/1a1005cd9b980732741b74385b891142bcba28 b/tests/resources/merge-resolve/.gitted/objects/24/1a1005cd9b980732741b74385b891142bcba28
index 9b65f666f..9b65f666f 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/24/1a1005cd9b980732741b74385b891142bcba28
+++ b/tests/resources/merge-resolve/.gitted/objects/24/1a1005cd9b980732741b74385b891142bcba28
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/24/2591eb280ee9eeb2ce63524b9a8b9bc4cb515d b/tests/resources/merge-resolve/.gitted/objects/24/2591eb280ee9eeb2ce63524b9a8b9bc4cb515d
index 74a01373f..74a01373f 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/24/2591eb280ee9eeb2ce63524b9a8b9bc4cb515d
+++ b/tests/resources/merge-resolve/.gitted/objects/24/2591eb280ee9eeb2ce63524b9a8b9bc4cb515d
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/24/90b9f1a079420870027deefb49f51d6656cf74 b/tests/resources/merge-resolve/.gitted/objects/24/90b9f1a079420870027deefb49f51d6656cf74
index 60497caa5..60497caa5 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/24/90b9f1a079420870027deefb49f51d6656cf74
+++ b/tests/resources/merge-resolve/.gitted/objects/24/90b9f1a079420870027deefb49f51d6656cf74
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/25/9d08ca43af9200e9ea9a098e44a5a350ebd9b3 b/tests/resources/merge-resolve/.gitted/objects/25/9d08ca43af9200e9ea9a098e44a5a350ebd9b3
index 2bae66998..2bae66998 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/25/9d08ca43af9200e9ea9a098e44a5a350ebd9b3
+++ b/tests/resources/merge-resolve/.gitted/objects/25/9d08ca43af9200e9ea9a098e44a5a350ebd9b3
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/25/c40b7660c08c8fb581f770312f41b9b03119d1 b/tests/resources/merge-resolve/.gitted/objects/25/c40b7660c08c8fb581f770312f41b9b03119d1
index 185214727..185214727 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/25/c40b7660c08c8fb581f770312f41b9b03119d1
+++ b/tests/resources/merge-resolve/.gitted/objects/25/c40b7660c08c8fb581f770312f41b9b03119d1
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/26/153a3ff3649b6c2bb652d3f06878c6e0a172f9 b/tests/resources/merge-resolve/.gitted/objects/26/153a3ff3649b6c2bb652d3f06878c6e0a172f9
index 4fcaa07e2..4fcaa07e2 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/26/153a3ff3649b6c2bb652d3f06878c6e0a172f9
+++ b/tests/resources/merge-resolve/.gitted/objects/26/153a3ff3649b6c2bb652d3f06878c6e0a172f9
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/27/133da702ba3c60af2a01e96c2555ff4045d692 b/tests/resources/merge-resolve/.gitted/objects/27/133da702ba3c60af2a01e96c2555ff4045d692
index 08e61f844..08e61f844 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/27/133da702ba3c60af2a01e96c2555ff4045d692
+++ b/tests/resources/merge-resolve/.gitted/objects/27/133da702ba3c60af2a01e96c2555ff4045d692
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/2b/0de5dc27505dcdd83a75c8bf1fcd9462cd7add b/tests/resources/merge-resolve/.gitted/objects/2b/0de5dc27505dcdd83a75c8bf1fcd9462cd7add
index a95f926f8..a95f926f8 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/2b/0de5dc27505dcdd83a75c8bf1fcd9462cd7add
+++ b/tests/resources/merge-resolve/.gitted/objects/2b/0de5dc27505dcdd83a75c8bf1fcd9462cd7add
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/2b/5f1f181ee3b58ea751f5dd5d8f9b445520a136 b/tests/resources/merge-resolve/.gitted/objects/2b/5f1f181ee3b58ea751f5dd5d8f9b445520a136
index d24231eda..d24231eda 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/2b/5f1f181ee3b58ea751f5dd5d8f9b445520a136
+++ b/tests/resources/merge-resolve/.gitted/objects/2b/5f1f181ee3b58ea751f5dd5d8f9b445520a136
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863 b/tests/resources/merge-resolve/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863
index d10ca636b..d10ca636b 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863
+++ b/tests/resources/merge-resolve/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/2d/a538570bc1e5b2c3e855bf702f35248ad0735f b/tests/resources/merge-resolve/.gitted/objects/2d/a538570bc1e5b2c3e855bf702f35248ad0735f
index 83253f81c..83253f81c 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/2d/a538570bc1e5b2c3e855bf702f35248ad0735f
+++ b/tests/resources/merge-resolve/.gitted/objects/2d/a538570bc1e5b2c3e855bf702f35248ad0735f
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/2f/2e37b7ebbae467978610896ca3aafcdad2ee67 b/tests/resources/merge-resolve/.gitted/objects/2f/2e37b7ebbae467978610896ca3aafcdad2ee67
index 7adffb165..7adffb165 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/2f/2e37b7ebbae467978610896ca3aafcdad2ee67
+++ b/tests/resources/merge-resolve/.gitted/objects/2f/2e37b7ebbae467978610896ca3aafcdad2ee67
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/2f/4024ce528d36d8670c289cce5a7963e625bb0c b/tests/resources/merge-resolve/.gitted/objects/2f/4024ce528d36d8670c289cce5a7963e625bb0c
index 0100fd70e..0100fd70e 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/2f/4024ce528d36d8670c289cce5a7963e625bb0c
+++ b/tests/resources/merge-resolve/.gitted/objects/2f/4024ce528d36d8670c289cce5a7963e625bb0c
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/2f/56120107d680129a5d9791b521cb1e73a2ed31 b/tests/resources/merge-resolve/.gitted/objects/2f/56120107d680129a5d9791b521cb1e73a2ed31
index 1f5f597b9..1f5f597b9 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/2f/56120107d680129a5d9791b521cb1e73a2ed31
+++ b/tests/resources/merge-resolve/.gitted/objects/2f/56120107d680129a5d9791b521cb1e73a2ed31
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/2f/598248eeccfc27e5ca44d9d96383f6dfea7b16 b/tests/resources/merge-resolve/.gitted/objects/2f/598248eeccfc27e5ca44d9d96383f6dfea7b16
index 1d9f226e2..1d9f226e2 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/2f/598248eeccfc27e5ca44d9d96383f6dfea7b16
+++ b/tests/resources/merge-resolve/.gitted/objects/2f/598248eeccfc27e5ca44d9d96383f6dfea7b16
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/31/68dca1a561889b045a6441909f4c56145e666d b/tests/resources/merge-resolve/.gitted/objects/31/68dca1a561889b045a6441909f4c56145e666d
index 2de1c5a79..2de1c5a79 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/31/68dca1a561889b045a6441909f4c56145e666d
+++ b/tests/resources/merge-resolve/.gitted/objects/31/68dca1a561889b045a6441909f4c56145e666d
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/31/d5472536041a83d986829240bbbdc897c6f8a6 b/tests/resources/merge-resolve/.gitted/objects/31/d5472536041a83d986829240bbbdc897c6f8a6
index 5ec5acb59..5ec5acb59 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/31/d5472536041a83d986829240bbbdc897c6f8a6
+++ b/tests/resources/merge-resolve/.gitted/objects/31/d5472536041a83d986829240bbbdc897c6f8a6
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/32/21dd512b7e2dc4b5bd03046df6c81b2ab2070b b/tests/resources/merge-resolve/.gitted/objects/32/21dd512b7e2dc4b5bd03046df6c81b2ab2070b
index d36138d79..d36138d79 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/32/21dd512b7e2dc4b5bd03046df6c81b2ab2070b
+++ b/tests/resources/merge-resolve/.gitted/objects/32/21dd512b7e2dc4b5bd03046df6c81b2ab2070b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/33/46d64325b39e5323733492cd55f808994a2475 b/tests/resources/merge-resolve/.gitted/objects/33/46d64325b39e5323733492cd55f808994a2475
index 11546cea4..11546cea4 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/33/46d64325b39e5323733492cd55f808994a2475
+++ b/tests/resources/merge-resolve/.gitted/objects/33/46d64325b39e5323733492cd55f808994a2475
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/33/d500f588fbbe65901d82b4e6b008e549064be0 b/tests/resources/merge-resolve/.gitted/objects/33/d500f588fbbe65901d82b4e6b008e549064be0
index 061a031b6..061a031b6 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/33/d500f588fbbe65901d82b4e6b008e549064be0
+++ b/tests/resources/merge-resolve/.gitted/objects/33/d500f588fbbe65901d82b4e6b008e549064be0
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/34/bfafff88eaf118402b44e6f3e2dbbf1a582b05 b/tests/resources/merge-resolve/.gitted/objects/34/bfafff88eaf118402b44e6f3e2dbbf1a582b05
index c653cec50..c653cec50 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/34/bfafff88eaf118402b44e6f3e2dbbf1a582b05
+++ b/tests/resources/merge-resolve/.gitted/objects/34/bfafff88eaf118402b44e6f3e2dbbf1a582b05
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/35/0c6eb3010efc403a6bed682332635314e9ed58 b/tests/resources/merge-resolve/.gitted/objects/35/0c6eb3010efc403a6bed682332635314e9ed58
index 2eee60233..2eee60233 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/35/0c6eb3010efc403a6bed682332635314e9ed58
+++ b/tests/resources/merge-resolve/.gitted/objects/35/0c6eb3010efc403a6bed682332635314e9ed58
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/35/411bfb77cd2cc431f3a03a2b4976ed94b5d241 b/tests/resources/merge-resolve/.gitted/objects/35/411bfb77cd2cc431f3a03a2b4976ed94b5d241
index ea024ccd9..ea024ccd9 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/35/411bfb77cd2cc431f3a03a2b4976ed94b5d241
+++ b/tests/resources/merge-resolve/.gitted/objects/35/411bfb77cd2cc431f3a03a2b4976ed94b5d241
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/35/4704d3613ad4228e4786fc76656b11e98236c4 b/tests/resources/merge-resolve/.gitted/objects/35/4704d3613ad4228e4786fc76656b11e98236c4
index 1dd13c44a..1dd13c44a 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/35/4704d3613ad4228e4786fc76656b11e98236c4
+++ b/tests/resources/merge-resolve/.gitted/objects/35/4704d3613ad4228e4786fc76656b11e98236c4
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/35/632e43612c06a3ea924bfbacd48333da874c29 b/tests/resources/merge-resolve/.gitted/objects/35/632e43612c06a3ea924bfbacd48333da874c29
index be7684f19..be7684f19 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/35/632e43612c06a3ea924bfbacd48333da874c29
+++ b/tests/resources/merge-resolve/.gitted/objects/35/632e43612c06a3ea924bfbacd48333da874c29
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/35/75826c96a975031d2c14368529cc5c4353a8fd b/tests/resources/merge-resolve/.gitted/objects/35/75826c96a975031d2c14368529cc5c4353a8fd
index 24e33bc41..24e33bc41 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/35/75826c96a975031d2c14368529cc5c4353a8fd
+++ b/tests/resources/merge-resolve/.gitted/objects/35/75826c96a975031d2c14368529cc5c4353a8fd
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/36/219b49367146cb2e6a1555b5a9ebd4d0328495 b/tests/resources/merge-resolve/.gitted/objects/36/219b49367146cb2e6a1555b5a9ebd4d0328495
index 7f8044372..7f8044372 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/36/219b49367146cb2e6a1555b5a9ebd4d0328495
+++ b/tests/resources/merge-resolve/.gitted/objects/36/219b49367146cb2e6a1555b5a9ebd4d0328495
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/36/4bbe4ce80c7bd31e6307dce77d46e3e1759fb3 b/tests/resources/merge-resolve/.gitted/objects/36/4bbe4ce80c7bd31e6307dce77d46e3e1759fb3
index 90fd9651f..90fd9651f 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/36/4bbe4ce80c7bd31e6307dce77d46e3e1759fb3
+++ b/tests/resources/merge-resolve/.gitted/objects/36/4bbe4ce80c7bd31e6307dce77d46e3e1759fb3
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/37/48859b001c6e627e712a07951aee40afd19b41 b/tests/resources/merge-resolve/.gitted/objects/37/48859b001c6e627e712a07951aee40afd19b41
index 6a0c389e4..6a0c389e4 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/37/48859b001c6e627e712a07951aee40afd19b41
+++ b/tests/resources/merge-resolve/.gitted/objects/37/48859b001c6e627e712a07951aee40afd19b41
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/38/5c8a0f26ddf79e9041e15e17dc352ed2c4cced b/tests/resources/merge-resolve/.gitted/objects/38/5c8a0f26ddf79e9041e15e17dc352ed2c4cced
index e95ff3a88..e95ff3a88 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/38/5c8a0f26ddf79e9041e15e17dc352ed2c4cced
+++ b/tests/resources/merge-resolve/.gitted/objects/38/5c8a0f26ddf79e9041e15e17dc352ed2c4cced
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/3b/47b031b3e55ae11e14a05260b1c3ffd6838d55 b/tests/resources/merge-resolve/.gitted/objects/3b/47b031b3e55ae11e14a05260b1c3ffd6838d55
index 82086466f..82086466f 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/3b/47b031b3e55ae11e14a05260b1c3ffd6838d55
+++ b/tests/resources/merge-resolve/.gitted/objects/3b/47b031b3e55ae11e14a05260b1c3ffd6838d55
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/3b/bf0bf59b20df5d5fc58b9fc1dc07be637c301f b/tests/resources/merge-resolve/.gitted/objects/3b/bf0bf59b20df5d5fc58b9fc1dc07be637c301f
index 723a9ae4c..723a9ae4c 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/3b/bf0bf59b20df5d5fc58b9fc1dc07be637c301f
+++ b/tests/resources/merge-resolve/.gitted/objects/3b/bf0bf59b20df5d5fc58b9fc1dc07be637c301f
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/3e/f4d30382ca33fdeba9fda895a99e0891ba37aa b/tests/resources/merge-resolve/.gitted/objects/3e/f4d30382ca33fdeba9fda895a99e0891ba37aa
index 49ee15239..49ee15239 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/3e/f4d30382ca33fdeba9fda895a99e0891ba37aa
+++ b/tests/resources/merge-resolve/.gitted/objects/3e/f4d30382ca33fdeba9fda895a99e0891ba37aa
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/3e/f9bfe82f9635518ae89152322f3b46fd4ba25b b/tests/resources/merge-resolve/.gitted/objects/3e/f9bfe82f9635518ae89152322f3b46fd4ba25b
index 3b5998ca6..3b5998ca6 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/3e/f9bfe82f9635518ae89152322f3b46fd4ba25b
+++ b/tests/resources/merge-resolve/.gitted/objects/3e/f9bfe82f9635518ae89152322f3b46fd4ba25b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/40/2784a46a4a3982294231594cbeb431f506d22c b/tests/resources/merge-resolve/.gitted/objects/40/2784a46a4a3982294231594cbeb431f506d22c
index a17e05d0f..a17e05d0f 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/40/2784a46a4a3982294231594cbeb431f506d22c
+++ b/tests/resources/merge-resolve/.gitted/objects/40/2784a46a4a3982294231594cbeb431f506d22c
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/41/2b32fb66137366147f1801ecc962452757d48a b/tests/resources/merge-resolve/.gitted/objects/41/2b32fb66137366147f1801ecc962452757d48a
index b183dd782..b183dd782 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/41/2b32fb66137366147f1801ecc962452757d48a
+++ b/tests/resources/merge-resolve/.gitted/objects/41/2b32fb66137366147f1801ecc962452757d48a
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/43/aafd43bea779ec74317dc361f45ae3f532a505 b/tests/resources/merge-resolve/.gitted/objects/43/aafd43bea779ec74317dc361f45ae3f532a505
index ac86823b6..ac86823b6 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/43/aafd43bea779ec74317dc361f45ae3f532a505
+++ b/tests/resources/merge-resolve/.gitted/objects/43/aafd43bea779ec74317dc361f45ae3f532a505
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/43/c338656342227a3a3cd3aa85cbf784061f5425 b/tests/resources/merge-resolve/.gitted/objects/43/c338656342227a3a3cd3aa85cbf784061f5425
index d9773118b..d9773118b 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/43/c338656342227a3a3cd3aa85cbf784061f5425
+++ b/tests/resources/merge-resolve/.gitted/objects/43/c338656342227a3a3cd3aa85cbf784061f5425
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/45/299c1ca5e07bba1fd90843056fb559f96b1f5a b/tests/resources/merge-resolve/.gitted/objects/45/299c1ca5e07bba1fd90843056fb559f96b1f5a
index 2093b4410..2093b4410 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/45/299c1ca5e07bba1fd90843056fb559f96b1f5a
+++ b/tests/resources/merge-resolve/.gitted/objects/45/299c1ca5e07bba1fd90843056fb559f96b1f5a
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/46/6daf8552b891e5c22bc58c9d7fc1a2eb8f0289 b/tests/resources/merge-resolve/.gitted/objects/46/6daf8552b891e5c22bc58c9d7fc1a2eb8f0289
index c39b53aa8..c39b53aa8 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/46/6daf8552b891e5c22bc58c9d7fc1a2eb8f0289
+++ b/tests/resources/merge-resolve/.gitted/objects/46/6daf8552b891e5c22bc58c9d7fc1a2eb8f0289
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/47/6dbb3e207313d1d8aaa120c6ad204bf1295e53 b/tests/resources/merge-resolve/.gitted/objects/47/6dbb3e207313d1d8aaa120c6ad204bf1295e53
index 3e5f66e55..3e5f66e55 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/47/6dbb3e207313d1d8aaa120c6ad204bf1295e53
+++ b/tests/resources/merge-resolve/.gitted/objects/47/6dbb3e207313d1d8aaa120c6ad204bf1295e53
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/47/8172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2 b/tests/resources/merge-resolve/.gitted/objects/47/8172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2
index d9e250e66..d9e250e66 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/47/8172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2
+++ b/tests/resources/merge-resolve/.gitted/objects/47/8172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/49/130a28ef567af9a6a6104c38773fedfa5f9742 b/tests/resources/merge-resolve/.gitted/objects/49/130a28ef567af9a6a6104c38773fedfa5f9742
index e2c49f5c4..e2c49f5c4 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/49/130a28ef567af9a6a6104c38773fedfa5f9742
+++ b/tests/resources/merge-resolve/.gitted/objects/49/130a28ef567af9a6a6104c38773fedfa5f9742
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/49/9df817155e4bdd3c6ee192a72c52f481818230 b/tests/resources/merge-resolve/.gitted/objects/49/9df817155e4bdd3c6ee192a72c52f481818230
index 9c7e471dd..9c7e471dd 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/49/9df817155e4bdd3c6ee192a72c52f481818230
+++ b/tests/resources/merge-resolve/.gitted/objects/49/9df817155e4bdd3c6ee192a72c52f481818230
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/4a/9550ebcc97ce22b22f45af7b829bb030d003f5 b/tests/resources/merge-resolve/.gitted/objects/4a/9550ebcc97ce22b22f45af7b829bb030d003f5
index 6ec674adc..6ec674adc 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/4a/9550ebcc97ce22b22f45af7b829bb030d003f5
+++ b/tests/resources/merge-resolve/.gitted/objects/4a/9550ebcc97ce22b22f45af7b829bb030d003f5
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/4b/253da36a0ae8bfce63aeabd8c5b58429925594 b/tests/resources/merge-resolve/.gitted/objects/4b/253da36a0ae8bfce63aeabd8c5b58429925594
index 1a4072794..1a4072794 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/4b/253da36a0ae8bfce63aeabd8c5b58429925594
+++ b/tests/resources/merge-resolve/.gitted/objects/4b/253da36a0ae8bfce63aeabd8c5b58429925594
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/4b/48deed3a433909bfd6b6ab3d4b91348b6af464 b/tests/resources/merge-resolve/.gitted/objects/4b/48deed3a433909bfd6b6ab3d4b91348b6af464
index 328c8506e..328c8506e 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/4b/48deed3a433909bfd6b6ab3d4b91348b6af464
+++ b/tests/resources/merge-resolve/.gitted/objects/4b/48deed3a433909bfd6b6ab3d4b91348b6af464
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 b/tests/resources/merge-resolve/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904
index adf64119a..adf64119a 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904
+++ b/tests/resources/merge-resolve/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/4c/9fac0707f8d4195037ae5a681aa48626491541 b/tests/resources/merge-resolve/.gitted/objects/4c/9fac0707f8d4195037ae5a681aa48626491541
index 6b8c85e2b..6b8c85e2b 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/4c/9fac0707f8d4195037ae5a681aa48626491541
+++ b/tests/resources/merge-resolve/.gitted/objects/4c/9fac0707f8d4195037ae5a681aa48626491541
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/4c/a408a8c88655f7586a1b580be6fad138121e98 b/tests/resources/merge-resolve/.gitted/objects/4c/a408a8c88655f7586a1b580be6fad138121e98
index 15cb7f29a..15cb7f29a 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/4c/a408a8c88655f7586a1b580be6fad138121e98
+++ b/tests/resources/merge-resolve/.gitted/objects/4c/a408a8c88655f7586a1b580be6fad138121e98
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/4e/0d9401aee78eb345a8685a859d37c8c3c0bbed b/tests/resources/merge-resolve/.gitted/objects/4e/0d9401aee78eb345a8685a859d37c8c3c0bbed
index 57f7eb68c..57f7eb68c 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/4e/0d9401aee78eb345a8685a859d37c8c3c0bbed
+++ b/tests/resources/merge-resolve/.gitted/objects/4e/0d9401aee78eb345a8685a859d37c8c3c0bbed
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10 b/tests/resources/merge-resolve/.gitted/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10
index 53168a038..53168a038 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10
+++ b/tests/resources/merge-resolve/.gitted/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/4e/b04c9e79e88f6640d01ff5b25ca2a60764f216 b/tests/resources/merge-resolve/.gitted/objects/4e/b04c9e79e88f6640d01ff5b25ca2a60764f216
index f4ec0efec..f4ec0efec 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/4e/b04c9e79e88f6640d01ff5b25ca2a60764f216
+++ b/tests/resources/merge-resolve/.gitted/objects/4e/b04c9e79e88f6640d01ff5b25ca2a60764f216
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/4f/e93c0ec83eb6305cbace3dace88ecee1b63cb6 b/tests/resources/merge-resolve/.gitted/objects/4f/e93c0ec83eb6305cbace3dace88ecee1b63cb6
index 67dc6842f..67dc6842f 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/4f/e93c0ec83eb6305cbace3dace88ecee1b63cb6
+++ b/tests/resources/merge-resolve/.gitted/objects/4f/e93c0ec83eb6305cbace3dace88ecee1b63cb6
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/50/12fd565b1393bdfda1805d4ec38ce6619e1fd1 b/tests/resources/merge-resolve/.gitted/objects/50/12fd565b1393bdfda1805d4ec38ce6619e1fd1
index d629a23a1..d629a23a1 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/50/12fd565b1393bdfda1805d4ec38ce6619e1fd1
+++ b/tests/resources/merge-resolve/.gitted/objects/50/12fd565b1393bdfda1805d4ec38ce6619e1fd1
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/50/4f75ac95a71ef98051817618576a68505b92f9 b/tests/resources/merge-resolve/.gitted/objects/50/4f75ac95a71ef98051817618576a68505b92f9
index 1b24c721a..1b24c721a 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/50/4f75ac95a71ef98051817618576a68505b92f9
+++ b/tests/resources/merge-resolve/.gitted/objects/50/4f75ac95a71ef98051817618576a68505b92f9
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/50/84fc2a88b6bdba8db93bd3953a8f4fdb470238 b/tests/resources/merge-resolve/.gitted/objects/50/84fc2a88b6bdba8db93bd3953a8f4fdb470238
index 84c9987ce..84c9987ce 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/50/84fc2a88b6bdba8db93bd3953a8f4fdb470238
+++ b/tests/resources/merge-resolve/.gitted/objects/50/84fc2a88b6bdba8db93bd3953a8f4fdb470238
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/50/ce7d7d01217679e26c55939eef119e0c93e272 b/tests/resources/merge-resolve/.gitted/objects/50/ce7d7d01217679e26c55939eef119e0c93e272
index e2f9f67fd..e2f9f67fd 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/50/ce7d7d01217679e26c55939eef119e0c93e272
+++ b/tests/resources/merge-resolve/.gitted/objects/50/ce7d7d01217679e26c55939eef119e0c93e272
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/51/95a1b480f66691b667f10a9e41e70115a78351 b/tests/resources/merge-resolve/.gitted/objects/51/95a1b480f66691b667f10a9e41e70115a78351
index 088ee5498..088ee5498 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/51/95a1b480f66691b667f10a9e41e70115a78351
+++ b/tests/resources/merge-resolve/.gitted/objects/51/95a1b480f66691b667f10a9e41e70115a78351
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/52/d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9 b/tests/resources/merge-resolve/.gitted/objects/52/d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9
index 6522209bd..6522209bd 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/52/d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9
+++ b/tests/resources/merge-resolve/.gitted/objects/52/d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/53/825f41ac8d640612f9423a2f03a69f3d96809a b/tests/resources/merge-resolve/.gitted/objects/53/825f41ac8d640612f9423a2f03a69f3d96809a
index 08cb0b66f..08cb0b66f 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/53/825f41ac8d640612f9423a2f03a69f3d96809a
+++ b/tests/resources/merge-resolve/.gitted/objects/53/825f41ac8d640612f9423a2f03a69f3d96809a
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/54/269b3f6ec3d7d4ede24dd350dd5d605495c3ae b/tests/resources/merge-resolve/.gitted/objects/54/269b3f6ec3d7d4ede24dd350dd5d605495c3ae
index 4a2415339..4a2415339 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/54/269b3f6ec3d7d4ede24dd350dd5d605495c3ae
+++ b/tests/resources/merge-resolve/.gitted/objects/54/269b3f6ec3d7d4ede24dd350dd5d605495c3ae
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/54/59c89aa0026d543ce8343bd89871bce543f9c2 b/tests/resources/merge-resolve/.gitted/objects/54/59c89aa0026d543ce8343bd89871bce543f9c2
index 178b833e8..178b833e8 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/54/59c89aa0026d543ce8343bd89871bce543f9c2
+++ b/tests/resources/merge-resolve/.gitted/objects/54/59c89aa0026d543ce8343bd89871bce543f9c2
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/54/7607c690372fe81fab8e3bb44c530e129118fd b/tests/resources/merge-resolve/.gitted/objects/54/7607c690372fe81fab8e3bb44c530e129118fd
index dccd22006..dccd22006 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/54/7607c690372fe81fab8e3bb44c530e129118fd
+++ b/tests/resources/merge-resolve/.gitted/objects/54/7607c690372fe81fab8e3bb44c530e129118fd
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/55/b4e4687e7a0d9ca367016ed930f385d4022e6f b/tests/resources/merge-resolve/.gitted/objects/55/b4e4687e7a0d9ca367016ed930f385d4022e6f
index fb157a214..fb157a214 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/55/b4e4687e7a0d9ca367016ed930f385d4022e6f
+++ b/tests/resources/merge-resolve/.gitted/objects/55/b4e4687e7a0d9ca367016ed930f385d4022e6f
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/56/6ab53c220a2eafc1212af1a024513230280ab9 b/tests/resources/merge-resolve/.gitted/objects/56/6ab53c220a2eafc1212af1a024513230280ab9
index a8855ae67..a8855ae67 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/56/6ab53c220a2eafc1212af1a024513230280ab9
+++ b/tests/resources/merge-resolve/.gitted/objects/56/6ab53c220a2eafc1212af1a024513230280ab9
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/56/a638b76b75e068590ac999c2f8621e7f3e264c b/tests/resources/merge-resolve/.gitted/objects/56/a638b76b75e068590ac999c2f8621e7f3e264c
index 36289bf7a..36289bf7a 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/56/a638b76b75e068590ac999c2f8621e7f3e264c
+++ b/tests/resources/merge-resolve/.gitted/objects/56/a638b76b75e068590ac999c2f8621e7f3e264c
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/57/079a46233ae2b6df62e9ade71c4948512abefb b/tests/resources/merge-resolve/.gitted/objects/57/079a46233ae2b6df62e9ade71c4948512abefb
index c7eabc46b..c7eabc46b 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/57/079a46233ae2b6df62e9ade71c4948512abefb
+++ b/tests/resources/merge-resolve/.gitted/objects/57/079a46233ae2b6df62e9ade71c4948512abefb
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/58/43febcb23480df0b5edb22a21c59c772bb8e29 b/tests/resources/merge-resolve/.gitted/objects/58/43febcb23480df0b5edb22a21c59c772bb8e29
index f6b2a2bfe..f6b2a2bfe 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/58/43febcb23480df0b5edb22a21c59c772bb8e29
+++ b/tests/resources/merge-resolve/.gitted/objects/58/43febcb23480df0b5edb22a21c59c772bb8e29
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/58/e853f66699fd02629fd50bde08082bc005933a b/tests/resources/merge-resolve/.gitted/objects/58/e853f66699fd02629fd50bde08082bc005933a
index cf6db633c..cf6db633c 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/58/e853f66699fd02629fd50bde08082bc005933a
+++ b/tests/resources/merge-resolve/.gitted/objects/58/e853f66699fd02629fd50bde08082bc005933a
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/59/6803b523203a4851c824c07366906f8353f4ad b/tests/resources/merge-resolve/.gitted/objects/59/6803b523203a4851c824c07366906f8353f4ad
index cbc8cbef3..cbc8cbef3 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/59/6803b523203a4851c824c07366906f8353f4ad
+++ b/tests/resources/merge-resolve/.gitted/objects/59/6803b523203a4851c824c07366906f8353f4ad
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/5c/2411f8075f48a6b2fdb85ebc0d371747c4df15 b/tests/resources/merge-resolve/.gitted/objects/5c/2411f8075f48a6b2fdb85ebc0d371747c4df15
index 7b41413da..7b41413da 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/5c/2411f8075f48a6b2fdb85ebc0d371747c4df15
+++ b/tests/resources/merge-resolve/.gitted/objects/5c/2411f8075f48a6b2fdb85ebc0d371747c4df15
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/5c/341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d b/tests/resources/merge-resolve/.gitted/objects/5c/341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d
index 63c86bd33..63c86bd33 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/5c/341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d
+++ b/tests/resources/merge-resolve/.gitted/objects/5c/341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/5c/3b68a71fc4fa5d362fd3875e53137c6a5ab7a5 b/tests/resources/merge-resolve/.gitted/objects/5c/3b68a71fc4fa5d362fd3875e53137c6a5ab7a5
index 541001456..541001456 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/5c/3b68a71fc4fa5d362fd3875e53137c6a5ab7a5
+++ b/tests/resources/merge-resolve/.gitted/objects/5c/3b68a71fc4fa5d362fd3875e53137c6a5ab7a5
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/5d/c1018e90b19654bee986b7a0c268804d39659d b/tests/resources/merge-resolve/.gitted/objects/5d/c1018e90b19654bee986b7a0c268804d39659d
index 7500b9914..7500b9914 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/5d/c1018e90b19654bee986b7a0c268804d39659d
+++ b/tests/resources/merge-resolve/.gitted/objects/5d/c1018e90b19654bee986b7a0c268804d39659d
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/5d/dd0fe66f990dc0e5cf9fec6d9b465240e9537f b/tests/resources/merge-resolve/.gitted/objects/5d/dd0fe66f990dc0e5cf9fec6d9b465240e9537f
index 9d8691eb2..9d8691eb2 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/5d/dd0fe66f990dc0e5cf9fec6d9b465240e9537f
+++ b/tests/resources/merge-resolve/.gitted/objects/5d/dd0fe66f990dc0e5cf9fec6d9b465240e9537f
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/5e/b7bb6a146eb3c7fd3990b240a2308eceb1cf8d b/tests/resources/merge-resolve/.gitted/objects/5e/b7bb6a146eb3c7fd3990b240a2308eceb1cf8d
index aca2666cf..aca2666cf 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/5e/b7bb6a146eb3c7fd3990b240a2308eceb1cf8d
+++ b/tests/resources/merge-resolve/.gitted/objects/5e/b7bb6a146eb3c7fd3990b240a2308eceb1cf8d
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/5f/bfbdc04b4eca46f54f4853a3c5a1dce28f5165 b/tests/resources/merge-resolve/.gitted/objects/5f/bfbdc04b4eca46f54f4853a3c5a1dce28f5165
index aec3867c8..aec3867c8 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/5f/bfbdc04b4eca46f54f4853a3c5a1dce28f5165
+++ b/tests/resources/merge-resolve/.gitted/objects/5f/bfbdc04b4eca46f54f4853a3c5a1dce28f5165
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/60/91fc2c036a382a69489e3f518ee5aae9a4e567 b/tests/resources/merge-resolve/.gitted/objects/60/91fc2c036a382a69489e3f518ee5aae9a4e567
index fa63afba1..fa63afba1 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/60/91fc2c036a382a69489e3f518ee5aae9a4e567
+++ b/tests/resources/merge-resolve/.gitted/objects/60/91fc2c036a382a69489e3f518ee5aae9a4e567
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/61/340eeed7340fa6a8792def9a5938bb5d4434bb b/tests/resources/merge-resolve/.gitted/objects/61/340eeed7340fa6a8792def9a5938bb5d4434bb
index e830cafe5..e830cafe5 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/61/340eeed7340fa6a8792def9a5938bb5d4434bb
+++ b/tests/resources/merge-resolve/.gitted/objects/61/340eeed7340fa6a8792def9a5938bb5d4434bb
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/61/78885b38fe96e825ac0f492c0a941f288b37f6 b/tests/resources/merge-resolve/.gitted/objects/61/78885b38fe96e825ac0f492c0a941f288b37f6
index bedc5f27e..bedc5f27e 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/61/78885b38fe96e825ac0f492c0a941f288b37f6
+++ b/tests/resources/merge-resolve/.gitted/objects/61/78885b38fe96e825ac0f492c0a941f288b37f6
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/62/12c31dab5e482247d7977e4f0dd3601decf13b b/tests/resources/merge-resolve/.gitted/objects/62/12c31dab5e482247d7977e4f0dd3601decf13b
index b6f0607bb..b6f0607bb 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/62/12c31dab5e482247d7977e4f0dd3601decf13b
+++ b/tests/resources/merge-resolve/.gitted/objects/62/12c31dab5e482247d7977e4f0dd3601decf13b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/62/269111c3b02a9355badcb9da8678b1bf41787b b/tests/resources/merge-resolve/.gitted/objects/62/269111c3b02a9355badcb9da8678b1bf41787b
index 0edf65994..0edf65994 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/62/269111c3b02a9355badcb9da8678b1bf41787b
+++ b/tests/resources/merge-resolve/.gitted/objects/62/269111c3b02a9355badcb9da8678b1bf41787b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/62/c4f6533c9a3894191fdcb96a3be935ade63f1a b/tests/resources/merge-resolve/.gitted/objects/62/c4f6533c9a3894191fdcb96a3be935ade63f1a
index c0f822d2c..c0f822d2c 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/62/c4f6533c9a3894191fdcb96a3be935ade63f1a
+++ b/tests/resources/merge-resolve/.gitted/objects/62/c4f6533c9a3894191fdcb96a3be935ade63f1a
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/63/247125386de9ec90a27ad36169307bf8a11a38 b/tests/resources/merge-resolve/.gitted/objects/63/247125386de9ec90a27ad36169307bf8a11a38
index bc2d7384d..bc2d7384d 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/63/247125386de9ec90a27ad36169307bf8a11a38
+++ b/tests/resources/merge-resolve/.gitted/objects/63/247125386de9ec90a27ad36169307bf8a11a38
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/67/18a45909532d1fcf5600d0877f7fe7e78f0b86 b/tests/resources/merge-resolve/.gitted/objects/67/18a45909532d1fcf5600d0877f7fe7e78f0b86
index ffda698f0..ffda698f0 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/67/18a45909532d1fcf5600d0877f7fe7e78f0b86
+++ b/tests/resources/merge-resolve/.gitted/objects/67/18a45909532d1fcf5600d0877f7fe7e78f0b86
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/68/c6c84b091926c7d90aa6a79b2bc3bb6adccd8e b/tests/resources/merge-resolve/.gitted/objects/68/c6c84b091926c7d90aa6a79b2bc3bb6adccd8e
index 1e4b07574..1e4b07574 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/68/c6c84b091926c7d90aa6a79b2bc3bb6adccd8e
+++ b/tests/resources/merge-resolve/.gitted/objects/68/c6c84b091926c7d90aa6a79b2bc3bb6adccd8e
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/69/f570c57b24ea7c086e94c5e574964798321435 b/tests/resources/merge-resolve/.gitted/objects/69/f570c57b24ea7c086e94c5e574964798321435
index 6975f0bab..6975f0bab 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/69/f570c57b24ea7c086e94c5e574964798321435
+++ b/tests/resources/merge-resolve/.gitted/objects/69/f570c57b24ea7c086e94c5e574964798321435
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/6a/e1a3967031a42cf955d9d5c2395211ac82f6cf b/tests/resources/merge-resolve/.gitted/objects/6a/e1a3967031a42cf955d9d5c2395211ac82f6cf
index 3b5713cbc..3b5713cbc 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/6a/e1a3967031a42cf955d9d5c2395211ac82f6cf
+++ b/tests/resources/merge-resolve/.gitted/objects/6a/e1a3967031a42cf955d9d5c2395211ac82f6cf
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/6b/7e37be8ce0b897093f2878a9dcd8f396beda2c b/tests/resources/merge-resolve/.gitted/objects/6b/7e37be8ce0b897093f2878a9dcd8f396beda2c
index c39318683..c39318683 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/6b/7e37be8ce0b897093f2878a9dcd8f396beda2c
+++ b/tests/resources/merge-resolve/.gitted/objects/6b/7e37be8ce0b897093f2878a9dcd8f396beda2c
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/6c/06dcd163587c2cc18be44857e0b71116382aeb b/tests/resources/merge-resolve/.gitted/objects/6c/06dcd163587c2cc18be44857e0b71116382aeb
index 2f54be818..2f54be818 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/6c/06dcd163587c2cc18be44857e0b71116382aeb
+++ b/tests/resources/merge-resolve/.gitted/objects/6c/06dcd163587c2cc18be44857e0b71116382aeb
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/6f/32739c3724d1d5f855299309f388606f407468 b/tests/resources/merge-resolve/.gitted/objects/6f/32739c3724d1d5f855299309f388606f407468
index 6741aa4d5..6741aa4d5 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/6f/32739c3724d1d5f855299309f388606f407468
+++ b/tests/resources/merge-resolve/.gitted/objects/6f/32739c3724d1d5f855299309f388606f407468
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/6f/a33014764bf1120a454eb8437ae098238e409b b/tests/resources/merge-resolve/.gitted/objects/6f/a33014764bf1120a454eb8437ae098238e409b
index 973a4f646..973a4f646 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/6f/a33014764bf1120a454eb8437ae098238e409b
+++ b/tests/resources/merge-resolve/.gitted/objects/6f/a33014764bf1120a454eb8437ae098238e409b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/6f/be9fb85c86d7d1435f728da418bdff52c640a9 b/tests/resources/merge-resolve/.gitted/objects/6f/be9fb85c86d7d1435f728da418bdff52c640a9
index a2c8d93ad..a2c8d93ad 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/6f/be9fb85c86d7d1435f728da418bdff52c640a9
+++ b/tests/resources/merge-resolve/.gitted/objects/6f/be9fb85c86d7d1435f728da418bdff52c640a9
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/71/17467b18605a660ebe5586df69e2311ed5609f b/tests/resources/merge-resolve/.gitted/objects/71/17467b18605a660ebe5586df69e2311ed5609f
index 02e183144..02e183144 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/71/17467b18605a660ebe5586df69e2311ed5609f
+++ b/tests/resources/merge-resolve/.gitted/objects/71/17467b18605a660ebe5586df69e2311ed5609f
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/71/2ebba6669ea847d9829e4f1059d6c830c8b531 b/tests/resources/merge-resolve/.gitted/objects/71/2ebba6669ea847d9829e4f1059d6c830c8b531
index dd7d58f1f..dd7d58f1f 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/71/2ebba6669ea847d9829e4f1059d6c830c8b531
+++ b/tests/resources/merge-resolve/.gitted/objects/71/2ebba6669ea847d9829e4f1059d6c830c8b531
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/71/add2d7b93d55bf3600f8a1582beceebbd050c8 b/tests/resources/merge-resolve/.gitted/objects/71/add2d7b93d55bf3600f8a1582beceebbd050c8
index 221afa3c8..221afa3c8 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/71/add2d7b93d55bf3600f8a1582beceebbd050c8
+++ b/tests/resources/merge-resolve/.gitted/objects/71/add2d7b93d55bf3600f8a1582beceebbd050c8
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/72/ea499e108df5ff0a4a913e7655bbeeb1fb69f2 b/tests/resources/merge-resolve/.gitted/objects/72/ea499e108df5ff0a4a913e7655bbeeb1fb69f2
new file mode 100644
index 000000000..4886e492e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/72/ea499e108df5ff0a4a913e7655bbeeb1fb69f2
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/74/df13f0793afdaa972150bba976f7de8284914e b/tests/resources/merge-resolve/.gitted/objects/74/df13f0793afdaa972150bba976f7de8284914e
index cb50e6757..cb50e6757 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/74/df13f0793afdaa972150bba976f7de8284914e
+++ b/tests/resources/merge-resolve/.gitted/objects/74/df13f0793afdaa972150bba976f7de8284914e
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/75/a811bf6bc57694adb3fe604786f3a4efd1cd1b b/tests/resources/merge-resolve/.gitted/objects/75/a811bf6bc57694adb3fe604786f3a4efd1cd1b
index 477fd87ec..477fd87ec 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/75/a811bf6bc57694adb3fe604786f3a4efd1cd1b
+++ b/tests/resources/merge-resolve/.gitted/objects/75/a811bf6bc57694adb3fe604786f3a4efd1cd1b
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/76/63fce0130db092936b137cabd693ec234eb060 b/tests/resources/merge-resolve/.gitted/objects/76/63fce0130db092936b137cabd693ec234eb060
index f578a4a68..f578a4a68 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/76/63fce0130db092936b137cabd693ec234eb060
+++ b/tests/resources/merge-resolve/.gitted/objects/76/63fce0130db092936b137cabd693ec234eb060
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/76/ab0e2868197ec158ddd6c78d8a0d2fd73d38f9 b/tests/resources/merge-resolve/.gitted/objects/76/ab0e2868197ec158ddd6c78d8a0d2fd73d38f9
index 4d41ad8cd..4d41ad8cd 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/76/ab0e2868197ec158ddd6c78d8a0d2fd73d38f9
+++ b/tests/resources/merge-resolve/.gitted/objects/76/ab0e2868197ec158ddd6c78d8a0d2fd73d38f9
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/7a/a3edf2bcfee22398e6b55295aa56366b7aaf76 b/tests/resources/merge-resolve/.gitted/objects/7a/a3edf2bcfee22398e6b55295aa56366b7aaf76
index 09f1e4d3a..09f1e4d3a 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/7a/a3edf2bcfee22398e6b55295aa56366b7aaf76
+++ b/tests/resources/merge-resolve/.gitted/objects/7a/a3edf2bcfee22398e6b55295aa56366b7aaf76
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/7c/2c5228c9e90170d4a35e6558e47163daf092e5 b/tests/resources/merge-resolve/.gitted/objects/7c/2c5228c9e90170d4a35e6558e47163daf092e5
index 52fde92a1..52fde92a1 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/7c/2c5228c9e90170d4a35e6558e47163daf092e5
+++ b/tests/resources/merge-resolve/.gitted/objects/7c/2c5228c9e90170d4a35e6558e47163daf092e5
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/7c/b63eed597130ba4abb87b3e544b85021905520 b/tests/resources/merge-resolve/.gitted/objects/7c/b63eed597130ba4abb87b3e544b85021905520
index 769f29c6e..769f29c6e 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/7c/b63eed597130ba4abb87b3e544b85021905520
+++ b/tests/resources/merge-resolve/.gitted/objects/7c/b63eed597130ba4abb87b3e544b85021905520
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/7e/2d058d5fedf8329db44db4fac610d6b1a89159 b/tests/resources/merge-resolve/.gitted/objects/7e/2d058d5fedf8329db44db4fac610d6b1a89159
index d12d7b4a7..d12d7b4a7 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/7e/2d058d5fedf8329db44db4fac610d6b1a89159
+++ b/tests/resources/merge-resolve/.gitted/objects/7e/2d058d5fedf8329db44db4fac610d6b1a89159
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/7f/7a2da58126226986d71c6ddfab4afba693280d b/tests/resources/merge-resolve/.gitted/objects/7f/7a2da58126226986d71c6ddfab4afba693280d
index 2f833c292..2f833c292 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/7f/7a2da58126226986d71c6ddfab4afba693280d
+++ b/tests/resources/merge-resolve/.gitted/objects/7f/7a2da58126226986d71c6ddfab4afba693280d
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/80/a8fbb3abb1ba423d554e9630b8fc2e5698f86b b/tests/resources/merge-resolve/.gitted/objects/80/a8fbb3abb1ba423d554e9630b8fc2e5698f86b
index 3daf6c3e0..3daf6c3e0 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/80/a8fbb3abb1ba423d554e9630b8fc2e5698f86b
+++ b/tests/resources/merge-resolve/.gitted/objects/80/a8fbb3abb1ba423d554e9630b8fc2e5698f86b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/81/87117062b750eed4f93fd7e899f17b52ce554d b/tests/resources/merge-resolve/.gitted/objects/81/87117062b750eed4f93fd7e899f17b52ce554d
index 19cac9faf..19cac9faf 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/81/87117062b750eed4f93fd7e899f17b52ce554d
+++ b/tests/resources/merge-resolve/.gitted/objects/81/87117062b750eed4f93fd7e899f17b52ce554d
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/83/07d93a155903a5c49576583f0ce1f6ff897c0e b/tests/resources/merge-resolve/.gitted/objects/83/07d93a155903a5c49576583f0ce1f6ff897c0e
index 5a96a4e4e..5a96a4e4e 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/83/07d93a155903a5c49576583f0ce1f6ff897c0e
+++ b/tests/resources/merge-resolve/.gitted/objects/83/07d93a155903a5c49576583f0ce1f6ff897c0e
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/83/824a8c6658768e2013905219cc8c64cc3d9a2e b/tests/resources/merge-resolve/.gitted/objects/83/824a8c6658768e2013905219cc8c64cc3d9a2e
index 066190fb8..066190fb8 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/83/824a8c6658768e2013905219cc8c64cc3d9a2e
+++ b/tests/resources/merge-resolve/.gitted/objects/83/824a8c6658768e2013905219cc8c64cc3d9a2e
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/84/9619b03ae540acee4d1edec96b86993da6b497 b/tests/resources/merge-resolve/.gitted/objects/84/9619b03ae540acee4d1edec96b86993da6b497
index 67271ac50..67271ac50 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/84/9619b03ae540acee4d1edec96b86993da6b497
+++ b/tests/resources/merge-resolve/.gitted/objects/84/9619b03ae540acee4d1edec96b86993da6b497
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/84/de84f8f3a6d63e636ee9ad81f4b80512fa9bbe b/tests/resources/merge-resolve/.gitted/objects/84/de84f8f3a6d63e636ee9ad81f4b80512fa9bbe
index 32f1461d4..32f1461d4 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/84/de84f8f3a6d63e636ee9ad81f4b80512fa9bbe
+++ b/tests/resources/merge-resolve/.gitted/objects/84/de84f8f3a6d63e636ee9ad81f4b80512fa9bbe
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/86/088dae8bade454995b21a1c88107b0e1accdab b/tests/resources/merge-resolve/.gitted/objects/86/088dae8bade454995b21a1c88107b0e1accdab
index 623a747f0..623a747f0 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/86/088dae8bade454995b21a1c88107b0e1accdab
+++ b/tests/resources/merge-resolve/.gitted/objects/86/088dae8bade454995b21a1c88107b0e1accdab
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/87/b4926260d77a3b851e71ecce06839bd650b231 b/tests/resources/merge-resolve/.gitted/objects/87/b4926260d77a3b851e71ecce06839bd650b231
index 91944ffb5..91944ffb5 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/87/b4926260d77a3b851e71ecce06839bd650b231
+++ b/tests/resources/merge-resolve/.gitted/objects/87/b4926260d77a3b851e71ecce06839bd650b231
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/88/e185910a15cd13bdf44854ad037f4842b03b29 b/tests/resources/merge-resolve/.gitted/objects/88/e185910a15cd13bdf44854ad037f4842b03b29
index ae1c5e242..ae1c5e242 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/88/e185910a15cd13bdf44854ad037f4842b03b29
+++ b/tests/resources/merge-resolve/.gitted/objects/88/e185910a15cd13bdf44854ad037f4842b03b29
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/8a/ad9d0ea334951da47b621a475b39cc6ed759bf b/tests/resources/merge-resolve/.gitted/objects/8a/ad9d0ea334951da47b621a475b39cc6ed759bf
index 5e2c94321..5e2c94321 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/8a/ad9d0ea334951da47b621a475b39cc6ed759bf
+++ b/tests/resources/merge-resolve/.gitted/objects/8a/ad9d0ea334951da47b621a475b39cc6ed759bf
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/8a/ae714f7d939309d7f132b30646d96743134a9f b/tests/resources/merge-resolve/.gitted/objects/8a/ae714f7d939309d7f132b30646d96743134a9f
index 34ff560e3..34ff560e3 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/8a/ae714f7d939309d7f132b30646d96743134a9f
+++ b/tests/resources/merge-resolve/.gitted/objects/8a/ae714f7d939309d7f132b30646d96743134a9f
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/8b/095d8fd01594f4d14454d073e3ac57b9ce485f b/tests/resources/merge-resolve/.gitted/objects/8b/095d8fd01594f4d14454d073e3ac57b9ce485f
index 4ec013881..4ec013881 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/8b/095d8fd01594f4d14454d073e3ac57b9ce485f
+++ b/tests/resources/merge-resolve/.gitted/objects/8b/095d8fd01594f4d14454d073e3ac57b9ce485f
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/8b/5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a b/tests/resources/merge-resolve/.gitted/objects/8b/5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a
index f4249c23d..f4249c23d 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/8b/5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a
+++ b/tests/resources/merge-resolve/.gitted/objects/8b/5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a
diff --git a/tests/resources/merge-resolve/.gitted/objects/8b/fb012a6d809e499bd8d3e194a3929bc8995b93 b/tests/resources/merge-resolve/.gitted/objects/8b/fb012a6d809e499bd8d3e194a3929bc8995b93
new file mode 100644
index 000000000..a90ee08ce
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/8b/fb012a6d809e499bd8d3e194a3929bc8995b93
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/8c/749d9968d4b10dcfb06c9f97d0e5d92d337071 b/tests/resources/merge-resolve/.gitted/objects/8c/749d9968d4b10dcfb06c9f97d0e5d92d337071
index e42393cf7..e42393cf7 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/8c/749d9968d4b10dcfb06c9f97d0e5d92d337071
+++ b/tests/resources/merge-resolve/.gitted/objects/8c/749d9968d4b10dcfb06c9f97d0e5d92d337071
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/8f/4433f8593ddd65b7dd43dd4564d841f4d9c8aa b/tests/resources/merge-resolve/.gitted/objects/8f/4433f8593ddd65b7dd43dd4564d841f4d9c8aa
index d2de777cc..d2de777cc 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/8f/4433f8593ddd65b7dd43dd4564d841f4d9c8aa
+++ b/tests/resources/merge-resolve/.gitted/objects/8f/4433f8593ddd65b7dd43dd4564d841f4d9c8aa
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/90/a336c7dacbe295159413559b0043b8bdc60d57 b/tests/resources/merge-resolve/.gitted/objects/90/a336c7dacbe295159413559b0043b8bdc60d57
index 35453ebfd..35453ebfd 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/90/a336c7dacbe295159413559b0043b8bdc60d57
+++ b/tests/resources/merge-resolve/.gitted/objects/90/a336c7dacbe295159413559b0043b8bdc60d57
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/91/2b2d7819cf9c1029e414883857ed61d597a1a5 b/tests/resources/merge-resolve/.gitted/objects/91/2b2d7819cf9c1029e414883857ed61d597a1a5
index d5df393e9..d5df393e9 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/91/2b2d7819cf9c1029e414883857ed61d597a1a5
+++ b/tests/resources/merge-resolve/.gitted/objects/91/2b2d7819cf9c1029e414883857ed61d597a1a5
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/91/8bb3e09090a9995d48af9a2a6296d7e6088d1c b/tests/resources/merge-resolve/.gitted/objects/91/8bb3e09090a9995d48af9a2a6296d7e6088d1c
index c214ab206..c214ab206 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/91/8bb3e09090a9995d48af9a2a6296d7e6088d1c
+++ b/tests/resources/merge-resolve/.gitted/objects/91/8bb3e09090a9995d48af9a2a6296d7e6088d1c
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/92/7d4943cdbdc9a667db8e62cfd0a41870235c51 b/tests/resources/merge-resolve/.gitted/objects/92/7d4943cdbdc9a667db8e62cfd0a41870235c51
index b6b92c842..b6b92c842 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/92/7d4943cdbdc9a667db8e62cfd0a41870235c51
+++ b/tests/resources/merge-resolve/.gitted/objects/92/7d4943cdbdc9a667db8e62cfd0a41870235c51
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/93/77fccdb210540b8c0520cc6e80eb632c20bd25 b/tests/resources/merge-resolve/.gitted/objects/93/77fccdb210540b8c0520cc6e80eb632c20bd25
index 4b2d93b07..4b2d93b07 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/93/77fccdb210540b8c0520cc6e80eb632c20bd25
+++ b/tests/resources/merge-resolve/.gitted/objects/93/77fccdb210540b8c0520cc6e80eb632c20bd25
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/94/4f5dd1a867cab4c2bbcb896493435cae1dcc1a b/tests/resources/merge-resolve/.gitted/objects/94/4f5dd1a867cab4c2bbcb896493435cae1dcc1a
index 143093831..143093831 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/94/4f5dd1a867cab4c2bbcb896493435cae1dcc1a
+++ b/tests/resources/merge-resolve/.gitted/objects/94/4f5dd1a867cab4c2bbcb896493435cae1dcc1a
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/94/8ba6e701c1edab0c2d394fb7c5538334129793 b/tests/resources/merge-resolve/.gitted/objects/94/8ba6e701c1edab0c2d394fb7c5538334129793
index b3e3ef985..b3e3ef985 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/94/8ba6e701c1edab0c2d394fb7c5538334129793
+++ b/tests/resources/merge-resolve/.gitted/objects/94/8ba6e701c1edab0c2d394fb7c5538334129793
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/95/646149ab6b6ba6edc83cff678582538b457b2b b/tests/resources/merge-resolve/.gitted/objects/95/646149ab6b6ba6edc83cff678582538b457b2b
index de9ba2894..de9ba2894 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/95/646149ab6b6ba6edc83cff678582538b457b2b
+++ b/tests/resources/merge-resolve/.gitted/objects/95/646149ab6b6ba6edc83cff678582538b457b2b
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/95/9de65e568274120fdf9e3af9f77b1550122149 b/tests/resources/merge-resolve/.gitted/objects/95/9de65e568274120fdf9e3af9f77b1550122149
index e998de849..e998de849 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/95/9de65e568274120fdf9e3af9f77b1550122149
+++ b/tests/resources/merge-resolve/.gitted/objects/95/9de65e568274120fdf9e3af9f77b1550122149
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/96/8ca794a4597f7f6abbb2b8d940b4078a0f3fd4 b/tests/resources/merge-resolve/.gitted/objects/96/8ca794a4597f7f6abbb2b8d940b4078a0f3fd4
index 359e43a88..359e43a88 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/96/8ca794a4597f7f6abbb2b8d940b4078a0f3fd4
+++ b/tests/resources/merge-resolve/.gitted/objects/96/8ca794a4597f7f6abbb2b8d940b4078a0f3fd4
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/97/7c696519c5a3004c5f1d15d60c89dbeb8f235f b/tests/resources/merge-resolve/.gitted/objects/97/7c696519c5a3004c5f1d15d60c89dbeb8f235f
index e561b473f..e561b473f 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/97/7c696519c5a3004c5f1d15d60c89dbeb8f235f
+++ b/tests/resources/merge-resolve/.gitted/objects/97/7c696519c5a3004c5f1d15d60c89dbeb8f235f
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/98/ba4205fcf31f5dd93c916d35fe3f3b3d0e6714 b/tests/resources/merge-resolve/.gitted/objects/98/ba4205fcf31f5dd93c916d35fe3f3b3d0e6714
index 6f5e97978..6f5e97978 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/98/ba4205fcf31f5dd93c916d35fe3f3b3d0e6714
+++ b/tests/resources/merge-resolve/.gitted/objects/98/ba4205fcf31f5dd93c916d35fe3f3b3d0e6714
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/98/d52d07c0b0bbf2b46548f6aa521295c2cb55db b/tests/resources/merge-resolve/.gitted/objects/98/d52d07c0b0bbf2b46548f6aa521295c2cb55db
index c8d636e8b..c8d636e8b 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/98/d52d07c0b0bbf2b46548f6aa521295c2cb55db
+++ b/tests/resources/merge-resolve/.gitted/objects/98/d52d07c0b0bbf2b46548f6aa521295c2cb55db
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/99/b4f7e4f24470fa06b980bc21f1095c2a9425c0 b/tests/resources/merge-resolve/.gitted/objects/99/b4f7e4f24470fa06b980bc21f1095c2a9425c0
index 01ad66eaa..01ad66eaa 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/99/b4f7e4f24470fa06b980bc21f1095c2a9425c0
+++ b/tests/resources/merge-resolve/.gitted/objects/99/b4f7e4f24470fa06b980bc21f1095c2a9425c0
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/9a/301fbe6fada7dcb74fcd7c20269b5c743459a7 b/tests/resources/merge-resolve/.gitted/objects/9a/301fbe6fada7dcb74fcd7c20269b5c743459a7
index f413cc5f7..f413cc5f7 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/9a/301fbe6fada7dcb74fcd7c20269b5c743459a7
+++ b/tests/resources/merge-resolve/.gitted/objects/9a/301fbe6fada7dcb74fcd7c20269b5c743459a7
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/9a/f731fa116d1eb9a6c0109562472cfee6f5a979 b/tests/resources/merge-resolve/.gitted/objects/9a/f731fa116d1eb9a6c0109562472cfee6f5a979
index 53233c4f1..53233c4f1 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/9a/f731fa116d1eb9a6c0109562472cfee6f5a979
+++ b/tests/resources/merge-resolve/.gitted/objects/9a/f731fa116d1eb9a6c0109562472cfee6f5a979
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/9c/0b6c34ef379a42d858f03fef38630f476b9102 b/tests/resources/merge-resolve/.gitted/objects/9c/0b6c34ef379a42d858f03fef38630f476b9102
index e6f850079..e6f850079 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/9c/0b6c34ef379a42d858f03fef38630f476b9102
+++ b/tests/resources/merge-resolve/.gitted/objects/9c/0b6c34ef379a42d858f03fef38630f476b9102
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/9e/7f4359c469f309b6057febf4c6e80742cbed5b b/tests/resources/merge-resolve/.gitted/objects/9e/7f4359c469f309b6057febf4c6e80742cbed5b
index 72b7c49fc..72b7c49fc 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/9e/7f4359c469f309b6057febf4c6e80742cbed5b
+++ b/tests/resources/merge-resolve/.gitted/objects/9e/7f4359c469f309b6057febf4c6e80742cbed5b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/9e/fe7723802d4305142eee177e018fee1572c4f4 b/tests/resources/merge-resolve/.gitted/objects/9e/fe7723802d4305142eee177e018fee1572c4f4
index c63fc2c96..c63fc2c96 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/9e/fe7723802d4305142eee177e018fee1572c4f4
+++ b/tests/resources/merge-resolve/.gitted/objects/9e/fe7723802d4305142eee177e018fee1572c4f4
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/9f/74397a3397b3585faf09e9926b110d7f654254 b/tests/resources/merge-resolve/.gitted/objects/9f/74397a3397b3585faf09e9926b110d7f654254
index e7ec3973a..e7ec3973a 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/9f/74397a3397b3585faf09e9926b110d7f654254
+++ b/tests/resources/merge-resolve/.gitted/objects/9f/74397a3397b3585faf09e9926b110d7f654254
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a0/31a28ae70e33a641ce4b8a8f6317f1ab79dee4 b/tests/resources/merge-resolve/.gitted/objects/a0/31a28ae70e33a641ce4b8a8f6317f1ab79dee4
index a6c05d182..a6c05d182 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/a0/31a28ae70e33a641ce4b8a8f6317f1ab79dee4
+++ b/tests/resources/merge-resolve/.gitted/objects/a0/31a28ae70e33a641ce4b8a8f6317f1ab79dee4
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a3/9a620dae5bc8b4e771cd4d251b7d080401a21e b/tests/resources/merge-resolve/.gitted/objects/a3/9a620dae5bc8b4e771cd4d251b7d080401a21e
index 4d22586eb..4d22586eb 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/a3/9a620dae5bc8b4e771cd4d251b7d080401a21e
+++ b/tests/resources/merge-resolve/.gitted/objects/a3/9a620dae5bc8b4e771cd4d251b7d080401a21e
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a3/fabece9eb8748da810e1e08266fef9b7136ad4 b/tests/resources/merge-resolve/.gitted/objects/a3/fabece9eb8748da810e1e08266fef9b7136ad4
index 24d7dbc2e..24d7dbc2e 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/a3/fabece9eb8748da810e1e08266fef9b7136ad4
+++ b/tests/resources/merge-resolve/.gitted/objects/a3/fabece9eb8748da810e1e08266fef9b7136ad4
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a4/1b1bb6d0be3c22fb654234c33b428e15c8cc27 b/tests/resources/merge-resolve/.gitted/objects/a4/1b1bb6d0be3c22fb654234c33b428e15c8cc27
index 60789ee36..60789ee36 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/a4/1b1bb6d0be3c22fb654234c33b428e15c8cc27
+++ b/tests/resources/merge-resolve/.gitted/objects/a4/1b1bb6d0be3c22fb654234c33b428e15c8cc27
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a4/3150a738849c59376cf30bb2a68348a83c8f48 b/tests/resources/merge-resolve/.gitted/objects/a4/3150a738849c59376cf30bb2a68348a83c8f48
index 06ae09eb6..06ae09eb6 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/a4/3150a738849c59376cf30bb2a68348a83c8f48
+++ b/tests/resources/merge-resolve/.gitted/objects/a4/3150a738849c59376cf30bb2a68348a83c8f48
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a5/563304ddf6caba25cb50323a2ea6f7dbfcadca b/tests/resources/merge-resolve/.gitted/objects/a5/563304ddf6caba25cb50323a2ea6f7dbfcadca
index a831878f8..a831878f8 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/a5/563304ddf6caba25cb50323a2ea6f7dbfcadca
+++ b/tests/resources/merge-resolve/.gitted/objects/a5/563304ddf6caba25cb50323a2ea6f7dbfcadca
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a7/08b253bd507417ec42d1467a7fd2d7519c4956 b/tests/resources/merge-resolve/.gitted/objects/a7/08b253bd507417ec42d1467a7fd2d7519c4956
index bae752a09..bae752a09 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/a7/08b253bd507417ec42d1467a7fd2d7519c4956
+++ b/tests/resources/merge-resolve/.gitted/objects/a7/08b253bd507417ec42d1467a7fd2d7519c4956
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a7/65fb87eb2f7a1920b73b2d5a057f8f8476a42b b/tests/resources/merge-resolve/.gitted/objects/a7/65fb87eb2f7a1920b73b2d5a057f8f8476a42b
index 30abd8b4d..30abd8b4d 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/a7/65fb87eb2f7a1920b73b2d5a057f8f8476a42b
+++ b/tests/resources/merge-resolve/.gitted/objects/a7/65fb87eb2f7a1920b73b2d5a057f8f8476a42b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a7/7a56a49f8f3ae242e02717f18ebbc60c5cc543 b/tests/resources/merge-resolve/.gitted/objects/a7/7a56a49f8f3ae242e02717f18ebbc60c5cc543
index 76dd5f91b..76dd5f91b 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/a7/7a56a49f8f3ae242e02717f18ebbc60c5cc543
+++ b/tests/resources/merge-resolve/.gitted/objects/a7/7a56a49f8f3ae242e02717f18ebbc60c5cc543
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a7/dbfcbfc1a60709cb80b5ca24539008456531d0 b/tests/resources/merge-resolve/.gitted/objects/a7/dbfcbfc1a60709cb80b5ca24539008456531d0
index 67126c90b..67126c90b 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/a7/dbfcbfc1a60709cb80b5ca24539008456531d0
+++ b/tests/resources/merge-resolve/.gitted/objects/a7/dbfcbfc1a60709cb80b5ca24539008456531d0
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a8/02e06f1782a9645b9851bc7202cee74a8a4972 b/tests/resources/merge-resolve/.gitted/objects/a8/02e06f1782a9645b9851bc7202cee74a8a4972
index d39034b82..d39034b82 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/a8/02e06f1782a9645b9851bc7202cee74a8a4972
+++ b/tests/resources/merge-resolve/.gitted/objects/a8/02e06f1782a9645b9851bc7202cee74a8a4972
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a8/87dd39ad3edd610fc9083dcb61e40ab50673d1 b/tests/resources/merge-resolve/.gitted/objects/a8/87dd39ad3edd610fc9083dcb61e40ab50673d1
index 968c42ae2..968c42ae2 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/a8/87dd39ad3edd610fc9083dcb61e40ab50673d1
+++ b/tests/resources/merge-resolve/.gitted/objects/a8/87dd39ad3edd610fc9083dcb61e40ab50673d1
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a9/0bc3fb6f15181972a2959a921429efbd81a473 b/tests/resources/merge-resolve/.gitted/objects/a9/0bc3fb6f15181972a2959a921429efbd81a473
index 91113ee8e..91113ee8e 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/a9/0bc3fb6f15181972a2959a921429efbd81a473
+++ b/tests/resources/merge-resolve/.gitted/objects/a9/0bc3fb6f15181972a2959a921429efbd81a473
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ab/40af3cb8a3ed2e2843e96d9aa7871336b94573 b/tests/resources/merge-resolve/.gitted/objects/ab/40af3cb8a3ed2e2843e96d9aa7871336b94573
index 7da1da656..7da1da656 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/ab/40af3cb8a3ed2e2843e96d9aa7871336b94573
+++ b/tests/resources/merge-resolve/.gitted/objects/ab/40af3cb8a3ed2e2843e96d9aa7871336b94573
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ab/6c44a2e84492ad4b41bb6bac87353e9d02ac8b b/tests/resources/merge-resolve/.gitted/objects/ab/6c44a2e84492ad4b41bb6bac87353e9d02ac8b
index d840c1a57..d840c1a57 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/ab/6c44a2e84492ad4b41bb6bac87353e9d02ac8b
+++ b/tests/resources/merge-resolve/.gitted/objects/ab/6c44a2e84492ad4b41bb6bac87353e9d02ac8b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ab/929391ac42572f92110f3deeb4f0844a951e22 b/tests/resources/merge-resolve/.gitted/objects/ab/929391ac42572f92110f3deeb4f0844a951e22
index 8840d00c5..8840d00c5 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/ab/929391ac42572f92110f3deeb4f0844a951e22
+++ b/tests/resources/merge-resolve/.gitted/objects/ab/929391ac42572f92110f3deeb4f0844a951e22
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ac/4045f965119e6998f4340ed0f411decfb3ec05 b/tests/resources/merge-resolve/.gitted/objects/ac/4045f965119e6998f4340ed0f411decfb3ec05
index 4c32d63f8..4c32d63f8 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/ac/4045f965119e6998f4340ed0f411decfb3ec05
+++ b/tests/resources/merge-resolve/.gitted/objects/ac/4045f965119e6998f4340ed0f411decfb3ec05
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ad/a14492498136771f69dd451866cabcb0e9ef9a b/tests/resources/merge-resolve/.gitted/objects/ad/a14492498136771f69dd451866cabcb0e9ef9a
index 71023de39..71023de39 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/ad/a14492498136771f69dd451866cabcb0e9ef9a
+++ b/tests/resources/merge-resolve/.gitted/objects/ad/a14492498136771f69dd451866cabcb0e9ef9a
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ad/a55a45d14527dc3dfc714ea1c65d2e1e6fbe87 b/tests/resources/merge-resolve/.gitted/objects/ad/a55a45d14527dc3dfc714ea1c65d2e1e6fbe87
index 3091b8f3d..3091b8f3d 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/ad/a55a45d14527dc3dfc714ea1c65d2e1e6fbe87
+++ b/tests/resources/merge-resolve/.gitted/objects/ad/a55a45d14527dc3dfc714ea1c65d2e1e6fbe87
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/b2/d399ae15224e1d58066e3c8df70ce37de7a656 b/tests/resources/merge-resolve/.gitted/objects/b2/d399ae15224e1d58066e3c8df70ce37de7a656
index 20fa838f2..20fa838f2 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/b2/d399ae15224e1d58066e3c8df70ce37de7a656
+++ b/tests/resources/merge-resolve/.gitted/objects/b2/d399ae15224e1d58066e3c8df70ce37de7a656
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/b4/2712cfe99a1a500b2a51fe984e0b8a7702ba11 b/tests/resources/merge-resolve/.gitted/objects/b4/2712cfe99a1a500b2a51fe984e0b8a7702ba11
index 2820b46cc..2820b46cc 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/b4/2712cfe99a1a500b2a51fe984e0b8a7702ba11
+++ b/tests/resources/merge-resolve/.gitted/objects/b4/2712cfe99a1a500b2a51fe984e0b8a7702ba11
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/b6/9fe837e4cecfd4c9a40cdca7c138468687df07 b/tests/resources/merge-resolve/.gitted/objects/b6/9fe837e4cecfd4c9a40cdca7c138468687df07
index 6dbcb05ea..6dbcb05ea 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/b6/9fe837e4cecfd4c9a40cdca7c138468687df07
+++ b/tests/resources/merge-resolve/.gitted/objects/b6/9fe837e4cecfd4c9a40cdca7c138468687df07
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/b6/f610aef53bd343e6c96227de874c66f00ee8e8 b/tests/resources/merge-resolve/.gitted/objects/b6/f610aef53bd343e6c96227de874c66f00ee8e8
index fb102f15d..fb102f15d 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/b6/f610aef53bd343e6c96227de874c66f00ee8e8
+++ b/tests/resources/merge-resolve/.gitted/objects/b6/f610aef53bd343e6c96227de874c66f00ee8e8
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/b7/a2576f9fc20024ac9ef17cb134acbd1ac73127 b/tests/resources/merge-resolve/.gitted/objects/b7/a2576f9fc20024ac9ef17cb134acbd1ac73127
index 22f2d137d..22f2d137d 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/b7/a2576f9fc20024ac9ef17cb134acbd1ac73127
+++ b/tests/resources/merge-resolve/.gitted/objects/b7/a2576f9fc20024ac9ef17cb134acbd1ac73127
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/b8/a3a806d3950e8c0a03a34f234a92eff0e2c68d b/tests/resources/merge-resolve/.gitted/objects/b8/a3a806d3950e8c0a03a34f234a92eff0e2c68d
index 24f029900..24f029900 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/b8/a3a806d3950e8c0a03a34f234a92eff0e2c68d
+++ b/tests/resources/merge-resolve/.gitted/objects/b8/a3a806d3950e8c0a03a34f234a92eff0e2c68d
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ba/cac9b3493509aa15e1730e1545fc0919d1dae0 b/tests/resources/merge-resolve/.gitted/objects/ba/cac9b3493509aa15e1730e1545fc0919d1dae0
index f35586f7f..f35586f7f 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/ba/cac9b3493509aa15e1730e1545fc0919d1dae0
+++ b/tests/resources/merge-resolve/.gitted/objects/ba/cac9b3493509aa15e1730e1545fc0919d1dae0
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/bc/744705e1d8a019993cf88f62bc4020f1b80919 b/tests/resources/merge-resolve/.gitted/objects/bc/744705e1d8a019993cf88f62bc4020f1b80919
index 0d4bdb323..0d4bdb323 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/bc/744705e1d8a019993cf88f62bc4020f1b80919
+++ b/tests/resources/merge-resolve/.gitted/objects/bc/744705e1d8a019993cf88f62bc4020f1b80919
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/bc/95c75d59386147d1e79a87c33068d8dbfd71f2 b/tests/resources/merge-resolve/.gitted/objects/bc/95c75d59386147d1e79a87c33068d8dbfd71f2
index 436d5a076..436d5a076 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/bc/95c75d59386147d1e79a87c33068d8dbfd71f2
+++ b/tests/resources/merge-resolve/.gitted/objects/bc/95c75d59386147d1e79a87c33068d8dbfd71f2
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/bd/593285fc7fe4ca18ccdbabf027f5d689101452 b/tests/resources/merge-resolve/.gitted/objects/bd/593285fc7fe4ca18ccdbabf027f5d689101452
index 75ab1f0f3..75ab1f0f3 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/bd/593285fc7fe4ca18ccdbabf027f5d689101452
+++ b/tests/resources/merge-resolve/.gitted/objects/bd/593285fc7fe4ca18ccdbabf027f5d689101452
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/bd/867fbae2faa80b920b002b80b1c91bcade7784 b/tests/resources/merge-resolve/.gitted/objects/bd/867fbae2faa80b920b002b80b1c91bcade7784
index 0f7421963..0f7421963 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/bd/867fbae2faa80b920b002b80b1c91bcade7784
+++ b/tests/resources/merge-resolve/.gitted/objects/bd/867fbae2faa80b920b002b80b1c91bcade7784
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/bd/9cb4cd0a770cb9adcb5fce212142ef40ea1c35 b/tests/resources/merge-resolve/.gitted/objects/bd/9cb4cd0a770cb9adcb5fce212142ef40ea1c35
index 2aafdc64f..2aafdc64f 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/bd/9cb4cd0a770cb9adcb5fce212142ef40ea1c35
+++ b/tests/resources/merge-resolve/.gitted/objects/bd/9cb4cd0a770cb9adcb5fce212142ef40ea1c35
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/be/f6e37b3ee632ba74159168836f382fed21d77d b/tests/resources/merge-resolve/.gitted/objects/be/f6e37b3ee632ba74159168836f382fed21d77d
index 6c243150d..6c243150d 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/be/f6e37b3ee632ba74159168836f382fed21d77d
+++ b/tests/resources/merge-resolve/.gitted/objects/be/f6e37b3ee632ba74159168836f382fed21d77d
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c0/6a9be584ac49aa02c5551312d9e2982c91df10 b/tests/resources/merge-resolve/.gitted/objects/c0/6a9be584ac49aa02c5551312d9e2982c91df10
index 963ef23ac..963ef23ac 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/c0/6a9be584ac49aa02c5551312d9e2982c91df10
+++ b/tests/resources/merge-resolve/.gitted/objects/c0/6a9be584ac49aa02c5551312d9e2982c91df10
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c1/b17981db0840109a820dae8674ee29684134ff b/tests/resources/merge-resolve/.gitted/objects/c1/b17981db0840109a820dae8674ee29684134ff
index fdcf28cb0..fdcf28cb0 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/c1/b17981db0840109a820dae8674ee29684134ff
+++ b/tests/resources/merge-resolve/.gitted/objects/c1/b17981db0840109a820dae8674ee29684134ff
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c1/b6a51bbb87c2f82b161412c3d20b59fc69b090 b/tests/resources/merge-resolve/.gitted/objects/c1/b6a51bbb87c2f82b161412c3d20b59fc69b090
index 3b369f8fe..3b369f8fe 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/c1/b6a51bbb87c2f82b161412c3d20b59fc69b090
+++ b/tests/resources/merge-resolve/.gitted/objects/c1/b6a51bbb87c2f82b161412c3d20b59fc69b090
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c3/5dee9bcc0e989f3b0c40f68372a9a51b6c4e6a b/tests/resources/merge-resolve/.gitted/objects/c3/5dee9bcc0e989f3b0c40f68372a9a51b6c4e6a
index d22b3b23c..d22b3b23c 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/c3/5dee9bcc0e989f3b0c40f68372a9a51b6c4e6a
+++ b/tests/resources/merge-resolve/.gitted/objects/c3/5dee9bcc0e989f3b0c40f68372a9a51b6c4e6a
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c3/d02eeef75183df7584d8d13ac03053910c1301 b/tests/resources/merge-resolve/.gitted/objects/c3/d02eeef75183df7584d8d13ac03053910c1301
index 2294f018d..2294f018d 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/c3/d02eeef75183df7584d8d13ac03053910c1301
+++ b/tests/resources/merge-resolve/.gitted/objects/c3/d02eeef75183df7584d8d13ac03053910c1301
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c4/efe31e9decccc8b2b4d3df9aac2cdfe2995618 b/tests/resources/merge-resolve/.gitted/objects/c4/efe31e9decccc8b2b4d3df9aac2cdfe2995618
index c7572d5bc..c7572d5bc 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/c4/efe31e9decccc8b2b4d3df9aac2cdfe2995618
+++ b/tests/resources/merge-resolve/.gitted/objects/c4/efe31e9decccc8b2b4d3df9aac2cdfe2995618
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c5/0d0f1cb60b8b0fe1615ad20ace557e9d68d7bd b/tests/resources/merge-resolve/.gitted/objects/c5/0d0f1cb60b8b0fe1615ad20ace557e9d68d7bd
index a1d5321e8..a1d5321e8 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/c5/0d0f1cb60b8b0fe1615ad20ace557e9d68d7bd
+++ b/tests/resources/merge-resolve/.gitted/objects/c5/0d0f1cb60b8b0fe1615ad20ace557e9d68d7bd
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c5/bbe550b9f09444bdddd3ecf3d97c0b42aa786c b/tests/resources/merge-resolve/.gitted/objects/c5/bbe550b9f09444bdddd3ecf3d97c0b42aa786c
index 2f2ada732..2f2ada732 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/c5/bbe550b9f09444bdddd3ecf3d97c0b42aa786c
+++ b/tests/resources/merge-resolve/.gitted/objects/c5/bbe550b9f09444bdddd3ecf3d97c0b42aa786c
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c6/07fc30883e335def28cd686b51f6cfa02b06ec b/tests/resources/merge-resolve/.gitted/objects/c6/07fc30883e335def28cd686b51f6cfa02b06ec
index 475b87ef9..475b87ef9 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/c6/07fc30883e335def28cd686b51f6cfa02b06ec
+++ b/tests/resources/merge-resolve/.gitted/objects/c6/07fc30883e335def28cd686b51f6cfa02b06ec
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c6/92ecf62007c0ac9fb26e2aa884de2933de15ed b/tests/resources/merge-resolve/.gitted/objects/c6/92ecf62007c0ac9fb26e2aa884de2933de15ed
index ae430bd4a..ae430bd4a 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/c6/92ecf62007c0ac9fb26e2aa884de2933de15ed
+++ b/tests/resources/merge-resolve/.gitted/objects/c6/92ecf62007c0ac9fb26e2aa884de2933de15ed
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c8/f06f2e3bb2964174677e91f0abead0e43c9e5d b/tests/resources/merge-resolve/.gitted/objects/c8/f06f2e3bb2964174677e91f0abead0e43c9e5d
index 5dae4c3ac..5dae4c3ac 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/c8/f06f2e3bb2964174677e91f0abead0e43c9e5d
+++ b/tests/resources/merge-resolve/.gitted/objects/c8/f06f2e3bb2964174677e91f0abead0e43c9e5d
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c9/174cef549ec94ecbc43ef03cdc775b4950becb b/tests/resources/merge-resolve/.gitted/objects/c9/174cef549ec94ecbc43ef03cdc775b4950becb
index da8dba244..da8dba244 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/c9/174cef549ec94ecbc43ef03cdc775b4950becb
+++ b/tests/resources/merge-resolve/.gitted/objects/c9/174cef549ec94ecbc43ef03cdc775b4950becb
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c9/4b27e41064c521120627e07e2035cca1d24ffa b/tests/resources/merge-resolve/.gitted/objects/c9/4b27e41064c521120627e07e2035cca1d24ffa
index fd1ec9fab..fd1ec9fab 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/c9/4b27e41064c521120627e07e2035cca1d24ffa
+++ b/tests/resources/merge-resolve/.gitted/objects/c9/4b27e41064c521120627e07e2035cca1d24ffa
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ca/b2cf23998b40f1af2d9d9a756dc9e285a8df4b b/tests/resources/merge-resolve/.gitted/objects/ca/b2cf23998b40f1af2d9d9a756dc9e285a8df4b
index 32ba2aa53..32ba2aa53 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/ca/b2cf23998b40f1af2d9d9a756dc9e285a8df4b
+++ b/tests/resources/merge-resolve/.gitted/objects/ca/b2cf23998b40f1af2d9d9a756dc9e285a8df4b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/cb/491780d82e46dc88a065b965ab307a038f2bc2 b/tests/resources/merge-resolve/.gitted/objects/cb/491780d82e46dc88a065b965ab307a038f2bc2
index cf9cd7d39..cf9cd7d39 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/cb/491780d82e46dc88a065b965ab307a038f2bc2
+++ b/tests/resources/merge-resolve/.gitted/objects/cb/491780d82e46dc88a065b965ab307a038f2bc2
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/cb/6693a788715b82440a54e0eacd19ba9f6ec559 b/tests/resources/merge-resolve/.gitted/objects/cb/6693a788715b82440a54e0eacd19ba9f6ec559
index e11181a96..e11181a96 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/cb/6693a788715b82440a54e0eacd19ba9f6ec559
+++ b/tests/resources/merge-resolve/.gitted/objects/cb/6693a788715b82440a54e0eacd19ba9f6ec559
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/cc/3e3009134cb88014129fc8858d1101359e5e2f b/tests/resources/merge-resolve/.gitted/objects/cc/3e3009134cb88014129fc8858d1101359e5e2f
index 9a0cb7a0c..9a0cb7a0c 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/cc/3e3009134cb88014129fc8858d1101359e5e2f
+++ b/tests/resources/merge-resolve/.gitted/objects/cc/3e3009134cb88014129fc8858d1101359e5e2f
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ce/8860d49e3bea6fd745874a01b7c3e46da8cbc3 b/tests/resources/merge-resolve/.gitted/objects/ce/8860d49e3bea6fd745874a01b7c3e46da8cbc3
index 860f9952f..860f9952f 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/ce/8860d49e3bea6fd745874a01b7c3e46da8cbc3
+++ b/tests/resources/merge-resolve/.gitted/objects/ce/8860d49e3bea6fd745874a01b7c3e46da8cbc3
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ce/e656c392ad0557b3aae0fb411475c206e2926f b/tests/resources/merge-resolve/.gitted/objects/ce/e656c392ad0557b3aae0fb411475c206e2926f
index ff0624ccb..ff0624ccb 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/ce/e656c392ad0557b3aae0fb411475c206e2926f
+++ b/tests/resources/merge-resolve/.gitted/objects/ce/e656c392ad0557b3aae0fb411475c206e2926f
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/cf/8c5cc8a85a1ff5a4ba51e0bc7cf5665669924d b/tests/resources/merge-resolve/.gitted/objects/cf/8c5cc8a85a1ff5a4ba51e0bc7cf5665669924d
index 36b0289e6..36b0289e6 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/cf/8c5cc8a85a1ff5a4ba51e0bc7cf5665669924d
+++ b/tests/resources/merge-resolve/.gitted/objects/cf/8c5cc8a85a1ff5a4ba51e0bc7cf5665669924d
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d0/7ec190c306ec690bac349e87d01c4358e49bb2 b/tests/resources/merge-resolve/.gitted/objects/d0/7ec190c306ec690bac349e87d01c4358e49bb2
index d52a56ffe..d52a56ffe 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/d0/7ec190c306ec690bac349e87d01c4358e49bb2
+++ b/tests/resources/merge-resolve/.gitted/objects/d0/7ec190c306ec690bac349e87d01c4358e49bb2
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d0/d4594e16f2e19107e3fa7ea63e7aaaff305ffb b/tests/resources/merge-resolve/.gitted/objects/d0/d4594e16f2e19107e3fa7ea63e7aaaff305ffb
index 5f7e286ff..5f7e286ff 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/d0/d4594e16f2e19107e3fa7ea63e7aaaff305ffb
+++ b/tests/resources/merge-resolve/.gitted/objects/d0/d4594e16f2e19107e3fa7ea63e7aaaff305ffb
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d2/f8637f2eab2507a1e13cbc9df4729ec386627e b/tests/resources/merge-resolve/.gitted/objects/d2/f8637f2eab2507a1e13cbc9df4729ec386627e
index 558a8513f..558a8513f 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/d2/f8637f2eab2507a1e13cbc9df4729ec386627e
+++ b/tests/resources/merge-resolve/.gitted/objects/d2/f8637f2eab2507a1e13cbc9df4729ec386627e
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d3/719a5ae8e4d92276b5313ce976f6ee5af2b436 b/tests/resources/merge-resolve/.gitted/objects/d3/719a5ae8e4d92276b5313ce976f6ee5af2b436
index 930bf5a5e..930bf5a5e 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/d3/719a5ae8e4d92276b5313ce976f6ee5af2b436
+++ b/tests/resources/merge-resolve/.gitted/objects/d3/719a5ae8e4d92276b5313ce976f6ee5af2b436
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d3/7aa3bbfe1c0c49b909781251b956dbabe85f96 b/tests/resources/merge-resolve/.gitted/objects/d3/7aa3bbfe1c0c49b909781251b956dbabe85f96
index 5902e0f32..5902e0f32 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/d3/7aa3bbfe1c0c49b909781251b956dbabe85f96
+++ b/tests/resources/merge-resolve/.gitted/objects/d3/7aa3bbfe1c0c49b909781251b956dbabe85f96
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d3/7ad72a2052685fc6201c2af90103ad42d2079b b/tests/resources/merge-resolve/.gitted/objects/d3/7ad72a2052685fc6201c2af90103ad42d2079b
index b2f39bff4..b2f39bff4 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/d3/7ad72a2052685fc6201c2af90103ad42d2079b
+++ b/tests/resources/merge-resolve/.gitted/objects/d3/7ad72a2052685fc6201c2af90103ad42d2079b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d4/207f77243500bec335ab477f9227fcdb1e271a b/tests/resources/merge-resolve/.gitted/objects/d4/207f77243500bec335ab477f9227fcdb1e271a
index 862e4e5bc..862e4e5bc 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/d4/207f77243500bec335ab477f9227fcdb1e271a
+++ b/tests/resources/merge-resolve/.gitted/objects/d4/207f77243500bec335ab477f9227fcdb1e271a
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46 b/tests/resources/merge-resolve/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46
index 0b3611ae4..0b3611ae4 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46
+++ b/tests/resources/merge-resolve/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d5/093787ef302b941b6aab081b99fb4880038bd8 b/tests/resources/merge-resolve/.gitted/objects/d5/093787ef302b941b6aab081b99fb4880038bd8
index 7d73449eb..7d73449eb 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/d5/093787ef302b941b6aab081b99fb4880038bd8
+++ b/tests/resources/merge-resolve/.gitted/objects/d5/093787ef302b941b6aab081b99fb4880038bd8
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d5/a61b0b4992a4f0caa887fa08b52431e727bb6f b/tests/resources/merge-resolve/.gitted/objects/d5/a61b0b4992a4f0caa887fa08b52431e727bb6f
index a7921de43..a7921de43 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/d5/a61b0b4992a4f0caa887fa08b52431e727bb6f
+++ b/tests/resources/merge-resolve/.gitted/objects/d5/a61b0b4992a4f0caa887fa08b52431e727bb6f
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d5/b6fc965c926a1bfc9ee456042b94088b5c5d21 b/tests/resources/merge-resolve/.gitted/objects/d5/b6fc965c926a1bfc9ee456042b94088b5c5d21
index 924bdbbb5..924bdbbb5 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/d5/b6fc965c926a1bfc9ee456042b94088b5c5d21
+++ b/tests/resources/merge-resolve/.gitted/objects/d5/b6fc965c926a1bfc9ee456042b94088b5c5d21
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d5/ec1152fe25e9fec00189eb00b3db71db24c218 b/tests/resources/merge-resolve/.gitted/objects/d5/ec1152fe25e9fec00189eb00b3db71db24c218
index 0d2534bc9..0d2534bc9 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/d5/ec1152fe25e9fec00189eb00b3db71db24c218
+++ b/tests/resources/merge-resolve/.gitted/objects/d5/ec1152fe25e9fec00189eb00b3db71db24c218
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d6/42b9770c66bba94a08df09b5efb095001f76d7 b/tests/resources/merge-resolve/.gitted/objects/d6/42b9770c66bba94a08df09b5efb095001f76d7
index 1671f9f2c..1671f9f2c 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/d6/42b9770c66bba94a08df09b5efb095001f76d7
+++ b/tests/resources/merge-resolve/.gitted/objects/d6/42b9770c66bba94a08df09b5efb095001f76d7
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d6/462fa3f5292857db599c54aea2bf91616230c5 b/tests/resources/merge-resolve/.gitted/objects/d6/462fa3f5292857db599c54aea2bf91616230c5
index baae3f0e0..baae3f0e0 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/d6/462fa3f5292857db599c54aea2bf91616230c5
+++ b/tests/resources/merge-resolve/.gitted/objects/d6/462fa3f5292857db599c54aea2bf91616230c5
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d6/cf6c7741b3316826af1314042550c97ded1d50 b/tests/resources/merge-resolve/.gitted/objects/d6/cf6c7741b3316826af1314042550c97ded1d50
index 8f9ae1fc6..8f9ae1fc6 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/d6/cf6c7741b3316826af1314042550c97ded1d50
+++ b/tests/resources/merge-resolve/.gitted/objects/d6/cf6c7741b3316826af1314042550c97ded1d50
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d8/74671ef5b20184836cb983bb273e5280384d0b b/tests/resources/merge-resolve/.gitted/objects/d8/74671ef5b20184836cb983bb273e5280384d0b
index 1d8037895..1d8037895 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/d8/74671ef5b20184836cb983bb273e5280384d0b
+++ b/tests/resources/merge-resolve/.gitted/objects/d8/74671ef5b20184836cb983bb273e5280384d0b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d8/fa77b6833082c1ea36b7828a582d4c43882450 b/tests/resources/merge-resolve/.gitted/objects/d8/fa77b6833082c1ea36b7828a582d4c43882450
index 988145322..988145322 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/d8/fa77b6833082c1ea36b7828a582d4c43882450
+++ b/tests/resources/merge-resolve/.gitted/objects/d8/fa77b6833082c1ea36b7828a582d4c43882450
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d9/63979c237d08b6ba39062ee7bf64c7d34a27f8 b/tests/resources/merge-resolve/.gitted/objects/d9/63979c237d08b6ba39062ee7bf64c7d34a27f8
index 5fa10405c..5fa10405c 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/d9/63979c237d08b6ba39062ee7bf64c7d34a27f8
+++ b/tests/resources/merge-resolve/.gitted/objects/d9/63979c237d08b6ba39062ee7bf64c7d34a27f8
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/da/178208145ef585a1bd5ca5f4c9785d738df2cf b/tests/resources/merge-resolve/.gitted/objects/da/178208145ef585a1bd5ca5f4c9785d738df2cf
index 6292118e0..6292118e0 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/da/178208145ef585a1bd5ca5f4c9785d738df2cf
+++ b/tests/resources/merge-resolve/.gitted/objects/da/178208145ef585a1bd5ca5f4c9785d738df2cf
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/db/6261a7c65c7fd678520c9bb6f2c47582ab9ed5 b/tests/resources/merge-resolve/.gitted/objects/db/6261a7c65c7fd678520c9bb6f2c47582ab9ed5
index b82e7fcaf..b82e7fcaf 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/db/6261a7c65c7fd678520c9bb6f2c47582ab9ed5
+++ b/tests/resources/merge-resolve/.gitted/objects/db/6261a7c65c7fd678520c9bb6f2c47582ab9ed5
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/dd/9a570c3400e6e07bc4d7651d6e20b08926b3d9 b/tests/resources/merge-resolve/.gitted/objects/dd/9a570c3400e6e07bc4d7651d6e20b08926b3d9
index 8fd60cbe8..8fd60cbe8 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/dd/9a570c3400e6e07bc4d7651d6e20b08926b3d9
+++ b/tests/resources/merge-resolve/.gitted/objects/dd/9a570c3400e6e07bc4d7651d6e20b08926b3d9
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/de/872ee3618b894992e9d1e18ba2ebe256a112f9 b/tests/resources/merge-resolve/.gitted/objects/de/872ee3618b894992e9d1e18ba2ebe256a112f9
index 04dda4a75..04dda4a75 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/de/872ee3618b894992e9d1e18ba2ebe256a112f9
+++ b/tests/resources/merge-resolve/.gitted/objects/de/872ee3618b894992e9d1e18ba2ebe256a112f9
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/df/e3f22baa1f6fce5447901c3086bae368de6bdd b/tests/resources/merge-resolve/.gitted/objects/df/e3f22baa1f6fce5447901c3086bae368de6bdd
index e13569440..e13569440 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/df/e3f22baa1f6fce5447901c3086bae368de6bdd
+++ b/tests/resources/merge-resolve/.gitted/objects/df/e3f22baa1f6fce5447901c3086bae368de6bdd
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e0/67f9361140f19391472df8a82d6610813c73b7 b/tests/resources/merge-resolve/.gitted/objects/e0/67f9361140f19391472df8a82d6610813c73b7
index 955431dd7..955431dd7 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/e0/67f9361140f19391472df8a82d6610813c73b7
+++ b/tests/resources/merge-resolve/.gitted/objects/e0/67f9361140f19391472df8a82d6610813c73b7
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e1/129b3cfb5898e0fbd606e0cb80b2755e50d161 b/tests/resources/merge-resolve/.gitted/objects/e1/129b3cfb5898e0fbd606e0cb80b2755e50d161
index 751f1dd33..751f1dd33 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/e1/129b3cfb5898e0fbd606e0cb80b2755e50d161
+++ b/tests/resources/merge-resolve/.gitted/objects/e1/129b3cfb5898e0fbd606e0cb80b2755e50d161
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e1/7ace1492648c9dc5701bad5c47af9d1b60c4e9 b/tests/resources/merge-resolve/.gitted/objects/e1/7ace1492648c9dc5701bad5c47af9d1b60c4e9
index 4a812e5df..4a812e5df 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/e1/7ace1492648c9dc5701bad5c47af9d1b60c4e9
+++ b/tests/resources/merge-resolve/.gitted/objects/e1/7ace1492648c9dc5701bad5c47af9d1b60c4e9
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e2/c6abbd55fed5ac71a5f2751e29b4a34726a595 b/tests/resources/merge-resolve/.gitted/objects/e2/c6abbd55fed5ac71a5f2751e29b4a34726a595
index 7b84ce966..7b84ce966 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/e2/c6abbd55fed5ac71a5f2751e29b4a34726a595
+++ b/tests/resources/merge-resolve/.gitted/objects/e2/c6abbd55fed5ac71a5f2751e29b4a34726a595
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e3/1e7ad3ed298f24e383c4950f4671993ec078e4 b/tests/resources/merge-resolve/.gitted/objects/e3/1e7ad3ed298f24e383c4950f4671993ec078e4
index a28ded3fb..a28ded3fb 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/e3/1e7ad3ed298f24e383c4950f4671993ec078e4
+++ b/tests/resources/merge-resolve/.gitted/objects/e3/1e7ad3ed298f24e383c4950f4671993ec078e4
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e3/76fbdd06ebf021c92724da9f26f44212734e3e b/tests/resources/merge-resolve/.gitted/objects/e3/76fbdd06ebf021c92724da9f26f44212734e3e
index 8da234114..8da234114 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/e3/76fbdd06ebf021c92724da9f26f44212734e3e
+++ b/tests/resources/merge-resolve/.gitted/objects/e3/76fbdd06ebf021c92724da9f26f44212734e3e
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e4/9f917b448d1340b31d76e54ba388268fd4c922 b/tests/resources/merge-resolve/.gitted/objects/e4/9f917b448d1340b31d76e54ba388268fd4c922
index 870c3e732..870c3e732 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/e4/9f917b448d1340b31d76e54ba388268fd4c922
+++ b/tests/resources/merge-resolve/.gitted/objects/e4/9f917b448d1340b31d76e54ba388268fd4c922
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e4/f618a2c3ed0669308735727df5ebf2447f022f b/tests/resources/merge-resolve/.gitted/objects/e4/f618a2c3ed0669308735727df5ebf2447f022f
index c7e1ee9d7..c7e1ee9d7 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/e4/f618a2c3ed0669308735727df5ebf2447f022f
+++ b/tests/resources/merge-resolve/.gitted/objects/e4/f618a2c3ed0669308735727df5ebf2447f022f
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e6/5a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812 b/tests/resources/merge-resolve/.gitted/objects/e6/5a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812
index 72f1cbcd9..72f1cbcd9 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/e6/5a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812
+++ b/tests/resources/merge-resolve/.gitted/objects/e6/5a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e8/107f24196736b870a318a0e28f048e29f6feff b/tests/resources/merge-resolve/.gitted/objects/e8/107f24196736b870a318a0e28f048e29f6feff
index ffcf843c2..ffcf843c2 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/e8/107f24196736b870a318a0e28f048e29f6feff
+++ b/tests/resources/merge-resolve/.gitted/objects/e8/107f24196736b870a318a0e28f048e29f6feff
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e9/2cdb7017dc6c5aed25cb4202c5b0104b872246 b/tests/resources/merge-resolve/.gitted/objects/e9/2cdb7017dc6c5aed25cb4202c5b0104b872246
index cb1260eb8..cb1260eb8 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/e9/2cdb7017dc6c5aed25cb4202c5b0104b872246
+++ b/tests/resources/merge-resolve/.gitted/objects/e9/2cdb7017dc6c5aed25cb4202c5b0104b872246
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e9/ad6ec3e38364a3d07feda7c4197d4d845c53b5 b/tests/resources/merge-resolve/.gitted/objects/e9/ad6ec3e38364a3d07feda7c4197d4d845c53b5
index da4a5edd1..da4a5edd1 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/e9/ad6ec3e38364a3d07feda7c4197d4d845c53b5
+++ b/tests/resources/merge-resolve/.gitted/objects/e9/ad6ec3e38364a3d07feda7c4197d4d845c53b5
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e9/f48beccc62d535739bfbdebe0a55ed716d8366 b/tests/resources/merge-resolve/.gitted/objects/e9/f48beccc62d535739bfbdebe0a55ed716d8366
index 23c59e4c9..23c59e4c9 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/e9/f48beccc62d535739bfbdebe0a55ed716d8366
+++ b/tests/resources/merge-resolve/.gitted/objects/e9/f48beccc62d535739bfbdebe0a55ed716d8366
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/eb/c09d0137cfb0c26697aed0109fb943ad906f3f b/tests/resources/merge-resolve/.gitted/objects/eb/c09d0137cfb0c26697aed0109fb943ad906f3f
index 83b489d3a..83b489d3a 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/eb/c09d0137cfb0c26697aed0109fb943ad906f3f
+++ b/tests/resources/merge-resolve/.gitted/objects/eb/c09d0137cfb0c26697aed0109fb943ad906f3f
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ec/67e5a86adff465359f1c8f995e12dbdfa08d8a b/tests/resources/merge-resolve/.gitted/objects/ec/67e5a86adff465359f1c8f995e12dbdfa08d8a
index 8490346e1..8490346e1 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/ec/67e5a86adff465359f1c8f995e12dbdfa08d8a
+++ b/tests/resources/merge-resolve/.gitted/objects/ec/67e5a86adff465359f1c8f995e12dbdfa08d8a
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ed/9523e62e453e50dd9be1606af19399b96e397a b/tests/resources/merge-resolve/.gitted/objects/ed/9523e62e453e50dd9be1606af19399b96e397a
index 7853e235c..7853e235c 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/ed/9523e62e453e50dd9be1606af19399b96e397a
+++ b/tests/resources/merge-resolve/.gitted/objects/ed/9523e62e453e50dd9be1606af19399b96e397a
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ee/1d6f164893c1866a323f072eeed36b855656be b/tests/resources/merge-resolve/.gitted/objects/ee/1d6f164893c1866a323f072eeed36b855656be
index 87d808007..87d808007 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/ee/1d6f164893c1866a323f072eeed36b855656be
+++ b/tests/resources/merge-resolve/.gitted/objects/ee/1d6f164893c1866a323f072eeed36b855656be
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf b/tests/resources/merge-resolve/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf
index 974b72dfd..974b72dfd 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf
+++ b/tests/resources/merge-resolve/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ee/a9286df54245fea72c5b557291470eb825f38f b/tests/resources/merge-resolve/.gitted/objects/ee/a9286df54245fea72c5b557291470eb825f38f
index ead0b2cda..ead0b2cda 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/ee/a9286df54245fea72c5b557291470eb825f38f
+++ b/tests/resources/merge-resolve/.gitted/objects/ee/a9286df54245fea72c5b557291470eb825f38f
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ef/58fdd8086c243bdc81f99e379acacfd21d32d6 b/tests/resources/merge-resolve/.gitted/objects/ef/58fdd8086c243bdc81f99e379acacfd21d32d6
index 55f79e066..55f79e066 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/ef/58fdd8086c243bdc81f99e379acacfd21d32d6
+++ b/tests/resources/merge-resolve/.gitted/objects/ef/58fdd8086c243bdc81f99e379acacfd21d32d6
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ef/c499524cf105d5264ac7fc54e07e95764e8075 b/tests/resources/merge-resolve/.gitted/objects/ef/c499524cf105d5264ac7fc54e07e95764e8075
index bc9350bc0..bc9350bc0 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/ef/c499524cf105d5264ac7fc54e07e95764e8075
+++ b/tests/resources/merge-resolve/.gitted/objects/ef/c499524cf105d5264ac7fc54e07e95764e8075
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ef/c9121fdedaf08ba180b53ebfbcf71bd488ed09 b/tests/resources/merge-resolve/.gitted/objects/ef/c9121fdedaf08ba180b53ebfbcf71bd488ed09
index 5f9cd3012..5f9cd3012 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/ef/c9121fdedaf08ba180b53ebfbcf71bd488ed09
+++ b/tests/resources/merge-resolve/.gitted/objects/ef/c9121fdedaf08ba180b53ebfbcf71bd488ed09
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f0/053b8060bb3f0be5cbcc3147a07ece26bf097e b/tests/resources/merge-resolve/.gitted/objects/f0/053b8060bb3f0be5cbcc3147a07ece26bf097e
index c63d37fb0..c63d37fb0 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/f0/053b8060bb3f0be5cbcc3147a07ece26bf097e
+++ b/tests/resources/merge-resolve/.gitted/objects/f0/053b8060bb3f0be5cbcc3147a07ece26bf097e
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f0/ce2b8e4986084d9b308fb72709e414c23eb5e6 b/tests/resources/merge-resolve/.gitted/objects/f0/ce2b8e4986084d9b308fb72709e414c23eb5e6
index e78c19f1a..e78c19f1a 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/f0/ce2b8e4986084d9b308fb72709e414c23eb5e6
+++ b/tests/resources/merge-resolve/.gitted/objects/f0/ce2b8e4986084d9b308fb72709e414c23eb5e6
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f2/0c9063fa0bda9a397c96947a7b687305c49753 b/tests/resources/merge-resolve/.gitted/objects/f2/0c9063fa0bda9a397c96947a7b687305c49753
index 34d9aed20..34d9aed20 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/f2/0c9063fa0bda9a397c96947a7b687305c49753
+++ b/tests/resources/merge-resolve/.gitted/objects/f2/0c9063fa0bda9a397c96947a7b687305c49753
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f2/9e7fb590551095230c6149cbe72f2e9104a796 b/tests/resources/merge-resolve/.gitted/objects/f2/9e7fb590551095230c6149cbe72f2e9104a796
index 663f6aeb7..663f6aeb7 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/f2/9e7fb590551095230c6149cbe72f2e9104a796
+++ b/tests/resources/merge-resolve/.gitted/objects/f2/9e7fb590551095230c6149cbe72f2e9104a796
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f3/293571dcd708b6a3faf03818cd2844d000e198 b/tests/resources/merge-resolve/.gitted/objects/f3/293571dcd708b6a3faf03818cd2844d000e198
index f748743b8..f748743b8 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/f3/293571dcd708b6a3faf03818cd2844d000e198
+++ b/tests/resources/merge-resolve/.gitted/objects/f3/293571dcd708b6a3faf03818cd2844d000e198
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f3/f1164b68b57b1995b658a828320e6df3081fae b/tests/resources/merge-resolve/.gitted/objects/f3/f1164b68b57b1995b658a828320e6df3081fae
index 5f0b4e424..5f0b4e424 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/f3/f1164b68b57b1995b658a828320e6df3081fae
+++ b/tests/resources/merge-resolve/.gitted/objects/f3/f1164b68b57b1995b658a828320e6df3081fae
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f4/15caf3fcad16304cb424b67f0ee6b12dc03aae b/tests/resources/merge-resolve/.gitted/objects/f4/15caf3fcad16304cb424b67f0ee6b12dc03aae
index 21ce1a0fc..21ce1a0fc 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/f4/15caf3fcad16304cb424b67f0ee6b12dc03aae
+++ b/tests/resources/merge-resolve/.gitted/objects/f4/15caf3fcad16304cb424b67f0ee6b12dc03aae
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f4/8097eb340dc5a7cae55aabcf1faf4548aa821f b/tests/resources/merge-resolve/.gitted/objects/f4/8097eb340dc5a7cae55aabcf1faf4548aa821f
index 5a4a9a54f..5a4a9a54f 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/f4/8097eb340dc5a7cae55aabcf1faf4548aa821f
+++ b/tests/resources/merge-resolve/.gitted/objects/f4/8097eb340dc5a7cae55aabcf1faf4548aa821f
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f5/504f36e6f4eb797a56fc5bac6c6c7f32969bf2 b/tests/resources/merge-resolve/.gitted/objects/f5/504f36e6f4eb797a56fc5bac6c6c7f32969bf2
index 2aa0c3b9a..2aa0c3b9a 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/f5/504f36e6f4eb797a56fc5bac6c6c7f32969bf2
+++ b/tests/resources/merge-resolve/.gitted/objects/f5/504f36e6f4eb797a56fc5bac6c6c7f32969bf2
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f5/b50c85a87cac64d7eb3254cdd1aec9564c0293 b/tests/resources/merge-resolve/.gitted/objects/f5/b50c85a87cac64d7eb3254cdd1aec9564c0293
index c1885cbe7..c1885cbe7 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/f5/b50c85a87cac64d7eb3254cdd1aec9564c0293
+++ b/tests/resources/merge-resolve/.gitted/objects/f5/b50c85a87cac64d7eb3254cdd1aec9564c0293
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f5/f9dd5886a6ee20272be0aafc790cba43b31931 b/tests/resources/merge-resolve/.gitted/objects/f5/f9dd5886a6ee20272be0aafc790cba43b31931
index 17ad5063d..17ad5063d 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/f5/f9dd5886a6ee20272be0aafc790cba43b31931
+++ b/tests/resources/merge-resolve/.gitted/objects/f5/f9dd5886a6ee20272be0aafc790cba43b31931
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f6/be049e284c0f9dcbbc745543885be3502ea521 b/tests/resources/merge-resolve/.gitted/objects/f6/be049e284c0f9dcbbc745543885be3502ea521
index 12d3c25c2..12d3c25c2 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/f6/be049e284c0f9dcbbc745543885be3502ea521
+++ b/tests/resources/merge-resolve/.gitted/objects/f6/be049e284c0f9dcbbc745543885be3502ea521
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f7/c332bd4d4d4b777366cae4d24d1687477576bf b/tests/resources/merge-resolve/.gitted/objects/f7/c332bd4d4d4b777366cae4d24d1687477576bf
index b36bceabf..b36bceabf 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/f7/c332bd4d4d4b777366cae4d24d1687477576bf
+++ b/tests/resources/merge-resolve/.gitted/objects/f7/c332bd4d4d4b777366cae4d24d1687477576bf
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f8/958bdf4d365a84a9a178b1f5f35ff1dacbd884 b/tests/resources/merge-resolve/.gitted/objects/f8/958bdf4d365a84a9a178b1f5f35ff1dacbd884
index 5dbbef276..5dbbef276 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/f8/958bdf4d365a84a9a178b1f5f35ff1dacbd884
+++ b/tests/resources/merge-resolve/.gitted/objects/f8/958bdf4d365a84a9a178b1f5f35ff1dacbd884
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/fa/c03f2c5139618d87d53614c153823bf1f31396 b/tests/resources/merge-resolve/.gitted/objects/fa/c03f2c5139618d87d53614c153823bf1f31396
index 30e07e5b7..30e07e5b7 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/fa/c03f2c5139618d87d53614c153823bf1f31396
+++ b/tests/resources/merge-resolve/.gitted/objects/fa/c03f2c5139618d87d53614c153823bf1f31396
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/fa/da9356aa3f74622327a3038ae9c6f92e1c5c1d b/tests/resources/merge-resolve/.gitted/objects/fa/da9356aa3f74622327a3038ae9c6f92e1c5c1d
index 16ce49a1b..16ce49a1b 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/fa/da9356aa3f74622327a3038ae9c6f92e1c5c1d
+++ b/tests/resources/merge-resolve/.gitted/objects/fa/da9356aa3f74622327a3038ae9c6f92e1c5c1d
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/fb/738a106cfd097a4acb96ce132ecb1ad6c46b03 b/tests/resources/merge-resolve/.gitted/objects/fb/738a106cfd097a4acb96ce132ecb1ad6c46b03
index 4f1e72688..4f1e72688 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/fb/738a106cfd097a4acb96ce132ecb1ad6c46b03
+++ b/tests/resources/merge-resolve/.gitted/objects/fb/738a106cfd097a4acb96ce132ecb1ad6c46b03
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/fc/4c636d6515e9e261f9260dbcf3cc6eca97ea08 b/tests/resources/merge-resolve/.gitted/objects/fc/4c636d6515e9e261f9260dbcf3cc6eca97ea08
index be8a810cd..be8a810cd 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/fc/4c636d6515e9e261f9260dbcf3cc6eca97ea08
+++ b/tests/resources/merge-resolve/.gitted/objects/fc/4c636d6515e9e261f9260dbcf3cc6eca97ea08
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/fc/7d7b805f7a9428574f4f802b2e34cd20ab9d99 b/tests/resources/merge-resolve/.gitted/objects/fc/7d7b805f7a9428574f4f802b2e34cd20ab9d99
index 20493e68c..20493e68c 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/fc/7d7b805f7a9428574f4f802b2e34cd20ab9d99
+++ b/tests/resources/merge-resolve/.gitted/objects/fc/7d7b805f7a9428574f4f802b2e34cd20ab9d99
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/fc/90237dc4891fa6c69827fc465632225e391618 b/tests/resources/merge-resolve/.gitted/objects/fc/90237dc4891fa6c69827fc465632225e391618
index 961814bae..961814bae 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/fc/90237dc4891fa6c69827fc465632225e391618
+++ b/tests/resources/merge-resolve/.gitted/objects/fc/90237dc4891fa6c69827fc465632225e391618
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/fd/57d2d6770fad8e9959124793a17f441b571e66 b/tests/resources/merge-resolve/.gitted/objects/fd/57d2d6770fad8e9959124793a17f441b571e66
index 21e6b2c55..21e6b2c55 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/fd/57d2d6770fad8e9959124793a17f441b571e66
+++ b/tests/resources/merge-resolve/.gitted/objects/fd/57d2d6770fad8e9959124793a17f441b571e66
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/fd/89f8cffb663ac89095a0f9764902e93ceaca6a b/tests/resources/merge-resolve/.gitted/objects/fd/89f8cffb663ac89095a0f9764902e93ceaca6a
index 2f9d83b26..2f9d83b26 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/fd/89f8cffb663ac89095a0f9764902e93ceaca6a
+++ b/tests/resources/merge-resolve/.gitted/objects/fd/89f8cffb663ac89095a0f9764902e93ceaca6a
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/fe/5407fc50a53aecb41d1a6e9ea7b612e581af87 b/tests/resources/merge-resolve/.gitted/objects/fe/5407fc50a53aecb41d1a6e9ea7b612e581af87
index 4ce7d2297..4ce7d2297 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/fe/5407fc50a53aecb41d1a6e9ea7b612e581af87
+++ b/tests/resources/merge-resolve/.gitted/objects/fe/5407fc50a53aecb41d1a6e9ea7b612e581af87
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ff/49d07869831ad761bbdaea026086f8789bcb00 b/tests/resources/merge-resolve/.gitted/objects/ff/49d07869831ad761bbdaea026086f8789bcb00
index eada39b77..eada39b77 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/ff/49d07869831ad761bbdaea026086f8789bcb00
+++ b/tests/resources/merge-resolve/.gitted/objects/ff/49d07869831ad761bbdaea026086f8789bcb00
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ff/b312248d607284c290023f9502eea010d34efd b/tests/resources/merge-resolve/.gitted/objects/ff/b312248d607284c290023f9502eea010d34efd
index 7e46c4fe3..7e46c4fe3 100644
--- a/tests-clar/resources/merge-resolve/.gitted/objects/ff/b312248d607284c290023f9502eea010d34efd
+++ b/tests/resources/merge-resolve/.gitted/objects/ff/b312248d607284c290023f9502eea010d34efd
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/branch b/tests/resources/merge-resolve/.gitted/refs/heads/branch
index 03f79a3dc..03f79a3dc 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/branch
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/df_ancestor b/tests/resources/merge-resolve/.gitted/refs/heads/df_ancestor
index 4bc37ac60..4bc37ac60 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/df_ancestor
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/df_ancestor
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/df_side1 b/tests/resources/merge-resolve/.gitted/refs/heads/df_side1
index ca6dd679d..ca6dd679d 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/df_side1
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/df_side1
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/df_side2 b/tests/resources/merge-resolve/.gitted/refs/heads/df_side2
index b8160f80e..b8160f80e 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/df_side2
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/df_side2
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/ff_branch b/tests/resources/merge-resolve/.gitted/refs/heads/ff_branch
index e9e90512f..e9e90512f 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/ff_branch
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/ff_branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/master b/tests/resources/merge-resolve/.gitted/refs/heads/master
index 8a329ae5f..8a329ae5f 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/master
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/master
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/octo1 b/tests/resources/merge-resolve/.gitted/refs/heads/octo1
index 4d2c66902..4d2c66902 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/octo1
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/octo1
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/octo2 b/tests/resources/merge-resolve/.gitted/refs/heads/octo2
index f503977a7..f503977a7 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/octo2
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/octo2
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/octo3 b/tests/resources/merge-resolve/.gitted/refs/heads/octo3
index b92994f10..b92994f10 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/octo3
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/octo3
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/octo4 b/tests/resources/merge-resolve/.gitted/refs/heads/octo4
index f33d57cbc..f33d57cbc 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/octo4
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/octo4
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/octo5 b/tests/resources/merge-resolve/.gitted/refs/heads/octo5
index e9f943385..e9f943385 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/octo5
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/octo5
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/octo6 b/tests/resources/merge-resolve/.gitted/refs/heads/octo6
index 4c5a98ad9..4c5a98ad9 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/octo6
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/octo6
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ancestor b/tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ancestor
index 4092d428f..4092d428f 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ancestor
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ancestor
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ours b/tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ours
index a1c50dce8..a1c50dce8 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ours
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ours
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/rename_conflict_theirs b/tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_theirs
index 130989399..130989399 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/rename_conflict_theirs
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_theirs
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/renames1 b/tests/resources/merge-resolve/.gitted/refs/heads/renames1
index 3d248102c..3d248102c 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/renames1
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/renames1
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/renames2 b/tests/resources/merge-resolve/.gitted/refs/heads/renames2
index d22621561..d22621561 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/renames2
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/renames2
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-10 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-10
index 5b378cd88..5b378cd88 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-10
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-10
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-10-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-10-branch
index b3db6c892..b3db6c892 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-10-branch
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-10-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-11 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-11
index 154de9a64..154de9a64 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-11
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-11
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-11-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-11-branch
index 2e4118029..2e4118029 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-11-branch
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-11-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-13 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-13
index 297573a57..297573a57 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-13
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-13
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-13-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-13-branch
index 22e429a61..22e429a61 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-13-branch
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-13-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-14 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-14
index 89051853a..89051853a 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-14
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-14
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-14-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-14-branch
index 0158f950c..0158f950c 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-14-branch
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-14-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-2alt b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-2alt
index 474074120..474074120 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-2alt
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-2alt
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-2alt-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-2alt-branch
index 2f5f1a4af..2f5f1a4af 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-2alt-branch
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-2alt-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-3alt b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-3alt
index 18e50ae12..18e50ae12 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-3alt
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-3alt
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-3alt-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-3alt-branch
index 7bc1a8d15..7bc1a8d15 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-3alt-branch
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-3alt-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-4 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-4
index f49bbf956..f49bbf956 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-4
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-4
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-4-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-4-branch
index bff519ef1..bff519ef1 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-4-branch
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-4-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1
index 963a7b336..963a7b336 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1-branch
index 4a22138e7..4a22138e7 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1-branch
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2
index aa4ada17e..aa4ada17e 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2-branch
index 5553cdba1..5553cdba1 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2-branch
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-6 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-6
index fb685bb63..fb685bb63 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-6
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-6
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-6-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-6-branch
index efc4c55ac..efc4c55ac 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-6-branch
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-6-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-7 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-7
index 9c9424346..9c9424346 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-7
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-7
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-7-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-7-branch
index 1762bb5db..1762bb5db 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-7-branch
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-7-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-8 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-8
index 837c4915a..837c4915a 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-8
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-8
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-8-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-8-branch
index 874230eff..874230eff 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-8-branch
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-8-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-9 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-9
index b968a3efb..b968a3efb 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-9
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-9
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-9-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-9-branch
index 7f3097b69..7f3097b69 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-9-branch
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-9-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/unrelated b/tests/resources/merge-resolve/.gitted/refs/heads/unrelated
index bb877be2e..bb877be2e 100644
--- a/tests-clar/resources/merge-resolve/.gitted/refs/heads/unrelated
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/unrelated
diff --git a/tests-clar/resources/merge-resolve/added-in-master.txt b/tests/resources/merge-resolve/added-in-master.txt
index 233c0919c..233c0919c 100644
--- a/tests-clar/resources/merge-resolve/added-in-master.txt
+++ b/tests/resources/merge-resolve/added-in-master.txt
diff --git a/tests-clar/resources/merge-resolve/automergeable.txt b/tests/resources/merge-resolve/automergeable.txt
index ee3fa1b8c..ee3fa1b8c 100644
--- a/tests-clar/resources/merge-resolve/automergeable.txt
+++ b/tests/resources/merge-resolve/automergeable.txt
diff --git a/tests-clar/resources/merge-resolve/changed-in-branch.txt b/tests/resources/merge-resolve/changed-in-branch.txt
index ab6c44a2e..ab6c44a2e 100644
--- a/tests-clar/resources/merge-resolve/changed-in-branch.txt
+++ b/tests/resources/merge-resolve/changed-in-branch.txt
diff --git a/tests-clar/resources/merge-resolve/changed-in-master.txt b/tests/resources/merge-resolve/changed-in-master.txt
index 11deab00b..11deab00b 100644
--- a/tests-clar/resources/merge-resolve/changed-in-master.txt
+++ b/tests/resources/merge-resolve/changed-in-master.txt
diff --git a/tests-clar/resources/merge-resolve/conflicting.txt b/tests/resources/merge-resolve/conflicting.txt
index 4e886e602..4e886e602 100644
--- a/tests-clar/resources/merge-resolve/conflicting.txt
+++ b/tests/resources/merge-resolve/conflicting.txt
diff --git a/tests-clar/resources/merge-resolve/removed-in-branch.txt b/tests/resources/merge-resolve/removed-in-branch.txt
index dfe3f22ba..dfe3f22ba 100644
--- a/tests-clar/resources/merge-resolve/removed-in-branch.txt
+++ b/tests/resources/merge-resolve/removed-in-branch.txt
diff --git a/tests-clar/resources/merge-resolve/unchanged.txt b/tests/resources/merge-resolve/unchanged.txt
index c8f06f2e3..c8f06f2e3 100644
--- a/tests-clar/resources/merge-resolve/unchanged.txt
+++ b/tests/resources/merge-resolve/unchanged.txt
diff --git a/tests-clar/resources/mergedrepo/.gitted/COMMIT_EDITMSG b/tests/resources/mergedrepo/.gitted/COMMIT_EDITMSG
index 1f7391f92..1f7391f92 100644
--- a/tests-clar/resources/mergedrepo/.gitted/COMMIT_EDITMSG
+++ b/tests/resources/mergedrepo/.gitted/COMMIT_EDITMSG
diff --git a/tests-clar/resources/peeled.git/HEAD b/tests/resources/mergedrepo/.gitted/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/peeled.git/HEAD
+++ b/tests/resources/mergedrepo/.gitted/HEAD
diff --git a/tests-clar/resources/mergedrepo/.gitted/MERGE_HEAD b/tests/resources/mergedrepo/.gitted/MERGE_HEAD
index a5bdf6e40..a5bdf6e40 100644
--- a/tests-clar/resources/mergedrepo/.gitted/MERGE_HEAD
+++ b/tests/resources/mergedrepo/.gitted/MERGE_HEAD
diff --git a/tests-clar/resources/mergedrepo/.gitted/MERGE_MODE b/tests/resources/mergedrepo/.gitted/MERGE_MODE
index e69de29bb..e69de29bb 100644
--- a/tests-clar/resources/mergedrepo/.gitted/MERGE_MODE
+++ b/tests/resources/mergedrepo/.gitted/MERGE_MODE
diff --git a/tests-clar/resources/mergedrepo/.gitted/MERGE_MSG b/tests/resources/mergedrepo/.gitted/MERGE_MSG
index 7c4d1f5a9..7c4d1f5a9 100644
--- a/tests-clar/resources/mergedrepo/.gitted/MERGE_MSG
+++ b/tests/resources/mergedrepo/.gitted/MERGE_MSG
diff --git a/tests-clar/resources/mergedrepo/.gitted/ORIG_HEAD b/tests/resources/mergedrepo/.gitted/ORIG_HEAD
index 13d4d6721..13d4d6721 100644
--- a/tests-clar/resources/mergedrepo/.gitted/ORIG_HEAD
+++ b/tests/resources/mergedrepo/.gitted/ORIG_HEAD
diff --git a/tests-clar/resources/mergedrepo/.gitted/config b/tests/resources/mergedrepo/.gitted/config
index af107929f..af107929f 100644
--- a/tests-clar/resources/mergedrepo/.gitted/config
+++ b/tests/resources/mergedrepo/.gitted/config
diff --git a/tests-clar/resources/push_src/.gitted/description b/tests/resources/mergedrepo/.gitted/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/push_src/.gitted/description
+++ b/tests/resources/mergedrepo/.gitted/description
diff --git a/tests-clar/resources/mergedrepo/.gitted/index b/tests/resources/mergedrepo/.gitted/index
index 3d29f78e7..3d29f78e7 100644
--- a/tests-clar/resources/mergedrepo/.gitted/index
+++ b/tests/resources/mergedrepo/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/info/exclude b/tests/resources/mergedrepo/.gitted/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/mergedrepo/.gitted/info/exclude
+++ b/tests/resources/mergedrepo/.gitted/info/exclude
diff --git a/tests-clar/resources/mergedrepo/.gitted/logs/HEAD b/tests/resources/mergedrepo/.gitted/logs/HEAD
index a385da67b..a385da67b 100644
--- a/tests-clar/resources/mergedrepo/.gitted/logs/HEAD
+++ b/tests/resources/mergedrepo/.gitted/logs/HEAD
diff --git a/tests-clar/resources/mergedrepo/.gitted/logs/refs/heads/branch b/tests/resources/mergedrepo/.gitted/logs/refs/heads/branch
index 26a5e8dc5..26a5e8dc5 100644
--- a/tests-clar/resources/mergedrepo/.gitted/logs/refs/heads/branch
+++ b/tests/resources/mergedrepo/.gitted/logs/refs/heads/branch
diff --git a/tests-clar/resources/mergedrepo/.gitted/logs/refs/heads/master b/tests/resources/mergedrepo/.gitted/logs/refs/heads/master
index 425f7bd89..425f7bd89 100644
--- a/tests-clar/resources/mergedrepo/.gitted/logs/refs/heads/master
+++ b/tests/resources/mergedrepo/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/03/db1d37504ca0c4f7c26d7776b0e28bdea08712 b/tests/resources/mergedrepo/.gitted/objects/03/db1d37504ca0c4f7c26d7776b0e28bdea08712
index 9232f79d9..9232f79d9 100644
--- a/tests-clar/resources/mergedrepo/.gitted/objects/03/db1d37504ca0c4f7c26d7776b0e28bdea08712
+++ b/tests/resources/mergedrepo/.gitted/objects/03/db1d37504ca0c4f7c26d7776b0e28bdea08712
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/17/0efc1023e0ed2390150bb4469c8456b63e8f91 b/tests/resources/mergedrepo/.gitted/objects/17/0efc1023e0ed2390150bb4469c8456b63e8f91
index 3e124d9a4..3e124d9a4 100644
--- a/tests-clar/resources/mergedrepo/.gitted/objects/17/0efc1023e0ed2390150bb4469c8456b63e8f91
+++ b/tests/resources/mergedrepo/.gitted/objects/17/0efc1023e0ed2390150bb4469c8456b63e8f91
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/1f/85ca51b8e0aac893a621b61a9c2661d6aa6d81 b/tests/resources/mergedrepo/.gitted/objects/1f/85ca51b8e0aac893a621b61a9c2661d6aa6d81
index 7bb19c873..7bb19c873 100644
--- a/tests-clar/resources/mergedrepo/.gitted/objects/1f/85ca51b8e0aac893a621b61a9c2661d6aa6d81
+++ b/tests/resources/mergedrepo/.gitted/objects/1f/85ca51b8e0aac893a621b61a9c2661d6aa6d81
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/22/0bd62631c8cf7a83ef39c6b94595f00517211e b/tests/resources/mergedrepo/.gitted/objects/22/0bd62631c8cf7a83ef39c6b94595f00517211e
index 487bcffb1..487bcffb1 100644
--- a/tests-clar/resources/mergedrepo/.gitted/objects/22/0bd62631c8cf7a83ef39c6b94595f00517211e
+++ b/tests/resources/mergedrepo/.gitted/objects/22/0bd62631c8cf7a83ef39c6b94595f00517211e
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/32/d55d59265db86dd690f0a7fc563db43e2bc6a6 b/tests/resources/mergedrepo/.gitted/objects/32/d55d59265db86dd690f0a7fc563db43e2bc6a6
index 2eb3954fc..2eb3954fc 100644
--- a/tests-clar/resources/mergedrepo/.gitted/objects/32/d55d59265db86dd690f0a7fc563db43e2bc6a6
+++ b/tests/resources/mergedrepo/.gitted/objects/32/d55d59265db86dd690f0a7fc563db43e2bc6a6
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/38/e2d82b9065a237904af4b780b4d68da6950534 b/tests/resources/mergedrepo/.gitted/objects/38/e2d82b9065a237904af4b780b4d68da6950534
index ebe83ccb2..ebe83ccb2 100644
--- a/tests-clar/resources/mergedrepo/.gitted/objects/38/e2d82b9065a237904af4b780b4d68da6950534
+++ b/tests/resources/mergedrepo/.gitted/objects/38/e2d82b9065a237904af4b780b4d68da6950534
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/3a/34580a35add43a4cf361e8e9a30060a905c876 b/tests/resources/mergedrepo/.gitted/objects/3a/34580a35add43a4cf361e8e9a30060a905c876
index 0d4095ffc..0d4095ffc 100644
--- a/tests-clar/resources/mergedrepo/.gitted/objects/3a/34580a35add43a4cf361e8e9a30060a905c876
+++ b/tests/resources/mergedrepo/.gitted/objects/3a/34580a35add43a4cf361e8e9a30060a905c876
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/44/58b8bc9e72b6c8755ae456f60e9844d0538d8c b/tests/resources/mergedrepo/.gitted/objects/44/58b8bc9e72b6c8755ae456f60e9844d0538d8c
index 33389c302..33389c302 100644
--- a/tests-clar/resources/mergedrepo/.gitted/objects/44/58b8bc9e72b6c8755ae456f60e9844d0538d8c
+++ b/tests/resources/mergedrepo/.gitted/objects/44/58b8bc9e72b6c8755ae456f60e9844d0538d8c
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/47/8871385b9cd03908c5383acfd568bef023c6b3 b/tests/resources/mergedrepo/.gitted/objects/47/8871385b9cd03908c5383acfd568bef023c6b3
index 5361ea685..5361ea685 100644
--- a/tests-clar/resources/mergedrepo/.gitted/objects/47/8871385b9cd03908c5383acfd568bef023c6b3
+++ b/tests/resources/mergedrepo/.gitted/objects/47/8871385b9cd03908c5383acfd568bef023c6b3
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/51/6bd85f78061e09ccc714561d7b504672cb52da b/tests/resources/mergedrepo/.gitted/objects/51/6bd85f78061e09ccc714561d7b504672cb52da
index a60da877c..a60da877c 100644
--- a/tests-clar/resources/mergedrepo/.gitted/objects/51/6bd85f78061e09ccc714561d7b504672cb52da
+++ b/tests/resources/mergedrepo/.gitted/objects/51/6bd85f78061e09ccc714561d7b504672cb52da
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/53/c1d95a01f4514b162066fc98564500c96c46ad b/tests/resources/mergedrepo/.gitted/objects/53/c1d95a01f4514b162066fc98564500c96c46ad
index 85e84d71e..85e84d71e 100644
--- a/tests-clar/resources/mergedrepo/.gitted/objects/53/c1d95a01f4514b162066fc98564500c96c46ad
+++ b/tests/resources/mergedrepo/.gitted/objects/53/c1d95a01f4514b162066fc98564500c96c46ad
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/6a/ea5f295304c36144ad6e9247a291b7f8112399 b/tests/resources/mergedrepo/.gitted/objects/6a/ea5f295304c36144ad6e9247a291b7f8112399
index b16b521e6..b16b521e6 100644
--- a/tests-clar/resources/mergedrepo/.gitted/objects/6a/ea5f295304c36144ad6e9247a291b7f8112399
+++ b/tests/resources/mergedrepo/.gitted/objects/6a/ea5f295304c36144ad6e9247a291b7f8112399
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/70/68e30a7f0090ae32db35dfa1e4189d8780fcb8 b/tests/resources/mergedrepo/.gitted/objects/70/68e30a7f0090ae32db35dfa1e4189d8780fcb8
index 7c4e85ffb..7c4e85ffb 100644
--- a/tests-clar/resources/mergedrepo/.gitted/objects/70/68e30a7f0090ae32db35dfa1e4189d8780fcb8
+++ b/tests/resources/mergedrepo/.gitted/objects/70/68e30a7f0090ae32db35dfa1e4189d8780fcb8
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/75/938de1e367098b3e9a7b1ec3c4ac4548afffe4 b/tests/resources/mergedrepo/.gitted/objects/75/938de1e367098b3e9a7b1ec3c4ac4548afffe4
index 65173fc4d..65173fc4d 100644
--- a/tests-clar/resources/mergedrepo/.gitted/objects/75/938de1e367098b3e9a7b1ec3c4ac4548afffe4
+++ b/tests/resources/mergedrepo/.gitted/objects/75/938de1e367098b3e9a7b1ec3c4ac4548afffe4
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/7b/26923aaf452b1977eb08617c59475fb3f74b71 b/tests/resources/mergedrepo/.gitted/objects/7b/26923aaf452b1977eb08617c59475fb3f74b71
index 162fa4455..162fa4455 100644
--- a/tests-clar/resources/mergedrepo/.gitted/objects/7b/26923aaf452b1977eb08617c59475fb3f74b71
+++ b/tests/resources/mergedrepo/.gitted/objects/7b/26923aaf452b1977eb08617c59475fb3f74b71
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/84/af62840be1b1c47b778a8a249f3ff45155038c b/tests/resources/mergedrepo/.gitted/objects/84/af62840be1b1c47b778a8a249f3ff45155038c
index 77a519f55..77a519f55 100644
--- a/tests-clar/resources/mergedrepo/.gitted/objects/84/af62840be1b1c47b778a8a249f3ff45155038c
+++ b/tests/resources/mergedrepo/.gitted/objects/84/af62840be1b1c47b778a8a249f3ff45155038c
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/88/71f7a2ee3addfc4ba39fbd0783c8e738d04cda b/tests/resources/mergedrepo/.gitted/objects/88/71f7a2ee3addfc4ba39fbd0783c8e738d04cda
index f624cd4f1..f624cd4f1 100644
--- a/tests-clar/resources/mergedrepo/.gitted/objects/88/71f7a2ee3addfc4ba39fbd0783c8e738d04cda
+++ b/tests/resources/mergedrepo/.gitted/objects/88/71f7a2ee3addfc4ba39fbd0783c8e738d04cda
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/88/7b153b165d32409c70163e0f734c090f12f673 b/tests/resources/mergedrepo/.gitted/objects/88/7b153b165d32409c70163e0f734c090f12f673
index 096474c03..096474c03 100644
--- a/tests-clar/resources/mergedrepo/.gitted/objects/88/7b153b165d32409c70163e0f734c090f12f673
+++ b/tests/resources/mergedrepo/.gitted/objects/88/7b153b165d32409c70163e0f734c090f12f673
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/8a/ad34cc83733590e74b93d0f7cf00375e2a735a b/tests/resources/mergedrepo/.gitted/objects/8a/ad34cc83733590e74b93d0f7cf00375e2a735a
index a413bc6b0..a413bc6b0 100644
--- a/tests-clar/resources/mergedrepo/.gitted/objects/8a/ad34cc83733590e74b93d0f7cf00375e2a735a
+++ b/tests/resources/mergedrepo/.gitted/objects/8a/ad34cc83733590e74b93d0f7cf00375e2a735a
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/8b/3f43d2402825c200f835ca1762413e386fd0b2 b/tests/resources/mergedrepo/.gitted/objects/8b/3f43d2402825c200f835ca1762413e386fd0b2
index 3ac8f6018..3ac8f6018 100644
--- a/tests-clar/resources/mergedrepo/.gitted/objects/8b/3f43d2402825c200f835ca1762413e386fd0b2
+++ b/tests/resources/mergedrepo/.gitted/objects/8b/3f43d2402825c200f835ca1762413e386fd0b2
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/8b/72416545c7e761b64cecad4f1686eae4078aa8 b/tests/resources/mergedrepo/.gitted/objects/8b/72416545c7e761b64cecad4f1686eae4078aa8
index 589a5ae9b..589a5ae9b 100644
--- a/tests-clar/resources/mergedrepo/.gitted/objects/8b/72416545c7e761b64cecad4f1686eae4078aa8
+++ b/tests/resources/mergedrepo/.gitted/objects/8b/72416545c7e761b64cecad4f1686eae4078aa8
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/8f/3c06cff9a83757cec40c80bc9bf31a2582bde9 b/tests/resources/mergedrepo/.gitted/objects/8f/3c06cff9a83757cec40c80bc9bf31a2582bde9
index 6503985e3..6503985e3 100644
--- a/tests-clar/resources/mergedrepo/.gitted/objects/8f/3c06cff9a83757cec40c80bc9bf31a2582bde9
+++ b/tests/resources/mergedrepo/.gitted/objects/8f/3c06cff9a83757cec40c80bc9bf31a2582bde9
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/8f/fcc405925511824a2240a6d3686aa7f8c7ac50 b/tests/resources/mergedrepo/.gitted/objects/8f/fcc405925511824a2240a6d3686aa7f8c7ac50
index 2eaa80838..2eaa80838 100644
--- a/tests-clar/resources/mergedrepo/.gitted/objects/8f/fcc405925511824a2240a6d3686aa7f8c7ac50
+++ b/tests/resources/mergedrepo/.gitted/objects/8f/fcc405925511824a2240a6d3686aa7f8c7ac50
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/9a/05ccb4e0f948de03128e095f39dae6976751c5 b/tests/resources/mergedrepo/.gitted/objects/9a/05ccb4e0f948de03128e095f39dae6976751c5
index 7373a80d8..7373a80d8 100644
--- a/tests-clar/resources/mergedrepo/.gitted/objects/9a/05ccb4e0f948de03128e095f39dae6976751c5
+++ b/tests/resources/mergedrepo/.gitted/objects/9a/05ccb4e0f948de03128e095f39dae6976751c5
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/9d/81f82fccc7dcd7de7a1ffead1815294c2e092c b/tests/resources/mergedrepo/.gitted/objects/9d/81f82fccc7dcd7de7a1ffead1815294c2e092c
index c5a651f97..c5a651f97 100644
--- a/tests-clar/resources/mergedrepo/.gitted/objects/9d/81f82fccc7dcd7de7a1ffead1815294c2e092c
+++ b/tests/resources/mergedrepo/.gitted/objects/9d/81f82fccc7dcd7de7a1ffead1815294c2e092c
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/b7/cedb8ad4cbb22b6363f9578cbd749797f7ef0d b/tests/resources/mergedrepo/.gitted/objects/b7/cedb8ad4cbb22b6363f9578cbd749797f7ef0d
index 3e14b5dc8..3e14b5dc8 100644
--- a/tests-clar/resources/mergedrepo/.gitted/objects/b7/cedb8ad4cbb22b6363f9578cbd749797f7ef0d
+++ b/tests/resources/mergedrepo/.gitted/objects/b7/cedb8ad4cbb22b6363f9578cbd749797f7ef0d
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/d0/1885ea594926eae9ba5b54ad76692af5969f51 b/tests/resources/mergedrepo/.gitted/objects/d0/1885ea594926eae9ba5b54ad76692af5969f51
index a641adc2e..a641adc2e 100644
--- a/tests-clar/resources/mergedrepo/.gitted/objects/d0/1885ea594926eae9ba5b54ad76692af5969f51
+++ b/tests/resources/mergedrepo/.gitted/objects/d0/1885ea594926eae9ba5b54ad76692af5969f51
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/e2/809157a7766f272e4cfe26e61ef2678a5357ff b/tests/resources/mergedrepo/.gitted/objects/e2/809157a7766f272e4cfe26e61ef2678a5357ff
index fa86662e0..fa86662e0 100644
--- a/tests-clar/resources/mergedrepo/.gitted/objects/e2/809157a7766f272e4cfe26e61ef2678a5357ff
+++ b/tests/resources/mergedrepo/.gitted/objects/e2/809157a7766f272e4cfe26e61ef2678a5357ff
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/e6/2cac5c88b9928f2695b934c70efa4285324478 b/tests/resources/mergedrepo/.gitted/objects/e6/2cac5c88b9928f2695b934c70efa4285324478
index c9841c698..c9841c698 100644
--- a/tests-clar/resources/mergedrepo/.gitted/objects/e6/2cac5c88b9928f2695b934c70efa4285324478
+++ b/tests/resources/mergedrepo/.gitted/objects/e6/2cac5c88b9928f2695b934c70efa4285324478
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/f7/2784290c151092abf04ce6b875068547f70406 b/tests/resources/mergedrepo/.gitted/objects/f7/2784290c151092abf04ce6b875068547f70406
index cd587dbec..cd587dbec 100644
--- a/tests-clar/resources/mergedrepo/.gitted/objects/f7/2784290c151092abf04ce6b875068547f70406
+++ b/tests/resources/mergedrepo/.gitted/objects/f7/2784290c151092abf04ce6b875068547f70406
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/refs/heads/branch b/tests/resources/mergedrepo/.gitted/refs/heads/branch
index a5bdf6e40..a5bdf6e40 100644
--- a/tests-clar/resources/mergedrepo/.gitted/refs/heads/branch
+++ b/tests/resources/mergedrepo/.gitted/refs/heads/branch
diff --git a/tests-clar/resources/mergedrepo/.gitted/refs/heads/master b/tests/resources/mergedrepo/.gitted/refs/heads/master
index 13d4d6721..13d4d6721 100644
--- a/tests-clar/resources/mergedrepo/.gitted/refs/heads/master
+++ b/tests/resources/mergedrepo/.gitted/refs/heads/master
diff --git a/tests-clar/resources/mergedrepo/conflicts-one.txt b/tests/resources/mergedrepo/conflicts-one.txt
index 8aad34cc8..8aad34cc8 100644
--- a/tests-clar/resources/mergedrepo/conflicts-one.txt
+++ b/tests/resources/mergedrepo/conflicts-one.txt
diff --git a/tests-clar/resources/mergedrepo/conflicts-two.txt b/tests/resources/mergedrepo/conflicts-two.txt
index e62cac5c8..e62cac5c8 100644
--- a/tests-clar/resources/mergedrepo/conflicts-two.txt
+++ b/tests/resources/mergedrepo/conflicts-two.txt
diff --git a/tests-clar/resources/mergedrepo/one.txt b/tests/resources/mergedrepo/one.txt
index 75938de1e..75938de1e 100644
--- a/tests-clar/resources/mergedrepo/one.txt
+++ b/tests/resources/mergedrepo/one.txt
diff --git a/tests-clar/resources/mergedrepo/two.txt b/tests/resources/mergedrepo/two.txt
index 7b26923aa..7b26923aa 100644
--- a/tests-clar/resources/mergedrepo/two.txt
+++ b/tests/resources/mergedrepo/two.txt
diff --git a/tests-clar/resources/partial-testrepo/.gitted/HEAD b/tests/resources/partial-testrepo/.gitted/HEAD
index 4bfb9c93f..4bfb9c93f 100644
--- a/tests-clar/resources/partial-testrepo/.gitted/HEAD
+++ b/tests/resources/partial-testrepo/.gitted/HEAD
diff --git a/tests-clar/resources/partial-testrepo/.gitted/config b/tests/resources/partial-testrepo/.gitted/config
index 99abaab97..99abaab97 100644
--- a/tests-clar/resources/partial-testrepo/.gitted/config
+++ b/tests/resources/partial-testrepo/.gitted/config
diff --git a/tests-clar/resources/partial-testrepo/.gitted/index b/tests/resources/partial-testrepo/.gitted/index
index 4f241f914..4f241f914 100644
--- a/tests-clar/resources/partial-testrepo/.gitted/index
+++ b/tests/resources/partial-testrepo/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/tests/resources/partial-testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
index cedb2a22e..cedb2a22e 100644
--- a/tests-clar/resources/partial-testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
+++ b/tests/resources/partial-testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e b/tests/resources/partial-testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e
index b7d944fa1..b7d944fa1 100644
--- a/tests-clar/resources/partial-testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e
+++ b/tests/resources/partial-testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925 b/tests/resources/partial-testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925
index d37b93e4f..d37b93e4f 100644
--- a/tests-clar/resources/partial-testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925
+++ b/tests/resources/partial-testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/tests/resources/partial-testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
index 93a16f146..93a16f146 100644
--- a/tests-clar/resources/partial-testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
+++ b/tests/resources/partial-testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd b/tests/resources/partial-testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
index ba0bfb30c..ba0bfb30c 100644
--- a/tests-clar/resources/partial-testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
+++ b/tests/resources/partial-testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/partial-testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
index 7ca4ceed5..7ca4ceed5 100644
--- a/tests-clar/resources/partial-testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
+++ b/tests/resources/partial-testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/tests/resources/partial-testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
index 8953b6cef..8953b6cef 100644
--- a/tests-clar/resources/partial-testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
+++ b/tests/resources/partial-testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63 b/tests/resources/partial-testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63
index e9150214b..e9150214b 100644
--- a/tests-clar/resources/partial-testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63
+++ b/tests/resources/partial-testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/tests/resources/partial-testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
index c1f22c54f..c1f22c54f 100644
--- a/tests-clar/resources/partial-testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
+++ b/tests/resources/partial-testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc b/tests/resources/partial-testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc
index b669961d8..b669961d8 100644
--- a/tests-clar/resources/partial-testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc
+++ b/tests/resources/partial-testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735 b/tests/resources/partial-testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735
index 9ff5eb2b5..9ff5eb2b5 100644
--- a/tests-clar/resources/partial-testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735
+++ b/tests/resources/partial-testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/tests/resources/partial-testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
index 2ef4faa0f..2ef4faa0f 100644
--- a/tests-clar/resources/partial-testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
+++ b/tests/resources/partial-testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/tests/resources/partial-testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
index 2f9b6b6e3..2f9b6b6e3 100644
--- a/tests-clar/resources/partial-testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
+++ b/tests/resources/partial-testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/tests/resources/partial-testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
index 5df58dda5..5df58dda5 100644
--- a/tests-clar/resources/partial-testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
+++ b/tests/resources/partial-testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/tests/resources/partial-testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
index a79612435..a79612435 100644
--- a/tests-clar/resources/partial-testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
+++ b/tests/resources/partial-testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/tests/resources/partial-testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
index f8588696b..f8588696b 100644
--- a/tests-clar/resources/partial-testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
+++ b/tests/resources/partial-testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/tests/resources/partial-testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
index d0d7e736e..d0d7e736e 100644
--- a/tests-clar/resources/partial-testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
+++ b/tests/resources/partial-testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/tests/resources/partial-testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
index 18a7f61c2..18a7f61c2 100644
--- a/tests-clar/resources/partial-testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
+++ b/tests/resources/partial-testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd b/tests/resources/partial-testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
index 75f541f10..75f541f10 100644
--- a/tests-clar/resources/partial-testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
+++ b/tests/resources/partial-testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9 b/tests/resources/partial-testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9
index 7620c514f..7620c514f 100644
--- a/tests-clar/resources/partial-testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9
+++ b/tests/resources/partial-testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83 b/tests/resources/partial-testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83
index 00940f0f2..00940f0f2 100644
--- a/tests-clar/resources/partial-testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83
+++ b/tests/resources/partial-testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 b/tests/resources/partial-testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
index 03770969a..03770969a 100644
--- a/tests-clar/resources/partial-testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
+++ b/tests/resources/partial-testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/tests/resources/partial-testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
index 112998d42..112998d42 100644
--- a/tests-clar/resources/partial-testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
+++ b/tests/resources/partial-testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765 b/tests/resources/partial-testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
index 12bf5f3e3..12bf5f3e3 100644
--- a/tests-clar/resources/partial-testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
+++ b/tests/resources/partial-testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/pack/.gitkeep b/tests/resources/partial-testrepo/.gitted/objects/pack/.gitkeep
index e69de29bb..e69de29bb 100644
--- a/tests-clar/resources/partial-testrepo/.gitted/objects/pack/.gitkeep
+++ b/tests/resources/partial-testrepo/.gitted/objects/pack/.gitkeep
diff --git a/tests-clar/resources/partial-testrepo/.gitted/refs/heads/dir b/tests/resources/partial-testrepo/.gitted/refs/heads/dir
index 4567d37fa..4567d37fa 100644
--- a/tests-clar/resources/partial-testrepo/.gitted/refs/heads/dir
+++ b/tests/resources/partial-testrepo/.gitted/refs/heads/dir
diff --git a/tests-clar/resources/push_src/.gitted/HEAD b/tests/resources/peeled.git/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/push_src/.gitted/HEAD
+++ b/tests/resources/peeled.git/HEAD
diff --git a/tests-clar/resources/peeled.git/config b/tests/resources/peeled.git/config
index 88300524a..88300524a 100644
--- a/tests-clar/resources/peeled.git/config
+++ b/tests/resources/peeled.git/config
diff --git a/tests-clar/resources/peeled.git/objects/info/packs b/tests/resources/peeled.git/objects/info/packs
index 0d88b32e5..0d88b32e5 100644
--- a/tests-clar/resources/peeled.git/objects/info/packs
+++ b/tests/resources/peeled.git/objects/info/packs
diff --git a/tests-clar/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.idx b/tests/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.idx
index 9b79e9b85..9b79e9b85 100644
--- a/tests-clar/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.idx
+++ b/tests/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.idx
Binary files differ
diff --git a/tests-clar/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.pack b/tests/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.pack
index 2459ca287..2459ca287 100644
--- a/tests-clar/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.pack
+++ b/tests/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.pack
Binary files differ
diff --git a/tests-clar/resources/peeled.git/packed-refs b/tests/resources/peeled.git/packed-refs
index ad053d550..ad053d550 100644
--- a/tests-clar/resources/peeled.git/packed-refs
+++ b/tests/resources/peeled.git/packed-refs
diff --git a/tests-clar/resources/peeled.git/refs/heads/master b/tests/resources/peeled.git/refs/heads/master
index 76c15e203..76c15e203 100644
--- a/tests-clar/resources/peeled.git/refs/heads/master
+++ b/tests/resources/peeled.git/refs/heads/master
diff --git a/tests-clar/resources/push.sh b/tests/resources/push.sh
index 607117675..607117675 100644
--- a/tests-clar/resources/push.sh
+++ b/tests/resources/push.sh
diff --git a/tests-clar/resources/push_src/.gitted/COMMIT_EDITMSG b/tests/resources/push_src/.gitted/COMMIT_EDITMSG
index b1295084c..b1295084c 100644
--- a/tests-clar/resources/push_src/.gitted/COMMIT_EDITMSG
+++ b/tests/resources/push_src/.gitted/COMMIT_EDITMSG
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/HEAD b/tests/resources/push_src/.gitted/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/HEAD
+++ b/tests/resources/push_src/.gitted/HEAD
diff --git a/tests-clar/resources/push_src/.gitted/ORIG_HEAD b/tests/resources/push_src/.gitted/ORIG_HEAD
index afadf9d26..afadf9d26 100644
--- a/tests-clar/resources/push_src/.gitted/ORIG_HEAD
+++ b/tests/resources/push_src/.gitted/ORIG_HEAD
diff --git a/tests-clar/resources/push_src/.gitted/config b/tests/resources/push_src/.gitted/config
index 51de0311b..51de0311b 100644
--- a/tests-clar/resources/push_src/.gitted/config
+++ b/tests/resources/push_src/.gitted/config
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/description b/tests/resources/push_src/.gitted/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/description
+++ b/tests/resources/push_src/.gitted/description
diff --git a/tests-clar/resources/push_src/.gitted/index b/tests/resources/push_src/.gitted/index
index 0ef6594b3..0ef6594b3 100644
--- a/tests-clar/resources/push_src/.gitted/index
+++ b/tests/resources/push_src/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/info/exclude b/tests/resources/push_src/.gitted/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/push_src/.gitted/info/exclude
+++ b/tests/resources/push_src/.gitted/info/exclude
diff --git a/tests-clar/resources/push_src/.gitted/logs/HEAD b/tests/resources/push_src/.gitted/logs/HEAD
index 4ef336f84..4ef336f84 100644
--- a/tests-clar/resources/push_src/.gitted/logs/HEAD
+++ b/tests/resources/push_src/.gitted/logs/HEAD
diff --git a/tests-clar/resources/push_src/.gitted/logs/refs/heads/b1 b/tests/resources/push_src/.gitted/logs/refs/heads/b1
index 390a03d5c..390a03d5c 100644
--- a/tests-clar/resources/push_src/.gitted/logs/refs/heads/b1
+++ b/tests/resources/push_src/.gitted/logs/refs/heads/b1
diff --git a/tests-clar/resources/push_src/.gitted/logs/refs/heads/b2 b/tests/resources/push_src/.gitted/logs/refs/heads/b2
index 390a03d5c..390a03d5c 100644
--- a/tests-clar/resources/push_src/.gitted/logs/refs/heads/b2
+++ b/tests/resources/push_src/.gitted/logs/refs/heads/b2
diff --git a/tests-clar/resources/push_src/.gitted/logs/refs/heads/b3 b/tests/resources/push_src/.gitted/logs/refs/heads/b3
index 01e302c44..01e302c44 100644
--- a/tests-clar/resources/push_src/.gitted/logs/refs/heads/b3
+++ b/tests/resources/push_src/.gitted/logs/refs/heads/b3
diff --git a/tests-clar/resources/push_src/.gitted/logs/refs/heads/b4 b/tests/resources/push_src/.gitted/logs/refs/heads/b4
index 7afddc54e..7afddc54e 100644
--- a/tests-clar/resources/push_src/.gitted/logs/refs/heads/b4
+++ b/tests/resources/push_src/.gitted/logs/refs/heads/b4
diff --git a/tests-clar/resources/push_src/.gitted/logs/refs/heads/b5 b/tests/resources/push_src/.gitted/logs/refs/heads/b5
index bc22567f7..bc22567f7 100644
--- a/tests-clar/resources/push_src/.gitted/logs/refs/heads/b5
+++ b/tests/resources/push_src/.gitted/logs/refs/heads/b5
diff --git a/tests-clar/resources/push_src/.gitted/logs/refs/heads/master b/tests/resources/push_src/.gitted/logs/refs/heads/master
index 8aafa9ca4..8aafa9ca4 100644
--- a/tests-clar/resources/push_src/.gitted/logs/refs/heads/master
+++ b/tests/resources/push_src/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/renames/.gitted/HEAD b/tests/resources/push_src/.gitted/modules/submodule/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/renames/.gitted/HEAD
+++ b/tests/resources/push_src/.gitted/modules/submodule/HEAD
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/config b/tests/resources/push_src/.gitted/modules/submodule/config
index 59810077d..59810077d 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/config
+++ b/tests/resources/push_src/.gitted/modules/submodule/config
diff --git a/tests-clar/resources/renames/.gitted/description b/tests/resources/push_src/.gitted/modules/submodule/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/renames/.gitted/description
+++ b/tests/resources/push_src/.gitted/modules/submodule/description
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/index b/tests/resources/push_src/.gitted/modules/submodule/index
index 8e44080f3..8e44080f3 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/index
+++ b/tests/resources/push_src/.gitted/modules/submodule/index
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/info/exclude b/tests/resources/push_src/.gitted/modules/submodule/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/info/exclude
+++ b/tests/resources/push_src/.gitted/modules/submodule/info/exclude
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/logs/HEAD b/tests/resources/push_src/.gitted/modules/submodule/logs/HEAD
index aedcdf295..aedcdf295 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/logs/HEAD
+++ b/tests/resources/push_src/.gitted/modules/submodule/logs/HEAD
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/logs/refs/heads/master b/tests/resources/push_src/.gitted/modules/submodule/logs/refs/heads/master
index aedcdf295..aedcdf295 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/logs/refs/heads/master
+++ b/tests/resources/push_src/.gitted/modules/submodule/logs/refs/heads/master
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/logs/refs/remotes/origin/HEAD b/tests/resources/push_src/.gitted/modules/submodule/logs/refs/remotes/origin/HEAD
index aedcdf295..aedcdf295 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/logs/refs/remotes/origin/HEAD
+++ b/tests/resources/push_src/.gitted/modules/submodule/logs/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/08/b041783f40edfe12bb406c9c9a8a040177c125 b/tests/resources/push_src/.gitted/modules/submodule/objects/08/b041783f40edfe12bb406c9c9a8a040177c125
index d1c032fce..d1c032fce 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/08/b041783f40edfe12bb406c9c9a8a040177c125
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/08/b041783f40edfe12bb406c9c9a8a040177c125
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/tests/resources/push_src/.gitted/modules/submodule/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
index cedb2a22e..cedb2a22e 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/tests/resources/push_src/.gitted/modules/submodule/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
index 93a16f146..93a16f146 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/18/10dff58d8a660512d4832e740f692884338ccd b/tests/resources/push_src/.gitted/modules/submodule/objects/18/10dff58d8a660512d4832e740f692884338ccd
index ba0bfb30c..ba0bfb30c 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/18/10dff58d8a660512d4832e740f692884338ccd
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/18/10dff58d8a660512d4832e740f692884338ccd
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd b/tests/resources/push_src/.gitted/modules/submodule/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd
index 3ec541288..3ec541288 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/1b/8cbad43e867676df601306689fe7c3def5e689 b/tests/resources/push_src/.gitted/modules/submodule/objects/1b/8cbad43e867676df601306689fe7c3def5e689
index 6048d4bad..6048d4bad 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/1b/8cbad43e867676df601306689fe7c3def5e689
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/1b/8cbad43e867676df601306689fe7c3def5e689
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b b/tests/resources/push_src/.gitted/modules/submodule/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
index 225c45734..225c45734 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10 b/tests/resources/push_src/.gitted/modules/submodule/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10
index cb1ed5712..cb1ed5712 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d b/tests/resources/push_src/.gitted/modules/submodule/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
index df40d99af..df40d99af 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7 b/tests/resources/push_src/.gitted/modules/submodule/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7
index 0a1500a6f..0a1500a6f 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54 b/tests/resources/push_src/.gitted/modules/submodule/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
index 321eaa867..321eaa867 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc b/tests/resources/push_src/.gitted/modules/submodule/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
index 9bb5b623b..9bb5b623b 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/push_src/.gitted/modules/submodule/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
index 7ca4ceed5..7ca4ceed5 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/tests/resources/push_src/.gitted/modules/submodule/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
index 8953b6cef..8953b6cef 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea b/tests/resources/push_src/.gitted/modules/submodule/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea
index b4e5aa186..b4e5aa186 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91 b/tests/resources/push_src/.gitted/modules/submodule/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91
index 351cff823..351cff823 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/tests/resources/push_src/.gitted/modules/submodule/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
index c1f22c54f..c1f22c54f 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/tests/resources/push_src/.gitted/modules/submodule/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
index 2ef4faa0f..2ef4faa0f 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af b/tests/resources/push_src/.gitted/modules/submodule/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
index 716b0c64b..716b0c64b 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/7b/4384978d2493e851f9cca7858815fac9b10980 b/tests/resources/push_src/.gitted/modules/submodule/objects/7b/4384978d2493e851f9cca7858815fac9b10980
index 23c462f34..23c462f34 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/7b/4384978d2493e851f9cca7858815fac9b10980
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/7b/4384978d2493e851f9cca7858815fac9b10980
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/tests/resources/push_src/.gitted/modules/submodule/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
index 2f9b6b6e3..2f9b6b6e3 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/tests/resources/push_src/.gitted/modules/submodule/objects/84/96071c1b46c854b31185ea97743be6a8774479
index 5df58dda5..5df58dda5 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/84/96071c1b46c854b31185ea97743be6a8774479
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/84/96071c1b46c854b31185ea97743be6a8774479
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe b/tests/resources/push_src/.gitted/modules/submodule/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe
index 71019a636..71019a636 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162 b/tests/resources/push_src/.gitted/modules/submodule/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
index 4cc3f4dff..4cc3f4dff 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4 b/tests/resources/push_src/.gitted/modules/submodule/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
index bf7b2bb68..bf7b2bb68 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2 b/tests/resources/push_src/.gitted/modules/submodule/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2
index 7f1cfb23c..7f1cfb23c 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/tests/resources/push_src/.gitted/modules/submodule/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
index a79612435..a79612435 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/tests/resources/push_src/.gitted/modules/submodule/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
index f8588696b..f8588696b 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750 b/tests/resources/push_src/.gitted/modules/submodule/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
index 29c8e824d..29c8e824d 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/tests/resources/push_src/.gitted/modules/submodule/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
index d0d7e736e..d0d7e736e 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/tests/resources/push_src/.gitted/modules/submodule/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
index 18a7f61c2..18a7f61c2 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/ae/90f12eea699729ed24555e40b9fd669da12a12 b/tests/resources/push_src/.gitted/modules/submodule/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
index d95254674..d95254674 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 b/tests/resources/push_src/.gitted/modules/submodule/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
index f460f2547..f460f2547 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593 b/tests/resources/push_src/.gitted/modules/submodule/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
index f613670e2..f613670e2 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 b/tests/resources/push_src/.gitted/modules/submodule/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
index 0817229bc..0817229bc 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd b/tests/resources/push_src/.gitted/modules/submodule/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
index 75f541f10..75f541f10 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659 b/tests/resources/push_src/.gitted/modules/submodule/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659
index f3b46b3ca..f3b46b3ca 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f b/tests/resources/push_src/.gitted/modules/submodule/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
index a67d6e647..a67d6e647 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759 b/tests/resources/push_src/.gitted/modules/submodule/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759
index 2d47e6faf..2d47e6faf 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/push_src/.gitted/modules/submodule/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
index 711223894..711223894 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0 b/tests/resources/push_src/.gitted/modules/submodule/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
index b135eccda..b135eccda 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3 b/tests/resources/push_src/.gitted/modules/submodule/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
index 82e2790e8..82e2790e8 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 b/tests/resources/push_src/.gitted/modules/submodule/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
index 697c94c92..697c94c92 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/tests/resources/push_src/.gitted/modules/submodule/objects/fa/49b077972391ad58037050f2a75f74e3671e92
index 112998d42..112998d42 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/fa/49b077972391ad58037050f2a75f74e3671e92
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/fa/49b077972391ad58037050f2a75f74e3671e92
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/fd/093bff70906175335656e6ce6ae05783708765 b/tests/resources/push_src/.gitted/modules/submodule/objects/fd/093bff70906175335656e6ce6ae05783708765
index 12bf5f3e3..12bf5f3e3 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/fd/093bff70906175335656e6ce6ae05783708765
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/fd/093bff70906175335656e6ce6ae05783708765
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09 b/tests/resources/push_src/.gitted/modules/submodule/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09
index 158aef21f..158aef21f 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
index 5068f2818..5068f2818 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
index a6a1f3020..a6a1f3020 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
index 94c3c71da..94c3c71da 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
index 74c7fe4f3..74c7fe4f3 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
index 555cfa977..555cfa977 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
index 4d539ed0a..4d539ed0a 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/packed-refs b/tests/resources/push_src/.gitted/modules/submodule/packed-refs
index 506a8607c..506a8607c 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/packed-refs
+++ b/tests/resources/push_src/.gitted/modules/submodule/packed-refs
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/refs/heads/master b/tests/resources/push_src/.gitted/modules/submodule/refs/heads/master
index 3d8f0a402..3d8f0a402 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/refs/heads/master
+++ b/tests/resources/push_src/.gitted/modules/submodule/refs/heads/master
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/refs/remotes/origin/HEAD b/tests/resources/push_src/.gitted/modules/submodule/refs/remotes/origin/HEAD
index 6efe28fff..6efe28fff 100644
--- a/tests-clar/resources/push_src/.gitted/modules/submodule/refs/remotes/origin/HEAD
+++ b/tests/resources/push_src/.gitted/modules/submodule/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/push_src/.gitted/objects/08/585692ce06452da6f82ae66b90d98b55536fca b/tests/resources/push_src/.gitted/objects/08/585692ce06452da6f82ae66b90d98b55536fca
index 39d126b2b..39d126b2b 100644
--- a/tests-clar/resources/push_src/.gitted/objects/08/585692ce06452da6f82ae66b90d98b55536fca
+++ b/tests/resources/push_src/.gitted/objects/08/585692ce06452da6f82ae66b90d98b55536fca
diff --git a/tests-clar/resources/push_src/.gitted/objects/27/b7ce66243eb1403862d05f958c002312df173d b/tests/resources/push_src/.gitted/objects/27/b7ce66243eb1403862d05f958c002312df173d
index 01d63b5c4..01d63b5c4 100644
--- a/tests-clar/resources/push_src/.gitted/objects/27/b7ce66243eb1403862d05f958c002312df173d
+++ b/tests/resources/push_src/.gitted/objects/27/b7ce66243eb1403862d05f958c002312df173d
diff --git a/tests-clar/resources/push_src/.gitted/objects/28/905c54ea45a4bed8d7b90f51bd8bd81eec8840 b/tests/resources/push_src/.gitted/objects/28/905c54ea45a4bed8d7b90f51bd8bd81eec8840
index dc10f6831..dc10f6831 100644
--- a/tests-clar/resources/push_src/.gitted/objects/28/905c54ea45a4bed8d7b90f51bd8bd81eec8840
+++ b/tests/resources/push_src/.gitted/objects/28/905c54ea45a4bed8d7b90f51bd8bd81eec8840
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/36/6226fb970ac0caa9d3f55967ab01334a548f60 b/tests/resources/push_src/.gitted/objects/36/6226fb970ac0caa9d3f55967ab01334a548f60
index 45c4d9208..45c4d9208 100644
--- a/tests-clar/resources/push_src/.gitted/objects/36/6226fb970ac0caa9d3f55967ab01334a548f60
+++ b/tests/resources/push_src/.gitted/objects/36/6226fb970ac0caa9d3f55967ab01334a548f60
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/36/f79b2846017d3761e0a02d0bccd573e0f90c57 b/tests/resources/push_src/.gitted/objects/36/f79b2846017d3761e0a02d0bccd573e0f90c57
new file mode 100644
index 000000000..0bc57f266
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/36/f79b2846017d3761e0a02d0bccd573e0f90c57
@@ -0,0 +1,2 @@
+x%Œ]
+ƒ0„ûœSì”ÄÍB)}é ¼@bµnÑéíf`f`>ö3(§nÞibˆC°èû¾ë0öhQ6¢BåMv¨‡à2&-ø÷M0Q)+ ®Œêæª tNsÚà¿E*;}àžøJϲN픹­§(th­ÔÖ@#”BŒËºC•?ÉÀTÃ…oÅyk7ÿ \ No newline at end of file
diff --git a/tests-clar/resources/push_src/.gitted/objects/5c/0bb3d1b9449d1cc69d7519fd05166f01840915 b/tests/resources/push_src/.gitted/objects/5c/0bb3d1b9449d1cc69d7519fd05166f01840915
index 883182138..883182138 100644
--- a/tests-clar/resources/push_src/.gitted/objects/5c/0bb3d1b9449d1cc69d7519fd05166f01840915
+++ b/tests/resources/push_src/.gitted/objects/5c/0bb3d1b9449d1cc69d7519fd05166f01840915
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/61/780798228d17af2d34fce4cfbdf35556832472 b/tests/resources/push_src/.gitted/objects/61/780798228d17af2d34fce4cfbdf35556832472
index 586bf17a4..586bf17a4 100644
--- a/tests-clar/resources/push_src/.gitted/objects/61/780798228d17af2d34fce4cfbdf35556832472
+++ b/tests/resources/push_src/.gitted/objects/61/780798228d17af2d34fce4cfbdf35556832472
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/64/fd55f9b6390202db5e5666fd1fb339089fba4d b/tests/resources/push_src/.gitted/objects/64/fd55f9b6390202db5e5666fd1fb339089fba4d
index bcaaa91c2..bcaaa91c2 100644
--- a/tests-clar/resources/push_src/.gitted/objects/64/fd55f9b6390202db5e5666fd1fb339089fba4d
+++ b/tests/resources/push_src/.gitted/objects/64/fd55f9b6390202db5e5666fd1fb339089fba4d
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/78/981922613b2afb6025042ff6bd878ac1994e85 b/tests/resources/push_src/.gitted/objects/78/981922613b2afb6025042ff6bd878ac1994e85
index e814d07b0..e814d07b0 100644
--- a/tests-clar/resources/push_src/.gitted/objects/78/981922613b2afb6025042ff6bd878ac1994e85
+++ b/tests/resources/push_src/.gitted/objects/78/981922613b2afb6025042ff6bd878ac1994e85
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/80/5c54522e614f29f70d2413a0470247d8b424ac b/tests/resources/push_src/.gitted/objects/80/5c54522e614f29f70d2413a0470247d8b424ac
index 552670c06..552670c06 100644
--- a/tests-clar/resources/push_src/.gitted/objects/80/5c54522e614f29f70d2413a0470247d8b424ac
+++ b/tests/resources/push_src/.gitted/objects/80/5c54522e614f29f70d2413a0470247d8b424ac
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/95/1bbbb90e2259a4c8950db78946784fb53fcbce b/tests/resources/push_src/.gitted/objects/95/1bbbb90e2259a4c8950db78946784fb53fcbce
index 596cd43de..596cd43de 100644
--- a/tests-clar/resources/push_src/.gitted/objects/95/1bbbb90e2259a4c8950db78946784fb53fcbce
+++ b/tests/resources/push_src/.gitted/objects/95/1bbbb90e2259a4c8950db78946784fb53fcbce
diff --git a/tests-clar/resources/push_src/.gitted/objects/a7/8705c3b2725f931d3ee05348d83cc26700f247 b/tests/resources/push_src/.gitted/objects/a7/8705c3b2725f931d3ee05348d83cc26700f247
index 6ad835e86..6ad835e86 100644
--- a/tests-clar/resources/push_src/.gitted/objects/a7/8705c3b2725f931d3ee05348d83cc26700f247
+++ b/tests/resources/push_src/.gitted/objects/a7/8705c3b2725f931d3ee05348d83cc26700f247
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/b4/83ae7ba66decee9aee971f501221dea84b1498 b/tests/resources/push_src/.gitted/objects/b4/83ae7ba66decee9aee971f501221dea84b1498
index 1e0bd3b05..1e0bd3b05 100644
--- a/tests-clar/resources/push_src/.gitted/objects/b4/83ae7ba66decee9aee971f501221dea84b1498
+++ b/tests/resources/push_src/.gitted/objects/b4/83ae7ba66decee9aee971f501221dea84b1498
diff --git a/tests-clar/resources/push_src/.gitted/objects/b4/e1f2b375a64c1ccd40c5ff6aa8bc96839ba4fd b/tests/resources/push_src/.gitted/objects/b4/e1f2b375a64c1ccd40c5ff6aa8bc96839ba4fd
index 4e650aaa1..4e650aaa1 100644
--- a/tests-clar/resources/push_src/.gitted/objects/b4/e1f2b375a64c1ccd40c5ff6aa8bc96839ba4fd
+++ b/tests/resources/push_src/.gitted/objects/b4/e1f2b375a64c1ccd40c5ff6aa8bc96839ba4fd
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/c1/0409136a7a75e025fa502a1b2fd7b62b77d279 b/tests/resources/push_src/.gitted/objects/c1/0409136a7a75e025fa502a1b2fd7b62b77d279
index fcb2b32f3..fcb2b32f3 100644
--- a/tests-clar/resources/push_src/.gitted/objects/c1/0409136a7a75e025fa502a1b2fd7b62b77d279
+++ b/tests/resources/push_src/.gitted/objects/c1/0409136a7a75e025fa502a1b2fd7b62b77d279
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/cd/881f90f2933db2e4cc26b8c71fe6037ac7fe4c b/tests/resources/push_src/.gitted/objects/cd/881f90f2933db2e4cc26b8c71fe6037ac7fe4c
index ad4272638..ad4272638 100644
--- a/tests-clar/resources/push_src/.gitted/objects/cd/881f90f2933db2e4cc26b8c71fe6037ac7fe4c
+++ b/tests/resources/push_src/.gitted/objects/cd/881f90f2933db2e4cc26b8c71fe6037ac7fe4c
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/d9/b63a88223d8367516f50bd131a5f7349b7f3e4 b/tests/resources/push_src/.gitted/objects/d9/b63a88223d8367516f50bd131a5f7349b7f3e4
index b471e2155..b471e2155 100644
--- a/tests-clar/resources/push_src/.gitted/objects/d9/b63a88223d8367516f50bd131a5f7349b7f3e4
+++ b/tests/resources/push_src/.gitted/objects/d9/b63a88223d8367516f50bd131a5f7349b7f3e4
diff --git a/tests-clar/resources/push_src/.gitted/objects/dc/ab83249f6f9d1ed735d651352a80519339b591 b/tests/resources/push_src/.gitted/objects/dc/ab83249f6f9d1ed735d651352a80519339b591
index 9f6b1502f..9f6b1502f 100644
--- a/tests-clar/resources/push_src/.gitted/objects/dc/ab83249f6f9d1ed735d651352a80519339b591
+++ b/tests/resources/push_src/.gitted/objects/dc/ab83249f6f9d1ed735d651352a80519339b591
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/ee/a4f2705eeec2db3813f2430829afce99cd00b5 b/tests/resources/push_src/.gitted/objects/ee/a4f2705eeec2db3813f2430829afce99cd00b5
new file mode 100644
index 000000000..b7b81d5e3
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/ee/a4f2705eeec2db3813f2430829afce99cd00b5
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/f7/8a3106c85fb549c65198b2a2086276c6174928 b/tests/resources/push_src/.gitted/objects/f7/8a3106c85fb549c65198b2a2086276c6174928
index b9813576d..b9813576d 100644
--- a/tests-clar/resources/push_src/.gitted/objects/f7/8a3106c85fb549c65198b2a2086276c6174928
+++ b/tests/resources/push_src/.gitted/objects/f7/8a3106c85fb549c65198b2a2086276c6174928
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/f8/f7aefc2900a3d737cea9eee45729fd55761e1a b/tests/resources/push_src/.gitted/objects/f8/f7aefc2900a3d737cea9eee45729fd55761e1a
index 888354fc2..888354fc2 100644
--- a/tests-clar/resources/push_src/.gitted/objects/f8/f7aefc2900a3d737cea9eee45729fd55761e1a
+++ b/tests/resources/push_src/.gitted/objects/f8/f7aefc2900a3d737cea9eee45729fd55761e1a
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/fa/38b91f199934685819bea316186d8b008c52a2 b/tests/resources/push_src/.gitted/objects/fa/38b91f199934685819bea316186d8b008c52a2
index 13d9bca20..13d9bca20 100644
--- a/tests-clar/resources/push_src/.gitted/objects/fa/38b91f199934685819bea316186d8b008c52a2
+++ b/tests/resources/push_src/.gitted/objects/fa/38b91f199934685819bea316186d8b008c52a2
diff --git a/tests-clar/resources/push_src/.gitted/objects/ff/83aa4c5e5d28e3bcba2f5c6e2adc61286a4e5e b/tests/resources/push_src/.gitted/objects/ff/83aa4c5e5d28e3bcba2f5c6e2adc61286a4e5e
index 10f25eb7c..10f25eb7c 100644
--- a/tests-clar/resources/push_src/.gitted/objects/ff/83aa4c5e5d28e3bcba2f5c6e2adc61286a4e5e
+++ b/tests/resources/push_src/.gitted/objects/ff/83aa4c5e5d28e3bcba2f5c6e2adc61286a4e5e
diff --git a/tests-clar/resources/push_src/.gitted/objects/ff/fe95c7fd0a37fa2ed702f8f93b56b2196b3925 b/tests/resources/push_src/.gitted/objects/ff/fe95c7fd0a37fa2ed702f8f93b56b2196b3925
index 1cdc048c0..1cdc048c0 100644
--- a/tests-clar/resources/push_src/.gitted/objects/ff/fe95c7fd0a37fa2ed702f8f93b56b2196b3925
+++ b/tests/resources/push_src/.gitted/objects/ff/fe95c7fd0a37fa2ed702f8f93b56b2196b3925
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/pack/dummy b/tests/resources/push_src/.gitted/objects/pack/dummy
index e69de29bb..e69de29bb 100644
--- a/tests-clar/resources/push_src/.gitted/objects/pack/dummy
+++ b/tests/resources/push_src/.gitted/objects/pack/dummy
diff --git a/tests-clar/resources/push_src/.gitted/refs/heads/b1 b/tests/resources/push_src/.gitted/refs/heads/b1
index afadf9d26..afadf9d26 100644
--- a/tests-clar/resources/push_src/.gitted/refs/heads/b1
+++ b/tests/resources/push_src/.gitted/refs/heads/b1
diff --git a/tests-clar/resources/push_src/.gitted/refs/heads/b2 b/tests/resources/push_src/.gitted/refs/heads/b2
index afadf9d26..afadf9d26 100644
--- a/tests-clar/resources/push_src/.gitted/refs/heads/b2
+++ b/tests/resources/push_src/.gitted/refs/heads/b2
diff --git a/tests-clar/resources/push_src/.gitted/refs/heads/b3 b/tests/resources/push_src/.gitted/refs/heads/b3
index 3056bb436..3056bb436 100644
--- a/tests-clar/resources/push_src/.gitted/refs/heads/b3
+++ b/tests/resources/push_src/.gitted/refs/heads/b3
diff --git a/tests-clar/resources/push_src/.gitted/refs/heads/b4 b/tests/resources/push_src/.gitted/refs/heads/b4
index efed6f064..efed6f064 100644
--- a/tests-clar/resources/push_src/.gitted/refs/heads/b4
+++ b/tests/resources/push_src/.gitted/refs/heads/b4
diff --git a/tests-clar/resources/push_src/.gitted/refs/heads/b5 b/tests/resources/push_src/.gitted/refs/heads/b5
index cf313ad05..cf313ad05 100644
--- a/tests-clar/resources/push_src/.gitted/refs/heads/b5
+++ b/tests/resources/push_src/.gitted/refs/heads/b5
diff --git a/tests-clar/resources/push_src/.gitted/refs/heads/b6 b/tests/resources/push_src/.gitted/refs/heads/b6
index 711e466ae..711e466ae 100644
--- a/tests-clar/resources/push_src/.gitted/refs/heads/b6
+++ b/tests/resources/push_src/.gitted/refs/heads/b6
diff --git a/tests-clar/resources/push_src/.gitted/refs/tags/tag-blob b/tests/resources/push_src/.gitted/refs/tags/tag-blob
index abfebf22e..abfebf22e 100644
--- a/tests-clar/resources/push_src/.gitted/refs/tags/tag-blob
+++ b/tests/resources/push_src/.gitted/refs/tags/tag-blob
diff --git a/tests-clar/resources/push_src/.gitted/refs/tags/tag-commit b/tests/resources/push_src/.gitted/refs/tags/tag-commit
index c023b8452..c023b8452 100644
--- a/tests-clar/resources/push_src/.gitted/refs/tags/tag-commit
+++ b/tests/resources/push_src/.gitted/refs/tags/tag-commit
diff --git a/tests/resources/push_src/.gitted/refs/tags/tag-commit-two b/tests/resources/push_src/.gitted/refs/tags/tag-commit-two
new file mode 100644
index 000000000..abb365080
--- /dev/null
+++ b/tests/resources/push_src/.gitted/refs/tags/tag-commit-two
@@ -0,0 +1 @@
+36f79b2846017d3761e0a02d0bccd573e0f90c57
diff --git a/tests-clar/resources/push_src/.gitted/refs/tags/tag-lightweight b/tests/resources/push_src/.gitted/refs/tags/tag-lightweight
index 711e466ae..711e466ae 100644
--- a/tests-clar/resources/push_src/.gitted/refs/tags/tag-lightweight
+++ b/tests/resources/push_src/.gitted/refs/tags/tag-lightweight
diff --git a/tests/resources/push_src/.gitted/refs/tags/tag-tag b/tests/resources/push_src/.gitted/refs/tags/tag-tag
new file mode 100644
index 000000000..d6cd74804
--- /dev/null
+++ b/tests/resources/push_src/.gitted/refs/tags/tag-tag
@@ -0,0 +1 @@
+eea4f2705eeec2db3813f2430829afce99cd00b5
diff --git a/tests-clar/resources/push_src/.gitted/refs/tags/tag-tree b/tests/resources/push_src/.gitted/refs/tags/tag-tree
index 7a530d381..7a530d381 100644
--- a/tests-clar/resources/push_src/.gitted/refs/tags/tag-tree
+++ b/tests/resources/push_src/.gitted/refs/tags/tag-tree
diff --git a/tests-clar/resources/push_src/a.txt b/tests/resources/push_src/a.txt
index f7eac1c51..f7eac1c51 100644
--- a/tests-clar/resources/push_src/a.txt
+++ b/tests/resources/push_src/a.txt
diff --git a/tests-clar/resources/push_src/fold/b.txt b/tests/resources/push_src/fold/b.txt
index 617807982..617807982 100644
--- a/tests-clar/resources/push_src/fold/b.txt
+++ b/tests/resources/push_src/fold/b.txt
diff --git a/tests-clar/resources/push_src/foldb.txt b/tests/resources/push_src/foldb.txt
index 5b38718be..5b38718be 100644
--- a/tests-clar/resources/push_src/foldb.txt
+++ b/tests/resources/push_src/foldb.txt
diff --git a/tests-clar/resources/push_src/gitmodules b/tests/resources/push_src/gitmodules
index f1734dfc1..f1734dfc1 100644
--- a/tests-clar/resources/push_src/gitmodules
+++ b/tests/resources/push_src/gitmodules
diff --git a/tests-clar/resources/push_src/submodule/.gitted b/tests/resources/push_src/submodule/.gitted
index 3ffcf960a..3ffcf960a 100644
--- a/tests-clar/resources/push_src/submodule/.gitted
+++ b/tests/resources/push_src/submodule/.gitted
diff --git a/tests-clar/resources/push_src/submodule/README b/tests/resources/push_src/submodule/README
index ca8c64728..ca8c64728 100644
--- a/tests-clar/resources/push_src/submodule/README
+++ b/tests/resources/push_src/submodule/README
diff --git a/tests-clar/resources/push_src/submodule/branch_file.txt b/tests/resources/push_src/submodule/branch_file.txt
index a26902575..a26902575 100644
--- a/tests-clar/resources/push_src/submodule/branch_file.txt
+++ b/tests/resources/push_src/submodule/branch_file.txt
diff --git a/tests-clar/resources/push_src/submodule/new.txt b/tests/resources/push_src/submodule/new.txt
index 8e0884e36..8e0884e36 100644
--- a/tests-clar/resources/push_src/submodule/new.txt
+++ b/tests/resources/push_src/submodule/new.txt
diff --git a/tests-clar/resources/shallow.git/HEAD b/tests/resources/renames/.gitted/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/shallow.git/HEAD
+++ b/tests/resources/renames/.gitted/HEAD
diff --git a/tests-clar/resources/renames/.gitted/config b/tests/resources/renames/.gitted/config
index bb4d11c1f..bb4d11c1f 100644
--- a/tests-clar/resources/renames/.gitted/config
+++ b/tests/resources/renames/.gitted/config
diff --git a/tests-clar/resources/status/.gitted/description b/tests/resources/renames/.gitted/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/status/.gitted/description
+++ b/tests/resources/renames/.gitted/description
diff --git a/tests-clar/resources/renames/.gitted/index b/tests/resources/renames/.gitted/index
index 72363c0f5..72363c0f5 100644
--- a/tests-clar/resources/renames/.gitted/index
+++ b/tests/resources/renames/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/info/exclude b/tests/resources/renames/.gitted/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/renames/.gitted/info/exclude
+++ b/tests/resources/renames/.gitted/info/exclude
diff --git a/tests-clar/resources/renames/.gitted/logs/HEAD b/tests/resources/renames/.gitted/logs/HEAD
index e69792263..e69792263 100644
--- a/tests-clar/resources/renames/.gitted/logs/HEAD
+++ b/tests/resources/renames/.gitted/logs/HEAD
diff --git a/tests-clar/resources/renames/.gitted/logs/refs/heads/master b/tests/resources/renames/.gitted/logs/refs/heads/master
index e69792263..e69792263 100644
--- a/tests-clar/resources/renames/.gitted/logs/refs/heads/master
+++ b/tests/resources/renames/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/renames/.gitted/objects/03/da7ad872536bd448da8d88eb7165338bf923a7 b/tests/resources/renames/.gitted/objects/03/da7ad872536bd448da8d88eb7165338bf923a7
index 2ee86444d..2ee86444d 100644
--- a/tests-clar/resources/renames/.gitted/objects/03/da7ad872536bd448da8d88eb7165338bf923a7
+++ b/tests/resources/renames/.gitted/objects/03/da7ad872536bd448da8d88eb7165338bf923a7
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/17/58bdd7c16a72ff7c17d8de0c957ced3ccad645 b/tests/resources/renames/.gitted/objects/17/58bdd7c16a72ff7c17d8de0c957ced3ccad645
index 01801ed11..01801ed11 100644
--- a/tests-clar/resources/renames/.gitted/objects/17/58bdd7c16a72ff7c17d8de0c957ced3ccad645
+++ b/tests/resources/renames/.gitted/objects/17/58bdd7c16a72ff7c17d8de0c957ced3ccad645
diff --git a/tests-clar/resources/renames/.gitted/objects/19/dd32dfb1520a64e5bbaae8dce6ef423dfa2f13 b/tests/resources/renames/.gitted/objects/19/dd32dfb1520a64e5bbaae8dce6ef423dfa2f13
index 4be4c6952..4be4c6952 100644
--- a/tests-clar/resources/renames/.gitted/objects/19/dd32dfb1520a64e5bbaae8dce6ef423dfa2f13
+++ b/tests/resources/renames/.gitted/objects/19/dd32dfb1520a64e5bbaae8dce6ef423dfa2f13
diff --git a/tests-clar/resources/renames/.gitted/objects/1c/068dee5790ef1580cfc4cd670915b48d790084 b/tests/resources/renames/.gitted/objects/1c/068dee5790ef1580cfc4cd670915b48d790084
index d65ab0a9b..d65ab0a9b 100644
--- a/tests-clar/resources/renames/.gitted/objects/1c/068dee5790ef1580cfc4cd670915b48d790084
+++ b/tests/resources/renames/.gitted/objects/1c/068dee5790ef1580cfc4cd670915b48d790084
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/2b/c7f351d20b53f1c72c16c4b036e491c478c49a b/tests/resources/renames/.gitted/objects/2b/c7f351d20b53f1c72c16c4b036e491c478c49a
index 93f1ccb3f..93f1ccb3f 100644
--- a/tests-clar/resources/renames/.gitted/objects/2b/c7f351d20b53f1c72c16c4b036e491c478c49a
+++ b/tests/resources/renames/.gitted/objects/2b/c7f351d20b53f1c72c16c4b036e491c478c49a
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/31/e47d8c1fa36d7f8d537b96158e3f024de0a9f2 b/tests/resources/renames/.gitted/objects/31/e47d8c1fa36d7f8d537b96158e3f024de0a9f2
index 00ce53212..00ce53212 100644
--- a/tests-clar/resources/renames/.gitted/objects/31/e47d8c1fa36d7f8d537b96158e3f024de0a9f2
+++ b/tests/resources/renames/.gitted/objects/31/e47d8c1fa36d7f8d537b96158e3f024de0a9f2
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/35/92953ff3ea5e8ba700c429f3aefe33c8806754 b/tests/resources/renames/.gitted/objects/35/92953ff3ea5e8ba700c429f3aefe33c8806754
index 886271d32..886271d32 100644
--- a/tests-clar/resources/renames/.gitted/objects/35/92953ff3ea5e8ba700c429f3aefe33c8806754
+++ b/tests/resources/renames/.gitted/objects/35/92953ff3ea5e8ba700c429f3aefe33c8806754
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/36/020db6cdacaa93497f31edcd8f242ff9bc366d b/tests/resources/renames/.gitted/objects/36/020db6cdacaa93497f31edcd8f242ff9bc366d
index f4f9303ed..f4f9303ed 100644
--- a/tests-clar/resources/renames/.gitted/objects/36/020db6cdacaa93497f31edcd8f242ff9bc366d
+++ b/tests/resources/renames/.gitted/objects/36/020db6cdacaa93497f31edcd8f242ff9bc366d
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/3c/04741dd4b96c4ae4b00ec0f6e10c816a30aad2 b/tests/resources/renames/.gitted/objects/3c/04741dd4b96c4ae4b00ec0f6e10c816a30aad2
index c23602262..c23602262 100644
--- a/tests-clar/resources/renames/.gitted/objects/3c/04741dd4b96c4ae4b00ec0f6e10c816a30aad2
+++ b/tests/resources/renames/.gitted/objects/3c/04741dd4b96c4ae4b00ec0f6e10c816a30aad2
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/42/10ffd5c390b21dd5483375e75288dea9ede512 b/tests/resources/renames/.gitted/objects/42/10ffd5c390b21dd5483375e75288dea9ede512
index d351a6d13..d351a6d13 100644
--- a/tests-clar/resources/renames/.gitted/objects/42/10ffd5c390b21dd5483375e75288dea9ede512
+++ b/tests/resources/renames/.gitted/objects/42/10ffd5c390b21dd5483375e75288dea9ede512
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/44/4a76ed3e45b183753f49376af30da8c3fe276a b/tests/resources/renames/.gitted/objects/44/4a76ed3e45b183753f49376af30da8c3fe276a
index 5ce12a3c5..5ce12a3c5 100644
--- a/tests-clar/resources/renames/.gitted/objects/44/4a76ed3e45b183753f49376af30da8c3fe276a
+++ b/tests/resources/renames/.gitted/objects/44/4a76ed3e45b183753f49376af30da8c3fe276a
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/47/184c1e7eb22abcbed2bf4ee87d4e38096f7951 b/tests/resources/renames/.gitted/objects/47/184c1e7eb22abcbed2bf4ee87d4e38096f7951
index aa9192699..aa9192699 100644
--- a/tests-clar/resources/renames/.gitted/objects/47/184c1e7eb22abcbed2bf4ee87d4e38096f7951
+++ b/tests/resources/renames/.gitted/objects/47/184c1e7eb22abcbed2bf4ee87d4e38096f7951
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/4e/4cae3e7dd56ed74bff39526d0469e554432953 b/tests/resources/renames/.gitted/objects/4e/4cae3e7dd56ed74bff39526d0469e554432953
index 5e6ebd5e0..5e6ebd5e0 100644
--- a/tests-clar/resources/renames/.gitted/objects/4e/4cae3e7dd56ed74bff39526d0469e554432953
+++ b/tests/resources/renames/.gitted/objects/4e/4cae3e7dd56ed74bff39526d0469e554432953
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/50/e90273af7d826ff0a95865bcd3ba8412c447d9 b/tests/resources/renames/.gitted/objects/50/e90273af7d826ff0a95865bcd3ba8412c447d9
index a98d14ee7..a98d14ee7 100644
--- a/tests-clar/resources/renames/.gitted/objects/50/e90273af7d826ff0a95865bcd3ba8412c447d9
+++ b/tests/resources/renames/.gitted/objects/50/e90273af7d826ff0a95865bcd3ba8412c447d9
diff --git a/tests-clar/resources/renames/.gitted/objects/5e/26abc56a5a84d89790f45416648899cbe13109 b/tests/resources/renames/.gitted/objects/5e/26abc56a5a84d89790f45416648899cbe13109
index 2acd3d583..2acd3d583 100644
--- a/tests-clar/resources/renames/.gitted/objects/5e/26abc56a5a84d89790f45416648899cbe13109
+++ b/tests/resources/renames/.gitted/objects/5e/26abc56a5a84d89790f45416648899cbe13109
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/61/8c6f2f8740bd6049b2fb9eb93fc15726462745 b/tests/resources/renames/.gitted/objects/61/8c6f2f8740bd6049b2fb9eb93fc15726462745
index 24eac54c5..24eac54c5 100644
--- a/tests-clar/resources/renames/.gitted/objects/61/8c6f2f8740bd6049b2fb9eb93fc15726462745
+++ b/tests/resources/renames/.gitted/objects/61/8c6f2f8740bd6049b2fb9eb93fc15726462745
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/66/311f5cfbe7836c27510a3ba2f43e282e2c8bba b/tests/resources/renames/.gitted/objects/66/311f5cfbe7836c27510a3ba2f43e282e2c8bba
index 5ee28a76a..5ee28a76a 100644
--- a/tests-clar/resources/renames/.gitted/objects/66/311f5cfbe7836c27510a3ba2f43e282e2c8bba
+++ b/tests/resources/renames/.gitted/objects/66/311f5cfbe7836c27510a3ba2f43e282e2c8bba
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/93/f538c45a57a87eb4c1e86f91c6ee41d66c7ba7 b/tests/resources/renames/.gitted/objects/93/f538c45a57a87eb4c1e86f91c6ee41d66c7ba7
index 39105f94d..39105f94d 100644
--- a/tests-clar/resources/renames/.gitted/objects/93/f538c45a57a87eb4c1e86f91c6ee41d66c7ba7
+++ b/tests/resources/renames/.gitted/objects/93/f538c45a57a87eb4c1e86f91c6ee41d66c7ba7
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/9a/69d960ae94b060f56c2a8702545e2bb1abb935 b/tests/resources/renames/.gitted/objects/9a/69d960ae94b060f56c2a8702545e2bb1abb935
index f75178c59..f75178c59 100644
--- a/tests-clar/resources/renames/.gitted/objects/9a/69d960ae94b060f56c2a8702545e2bb1abb935
+++ b/tests/resources/renames/.gitted/objects/9a/69d960ae94b060f56c2a8702545e2bb1abb935
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/ad/0a8e55a104ac54a8a29ed4b84b49e76837a113 b/tests/resources/renames/.gitted/objects/ad/0a8e55a104ac54a8a29ed4b84b49e76837a113
index 440b7bec3..440b7bec3 100644
--- a/tests-clar/resources/renames/.gitted/objects/ad/0a8e55a104ac54a8a29ed4b84b49e76837a113
+++ b/tests/resources/renames/.gitted/objects/ad/0a8e55a104ac54a8a29ed4b84b49e76837a113
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/b9/25b224cc91f897001a9993fbce169fdaa8858f b/tests/resources/renames/.gitted/objects/b9/25b224cc91f897001a9993fbce169fdaa8858f
index 90e107fa2..90e107fa2 100644
--- a/tests-clar/resources/renames/.gitted/objects/b9/25b224cc91f897001a9993fbce169fdaa8858f
+++ b/tests/resources/renames/.gitted/objects/b9/25b224cc91f897001a9993fbce169fdaa8858f
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/d7/9b202de198fa61b02424b9e25e840dc75e1323 b/tests/resources/renames/.gitted/objects/d7/9b202de198fa61b02424b9e25e840dc75e1323
index daa2b3997..daa2b3997 100644
--- a/tests-clar/resources/renames/.gitted/objects/d7/9b202de198fa61b02424b9e25e840dc75e1323
+++ b/tests/resources/renames/.gitted/objects/d7/9b202de198fa61b02424b9e25e840dc75e1323
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/ea/c43f5195a2cee53b7458d8dad16aedde10711b b/tests/resources/renames/.gitted/objects/ea/c43f5195a2cee53b7458d8dad16aedde10711b
index 2fb025080..2fb025080 100644
--- a/tests-clar/resources/renames/.gitted/objects/ea/c43f5195a2cee53b7458d8dad16aedde10711b
+++ b/tests/resources/renames/.gitted/objects/ea/c43f5195a2cee53b7458d8dad16aedde10711b
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/ea/f4a3e3bfe68585e90cada20736ace491cd100b b/tests/resources/renames/.gitted/objects/ea/f4a3e3bfe68585e90cada20736ace491cd100b
index f72df8d82..f72df8d82 100644
--- a/tests-clar/resources/renames/.gitted/objects/ea/f4a3e3bfe68585e90cada20736ace491cd100b
+++ b/tests/resources/renames/.gitted/objects/ea/f4a3e3bfe68585e90cada20736ace491cd100b
diff --git a/tests-clar/resources/renames/.gitted/objects/f9/0d4fc20ecddf21eebe6a37e9225d244339d2b5 b/tests/resources/renames/.gitted/objects/f9/0d4fc20ecddf21eebe6a37e9225d244339d2b5
index f6d933be9..f6d933be9 100644
--- a/tests-clar/resources/renames/.gitted/objects/f9/0d4fc20ecddf21eebe6a37e9225d244339d2b5
+++ b/tests/resources/renames/.gitted/objects/f9/0d4fc20ecddf21eebe6a37e9225d244339d2b5
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/refs/heads/master b/tests/resources/renames/.gitted/refs/heads/master
index 642c3198d..642c3198d 100644
--- a/tests-clar/resources/renames/.gitted/refs/heads/master
+++ b/tests/resources/renames/.gitted/refs/heads/master
diff --git a/tests-clar/resources/renames/.gitted/refs/heads/renames_similar b/tests/resources/renames/.gitted/refs/heads/renames_similar
index 8ffb6d94b..8ffb6d94b 100644
--- a/tests-clar/resources/renames/.gitted/refs/heads/renames_similar
+++ b/tests/resources/renames/.gitted/refs/heads/renames_similar
diff --git a/tests-clar/resources/renames/.gitted/refs/heads/renames_similar_two b/tests/resources/renames/.gitted/refs/heads/renames_similar_two
index 4ee5d04e1..4ee5d04e1 100644
--- a/tests-clar/resources/renames/.gitted/refs/heads/renames_similar_two
+++ b/tests/resources/renames/.gitted/refs/heads/renames_similar_two
diff --git a/tests-clar/resources/renames/ikeepsix.txt b/tests/resources/renames/ikeepsix.txt
index eaf4a3e3b..eaf4a3e3b 100644
--- a/tests-clar/resources/renames/ikeepsix.txt
+++ b/tests/resources/renames/ikeepsix.txt
diff --git a/tests-clar/resources/renames/sixserving.txt b/tests/resources/renames/sixserving.txt
index f90d4fc20..f90d4fc20 100644
--- a/tests-clar/resources/renames/sixserving.txt
+++ b/tests/resources/renames/sixserving.txt
diff --git a/tests-clar/resources/renames/songof7cities.txt b/tests/resources/renames/songof7cities.txt
index 4210ffd5c..4210ffd5c 100644
--- a/tests-clar/resources/renames/songof7cities.txt
+++ b/tests/resources/renames/songof7cities.txt
diff --git a/tests-clar/resources/renames/untimely.txt b/tests/resources/renames/untimely.txt
index 9a69d960a..9a69d960a 100644
--- a/tests-clar/resources/renames/untimely.txt
+++ b/tests/resources/renames/untimely.txt
diff --git a/tests-clar/resources/short_tag.git/HEAD b/tests/resources/shallow.git/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/short_tag.git/HEAD
+++ b/tests/resources/shallow.git/HEAD
diff --git a/tests-clar/resources/shallow.git/config b/tests/resources/shallow.git/config
index a88b74b69..a88b74b69 100644
--- a/tests-clar/resources/shallow.git/config
+++ b/tests/resources/shallow.git/config
diff --git a/tests-clar/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.idx b/tests/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.idx
index bfc7d24ff..bfc7d24ff 100644
--- a/tests-clar/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.idx
+++ b/tests/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.idx
Binary files differ
diff --git a/tests-clar/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.pack b/tests/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.pack
index ccc6932fc..ccc6932fc 100644
--- a/tests-clar/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.pack
+++ b/tests/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.pack
Binary files differ
diff --git a/tests-clar/resources/shallow.git/packed-refs b/tests/resources/shallow.git/packed-refs
index 97eed743b..97eed743b 100644
--- a/tests-clar/resources/shallow.git/packed-refs
+++ b/tests/resources/shallow.git/packed-refs
diff --git a/tests-clar/resources/shallow.git/refs/.gitkeep b/tests/resources/shallow.git/refs/.gitkeep
index e69de29bb..e69de29bb 100644
--- a/tests-clar/resources/shallow.git/refs/.gitkeep
+++ b/tests/resources/shallow.git/refs/.gitkeep
diff --git a/tests-clar/resources/shallow.git/shallow b/tests/resources/shallow.git/shallow
index 9536ad89c..9536ad89c 100644
--- a/tests-clar/resources/shallow.git/shallow
+++ b/tests/resources/shallow.git/shallow
diff --git a/tests-clar/resources/status/.gitted/HEAD b/tests/resources/short_tag.git/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/status/.gitted/HEAD
+++ b/tests/resources/short_tag.git/HEAD
diff --git a/tests/resources/short_tag.git/config b/tests/resources/short_tag.git/config
new file mode 100644
index 000000000..a4ef456cb
--- /dev/null
+++ b/tests/resources/short_tag.git/config
@@ -0,0 +1,5 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = true
+ logallrefupdates = true
diff --git a/tests-clar/resources/short_tag.git/index b/tests/resources/short_tag.git/index
index 87fef7847..87fef7847 100644
--- a/tests-clar/resources/short_tag.git/index
+++ b/tests/resources/short_tag.git/index
Binary files differ
diff --git a/tests-clar/resources/short_tag.git/objects/4a/5ed60bafcf4638b7c8356bd4ce1916bfede93c b/tests/resources/short_tag.git/objects/4a/5ed60bafcf4638b7c8356bd4ce1916bfede93c
index aeb4e4b0b..aeb4e4b0b 100644
--- a/tests-clar/resources/short_tag.git/objects/4a/5ed60bafcf4638b7c8356bd4ce1916bfede93c
+++ b/tests/resources/short_tag.git/objects/4a/5ed60bafcf4638b7c8356bd4ce1916bfede93c
Binary files differ
diff --git a/tests-clar/resources/short_tag.git/objects/4d/5fcadc293a348e88f777dc0920f11e7d71441c b/tests/resources/short_tag.git/objects/4d/5fcadc293a348e88f777dc0920f11e7d71441c
index 806ce71a5..806ce71a5 100644
--- a/tests-clar/resources/short_tag.git/objects/4d/5fcadc293a348e88f777dc0920f11e7d71441c
+++ b/tests/resources/short_tag.git/objects/4d/5fcadc293a348e88f777dc0920f11e7d71441c
Binary files differ
diff --git a/tests-clar/resources/short_tag.git/objects/5d/a7760512a953e3c7c4e47e4392c7a4338fb729 b/tests/resources/short_tag.git/objects/5d/a7760512a953e3c7c4e47e4392c7a4338fb729
index 1192707c9..1192707c9 100644
--- a/tests-clar/resources/short_tag.git/objects/5d/a7760512a953e3c7c4e47e4392c7a4338fb729
+++ b/tests/resources/short_tag.git/objects/5d/a7760512a953e3c7c4e47e4392c7a4338fb729
diff --git a/tests-clar/resources/short_tag.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/short_tag.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
index 711223894..711223894 100644
--- a/tests-clar/resources/short_tag.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
+++ b/tests/resources/short_tag.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests-clar/resources/short_tag.git/packed-refs b/tests/resources/short_tag.git/packed-refs
index ca5197e3c..ca5197e3c 100644
--- a/tests-clar/resources/short_tag.git/packed-refs
+++ b/tests/resources/short_tag.git/packed-refs
diff --git a/tests-clar/resources/short_tag.git/refs/heads/master b/tests/resources/short_tag.git/refs/heads/master
index fcefd1ef0..fcefd1ef0 100644
--- a/tests-clar/resources/short_tag.git/refs/heads/master
+++ b/tests/resources/short_tag.git/refs/heads/master
diff --git a/tests-clar/resources/status/.gitted/COMMIT_EDITMSG b/tests/resources/status/.gitted/COMMIT_EDITMSG
index 1a25cd4a6..1a25cd4a6 100644
--- a/tests-clar/resources/status/.gitted/COMMIT_EDITMSG
+++ b/tests/resources/status/.gitted/COMMIT_EDITMSG
diff --git a/tests-clar/resources/submod2/.gitted/HEAD b/tests/resources/status/.gitted/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/submod2/.gitted/HEAD
+++ b/tests/resources/status/.gitted/HEAD
diff --git a/tests-clar/resources/status/.gitted/ORIG_HEAD b/tests/resources/status/.gitted/ORIG_HEAD
index b46871fd6..b46871fd6 100644
--- a/tests-clar/resources/status/.gitted/ORIG_HEAD
+++ b/tests/resources/status/.gitted/ORIG_HEAD
diff --git a/tests-clar/resources/status/.gitted/config b/tests/resources/status/.gitted/config
index af107929f..af107929f 100644
--- a/tests-clar/resources/status/.gitted/config
+++ b/tests/resources/status/.gitted/config
diff --git a/tests-clar/resources/submod2/.gitted/description b/tests/resources/status/.gitted/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/submod2/.gitted/description
+++ b/tests/resources/status/.gitted/description
diff --git a/tests-clar/resources/status/.gitted/index b/tests/resources/status/.gitted/index
index 2af99a183..2af99a183 100644
--- a/tests-clar/resources/status/.gitted/index
+++ b/tests/resources/status/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/info/exclude b/tests/resources/status/.gitted/info/exclude
index 0c4042a6a..0c4042a6a 100644
--- a/tests-clar/resources/status/.gitted/info/exclude
+++ b/tests/resources/status/.gitted/info/exclude
diff --git a/tests-clar/resources/status/.gitted/logs/HEAD b/tests/resources/status/.gitted/logs/HEAD
index 7b95b3cf1..7b95b3cf1 100644
--- a/tests-clar/resources/status/.gitted/logs/HEAD
+++ b/tests/resources/status/.gitted/logs/HEAD
diff --git a/tests-clar/resources/status/.gitted/logs/refs/heads/master b/tests/resources/status/.gitted/logs/refs/heads/master
index 7b95b3cf1..7b95b3cf1 100644
--- a/tests-clar/resources/status/.gitted/logs/refs/heads/master
+++ b/tests/resources/status/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/status/.gitted/objects/00/17bd4ab1ec30440b17bae1680cff124ab5f1f6 b/tests/resources/status/.gitted/objects/00/17bd4ab1ec30440b17bae1680cff124ab5f1f6
index b256d95a3..b256d95a3 100644
--- a/tests-clar/resources/status/.gitted/objects/00/17bd4ab1ec30440b17bae1680cff124ab5f1f6
+++ b/tests/resources/status/.gitted/objects/00/17bd4ab1ec30440b17bae1680cff124ab5f1f6
diff --git a/tests-clar/resources/status/.gitted/objects/06/1d42a44cacde5726057b67558821d95db96f19 b/tests/resources/status/.gitted/objects/06/1d42a44cacde5726057b67558821d95db96f19
index 82e02cb0e..82e02cb0e 100644
--- a/tests-clar/resources/status/.gitted/objects/06/1d42a44cacde5726057b67558821d95db96f19
+++ b/tests/resources/status/.gitted/objects/06/1d42a44cacde5726057b67558821d95db96f19
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/18/88c805345ba265b0ee9449b8877b6064592058 b/tests/resources/status/.gitted/objects/18/88c805345ba265b0ee9449b8877b6064592058
index e3cad2f02..e3cad2f02 100644
--- a/tests-clar/resources/status/.gitted/objects/18/88c805345ba265b0ee9449b8877b6064592058
+++ b/tests/resources/status/.gitted/objects/18/88c805345ba265b0ee9449b8877b6064592058
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/19/d9cc8584ac2c7dcf57d2680375e80f099dc481 b/tests/resources/status/.gitted/objects/19/d9cc8584ac2c7dcf57d2680375e80f099dc481
index 2d5e711b9..2d5e711b9 100644
--- a/tests-clar/resources/status/.gitted/objects/19/d9cc8584ac2c7dcf57d2680375e80f099dc481
+++ b/tests/resources/status/.gitted/objects/19/d9cc8584ac2c7dcf57d2680375e80f099dc481
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/26/a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f b/tests/resources/status/.gitted/objects/26/a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f
index f7dddc4ff..f7dddc4ff 100644
--- a/tests-clar/resources/status/.gitted/objects/26/a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f
+++ b/tests/resources/status/.gitted/objects/26/a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f
diff --git a/tests-clar/resources/status/.gitted/objects/32/504b727382542f9f089e24fddac5e78533e96c b/tests/resources/status/.gitted/objects/32/504b727382542f9f089e24fddac5e78533e96c
index 7fca67be8..7fca67be8 100644
--- a/tests-clar/resources/status/.gitted/objects/32/504b727382542f9f089e24fddac5e78533e96c
+++ b/tests/resources/status/.gitted/objects/32/504b727382542f9f089e24fddac5e78533e96c
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/37/fcb02ccc1a85d1941e7f106d52dc3702dcf0d0 b/tests/resources/status/.gitted/objects/37/fcb02ccc1a85d1941e7f106d52dc3702dcf0d0
index b75481b51..b75481b51 100644
--- a/tests-clar/resources/status/.gitted/objects/37/fcb02ccc1a85d1941e7f106d52dc3702dcf0d0
+++ b/tests/resources/status/.gitted/objects/37/fcb02ccc1a85d1941e7f106d52dc3702dcf0d0
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/45/2e4244b5d083ddf0460acf1ecc74db9dcfa11a b/tests/resources/status/.gitted/objects/45/2e4244b5d083ddf0460acf1ecc74db9dcfa11a
index 5b47461e9..5b47461e9 100644
--- a/tests-clar/resources/status/.gitted/objects/45/2e4244b5d083ddf0460acf1ecc74db9dcfa11a
+++ b/tests/resources/status/.gitted/objects/45/2e4244b5d083ddf0460acf1ecc74db9dcfa11a
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/52/9a16e8e762d4acb7b9636ff540a00831f9155a b/tests/resources/status/.gitted/objects/52/9a16e8e762d4acb7b9636ff540a00831f9155a
index 615009ad0..615009ad0 100644
--- a/tests-clar/resources/status/.gitted/objects/52/9a16e8e762d4acb7b9636ff540a00831f9155a
+++ b/tests/resources/status/.gitted/objects/52/9a16e8e762d4acb7b9636ff540a00831f9155a
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/53/ace0d1cc1145a5f4fe4f78a186a60263190733 b/tests/resources/status/.gitted/objects/53/ace0d1cc1145a5f4fe4f78a186a60263190733
index cdb7e961a..cdb7e961a 100644
--- a/tests-clar/resources/status/.gitted/objects/53/ace0d1cc1145a5f4fe4f78a186a60263190733
+++ b/tests/resources/status/.gitted/objects/53/ace0d1cc1145a5f4fe4f78a186a60263190733
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/54/52d32f1dd538eb0405e8a83cc185f79e25e80f b/tests/resources/status/.gitted/objects/54/52d32f1dd538eb0405e8a83cc185f79e25e80f
index a72dff646..a72dff646 100644
--- a/tests-clar/resources/status/.gitted/objects/54/52d32f1dd538eb0405e8a83cc185f79e25e80f
+++ b/tests/resources/status/.gitted/objects/54/52d32f1dd538eb0405e8a83cc185f79e25e80f
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/55/d316c9ba708999f1918e9677d01dfcae69c6b9 b/tests/resources/status/.gitted/objects/55/d316c9ba708999f1918e9677d01dfcae69c6b9
index 72807f3d0..72807f3d0 100644
--- a/tests-clar/resources/status/.gitted/objects/55/d316c9ba708999f1918e9677d01dfcae69c6b9
+++ b/tests/resources/status/.gitted/objects/55/d316c9ba708999f1918e9677d01dfcae69c6b9
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/70/bd9443ada07063e7fbf0b3ff5c13f7494d89c2 b/tests/resources/status/.gitted/objects/70/bd9443ada07063e7fbf0b3ff5c13f7494d89c2
index 3665a8f7c..3665a8f7c 100644
--- a/tests-clar/resources/status/.gitted/objects/70/bd9443ada07063e7fbf0b3ff5c13f7494d89c2
+++ b/tests/resources/status/.gitted/objects/70/bd9443ada07063e7fbf0b3ff5c13f7494d89c2
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/73/5b6a258cd196a8f7c9428419b02c1dca93fd75 b/tests/resources/status/.gitted/objects/73/5b6a258cd196a8f7c9428419b02c1dca93fd75
index 08e6fd246..08e6fd246 100644
--- a/tests-clar/resources/status/.gitted/objects/73/5b6a258cd196a8f7c9428419b02c1dca93fd75
+++ b/tests/resources/status/.gitted/objects/73/5b6a258cd196a8f7c9428419b02c1dca93fd75
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/75/6e27627e67bfbc048d01ece5819c6de733d7ea b/tests/resources/status/.gitted/objects/75/6e27627e67bfbc048d01ece5819c6de733d7ea
index 8f3fa89e5..8f3fa89e5 100644
--- a/tests-clar/resources/status/.gitted/objects/75/6e27627e67bfbc048d01ece5819c6de733d7ea
+++ b/tests/resources/status/.gitted/objects/75/6e27627e67bfbc048d01ece5819c6de733d7ea
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/90/6ee7711f4f4928ddcb2a5f8fbc500deba0d2a8 b/tests/resources/status/.gitted/objects/90/6ee7711f4f4928ddcb2a5f8fbc500deba0d2a8
index bb732b08e..bb732b08e 100644
--- a/tests-clar/resources/status/.gitted/objects/90/6ee7711f4f4928ddcb2a5f8fbc500deba0d2a8
+++ b/tests/resources/status/.gitted/objects/90/6ee7711f4f4928ddcb2a5f8fbc500deba0d2a8
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/90/b8c29d8ba39434d1c63e1b093daaa26e5bd972 b/tests/resources/status/.gitted/objects/90/b8c29d8ba39434d1c63e1b093daaa26e5bd972
index 7a96618ff..7a96618ff 100644
--- a/tests-clar/resources/status/.gitted/objects/90/b8c29d8ba39434d1c63e1b093daaa26e5bd972
+++ b/tests/resources/status/.gitted/objects/90/b8c29d8ba39434d1c63e1b093daaa26e5bd972
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/9c/2e02cdffa8d73e6c189074594477a6baf87960 b/tests/resources/status/.gitted/objects/9c/2e02cdffa8d73e6c189074594477a6baf87960
index 20a3c497e..20a3c497e 100644
--- a/tests-clar/resources/status/.gitted/objects/9c/2e02cdffa8d73e6c189074594477a6baf87960
+++ b/tests/resources/status/.gitted/objects/9c/2e02cdffa8d73e6c189074594477a6baf87960
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/a0/de7e0ac200c489c41c59dfa910154a70264e6e b/tests/resources/status/.gitted/objects/a0/de7e0ac200c489c41c59dfa910154a70264e6e
index a1789c9a6..a1789c9a6 100644
--- a/tests-clar/resources/status/.gitted/objects/a0/de7e0ac200c489c41c59dfa910154a70264e6e
+++ b/tests/resources/status/.gitted/objects/a0/de7e0ac200c489c41c59dfa910154a70264e6e
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/a6/191982709b746d5650e93c2acf34ef74e11504 b/tests/resources/status/.gitted/objects/a6/191982709b746d5650e93c2acf34ef74e11504
index cc1f377b3..cc1f377b3 100644
--- a/tests-clar/resources/status/.gitted/objects/a6/191982709b746d5650e93c2acf34ef74e11504
+++ b/tests/resources/status/.gitted/objects/a6/191982709b746d5650e93c2acf34ef74e11504
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/a6/be623522ce87a1d862128ac42672604f7b468b b/tests/resources/status/.gitted/objects/a6/be623522ce87a1d862128ac42672604f7b468b
index c47298347..c47298347 100644
--- a/tests-clar/resources/status/.gitted/objects/a6/be623522ce87a1d862128ac42672604f7b468b
+++ b/tests/resources/status/.gitted/objects/a6/be623522ce87a1d862128ac42672604f7b468b
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/aa/27a641456848200fdb7f7c99ba36f8a0952877 b/tests/resources/status/.gitted/objects/aa/27a641456848200fdb7f7c99ba36f8a0952877
index a4669ccbb..a4669ccbb 100644
--- a/tests-clar/resources/status/.gitted/objects/aa/27a641456848200fdb7f7c99ba36f8a0952877
+++ b/tests/resources/status/.gitted/objects/aa/27a641456848200fdb7f7c99ba36f8a0952877
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/da/bc8af9bd6e9f5bbe96a176f1a24baf3d1f8916 b/tests/resources/status/.gitted/objects/da/bc8af9bd6e9f5bbe96a176f1a24baf3d1f8916
index 3e3c03c96..3e3c03c96 100644
--- a/tests-clar/resources/status/.gitted/objects/da/bc8af9bd6e9f5bbe96a176f1a24baf3d1f8916
+++ b/tests/resources/status/.gitted/objects/da/bc8af9bd6e9f5bbe96a176f1a24baf3d1f8916
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/e8/ee89e15bbe9b20137715232387b3de5b28972e b/tests/resources/status/.gitted/objects/e8/ee89e15bbe9b20137715232387b3de5b28972e
index cfc2413d5..cfc2413d5 100644
--- a/tests-clar/resources/status/.gitted/objects/e8/ee89e15bbe9b20137715232387b3de5b28972e
+++ b/tests/resources/status/.gitted/objects/e8/ee89e15bbe9b20137715232387b3de5b28972e
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/e9/b9107f290627c04d097733a10055af941f6bca b/tests/resources/status/.gitted/objects/e9/b9107f290627c04d097733a10055af941f6bca
index 1266d3eac..1266d3eac 100644
--- a/tests-clar/resources/status/.gitted/objects/e9/b9107f290627c04d097733a10055af941f6bca
+++ b/tests/resources/status/.gitted/objects/e9/b9107f290627c04d097733a10055af941f6bca
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/ed/062903b8f6f3dccb2fa81117ba6590944ef9bd b/tests/resources/status/.gitted/objects/ed/062903b8f6f3dccb2fa81117ba6590944ef9bd
index 8fa8c1707..8fa8c1707 100644
--- a/tests-clar/resources/status/.gitted/objects/ed/062903b8f6f3dccb2fa81117ba6590944ef9bd
+++ b/tests/resources/status/.gitted/objects/ed/062903b8f6f3dccb2fa81117ba6590944ef9bd
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/refs/heads/master b/tests/resources/status/.gitted/refs/heads/master
index 3e2e2a07a..3e2e2a07a 100644
--- a/tests-clar/resources/status/.gitted/refs/heads/master
+++ b/tests/resources/status/.gitted/refs/heads/master
diff --git a/tests-clar/resources/status/current_file b/tests/resources/status/current_file
index a0de7e0ac..a0de7e0ac 100644
--- a/tests-clar/resources/status/current_file
+++ b/tests/resources/status/current_file
diff --git a/tests-clar/resources/status/ignored_file b/tests/resources/status/ignored_file
index 6a79f808a..6a79f808a 100644
--- a/tests-clar/resources/status/ignored_file
+++ b/tests/resources/status/ignored_file
diff --git a/tests-clar/resources/status/modified_file b/tests/resources/status/modified_file
index 0a5396305..0a5396305 100644
--- a/tests-clar/resources/status/modified_file
+++ b/tests/resources/status/modified_file
diff --git a/tests-clar/resources/status/new_file b/tests/resources/status/new_file
index d4fa8600b..d4fa8600b 100644
--- a/tests-clar/resources/status/new_file
+++ b/tests/resources/status/new_file
diff --git a/tests-clar/resources/status/staged_changes b/tests/resources/status/staged_changes
index 55d316c9b..55d316c9b 100644
--- a/tests-clar/resources/status/staged_changes
+++ b/tests/resources/status/staged_changes
diff --git a/tests-clar/resources/status/staged_changes_modified_file b/tests/resources/status/staged_changes_modified_file
index 011c3440d..011c3440d 100644
--- a/tests-clar/resources/status/staged_changes_modified_file
+++ b/tests/resources/status/staged_changes_modified_file
diff --git a/tests-clar/resources/status/staged_delete_modified_file b/tests/resources/status/staged_delete_modified_file
index dabc8af9b..dabc8af9b 100644
--- a/tests-clar/resources/status/staged_delete_modified_file
+++ b/tests/resources/status/staged_delete_modified_file
diff --git a/tests-clar/resources/status/staged_new_file b/tests/resources/status/staged_new_file
index 529a16e8e..529a16e8e 100644
--- a/tests-clar/resources/status/staged_new_file
+++ b/tests/resources/status/staged_new_file
diff --git a/tests-clar/resources/status/staged_new_file_modified_file b/tests/resources/status/staged_new_file_modified_file
index 8b090c06d..8b090c06d 100644
--- a/tests-clar/resources/status/staged_new_file_modified_file
+++ b/tests/resources/status/staged_new_file_modified_file
diff --git a/tests-clar/resources/status/subdir.txt b/tests/resources/status/subdir.txt
index e8ee89e15..e8ee89e15 100644
--- a/tests-clar/resources/status/subdir.txt
+++ b/tests/resources/status/subdir.txt
diff --git a/tests-clar/resources/status/subdir/current_file b/tests/resources/status/subdir/current_file
index 53ace0d1c..53ace0d1c 100644
--- a/tests-clar/resources/status/subdir/current_file
+++ b/tests/resources/status/subdir/current_file
diff --git a/tests-clar/resources/status/subdir/modified_file b/tests/resources/status/subdir/modified_file
index 57274b75e..57274b75e 100644
--- a/tests-clar/resources/status/subdir/modified_file
+++ b/tests/resources/status/subdir/modified_file
diff --git a/tests-clar/resources/status/subdir/new_file b/tests/resources/status/subdir/new_file
index 80a86a693..80a86a693 100644
--- a/tests-clar/resources/status/subdir/new_file
+++ b/tests/resources/status/subdir/new_file
diff --git a/tests-clar/resources/status/è¿™ b/tests/resources/status/è¿™
index f0ff9a197..f0ff9a197 100644
--- a/tests-clar/resources/status/è¿™
+++ b/tests/resources/status/è¿™
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/HEAD b/tests/resources/submod2/.gitted/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/HEAD
+++ b/tests/resources/submod2/.gitted/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/config b/tests/resources/submod2/.gitted/config
index abc420734..abc420734 100644
--- a/tests-clar/resources/submod2/.gitted/config
+++ b/tests/resources/submod2/.gitted/config
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/description b/tests/resources/submod2/.gitted/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/description
+++ b/tests/resources/submod2/.gitted/description
diff --git a/tests-clar/resources/submod2/.gitted/index b/tests/resources/submod2/.gitted/index
index 0c17e8629..0c17e8629 100644
--- a/tests-clar/resources/submod2/.gitted/index
+++ b/tests/resources/submod2/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/info/exclude b/tests/resources/submod2/.gitted/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/submod2/.gitted/info/exclude
+++ b/tests/resources/submod2/.gitted/info/exclude
diff --git a/tests-clar/resources/submod2/.gitted/logs/HEAD b/tests/resources/submod2/.gitted/logs/HEAD
index 2cf2ca74d..2cf2ca74d 100644
--- a/tests-clar/resources/submod2/.gitted/logs/HEAD
+++ b/tests/resources/submod2/.gitted/logs/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/logs/refs/heads/master b/tests/resources/submod2/.gitted/logs/refs/heads/master
index 2cf2ca74d..2cf2ca74d 100644
--- a/tests-clar/resources/submod2/.gitted/logs/refs/heads/master
+++ b/tests/resources/submod2/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/HEAD b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/HEAD
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/config b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/config
index 2d0583e99..2d0583e99 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/config
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/config
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/description b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/description
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/description
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/index b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/index
index 65140a510..65140a510 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/index
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/index
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/info/exclude b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/info/exclude
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/info/exclude
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/HEAD b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/HEAD
index 53753e7dd..53753e7dd 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/HEAD
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/heads/master
index 53753e7dd..53753e7dd 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/heads/master
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/remotes/origin/HEAD
index 53753e7dd..53753e7dd 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/remotes/origin/HEAD
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
index f4b7094c5..f4b7094c5 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
index 56c845e49..56c845e49 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
index bd179b5f5..bd179b5f5 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/41/bd4bc3df978de695f67ace64c560913da11653
index ccf49bd15..ccf49bd15 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/41/bd4bc3df978de695f67ace64c560913da11653
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
index 53029069a..53029069a 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/5e/4963595a9774b90524d35a807169049de8ccad
index 38c791eba..38c791eba 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/5e/4963595a9774b90524d35a807169049de8ccad
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
index a26d29993..a26d29993 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/73/ba924a80437097795ae839e66e187c55d3babf
index 83d1ba481..83d1ba481 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/73/ba924a80437097795ae839e66e187c55d3babf
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
index 6d27af8a8..6d27af8a8 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/9efbdadaa4a582778d4584385495559ea0994b
index 17458840b..17458840b 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/9efbdadaa4a582778d4584385495559ea0994b
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/9efbdadaa4a582778d4584385495559ea0994b
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
index 83cc29fb1..83cc29fb1 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
index 55bda40ef..55bda40ef 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/packed-refs b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/packed-refs
index 5a4ebc47c..5a4ebc47c 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/packed-refs
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/packed-refs
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/heads/master
index e12c44d7a..e12c44d7a 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/heads/master
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/remotes/origin/HEAD
index 6efe28fff..6efe28fff 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/remotes/origin/HEAD
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_file/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/HEAD
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/config b/tests/resources/submod2/.gitted/modules/sm_changed_file/config
index 10cc2508e..10cc2508e 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/config
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/config
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/description b/tests/resources/submod2/.gitted/modules/sm_changed_file/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/description
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/description
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/index b/tests/resources/submod2/.gitted/modules/sm_changed_file/index
index 6914a3b6e..6914a3b6e 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/index
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/index
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/info/exclude b/tests/resources/submod2/.gitted/modules/sm_changed_file/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/info/exclude
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/info/exclude
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/logs/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_file/logs/HEAD
index e5cb63f8d..e5cb63f8d 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/logs/HEAD
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/logs/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/heads/master
index e5cb63f8d..e5cb63f8d 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/heads/master
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/remotes/origin/HEAD
index e5cb63f8d..e5cb63f8d 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/remotes/origin/HEAD
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
index f4b7094c5..f4b7094c5 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
index 56c845e49..56c845e49 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
index bd179b5f5..bd179b5f5 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/41/bd4bc3df978de695f67ace64c560913da11653
index ccf49bd15..ccf49bd15 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/41/bd4bc3df978de695f67ace64c560913da11653
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
index 53029069a..53029069a 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/5e/4963595a9774b90524d35a807169049de8ccad
index 38c791eba..38c791eba 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/5e/4963595a9774b90524d35a807169049de8ccad
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
index a26d29993..a26d29993 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/73/ba924a80437097795ae839e66e187c55d3babf
index 83d1ba481..83d1ba481 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/73/ba924a80437097795ae839e66e187c55d3babf
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
index 6d27af8a8..6d27af8a8 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b
index 17458840b..17458840b 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
index 83cc29fb1..83cc29fb1 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
index 55bda40ef..55bda40ef 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/packed-refs b/tests/resources/submod2/.gitted/modules/sm_changed_file/packed-refs
index 5a4ebc47c..5a4ebc47c 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/packed-refs
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/packed-refs
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_file/refs/heads/master
index e12c44d7a..e12c44d7a 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/refs/heads/master
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_file/refs/remotes/origin/HEAD
index 6efe28fff..6efe28fff 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/refs/remotes/origin/HEAD
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/COMMIT_EDITMSG b/tests/resources/submod2/.gitted/modules/sm_changed_head/COMMIT_EDITMSG
index 6b8d1e3fc..6b8d1e3fc 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/COMMIT_EDITMSG
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/COMMIT_EDITMSG
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_head/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/HEAD
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/config b/tests/resources/submod2/.gitted/modules/sm_changed_head/config
index 7d002536a..7d002536a 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/config
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/config
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/description b/tests/resources/submod2/.gitted/modules/sm_changed_head/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/description
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/description
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/index b/tests/resources/submod2/.gitted/modules/sm_changed_head/index
index 728fa292f..728fa292f 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/index
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/index
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/info/exclude b/tests/resources/submod2/.gitted/modules/sm_changed_head/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/info/exclude
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/info/exclude
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/logs/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_head/logs/HEAD
index cabdeb2b5..cabdeb2b5 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/logs/HEAD
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/logs/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/heads/master
index cabdeb2b5..cabdeb2b5 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/heads/master
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/remotes/origin/HEAD
index 257ca21d1..257ca21d1 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/remotes/origin/HEAD
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
index f4b7094c5..f4b7094c5 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
index 56c845e49..56c845e49 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
index bd179b5f5..bd179b5f5 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/3d/9386c507f6b093471a3e324085657a3c2b4247 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/3d/9386c507f6b093471a3e324085657a3c2b4247
index a2c371642..a2c371642 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/3d/9386c507f6b093471a3e324085657a3c2b4247
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/3d/9386c507f6b093471a3e324085657a3c2b4247
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/41/bd4bc3df978de695f67ace64c560913da11653
index ccf49bd15..ccf49bd15 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/41/bd4bc3df978de695f67ace64c560913da11653
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
index 53029069a..53029069a 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/5e/4963595a9774b90524d35a807169049de8ccad
index 38c791eba..38c791eba 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/5e/4963595a9774b90524d35a807169049de8ccad
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
index a26d29993..a26d29993 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/73/ba924a80437097795ae839e66e187c55d3babf
index 83d1ba481..83d1ba481 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/73/ba924a80437097795ae839e66e187c55d3babf
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/77/fb0ed3e58568d6ad362c78de08ab8649d76e29 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/77/fb0ed3e58568d6ad362c78de08ab8649d76e29
index f8a236f3d..f8a236f3d 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/77/fb0ed3e58568d6ad362c78de08ab8649d76e29
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/77/fb0ed3e58568d6ad362c78de08ab8649d76e29
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
index 6d27af8a8..6d27af8a8 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/78/9efbdadaa4a582778d4584385495559ea0994b
index 17458840b..17458840b 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/78/9efbdadaa4a582778d4584385495559ea0994b
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/78/9efbdadaa4a582778d4584385495559ea0994b
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
index 83cc29fb1..83cc29fb1 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/8e/b1e637ed9fc8e5454fa20d38f809091f9395f4 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/8e/b1e637ed9fc8e5454fa20d38f809091f9395f4
index 8155b3e87..8155b3e87 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/8e/b1e637ed9fc8e5454fa20d38f809091f9395f4
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/8e/b1e637ed9fc8e5454fa20d38f809091f9395f4
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
index 55bda40ef..55bda40ef 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/packed-refs b/tests/resources/submod2/.gitted/modules/sm_changed_head/packed-refs
index 5a4ebc47c..5a4ebc47c 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/packed-refs
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/packed-refs
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_head/refs/heads/master
index ae079bd79..ae079bd79 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/refs/heads/master
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_head/refs/remotes/origin/HEAD
index 6efe28fff..6efe28fff 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/refs/remotes/origin/HEAD
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_index/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/HEAD
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/config b/tests/resources/submod2/.gitted/modules/sm_changed_index/config
index 0274ff7e3..0274ff7e3 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/config
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/config
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/description b/tests/resources/submod2/.gitted/modules/sm_changed_index/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/description
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/description
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/index b/tests/resources/submod2/.gitted/modules/sm_changed_index/index
index 6fad3b43e..6fad3b43e 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/index
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/index
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/info/exclude b/tests/resources/submod2/.gitted/modules/sm_changed_index/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/info/exclude
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/info/exclude
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/logs/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_index/logs/HEAD
index 80eb54102..80eb54102 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/logs/HEAD
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/logs/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/heads/master
index 80eb54102..80eb54102 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/heads/master
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/remotes/origin/HEAD
index 80eb54102..80eb54102 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/remotes/origin/HEAD
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
index f4b7094c5..f4b7094c5 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
index 56c845e49..56c845e49 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
index bd179b5f5..bd179b5f5 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/41/bd4bc3df978de695f67ace64c560913da11653
index ccf49bd15..ccf49bd15 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/41/bd4bc3df978de695f67ace64c560913da11653
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
index 53029069a..53029069a 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/5e/4963595a9774b90524d35a807169049de8ccad
index 38c791eba..38c791eba 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/5e/4963595a9774b90524d35a807169049de8ccad
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
index a26d29993..a26d29993 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/73/ba924a80437097795ae839e66e187c55d3babf
index 83d1ba481..83d1ba481 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/73/ba924a80437097795ae839e66e187c55d3babf
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
index 6d27af8a8..6d27af8a8 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/78/9efbdadaa4a582778d4584385495559ea0994b
index 17458840b..17458840b 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/78/9efbdadaa4a582778d4584385495559ea0994b
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/78/9efbdadaa4a582778d4584385495559ea0994b
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
index 83cc29fb1..83cc29fb1 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/a0/2d31770687965547ab7a04cee199b29ee458d6 b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/a0/2d31770687965547ab7a04cee199b29ee458d6
index cb3f5a002..cb3f5a002 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/a0/2d31770687965547ab7a04cee199b29ee458d6
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/a0/2d31770687965547ab7a04cee199b29ee458d6
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
index 55bda40ef..55bda40ef 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/packed-refs b/tests/resources/submod2/.gitted/modules/sm_changed_index/packed-refs
index 5a4ebc47c..5a4ebc47c 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/packed-refs
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/packed-refs
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_index/refs/heads/master
index e12c44d7a..e12c44d7a 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/refs/heads/master
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_index/refs/remotes/origin/HEAD
index 6efe28fff..6efe28fff 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/refs/remotes/origin/HEAD
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/HEAD
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/config b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/config
index 7f2584476..7f2584476 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/config
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/config
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/description b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/description
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/description
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/index b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/index
index 598e30a32..598e30a32 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/index
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/index
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/info/exclude b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/info/exclude
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/info/exclude
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/HEAD
index d1beafbd6..d1beafbd6 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/HEAD
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/heads/master
index d1beafbd6..d1beafbd6 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/heads/master
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/remotes/origin/HEAD
index d1beafbd6..d1beafbd6 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/remotes/origin/HEAD
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
index f4b7094c5..f4b7094c5 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
index 56c845e49..56c845e49 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
index bd179b5f5..bd179b5f5 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/41/bd4bc3df978de695f67ace64c560913da11653
index ccf49bd15..ccf49bd15 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/41/bd4bc3df978de695f67ace64c560913da11653
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
index 53029069a..53029069a 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/5e/4963595a9774b90524d35a807169049de8ccad
index 38c791eba..38c791eba 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/5e/4963595a9774b90524d35a807169049de8ccad
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
index a26d29993..a26d29993 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/73/ba924a80437097795ae839e66e187c55d3babf
index 83d1ba481..83d1ba481 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/73/ba924a80437097795ae839e66e187c55d3babf
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
index 6d27af8a8..6d27af8a8 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b
index 17458840b..17458840b 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
index 83cc29fb1..83cc29fb1 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
index 55bda40ef..55bda40ef 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/packed-refs b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/packed-refs
index 5a4ebc47c..5a4ebc47c 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/packed-refs
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/packed-refs
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/heads/master
index e12c44d7a..e12c44d7a 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/heads/master
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/remotes/origin/HEAD
index 6efe28fff..6efe28fff 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/remotes/origin/HEAD
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/HEAD b/tests/resources/submod2/.gitted/modules/sm_missing_commits/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/HEAD
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/config b/tests/resources/submod2/.gitted/modules/sm_missing_commits/config
index 45fbb30cf..45fbb30cf 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/config
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/config
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/description b/tests/resources/submod2/.gitted/modules/sm_missing_commits/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/description
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/description
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/index b/tests/resources/submod2/.gitted/modules/sm_missing_commits/index
index 490356524..490356524 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/index
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/index
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/info/exclude b/tests/resources/submod2/.gitted/modules/sm_missing_commits/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/info/exclude
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/info/exclude
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/logs/HEAD b/tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/HEAD
index ee08c9706..ee08c9706 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/logs/HEAD
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/heads/master
index ee08c9706..ee08c9706 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/heads/master
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/remotes/origin/HEAD
index ee08c9706..ee08c9706 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/remotes/origin/HEAD
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
index f4b7094c5..f4b7094c5 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
index 56c845e49..56c845e49 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
index bd179b5f5..bd179b5f5 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/41/bd4bc3df978de695f67ace64c560913da11653
index ccf49bd15..ccf49bd15 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/41/bd4bc3df978de695f67ace64c560913da11653
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/5e/4963595a9774b90524d35a807169049de8ccad
index 38c791eba..38c791eba 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/5e/4963595a9774b90524d35a807169049de8ccad
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
index a26d29993..a26d29993 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
index 6d27af8a8..6d27af8a8 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
index 83cc29fb1..83cc29fb1 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
index 55bda40ef..55bda40ef 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/packed-refs b/tests/resources/submod2/.gitted/modules/sm_missing_commits/packed-refs
index 66fbf5daf..66fbf5daf 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/packed-refs
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/packed-refs
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_missing_commits/refs/heads/master
index 3913aca5d..3913aca5d 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/refs/heads/master
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_missing_commits/refs/remotes/origin/HEAD
index 6efe28fff..6efe28fff 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/refs/remotes/origin/HEAD
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/submod2/not-submodule/.gitted/HEAD b/tests/resources/submod2/.gitted/modules/sm_unchanged/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/submod2/not-submodule/.gitted/HEAD
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/config b/tests/resources/submod2/.gitted/modules/sm_unchanged/config
index fc706c9dd..fc706c9dd 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/config
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/config
diff --git a/tests-clar/resources/submod2/not-submodule/.gitted/description b/tests/resources/submod2/.gitted/modules/sm_unchanged/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/submod2/not-submodule/.gitted/description
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/description
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/index b/tests/resources/submod2/.gitted/modules/sm_unchanged/index
index 629c849ec..629c849ec 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/index
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/index
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/info/exclude b/tests/resources/submod2/.gitted/modules/sm_unchanged/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/info/exclude
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/info/exclude
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/logs/HEAD b/tests/resources/submod2/.gitted/modules/sm_unchanged/logs/HEAD
index 72653286a..72653286a 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/logs/HEAD
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/logs/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/heads/master
index 72653286a..72653286a 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/heads/master
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/remotes/origin/HEAD
index 72653286a..72653286a 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/remotes/origin/HEAD
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
index f4b7094c5..f4b7094c5 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
index 56c845e49..56c845e49 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
index bd179b5f5..bd179b5f5 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/41/bd4bc3df978de695f67ace64c560913da11653
index ccf49bd15..ccf49bd15 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/41/bd4bc3df978de695f67ace64c560913da11653
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
index 53029069a..53029069a 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/5e/4963595a9774b90524d35a807169049de8ccad
index 38c791eba..38c791eba 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/5e/4963595a9774b90524d35a807169049de8ccad
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
index a26d29993..a26d29993 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/73/ba924a80437097795ae839e66e187c55d3babf
index 83d1ba481..83d1ba481 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/73/ba924a80437097795ae839e66e187c55d3babf
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
index 6d27af8a8..6d27af8a8 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/78/9efbdadaa4a582778d4584385495559ea0994b
index 17458840b..17458840b 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/78/9efbdadaa4a582778d4584385495559ea0994b
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/78/9efbdadaa4a582778d4584385495559ea0994b
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
index 83cc29fb1..83cc29fb1 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
index 55bda40ef..55bda40ef 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/packed-refs b/tests/resources/submod2/.gitted/modules/sm_unchanged/packed-refs
index 5a4ebc47c..5a4ebc47c 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/packed-refs
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/packed-refs
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_unchanged/refs/heads/master
index e12c44d7a..e12c44d7a 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/refs/heads/master
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_unchanged/refs/remotes/origin/HEAD
index 6efe28fff..6efe28fff 100644
--- a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/refs/remotes/origin/HEAD
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/objects/09/460e5b6cbcb05a3e404593c32a3aa7221eca0e b/tests/resources/submod2/.gitted/objects/09/460e5b6cbcb05a3e404593c32a3aa7221eca0e
index f1ea5f4c8..f1ea5f4c8 100644
--- a/tests-clar/resources/submod2/.gitted/objects/09/460e5b6cbcb05a3e404593c32a3aa7221eca0e
+++ b/tests/resources/submod2/.gitted/objects/09/460e5b6cbcb05a3e404593c32a3aa7221eca0e
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/14/fe9ccf104058df25e0a08361c4494e167ef243 b/tests/resources/submod2/.gitted/objects/14/fe9ccf104058df25e0a08361c4494e167ef243
index d3c8582e3..d3c8582e3 100644
--- a/tests-clar/resources/submod2/.gitted/objects/14/fe9ccf104058df25e0a08361c4494e167ef243
+++ b/tests/resources/submod2/.gitted/objects/14/fe9ccf104058df25e0a08361c4494e167ef243
diff --git a/tests-clar/resources/submod2/.gitted/objects/22/ce3e0311dda73a5992d54a4a595518d3876ea7 b/tests/resources/submod2/.gitted/objects/22/ce3e0311dda73a5992d54a4a595518d3876ea7
index fce6a94b5..fce6a94b5 100644
--- a/tests-clar/resources/submod2/.gitted/objects/22/ce3e0311dda73a5992d54a4a595518d3876ea7
+++ b/tests/resources/submod2/.gitted/objects/22/ce3e0311dda73a5992d54a4a595518d3876ea7
diff --git a/tests-clar/resources/submod2/.gitted/objects/25/5546424b0efb847b1bfc91dbf7348b277f8970 b/tests/resources/submod2/.gitted/objects/25/5546424b0efb847b1bfc91dbf7348b277f8970
index 2965becf6..2965becf6 100644
--- a/tests-clar/resources/submod2/.gitted/objects/25/5546424b0efb847b1bfc91dbf7348b277f8970
+++ b/tests/resources/submod2/.gitted/objects/25/5546424b0efb847b1bfc91dbf7348b277f8970
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/2a/30f1e6f94b20917005a21273f65b406d0f8bad b/tests/resources/submod2/.gitted/objects/2a/30f1e6f94b20917005a21273f65b406d0f8bad
index 08faf0fa8..08faf0fa8 100644
--- a/tests-clar/resources/submod2/.gitted/objects/2a/30f1e6f94b20917005a21273f65b406d0f8bad
+++ b/tests/resources/submod2/.gitted/objects/2a/30f1e6f94b20917005a21273f65b406d0f8bad
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/42/cfb95cd01bf9225b659b5ee3edcc78e8eeb478 b/tests/resources/submod2/.gitted/objects/42/cfb95cd01bf9225b659b5ee3edcc78e8eeb478
index ee7848ae6..ee7848ae6 100644
--- a/tests-clar/resources/submod2/.gitted/objects/42/cfb95cd01bf9225b659b5ee3edcc78e8eeb478
+++ b/tests/resources/submod2/.gitted/objects/42/cfb95cd01bf9225b659b5ee3edcc78e8eeb478
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/57/958699c2dc394f81cfc76950e9c3ac3025c398 b/tests/resources/submod2/.gitted/objects/57/958699c2dc394f81cfc76950e9c3ac3025c398
index ca9203a6e..ca9203a6e 100644
--- a/tests-clar/resources/submod2/.gitted/objects/57/958699c2dc394f81cfc76950e9c3ac3025c398
+++ b/tests/resources/submod2/.gitted/objects/57/958699c2dc394f81cfc76950e9c3ac3025c398
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/59/01da4f1c67756eeadc5121d206bec2431f253b b/tests/resources/submod2/.gitted/objects/59/01da4f1c67756eeadc5121d206bec2431f253b
index 9f88f6bdf..9f88f6bdf 100644
--- a/tests-clar/resources/submod2/.gitted/objects/59/01da4f1c67756eeadc5121d206bec2431f253b
+++ b/tests/resources/submod2/.gitted/objects/59/01da4f1c67756eeadc5121d206bec2431f253b
diff --git a/tests-clar/resources/submod2/.gitted/objects/60/7d96653d4d0a4f733107f7890c2e67b55b620d b/tests/resources/submod2/.gitted/objects/60/7d96653d4d0a4f733107f7890c2e67b55b620d
index 30bee40e9..30bee40e9 100644
--- a/tests-clar/resources/submod2/.gitted/objects/60/7d96653d4d0a4f733107f7890c2e67b55b620d
+++ b/tests/resources/submod2/.gitted/objects/60/7d96653d4d0a4f733107f7890c2e67b55b620d
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/74/84482eb8db738cafa696993664607500a3f2b9 b/tests/resources/submod2/.gitted/objects/74/84482eb8db738cafa696993664607500a3f2b9
index 79018042d..79018042d 100644
--- a/tests-clar/resources/submod2/.gitted/objects/74/84482eb8db738cafa696993664607500a3f2b9
+++ b/tests/resources/submod2/.gitted/objects/74/84482eb8db738cafa696993664607500a3f2b9
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/7b/a4c5c3561daa5ab1a86215cfb0587e96d404d6 b/tests/resources/submod2/.gitted/objects/7b/a4c5c3561daa5ab1a86215cfb0587e96d404d6
index cde89e5bb..cde89e5bb 100644
--- a/tests-clar/resources/submod2/.gitted/objects/7b/a4c5c3561daa5ab1a86215cfb0587e96d404d6
+++ b/tests/resources/submod2/.gitted/objects/7b/a4c5c3561daa5ab1a86215cfb0587e96d404d6
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/87/3585b94bdeabccea991ea5e3ec1a277895b698 b/tests/resources/submod2/.gitted/objects/87/3585b94bdeabccea991ea5e3ec1a277895b698
index 41af98aa9..41af98aa9 100644
--- a/tests-clar/resources/submod2/.gitted/objects/87/3585b94bdeabccea991ea5e3ec1a277895b698
+++ b/tests/resources/submod2/.gitted/objects/87/3585b94bdeabccea991ea5e3ec1a277895b698
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/97/4cf7c73de336b0c4e019f918f3cee367d72e84 b/tests/resources/submod2/.gitted/objects/97/4cf7c73de336b0c4e019f918f3cee367d72e84
index 160f1caf4..160f1caf4 100644
--- a/tests-clar/resources/submod2/.gitted/objects/97/4cf7c73de336b0c4e019f918f3cee367d72e84
+++ b/tests/resources/submod2/.gitted/objects/97/4cf7c73de336b0c4e019f918f3cee367d72e84
diff --git a/tests-clar/resources/submod2/.gitted/objects/9d/bc299bc013ea253583b40bf327b5a6e4037b89 b/tests/resources/submod2/.gitted/objects/9d/bc299bc013ea253583b40bf327b5a6e4037b89
index 1ee52218d..1ee52218d 100644
--- a/tests-clar/resources/submod2/.gitted/objects/9d/bc299bc013ea253583b40bf327b5a6e4037b89
+++ b/tests/resources/submod2/.gitted/objects/9d/bc299bc013ea253583b40bf327b5a6e4037b89
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/a9/104bf89e911387244ef499413960ba472066d9 b/tests/resources/submod2/.gitted/objects/a9/104bf89e911387244ef499413960ba472066d9
index 2239e14a8..2239e14a8 100644
--- a/tests-clar/resources/submod2/.gitted/objects/a9/104bf89e911387244ef499413960ba472066d9
+++ b/tests/resources/submod2/.gitted/objects/a9/104bf89e911387244ef499413960ba472066d9
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/b6/14088620bbdc1d29549d223ceba0f4419fd4cb b/tests/resources/submod2/.gitted/objects/b6/14088620bbdc1d29549d223ceba0f4419fd4cb
index a03ea66e4..a03ea66e4 100644
--- a/tests-clar/resources/submod2/.gitted/objects/b6/14088620bbdc1d29549d223ceba0f4419fd4cb
+++ b/tests/resources/submod2/.gitted/objects/b6/14088620bbdc1d29549d223ceba0f4419fd4cb
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/d4/07f19e50c1da1ff584beafe0d6dac7237c5d06 b/tests/resources/submod2/.gitted/objects/d4/07f19e50c1da1ff584beafe0d6dac7237c5d06
index 292303eb9..292303eb9 100644
--- a/tests-clar/resources/submod2/.gitted/objects/d4/07f19e50c1da1ff584beafe0d6dac7237c5d06
+++ b/tests/resources/submod2/.gitted/objects/d4/07f19e50c1da1ff584beafe0d6dac7237c5d06
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/d9/3e95571d92cceb5de28c205f1d5f3cc8b88bc8 b/tests/resources/submod2/.gitted/objects/d9/3e95571d92cceb5de28c205f1d5f3cc8b88bc8
index b92c7eebd..b92c7eebd 100644
--- a/tests-clar/resources/submod2/.gitted/objects/d9/3e95571d92cceb5de28c205f1d5f3cc8b88bc8
+++ b/tests/resources/submod2/.gitted/objects/d9/3e95571d92cceb5de28c205f1d5f3cc8b88bc8
diff --git a/tests-clar/resources/submod2/.gitted/objects/e3/b83bf274ee065eee48734cf8c6dfaf5e81471c b/tests/resources/submod2/.gitted/objects/e3/b83bf274ee065eee48734cf8c6dfaf5e81471c
index 3c7750b12..3c7750b12 100644
--- a/tests-clar/resources/submod2/.gitted/objects/e3/b83bf274ee065eee48734cf8c6dfaf5e81471c
+++ b/tests/resources/submod2/.gitted/objects/e3/b83bf274ee065eee48734cf8c6dfaf5e81471c
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/f5/4414c25e6d24fe39f5c3f128d7c8a17bc23833 b/tests/resources/submod2/.gitted/objects/f5/4414c25e6d24fe39f5c3f128d7c8a17bc23833
index 219620b25..219620b25 100644
--- a/tests-clar/resources/submod2/.gitted/objects/f5/4414c25e6d24fe39f5c3f128d7c8a17bc23833
+++ b/tests/resources/submod2/.gitted/objects/f5/4414c25e6d24fe39f5c3f128d7c8a17bc23833
diff --git a/tests-clar/resources/submod2/.gitted/objects/f9/90a25a74d1a8281ce2ab018ea8df66795cd60b b/tests/resources/submod2/.gitted/objects/f9/90a25a74d1a8281ce2ab018ea8df66795cd60b
index 883a40bfc..883a40bfc 100644
--- a/tests-clar/resources/submod2/.gitted/objects/f9/90a25a74d1a8281ce2ab018ea8df66795cd60b
+++ b/tests/resources/submod2/.gitted/objects/f9/90a25a74d1a8281ce2ab018ea8df66795cd60b
diff --git a/tests-clar/resources/submod2/.gitted/refs/heads/master b/tests/resources/submod2/.gitted/refs/heads/master
index d1d38aa49..d1d38aa49 100644
--- a/tests-clar/resources/submod2/.gitted/refs/heads/master
+++ b/tests/resources/submod2/.gitted/refs/heads/master
diff --git a/tests-clar/resources/submod2/README.txt b/tests/resources/submod2/README.txt
index f990a25a7..f990a25a7 100644
--- a/tests-clar/resources/submod2/README.txt
+++ b/tests/resources/submod2/README.txt
diff --git a/tests-clar/resources/submod2/gitmodules b/tests/resources/submod2/gitmodules
index 4c31108ed..4c31108ed 100644
--- a/tests-clar/resources/submod2/gitmodules
+++ b/tests/resources/submod2/gitmodules
diff --git a/tests-clar/resources/submod2/just_a_dir/contents b/tests/resources/submod2/just_a_dir/contents
index 7ba4c5c35..7ba4c5c35 100644
--- a/tests-clar/resources/submod2/just_a_dir/contents
+++ b/tests/resources/submod2/just_a_dir/contents
diff --git a/tests-clar/resources/submod2/just_a_file b/tests/resources/submod2/just_a_file
index 42cfb95cd..42cfb95cd 100644
--- a/tests-clar/resources/submod2/just_a_file
+++ b/tests/resources/submod2/just_a_file
diff --git a/tests-clar/resources/submod2_target/.gitted/HEAD b/tests/resources/submod2/not-submodule/.gitted/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/submod2_target/.gitted/HEAD
+++ b/tests/resources/submod2/not-submodule/.gitted/HEAD
diff --git a/tests-clar/resources/submod2/not-submodule/.gitted/config b/tests/resources/submod2/not-submodule/.gitted/config
index af107929f..af107929f 100644
--- a/tests-clar/resources/submod2/not-submodule/.gitted/config
+++ b/tests/resources/submod2/not-submodule/.gitted/config
diff --git a/tests-clar/resources/submod2_target/.gitted/description b/tests/resources/submod2/not-submodule/.gitted/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/submod2_target/.gitted/description
+++ b/tests/resources/submod2/not-submodule/.gitted/description
diff --git a/tests-clar/resources/submod2/not-submodule/.gitted/index b/tests/resources/submod2/not-submodule/.gitted/index
index f3fafa536..f3fafa536 100644
--- a/tests-clar/resources/submod2/not-submodule/.gitted/index
+++ b/tests/resources/submod2/not-submodule/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/submod2/not-submodule/.gitted/info/exclude b/tests/resources/submod2/not-submodule/.gitted/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/submod2/not-submodule/.gitted/info/exclude
+++ b/tests/resources/submod2/not-submodule/.gitted/info/exclude
diff --git a/tests-clar/resources/submod2/not-submodule/.gitted/logs/HEAD b/tests/resources/submod2/not-submodule/.gitted/logs/HEAD
index 1749e7dff..1749e7dff 100644
--- a/tests-clar/resources/submod2/not-submodule/.gitted/logs/HEAD
+++ b/tests/resources/submod2/not-submodule/.gitted/logs/HEAD
diff --git a/tests-clar/resources/submod2/not-submodule/.gitted/logs/refs/heads/master b/tests/resources/submod2/not-submodule/.gitted/logs/refs/heads/master
index 1749e7dff..1749e7dff 100644
--- a/tests-clar/resources/submod2/not-submodule/.gitted/logs/refs/heads/master
+++ b/tests/resources/submod2/not-submodule/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/submod2/not-submodule/.gitted/objects/68/e92c611b80ee1ed8f38314ff9577f0d15b2444 b/tests/resources/submod2/not-submodule/.gitted/objects/68/e92c611b80ee1ed8f38314ff9577f0d15b2444
index 8892531a7..8892531a7 100644
--- a/tests-clar/resources/submod2/not-submodule/.gitted/objects/68/e92c611b80ee1ed8f38314ff9577f0d15b2444
+++ b/tests/resources/submod2/not-submodule/.gitted/objects/68/e92c611b80ee1ed8f38314ff9577f0d15b2444
Binary files differ
diff --git a/tests-clar/resources/submod2/not-submodule/.gitted/objects/71/ff9927d7c8a5639e062c38a7d35c433c424627 b/tests/resources/submod2/not-submodule/.gitted/objects/71/ff9927d7c8a5639e062c38a7d35c433c424627
index c4e1a77d7..c4e1a77d7 100644
--- a/tests-clar/resources/submod2/not-submodule/.gitted/objects/71/ff9927d7c8a5639e062c38a7d35c433c424627
+++ b/tests/resources/submod2/not-submodule/.gitted/objects/71/ff9927d7c8a5639e062c38a7d35c433c424627
Binary files differ
diff --git a/tests-clar/resources/submod2/not-submodule/.gitted/objects/f0/1d56b18efd353ef2bb93a4585d590a0847195e b/tests/resources/submod2/not-submodule/.gitted/objects/f0/1d56b18efd353ef2bb93a4585d590a0847195e
index e9f1942a9..e9f1942a9 100644
--- a/tests-clar/resources/submod2/not-submodule/.gitted/objects/f0/1d56b18efd353ef2bb93a4585d590a0847195e
+++ b/tests/resources/submod2/not-submodule/.gitted/objects/f0/1d56b18efd353ef2bb93a4585d590a0847195e
Binary files differ
diff --git a/tests-clar/resources/submod2/not-submodule/.gitted/refs/heads/master b/tests/resources/submod2/not-submodule/.gitted/refs/heads/master
index 0bd8514bd..0bd8514bd 100644
--- a/tests-clar/resources/submod2/not-submodule/.gitted/refs/heads/master
+++ b/tests/resources/submod2/not-submodule/.gitted/refs/heads/master
diff --git a/tests-clar/resources/submod2/not-submodule/README.txt b/tests/resources/submod2/not-submodule/README.txt
index 71ff9927d..71ff9927d 100644
--- a/tests-clar/resources/submod2/not-submodule/README.txt
+++ b/tests/resources/submod2/not-submodule/README.txt
diff --git a/tests-clar/resources/submod2/not/.gitted/notempty b/tests/resources/submod2/not/.gitted/notempty
index 9b33ac4e4..9b33ac4e4 100644
--- a/tests-clar/resources/submod2/not/.gitted/notempty
+++ b/tests/resources/submod2/not/.gitted/notempty
diff --git a/tests-clar/resources/submod2/not/README.txt b/tests/resources/submod2/not/README.txt
index 4f6935b98..4f6935b98 100644
--- a/tests-clar/resources/submod2/not/README.txt
+++ b/tests/resources/submod2/not/README.txt
diff --git a/tests-clar/resources/submod2/sm_added_and_uncommited/.gitted b/tests/resources/submod2/sm_added_and_uncommited/.gitted
index 2b2a4cf90..2b2a4cf90 100644
--- a/tests-clar/resources/submod2/sm_added_and_uncommited/.gitted
+++ b/tests/resources/submod2/sm_added_and_uncommited/.gitted
diff --git a/tests-clar/resources/submod2/sm_added_and_uncommited/README.txt b/tests/resources/submod2/sm_added_and_uncommited/README.txt
index 780d7397f..780d7397f 100644
--- a/tests-clar/resources/submod2/sm_added_and_uncommited/README.txt
+++ b/tests/resources/submod2/sm_added_and_uncommited/README.txt
diff --git a/tests-clar/resources/submod2/sm_added_and_uncommited/file_to_modify b/tests/resources/submod2/sm_added_and_uncommited/file_to_modify
index 789efbdad..789efbdad 100644
--- a/tests-clar/resources/submod2/sm_added_and_uncommited/file_to_modify
+++ b/tests/resources/submod2/sm_added_and_uncommited/file_to_modify
diff --git a/tests-clar/resources/submod2/sm_changed_file/.gitted b/tests/resources/submod2/sm_changed_file/.gitted
index dc98b1674..dc98b1674 100644
--- a/tests-clar/resources/submod2/sm_changed_file/.gitted
+++ b/tests/resources/submod2/sm_changed_file/.gitted
diff --git a/tests-clar/resources/submod2/sm_changed_file/README.txt b/tests/resources/submod2/sm_changed_file/README.txt
index 780d7397f..780d7397f 100644
--- a/tests-clar/resources/submod2/sm_changed_file/README.txt
+++ b/tests/resources/submod2/sm_changed_file/README.txt
diff --git a/tests-clar/resources/submod2/sm_changed_file/file_to_modify b/tests/resources/submod2/sm_changed_file/file_to_modify
index e5ba67168..e5ba67168 100644
--- a/tests-clar/resources/submod2/sm_changed_file/file_to_modify
+++ b/tests/resources/submod2/sm_changed_file/file_to_modify
diff --git a/tests-clar/resources/submod2/sm_changed_head/.gitted b/tests/resources/submod2/sm_changed_head/.gitted
index d5419b62d..d5419b62d 100644
--- a/tests-clar/resources/submod2/sm_changed_head/.gitted
+++ b/tests/resources/submod2/sm_changed_head/.gitted
diff --git a/tests-clar/resources/submod2/sm_changed_head/README.txt b/tests/resources/submod2/sm_changed_head/README.txt
index 780d7397f..780d7397f 100644
--- a/tests-clar/resources/submod2/sm_changed_head/README.txt
+++ b/tests/resources/submod2/sm_changed_head/README.txt
diff --git a/tests-clar/resources/submod2/sm_changed_head/file_to_modify b/tests/resources/submod2/sm_changed_head/file_to_modify
index 8eb1e637e..8eb1e637e 100644
--- a/tests-clar/resources/submod2/sm_changed_head/file_to_modify
+++ b/tests/resources/submod2/sm_changed_head/file_to_modify
diff --git a/tests-clar/resources/submod2/sm_changed_index/.gitted b/tests/resources/submod2/sm_changed_index/.gitted
index 2c7a5b271..2c7a5b271 100644
--- a/tests-clar/resources/submod2/sm_changed_index/.gitted
+++ b/tests/resources/submod2/sm_changed_index/.gitted
diff --git a/tests-clar/resources/submod2/sm_changed_index/README.txt b/tests/resources/submod2/sm_changed_index/README.txt
index 780d7397f..780d7397f 100644
--- a/tests-clar/resources/submod2/sm_changed_index/README.txt
+++ b/tests/resources/submod2/sm_changed_index/README.txt
diff --git a/tests-clar/resources/submod2/sm_changed_index/file_to_modify b/tests/resources/submod2/sm_changed_index/file_to_modify
index a02d31770..a02d31770 100644
--- a/tests-clar/resources/submod2/sm_changed_index/file_to_modify
+++ b/tests/resources/submod2/sm_changed_index/file_to_modify
diff --git a/tests-clar/resources/submod2/sm_changed_untracked_file/.gitted b/tests/resources/submod2/sm_changed_untracked_file/.gitted
index 9a1070647..9a1070647 100644
--- a/tests-clar/resources/submod2/sm_changed_untracked_file/.gitted
+++ b/tests/resources/submod2/sm_changed_untracked_file/.gitted
diff --git a/tests-clar/resources/submod2/sm_changed_untracked_file/README.txt b/tests/resources/submod2/sm_changed_untracked_file/README.txt
index 780d7397f..780d7397f 100644
--- a/tests-clar/resources/submod2/sm_changed_untracked_file/README.txt
+++ b/tests/resources/submod2/sm_changed_untracked_file/README.txt
diff --git a/tests-clar/resources/submod2/sm_changed_untracked_file/file_to_modify b/tests/resources/submod2/sm_changed_untracked_file/file_to_modify
index 789efbdad..789efbdad 100644
--- a/tests-clar/resources/submod2/sm_changed_untracked_file/file_to_modify
+++ b/tests/resources/submod2/sm_changed_untracked_file/file_to_modify
diff --git a/tests-clar/resources/submod2/sm_changed_untracked_file/i_am_untracked b/tests/resources/submod2/sm_changed_untracked_file/i_am_untracked
index d2bae6167..d2bae6167 100644
--- a/tests-clar/resources/submod2/sm_changed_untracked_file/i_am_untracked
+++ b/tests/resources/submod2/sm_changed_untracked_file/i_am_untracked
diff --git a/tests-clar/resources/submod2/sm_missing_commits/.gitted b/tests/resources/submod2/sm_missing_commits/.gitted
index 70193be84..70193be84 100644
--- a/tests-clar/resources/submod2/sm_missing_commits/.gitted
+++ b/tests/resources/submod2/sm_missing_commits/.gitted
diff --git a/tests-clar/resources/submod2/sm_missing_commits/README.txt b/tests/resources/submod2/sm_missing_commits/README.txt
index 780d7397f..780d7397f 100644
--- a/tests-clar/resources/submod2/sm_missing_commits/README.txt
+++ b/tests/resources/submod2/sm_missing_commits/README.txt
diff --git a/tests-clar/resources/submod2/sm_missing_commits/file_to_modify b/tests/resources/submod2/sm_missing_commits/file_to_modify
index 8834b635d..8834b635d 100644
--- a/tests-clar/resources/submod2/sm_missing_commits/file_to_modify
+++ b/tests/resources/submod2/sm_missing_commits/file_to_modify
diff --git a/tests-clar/resources/submod2/sm_unchanged/.gitted b/tests/resources/submod2/sm_unchanged/.gitted
index 51a679c80..51a679c80 100644
--- a/tests-clar/resources/submod2/sm_unchanged/.gitted
+++ b/tests/resources/submod2/sm_unchanged/.gitted
diff --git a/tests-clar/resources/submod2/sm_unchanged/README.txt b/tests/resources/submod2/sm_unchanged/README.txt
index 780d7397f..780d7397f 100644
--- a/tests-clar/resources/submod2/sm_unchanged/README.txt
+++ b/tests/resources/submod2/sm_unchanged/README.txt
diff --git a/tests-clar/resources/submod2/sm_unchanged/file_to_modify b/tests/resources/submod2/sm_unchanged/file_to_modify
index 789efbdad..789efbdad 100644
--- a/tests-clar/resources/submod2/sm_unchanged/file_to_modify
+++ b/tests/resources/submod2/sm_unchanged/file_to_modify
diff --git a/tests-clar/resources/submodules/.gitted/HEAD b/tests/resources/submod2_target/.gitted/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/submodules/.gitted/HEAD
+++ b/tests/resources/submod2_target/.gitted/HEAD
diff --git a/tests-clar/resources/submod2_target/.gitted/config b/tests/resources/submod2_target/.gitted/config
index af107929f..af107929f 100644
--- a/tests-clar/resources/submod2_target/.gitted/config
+++ b/tests/resources/submod2_target/.gitted/config
diff --git a/tests-clar/resources/submodules/.gitted/description b/tests/resources/submod2_target/.gitted/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/submodules/.gitted/description
+++ b/tests/resources/submod2_target/.gitted/description
diff --git a/tests-clar/resources/submod2_target/.gitted/index b/tests/resources/submod2_target/.gitted/index
index eb3ff8c10..eb3ff8c10 100644
--- a/tests-clar/resources/submod2_target/.gitted/index
+++ b/tests/resources/submod2_target/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/info/exclude b/tests/resources/submod2_target/.gitted/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/submod2_target/.gitted/info/exclude
+++ b/tests/resources/submod2_target/.gitted/info/exclude
diff --git a/tests-clar/resources/submod2_target/.gitted/logs/HEAD b/tests/resources/submod2_target/.gitted/logs/HEAD
index 0ecd1113f..0ecd1113f 100644
--- a/tests-clar/resources/submod2_target/.gitted/logs/HEAD
+++ b/tests/resources/submod2_target/.gitted/logs/HEAD
diff --git a/tests-clar/resources/submod2_target/.gitted/logs/refs/heads/master b/tests/resources/submod2_target/.gitted/logs/refs/heads/master
index 0ecd1113f..0ecd1113f 100644
--- a/tests-clar/resources/submod2_target/.gitted/logs/refs/heads/master
+++ b/tests/resources/submod2_target/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2_target/.gitted/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
index f4b7094c5..f4b7094c5 100644
--- a/tests-clar/resources/submod2_target/.gitted/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
+++ b/tests/resources/submod2_target/.gitted/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2_target/.gitted/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
index 56c845e49..56c845e49 100644
--- a/tests-clar/resources/submod2_target/.gitted/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
+++ b/tests/resources/submod2_target/.gitted/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2_target/.gitted/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
index bd179b5f5..bd179b5f5 100644
--- a/tests-clar/resources/submod2_target/.gitted/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
+++ b/tests/resources/submod2_target/.gitted/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2_target/.gitted/objects/41/bd4bc3df978de695f67ace64c560913da11653
index ccf49bd15..ccf49bd15 100644
--- a/tests-clar/resources/submod2_target/.gitted/objects/41/bd4bc3df978de695f67ace64c560913da11653
+++ b/tests/resources/submod2_target/.gitted/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod2_target/.gitted/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
index 53029069a..53029069a 100644
--- a/tests-clar/resources/submod2_target/.gitted/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
+++ b/tests/resources/submod2_target/.gitted/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2_target/.gitted/objects/5e/4963595a9774b90524d35a807169049de8ccad
index 38c791eba..38c791eba 100644
--- a/tests-clar/resources/submod2_target/.gitted/objects/5e/4963595a9774b90524d35a807169049de8ccad
+++ b/tests/resources/submod2_target/.gitted/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2_target/.gitted/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
index a26d29993..a26d29993 100644
--- a/tests-clar/resources/submod2_target/.gitted/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
+++ b/tests/resources/submod2_target/.gitted/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod2_target/.gitted/objects/73/ba924a80437097795ae839e66e187c55d3babf
index 83d1ba481..83d1ba481 100644
--- a/tests-clar/resources/submod2_target/.gitted/objects/73/ba924a80437097795ae839e66e187c55d3babf
+++ b/tests/resources/submod2_target/.gitted/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2_target/.gitted/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
index 6d27af8a8..6d27af8a8 100644
--- a/tests-clar/resources/submod2_target/.gitted/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
+++ b/tests/resources/submod2_target/.gitted/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod2_target/.gitted/objects/78/9efbdadaa4a582778d4584385495559ea0994b
index 17458840b..17458840b 100644
--- a/tests-clar/resources/submod2_target/.gitted/objects/78/9efbdadaa4a582778d4584385495559ea0994b
+++ b/tests/resources/submod2_target/.gitted/objects/78/9efbdadaa4a582778d4584385495559ea0994b
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2_target/.gitted/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
index 83cc29fb1..83cc29fb1 100644
--- a/tests-clar/resources/submod2_target/.gitted/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
+++ b/tests/resources/submod2_target/.gitted/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2_target/.gitted/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
index 55bda40ef..55bda40ef 100644
--- a/tests-clar/resources/submod2_target/.gitted/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
+++ b/tests/resources/submod2_target/.gitted/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/refs/heads/master b/tests/resources/submod2_target/.gitted/refs/heads/master
index e12c44d7a..e12c44d7a 100644
--- a/tests-clar/resources/submod2_target/.gitted/refs/heads/master
+++ b/tests/resources/submod2_target/.gitted/refs/heads/master
diff --git a/tests-clar/resources/submod2_target/README.txt b/tests/resources/submod2_target/README.txt
index 780d7397f..780d7397f 100644
--- a/tests-clar/resources/submod2_target/README.txt
+++ b/tests/resources/submod2_target/README.txt
diff --git a/tests-clar/resources/submod2_target/file_to_modify b/tests/resources/submod2_target/file_to_modify
index 789efbdad..789efbdad 100644
--- a/tests-clar/resources/submod2_target/file_to_modify
+++ b/tests/resources/submod2_target/file_to_modify
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/HEAD b/tests/resources/submodules/.gitted/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/HEAD
+++ b/tests/resources/submodules/.gitted/HEAD
diff --git a/tests-clar/resources/submodules/.gitted/config b/tests/resources/submodules/.gitted/config
index af107929f..af107929f 100644
--- a/tests-clar/resources/submodules/.gitted/config
+++ b/tests/resources/submodules/.gitted/config
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/description b/tests/resources/submodules/.gitted/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/description
+++ b/tests/resources/submodules/.gitted/description
diff --git a/tests-clar/resources/submodules/.gitted/index b/tests/resources/submodules/.gitted/index
index 97bf8ef51..97bf8ef51 100644
--- a/tests-clar/resources/submodules/.gitted/index
+++ b/tests/resources/submodules/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/submodules/.gitted/info/exclude b/tests/resources/submodules/.gitted/info/exclude
index dfc411579..dfc411579 100644
--- a/tests-clar/resources/submodules/.gitted/info/exclude
+++ b/tests/resources/submodules/.gitted/info/exclude
diff --git a/tests-clar/resources/submodules/.gitted/info/refs b/tests/resources/submodules/.gitted/info/refs
index ba17abdde..ba17abdde 100644
--- a/tests-clar/resources/submodules/.gitted/info/refs
+++ b/tests/resources/submodules/.gitted/info/refs
diff --git a/tests-clar/resources/submodules/.gitted/logs/HEAD b/tests/resources/submodules/.gitted/logs/HEAD
index 87a7bdafc..87a7bdafc 100644
--- a/tests-clar/resources/submodules/.gitted/logs/HEAD
+++ b/tests/resources/submodules/.gitted/logs/HEAD
diff --git a/tests-clar/resources/submodules/.gitted/logs/refs/heads/master b/tests/resources/submodules/.gitted/logs/refs/heads/master
index 87a7bdafc..87a7bdafc 100644
--- a/tests-clar/resources/submodules/.gitted/logs/refs/heads/master
+++ b/tests/resources/submodules/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/submodules/.gitted/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e b/tests/resources/submodules/.gitted/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e
index 2c3c2cb61..2c3c2cb61 100644
--- a/tests-clar/resources/submodules/.gitted/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e
+++ b/tests/resources/submodules/.gitted/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e
diff --git a/tests-clar/resources/submodules/.gitted/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357 b/tests/resources/submodules/.gitted/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357
index c85fb5512..c85fb5512 100644
--- a/tests-clar/resources/submodules/.gitted/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357
+++ b/tests/resources/submodules/.gitted/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357
Binary files differ
diff --git a/tests-clar/resources/submodules/.gitted/objects/97/896810b3210244a62a82458b8e0819ecfc6850 b/tests/resources/submodules/.gitted/objects/97/896810b3210244a62a82458b8e0819ecfc6850
index 1c8dbdf9f..1c8dbdf9f 100644
--- a/tests-clar/resources/submodules/.gitted/objects/97/896810b3210244a62a82458b8e0819ecfc6850
+++ b/tests/resources/submodules/.gitted/objects/97/896810b3210244a62a82458b8e0819ecfc6850
diff --git a/tests-clar/resources/submodules/.gitted/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888 b/tests/resources/submodules/.gitted/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888
index 3d78bd6be..3d78bd6be 100644
--- a/tests-clar/resources/submodules/.gitted/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888
+++ b/tests/resources/submodules/.gitted/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888
Binary files differ
diff --git a/tests-clar/resources/submodules/.gitted/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818 b/tests/resources/submodules/.gitted/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818
index 6e0b49e86..6e0b49e86 100644
--- a/tests-clar/resources/submodules/.gitted/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818
+++ b/tests/resources/submodules/.gitted/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818
Binary files differ
diff --git a/tests-clar/resources/submodules/.gitted/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae b/tests/resources/submodules/.gitted/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae
index 082a58941..082a58941 100644
--- a/tests-clar/resources/submodules/.gitted/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae
+++ b/tests/resources/submodules/.gitted/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae
Binary files differ
diff --git a/tests-clar/resources/submodules/.gitted/objects/info/packs b/tests/resources/submodules/.gitted/objects/info/packs
index 0785ef698..0785ef698 100644
--- a/tests-clar/resources/submodules/.gitted/objects/info/packs
+++ b/tests/resources/submodules/.gitted/objects/info/packs
diff --git a/tests-clar/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idx b/tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idx
index 810fc3181..810fc3181 100644
--- a/tests-clar/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idx
+++ b/tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idx
Binary files differ
diff --git a/tests-clar/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack b/tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack
index c25c4a73f..c25c4a73f 100644
--- a/tests-clar/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack
+++ b/tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack
Binary files differ
diff --git a/tests-clar/resources/submodules/.gitted/packed-refs b/tests/resources/submodules/.gitted/packed-refs
index a6450691e..a6450691e 100644
--- a/tests-clar/resources/submodules/.gitted/packed-refs
+++ b/tests/resources/submodules/.gitted/packed-refs
diff --git a/tests-clar/resources/submodules/.gitted/refs/heads/master b/tests/resources/submodules/.gitted/refs/heads/master
index 32b935853..32b935853 100644
--- a/tests-clar/resources/submodules/.gitted/refs/heads/master
+++ b/tests/resources/submodules/.gitted/refs/heads/master
diff --git a/tests-clar/resources/submodules/added b/tests/resources/submodules/added
index d5f7fc3f7..d5f7fc3f7 100644
--- a/tests-clar/resources/submodules/added
+++ b/tests/resources/submodules/added
diff --git a/tests/resources/submodules/gitmodules b/tests/resources/submodules/gitmodules
new file mode 100644
index 000000000..2798b696c
--- /dev/null
+++ b/tests/resources/submodules/gitmodules
@@ -0,0 +1,6 @@
+[submodule "testrepo"]
+ path = testrepo
+ url =
+[submodule ""]
+ path = testrepo
+ url = \ No newline at end of file
diff --git a/tests-clar/resources/submodules/ignored b/tests/resources/submodules/ignored
index 092bfb9bd..092bfb9bd 100644
--- a/tests-clar/resources/submodules/ignored
+++ b/tests/resources/submodules/ignored
diff --git a/tests-clar/resources/submodules/modified b/tests/resources/submodules/modified
index 452216e1d..452216e1d 100644
--- a/tests-clar/resources/submodules/modified
+++ b/tests/resources/submodules/modified
diff --git a/tests-clar/resources/testrepo.git/HEAD b/tests/resources/submodules/testrepo/.gitted/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/testrepo.git/HEAD
+++ b/tests/resources/submodules/testrepo/.gitted/HEAD
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/config b/tests/resources/submodules/testrepo/.gitted/config
index d6dcad12b..d6dcad12b 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/config
+++ b/tests/resources/submodules/testrepo/.gitted/config
diff --git a/tests-clar/resources/testrepo2/.gitted/description b/tests/resources/submodules/testrepo/.gitted/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/testrepo2/.gitted/description
+++ b/tests/resources/submodules/testrepo/.gitted/description
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/index b/tests/resources/submodules/testrepo/.gitted/index
index 3eb8d84fe..3eb8d84fe 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/index
+++ b/tests/resources/submodules/testrepo/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/info/exclude b/tests/resources/submodules/testrepo/.gitted/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/info/exclude
+++ b/tests/resources/submodules/testrepo/.gitted/info/exclude
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/logs/HEAD b/tests/resources/submodules/testrepo/.gitted/logs/HEAD
index 147643a30..147643a30 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/logs/HEAD
+++ b/tests/resources/submodules/testrepo/.gitted/logs/HEAD
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/logs/refs/heads/master b/tests/resources/submodules/testrepo/.gitted/logs/refs/heads/master
index 147643a30..147643a30 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/logs/refs/heads/master
+++ b/tests/resources/submodules/testrepo/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/tests/resources/submodules/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
index cedb2a22e..cedb2a22e 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
+++ b/tests/resources/submodules/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/tests/resources/submodules/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
index 93a16f146..93a16f146 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
+++ b/tests/resources/submodules/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd b/tests/resources/submodules/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
index ba0bfb30c..ba0bfb30c 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
+++ b/tests/resources/submodules/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b b/tests/resources/submodules/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
index 225c45734..225c45734 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
+++ b/tests/resources/submodules/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d b/tests/resources/submodules/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
index df40d99af..df40d99af 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
+++ b/tests/resources/submodules/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54 b/tests/resources/submodules/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
index 321eaa867..321eaa867 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
+++ b/tests/resources/submodules/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc b/tests/resources/submodules/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
index 9bb5b623b..9bb5b623b 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
+++ b/tests/resources/submodules/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/submodules/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
index 7ca4ceed5..7ca4ceed5 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
+++ b/tests/resources/submodules/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/tests/resources/submodules/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
index 8953b6cef..8953b6cef 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
+++ b/tests/resources/submodules/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/tests/resources/submodules/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
index c1f22c54f..c1f22c54f 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
+++ b/tests/resources/submodules/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/tests/resources/submodules/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
index 2ef4faa0f..2ef4faa0f 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
+++ b/tests/resources/submodules/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af b/tests/resources/submodules/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
index 716b0c64b..716b0c64b 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
+++ b/tests/resources/submodules/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980 b/tests/resources/submodules/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980
index 23c462f34..23c462f34 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980
+++ b/tests/resources/submodules/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/tests/resources/submodules/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
index 2f9b6b6e3..2f9b6b6e3 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
+++ b/tests/resources/submodules/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/tests/resources/submodules/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
index 5df58dda5..5df58dda5 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
+++ b/tests/resources/submodules/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162 b/tests/resources/submodules/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
index 4cc3f4dff..4cc3f4dff 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
+++ b/tests/resources/submodules/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4 b/tests/resources/submodules/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
index bf7b2bb68..bf7b2bb68 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
+++ b/tests/resources/submodules/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/tests/resources/submodules/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
index a79612435..a79612435 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
+++ b/tests/resources/submodules/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/tests/resources/submodules/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
index f8588696b..f8588696b 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
+++ b/tests/resources/submodules/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750 b/tests/resources/submodules/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
index 29c8e824d..29c8e824d 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
+++ b/tests/resources/submodules/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/tests/resources/submodules/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
index d0d7e736e..d0d7e736e 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
+++ b/tests/resources/submodules/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/tests/resources/submodules/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
index 18a7f61c2..18a7f61c2 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
+++ b/tests/resources/submodules/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12 b/tests/resources/submodules/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
index d95254674..d95254674 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
+++ b/tests/resources/submodules/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 b/tests/resources/submodules/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
index f460f2547..f460f2547 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
+++ b/tests/resources/submodules/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593 b/tests/resources/submodules/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
index f613670e2..f613670e2 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
+++ b/tests/resources/submodules/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 b/tests/resources/submodules/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
index 0817229bc..0817229bc 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
+++ b/tests/resources/submodules/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd b/tests/resources/submodules/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
index 75f541f10..75f541f10 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
+++ b/tests/resources/submodules/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f b/tests/resources/submodules/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
index a67d6e647..a67d6e647 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
+++ b/tests/resources/submodules/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/submodules/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
index 711223894..711223894 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
+++ b/tests/resources/submodules/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0 b/tests/resources/submodules/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
index b135eccda..b135eccda 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
+++ b/tests/resources/submodules/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3 b/tests/resources/submodules/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
index 82e2790e8..82e2790e8 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
+++ b/tests/resources/submodules/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 b/tests/resources/submodules/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
index 697c94c92..697c94c92 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
+++ b/tests/resources/submodules/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/tests/resources/submodules/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
index 112998d42..112998d42 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
+++ b/tests/resources/submodules/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765 b/tests/resources/submodules/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
index 12bf5f3e3..12bf5f3e3 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
+++ b/tests/resources/submodules/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
index 5068f2818..5068f2818 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
+++ b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
index a6a1f3020..a6a1f3020 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
+++ b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
index 94c3c71da..94c3c71da 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
+++ b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
index 74c7fe4f3..74c7fe4f3 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
+++ b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
index 555cfa977..555cfa977 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
+++ b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
index 4d539ed0a..4d539ed0a 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
+++ b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/packed-refs b/tests/resources/submodules/testrepo/.gitted/packed-refs
index 9c0433e1c..9c0433e1c 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/packed-refs
+++ b/tests/resources/submodules/testrepo/.gitted/packed-refs
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/refs/heads/master b/tests/resources/submodules/testrepo/.gitted/refs/heads/master
index 3d8f0a402..3d8f0a402 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/refs/heads/master
+++ b/tests/resources/submodules/testrepo/.gitted/refs/heads/master
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/refs/remotes/origin/HEAD b/tests/resources/submodules/testrepo/.gitted/refs/remotes/origin/HEAD
index 6efe28fff..6efe28fff 100644
--- a/tests-clar/resources/submodules/testrepo/.gitted/refs/remotes/origin/HEAD
+++ b/tests/resources/submodules/testrepo/.gitted/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/submodules/testrepo/README b/tests/resources/submodules/testrepo/README
index a8233120f..a8233120f 100644
--- a/tests-clar/resources/submodules/testrepo/README
+++ b/tests/resources/submodules/testrepo/README
diff --git a/tests-clar/resources/submodules/testrepo/branch_file.txt b/tests/resources/submodules/testrepo/branch_file.txt
index 3697d64be..3697d64be 100644
--- a/tests-clar/resources/submodules/testrepo/branch_file.txt
+++ b/tests/resources/submodules/testrepo/branch_file.txt
diff --git a/tests-clar/resources/submodules/testrepo/new.txt b/tests/resources/submodules/testrepo/new.txt
index a71586c1d..a71586c1d 100644
--- a/tests-clar/resources/submodules/testrepo/new.txt
+++ b/tests/resources/submodules/testrepo/new.txt
diff --git a/tests-clar/resources/submodules/unmodified b/tests/resources/submodules/unmodified
index 092bfb9bd..092bfb9bd 100644
--- a/tests-clar/resources/submodules/unmodified
+++ b/tests/resources/submodules/unmodified
diff --git a/tests-clar/resources/submodules/untracked b/tests/resources/submodules/untracked
index 092bfb9bd..092bfb9bd 100644
--- a/tests-clar/resources/submodules/untracked
+++ b/tests/resources/submodules/untracked
diff --git a/tests-clar/resources/template/branches/.gitignore b/tests/resources/template/branches/.gitignore
index 16868cedb..16868cedb 100644
--- a/tests-clar/resources/template/branches/.gitignore
+++ b/tests/resources/template/branches/.gitignore
diff --git a/tests-clar/resources/template/description b/tests/resources/template/description
index ff04c4c13..ff04c4c13 100644
--- a/tests-clar/resources/template/description
+++ b/tests/resources/template/description
diff --git a/tests-clar/resources/template/hooks/link.sample b/tests/resources/template/hooks/link.sample
index 771acc43b..771acc43b 120000
--- a/tests-clar/resources/template/hooks/link.sample
+++ b/tests/resources/template/hooks/link.sample
diff --git a/tests-clar/resources/template/hooks/update.sample b/tests/resources/template/hooks/update.sample
index 3b5f41202..3b5f41202 100755
--- a/tests-clar/resources/template/hooks/update.sample
+++ b/tests/resources/template/hooks/update.sample
diff --git a/tests-clar/resources/template/info/exclude b/tests/resources/template/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/template/info/exclude
+++ b/tests/resources/template/info/exclude
diff --git a/tests-clar/resources/testrepo.git/FETCH_HEAD b/tests/resources/testrepo.git/FETCH_HEAD
index 48446265c..48446265c 100644
--- a/tests-clar/resources/testrepo.git/FETCH_HEAD
+++ b/tests/resources/testrepo.git/FETCH_HEAD
diff --git a/tests-clar/resources/testrepo/.gitted/HEAD b/tests/resources/testrepo.git/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/testrepo/.gitted/HEAD
+++ b/tests/resources/testrepo.git/HEAD
diff --git a/tests-clar/resources/testrepo.git/HEAD_TRACKER b/tests/resources/testrepo.git/HEAD_TRACKER
index 40d876b4c..40d876b4c 100644
--- a/tests-clar/resources/testrepo.git/HEAD_TRACKER
+++ b/tests/resources/testrepo.git/HEAD_TRACKER
diff --git a/tests/resources/testrepo.git/config b/tests/resources/testrepo.git/config
new file mode 100644
index 000000000..dfab4eeb4
--- /dev/null
+++ b/tests/resources/testrepo.git/config
@@ -0,0 +1,40 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = true
+ logallrefupdates = true
+[remote "test"]
+ url = git://github.com/libgit2/libgit2
+ fetch = +refs/heads/*:refs/remotes/test/*
+[remote "joshaber"]
+ url = git://github.com/libgit2/libgit2
+[remote "empty-remote-url"]
+ url =
+ pushurl =
+[remote "empty-remote-pushurl"]
+ pushurl =
+[remote "no-remote-url"]
+ fetch =
+[remote "test_with_pushurl"]
+ url = git://github.com/libgit2/fetchlibgit2
+ pushurl = git://github.com/libgit2/pushlibgit2
+ fetch = +refs/heads/*:refs/remotes/test_with_pushurl/*
+
+[branch "master"]
+ remote = test
+ merge = refs/heads/master
+[branch "track-local"]
+ remote = .
+ merge = refs/heads/master
+[branch "cannot-fetch"]
+ remote = joshaber
+ merge = refs/heads/cannot-fetch
+[branch "remoteless"]
+ remote =
+ merge = refs/heads/master
+[branch "mergeless"]
+ remote = test
+ merge =
+[branch "mergeandremoteless"]
+ remote =
+ merge =
diff --git a/tests-clar/resources/testrepo.git/index b/tests/resources/testrepo.git/index
index a27fb9c96..a27fb9c96 100644
--- a/tests-clar/resources/testrepo.git/index
+++ b/tests/resources/testrepo.git/index
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/logs/HEAD b/tests/resources/testrepo.git/logs/HEAD
index 9413b72cc..9413b72cc 100644
--- a/tests-clar/resources/testrepo.git/logs/HEAD
+++ b/tests/resources/testrepo.git/logs/HEAD
diff --git a/tests-clar/resources/testrepo.git/logs/refs/heads/br2 b/tests/resources/testrepo.git/logs/refs/heads/br2
index 4e27f6b8d..4e27f6b8d 100644
--- a/tests-clar/resources/testrepo.git/logs/refs/heads/br2
+++ b/tests/resources/testrepo.git/logs/refs/heads/br2
diff --git a/tests-clar/resources/testrepo.git/logs/refs/heads/master b/tests/resources/testrepo.git/logs/refs/heads/master
index e1c729a45..e1c729a45 100644
--- a/tests-clar/resources/testrepo.git/logs/refs/heads/master
+++ b/tests/resources/testrepo.git/logs/refs/heads/master
diff --git a/tests-clar/resources/testrepo.git/logs/refs/heads/not-good b/tests/resources/testrepo.git/logs/refs/heads/not-good
index bfbeacb8a..bfbeacb8a 100644
--- a/tests-clar/resources/testrepo.git/logs/refs/heads/not-good
+++ b/tests/resources/testrepo.git/logs/refs/heads/not-good
diff --git a/tests-clar/resources/testrepo.git/logs/refs/remotes/origin/HEAD b/tests/resources/testrepo.git/logs/refs/remotes/origin/HEAD
index f1aac6d0f..f1aac6d0f 100644
--- a/tests-clar/resources/testrepo.git/logs/refs/remotes/origin/HEAD
+++ b/tests/resources/testrepo.git/logs/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/testrepo.git/logs/refs/remotes/test/master b/tests/resources/testrepo.git/logs/refs/remotes/test/master
index 8d49ba3e0..8d49ba3e0 100644
--- a/tests-clar/resources/testrepo.git/logs/refs/remotes/test/master
+++ b/tests/resources/testrepo.git/logs/refs/remotes/test/master
diff --git a/tests-clar/resources/testrepo.git/objects/08/b041783f40edfe12bb406c9c9a8a040177c125 b/tests/resources/testrepo.git/objects/08/b041783f40edfe12bb406c9c9a8a040177c125
index d1c032fce..d1c032fce 100644
--- a/tests-clar/resources/testrepo.git/objects/08/b041783f40edfe12bb406c9c9a8a040177c125
+++ b/tests/resources/testrepo.git/objects/08/b041783f40edfe12bb406c9c9a8a040177c125
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/tests/resources/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
index cedb2a22e..cedb2a22e 100644
--- a/tests-clar/resources/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
+++ b/tests/resources/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/tests/resources/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
index 93a16f146..93a16f146 100644
--- a/tests-clar/resources/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
+++ b/tests/resources/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccd b/tests/resources/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccd
index ba0bfb30c..ba0bfb30c 100644
--- a/tests-clar/resources/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccd
+++ b/tests/resources/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccd
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd b/tests/resources/testrepo.git/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd
index 3ec541288..3ec541288 100644
--- a/tests-clar/resources/testrepo.git/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd
+++ b/tests/resources/testrepo.git/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/1b/8cbad43e867676df601306689fe7c3def5e689 b/tests/resources/testrepo.git/objects/1b/8cbad43e867676df601306689fe7c3def5e689
index 6048d4bad..6048d4bad 100644
--- a/tests-clar/resources/testrepo.git/objects/1b/8cbad43e867676df601306689fe7c3def5e689
+++ b/tests/resources/testrepo.git/objects/1b/8cbad43e867676df601306689fe7c3def5e689
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b b/tests/resources/testrepo.git/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
index 225c45734..225c45734 100644
--- a/tests-clar/resources/testrepo.git/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
+++ b/tests/resources/testrepo.git/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10 b/tests/resources/testrepo.git/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10
index cb1ed5712..cb1ed5712 100644
--- a/tests-clar/resources/testrepo.git/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10
+++ b/tests/resources/testrepo.git/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d b/tests/resources/testrepo.git/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
index df40d99af..df40d99af 100644
--- a/tests-clar/resources/testrepo.git/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
+++ b/tests/resources/testrepo.git/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7 b/tests/resources/testrepo.git/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7
index 0a1500a6f..0a1500a6f 100644
--- a/tests-clar/resources/testrepo.git/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7
+++ b/tests/resources/testrepo.git/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54 b/tests/resources/testrepo.git/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
index 321eaa867..321eaa867 100644
--- a/tests-clar/resources/testrepo.git/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
+++ b/tests/resources/testrepo.git/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc b/tests/resources/testrepo.git/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
index 9bb5b623b..9bb5b623b 100644
--- a/tests-clar/resources/testrepo.git/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
+++ b/tests/resources/testrepo.git/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
index 7ca4ceed5..7ca4ceed5 100644
--- a/tests-clar/resources/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
+++ b/tests/resources/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/tests/resources/testrepo.git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
index 8953b6cef..8953b6cef 100644
--- a/tests-clar/resources/testrepo.git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
+++ b/tests/resources/testrepo.git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
diff --git a/tests-clar/resources/testrepo.git/objects/4a/23e2e65ad4e31c4c9db7dc746650bfad082679 b/tests/resources/testrepo.git/objects/4a/23e2e65ad4e31c4c9db7dc746650bfad082679
index 18e3964b3..18e3964b3 100644
--- a/tests-clar/resources/testrepo.git/objects/4a/23e2e65ad4e31c4c9db7dc746650bfad082679
+++ b/tests/resources/testrepo.git/objects/4a/23e2e65ad4e31c4c9db7dc746650bfad082679
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea b/tests/resources/testrepo.git/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea
index b4e5aa186..b4e5aa186 100644
--- a/tests-clar/resources/testrepo.git/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea
+++ b/tests/resources/testrepo.git/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91 b/tests/resources/testrepo.git/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91
index 351cff823..351cff823 100644
--- a/tests-clar/resources/testrepo.git/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91
+++ b/tests/resources/testrepo.git/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/tests/resources/testrepo.git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
index c1f22c54f..c1f22c54f 100644
--- a/tests-clar/resources/testrepo.git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
+++ b/tests/resources/testrepo.git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
diff --git a/tests-clar/resources/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/tests/resources/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
index 2ef4faa0f..2ef4faa0f 100644
--- a/tests-clar/resources/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
+++ b/tests/resources/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af b/tests/resources/testrepo.git/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
index 716b0c64b..716b0c64b 100644
--- a/tests-clar/resources/testrepo.git/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
+++ b/tests/resources/testrepo.git/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
diff --git a/tests-clar/resources/testrepo.git/objects/7b/4384978d2493e851f9cca7858815fac9b10980 b/tests/resources/testrepo.git/objects/7b/4384978d2493e851f9cca7858815fac9b10980
index 23c462f34..23c462f34 100644
--- a/tests-clar/resources/testrepo.git/objects/7b/4384978d2493e851f9cca7858815fac9b10980
+++ b/tests/resources/testrepo.git/objects/7b/4384978d2493e851f9cca7858815fac9b10980
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/tests/resources/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
index 2f9b6b6e3..2f9b6b6e3 100644
--- a/tests-clar/resources/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
+++ b/tests/resources/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/tests/resources/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479
index 5df58dda5..5df58dda5 100644
--- a/tests-clar/resources/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479
+++ b/tests/resources/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe b/tests/resources/testrepo.git/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe
index 71019a636..71019a636 100644
--- a/tests-clar/resources/testrepo.git/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe
+++ b/tests/resources/testrepo.git/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe
diff --git a/tests-clar/resources/testrepo.git/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162 b/tests/resources/testrepo.git/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
index 4cc3f4dff..4cc3f4dff 100644
--- a/tests-clar/resources/testrepo.git/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
+++ b/tests/resources/testrepo.git/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
diff --git a/tests-clar/resources/testrepo.git/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4 b/tests/resources/testrepo.git/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
index bf7b2bb68..bf7b2bb68 100644
--- a/tests-clar/resources/testrepo.git/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
+++ b/tests/resources/testrepo.git/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2 b/tests/resources/testrepo.git/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2
index 7f1cfb23c..7f1cfb23c 100644
--- a/tests-clar/resources/testrepo.git/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2
+++ b/tests/resources/testrepo.git/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2
diff --git a/tests-clar/resources/testrepo.git/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/tests/resources/testrepo.git/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
index a79612435..a79612435 100644
--- a/tests-clar/resources/testrepo.git/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
+++ b/tests/resources/testrepo.git/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
diff --git a/tests-clar/resources/testrepo.git/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/tests/resources/testrepo.git/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
index f8588696b..f8588696b 100644
--- a/tests-clar/resources/testrepo.git/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
+++ b/tests/resources/testrepo.git/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
diff --git a/tests-clar/resources/testrepo.git/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750 b/tests/resources/testrepo.git/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
index 29c8e824d..29c8e824d 100644
--- a/tests-clar/resources/testrepo.git/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
+++ b/tests/resources/testrepo.git/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
diff --git a/tests-clar/resources/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/tests/resources/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
index d0d7e736e..d0d7e736e 100644
--- a/tests-clar/resources/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
+++ b/tests/resources/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/tests/resources/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
index 18a7f61c2..18a7f61c2 100644
--- a/tests-clar/resources/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
+++ b/tests/resources/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/ae/90f12eea699729ed24555e40b9fd669da12a12 b/tests/resources/testrepo.git/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
index d95254674..d95254674 100644
--- a/tests-clar/resources/testrepo.git/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
+++ b/tests/resources/testrepo.git/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 b/tests/resources/testrepo.git/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
index f460f2547..f460f2547 100644
--- a/tests-clar/resources/testrepo.git/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
+++ b/tests/resources/testrepo.git/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
diff --git a/tests-clar/resources/testrepo.git/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593 b/tests/resources/testrepo.git/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
index f613670e2..f613670e2 100644
--- a/tests-clar/resources/testrepo.git/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
+++ b/tests/resources/testrepo.git/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 b/tests/resources/testrepo.git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
index 0817229bc..0817229bc 100644
--- a/tests-clar/resources/testrepo.git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
+++ b/tests/resources/testrepo.git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
diff --git a/tests-clar/resources/testrepo.git/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd b/tests/resources/testrepo.git/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
index 75f541f10..75f541f10 100644
--- a/tests-clar/resources/testrepo.git/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
+++ b/tests/resources/testrepo.git/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
diff --git a/tests-clar/resources/testrepo.git/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659 b/tests/resources/testrepo.git/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659
index f3b46b3ca..f3b46b3ca 100644
--- a/tests-clar/resources/testrepo.git/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659
+++ b/tests/resources/testrepo.git/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f b/tests/resources/testrepo.git/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
index a67d6e647..a67d6e647 100644
--- a/tests-clar/resources/testrepo.git/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
+++ b/tests/resources/testrepo.git/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759 b/tests/resources/testrepo.git/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759
index 2d47e6faf..2d47e6faf 100644
--- a/tests-clar/resources/testrepo.git/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759
+++ b/tests/resources/testrepo.git/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
index 711223894..711223894 100644
--- a/tests-clar/resources/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
+++ b/tests/resources/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0 b/tests/resources/testrepo.git/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
index b135eccda..b135eccda 100644
--- a/tests-clar/resources/testrepo.git/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
+++ b/tests/resources/testrepo.git/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3 b/tests/resources/testrepo.git/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
index 82e2790e8..82e2790e8 100644
--- a/tests-clar/resources/testrepo.git/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
+++ b/tests/resources/testrepo.git/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 b/tests/resources/testrepo.git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
index 697c94c92..697c94c92 100644
--- a/tests-clar/resources/testrepo.git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
+++ b/tests/resources/testrepo.git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/tests/resources/testrepo.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92
index 112998d42..112998d42 100644
--- a/tests-clar/resources/testrepo.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92
+++ b/tests/resources/testrepo.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/fd/093bff70906175335656e6ce6ae05783708765 b/tests/resources/testrepo.git/objects/fd/093bff70906175335656e6ce6ae05783708765
index 12bf5f3e3..12bf5f3e3 100644
--- a/tests-clar/resources/testrepo.git/objects/fd/093bff70906175335656e6ce6ae05783708765
+++ b/tests/resources/testrepo.git/objects/fd/093bff70906175335656e6ce6ae05783708765
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09 b/tests/resources/testrepo.git/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09
index 158aef21f..158aef21f 100644
--- a/tests-clar/resources/testrepo.git/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09
+++ b/tests/resources/testrepo.git/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx b/tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
index 5068f2818..5068f2818 100644
--- a/tests-clar/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
+++ b/tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack b/tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
index a6a1f3020..a6a1f3020 100644
--- a/tests-clar/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
+++ b/tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx b/tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
index 94c3c71da..94c3c71da 100644
--- a/tests-clar/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
+++ b/tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack b/tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
index 74c7fe4f3..74c7fe4f3 100644
--- a/tests-clar/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
+++ b/tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx b/tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
index 555cfa977..555cfa977 100644
--- a/tests-clar/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
+++ b/tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack b/tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
index 4d539ed0a..4d539ed0a 100644
--- a/tests-clar/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
+++ b/tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/packed-refs b/tests/resources/testrepo.git/packed-refs
index 52f5e876f..52f5e876f 100644
--- a/tests-clar/resources/testrepo.git/packed-refs
+++ b/tests/resources/testrepo.git/packed-refs
diff --git a/tests-clar/resources/testrepo.git/refs/heads/br2 b/tests/resources/testrepo.git/refs/heads/br2
index aab87e5e7..aab87e5e7 100644
--- a/tests-clar/resources/testrepo.git/refs/heads/br2
+++ b/tests/resources/testrepo.git/refs/heads/br2
diff --git a/tests-clar/resources/testrepo.git/refs/heads/cannot-fetch b/tests/resources/testrepo.git/refs/heads/cannot-fetch
index aab87e5e7..aab87e5e7 100644
--- a/tests-clar/resources/testrepo.git/refs/heads/cannot-fetch
+++ b/tests/resources/testrepo.git/refs/heads/cannot-fetch
diff --git a/tests-clar/resources/testrepo.git/refs/heads/chomped b/tests/resources/testrepo.git/refs/heads/chomped
index 0166a7f92..0166a7f92 100644
--- a/tests-clar/resources/testrepo.git/refs/heads/chomped
+++ b/tests/resources/testrepo.git/refs/heads/chomped
diff --git a/tests-clar/resources/testrepo.git/refs/heads/haacked b/tests/resources/testrepo.git/refs/heads/haacked
index 17f591222..17f591222 100644
--- a/tests-clar/resources/testrepo.git/refs/heads/haacked
+++ b/tests/resources/testrepo.git/refs/heads/haacked
diff --git a/tests-clar/resources/testrepo.git/refs/heads/master b/tests/resources/testrepo.git/refs/heads/master
index 3d8f0a402..3d8f0a402 100644
--- a/tests-clar/resources/testrepo.git/refs/heads/master
+++ b/tests/resources/testrepo.git/refs/heads/master
diff --git a/tests-clar/resources/testrepo.git/refs/heads/not-good b/tests/resources/testrepo.git/refs/heads/not-good
index 3d8f0a402..3d8f0a402 100644
--- a/tests-clar/resources/testrepo.git/refs/heads/not-good
+++ b/tests/resources/testrepo.git/refs/heads/not-good
diff --git a/tests-clar/resources/testrepo.git/refs/heads/packed-test b/tests/resources/testrepo.git/refs/heads/packed-test
index f2c14ad83..f2c14ad83 100644
--- a/tests-clar/resources/testrepo.git/refs/heads/packed-test
+++ b/tests/resources/testrepo.git/refs/heads/packed-test
diff --git a/tests-clar/resources/testrepo.git/refs/heads/subtrees b/tests/resources/testrepo.git/refs/heads/subtrees
index ad27e0b13..ad27e0b13 100644
--- a/tests-clar/resources/testrepo.git/refs/heads/subtrees
+++ b/tests/resources/testrepo.git/refs/heads/subtrees
diff --git a/tests-clar/resources/testrepo.git/refs/heads/test b/tests/resources/testrepo.git/refs/heads/test
index 399c4c73e..399c4c73e 100644
--- a/tests-clar/resources/testrepo.git/refs/heads/test
+++ b/tests/resources/testrepo.git/refs/heads/test
diff --git a/tests-clar/resources/testrepo.git/refs/heads/track-local b/tests/resources/testrepo.git/refs/heads/track-local
index f37febb2c..f37febb2c 100644
--- a/tests-clar/resources/testrepo.git/refs/heads/track-local
+++ b/tests/resources/testrepo.git/refs/heads/track-local
diff --git a/tests-clar/resources/testrepo.git/refs/heads/trailing b/tests/resources/testrepo.git/refs/heads/trailing
index 2a4a6e62f..2a4a6e62f 100644
--- a/tests-clar/resources/testrepo.git/refs/heads/trailing
+++ b/tests/resources/testrepo.git/refs/heads/trailing
diff --git a/tests-clar/resources/testrepo.git/refs/notes/fanout b/tests/resources/testrepo.git/refs/notes/fanout
index 1f1703631..1f1703631 100644
--- a/tests-clar/resources/testrepo.git/refs/notes/fanout
+++ b/tests/resources/testrepo.git/refs/notes/fanout
diff --git a/tests-clar/resources/testrepo.git/refs/remotes/test/master b/tests/resources/testrepo.git/refs/remotes/test/master
index 9536ad89c..9536ad89c 100644
--- a/tests-clar/resources/testrepo.git/refs/remotes/test/master
+++ b/tests/resources/testrepo.git/refs/remotes/test/master
diff --git a/tests-clar/resources/testrepo.git/refs/tags/annotated_tag_to_blob b/tests/resources/testrepo.git/refs/tags/annotated_tag_to_blob
index 6c146d6e3..6c146d6e3 100644
--- a/tests-clar/resources/testrepo.git/refs/tags/annotated_tag_to_blob
+++ b/tests/resources/testrepo.git/refs/tags/annotated_tag_to_blob
diff --git a/tests-clar/resources/testrepo.git/refs/tags/e90810b b/tests/resources/testrepo.git/refs/tags/e90810b
index 584495d3c..584495d3c 100644
--- a/tests-clar/resources/testrepo.git/refs/tags/e90810b
+++ b/tests/resources/testrepo.git/refs/tags/e90810b
diff --git a/tests-clar/resources/testrepo.git/refs/tags/hard_tag b/tests/resources/testrepo.git/refs/tags/hard_tag
index 59ce65649..59ce65649 100644
--- a/tests-clar/resources/testrepo.git/refs/tags/hard_tag
+++ b/tests/resources/testrepo.git/refs/tags/hard_tag
diff --git a/tests-clar/resources/testrepo.git/refs/tags/point_to_blob b/tests/resources/testrepo.git/refs/tags/point_to_blob
index f874a3ffc..f874a3ffc 100644
--- a/tests-clar/resources/testrepo.git/refs/tags/point_to_blob
+++ b/tests/resources/testrepo.git/refs/tags/point_to_blob
diff --git a/tests-clar/resources/testrepo.git/refs/tags/taggerless b/tests/resources/testrepo.git/refs/tags/taggerless
index f960c7d62..f960c7d62 100644
--- a/tests-clar/resources/testrepo.git/refs/tags/taggerless
+++ b/tests/resources/testrepo.git/refs/tags/taggerless
diff --git a/tests-clar/resources/testrepo.git/refs/tags/test b/tests/resources/testrepo.git/refs/tags/test
index 6ee952a03..6ee952a03 100644
--- a/tests-clar/resources/testrepo.git/refs/tags/test
+++ b/tests/resources/testrepo.git/refs/tags/test
diff --git a/tests-clar/resources/testrepo.git/refs/tags/wrapped_tag b/tests/resources/testrepo.git/refs/tags/wrapped_tag
index 59ce65649..59ce65649 100644
--- a/tests-clar/resources/testrepo.git/refs/tags/wrapped_tag
+++ b/tests/resources/testrepo.git/refs/tags/wrapped_tag
diff --git a/tests-clar/resources/testrepo2/.gitted/HEAD b/tests/resources/testrepo/.gitted/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/testrepo2/.gitted/HEAD
+++ b/tests/resources/testrepo/.gitted/HEAD
diff --git a/tests-clar/resources/testrepo/.gitted/HEAD_TRACKER b/tests/resources/testrepo/.gitted/HEAD_TRACKER
index 40d876b4c..40d876b4c 100644
--- a/tests-clar/resources/testrepo/.gitted/HEAD_TRACKER
+++ b/tests/resources/testrepo/.gitted/HEAD_TRACKER
diff --git a/tests-clar/resources/testrepo/.gitted/config b/tests/resources/testrepo/.gitted/config
index d0114012f..d0114012f 100644
--- a/tests-clar/resources/testrepo/.gitted/config
+++ b/tests/resources/testrepo/.gitted/config
diff --git a/tests-clar/resources/testrepo/.gitted/index b/tests/resources/testrepo/.gitted/index
index a27fb9c96..a27fb9c96 100644
--- a/tests-clar/resources/testrepo/.gitted/index
+++ b/tests/resources/testrepo/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/09/9fabac3a9ea935598528c27f866e34089c2eff b/tests/resources/testrepo/.gitted/objects/09/9fabac3a9ea935598528c27f866e34089c2eff
index c60c78fb5..c60c78fb5 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/09/9fabac3a9ea935598528c27f866e34089c2eff
+++ b/tests/resources/testrepo/.gitted/objects/09/9fabac3a9ea935598528c27f866e34089c2eff
diff --git a/tests-clar/resources/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/tests/resources/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
index cedb2a22e..cedb2a22e 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
+++ b/tests/resources/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e b/tests/resources/testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e
index b7d944fa1..b7d944fa1 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e
+++ b/tests/resources/testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925 b/tests/resources/testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925
index d37b93e4f..d37b93e4f 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925
+++ b/tests/resources/testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/tests/resources/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
index 93a16f146..93a16f146 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
+++ b/tests/resources/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd b/tests/resources/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
index ba0bfb30c..ba0bfb30c 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
+++ b/tests/resources/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b b/tests/resources/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
index 225c45734..225c45734 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
+++ b/tests/resources/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d b/tests/resources/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
index df40d99af..df40d99af 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
+++ b/tests/resources/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54 b/tests/resources/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
index 321eaa867..321eaa867 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
+++ b/tests/resources/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc b/tests/resources/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
index 9bb5b623b..9bb5b623b 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
+++ b/tests/resources/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
index 7ca4ceed5..7ca4ceed5 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
+++ b/tests/resources/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/45/dd856fdd4d89b884c340ba0e047752d9b085d6 b/tests/resources/testrepo/.gitted/objects/45/dd856fdd4d89b884c340ba0e047752d9b085d6
index a83ed9763..a83ed9763 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/45/dd856fdd4d89b884c340ba0e047752d9b085d6
+++ b/tests/resources/testrepo/.gitted/objects/45/dd856fdd4d89b884c340ba0e047752d9b085d6
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/tests/resources/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
index 8953b6cef..8953b6cef 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
+++ b/tests/resources/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
diff --git a/tests-clar/resources/testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63 b/tests/resources/testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63
index e9150214b..e9150214b 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63
+++ b/tests/resources/testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/tests/resources/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
index c1f22c54f..c1f22c54f 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
+++ b/tests/resources/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
diff --git a/tests-clar/resources/testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc b/tests/resources/testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc
index b669961d8..b669961d8 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc
+++ b/tests/resources/testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735 b/tests/resources/testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735
index 9ff5eb2b5..9ff5eb2b5 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735
+++ b/tests/resources/testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/6b/377958d8c6a4906e8573b53672a1a23a4e8ce6 b/tests/resources/testrepo/.gitted/objects/6b/377958d8c6a4906e8573b53672a1a23a4e8ce6
new file mode 100644
index 000000000..ee7c78174
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/6b/377958d8c6a4906e8573b53672a1a23a4e8ce6
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/6b/9b767af9992b4abad5e24ffb1ba2d688ca602e b/tests/resources/testrepo/.gitted/objects/6b/9b767af9992b4abad5e24ffb1ba2d688ca602e
new file mode 100644
index 000000000..197685b86
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/6b/9b767af9992b4abad5e24ffb1ba2d688ca602e
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/tests/resources/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
index 2ef4faa0f..2ef4faa0f 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
+++ b/tests/resources/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af b/tests/resources/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
index 716b0c64b..716b0c64b 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
+++ b/tests/resources/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
diff --git a/tests/resources/testrepo/.gitted/objects/7b/2417a23b63e1fdde88c80e14b33247c6e5785a b/tests/resources/testrepo/.gitted/objects/7b/2417a23b63e1fdde88c80e14b33247c6e5785a
new file mode 100644
index 000000000..db778aaae
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/7b/2417a23b63e1fdde88c80e14b33247c6e5785a
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980 b/tests/resources/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980
index 23c462f34..23c462f34 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980
+++ b/tests/resources/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/tests/resources/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
index 2f9b6b6e3..2f9b6b6e3 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
+++ b/tests/resources/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/tests/resources/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
index 5df58dda5..5df58dda5 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
+++ b/tests/resources/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/87/380ae84009e9c503506c2f6143a4fc6c60bf80 b/tests/resources/testrepo/.gitted/objects/87/380ae84009e9c503506c2f6143a4fc6c60bf80
index 3042f5790..3042f5790 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/87/380ae84009e9c503506c2f6143a4fc6c60bf80
+++ b/tests/resources/testrepo/.gitted/objects/87/380ae84009e9c503506c2f6143a4fc6c60bf80
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162 b/tests/resources/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
index 4cc3f4dff..4cc3f4dff 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
+++ b/tests/resources/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
diff --git a/tests-clar/resources/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4 b/tests/resources/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
index bf7b2bb68..bf7b2bb68 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
+++ b/tests/resources/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/tests/resources/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
index a79612435..a79612435 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
+++ b/tests/resources/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
diff --git a/tests-clar/resources/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/tests/resources/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
index f8588696b..f8588696b 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
+++ b/tests/resources/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
diff --git a/tests-clar/resources/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750 b/tests/resources/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
index 29c8e824d..29c8e824d 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
+++ b/tests/resources/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
diff --git a/tests-clar/resources/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/tests/resources/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
index d0d7e736e..d0d7e736e 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
+++ b/tests/resources/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/tests/resources/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
index 18a7f61c2..18a7f61c2 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
+++ b/tests/resources/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12 b/tests/resources/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
index d95254674..d95254674 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
+++ b/tests/resources/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 b/tests/resources/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
index f460f2547..f460f2547 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
+++ b/tests/resources/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
diff --git a/tests-clar/resources/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593 b/tests/resources/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
index f613670e2..f613670e2 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
+++ b/tests/resources/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 b/tests/resources/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
index 0817229bc..0817229bc 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
+++ b/tests/resources/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
diff --git a/tests-clar/resources/testrepo/.gitted/objects/c0/528fd6cc988c0a40ce0be11bc192fc8dc5346e b/tests/resources/testrepo/.gitted/objects/c0/528fd6cc988c0a40ce0be11bc192fc8dc5346e
index 0401ab489..0401ab489 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/c0/528fd6cc988c0a40ce0be11bc192fc8dc5346e
+++ b/tests/resources/testrepo/.gitted/objects/c0/528fd6cc988c0a40ce0be11bc192fc8dc5346e
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd b/tests/resources/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
index 75f541f10..75f541f10 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
+++ b/tests/resources/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
diff --git a/tests-clar/resources/testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9 b/tests/resources/testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9
index 7620c514f..7620c514f 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9
+++ b/tests/resources/testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83 b/tests/resources/testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83
index 00940f0f2..00940f0f2 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83
+++ b/tests/resources/testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f b/tests/resources/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
index a67d6e647..a67d6e647 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
+++ b/tests/resources/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
index 711223894..711223894 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
+++ b/tests/resources/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0 b/tests/resources/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
index b135eccda..b135eccda 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
+++ b/tests/resources/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3 b/tests/resources/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
index 82e2790e8..82e2790e8 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
+++ b/tests/resources/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 b/tests/resources/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
index 697c94c92..697c94c92 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
+++ b/tests/resources/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/tests/resources/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
index 112998d42..112998d42 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
+++ b/tests/resources/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765 b/tests/resources/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
index 12bf5f3e3..12bf5f3e3 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
+++ b/tests/resources/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx b/tests/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
index 5068f2818..5068f2818 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
+++ b/tests/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack b/tests/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
index a6a1f3020..a6a1f3020 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
+++ b/tests/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx b/tests/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
index 94c3c71da..94c3c71da 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
+++ b/tests/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack b/tests/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
index 74c7fe4f3..74c7fe4f3 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
+++ b/tests/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx b/tests/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
index 555cfa977..555cfa977 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
+++ b/tests/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack b/tests/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
index 4d539ed0a..4d539ed0a 100644
--- a/tests-clar/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
+++ b/tests/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/packed-refs b/tests/resources/testrepo/.gitted/packed-refs
index 6018a19d2..6018a19d2 100644
--- a/tests-clar/resources/testrepo/.gitted/packed-refs
+++ b/tests/resources/testrepo/.gitted/packed-refs
diff --git a/tests-clar/resources/testrepo/.gitted/refs/heads/br2 b/tests/resources/testrepo/.gitted/refs/heads/br2
index aab87e5e7..aab87e5e7 100644
--- a/tests-clar/resources/testrepo/.gitted/refs/heads/br2
+++ b/tests/resources/testrepo/.gitted/refs/heads/br2
diff --git a/tests-clar/resources/testrepo/.gitted/refs/heads/dir b/tests/resources/testrepo/.gitted/refs/heads/dir
index 4567d37fa..4567d37fa 100644
--- a/tests-clar/resources/testrepo/.gitted/refs/heads/dir
+++ b/tests/resources/testrepo/.gitted/refs/heads/dir
diff --git a/tests/resources/testrepo/.gitted/refs/heads/long-file-name b/tests/resources/testrepo/.gitted/refs/heads/long-file-name
new file mode 100644
index 000000000..1f942a746
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/heads/long-file-name
@@ -0,0 +1 @@
+6b377958d8c6a4906e8573b53672a1a23a4e8ce6
diff --git a/tests-clar/resources/testrepo/.gitted/refs/heads/master b/tests/resources/testrepo/.gitted/refs/heads/master
index f31fe781b..f31fe781b 100644
--- a/tests-clar/resources/testrepo/.gitted/refs/heads/master
+++ b/tests/resources/testrepo/.gitted/refs/heads/master
diff --git a/tests-clar/resources/testrepo/.gitted/refs/heads/packed-test b/tests/resources/testrepo/.gitted/refs/heads/packed-test
index f2c14ad83..f2c14ad83 100644
--- a/tests-clar/resources/testrepo/.gitted/refs/heads/packed-test
+++ b/tests/resources/testrepo/.gitted/refs/heads/packed-test
diff --git a/tests-clar/resources/testrepo/.gitted/refs/heads/subtrees b/tests/resources/testrepo/.gitted/refs/heads/subtrees
index ad27e0b13..ad27e0b13 100644
--- a/tests-clar/resources/testrepo/.gitted/refs/heads/subtrees
+++ b/tests/resources/testrepo/.gitted/refs/heads/subtrees
diff --git a/tests-clar/resources/testrepo/.gitted/refs/heads/test b/tests/resources/testrepo/.gitted/refs/heads/test
index 399c4c73e..399c4c73e 100644
--- a/tests-clar/resources/testrepo/.gitted/refs/heads/test
+++ b/tests/resources/testrepo/.gitted/refs/heads/test
diff --git a/tests-clar/resources/testrepo/.gitted/refs/tags/e90810b b/tests/resources/testrepo/.gitted/refs/tags/e90810b
index 584495d3c..584495d3c 100644
--- a/tests-clar/resources/testrepo/.gitted/refs/tags/e90810b
+++ b/tests/resources/testrepo/.gitted/refs/tags/e90810b
diff --git a/tests-clar/resources/testrepo/.gitted/refs/tags/foo/bar b/tests/resources/testrepo/.gitted/refs/tags/foo/bar
index 6ee952a03..6ee952a03 100644
--- a/tests-clar/resources/testrepo/.gitted/refs/tags/foo/bar
+++ b/tests/resources/testrepo/.gitted/refs/tags/foo/bar
diff --git a/tests-clar/resources/testrepo/.gitted/refs/tags/foo/foo/bar b/tests/resources/testrepo/.gitted/refs/tags/foo/foo/bar
index 6ee952a03..6ee952a03 100644
--- a/tests-clar/resources/testrepo/.gitted/refs/tags/foo/foo/bar
+++ b/tests/resources/testrepo/.gitted/refs/tags/foo/foo/bar
diff --git a/tests-clar/resources/testrepo/.gitted/refs/tags/point_to_blob b/tests/resources/testrepo/.gitted/refs/tags/point_to_blob
index f874a3ffc..f874a3ffc 100644
--- a/tests-clar/resources/testrepo/.gitted/refs/tags/point_to_blob
+++ b/tests/resources/testrepo/.gitted/refs/tags/point_to_blob
diff --git a/tests-clar/resources/testrepo/.gitted/refs/tags/test b/tests/resources/testrepo/.gitted/refs/tags/test
index 6ee952a03..6ee952a03 100644
--- a/tests-clar/resources/testrepo/.gitted/refs/tags/test
+++ b/tests/resources/testrepo/.gitted/refs/tags/test
diff --git a/tests-clar/resources/twowaymerge.git/HEAD b/tests/resources/testrepo2/.gitted/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/twowaymerge.git/HEAD
+++ b/tests/resources/testrepo2/.gitted/HEAD
diff --git a/tests-clar/resources/testrepo2/.gitted/config b/tests/resources/testrepo2/.gitted/config
index fc2433caf..fc2433caf 100644
--- a/tests-clar/resources/testrepo2/.gitted/config
+++ b/tests/resources/testrepo2/.gitted/config
diff --git a/tests-clar/resources/twowaymerge.git/description b/tests/resources/testrepo2/.gitted/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/twowaymerge.git/description
+++ b/tests/resources/testrepo2/.gitted/description
diff --git a/tests-clar/resources/testrepo2/.gitted/index b/tests/resources/testrepo2/.gitted/index
index b614d0727..b614d0727 100644
--- a/tests-clar/resources/testrepo2/.gitted/index
+++ b/tests/resources/testrepo2/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/info/exclude b/tests/resources/testrepo2/.gitted/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/testrepo2/.gitted/info/exclude
+++ b/tests/resources/testrepo2/.gitted/info/exclude
diff --git a/tests-clar/resources/testrepo2/.gitted/logs/HEAD b/tests/resources/testrepo2/.gitted/logs/HEAD
index 4e80c69fa..4e80c69fa 100644
--- a/tests-clar/resources/testrepo2/.gitted/logs/HEAD
+++ b/tests/resources/testrepo2/.gitted/logs/HEAD
diff --git a/tests-clar/resources/testrepo2/.gitted/logs/refs/heads/master b/tests/resources/testrepo2/.gitted/logs/refs/heads/master
index 4e80c69fa..4e80c69fa 100644
--- a/tests-clar/resources/testrepo2/.gitted/logs/refs/heads/master
+++ b/tests/resources/testrepo2/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/testrepo2/.gitted/logs/refs/remotes/origin/HEAD b/tests/resources/testrepo2/.gitted/logs/refs/remotes/origin/HEAD
index 4e80c69fa..4e80c69fa 100644
--- a/tests-clar/resources/testrepo2/.gitted/logs/refs/remotes/origin/HEAD
+++ b/tests/resources/testrepo2/.gitted/logs/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/0c/37a5391bbff43c37f0d0371823a5509eed5b1d b/tests/resources/testrepo2/.gitted/objects/0c/37a5391bbff43c37f0d0371823a5509eed5b1d
index bfe146a5a..bfe146a5a 100644
--- a/tests-clar/resources/testrepo2/.gitted/objects/0c/37a5391bbff43c37f0d0371823a5509eed5b1d
+++ b/tests/resources/testrepo2/.gitted/objects/0c/37a5391bbff43c37f0d0371823a5509eed5b1d
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/tests/resources/testrepo2/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
index cedb2a22e..cedb2a22e 100644
--- a/tests-clar/resources/testrepo2/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
+++ b/tests/resources/testrepo2/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/tests/resources/testrepo2/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
index 93a16f146..93a16f146 100644
--- a/tests-clar/resources/testrepo2/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
+++ b/tests/resources/testrepo2/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd b/tests/resources/testrepo2/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
index ba0bfb30c..ba0bfb30c 100644
--- a/tests-clar/resources/testrepo2/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
+++ b/tests/resources/testrepo2/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/2d/2eff63372b08adf0a9eb84109ccf7d19e2f3a2 b/tests/resources/testrepo2/.gitted/objects/2d/2eff63372b08adf0a9eb84109ccf7d19e2f3a2
index 3cd240db5..3cd240db5 100644
--- a/tests-clar/resources/testrepo2/.gitted/objects/2d/2eff63372b08adf0a9eb84109ccf7d19e2f3a2
+++ b/tests/resources/testrepo2/.gitted/objects/2d/2eff63372b08adf0a9eb84109ccf7d19e2f3a2
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/36/060c58702ed4c2a40832c51758d5344201d89a b/tests/resources/testrepo2/.gitted/objects/36/060c58702ed4c2a40832c51758d5344201d89a
index 0c6246061..0c6246061 100644
--- a/tests-clar/resources/testrepo2/.gitted/objects/36/060c58702ed4c2a40832c51758d5344201d89a
+++ b/tests/resources/testrepo2/.gitted/objects/36/060c58702ed4c2a40832c51758d5344201d89a
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/testrepo2/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
index 7ca4ceed5..7ca4ceed5 100644
--- a/tests-clar/resources/testrepo2/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
+++ b/tests/resources/testrepo2/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/tests/resources/testrepo2/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
index 8953b6cef..8953b6cef 100644
--- a/tests-clar/resources/testrepo2/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
+++ b/tests/resources/testrepo2/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/tests/resources/testrepo2/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
index c1f22c54f..c1f22c54f 100644
--- a/tests-clar/resources/testrepo2/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
+++ b/tests/resources/testrepo2/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/61/9f9935957e010c419cb9d15621916ddfcc0b96 b/tests/resources/testrepo2/.gitted/objects/61/9f9935957e010c419cb9d15621916ddfcc0b96
index 1fd79b477..1fd79b477 100644
--- a/tests-clar/resources/testrepo2/.gitted/objects/61/9f9935957e010c419cb9d15621916ddfcc0b96
+++ b/tests/resources/testrepo2/.gitted/objects/61/9f9935957e010c419cb9d15621916ddfcc0b96
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/tests/resources/testrepo2/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
index 2ef4faa0f..2ef4faa0f 100644
--- a/tests-clar/resources/testrepo2/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
+++ b/tests/resources/testrepo2/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/7f/043268ea43ce18e3540acaabf9e090c91965b0 b/tests/resources/testrepo2/.gitted/objects/7f/043268ea43ce18e3540acaabf9e090c91965b0
index 3d1016daa..3d1016daa 100644
--- a/tests-clar/resources/testrepo2/.gitted/objects/7f/043268ea43ce18e3540acaabf9e090c91965b0
+++ b/tests/resources/testrepo2/.gitted/objects/7f/043268ea43ce18e3540acaabf9e090c91965b0
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/tests/resources/testrepo2/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
index 2f9b6b6e3..2f9b6b6e3 100644
--- a/tests-clar/resources/testrepo2/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
+++ b/tests/resources/testrepo2/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/tests/resources/testrepo2/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
index 5df58dda5..5df58dda5 100644
--- a/tests-clar/resources/testrepo2/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
+++ b/tests/resources/testrepo2/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/tests/resources/testrepo2/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
index a79612435..a79612435 100644
--- a/tests-clar/resources/testrepo2/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
+++ b/tests/resources/testrepo2/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/tests/resources/testrepo2/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
index f8588696b..f8588696b 100644
--- a/tests-clar/resources/testrepo2/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
+++ b/tests/resources/testrepo2/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/tests/resources/testrepo2/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
index d0d7e736e..d0d7e736e 100644
--- a/tests-clar/resources/testrepo2/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
+++ b/tests/resources/testrepo2/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/tests/resources/testrepo2/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
index 18a7f61c2..18a7f61c2 100644
--- a/tests-clar/resources/testrepo2/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
+++ b/tests/resources/testrepo2/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 b/tests/resources/testrepo2/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
index 0817229bc..0817229bc 100644
--- a/tests-clar/resources/testrepo2/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
+++ b/tests/resources/testrepo2/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd b/tests/resources/testrepo2/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
index 75f541f10..75f541f10 100644
--- a/tests-clar/resources/testrepo2/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
+++ b/tests/resources/testrepo2/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/c4/dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b b/tests/resources/testrepo2/.gitted/objects/c4/dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b
index 599e16084..599e16084 100644
--- a/tests-clar/resources/testrepo2/.gitted/objects/c4/dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b
+++ b/tests/resources/testrepo2/.gitted/objects/c4/dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/testrepo2/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
index 711223894..711223894 100644
--- a/tests-clar/resources/testrepo2/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
+++ b/tests/resources/testrepo2/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 b/tests/resources/testrepo2/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
index 03770969a..03770969a 100644
--- a/tests-clar/resources/testrepo2/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
+++ b/tests/resources/testrepo2/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/tests/resources/testrepo2/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
index 112998d42..112998d42 100644
--- a/tests-clar/resources/testrepo2/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
+++ b/tests/resources/testrepo2/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765 b/tests/resources/testrepo2/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
index 12bf5f3e3..12bf5f3e3 100644
--- a/tests-clar/resources/testrepo2/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
+++ b/tests/resources/testrepo2/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx b/tests/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
index 94c3c71da..94c3c71da 100644
--- a/tests-clar/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
+++ b/tests/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack b/tests/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
index 74c7fe4f3..74c7fe4f3 100644
--- a/tests-clar/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
+++ b/tests/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/packed-refs b/tests/resources/testrepo2/.gitted/packed-refs
index 97ea6a848..97ea6a848 100644
--- a/tests-clar/resources/testrepo2/.gitted/packed-refs
+++ b/tests/resources/testrepo2/.gitted/packed-refs
diff --git a/tests-clar/resources/testrepo2/.gitted/refs/heads/master b/tests/resources/testrepo2/.gitted/refs/heads/master
index a7eafce3c..a7eafce3c 100644
--- a/tests-clar/resources/testrepo2/.gitted/refs/heads/master
+++ b/tests/resources/testrepo2/.gitted/refs/heads/master
diff --git a/tests-clar/resources/testrepo2/.gitted/refs/remotes/origin/HEAD b/tests/resources/testrepo2/.gitted/refs/remotes/origin/HEAD
index 6efe28fff..6efe28fff 100644
--- a/tests-clar/resources/testrepo2/.gitted/refs/remotes/origin/HEAD
+++ b/tests/resources/testrepo2/.gitted/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/testrepo2/README b/tests/resources/testrepo2/README
index 1385f264a..1385f264a 100644
--- a/tests-clar/resources/testrepo2/README
+++ b/tests/resources/testrepo2/README
diff --git a/tests-clar/resources/testrepo2/new.txt b/tests/resources/testrepo2/new.txt
index fa49b0779..fa49b0779 100644
--- a/tests-clar/resources/testrepo2/new.txt
+++ b/tests/resources/testrepo2/new.txt
diff --git a/tests-clar/resources/testrepo2/subdir/README b/tests/resources/testrepo2/subdir/README
index 1385f264a..1385f264a 100644
--- a/tests-clar/resources/testrepo2/subdir/README
+++ b/tests/resources/testrepo2/subdir/README
diff --git a/tests-clar/resources/testrepo2/subdir/new.txt b/tests/resources/testrepo2/subdir/new.txt
index fa49b0779..fa49b0779 100644
--- a/tests-clar/resources/testrepo2/subdir/new.txt
+++ b/tests/resources/testrepo2/subdir/new.txt
diff --git a/tests-clar/resources/testrepo2/subdir/subdir2/README b/tests/resources/testrepo2/subdir/subdir2/README
index 1385f264a..1385f264a 100644
--- a/tests-clar/resources/testrepo2/subdir/subdir2/README
+++ b/tests/resources/testrepo2/subdir/subdir2/README
diff --git a/tests-clar/resources/testrepo2/subdir/subdir2/new.txt b/tests/resources/testrepo2/subdir/subdir2/new.txt
index fa49b0779..fa49b0779 100644
--- a/tests-clar/resources/testrepo2/subdir/subdir2/new.txt
+++ b/tests/resources/testrepo2/subdir/subdir2/new.txt
diff --git a/tests-clar/resources/typechanges/.gitted/HEAD b/tests/resources/twowaymerge.git/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/typechanges/.gitted/HEAD
+++ b/tests/resources/twowaymerge.git/HEAD
diff --git a/tests/resources/twowaymerge.git/config b/tests/resources/twowaymerge.git/config
new file mode 100644
index 000000000..c53d818dd
--- /dev/null
+++ b/tests/resources/twowaymerge.git/config
@@ -0,0 +1,5 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = true
+ ignorecase = true
diff --git a/tests-clar/resources/typechanges/.gitted/description b/tests/resources/twowaymerge.git/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/typechanges/.gitted/description
+++ b/tests/resources/twowaymerge.git/description
diff --git a/tests-clar/resources/twowaymerge.git/info/exclude b/tests/resources/twowaymerge.git/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/twowaymerge.git/info/exclude
+++ b/tests/resources/twowaymerge.git/info/exclude
diff --git a/tests-clar/resources/twowaymerge.git/objects/0c/8a3f1f3d5f421cf83048c7c73ee3b55a5e0f29 b/tests/resources/twowaymerge.git/objects/0c/8a3f1f3d5f421cf83048c7c73ee3b55a5e0f29
index 12698affa..12698affa 100644
--- a/tests-clar/resources/twowaymerge.git/objects/0c/8a3f1f3d5f421cf83048c7c73ee3b55a5e0f29
+++ b/tests/resources/twowaymerge.git/objects/0c/8a3f1f3d5f421cf83048c7c73ee3b55a5e0f29
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/10/2dce8e3081f398e4bdd9fd894dc85ac3ca6a67 b/tests/resources/twowaymerge.git/objects/10/2dce8e3081f398e4bdd9fd894dc85ac3ca6a67
index 3806ee74c..3806ee74c 100644
--- a/tests-clar/resources/twowaymerge.git/objects/10/2dce8e3081f398e4bdd9fd894dc85ac3ca6a67
+++ b/tests/resources/twowaymerge.git/objects/10/2dce8e3081f398e4bdd9fd894dc85ac3ca6a67
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/17/7d8634a28e26ec7819284752757ebe01a479d5 b/tests/resources/twowaymerge.git/objects/17/7d8634a28e26ec7819284752757ebe01a479d5
index e91e06db2..e91e06db2 100644
--- a/tests-clar/resources/twowaymerge.git/objects/17/7d8634a28e26ec7819284752757ebe01a479d5
+++ b/tests/resources/twowaymerge.git/objects/17/7d8634a28e26ec7819284752757ebe01a479d5
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/1c/30b88f5f3ee66d78df6520a7de9e89b890818b b/tests/resources/twowaymerge.git/objects/1c/30b88f5f3ee66d78df6520a7de9e89b890818b
index 57d1a0f1e..57d1a0f1e 100644
--- a/tests-clar/resources/twowaymerge.git/objects/1c/30b88f5f3ee66d78df6520a7de9e89b890818b
+++ b/tests/resources/twowaymerge.git/objects/1c/30b88f5f3ee66d78df6520a7de9e89b890818b
diff --git a/tests-clar/resources/twowaymerge.git/objects/1f/4c0311a24b63f6fc209a59a1e404942d4a5006 b/tests/resources/twowaymerge.git/objects/1f/4c0311a24b63f6fc209a59a1e404942d4a5006
index 99288fdd7..99288fdd7 100644
--- a/tests-clar/resources/twowaymerge.git/objects/1f/4c0311a24b63f6fc209a59a1e404942d4a5006
+++ b/tests/resources/twowaymerge.git/objects/1f/4c0311a24b63f6fc209a59a1e404942d4a5006
diff --git a/tests-clar/resources/twowaymerge.git/objects/22/24e191514cb4bd8c566d80dac22dfcb1e9bb83 b/tests/resources/twowaymerge.git/objects/22/24e191514cb4bd8c566d80dac22dfcb1e9bb83
index 48466ea51..48466ea51 100644
--- a/tests-clar/resources/twowaymerge.git/objects/22/24e191514cb4bd8c566d80dac22dfcb1e9bb83
+++ b/tests/resources/twowaymerge.git/objects/22/24e191514cb4bd8c566d80dac22dfcb1e9bb83
diff --git a/tests-clar/resources/twowaymerge.git/objects/29/6e56023cdc034d2735fee8c0d85a659d1b07f4 b/tests/resources/twowaymerge.git/objects/29/6e56023cdc034d2735fee8c0d85a659d1b07f4
index aa3fccdf0..aa3fccdf0 100644
--- a/tests-clar/resources/twowaymerge.git/objects/29/6e56023cdc034d2735fee8c0d85a659d1b07f4
+++ b/tests/resources/twowaymerge.git/objects/29/6e56023cdc034d2735fee8c0d85a659d1b07f4
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/31/51880ae2b363f1c262cf98b750c1f169a0d432 b/tests/resources/twowaymerge.git/objects/31/51880ae2b363f1c262cf98b750c1f169a0d432
index 235d42bff..235d42bff 100644
--- a/tests-clar/resources/twowaymerge.git/objects/31/51880ae2b363f1c262cf98b750c1f169a0d432
+++ b/tests/resources/twowaymerge.git/objects/31/51880ae2b363f1c262cf98b750c1f169a0d432
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/3b/287f8730c81d0b763c2d294618a5e32b67b4f8 b/tests/resources/twowaymerge.git/objects/3b/287f8730c81d0b763c2d294618a5e32b67b4f8
index 56ddac5ee..56ddac5ee 100644
--- a/tests-clar/resources/twowaymerge.git/objects/3b/287f8730c81d0b763c2d294618a5e32b67b4f8
+++ b/tests/resources/twowaymerge.git/objects/3b/287f8730c81d0b763c2d294618a5e32b67b4f8
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/42/b7311aa626e712891940c1ec5d5cba201946a4 b/tests/resources/twowaymerge.git/objects/42/b7311aa626e712891940c1ec5d5cba201946a4
index a8e6581f8..a8e6581f8 100644
--- a/tests-clar/resources/twowaymerge.git/objects/42/b7311aa626e712891940c1ec5d5cba201946a4
+++ b/tests/resources/twowaymerge.git/objects/42/b7311aa626e712891940c1ec5d5cba201946a4
diff --git a/tests-clar/resources/twowaymerge.git/objects/49/6d6428b9cf92981dc9495211e6e1120fb6f2ba b/tests/resources/twowaymerge.git/objects/49/6d6428b9cf92981dc9495211e6e1120fb6f2ba
index 978bc3448..978bc3448 100644
--- a/tests-clar/resources/twowaymerge.git/objects/49/6d6428b9cf92981dc9495211e6e1120fb6f2ba
+++ b/tests/resources/twowaymerge.git/objects/49/6d6428b9cf92981dc9495211e6e1120fb6f2ba
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/59/b0cf7d74659e1cdb13305319d6d4ce2733c118 b/tests/resources/twowaymerge.git/objects/59/b0cf7d74659e1cdb13305319d6d4ce2733c118
index 30b507c06..30b507c06 100644
--- a/tests-clar/resources/twowaymerge.git/objects/59/b0cf7d74659e1cdb13305319d6d4ce2733c118
+++ b/tests/resources/twowaymerge.git/objects/59/b0cf7d74659e1cdb13305319d6d4ce2733c118
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/6a/b5d28acbf3c3bdff276f7ccfdf29c1520e542f b/tests/resources/twowaymerge.git/objects/6a/b5d28acbf3c3bdff276f7ccfdf29c1520e542f
index ff6a386ac..ff6a386ac 100644
--- a/tests-clar/resources/twowaymerge.git/objects/6a/b5d28acbf3c3bdff276f7ccfdf29c1520e542f
+++ b/tests/resources/twowaymerge.git/objects/6a/b5d28acbf3c3bdff276f7ccfdf29c1520e542f
diff --git a/tests-clar/resources/twowaymerge.git/objects/6c/fca542b55b8b37017e6125a4b8f59a6eae6f11 b/tests/resources/twowaymerge.git/objects/6c/fca542b55b8b37017e6125a4b8f59a6eae6f11
index 9a969a279..9a969a279 100644
--- a/tests-clar/resources/twowaymerge.git/objects/6c/fca542b55b8b37017e6125a4b8f59a6eae6f11
+++ b/tests/resources/twowaymerge.git/objects/6c/fca542b55b8b37017e6125a4b8f59a6eae6f11
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/76/5b32c65d38f04c4f287abda055818ec0f26912 b/tests/resources/twowaymerge.git/objects/76/5b32c65d38f04c4f287abda055818ec0f26912
index 493bbc076..493bbc076 100644
--- a/tests-clar/resources/twowaymerge.git/objects/76/5b32c65d38f04c4f287abda055818ec0f26912
+++ b/tests/resources/twowaymerge.git/objects/76/5b32c65d38f04c4f287abda055818ec0f26912
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/7b/8c336c45fc6895c1c60827260fe5d798e5d247 b/tests/resources/twowaymerge.git/objects/7b/8c336c45fc6895c1c60827260fe5d798e5d247
index 19e7ef463..19e7ef463 100644
--- a/tests-clar/resources/twowaymerge.git/objects/7b/8c336c45fc6895c1c60827260fe5d798e5d247
+++ b/tests/resources/twowaymerge.git/objects/7b/8c336c45fc6895c1c60827260fe5d798e5d247
diff --git a/tests-clar/resources/twowaymerge.git/objects/82/bf9a1a10a4b25c1f14c9607b60970705e92545 b/tests/resources/twowaymerge.git/objects/82/bf9a1a10a4b25c1f14c9607b60970705e92545
index 89b0b9f9b..89b0b9f9b 100644
--- a/tests-clar/resources/twowaymerge.git/objects/82/bf9a1a10a4b25c1f14c9607b60970705e92545
+++ b/tests/resources/twowaymerge.git/objects/82/bf9a1a10a4b25c1f14c9607b60970705e92545
diff --git a/tests-clar/resources/twowaymerge.git/objects/8b/82fb1794cb1c8c7f172ec730a4c2db0ae3e650 b/tests/resources/twowaymerge.git/objects/8b/82fb1794cb1c8c7f172ec730a4c2db0ae3e650
index 8e9b758ea..8e9b758ea 100644
--- a/tests-clar/resources/twowaymerge.git/objects/8b/82fb1794cb1c8c7f172ec730a4c2db0ae3e650
+++ b/tests/resources/twowaymerge.git/objects/8b/82fb1794cb1c8c7f172ec730a4c2db0ae3e650
diff --git a/tests-clar/resources/twowaymerge.git/objects/9a/40a2f11c191f180c47e54b11567cb3c1e89b30 b/tests/resources/twowaymerge.git/objects/9a/40a2f11c191f180c47e54b11567cb3c1e89b30
index 1de1224f7..1de1224f7 100644
--- a/tests-clar/resources/twowaymerge.git/objects/9a/40a2f11c191f180c47e54b11567cb3c1e89b30
+++ b/tests/resources/twowaymerge.git/objects/9a/40a2f11c191f180c47e54b11567cb3c1e89b30
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/9b/219343610c88a1187c996d0dc58330b55cee28 b/tests/resources/twowaymerge.git/objects/9b/219343610c88a1187c996d0dc58330b55cee28
index 8b64b4381..8b64b4381 100644
--- a/tests-clar/resources/twowaymerge.git/objects/9b/219343610c88a1187c996d0dc58330b55cee28
+++ b/tests/resources/twowaymerge.git/objects/9b/219343610c88a1187c996d0dc58330b55cee28
diff --git a/tests-clar/resources/twowaymerge.git/objects/9f/e06a50f4d1634d6c6879854d01d80857388706 b/tests/resources/twowaymerge.git/objects/9f/e06a50f4d1634d6c6879854d01d80857388706
index 055de0158..055de0158 100644
--- a/tests-clar/resources/twowaymerge.git/objects/9f/e06a50f4d1634d6c6879854d01d80857388706
+++ b/tests/resources/twowaymerge.git/objects/9f/e06a50f4d1634d6c6879854d01d80857388706
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/a4/1a49f8f5cd9b6cb14a076bf8394881ed0b4d19 b/tests/resources/twowaymerge.git/objects/a4/1a49f8f5cd9b6cb14a076bf8394881ed0b4d19
index cb4d34e77..cb4d34e77 100644
--- a/tests-clar/resources/twowaymerge.git/objects/a4/1a49f8f5cd9b6cb14a076bf8394881ed0b4d19
+++ b/tests/resources/twowaymerge.git/objects/a4/1a49f8f5cd9b6cb14a076bf8394881ed0b4d19
diff --git a/tests-clar/resources/twowaymerge.git/objects/a9/53a018c5b10b20c86e69fef55ebc8ad4c5a417 b/tests/resources/twowaymerge.git/objects/a9/53a018c5b10b20c86e69fef55ebc8ad4c5a417
index 8235f1839..8235f1839 100644
--- a/tests-clar/resources/twowaymerge.git/objects/a9/53a018c5b10b20c86e69fef55ebc8ad4c5a417
+++ b/tests/resources/twowaymerge.git/objects/a9/53a018c5b10b20c86e69fef55ebc8ad4c5a417
diff --git a/tests-clar/resources/twowaymerge.git/objects/a9/cce3cd1b3efbda5b1f4a6dcc3f1570b2d3d74c b/tests/resources/twowaymerge.git/objects/a9/cce3cd1b3efbda5b1f4a6dcc3f1570b2d3d74c
index 4da7e826a..4da7e826a 100644
--- a/tests-clar/resources/twowaymerge.git/objects/a9/cce3cd1b3efbda5b1f4a6dcc3f1570b2d3d74c
+++ b/tests/resources/twowaymerge.git/objects/a9/cce3cd1b3efbda5b1f4a6dcc3f1570b2d3d74c
diff --git a/tests-clar/resources/twowaymerge.git/objects/bd/1732c43c68d712ad09e1d872b9be6d4b9efdc4 b/tests/resources/twowaymerge.git/objects/bd/1732c43c68d712ad09e1d872b9be6d4b9efdc4
index b9b60122d..b9b60122d 100644
--- a/tests-clar/resources/twowaymerge.git/objects/bd/1732c43c68d712ad09e1d872b9be6d4b9efdc4
+++ b/tests/resources/twowaymerge.git/objects/bd/1732c43c68d712ad09e1d872b9be6d4b9efdc4
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/c3/7a783c20d92ac92362a78a32860f7eebf938ef b/tests/resources/twowaymerge.git/objects/c3/7a783c20d92ac92362a78a32860f7eebf938ef
index 041e890ab..041e890ab 100644
--- a/tests-clar/resources/twowaymerge.git/objects/c3/7a783c20d92ac92362a78a32860f7eebf938ef
+++ b/tests/resources/twowaymerge.git/objects/c3/7a783c20d92ac92362a78a32860f7eebf938ef
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/cb/dd40facab1682754eb67f7a43f29e672903cf6 b/tests/resources/twowaymerge.git/objects/cb/dd40facab1682754eb67f7a43f29e672903cf6
index ccb156d88..ccb156d88 100644
--- a/tests-clar/resources/twowaymerge.git/objects/cb/dd40facab1682754eb67f7a43f29e672903cf6
+++ b/tests/resources/twowaymerge.git/objects/cb/dd40facab1682754eb67f7a43f29e672903cf6
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/cd/f97fd3bb48eb3827638bb33d208f5fd32d0aa6 b/tests/resources/twowaymerge.git/objects/cd/f97fd3bb48eb3827638bb33d208f5fd32d0aa6
index 0e028dc01..0e028dc01 100644
--- a/tests-clar/resources/twowaymerge.git/objects/cd/f97fd3bb48eb3827638bb33d208f5fd32d0aa6
+++ b/tests/resources/twowaymerge.git/objects/cd/f97fd3bb48eb3827638bb33d208f5fd32d0aa6
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/d6/f10d549cb335b9e6d38afc1f0088be69b50494 b/tests/resources/twowaymerge.git/objects/d6/f10d549cb335b9e6d38afc1f0088be69b50494
index b298c520e..b298c520e 100644
--- a/tests-clar/resources/twowaymerge.git/objects/d6/f10d549cb335b9e6d38afc1f0088be69b50494
+++ b/tests/resources/twowaymerge.git/objects/d6/f10d549cb335b9e6d38afc1f0088be69b50494
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/d9/acdc7ae7632adfeec67fa73c1e343cf4d1f47e b/tests/resources/twowaymerge.git/objects/d9/acdc7ae7632adfeec67fa73c1e343cf4d1f47e
index de94528a4..de94528a4 100644
--- a/tests-clar/resources/twowaymerge.git/objects/d9/acdc7ae7632adfeec67fa73c1e343cf4d1f47e
+++ b/tests/resources/twowaymerge.git/objects/d9/acdc7ae7632adfeec67fa73c1e343cf4d1f47e
diff --git a/tests-clar/resources/twowaymerge.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/twowaymerge.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
index 711223894..711223894 100644
--- a/tests-clar/resources/twowaymerge.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
+++ b/tests/resources/twowaymerge.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/ef/0488f0b722f0be8bcb90a7730ac7efafd1d694 b/tests/resources/twowaymerge.git/objects/ef/0488f0b722f0be8bcb90a7730ac7efafd1d694
index 00f7d3615..00f7d3615 100644
--- a/tests-clar/resources/twowaymerge.git/objects/ef/0488f0b722f0be8bcb90a7730ac7efafd1d694
+++ b/tests/resources/twowaymerge.git/objects/ef/0488f0b722f0be8bcb90a7730ac7efafd1d694
diff --git a/tests-clar/resources/twowaymerge.git/objects/fc/f7e3f51c11d199ab7a78403ee4f9ccd028da25 b/tests/resources/twowaymerge.git/objects/fc/f7e3f51c11d199ab7a78403ee4f9ccd028da25
index 54989ea87..54989ea87 100644
--- a/tests-clar/resources/twowaymerge.git/objects/fc/f7e3f51c11d199ab7a78403ee4f9ccd028da25
+++ b/tests/resources/twowaymerge.git/objects/fc/f7e3f51c11d199ab7a78403ee4f9ccd028da25
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/refs/heads/first-branch b/tests/resources/twowaymerge.git/refs/heads/first-branch
index ef0dead7f..ef0dead7f 100644
--- a/tests-clar/resources/twowaymerge.git/refs/heads/first-branch
+++ b/tests/resources/twowaymerge.git/refs/heads/first-branch
diff --git a/tests-clar/resources/twowaymerge.git/refs/heads/master b/tests/resources/twowaymerge.git/refs/heads/master
index ebf18f58e..ebf18f58e 100644
--- a/tests-clar/resources/twowaymerge.git/refs/heads/master
+++ b/tests/resources/twowaymerge.git/refs/heads/master
diff --git a/tests-clar/resources/twowaymerge.git/refs/heads/second-branch b/tests/resources/twowaymerge.git/refs/heads/second-branch
index 586a14a84..586a14a84 100644
--- a/tests-clar/resources/twowaymerge.git/refs/heads/second-branch
+++ b/tests/resources/twowaymerge.git/refs/heads/second-branch
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/HEAD b/tests/resources/typechanges/.gitted/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/b/HEAD
+++ b/tests/resources/typechanges/.gitted/HEAD
diff --git a/tests-clar/resources/typechanges/.gitted/config b/tests/resources/typechanges/.gitted/config
index 4cc6e1ddf..4cc6e1ddf 100644
--- a/tests-clar/resources/typechanges/.gitted/config
+++ b/tests/resources/typechanges/.gitted/config
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/description b/tests/resources/typechanges/.gitted/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/b/description
+++ b/tests/resources/typechanges/.gitted/description
diff --git a/tests-clar/resources/typechanges/.gitted/index b/tests/resources/typechanges/.gitted/index
index 4f6d12a3b..4f6d12a3b 100644
--- a/tests-clar/resources/typechanges/.gitted/index
+++ b/tests/resources/typechanges/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/info/exclude b/tests/resources/typechanges/.gitted/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/typechanges/.gitted/info/exclude
+++ b/tests/resources/typechanges/.gitted/info/exclude
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/HEAD b/tests/resources/typechanges/.gitted/modules/b/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/d/HEAD
+++ b/tests/resources/typechanges/.gitted/modules/b/HEAD
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/config b/tests/resources/typechanges/.gitted/modules/b/config
index f57cd4a6f..f57cd4a6f 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/b/config
+++ b/tests/resources/typechanges/.gitted/modules/b/config
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/description b/tests/resources/typechanges/.gitted/modules/b/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/d/description
+++ b/tests/resources/typechanges/.gitted/modules/b/description
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/index b/tests/resources/typechanges/.gitted/modules/b/index
index c16a026b7..c16a026b7 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/b/index
+++ b/tests/resources/typechanges/.gitted/modules/b/index
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/info/exclude b/tests/resources/typechanges/.gitted/modules/b/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/b/info/exclude
+++ b/tests/resources/typechanges/.gitted/modules/b/info/exclude
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/typechanges/.gitted/modules/b/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
index f4b7094c5..f4b7094c5 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/b/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/typechanges/.gitted/modules/b/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
index 56c845e49..56c845e49 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/b/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/typechanges/.gitted/modules/b/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
index bd179b5f5..bd179b5f5 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/b/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/typechanges/.gitted/modules/b/objects/41/bd4bc3df978de695f67ace64c560913da11653
index ccf49bd15..ccf49bd15 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/b/objects/41/bd4bc3df978de695f67ace64c560913da11653
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/typechanges/.gitted/modules/b/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
index 53029069a..53029069a 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/b/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/typechanges/.gitted/modules/b/objects/5e/4963595a9774b90524d35a807169049de8ccad
index 38c791eba..38c791eba 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/b/objects/5e/4963595a9774b90524d35a807169049de8ccad
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/typechanges/.gitted/modules/b/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
index a26d29993..a26d29993 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/b/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/typechanges/.gitted/modules/b/objects/73/ba924a80437097795ae839e66e187c55d3babf
index 83d1ba481..83d1ba481 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/b/objects/73/ba924a80437097795ae839e66e187c55d3babf
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/typechanges/.gitted/modules/b/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
index 6d27af8a8..6d27af8a8 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/b/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/typechanges/.gitted/modules/b/objects/78/9efbdadaa4a582778d4584385495559ea0994b
index 17458840b..17458840b 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/b/objects/78/9efbdadaa4a582778d4584385495559ea0994b
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/78/9efbdadaa4a582778d4584385495559ea0994b
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/typechanges/.gitted/modules/b/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
index 83cc29fb1..83cc29fb1 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/b/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/typechanges/.gitted/modules/b/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
index 55bda40ef..55bda40ef 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/b/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/packed-refs b/tests/resources/typechanges/.gitted/modules/b/packed-refs
index 5a4ebc47c..5a4ebc47c 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/b/packed-refs
+++ b/tests/resources/typechanges/.gitted/modules/b/packed-refs
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/refs/heads/master b/tests/resources/typechanges/.gitted/modules/b/refs/heads/master
index e12c44d7a..e12c44d7a 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/b/refs/heads/master
+++ b/tests/resources/typechanges/.gitted/modules/b/refs/heads/master
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/refs/remotes/origin/HEAD b/tests/resources/typechanges/.gitted/modules/b/refs/remotes/origin/HEAD
index 6efe28fff..6efe28fff 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/b/refs/remotes/origin/HEAD
+++ b/tests/resources/typechanges/.gitted/modules/b/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/HEAD b/tests/resources/typechanges/.gitted/modules/d/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/e/HEAD
+++ b/tests/resources/typechanges/.gitted/modules/d/HEAD
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/config b/tests/resources/typechanges/.gitted/modules/d/config
index 42e1bddda..42e1bddda 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/d/config
+++ b/tests/resources/typechanges/.gitted/modules/d/config
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/description b/tests/resources/typechanges/.gitted/modules/d/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/e/description
+++ b/tests/resources/typechanges/.gitted/modules/d/description
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/index b/tests/resources/typechanges/.gitted/modules/d/index
index 86d0266e8..86d0266e8 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/d/index
+++ b/tests/resources/typechanges/.gitted/modules/d/index
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/info/exclude b/tests/resources/typechanges/.gitted/modules/d/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/d/info/exclude
+++ b/tests/resources/typechanges/.gitted/modules/d/info/exclude
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/typechanges/.gitted/modules/d/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
index f4b7094c5..f4b7094c5 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/d/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/typechanges/.gitted/modules/d/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
index 56c845e49..56c845e49 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/d/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/typechanges/.gitted/modules/d/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
index bd179b5f5..bd179b5f5 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/d/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/typechanges/.gitted/modules/d/objects/41/bd4bc3df978de695f67ace64c560913da11653
index ccf49bd15..ccf49bd15 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/d/objects/41/bd4bc3df978de695f67ace64c560913da11653
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/typechanges/.gitted/modules/d/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
index 53029069a..53029069a 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/d/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/typechanges/.gitted/modules/d/objects/5e/4963595a9774b90524d35a807169049de8ccad
index 38c791eba..38c791eba 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/d/objects/5e/4963595a9774b90524d35a807169049de8ccad
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/typechanges/.gitted/modules/d/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
index a26d29993..a26d29993 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/d/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/typechanges/.gitted/modules/d/objects/73/ba924a80437097795ae839e66e187c55d3babf
index 83d1ba481..83d1ba481 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/d/objects/73/ba924a80437097795ae839e66e187c55d3babf
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/typechanges/.gitted/modules/d/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
index 6d27af8a8..6d27af8a8 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/d/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/typechanges/.gitted/modules/d/objects/78/9efbdadaa4a582778d4584385495559ea0994b
index 17458840b..17458840b 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/d/objects/78/9efbdadaa4a582778d4584385495559ea0994b
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/78/9efbdadaa4a582778d4584385495559ea0994b
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/typechanges/.gitted/modules/d/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
index 83cc29fb1..83cc29fb1 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/d/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/typechanges/.gitted/modules/d/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
index 55bda40ef..55bda40ef 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/d/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/packed-refs b/tests/resources/typechanges/.gitted/modules/d/packed-refs
index 5a4ebc47c..5a4ebc47c 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/d/packed-refs
+++ b/tests/resources/typechanges/.gitted/modules/d/packed-refs
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/refs/heads/master b/tests/resources/typechanges/.gitted/modules/d/refs/heads/master
index e12c44d7a..e12c44d7a 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/d/refs/heads/master
+++ b/tests/resources/typechanges/.gitted/modules/d/refs/heads/master
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/refs/remotes/origin/HEAD b/tests/resources/typechanges/.gitted/modules/d/refs/remotes/origin/HEAD
index 6efe28fff..6efe28fff 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/d/refs/remotes/origin/HEAD
+++ b/tests/resources/typechanges/.gitted/modules/d/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/unsymlinked.git/HEAD b/tests/resources/typechanges/.gitted/modules/e/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests-clar/resources/unsymlinked.git/HEAD
+++ b/tests/resources/typechanges/.gitted/modules/e/HEAD
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/config b/tests/resources/typechanges/.gitted/modules/e/config
index 89b3b9b4f..89b3b9b4f 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/e/config
+++ b/tests/resources/typechanges/.gitted/modules/e/config
diff --git a/tests-clar/resources/unsymlinked.git/description b/tests/resources/typechanges/.gitted/modules/e/description
index 498b267a8..498b267a8 100644
--- a/tests-clar/resources/unsymlinked.git/description
+++ b/tests/resources/typechanges/.gitted/modules/e/description
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/index b/tests/resources/typechanges/.gitted/modules/e/index
index cd6e2da6c..cd6e2da6c 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/e/index
+++ b/tests/resources/typechanges/.gitted/modules/e/index
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/info/exclude b/tests/resources/typechanges/.gitted/modules/e/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/e/info/exclude
+++ b/tests/resources/typechanges/.gitted/modules/e/info/exclude
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/typechanges/.gitted/modules/e/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
index f4b7094c5..f4b7094c5 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/e/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/typechanges/.gitted/modules/e/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
index 56c845e49..56c845e49 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/e/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/typechanges/.gitted/modules/e/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
index bd179b5f5..bd179b5f5 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/e/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/typechanges/.gitted/modules/e/objects/41/bd4bc3df978de695f67ace64c560913da11653
index ccf49bd15..ccf49bd15 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/e/objects/41/bd4bc3df978de695f67ace64c560913da11653
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/typechanges/.gitted/modules/e/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
index 53029069a..53029069a 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/e/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/typechanges/.gitted/modules/e/objects/5e/4963595a9774b90524d35a807169049de8ccad
index 38c791eba..38c791eba 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/e/objects/5e/4963595a9774b90524d35a807169049de8ccad
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/typechanges/.gitted/modules/e/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
index a26d29993..a26d29993 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/e/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/typechanges/.gitted/modules/e/objects/73/ba924a80437097795ae839e66e187c55d3babf
index 83d1ba481..83d1ba481 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/e/objects/73/ba924a80437097795ae839e66e187c55d3babf
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/typechanges/.gitted/modules/e/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
index 6d27af8a8..6d27af8a8 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/e/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/typechanges/.gitted/modules/e/objects/78/9efbdadaa4a582778d4584385495559ea0994b
index 17458840b..17458840b 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/e/objects/78/9efbdadaa4a582778d4584385495559ea0994b
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/78/9efbdadaa4a582778d4584385495559ea0994b
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/typechanges/.gitted/modules/e/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
index 83cc29fb1..83cc29fb1 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/e/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/typechanges/.gitted/modules/e/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
index 55bda40ef..55bda40ef 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/e/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/packed-refs b/tests/resources/typechanges/.gitted/modules/e/packed-refs
index 5a4ebc47c..5a4ebc47c 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/e/packed-refs
+++ b/tests/resources/typechanges/.gitted/modules/e/packed-refs
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/refs/heads/master b/tests/resources/typechanges/.gitted/modules/e/refs/heads/master
index e12c44d7a..e12c44d7a 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/e/refs/heads/master
+++ b/tests/resources/typechanges/.gitted/modules/e/refs/heads/master
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/refs/remotes/origin/HEAD b/tests/resources/typechanges/.gitted/modules/e/refs/remotes/origin/HEAD
index 6efe28fff..6efe28fff 100644
--- a/tests-clar/resources/typechanges/.gitted/modules/e/refs/remotes/origin/HEAD
+++ b/tests/resources/typechanges/.gitted/modules/e/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/typechanges/.gitted/objects/0d/78578795b7ca49fd8df6c4b6d27c5c02d991d8 b/tests/resources/typechanges/.gitted/objects/0d/78578795b7ca49fd8df6c4b6d27c5c02d991d8
index f2d02f4f7..f2d02f4f7 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/0d/78578795b7ca49fd8df6c4b6d27c5c02d991d8
+++ b/tests/resources/typechanges/.gitted/objects/0d/78578795b7ca49fd8df6c4b6d27c5c02d991d8
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/0e/7ed140b514b8cae23254cb8656fe1674403aff b/tests/resources/typechanges/.gitted/objects/0e/7ed140b514b8cae23254cb8656fe1674403aff
index 527964c92..527964c92 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/0e/7ed140b514b8cae23254cb8656fe1674403aff
+++ b/tests/resources/typechanges/.gitted/objects/0e/7ed140b514b8cae23254cb8656fe1674403aff
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/0f/f461da9689266f482d8f6654a4400b4e33c586 b/tests/resources/typechanges/.gitted/objects/0f/f461da9689266f482d8f6654a4400b4e33c586
index 2694e4fa0..2694e4fa0 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/0f/f461da9689266f482d8f6654a4400b4e33c586
+++ b/tests/resources/typechanges/.gitted/objects/0f/f461da9689266f482d8f6654a4400b4e33c586
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/18/aa7e45bbe4c3cc24a0b079696c59d36675af97 b/tests/resources/typechanges/.gitted/objects/18/aa7e45bbe4c3cc24a0b079696c59d36675af97
index 032a960b4..032a960b4 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/18/aa7e45bbe4c3cc24a0b079696c59d36675af97
+++ b/tests/resources/typechanges/.gitted/objects/18/aa7e45bbe4c3cc24a0b079696c59d36675af97
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/1b/63caae4a5ca96f78e8dfefc376c6a39a142475 b/tests/resources/typechanges/.gitted/objects/1b/63caae4a5ca96f78e8dfefc376c6a39a142475
index d32622e67..d32622e67 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/1b/63caae4a5ca96f78e8dfefc376c6a39a142475
+++ b/tests/resources/typechanges/.gitted/objects/1b/63caae4a5ca96f78e8dfefc376c6a39a142475
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/1e/abe82aa3b2365a394f6108f24435df6e193d02 b/tests/resources/typechanges/.gitted/objects/1e/abe82aa3b2365a394f6108f24435df6e193d02
index 42d5f92f3..42d5f92f3 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/1e/abe82aa3b2365a394f6108f24435df6e193d02
+++ b/tests/resources/typechanges/.gitted/objects/1e/abe82aa3b2365a394f6108f24435df6e193d02
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/42/061c01a1c70097d1e4579f29a5adf40abdec95 b/tests/resources/typechanges/.gitted/objects/42/061c01a1c70097d1e4579f29a5adf40abdec95
index 0a8f32e15..0a8f32e15 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/42/061c01a1c70097d1e4579f29a5adf40abdec95
+++ b/tests/resources/typechanges/.gitted/objects/42/061c01a1c70097d1e4579f29a5adf40abdec95
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/46/2838cee476a87e7cff32196b66fa18ed756592 b/tests/resources/typechanges/.gitted/objects/46/2838cee476a87e7cff32196b66fa18ed756592
index 52af51f74..52af51f74 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/46/2838cee476a87e7cff32196b66fa18ed756592
+++ b/tests/resources/typechanges/.gitted/objects/46/2838cee476a87e7cff32196b66fa18ed756592
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/63/499e4ea8e096b831515ceb1d5a7593e4d87ae5 b/tests/resources/typechanges/.gitted/objects/63/499e4ea8e096b831515ceb1d5a7593e4d87ae5
index afafa89f4..afafa89f4 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/63/499e4ea8e096b831515ceb1d5a7593e4d87ae5
+++ b/tests/resources/typechanges/.gitted/objects/63/499e4ea8e096b831515ceb1d5a7593e4d87ae5
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/68/1af94e10eaf262f3ab7cb9b8fd5f4158ba4d3e b/tests/resources/typechanges/.gitted/objects/68/1af94e10eaf262f3ab7cb9b8fd5f4158ba4d3e
index 9e518fc28..9e518fc28 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/68/1af94e10eaf262f3ab7cb9b8fd5f4158ba4d3e
+++ b/tests/resources/typechanges/.gitted/objects/68/1af94e10eaf262f3ab7cb9b8fd5f4158ba4d3e
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/6a/9008602b811e69a9b7a2d83496f39a794fdeeb b/tests/resources/typechanges/.gitted/objects/6a/9008602b811e69a9b7a2d83496f39a794fdeeb
index a245727a1..a245727a1 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/6a/9008602b811e69a9b7a2d83496f39a794fdeeb
+++ b/tests/resources/typechanges/.gitted/objects/6a/9008602b811e69a9b7a2d83496f39a794fdeeb
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/6e/ae26c90e8ccc4d16208972119c40635489c6f0 b/tests/resources/typechanges/.gitted/objects/6e/ae26c90e8ccc4d16208972119c40635489c6f0
index ea35cd311..ea35cd311 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/6e/ae26c90e8ccc4d16208972119c40635489c6f0
+++ b/tests/resources/typechanges/.gitted/objects/6e/ae26c90e8ccc4d16208972119c40635489c6f0
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/6f/39eabbb8a7541515e0d35971078bccb502e7e0 b/tests/resources/typechanges/.gitted/objects/6f/39eabbb8a7541515e0d35971078bccb502e7e0
index c54817598..c54817598 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/6f/39eabbb8a7541515e0d35971078bccb502e7e0
+++ b/tests/resources/typechanges/.gitted/objects/6f/39eabbb8a7541515e0d35971078bccb502e7e0
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/71/54d3083461536dfc71ad5542f3e65e723a06c4 b/tests/resources/typechanges/.gitted/objects/71/54d3083461536dfc71ad5542f3e65e723a06c4
index 9fdd8f245..9fdd8f245 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/71/54d3083461536dfc71ad5542f3e65e723a06c4
+++ b/tests/resources/typechanges/.gitted/objects/71/54d3083461536dfc71ad5542f3e65e723a06c4
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/75/56c1d893a4c0ca85ac8ac51de47ff399758729 b/tests/resources/typechanges/.gitted/objects/75/56c1d893a4c0ca85ac8ac51de47ff399758729
index d43630f44..d43630f44 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/75/56c1d893a4c0ca85ac8ac51de47ff399758729
+++ b/tests/resources/typechanges/.gitted/objects/75/56c1d893a4c0ca85ac8ac51de47ff399758729
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/76/fef844064c26d5e06c2508240dae661e7231b2 b/tests/resources/typechanges/.gitted/objects/76/fef844064c26d5e06c2508240dae661e7231b2
index 355ce4b5b..355ce4b5b 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/76/fef844064c26d5e06c2508240dae661e7231b2
+++ b/tests/resources/typechanges/.gitted/objects/76/fef844064c26d5e06c2508240dae661e7231b2
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/79/b9f23e85f55ea36a472a902e875bc1121a94cb b/tests/resources/typechanges/.gitted/objects/79/b9f23e85f55ea36a472a902e875bc1121a94cb
index 2b07ad256..2b07ad256 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/79/b9f23e85f55ea36a472a902e875bc1121a94cb
+++ b/tests/resources/typechanges/.gitted/objects/79/b9f23e85f55ea36a472a902e875bc1121a94cb
diff --git a/tests-clar/resources/typechanges/.gitted/objects/85/28da0ea65eacf1f74f9ed6696adbac547963ad b/tests/resources/typechanges/.gitted/objects/85/28da0ea65eacf1f74f9ed6696adbac547963ad
index 6d2da6c93..6d2da6c93 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/85/28da0ea65eacf1f74f9ed6696adbac547963ad
+++ b/tests/resources/typechanges/.gitted/objects/85/28da0ea65eacf1f74f9ed6696adbac547963ad
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/8b/3726b365824ad5a07c537247f4bc73ed7d37ea b/tests/resources/typechanges/.gitted/objects/8b/3726b365824ad5a07c537247f4bc73ed7d37ea
index 3dc333bc6..3dc333bc6 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/8b/3726b365824ad5a07c537247f4bc73ed7d37ea
+++ b/tests/resources/typechanges/.gitted/objects/8b/3726b365824ad5a07c537247f4bc73ed7d37ea
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/93/3e28c1c8a68838a763d250bdf0b2c6068289c3 b/tests/resources/typechanges/.gitted/objects/93/3e28c1c8a68838a763d250bdf0b2c6068289c3
index 02ad0e97a..02ad0e97a 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/93/3e28c1c8a68838a763d250bdf0b2c6068289c3
+++ b/tests/resources/typechanges/.gitted/objects/93/3e28c1c8a68838a763d250bdf0b2c6068289c3
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/96/2710fe5b4e453e9e827945b3487c525968ec4a b/tests/resources/typechanges/.gitted/objects/96/2710fe5b4e453e9e827945b3487c525968ec4a
index d06b06a52..d06b06a52 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/96/2710fe5b4e453e9e827945b3487c525968ec4a
+++ b/tests/resources/typechanges/.gitted/objects/96/2710fe5b4e453e9e827945b3487c525968ec4a
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/96/6cf1b3598e195b31b2cde3784f9a19f0728a6f b/tests/resources/typechanges/.gitted/objects/96/6cf1b3598e195b31b2cde3784f9a19f0728a6f
index 5f9ffd4ed..5f9ffd4ed 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/96/6cf1b3598e195b31b2cde3784f9a19f0728a6f
+++ b/tests/resources/typechanges/.gitted/objects/96/6cf1b3598e195b31b2cde3784f9a19f0728a6f
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/99/e8bab9ece009f0fba7eb41f850f4c12bedb9b7 b/tests/resources/typechanges/.gitted/objects/99/e8bab9ece009f0fba7eb41f850f4c12bedb9b7
index ac17defac..ac17defac 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/99/e8bab9ece009f0fba7eb41f850f4c12bedb9b7
+++ b/tests/resources/typechanges/.gitted/objects/99/e8bab9ece009f0fba7eb41f850f4c12bedb9b7
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/9b/19edf33a03a0c59cdfc113bfa5c06179bf9b1a b/tests/resources/typechanges/.gitted/objects/9b/19edf33a03a0c59cdfc113bfa5c06179bf9b1a
index 7ab83aefe..7ab83aefe 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/9b/19edf33a03a0c59cdfc113bfa5c06179bf9b1a
+++ b/tests/resources/typechanges/.gitted/objects/9b/19edf33a03a0c59cdfc113bfa5c06179bf9b1a
diff --git a/tests-clar/resources/typechanges/.gitted/objects/9b/db75b73836a99e3dbeea640a81de81031fdc29 b/tests/resources/typechanges/.gitted/objects/9b/db75b73836a99e3dbeea640a81de81031fdc29
index aed4d8165..aed4d8165 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/9b/db75b73836a99e3dbeea640a81de81031fdc29
+++ b/tests/resources/typechanges/.gitted/objects/9b/db75b73836a99e3dbeea640a81de81031fdc29
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/9d/0235c7a7edc0889a18f97a42ee6db9fe688447 b/tests/resources/typechanges/.gitted/objects/9d/0235c7a7edc0889a18f97a42ee6db9fe688447
index 3e02a41b2..3e02a41b2 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/9d/0235c7a7edc0889a18f97a42ee6db9fe688447
+++ b/tests/resources/typechanges/.gitted/objects/9d/0235c7a7edc0889a18f97a42ee6db9fe688447
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/9e/ffc457877f109b2a4319e14bee613a15f2a00d b/tests/resources/typechanges/.gitted/objects/9e/ffc457877f109b2a4319e14bee613a15f2a00d
index fb24100fc..fb24100fc 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/9e/ffc457877f109b2a4319e14bee613a15f2a00d
+++ b/tests/resources/typechanges/.gitted/objects/9e/ffc457877f109b2a4319e14bee613a15f2a00d
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/a0/a9bad6f6f40325198f938a0e3ae981622d7707 b/tests/resources/typechanges/.gitted/objects/a0/a9bad6f6f40325198f938a0e3ae981622d7707
index b6b7db785..b6b7db785 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/a0/a9bad6f6f40325198f938a0e3ae981622d7707
+++ b/tests/resources/typechanges/.gitted/objects/a0/a9bad6f6f40325198f938a0e3ae981622d7707
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/b1/977dc4e573b812d4619754c98138c56999dc0d b/tests/resources/typechanges/.gitted/objects/b1/977dc4e573b812d4619754c98138c56999dc0d
index e1334057c..e1334057c 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/b1/977dc4e573b812d4619754c98138c56999dc0d
+++ b/tests/resources/typechanges/.gitted/objects/b1/977dc4e573b812d4619754c98138c56999dc0d
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/d7/5992dd02391e128dac332dcc78d649dd9ab095 b/tests/resources/typechanges/.gitted/objects/d7/5992dd02391e128dac332dcc78d649dd9ab095
index 65f1f530f..65f1f530f 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/d7/5992dd02391e128dac332dcc78d649dd9ab095
+++ b/tests/resources/typechanges/.gitted/objects/d7/5992dd02391e128dac332dcc78d649dd9ab095
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/da/e2709d638df52212b1f43ff61797ebfedfcc7c b/tests/resources/typechanges/.gitted/objects/da/e2709d638df52212b1f43ff61797ebfedfcc7c
index 355faa61f..355faa61f 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/da/e2709d638df52212b1f43ff61797ebfedfcc7c
+++ b/tests/resources/typechanges/.gitted/objects/da/e2709d638df52212b1f43ff61797ebfedfcc7c
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/e1/152adcb9adf37ec551ada9ba377ab53aec3bad b/tests/resources/typechanges/.gitted/objects/e1/152adcb9adf37ec551ada9ba377ab53aec3bad
index c68fdcfab..c68fdcfab 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/e1/152adcb9adf37ec551ada9ba377ab53aec3bad
+++ b/tests/resources/typechanges/.gitted/objects/e1/152adcb9adf37ec551ada9ba377ab53aec3bad
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/e4/ed436a9eb0f198cda722886a5f8d6d6c836b7b b/tests/resources/typechanges/.gitted/objects/e4/ed436a9eb0f198cda722886a5f8d6d6c836b7b
index c9229ba25..c9229ba25 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/e4/ed436a9eb0f198cda722886a5f8d6d6c836b7b
+++ b/tests/resources/typechanges/.gitted/objects/e4/ed436a9eb0f198cda722886a5f8d6d6c836b7b
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/typechanges/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
index 711223894..711223894 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
+++ b/tests/resources/typechanges/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/f2/0b79342712e0b2315647cd8227a573fd3bc46e b/tests/resources/typechanges/.gitted/objects/f2/0b79342712e0b2315647cd8227a573fd3bc46e
index 3962ba6b4..3962ba6b4 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/f2/0b79342712e0b2315647cd8227a573fd3bc46e
+++ b/tests/resources/typechanges/.gitted/objects/f2/0b79342712e0b2315647cd8227a573fd3bc46e
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/fd/e0147e3b59f381635a3b016e3fe6dacb70779d b/tests/resources/typechanges/.gitted/objects/fd/e0147e3b59f381635a3b016e3fe6dacb70779d
index e3663da9f..e3663da9f 100644
--- a/tests-clar/resources/typechanges/.gitted/objects/fd/e0147e3b59f381635a3b016e3fe6dacb70779d
+++ b/tests/resources/typechanges/.gitted/objects/fd/e0147e3b59f381635a3b016e3fe6dacb70779d
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/refs/heads/master b/tests/resources/typechanges/.gitted/refs/heads/master
index 546481a33..546481a33 100644
--- a/tests-clar/resources/typechanges/.gitted/refs/heads/master
+++ b/tests/resources/typechanges/.gitted/refs/heads/master
diff --git a/tests-clar/resources/typechanges/README.md b/tests/resources/typechanges/README.md
index 1f5a95a9f..1f5a95a9f 100644
--- a/tests-clar/resources/typechanges/README.md
+++ b/tests/resources/typechanges/README.md
diff --git a/tests-clar/resources/typechanges/gitmodules b/tests/resources/typechanges/gitmodules
index e69de29bb..e69de29bb 100644
--- a/tests-clar/resources/typechanges/gitmodules
+++ b/tests/resources/typechanges/gitmodules
diff --git a/tests/resources/unsymlinked.git/HEAD b/tests/resources/unsymlinked.git/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/unsymlinked.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests-clar/resources/unsymlinked.git/config b/tests/resources/unsymlinked.git/config
index f57351fd5..f57351fd5 100644
--- a/tests-clar/resources/unsymlinked.git/config
+++ b/tests/resources/unsymlinked.git/config
diff --git a/tests/resources/unsymlinked.git/description b/tests/resources/unsymlinked.git/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/unsymlinked.git/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests-clar/resources/unsymlinked.git/info/exclude b/tests/resources/unsymlinked.git/info/exclude
index 6d05881d3..6d05881d3 100644
--- a/tests-clar/resources/unsymlinked.git/info/exclude
+++ b/tests/resources/unsymlinked.git/info/exclude
diff --git a/tests-clar/resources/unsymlinked.git/objects/08/8b64704e0d6b8bd061dea879418cb5442a3fbf b/tests/resources/unsymlinked.git/objects/08/8b64704e0d6b8bd061dea879418cb5442a3fbf
index 953262fa9..953262fa9 100644
--- a/tests-clar/resources/unsymlinked.git/objects/08/8b64704e0d6b8bd061dea879418cb5442a3fbf
+++ b/tests/resources/unsymlinked.git/objects/08/8b64704e0d6b8bd061dea879418cb5442a3fbf
Binary files differ
diff --git a/tests-clar/resources/unsymlinked.git/objects/13/a5e939bca25940c069fd2169d993dba328e30b b/tests/resources/unsymlinked.git/objects/13/a5e939bca25940c069fd2169d993dba328e30b
index 91ec8fa5e..91ec8fa5e 100644
--- a/tests-clar/resources/unsymlinked.git/objects/13/a5e939bca25940c069fd2169d993dba328e30b
+++ b/tests/resources/unsymlinked.git/objects/13/a5e939bca25940c069fd2169d993dba328e30b
Binary files differ
diff --git a/tests-clar/resources/unsymlinked.git/objects/19/bf568e59e3a0b363cafb4106226e62d4a4c41c b/tests/resources/unsymlinked.git/objects/19/bf568e59e3a0b363cafb4106226e62d4a4c41c
index 94afd01e8..94afd01e8 100644
--- a/tests-clar/resources/unsymlinked.git/objects/19/bf568e59e3a0b363cafb4106226e62d4a4c41c
+++ b/tests/resources/unsymlinked.git/objects/19/bf568e59e3a0b363cafb4106226e62d4a4c41c
Binary files differ
diff --git a/tests-clar/resources/unsymlinked.git/objects/58/1fadd35b4cf320d102a152f918729011604773 b/tests/resources/unsymlinked.git/objects/58/1fadd35b4cf320d102a152f918729011604773
index 5b33d027c..5b33d027c 100644
--- a/tests-clar/resources/unsymlinked.git/objects/58/1fadd35b4cf320d102a152f918729011604773
+++ b/tests/resources/unsymlinked.git/objects/58/1fadd35b4cf320d102a152f918729011604773
Binary files differ
diff --git a/tests-clar/resources/unsymlinked.git/objects/5c/87b6791e8b13da658a14d1ef7e09b5dc3bac8c b/tests/resources/unsymlinked.git/objects/5c/87b6791e8b13da658a14d1ef7e09b5dc3bac8c
index 67eb14930..67eb14930 100644
--- a/tests-clar/resources/unsymlinked.git/objects/5c/87b6791e8b13da658a14d1ef7e09b5dc3bac8c
+++ b/tests/resources/unsymlinked.git/objects/5c/87b6791e8b13da658a14d1ef7e09b5dc3bac8c
Binary files differ
diff --git a/tests-clar/resources/unsymlinked.git/objects/6f/e5f5398af85fb3de8a6aba0339b6d3bfa26a27 b/tests/resources/unsymlinked.git/objects/6f/e5f5398af85fb3de8a6aba0339b6d3bfa26a27
index c1ea0de75..c1ea0de75 100644
--- a/tests-clar/resources/unsymlinked.git/objects/6f/e5f5398af85fb3de8a6aba0339b6d3bfa26a27
+++ b/tests/resources/unsymlinked.git/objects/6f/e5f5398af85fb3de8a6aba0339b6d3bfa26a27
Binary files differ
diff --git a/tests-clar/resources/unsymlinked.git/objects/7f/ccd75616ec188b8f1b23d67506a334cc34a49d b/tests/resources/unsymlinked.git/objects/7f/ccd75616ec188b8f1b23d67506a334cc34a49d
index 028505563..028505563 100644
--- a/tests-clar/resources/unsymlinked.git/objects/7f/ccd75616ec188b8f1b23d67506a334cc34a49d
+++ b/tests/resources/unsymlinked.git/objects/7f/ccd75616ec188b8f1b23d67506a334cc34a49d
Binary files differ
diff --git a/tests-clar/resources/unsymlinked.git/objects/80/6999882bf91d24241e4077906b9017605eb1f3 b/tests/resources/unsymlinked.git/objects/80/6999882bf91d24241e4077906b9017605eb1f3
index e866a75a6..e866a75a6 100644
--- a/tests-clar/resources/unsymlinked.git/objects/80/6999882bf91d24241e4077906b9017605eb1f3
+++ b/tests/resources/unsymlinked.git/objects/80/6999882bf91d24241e4077906b9017605eb1f3
Binary files differ
diff --git a/tests-clar/resources/unsymlinked.git/objects/83/7d176303c5005505ec1e4a30231c40930c0230 b/tests/resources/unsymlinked.git/objects/83/7d176303c5005505ec1e4a30231c40930c0230
index 189ab044d..189ab044d 100644
--- a/tests-clar/resources/unsymlinked.git/objects/83/7d176303c5005505ec1e4a30231c40930c0230
+++ b/tests/resources/unsymlinked.git/objects/83/7d176303c5005505ec1e4a30231c40930c0230
Binary files differ
diff --git a/tests-clar/resources/unsymlinked.git/objects/a8/595ccca04f40818ae0155c8f9c77a230e597b6 b/tests/resources/unsymlinked.git/objects/a8/595ccca04f40818ae0155c8f9c77a230e597b6
index a2ef6be45..a2ef6be45 100644
--- a/tests-clar/resources/unsymlinked.git/objects/a8/595ccca04f40818ae0155c8f9c77a230e597b6
+++ b/tests/resources/unsymlinked.git/objects/a8/595ccca04f40818ae0155c8f9c77a230e597b6
diff --git a/tests-clar/resources/unsymlinked.git/objects/cf/8f1cf5cce859c438d6cc067284cb5e161206e7 b/tests/resources/unsymlinked.git/objects/cf/8f1cf5cce859c438d6cc067284cb5e161206e7
index ec274cb1d..ec274cb1d 100644
--- a/tests-clar/resources/unsymlinked.git/objects/cf/8f1cf5cce859c438d6cc067284cb5e161206e7
+++ b/tests/resources/unsymlinked.git/objects/cf/8f1cf5cce859c438d6cc067284cb5e161206e7
Binary files differ
diff --git a/tests-clar/resources/unsymlinked.git/objects/d5/278d05c8607ec420bfee4cf219fbc0eeebfd6a b/tests/resources/unsymlinked.git/objects/d5/278d05c8607ec420bfee4cf219fbc0eeebfd6a
index c1b6a5101..c1b6a5101 100644
--- a/tests-clar/resources/unsymlinked.git/objects/d5/278d05c8607ec420bfee4cf219fbc0eeebfd6a
+++ b/tests/resources/unsymlinked.git/objects/d5/278d05c8607ec420bfee4cf219fbc0eeebfd6a
Binary files differ
diff --git a/tests-clar/resources/unsymlinked.git/objects/f4/e16fb76536591a41454194058d048d8e4dd2e9 b/tests/resources/unsymlinked.git/objects/f4/e16fb76536591a41454194058d048d8e4dd2e9
index ad751adbe..ad751adbe 100644
--- a/tests-clar/resources/unsymlinked.git/objects/f4/e16fb76536591a41454194058d048d8e4dd2e9
+++ b/tests/resources/unsymlinked.git/objects/f4/e16fb76536591a41454194058d048d8e4dd2e9
Binary files differ
diff --git a/tests-clar/resources/unsymlinked.git/objects/f9/e65619d93fdf2673882e0a261c5e93b1a84006 b/tests/resources/unsymlinked.git/objects/f9/e65619d93fdf2673882e0a261c5e93b1a84006
index f87cd42fb..f87cd42fb 100644
--- a/tests-clar/resources/unsymlinked.git/objects/f9/e65619d93fdf2673882e0a261c5e93b1a84006
+++ b/tests/resources/unsymlinked.git/objects/f9/e65619d93fdf2673882e0a261c5e93b1a84006
Binary files differ
diff --git a/tests-clar/resources/unsymlinked.git/refs/heads/exe-file b/tests/resources/unsymlinked.git/refs/heads/exe-file
index b96ef46ca..b96ef46ca 100644
--- a/tests-clar/resources/unsymlinked.git/refs/heads/exe-file
+++ b/tests/resources/unsymlinked.git/refs/heads/exe-file
diff --git a/tests-clar/resources/unsymlinked.git/refs/heads/master b/tests/resources/unsymlinked.git/refs/heads/master
index 96c17ab17..96c17ab17 100644
--- a/tests-clar/resources/unsymlinked.git/refs/heads/master
+++ b/tests/resources/unsymlinked.git/refs/heads/master
diff --git a/tests-clar/resources/unsymlinked.git/refs/heads/reg-file b/tests/resources/unsymlinked.git/refs/heads/reg-file
index b428c00d5..b428c00d5 100644
--- a/tests-clar/resources/unsymlinked.git/refs/heads/reg-file
+++ b/tests/resources/unsymlinked.git/refs/heads/reg-file
diff --git a/tests/revwalk/basic.c b/tests/revwalk/basic.c
new file mode 100644
index 000000000..6d55aed54
--- /dev/null
+++ b/tests/revwalk/basic.c
@@ -0,0 +1,254 @@
+#include "clar_libgit2.h"
+
+/*
+ * a4a7dce [0] Merge branch 'master' into br2
+ |\
+ | * 9fd738e [1] a fourth commit
+ | * 4a202b3 [2] a third commit
+ * | c47800c [3] branch commit one
+ |/
+ * 5b5b025 [5] another commit
+ * 8496071 [4] testing
+*/
+static const char *commit_head = "a4a7dce85cf63874e984719f4fdd239f5145052f";
+
+static const char *commit_ids[] = {
+ "a4a7dce85cf63874e984719f4fdd239f5145052f", /* 0 */
+ "9fd738e8f7967c078dceed8190330fc8648ee56a", /* 1 */
+ "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", /* 2 */
+ "c47800c7266a2be04c571c04d5a6614691ea99bd", /* 3 */
+ "8496071c1b46c854b31185ea97743be6a8774479", /* 4 */
+ "5b5b025afb0b4c913b4c338a42934a3863bf3644", /* 5 */
+};
+
+/* Careful: there are two possible topological sorts */
+static const int commit_sorting_topo[][6] = {
+ {0, 1, 2, 3, 5, 4}, {0, 3, 1, 2, 5, 4}
+};
+
+static const int commit_sorting_time[][6] = {
+ {0, 3, 1, 2, 5, 4}
+};
+
+static const int commit_sorting_topo_reverse[][6] = {
+ {4, 5, 3, 2, 1, 0}, {4, 5, 2, 1, 3, 0}
+};
+
+static const int commit_sorting_time_reverse[][6] = {
+ {4, 5, 2, 1, 3, 0}
+};
+
+static const int commit_sorting_segment[][6] = {
+ {1, 2, -1, -1, -1, -1}
+};
+
+#define commit_count 6
+static const int result_bytes = 24;
+
+
+static int get_commit_index(git_oid *raw_oid)
+{
+ int i;
+ char oid[40];
+
+ git_oid_fmt(oid, raw_oid);
+
+ for (i = 0; i < commit_count; ++i)
+ if (memcmp(oid, commit_ids[i], 40) == 0)
+ return i;
+
+ return -1;
+}
+
+static int test_walk_only(git_revwalk *walk,
+ const int possible_results[][commit_count], int results_count)
+{
+ git_oid oid;
+ int i;
+ int result_array[commit_count];
+
+ for (i = 0; i < commit_count; ++i)
+ result_array[i] = -1;
+
+ i = 0;
+ while (git_revwalk_next(&oid, walk) == 0) {
+ result_array[i++] = get_commit_index(&oid);
+ /*{
+ char str[41];
+ git_oid_fmt(str, &oid);
+ str[40] = 0;
+ printf(" %d) %s\n", i, str);
+ }*/
+ }
+
+ for (i = 0; i < results_count; ++i)
+ if (memcmp(possible_results[i],
+ result_array, result_bytes) == 0)
+ return 0;
+
+ return GIT_ERROR;
+}
+
+static int test_walk(git_revwalk *walk, const git_oid *root,
+ int flags, const int possible_results[][6], int results_count)
+{
+ git_revwalk_sorting(walk, flags);
+ git_revwalk_push(walk, root);
+
+ return test_walk_only(walk, possible_results, results_count);
+}
+
+static git_repository *_repo = NULL;
+static git_revwalk *_walk = NULL;
+static const char *_fixture = NULL;
+
+void test_revwalk_basic__initialize(void)
+{
+}
+
+void test_revwalk_basic__cleanup(void)
+{
+ git_revwalk_free(_walk);
+
+ if (_fixture)
+ cl_git_sandbox_cleanup();
+ else
+ git_repository_free(_repo);
+
+ _fixture = NULL;
+ _repo = NULL;
+ _walk = NULL;
+}
+
+static void revwalk_basic_setup_walk(const char *fixture)
+{
+ if (fixture) {
+ _fixture = fixture;
+ _repo = cl_git_sandbox_init(fixture);
+ } else {
+ cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git")));
+ }
+
+ cl_git_pass(git_revwalk_new(&_walk, _repo));
+}
+
+void test_revwalk_basic__sorting_modes(void)
+{
+ git_oid id;
+
+ revwalk_basic_setup_walk(NULL);
+
+ git_oid_fromstr(&id, commit_head);
+
+ cl_git_pass(test_walk(_walk, &id, GIT_SORT_TIME, commit_sorting_time, 1));
+ cl_git_pass(test_walk(_walk, &id, GIT_SORT_TOPOLOGICAL, commit_sorting_topo, 2));
+ cl_git_pass(test_walk(_walk, &id, GIT_SORT_TIME | GIT_SORT_REVERSE, commit_sorting_time_reverse, 1));
+ cl_git_pass(test_walk(_walk, &id, GIT_SORT_TOPOLOGICAL | GIT_SORT_REVERSE, commit_sorting_topo_reverse, 2));
+}
+
+void test_revwalk_basic__glob_heads(void)
+{
+ int i = 0;
+ git_oid oid;
+
+ revwalk_basic_setup_walk(NULL);
+
+ cl_git_pass(git_revwalk_push_glob(_walk, "heads"));
+
+ while (git_revwalk_next(&oid, _walk) == 0) {
+ i++;
+ }
+
+ /* git log --branches --oneline | wc -l => 14 */
+ cl_assert(i == 14);
+}
+
+void test_revwalk_basic__glob_heads_with_invalid(void)
+{
+ int i;
+ git_oid oid;
+
+ revwalk_basic_setup_walk("testrepo");
+
+ cl_git_mkfile("testrepo/.git/refs/heads/garbage", "not-a-ref");
+ cl_git_pass(git_revwalk_push_glob(_walk, "heads"));
+
+ for (i = 0; !git_revwalk_next(&oid, _walk); ++i)
+ /* walking */;
+
+ /* git log --branches --oneline | wc -l => 16 */
+ cl_assert_equal_i(17, i);
+}
+
+void test_revwalk_basic__push_head(void)
+{
+ int i = 0;
+ git_oid oid;
+
+ revwalk_basic_setup_walk(NULL);
+
+ cl_git_pass(git_revwalk_push_head(_walk));
+
+ while (git_revwalk_next(&oid, _walk) == 0) {
+ i++;
+ }
+
+ /* git log HEAD --oneline | wc -l => 7 */
+ cl_assert(i == 7);
+}
+
+void test_revwalk_basic__push_head_hide_ref(void)
+{
+ int i = 0;
+ git_oid oid;
+
+ revwalk_basic_setup_walk(NULL);
+
+ cl_git_pass(git_revwalk_push_head(_walk));
+ cl_git_pass(git_revwalk_hide_ref(_walk, "refs/heads/packed-test"));
+
+ while (git_revwalk_next(&oid, _walk) == 0) {
+ i++;
+ }
+
+ /* git log HEAD --oneline --not refs/heads/packed-test | wc -l => 4 */
+ cl_assert(i == 4);
+}
+
+void test_revwalk_basic__push_head_hide_ref_nobase(void)
+{
+ int i = 0;
+ git_oid oid;
+
+ revwalk_basic_setup_walk(NULL);
+
+ cl_git_pass(git_revwalk_push_head(_walk));
+ cl_git_pass(git_revwalk_hide_ref(_walk, "refs/heads/packed"));
+
+ while (git_revwalk_next(&oid, _walk) == 0) {
+ i++;
+ }
+
+ /* git log HEAD --oneline --not refs/heads/packed | wc -l => 7 */
+ cl_assert(i == 7);
+}
+
+void test_revwalk_basic__disallow_non_commit(void)
+{
+ git_oid oid;
+
+ revwalk_basic_setup_walk(NULL);
+
+ cl_git_pass(git_oid_fromstr(&oid, "521d87c1ec3aef9824daf6d96cc0ae3710766d91"));
+ cl_git_fail(git_revwalk_push(_walk, &oid));
+}
+
+void test_revwalk_basic__push_range(void)
+{
+ revwalk_basic_setup_walk(NULL);
+
+ git_revwalk_reset(_walk);
+ git_revwalk_sorting(_walk, 0);
+ cl_git_pass(git_revwalk_push_range(_walk, "9fd738e~2..9fd738e"));
+ cl_git_pass(test_walk_only(_walk, commit_sorting_segment, 1));
+}
diff --git a/tests/revwalk/mergebase.c b/tests/revwalk/mergebase.c
new file mode 100644
index 000000000..2d01647fd
--- /dev/null
+++ b/tests/revwalk/mergebase.c
@@ -0,0 +1,392 @@
+#include "clar_libgit2.h"
+#include "vector.h"
+#include <stdarg.h>
+
+static git_repository *_repo;
+static git_repository *_repo2;
+
+void test_revwalk_mergebase__initialize(void)
+{
+ cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_repository_open(&_repo2, cl_fixture("twowaymerge.git")));
+}
+
+void test_revwalk_mergebase__cleanup(void)
+{
+ git_repository_free(_repo);
+ _repo = NULL;
+
+ git_repository_free(_repo2);
+ _repo2 = NULL;
+}
+
+void test_revwalk_mergebase__single1(void)
+{
+ git_oid result, one, two, expected;
+ size_t ahead, behind;
+
+ cl_git_pass(git_oid_fromstr(&one, "c47800c7266a2be04c571c04d5a6614691ea99bd "));
+ cl_git_pass(git_oid_fromstr(&two, "9fd738e8f7967c078dceed8190330fc8648ee56a"));
+ cl_git_pass(git_oid_fromstr(&expected, "5b5b025afb0b4c913b4c338a42934a3863bf3644"));
+
+ cl_git_pass(git_merge_base(&result, _repo, &one, &two));
+ cl_assert(git_oid_cmp(&result, &expected) == 0);
+
+ cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, &one, &two));
+ cl_assert_equal_sz(ahead, 2);
+ cl_assert_equal_sz(behind, 1);
+
+ cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, &two, &one));
+ cl_assert_equal_sz(ahead, 1);
+ cl_assert_equal_sz(behind, 2);
+}
+
+void test_revwalk_mergebase__single2(void)
+{
+ git_oid result, one, two, expected;
+ size_t ahead, behind;
+
+ cl_git_pass(git_oid_fromstr(&one, "763d71aadf09a7951596c9746c024e7eece7c7af"));
+ cl_git_pass(git_oid_fromstr(&two, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"));
+ cl_git_pass(git_oid_fromstr(&expected, "c47800c7266a2be04c571c04d5a6614691ea99bd"));
+
+ cl_git_pass(git_merge_base(&result, _repo, &one, &two));
+ cl_assert(git_oid_cmp(&result, &expected) == 0);
+
+ cl_git_pass(git_graph_ahead_behind( &ahead, &behind, _repo, &one, &two));
+ cl_assert_equal_sz(ahead, 4);
+ cl_assert_equal_sz(behind, 1);
+
+ cl_git_pass(git_graph_ahead_behind( &ahead, &behind, _repo, &two, &one));
+ cl_assert_equal_sz(ahead, 1);
+ cl_assert_equal_sz(behind, 4);
+}
+
+void test_revwalk_mergebase__merged_branch(void)
+{
+ git_oid result, one, two, expected;
+ size_t ahead, behind;
+
+ cl_git_pass(git_oid_fromstr(&one, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"));
+ cl_git_pass(git_oid_fromstr(&two, "9fd738e8f7967c078dceed8190330fc8648ee56a"));
+ cl_git_pass(git_oid_fromstr(&expected, "9fd738e8f7967c078dceed8190330fc8648ee56a"));
+
+ cl_git_pass(git_merge_base(&result, _repo, &one, &two));
+ cl_assert(git_oid_cmp(&result, &expected) == 0);
+
+ cl_git_pass(git_merge_base(&result, _repo, &two, &one));
+ cl_assert(git_oid_cmp(&result, &expected) == 0);
+
+ cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, &one, &two));
+ cl_assert_equal_sz(ahead, 0);
+ cl_assert_equal_sz(behind, 3);
+
+ cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, &two, &one));
+ cl_assert_equal_sz(ahead, 3);
+ cl_assert_equal_sz(behind, 0);
+}
+
+void test_revwalk_mergebase__two_way_merge(void)
+{
+ git_oid one, two;
+ size_t ahead, behind;
+
+ cl_git_pass(git_oid_fromstr(&one, "9b219343610c88a1187c996d0dc58330b55cee28"));
+ cl_git_pass(git_oid_fromstr(&two, "a953a018c5b10b20c86e69fef55ebc8ad4c5a417"));
+ cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo2, &one, &two));
+
+ cl_assert_equal_sz(ahead, 2);
+ cl_assert_equal_sz(behind, 8);
+
+ cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo2, &two, &one));
+
+ cl_assert_equal_sz(ahead, 8);
+ cl_assert_equal_sz(behind, 2);
+}
+
+void test_revwalk_mergebase__no_common_ancestor_returns_ENOTFOUND(void)
+{
+ git_oid result, one, two;
+ size_t ahead, behind;
+ int error;
+
+ cl_git_pass(git_oid_fromstr(&one, "763d71aadf09a7951596c9746c024e7eece7c7af"));
+ cl_git_pass(git_oid_fromstr(&two, "e90810b8df3e80c413d903f631643c716887138d"));
+
+ error = git_merge_base(&result, _repo, &one, &two);
+ cl_git_fail(error);
+
+ cl_assert_equal_i(GIT_ENOTFOUND, error);
+
+ cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, &one, &two));
+ cl_assert_equal_sz(2, ahead);
+ cl_assert_equal_sz(4, behind);
+}
+
+void test_revwalk_mergebase__prefer_youngest_merge_base(void)
+{
+ git_oid result, one, two, expected;
+
+ cl_git_pass(git_oid_fromstr(&one, "a4a7dce85cf63874e984719f4fdd239f5145052f "));
+ cl_git_pass(git_oid_fromstr(&two, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
+ cl_git_pass(git_oid_fromstr(&expected, "c47800c7266a2be04c571c04d5a6614691ea99bd"));
+
+ cl_git_pass(git_merge_base(&result, _repo, &one, &two));
+ cl_assert(git_oid_cmp(&result, &expected) == 0);
+}
+
+void test_revwalk_mergebase__no_off_by_one_missing(void)
+{
+ git_oid result, one, two;
+
+ cl_git_pass(git_oid_fromstr(&one, "1a443023183e3f2bfbef8ac923cd81c1018a18fd"));
+ cl_git_pass(git_oid_fromstr(&two, "9f13f7d0a9402c681f91dc590cf7b5470e6a77d2"));
+ cl_git_pass(git_merge_base(&result, _repo, &one, &two));
+}
+
+static void assert_mergebase_many(const char *expected_sha, int count, ...)
+{
+ va_list ap;
+ int i;
+ git_oid *oids;
+ git_oid oid, expected;
+ char *partial_oid;
+ git_object *object;
+
+ oids = git__malloc(count * sizeof(git_oid));
+ cl_assert(oids != NULL);
+
+ memset(oids, 0x0, count * sizeof(git_oid));
+
+ va_start(ap, count);
+
+ for (i = 0; i < count; ++i) {
+ partial_oid = va_arg(ap, char *);
+ cl_git_pass(git_oid_fromstrn(&oid, partial_oid, strlen(partial_oid)));
+
+ cl_git_pass(git_object_lookup_prefix(&object, _repo, &oid, strlen(partial_oid), GIT_OBJ_COMMIT));
+ git_oid_cpy(&oids[i], git_object_id(object));
+ git_object_free(object);
+ }
+
+ va_end(ap);
+
+ if (expected_sha == NULL)
+ cl_assert_equal_i(GIT_ENOTFOUND, git_merge_base_many(&oid, _repo, count, oids));
+ else {
+ cl_git_pass(git_merge_base_many(&oid, _repo, count, oids));
+ cl_git_pass(git_oid_fromstr(&expected, expected_sha));
+
+ cl_assert(git_oid_cmp(&expected, &oid) == 0);
+ }
+
+ git__free(oids);
+}
+
+void test_revwalk_mergebase__many_no_common_ancestor_returns_ENOTFOUND(void)
+{
+ assert_mergebase_many(NULL, 3, "41bc8c", "e90810", "a65fed");
+ assert_mergebase_many(NULL, 3, "e90810", "41bc8c", "a65fed");
+ assert_mergebase_many(NULL, 3, "e90810", "a65fed", "41bc8c");
+ assert_mergebase_many(NULL, 3, "a65fed", "e90810", "41bc8c");
+ assert_mergebase_many(NULL, 3, "a65fed", "e90810", "41bc8c");
+ assert_mergebase_many(NULL, 3, "a65fed", "41bc8c", "e90810");
+
+ assert_mergebase_many(NULL, 3, "e90810", "763d71", "a65fed");
+}
+
+void test_revwalk_mergebase__many_merge_branch(void)
+{
+ assert_mergebase_many("c47800c7266a2be04c571c04d5a6614691ea99bd", 3, "a65fed", "763d71", "849607");
+
+ assert_mergebase_many("c47800c7266a2be04c571c04d5a6614691ea99bd", 3, "763d71", "e90810", "a65fed");
+ assert_mergebase_many("c47800c7266a2be04c571c04d5a6614691ea99bd", 3, "763d71", "a65fed", "e90810");
+
+ assert_mergebase_many("c47800c7266a2be04c571c04d5a6614691ea99bd", 3, "a65fed", "763d71", "849607");
+ assert_mergebase_many("c47800c7266a2be04c571c04d5a6614691ea99bd", 3, "a65fed", "849607", "763d71");
+ assert_mergebase_many("8496071c1b46c854b31185ea97743be6a8774479", 3, "849607", "a65fed", "763d71");
+
+ assert_mergebase_many("5b5b025afb0b4c913b4c338a42934a3863bf3644", 5, "5b5b02", "763d71", "a4a7dc", "a65fed", "41bc8c");
+}
+
+/*
+ * testrepo.git $ git log --graph --all
+ * * commit 763d71aadf09a7951596c9746c024e7eece7c7af
+ * | Author: nulltoken <emeric.fermas@gmail.com>
+ * | Date: Sun Oct 9 12:54:47 2011 +0200
+ * |
+ * | Add some files into subdirectories
+ * |
+ * | * commit a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+ * | | Author: Scott Chacon <schacon@gmail.com>
+ * | | Date: Tue Aug 9 19:33:46 2011 -0700
+ * | |
+ * | * commit be3563ae3f795b2b4353bcce3a527ad0a4f7f644
+ * | |\ Merge: 9fd738e c47800c
+ * | |/ Author: Scott Chacon <schacon@gmail.com>
+ * |/| Date: Tue May 25 11:58:27 2010 -0700
+ * | |
+ * | | Merge branch 'br2'
+ * | |
+ * | | * commit e90810b8df3e80c413d903f631643c716887138d
+ * | | | Author: Vicent Marti <tanoku@gmail.com>
+ * | | | Date: Thu Aug 5 18:42:20 2010 +0200
+ * | | |
+ * | | | Test commit 2
+ * | | |
+ * | | * commit 6dcf9bf7541ee10456529833502442f385010c3d
+ * | | Author: Vicent Marti <tanoku@gmail.com>
+ * | | Date: Thu Aug 5 18:41:33 2010 +0200
+ * | |
+ * | | Test commit 1
+ * | |
+ * | | * commit a4a7dce85cf63874e984719f4fdd239f5145052f
+ * | | |\ Merge: c47800c 9fd738e
+ * | |/ / Author: Scott Chacon <schacon@gmail.com>
+ * |/| / Date: Tue May 25 12:00:23 2010 -0700
+ * | |/
+ * | | Merge branch 'master' into br2
+ * | |
+ * | * commit 9fd738e8f7967c078dceed8190330fc8648ee56a
+ * | | Author: Scott Chacon <schacon@gmail.com>
+ * | | Date: Mon May 24 10:19:19 2010 -0700
+ * | |
+ * | | a fourth commit
+ * | |
+ * | * commit 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+ * | | Author: Scott Chacon <schacon@gmail.com>
+ * | | Date: Mon May 24 10:19:04 2010 -0700
+ * | |
+ * | | a third commit
+ * | |
+ * * | commit c47800c7266a2be04c571c04d5a6614691ea99bd
+ * |/ Author: Scott Chacon <schacon@gmail.com>
+ * | Date: Tue May 25 11:58:14 2010 -0700
+ * |
+ * | branch commit one
+ * |
+ * * commit 5b5b025afb0b4c913b4c338a42934a3863bf3644
+ * | Author: Scott Chacon <schacon@gmail.com>
+ * | Date: Tue May 11 13:38:42 2010 -0700
+ * |
+ * | another commit
+ * |
+ * * commit 8496071c1b46c854b31185ea97743be6a8774479
+ * Author: Scott Chacon <schacon@gmail.com>
+ * Date: Sat May 8 16:13:06 2010 -0700
+ *
+ * testing
+ *
+ * * commit 41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9
+ * | Author: Scott Chacon <schacon@gmail.com>
+ * | Date: Tue May 11 13:40:41 2010 -0700
+ * |
+ * | packed commit two
+ * |
+ * * commit 5001298e0c09ad9c34e4249bc5801c75e9754fa5
+ * Author: Scott Chacon <schacon@gmail.com>
+ * Date: Tue May 11 13:40:23 2010 -0700
+ *
+ * packed commit one
+ */
+
+/*
+ * twowaymerge.git $ git log --graph --all
+ * * commit 9b219343610c88a1187c996d0dc58330b55cee28
+ * |\ Merge: c37a783 2224e19
+ * | | Author: Scott J. Goldman <scottjg@github.com>
+ * | | Date: Tue Nov 27 20:31:04 2012 -0800
+ * | |
+ * | | Merge branch 'first-branch' into second-branch
+ * | |
+ * | * commit 2224e191514cb4bd8c566d80dac22dfcb1e9bb83
+ * | | Author: Scott J. Goldman <scottjg@github.com>
+ * | | Date: Tue Nov 27 20:28:51 2012 -0800
+ * | |
+ * | | j
+ * | |
+ * | * commit a41a49f8f5cd9b6cb14a076bf8394881ed0b4d19
+ * | | Author: Scott J. Goldman <scottjg@github.com>
+ * | | Date: Tue Nov 27 20:28:39 2012 -0800
+ * | |
+ * | | i
+ * | |
+ * | * commit 82bf9a1a10a4b25c1f14c9607b60970705e92545
+ * | | Author: Scott J. Goldman <scottjg@github.com>
+ * | | Date: Tue Nov 27 20:28:28 2012 -0800
+ * | |
+ * | | h
+ * | |
+ * * | commit c37a783c20d92ac92362a78a32860f7eebf938ef
+ * | | Author: Scott J. Goldman <scottjg@github.com>
+ * | | Date: Tue Nov 27 20:30:57 2012 -0800
+ * | |
+ * | | n
+ * | |
+ * * | commit 8b82fb1794cb1c8c7f172ec730a4c2db0ae3e650
+ * | | Author: Scott J. Goldman <scottjg@github.com>
+ * | | Date: Tue Nov 27 20:30:43 2012 -0800
+ * | |
+ * | | m
+ * | |
+ * * | commit 6ab5d28acbf3c3bdff276f7ccfdf29c1520e542f
+ * | | Author: Scott J. Goldman <scottjg@github.com>
+ * | | Date: Tue Nov 27 20:30:38 2012 -0800
+ * | |
+ * | | l
+ * | |
+ * * | commit 7b8c336c45fc6895c1c60827260fe5d798e5d247
+ * | | Author: Scott J. Goldman <scottjg@github.com>
+ * | | Date: Tue Nov 27 20:30:24 2012 -0800
+ * | |
+ * | | k
+ * | |
+ * | | * commit 1c30b88f5f3ee66d78df6520a7de9e89b890818b
+ * | | | Author: Scott J. Goldman <scottjg@github.com>
+ * | | | Date: Tue Nov 27 20:28:10 2012 -0800
+ * | | |
+ * | | | e
+ * | | |
+ * | | * commit 42b7311aa626e712891940c1ec5d5cba201946a4
+ * | | | Author: Scott J. Goldman <scottjg@github.com>
+ * | | | Date: Tue Nov 27 20:28:06 2012 -0800
+ * | | |
+ * | | | d
+ * | | |
+ * | | * commit a953a018c5b10b20c86e69fef55ebc8ad4c5a417
+ * | | |\ Merge: bd1732c cdf97fd
+ * | | |/ Author: Scott J. Goldman <scottjg@github.com>
+ * | |/| Date: Tue Nov 27 20:26:43 2012 -0800
+ * | | |
+ * | | | Merge branch 'first-branch'
+ * | | |
+ * | * | commit cdf97fd3bb48eb3827638bb33d208f5fd32d0aa6
+ * | | | Author: Scott J. Goldman <scottjg@github.com>
+ * | | | Date: Tue Nov 27 20:24:46 2012 -0800
+ * | | |
+ * | | | g
+ * | | |
+ * | * | commit ef0488f0b722f0be8bcb90a7730ac7efafd1d694
+ * | | | Author: Scott J. Goldman <scottjg@github.com>
+ * | | | Date: Tue Nov 27 20:24:39 2012 -0800
+ * | | |
+ * | | | f
+ * | | |
+ * | | * commit bd1732c43c68d712ad09e1d872b9be6d4b9efdc4
+ * | |/ Author: Scott J. Goldman <scottjg@github.com>
+ * | | Date: Tue Nov 27 17:43:58 2012 -0800
+ * | |
+ * | | c
+ * | |
+ * | * commit 0c8a3f1f3d5f421cf83048c7c73ee3b55a5e0f29
+ * |/ Author: Scott J. Goldman <scottjg@github.com>
+ * | Date: Tue Nov 27 17:43:48 2012 -0800
+ * |
+ * | b
+ * |
+ * * commit 1f4c0311a24b63f6fc209a59a1e404942d4a5006
+ * Author: Scott J. Goldman <scottjg@github.com>
+ * Date: Tue Nov 27 17:43:41 2012 -0800
+ *
+ * a
+ */
diff --git a/tests-clar/revwalk/signatureparsing.c b/tests/revwalk/signatureparsing.c
index 5c7d8813d..5c7d8813d 100644
--- a/tests-clar/revwalk/signatureparsing.c
+++ b/tests/revwalk/signatureparsing.c
diff --git a/tests/revwalk/simplify.c b/tests/revwalk/simplify.c
new file mode 100644
index 000000000..81c19d366
--- /dev/null
+++ b/tests/revwalk/simplify.c
@@ -0,0 +1,55 @@
+#include "clar_libgit2.h"
+
+void test_revwalk_simplify__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+/*
+ * a4a7dce [0] Merge branch 'master' into br2
+ |\
+ | * 9fd738e [1] a fourth commit
+ | * 4a202b3 [2] a third commit
+ * | c47800c [3] branch commit one
+ |/
+ * 5b5b025 [5] another commit
+ * 8496071 [4] testing
+*/
+static const char *commit_head = "a4a7dce85cf63874e984719f4fdd239f5145052f";
+
+static const char *expected_str[] = {
+ "a4a7dce85cf63874e984719f4fdd239f5145052f", /* 0 */
+ "c47800c7266a2be04c571c04d5a6614691ea99bd", /* 3 */
+ "8496071c1b46c854b31185ea97743be6a8774479", /* 4 */
+ "5b5b025afb0b4c913b4c338a42934a3863bf3644", /* 5 */
+};
+
+void test_revwalk_simplify__first_parent(void)
+{
+ git_repository *repo;
+ git_revwalk *walk;
+ git_oid id, expected[4];
+ int i, error;
+
+ for (i = 0; i < 4; i++) {
+ git_oid_fromstr(&expected[i], expected_str[i]);
+ }
+
+ repo = cl_git_sandbox_init("testrepo.git");
+ cl_git_pass(git_revwalk_new(&walk, repo));
+
+ git_oid_fromstr(&id, commit_head);
+ cl_git_pass(git_revwalk_push(walk, &id));
+ git_revwalk_simplify_first_parent(walk);
+
+ i = 0;
+ while ((error = git_revwalk_next(&id, walk)) == 0) {
+ git_oid_cmp(&id, &expected[i]);
+ i++;
+ }
+
+ cl_assert_equal_i(i, 4);
+ cl_assert_equal_i(error, GIT_ITEROVER);
+
+ git_revwalk_free(walk);
+}
diff --git a/tests/stash/drop.c b/tests/stash/drop.c
new file mode 100644
index 000000000..63ff0377c
--- /dev/null
+++ b/tests/stash/drop.c
@@ -0,0 +1,174 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+#include "stash_helpers.h"
+#include "refs.h"
+
+static git_repository *repo;
+static git_signature *signature;
+
+void test_stash_drop__initialize(void)
+{
+ cl_git_pass(git_repository_init(&repo, "stash", 0));
+ cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60)); /* Wed Dec 14 08:29:03 2011 +0100 */
+}
+
+void test_stash_drop__cleanup(void)
+{
+ git_signature_free(signature);
+ signature = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+
+ cl_git_pass(git_futils_rmdir_r("stash", NULL, GIT_RMDIR_REMOVE_FILES));
+}
+
+void test_stash_drop__cannot_drop_from_an_empty_stash(void)
+{
+ cl_git_fail_with(git_stash_drop(repo, 0), GIT_ENOTFOUND);
+}
+
+static void push_three_states(void)
+{
+ git_oid oid;
+ git_index *index;
+
+ cl_git_mkfile("stash/zero.txt", "content\n");
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_add_bypath(index, "zero.txt"));
+ cl_repo_commit_from_index(NULL, repo, signature, 0, "Initial commit");
+ cl_assert(git_path_exists("stash/zero.txt"));
+ git_index_free(index);
+
+ cl_git_mkfile("stash/one.txt", "content\n");
+ cl_git_pass(git_stash_save(
+ &oid, repo, signature, "First", GIT_STASH_INCLUDE_UNTRACKED));
+ cl_assert(!git_path_exists("stash/one.txt"));
+ cl_assert(git_path_exists("stash/zero.txt"));
+
+ cl_git_mkfile("stash/two.txt", "content\n");
+ cl_git_pass(git_stash_save(
+ &oid, repo, signature, "Second", GIT_STASH_INCLUDE_UNTRACKED));
+ cl_assert(!git_path_exists("stash/two.txt"));
+ cl_assert(git_path_exists("stash/zero.txt"));
+
+ cl_git_mkfile("stash/three.txt", "content\n");
+ cl_git_pass(git_stash_save(
+ &oid, repo, signature, "Third", GIT_STASH_INCLUDE_UNTRACKED));
+ cl_assert(!git_path_exists("stash/three.txt"));
+ cl_assert(git_path_exists("stash/zero.txt"));
+}
+
+void test_stash_drop__cannot_drop_a_non_existing_stashed_state(void)
+{
+ push_three_states();
+
+ cl_git_fail_with(git_stash_drop(repo, 666), GIT_ENOTFOUND);
+ cl_git_fail_with(git_stash_drop(repo, 42), GIT_ENOTFOUND);
+ cl_git_fail_with(git_stash_drop(repo, 3), GIT_ENOTFOUND);
+}
+
+void test_stash_drop__can_purge_the_stash_from_the_top(void)
+{
+ push_three_states();
+
+ cl_git_pass(git_stash_drop(repo, 0));
+ cl_git_pass(git_stash_drop(repo, 0));
+ cl_git_pass(git_stash_drop(repo, 0));
+
+ cl_git_fail_with(git_stash_drop(repo, 0), GIT_ENOTFOUND);
+}
+
+void test_stash_drop__can_purge_the_stash_from_the_bottom(void)
+{
+ push_three_states();
+
+ cl_git_pass(git_stash_drop(repo, 2));
+ cl_git_pass(git_stash_drop(repo, 1));
+ cl_git_pass(git_stash_drop(repo, 0));
+
+ cl_git_fail_with(git_stash_drop(repo, 0), GIT_ENOTFOUND);
+}
+
+void test_stash_drop__dropping_an_entry_rewrites_reflog_history(void)
+{
+ git_reference *stash;
+ git_reflog *reflog;
+ const git_reflog_entry *entry;
+ git_oid oid;
+ size_t count;
+
+ push_three_states();
+
+ cl_git_pass(git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE));
+
+ cl_git_pass(git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE));
+ entry = git_reflog_entry_byindex(reflog, 1);
+
+ git_oid_cpy(&oid, git_reflog_entry_id_old(entry));
+ count = git_reflog_entrycount(reflog);
+
+ git_reflog_free(reflog);
+
+ cl_git_pass(git_stash_drop(repo, 1));
+
+ cl_git_pass(git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE));
+ entry = git_reflog_entry_byindex(reflog, 0);
+
+ cl_assert_equal_i(0, git_oid_cmp(&oid, git_reflog_entry_id_old(entry)));
+ cl_assert_equal_sz(count - 1, git_reflog_entrycount(reflog));
+
+ git_reflog_free(reflog);
+
+ git_reference_free(stash);
+}
+
+void test_stash_drop__dropping_the_last_entry_removes_the_stash(void)
+{
+ git_reference *stash;
+
+ push_three_states();
+
+ cl_git_pass(git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE));
+ git_reference_free(stash);
+
+ cl_git_pass(git_stash_drop(repo, 0));
+ cl_git_pass(git_stash_drop(repo, 0));
+ cl_git_pass(git_stash_drop(repo, 0));
+
+ cl_git_fail_with(
+ git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE), GIT_ENOTFOUND);
+}
+
+void retrieve_top_stash_id(git_oid *out)
+{
+ git_object *top_stash;
+
+ cl_git_pass(git_revparse_single(&top_stash, repo, "stash@{0}"));
+ cl_git_pass(git_reference_name_to_id(out, repo, GIT_REFS_STASH_FILE));
+
+ cl_assert_equal_i(true, git_oid_cmp(out, git_object_id(top_stash)) == 0);
+
+ git_object_free(top_stash);
+}
+
+void test_stash_drop__dropping_the_top_stash_updates_the_stash_reference(void)
+{
+ git_object *next_top_stash;
+ git_oid oid;
+
+ push_three_states();
+
+ retrieve_top_stash_id(&oid);
+
+ cl_git_pass(git_revparse_single(&next_top_stash, repo, "stash@{1}"));
+ cl_assert(git_oid_cmp(&oid, git_object_id(next_top_stash)) != 0);
+
+ cl_git_pass(git_stash_drop(repo, 0));
+
+ retrieve_top_stash_id(&oid);
+
+ cl_git_pass(git_oid_cmp(&oid, git_object_id(next_top_stash)));
+
+ git_object_free(next_top_stash);
+}
diff --git a/tests-clar/stash/foreach.c b/tests/stash/foreach.c
index f1983625f..f1983625f 100644
--- a/tests-clar/stash/foreach.c
+++ b/tests/stash/foreach.c
diff --git a/tests/stash/save.c b/tests/stash/save.c
new file mode 100644
index 000000000..3d92b26bd
--- /dev/null
+++ b/tests/stash/save.c
@@ -0,0 +1,354 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+#include "stash_helpers.h"
+
+static git_repository *repo;
+static git_signature *signature;
+static git_oid stash_tip_oid;
+
+/*
+ * Friendly reminder, in order to ease the reading of the following tests:
+ *
+ * "stash" points to the worktree commit
+ * "stash^1" points to the base commit (HEAD when the stash was created)
+ * "stash^2" points to the index commit
+ * "stash^3" points to the untracked commit
+ */
+
+void test_stash_save__initialize(void)
+{
+ cl_git_pass(git_repository_init(&repo, "stash", 0));
+ cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60)); /* Wed Dec 14 08:29:03 2011 +0100 */
+
+ setup_stash(repo, signature);
+}
+
+void test_stash_save__cleanup(void)
+{
+ git_signature_free(signature);
+ signature = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+
+ cl_git_pass(git_futils_rmdir_r("stash", NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_fixture_cleanup("sorry-it-is-a-non-bare-only-party");
+}
+
+static void assert_object_oid(const char* revision, const char* expected_oid, git_otype type)
+{
+ int result;
+ git_object *obj;
+
+ result = git_revparse_single(&obj, repo, revision);
+
+ if (!expected_oid) {
+ cl_assert_equal_i(GIT_ENOTFOUND, result);
+ return;
+ } else
+ cl_assert_equal_i(0, result);
+
+ cl_git_pass(git_oid_streq(git_object_id(obj), expected_oid));
+ cl_assert_equal_i(type, git_object_type(obj));
+ git_object_free(obj);
+}
+
+static void assert_blob_oid(const char* revision, const char* expected_oid)
+{
+ assert_object_oid(revision, expected_oid, GIT_OBJ_BLOB);
+}
+
+void test_stash_save__does_not_keep_index_by_default(void)
+{
+/*
+$ git stash
+
+$ git show refs/stash:what
+see you later
+
+$ git show refs/stash:how
+not so small and
+
+$ git show refs/stash:who
+funky world
+
+$ git show refs/stash:when
+fatal: Path 'when' exists on disk, but not in 'stash'.
+
+$ git show refs/stash^2:what
+goodbye
+
+$ git show refs/stash^2:how
+not so small and
+
+$ git show refs/stash^2:who
+world
+
+$ git show refs/stash^2:when
+fatal: Path 'when' exists on disk, but not in 'stash^2'.
+
+$ git status --short
+?? when
+
+*/
+ unsigned int status;
+
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
+ cl_git_pass(git_status_file(&status, repo, "when"));
+
+ assert_blob_oid("refs/stash:what", "bc99dc98b3eba0e9157e94769cd4d49cb49de449"); /* see you later */
+ assert_blob_oid("refs/stash:how", "e6d64adb2c7f3eb8feb493b556cc8070dca379a3"); /* not so small and */
+ assert_blob_oid("refs/stash:who", "a0400d4954659306a976567af43125a0b1aa8595"); /* funky world */
+ assert_blob_oid("refs/stash:when", NULL);
+ assert_blob_oid("refs/stash:just.ignore", NULL);
+
+ assert_blob_oid("refs/stash^2:what", "dd7e1c6f0fefe118f0b63d9f10908c460aa317a6"); /* goodbye */
+ assert_blob_oid("refs/stash^2:how", "e6d64adb2c7f3eb8feb493b556cc8070dca379a3"); /* not so small and */
+ assert_blob_oid("refs/stash^2:who", "cc628ccd10742baea8241c5924df992b5c019f71"); /* world */
+ assert_blob_oid("refs/stash^2:when", NULL);
+ assert_blob_oid("refs/stash^2:just.ignore", NULL);
+
+ assert_blob_oid("refs/stash^3", NULL);
+
+ cl_assert_equal_i(GIT_STATUS_WT_NEW, status);
+}
+
+void test_stash_save__can_keep_index(void)
+{
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_KEEP_INDEX));
+
+ assert_status(repo, "what", GIT_STATUS_INDEX_MODIFIED);
+ assert_status(repo, "how", GIT_STATUS_INDEX_MODIFIED);
+ assert_status(repo, "who", GIT_STATUS_CURRENT);
+ assert_status(repo, "when", GIT_STATUS_WT_NEW);
+ assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
+}
+
+static void assert_commit_message_contains(const char *revision, const char *fragment)
+{
+ git_commit *commit;
+
+ cl_git_pass(git_revparse_single((git_object**)&commit, repo, revision));
+
+ cl_assert(strstr(git_commit_message(commit), fragment) != NULL);
+
+ git_commit_free(commit);
+}
+
+void test_stash_save__can_include_untracked_files(void)
+{
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
+
+ assert_commit_message_contains("refs/stash^3", "untracked files on master: ");
+
+ assert_blob_oid("refs/stash^3:what", NULL);
+ assert_blob_oid("refs/stash^3:how", NULL);
+ assert_blob_oid("refs/stash^3:who", NULL);
+ assert_blob_oid("refs/stash^3:when", "b6ed15e81e2593d7bb6265eb4a991d29dc3e628b");
+ assert_blob_oid("refs/stash^3:just.ignore", NULL);
+}
+
+void test_stash_save__can_include_untracked_and_ignored_files(void)
+{
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED | GIT_STASH_INCLUDE_IGNORED));
+
+ assert_commit_message_contains("refs/stash^3", "untracked files on master: ");
+
+ assert_blob_oid("refs/stash^3:what", NULL);
+ assert_blob_oid("refs/stash^3:how", NULL);
+ assert_blob_oid("refs/stash^3:who", NULL);
+ assert_blob_oid("refs/stash^3:when", "b6ed15e81e2593d7bb6265eb4a991d29dc3e628b");
+ assert_blob_oid("refs/stash^3:just.ignore", "78925fb1236b98b37a35e9723033e627f97aa88b");
+}
+
+#define MESSAGE "Look Ma! I'm on TV!"
+void test_stash_save__can_accept_a_message(void)
+{
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, MESSAGE, GIT_STASH_DEFAULT));
+
+ assert_commit_message_contains("refs/stash^2", "index on master: ");
+ assert_commit_message_contains("refs/stash", "On master: " MESSAGE);
+}
+
+void test_stash_save__cannot_stash_against_an_unborn_branch(void)
+{
+ git_reference *head;
+
+ cl_git_pass(git_reference_symbolic_create(&head, repo, "HEAD", "refs/heads/unborn", 1));
+
+ cl_assert_equal_i(GIT_EUNBORNBRANCH,
+ git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
+
+ git_reference_free(head);
+}
+
+void test_stash_save__cannot_stash_against_a_bare_repository(void)
+{
+ git_repository *local;
+
+ cl_git_pass(git_repository_init(&local, "sorry-it-is-a-non-bare-only-party", 1));
+
+ cl_assert_equal_i(GIT_EBAREREPO,
+ git_stash_save(&stash_tip_oid, local, signature, NULL, GIT_STASH_DEFAULT));
+
+ git_repository_free(local);
+}
+
+void test_stash_save__can_stash_against_a_detached_head(void)
+{
+ git_repository_detach_head(repo);
+
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
+
+ assert_commit_message_contains("refs/stash^2", "index on (no branch): ");
+ assert_commit_message_contains("refs/stash", "WIP on (no branch): ");
+}
+
+void test_stash_save__stashing_updates_the_reflog(void)
+{
+ char *sha;
+
+ assert_object_oid("refs/stash@{0}", NULL, GIT_OBJ_COMMIT);
+
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
+
+ sha = git_oid_allocfmt(&stash_tip_oid);
+
+ assert_object_oid("refs/stash@{0}", sha, GIT_OBJ_COMMIT);
+ assert_object_oid("refs/stash@{1}", NULL, GIT_OBJ_COMMIT);
+
+ git__free(sha);
+}
+
+void test_stash_save__cannot_stash_when_there_are_no_local_change(void)
+{
+ git_index *index;
+ git_oid stash_tip_oid;
+
+ cl_git_pass(git_repository_index(&index, repo));
+
+ /*
+ * 'what' and 'who' are being committed.
+ * 'when' remain untracked.
+ */
+ cl_git_pass(git_index_add_bypath(index, "what"));
+ cl_git_pass(git_index_add_bypath(index, "who"));
+ cl_repo_commit_from_index(NULL, repo, signature, 0, "Initial commit");
+ git_index_free(index);
+
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
+
+ p_unlink("stash/when");
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
+}
+
+void test_stash_save__can_stage_normal_then_stage_untracked(void)
+{
+ /*
+ * $ git ls-tree stash@{1}^0
+ * 100644 blob ac4d88de61733173d9959e4b77c69b9f17a00980 .gitignore
+ * 100644 blob e6d64adb2c7f3eb8feb493b556cc8070dca379a3 how
+ * 100644 blob bc99dc98b3eba0e9157e94769cd4d49cb49de449 what
+ * 100644 blob a0400d4954659306a976567af43125a0b1aa8595 who
+ *
+ * $ git ls-tree stash@{1}^1
+ * 100644 blob ac4d88de61733173d9959e4b77c69b9f17a00980 .gitignore
+ * 100644 blob ac790413e2d7a26c3767e78c57bb28716686eebc how
+ * 100644 blob ce013625030ba8dba906f756967f9e9ca394464a what
+ * 100644 blob cc628ccd10742baea8241c5924df992b5c019f71 who
+ *
+ * $ git ls-tree stash@{1}^2
+ * 100644 blob ac4d88de61733173d9959e4b77c69b9f17a00980 .gitignore
+ * 100644 blob e6d64adb2c7f3eb8feb493b556cc8070dca379a3 how
+ * 100644 blob dd7e1c6f0fefe118f0b63d9f10908c460aa317a6 what
+ * 100644 blob cc628ccd10742baea8241c5924df992b5c019f71 who
+ *
+ * $ git ls-tree stash@{1}^3
+ * fatal: Not a valid object name stash@{1}^3
+ *
+ * $ git ls-tree stash@{0}^0
+ * 100644 blob ac4d88de61733173d9959e4b77c69b9f17a00980 .gitignore
+ * 100644 blob ac790413e2d7a26c3767e78c57bb28716686eebc how
+ * 100644 blob ce013625030ba8dba906f756967f9e9ca394464a what
+ * 100644 blob cc628ccd10742baea8241c5924df992b5c019f71 who
+ *
+ * $ git ls-tree stash@{0}^1
+ * 100644 blob ac4d88de61733173d9959e4b77c69b9f17a00980 .gitignore
+ * 100644 blob ac790413e2d7a26c3767e78c57bb28716686eebc how
+ * 100644 blob ce013625030ba8dba906f756967f9e9ca394464a what
+ * 100644 blob cc628ccd10742baea8241c5924df992b5c019f71 who
+ *
+ * $ git ls-tree stash@{0}^2
+ * 100644 blob ac4d88de61733173d9959e4b77c69b9f17a00980 .gitignore
+ * 100644 blob ac790413e2d7a26c3767e78c57bb28716686eebc how
+ * 100644 blob ce013625030ba8dba906f756967f9e9ca394464a what
+ * 100644 blob cc628ccd10742baea8241c5924df992b5c019f71 who
+ *
+ * $ git ls-tree stash@{0}^3
+ * 100644 blob b6ed15e81e2593d7bb6265eb4a991d29dc3e628b when
+ */
+
+ assert_status(repo, "what", GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED);
+ assert_status(repo, "how", GIT_STATUS_INDEX_MODIFIED);
+ assert_status(repo, "who", GIT_STATUS_WT_MODIFIED);
+ assert_status(repo, "when", GIT_STATUS_WT_NEW);
+ assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
+
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
+ assert_status(repo, "what", GIT_STATUS_CURRENT);
+ assert_status(repo, "how", GIT_STATUS_CURRENT);
+ assert_status(repo, "who", GIT_STATUS_CURRENT);
+ assert_status(repo, "when", GIT_STATUS_WT_NEW);
+ assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
+
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
+ assert_status(repo, "what", GIT_STATUS_CURRENT);
+ assert_status(repo, "how", GIT_STATUS_CURRENT);
+ assert_status(repo, "who", GIT_STATUS_CURRENT);
+ assert_status(repo, "when", GIT_ENOTFOUND);
+ assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
+
+
+ assert_blob_oid("stash@{1}^0:what", "bc99dc98b3eba0e9157e94769cd4d49cb49de449"); /* see you later */
+ assert_blob_oid("stash@{1}^0:how", "e6d64adb2c7f3eb8feb493b556cc8070dca379a3"); /* not so small and */
+ assert_blob_oid("stash@{1}^0:who", "a0400d4954659306a976567af43125a0b1aa8595"); /* funky world */
+ assert_blob_oid("stash@{1}^0:when", NULL);
+
+ assert_blob_oid("stash@{1}^2:what", "dd7e1c6f0fefe118f0b63d9f10908c460aa317a6"); /* goodbye */
+ assert_blob_oid("stash@{1}^2:how", "e6d64adb2c7f3eb8feb493b556cc8070dca379a3"); /* not so small and */
+ assert_blob_oid("stash@{1}^2:who", "cc628ccd10742baea8241c5924df992b5c019f71"); /* world */
+ assert_blob_oid("stash@{1}^2:when", NULL);
+
+ assert_object_oid("stash@{1}^3", NULL, GIT_OBJ_COMMIT);
+
+ assert_blob_oid("stash@{0}^0:what", "ce013625030ba8dba906f756967f9e9ca394464a"); /* hello */
+ assert_blob_oid("stash@{0}^0:how", "ac790413e2d7a26c3767e78c57bb28716686eebc"); /* small */
+ assert_blob_oid("stash@{0}^0:who", "cc628ccd10742baea8241c5924df992b5c019f71"); /* world */
+ assert_blob_oid("stash@{0}^0:when", NULL);
+
+ assert_blob_oid("stash@{0}^2:what", "ce013625030ba8dba906f756967f9e9ca394464a"); /* hello */
+ assert_blob_oid("stash@{0}^2:how", "ac790413e2d7a26c3767e78c57bb28716686eebc"); /* small */
+ assert_blob_oid("stash@{0}^2:who", "cc628ccd10742baea8241c5924df992b5c019f71"); /* world */
+ assert_blob_oid("stash@{0}^2:when", NULL);
+
+ assert_blob_oid("stash@{0}^3:when", "b6ed15e81e2593d7bb6265eb4a991d29dc3e628b"); /* now */
+}
+
+#define EMPTY_TREE "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
+
+void test_stash_save__including_untracked_without_any_untracked_file_creates_an_empty_tree(void)
+{
+ cl_git_pass(p_unlink("stash/when"));
+
+ assert_status(repo, "what", GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED);
+ assert_status(repo, "how", GIT_STATUS_INDEX_MODIFIED);
+ assert_status(repo, "who", GIT_STATUS_WT_MODIFIED);
+ assert_status(repo, "when", GIT_ENOTFOUND);
+ assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
+
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
+
+ assert_object_oid("stash^3^{tree}", EMPTY_TREE, GIT_OBJ_TREE);
+}
diff --git a/tests/stash/stash_helpers.c b/tests/stash/stash_helpers.c
new file mode 100644
index 000000000..8b7d685f8
--- /dev/null
+++ b/tests/stash/stash_helpers.c
@@ -0,0 +1,56 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+#include "stash_helpers.h"
+
+void setup_stash(git_repository *repo, git_signature *signature)
+{
+ git_index *index;
+
+ cl_git_pass(git_repository_index(&index, repo));
+
+ cl_git_mkfile("stash/what", "hello\n"); /* ce013625030ba8dba906f756967f9e9ca394464a */
+ cl_git_mkfile("stash/how", "small\n"); /* ac790413e2d7a26c3767e78c57bb28716686eebc */
+ cl_git_mkfile("stash/who", "world\n"); /* cc628ccd10742baea8241c5924df992b5c019f71 */
+ cl_git_mkfile("stash/when", "now\n"); /* b6ed15e81e2593d7bb6265eb4a991d29dc3e628b */
+ cl_git_mkfile("stash/just.ignore", "me\n"); /* 78925fb1236b98b37a35e9723033e627f97aa88b */
+
+ cl_git_mkfile("stash/.gitignore", "*.ignore\n");
+
+ cl_git_pass(git_index_add_bypath(index, "what"));
+ cl_git_pass(git_index_add_bypath(index, "how"));
+ cl_git_pass(git_index_add_bypath(index, "who"));
+ cl_git_pass(git_index_add_bypath(index, ".gitignore"));
+
+ cl_repo_commit_from_index(NULL, repo, signature, 0, "Initial commit");
+
+ cl_git_rewritefile("stash/what", "goodbye\n"); /* dd7e1c6f0fefe118f0b63d9f10908c460aa317a6 */
+ cl_git_rewritefile("stash/how", "not so small and\n"); /* e6d64adb2c7f3eb8feb493b556cc8070dca379a3 */
+ cl_git_rewritefile("stash/who", "funky world\n"); /* a0400d4954659306a976567af43125a0b1aa8595 */
+
+ cl_git_pass(git_index_add_bypath(index, "what"));
+ cl_git_pass(git_index_add_bypath(index, "how"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_rewritefile("stash/what", "see you later\n"); /* bc99dc98b3eba0e9157e94769cd4d49cb49de449 */
+
+ git_index_free(index);
+}
+
+void assert_status(
+ git_repository *repo,
+ const char *path,
+ int status_flags)
+{
+ unsigned int status;
+ int error;
+
+ error = git_status_file(&status, repo, path);
+
+ if (status_flags < 0) {
+ cl_assert_equal_i(status_flags, error);
+ return;
+ }
+
+ cl_assert_equal_i(0, error);
+ cl_assert_equal_i((unsigned int)status_flags, status);
+}
diff --git a/tests/stash/stash_helpers.h b/tests/stash/stash_helpers.h
new file mode 100644
index 000000000..66d758fe2
--- /dev/null
+++ b/tests/stash/stash_helpers.h
@@ -0,0 +1,8 @@
+void setup_stash(
+ git_repository *repo,
+ git_signature *signature);
+
+void assert_status(
+ git_repository *repo,
+ const char *path,
+ int status_flags);
diff --git a/tests/stash/submodules.c b/tests/stash/submodules.c
new file mode 100644
index 000000000..137c4408c
--- /dev/null
+++ b/tests/stash/submodules.c
@@ -0,0 +1,80 @@
+#include "clar_libgit2.h"
+#include "stash_helpers.h"
+#include "../submodule/submodule_helpers.h"
+
+static git_repository *repo;
+static git_signature *signature;
+static git_oid stash_tip_oid;
+
+static git_submodule *sm;
+
+void test_stash_submodules__initialize(void)
+{
+ cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60)); /* Wed Dec 14 08:29:03 2011 +0100 */
+
+ repo = setup_fixture_submodules();
+
+ cl_git_pass(git_submodule_lookup(&sm, repo, "testrepo"));
+}
+
+void test_stash_submodules__cleanup(void)
+{
+ git_signature_free(signature);
+ signature = NULL;
+}
+
+void test_stash_submodules__does_not_stash_modified_submodules(void)
+{
+ static git_index *smindex;
+ static git_repository *smrepo;
+
+ assert_status(repo, "modified", GIT_STATUS_WT_MODIFIED);
+
+ /* modify file in submodule */
+ cl_git_rewritefile("submodules/testrepo/README", "heyheyhey");
+ assert_status(repo, "testrepo", GIT_STATUS_WT_MODIFIED);
+
+ /* add file to index in submodule */
+ cl_git_pass(git_submodule_open(&smrepo, sm));
+ cl_git_pass(git_repository_index(&smindex, smrepo));
+ cl_git_pass(git_index_add_bypath(smindex, "README"));
+
+ /* commit changed index of submodule */
+ cl_repo_commit_from_index(NULL, smrepo, NULL, 1372350000, "Modify it");
+ assert_status(repo, "testrepo", GIT_STATUS_WT_MODIFIED);
+
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
+
+ assert_status(repo, "testrepo", GIT_STATUS_WT_MODIFIED);
+ assert_status(repo, "modified", GIT_STATUS_CURRENT);
+
+ git_index_free(smindex);
+ git_repository_free(smrepo);
+}
+
+void test_stash_submodules__stash_is_empty_with_modified_submodules(void)
+{
+ static git_index *smindex;
+ static git_repository *smrepo;
+
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
+ assert_status(repo, "modified", GIT_STATUS_CURRENT);
+
+ /* modify file in submodule */
+ cl_git_rewritefile("submodules/testrepo/README", "heyheyhey");
+ assert_status(repo, "testrepo", GIT_STATUS_WT_MODIFIED);
+
+ /* add file to index in submodule */
+ cl_git_pass(git_submodule_open(&smrepo, sm));
+ cl_git_pass(git_repository_index(&smindex, smrepo));
+ cl_git_pass(git_index_add_bypath(smindex, "README"));
+
+ /* commit changed index of submodule */
+ cl_repo_commit_from_index(NULL, smrepo, NULL, 1372350000, "Modify it");
+ assert_status(repo, "testrepo", GIT_STATUS_WT_MODIFIED);
+
+ cl_git_fail_with(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT), GIT_ENOTFOUND);
+
+ git_index_free(smindex);
+ git_repository_free(smrepo);
+}
diff --git a/tests/status/ignore.c b/tests/status/ignore.c
new file mode 100644
index 000000000..acdc8fb58
--- /dev/null
+++ b/tests/status/ignore.c
@@ -0,0 +1,582 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+#include "git2/attr.h"
+#include "ignore.h"
+#include "attr.h"
+#include "status_helpers.h"
+
+static git_repository *g_repo = NULL;
+
+void test_status_ignore__initialize(void)
+{
+}
+
+void test_status_ignore__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_status_ignore__0(void)
+{
+ struct {
+ const char *path;
+ int expected;
+ } test_cases[] = {
+ /* pattern "ign" from .gitignore */
+ { "file", 0 },
+ { "ign", 1 },
+ { "sub", 0 },
+ { "sub/file", 0 },
+ { "sub/ign", 1 },
+ { "sub/ign/file", 1 },
+ { "sub/ign/sub", 1 },
+ { "sub/ign/sub/file", 1 },
+ { "sub/sub", 0 },
+ { "sub/sub/file", 0 },
+ { "sub/sub/ign", 1 },
+ { "sub/sub/sub", 0 },
+ /* pattern "dir/" from .gitignore */
+ { "dir", 1 },
+ { "dir/", 1 },
+ { "sub/dir", 1 },
+ { "sub/dir/", 1 },
+ { "sub/dir/file", 1 }, /* contained in ignored parent */
+ { "sub/sub/dir", 0 }, /* dir is not actually a dir, but a file */
+ { NULL, 0 }
+ }, *one_test;
+
+ g_repo = cl_git_sandbox_init("attr");
+
+ for (one_test = test_cases; one_test->path != NULL; one_test++) {
+ int ignored;
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, one_test->path));
+ cl_assert_(ignored == one_test->expected, one_test->path);
+ }
+
+ /* confirm that ignore files were cached */
+ cl_assert(git_attr_cache__is_cached(g_repo, 0, ".git/info/exclude"));
+ cl_assert(git_attr_cache__is_cached(g_repo, 0, ".gitignore"));
+}
+
+
+void test_status_ignore__1(void)
+{
+ int ignored;
+
+ g_repo = cl_git_sandbox_init("attr");
+
+ cl_git_rewritefile("attr/.gitignore", "/*.txt\n/dir/\n");
+ git_attr_cache_flush(g_repo);
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "root_test4.txt"));
+ cl_assert(ignored);
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "sub/subdir_test2.txt"));
+ cl_assert(!ignored);
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "dir"));
+ cl_assert(ignored);
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "dir/"));
+ cl_assert(ignored);
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "sub/dir"));
+ cl_assert(!ignored);
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "sub/dir/"));
+ cl_assert(!ignored);
+}
+
+
+void test_status_ignore__empty_repo_with_gitignore_rewrite(void)
+{
+ status_entry_single st;
+ int ignored;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_mkfile(
+ "empty_standard_repo/look-ma.txt", "I'm going to be ignored!");
+
+ memset(&st, 0, sizeof(st));
+ cl_git_pass(git_status_foreach(g_repo, cb_status__single, &st));
+ cl_assert(st.count == 1);
+ cl_assert(st.status == GIT_STATUS_WT_NEW);
+
+ cl_git_pass(git_status_file(&st.status, g_repo, "look-ma.txt"));
+ cl_assert(st.status == GIT_STATUS_WT_NEW);
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "look-ma.txt"));
+ cl_assert(!ignored);
+
+ cl_git_rewritefile("empty_standard_repo/.gitignore", "*.nomatch\n");
+
+ memset(&st, 0, sizeof(st));
+ cl_git_pass(git_status_foreach(g_repo, cb_status__single, &st));
+ cl_assert(st.count == 2);
+ cl_assert(st.status == GIT_STATUS_WT_NEW);
+
+ cl_git_pass(git_status_file(&st.status, g_repo, "look-ma.txt"));
+ cl_assert(st.status == GIT_STATUS_WT_NEW);
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "look-ma.txt"));
+ cl_assert(!ignored);
+
+ cl_git_rewritefile("empty_standard_repo/.gitignore", "*.txt\n");
+
+ memset(&st, 0, sizeof(st));
+ cl_git_pass(git_status_foreach(g_repo, cb_status__single, &st));
+ cl_assert(st.count == 2);
+ cl_assert(st.status == GIT_STATUS_IGNORED);
+
+ cl_git_pass(git_status_file(&st.status, g_repo, "look-ma.txt"));
+ cl_assert(st.status == GIT_STATUS_IGNORED);
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "look-ma.txt"));
+ cl_assert(ignored);
+}
+
+void test_status_ignore__ignore_pattern_contains_space(void)
+{
+ unsigned int flags;
+ const mode_t mode = 0777;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+ cl_git_rewritefile("empty_standard_repo/.gitignore", "foo bar.txt\n");
+
+ cl_git_mkfile(
+ "empty_standard_repo/foo bar.txt", "I'm going to be ignored!");
+
+ cl_git_pass(git_status_file(&flags, g_repo, "foo bar.txt"));
+ cl_assert(flags == GIT_STATUS_IGNORED);
+
+ cl_git_pass(git_futils_mkdir_r("empty_standard_repo/foo", NULL, mode));
+ cl_git_mkfile("empty_standard_repo/foo/look-ma.txt", "I'm not going to be ignored!");
+
+ cl_git_pass(git_status_file(&flags, g_repo, "foo/look-ma.txt"));
+ cl_assert(flags == GIT_STATUS_WT_NEW);
+}
+
+void test_status_ignore__ignore_pattern_ignorecase(void)
+{
+ unsigned int flags;
+ bool ignore_case;
+ git_index *index;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+ cl_git_rewritefile("empty_standard_repo/.gitignore", "a.txt\n");
+
+ cl_git_mkfile("empty_standard_repo/A.txt", "Differs in case");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ ignore_case = (git_index_caps(index) & GIT_INDEXCAP_IGNORE_CASE) != 0;
+ git_index_free(index);
+
+ cl_git_pass(git_status_file(&flags, g_repo, "A.txt"));
+ cl_assert(flags == ignore_case ? GIT_STATUS_IGNORED : GIT_STATUS_WT_NEW);
+}
+
+void test_status_ignore__subdirectories(void)
+{
+ status_entry_single st;
+ int ignored;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_mkfile(
+ "empty_standard_repo/ignore_me", "I'm going to be ignored!");
+
+ cl_git_rewritefile("empty_standard_repo/.gitignore", "ignore_me\n");
+
+ memset(&st, 0, sizeof(st));
+ cl_git_pass(git_status_foreach(g_repo, cb_status__single, &st));
+ cl_assert_equal_i(2, st.count);
+ cl_assert(st.status == GIT_STATUS_IGNORED);
+
+ cl_git_pass(git_status_file(&st.status, g_repo, "ignore_me"));
+ cl_assert(st.status == GIT_STATUS_IGNORED);
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "ignore_me"));
+ cl_assert(ignored);
+
+ /* I've changed libgit2 so that the behavior here now differs from
+ * core git but seems to make more sense. In core git, the following
+ * items are skipped completed, even if --ignored is passed to status.
+ * It you mirror these steps and run "git status -uall --ignored" then
+ * you will not see "test/ignore_me/" in the results.
+ *
+ * However, we had a couple reports of this as a bug, plus there is a
+ * similar circumstance where we were differing for core git when you
+ * used a rooted path for an ignore, so I changed this behavior.
+ */
+ cl_git_pass(git_futils_mkdir_r(
+ "empty_standard_repo/test/ignore_me", NULL, 0775));
+ cl_git_mkfile(
+ "empty_standard_repo/test/ignore_me/file", "I'm going to be ignored!");
+ cl_git_mkfile(
+ "empty_standard_repo/test/ignore_me/file2", "Me, too!");
+
+ memset(&st, 0, sizeof(st));
+ cl_git_pass(git_status_foreach(g_repo, cb_status__single, &st));
+ cl_assert_equal_i(3, st.count);
+
+ cl_git_pass(git_status_file(&st.status, g_repo, "test/ignore_me/file"));
+ cl_assert(st.status == GIT_STATUS_IGNORED);
+
+ cl_git_pass(
+ git_status_should_ignore(&ignored, g_repo, "test/ignore_me/file"));
+ cl_assert(ignored);
+}
+
+void test_status_ignore__subdirectories_recursion(void)
+{
+ /* Let's try again with recursing into ignored dirs turned on */
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ status_entry_counts counts;
+ static const char *paths_r[] = {
+ ".gitignore",
+ "ignore_also/file",
+ "ignore_me",
+ "test/ignore_me/and_me/file",
+ "test/ignore_me/file",
+ "test/ignore_me/file2",
+ };
+ static const unsigned int statuses_r[] = {
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_IGNORED,
+ GIT_STATUS_IGNORED,
+ GIT_STATUS_IGNORED,
+ GIT_STATUS_IGNORED,
+ GIT_STATUS_IGNORED,
+ };
+ static const char *paths_nr[] = {
+ ".gitignore",
+ "ignore_also/",
+ "ignore_me",
+ "test/ignore_me/",
+ };
+ static const unsigned int statuses_nr[] = {
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_IGNORED,
+ GIT_STATUS_IGNORED,
+ GIT_STATUS_IGNORED,
+ };
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_rewritefile("empty_standard_repo/.gitignore", "ignore_me\n/ignore_also\n");
+
+ cl_git_mkfile(
+ "empty_standard_repo/ignore_me", "I'm going to be ignored!");
+ cl_git_pass(git_futils_mkdir_r(
+ "empty_standard_repo/test/ignore_me", NULL, 0775));
+ cl_git_mkfile(
+ "empty_standard_repo/test/ignore_me/file", "I'm going to be ignored!");
+ cl_git_mkfile(
+ "empty_standard_repo/test/ignore_me/file2", "Me, too!");
+ cl_git_pass(git_futils_mkdir_r(
+ "empty_standard_repo/test/ignore_me/and_me", NULL, 0775));
+ cl_git_mkfile(
+ "empty_standard_repo/test/ignore_me/and_me/file", "Deeply ignored");
+ cl_git_pass(git_futils_mkdir_r(
+ "empty_standard_repo/ignore_also", NULL, 0775));
+ cl_git_mkfile(
+ "empty_standard_repo/ignore_also/file", "I'm going to be ignored!");
+
+ memset(&counts, 0x0, sizeof(status_entry_counts));
+ counts.expected_entry_count = 6;
+ counts.expected_paths = paths_r;
+ counts.expected_statuses = statuses_r;
+
+ opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_RECURSE_IGNORED_DIRS;
+
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__normal, &counts));
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+
+
+ memset(&counts, 0x0, sizeof(status_entry_counts));
+ counts.expected_entry_count = 4;
+ counts.expected_paths = paths_nr;
+ counts.expected_statuses = statuses_nr;
+
+ opts.flags = GIT_STATUS_OPT_DEFAULTS;
+
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__normal, &counts));
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+}
+
+void test_status_ignore__adding_internal_ignores(void)
+{
+ int ignored;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "one.txt"));
+ cl_assert(!ignored);
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "two.bar"));
+ cl_assert(!ignored);
+
+ cl_git_pass(git_ignore_add_rule(g_repo, "*.nomatch\n"));
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "one.txt"));
+ cl_assert(!ignored);
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "two.bar"));
+ cl_assert(!ignored);
+
+ cl_git_pass(git_ignore_add_rule(g_repo, "*.txt\n"));
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "one.txt"));
+ cl_assert(ignored);
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "two.bar"));
+ cl_assert(!ignored);
+
+ cl_git_pass(git_ignore_add_rule(g_repo, "*.bar\n"));
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "one.txt"));
+ cl_assert(ignored);
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "two.bar"));
+ cl_assert(ignored);
+
+ cl_git_pass(git_ignore_clear_internal_rules(g_repo));
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "one.txt"));
+ cl_assert(!ignored);
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "two.bar"));
+ cl_assert(!ignored);
+
+ cl_git_pass(git_ignore_add_rule(
+ g_repo, "multiple\n*.rules\n# comment line\n*.bar\n"));
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "one.txt"));
+ cl_assert(!ignored);
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "two.bar"));
+ cl_assert(ignored);
+}
+
+void test_status_ignore__add_internal_as_first_thing(void)
+{
+ int ignored;
+ const char *add_me = "\n#################\n## Eclipse\n#################\n\n*.pydevproject\n.project\n.metadata\nbin/\ntmp/\n*.tmp\n\n";
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_pass(git_ignore_add_rule(g_repo, add_me));
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "one.tmp"));
+ cl_assert(ignored);
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "two.bar"));
+ cl_assert(!ignored);
+}
+
+void test_status_ignore__internal_ignores_inside_deep_paths(void)
+{
+ int ignored;
+ const char *add_me = "Debug\nthis/is/deep\npatterned*/dir\n";
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_pass(git_ignore_add_rule(g_repo, add_me));
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "Debug"));
+ cl_assert(ignored);
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "and/Debug"));
+ cl_assert(ignored);
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "really/Debug/this/file"));
+ cl_assert(ignored);
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "Debug/what/I/say"));
+ cl_assert(ignored);
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "and/NoDebug"));
+ cl_assert(!ignored);
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "NoDebug/this"));
+ cl_assert(!ignored);
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "please/NoDebug/this"));
+ cl_assert(!ignored);
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "this/is/deep"));
+ cl_assert(ignored);
+ /* pattern containing slash gets FNM_PATHNAME so all slashes must match */
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "and/this/is/deep"));
+ cl_assert(!ignored);
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "this/is/deep/too"));
+ cl_assert(ignored);
+ /* pattern containing slash gets FNM_PATHNAME so all slashes must match */
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "but/this/is/deep/and/ignored"));
+ cl_assert(!ignored);
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "this/is/not/deep"));
+ cl_assert(!ignored);
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "is/this/not/as/deep"));
+ cl_assert(!ignored);
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "this/is/deepish"));
+ cl_assert(!ignored);
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "xthis/is/deep"));
+ cl_assert(!ignored);
+}
+
+void test_status_ignore__automatically_ignore_bad_files(void)
+{
+ int ignored;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, ".git"));
+ cl_assert(ignored);
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "this/file/."));
+ cl_assert(ignored);
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "path/../funky"));
+ cl_assert(ignored);
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "path/whatever.c"));
+ cl_assert(!ignored);
+
+ cl_git_pass(git_ignore_add_rule(g_repo, "*.c\n"));
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, ".git"));
+ cl_assert(ignored);
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "this/file/."));
+ cl_assert(ignored);
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "path/../funky"));
+ cl_assert(ignored);
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "path/whatever.c"));
+ cl_assert(ignored);
+
+ cl_git_pass(git_ignore_clear_internal_rules(g_repo));
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, ".git"));
+ cl_assert(ignored);
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "this/file/."));
+ cl_assert(ignored);
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "path/../funky"));
+ cl_assert(ignored);
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "path/whatever.c"));
+ cl_assert(!ignored);
+}
+
+void test_status_ignore__filenames_with_special_prefixes_do_not_interfere_with_status_retrieval(void)
+{
+ status_entry_single st;
+ char *test_cases[] = {
+ "!file",
+ "#blah",
+ "[blah]",
+ "[attr]",
+ "[attr]blah",
+ NULL
+ };
+ int i;
+
+ for (i = 0; *(test_cases + i) != NULL; i++) {
+ git_buf file = GIT_BUF_INIT;
+ char *file_name = *(test_cases + i);
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_pass(git_buf_joinpath(&file, "empty_standard_repo", file_name));
+ cl_git_mkfile(git_buf_cstr(&file), "Please don't ignore me!");
+
+ memset(&st, 0, sizeof(st));
+ cl_git_pass(git_status_foreach(repo, cb_status__single, &st));
+ cl_assert(st.count == 1);
+ cl_assert(st.status == GIT_STATUS_WT_NEW);
+
+ cl_git_pass(git_status_file(&st.status, repo, file_name));
+ cl_assert(st.status == GIT_STATUS_WT_NEW);
+
+ cl_git_sandbox_cleanup();
+ git_buf_free(&file);
+ }
+}
+
+void test_status_ignore__issue_1766_negated_ignores(void)
+{
+ int ignored = 0;
+ unsigned int status;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_pass(git_futils_mkdir_r(
+ "empty_standard_repo/a", NULL, 0775));
+ cl_git_mkfile(
+ "empty_standard_repo/a/.gitignore", "*\n!.gitignore\n");
+ cl_git_mkfile(
+ "empty_standard_repo/a/ignoreme", "I should be ignored\n");
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "a/.gitignore"));
+ cl_assert(!ignored);
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "a/ignoreme"));
+ cl_assert(ignored);
+
+ cl_git_pass(git_futils_mkdir_r(
+ "empty_standard_repo/b", NULL, 0775));
+ cl_git_mkfile(
+ "empty_standard_repo/b/.gitignore", "*\n!.gitignore\n");
+ cl_git_mkfile(
+ "empty_standard_repo/b/ignoreme", "I should be ignored\n");
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "b/.gitignore"));
+ cl_assert(!ignored);
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "b/ignoreme"));
+ cl_assert(ignored);
+
+ /* shouldn't have changed results from first couple either */
+
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "a/.gitignore"));
+ cl_assert(!ignored);
+ cl_git_pass(git_status_should_ignore(&ignored, g_repo, "a/ignoreme"));
+ cl_assert(ignored);
+
+ /* status should find the two ignore files and nothing else */
+
+ cl_git_pass(git_status_file(&status, g_repo, "a/.gitignore"));
+ cl_assert_equal_i(GIT_STATUS_WT_NEW, (int)status);
+
+ cl_git_pass(git_status_file(&status, g_repo, "a/ignoreme"));
+ cl_assert_equal_i(GIT_STATUS_IGNORED, (int)status);
+
+ cl_git_pass(git_status_file(&status, g_repo, "b/.gitignore"));
+ cl_assert_equal_i(GIT_STATUS_WT_NEW, (int)status);
+
+ cl_git_pass(git_status_file(&status, g_repo, "b/ignoreme"));
+ cl_assert_equal_i(GIT_STATUS_IGNORED, (int)status);
+
+ {
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ status_entry_counts counts;
+ static const char *paths[] = {
+ "a/.gitignore",
+ "a/ignoreme",
+ "b/.gitignore",
+ "b/ignoreme",
+ };
+ static const unsigned int statuses[] = {
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_IGNORED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_IGNORED,
+ };
+
+ memset(&counts, 0x0, sizeof(status_entry_counts));
+ counts.expected_entry_count = 4;
+ counts.expected_paths = paths;
+ counts.expected_statuses = statuses;
+
+ opts.flags = GIT_STATUS_OPT_DEFAULTS;
+
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__normal, &counts));
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+ }
+}
+
diff --git a/tests/status/renames.c b/tests/status/renames.c
new file mode 100644
index 000000000..16fd02676
--- /dev/null
+++ b/tests/status/renames.c
@@ -0,0 +1,557 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "path.h"
+#include "posix.h"
+#include "status_helpers.h"
+#include "util.h"
+#include "status.h"
+
+static git_repository *g_repo = NULL;
+
+void test_status_renames__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("renames");
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", false);
+}
+
+void test_status_renames__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static void rename_file(git_repository *repo, const char *oldname, const char *newname)
+{
+ git_buf oldpath = GIT_BUF_INIT, newpath = GIT_BUF_INIT;
+
+ git_buf_joinpath(&oldpath, git_repository_workdir(repo), oldname);
+ git_buf_joinpath(&newpath, git_repository_workdir(repo), newname);
+
+ cl_git_pass(p_rename(oldpath.ptr, newpath.ptr));
+
+ git_buf_free(&oldpath);
+ git_buf_free(&newpath);
+}
+
+static void rename_and_edit_file(git_repository *repo, const char *oldname, const char *newname)
+{
+ git_buf oldpath = GIT_BUF_INIT, newpath = GIT_BUF_INIT;
+
+ git_buf_joinpath(&oldpath, git_repository_workdir(repo), oldname);
+ git_buf_joinpath(&newpath, git_repository_workdir(repo), newname);
+
+ cl_git_pass(p_rename(oldpath.ptr, newpath.ptr));
+ cl_git_append2file(newpath.ptr, "Added at the end to keep similarity!");
+
+ git_buf_free(&oldpath);
+ git_buf_free(&newpath);
+}
+
+struct status_entry {
+ git_status_t status;
+ const char *oldname;
+ const char *newname;
+};
+
+static void test_status(
+ git_status_list *status_list,
+ struct status_entry *expected_list,
+ size_t expected_len)
+{
+ const git_status_entry *actual;
+ const struct status_entry *expected;
+ const char *oldname, *newname;
+ size_t i;
+
+ cl_assert_equal_sz(expected_len, git_status_list_entrycount(status_list));
+
+ for (i = 0; i < expected_len; i++) {
+ actual = git_status_byindex(status_list, i);
+ expected = &expected_list[i];
+
+ oldname = actual->head_to_index ? actual->head_to_index->old_file.path :
+ actual->index_to_workdir ? actual->index_to_workdir->old_file.path : NULL;
+
+ newname = actual->index_to_workdir ? actual->index_to_workdir->new_file.path :
+ actual->head_to_index ? actual->head_to_index->new_file.path : NULL;
+
+ cl_assert_equal_i_fmt(expected->status, actual->status, "%04x");
+
+ if (oldname)
+ cl_assert(git__strcmp(oldname, expected->oldname) == 0);
+ else
+ cl_assert(expected->oldname == NULL);
+
+ if (newname)
+ cl_assert(git__strcmp(newname, expected->newname) == 0);
+ else
+ cl_assert(expected->newname == NULL);
+ }
+}
+
+void test_status_renames__head2index_one(void)
+{
+ git_index *index;
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ struct status_entry expected[] = {
+ { GIT_STATUS_INDEX_RENAMED, "ikeepsix.txt", "newname.txt" },
+ };
+
+ opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ rename_file(g_repo, "ikeepsix.txt", "newname.txt");
+
+ cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
+ cl_git_pass(git_index_add_bypath(index, "newname.txt"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ test_status(statuslist, expected, 1);
+ git_status_list_free(statuslist);
+
+ git_index_free(index);
+}
+
+void test_status_renames__head2index_two(void)
+{
+ git_index *index;
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ struct status_entry expected[] = {
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED,
+ "sixserving.txt", "aaa.txt" },
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED,
+ "untimely.txt", "bbb.txt" },
+ { GIT_STATUS_INDEX_RENAMED, "songof7cities.txt", "ccc.txt" },
+ { GIT_STATUS_INDEX_RENAMED, "ikeepsix.txt", "ddd.txt" },
+ };
+
+ opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ rename_file(g_repo, "ikeepsix.txt", "ddd.txt");
+ rename_and_edit_file(g_repo, "sixserving.txt", "aaa.txt");
+ rename_file(g_repo, "songof7cities.txt", "ccc.txt");
+ rename_and_edit_file(g_repo, "untimely.txt", "bbb.txt");
+
+ cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
+ cl_git_pass(git_index_remove_bypath(index, "sixserving.txt"));
+ cl_git_pass(git_index_remove_bypath(index, "songof7cities.txt"));
+ cl_git_pass(git_index_remove_bypath(index, "untimely.txt"));
+ cl_git_pass(git_index_add_bypath(index, "ddd.txt"));
+ cl_git_pass(git_index_add_bypath(index, "aaa.txt"));
+ cl_git_pass(git_index_add_bypath(index, "ccc.txt"));
+ cl_git_pass(git_index_add_bypath(index, "bbb.txt"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ test_status(statuslist, expected, 4);
+ git_status_list_free(statuslist);
+
+ git_index_free(index);
+}
+
+void test_status_renames__head2index_no_rename_from_rewrite(void)
+{
+ git_index *index;
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ struct status_entry expected[] = {
+ { GIT_STATUS_INDEX_MODIFIED, "ikeepsix.txt", "ikeepsix.txt" },
+ { GIT_STATUS_INDEX_MODIFIED, "sixserving.txt", "sixserving.txt" },
+ };
+
+ opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ rename_file(g_repo, "ikeepsix.txt", "_temp_.txt");
+ rename_file(g_repo, "sixserving.txt", "ikeepsix.txt");
+ rename_file(g_repo, "_temp_.txt", "sixserving.txt");
+
+ cl_git_pass(git_index_add_bypath(index, "ikeepsix.txt"));
+ cl_git_pass(git_index_add_bypath(index, "sixserving.txt"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ test_status(statuslist, expected, 2);
+ git_status_list_free(statuslist);
+
+ git_index_free(index);
+}
+
+void test_status_renames__head2index_rename_from_rewrite(void)
+{
+ git_index *index;
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ struct status_entry expected[] = {
+ { GIT_STATUS_INDEX_RENAMED, "sixserving.txt", "ikeepsix.txt" },
+ { GIT_STATUS_INDEX_RENAMED, "ikeepsix.txt", "sixserving.txt" },
+ };
+
+ opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ rename_file(g_repo, "ikeepsix.txt", "_temp_.txt");
+ rename_file(g_repo, "sixserving.txt", "ikeepsix.txt");
+ rename_file(g_repo, "_temp_.txt", "sixserving.txt");
+
+ cl_git_pass(git_index_add_bypath(index, "ikeepsix.txt"));
+ cl_git_pass(git_index_add_bypath(index, "sixserving.txt"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ test_status(statuslist, expected, 2);
+ git_status_list_free(statuslist);
+
+ git_index_free(index);
+}
+
+void test_status_renames__index2workdir_one(void)
+{
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ struct status_entry expected[] = {
+ { GIT_STATUS_WT_RENAMED, "ikeepsix.txt", "newname.txt" },
+ };
+
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
+
+ rename_file(g_repo, "ikeepsix.txt", "newname.txt");
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ test_status(statuslist, expected, 1);
+ git_status_list_free(statuslist);
+}
+
+void test_status_renames__index2workdir_two(void)
+{
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ struct status_entry expected[] = {
+ { GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
+ "sixserving.txt", "aaa.txt" },
+ { GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
+ "untimely.txt", "bbb.txt" },
+ { GIT_STATUS_WT_RENAMED, "songof7cities.txt", "ccc.txt" },
+ { GIT_STATUS_WT_RENAMED, "ikeepsix.txt", "ddd.txt" },
+ };
+
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
+
+ rename_file(g_repo, "ikeepsix.txt", "ddd.txt");
+ rename_and_edit_file(g_repo, "sixserving.txt", "aaa.txt");
+ rename_file(g_repo, "songof7cities.txt", "ccc.txt");
+ rename_and_edit_file(g_repo, "untimely.txt", "bbb.txt");
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ test_status(statuslist, expected, 4);
+ git_status_list_free(statuslist);
+}
+
+void test_status_renames__index2workdir_rename_from_rewrite(void)
+{
+ git_index *index;
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ struct status_entry expected[] = {
+ { GIT_STATUS_WT_RENAMED, "sixserving.txt", "ikeepsix.txt" },
+ { GIT_STATUS_WT_RENAMED, "ikeepsix.txt", "sixserving.txt" },
+ };
+
+ opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ rename_file(g_repo, "ikeepsix.txt", "_temp_.txt");
+ rename_file(g_repo, "sixserving.txt", "ikeepsix.txt");
+ rename_file(g_repo, "_temp_.txt", "sixserving.txt");
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ test_status(statuslist, expected, 2);
+ git_status_list_free(statuslist);
+
+ git_index_free(index);
+}
+
+void test_status_renames__both_one(void)
+{
+ git_index *index;
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ struct status_entry expected[] = {
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
+ "ikeepsix.txt", "newname-workdir.txt" },
+ };
+
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ rename_file(g_repo, "ikeepsix.txt", "newname-index.txt");
+
+ cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
+ cl_git_pass(git_index_add_bypath(index, "newname-index.txt"));
+ cl_git_pass(git_index_write(index));
+
+ rename_file(g_repo, "newname-index.txt", "newname-workdir.txt");
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ test_status(statuslist, expected, 1);
+ git_status_list_free(statuslist);
+
+ git_index_free(index);
+}
+
+void test_status_renames__both_two(void)
+{
+ git_index *index;
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ struct status_entry expected[] = {
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED |
+ GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
+ "ikeepsix.txt", "ikeepsix-both.txt" },
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED,
+ "sixserving.txt", "sixserving-index.txt" },
+ { GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
+ "songof7cities.txt", "songof7cities-workdir.txt" },
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
+ "untimely.txt", "untimely-both.txt" },
+ };
+
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ rename_and_edit_file(g_repo, "ikeepsix.txt", "ikeepsix-index.txt");
+ rename_and_edit_file(g_repo, "sixserving.txt", "sixserving-index.txt");
+ rename_file(g_repo, "untimely.txt", "untimely-index.txt");
+
+ cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
+ cl_git_pass(git_index_remove_bypath(index, "sixserving.txt"));
+ cl_git_pass(git_index_remove_bypath(index, "untimely.txt"));
+ cl_git_pass(git_index_add_bypath(index, "ikeepsix-index.txt"));
+ cl_git_pass(git_index_add_bypath(index, "sixserving-index.txt"));
+ cl_git_pass(git_index_add_bypath(index, "untimely-index.txt"));
+ cl_git_pass(git_index_write(index));
+
+ rename_and_edit_file(g_repo, "ikeepsix-index.txt", "ikeepsix-both.txt");
+ rename_and_edit_file(g_repo, "songof7cities.txt", "songof7cities-workdir.txt");
+ rename_file(g_repo, "untimely-index.txt", "untimely-both.txt");
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ test_status(statuslist, expected, 4);
+ git_status_list_free(statuslist);
+
+ git_index_free(index);
+}
+
+
+void test_status_renames__both_rename_from_rewrite(void)
+{
+ git_index *index;
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ struct status_entry expected[] = {
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
+ "songof7cities.txt", "ikeepsix.txt" },
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
+ "ikeepsix.txt", "sixserving.txt" },
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
+ "sixserving.txt", "songof7cities.txt" },
+ };
+
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ rename_file(g_repo, "ikeepsix.txt", "_temp_.txt");
+ rename_file(g_repo, "sixserving.txt", "ikeepsix.txt");
+ rename_file(g_repo, "songof7cities.txt", "sixserving.txt");
+ rename_file(g_repo, "_temp_.txt", "songof7cities.txt");
+
+ cl_git_pass(git_index_add_bypath(index, "ikeepsix.txt"));
+ cl_git_pass(git_index_add_bypath(index, "sixserving.txt"));
+ cl_git_pass(git_index_add_bypath(index, "songof7cities.txt"));
+ cl_git_pass(git_index_write(index));
+
+ rename_file(g_repo, "songof7cities.txt", "_temp_.txt");
+ rename_file(g_repo, "ikeepsix.txt", "songof7cities.txt");
+ rename_file(g_repo, "sixserving.txt", "ikeepsix.txt");
+ rename_file(g_repo, "_temp_.txt", "sixserving.txt");
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ test_status(statuslist, expected, 3);
+ git_status_list_free(statuslist);
+
+ git_index_free(index);
+}
+
+void test_status_renames__rewrites_only_for_renames(void)
+{
+ git_index *index;
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ struct status_entry expected[] = {
+ { GIT_STATUS_WT_MODIFIED, "ikeepsix.txt", "ikeepsix.txt" },
+ };
+
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_rewritefile("renames/ikeepsix.txt",
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n");
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ test_status(statuslist, expected, 1);
+ git_status_list_free(statuslist);
+
+ git_index_free(index);
+}
+
+void test_status_renames__both_casechange_one(void)
+{
+ git_index *index;
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ int index_caps;
+ struct status_entry expected_icase[] = {
+ { GIT_STATUS_INDEX_RENAMED,
+ "ikeepsix.txt", "IKeepSix.txt" },
+ };
+ struct status_entry expected_case[] = {
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
+ "ikeepsix.txt", "IKEEPSIX.txt" },
+ };
+
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ index_caps = git_index_caps(index);
+
+ rename_file(g_repo, "ikeepsix.txt", "IKeepSix.txt");
+
+ cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
+ cl_git_pass(git_index_add_bypath(index, "IKeepSix.txt"));
+ cl_git_pass(git_index_write(index));
+
+ /* on a case-insensitive file system, this change won't matter.
+ * on a case-sensitive one, it will.
+ */
+ rename_file(g_repo, "IKeepSix.txt", "IKEEPSIX.txt");
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+
+ test_status(statuslist, (index_caps & GIT_INDEXCAP_IGNORE_CASE) ?
+ expected_icase : expected_case, 1);
+
+ git_status_list_free(statuslist);
+
+ git_index_free(index);
+}
+
+void test_status_renames__both_casechange_two(void)
+{
+ git_index *index;
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ int index_caps;
+ struct status_entry expected_icase[] = {
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED |
+ GIT_STATUS_WT_MODIFIED,
+ "ikeepsix.txt", "IKeepSix.txt" },
+ { GIT_STATUS_INDEX_MODIFIED,
+ "sixserving.txt", "sixserving.txt" },
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_MODIFIED,
+ "songof7cities.txt", "songof7.txt" },
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
+ "untimely.txt", "untimeliest.txt" }
+ };
+ struct status_entry expected_case[] = {
+ { GIT_STATUS_INDEX_RENAMED |
+ GIT_STATUS_WT_MODIFIED | GIT_STATUS_WT_RENAMED,
+ "songof7cities.txt", "SONGOF7.txt" },
+ { GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_RENAMED,
+ "sixserving.txt", "SixServing.txt" },
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED |
+ GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
+ "ikeepsix.txt", "ikeepsix.txt" },
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
+ "untimely.txt", "untimeliest.txt" }
+ };
+
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ index_caps = git_index_caps(index);
+
+ rename_and_edit_file(g_repo, "ikeepsix.txt", "IKeepSix.txt");
+ rename_and_edit_file(g_repo, "sixserving.txt", "sixserving.txt");
+ rename_file(g_repo, "songof7cities.txt", "songof7.txt");
+ rename_file(g_repo, "untimely.txt", "untimelier.txt");
+
+ cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
+ cl_git_pass(git_index_remove_bypath(index, "sixserving.txt"));
+ cl_git_pass(git_index_remove_bypath(index, "songof7cities.txt"));
+ cl_git_pass(git_index_remove_bypath(index, "untimely.txt"));
+ cl_git_pass(git_index_add_bypath(index, "IKeepSix.txt"));
+ cl_git_pass(git_index_add_bypath(index, "sixserving.txt"));
+ cl_git_pass(git_index_add_bypath(index, "songof7.txt"));
+ cl_git_pass(git_index_add_bypath(index, "untimelier.txt"));
+ cl_git_pass(git_index_write(index));
+
+ rename_and_edit_file(g_repo, "IKeepSix.txt", "ikeepsix.txt");
+ rename_file(g_repo, "sixserving.txt", "SixServing.txt");
+ rename_and_edit_file(g_repo, "songof7.txt", "SONGOF7.txt");
+ rename_file(g_repo, "untimelier.txt", "untimeliest.txt");
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+
+ test_status(statuslist, (index_caps & GIT_INDEXCAP_IGNORE_CASE) ?
+ expected_icase : expected_case, 4);
+
+ git_status_list_free(statuslist);
+
+ git_index_free(index);
+}
diff --git a/tests-clar/status/single.c b/tests/status/single.c
index 292c9120a..292c9120a 100644
--- a/tests-clar/status/single.c
+++ b/tests/status/single.c
diff --git a/tests/status/status_data.h b/tests/status/status_data.h
new file mode 100644
index 000000000..8ad4235fd
--- /dev/null
+++ b/tests/status/status_data.h
@@ -0,0 +1,326 @@
+#include "status_helpers.h"
+
+// A utf-8 string with 83 characters, but 249 bytes.
+static const char *longname = "\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97";
+
+
+/* entries for a plain copy of tests/resources/status */
+
+static const char *entry_paths0[] = {
+ "file_deleted",
+ "ignored_file",
+ "modified_file",
+ "new_file",
+ "staged_changes",
+ "staged_changes_file_deleted",
+ "staged_changes_modified_file",
+ "staged_delete_file_deleted",
+ "staged_delete_modified_file",
+ "staged_new_file",
+ "staged_new_file_deleted_file",
+ "staged_new_file_modified_file",
+
+ "subdir/deleted_file",
+ "subdir/modified_file",
+ "subdir/new_file",
+
+ "\xe8\xbf\x99",
+};
+
+static const unsigned int entry_statuses0[] = {
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_IGNORED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_DELETED,
+ GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_INDEX_DELETED | GIT_STATUS_WT_NEW,
+ GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_INDEX_NEW | GIT_STATUS_WT_DELETED,
+ GIT_STATUS_INDEX_NEW | GIT_STATUS_WT_MODIFIED,
+
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW,
+
+ GIT_STATUS_WT_NEW,
+};
+
+static const int entry_count0 = 16;
+
+/* entries for a copy of tests/resources/status with all content
+ * deleted from the working directory
+ */
+
+static const char *entry_paths2[] = {
+ "current_file",
+ "file_deleted",
+ "modified_file",
+ "staged_changes",
+ "staged_changes_file_deleted",
+ "staged_changes_modified_file",
+ "staged_delete_file_deleted",
+ "staged_delete_modified_file",
+ "staged_new_file",
+ "staged_new_file_deleted_file",
+ "staged_new_file_modified_file",
+ "subdir.txt",
+ "subdir/current_file",
+ "subdir/deleted_file",
+ "subdir/modified_file",
+};
+
+static const unsigned int entry_statuses2[] = {
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+};
+
+static const int entry_count2 = 15;
+
+/* entries for a copy of tests/resources/status with some mods */
+
+static const char *entry_paths3_icase[] = {
+ ".HEADER",
+ "42-is-not-prime.sigh",
+ "current_file",
+ "current_file/",
+ "file_deleted",
+ "ignored_file",
+ "modified_file",
+ "new_file",
+ "README.md",
+ "staged_changes",
+ "staged_changes_file_deleted",
+ "staged_changes_modified_file",
+ "staged_delete_file_deleted",
+ "staged_delete_modified_file",
+ "staged_new_file",
+ "staged_new_file_deleted_file",
+ "staged_new_file_modified_file",
+ "subdir",
+ "subdir/current_file",
+ "subdir/deleted_file",
+ "subdir/modified_file",
+ "\xe8\xbf\x99",
+};
+
+static const unsigned int entry_statuses3_icase[] = {
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_IGNORED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_WT_NEW | GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_NEW,
+};
+
+static const char *entry_paths3[] = {
+ ".HEADER",
+ "42-is-not-prime.sigh",
+ "README.md",
+ "current_file",
+ "current_file/",
+ "file_deleted",
+ "ignored_file",
+ "modified_file",
+ "new_file",
+ "staged_changes",
+ "staged_changes_file_deleted",
+ "staged_changes_modified_file",
+ "staged_delete_file_deleted",
+ "staged_delete_modified_file",
+ "staged_new_file",
+ "staged_new_file_deleted_file",
+ "staged_new_file_modified_file",
+ "subdir",
+ "subdir/current_file",
+ "subdir/deleted_file",
+ "subdir/modified_file",
+ "\xe8\xbf\x99",
+};
+
+static const unsigned int entry_statuses3[] = {
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_IGNORED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_WT_NEW | GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_NEW,
+};
+
+static const int entry_count3 = 22;
+
+
+/* entries for a copy of tests/resources/status with some mods
+ * and different options to the status call
+ */
+
+static const char *entry_paths4[] = {
+ ".new_file",
+ "current_file",
+ "current_file/current_file",
+ "current_file/modified_file",
+ "current_file/new_file",
+ "file_deleted",
+ "modified_file",
+ "new_file",
+ "staged_changes",
+ "staged_changes_file_deleted",
+ "staged_changes_modified_file",
+ "staged_delete_file_deleted",
+ "staged_delete_modified_file",
+ "staged_new_file",
+ "staged_new_file_deleted_file",
+ "staged_new_file_modified_file",
+ "subdir",
+ "subdir/current_file",
+ "subdir/deleted_file",
+ "subdir/modified_file",
+ "zzz_new_dir/new_file",
+ "zzz_new_file",
+ "\xe8\xbf\x99",
+};
+
+static const unsigned int entry_statuses4[] = {
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_WT_NEW | GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_NEW,
+};
+
+static const int entry_count4 = 23;
+
+
+/* entries for a copy of tests/resources/status with options
+ * passed to the status call in order to only get the differences
+ * between the HEAD and the index (changes to be committed)
+ */
+
+static const char *entry_paths5[] = {
+ "staged_changes",
+ "staged_changes_file_deleted",
+ "staged_changes_modified_file",
+ "staged_delete_file_deleted",
+ "staged_delete_modified_file",
+ "staged_new_file",
+ "staged_new_file_deleted_file",
+ "staged_new_file_modified_file",
+};
+
+static const unsigned int entry_statuses5[] = {
+ GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_INDEX_NEW,
+};
+
+static const int entry_count5 = 8;
+
+
+/* entries for a copy of tests/resources/status with options
+ * passed to the status call in order to only get the differences
+ * between the workdir and the index (changes not staged, untracked files)
+ */
+
+static const char *entry_paths6[] = {
+ "file_deleted",
+ "ignored_file",
+ "modified_file",
+ "new_file",
+ "staged_changes_file_deleted",
+ "staged_changes_modified_file",
+ "staged_delete_modified_file",
+ "staged_new_file_deleted_file",
+ "staged_new_file_modified_file",
+ "subdir/deleted_file",
+ "subdir/modified_file",
+ "subdir/new_file",
+ "\xe8\xbf\x99",
+};
+
+static const unsigned int entry_statuses6[] = {
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_IGNORED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_NEW,
+};
+
+static const int entry_count6 = 13;
diff --git a/tests-clar/status/status_helpers.c b/tests/status/status_helpers.c
index 902b65c4f..902b65c4f 100644
--- a/tests-clar/status/status_helpers.c
+++ b/tests/status/status_helpers.c
diff --git a/tests-clar/status/status_helpers.h b/tests/status/status_helpers.h
index f1f009e02..f1f009e02 100644
--- a/tests-clar/status/status_helpers.h
+++ b/tests/status/status_helpers.h
diff --git a/tests/status/submodules.c b/tests/status/submodules.c
new file mode 100644
index 000000000..ef2888f7d
--- /dev/null
+++ b/tests/status/submodules.c
@@ -0,0 +1,223 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "path.h"
+#include "posix.h"
+#include "status_helpers.h"
+#include "../submodule/submodule_helpers.h"
+
+static git_repository *g_repo = NULL;
+
+void test_status_submodules__initialize(void)
+{
+}
+
+void test_status_submodules__cleanup(void)
+{
+}
+
+void test_status_submodules__api(void)
+{
+ git_submodule *sm;
+
+ g_repo = setup_fixture_submodules();
+
+ cl_assert(git_submodule_lookup(NULL, g_repo, "nonexistent") == GIT_ENOTFOUND);
+
+ cl_assert(git_submodule_lookup(NULL, g_repo, "modified") == GIT_ENOTFOUND);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
+ cl_assert(sm != NULL);
+ cl_assert_equal_s("testrepo", git_submodule_name(sm));
+ cl_assert_equal_s("testrepo", git_submodule_path(sm));
+}
+
+void test_status_submodules__0(void)
+{
+ int counts = 0;
+
+ g_repo = setup_fixture_submodules();
+
+ cl_assert(git_path_isdir("submodules/.git"));
+ cl_assert(git_path_isdir("submodules/testrepo/.git"));
+ cl_assert(git_path_isfile("submodules/.gitmodules"));
+
+ cl_git_pass(
+ git_status_foreach(g_repo, cb_status__count, &counts)
+ );
+
+ cl_assert_equal_i(6, counts);
+}
+
+static const char *expected_files[] = {
+ ".gitmodules",
+ "added",
+ "deleted",
+ "ignored",
+ "modified",
+ "untracked"
+};
+
+static unsigned int expected_status[] = {
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_IGNORED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW
+};
+
+static int cb_status__match(const char *p, unsigned int s, void *payload)
+{
+ status_entry_counts *counts = payload;
+ int idx = counts->entry_count++;
+
+ cl_assert_equal_s(counts->expected_paths[idx], p);
+ cl_assert(counts->expected_statuses[idx] == s);
+
+ return 0;
+}
+
+void test_status_submodules__1(void)
+{
+ status_entry_counts counts;
+
+ g_repo = setup_fixture_submodules();
+
+ cl_assert(git_path_isdir("submodules/.git"));
+ cl_assert(git_path_isdir("submodules/testrepo/.git"));
+ cl_assert(git_path_isfile("submodules/.gitmodules"));
+
+ memset(&counts, 0, sizeof(counts));
+ counts.expected_paths = expected_files;
+ counts.expected_statuses = expected_status;
+
+ cl_git_pass(
+ git_status_foreach(g_repo, cb_status__match, &counts)
+ );
+
+ cl_assert_equal_i(6, counts.entry_count);
+}
+
+void test_status_submodules__single_file(void)
+{
+ unsigned int status = 0;
+ g_repo = setup_fixture_submodules();
+ cl_git_pass( git_status_file(&status, g_repo, "testrepo") );
+ cl_assert(!status);
+}
+
+void test_status_submodules__moved_head(void)
+{
+ git_submodule *sm;
+ git_repository *smrepo;
+ git_oid oid;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ status_entry_counts counts;
+ static const char *expected_files_with_sub[] = {
+ ".gitmodules",
+ "added",
+ "deleted",
+ "ignored",
+ "modified",
+ "testrepo",
+ "untracked"
+ };
+ static unsigned int expected_status_with_sub[] = {
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_IGNORED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW
+ };
+
+ g_repo = setup_fixture_submodules();
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
+ cl_git_pass(git_submodule_open(&smrepo, sm));
+
+ /* move submodule HEAD to c47800c7266a2be04c571c04d5a6614691ea99bd */
+ cl_git_pass(
+ git_oid_fromstr(&oid, "c47800c7266a2be04c571c04d5a6614691ea99bd"));
+ cl_git_pass(git_repository_set_head_detached(smrepo, &oid));
+
+ /* first do a normal status, which should now include the submodule */
+
+ memset(&counts, 0, sizeof(counts));
+ counts.expected_paths = expected_files_with_sub;
+ counts.expected_statuses = expected_status_with_sub;
+
+ opts.flags = GIT_STATUS_OPT_DEFAULTS;
+
+ cl_git_pass(
+ git_status_foreach_ext(g_repo, &opts, cb_status__match, &counts));
+ cl_assert_equal_i(7, counts.entry_count);
+
+ /* try again with EXCLUDE_SUBMODULES which should skip it */
+
+ memset(&counts, 0, sizeof(counts));
+ counts.expected_paths = expected_files;
+ counts.expected_statuses = expected_status;
+
+ opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
+
+ cl_git_pass(
+ git_status_foreach_ext(g_repo, &opts, cb_status__match, &counts));
+ cl_assert_equal_i(6, counts.entry_count);
+
+ git_repository_free(smrepo);
+}
+
+void test_status_submodules__dirty_workdir_only(void)
+{
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ status_entry_counts counts;
+ static const char *expected_files_with_sub[] = {
+ ".gitmodules",
+ "added",
+ "deleted",
+ "ignored",
+ "modified",
+ "testrepo",
+ "untracked"
+ };
+ static unsigned int expected_status_with_sub[] = {
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_IGNORED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW
+ };
+
+ g_repo = setup_fixture_submodules();
+
+ cl_git_rewritefile("submodules/testrepo/README", "heyheyhey");
+ cl_git_mkfile("submodules/testrepo/all_new.txt", "never seen before");
+
+ /* first do a normal status, which should now include the submodule */
+
+ memset(&counts, 0, sizeof(counts));
+ counts.expected_paths = expected_files_with_sub;
+ counts.expected_statuses = expected_status_with_sub;
+
+ opts.flags = GIT_STATUS_OPT_DEFAULTS;
+
+ cl_git_pass(
+ git_status_foreach_ext(g_repo, &opts, cb_status__match, &counts));
+ cl_assert_equal_i(7, counts.entry_count);
+
+ /* try again with EXCLUDE_SUBMODULES which should skip it */
+
+ memset(&counts, 0, sizeof(counts));
+ counts.expected_paths = expected_files;
+ counts.expected_statuses = expected_status;
+
+ opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
+
+ cl_git_pass(
+ git_status_foreach_ext(g_repo, &opts, cb_status__match, &counts));
+ cl_assert_equal_i(6, counts.entry_count);
+}
diff --git a/tests/status/worktree.c b/tests/status/worktree.c
new file mode 100644
index 000000000..34be6d34c
--- /dev/null
+++ b/tests/status/worktree.c
@@ -0,0 +1,875 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+#include "ignore.h"
+#include "status_data.h"
+#include "posix.h"
+#include "util.h"
+#include "path.h"
+
+/**
+ * Cleanup
+ *
+ * This will be called once after each test finishes, even
+ * if the test failed
+ */
+void test_status_worktree__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+/**
+ * Tests - Status determination on a working tree
+ */
+/* this test is equivalent to t18-status.c:statuscb0 */
+void test_status_worktree__whole_repository(void)
+{
+ status_entry_counts counts;
+ git_repository *repo = cl_git_sandbox_init("status");
+
+ memset(&counts, 0x0, sizeof(status_entry_counts));
+ counts.expected_entry_count = entry_count0;
+ counts.expected_paths = entry_paths0;
+ counts.expected_statuses = entry_statuses0;
+
+ cl_git_pass(
+ git_status_foreach(repo, cb_status__normal, &counts)
+ );
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+}
+
+void assert_show(const int entry_counts, const char *entry_paths[],
+ const unsigned int entry_statuses[], git_status_show_t show)
+{
+ status_entry_counts counts;
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+
+ memset(&counts, 0x0, sizeof(status_entry_counts));
+ counts.expected_entry_count = entry_counts;
+ counts.expected_paths = entry_paths;
+ counts.expected_statuses = entry_statuses;
+
+ opts.flags = GIT_STATUS_OPT_DEFAULTS;
+ opts.show = show;
+
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)
+ );
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+}
+
+void test_status_worktree__show_index_and_workdir(void)
+{
+ assert_show(entry_count0, entry_paths0, entry_statuses0,
+ GIT_STATUS_SHOW_INDEX_AND_WORKDIR);
+}
+
+void test_status_worktree__show_index_only(void)
+{
+ assert_show(entry_count5, entry_paths5, entry_statuses5,
+ GIT_STATUS_SHOW_INDEX_ONLY);
+}
+
+void test_status_worktree__show_workdir_only(void)
+{
+ assert_show(entry_count6, entry_paths6, entry_statuses6,
+ GIT_STATUS_SHOW_WORKDIR_ONLY);
+}
+
+/* this test is equivalent to t18-status.c:statuscb1 */
+void test_status_worktree__empty_repository(void)
+{
+ int count = 0;
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_pass(git_status_foreach(repo, cb_status__count, &count));
+
+ cl_assert_equal_i(0, count);
+}
+
+static int remove_file_cb(void *data, git_buf *file)
+{
+ const char *filename = git_buf_cstr(file);
+
+ GIT_UNUSED(data);
+
+ if (git__suffixcmp(filename, ".git") == 0)
+ return 0;
+
+ if (git_path_isdir(filename))
+ cl_git_pass(git_futils_rmdir_r(filename, NULL, GIT_RMDIR_REMOVE_FILES));
+ else
+ cl_git_pass(p_unlink(git_buf_cstr(file)));
+
+ return 0;
+}
+
+/* this test is equivalent to t18-status.c:statuscb2 */
+void test_status_worktree__purged_worktree(void)
+{
+ status_entry_counts counts;
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_buf workdir = GIT_BUF_INIT;
+
+ /* first purge the contents of the worktree */
+ cl_git_pass(git_buf_sets(&workdir, git_repository_workdir(repo)));
+ cl_git_pass(git_path_direach(&workdir, 0, remove_file_cb, NULL));
+ git_buf_free(&workdir);
+
+ /* now get status */
+ memset(&counts, 0x0, sizeof(status_entry_counts));
+ counts.expected_entry_count = entry_count2;
+ counts.expected_paths = entry_paths2;
+ counts.expected_statuses = entry_statuses2;
+
+ cl_git_pass(
+ git_status_foreach(repo, cb_status__normal, &counts)
+ );
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+}
+
+/* this test is similar to t18-status.c:statuscb3 */
+void test_status_worktree__swap_subdir_and_file(void)
+{
+ status_entry_counts counts;
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_index *index;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ bool ignore_case;
+
+ cl_git_pass(git_repository_index(&index, repo));
+ ignore_case = (git_index_caps(index) & GIT_INDEXCAP_IGNORE_CASE) != 0;
+ git_index_free(index);
+
+ /* first alter the contents of the worktree */
+ cl_git_pass(p_rename("status/current_file", "status/swap"));
+ cl_git_pass(p_rename("status/subdir", "status/current_file"));
+ cl_git_pass(p_rename("status/swap", "status/subdir"));
+
+ cl_git_mkfile("status/.HEADER", "dummy");
+ cl_git_mkfile("status/42-is-not-prime.sigh", "dummy");
+ cl_git_mkfile("status/README.md", "dummy");
+
+ /* now get status */
+ memset(&counts, 0x0, sizeof(status_entry_counts));
+ counts.expected_entry_count = entry_count3;
+ counts.expected_paths = ignore_case ? entry_paths3_icase : entry_paths3;
+ counts.expected_statuses = ignore_case ? entry_statuses3_icase : entry_statuses3;
+
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_INCLUDE_IGNORED;
+
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)
+ );
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+}
+
+void test_status_worktree__swap_subdir_with_recurse_and_pathspec(void)
+{
+ status_entry_counts counts;
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+
+ /* first alter the contents of the worktree */
+ cl_git_pass(p_rename("status/current_file", "status/swap"));
+ cl_git_pass(p_rename("status/subdir", "status/current_file"));
+ cl_git_pass(p_rename("status/swap", "status/subdir"));
+ cl_git_mkfile("status/.new_file", "dummy");
+ cl_git_pass(git_futils_mkdir_r("status/zzz_new_dir", NULL, 0777));
+ cl_git_mkfile("status/zzz_new_dir/new_file", "dummy");
+ cl_git_mkfile("status/zzz_new_file", "dummy");
+
+ /* now get status */
+ memset(&counts, 0x0, sizeof(status_entry_counts));
+ counts.expected_entry_count = entry_count4;
+ counts.expected_paths = entry_paths4;
+ counts.expected_statuses = entry_statuses4;
+
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
+ /* TODO: set pathspec to "current_file" eventually */
+
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)
+ );
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+}
+
+/* this test is equivalent to t18-status.c:singlestatus0 */
+void test_status_worktree__single_file(void)
+{
+ int i;
+ unsigned int status_flags;
+ git_repository *repo = cl_git_sandbox_init("status");
+
+ for (i = 0; i < (int)entry_count0; i++) {
+ cl_git_pass(
+ git_status_file(&status_flags, repo, entry_paths0[i])
+ );
+ cl_assert(entry_statuses0[i] == status_flags);
+ }
+}
+
+/* this test is equivalent to t18-status.c:singlestatus1 */
+void test_status_worktree__single_nonexistent_file(void)
+{
+ int error;
+ unsigned int status_flags;
+ git_repository *repo = cl_git_sandbox_init("status");
+
+ error = git_status_file(&status_flags, repo, "nonexistent");
+ cl_git_fail(error);
+ cl_assert(error == GIT_ENOTFOUND);
+}
+
+/* this test is equivalent to t18-status.c:singlestatus2 */
+void test_status_worktree__single_nonexistent_file_empty_repo(void)
+{
+ int error;
+ unsigned int status_flags;
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+
+ error = git_status_file(&status_flags, repo, "nonexistent");
+ cl_git_fail(error);
+ cl_assert(error == GIT_ENOTFOUND);
+}
+
+/* this test is equivalent to t18-status.c:singlestatus3 */
+void test_status_worktree__single_file_empty_repo(void)
+{
+ unsigned int status_flags;
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_mkfile("empty_standard_repo/new_file", "new_file\n");
+
+ cl_git_pass(git_status_file(&status_flags, repo, "new_file"));
+ cl_assert(status_flags == GIT_STATUS_WT_NEW);
+}
+
+/* this test is equivalent to t18-status.c:singlestatus4 */
+void test_status_worktree__single_folder(void)
+{
+ int error;
+ unsigned int status_flags;
+ git_repository *repo = cl_git_sandbox_init("status");
+
+ error = git_status_file(&status_flags, repo, "subdir");
+ cl_git_fail(error);
+ cl_assert(error != GIT_ENOTFOUND);
+}
+
+
+void test_status_worktree__ignores(void)
+{
+ int i, ignored;
+ git_repository *repo = cl_git_sandbox_init("status");
+
+ for (i = 0; i < (int)entry_count0; i++) {
+ cl_git_pass(
+ git_status_should_ignore(&ignored, repo, entry_paths0[i])
+ );
+ cl_assert(ignored == (entry_statuses0[i] == GIT_STATUS_IGNORED));
+ }
+
+ cl_git_pass(
+ git_status_should_ignore(&ignored, repo, "nonexistent_file")
+ );
+ cl_assert(!ignored);
+
+ cl_git_pass(
+ git_status_should_ignore(&ignored, repo, "ignored_nonexistent_file")
+ );
+ cl_assert(ignored);
+}
+
+static int cb_status__check_592(const char *p, unsigned int s, void *payload)
+{
+ if (s != GIT_STATUS_WT_DELETED ||
+ (payload != NULL && strcmp(p, (const char *)payload) != 0))
+ return -1;
+
+ return 0;
+}
+
+void test_status_worktree__issue_592(void)
+{
+ git_repository *repo;
+ git_buf path = GIT_BUF_INIT;
+
+ repo = cl_git_sandbox_init("issue_592");
+ cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(repo), "l.txt"));
+ cl_git_pass(p_unlink(git_buf_cstr(&path)));
+ cl_assert(!git_path_exists("issue_592/l.txt"));
+
+ cl_git_pass(git_status_foreach(repo, cb_status__check_592, "l.txt"));
+
+ git_buf_free(&path);
+}
+
+void test_status_worktree__issue_592_2(void)
+{
+ git_repository *repo;
+ git_buf path = GIT_BUF_INIT;
+
+ repo = cl_git_sandbox_init("issue_592");
+ cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(repo), "c/a.txt"));
+ cl_git_pass(p_unlink(git_buf_cstr(&path)));
+ cl_assert(!git_path_exists("issue_592/c/a.txt"));
+
+ cl_git_pass(git_status_foreach(repo, cb_status__check_592, "c/a.txt"));
+
+ git_buf_free(&path);
+}
+
+void test_status_worktree__issue_592_3(void)
+{
+ git_repository *repo;
+ git_buf path = GIT_BUF_INIT;
+
+ repo = cl_git_sandbox_init("issue_592");
+
+ cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(repo), "c"));
+ cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_assert(!git_path_exists("issue_592/c/a.txt"));
+
+ cl_git_pass(git_status_foreach(repo, cb_status__check_592, "c/a.txt"));
+
+ git_buf_free(&path);
+}
+
+void test_status_worktree__issue_592_4(void)
+{
+ git_repository *repo;
+ git_buf path = GIT_BUF_INIT;
+
+ repo = cl_git_sandbox_init("issue_592");
+
+ cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(repo), "t/b.txt"));
+ cl_git_pass(p_unlink(git_buf_cstr(&path)));
+
+ cl_git_pass(git_status_foreach(repo, cb_status__check_592, "t/b.txt"));
+
+ git_buf_free(&path);
+}
+
+void test_status_worktree__issue_592_5(void)
+{
+ git_repository *repo;
+ git_buf path = GIT_BUF_INIT;
+
+ repo = cl_git_sandbox_init("issue_592");
+
+ cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(repo), "t"));
+ cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_git_pass(p_mkdir(git_buf_cstr(&path), 0777));
+
+ cl_git_pass(git_status_foreach(repo, cb_status__check_592, NULL));
+
+ git_buf_free(&path);
+}
+
+void test_status_worktree__issue_592_ignores_0(void)
+{
+ int count = 0;
+ status_entry_single st;
+ git_repository *repo = cl_git_sandbox_init("issue_592");
+
+ cl_git_pass(git_status_foreach(repo, cb_status__count, &count));
+ cl_assert_equal_i(0, count);
+
+ cl_git_rewritefile("issue_592/.gitignore",
+ ".gitignore\n*.txt\nc/\n[tT]*/\n");
+
+ cl_git_pass(git_status_foreach(repo, cb_status__count, &count));
+ cl_assert_equal_i(1, count);
+
+ /* This is a situation where the behavior of libgit2 is
+ * different from core git. Core git will show ignored.txt
+ * in the list of ignored files, even though the directory
+ * "t" is ignored and the file is untracked because we have
+ * the explicit "*.txt" ignore rule. Libgit2 just excludes
+ * all untracked files that are contained within ignored
+ * directories without explicitly listing them.
+ */
+ cl_git_rewritefile("issue_592/t/ignored.txt", "ping");
+
+ memset(&st, 0, sizeof(st));
+ cl_git_pass(git_status_foreach(repo, cb_status__single, &st));
+ cl_assert_equal_i(1, st.count);
+ cl_assert(st.status == GIT_STATUS_IGNORED);
+
+ cl_git_rewritefile("issue_592/c/ignored_by_dir", "ping");
+
+ memset(&st, 0, sizeof(st));
+ cl_git_pass(git_status_foreach(repo, cb_status__single, &st));
+ cl_assert_equal_i(1, st.count);
+ cl_assert(st.status == GIT_STATUS_IGNORED);
+
+ cl_git_rewritefile("issue_592/t/ignored_by_dir_pattern", "ping");
+
+ memset(&st, 0, sizeof(st));
+ cl_git_pass(git_status_foreach(repo, cb_status__single, &st));
+ cl_assert_equal_i(1, st.count);
+ cl_assert(st.status == GIT_STATUS_IGNORED);
+}
+
+void test_status_worktree__issue_592_ignored_dirs_with_tracked_content(void)
+{
+ int count = 0;
+ git_repository *repo = cl_git_sandbox_init("issue_592b");
+
+ cl_git_pass(git_status_foreach(repo, cb_status__count, &count));
+ cl_assert_equal_i(1, count);
+
+ /* if we are really mimicking core git, then only ignored1.txt
+ * at the top level will show up in the ignores list here.
+ * everything else will be unmodified or skipped completely.
+ */
+}
+
+void test_status_worktree__conflict_with_diff3(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_index *index;
+ unsigned int status;
+ git_index_entry ancestor_entry, our_entry, their_entry;
+
+ memset(&ancestor_entry, 0x0, sizeof(git_index_entry));
+ memset(&our_entry, 0x0, sizeof(git_index_entry));
+ memset(&their_entry, 0x0, sizeof(git_index_entry));
+
+ ancestor_entry.path = "modified_file";
+ git_oid_fromstr(&ancestor_entry.oid,
+ "452e4244b5d083ddf0460acf1ecc74db9dcfa11a");
+
+ our_entry.path = "modified_file";
+ git_oid_fromstr(&our_entry.oid,
+ "452e4244b5d083ddf0460acf1ecc74db9dcfa11a");
+
+ their_entry.path = "modified_file";
+ git_oid_fromstr(&their_entry.oid,
+ "452e4244b5d083ddf0460acf1ecc74db9dcfa11a");
+
+ cl_git_pass(git_status_file(&status, repo, "modified_file"));
+ cl_assert_equal_i(GIT_STATUS_WT_MODIFIED, status);
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_remove(index, "modified_file", 0));
+ cl_git_pass(git_index_conflict_add(
+ index, &ancestor_entry, &our_entry, &their_entry));
+ cl_git_pass(git_index_write(index));
+ git_index_free(index);
+
+ cl_git_pass(git_status_file(&status, repo, "modified_file"));
+
+ cl_assert_equal_i(GIT_STATUS_INDEX_DELETED | GIT_STATUS_WT_NEW, status);
+}
+
+static const char *filemode_paths[] = {
+ "exec_off",
+ "exec_off2on_staged",
+ "exec_off2on_workdir",
+ "exec_off_untracked",
+ "exec_on",
+ "exec_on2off_staged",
+ "exec_on2off_workdir",
+ "exec_on_untracked",
+};
+
+static unsigned int filemode_statuses[] = {
+ GIT_STATUS_CURRENT,
+ GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_CURRENT,
+ GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW
+};
+
+static const int filemode_count = 8;
+
+void test_status_worktree__filemode_changes(void)
+{
+ git_repository *repo = cl_git_sandbox_init("filemodes");
+ status_entry_counts counts;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+
+ /* overwrite stored filemode with platform appropriate value */
+ if (cl_is_chmod_supported())
+ cl_repo_set_bool(repo, "core.filemode", true);
+ else {
+ int i;
+
+ cl_repo_set_bool(repo, "core.filemode", false);
+
+ /* won't trust filesystem mode diffs, so these will appear unchanged */
+ for (i = 0; i < filemode_count; ++i)
+ if (filemode_statuses[i] == GIT_STATUS_WT_MODIFIED)
+ filemode_statuses[i] = GIT_STATUS_CURRENT;
+ }
+
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_INCLUDE_IGNORED |
+ GIT_STATUS_OPT_INCLUDE_UNMODIFIED;
+
+ memset(&counts, 0, sizeof(counts));
+ counts.expected_entry_count = filemode_count;
+ counts.expected_paths = filemode_paths;
+ counts.expected_statuses = filemode_statuses;
+
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)
+ );
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+}
+
+static int cb_status__interrupt(const char *p, unsigned int s, void *payload)
+{
+ volatile int *count = (int *)payload;
+
+ GIT_UNUSED(p);
+ GIT_UNUSED(s);
+
+ (*count)++;
+
+ return (*count == 8);
+}
+
+void test_status_worktree__interruptable_foreach(void)
+{
+ int count = 0;
+ git_repository *repo = cl_git_sandbox_init("status");
+
+ cl_assert_equal_i(
+ GIT_EUSER, git_status_foreach(repo, cb_status__interrupt, &count)
+ );
+
+ cl_assert_equal_i(8, count);
+}
+
+void test_status_worktree__line_endings_dont_count_as_changes_with_autocrlf(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ unsigned int status;
+
+ cl_repo_set_bool(repo, "core.autocrlf", true);
+
+ cl_git_rewritefile("status/current_file", "current_file\r\n");
+
+ cl_git_pass(git_status_file(&status, repo, "current_file"));
+
+ cl_assert_equal_i(GIT_STATUS_CURRENT, status);
+}
+
+void test_status_worktree__line_endings_dont_count_as_changes_with_autocrlf_issue_1397(void)
+{
+ git_repository *repo = cl_git_sandbox_init("issue_1397");
+ unsigned int status;
+
+ cl_repo_set_bool(repo, "core.autocrlf", true);
+
+ cl_git_pass(git_status_file(&status, repo, "crlf_file.txt"));
+
+ cl_assert_equal_i(GIT_STATUS_CURRENT, status);
+}
+
+void test_status_worktree__conflicted_item(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_index *index;
+ unsigned int status;
+ git_index_entry ancestor_entry, our_entry, their_entry;
+
+ memset(&ancestor_entry, 0x0, sizeof(git_index_entry));
+ memset(&our_entry, 0x0, sizeof(git_index_entry));
+ memset(&their_entry, 0x0, sizeof(git_index_entry));
+
+ ancestor_entry.path = "modified_file";
+ git_oid_fromstr(&ancestor_entry.oid,
+ "452e4244b5d083ddf0460acf1ecc74db9dcfa11a");
+
+ our_entry.path = "modified_file";
+ git_oid_fromstr(&our_entry.oid,
+ "452e4244b5d083ddf0460acf1ecc74db9dcfa11a");
+
+ their_entry.path = "modified_file";
+ git_oid_fromstr(&their_entry.oid,
+ "452e4244b5d083ddf0460acf1ecc74db9dcfa11a");
+
+ cl_git_pass(git_status_file(&status, repo, "modified_file"));
+ cl_assert_equal_i(GIT_STATUS_WT_MODIFIED, status);
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_conflict_add(index, &ancestor_entry,
+ &our_entry, &their_entry));
+
+ cl_git_pass(git_status_file(&status, repo, "modified_file"));
+ cl_assert_equal_i(GIT_STATUS_WT_MODIFIED, status);
+
+ git_index_free(index);
+}
+
+static void stage_and_commit(git_repository *repo, const char *path)
+{
+ git_index *index;
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_add_bypath(index, path));
+ cl_repo_commit_from_index(NULL, repo, NULL, 1323847743, "Initial commit\n");
+ git_index_free(index);
+}
+
+static void assert_ignore_case(
+ bool should_ignore_case,
+ int expected_lower_cased_file_status,
+ int expected_camel_cased_file_status)
+{
+ unsigned int status;
+ git_buf lower_case_path = GIT_BUF_INIT, camel_case_path = GIT_BUF_INIT;
+ git_repository *repo, *repo2;
+
+ repo = cl_git_sandbox_init("empty_standard_repo");
+ cl_git_remove_placeholders(git_repository_path(repo), "dummy-marker.txt");
+
+ cl_repo_set_bool(repo, "core.ignorecase", should_ignore_case);
+
+ cl_git_pass(git_buf_joinpath(&lower_case_path,
+ git_repository_workdir(repo), "plop"));
+
+ cl_git_mkfile(git_buf_cstr(&lower_case_path), "");
+
+ stage_and_commit(repo, "plop");
+
+ cl_git_pass(git_repository_open(&repo2, "./empty_standard_repo"));
+
+ cl_git_pass(git_status_file(&status, repo2, "plop"));
+ cl_assert_equal_i(GIT_STATUS_CURRENT, status);
+
+ cl_git_pass(git_buf_joinpath(&camel_case_path,
+ git_repository_workdir(repo), "Plop"));
+
+ cl_git_pass(p_rename(git_buf_cstr(&lower_case_path), git_buf_cstr(&camel_case_path)));
+
+ cl_git_pass(git_status_file(&status, repo2, "plop"));
+ cl_assert_equal_i(expected_lower_cased_file_status, status);
+
+ cl_git_pass(git_status_file(&status, repo2, "Plop"));
+ cl_assert_equal_i(expected_camel_cased_file_status, status);
+
+ git_repository_free(repo2);
+ git_buf_free(&lower_case_path);
+ git_buf_free(&camel_case_path);
+}
+
+void test_status_worktree__file_status_honors_core_ignorecase_true(void)
+{
+ assert_ignore_case(true, GIT_STATUS_CURRENT, GIT_STATUS_CURRENT);
+}
+
+void test_status_worktree__file_status_honors_core_ignorecase_false(void)
+{
+ assert_ignore_case(false, GIT_STATUS_WT_DELETED, GIT_STATUS_WT_NEW);
+}
+
+void test_status_worktree__file_status_honors_case_ignorecase_regarding_untracked_files(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ unsigned int status;
+ git_index *index;
+
+ cl_repo_set_bool(repo, "core.ignorecase", false);
+
+ repo = cl_git_sandbox_reopen();
+
+ /* Actually returns GIT_STATUS_IGNORED on Windows */
+ cl_git_fail_with(git_status_file(&status, repo, "NEW_FILE"), GIT_ENOTFOUND);
+
+ cl_git_pass(git_repository_index(&index, repo));
+
+ cl_git_pass(git_index_add_bypath(index, "new_file"));
+ cl_git_pass(git_index_write(index));
+ git_index_free(index);
+
+ /* Actually returns GIT_STATUS_IGNORED on Windows */
+ cl_git_fail_with(git_status_file(&status, repo, "NEW_FILE"), GIT_ENOTFOUND);
+}
+
+void test_status_worktree__simple_delete(void)
+{
+ git_repository *repo = cl_git_sandbox_init("renames");
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ int count;
+
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH |
+ GIT_STATUS_OPT_EXCLUDE_SUBMODULES |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
+
+ count = 0;
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__count, &count) );
+ cl_assert_equal_i(0, count);
+
+ cl_must_pass(p_unlink("renames/untimely.txt"));
+
+ count = 0;
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__count, &count) );
+ cl_assert_equal_i(1, count);
+}
+
+void test_status_worktree__simple_delete_indexed(void)
+{
+ git_repository *repo = cl_git_sandbox_init("renames");
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ git_status_list *status;
+
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH |
+ GIT_STATUS_OPT_EXCLUDE_SUBMODULES |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
+
+ cl_git_pass(git_status_list_new(&status, repo, &opts));
+ cl_assert_equal_sz(0, git_status_list_entrycount(status));
+ git_status_list_free(status);
+
+ cl_must_pass(p_unlink("renames/untimely.txt"));
+
+ cl_git_pass(git_status_list_new(&status, repo, &opts));
+ cl_assert_equal_sz(1, git_status_list_entrycount(status));
+ cl_assert_equal_i(
+ GIT_STATUS_WT_DELETED, git_status_byindex(status, 0)->status);
+ git_status_list_free(status);
+}
+
+static const char *icase_paths[] = { "B", "c", "g", "H" };
+static unsigned int icase_statuses[] = {
+ GIT_STATUS_WT_MODIFIED, GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_MODIFIED, GIT_STATUS_WT_DELETED,
+};
+
+static const char *case_paths[] = { "B", "H", "c", "g" };
+static unsigned int case_statuses[] = {
+ GIT_STATUS_WT_MODIFIED, GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED, GIT_STATUS_WT_MODIFIED,
+};
+
+void test_status_worktree__sorting_by_case(void)
+{
+ git_repository *repo = cl_git_sandbox_init("icase");
+ git_index *index;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ bool native_ignore_case;
+ status_entry_counts counts;
+
+ cl_git_pass(git_repository_index(&index, repo));
+ native_ignore_case =
+ (git_index_caps(index) & GIT_INDEXCAP_IGNORE_CASE) != 0;
+ git_index_free(index);
+
+ memset(&counts, 0, sizeof(counts));
+ counts.expected_entry_count = 0;
+ counts.expected_paths = NULL;
+ counts.expected_statuses = NULL;
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts));
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+
+ cl_git_rewritefile("icase/B", "new stuff");
+ cl_must_pass(p_unlink("icase/c"));
+ cl_git_rewritefile("icase/g", "new stuff");
+ cl_must_pass(p_unlink("icase/H"));
+
+ memset(&counts, 0, sizeof(counts));
+ counts.expected_entry_count = 4;
+ if (native_ignore_case) {
+ counts.expected_paths = icase_paths;
+ counts.expected_statuses = icase_statuses;
+ } else {
+ counts.expected_paths = case_paths;
+ counts.expected_statuses = case_statuses;
+ }
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts));
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+
+ opts.flags = GIT_STATUS_OPT_SORT_CASE_SENSITIVELY;
+
+ memset(&counts, 0, sizeof(counts));
+ counts.expected_entry_count = 4;
+ counts.expected_paths = case_paths;
+ counts.expected_statuses = case_statuses;
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts));
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+
+ opts.flags = GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY;
+
+ memset(&counts, 0, sizeof(counts));
+ counts.expected_entry_count = 4;
+ counts.expected_paths = icase_paths;
+ counts.expected_statuses = icase_statuses;
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts));
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+}
+
+void test_status_worktree__long_filenames(void)
+{
+ char path[260*4+1];
+ const char *expected_paths[] = {path};
+ const unsigned int expected_statuses[] = {GIT_STATUS_WT_NEW};
+
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ status_entry_counts counts = {0};
+
+ // Create directory with amazingly long filename
+ sprintf(path, "empty_standard_repo/%s", longname);
+ cl_git_pass(git_futils_mkdir_r(path, NULL, 0777));
+ sprintf(path, "empty_standard_repo/%s/foo", longname);
+ cl_git_mkfile(path, "dummy");
+
+ sprintf(path, "%s/foo", longname);
+ counts.expected_entry_count = 1;
+ counts.expected_paths = expected_paths;
+ counts.expected_statuses = expected_statuses;
+
+ opts.show = GIT_STATUS_SHOW_WORKDIR_ONLY;
+ opts.flags = GIT_STATUS_OPT_DEFAULTS;
+
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) );
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+}
+
diff --git a/tests-clar/status/worktree_init.c b/tests/status/worktree_init.c
index 296c27c86..296c27c86 100644
--- a/tests-clar/status/worktree_init.c
+++ b/tests/status/worktree_init.c
diff --git a/tests/stress/diff.c b/tests/stress/diff.c
new file mode 100644
index 000000000..3d2092614
--- /dev/null
+++ b/tests/stress/diff.c
@@ -0,0 +1,146 @@
+#include "clar_libgit2.h"
+#include "../diff/diff_helpers.h"
+
+static git_repository *g_repo = NULL;
+
+void test_stress_diff__initialize(void)
+{
+}
+
+void test_stress_diff__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+#define ANOTHER_POEM \
+"OH, glorious are the guarded heights\nWhere guardian souls abide—\nSelf-exiled from our gross delights—\nAbove, beyond, outside:\nAn ampler arc their spirit swings—\nCommands a juster view—\nWe have their word for all these things,\nNo doubt their words are true.\n\nYet we, the bond slaves of our day,\nWhom dirt and danger press—\nCo-heirs of insolence, delay,\nAnd leagued unfaithfulness—\nSuch is our need must seek indeed\nAnd, having found, engage\nThe men who merely do the work\nFor which they draw the wage.\n\nFrom forge and farm and mine and bench,\nDeck, altar, outpost lone—\nMill, school, battalion, counter, trench,\nRail, senate, sheepfold, throne—\nCreation's cry goes up on high\nFrom age to cheated age:\n\"Send us the men who do the work\n\"For which they draw the wage!\"\n"
+
+static void test_with_many(int expected_new)
+{
+ git_index *index;
+ git_tree *tree, *new_tree;
+ git_diff *diff = NULL;
+ diff_expects exp;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(
+ git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(p_rename("renames/ikeepsix.txt", "renames/ikeepsix2.txt"));
+ cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
+ cl_git_pass(git_index_add_bypath(index, "ikeepsix2.txt"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, NULL, NULL, &exp));
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(expected_new + 1, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(expected_new + 2, exp.files);
+
+ opts.flags = GIT_DIFF_FIND_ALL;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, NULL, NULL, &exp));
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
+ cl_assert_equal_i(expected_new, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(expected_new + 1, exp.files);
+
+ git_diff_free(diff);
+
+ cl_repo_commit_from_index(NULL, g_repo, NULL, 1372350000, "yoyoyo");
+ cl_git_pass(git_revparse_single(
+ (git_object **)&new_tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, tree, new_tree, &diffopts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, NULL, NULL, &exp));
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(expected_new + 1, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(expected_new + 2, exp.files);
+
+ opts.flags = GIT_DIFF_FIND_ALL;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, NULL, NULL, &exp));
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
+ cl_assert_equal_i(expected_new, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(expected_new + 1, exp.files);
+
+ git_diff_free(diff);
+
+ git_tree_free(new_tree);
+ git_tree_free(tree);
+ git_index_free(index);
+}
+
+void test_stress_diff__rename_big_files(void)
+{
+ git_index *index;
+ char tmp[64];
+ int i, j;
+ git_buf b = GIT_BUF_INIT;
+
+ g_repo = cl_git_sandbox_init("renames");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ for (i = 0; i < 100; i += 1) {
+ snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i);
+ for (j = i * 256; j > 0; --j)
+ git_buf_printf(&b, "more content %d\n", i);
+ cl_git_mkfile(tmp, b.ptr);
+ }
+
+ for (i = 0; i < 100; i += 1) {
+ snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i);
+ cl_git_pass(git_index_add_bypath(index, tmp + strlen("renames/")));
+ }
+
+ git_buf_free(&b);
+ git_index_free(index);
+
+ test_with_many(100);
+}
+
+void test_stress_diff__rename_many_files(void)
+{
+ git_index *index;
+ char tmp[64];
+ int i;
+ git_buf b = GIT_BUF_INIT;
+
+ g_repo = cl_git_sandbox_init("renames");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ git_buf_printf(&b, "%08d\n" ANOTHER_POEM "%08d\n" ANOTHER_POEM ANOTHER_POEM, 0, 0);
+
+ for (i = 0; i < 2500; i += 1) {
+ snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i);
+ snprintf(b.ptr, 9, "%08d", i);
+ b.ptr[8] = '\n';
+ cl_git_mkfile(tmp, b.ptr);
+ }
+ git_buf_free(&b);
+
+ for (i = 0; i < 2500; i += 1) {
+ snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i);
+ cl_git_pass(git_index_add_bypath(index, tmp + strlen("renames/")));
+ }
+
+ git_index_free(index);
+
+ test_with_many(2500);
+}
diff --git a/tests/submodule/lookup.c b/tests/submodule/lookup.c
new file mode 100644
index 000000000..5f320e702
--- /dev/null
+++ b/tests/submodule/lookup.c
@@ -0,0 +1,172 @@
+#include "clar_libgit2.h"
+#include "submodule_helpers.h"
+#include "posix.h"
+#include "git2/sys/repository.h"
+
+static git_repository *g_repo = NULL;
+
+void test_submodule_lookup__initialize(void)
+{
+ g_repo = setup_fixture_submod2();
+}
+
+void test_submodule_lookup__simple_lookup(void)
+{
+ git_submodule *sm;
+
+ /* lookup existing */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_assert(sm);
+
+ /* lookup pending change in .gitmodules that is not in HEAD */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited"));
+ cl_assert(sm);
+
+ /* lookup pending change in .gitmodules that is neither in HEAD nor index */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_gitmodules_only"));
+ cl_assert(sm);
+
+ /* lookup git repo subdir that is not added as submodule */
+ cl_assert(git_submodule_lookup(&sm, g_repo, "not-submodule") == GIT_EEXISTS);
+
+ /* lookup existing directory that is not a submodule */
+ cl_assert(git_submodule_lookup(&sm, g_repo, "just_a_dir") == GIT_ENOTFOUND);
+
+ /* lookup existing file that is not a submodule */
+ cl_assert(git_submodule_lookup(&sm, g_repo, "just_a_file") == GIT_ENOTFOUND);
+
+ /* lookup non-existent item */
+ cl_assert(git_submodule_lookup(&sm, g_repo, "no_such_file") == GIT_ENOTFOUND);
+}
+
+void test_submodule_lookup__accessors(void)
+{
+ git_submodule *sm;
+ const char *oid = "480095882d281ed676fe5b863569520e54a7d5c0";
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_assert(git_submodule_owner(sm) == g_repo);
+ cl_assert_equal_s("sm_unchanged", git_submodule_name(sm));
+ cl_assert(git__suffixcmp(git_submodule_path(sm), "sm_unchanged") == 0);
+ cl_assert(git__suffixcmp(git_submodule_url(sm), "/submod2_target") == 0);
+
+ cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
+ cl_assert(git_oid_streq(git_submodule_head_id(sm), oid) == 0);
+ cl_assert(git_oid_streq(git_submodule_wd_id(sm), oid) == 0);
+
+ cl_assert(git_submodule_ignore(sm) == GIT_SUBMODULE_IGNORE_NONE);
+ cl_assert(git_submodule_update(sm) == GIT_SUBMODULE_UPDATE_CHECKOUT);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
+ cl_assert_equal_s("sm_changed_head", git_submodule_name(sm));
+
+ cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
+ cl_assert(git_oid_streq(git_submodule_head_id(sm), oid) == 0);
+ cl_assert(git_oid_streq(git_submodule_wd_id(sm),
+ "3d9386c507f6b093471a3e324085657a3c2b4247") == 0);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited"));
+ cl_assert_equal_s("sm_added_and_uncommited", git_submodule_name(sm));
+
+ cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
+ cl_assert(git_submodule_head_id(sm) == NULL);
+ cl_assert(git_oid_streq(git_submodule_wd_id(sm), oid) == 0);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits"));
+ cl_assert_equal_s("sm_missing_commits", git_submodule_name(sm));
+
+ cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
+ cl_assert(git_oid_streq(git_submodule_head_id(sm), oid) == 0);
+ cl_assert(git_oid_streq(git_submodule_wd_id(sm),
+ "5e4963595a9774b90524d35a807169049de8ccad") == 0);
+}
+
+typedef struct {
+ int count;
+} sm_lookup_data;
+
+static int sm_lookup_cb(git_submodule *sm, const char *name, void *payload)
+{
+ sm_lookup_data *data = payload;
+ data->count += 1;
+ cl_assert_equal_s(git_submodule_name(sm), name);
+ return 0;
+}
+
+void test_submodule_lookup__foreach(void)
+{
+ sm_lookup_data data;
+ memset(&data, 0, sizeof(data));
+ cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data));
+ cl_assert_equal_i(8, data.count);
+}
+
+void test_submodule_lookup__lookup_even_with_unborn_head(void)
+{
+ git_reference *head;
+ git_submodule *sm;
+
+ /* put us on an unborn branch */
+ cl_git_pass(git_reference_symbolic_create(
+ &head, g_repo, "HEAD", "refs/heads/garbage", 1));
+ git_reference_free(head);
+
+ /* lookup existing */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_assert(sm);
+
+ /* lookup pending change in .gitmodules that is not in HEAD */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited"));
+ cl_assert(sm);
+
+ /* lookup pending change in .gitmodules that is neither in HEAD nor index */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_gitmodules_only"));
+ cl_assert(sm);
+
+ /* lookup git repo subdir that is not added as submodule */
+ cl_assert_equal_i(GIT_EEXISTS, git_submodule_lookup(&sm, g_repo, "not-submodule"));
+
+ /* lookup existing directory that is not a submodule */
+ cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "just_a_dir"));
+
+ /* lookup existing file that is not a submodule */
+ cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "just_a_file"));
+
+ /* lookup non-existent item */
+ cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "no_such_file"));
+}
+
+void test_submodule_lookup__lookup_even_with_missing_index(void)
+{
+ git_index *idx;
+ git_submodule *sm;
+
+ /* give the repo an empty index */
+ cl_git_pass(git_index_new(&idx));
+ git_repository_set_index(g_repo, idx);
+ git_index_free(idx);
+
+ /* lookup existing */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_assert(sm);
+
+ /* lookup pending change in .gitmodules that is not in HEAD */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited"));
+ cl_assert(sm);
+
+ /* lookup pending change in .gitmodules that is neither in HEAD nor index */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_gitmodules_only"));
+ cl_assert(sm);
+
+ /* lookup git repo subdir that is not added as submodule */
+ cl_assert_equal_i(GIT_EEXISTS, git_submodule_lookup(&sm, g_repo, "not-submodule"));
+
+ /* lookup existing directory that is not a submodule */
+ cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "just_a_dir"));
+
+ /* lookup existing file that is not a submodule */
+ cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "just_a_file"));
+
+ /* lookup non-existent item */
+ cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "no_such_file"));
+}
diff --git a/tests/submodule/modify.c b/tests/submodule/modify.c
new file mode 100644
index 000000000..e326287a6
--- /dev/null
+++ b/tests/submodule/modify.c
@@ -0,0 +1,253 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "path.h"
+#include "submodule_helpers.h"
+
+static git_repository *g_repo = NULL;
+
+#define SM_LIBGIT2_URL "https://github.com/libgit2/libgit2.git"
+#define SM_LIBGIT2 "sm_libgit2"
+#define SM_LIBGIT2B "sm_libgit2b"
+
+void test_submodule_modify__initialize(void)
+{
+ g_repo = setup_fixture_submod2();
+}
+
+void test_submodule_modify__add(void)
+{
+ git_submodule *sm;
+ git_config *cfg;
+ const char *s;
+
+ /* re-add existing submodule */
+ cl_assert(
+ git_submodule_add_setup(NULL, g_repo, "whatever", "sm_unchanged", 1) ==
+ GIT_EEXISTS );
+
+ /* add a submodule using a gitlink */
+
+ cl_git_pass(
+ git_submodule_add_setup(&sm, g_repo, SM_LIBGIT2_URL, SM_LIBGIT2, 1)
+ );
+
+ cl_assert(git_path_isfile("submod2/" SM_LIBGIT2 "/.git"));
+
+ cl_assert(git_path_isdir("submod2/.git/modules"));
+ cl_assert(git_path_isdir("submod2/.git/modules/" SM_LIBGIT2));
+ cl_assert(git_path_isfile("submod2/.git/modules/" SM_LIBGIT2 "/HEAD"));
+
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(
+ git_config_get_string(&s, cfg, "submodule." SM_LIBGIT2 ".url"));
+ cl_assert_equal_s(s, SM_LIBGIT2_URL);
+ git_config_free(cfg);
+
+ /* add a submodule not using a gitlink */
+
+ cl_git_pass(
+ git_submodule_add_setup(&sm, g_repo, SM_LIBGIT2_URL, SM_LIBGIT2B, 0)
+ );
+
+ cl_assert(git_path_isdir("submod2/" SM_LIBGIT2B "/.git"));
+ cl_assert(git_path_isfile("submod2/" SM_LIBGIT2B "/.git/HEAD"));
+ cl_assert(!git_path_exists("submod2/.git/modules/" SM_LIBGIT2B));
+
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(
+ git_config_get_string(&s, cfg, "submodule." SM_LIBGIT2B ".url"));
+ cl_assert_equal_s(s, SM_LIBGIT2_URL);
+ git_config_free(cfg);
+}
+
+static int delete_one_config(const git_config_entry *entry, void *payload)
+{
+ git_config *cfg = payload;
+ return git_config_delete_entry(cfg, entry->name);
+}
+
+static int init_one_submodule(
+ git_submodule *sm, const char *name, void *payload)
+{
+ GIT_UNUSED(name);
+ GIT_UNUSED(payload);
+ return git_submodule_init(sm, false);
+}
+
+void test_submodule_modify__init(void)
+{
+ git_config *cfg;
+ const char *str;
+
+ /* erase submodule data from .git/config */
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(
+ git_config_foreach_match(cfg, "submodule\\..*", delete_one_config, cfg));
+ git_config_free(cfg);
+
+ /* confirm no submodule data in config */
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_fail(git_config_get_string(&str, cfg, "submodule.sm_unchanged.url"));
+ cl_git_fail(git_config_get_string(&str, cfg, "submodule.sm_changed_head.url"));
+ cl_git_fail(git_config_get_string(&str, cfg, "submodule.sm_added_and_uncommited.url"));
+ git_config_free(cfg);
+
+ /* call init and see that settings are copied */
+ cl_git_pass(git_submodule_foreach(g_repo, init_one_submodule, NULL));
+
+ git_submodule_reload_all(g_repo);
+
+ /* confirm submodule data in config */
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(git_config_get_string(&str, cfg, "submodule.sm_unchanged.url"));
+ cl_assert(git__suffixcmp(str, "/submod2_target") == 0);
+ cl_git_pass(git_config_get_string(&str, cfg, "submodule.sm_changed_head.url"));
+ cl_assert(git__suffixcmp(str, "/submod2_target") == 0);
+ cl_git_pass(git_config_get_string(&str, cfg, "submodule.sm_added_and_uncommited.url"));
+ cl_assert(git__suffixcmp(str, "/submod2_target") == 0);
+ git_config_free(cfg);
+}
+
+static int sync_one_submodule(
+ git_submodule *sm, const char *name, void *payload)
+{
+ GIT_UNUSED(name);
+ GIT_UNUSED(payload);
+ return git_submodule_sync(sm);
+}
+
+void test_submodule_modify__sync(void)
+{
+ git_submodule *sm1, *sm2, *sm3;
+ git_config *cfg;
+ const char *str;
+
+#define SM1 "sm_unchanged"
+#define SM2 "sm_changed_head"
+#define SM3 "sm_added_and_uncommited"
+
+ /* look up some submodules */
+ cl_git_pass(git_submodule_lookup(&sm1, g_repo, SM1));
+ cl_git_pass(git_submodule_lookup(&sm2, g_repo, SM2));
+ cl_git_pass(git_submodule_lookup(&sm3, g_repo, SM3));
+
+ /* At this point, the .git/config URLs for the submodules have
+ * not be rewritten with the absolute paths (although the
+ * .gitmodules have. Let's confirm that they DO NOT match
+ * yet, then we can do a sync to make them match...
+ */
+
+ /* check submodule info does not match before sync */
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(git_config_get_string(&str, cfg, "submodule."SM1".url"));
+ cl_assert(strcmp(git_submodule_url(sm1), str) != 0);
+ cl_git_pass(git_config_get_string(&str, cfg, "submodule."SM2".url"));
+ cl_assert(strcmp(git_submodule_url(sm2), str) != 0);
+ cl_git_pass(git_config_get_string(&str, cfg, "submodule."SM3".url"));
+ cl_assert(strcmp(git_submodule_url(sm3), str) != 0);
+ git_config_free(cfg);
+
+ /* sync all the submodules */
+ cl_git_pass(git_submodule_foreach(g_repo, sync_one_submodule, NULL));
+
+ /* check that submodule config is updated */
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(git_config_get_string(&str, cfg, "submodule."SM1".url"));
+ cl_assert_equal_s(git_submodule_url(sm1), str);
+ cl_git_pass(git_config_get_string(&str, cfg, "submodule."SM2".url"));
+ cl_assert_equal_s(git_submodule_url(sm2), str);
+ cl_git_pass(git_config_get_string(&str, cfg, "submodule."SM3".url"));
+ cl_assert_equal_s(git_submodule_url(sm3), str);
+ git_config_free(cfg);
+}
+
+void test_submodule_modify__edit_and_save(void)
+{
+ git_submodule *sm1, *sm2;
+ char *old_url;
+ git_submodule_ignore_t old_ignore;
+ git_submodule_update_t old_update;
+ git_repository *r2;
+ int old_fetchrecurse;
+
+ cl_git_pass(git_submodule_lookup(&sm1, g_repo, "sm_changed_head"));
+
+ old_url = git__strdup(git_submodule_url(sm1));
+
+ /* modify properties of submodule */
+ cl_git_pass(git_submodule_set_url(sm1, SM_LIBGIT2_URL));
+ old_ignore = git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_UNTRACKED);
+ old_update = git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_REBASE);
+ old_fetchrecurse = git_submodule_set_fetch_recurse_submodules(sm1, 1);
+
+ cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm1));
+ cl_assert_equal_i(
+ (int)GIT_SUBMODULE_IGNORE_UNTRACKED, (int)git_submodule_ignore(sm1));
+ cl_assert_equal_i(
+ (int)GIT_SUBMODULE_UPDATE_REBASE, (int)git_submodule_update(sm1));
+ cl_assert_equal_i(1, git_submodule_fetch_recurse_submodules(sm1));
+
+ /* revert without saving (and confirm setters return old value) */
+ cl_git_pass(git_submodule_set_url(sm1, old_url));
+ cl_assert_equal_i(
+ (int)GIT_SUBMODULE_IGNORE_UNTRACKED,
+ (int)git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_RESET));
+ cl_assert_equal_i(
+ (int)GIT_SUBMODULE_UPDATE_REBASE,
+ (int)git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_RESET));
+ cl_assert_equal_i(
+ 1, git_submodule_set_fetch_recurse_submodules(sm1, old_fetchrecurse));
+
+ /* check that revert was successful */
+ cl_assert_equal_s(old_url, git_submodule_url(sm1));
+ cl_assert_equal_i((int)old_ignore, (int)git_submodule_ignore(sm1));
+ cl_assert_equal_i((int)old_update, (int)git_submodule_update(sm1));
+ cl_assert_equal_i(
+ old_fetchrecurse, git_submodule_fetch_recurse_submodules(sm1));
+
+ /* modify properties of submodule (again) */
+ cl_git_pass(git_submodule_set_url(sm1, SM_LIBGIT2_URL));
+ git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_UNTRACKED);
+ git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_REBASE);
+ git_submodule_set_fetch_recurse_submodules(sm1, 1);
+
+ /* call save */
+ cl_git_pass(git_submodule_save(sm1));
+
+ /* attempt to "revert" values */
+ git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_RESET);
+ git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_RESET);
+
+ /* but ignore and update should NOT revert because the RESET
+ * should now be the newly saved value...
+ */
+ cl_assert_equal_i(
+ (int)GIT_SUBMODULE_IGNORE_UNTRACKED, (int)git_submodule_ignore(sm1));
+ cl_assert_equal_i(
+ (int)GIT_SUBMODULE_UPDATE_REBASE, (int)git_submodule_update(sm1));
+ cl_assert_equal_i(1, git_submodule_fetch_recurse_submodules(sm1));
+
+ /* call reload and check that the new values are loaded */
+ cl_git_pass(git_submodule_reload(sm1));
+
+ cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm1));
+ cl_assert_equal_i(
+ (int)GIT_SUBMODULE_IGNORE_UNTRACKED, (int)git_submodule_ignore(sm1));
+ cl_assert_equal_i(
+ (int)GIT_SUBMODULE_UPDATE_REBASE, (int)git_submodule_update(sm1));
+ cl_assert_equal_i(1, git_submodule_fetch_recurse_submodules(sm1));
+
+ /* open a second copy of the repo and compare submodule */
+ cl_git_pass(git_repository_open(&r2, "submod2"));
+ cl_git_pass(git_submodule_lookup(&sm2, r2, "sm_changed_head"));
+
+ cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm2));
+ cl_assert_equal_i(
+ (int)GIT_SUBMODULE_IGNORE_UNTRACKED, (int)git_submodule_ignore(sm2));
+ cl_assert_equal_i(
+ (int)GIT_SUBMODULE_UPDATE_REBASE, (int)git_submodule_update(sm2));
+ cl_assert_equal_i(1, git_submodule_fetch_recurse_submodules(sm2));
+
+ git_repository_free(r2);
+ git__free(old_url);
+}
diff --git a/tests/submodule/status.c b/tests/submodule/status.c
new file mode 100644
index 000000000..f5111c84f
--- /dev/null
+++ b/tests/submodule/status.c
@@ -0,0 +1,425 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "path.h"
+#include "submodule_helpers.h"
+#include "fileops.h"
+#include "iterator.h"
+
+static git_repository *g_repo = NULL;
+
+void test_submodule_status__initialize(void)
+{
+ g_repo = setup_fixture_submod2();
+}
+
+void test_submodule_status__cleanup(void)
+{
+}
+
+void test_submodule_status__unchanged(void)
+{
+ unsigned int status, expected;
+ git_submodule *sm;
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ expected = GIT_SUBMODULE_STATUS_IN_HEAD |
+ GIT_SUBMODULE_STATUS_IN_INDEX |
+ GIT_SUBMODULE_STATUS_IN_CONFIG |
+ GIT_SUBMODULE_STATUS_IN_WD;
+
+ cl_assert(status == expected);
+}
+
+/* 4 values of GIT_SUBMODULE_IGNORE to check */
+
+void test_submodule_status__ignore_none(void)
+{
+ unsigned int status;
+ git_submodule *sm;
+ git_buf path = GIT_BUF_INIT;
+
+ cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged"));
+ cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES));
+
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_submodule_lookup(&sm, g_repo, "just_a_dir"));
+ cl_assert_equal_i(GIT_EEXISTS,
+ git_submodule_lookup(&sm, g_repo, "not-submodule"));
+ cl_assert_equal_i(GIT_EEXISTS,
+ git_submodule_lookup(&sm, g_repo, "not"));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_index"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED) != 0);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_file"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED) != 0);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_untracked_file"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNTRACKED) != 0);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_ADDED) != 0);
+
+ /* removed sm_unchanged for deleted workdir */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0);
+
+ /* now mkdir sm_unchanged to test uninitialized */
+ cl_git_pass(git_futils_mkdir(git_buf_cstr(&path), NULL, 0755, 0));
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_git_pass(git_submodule_reload(sm));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0);
+
+ /* update sm_changed_head in index */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
+ cl_git_pass(git_submodule_add_to_index(sm, true));
+ /* reload is not needed because add_to_index updates the submodule data */
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0);
+
+ /* remove sm_changed_head from index */
+ {
+ git_index *index;
+ size_t pos;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_assert(!git_index_find(&pos, index, "sm_changed_head"));
+ cl_git_pass(git_index_remove(index, "sm_changed_head", 0));
+ cl_git_pass(git_index_write(index));
+
+ git_index_free(index);
+ }
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
+ cl_git_pass(git_submodule_reload(sm));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_DELETED) != 0);
+
+ git_buf_free(&path);
+}
+
+static int set_sm_ignore(git_submodule *sm, const char *name, void *payload)
+{
+ git_submodule_ignore_t ignore = *(git_submodule_ignore_t *)payload;
+ GIT_UNUSED(name);
+ git_submodule_set_ignore(sm, ignore);
+ return 0;
+}
+
+void test_submodule_status__ignore_untracked(void)
+{
+ unsigned int status;
+ git_submodule *sm;
+ git_buf path = GIT_BUF_INIT;
+ git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_UNTRACKED;
+
+ cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged"));
+ cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES));
+
+ cl_git_pass(git_submodule_foreach(g_repo, set_sm_ignore, &ign));
+
+ cl_git_fail(git_submodule_lookup(&sm, g_repo, "not-submodule"));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_index"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED) != 0);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_file"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED) != 0);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_untracked_file"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_ADDED) != 0);
+
+ /* removed sm_unchanged for deleted workdir */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0);
+
+ /* now mkdir sm_unchanged to test uninitialized */
+ cl_git_pass(git_futils_mkdir(git_buf_cstr(&path), NULL, 0755, 0));
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_git_pass(git_submodule_reload(sm));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0);
+
+ /* update sm_changed_head in index */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
+ cl_git_pass(git_submodule_add_to_index(sm, true));
+ /* reload is not needed because add_to_index updates the submodule data */
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0);
+
+ git_buf_free(&path);
+}
+
+void test_submodule_status__ignore_dirty(void)
+{
+ unsigned int status;
+ git_submodule *sm;
+ git_buf path = GIT_BUF_INIT;
+ git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_DIRTY;
+
+ cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged"));
+ cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES));
+
+ cl_git_pass(git_submodule_foreach(g_repo, set_sm_ignore, &ign));
+
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_submodule_lookup(&sm, g_repo, "just_a_dir"));
+ cl_assert_equal_i(GIT_EEXISTS,
+ git_submodule_lookup(&sm, g_repo, "not-submodule"));
+ cl_assert_equal_i(GIT_EEXISTS,
+ git_submodule_lookup(&sm, g_repo, "not"));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_index"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_file"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_untracked_file"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_ADDED) != 0);
+
+ /* removed sm_unchanged for deleted workdir */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0);
+
+ /* now mkdir sm_unchanged to test uninitialized */
+ cl_git_pass(git_futils_mkdir(git_buf_cstr(&path), NULL, 0755, 0));
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_git_pass(git_submodule_reload(sm));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0);
+
+ /* update sm_changed_head in index */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
+ cl_git_pass(git_submodule_add_to_index(sm, true));
+ /* reload is not needed because add_to_index updates the submodule data */
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0);
+
+ git_buf_free(&path);
+}
+
+void test_submodule_status__ignore_all(void)
+{
+ unsigned int status;
+ git_submodule *sm;
+ git_buf path = GIT_BUF_INIT;
+ git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_ALL;
+
+ cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged"));
+ cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES));
+
+ cl_git_pass(git_submodule_foreach(g_repo, set_sm_ignore, &ign));
+
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_submodule_lookup(&sm, g_repo, "just_a_dir"));
+ cl_assert_equal_i(GIT_EEXISTS,
+ git_submodule_lookup(&sm, g_repo, "not-submodule"));
+ cl_assert_equal_i(GIT_EEXISTS,
+ git_submodule_lookup(&sm, g_repo, "not"));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_index"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_file"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_untracked_file"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ /* removed sm_unchanged for deleted workdir */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ /* now mkdir sm_unchanged to test uninitialized */
+ cl_git_pass(git_futils_mkdir(git_buf_cstr(&path), NULL, 0755, 0));
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_git_pass(git_submodule_reload(sm));
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ /* update sm_changed_head in index */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
+ cl_git_pass(git_submodule_add_to_index(sm, true));
+ /* reload is not needed because add_to_index updates the submodule data */
+ cl_git_pass(git_submodule_status(&status, sm));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ git_buf_free(&path);
+}
+
+typedef struct {
+ size_t counter;
+ const char **paths;
+ int *statuses;
+} submodule_expectations;
+
+static int confirm_submodule_status(
+ const char *path, unsigned int status_flags, void *payload)
+{
+ submodule_expectations *exp = payload;
+
+ while (git__suffixcmp(exp->paths[exp->counter], "/") == 0)
+ exp->counter++;
+
+ cl_assert_equal_i(exp->statuses[exp->counter], (int)status_flags);
+ cl_assert_equal_s(exp->paths[exp->counter++], path);
+
+ GIT_UNUSED(status_flags);
+
+ return 0;
+}
+
+void test_submodule_status__iterator(void)
+{
+ git_iterator *iter;
+ const git_index_entry *entry;
+ size_t i;
+ static const char *expected[] = {
+ ".gitmodules",
+ "just_a_dir/",
+ "just_a_dir/contents",
+ "just_a_file",
+ "not",
+ "not-submodule",
+ "README.txt",
+ "sm_added_and_uncommited",
+ "sm_changed_file",
+ "sm_changed_head",
+ "sm_changed_index",
+ "sm_changed_untracked_file",
+ "sm_missing_commits",
+ "sm_unchanged",
+ NULL
+ };
+ static int expected_flags[] = {
+ GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_MODIFIED, /* ".gitmodules" */
+ 0, /* "just_a_dir/" will be skipped */
+ GIT_STATUS_CURRENT, /* "just_a_dir/contents" */
+ GIT_STATUS_CURRENT, /* "just_a_file" */
+ GIT_STATUS_IGNORED, /* "not" (contains .git) */
+ GIT_STATUS_IGNORED, /* "not-submodule" (contains .git) */
+ GIT_STATUS_CURRENT, /* "README.txt */
+ GIT_STATUS_INDEX_NEW, /* "sm_added_and_uncommited" */
+ GIT_STATUS_WT_MODIFIED, /* "sm_changed_file" */
+ GIT_STATUS_WT_MODIFIED, /* "sm_changed_head" */
+ GIT_STATUS_WT_MODIFIED, /* "sm_changed_index" */
+ GIT_STATUS_WT_MODIFIED, /* "sm_changed_untracked_file" */
+ GIT_STATUS_WT_MODIFIED, /* "sm_missing_commits" */
+ GIT_STATUS_CURRENT, /* "sm_unchanged" */
+ 0
+ };
+ submodule_expectations exp = { 0, expected, expected_flags };
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+
+ cl_git_pass(git_iterator_for_workdir(&iter, g_repo,
+ GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES, NULL, NULL));
+
+ for (i = 0; !git_iterator_advance(&entry, iter); ++i)
+ cl_assert_equal_s(expected[i], entry->path);
+
+ git_iterator_free(iter);
+
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_INCLUDE_UNMODIFIED |
+ GIT_STATUS_OPT_INCLUDE_IGNORED |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS |
+ GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY;
+
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, confirm_submodule_status, &exp));
+}
+
+void test_submodule_status__untracked_dirs_containing_ignored_files(void)
+{
+ git_buf path = GIT_BUF_INIT;
+ unsigned int status, expected;
+ git_submodule *sm;
+
+ cl_git_pass(git_buf_joinpath(&path, git_repository_path(g_repo), "modules/sm_unchanged/info/exclude"));
+ cl_git_append2file(git_buf_cstr(&path), "\n*.ignored\n");
+
+ cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged/directory"));
+ cl_git_pass(git_futils_mkdir(git_buf_cstr(&path), NULL, 0755, 0));
+ cl_git_pass(git_buf_joinpath(&path, git_buf_cstr(&path), "i_am.ignored"));
+ cl_git_mkfile(git_buf_cstr(&path), "ignored this file, please\n");
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_git_pass(git_submodule_status(&status, sm));
+
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ expected = GIT_SUBMODULE_STATUS_IN_HEAD |
+ GIT_SUBMODULE_STATUS_IN_INDEX |
+ GIT_SUBMODULE_STATUS_IN_CONFIG |
+ GIT_SUBMODULE_STATUS_IN_WD;
+
+ cl_assert(status == expected);
+
+ git_buf_free(&path);
+}
diff --git a/tests/submodule/submodule_helpers.c b/tests/submodule/submodule_helpers.c
new file mode 100644
index 000000000..d5750675c
--- /dev/null
+++ b/tests/submodule/submodule_helpers.c
@@ -0,0 +1,127 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "path.h"
+#include "util.h"
+#include "posix.h"
+#include "submodule_helpers.h"
+#include "git2/sys/repository.h"
+
+/* rewrite gitmodules -> .gitmodules
+ * rewrite the empty or relative urls inside each module
+ * rename the .gitted directory inside any submodule to .git
+ */
+void rewrite_gitmodules(const char *workdir)
+{
+ git_buf in_f = GIT_BUF_INIT, out_f = GIT_BUF_INIT, path = GIT_BUF_INIT;
+ FILE *in, *out;
+ char line[256];
+
+ cl_git_pass(git_buf_joinpath(&in_f, workdir, "gitmodules"));
+ cl_git_pass(git_buf_joinpath(&out_f, workdir, ".gitmodules"));
+
+ cl_assert((in = fopen(in_f.ptr, "r")) != NULL);
+ cl_assert((out = fopen(out_f.ptr, "w")) != NULL);
+
+ while (fgets(line, sizeof(line), in) != NULL) {
+ char *scan = line;
+
+ while (*scan == ' ' || *scan == '\t') scan++;
+
+ /* rename .gitted -> .git in submodule directories */
+ if (git__prefixcmp(scan, "path =") == 0) {
+ scan += strlen("path =");
+ while (*scan == ' ') scan++;
+
+ git_buf_joinpath(&path, workdir, scan);
+ git_buf_rtrim(&path);
+ git_buf_joinpath(&path, path.ptr, ".gitted");
+
+ if (!git_buf_oom(&path) && p_access(path.ptr, F_OK) == 0) {
+ git_buf_joinpath(&out_f, workdir, scan);
+ git_buf_rtrim(&out_f);
+ git_buf_joinpath(&out_f, out_f.ptr, ".git");
+
+ if (!git_buf_oom(&out_f))
+ p_rename(path.ptr, out_f.ptr);
+ }
+ }
+
+ /* copy non-"url =" lines verbatim */
+ if (git__prefixcmp(scan, "url =") != 0) {
+ fputs(line, out);
+ continue;
+ }
+
+ /* convert relative URLs in "url =" lines */
+ scan += strlen("url =");
+ while (*scan == ' ') scan++;
+
+ if (*scan == '.') {
+ git_buf_joinpath(&path, workdir, scan);
+ git_buf_rtrim(&path);
+ } else if (!*scan || *scan == '\n') {
+ git_buf_joinpath(&path, workdir, "../testrepo.git");
+ } else {
+ fputs(line, out);
+ continue;
+ }
+
+ git_path_prettify(&path, path.ptr, NULL);
+ git_buf_putc(&path, '\n');
+ cl_assert(!git_buf_oom(&path));
+
+ fwrite(line, scan - line, sizeof(char), out);
+ fputs(path.ptr, out);
+ }
+
+ fclose(in);
+ fclose(out);
+
+ cl_must_pass(p_unlink(in_f.ptr));
+
+ git_buf_free(&in_f);
+ git_buf_free(&out_f);
+ git_buf_free(&path);
+}
+
+static void cleanup_fixture_submodules(void *payload)
+{
+ cl_git_sandbox_cleanup(); /* either "submodules" or "submod2" */
+
+ if (payload)
+ cl_fixture_cleanup(payload);
+}
+
+git_repository *setup_fixture_submodules(void)
+{
+ git_repository *repo = cl_git_sandbox_init("submodules");
+
+ cl_fixture_sandbox("testrepo.git");
+
+ rewrite_gitmodules(git_repository_workdir(repo));
+ p_rename("submodules/testrepo/.gitted", "submodules/testrepo/.git");
+
+ cl_set_cleanup(cleanup_fixture_submodules, "testrepo.git");
+
+ cl_git_pass(git_repository_reinit_filesystem(repo, 1));
+
+ return repo;
+}
+
+git_repository *setup_fixture_submod2(void)
+{
+ git_repository *repo = cl_git_sandbox_init("submod2");
+
+ cl_fixture_sandbox("submod2_target");
+ p_rename("submod2_target/.gitted", "submod2_target/.git");
+
+ rewrite_gitmodules(git_repository_workdir(repo));
+ p_rename("submod2/not-submodule/.gitted", "submod2/not-submodule/.git");
+ p_rename("submod2/not/.gitted", "submod2/not/.git");
+
+ cl_set_cleanup(cleanup_fixture_submodules, "submod2_target");
+
+ cl_git_pass(git_repository_reinit_filesystem(repo, 1));
+
+ return repo;
+}
diff --git a/tests/submodule/submodule_helpers.h b/tests/submodule/submodule_helpers.h
new file mode 100644
index 000000000..610c40720
--- /dev/null
+++ b/tests/submodule/submodule_helpers.h
@@ -0,0 +1,5 @@
+extern void rewrite_gitmodules(const char *workdir);
+
+/* these will automatically set a cleanup callback */
+extern git_repository *setup_fixture_submodules(void);
+extern git_repository *setup_fixture_submod2(void);
diff --git a/tests/threads/basic.c b/tests/threads/basic.c
new file mode 100644
index 000000000..a329ee7f9
--- /dev/null
+++ b/tests/threads/basic.c
@@ -0,0 +1,36 @@
+#include "clar_libgit2.h"
+
+#include "cache.h"
+
+
+static git_repository *g_repo;
+
+void test_threads_basic__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_threads_basic__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+
+void test_threads_basic__cache(void)
+{
+ // run several threads polling the cache at the same time
+ cl_assert(1 == 1);
+}
+
+void test_threads_basic__multiple_init(void)
+{
+ git_repository *nested_repo;
+
+ git_threads_init();
+ cl_git_pass(git_repository_open(&nested_repo, cl_fixture("testrepo.git")));
+ git_repository_free(nested_repo);
+
+ git_threads_shutdown();
+ cl_git_pass(git_repository_open(&nested_repo, cl_fixture("testrepo.git")));
+ git_repository_free(nested_repo);
+}
diff --git a/tests/threads/refdb.c b/tests/threads/refdb.c
new file mode 100644
index 000000000..3c651e341
--- /dev/null
+++ b/tests/threads/refdb.c
@@ -0,0 +1,213 @@
+#include "clar_libgit2.h"
+#include "git2/refdb.h"
+#include "refdb.h"
+
+static git_repository *g_repo;
+static int g_expected = 0;
+
+void test_threads_refdb__initialize(void)
+{
+ g_repo = NULL;
+}
+
+void test_threads_refdb__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ g_repo = NULL;
+}
+
+#define REPEAT 20
+#define THREADS 20
+
+static void *iterate_refs(void *arg)
+{
+ git_reference_iterator *i;
+ git_reference *ref;
+ int count = 0;
+
+ cl_git_pass(git_reference_iterator_new(&i, g_repo));
+
+ for (count = 0; !git_reference_next(&ref, i); ++count) {
+ cl_assert(ref != NULL);
+ git_reference_free(ref);
+ }
+
+ if (g_expected > 0)
+ cl_assert_equal_i(g_expected, count);
+
+ git_reference_iterator_free(i);
+
+ return arg;
+}
+
+void test_threads_refdb__iterator(void)
+{
+ int r, t;
+ git_thread th[THREADS];
+ int id[THREADS];
+ git_oid head;
+ git_reference *ref;
+ char name[128];
+ git_refdb *refdb;
+
+ g_repo = cl_git_sandbox_init("testrepo2");
+
+ cl_git_pass(git_reference_name_to_id(&head, g_repo, "HEAD"));
+
+ /* make a bunch of references */
+
+ for (r = 0; r < 200; ++r) {
+ snprintf(name, sizeof(name), "refs/heads/direct-%03d", r);
+ cl_git_pass(git_reference_create(&ref, g_repo, name, &head, 0));
+ git_reference_free(ref);
+ }
+
+ cl_git_pass(git_repository_refdb(&refdb, g_repo));
+ cl_git_pass(git_refdb_compress(refdb));
+ git_refdb_free(refdb);
+
+ g_expected = 206;
+
+ for (r = 0; r < REPEAT; ++r) {
+ g_repo = cl_git_sandbox_reopen(); /* reopen to flush caches */
+
+ for (t = 0; t < THREADS; ++t) {
+ id[t] = t;
+#ifdef GIT_THREADS
+ cl_git_pass(git_thread_create(&th[t], NULL, iterate_refs, &id[t]));
+#else
+ th[t] = t;
+ iterate_refs(&id[t]);
+#endif
+ }
+
+#ifdef GIT_THREADS
+ for (t = 0; t < THREADS; ++t) {
+ cl_git_pass(git_thread_join(th[t], NULL));
+ }
+#endif
+
+ memset(th, 0, sizeof(th));
+ }
+}
+
+static void *create_refs(void *arg)
+{
+ int *id = arg, i;
+ git_oid head;
+ char name[128];
+ git_reference *ref[10];
+
+ cl_git_pass(git_reference_name_to_id(&head, g_repo, "HEAD"));
+
+ for (i = 0; i < 10; ++i) {
+ snprintf(name, sizeof(name), "refs/heads/thread-%03d-%02d", *id, i);
+ cl_git_pass(git_reference_create(&ref[i], g_repo, name, &head, 0));
+
+ if (i == 5) {
+ git_refdb *refdb;
+ cl_git_pass(git_repository_refdb(&refdb, g_repo));
+ cl_git_pass(git_refdb_compress(refdb));
+ git_refdb_free(refdb);
+ }
+ }
+
+ for (i = 0; i < 10; ++i)
+ git_reference_free(ref[i]);
+
+ return arg;
+}
+
+static void *delete_refs(void *arg)
+{
+ int *id = arg, i;
+ git_reference *ref;
+ char name[128];
+
+ for (i = 0; i < 10; ++i) {
+ snprintf(
+ name, sizeof(name), "refs/heads/thread-%03d-%02d", (*id) & ~0x3, i);
+
+ if (!git_reference_lookup(&ref, g_repo, name)) {
+ cl_git_pass(git_reference_delete(ref));
+ git_reference_free(ref);
+ }
+
+ if (i == 5) {
+ git_refdb *refdb;
+ cl_git_pass(git_repository_refdb(&refdb, g_repo));
+ cl_git_pass(git_refdb_compress(refdb));
+ git_refdb_free(refdb);
+ }
+ }
+
+ return arg;
+}
+
+void test_threads_refdb__edit_while_iterate(void)
+{
+ int r, t;
+ int id[THREADS];
+ git_oid head;
+ git_reference *ref;
+ char name[128];
+ git_refdb *refdb;
+
+#ifdef GIT_THREADS
+ git_thread th[THREADS];
+#endif
+
+ g_repo = cl_git_sandbox_init("testrepo2");
+
+ cl_git_pass(git_reference_name_to_id(&head, g_repo, "HEAD"));
+
+ /* make a bunch of references */
+
+ for (r = 0; r < 50; ++r) {
+ snprintf(name, sizeof(name), "refs/heads/starter-%03d", r);
+ cl_git_pass(git_reference_create(&ref, g_repo, name, &head, 0));
+ git_reference_free(ref);
+ }
+
+ cl_git_pass(git_repository_refdb(&refdb, g_repo));
+ cl_git_pass(git_refdb_compress(refdb));
+ git_refdb_free(refdb);
+
+ g_expected = -1;
+
+ g_repo = cl_git_sandbox_reopen(); /* reopen to flush caches */
+
+ for (t = 0; t < THREADS; ++t) {
+ void *(*fn)(void *arg);
+
+ switch (t & 0x3) {
+ case 0: fn = create_refs; break;
+ case 1: fn = delete_refs; break;
+ default: fn = iterate_refs; break;
+ }
+
+ id[t] = t;
+#ifdef GIT_THREADS
+ cl_git_pass(git_thread_create(&th[t], NULL, fn, &id[t]));
+#else
+ fn(&id[t]);
+#endif
+ }
+
+#ifdef GIT_THREADS
+ for (t = 0; t < THREADS; ++t) {
+ cl_git_pass(git_thread_join(th[t], NULL));
+ }
+
+ memset(th, 0, sizeof(th));
+
+ for (t = 0; t < THREADS; ++t) {
+ id[t] = t;
+ cl_git_pass(git_thread_create(&th[t], NULL, iterate_refs, &id[t]));
+ }
+
+ for (t = 0; t < THREADS; ++t) {
+ cl_git_pass(git_thread_join(th[t], NULL));
+ }
+#endif
+}
diff --git a/tests-clar/trace/trace.c b/tests/trace/trace.c
index 87b325378..87b325378 100644
--- a/tests-clar/trace/trace.c
+++ b/tests/trace/trace.c
diff --git a/tests/valgrind-supp-mac.txt b/tests/valgrind-supp-mac.txt
new file mode 100644
index 000000000..99833d091
--- /dev/null
+++ b/tests/valgrind-supp-mac.txt
@@ -0,0 +1,184 @@
+{
+ libgit2-giterr-set-buffer
+ Memcheck:Leak
+ ...
+ fun:git__realloc
+ fun:git_buf_try_grow
+ fun:git_buf_grow
+ fun:git_buf_vprintf
+ fun:giterr_set
+}
+{
+ mac-setenv-leak-1
+ Memcheck:Leak
+ fun:malloc_zone_malloc
+ fun:__setenv
+ fun:setenv
+}
+{
+ mac-setenv-leak-2
+ Memcheck:Leak
+ fun:malloc_zone_malloc
+ fun:malloc_set_zone_name
+ ...
+ fun:init__zone0
+ fun:setenv
+}
+{
+ mac-dyld-initializer-leak
+ Memcheck:Leak
+ fun:malloc
+ ...
+ fun:dyld_register_image_state_change_handler
+ fun:_dyld_initializer
+}
+{
+ mac-tz-leak-1
+ Memcheck:Leak
+ ...
+ fun:token_table_add
+ fun:notify_register_check
+ fun:notify_register_tz
+}
+{
+ mac-tz-leak-2
+ Memcheck:Leak
+ fun:malloc
+ fun:tzload
+}
+{
+ mac-tz-leak-3
+ Memcheck:Leak
+ fun:malloc
+ fun:tzsetwall_basic
+}
+{
+ mac-tz-leak-4
+ Memcheck:Leak
+ fun:malloc
+ fun:gmtsub
+}
+{
+ mac-system-init-leak-1
+ Memcheck:Leak
+ ...
+ fun:_libxpc_initializer
+ fun:libSystem_initializer
+}
+{
+ mac-system-init-leak-2
+ Memcheck:Leak
+ ...
+ fun:__keymgr_initializer
+ fun:libSystem_initializer
+}
+{
+ mac-puts-leak
+ Memcheck:Leak
+ fun:malloc
+ fun:__smakebuf
+ ...
+ fun:puts
+}
+{
+ mac-ssl-uninitialized-1
+ Memcheck:Cond
+ obj:/usr/lib/libcrypto.0.9.8.dylib
+ ...
+ fun:ssl23_connect
+}
+{
+ mac-ssl-uninitialized-2
+ Memcheck:Cond
+ ...
+ obj:/usr/lib/libssl.0.9.8.dylib
+ ...
+ fun:ssl23_connect
+}
+{
+ mac-ssl-uninitialized-3
+ Memcheck:Value8
+ obj:/usr/lib/libcrypto.0.9.8.dylib
+ ...
+ fun:ssl23_connect
+}
+{
+ mac-ssl-uninitialized-4
+ Memcheck:Param
+ ...
+ obj:/usr/lib/libcrypto.0.9.8.dylib
+ ...
+ fun:ssl23_connect
+}
+{
+ mac-ssl-leak-1
+ Memcheck:Leak
+ ...
+ fun:ERR_load_strings
+}
+{
+ mac-ssl-leak-2
+ Memcheck:Leak
+ ...
+ fun:SSL_library_init
+}
+{
+ mac-ssl-leak-3
+ Memcheck:Leak
+ ...
+ fun:si_module_with_name
+ fun:getaddrinfo
+}
+{
+ mac-ssl-leak-4
+ Memcheck:Leak
+ fun:malloc
+ fun:CRYPTO_malloc
+ ...
+ fun:ssl3_get_server_certificate
+}
+{
+ mac-ssl-leak-5
+ Memcheck:Leak
+ fun:malloc
+ fun:CRYPTO_malloc
+ ...
+ fun:ERR_put_error
+}
+{
+ clar-printf-buf
+ Memcheck:Leak
+ fun:malloc
+ fun:__smakebuf
+ ...
+ fun:printf
+ fun:clar_print_init
+}
+{
+ molo-1
+ Memcheck:Leak
+ fun:malloc_zone_malloc
+ ...
+ fun:_objc_init
+}
+{
+ molo-2
+ Memcheck:Leak
+ fun:malloc_zone_calloc
+ ...
+ fun:_objc_init
+}
+{
+ molo-3
+ Memcheck:Leak
+ fun:malloc
+ ...
+ fun:_objc_init
+}
+{
+ molo-4
+ Memcheck:Leak
+ fun:malloc
+ ...
+ fun:dyld_register_image_state_change_handler
+}