summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/README.md22
-rw-r--r--tests/attr/attr_expect.h43
-rw-r--r--tests/attr/file.c224
-rw-r--r--tests/attr/flags.c108
-rw-r--r--tests/attr/ignore.c102
-rw-r--r--tests/attr/lookup.c262
-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.c29
-rw-r--r--tests/buf/splice.c93
-rw-r--r--tests/checkout/binaryunicode.c58
-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.c240
-rw-r--r--tests/clar.c511
-rw-r--r--tests/clar.h95
-rw-r--r--tests/clar/fixtures.h38
-rw-r--r--tests/clar/fs.h325
-rw-r--r--tests/clar/print.h60
-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.c46
-rw-r--r--tests/commit/parent.c60
-rw-r--r--tests/commit/parse.c413
-rw-r--r--tests/commit/signature.c81
-rw-r--r--tests/commit/write.c147
-rw-r--r--tests/config/add.c37
-rw-r--r--tests/config/backend.c24
-rw-r--r--tests/config/config_helpers.c37
-rw-r--r--tests/config/config_helpers.h9
-rw-r--r--tests/config/configlevel.c71
-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.c32
-rw-r--r--tests/config/read.c569
-rw-r--r--tests/config/refresh.c67
-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.c126
-rw-r--r--tests/core/dirent.c236
-rw-r--r--tests/core/env.c303
-rw-r--r--tests/core/errors.c87
-rw-r--r--tests/core/filebuf.c126
-rw-r--r--tests/core/hex.c22
-rw-r--r--tests/core/iconv.c68
-rw-r--r--tests/core/mkdir.c188
-rw-r--r--tests/core/oid.c70
-rw-r--r--tests/core/oidmap.c110
-rw-r--r--tests/core/opts.c19
-rw-r--r--tests/core/path.c583
-rw-r--r--tests/core/pool.c145
-rw-r--r--tests/core/posix.c99
-rw-r--r--tests/core/rmdir.c98
-rw-r--r--tests/core/sortedcache.c363
-rw-r--r--tests/core/stat.c97
-rw-r--r--tests/core/string.c41
-rw-r--r--tests/core/strmap.c102
-rw-r--r--tests/core/strtol.c37
-rw-r--r--tests/core/vector.c275
-rw-r--r--tests/date/date.c15
-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.c937
-rw-r--r--tests/diff/notify.c228
-rw-r--r--tests/diff/patch.c571
-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.h31
-rw-r--r--tests/fetchhead/nonetwork.c309
-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.py244
-rw-r--r--tests/index/addall.c258
-rw-r--r--tests/index/conflicts.c287
-rw-r--r--tests/index/filemodes.c154
-rw-r--r--tests/index/inmemory.c22
-rw-r--r--tests/index/names.c148
-rw-r--r--tests/index/read_tree.c46
-rw-r--r--tests/index/rename.c50
-rw-r--r--tests/index/reuc.c372
-rw-r--r--tests/index/stage.c62
-rw-r--r--tests/index/tests.c537
-rw-r--r--tests/main.c20
-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.c59
-rw-r--r--tests/merge/trees/renames.c252
-rw-r--r--tests/merge/trees/treediff.c542
-rw-r--r--tests/merge/trees/trivial.c397
-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.c50
-rw-r--r--tests/network/fetchlocal.c88
-rw-r--r--tests/network/refspecs.c87
-rw-r--r--tests/network/remote/createthenload.c37
-rw-r--r--tests/network/remote/isvalidname.c17
-rw-r--r--tests/network/remote/local.c234
-rw-r--r--tests/network/remote/remotes.c510
-rw-r--r--tests/network/remote/rename.c174
-rw-r--r--tests/network/urlparse.c193
-rw-r--r--tests/notes/notes.c387
-rw-r--r--tests/notes/notesref.c64
-rw-r--r--tests/object/blob/filter.c143
-rw-r--r--tests/object/blob/fromchunks.c119
-rw-r--r--tests/object/blob/write.c69
-rw-r--r--tests/object/cache.c287
-rw-r--r--tests/object/commit/commitstagedfile.c132
-rw-r--r--tests/object/lookup.c65
-rw-r--r--tests/object/lookupbypath.c83
-rw-r--r--tests/object/message.c236
-rw-r--r--tests/object/peel.c105
-rw-r--r--tests/object/raw/chars.c41
-rw-r--r--tests/object/raw/compare.c124
-rw-r--r--tests/object/raw/convert.c112
-rw-r--r--tests/object/raw/data.h323
-rw-r--r--tests/object/raw/fromstr.c30
-rw-r--r--tests/object/raw/hash.c166
-rw-r--r--tests/object/raw/short.c137
-rw-r--r--tests/object/raw/size.c13
-rw-r--r--tests/object/raw/type2string.c54
-rw-r--r--tests/object/raw/write.c462
-rw-r--r--tests/object/tag/list.c115
-rw-r--r--tests/object/tag/peel.c61
-rw-r--r--tests/object/tag/read.c142
-rw-r--r--tests/object/tag/write.c260
-rw-r--r--tests/object/tree/attributes.c115
-rw-r--r--tests/object/tree/duplicateentries.c157
-rw-r--r--tests/object/tree/frompath.c68
-rw-r--r--tests/object/tree/read.c75
-rw-r--r--tests/object/tree/walk.c177
-rw-r--r--tests/object/tree/write.c262
-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.h522
-rw-r--r--tests/odb/mixed.c93
-rw-r--r--tests/odb/pack_data.h151
-rw-r--r--tests/odb/pack_data_one.h19
-rw-r--r--tests/odb/packed.c79
-rw-r--r--tests/odb/packed_one.c60
-rw-r--r--tests/odb/sorting.c69
-rw-r--r--tests/odb/streamwrite.c56
-rw-r--r--tests/online/clone.c256
-rw-r--r--tests/online/fetch.c171
-rw-r--r--tests/online/fetchhead.c91
-rw-r--r--tests/online/push.c770
-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.c76
-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.c45
-rw-r--r--tests/refs/branches/move.c146
-rw-r--r--tests/refs/branches/name.c45
-rw-r--r--tests/refs/branches/remote.c86
-rw-r--r--tests/refs/branches/upstream.c143
-rw-r--r--tests/refs/branches/upstreamname.c42
-rw-r--r--tests/refs/crashes.c20
-rw-r--r--tests/refs/create.c168
-rw-r--r--tests/refs/delete.c93
-rw-r--r--tests/refs/foreachglob.c95
-rw-r--r--tests/refs/isvalidname.c31
-rw-r--r--tests/refs/iterator.c97
-rw-r--r--tests/refs/list.c57
-rw-r--r--tests/refs/listall.c47
-rw-r--r--tests/refs/lookup.c60
-rw-r--r--tests/refs/normalize.c403
-rw-r--r--tests/refs/overwrite.c136
-rw-r--r--tests/refs/pack.c105
-rw-r--r--tests/refs/peel.c119
-rw-r--r--tests/refs/read.c284
-rw-r--r--tests/refs/ref_helpers.c25
-rw-r--r--tests/refs/ref_helpers.h1
-rw-r--r--tests/refs/reflog/drop.c115
-rw-r--r--tests/refs/reflog/reflog.c209
-rw-r--r--tests/refs/rename.c367
-rw-r--r--tests/refs/revparse.c813
-rw-r--r--tests/refs/setter.c99
-rw-r--r--tests/refs/shorthand.c27
-rw-r--r--tests/refs/unicode.c56
-rw-r--r--tests/refs/update.c26
-rw-r--r--tests/repo/config.c208
-rw-r--r--tests/repo/discover.c142
-rw-r--r--tests/repo/getters.c40
-rw-r--r--tests/repo/hashfile.c85
-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.c52
-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.c107
-rw-r--r--tests/repo/shallow.c33
-rw-r--r--tests/repo/state.c96
-rw-r--r--tests/reset/default.c180
-rw-r--r--tests/reset/hard.c200
-rw-r--r--tests/reset/mixed.c49
-rw-r--r--tests/reset/reset_helpers.c10
-rw-r--r--tests/reset/reset_helpers.h6
-rw-r--r--tests/reset/soft.c157
-rw-r--r--tests/resources/.gitattributes1
-rw-r--r--tests/resources/.gitignore1
-rw-r--r--tests/resources/attr/.gitted/HEAD1
-rw-r--r--tests/resources/attr/.gitted/config6
-rw-r--r--tests/resources/attr/.gitted/description1
-rw-r--r--tests/resources/attr/.gitted/indexbin0 -> 1856 bytes
-rw-r--r--tests/resources/attr/.gitted/info/attributes4
-rw-r--r--tests/resources/attr/.gitted/info/exclude6
-rw-r--r--tests/resources/attr/.gitted/logs/HEAD9
-rw-r--r--tests/resources/attr/.gitted/logs/refs/heads/master9
-rw-r--r--tests/resources/attr/.gitted/objects/10/8bb4e7fd7b16490dc33ff7d972151e73d7166ebin0 -> 130 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/16/983da6643656bb44c43965ecb6855c6d574512bin0 -> 446 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/21/7878ab49e1314388ea2e32dc6fdb58a1b969e04
-rw-r--r--tests/resources/attr/.gitted/objects/24/fa9a9fc4e202313e24b648087495441dab432bbin0 -> 180 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/29/29de282ce999e95183aedac6451d3384559c4bbin0 -> 58 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/2b/40c5aca159b04ea8d20ffe36cdf8b09369b14a1
-rw-r--r--tests/resources/attr/.gitted/objects/2c/66e14f77196ea763fb1e41612c1aa2bc2d8ed2bin0 -> 316 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/2d/e7dfe3588f3c7e9ad59e7d50ba90e3329df9d9bin0 -> 124 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/37/0fe9ec224ce33e71f9e5ec2bd1142ce9937a6abin0 -> 177 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/3a/6df026462ebafe455af9867d27eda20a9e0974bin0 -> 84 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/3b/74db7ab381105dc0d28f8295a77f6a82989292bin0 -> 276 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/3e/42ffc54a663f9401cc25843d6c0e71a33e4249bin0 -> 596 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/45/141a79a77842c59a63229403220a4e4be74e3dbin0 -> 36 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/45/5a314fa848d52ae1f11d254da4f60858fc97f4bin0 -> 446 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057bin0 -> 18 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/4d/713dc48e6b1bd75b0d61ad078ba9ca3a56745d2
-rw-r--r--tests/resources/attr/.gitted/objects/4e/49ba8c5b6c32ff28cd9dcb60be34df50fcc485bin0 -> 81 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/55/6f8c827b8e4a02ad5cab77dca2bcb3e226b0b3bin0 -> 24 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/58/19a185d77b03325aaf87cafc771db36f6ddca7bin0 -> 19 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/60/5812ab7fe421fdd325a935d35cb06a9234a7d72
-rw-r--r--tests/resources/attr/.gitted/objects/6b/ab5c79cd5140d0f800917f550eb2a3dc32b0da3
-rw-r--r--tests/resources/attr/.gitted/objects/6d/968d62c89c7d9ea23a4c9a7b665d017c3d8ffdbin0 -> 422 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/71/7fc31f6b84f9d6fc3a4edbca259d7fc92beee2bin0 -> 422 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/8d/0b9df9bd30be7910ddda60548d485bc302b9111
-rw-r--r--tests/resources/attr/.gitted/objects/93/61f40bb97239cf55811892e14de2e344168ba1bin0 -> 45 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/94/da4faa0a6bfb8ee6ccf7153801a69202b31857bin0 -> 124 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/96/089fd31ce1d3ee2afb0ba09ba063066932f027bin0 -> 422 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9bin0 -> 95 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/9e/5bdc47d6a80f2be0ea3049ad74231b94609242bin0 -> 20 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9bin0 -> 151 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/a0/f7217ae99f5ac3e88534f5cea267febc5fa85b1
-rw-r--r--tests/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320bin0 -> 351 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/a5/d76cad53f66f1312bd995909a5bab3c08207704
-rw-r--r--tests/resources/attr/.gitted/objects/a9/7cc019851d401a4f1d091cb91a15890a0dd1ba2
-rw-r--r--tests/resources/attr/.gitted/objects/b4/35cd5689a0fb54afbeda4ac20368aa480e8f04bin0 -> 40 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/c0/091889c0c77142b87a1fa5123a6398a61d33e7bin0 -> 290 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/c4/85abe35abd4aa6fd83b076a78bbea9e2e7e06cbin0 -> 129 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/c7/aadd770d5907a8475c29e9ee21a27b88bf675dbin0 -> 60 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/c9/6bbb2c2557a8325ae1559e3ba79cdcecb230762
-rw-r--r--tests/resources/attr/.gitted/objects/ce/39a97a7fb1fa90bcf5e711249c1e507476ae0ebin0 -> 446 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/d5/7da33c16b14326ecb05d19bbea908f5e4c47d9bin0 -> 379 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2bin0 -> 18 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/dc/cada462d3df8ac6de596fb8c896aba9344f941bin0 -> 35 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/de/863bff4976c9ed7e17a4da0fd524908dc84049bin0 -> 4115 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/e5/63cf4758f0d646f1b14b76016aa17fa9e549a4bin0 -> 39 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/ec/b97df2a174987475ac816e3847fc8e9f6c596bbin0 -> 171 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/ed/f3dcee4003d71f139777898882ccd097e34c53bin0 -> 6289 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/f2/c6d717cf4a5a3e6b02684155ab07b766982165bin0 -> 44 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/f5/b0af1fb4f5c0cd7aad880711d368a07333c3072
-rw-r--r--tests/resources/attr/.gitted/objects/fb/5067b1aef3ac1ada4b379dbcb7d17255df7d78bin0 -> 28 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/fe/773770c5a6cc7185580c9204b1ff18a33ff3fc1
-rw-r--r--tests/resources/attr/.gitted/objects/ff/69f8639ce2e6010b3f33a74160aad98b48da2bbin0 -> 18 bytes
-rw-r--r--tests/resources/attr/.gitted/refs/heads/master1
-rw-r--r--tests/resources/attr/attr01
-rw-r--r--tests/resources/attr/attr129
-rw-r--r--tests/resources/attr/attr221
-rw-r--r--tests/resources/attr/attr34
-rw-r--r--tests/resources/attr/binfile1
-rw-r--r--tests/resources/attr/dir/file0
-rw-r--r--tests/resources/attr/file1
-rw-r--r--tests/resources/attr/gitattributes29
-rw-r--r--tests/resources/attr/gitignore2
-rw-r--r--tests/resources/attr/ign1
-rw-r--r--tests/resources/attr/macro_bad1
-rw-r--r--tests/resources/attr/macro_test1
-rw-r--r--tests/resources/attr/root_test11
-rw-r--r--tests/resources/attr/root_test26
-rw-r--r--tests/resources/attr/root_test319
-rw-r--r--tests/resources/attr/root_test4.txt14
-rw-r--r--tests/resources/attr/sub/.gitattributes7
-rw-r--r--tests/resources/attr/sub/abc37
-rw-r--r--tests/resources/attr/sub/dir/file0
-rw-r--r--tests/resources/attr/sub/file1
-rw-r--r--tests/resources/attr/sub/ign/file1
-rw-r--r--tests/resources/attr/sub/ign/sub/file1
-rw-r--r--tests/resources/attr/sub/sub/.gitattributes3
-rw-r--r--tests/resources/attr/sub/sub/dir0
-rw-r--r--tests/resources/attr/sub/sub/file1
-rw-r--r--tests/resources/attr/sub/sub/subsub.txt1
-rw-r--r--tests/resources/attr/sub/subdir_test12
-rw-r--r--tests/resources/attr/sub/subdir_test2.txt1
-rw-r--r--tests/resources/attr_index/.gitted/HEAD1
-rw-r--r--tests/resources/attr_index/.gitted/config6
-rw-r--r--tests/resources/attr_index/.gitted/description1
-rw-r--r--tests/resources/attr_index/.gitted/indexbin0 -> 520 bytes
-rw-r--r--tests/resources/attr_index/.gitted/info/exclude6
-rw-r--r--tests/resources/attr_index/.gitted/info/refs1
-rw-r--r--tests/resources/attr_index/.gitted/logs/HEAD4
-rw-r--r--tests/resources/attr_index/.gitted/logs/refs/heads/master4
-rw-r--r--tests/resources/attr_index/.gitted/objects/38/12cfef36615db1788d4e63f90028007e17a3483
-rw-r--r--tests/resources/attr_index/.gitted/objects/59/d942b8be2784bc96db9b22202c10815c9a077b1
-rw-r--r--tests/resources/attr_index/.gitted/objects/cd/f17ea3fe625ef812f4dce7f423f4f299287505bin0 -> 61 bytes
-rw-r--r--tests/resources/attr_index/.gitted/objects/f7/2502ddd01412bb20796ff812af56fd53b82b52bin0 -> 149 bytes
-rw-r--r--tests/resources/attr_index/.gitted/objects/info/packs2
-rw-r--r--tests/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.idxbin0 -> 1492 bytes
-rw-r--r--tests/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.packbin0 -> 1106 bytes
-rw-r--r--tests/resources/attr_index/.gitted/packed-refs2
-rw-r--r--tests/resources/attr_index/.gitted/refs/heads/master1
-rw-r--r--tests/resources/attr_index/README.md1
-rw-r--r--tests/resources/attr_index/README.txt1
-rw-r--r--tests/resources/attr_index/gitattributes4
-rw-r--r--tests/resources/attr_index/sub/sub/.gitattributes3
-rw-r--r--tests/resources/attr_index/sub/sub/README.md1
-rw-r--r--tests/resources/attr_index/sub/sub/README.txt1
-rw-r--r--tests/resources/bad_tag.git/HEAD1
-rw-r--r--tests/resources/bad_tag.git/config5
-rw-r--r--tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.idxbin0 -> 1268 bytes
-rw-r--r--tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.packbin0 -> 596 bytes
-rw-r--r--tests/resources/bad_tag.git/packed-refs5
-rw-r--r--tests/resources/bad_tag.git/refs/dummy-marker.txt0
-rw-r--r--tests/resources/big.indexbin0 -> 335272 bytes
-rw-r--r--tests/resources/binaryunicode/.gitted/HEAD1
-rw-r--r--tests/resources/binaryunicode/.gitted/config6
-rw-r--r--tests/resources/binaryunicode/.gitted/description1
-rw-r--r--tests/resources/binaryunicode/.gitted/indexbin0 -> 104 bytes
-rw-r--r--tests/resources/binaryunicode/.gitted/info/exclude6
-rw-r--r--tests/resources/binaryunicode/.gitted/info/refs3
-rw-r--r--tests/resources/binaryunicode/.gitted/objects/info/packs2
-rw-r--r--tests/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.idxbin0 -> 1380 bytes
-rw-r--r--tests/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.packbin0 -> 20879 bytes
-rw-r--r--tests/resources/binaryunicode/.gitted/refs/heads/branch11
-rw-r--r--tests/resources/binaryunicode/.gitted/refs/heads/branch21
-rw-r--r--tests/resources/binaryunicode/.gitted/refs/heads/master1
-rw-r--r--tests/resources/binaryunicode/file.txt1
-rw-r--r--tests/resources/blametest.git/HEAD1
-rw-r--r--tests/resources/blametest.git/config5
-rw-r--r--tests/resources/blametest.git/description1
-rw-r--r--tests/resources/blametest.git/objects/0c/bab4d45fd61e55a1c9697f9f9cb07a12e15448bin0 -> 46 bytes
-rw-r--r--tests/resources/blametest.git/objects/1a/ac69ae5d96461afc4d81d0066cb12f5b05a35bbin0 -> 28 bytes
-rw-r--r--tests/resources/blametest.git/objects/1b/5f0775af166331c854bd8d1bca3450eaf2532abin0 -> 35 bytes
-rw-r--r--tests/resources/blametest.git/objects/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/.gitconfig3
-rw-r--r--tests/resources/config/config-include2
-rw-r--r--tests/resources/config/config-included2
-rw-r--r--tests/resources/config/config07
-rw-r--r--tests/resources/config/config15
-rw-r--r--tests/resources/config/config101
-rw-r--r--tests/resources/config/config115
-rw-r--r--tests/resources/config/config127
-rw-r--r--tests/resources/config/config132
-rw-r--r--tests/resources/config/config144
-rw-r--r--tests/resources/config/config153
-rw-r--r--tests/resources/config/config163
-rw-r--r--tests/resources/config/config173
-rw-r--r--tests/resources/config/config185
-rw-r--r--tests/resources/config/config195
-rw-r--r--tests/resources/config/config25
-rw-r--r--tests/resources/config/config2011
-rw-r--r--tests/resources/config/config33
-rw-r--r--tests/resources/config/config45
-rw-r--r--tests/resources/config/config59
-rw-r--r--tests/resources/config/config65
-rw-r--r--tests/resources/config/config75
-rw-r--r--tests/resources/config/config80
-rw-r--r--tests/resources/config/config99
-rw-r--r--tests/resources/crlf/.gitted/HEAD1
-rw-r--r--tests/resources/crlf/.gitted/config0
-rw-r--r--tests/resources/crlf/.gitted/objects/04/de00b358f13389948756732158eaaaefa1448cbin0 -> 28 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/0a/a76e474d259bd7c13eb726a1396c381db55c88bin0 -> 27 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/0d/06894e14df22e066763ae906e0ed3eb79c205fbin0 -> 134 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/0f/f5a53f19bfd2b5eea1ba550295c47515678987bin0 -> 29 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/12/faf3c1ea55f572473cec9052fca468c3584ccb1
-rw-r--r--tests/resources/crlf/.gitted/objects/38/1cfe630df902bc29271a202d3277981180e4a6bin0 -> 25 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/79/9770d1cff46753a57db7a066159b5610da6e3abin0 -> 20 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/7c/ce67e58173e2b01f7db124ceaabe3183d19c49bin0 -> 24 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/a9/a2e8913c1dbe2812fac5e6b4e0a4bd5d0d59661
-rw-r--r--tests/resources/crlf/.gitted/objects/ba/aa042ab2976f8264e467988e6112ee518ec62ebin0 -> 159 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/dc/88e3b917de821e25962bea7ec1f55c4ce2112cbin0 -> 32 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/ea/030d3c6cec212069eca698cabaa5b4550f1511bin0 -> 32 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/fe/085d9ace90cc675b87df15e1aeed0c3a31407fbin0 -> 139 bytes
-rw-r--r--tests/resources/crlf/.gitted/refs/heads/master1
-rw-r--r--tests/resources/crlf/.gitted/refs/heads/utf81
-rw-r--r--tests/resources/deprecated-mode.git/HEAD1
-rw-r--r--tests/resources/deprecated-mode.git/config6
-rw-r--r--tests/resources/deprecated-mode.git/description1
-rw-r--r--tests/resources/deprecated-mode.git/indexbin0 -> 112 bytes
-rw-r--r--tests/resources/deprecated-mode.git/info/exclude2
-rw-r--r--tests/resources/deprecated-mode.git/objects/06/262edc257418e9987caf999f9a7a3e1547adffbin0 -> 124 bytes
-rw-r--r--tests/resources/deprecated-mode.git/objects/08/10fb7818088ff5ac41ee49199b51473b1bd6c7bin0 -> 350 bytes
-rw-r--r--tests/resources/deprecated-mode.git/objects/1b/05fdaa881ee45b48cbaa5e9b037d667a47745ebin0 -> 57 bytes
-rw-r--r--tests/resources/deprecated-mode.git/objects/3d/0970ec547fc41ef8a5882dde99c6adce65b021bin0 -> 29 bytes
-rw-r--r--tests/resources/deprecated-mode.git/refs/heads/master1
-rw-r--r--tests/resources/diff/.gitted/HEAD1
-rw-r--r--tests/resources/diff/.gitted/config6
-rw-r--r--tests/resources/diff/.gitted/description1
-rw-r--r--tests/resources/diff/.gitted/indexbin0 -> 225 bytes
-rw-r--r--tests/resources/diff/.gitted/info/exclude6
-rw-r--r--tests/resources/diff/.gitted/logs/HEAD2
-rw-r--r--tests/resources/diff/.gitted/logs/refs/heads/master2
-rw-r--r--tests/resources/diff/.gitted/objects/29/ab7053bb4dde0298e03e2c179e890b7dd465a7bin0 -> 730 bytes
-rw-r--r--tests/resources/diff/.gitted/objects/3e/5bcbad2a68e5bc60a53b8388eea53a1a7ab847bin0 -> 1108 bytes
-rw-r--r--tests/resources/diff/.gitted/objects/54/6c735f16a3b44d9784075c2c0dab2ac9bf1989bin0 -> 1110 bytes
-rw-r--r--tests/resources/diff/.gitted/objects/7a/9e0b02e63179929fed24f0a3e0f19168114d10bin0 -> 160 bytes
-rw-r--r--tests/resources/diff/.gitted/objects/7b/808f723a8ca90df319682c221187235af76693bin0 -> 922 bytes
-rw-r--r--tests/resources/diff/.gitted/objects/88/789109439c1e1c3cd45224001edee5304ed53c1
-rw-r--r--tests/resources/diff/.gitted/objects/cb/8294e696339863df760b2ff5d1e275bee72455bin0 -> 86 bytes
-rw-r--r--tests/resources/diff/.gitted/objects/d7/0d245ed97ed2aa596dd1af6536e4bfdb047b691
-rw-r--r--tests/resources/diff/.gitted/refs/heads/master1
-rw-r--r--tests/resources/diff/another.txt38
-rw-r--r--tests/resources/diff/readme.txt36
-rw-r--r--tests/resources/duplicate.git/COMMIT_EDITMSG1
-rw-r--r--tests/resources/duplicate.git/HEAD1
-rw-r--r--tests/resources/duplicate.git/config5
-rw-r--r--tests/resources/duplicate.git/description1
-rw-r--r--tests/resources/duplicate.git/indexbin0 -> 104 bytes
-rw-r--r--tests/resources/duplicate.git/info/exclude6
-rw-r--r--tests/resources/duplicate.git/info/refs1
-rw-r--r--tests/resources/duplicate.git/logs/HEAD1
-rw-r--r--tests/resources/duplicate.git/logs/refs/heads/master1
-rw-r--r--tests/resources/duplicate.git/objects/0d/deadede9e6d6ccddce0ee1e5749eed0485e5eabin0 -> 22 bytes
-rw-r--r--tests/resources/duplicate.git/objects/ce/013625030ba8dba906f756967f9e9ca394464abin0 -> 21 bytes
-rw-r--r--tests/resources/duplicate.git/objects/info/packs3
-rw-r--r--tests/resources/duplicate.git/objects/pack/pack-29a4896f0a0b9c9947b0927c57a5c03dcae052e3.idxbin0 -> 1184 bytes
-rw-r--r--tests/resources/duplicate.git/objects/pack/pack-29a4896f0a0b9c9947b0927c57a5c03dcae052e3.packbin0 -> 249 bytes
-rw-r--r--tests/resources/duplicate.git/objects/pack/pack-b18eeacbd65cbd30a365d7564b45a468e8bd43d6.idxbin0 -> 1268 bytes
-rw-r--r--tests/resources/duplicate.git/objects/pack/pack-b18eeacbd65cbd30a365d7564b45a468e8bd43d6.packbin0 -> 369 bytes
-rw-r--r--tests/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.idxbin0 -> 1156 bytes
-rw-r--r--tests/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.packbin0 -> 213 bytes
-rw-r--r--tests/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.idxbin0 -> 1100 bytes
-rw-r--r--tests/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.packbin0 -> 47 bytes
-rw-r--r--tests/resources/duplicate.git/packed-refs2
-rw-r--r--tests/resources/duplicate.git/refs/heads/dummy-marker.txt1
-rw-r--r--tests/resources/empty_bare.git/HEAD1
-rw-r--r--tests/resources/empty_bare.git/config7
-rw-r--r--tests/resources/empty_bare.git/description1
-rw-r--r--tests/resources/empty_bare.git/info/exclude6
-rw-r--r--tests/resources/empty_bare.git/objects/info/dummy-marker.txt0
-rw-r--r--tests/resources/empty_bare.git/objects/pack/dummy-marker.txt0
-rw-r--r--tests/resources/empty_bare.git/refs/heads/dummy-marker.txt0
-rw-r--r--tests/resources/empty_bare.git/refs/tags/dummy-marker.txt0
-rw-r--r--tests/resources/empty_standard_repo/.gitted/HEAD1
-rw-r--r--tests/resources/empty_standard_repo/.gitted/config8
-rw-r--r--tests/resources/empty_standard_repo/.gitted/description1
-rw-r--r--tests/resources/empty_standard_repo/.gitted/info/exclude6
-rw-r--r--tests/resources/empty_standard_repo/.gitted/objects/info/dummy-marker.txt0
-rw-r--r--tests/resources/empty_standard_repo/.gitted/objects/pack/dummy-marker.txt0
-rw-r--r--tests/resources/empty_standard_repo/.gitted/refs/heads/dummy-marker.txt0
-rw-r--r--tests/resources/empty_standard_repo/.gitted/refs/tags/dummy-marker.txt0
-rw-r--r--tests/resources/filemodes/.gitted/HEAD1
-rw-r--r--tests/resources/filemodes/.gitted/config6
-rw-r--r--tests/resources/filemodes/.gitted/description1
-rw-r--r--tests/resources/filemodes/.gitted/indexbin0 -> 528 bytes
-rw-r--r--tests/resources/filemodes/.gitted/info/exclude6
-rw-r--r--tests/resources/filemodes/.gitted/logs/HEAD1
-rw-r--r--tests/resources/filemodes/.gitted/logs/refs/heads/master1
-rw-r--r--tests/resources/filemodes/.gitted/objects/99/62c8453ba6f0cf8dac7c5dcc2fa2897fa9964abin0 -> 139 bytes
-rw-r--r--tests/resources/filemodes/.gitted/objects/a5/c5dd0fc6c313159a69b1d19d7f61a9f978e8f1bin0 -> 21 bytes
-rw-r--r--tests/resources/filemodes/.gitted/objects/e7/48d196331bcb20267eaaee4ff3326cb73b8182bin0 -> 99 bytes
-rw-r--r--tests/resources/filemodes/.gitted/refs/heads/master1
-rw-r--r--tests/resources/filemodes/exec_off1
-rwxr-xr-xtests/resources/filemodes/exec_off2on_staged1
-rwxr-xr-xtests/resources/filemodes/exec_off2on_workdir1
-rw-r--r--tests/resources/filemodes/exec_off_untracked1
-rwxr-xr-xtests/resources/filemodes/exec_on1
-rw-r--r--tests/resources/filemodes/exec_on2off_staged1
-rw-r--r--tests/resources/filemodes/exec_on2off_workdir1
-rwxr-xr-xtests/resources/filemodes/exec_on_untracked1
-rw-r--r--tests/resources/gitgit.indexbin0 -> 134799 bytes
-rw-r--r--tests/resources/icase/.gitted/HEAD1
-rw-r--r--tests/resources/icase/.gitted/config7
-rw-r--r--tests/resources/icase/.gitted/description1
-rw-r--r--tests/resources/icase/.gitted/indexbin0 -> 1392 bytes
-rw-r--r--tests/resources/icase/.gitted/info/exclude6
-rw-r--r--tests/resources/icase/.gitted/logs/HEAD1
-rw-r--r--tests/resources/icase/.gitted/logs/refs/heads/master1
-rw-r--r--tests/resources/icase/.gitted/objects/3e/257c57f136a1cb8f2b8e9a2e5bc8ec0258bdcebin0 -> 114 bytes
-rw-r--r--tests/resources/icase/.gitted/objects/4d/d6027d083575c7431396dc2a3174afeb393c93bin0 -> 61 bytes
-rw-r--r--tests/resources/icase/.gitted/objects/62/e0af52c199ec731fe4ad230041cd3286192d49bin0 -> 19 bytes
-rw-r--r--tests/resources/icase/.gitted/objects/76/d6e1d231b1085fcce151427e9899335de74be63
-rw-r--r--tests/resources/icase/.gitted/objects/d4/4e18fb93b7107b5cd1b95d601591d77869a1b6bin0 -> 21 bytes
-rw-r--r--tests/resources/icase/.gitted/refs/heads/master1
-rw-r--r--tests/resources/icase/B1
-rw-r--r--tests/resources/icase/D1
-rw-r--r--tests/resources/icase/F1
-rw-r--r--tests/resources/icase/H1
-rw-r--r--tests/resources/icase/J1
-rw-r--r--tests/resources/icase/L/11
-rw-r--r--tests/resources/icase/L/B1
-rw-r--r--tests/resources/icase/L/D1
-rw-r--r--tests/resources/icase/L/a1
-rw-r--r--tests/resources/icase/L/c1
-rw-r--r--tests/resources/icase/a1
-rw-r--r--tests/resources/icase/c1
-rw-r--r--tests/resources/icase/e1
-rw-r--r--tests/resources/icase/g1
-rw-r--r--tests/resources/icase/i1
-rw-r--r--tests/resources/icase/k/11
-rw-r--r--tests/resources/icase/k/B1
-rw-r--r--tests/resources/icase/k/D1
-rw-r--r--tests/resources/icase/k/a1
-rw-r--r--tests/resources/icase/k/c1
-rw-r--r--tests/resources/issue_1397/.gitted/HEAD1
-rw-r--r--tests/resources/issue_1397/.gitted/config6
-rw-r--r--tests/resources/issue_1397/.gitted/indexbin0 -> 233 bytes
-rw-r--r--tests/resources/issue_1397/.gitted/objects/7f/483a738f867e5b21c8f377d70311f011eb48b53
-rw-r--r--tests/resources/issue_1397/.gitted/objects/83/12e0889a9cbab77c732b6bc39b51a683e3a318bin0 -> 48 bytes
-rw-r--r--tests/resources/issue_1397/.gitted/objects/8a/7ef047fc933edb62e84e7977b0612ec3f6f283bin0 -> 141 bytes
-rw-r--r--tests/resources/issue_1397/.gitted/objects/8e/8f80088a9274fd23584992f587083ca1bcbbacbin0 -> 63 bytes
-rw-r--r--tests/resources/issue_1397/.gitted/objects/f2/c62dea0372a0578e053697d5c1ba1ac05e774abin0 -> 94 bytes
-rw-r--r--tests/resources/issue_1397/.gitted/objects/ff/3578d64d199d5b48d92bbb569e0a273e411741bin0 -> 73 bytes
-rw-r--r--tests/resources/issue_1397/.gitted/refs/heads/master1
-rw-r--r--tests/resources/issue_1397/crlf_file.txt3
-rw-r--r--tests/resources/issue_1397/some_other_crlf_file.txt3
-rw-r--r--tests/resources/issue_592/.gitted/COMMIT_EDITMSG1
-rw-r--r--tests/resources/issue_592/.gitted/HEAD1
-rw-r--r--tests/resources/issue_592/.gitted/config8
-rw-r--r--tests/resources/issue_592/.gitted/indexbin0 -> 392 bytes
-rw-r--r--tests/resources/issue_592/.gitted/info/exclude6
-rw-r--r--tests/resources/issue_592/.gitted/logs/HEAD2
-rw-r--r--tests/resources/issue_592/.gitted/logs/refs/heads/master2
-rw-r--r--tests/resources/issue_592/.gitted/objects/06/07ee9d4ccce8e4c4fa13c2c7d727e7faba4e0ebin0 -> 87 bytes
-rw-r--r--tests/resources/issue_592/.gitted/objects/49/363a72a90d9424240258cd3759f23788ecf1d8bin0 -> 55 bytes
-rw-r--r--tests/resources/issue_592/.gitted/objects/4d/383e87f0371ba8fa353f3912db6862b2625e852
-rw-r--r--tests/resources/issue_592/.gitted/objects/71/44be264b61825fbff68046fe999bdfe96a1792bin0 -> 50 bytes
-rw-r--r--tests/resources/issue_592/.gitted/objects/be/de83ee10b5b3f00239660b00acec2d55fd0b84bin0 -> 107 bytes
-rw-r--r--tests/resources/issue_592/.gitted/objects/e3/8fcc7a6060f5eb5b876e836b52ae4769363f21bin0 -> 137 bytes
-rw-r--r--tests/resources/issue_592/.gitted/objects/f1/adef63cb08891a0942b76fc4b9c50c6c494bc7bin0 -> 29 bytes
-rw-r--r--tests/resources/issue_592/.gitted/refs/heads/master1
-rw-r--r--tests/resources/issue_592/a.txt1
-rw-r--r--tests/resources/issue_592/c/a.txt1
-rw-r--r--tests/resources/issue_592/l.txt1
-rw-r--r--tests/resources/issue_592/t/a.txt1
-rw-r--r--tests/resources/issue_592/t/b.txt1
-rw-r--r--tests/resources/issue_592b/.gitted/HEAD1
-rw-r--r--tests/resources/issue_592b/.gitted/config6
-rw-r--r--tests/resources/issue_592b/.gitted/description1
-rw-r--r--tests/resources/issue_592b/.gitted/indexbin0 -> 376 bytes
-rw-r--r--tests/resources/issue_592b/.gitted/info/exclude6
-rw-r--r--tests/resources/issue_592b/.gitted/logs/HEAD1
-rw-r--r--tests/resources/issue_592b/.gitted/logs/refs/heads/master1
-rw-r--r--tests/resources/issue_592b/.gitted/objects/3f/bf1852f72fd268e36457b13a18cdd9a4c9ea352
-rw-r--r--tests/resources/issue_592b/.gitted/objects/6f/a891d3e578c83e1c03bdb9e0fdd8e6e934157fbin0 -> 28 bytes
-rw-r--r--tests/resources/issue_592b/.gitted/objects/80/07d41d5794e6ce4d4d2c97e370d5a9aa6d5213bin0 -> 24 bytes
-rw-r--r--tests/resources/issue_592b/.gitted/objects/a6/5fb6583a7c425284142f285bc359a2d6565513bin0 -> 93 bytes
-rw-r--r--tests/resources/issue_592b/.gitted/objects/ae/be7a55922c7097ef91ca3a7bc327a901d87c2cbin0 -> 122 bytes
-rw-r--r--tests/resources/issue_592b/.gitted/objects/b3/44b055867fcdc1f01eaa75056a43e868eb4fbcbin0 -> 36 bytes
-rw-r--r--tests/resources/issue_592b/.gitted/objects/f7/d75fbfad8b1d2e307ced287ea78aad403cdce3bin0 -> 57 bytes
-rw-r--r--tests/resources/issue_592b/.gitted/refs/heads/master1
-rw-r--r--tests/resources/issue_592b/gitignore1
-rw-r--r--tests/resources/issue_592b/ignored/contained/ignored3.txt1
-rw-r--r--tests/resources/issue_592b/ignored/contained/tracked3.txt1
-rw-r--r--tests/resources/issue_592b/ignored/ignored2.txt1
-rw-r--r--tests/resources/issue_592b/ignored/tracked2.txt1
-rw-r--r--tests/resources/issue_592b/ignored1.txt1
-rw-r--r--tests/resources/issue_592b/tracked1.txt1
-rw-r--r--tests/resources/merge-resolve/.gitted/COMMIT_EDITMSG1
-rw-r--r--tests/resources/merge-resolve/.gitted/HEAD1
-rw-r--r--tests/resources/merge-resolve/.gitted/ORIG_HEAD1
-rw-r--r--tests/resources/merge-resolve/.gitted/config6
-rw-r--r--tests/resources/merge-resolve/.gitted/description1
-rw-r--r--tests/resources/merge-resolve/.gitted/indexbin0 -> 624 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/HEAD236
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/branch2
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/df_ancestor5
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/df_side114
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/df_side29
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/ff_branch5
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/master5
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/octo12
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/octo22
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/octo32
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/octo42
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/octo52
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/octo63
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/renames12
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/renames23
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-103
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10-branch2
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-113
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11-branch2
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-133
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13-branch2
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-143
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14-branch2
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt2
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt-branch2
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt3
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt-branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-42
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4-branch2
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-12
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1-branch2
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-23
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2-branch2
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-63
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6-branch2
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-73
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7-branch5
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-83
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8-branch2
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-93
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9-branch2
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/unrelated1
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/00/5b6fcc8fec71d2550bef8462d169b3c26aa14bbin0 -> 168 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/00/9b9cab6fdac02915a88ecd078b7a792ed802d8bin0 -> 164 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/00/c7d33f1ffa79d19c2272b370fcaeaadba49c08bin0 -> 147 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/01/f149e1b8f84bd8896aaff6d6b22af88459ded0bin0 -> 166 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/02/04a84f822acbf6386b36d33f1f6bc68bbbf858bin0 -> 168 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/02/251f990ca8e92e7ae61d3426163fa821c64001bin0 -> 264 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/03/21415405cb906c46869919af56d51dbbe5e85cbin0 -> 271 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/03/2ebc5ab85d9553bb187d3cd40875ff23a63ed0bin0 -> 29 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/03/b87706555accbf874ccd410dbda01e8e70a67fbin0 -> 353 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/03/dad1005e5d06d418f50b12e0bcd48ff2306a03bin0 -> 264 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/05/1ffd7901a442faf56b226161649074f15c7c471
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/05/8541fc37114bfc1dddf6bd6bffc7fae5c2e6febin0 -> 63 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/05/f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91cbin0 -> 170 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/07/a759da919f737221791d542f176ab49c88837fbin0 -> 165 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/07/c514b04698e068892b31c8d352b85813b99c6ebin0 -> 32 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/09/055301463b7f2f8ee5d368f8ed5c0a40ad8515bin0 -> 41 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/09/17bb159596aea4d295f4857da77e8f96b3c7dcbin0 -> 36 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/09/2ce8682d7f3a2a3a769a6daca58950168ba5c4bin0 -> 163 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/09/3bebf072dd4bbba88833667d6ffe454df199e1bin0 -> 266 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/09/768bed22680cdb0859683fa9677ccc8d5a25c1bin0 -> 275 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/0a/75d9aac1dc84fb5aa51f7325c0ab53242ddef7bin0 -> 275 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/0c/fd6c54ef6532d862408f562309dc9c74a401e8bin0 -> 28 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/0d/52e3a556e189ba0948ae56780918011c1b167dbin0 -> 235 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/0d/872f8e871a30208305978ecbf9e66d864f1638bin0 -> 89 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/0e/c5f433959cd46177f745903353efb5be08d151bin0 -> 165 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/11/deab00b2d3a6f5a3073988ac050c2d7b6655e2bin0 -> 34 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/11/f4f3c08b737f5fd896cbefa1425ee63b21b2fa1
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/13/d1be4ea52a6ced1d7a1d832f0ee3c399348e5ebin0 -> 168 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/14/39088f509b79b1535b64193137d3ce4b240734bin0 -> 58 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/15/8dc7bedb202f5b26502bf3574faa7f4238d56c2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/16/f825815cfd20a07a75c71554e82d8eede0b0611
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/17/8940b450f238a56c0d75b7955cb57b38191982bin0 -> 65 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/18/3310e30fb1499af8c619108ffea4d300b5e778bin0 -> 170 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/18/cb316b1cefa0f8a6946f0e201a8e1a6f845ab9bin0 -> 68 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/19/b7ac485269b672a101060894de3ba9c2a24dd1bin0 -> 53 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/1a/010b1c0f081b2e8901d55307a15c29ff30af0ebin0 -> 19 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/1c/ff9ec6a47a537380dedfdd17c9e76d74259a2bbin0 -> 33 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/1e/4ff029aee68d0d69ef9eb6efa6cbf1ec732f99bin0 -> 29 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/1f/81433e3161efbf250576c58fede7f6b836f3d3bin0 -> 262 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/20/91d94c8bd3eb0835dc5220de5e8bb310fa1513bin0 -> 271 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/21/671e290278286fb2ce4c63d01699b67adce331bin0 -> 79 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/22/7792b52aaa0b238bea00ec7e509b02623f168cbin0 -> 102 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/23/3c0919c998ed110a4b6ff36f353aec8b713487bin0 -> 43 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/23/92a2dacc9efb562b8635d6579fb458751c7c5bbin0 -> 142 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/24/1a1005cd9b980732741b74385b891142bcba28bin0 -> 67 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/24/2591eb280ee9eeb2ce63524b9a8b9bc4cb515dbin0 -> 30 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/24/90b9f1a079420870027deefb49f51d6656cf74bin0 -> 268 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/25/9d08ca43af9200e9ea9a098e44a5a350ebd9b3bin0 -> 381 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/25/c40b7660c08c8fb581f770312f41b9b03119d1bin0 -> 31 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/26/153a3ff3649b6c2bb652d3f06878c6e0a172f9bin0 -> 48 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/27/133da702ba3c60af2a01e96c2555ff4045d692bin0 -> 32 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/2b/0de5dc27505dcdd83a75c8bf1fcd9462cd7addbin0 -> 147 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/2b/5f1f181ee3b58ea751f5dd5d8f9b445520a136bin0 -> 53 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863bin0 -> 56 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/2d/a538570bc1e5b2c3e855bf702f35248ad0735f2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/2f/2e37b7ebbae467978610896ca3aafcdad2ee67bin0 -> 52 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/2f/4024ce528d36d8670c289cce5a7963e625bb0cbin0 -> 179 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/2f/56120107d680129a5d9791b521cb1e73a2ed313
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/2f/598248eeccfc27e5ca44d9d96383f6dfea7b161
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/31/68dca1a561889b045a6441909f4c56145e666d2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/31/d5472536041a83d986829240bbbdc897c6f8a6bin0 -> 41 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/32/21dd512b7e2dc4b5bd03046df6c81b2ab2070bbin0 -> 47 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/33/46d64325b39e5323733492cd55f808994a2475bin0 -> 33 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/33/d500f588fbbe65901d82b4e6b008e549064be02
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/34/bfafff88eaf118402b44e6f3e2dbbf1a582b051
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/35/0c6eb3010efc403a6bed682332635314e9ed58bin0 -> 92 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/35/411bfb77cd2cc431f3a03a2b4976ed94b5d241bin0 -> 31 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/35/4704d3613ad4228e4786fc76656b11e98236c4bin0 -> 41 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/35/632e43612c06a3ea924bfbacd48333da874c291
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/35/75826c96a975031d2c14368529cc5c4353a8fdbin0 -> 163 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/36/219b49367146cb2e6a1555b5a9ebd4d0328495bin0 -> 68 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/36/4bbe4ce80c7bd31e6307dce77d46e3e1759fb3bin0 -> 35 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/37/48859b001c6e627e712a07951aee40afd19b41bin0 -> 41 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/38/5c8a0f26ddf79e9041e15e17dc352ed2c4cced2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/3b/47b031b3e55ae11e14a05260b1c3ffd6838d55bin0 -> 161 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/3b/bf0bf59b20df5d5fc58b9fc1dc07be637c301fbin0 -> 269 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/3e/f4d30382ca33fdeba9fda895a99e0891ba37aabin0 -> 36 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/3e/f9bfe82f9635518ae89152322f3b46fd4ba25bbin0 -> 172 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/40/2784a46a4a3982294231594cbeb431f506d22cbin0 -> 83 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/41/2b32fb66137366147f1801ecc962452757d48a2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/43/aafd43bea779ec74317dc361f45ae3f532a505bin0 -> 37 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/43/c338656342227a3a3cd3aa85cbf784061f5425bin0 -> 266 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/45/299c1ca5e07bba1fd90843056fb559f96b1f5abin0 -> 58 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/46/6daf8552b891e5c22bc58c9d7fc1a2eb8f0289bin0 -> 382 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/47/6dbb3e207313d1d8aaa120c6ad204bf1295e53bin0 -> 522 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/47/8172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2bin0 -> 165 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/49/130a28ef567af9a6a6104c38773fedfa5f9742bin0 -> 37 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/49/9df817155e4bdd3c6ee192a72c52f481818230bin0 -> 35 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4a/9550ebcc97ce22b22f45af7b829bb030d003f5bin0 -> 53 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4b/253da36a0ae8bfce63aeabd8c5b584299255942
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4b/48deed3a433909bfd6b6ab3d4b91348b6af464bin0 -> 24 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904bin0 -> 15 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4c/9fac0707f8d4195037ae5a681aa48626491541bin0 -> 167 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4c/a408a8c88655f7586a1b580be6fad138121e98bin0 -> 159 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4e/0d9401aee78eb345a8685a859d37c8c3c0bbedbin0 -> 262 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10bin0 -> 56 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4e/b04c9e79e88f6640d01ff5b25ca2a60764f216bin0 -> 34 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4f/e93c0ec83eb6305cbace3dace88ecee1b63cb6bin0 -> 161 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/50/12fd565b1393bdfda1805d4ec38ce6619e1fd1bin0 -> 29 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/50/4f75ac95a71ef98051817618576a68505b92f9bin0 -> 93 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/50/84fc2a88b6bdba8db93bd3953a8f4fdb470238bin0 -> 53 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/50/ce7d7d01217679e26c55939eef119e0c93e272bin0 -> 159 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/51/95a1b480f66691b667f10a9e41e70115a78351bin0 -> 170 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/52/d8bc572af2b6d4ee0d5e62ed5d1fbad92210a93
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/53/825f41ac8d640612f9423a2f03a69f3d96809abin0 -> 164 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/54/269b3f6ec3d7d4ede24dd350dd5d605495c3ae2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/54/59c89aa0026d543ce8343bd89871bce543f9c23
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/54/7607c690372fe81fab8e3bb44c530e129118fdbin0 -> 58 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/55/b4e4687e7a0d9ca367016ed930f385d4022e6f1
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/56/6ab53c220a2eafc1212af1a024513230280ab93
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/56/a638b76b75e068590ac999c2f8621e7f3e264c1
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/57/079a46233ae2b6df62e9ade71c4948512abefbbin0 -> 168 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/58/43febcb23480df0b5edb22a21c59c772bb8e29bin0 -> 71 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/58/e853f66699fd02629fd50bde08082bc005933abin0 -> 160 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/59/6803b523203a4851c824c07366906f8353f4adbin0 -> 163 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/5c/2411f8075f48a6b2fdb85ebc0d371747c4df15bin0 -> 37 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/5c/341ead2ba6f2af98ce5ec3fe84f6b6d2899c0dbin0 -> 37 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/5c/3b68a71fc4fa5d362fd3875e53137c6a5ab7a5bin0 -> 40 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/5d/c1018e90b19654bee986b7a0c268804d39659dbin0 -> 168 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/5d/dd0fe66f990dc0e5cf9fec6d9b465240e9537fbin0 -> 43 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/5e/b7bb6a146eb3c7fd3990b240a2308eceb1cf8dbin0 -> 268 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/5f/bfbdc04b4eca46f54f4853a3c5a1dce28f5165bin0 -> 283 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/60/91fc2c036a382a69489e3f518ee5aae9a4e567bin0 -> 258 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/61/340eeed7340fa6a8792def9a5938bb5d4434bbbin0 -> 92 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/61/78885b38fe96e825ac0f492c0a941f288b37f6bin0 -> 289 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/62/12c31dab5e482247d7977e4f0dd3601decf13bbin0 -> 45 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/62/269111c3b02a9355badcb9da8678b1bf41787bbin0 -> 269 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/62/c4f6533c9a3894191fdcb96a3be935ade63f1abin0 -> 53 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/63/247125386de9ec90a27ad36169307bf8a11a381
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/67/18a45909532d1fcf5600d0877f7fe7e78f0b861
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/68/c6c84b091926c7d90aa6a79b2bc3bb6adccd8ebin0 -> 55 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/69/f570c57b24ea7c086e94c5e574964798321435bin0 -> 266 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/6a/e1a3967031a42cf955d9d5c2395211ac82f6cfbin0 -> 272 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/6b/7e37be8ce0b897093f2878a9dcd8f396beda2cbin0 -> 53 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/6c/06dcd163587c2cc18be44857e0b71116382aebbin0 -> 30 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/6f/32739c3724d1d5f855299309f388606f407468bin0 -> 630 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/6f/a33014764bf1120a454eb8437ae098238e409bbin0 -> 168 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/6f/be9fb85c86d7d1435f728da418bdff52c640a9bin0 -> 83 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/71/17467b18605a660ebe5586df69e2311ed5609fbin0 -> 265 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/71/2ebba6669ea847d9829e4f1059d6c830c8b531bin0 -> 152 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/71/add2d7b93d55bf3600f8a1582beceebbd050c8bin0 -> 264 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/72/ea499e108df5ff0a4a913e7655bbeeb1fb69f2bin0 -> 26 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/74/df13f0793afdaa972150bba976f7de8284914ebin0 -> 26 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/75/a811bf6bc57694adb3fe604786f3a4efd1cd1b2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/76/63fce0130db092936b137cabd693ec234eb060bin0 -> 49 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/76/ab0e2868197ec158ddd6c78d8a0d2fd73d38f9bin0 -> 37 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/7a/a3edf2bcfee22398e6b55295aa56366b7aaf76bin0 -> 271 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/7c/2c5228c9e90170d4a35e6558e47163daf092e5bin0 -> 172 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/7c/b63eed597130ba4abb87b3e544b850219055203
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/7e/2d058d5fedf8329db44db4fac610d6b1a89159bin0 -> 165 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/7f/7a2da58126226986d71c6ddfab4afba693280dbin0 -> 199 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/80/a8fbb3abb1ba423d554e9630b8fc2e5698f86bbin0 -> 168 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/81/87117062b750eed4f93fd7e899f17b52ce554dbin0 -> 170 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/83/07d93a155903a5c49576583f0ce1f6ff897c0ebin0 -> 30 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/83/824a8c6658768e2013905219cc8c64cc3d9a2ebin0 -> 382 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/84/9619b03ae540acee4d1edec96b86993da6b4973
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/84/de84f8f3a6d63e636ee9ad81f4b80512fa9bbebin0 -> 41 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/86/088dae8bade454995b21a1c88107b0e1accdabbin0 -> 47 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/87/b4926260d77a3b851e71ecce06839bd650b231bin0 -> 43 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/88/e185910a15cd13bdf44854ad037f4842b03b29bin0 -> 177 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/8a/ad9d0ea334951da47b621a475b39cc6ed759bfbin0 -> 51 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/8a/ae714f7d939309d7f132b30646d96743134a9f1
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/8b/095d8fd01594f4d14454d073e3ac57b9ce485fbin0 -> 201 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/8b/5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a1
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/8b/fb012a6d809e499bd8d3e194a3929bc8995b93bin0 -> 34 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/8c/749d9968d4b10dcfb06c9f97d0e5d92d3370712
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/8f/4433f8593ddd65b7dd43dd4564d841f4d9c8aabin0 -> 164 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/90/a336c7dacbe295159413559b0043b8bdc60d57bin0 -> 271 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/91/2b2d7819cf9c1029e414883857ed61d597a1a5bin0 -> 295 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/91/8bb3e09090a9995d48af9a2a6296d7e6088d1cbin0 -> 38 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/92/7d4943cdbdc9a667db8e62cfd0a41870235c51bin0 -> 535 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/93/77fccdb210540b8c0520cc6e80eb632c20bd25bin0 -> 53 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/94/4f5dd1a867cab4c2bbcb896493435cae1dcc1a2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/94/8ba6e701c1edab0c2d394fb7c5538334129793bin0 -> 71 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/95/646149ab6b6ba6edc83cff678582538b457b2b3
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/95/9de65e568274120fdf9e3af9f77b1550122149bin0 -> 40 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/96/8ca794a4597f7f6abbb2b8d940b4078a0f3fd4bin0 -> 53 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/97/7c696519c5a3004c5f1d15d60c89dbeb8f235fbin0 -> 160 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/98/ba4205fcf31f5dd93c916d35fe3f3b3d0e67141
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/98/d52d07c0b0bbf2b46548f6aa521295c2cb55db3
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/99/b4f7e4f24470fa06b980bc21f1095c2a9425c0bin0 -> 164 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/9a/301fbe6fada7dcb74fcd7c20269b5c743459a7bin0 -> 163 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/9a/f731fa116d1eb9a6c0109562472cfee6f5a979bin0 -> 48 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/9c/0b6c34ef379a42d858f03fef38630f476b9102bin0 -> 38 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/9e/7f4359c469f309b6057febf4c6e80742cbed5bbin0 -> 539 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/9e/fe7723802d4305142eee177e018fee1572c4f4bin0 -> 36 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/9f/74397a3397b3585faf09e9926b110d7f654254bin0 -> 621 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a0/31a28ae70e33a641ce4b8a8f6317f1ab79dee4bin0 -> 37 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a3/9a620dae5bc8b4e771cd4d251b7d080401a21ebin0 -> 29 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a3/fabece9eb8748da810e1e08266fef9b7136ad4bin0 -> 164 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a4/1b1bb6d0be3c22fb654234c33b428e15c8cc27bin0 -> 92 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a4/3150a738849c59376cf30bb2a68348a83c8f48bin0 -> 162 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a5/563304ddf6caba25cb50323a2ea6f7dbfcadcabin0 -> 48 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a7/08b253bd507417ec42d1467a7fd2d7519c4956bin0 -> 40 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a7/65fb87eb2f7a1920b73b2d5a057f8f8476a42bbin0 -> 170 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a7/7a56a49f8f3ae242e02717f18ebbc60c5cc543bin0 -> 65 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a7/dbfcbfc1a60709cb80b5ca24539008456531d01
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a8/02e06f1782a9645b9851bc7202cee74a8a4972bin0 -> 172 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a8/87dd39ad3edd610fc9083dcb61e40ab50673d11
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a9/0bc3fb6f15181972a2959a921429efbd81a4732
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ab/40af3cb8a3ed2e2843e96d9aa7871336b94573bin0 -> 161 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ab/6c44a2e84492ad4b41bb6bac87353e9d02ac8bbin0 -> 33 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ab/929391ac42572f92110f3deeb4f0844a951e22bin0 -> 40 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ac/4045f965119e6998f4340ed0f411decfb3ec05bin0 -> 29 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ad/a14492498136771f69dd451866cabcb0e9ef9abin0 -> 39 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ad/a55a45d14527dc3dfc714ea1c65d2e1e6fbe871
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/b2/d399ae15224e1d58066e3c8df70ce37de7a6562
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/b4/2712cfe99a1a500b2a51fe984e0b8a7702ba115
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/b6/9fe837e4cecfd4c9a40cdca7c138468687df072
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/b6/f610aef53bd343e6c96227de874c66f00ee8e8bin0 -> 162 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/b7/a2576f9fc20024ac9ef17cb134acbd1ac73127bin0 -> 320 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/b8/a3a806d3950e8c0a03a34f234a92eff0e2c68dbin0 -> 286 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ba/cac9b3493509aa15e1730e1545fc0919d1dae0bin0 -> 29 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/bc/744705e1d8a019993cf88f62bc4020f1b809192
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/bc/95c75d59386147d1e79a87c33068d8dbfd71f2bin0 -> 348 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/bd/593285fc7fe4ca18ccdbabf027f5d689101452bin0 -> 159 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/bd/867fbae2faa80b920b002b80b1c91bcade7784bin0 -> 48 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/bd/9cb4cd0a770cb9adcb5fce212142ef40ea1c35bin0 -> 51 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/be/f6e37b3ee632ba74159168836f382fed21d77d2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c0/6a9be584ac49aa02c5551312d9e2982c91df10bin0 -> 348 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c1/b17981db0840109a820dae8674ee29684134ffbin0 -> 348 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c1/b6a51bbb87c2f82b161412c3d20b59fc69b090bin0 -> 47 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c3/5dee9bcc0e989f3b0c40f68372a9a51b6c4e6abin0 -> 162 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c3/d02eeef75183df7584d8d13ac03053910c1301bin0 -> 67 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c4/efe31e9decccc8b2b4d3df9aac2cdfe2995618bin0 -> 538 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c5/0d0f1cb60b8b0fe1615ad20ace557e9d68d7bd1
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c5/bbe550b9f09444bdddd3ecf3d97c0b42aa786cbin0 -> 269 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c6/07fc30883e335def28cd686b51f6cfa02b06ec2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c6/92ecf62007c0ac9fb26e2aa884de2933de15edbin0 -> 40 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c8/f06f2e3bb2964174677e91f0abead0e43c9e5dbin0 -> 45 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c9/174cef549ec94ecbc43ef03cdc775b4950becb2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c9/4b27e41064c521120627e07e2035cca1d24ffabin0 -> 162 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ca/b2cf23998b40f1af2d9d9a756dc9e285a8df4bbin0 -> 40 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/cb/491780d82e46dc88a065b965ab307a038f2bc2bin0 -> 163 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/cb/6693a788715b82440a54e0eacd19ba9f6ec559bin0 -> 41 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/cc/3e3009134cb88014129fc8858d1101359e5e2f2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ce/8860d49e3bea6fd745874a01b7c3e46da8cbc3bin0 -> 48 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ce/e656c392ad0557b3aae0fb411475c206e2926fbin0 -> 32 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/cf/8c5cc8a85a1ff5a4ba51e0bc7cf5665669924dbin0 -> 29 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d0/7ec190c306ec690bac349e87d01c4358e49bb22
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d0/d4594e16f2e19107e3fa7ea63e7aaaff305ffbbin0 -> 51 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d2/f8637f2eab2507a1e13cbc9df4729ec386627ebin0 -> 268 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d3/719a5ae8e4d92276b5313ce976f6ee5af2b4362
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d3/7aa3bbfe1c0c49b909781251b956dbabe85f96bin0 -> 80 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d3/7ad72a2052685fc6201c2af90103ad42d2079bbin0 -> 233 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d4/207f77243500bec335ab477f9227fcdb1e271a2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46bin0 -> 38 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d5/093787ef302b941b6aab081b99fb4880038bd8bin0 -> 30 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d5/a61b0b4992a4f0caa887fa08b52431e727bb6fbin0 -> 81 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d5/b6fc965c926a1bfc9ee456042b94088b5c5d21bin0 -> 319 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d5/ec1152fe25e9fec00189eb00b3db71db24c218bin0 -> 24 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d6/42b9770c66bba94a08df09b5efb095001f76d7bin0 -> 539 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d6/462fa3f5292857db599c54aea2bf91616230c5bin0 -> 48 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d6/cf6c7741b3316826af1314042550c97ded1d502
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d8/74671ef5b20184836cb983bb273e5280384d0bbin0 -> 162 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d8/fa77b6833082c1ea36b7828a582d4c438824501
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d9/63979c237d08b6ba39062ee7bf64c7d34a27f8bin0 -> 48 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/da/178208145ef585a1bd5ca5f4c9785d738df2cfbin0 -> 41 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/db/6261a7c65c7fd678520c9bb6f2c47582ab9ed5bin0 -> 624 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/dd/9a570c3400e6e07bc4d7651d6e20b08926b3d9bin0 -> 36 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/de/872ee3618b894992e9d1e18ba2ebe256a112f91
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/df/e3f22baa1f6fce5447901c3086bae368de6bddbin0 -> 40 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e0/67f9361140f19391472df8a82d6610813c73b7bin0 -> 53 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e1/129b3cfb5898e0fbd606e0cb80b2755e50d161bin0 -> 92 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e1/7ace1492648c9dc5701bad5c47af9d1b60c4e9bin0 -> 264 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e2/c6abbd55fed5ac71a5f2751e29b4a34726a5951
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e3/1e7ad3ed298f24e383c4950f4671993ec078e4bin0 -> 210 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e3/76fbdd06ebf021c92724da9f26f44212734e3e3
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e4/9f917b448d1340b31d76e54ba388268fd4c922bin0 -> 36 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e4/f618a2c3ed0669308735727df5ebf2447f022f2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e6/5a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812bin0 -> 160 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e8/107f24196736b870a318a0e28f048e29f6feff3
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e9/2cdb7017dc6c5aed25cb4202c5b0104b872246bin0 -> 48 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e9/ad6ec3e38364a3d07feda7c4197d4d845c53b5bin0 -> 36 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e9/f48beccc62d535739bfbdebe0a55ed716d8366bin0 -> 382 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/eb/c09d0137cfb0c26697aed0109fb943ad906f3fbin0 -> 166 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ec/67e5a86adff465359f1c8f995e12dbdfa08d8abin0 -> 166 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ed/9523e62e453e50dd9be1606af19399b96e397abin0 -> 87 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ee/1d6f164893c1866a323f072eeed36b855656bebin0 -> 291 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccfbin0 -> 64 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ee/a9286df54245fea72c5b557291470eb825f38fbin0 -> 235 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ef/58fdd8086c243bdc81f99e379acacfd21d32d62
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ef/c499524cf105d5264ac7fc54e07e95764e8075bin0 -> 32 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ef/c9121fdedaf08ba180b53ebfbcf71bd488ed09bin0 -> 160 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f0/053b8060bb3f0be5cbcc3147a07ece26bf097ebin0 -> 163 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f0/ce2b8e4986084d9b308fb72709e414c23eb5e6bin0 -> 125 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f2/0c9063fa0bda9a397c96947a7b687305c49753bin0 -> 29 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f2/9e7fb590551095230c6149cbe72f2e9104a796bin0 -> 41 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f3/293571dcd708b6a3faf03818cd2844d000e1981
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f3/f1164b68b57b1995b658a828320e6df3081faebin0 -> 310 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f4/15caf3fcad16304cb424b67f0ee6b12dc03aaebin0 -> 320 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f4/8097eb340dc5a7cae55aabcf1faf4548aa821fbin0 -> 165 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f5/504f36e6f4eb797a56fc5bac6c6c7f32969bf2bin0 -> 42 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f5/b50c85a87cac64d7eb3254cdd1aec9564c0293bin0 -> 35 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f5/f9dd5886a6ee20272be0aafc790cba43b31931bin0 -> 244 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f6/be049e284c0f9dcbbc745543885be3502ea521bin0 -> 265 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f7/c332bd4d4d4b777366cae4d24d1687477576bfbin0 -> 156 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f8/958bdf4d365a84a9a178b1f5f35ff1dacbd8842
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/fa/c03f2c5139618d87d53614c153823bf1f31396bin0 -> 76 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/fa/da9356aa3f74622327a3038ae9c6f92e1c5c1dbin0 -> 168 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/fb/738a106cfd097a4acb96ce132ecb1ad6c46b03bin0 -> 264 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/fc/4c636d6515e9e261f9260dbcf3cc6eca97ea08bin0 -> 29 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/fc/7d7b805f7a9428574f4f802b2e34cd20ab9d99bin0 -> 575 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/fc/90237dc4891fa6c69827fc465632225e391618bin0 -> 163 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/fd/57d2d6770fad8e9959124793a17f441b571e66bin0 -> 279 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/fd/89f8cffb663ac89095a0f9764902e93ceaca6a2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/fe/5407fc50a53aecb41d1a6e9ea7b612e581af87bin0 -> 48 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ff/49d07869831ad761bbdaea026086f8789bcb00bin0 -> 24 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ff/b312248d607284c290023f9502eea010d34efdbin0 -> 68 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/df_ancestor1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/df_side11
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/df_side21
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/ff_branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/master1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/octo11
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/octo21
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/octo31
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/octo41
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/octo51
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/octo61
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ancestor1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ours1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_theirs1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/renames11
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/renames21
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-101
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-10-branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-111
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-11-branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-131
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-13-branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-141
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-14-branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-2alt1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-2alt-branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-3alt1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-3alt-branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-41
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-4-branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-11
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1-branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-21
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2-branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-61
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-6-branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-71
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-7-branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-81
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-8-branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-91
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-9-branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/unrelated1
-rw-r--r--tests/resources/merge-resolve/added-in-master.txt1
-rw-r--r--tests/resources/merge-resolve/automergeable.txt9
-rw-r--r--tests/resources/merge-resolve/changed-in-branch.txt1
-rw-r--r--tests/resources/merge-resolve/changed-in-master.txt1
-rw-r--r--tests/resources/merge-resolve/conflicting.txt1
-rw-r--r--tests/resources/merge-resolve/removed-in-branch.txt1
-rw-r--r--tests/resources/merge-resolve/unchanged.txt1
-rw-r--r--tests/resources/mergedrepo/.gitted/COMMIT_EDITMSG1
-rw-r--r--tests/resources/mergedrepo/.gitted/HEAD1
-rw-r--r--tests/resources/mergedrepo/.gitted/MERGE_HEAD1
-rw-r--r--tests/resources/mergedrepo/.gitted/MERGE_MODE0
-rw-r--r--tests/resources/mergedrepo/.gitted/MERGE_MSG5
-rw-r--r--tests/resources/mergedrepo/.gitted/ORIG_HEAD1
-rw-r--r--tests/resources/mergedrepo/.gitted/config6
-rw-r--r--tests/resources/mergedrepo/.gitted/description1
-rw-r--r--tests/resources/mergedrepo/.gitted/indexbin0 -> 842 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/info/exclude6
-rw-r--r--tests/resources/mergedrepo/.gitted/logs/HEAD5
-rw-r--r--tests/resources/mergedrepo/.gitted/logs/refs/heads/branch2
-rw-r--r--tests/resources/mergedrepo/.gitted/logs/refs/heads/master2
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/03/db1d37504ca0c4f7c26d7776b0e28bdea08712bin0 -> 141 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/17/0efc1023e0ed2390150bb4469c8456b63e8f91bin0 -> 141 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/1f/85ca51b8e0aac893a621b61a9c2661d6aa6d81bin0 -> 34 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/22/0bd62631c8cf7a83ef39c6b94595f00517211ebin0 -> 42 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/32/d55d59265db86dd690f0a7fc563db43e2bc6a6bin0 -> 159 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/38/e2d82b9065a237904af4b780b4d68da6950534bin0 -> 74 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/3a/34580a35add43a4cf361e8e9a30060a905c8762
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/44/58b8bc9e72b6c8755ae456f60e9844d0538d8cbin0 -> 39 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/47/8871385b9cd03908c5383acfd568bef023c6b3bin0 -> 36 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/51/6bd85f78061e09ccc714561d7b504672cb52dabin0 -> 36 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/53/c1d95a01f4514b162066fc98564500c96c46adbin0 -> 45 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/6a/ea5f295304c36144ad6e9247a291b7f8112399bin0 -> 49 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/70/68e30a7f0090ae32db35dfa1e4189d8780fcb8bin0 -> 85 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/75/938de1e367098b3e9a7b1ec3c4ac4548afffe4bin0 -> 41 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/7b/26923aaf452b1977eb08617c59475fb3f74b71bin0 -> 41 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/84/af62840be1b1c47b778a8a249f3ff45155038cbin0 -> 40 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/88/71f7a2ee3addfc4ba39fbd0783c8e738d04cdabin0 -> 66 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/88/7b153b165d32409c70163e0f734c090f12f673bin0 -> 38 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/8a/ad34cc83733590e74b93d0f7cf00375e2a735abin0 -> 78 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/8b/3f43d2402825c200f835ca1762413e386fd0b2bin0 -> 57 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/8b/72416545c7e761b64cecad4f1686eae4078aa8bin0 -> 38 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/8f/3c06cff9a83757cec40c80bc9bf31a2582bde9bin0 -> 39 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/8f/fcc405925511824a2240a6d3686aa7f8c7ac50bin0 -> 140 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/9a/05ccb4e0f948de03128e095f39dae6976751c51
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/9d/81f82fccc7dcd7de7a1ffead1815294c2e092cbin0 -> 36 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/b7/cedb8ad4cbb22b6363f9578cbd749797f7ef0dbin0 -> 66 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/d0/1885ea594926eae9ba5b54ad76692af5969f51bin0 -> 55 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/e2/809157a7766f272e4cfe26e61ef2678a5357ff3
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/e6/2cac5c88b9928f2695b934c70efa4285324478bin0 -> 87 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/f7/2784290c151092abf04ce6b875068547f70406bin0 -> 141 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/refs/heads/branch1
-rw-r--r--tests/resources/mergedrepo/.gitted/refs/heads/master1
-rw-r--r--tests/resources/mergedrepo/conflicts-one.txt5
-rw-r--r--tests/resources/mergedrepo/conflicts-two.txt5
-rw-r--r--tests/resources/mergedrepo/one.txt10
-rw-r--r--tests/resources/mergedrepo/two.txt12
-rw-r--r--tests/resources/partial-testrepo/.gitted/HEAD1
-rw-r--r--tests/resources/partial-testrepo/.gitted/config7
-rw-r--r--tests/resources/partial-testrepo/.gitted/indexbin0 -> 328 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08bin0 -> 19 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0ebin0 -> 163 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925bin0 -> 147 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7bin0 -> 51 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccdbin0 -> 119 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057bin0 -> 18 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd20452
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63bin0 -> 50 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf36442
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dccbin0 -> 50 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735bin0 -> 19 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60abin0 -> 119 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344dbin0 -> 82 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479bin0 -> 126 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a3
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f2
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bdbin0 -> 28 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6bin0 -> 26 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd3
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9bin0 -> 162 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83bin0 -> 147 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1bin0 -> 82 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92bin0 -> 24 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765bin0 -> 82 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/pack/.gitkeep0
-rw-r--r--tests/resources/partial-testrepo/.gitted/refs/heads/dir1
-rw-r--r--tests/resources/peeled.git/HEAD1
-rw-r--r--tests/resources/peeled.git/config8
-rw-r--r--tests/resources/peeled.git/objects/info/packs2
-rw-r--r--tests/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.idxbin0 -> 1156 bytes
-rw-r--r--tests/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.packbin0 -> 274 bytes
-rw-r--r--tests/resources/peeled.git/packed-refs6
-rw-r--r--tests/resources/peeled.git/refs/heads/master1
-rw-r--r--tests/resources/push.sh55
-rw-r--r--tests/resources/push_src/.gitted/COMMIT_EDITMSG1
-rw-r--r--tests/resources/push_src/.gitted/HEAD1
-rw-r--r--tests/resources/push_src/.gitted/ORIG_HEAD1
-rw-r--r--tests/resources/push_src/.gitted/config10
-rw-r--r--tests/resources/push_src/.gitted/description1
-rw-r--r--tests/resources/push_src/.gitted/indexbin0 -> 470 bytes
-rw-r--r--tests/resources/push_src/.gitted/info/exclude6
-rw-r--r--tests/resources/push_src/.gitted/logs/HEAD10
-rw-r--r--tests/resources/push_src/.gitted/logs/refs/heads/b11
-rw-r--r--tests/resources/push_src/.gitted/logs/refs/heads/b21
-rw-r--r--tests/resources/push_src/.gitted/logs/refs/heads/b32
-rw-r--r--tests/resources/push_src/.gitted/logs/refs/heads/b42
-rw-r--r--tests/resources/push_src/.gitted/logs/refs/heads/b52
-rw-r--r--tests/resources/push_src/.gitted/logs/refs/heads/master3
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/HEAD1
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/config15
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/description1
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/indexbin0 -> 256 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/info/exclude6
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/logs/HEAD1
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/logs/refs/heads/master1
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/logs/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/08/b041783f40edfe12bb406c9c9a8a040177c125bin0 -> 54 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08bin0 -> 19 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/18/1037049a54a1eb5fab404658a3a250b44335d7bin0 -> 51 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/18/10dff58d8a660512d4832e740f692884338ccdbin0 -> 119 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fdbin0 -> 122 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/1b/8cbad43e867676df601306689fe7c3def5e689bin0 -> 51 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9bbin0 -> 21 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10bin0 -> 168 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/27/0b8ea76056d5cad83af921837702d3e3c2924dbin0 -> 21 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7bin0 -> 44 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54bin0 -> 50 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022ccbin0 -> 23 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057bin0 -> 18 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd20452
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/4b/22b35d44b5a4f589edf3dc89196399771796eabin0 -> 44 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91bin0 -> 152 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/5b/5b025afb0b4c913b4c338a42934a3863bf36442
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/75/057dd4114e74cca1d750d0aee1647c903cb60abin0 -> 119 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af1
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/7b/4384978d2493e851f9cca7858815fac9b10980bin0 -> 145 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/81/4889a078c031f61ed08ab5fa863aea9314344dbin0 -> 82 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/84/96071c1b46c854b31185ea97743be6a8774479bin0 -> 126 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe2
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe181621
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4bin0 -> 50 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d22
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a3
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f2
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe45477503
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bdbin0 -> 28 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6bin0 -> 26 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/ae/90f12eea699729ed24555e40b9fd669da12a12bin0 -> 148 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d12
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593bin0 -> 80 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f6443
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd3
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659bin0 -> 149 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487fbin0 -> 21 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759bin0 -> 79 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391bin0 -> 15 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0bin0 -> 21 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3bin0 -> 103 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1bin0 -> 82 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/fa/49b077972391ad58037050f2a75f74e3671e92bin0 -> 24 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/fd/093bff70906175335656e6ce6ae05783708765bin0 -> 82 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09bin0 -> 152 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idxbin0 -> 46656 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.packbin0 -> 386089 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idxbin0 -> 1240 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.packbin0 -> 491 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idxbin0 -> 1240 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.packbin0 -> 498 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/packed-refs24
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/refs/heads/master1
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/push_src/.gitted/objects/08/585692ce06452da6f82ae66b90d98b55536fca1
-rw-r--r--tests/resources/push_src/.gitted/objects/27/b7ce66243eb1403862d05f958c002312df173d4
-rw-r--r--tests/resources/push_src/.gitted/objects/28/905c54ea45a4bed8d7b90f51bd8bd81eec8840bin0 -> 109 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/36/6226fb970ac0caa9d3f55967ab01334a548f60bin0 -> 20 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/36/f79b2846017d3761e0a02d0bccd573e0f90c572
-rw-r--r--tests/resources/push_src/.gitted/objects/5c/0bb3d1b9449d1cc69d7519fd05166f01840915bin0 -> 128 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/61/780798228d17af2d34fce4cfbdf35556832472bin0 -> 17 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/64/fd55f9b6390202db5e5666fd1fb339089fba4dbin0 -> 176 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/78/981922613b2afb6025042ff6bd878ac1994e85bin0 -> 17 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/80/5c54522e614f29f70d2413a0470247d8b424acbin0 -> 131 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/95/1bbbb90e2259a4c8950db78946784fb53fcbce2
-rw-r--r--tests/resources/push_src/.gitted/objects/a7/8705c3b2725f931d3ee05348d83cc26700f247bin0 -> 166 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/b4/83ae7ba66decee9aee971f501221dea84b14983
-rw-r--r--tests/resources/push_src/.gitted/objects/b4/e1f2b375a64c1ccd40c5ff6aa8bc96839ba4fdbin0 -> 148 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/c1/0409136a7a75e025fa502a1b2fd7b62b77d279bin0 -> 22 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/cd/881f90f2933db2e4cc26b8c71fe6037ac7fe4cbin0 -> 80 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/d9/b63a88223d8367516f50bd131a5f7349b7f3e42
-rw-r--r--tests/resources/push_src/.gitted/objects/dc/ab83249f6f9d1ed735d651352a80519339b591bin0 -> 80 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/ee/a4f2705eeec2db3813f2430829afce99cd00b5bin0 -> 141 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/f7/8a3106c85fb549c65198b2a2086276c6174928bin0 -> 65 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/f8/f7aefc2900a3d737cea9eee45729fd55761e1abin0 -> 50 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/fa/38b91f199934685819bea316186d8b008c52a22
-rw-r--r--tests/resources/push_src/.gitted/objects/ff/83aa4c5e5d28e3bcba2f5c6e2adc61286a4e5e4
-rw-r--r--tests/resources/push_src/.gitted/objects/ff/fe95c7fd0a37fa2ed702f8f93b56b2196b3925bin0 -> 109 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/pack/dummy0
-rw-r--r--tests/resources/push_src/.gitted/refs/heads/b11
-rw-r--r--tests/resources/push_src/.gitted/refs/heads/b21
-rw-r--r--tests/resources/push_src/.gitted/refs/heads/b31
-rw-r--r--tests/resources/push_src/.gitted/refs/heads/b41
-rw-r--r--tests/resources/push_src/.gitted/refs/heads/b51
-rw-r--r--tests/resources/push_src/.gitted/refs/heads/b61
-rw-r--r--tests/resources/push_src/.gitted/refs/tags/tag-blob1
-rw-r--r--tests/resources/push_src/.gitted/refs/tags/tag-commit1
-rw-r--r--tests/resources/push_src/.gitted/refs/tags/tag-commit-two1
-rw-r--r--tests/resources/push_src/.gitted/refs/tags/tag-lightweight1
-rw-r--r--tests/resources/push_src/.gitted/refs/tags/tag-tag1
-rw-r--r--tests/resources/push_src/.gitted/refs/tags/tag-tree1
-rw-r--r--tests/resources/push_src/a.txt2
-rw-r--r--tests/resources/push_src/fold/b.txt1
-rw-r--r--tests/resources/push_src/foldb.txt1
-rw-r--r--tests/resources/push_src/gitmodules3
-rw-r--r--tests/resources/push_src/submodule/.gitted1
-rw-r--r--tests/resources/push_src/submodule/README1
-rw-r--r--tests/resources/push_src/submodule/branch_file.txt2
-rw-r--r--tests/resources/push_src/submodule/new.txt1
-rw-r--r--tests/resources/renames/.gitted/HEAD1
-rw-r--r--tests/resources/renames/.gitted/config7
-rw-r--r--tests/resources/renames/.gitted/description1
-rw-r--r--tests/resources/renames/.gitted/indexbin0 -> 352 bytes
-rw-r--r--tests/resources/renames/.gitted/info/exclude6
-rw-r--r--tests/resources/renames/.gitted/logs/HEAD4
-rw-r--r--tests/resources/renames/.gitted/logs/refs/heads/master4
-rw-r--r--tests/resources/renames/.gitted/objects/03/da7ad872536bd448da8d88eb7165338bf923a7bin0 -> 90 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/17/58bdd7c16a72ff7c17d8de0c957ced3ccad6455
-rw-r--r--tests/resources/renames/.gitted/objects/19/dd32dfb1520a64e5bbaae8dce6ef423dfa2f131
-rw-r--r--tests/resources/renames/.gitted/objects/1c/068dee5790ef1580cfc4cd670915b48d790084bin0 -> 176 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/2b/c7f351d20b53f1c72c16c4b036e491c478c49abin0 -> 173 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/31/e47d8c1fa36d7f8d537b96158e3f024de0a9f2bin0 -> 131 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/35/92953ff3ea5e8ba700c429f3aefe33c8806754bin0 -> 80 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/36/020db6cdacaa93497f31edcd8f242ff9bc366dbin0 -> 431 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/3c/04741dd4b96c4ae4b00ec0f6e10c816a30aad2bin0 -> 159 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/42/10ffd5c390b21dd5483375e75288dea9ede512bin0 -> 1145 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/44/4a76ed3e45b183753f49376af30da8c3fe276abin0 -> 135 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/47/184c1e7eb22abcbed2bf4ee87d4e38096f7951bin0 -> 229 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/4e/4cae3e7dd56ed74bff39526d0469e554432953bin0 -> 452 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/50/e90273af7d826ff0a95865bcd3ba8412c447d93
-rw-r--r--tests/resources/renames/.gitted/objects/5e/26abc56a5a84d89790f45416648899cbe13109bin0 -> 163 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/61/8c6f2f8740bd6049b2fb9eb93fc15726462745bin0 -> 106 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/66/311f5cfbe7836c27510a3ba2f43e282e2c8bbabin0 -> 1155 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/93/f538c45a57a87eb4c1e86f91c6ee41d66c7ba7bin0 -> 229 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/9a/69d960ae94b060f56c2a8702545e2bb1abb935bin0 -> 464 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/ad/0a8e55a104ac54a8a29ed4b84b49e76837a113bin0 -> 415 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/b9/25b224cc91f897001a9993fbce169fdaa8858fbin0 -> 76 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/d7/9b202de198fa61b02424b9e25e840dc75e1323bin0 -> 421 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/ea/c43f5195a2cee53b7458d8dad16aedde10711bbin0 -> 118 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/ea/f4a3e3bfe68585e90cada20736ace491cd100b5
-rw-r--r--tests/resources/renames/.gitted/objects/f9/0d4fc20ecddf21eebe6a37e9225d244339d2b5bin0 -> 441 bytes
-rw-r--r--tests/resources/renames/.gitted/refs/heads/master1
-rw-r--r--tests/resources/renames/.gitted/refs/heads/renames_similar1
-rw-r--r--tests/resources/renames/.gitted/refs/heads/renames_similar_two1
-rw-r--r--tests/resources/renames/ikeepsix.txt27
-rw-r--r--tests/resources/renames/sixserving.txt25
-rw-r--r--tests/resources/renames/songof7cities.txt49
-rw-r--r--tests/resources/renames/untimely.txt24
-rw-r--r--tests/resources/shallow.git/HEAD1
-rw-r--r--tests/resources/shallow.git/config8
-rw-r--r--tests/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.idxbin0 -> 1324 bytes
-rw-r--r--tests/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.packbin0 -> 791 bytes
-rw-r--r--tests/resources/shallow.git/packed-refs2
-rw-r--r--tests/resources/shallow.git/refs/.gitkeep0
-rw-r--r--tests/resources/shallow.git/shallow1
-rw-r--r--tests/resources/short_tag.git/HEAD1
-rw-r--r--tests/resources/short_tag.git/config5
-rw-r--r--tests/resources/short_tag.git/indexbin0 -> 104 bytes
-rw-r--r--tests/resources/short_tag.git/objects/4a/5ed60bafcf4638b7c8356bd4ce1916bfede93cbin0 -> 169 bytes
-rw-r--r--tests/resources/short_tag.git/objects/4d/5fcadc293a348e88f777dc0920f11e7d71441cbin0 -> 48 bytes
-rw-r--r--tests/resources/short_tag.git/objects/5d/a7760512a953e3c7c4e47e4392c7a4338fb7291
-rw-r--r--tests/resources/short_tag.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391bin0 -> 15 bytes
-rw-r--r--tests/resources/short_tag.git/packed-refs1
-rw-r--r--tests/resources/short_tag.git/refs/heads/master1
-rw-r--r--tests/resources/status/.gitted/COMMIT_EDITMSG1
-rw-r--r--tests/resources/status/.gitted/HEAD1
-rw-r--r--tests/resources/status/.gitted/ORIG_HEAD1
-rw-r--r--tests/resources/status/.gitted/config6
-rw-r--r--tests/resources/status/.gitted/description1
-rw-r--r--tests/resources/status/.gitted/indexbin0 -> 1160 bytes
-rw-r--r--tests/resources/status/.gitted/info/exclude8
-rw-r--r--tests/resources/status/.gitted/logs/HEAD3
-rw-r--r--tests/resources/status/.gitted/logs/refs/heads/master3
-rw-r--r--tests/resources/status/.gitted/objects/00/17bd4ab1ec30440b17bae1680cff124ab5f1f62
-rw-r--r--tests/resources/status/.gitted/objects/06/1d42a44cacde5726057b67558821d95db96f19bin0 -> 44 bytes
-rw-r--r--tests/resources/status/.gitted/objects/18/88c805345ba265b0ee9449b8877b6064592058bin0 -> 36 bytes
-rw-r--r--tests/resources/status/.gitted/objects/19/d9cc8584ac2c7dcf57d2680375e80f099dc481bin0 -> 22 bytes
-rw-r--r--tests/resources/status/.gitted/objects/26/a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f2
-rw-r--r--tests/resources/status/.gitted/objects/32/504b727382542f9f089e24fddac5e78533e96cbin0 -> 31 bytes
-rw-r--r--tests/resources/status/.gitted/objects/37/fcb02ccc1a85d1941e7f106d52dc3702dcf0d0bin0 -> 331 bytes
-rw-r--r--tests/resources/status/.gitted/objects/45/2e4244b5d083ddf0460acf1ecc74db9dcfa11abin0 -> 30 bytes
-rw-r--r--tests/resources/status/.gitted/objects/52/9a16e8e762d4acb7b9636ff540a00831f9155abin0 -> 32 bytes
-rw-r--r--tests/resources/status/.gitted/objects/53/ace0d1cc1145a5f4fe4f78a186a60263190733bin0 -> 36 bytes
-rw-r--r--tests/resources/status/.gitted/objects/54/52d32f1dd538eb0405e8a83cc185f79e25e80fbin0 -> 29 bytes
-rw-r--r--tests/resources/status/.gitted/objects/55/d316c9ba708999f1918e9677d01dfcae69c6b9bin0 -> 33 bytes
-rw-r--r--tests/resources/status/.gitted/objects/70/bd9443ada07063e7fbf0b3ff5c13f7494d89c2bin0 -> 44 bytes
-rw-r--r--tests/resources/status/.gitted/objects/73/5b6a258cd196a8f7c9428419b02c1dca93fd75bin0 -> 160 bytes
-rw-r--r--tests/resources/status/.gitted/objects/75/6e27627e67bfbc048d01ece5819c6de733d7eabin0 -> 301 bytes
-rw-r--r--tests/resources/status/.gitted/objects/90/6ee7711f4f4928ddcb2a5f8fbc500deba0d2a8bin0 -> 46 bytes
-rw-r--r--tests/resources/status/.gitted/objects/90/b8c29d8ba39434d1c63e1b093daaa26e5bd972bin0 -> 41 bytes
-rw-r--r--tests/resources/status/.gitted/objects/9c/2e02cdffa8d73e6c189074594477a6baf87960bin0 -> 268 bytes
-rw-r--r--tests/resources/status/.gitted/objects/a0/de7e0ac200c489c41c59dfa910154a70264e6ebin0 -> 29 bytes
-rw-r--r--tests/resources/status/.gitted/objects/a6/191982709b746d5650e93c2acf34ef74e11504bin0 -> 37 bytes
-rw-r--r--tests/resources/status/.gitted/objects/a6/be623522ce87a1d862128ac42672604f7b468bbin0 -> 46 bytes
-rw-r--r--tests/resources/status/.gitted/objects/aa/27a641456848200fdb7f7c99ba36f8a0952877bin0 -> 120 bytes
-rw-r--r--tests/resources/status/.gitted/objects/da/bc8af9bd6e9f5bbe96a176f1a24baf3d1f8916bin0 -> 42 bytes
-rw-r--r--tests/resources/status/.gitted/objects/e8/ee89e15bbe9b20137715232387b3de5b28972ebin0 -> 38 bytes
-rw-r--r--tests/resources/status/.gitted/objects/e9/b9107f290627c04d097733a10055af941f6bcabin0 -> 37 bytes
-rw-r--r--tests/resources/status/.gitted/objects/ed/062903b8f6f3dccb2fa81117ba6590944ef9bdbin0 -> 42 bytes
-rw-r--r--tests/resources/status/.gitted/refs/heads/master1
-rw-r--r--tests/resources/status/current_file1
-rw-r--r--tests/resources/status/ignored_file1
-rw-r--r--tests/resources/status/modified_file2
-rw-r--r--tests/resources/status/new_file1
-rw-r--r--tests/resources/status/staged_changes2
-rw-r--r--tests/resources/status/staged_changes_modified_file3
-rw-r--r--tests/resources/status/staged_delete_modified_file1
-rw-r--r--tests/resources/status/staged_new_file1
-rw-r--r--tests/resources/status/staged_new_file_modified_file2
-rw-r--r--tests/resources/status/subdir.txt2
-rw-r--r--tests/resources/status/subdir/current_file1
-rw-r--r--tests/resources/status/subdir/modified_file2
-rw-r--r--tests/resources/status/subdir/new_file1
-rw-r--r--tests/resources/status/è¿™1
-rw-r--r--tests/resources/submod2/.gitted/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/config20
-rw-r--r--tests/resources/submod2/.gitted/description1
-rw-r--r--tests/resources/submod2/.gitted/indexbin0 -> 944 bytes
-rw-r--r--tests/resources/submod2/.gitted/info/exclude6
-rw-r--r--tests/resources/submod2/.gitted/logs/HEAD4
-rw-r--r--tests/resources/submod2/.gitted/logs/refs/heads/master4
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/config13
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/description1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/info/exclude6
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/heads/master1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/packed-refs2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/heads/master1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/config13
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/description1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/info/exclude6
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/logs/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/heads/master1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/packed-refs2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/refs/heads/master1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/COMMIT_EDITMSG1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/config13
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/description1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/info/exclude6
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/logs/HEAD2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/heads/master2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/3d/9386c507f6b093471a3e324085657a3c2b42473
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/77/fb0ed3e58568d6ad362c78de08ab8649d76e29bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/8e/b1e637ed9fc8e5454fa20d38f809091f9395f42
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/packed-refs2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/refs/heads/master1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/config13
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/description1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/info/exclude6
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/logs/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/heads/master1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/a0/2d31770687965547ab7a04cee199b29ee458d6bin0 -> 134 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/packed-refs2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/refs/heads/master1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/config13
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/description1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/info/exclude6
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/heads/master1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/packed-refs2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/heads/master1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/config13
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/description1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/info/exclude6
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/heads/master1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/packed-refs2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/refs/heads/master1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/config13
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/description1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/info/exclude6
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/logs/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/heads/master1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/packed-refs2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/refs/heads/master1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/objects/09/460e5b6cbcb05a3e404593c32a3aa7221eca0ebin0 -> 197 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/14/fe9ccf104058df25e0a08361c4494e167ef2431
-rw-r--r--tests/resources/submod2/.gitted/objects/22/ce3e0311dda73a5992d54a4a595518d3876ea74
-rw-r--r--tests/resources/submod2/.gitted/objects/25/5546424b0efb847b1bfc91dbf7348b277f8970bin0 -> 157 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/2a/30f1e6f94b20917005a21273f65b406d0f8badbin0 -> 144 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/42/cfb95cd01bf9225b659b5ee3edcc78e8eeb478bin0 -> 40 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/57/958699c2dc394f81cfc76950e9c3ac3025c398bin0 -> 136 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/59/01da4f1c67756eeadc5121d206bec2431f253b2
-rw-r--r--tests/resources/submod2/.gitted/objects/60/7d96653d4d0a4f733107f7890c2e67b55b620dbin0 -> 53 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/74/84482eb8db738cafa696993664607500a3f2b9bin0 -> 173 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/7b/a4c5c3561daa5ab1a86215cfb0587e96d404d6bin0 -> 48 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/87/3585b94bdeabccea991ea5e3ec1a277895b698bin0 -> 137 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/97/4cf7c73de336b0c4e019f918f3cee367d72e842
-rw-r--r--tests/resources/submod2/.gitted/objects/9d/bc299bc013ea253583b40bf327b5a6e4037b89bin0 -> 80 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/a9/104bf89e911387244ef499413960ba472066d9bin0 -> 165 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/b6/14088620bbdc1d29549d223ceba0f4419fd4cbbin0 -> 110 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/d4/07f19e50c1da1ff584beafe0d6dac7237c5d06bin0 -> 55 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/d9/3e95571d92cceb5de28c205f1d5f3cc8b88bc82
-rw-r--r--tests/resources/submod2/.gitted/objects/e3/b83bf274ee065eee48734cf8c6dfaf5e81471cbin0 -> 246 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/f5/4414c25e6d24fe39f5c3f128d7c8a17bc238332
-rw-r--r--tests/resources/submod2/.gitted/objects/f9/90a25a74d1a8281ce2ab018ea8df66795cd60b1
-rw-r--r--tests/resources/submod2/.gitted/refs/heads/master1
-rw-r--r--tests/resources/submod2/README.txt3
-rw-r--r--tests/resources/submod2/gitmodules24
-rw-r--r--tests/resources/submod2/just_a_dir/contents1
-rw-r--r--tests/resources/submod2/just_a_file1
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/HEAD1
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/config6
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/description1
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/indexbin0 -> 112 bytes
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/info/exclude6
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/logs/HEAD1
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/logs/refs/heads/master1
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/objects/68/e92c611b80ee1ed8f38314ff9577f0d15b2444bin0 -> 132 bytes
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/objects/71/ff9927d7c8a5639e062c38a7d35c433c424627bin0 -> 52 bytes
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/objects/f0/1d56b18efd353ef2bb93a4585d590a0847195ebin0 -> 55 bytes
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/refs/heads/master1
-rw-r--r--tests/resources/submod2/not-submodule/README.txt1
-rw-r--r--tests/resources/submod2/not/.gitted/notempty1
-rw-r--r--tests/resources/submod2/not/README.txt1
-rw-r--r--tests/resources/submod2/sm_added_and_uncommited/.gitted1
-rw-r--r--tests/resources/submod2/sm_added_and_uncommited/README.txt3
-rw-r--r--tests/resources/submod2/sm_added_and_uncommited/file_to_modify3
-rw-r--r--tests/resources/submod2/sm_changed_file/.gitted1
-rw-r--r--tests/resources/submod2/sm_changed_file/README.txt3
-rw-r--r--tests/resources/submod2/sm_changed_file/file_to_modify4
-rw-r--r--tests/resources/submod2/sm_changed_head/.gitted1
-rw-r--r--tests/resources/submod2/sm_changed_head/README.txt3
-rw-r--r--tests/resources/submod2/sm_changed_head/file_to_modify4
-rw-r--r--tests/resources/submod2/sm_changed_index/.gitted1
-rw-r--r--tests/resources/submod2/sm_changed_index/README.txt3
-rw-r--r--tests/resources/submod2/sm_changed_index/file_to_modify4
-rw-r--r--tests/resources/submod2/sm_changed_untracked_file/.gitted1
-rw-r--r--tests/resources/submod2/sm_changed_untracked_file/README.txt3
-rw-r--r--tests/resources/submod2/sm_changed_untracked_file/file_to_modify3
-rw-r--r--tests/resources/submod2/sm_changed_untracked_file/i_am_untracked1
-rw-r--r--tests/resources/submod2/sm_missing_commits/.gitted1
-rw-r--r--tests/resources/submod2/sm_missing_commits/README.txt3
-rw-r--r--tests/resources/submod2/sm_missing_commits/file_to_modify3
-rw-r--r--tests/resources/submod2/sm_unchanged/.gitted1
-rw-r--r--tests/resources/submod2/sm_unchanged/README.txt3
-rw-r--r--tests/resources/submod2/sm_unchanged/file_to_modify3
-rw-r--r--tests/resources/submod2_target/.gitted/HEAD1
-rw-r--r--tests/resources/submod2_target/.gitted/config6
-rw-r--r--tests/resources/submod2_target/.gitted/description1
-rw-r--r--tests/resources/submod2_target/.gitted/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/submod2_target/.gitted/info/exclude6
-rw-r--r--tests/resources/submod2_target/.gitted/logs/HEAD4
-rw-r--r--tests/resources/submod2_target/.gitted/logs/refs/heads/master4
-rw-r--r--tests/resources/submod2_target/.gitted/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/submod2_target/.gitted/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/submod2_target/.gitted/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2_target/.gitted/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/submod2_target/.gitted/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests/resources/submod2_target/.gitted/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/submod2_target/.gitted/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/submod2_target/.gitted/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests/resources/submod2_target/.gitted/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/submod2_target/.gitted/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests/resources/submod2_target/.gitted/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/submod2_target/.gitted/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2_target/.gitted/refs/heads/master1
-rw-r--r--tests/resources/submod2_target/README.txt3
-rw-r--r--tests/resources/submod2_target/file_to_modify3
-rw-r--r--tests/resources/submodules/.gitted/HEAD1
-rw-r--r--tests/resources/submodules/.gitted/config6
-rw-r--r--tests/resources/submodules/.gitted/description1
-rw-r--r--tests/resources/submodules/.gitted/indexbin0 -> 408 bytes
-rw-r--r--tests/resources/submodules/.gitted/info/exclude8
-rw-r--r--tests/resources/submodules/.gitted/info/refs1
-rw-r--r--tests/resources/submodules/.gitted/logs/HEAD2
-rw-r--r--tests/resources/submodules/.gitted/logs/refs/heads/master2
-rw-r--r--tests/resources/submodules/.gitted/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e2
-rw-r--r--tests/resources/submodules/.gitted/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357bin0 -> 97 bytes
-rw-r--r--tests/resources/submodules/.gitted/objects/97/896810b3210244a62a82458b8e0819ecfc68503
-rw-r--r--tests/resources/submodules/.gitted/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888bin0 -> 138 bytes
-rw-r--r--tests/resources/submodules/.gitted/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818bin0 -> 21 bytes
-rw-r--r--tests/resources/submodules/.gitted/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4aebin0 -> 120 bytes
-rw-r--r--tests/resources/submodules/.gitted/objects/info/packs2
-rw-r--r--tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idxbin0 -> 1156 bytes
-rw-r--r--tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.packbin0 -> 228 bytes
-rw-r--r--tests/resources/submodules/.gitted/packed-refs2
-rw-r--r--tests/resources/submodules/.gitted/refs/heads/master1
-rw-r--r--tests/resources/submodules/added1
-rw-r--r--tests/resources/submodules/gitmodules6
-rw-r--r--tests/resources/submodules/ignored1
-rw-r--r--tests/resources/submodules/modified2
-rw-r--r--tests/resources/submodules/testrepo/.gitted/HEAD1
-rw-r--r--tests/resources/submodules/testrepo/.gitted/config12
-rw-r--r--tests/resources/submodules/testrepo/.gitted/description1
-rw-r--r--tests/resources/submodules/testrepo/.gitted/indexbin0 -> 256 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/info/exclude6
-rw-r--r--tests/resources/submodules/testrepo/.gitted/logs/HEAD1
-rw-r--r--tests/resources/submodules/testrepo/.gitted/logs/refs/heads/master1
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08bin0 -> 19 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7bin0 -> 51 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccdbin0 -> 119 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9bbin0 -> 21 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924dbin0 -> 21 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54bin0 -> 50 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022ccbin0 -> 23 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057bin0 -> 18 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd20452
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf36442
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60abin0 -> 119 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af1
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980bin0 -> 145 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344dbin0 -> 82 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479bin0 -> 126 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe181621
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4bin0 -> 50 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a3
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f2
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe45477503
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bdbin0 -> 28 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6bin0 -> 26 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12bin0 -> 148 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d12
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593bin0 -> 80 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f6443
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd3
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487fbin0 -> 21 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391bin0 -> 15 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0bin0 -> 21 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3bin0 -> 103 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1bin0 -> 82 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92bin0 -> 24 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765bin0 -> 82 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idxbin0 -> 46656 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.packbin0 -> 386089 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idxbin0 -> 1240 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.packbin0 -> 491 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idxbin0 -> 1240 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.packbin0 -> 498 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/packed-refs12
-rw-r--r--tests/resources/submodules/testrepo/.gitted/refs/heads/master1
-rw-r--r--tests/resources/submodules/testrepo/.gitted/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submodules/testrepo/README1
-rw-r--r--tests/resources/submodules/testrepo/branch_file.txt2
-rw-r--r--tests/resources/submodules/testrepo/new.txt1
-rw-r--r--tests/resources/submodules/unmodified1
-rw-r--r--tests/resources/submodules/untracked1
-rw-r--r--tests/resources/template/branches/.gitignore2
-rw-r--r--tests/resources/template/description1
l---------tests/resources/template/hooks/link.sample1
-rwxr-xr-xtests/resources/template/hooks/update.sample9
-rw-r--r--tests/resources/template/info/exclude6
-rw-r--r--tests/resources/testrepo.git/FETCH_HEAD2
-rw-r--r--tests/resources/testrepo.git/HEAD1
-rw-r--r--tests/resources/testrepo.git/HEAD_TRACKER1
-rw-r--r--tests/resources/testrepo.git/config40
-rw-r--r--tests/resources/testrepo.git/indexbin0 -> 10041 bytes
-rw-r--r--tests/resources/testrepo.git/logs/HEAD7
-rw-r--r--tests/resources/testrepo.git/logs/refs/heads/br22
-rw-r--r--tests/resources/testrepo.git/logs/refs/heads/master2
-rw-r--r--tests/resources/testrepo.git/logs/refs/heads/not-good1
-rw-r--r--tests/resources/testrepo.git/logs/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/testrepo.git/logs/refs/remotes/test/master2
-rw-r--r--tests/resources/testrepo.git/objects/08/b041783f40edfe12bb406c9c9a8a040177c125bin0 -> 54 bytes
-rw-r--r--tests/resources/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08bin0 -> 19 bytes
-rw-r--r--tests/resources/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7bin0 -> 51 bytes
-rw-r--r--tests/resources/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccdbin0 -> 119 bytes
-rw-r--r--tests/resources/testrepo.git/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fdbin0 -> 122 bytes
-rw-r--r--tests/resources/testrepo.git/objects/1b/8cbad43e867676df601306689fe7c3def5e689bin0 -> 51 bytes
-rw-r--r--tests/resources/testrepo.git/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9bbin0 -> 21 bytes
-rw-r--r--tests/resources/testrepo.git/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10bin0 -> 168 bytes
-rw-r--r--tests/resources/testrepo.git/objects/27/0b8ea76056d5cad83af921837702d3e3c2924dbin0 -> 21 bytes
-rw-r--r--tests/resources/testrepo.git/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7bin0 -> 44 bytes
-rw-r--r--tests/resources/testrepo.git/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54bin0 -> 50 bytes
-rw-r--r--tests/resources/testrepo.git/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022ccbin0 -> 23 bytes
-rw-r--r--tests/resources/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057bin0 -> 18 bytes
-rw-r--r--tests/resources/testrepo.git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd20452
-rw-r--r--tests/resources/testrepo.git/objects/4a/23e2e65ad4e31c4c9db7dc746650bfad082679bin0 -> 83 bytes
-rw-r--r--tests/resources/testrepo.git/objects/4b/22b35d44b5a4f589edf3dc89196399771796eabin0 -> 44 bytes
-rw-r--r--tests/resources/testrepo.git/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91bin0 -> 152 bytes
-rw-r--r--tests/resources/testrepo.git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf36442
-rw-r--r--tests/resources/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60abin0 -> 119 bytes
-rw-r--r--tests/resources/testrepo.git/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af1
-rw-r--r--tests/resources/testrepo.git/objects/7b/4384978d2493e851f9cca7858815fac9b10980bin0 -> 145 bytes
-rw-r--r--tests/resources/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344dbin0 -> 82 bytes
-rw-r--r--tests/resources/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479bin0 -> 126 bytes
-rw-r--r--tests/resources/testrepo.git/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe2
-rw-r--r--tests/resources/testrepo.git/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe181621
-rw-r--r--tests/resources/testrepo.git/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4bin0 -> 50 bytes
-rw-r--r--tests/resources/testrepo.git/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d22
-rw-r--r--tests/resources/testrepo.git/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a3
-rw-r--r--tests/resources/testrepo.git/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f2
-rw-r--r--tests/resources/testrepo.git/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe45477503
-rw-r--r--tests/resources/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bdbin0 -> 28 bytes
-rw-r--r--tests/resources/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6bin0 -> 26 bytes
-rw-r--r--tests/resources/testrepo.git/objects/ae/90f12eea699729ed24555e40b9fd669da12a12bin0 -> 148 bytes
-rw-r--r--tests/resources/testrepo.git/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d12
-rw-r--r--tests/resources/testrepo.git/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593bin0 -> 80 bytes
-rw-r--r--tests/resources/testrepo.git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f6443
-rw-r--r--tests/resources/testrepo.git/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd3
-rw-r--r--tests/resources/testrepo.git/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659bin0 -> 149 bytes
-rw-r--r--tests/resources/testrepo.git/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487fbin0 -> 21 bytes
-rw-r--r--tests/resources/testrepo.git/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759bin0 -> 79 bytes
-rw-r--r--tests/resources/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391bin0 -> 15 bytes
-rw-r--r--tests/resources/testrepo.git/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0bin0 -> 21 bytes
-rw-r--r--tests/resources/testrepo.git/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3bin0 -> 103 bytes
-rw-r--r--tests/resources/testrepo.git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1bin0 -> 82 bytes
-rw-r--r--tests/resources/testrepo.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92bin0 -> 24 bytes
-rw-r--r--tests/resources/testrepo.git/objects/fd/093bff70906175335656e6ce6ae05783708765bin0 -> 82 bytes
-rw-r--r--tests/resources/testrepo.git/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09bin0 -> 152 bytes
-rw-r--r--tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idxbin0 -> 46656 bytes
-rw-r--r--tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.packbin0 -> 386089 bytes
-rw-r--r--tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idxbin0 -> 1240 bytes
-rw-r--r--tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.packbin0 -> 491 bytes
-rw-r--r--tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idxbin0 -> 1240 bytes
-rw-r--r--tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.packbin0 -> 498 bytes
-rw-r--r--tests/resources/testrepo.git/packed-refs3
-rw-r--r--tests/resources/testrepo.git/refs/heads/br21
-rw-r--r--tests/resources/testrepo.git/refs/heads/cannot-fetch1
-rw-r--r--tests/resources/testrepo.git/refs/heads/chomped1
-rw-r--r--tests/resources/testrepo.git/refs/heads/haacked1
-rw-r--r--tests/resources/testrepo.git/refs/heads/master1
-rw-r--r--tests/resources/testrepo.git/refs/heads/not-good1
-rw-r--r--tests/resources/testrepo.git/refs/heads/packed-test1
-rw-r--r--tests/resources/testrepo.git/refs/heads/subtrees1
-rw-r--r--tests/resources/testrepo.git/refs/heads/test1
-rw-r--r--tests/resources/testrepo.git/refs/heads/track-local1
-rw-r--r--tests/resources/testrepo.git/refs/heads/trailing1
-rw-r--r--tests/resources/testrepo.git/refs/notes/fanout1
-rw-r--r--tests/resources/testrepo.git/refs/remotes/test/master1
-rw-r--r--tests/resources/testrepo.git/refs/tags/annotated_tag_to_blob1
-rw-r--r--tests/resources/testrepo.git/refs/tags/e90810b1
-rw-r--r--tests/resources/testrepo.git/refs/tags/hard_tag1
-rw-r--r--tests/resources/testrepo.git/refs/tags/point_to_blob1
-rw-r--r--tests/resources/testrepo.git/refs/tags/taggerless1
-rw-r--r--tests/resources/testrepo.git/refs/tags/test1
-rw-r--r--tests/resources/testrepo.git/refs/tags/wrapped_tag1
-rw-r--r--tests/resources/testrepo/.gitted/HEAD1
-rw-r--r--tests/resources/testrepo/.gitted/HEAD_TRACKER1
-rw-r--r--tests/resources/testrepo/.gitted/config8
-rw-r--r--tests/resources/testrepo/.gitted/indexbin0 -> 10041 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/09/9fabac3a9ea935598528c27f866e34089c2eff1
-rw-r--r--tests/resources/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08bin0 -> 19 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0ebin0 -> 163 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925bin0 -> 147 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7bin0 -> 51 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccdbin0 -> 119 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9bbin0 -> 21 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924dbin0 -> 21 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54bin0 -> 50 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022ccbin0 -> 23 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057bin0 -> 18 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/45/dd856fdd4d89b884c340ba0e047752d9b085d6bin0 -> 156 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd20452
-rw-r--r--tests/resources/testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63bin0 -> 50 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf36442
-rw-r--r--tests/resources/testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dccbin0 -> 50 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735bin0 -> 19 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/6b/377958d8c6a4906e8573b53672a1a23a4e8ce6bin0 -> 167 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/6b/9b767af9992b4abad5e24ffb1ba2d688ca602ebin0 -> 41 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60abin0 -> 119 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af1
-rw-r--r--tests/resources/testrepo/.gitted/objects/7b/2417a23b63e1fdde88c80e14b33247c6e5785abin0 -> 187 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980bin0 -> 145 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344dbin0 -> 82 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479bin0 -> 126 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/87/380ae84009e9c503506c2f6143a4fc6c60bf80bin0 -> 161 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe181621
-rw-r--r--tests/resources/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4bin0 -> 50 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a3
-rw-r--r--tests/resources/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f2
-rw-r--r--tests/resources/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe45477503
-rw-r--r--tests/resources/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bdbin0 -> 28 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6bin0 -> 26 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12bin0 -> 148 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d12
-rw-r--r--tests/resources/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593bin0 -> 80 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f6443
-rw-r--r--tests/resources/testrepo/.gitted/objects/c0/528fd6cc988c0a40ce0be11bc192fc8dc5346ebin0 -> 22 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd3
-rw-r--r--tests/resources/testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9bin0 -> 162 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83bin0 -> 147 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487fbin0 -> 21 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391bin0 -> 15 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0bin0 -> 21 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3bin0 -> 103 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1bin0 -> 82 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92bin0 -> 24 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765bin0 -> 82 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idxbin0 -> 46656 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.packbin0 -> 386089 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idxbin0 -> 1240 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.packbin0 -> 491 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idxbin0 -> 1240 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.packbin0 -> 498 bytes
-rw-r--r--tests/resources/testrepo/.gitted/packed-refs4
-rw-r--r--tests/resources/testrepo/.gitted/refs/heads/br21
-rw-r--r--tests/resources/testrepo/.gitted/refs/heads/dir1
-rw-r--r--tests/resources/testrepo/.gitted/refs/heads/long-file-name1
-rw-r--r--tests/resources/testrepo/.gitted/refs/heads/master1
-rw-r--r--tests/resources/testrepo/.gitted/refs/heads/packed-test1
-rw-r--r--tests/resources/testrepo/.gitted/refs/heads/subtrees1
-rw-r--r--tests/resources/testrepo/.gitted/refs/heads/test1
-rw-r--r--tests/resources/testrepo/.gitted/refs/tags/e90810b1
-rw-r--r--tests/resources/testrepo/.gitted/refs/tags/foo/bar1
-rw-r--r--tests/resources/testrepo/.gitted/refs/tags/foo/foo/bar1
-rw-r--r--tests/resources/testrepo/.gitted/refs/tags/point_to_blob1
-rw-r--r--tests/resources/testrepo/.gitted/refs/tags/test1
-rw-r--r--tests/resources/testrepo2/.gitted/HEAD1
-rw-r--r--tests/resources/testrepo2/.gitted/config14
-rw-r--r--tests/resources/testrepo2/.gitted/description1
-rw-r--r--tests/resources/testrepo2/.gitted/indexbin0 -> 512 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/info/exclude6
-rw-r--r--tests/resources/testrepo2/.gitted/logs/HEAD1
-rw-r--r--tests/resources/testrepo2/.gitted/logs/refs/heads/master1
-rw-r--r--tests/resources/testrepo2/.gitted/logs/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/testrepo2/.gitted/objects/0c/37a5391bbff43c37f0d0371823a5509eed5b1dbin0 -> 134 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08bin0 -> 19 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7bin0 -> 51 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccdbin0 -> 119 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/2d/2eff63372b08adf0a9eb84109ccf7d19e2f3a2bin0 -> 125 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/36/060c58702ed4c2a40832c51758d5344201d89a2
-rw-r--r--tests/resources/testrepo2/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057bin0 -> 18 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd20452
-rw-r--r--tests/resources/testrepo2/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf36442
-rw-r--r--tests/resources/testrepo2/.gitted/objects/61/9f9935957e010c419cb9d15621916ddfcc0b96bin0 -> 116 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60abin0 -> 119 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/7f/043268ea43ce18e3540acaabf9e090c91965b0bin0 -> 55 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344dbin0 -> 82 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479bin0 -> 126 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a3
-rw-r--r--tests/resources/testrepo2/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f2
-rw-r--r--tests/resources/testrepo2/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bdbin0 -> 28 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6bin0 -> 26 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f6443
-rw-r--r--tests/resources/testrepo2/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd3
-rw-r--r--tests/resources/testrepo2/.gitted/objects/c4/dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90bbin0 -> 116 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391bin0 -> 15 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1bin0 -> 82 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92bin0 -> 24 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765bin0 -> 82 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idxbin0 -> 1240 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.packbin0 -> 491 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/packed-refs6
-rw-r--r--tests/resources/testrepo2/.gitted/refs/heads/master1
-rw-r--r--tests/resources/testrepo2/.gitted/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/testrepo2/README1
-rw-r--r--tests/resources/testrepo2/new.txt1
-rw-r--r--tests/resources/testrepo2/subdir/README1
-rw-r--r--tests/resources/testrepo2/subdir/new.txt1
-rw-r--r--tests/resources/testrepo2/subdir/subdir2/README1
-rw-r--r--tests/resources/testrepo2/subdir/subdir2/new.txt1
-rw-r--r--tests/resources/twowaymerge.git/HEAD1
-rw-r--r--tests/resources/twowaymerge.git/config5
-rw-r--r--tests/resources/twowaymerge.git/description1
-rw-r--r--tests/resources/twowaymerge.git/info/exclude6
-rw-r--r--tests/resources/twowaymerge.git/objects/0c/8a3f1f3d5f421cf83048c7c73ee3b55a5e0f29bin0 -> 157 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/10/2dce8e3081f398e4bdd9fd894dc85ac3ca6a67bin0 -> 54 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/17/7d8634a28e26ec7819284752757ebe01a479d5bin0 -> 80 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/1c/30b88f5f3ee66d78df6520a7de9e89b890818b3
-rw-r--r--tests/resources/twowaymerge.git/objects/1f/4c0311a24b63f6fc209a59a1e404942d4a50062
-rw-r--r--tests/resources/twowaymerge.git/objects/22/24e191514cb4bd8c566d80dac22dfcb1e9bb833
-rw-r--r--tests/resources/twowaymerge.git/objects/29/6e56023cdc034d2735fee8c0d85a659d1b07f4bin0 -> 51 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/31/51880ae2b363f1c262cf98b750c1f169a0d432bin0 -> 68 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/3b/287f8730c81d0b763c2d294618a5e32b67b4f8bin0 -> 54 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/42/b7311aa626e712891940c1ec5d5cba201946a43
-rw-r--r--tests/resources/twowaymerge.git/objects/49/6d6428b9cf92981dc9495211e6e1120fb6f2babin0 -> 46 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/59/b0cf7d74659e1cdb13305319d6d4ce2733c118bin0 -> 65 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/6a/b5d28acbf3c3bdff276f7ccfdf29c1520e542f1
-rw-r--r--tests/resources/twowaymerge.git/objects/6c/fca542b55b8b37017e6125a4b8f59a6eae6f11bin0 -> 68 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/76/5b32c65d38f04c4f287abda055818ec0f26912bin0 -> 54 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/7b/8c336c45fc6895c1c60827260fe5d798e5d2473
-rw-r--r--tests/resources/twowaymerge.git/objects/82/bf9a1a10a4b25c1f14c9607b60970705e925451
-rw-r--r--tests/resources/twowaymerge.git/objects/8b/82fb1794cb1c8c7f172ec730a4c2db0ae3e6503
-rw-r--r--tests/resources/twowaymerge.git/objects/9a/40a2f11c191f180c47e54b11567cb3c1e89b30bin0 -> 62 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/9b/219343610c88a1187c996d0dc58330b55cee282
-rw-r--r--tests/resources/twowaymerge.git/objects/9f/e06a50f4d1634d6c6879854d01d80857388706bin0 -> 65 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/a4/1a49f8f5cd9b6cb14a076bf8394881ed0b4d193
-rw-r--r--tests/resources/twowaymerge.git/objects/a9/53a018c5b10b20c86e69fef55ebc8ad4c5a4171
-rw-r--r--tests/resources/twowaymerge.git/objects/a9/cce3cd1b3efbda5b1f4a6dcc3f1570b2d3d74c1
-rw-r--r--tests/resources/twowaymerge.git/objects/bd/1732c43c68d712ad09e1d872b9be6d4b9efdc4bin0 -> 158 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/c3/7a783c20d92ac92362a78a32860f7eebf938efbin0 -> 158 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/cb/dd40facab1682754eb67f7a43f29e672903cf6bin0 -> 51 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/cd/f97fd3bb48eb3827638bb33d208f5fd32d0aa6bin0 -> 158 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/d6/f10d549cb335b9e6d38afc1f0088be69b50494bin0 -> 62 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/d9/acdc7ae7632adfeec67fa73c1e343cf4d1f47e1
-rw-r--r--tests/resources/twowaymerge.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391bin0 -> 15 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/ef/0488f0b722f0be8bcb90a7730ac7efafd1d6941
-rw-r--r--tests/resources/twowaymerge.git/objects/fc/f7e3f51c11d199ab7a78403ee4f9ccd028da25bin0 -> 62 bytes
-rw-r--r--tests/resources/twowaymerge.git/refs/heads/first-branch1
-rw-r--r--tests/resources/twowaymerge.git/refs/heads/master1
-rw-r--r--tests/resources/twowaymerge.git/refs/heads/second-branch1
-rw-r--r--tests/resources/typechanges/.gitted/HEAD1
-rw-r--r--tests/resources/typechanges/.gitted/config12
-rw-r--r--tests/resources/typechanges/.gitted/description1
-rw-r--r--tests/resources/typechanges/.gitted/indexbin0 -> 184 bytes
-rw-r--r--tests/resources/typechanges/.gitted/info/exclude6
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/HEAD1
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/config13
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/description1
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/info/exclude6
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/packed-refs2
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/refs/heads/master1
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/HEAD1
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/config13
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/description1
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/info/exclude6
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/packed-refs2
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/refs/heads/master1
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/HEAD1
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/config13
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/description1
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/info/exclude6
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/packed-refs2
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/refs/heads/master1
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/typechanges/.gitted/objects/0d/78578795b7ca49fd8df6c4b6d27c5c02d991d8bin0 -> 76 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/0e/7ed140b514b8cae23254cb8656fe1674403affbin0 -> 162 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/0f/f461da9689266f482d8f6654a4400b4e33c586bin0 -> 486 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/18/aa7e45bbe4c3cc24a0b079696c59d36675af97bin0 -> 89 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/1b/63caae4a5ca96f78e8dfefc376c6a39a142475bin0 -> 161 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/1e/abe82aa3b2365a394f6108f24435df6e193d02bin0 -> 549 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/42/061c01a1c70097d1e4579f29a5adf40abdec95bin0 -> 24 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/46/2838cee476a87e7cff32196b66fa18ed756592bin0 -> 76 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/63/499e4ea8e096b831515ceb1d5a7593e4d87ae5bin0 -> 18 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/68/1af94e10eaf262f3ab7cb9b8fd5f4158ba4d3ebin0 -> 24 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/6a/9008602b811e69a9b7a2d83496f39a794fdeebbin0 -> 602 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/6e/ae26c90e8ccc4d16208972119c40635489c6f0bin0 -> 160 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/6f/39eabbb8a7541515e0d35971078bccb502e7e0bin0 -> 66 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/71/54d3083461536dfc71ad5542f3e65e723a06c4bin0 -> 657 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/75/56c1d893a4c0ca85ac8ac51de47ff399758729bin0 -> 226 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/76/fef844064c26d5e06c2508240dae661e7231b2bin0 -> 66 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/79/b9f23e85f55ea36a472a902e875bc1121a94cb2
-rw-r--r--tests/resources/typechanges/.gitted/objects/85/28da0ea65eacf1f74f9ed6696adbac547963adbin0 -> 451 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/8b/3726b365824ad5a07c537247f4bc73ed7d37eabin0 -> 76 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/93/3e28c1c8a68838a763d250bdf0b2c6068289c3bin0 -> 226 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/96/2710fe5b4e453e9e827945b3487c525968ec4abin0 -> 76 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/96/6cf1b3598e195b31b2cde3784f9a19f0728a6fbin0 -> 226 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/99/e8bab9ece009f0fba7eb41f850f4c12bedb9b7bin0 -> 701 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/9b/19edf33a03a0c59cdfc113bfa5c06179bf9b1a5
-rw-r--r--tests/resources/typechanges/.gitted/objects/9b/db75b73836a99e3dbeea640a81de81031fdc29bin0 -> 162 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/9d/0235c7a7edc0889a18f97a42ee6db9fe688447bin0 -> 160 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/9e/ffc457877f109b2a4319e14bee613a15f2a00dbin0 -> 226 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/a0/a9bad6f6f40325198f938a0e3ae981622d7707bin0 -> 54 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/b1/977dc4e573b812d4619754c98138c56999dc0dbin0 -> 518 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/d7/5992dd02391e128dac332dcc78d649dd9ab095bin0 -> 577 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/da/e2709d638df52212b1f43ff61797ebfedfcc7cbin0 -> 78 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/e1/152adcb9adf37ec551ada9ba377ab53aec3badbin0 -> 19 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/e4/ed436a9eb0f198cda722886a5f8d6d6c836b7bbin0 -> 225 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391bin0 -> 15 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/f2/0b79342712e0b2315647cd8227a573fd3bc46ebin0 -> 66 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/fd/e0147e3b59f381635a3b016e3fe6dacb70779dbin0 -> 53 bytes
-rw-r--r--tests/resources/typechanges/.gitted/refs/heads/master1
-rw-r--r--tests/resources/typechanges/README.md43
-rw-r--r--tests/resources/typechanges/gitmodules0
-rw-r--r--tests/resources/unsymlinked.git/HEAD1
-rw-r--r--tests/resources/unsymlinked.git/config6
-rw-r--r--tests/resources/unsymlinked.git/description1
-rw-r--r--tests/resources/unsymlinked.git/info/exclude2
-rw-r--r--tests/resources/unsymlinked.git/objects/08/8b64704e0d6b8bd061dea879418cb5442a3fbfbin0 -> 49 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/13/a5e939bca25940c069fd2169d993dba328e30bbin0 -> 44 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/19/bf568e59e3a0b363cafb4106226e62d4a4c41cbin0 -> 29 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/58/1fadd35b4cf320d102a152f918729011604773bin0 -> 47 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/5c/87b6791e8b13da658a14d1ef7e09b5dc3bac8cbin0 -> 78 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/6f/e5f5398af85fb3de8a6aba0339b6d3bfa26a27bin0 -> 49 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/7f/ccd75616ec188b8f1b23d67506a334cc34a49dbin0 -> 132 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/80/6999882bf91d24241e4077906b9017605eb1f3bin0 -> 170 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/83/7d176303c5005505ec1e4a30231c40930c0230bin0 -> 44 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/a8/595ccca04f40818ae0155c8f9c77a230e597b62
-rw-r--r--tests/resources/unsymlinked.git/objects/cf/8f1cf5cce859c438d6cc067284cb5e161206e7bin0 -> 49 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/d5/278d05c8607ec420bfee4cf219fbc0eeebfd6abin0 -> 49 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/f4/e16fb76536591a41454194058d048d8e4dd2e9bin0 -> 44 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/f9/e65619d93fdf2673882e0a261c5e93b1a84006bin0 -> 32 bytes
-rw-r--r--tests/resources/unsymlinked.git/refs/heads/exe-file1
-rw-r--r--tests/resources/unsymlinked.git/refs/heads/master1
-rw-r--r--tests/resources/unsymlinked.git/refs/heads/reg-file1
-rw-r--r--tests/revwalk/basic.c254
-rw-r--r--tests/revwalk/mergebase.c392
-rw-r--r--tests/revwalk/signatureparsing.c47
-rw-r--r--tests/revwalk/simplify.c55
-rw-r--r--tests/stash/drop.c174
-rw-r--r--tests/stash/foreach.c124
-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.c45
-rw-r--r--tests/status/status_data.h326
-rw-r--r--tests/status/status_helpers.c101
-rw-r--r--tests/status/status_helpers.h39
-rw-r--r--tests/status/submodules.c223
-rw-r--r--tests/status/worktree.c875
-rw-r--r--tests/status/worktree_init.c341
-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.c87
-rw-r--r--tests/valgrind-supp-mac.txt184
2062 files changed, 55912 insertions, 0 deletions
diff --git a/tests/README.md b/tests/README.md
new file mode 100644
index 000000000..3aeaaf464
--- /dev/null
+++ b/tests/README.md
@@ -0,0 +1,22 @@
+Writing Clar tests for libgit2
+==============================
+
+For information on the Clar testing framework and a detailed introduction
+please visit:
+
+https://github.com/vmg/clar
+
+
+* Write your modules and tests. Use good, meaningful names.
+
+* Make sure you actually build the tests by setting:
+
+ cmake -DBUILD_CLAR=ON build/
+
+* Test:
+
+ ./build/libgit2_clar
+
+* Make sure everything is fine.
+
+* Send your pull request. That's it.
diff --git a/tests/attr/attr_expect.h b/tests/attr/attr_expect.h
new file mode 100644
index 000000000..70f1ab4f5
--- /dev/null
+++ b/tests/attr/attr_expect.h
@@ -0,0 +1,43 @@
+#ifndef __CLAR_TEST_ATTR_EXPECT__
+#define __CLAR_TEST_ATTR_EXPECT__
+
+enum attr_expect_t {
+ EXPECT_FALSE,
+ EXPECT_TRUE,
+ EXPECT_UNDEFINED,
+ EXPECT_STRING
+};
+
+struct attr_expected {
+ const char *path;
+ const char *attr;
+ enum attr_expect_t expected;
+ const char *expected_str;
+};
+
+GIT_INLINE(void) attr_check_expected(
+ enum attr_expect_t expected,
+ const char *expected_str,
+ const char *name,
+ const char *value)
+{
+ switch (expected) {
+ case EXPECT_TRUE:
+ cl_assert_(GIT_ATTR_TRUE(value), name);
+ break;
+
+ case EXPECT_FALSE:
+ cl_assert_(GIT_ATTR_FALSE(value), name);
+ break;
+
+ case EXPECT_UNDEFINED:
+ cl_assert_(GIT_ATTR_UNSPECIFIED(value), name);
+ break;
+
+ case EXPECT_STRING:
+ cl_assert_equal_s(expected_str, value);
+ break;
+ }
+}
+
+#endif
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/attr/flags.c b/tests/attr/flags.c
new file mode 100644
index 000000000..80c6e1171
--- /dev/null
+++ b/tests/attr/flags.c
@@ -0,0 +1,108 @@
+#include "clar_libgit2.h"
+#include "git2/attr.h"
+
+void test_attr_flags__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_attr_flags__bare(void)
+{
+ git_repository *repo = cl_git_sandbox_init("testrepo.git");
+ const char *value;
+
+ cl_assert(git_repository_is_bare(repo));
+
+ cl_git_pass(git_attr_get(
+ &value, repo, GIT_ATTR_CHECK_NO_SYSTEM, "README.md", "diff"));
+ cl_assert(GIT_ATTR_UNSPECIFIED(value));
+}
+
+void test_attr_flags__index_vs_workdir(void)
+{
+ git_repository *repo = cl_git_sandbox_init("attr_index");
+ const char *value;
+
+ cl_assert(!git_repository_is_bare(repo));
+
+ /* wd then index */
+ cl_git_pass(git_attr_get(
+ &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX,
+ "README.md", "bar"));
+ cl_assert(GIT_ATTR_FALSE(value));
+
+ cl_git_pass(git_attr_get(
+ &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX,
+ "README.md", "blargh"));
+ cl_assert_equal_s(value, "goop");
+
+ cl_git_pass(git_attr_get(
+ &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX,
+ "README.txt", "foo"));
+ cl_assert(GIT_ATTR_FALSE(value));
+
+ /* index then wd */
+ cl_git_pass(git_attr_get(
+ &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE,
+ "README.md", "bar"));
+ cl_assert(GIT_ATTR_TRUE(value));
+
+ cl_git_pass(git_attr_get(
+ &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE,
+ "README.md", "blargh"));
+ cl_assert_equal_s(value, "garble");
+
+ cl_git_pass(git_attr_get(
+ &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE,
+ "README.txt", "foo"));
+ cl_assert(GIT_ATTR_TRUE(value));
+}
+
+void test_attr_flags__subdir(void)
+{
+ git_repository *repo = cl_git_sandbox_init("attr_index");
+ const char *value;
+
+ /* wd then index */
+ cl_git_pass(git_attr_get(
+ &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX,
+ "sub/sub/README.md", "bar"));
+ cl_assert_equal_s(value, "1234");
+
+ cl_git_pass(git_attr_get(
+ &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX,
+ "sub/sub/README.txt", "another"));
+ cl_assert_equal_s(value, "one");
+
+ cl_git_pass(git_attr_get(
+ &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX,
+ "sub/sub/README.txt", "again"));
+ cl_assert(GIT_ATTR_TRUE(value));
+
+ cl_git_pass(git_attr_get(
+ &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX,
+ "sub/sub/README.txt", "beep"));
+ cl_assert_equal_s(value, "10");
+
+ /* index then wd */
+ cl_git_pass(git_attr_get(
+ &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE,
+ "sub/sub/README.md", "bar"));
+ cl_assert_equal_s(value, "1337");
+
+ cl_git_pass(git_attr_get(
+ &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE,
+ "sub/sub/README.txt", "another"));
+ cl_assert_equal_s(value, "one");
+
+ cl_git_pass(git_attr_get(
+ &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE,
+ "sub/sub/README.txt", "again"));
+ cl_assert(GIT_ATTR_TRUE(value));
+
+ cl_git_pass(git_attr_get(
+ &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE,
+ "sub/sub/README.txt", "beep"));
+ cl_assert_equal_s(value, "5");
+}
+
diff --git a/tests/attr/ignore.c b/tests/attr/ignore.c
new file mode 100644
index 000000000..0f945ebf6
--- /dev/null
+++ b/tests/attr/ignore.c
@@ -0,0 +1,102 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "path.h"
+#include "fileops.h"
+
+static git_repository *g_repo = NULL;
+
+void test_attr_ignore__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("attr");
+}
+
+void test_attr_ignore__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ g_repo = NULL;
+}
+
+void assert_is_ignored(bool expected, const char *filepath)
+{
+ int is_ignored;
+
+ cl_git_pass(git_ignore_path_is_ignored(&is_ignored, g_repo, filepath));
+ cl_assert_equal_b(expected, is_ignored);
+}
+
+void test_attr_ignore__honor_temporary_rules(void)
+{
+ cl_git_rewritefile("attr/.gitignore", "/NewFolder\n/NewFolder/NewFolder");
+
+ assert_is_ignored(false, "File.txt");
+ assert_is_ignored(true, "NewFolder");
+ assert_is_ignored(true, "NewFolder/NewFolder");
+ assert_is_ignored(true, "NewFolder/NewFolder/File.txt");
+}
+
+void test_attr_ignore__allow_root(void)
+{
+ cl_git_rewritefile("attr/.gitignore", "/");
+
+ assert_is_ignored(false, "File.txt");
+ assert_is_ignored(false, "NewFolder");
+ assert_is_ignored(false, "NewFolder/NewFolder");
+ assert_is_ignored(false, "NewFolder/NewFolder/File.txt");
+}
+
+void test_attr_ignore__ignore_root(void)
+{
+ cl_git_rewritefile("attr/.gitignore", "/\n\n/NewFolder\n/NewFolder/NewFolder");
+
+ assert_is_ignored(false, "File.txt");
+ assert_is_ignored(true, "NewFolder");
+ assert_is_ignored(true, "NewFolder/NewFolder");
+ assert_is_ignored(true, "NewFolder/NewFolder/File.txt");
+}
+
+
+void test_attr_ignore__skip_gitignore_directory(void)
+{
+ cl_git_rewritefile("attr/.git/info/exclude", "/NewFolder\n/NewFolder/NewFolder");
+ p_unlink("attr/.gitignore");
+ cl_assert(!git_path_exists("attr/.gitignore"));
+ p_mkdir("attr/.gitignore", 0777);
+ cl_git_mkfile("attr/.gitignore/garbage.txt", "new_file\n");
+
+ assert_is_ignored(false, "File.txt");
+ assert_is_ignored(true, "NewFolder");
+ assert_is_ignored(true, "NewFolder/NewFolder");
+ assert_is_ignored(true, "NewFolder/NewFolder/File.txt");
+}
+
+void test_attr_ignore__expand_tilde_to_homedir(void)
+{
+ git_buf path = GIT_BUF_INIT;
+ git_config *cfg;
+
+ assert_is_ignored(false, "example.global_with_tilde");
+
+ /* construct fake home with fake global excludes */
+
+ 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_git_mkfile("home/globalexcludes", "# found me\n*.global_with_tilde\n");
+
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(git_config_set_string(cfg, "core.excludesfile", "~/globalexcludes"));
+ git_config_free(cfg);
+
+ git_attr_cache_flush(g_repo); /* must reset to pick up change */
+
+ assert_is_ignored(true, "example.global_with_tilde");
+
+ cl_git_pass(git_futils_rmdir_r("home", NULL, GIT_RMDIR_REMOVE_FILES));
+
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, NULL));
+
+ git_buf_free(&path);
+}
diff --git a/tests/attr/lookup.c b/tests/attr/lookup.c
new file mode 100644
index 000000000..200bdd2c7
--- /dev/null
+++ b/tests/attr/lookup.c
@@ -0,0 +1,262 @@
+#include "clar_libgit2.h"
+#include "attr_file.h"
+
+#include "attr_expect.h"
+
+void test_attr_lookup__simple(void)
+{
+ git_attr_file *file;
+ git_attr_path path;
+ const char *value = NULL;
+
+ cl_git_pass(git_attr_file__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);
+
+ cl_git_pass(git_attr_path__init(&path, "test", NULL));
+ cl_assert_equal_s("test", path.path);
+ cl_assert_equal_s("test", path.basename);
+ cl_assert(!path.is_dir);
+
+ cl_git_pass(git_attr_file__lookup_one(file,&path,"binary",&value));
+ cl_assert(GIT_ATTR_TRUE(value));
+
+ cl_git_pass(git_attr_file__lookup_one(file,&path,"missing",&value));
+ cl_assert(!value);
+
+ git_attr_path__free(&path);
+ git_attr_file__free(file);
+}
+
+static void run_test_cases(git_attr_file *file, struct attr_expected *cases, int force_dir)
+{
+ git_attr_path path;
+ const char *value = NULL;
+ struct attr_expected *c;
+ int error;
+
+ for (c = cases; c->path != NULL; c++) {
+ cl_git_pass(git_attr_path__init(&path, c->path, NULL));
+
+ if (force_dir)
+ path.is_dir = 1;
+
+ error = git_attr_file__lookup_one(file,&path,c->attr,&value);
+ cl_git_pass(error);
+
+ attr_check_expected(c->expected, c->expected_str, c->attr, value);
+
+ git_attr_path__free(&path);
+ }
+}
+
+void test_attr_lookup__match_variants(void)
+{
+ git_attr_file *file;
+ git_attr_path path;
+
+ struct attr_expected dir_cases[] = {
+ { "pat2", "attr2", EXPECT_TRUE, NULL },
+ { "/testing/for/pat2", "attr2", EXPECT_TRUE, NULL },
+ { "/not/pat2/yousee", "attr2", EXPECT_UNDEFINED, NULL },
+ { "/fun/fun/fun/pat4.dir", "attr4", EXPECT_TRUE, NULL },
+ { "foo.pat5", "attr5", EXPECT_TRUE, NULL },
+ { NULL, NULL, 0, NULL }
+ };
+
+ struct attr_expected cases[] = {
+ /* pat0 -> simple match */
+ { "pat0", "attr0", EXPECT_TRUE, NULL },
+ { "/testing/for/pat0", "attr0", EXPECT_TRUE, NULL },
+ { "relative/to/pat0", "attr0", EXPECT_TRUE, NULL },
+ { "this-contains-pat0-inside", "attr0", EXPECT_UNDEFINED, NULL },
+ { "this-aint-right", "attr0", EXPECT_UNDEFINED, NULL },
+ { "/this/pat0/dont/match", "attr0", EXPECT_UNDEFINED, NULL },
+ /* negative match */
+ { "pat0", "attr1", EXPECT_TRUE, NULL },
+ { "pat1", "attr1", EXPECT_UNDEFINED, NULL },
+ { "/testing/for/pat1", "attr1", EXPECT_UNDEFINED, NULL },
+ { "/testing/for/pat0", "attr1", EXPECT_TRUE, NULL },
+ { "/testing/for/pat1/inside", "attr1", EXPECT_TRUE, NULL },
+ { "misc", "attr1", EXPECT_TRUE, NULL },
+ /* dir match */
+ { "pat2", "attr2", EXPECT_UNDEFINED, NULL },
+ { "/testing/for/pat2", "attr2", EXPECT_UNDEFINED, NULL },
+ { "/not/pat2/yousee", "attr2", EXPECT_UNDEFINED, NULL },
+ /* path match */
+ { "pat3file", "attr3", EXPECT_UNDEFINED, NULL },
+ { "/pat3dir/pat3file", "attr3", EXPECT_TRUE, NULL },
+ { "pat3dir/pat3file", "attr3", EXPECT_TRUE, NULL },
+ /* pattern* match */
+ { "pat4.txt", "attr4", EXPECT_TRUE, NULL },
+ { "/fun/fun/fun/pat4.c", "attr4", EXPECT_TRUE, NULL },
+ { "pat4.", "attr4", EXPECT_TRUE, NULL },
+ { "pat4", "attr4", EXPECT_UNDEFINED, NULL },
+ /* *pattern match */
+ { "foo.pat5", "attr5", EXPECT_TRUE, NULL },
+ { "/this/is/ok.pat5", "attr5", EXPECT_TRUE, NULL },
+ { "/this/is/bad.pat5/yousee.txt", "attr5", EXPECT_UNDEFINED, NULL },
+ { "foo.pat5", "attr100", EXPECT_UNDEFINED, NULL },
+ /* glob match with slashes */
+ { "foo.pat6", "attr6", EXPECT_UNDEFINED, NULL },
+ { "pat6/pat6/foobar.pat6", "attr6", EXPECT_TRUE, NULL },
+ { "pat6/pat6/.pat6", "attr6", EXPECT_TRUE, NULL },
+ { "pat6/pat6/extra/foobar.pat6", "attr6", EXPECT_UNDEFINED, NULL },
+ { "/prefix/pat6/pat6/foobar.pat6", "attr6", EXPECT_UNDEFINED, NULL },
+ { "/pat6/pat6/foobar.pat6", "attr6", EXPECT_TRUE, NULL },
+ /* complex pattern */
+ { "pat7a12z", "attr7", EXPECT_TRUE, NULL },
+ { "pat7e__x", "attr7", EXPECT_TRUE, NULL },
+ { "pat7b/1y", "attr7", EXPECT_UNDEFINED, NULL }, /* ? does not match / */
+ { "pat7e_x", "attr7", EXPECT_UNDEFINED, NULL },
+ { "pat7aaaa", "attr7", EXPECT_UNDEFINED, NULL },
+ { "pat7zzzz", "attr7", EXPECT_UNDEFINED, NULL },
+ { "/this/can/be/anything/pat7a12z", "attr7", EXPECT_TRUE, NULL },
+ { "but/it/still/must/match/pat7aaaa", "attr7", EXPECT_UNDEFINED, NULL },
+ { "pat7aaay.fail", "attr7", EXPECT_UNDEFINED, NULL },
+ /* pattern with spaces */
+ { "pat8 with spaces", "attr8", EXPECT_TRUE, NULL },
+ { "/gotta love/pat8 with spaces", "attr8", EXPECT_TRUE, NULL },
+ { "failing pat8 with spaces", "attr8", EXPECT_UNDEFINED, NULL },
+ { "spaces", "attr8", EXPECT_UNDEFINED, NULL },
+ /* pattern at eof */
+ { "pat9", "attr9", EXPECT_TRUE, NULL },
+ { "/eof/pat9", "attr9", EXPECT_TRUE, NULL },
+ { "pat", "attr9", EXPECT_UNDEFINED, NULL },
+ { "at9", "attr9", EXPECT_UNDEFINED, NULL },
+ { "pat9.fail", "attr9", EXPECT_UNDEFINED, NULL },
+ /* sentinel at end */
+ { NULL, NULL, 0, NULL }
+ };
+
+ cl_git_pass(git_attr_file__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);
+
+ cl_git_pass(git_attr_path__init(&path, "/testing/for/pat0", NULL));
+ cl_assert_equal_s("pat0", path.basename);
+
+ run_test_cases(file, cases, 0);
+ run_test_cases(file, dir_cases, 1);
+
+ git_attr_file__free(file);
+ git_attr_path__free(&path);
+}
+
+void test_attr_lookup__assign_variants(void)
+{
+ git_attr_file *file;
+
+ struct attr_expected cases[] = {
+ /* pat0 -> simple assign */
+ { "pat0", "simple", EXPECT_TRUE, NULL },
+ { "/testing/pat0", "simple", EXPECT_TRUE, NULL },
+ { "pat0", "fail", EXPECT_UNDEFINED, NULL },
+ { "/testing/pat0", "fail", EXPECT_UNDEFINED, NULL },
+ /* negative assign */
+ { "pat1", "neg", EXPECT_FALSE, NULL },
+ { "/testing/pat1", "neg", EXPECT_FALSE, NULL },
+ { "pat1", "fail", EXPECT_UNDEFINED, NULL },
+ { "/testing/pat1", "fail", EXPECT_UNDEFINED, NULL },
+ /* forced undef */
+ { "pat1", "notundef", EXPECT_TRUE, NULL },
+ { "pat2", "notundef", EXPECT_UNDEFINED, NULL },
+ { "/lead/in/pat1", "notundef", EXPECT_TRUE, NULL },
+ { "/lead/in/pat2", "notundef", EXPECT_UNDEFINED, NULL },
+ /* assign value */
+ { "pat3", "assigned", EXPECT_STRING, "test-value" },
+ { "pat3", "notassigned", EXPECT_UNDEFINED, NULL },
+ /* assign value */
+ { "pat4", "rule-with-more-chars", EXPECT_STRING, "value-with-more-chars" },
+ { "pat4", "notassigned-rule-with-more-chars", EXPECT_UNDEFINED, NULL },
+ /* empty assignments */
+ { "pat5", "empty", EXPECT_TRUE, NULL },
+ { "pat6", "negempty", EXPECT_FALSE, NULL },
+ /* multiple assignment */
+ { "pat7", "multiple", EXPECT_TRUE, NULL },
+ { "pat7", "single", EXPECT_FALSE, NULL },
+ { "pat7", "values", EXPECT_STRING, "1" },
+ { "pat7", "also", EXPECT_STRING, "a-really-long-value/*" },
+ { "pat7", "happy", EXPECT_STRING, "yes!" },
+ { "pat8", "again", EXPECT_TRUE, NULL },
+ { "pat8", "another", EXPECT_STRING, "12321" },
+ /* bad assignment */
+ { "patbad0", "simple", EXPECT_UNDEFINED, NULL },
+ { "patbad0", "notundef", EXPECT_TRUE, NULL },
+ { "patbad1", "simple", EXPECT_UNDEFINED, NULL },
+ /* eof assignment */
+ { "pat9", "at-eof", EXPECT_FALSE, NULL },
+ /* sentinel at end */
+ { NULL, NULL, 0, NULL }
+ };
+
+ cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr2")));
+ cl_assert(file->rules.length == 11);
+
+ run_test_cases(file, cases, 0);
+
+ git_attr_file__free(file);
+}
+
+void test_attr_lookup__check_attr_examples(void)
+{
+ git_attr_file *file;
+
+ struct attr_expected cases[] = {
+ { "foo.java", "diff", EXPECT_STRING, "java" },
+ { "foo.java", "crlf", EXPECT_FALSE, NULL },
+ { "foo.java", "myAttr", EXPECT_TRUE, NULL },
+ { "foo.java", "other", EXPECT_UNDEFINED, NULL },
+ { "/prefix/dir/foo.java", "diff", EXPECT_STRING, "java" },
+ { "/prefix/dir/foo.java", "crlf", EXPECT_FALSE, NULL },
+ { "/prefix/dir/foo.java", "myAttr", EXPECT_TRUE, NULL },
+ { "/prefix/dir/foo.java", "other", EXPECT_UNDEFINED, NULL },
+ { "NoMyAttr.java", "crlf", EXPECT_FALSE, NULL },
+ { "NoMyAttr.java", "myAttr", EXPECT_UNDEFINED, NULL },
+ { "NoMyAttr.java", "other", EXPECT_UNDEFINED, NULL },
+ { "/prefix/dir/NoMyAttr.java", "crlf", EXPECT_FALSE, NULL },
+ { "/prefix/dir/NoMyAttr.java", "myAttr", EXPECT_UNDEFINED, NULL },
+ { "/prefix/dir/NoMyAttr.java", "other", EXPECT_UNDEFINED, NULL },
+ { "README", "caveat", EXPECT_STRING, "unspecified" },
+ { "/specific/path/README", "caveat", EXPECT_STRING, "unspecified" },
+ { "README", "missing", EXPECT_UNDEFINED, NULL },
+ { "/specific/path/README", "missing", EXPECT_UNDEFINED, NULL },
+ /* sentinel at end */
+ { NULL, NULL, 0, NULL }
+ };
+
+ cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr3")));
+ cl_assert(file->rules.length == 3);
+
+ run_test_cases(file, cases, 0);
+
+ git_attr_file__free(file);
+}
+
+void test_attr_lookup__from_buffer(void)
+{
+ git_attr_file *file;
+
+ struct attr_expected cases[] = {
+ { "abc", "foo", EXPECT_TRUE, NULL },
+ { "abc", "bar", EXPECT_TRUE, NULL },
+ { "abc", "baz", EXPECT_TRUE, NULL },
+ { "aaa", "foo", EXPECT_TRUE, NULL },
+ { "aaa", "bar", EXPECT_UNDEFINED, NULL },
+ { "aaa", "baz", EXPECT_TRUE, NULL },
+ { "qqq", "foo", EXPECT_UNDEFINED, NULL },
+ { "qqq", "bar", EXPECT_UNDEFINED, NULL },
+ { "qqq", "baz", EXPECT_TRUE, NULL },
+ { NULL, NULL, 0, NULL }
+ };
+
+ cl_git_pass(git_attr_file__new(&file, 0, NULL, NULL));
+
+ cl_git_pass(git_attr_file__parse_buffer(NULL, NULL, "a* foo\nabc bar\n* baz", file));
+
+ cl_assert(file->rules.length == 3);
+
+ run_test_cases(file, cases, 0);
+
+ git_attr_file__free(file);
+}
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/buf/basic.c b/tests/buf/basic.c
new file mode 100644
index 000000000..d558757a9
--- /dev/null
+++ b/tests/buf/basic.c
@@ -0,0 +1,29 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+
+static const char *test_string = "Have you seen that? Have you seeeen that??";
+
+void test_buf_basic__resize(void)
+{
+ git_buf buf1 = GIT_BUF_INIT;
+ git_buf_puts(&buf1, test_string);
+ cl_assert(git_buf_oom(&buf1) == 0);
+ cl_assert_equal_s(git_buf_cstr(&buf1), test_string);
+
+ git_buf_puts(&buf1, test_string);
+ cl_assert(strlen(git_buf_cstr(&buf1)) == strlen(test_string) * 2);
+ git_buf_free(&buf1);
+}
+
+void test_buf_basic__printf(void)
+{
+ git_buf buf2 = GIT_BUF_INIT;
+ git_buf_printf(&buf2, "%s %s %d ", "shoop", "da", 23);
+ cl_assert(git_buf_oom(&buf2) == 0);
+ cl_assert_equal_s(git_buf_cstr(&buf2), "shoop da 23 ");
+
+ git_buf_printf(&buf2, "%s %d", "woop", 42);
+ cl_assert(git_buf_oom(&buf2) == 0);
+ cl_assert_equal_s(git_buf_cstr(&buf2), "shoop da 23 woop 42");
+ git_buf_free(&buf2);
+}
diff --git a/tests/buf/splice.c b/tests/buf/splice.c
new file mode 100644
index 000000000..e80c93105
--- /dev/null
+++ b/tests/buf/splice.c
@@ -0,0 +1,93 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+
+static git_buf _buf;
+
+void test_buf_splice__initialize(void) {
+ git_buf_init(&_buf, 16);
+}
+
+void test_buf_splice__cleanup(void) {
+ git_buf_free(&_buf);
+}
+
+void test_buf_splice__preprend(void)
+{
+ git_buf_sets(&_buf, "world!");
+
+ cl_git_pass(git_buf_splice(&_buf, 0, 0, "Hello Dolly", strlen("Hello ")));
+
+ cl_assert_equal_s("Hello world!", git_buf_cstr(&_buf));
+}
+
+void test_buf_splice__append(void)
+{
+ git_buf_sets(&_buf, "Hello");
+
+ cl_git_pass(git_buf_splice(&_buf, git_buf_len(&_buf), 0, " world!", strlen(" world!")));
+
+ cl_assert_equal_s("Hello world!", git_buf_cstr(&_buf));
+}
+
+void test_buf_splice__insert_at(void)
+{
+ git_buf_sets(&_buf, "Hell world!");
+
+ cl_git_pass(git_buf_splice(&_buf, strlen("Hell"), 0, "o", strlen("o")));
+
+ cl_assert_equal_s("Hello world!", git_buf_cstr(&_buf));
+}
+
+void test_buf_splice__remove_at(void)
+{
+ git_buf_sets(&_buf, "Hello world of warcraft!");
+
+ cl_git_pass(git_buf_splice(&_buf, strlen("Hello world"), strlen(" of warcraft"), "", 0));
+
+ cl_assert_equal_s("Hello world!", git_buf_cstr(&_buf));
+}
+
+void test_buf_splice__replace(void)
+{
+ git_buf_sets(&_buf, "Hell0 w0rld!");
+
+ cl_git_pass(git_buf_splice(&_buf, strlen("Hell"), strlen("0 w0"), "o wo", strlen("o wo")));
+
+ cl_assert_equal_s("Hello world!", git_buf_cstr(&_buf));
+}
+
+void test_buf_splice__replace_with_longer(void)
+{
+ git_buf_sets(&_buf, "Hello you!");
+
+ cl_git_pass(git_buf_splice(&_buf, strlen("Hello "), strlen("you"), "world", strlen("world")));
+
+ cl_assert_equal_s("Hello world!", git_buf_cstr(&_buf));
+}
+
+void test_buf_splice__replace_with_shorter(void)
+{
+ git_buf_sets(&_buf, "Brave new world!");
+
+ cl_git_pass(git_buf_splice(&_buf, 0, strlen("Brave new"), "Hello", strlen("Hello")));
+
+ cl_assert_equal_s("Hello world!", git_buf_cstr(&_buf));
+}
+
+void test_buf_splice__truncate(void)
+{
+ git_buf_sets(&_buf, "Hello world!!");
+
+ cl_git_pass(git_buf_splice(&_buf, strlen("Hello world!"), strlen("!"), "", 0));
+
+ cl_assert_equal_s("Hello world!", git_buf_cstr(&_buf));
+}
+
+void test_buf_splice__dont_do_anything(void)
+{
+ git_buf_sets(&_buf, "Hello world!");
+
+ cl_git_pass(git_buf_splice(&_buf, 3, 0, "Hello", 0));
+
+ cl_assert_equal_s("Hello world!", git_buf_cstr(&_buf));
+}
diff --git a/tests/checkout/binaryunicode.c b/tests/checkout/binaryunicode.c
new file mode 100644
index 000000000..14ab9fdfa
--- /dev/null
+++ b/tests/checkout/binaryunicode.c
@@ -0,0 +1,58 @@
+#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_binaryunicode__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("binaryunicode");
+}
+
+void test_checkout_binaryunicode__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static void execute_test(void)
+{
+ git_oid oid, check;
+ git_commit *commit;
+ git_tree *tree;
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/branch1"));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &oid));
+ cl_git_pass(git_commit_tree(&tree, commit));
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+
+ cl_git_pass(git_checkout_tree(g_repo, (git_object *)tree, &opts));
+
+ git_tree_free(tree);
+ git_commit_free(commit);
+
+ /* Verify that the lenna.jpg file was checked out correctly */
+ cl_git_pass(git_oid_fromstr(&check, "8ab005d890fe53f65eda14b23672f60d9f4ec5a1"));
+ cl_git_pass(git_odb_hashfile(&oid, "binaryunicode/lenna.jpg", GIT_OBJ_BLOB));
+ cl_assert(git_oid_equal(&oid, &check));
+
+ /* Verify that the text file was checked out correctly */
+ cl_git_pass(git_oid_fromstr(&check, "965b223880dd4249e2c66a0cc0b4cffe1dc40f5a"));
+ cl_git_pass(git_odb_hashfile(&oid, "binaryunicode/utf16_withbom_noeol_crlf.txt", GIT_OBJ_BLOB));
+ cl_assert(git_oid_equal(&oid, &check));
+}
+
+void test_checkout_binaryunicode__noautocrlf(void)
+{
+ cl_repo_set_bool(g_repo, "core.autocrlf", false);
+ execute_test();
+}
+
+void test_checkout_binaryunicode__autocrlf(void)
+{
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+ execute_test();
+}
diff --git a/tests/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/checkout/typechange.c b/tests/checkout/typechange.c
new file mode 100644
index 000000000..6cf99ac15
--- /dev/null
+++ b/tests/checkout/typechange.c
@@ -0,0 +1,240 @@
+#include "clar_libgit2.h"
+#include "git2/checkout.h"
+#include "path.h"
+#include "posix.h"
+#include "fileops.h"
+
+static git_repository *g_repo = NULL;
+
+static const char *g_typechange_oids[] = {
+ "79b9f23e85f55ea36a472a902e875bc1121a94cb",
+ "9bdb75b73836a99e3dbeea640a81de81031fdc29",
+ "0e7ed140b514b8cae23254cb8656fe1674403aff",
+ "9d0235c7a7edc0889a18f97a42ee6db9fe688447",
+ "9b19edf33a03a0c59cdfc113bfa5c06179bf9b1a",
+ "1b63caae4a5ca96f78e8dfefc376c6a39a142475",
+ "6eae26c90e8ccc4d16208972119c40635489c6f0",
+ NULL
+};
+
+static bool g_typechange_empty[] = {
+ true, false, false, false, false, false, true, true
+};
+
+void test_checkout_typechange__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("typechanges");
+
+ cl_fixture_sandbox("submod2_target");
+ p_rename("submod2_target/.gitted", "submod2_target/.git");
+}
+
+void test_checkout_typechange__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ cl_fixture_cleanup("submod2_target");
+}
+
+static void assert_file_exists(const char *path)
+{
+ cl_assert_(git_path_isfile(path), path);
+}
+
+static void assert_dir_exists(const char *path)
+{
+ cl_assert_(git_path_isdir(path), path);
+}
+
+static void assert_workdir_matches_tree(
+ git_repository *repo, const git_oid *id, const char *root, bool recurse)
+{
+ git_object *obj;
+ git_tree *tree;
+ size_t i, max_i;
+ git_buf path = GIT_BUF_INIT;
+
+ if (!root)
+ root = git_repository_workdir(repo);
+ cl_assert(root);
+
+ cl_git_pass(git_object_lookup(&obj, repo, id, GIT_OBJ_ANY));
+ cl_git_pass(git_object_peel((git_object **)&tree, obj, GIT_OBJ_TREE));
+ git_object_free(obj);
+
+ max_i = git_tree_entrycount(tree);
+
+ for (i = 0; i < max_i; ++i) {
+ const git_tree_entry *te = git_tree_entry_byindex(tree, i);
+ cl_assert(te);
+
+ cl_git_pass(git_buf_joinpath(&path, root, git_tree_entry_name(te)));
+
+ switch (git_tree_entry_type(te)) {
+ case GIT_OBJ_COMMIT:
+ assert_dir_exists(path.ptr);
+ break;
+ case GIT_OBJ_TREE:
+ assert_dir_exists(path.ptr);
+ if (recurse)
+ assert_workdir_matches_tree(
+ repo, git_tree_entry_id(te), path.ptr, true);
+ break;
+ case GIT_OBJ_BLOB:
+ switch (git_tree_entry_filemode(te)) {
+ case GIT_FILEMODE_BLOB:
+ case GIT_FILEMODE_BLOB_EXECUTABLE:
+ assert_file_exists(path.ptr);
+ /* because of cross-platform, don't confirm exec bit yet */
+ break;
+ case GIT_FILEMODE_LINK:
+ cl_assert_(git_path_exists(path.ptr), path.ptr);
+ /* because of cross-platform, don't confirm link yet */
+ break;
+ default:
+ cl_assert(false); /* really?! */
+ }
+ break;
+ default:
+ cl_assert(false); /* really?!! */
+ }
+ }
+
+ git_tree_free(tree);
+ git_buf_free(&path);
+}
+
+void test_checkout_typechange__checkout_typechanges_safe(void)
+{
+ int i;
+ git_object *obj;
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+ for (i = 0; g_typechange_oids[i] != NULL; ++i) {
+ cl_git_pass(git_revparse_single(&obj, g_repo, g_typechange_oids[i]));
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ /* There are bugs in some submodule->tree changes that prevent
+ * SAFE from passing here, even though the following should work:
+ */
+ /* !i ? GIT_CHECKOUT_FORCE : GIT_CHECKOUT_SAFE; */
+
+ cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+
+ cl_git_pass(
+ git_repository_set_head_detached(g_repo, git_object_id(obj)));
+
+ assert_workdir_matches_tree(g_repo, git_object_id(obj), NULL, true);
+
+ git_object_free(obj);
+
+ if (!g_typechange_empty[i]) {
+ cl_assert(git_path_isdir("typechanges"));
+ cl_assert(git_path_exists("typechanges/a"));
+ cl_assert(git_path_exists("typechanges/b"));
+ cl_assert(git_path_exists("typechanges/c"));
+ cl_assert(git_path_exists("typechanges/d"));
+ cl_assert(git_path_exists("typechanges/e"));
+ } else {
+ cl_assert(git_path_isdir("typechanges"));
+ cl_assert(!git_path_exists("typechanges/a"));
+ cl_assert(!git_path_exists("typechanges/b"));
+ cl_assert(!git_path_exists("typechanges/c"));
+ cl_assert(!git_path_exists("typechanges/d"));
+ cl_assert(!git_path_exists("typechanges/e"));
+ }
+ }
+}
+
+typedef struct {
+ int conflicts;
+ int dirty;
+ int updates;
+ int untracked;
+ int ignored;
+} notify_counts;
+
+static int notify_counter(
+ git_checkout_notify_t why,
+ const char *path,
+ const git_diff_file *baseline,
+ const git_diff_file *target,
+ const git_diff_file *workdir,
+ void *payload)
+{
+ notify_counts *cts = payload;
+
+ GIT_UNUSED(path);
+ GIT_UNUSED(baseline);
+ GIT_UNUSED(target);
+ GIT_UNUSED(workdir);
+
+ switch (why) {
+ case GIT_CHECKOUT_NOTIFY_CONFLICT: cts->conflicts++; break;
+ case GIT_CHECKOUT_NOTIFY_DIRTY: cts->dirty++; break;
+ case GIT_CHECKOUT_NOTIFY_UPDATED: cts->updates++; break;
+ case GIT_CHECKOUT_NOTIFY_UNTRACKED: cts->untracked++; break;
+ case GIT_CHECKOUT_NOTIFY_IGNORED: cts->ignored++; break;
+ default: break;
+ }
+
+ return 0;
+}
+
+static void force_create_file(const char *file)
+{
+ int error = git_futils_rmdir_r(file, NULL,
+ GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_REMOVE_BLOCKERS);
+ cl_assert(!error || error == GIT_ENOTFOUND);
+ cl_git_pass(git_futils_mkpath2file(file, 0777));
+ cl_git_rewritefile(file, "yowza!!");
+}
+
+void test_checkout_typechange__checkout_with_conflicts(void)
+{
+ int i;
+ git_object *obj;
+ git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ notify_counts cts = {0};
+
+ opts.notify_flags =
+ GIT_CHECKOUT_NOTIFY_CONFLICT | GIT_CHECKOUT_NOTIFY_UNTRACKED;
+ opts.notify_cb = notify_counter;
+ opts.notify_payload = &cts;
+
+ for (i = 0; g_typechange_oids[i] != NULL; ++i) {
+ cl_git_pass(git_revparse_single(&obj, g_repo, g_typechange_oids[i]));
+
+ force_create_file("typechanges/a/blocker");
+ force_create_file("typechanges/b");
+ force_create_file("typechanges/c/sub/sub/file");
+ git_futils_rmdir_r("typechanges/d", NULL, GIT_RMDIR_REMOVE_FILES);
+ p_mkdir("typechanges/d", 0777); /* intentionally empty dir */
+ force_create_file("typechanges/untracked");
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+ memset(&cts, 0, sizeof(cts));
+
+ cl_git_fail(git_checkout_tree(g_repo, obj, &opts));
+ cl_assert(cts.conflicts > 0);
+ cl_assert(cts.untracked > 0);
+
+ opts.checkout_strategy =
+ GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED;
+ memset(&cts, 0, sizeof(cts));
+
+ cl_assert(git_path_exists("typechanges/untracked"));
+
+ cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+ cl_assert_equal_i(0, cts.conflicts);
+
+ cl_assert(!git_path_exists("typechanges/untracked"));
+
+ cl_git_pass(
+ git_repository_set_head_detached(g_repo, git_object_id(obj)));
+
+ assert_workdir_matches_tree(g_repo, git_object_id(obj), NULL, true);
+
+ git_object_free(obj);
+ }
+}
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/fixtures.h b/tests/clar/fixtures.h
new file mode 100644
index 000000000..264cd7f4f
--- /dev/null
+++ b/tests/clar/fixtures.h
@@ -0,0 +1,38 @@
+static const char *
+fixture_path(const char *base, const char *fixture_name)
+{
+ static char _path[4096];
+ size_t root_len;
+
+ root_len = strlen(base);
+ strncpy(_path, base, sizeof(_path));
+
+ if (_path[root_len - 1] != '/')
+ _path[root_len++] = '/';
+
+ if (fixture_name[0] == '/')
+ fixture_name++;
+
+ strncpy(_path + root_len,
+ fixture_name,
+ sizeof(_path) - root_len);
+
+ return _path;
+}
+
+#ifdef CLAR_FIXTURE_PATH
+const char *cl_fixture(const char *fixture_name)
+{
+ return fixture_path(CLAR_FIXTURE_PATH, fixture_name);
+}
+
+void cl_fixture_sandbox(const char *fixture_name)
+{
+ fs_copy(cl_fixture(fixture_name), _clar_path);
+}
+
+void cl_fixture_cleanup(const char *fixture_name)
+{
+ fs_rm(fixture_path(_clar_path, fixture_name));
+}
+#endif
diff --git a/tests/clar/fs.h b/tests/clar/fs.h
new file mode 100644
index 000000000..b7a1ff9d2
--- /dev/null
+++ b/tests/clar/fs.h
@@ -0,0 +1,325 @@
+#ifdef _WIN32
+
+#define RM_RETRY_COUNT 5
+#define RM_RETRY_DELAY 10
+
+#ifdef __MINGW32__
+
+/* These security-enhanced functions are not available
+ * in MinGW, so just use the vanilla ones */
+#define wcscpy_s(a, b, c) wcscpy((a), (c))
+#define wcscat_s(a, b, c) wcscat((a), (c))
+
+#endif /* __MINGW32__ */
+
+static int
+fs__dotordotdot(WCHAR *_tocheck)
+{
+ return _tocheck[0] == '.' &&
+ (_tocheck[1] == '\0' ||
+ (_tocheck[1] == '.' && _tocheck[2] == '\0'));
+}
+
+static int
+fs_rmdir_rmdir(WCHAR *_wpath)
+{
+ unsigned retries = 1;
+
+ while (!RemoveDirectoryW(_wpath)) {
+ /* Only retry when we have retries remaining, and the
+ * error was ERROR_DIR_NOT_EMPTY. */
+ if (retries++ > RM_RETRY_COUNT ||
+ ERROR_DIR_NOT_EMPTY != GetLastError())
+ return -1;
+
+ /* Give whatever has a handle to a child item some time
+ * to release it before trying again */
+ Sleep(RM_RETRY_DELAY * retries * retries);
+ }
+
+ return 0;
+}
+
+static void
+fs_rmdir_helper(WCHAR *_wsource)
+{
+ WCHAR buffer[MAX_PATH];
+ HANDLE find_handle;
+ WIN32_FIND_DATAW find_data;
+ size_t buffer_prefix_len;
+
+ /* Set up the buffer and capture the length */
+ wcscpy_s(buffer, MAX_PATH, _wsource);
+ wcscat_s(buffer, MAX_PATH, L"\\");
+ buffer_prefix_len = wcslen(buffer);
+
+ /* FindFirstFile needs a wildcard to match multiple items */
+ wcscat_s(buffer, MAX_PATH, L"*");
+ find_handle = FindFirstFileW(buffer, &find_data);
+ cl_assert(INVALID_HANDLE_VALUE != find_handle);
+
+ do {
+ /* FindFirstFile/FindNextFile gives back . and ..
+ * entries at the beginning */
+ if (fs__dotordotdot(find_data.cFileName))
+ continue;
+
+ wcscpy_s(buffer + buffer_prefix_len, MAX_PATH - buffer_prefix_len, find_data.cFileName);
+
+ if (FILE_ATTRIBUTE_DIRECTORY & find_data.dwFileAttributes)
+ fs_rmdir_helper(buffer);
+ else {
+ /* If set, the +R bit must be cleared before deleting */
+ if (FILE_ATTRIBUTE_READONLY & find_data.dwFileAttributes)
+ cl_assert(SetFileAttributesW(buffer, find_data.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY));
+
+ cl_assert(DeleteFileW(buffer));
+ }
+ }
+ while (FindNextFileW(find_handle, &find_data));
+
+ /* Ensure that we successfully completed the enumeration */
+ cl_assert(ERROR_NO_MORE_FILES == GetLastError());
+
+ /* Close the find handle */
+ FindClose(find_handle);
+
+ /* Now that the directory is empty, remove it */
+ cl_assert(0 == fs_rmdir_rmdir(_wsource));
+}
+
+static int
+fs_rm_wait(WCHAR *_wpath)
+{
+ unsigned retries = 1;
+ DWORD last_error;
+
+ do {
+ if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(_wpath))
+ last_error = GetLastError();
+ else
+ last_error = ERROR_SUCCESS;
+
+ /* Is the item gone? */
+ if (ERROR_FILE_NOT_FOUND == last_error ||
+ ERROR_PATH_NOT_FOUND == last_error)
+ return 0;
+
+ Sleep(RM_RETRY_DELAY * retries * retries);
+ }
+ while (retries++ <= RM_RETRY_COUNT);
+
+ return -1;
+}
+
+static void
+fs_rm(const char *_source)
+{
+ WCHAR wsource[MAX_PATH];
+ DWORD attrs;
+
+ /* The input path is UTF-8. Convert it to wide characters
+ * for use with the Windows API */
+ cl_assert(MultiByteToWideChar(CP_UTF8,
+ MB_ERR_INVALID_CHARS,
+ _source,
+ -1, /* Indicates NULL termination */
+ wsource,
+ MAX_PATH));
+
+ /* Does the item exist? If not, we have no work to do */
+ attrs = GetFileAttributesW(wsource);
+
+ if (INVALID_FILE_ATTRIBUTES == attrs)
+ return;
+
+ if (FILE_ATTRIBUTE_DIRECTORY & attrs)
+ fs_rmdir_helper(wsource);
+ else {
+ /* The item is a file. Strip the +R bit */
+ if (FILE_ATTRIBUTE_READONLY & attrs)
+ cl_assert(SetFileAttributesW(wsource, attrs & ~FILE_ATTRIBUTE_READONLY));
+
+ cl_assert(DeleteFileW(wsource));
+ }
+
+ /* Wait for the DeleteFile or RemoveDirectory call to complete */
+ cl_assert(0 == fs_rm_wait(wsource));
+}
+
+static void
+fs_copydir_helper(WCHAR *_wsource, WCHAR *_wdest)
+{
+ WCHAR buf_source[MAX_PATH], buf_dest[MAX_PATH];
+ HANDLE find_handle;
+ WIN32_FIND_DATAW find_data;
+ size_t buf_source_prefix_len, buf_dest_prefix_len;
+
+ wcscpy_s(buf_source, MAX_PATH, _wsource);
+ wcscat_s(buf_source, MAX_PATH, L"\\");
+ buf_source_prefix_len = wcslen(buf_source);
+
+ wcscpy_s(buf_dest, MAX_PATH, _wdest);
+ wcscat_s(buf_dest, MAX_PATH, L"\\");
+ buf_dest_prefix_len = wcslen(buf_dest);
+
+ /* Get an enumerator for the items in the source. */
+ wcscat_s(buf_source, MAX_PATH, L"*");
+ find_handle = FindFirstFileW(buf_source, &find_data);
+ cl_assert(INVALID_HANDLE_VALUE != find_handle);
+
+ /* Create the target directory. */
+ cl_assert(CreateDirectoryW(_wdest, NULL));
+
+ do {
+ /* FindFirstFile/FindNextFile gives back . and ..
+ * entries at the beginning */
+ if (fs__dotordotdot(find_data.cFileName))
+ continue;
+
+ wcscpy_s(buf_source + buf_source_prefix_len, MAX_PATH - buf_source_prefix_len, find_data.cFileName);
+ wcscpy_s(buf_dest + buf_dest_prefix_len, MAX_PATH - buf_dest_prefix_len, find_data.cFileName);
+
+ if (FILE_ATTRIBUTE_DIRECTORY & find_data.dwFileAttributes)
+ fs_copydir_helper(buf_source, buf_dest);
+ else
+ cl_assert(CopyFileW(buf_source, buf_dest, TRUE));
+ }
+ while (FindNextFileW(find_handle, &find_data));
+
+ /* Ensure that we successfully completed the enumeration */
+ cl_assert(ERROR_NO_MORE_FILES == GetLastError());
+
+ /* Close the find handle */
+ FindClose(find_handle);
+}
+
+static void
+fs_copy(const char *_source, const char *_dest)
+{
+ WCHAR wsource[MAX_PATH], wdest[MAX_PATH];
+ DWORD source_attrs, dest_attrs;
+ HANDLE find_handle;
+ WIN32_FIND_DATAW find_data;
+
+ /* The input paths are UTF-8. Convert them to wide characters
+ * for use with the Windows API. */
+ cl_assert(MultiByteToWideChar(CP_UTF8,
+ MB_ERR_INVALID_CHARS,
+ _source,
+ -1,
+ wsource,
+ MAX_PATH));
+
+ cl_assert(MultiByteToWideChar(CP_UTF8,
+ MB_ERR_INVALID_CHARS,
+ _dest,
+ -1,
+ wdest,
+ MAX_PATH));
+
+ /* Check the source for existence */
+ source_attrs = GetFileAttributesW(wsource);
+ cl_assert(INVALID_FILE_ATTRIBUTES != source_attrs);
+
+ /* Check the target for existence */
+ dest_attrs = GetFileAttributesW(wdest);
+
+ if (INVALID_FILE_ATTRIBUTES != dest_attrs) {
+ /* Target exists; append last path part of source to target.
+ * Use FindFirstFile to parse the path */
+ find_handle = FindFirstFileW(wsource, &find_data);
+ cl_assert(INVALID_HANDLE_VALUE != find_handle);
+ wcscat_s(wdest, MAX_PATH, L"\\");
+ wcscat_s(wdest, MAX_PATH, find_data.cFileName);
+ FindClose(find_handle);
+
+ /* Check the new target for existence */
+ cl_assert(INVALID_FILE_ATTRIBUTES == GetFileAttributesW(wdest));
+ }
+
+ if (FILE_ATTRIBUTE_DIRECTORY & source_attrs)
+ fs_copydir_helper(wsource, wdest);
+ else
+ cl_assert(CopyFileW(wsource, wdest, TRUE));
+}
+
+void
+cl_fs_cleanup(void)
+{
+ fs_rm(fixture_path(_clar_path, "*"));
+}
+
+#else
+static int
+shell_out(char * const argv[])
+{
+ int status;
+ pid_t pid;
+
+ pid = fork();
+
+ if (pid < 0) {
+ fprintf(stderr,
+ "System error: `fork()` call failed.\n");
+ exit(-1);
+ }
+
+ if (pid == 0) {
+ execv(argv[0], argv);
+ }
+
+ waitpid(pid, &status, 0);
+ return WEXITSTATUS(status);
+}
+
+static void
+fs_copy(const char *_source, const char *dest)
+{
+ char *argv[5];
+ char *source;
+ size_t source_len;
+
+ source = strdup(_source);
+ source_len = strlen(source);
+
+ if (source[source_len - 1] == '/')
+ source[source_len - 1] = 0;
+
+ argv[0] = "/bin/cp";
+ argv[1] = "-R";
+ argv[2] = source;
+ argv[3] = (char *)dest;
+ argv[4] = NULL;
+
+ cl_must_pass_(
+ shell_out(argv),
+ "Failed to copy test fixtures to sandbox"
+ );
+
+ free(source);
+}
+
+static void
+fs_rm(const char *source)
+{
+ char *argv[4];
+
+ argv[0] = "/bin/rm";
+ argv[1] = "-Rf";
+ argv[2] = (char *)source;
+ argv[3] = NULL;
+
+ cl_must_pass_(
+ shell_out(argv),
+ "Failed to cleanup the sandbox"
+ );
+}
+
+void
+cl_fs_cleanup(void)
+{
+ clar_unsandbox();
+ clar_sandbox();
+}
+#endif
diff --git a/tests/clar/print.h b/tests/clar/print.h
new file mode 100644
index 000000000..368016f2f
--- /dev/null
+++ b/tests/clar/print.h
@@ -0,0 +1,60 @@
+
+static void clar_print_init(int test_count, int suite_count, const char *suite_names)
+{
+ (void)test_count;
+ printf("Loaded %d suites: %s\n", (int)suite_count, suite_names);
+ printf("Started\n");
+}
+
+static void clar_print_shutdown(int test_count, int suite_count, int error_count)
+{
+ (void)test_count;
+ (void)suite_count;
+ (void)error_count;
+
+ printf("\n\n");
+ clar_report_errors();
+}
+
+static void clar_print_error(int num, const struct clar_error *error)
+{
+ printf(" %d) Failure:\n", num);
+
+ printf("%s::%s [%s:%d]\n",
+ error->suite,
+ error->test,
+ error->file,
+ error->line_number);
+
+ printf(" %s\n", error->error_msg);
+
+ if (error->description != NULL)
+ printf(" %s\n", error->description);
+
+ printf("\n");
+ fflush(stdout);
+}
+
+static void clar_print_ontest(const char *test_name, int test_number, int failed)
+{
+ (void)test_name;
+ (void)test_number;
+ printf("%c", failed ? 'F' : '.');
+ fflush(stdout);
+}
+
+static void clar_print_onsuite(const char *suite_name, int suite_index)
+{
+ if (_clar.report_suite_names)
+ printf("\n%s", suite_name);
+
+ (void)suite_index;
+}
+
+static void clar_print_onabort(const char *msg, ...)
+{
+ va_list argp;
+ va_start(argp, msg);
+ vfprintf(stderr, msg, argp);
+ va_end(argp);
+}
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/commit/commit.c b/tests/commit/commit.c
new file mode 100644
index 000000000..8f071ff94
--- /dev/null
+++ b/tests/commit/commit.c
@@ -0,0 +1,46 @@
+#include "clar_libgit2.h"
+
+static git_repository *_repo;
+
+void test_commit_commit__initialize(void)
+{
+ cl_fixture_sandbox("testrepo.git");
+ cl_git_pass(git_repository_open(&_repo, "testrepo.git"));
+}
+
+void test_commit_commit__cleanup(void)
+{
+ git_repository_free(_repo);
+ _repo = NULL;
+
+ cl_fixture_cleanup("testrepo.git");
+}
+
+void test_commit_commit__create_unexisting_update_ref(void)
+{
+ git_oid oid;
+ git_tree *tree;
+ git_commit *commit;
+ git_signature *s;
+ git_reference *ref;
+
+ git_oid_fromstr(&oid, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ cl_git_pass(git_commit_lookup(&commit, _repo, &oid));
+
+ git_oid_fromstr(&oid, "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162");
+ cl_git_pass(git_tree_lookup(&tree, _repo, &oid));
+
+ cl_git_pass(git_signature_now(&s, "alice", "alice@example.com"));
+
+ cl_git_fail(git_reference_lookup(&ref, _repo, "refs/heads/foo/bar"));
+ cl_git_pass(git_commit_create(&oid, _repo, "refs/heads/foo/bar", s, s,
+ NULL, "some msg", tree, 1, (const git_commit **) &commit));
+
+ cl_git_pass(git_reference_lookup(&ref, _repo, "refs/heads/foo/bar"));
+ cl_assert(!git_oid_cmp(&oid, git_reference_target(ref)));
+
+ git_tree_free(tree);
+ git_commit_free(commit);
+ git_signature_free(s);
+ git_reference_free(ref);
+}
diff --git a/tests/commit/parent.c b/tests/commit/parent.c
new file mode 100644
index 000000000..18ce0bba6
--- /dev/null
+++ b/tests/commit/parent.c
@@ -0,0 +1,60 @@
+#include "clar_libgit2.h"
+
+static git_repository *_repo;
+static git_commit *commit;
+
+void test_commit_parent__initialize(void)
+{
+ git_oid oid;
+
+ cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git")));
+
+ git_oid_fromstr(&oid, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ cl_git_pass(git_commit_lookup(&commit, _repo, &oid));
+}
+
+void test_commit_parent__cleanup(void)
+{
+ git_commit_free(commit);
+ commit = NULL;
+
+ git_repository_free(_repo);
+ _repo = NULL;
+}
+
+static void assert_nth_gen_parent(unsigned int gen, const char *expected_oid)
+{
+ git_commit *parent = NULL;
+ int error;
+
+ error = git_commit_nth_gen_ancestor(&parent, commit, gen);
+
+ if (expected_oid != NULL) {
+ cl_assert_equal_i(0, error);
+ cl_assert_equal_i(0, git_oid_streq(git_commit_id(parent), expected_oid));
+ } else
+ cl_assert_equal_i(GIT_ENOTFOUND, error);
+
+ git_commit_free(parent);
+}
+
+/*
+ * $ git show be35~0
+ * commit be3563ae3f795b2b4353bcce3a527ad0a4f7f644
+ *
+ * $ git show be35~1
+ * commit 9fd738e8f7967c078dceed8190330fc8648ee56a
+ *
+ * $ git show be35~3
+ * commit 5b5b025afb0b4c913b4c338a42934a3863bf3644
+ *
+ * $ git show be35~42
+ * fatal: ambiguous argument 'be35~42': unknown revision or path not in the working tree.
+ */
+void test_commit_parent__can_retrieve_nth_generation_parent(void)
+{
+ assert_nth_gen_parent(0, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ assert_nth_gen_parent(1, "9fd738e8f7967c078dceed8190330fc8648ee56a");
+ assert_nth_gen_parent(3, "5b5b025afb0b4c913b4c338a42934a3863bf3644");
+ assert_nth_gen_parent(42, NULL);
+}
diff --git a/tests/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/commit/signature.c b/tests/commit/signature.c
new file mode 100644
index 000000000..e9dcfab41
--- /dev/null
+++ b/tests/commit/signature.c
@@ -0,0 +1,81 @@
+#include "clar_libgit2.h"
+
+static int try_build_signature(const char *name, const char *email, git_time_t time, int offset)
+{
+ git_signature *sign;
+ int error = 0;
+
+ if ((error = git_signature_new(&sign, name, email, time, offset)) < 0)
+ return error;
+
+ git_signature_free((git_signature *)sign);
+
+ return error;
+}
+
+static void assert_name_and_email(
+ const char *expected_name,
+ const char *expected_email,
+ const char *name,
+ const char *email)
+{
+ git_signature *sign;
+
+ cl_git_pass(git_signature_new(&sign, name, email, 1234567890, 60));
+ cl_assert_equal_s(expected_name, sign->name);
+ cl_assert_equal_s(expected_email, sign->email);
+
+ git_signature_free(sign);
+}
+
+void test_commit_signature__leading_and_trailing_spaces_are_trimmed(void)
+{
+ assert_name_and_email("nulltoken", "emeric.fermas@gmail.com", " nulltoken ", " emeric.fermas@gmail.com ");
+ assert_name_and_email("nulltoken", "emeric.fermas@gmail.com", " nulltoken ", " emeric.fermas@gmail.com \n");
+ assert_name_and_email("nulltoken", "emeric.fermas@gmail.com", " \t nulltoken \n", " \n emeric.fermas@gmail.com \n");
+}
+
+void test_commit_signature__angle_brackets_in_names_are_not_supported(void)
+{
+ cl_git_fail(try_build_signature("<Phil Haack", "phil@haack", 1234567890, 60));
+ cl_git_fail(try_build_signature("Phil>Haack", "phil@haack", 1234567890, 60));
+ cl_git_fail(try_build_signature("<Phil Haack>", "phil@haack", 1234567890, 60));
+}
+
+void test_commit_signature__angle_brackets_in_email_are_not_supported(void)
+{
+ cl_git_fail(try_build_signature("Phil Haack", ">phil@haack", 1234567890, 60));
+ cl_git_fail(try_build_signature("Phil Haack", "phil@>haack", 1234567890, 60));
+ cl_git_fail(try_build_signature("Phil Haack", "<phil@haack>", 1234567890, 60));
+}
+
+void test_commit_signature__create_empties(void)
+{
+ // can not create a signature with empty name or email
+ cl_git_pass(try_build_signature("nulltoken", "emeric.fermas@gmail.com", 1234567890, 60));
+
+ cl_git_fail(try_build_signature("", "emeric.fermas@gmail.com", 1234567890, 60));
+ cl_git_fail(try_build_signature(" ", "emeric.fermas@gmail.com", 1234567890, 60));
+ cl_git_pass(try_build_signature("nulltoken", "", 1234567890, 60));
+ cl_git_pass(try_build_signature("nulltoken", " ", 1234567890, 60));
+}
+
+void test_commit_signature__create_one_char(void)
+{
+ // creating a one character signature
+ assert_name_and_email("x", "foo@bar.baz", "x", "foo@bar.baz");
+}
+
+void test_commit_signature__create_two_char(void)
+{
+ // creating a two character signature
+ assert_name_and_email("xx", "foo@bar.baz", "xx", "foo@bar.baz");
+}
+
+void test_commit_signature__create_zero_char(void)
+{
+ // creating a zero character signature
+ git_signature *sign;
+ cl_git_fail(git_signature_new(&sign, "", "x@y.z", 1234567890, 60));
+ cl_assert(sign == NULL);
+}
diff --git a/tests/commit/write.c b/tests/commit/write.c
new file mode 100644
index 000000000..73436b74b
--- /dev/null
+++ b/tests/commit/write.c
@@ -0,0 +1,147 @@
+#include "clar_libgit2.h"
+
+static const char *committer_name = "Vicent Marti";
+static const char *committer_email = "vicent@github.com";
+static const char *commit_message = "This commit has been created in memory\n\
+ This is a commit created in memory and it will be written back to disk\n";
+static const char *tree_oid = "1810dff58d8a660512d4832e740f692884338ccd";
+static const char *root_commit_message = "This is a root commit\n\
+ This is a root commit and should be the only one in this branch\n";
+static char *head_old;
+static git_reference *head, *branch;
+static git_commit *commit;
+
+// Fixture setup
+static git_repository *g_repo;
+void test_commit_write__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_commit_write__cleanup(void)
+{
+ git_reference_free(head);
+ head = NULL;
+
+ git_reference_free(branch);
+ branch = NULL;
+
+ git_commit_free(commit);
+ commit = NULL;
+
+ git__free(head_old);
+ head_old = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+
+// write a new commit object from memory to disk
+void test_commit_write__from_memory(void)
+{
+ git_oid tree_id, parent_id, commit_id;
+ git_signature *author, *committer;
+ const git_signature *author1, *committer1;
+ git_commit *parent;
+ git_tree *tree;
+ const char *commit_id_str = "8496071c1b46c854b31185ea97743be6a8774479";
+
+ git_oid_fromstr(&tree_id, tree_oid);
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
+
+ git_oid_fromstr(&parent_id, commit_id_str);
+ cl_git_pass(git_commit_lookup(&parent, g_repo, &parent_id));
+
+ /* create signatures */
+ cl_git_pass(git_signature_new(&committer, committer_name, committer_email, 123456789, 60));
+ cl_git_pass(git_signature_new(&author, committer_name, committer_email, 987654321, 90));
+
+ cl_git_pass(git_commit_create_v(
+ &commit_id, /* out id */
+ g_repo,
+ NULL, /* do not update the HEAD */
+ author,
+ committer,
+ NULL,
+ commit_message,
+ tree,
+ 1, parent));
+
+ git_object_free((git_object *)parent);
+ git_object_free((git_object *)tree);
+
+ git_signature_free(committer);
+ git_signature_free(author);
+
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &commit_id));
+
+ /* Check attributes were set correctly */
+ author1 = git_commit_author(commit);
+ cl_assert(author1 != NULL);
+ cl_assert_equal_s(committer_name, author1->name);
+ cl_assert_equal_s(committer_email, author1->email);
+ cl_assert(author1->when.time == 987654321);
+ cl_assert(author1->when.offset == 90);
+
+ committer1 = git_commit_committer(commit);
+ cl_assert(committer1 != NULL);
+ cl_assert_equal_s(committer_name, committer1->name);
+ cl_assert_equal_s(committer_email, committer1->email);
+ cl_assert(committer1->when.time == 123456789);
+ cl_assert(committer1->when.offset == 60);
+
+ cl_assert_equal_s(commit_message, git_commit_message(commit));
+}
+
+// create a root commit
+void test_commit_write__root(void)
+{
+ git_oid tree_id, commit_id;
+ const git_oid *branch_oid;
+ git_signature *author, *committer;
+ const char *branch_name = "refs/heads/root-commit-branch";
+ git_tree *tree;
+
+ git_oid_fromstr(&tree_id, tree_oid);
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
+
+ /* create signatures */
+ cl_git_pass(git_signature_new(&committer, committer_name, committer_email, 123456789, 60));
+ cl_git_pass(git_signature_new(&author, committer_name, committer_email, 987654321, 90));
+
+ /* First we need to update HEAD so it points to our non-existant branch */
+ cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
+ cl_assert(git_reference_type(head) == GIT_REF_SYMBOLIC);
+ head_old = git__strdup(git_reference_symbolic_target(head));
+ cl_assert(head_old != NULL);
+ git_reference_free(head);
+
+ cl_git_pass(git_reference_symbolic_create(&head, g_repo, "HEAD", branch_name, 1));
+
+ cl_git_pass(git_commit_create_v(
+ &commit_id, /* out id */
+ g_repo,
+ "HEAD",
+ author,
+ committer,
+ NULL,
+ root_commit_message,
+ tree,
+ 0));
+
+ git_object_free((git_object *)tree);
+ git_signature_free(committer);
+ git_signature_free(author);
+
+ /*
+ * The fact that creating a commit works has already been
+ * tested. Here we just make sure it's our commit and that it was
+ * written as a root commit.
+ */
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &commit_id));
+ cl_assert(git_commit_parentcount(commit) == 0);
+ cl_git_pass(git_reference_lookup(&branch, g_repo, branch_name));
+ branch_oid = git_reference_target(branch);
+ cl_git_pass(git_oid_cmp(branch_oid, &commit_id));
+ cl_assert_equal_s(root_commit_message, git_commit_message(commit));
+}
diff --git a/tests/config/add.c b/tests/config/add.c
new file mode 100644
index 000000000..405f1e2c9
--- /dev/null
+++ b/tests/config/add.c
@@ -0,0 +1,37 @@
+#include "clar_libgit2.h"
+
+void test_config_add__initialize(void)
+{
+ cl_fixture_sandbox("config/config10");
+}
+
+void test_config_add__cleanup(void)
+{
+ cl_fixture_cleanup("config10");
+}
+
+void test_config_add__to_existing_section(void)
+{
+ git_config *cfg;
+ int32_t i;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config10"));
+ cl_git_pass(git_config_set_int32(cfg, "empty.tmp", 5));
+ cl_git_pass(git_config_get_int32(&i, cfg, "empty.tmp"));
+ cl_assert(i == 5);
+ cl_git_pass(git_config_delete_entry(cfg, "empty.tmp"));
+ git_config_free(cfg);
+}
+
+void test_config_add__to_new_section(void)
+{
+ git_config *cfg;
+ int32_t i;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config10"));
+ cl_git_pass(git_config_set_int32(cfg, "section.tmp", 5));
+ cl_git_pass(git_config_get_int32(&i, cfg, "section.tmp"));
+ cl_assert(i == 5);
+ cl_git_pass(git_config_delete_entry(cfg, "section.tmp"));
+ git_config_free(cfg);
+}
diff --git a/tests/config/backend.c b/tests/config/backend.c
new file mode 100644
index 000000000..3fd6eb114
--- /dev/null
+++ b/tests/config/backend.c
@@ -0,0 +1,24 @@
+#include "clar_libgit2.h"
+#include "git2/sys/config.h"
+
+void test_config_backend__checks_version(void)
+{
+ git_config *cfg;
+ git_config_backend backend = GIT_CONFIG_BACKEND_INIT;
+ const git_error *err;
+
+ backend.version = 1024;
+
+ cl_git_pass(git_config_new(&cfg));
+ cl_git_fail(git_config_add_backend(cfg, &backend, 0, false));
+ err = giterr_last();
+ cl_assert_equal_i(GITERR_INVALID, err->klass);
+
+ giterr_clear();
+ backend.version = 1024;
+ cl_git_fail(git_config_add_backend(cfg, &backend, 0, false));
+ err = giterr_last();
+ cl_assert_equal_i(GITERR_INVALID, err->klass);
+
+ git_config_free(cfg);
+}
diff --git a/tests/config/config_helpers.c b/tests/config/config_helpers.c
new file mode 100644
index 000000000..53bd945a0
--- /dev/null
+++ b/tests/config/config_helpers.c
@@ -0,0 +1,37 @@
+#include "clar_libgit2.h"
+#include "config_helpers.h"
+#include "repository.h"
+
+void assert_config_entry_existence(
+ git_repository *repo,
+ const char *name,
+ bool is_supposed_to_exist)
+{
+ git_config *config;
+ const char *out;
+ int result;
+
+ cl_git_pass(git_repository_config__weakptr(&config, repo));
+
+ result = git_config_get_string(&out, config, name);
+
+ if (is_supposed_to_exist)
+ cl_git_pass(result);
+ else
+ cl_assert_equal_i(GIT_ENOTFOUND, result);
+}
+
+void assert_config_entry_value(
+ git_repository *repo,
+ const char *name,
+ const char *expected_value)
+{
+ git_config *config;
+ const char *out;
+
+ cl_git_pass(git_repository_config__weakptr(&config, repo));
+
+ cl_git_pass(git_config_get_string(&out, config, name));
+
+ cl_assert_equal_s(expected_value, out);
+}
diff --git a/tests/config/config_helpers.h b/tests/config/config_helpers.h
new file mode 100644
index 000000000..b887b3d38
--- /dev/null
+++ b/tests/config/config_helpers.h
@@ -0,0 +1,9 @@
+extern void assert_config_entry_existence(
+ git_repository *repo,
+ const char *name,
+ bool is_supposed_to_exist);
+
+extern void assert_config_entry_value(
+ git_repository *repo,
+ const char *name,
+ const char *expected_value);
diff --git a/tests/config/configlevel.c b/tests/config/configlevel.c
new file mode 100644
index 000000000..1c22e8d9f
--- /dev/null
+++ b/tests/config/configlevel.c
@@ -0,0 +1,71 @@
+#include "clar_libgit2.h"
+
+void test_config_configlevel__adding_the_same_level_twice_returns_EEXISTS(void)
+{
+ int error;
+ git_config *cfg;
+
+ cl_git_pass(git_config_new(&cfg));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
+ GIT_CONFIG_LEVEL_LOCAL, 0));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
+ GIT_CONFIG_LEVEL_GLOBAL, 0));
+ error = git_config_add_file_ondisk(cfg, cl_fixture("config/config16"),
+ GIT_CONFIG_LEVEL_GLOBAL, 0);
+
+ cl_git_fail(error);
+ cl_assert_equal_i(GIT_EEXISTS, error);
+
+ git_config_free(cfg);
+}
+
+void test_config_configlevel__can_replace_a_config_file_at_an_existing_level(void)
+{
+ git_config *cfg;
+ 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_LOCAL, 1));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config19"),
+ GIT_CONFIG_LEVEL_LOCAL, 1));
+
+ cl_git_pass(git_config_get_string(&s, cfg, "core.stringglobal"));
+ cl_assert_equal_s("don't find me!", s);
+
+ git_config_free(cfg);
+}
+
+void test_config_configlevel__can_read_from_a_single_level_focused_file_after_parent_config_has_been_freed(void)
+{
+ git_config *cfg;
+ git_config *single_level_cfg;
+ 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_LOCAL, 0));
+
+ cl_git_pass(git_config_open_level(&single_level_cfg, cfg, GIT_CONFIG_LEVEL_LOCAL));
+
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_get_string(&s, single_level_cfg, "core.stringglobal"));
+ cl_assert_equal_s("don't find me!", s);
+
+ git_config_free(single_level_cfg);
+}
+
+void test_config_configlevel__fetching_a_level_from_an_empty_compound_config_returns_ENOTFOUND(void)
+{
+ git_config *cfg;
+ git_config *local_cfg;
+
+ cl_git_pass(git_config_new(&cfg));
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_config_open_level(&local_cfg, cfg, GIT_CONFIG_LEVEL_LOCAL));
+
+ git_config_free(cfg);
+}
diff --git a/tests/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/config/new.c b/tests/config/new.c
new file mode 100644
index 000000000..dd6dbca9e
--- /dev/null
+++ b/tests/config/new.c
@@ -0,0 +1,32 @@
+#include "clar_libgit2.h"
+
+#include "filebuf.h"
+#include "fileops.h"
+#include "posix.h"
+
+#define TEST_CONFIG "git-new-config"
+
+void test_config_new__write_new_config(void)
+{
+ const char *out;
+ git_config *config;
+
+ cl_git_mkfile(TEST_CONFIG, "");
+ cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
+
+ cl_git_pass(git_config_set_string(config, "color.ui", "auto"));
+ cl_git_pass(git_config_set_string(config, "core.editor", "ed"));
+
+ git_config_free(config);
+
+ cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
+
+ cl_git_pass(git_config_get_string(&out, config, "color.ui"));
+ cl_assert_equal_s(out, "auto");
+ cl_git_pass(git_config_get_string(&out, config, "core.editor"));
+ cl_assert_equal_s(out, "ed");
+
+ git_config_free(config);
+
+ p_unlink(TEST_CONFIG);
+}
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/config/refresh.c b/tests/config/refresh.c
new file mode 100644
index 000000000..99d677f0e
--- /dev/null
+++ b/tests/config/refresh.c
@@ -0,0 +1,67 @@
+#include "clar_libgit2.h"
+
+#define TEST_FILE "config.refresh"
+
+void test_config_refresh__initialize(void)
+{
+}
+
+void test_config_refresh__cleanup(void)
+{
+ cl_fixture_cleanup(TEST_FILE);
+}
+
+void test_config_refresh__update_value(void)
+{
+ git_config *cfg;
+ int32_t v;
+
+ cl_git_mkfile(TEST_FILE, "[section]\n\tvalue = 1\n\n");
+
+ /* By freeing the config, we make sure we flush the values */
+ cl_git_pass(git_config_open_ondisk(&cfg, TEST_FILE));
+
+ cl_git_pass(git_config_get_int32(&v, cfg, "section.value"));
+ cl_assert_equal_i(1, v);
+
+ cl_git_rewritefile(TEST_FILE, "[section]\n\tvalue = 10\n\n");
+
+ cl_git_pass(git_config_get_int32(&v, cfg, "section.value"));
+ cl_assert_equal_i(1, v);
+
+ cl_git_pass(git_config_refresh(cfg));
+
+ cl_git_pass(git_config_get_int32(&v, cfg, "section.value"));
+ cl_assert_equal_i(10, v);
+
+ git_config_free(cfg);
+}
+
+void test_config_refresh__delete_value(void)
+{
+ git_config *cfg;
+ int32_t v;
+
+ cl_git_mkfile(TEST_FILE, "[section]\n\tvalue = 1\n\n");
+
+ /* By freeing the config, we make sure we flush the values */
+ cl_git_pass(git_config_open_ondisk(&cfg, TEST_FILE));
+
+ cl_git_pass(git_config_get_int32(&v, cfg, "section.value"));
+ cl_assert_equal_i(1, v);
+ cl_git_fail(git_config_get_int32(&v, cfg, "section.newval"));
+
+ cl_git_rewritefile(TEST_FILE, "[section]\n\tnewval = 10\n\n");
+
+ cl_git_pass(git_config_get_int32(&v, cfg, "section.value"));
+ cl_assert_equal_i(1, v);
+ cl_git_fail(git_config_get_int32(&v, cfg, "section.newval"));
+
+ cl_git_pass(git_config_refresh(cfg));
+
+ cl_git_fail(git_config_get_int32(&v, cfg, "section.value"));
+ cl_git_pass(git_config_get_int32(&v, cfg, "section.newval"));
+ cl_assert_equal_i(10, v);
+
+ git_config_free(cfg);
+}
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/core/copy.c b/tests/core/copy.c
new file mode 100644
index 000000000..c0c59c056
--- /dev/null
+++ b/tests/core/copy.c
@@ -0,0 +1,126 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+#include "path.h"
+#include "posix.h"
+
+void test_core_copy__file(void)
+{
+ struct stat st;
+ const char *content = "This is some stuff to copy\n";
+
+ cl_git_mkfile("copy_me", content);
+
+ cl_git_pass(git_futils_cp("copy_me", "copy_me_two", 0664));
+
+ cl_git_pass(git_path_lstat("copy_me_two", &st));
+ cl_assert(S_ISREG(st.st_mode));
+ cl_assert(strlen(content) == (size_t)st.st_size);
+
+ cl_git_pass(p_unlink("copy_me_two"));
+ cl_git_pass(p_unlink("copy_me"));
+}
+
+void test_core_copy__file_in_dir(void)
+{
+ struct stat st;
+ const char *content = "This is some other stuff to copy\n";
+
+ cl_git_pass(git_futils_mkdir("an_dir/in_a_dir", NULL, 0775, GIT_MKDIR_PATH));
+ cl_git_mkfile("an_dir/in_a_dir/copy_me", content);
+ cl_assert(git_path_isdir("an_dir"));
+
+ cl_git_pass(git_futils_mkpath2file
+ ("an_dir/second_dir/and_more/copy_me_two", 0775));
+
+ cl_git_pass(git_futils_cp
+ ("an_dir/in_a_dir/copy_me",
+ "an_dir/second_dir/and_more/copy_me_two",
+ 0664));
+
+ cl_git_pass(git_path_lstat("an_dir/second_dir/and_more/copy_me_two", &st));
+ cl_assert(S_ISREG(st.st_mode));
+ cl_assert(strlen(content) == (size_t)st.st_size);
+
+ cl_git_pass(git_futils_rmdir_r("an_dir", NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_assert(!git_path_isdir("an_dir"));
+}
+
+void test_core_copy__tree(void)
+{
+ struct stat st;
+ const char *content = "File content\n";
+
+ cl_git_pass(git_futils_mkdir("src/b", NULL, 0775, GIT_MKDIR_PATH));
+ cl_git_pass(git_futils_mkdir("src/c/d", NULL, 0775, GIT_MKDIR_PATH));
+ cl_git_pass(git_futils_mkdir("src/c/e", NULL, 0775, GIT_MKDIR_PATH));
+
+ cl_git_mkfile("src/f1", content);
+ cl_git_mkfile("src/b/f2", content);
+ cl_git_mkfile("src/c/f3", content);
+ cl_git_mkfile("src/c/d/f4", content);
+ cl_git_mkfile("src/c/d/.f5", content);
+
+#ifndef GIT_WIN32
+ cl_assert(p_symlink("../../b/f2", "src/c/d/l1") == 0);
+#endif
+
+ cl_assert(git_path_isdir("src"));
+ cl_assert(git_path_isdir("src/b"));
+ cl_assert(git_path_isdir("src/c/d"));
+ cl_assert(git_path_isfile("src/c/d/f4"));
+
+ /* copy with no empty dirs, yes links, no dotfiles, no overwrite */
+
+ cl_git_pass(
+ git_futils_cp_r("src", "t1", GIT_CPDIR_COPY_SYMLINKS, 0) );
+
+ cl_assert(git_path_isdir("t1"));
+ cl_assert(git_path_isdir("t1/b"));
+ cl_assert(git_path_isdir("t1/c"));
+ cl_assert(git_path_isdir("t1/c/d"));
+ cl_assert(!git_path_isdir("t1/c/e"));
+
+ cl_assert(git_path_isfile("t1/f1"));
+ cl_assert(git_path_isfile("t1/b/f2"));
+ cl_assert(git_path_isfile("t1/c/f3"));
+ cl_assert(git_path_isfile("t1/c/d/f4"));
+ cl_assert(!git_path_isfile("t1/c/d/.f5"));
+
+ cl_git_pass(git_path_lstat("t1/c/f3", &st));
+ cl_assert(S_ISREG(st.st_mode));
+ cl_assert(strlen(content) == (size_t)st.st_size);
+
+#ifndef GIT_WIN32
+ cl_git_pass(git_path_lstat("t1/c/d/l1", &st));
+ cl_assert(S_ISLNK(st.st_mode));
+#endif
+
+ cl_git_pass(git_futils_rmdir_r("t1", NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_assert(!git_path_isdir("t1"));
+
+ /* copy with empty dirs, no links, yes dotfiles, no overwrite */
+
+ cl_git_pass(
+ git_futils_cp_r("src", "t2", GIT_CPDIR_CREATE_EMPTY_DIRS | GIT_CPDIR_COPY_DOTFILES, 0) );
+
+ cl_assert(git_path_isdir("t2"));
+ cl_assert(git_path_isdir("t2/b"));
+ cl_assert(git_path_isdir("t2/c"));
+ cl_assert(git_path_isdir("t2/c/d"));
+ cl_assert(git_path_isdir("t2/c/e"));
+
+ cl_assert(git_path_isfile("t2/f1"));
+ cl_assert(git_path_isfile("t2/b/f2"));
+ cl_assert(git_path_isfile("t2/c/f3"));
+ cl_assert(git_path_isfile("t2/c/d/f4"));
+ cl_assert(git_path_isfile("t2/c/d/.f5"));
+
+#ifndef GIT_WIN32
+ cl_git_fail(git_path_lstat("t2/c/d/l1", &st));
+#endif
+
+ cl_git_pass(git_futils_rmdir_r("t2", NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_assert(!git_path_isdir("t2"));
+
+ cl_git_pass(git_futils_rmdir_r("src", NULL, GIT_RMDIR_REMOVE_FILES));
+}
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/core/env.c b/tests/core/env.c
new file mode 100644
index 000000000..0fa6472d7
--- /dev/null
+++ b/tests/core/env.c
@@ -0,0 +1,303 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+#include "path.h"
+
+#ifdef GIT_WIN32
+#define NUM_VARS 5
+static const char *env_vars[NUM_VARS] = {
+ "HOME", "HOMEDRIVE", "HOMEPATH", "USERPROFILE", "PROGRAMFILES"
+};
+#else
+#define NUM_VARS 1
+static const char *env_vars[NUM_VARS] = { "HOME" };
+#endif
+
+static char *env_save[NUM_VARS];
+
+static char *home_values[] = {
+ "fake_home",
+ "f\xc3\xa1ke_h\xc3\xb5me", /* all in latin-1 supplement */
+ "f\xc4\x80ke_\xc4\xa4ome", /* latin extended */
+ "f\xce\xb1\xce\xba\xce\xb5_h\xce\xbfm\xce\xad", /* having fun with greek */
+ "fa\xe0" "\xb8" "\x87" "e_\xe0" "\xb8" "\x99" "ome", /* thai characters */
+ "f\xe1\x9cx80ke_\xe1\x9c\x91ome", /* tagalog characters */
+ "\xe1\xb8\x9f\xe1\xba\xa2" "ke_ho" "\xe1" "\xb9" "\x81" "e", /* latin extended additional */
+ "\xf0\x9f\x98\x98\xf0\x9f\x98\x82", /* emoticons */
+ NULL
+};
+
+void test_core_env__initialize(void)
+{
+ int i;
+ for (i = 0; i < NUM_VARS; ++i) {
+ const char *original = cl_getenv(env_vars[i]);
+#ifdef GIT_WIN32
+ env_save[i] = (char *)original;
+#else
+ env_save[i] = original ? git__strdup(original) : NULL;
+#endif
+ }
+}
+
+static void reset_global_search_path(void)
+{
+ cl_git_pass(git_futils_dirs_set(GIT_FUTILS_DIR_GLOBAL, NULL));
+}
+
+static void reset_system_search_path(void)
+{
+ cl_git_pass(git_futils_dirs_set(GIT_FUTILS_DIR_SYSTEM, NULL));
+}
+
+void test_core_env__cleanup(void)
+{
+ int i;
+ char **val;
+
+ for (i = 0; i < NUM_VARS; ++i) {
+ cl_setenv(env_vars[i], env_save[i]);
+ git__free(env_save[i]);
+ env_save[i] = NULL;
+ }
+
+ /* these will probably have already been cleaned up, but if a test
+ * fails, then it's probably good to try and clear out these dirs
+ */
+ for (val = home_values; *val != NULL; val++) {
+ if (**val != '\0')
+ (void)p_rmdir(*val);
+ }
+
+ /* reset search paths to default */
+ reset_global_search_path();
+ reset_system_search_path();
+}
+
+static void setenv_and_check(const char *name, const char *value)
+{
+ char *check;
+
+ cl_git_pass(cl_setenv(name, value));
+
+ check = cl_getenv(name);
+ cl_assert_equal_s(value, check);
+#ifdef GIT_WIN32
+ git__free(check);
+#endif
+}
+
+void test_core_env__0(void)
+{
+ git_buf path = GIT_BUF_INIT, found = GIT_BUF_INIT;
+ char testfile[16], tidx = '0';
+ char **val;
+ const char *testname = "testfile";
+ size_t testlen = strlen(testname);
+
+ strncpy(testfile, testname, sizeof(testfile));
+ cl_assert_equal_s(testname, testfile);
+
+ for (val = home_values; *val != NULL; val++) {
+
+ /* if we can't make the directory, let's just assume
+ * we are on a filesystem that doesn't support the
+ * characters in question and skip this test...
+ */
+ if (p_mkdir(*val, 0777) != 0) {
+ *val = ""; /* mark as not created */
+ continue;
+ }
+
+ cl_git_pass(git_path_prettify(&path, *val, NULL));
+
+ /* vary testfile name in each directory so accidentally leaving
+ * an environment variable set from a previous iteration won't
+ * accidentally make this test pass...
+ */
+ testfile[testlen] = tidx++;
+ cl_git_pass(git_buf_joinpath(&path, path.ptr, testfile));
+ cl_git_mkfile(path.ptr, "find me");
+ git_buf_rtruncate_at_char(&path, '/');
+
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_futils_find_global_file(&found, testfile));
+
+ setenv_and_check("HOME", path.ptr);
+ reset_global_search_path();
+
+ cl_git_pass(git_futils_find_global_file(&found, testfile));
+
+ cl_setenv("HOME", env_save[0]);
+ reset_global_search_path();
+
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_futils_find_global_file(&found, testfile));
+
+#ifdef GIT_WIN32
+ setenv_and_check("HOMEDRIVE", NULL);
+ setenv_and_check("HOMEPATH", NULL);
+ setenv_and_check("USERPROFILE", path.ptr);
+ reset_global_search_path();
+
+ cl_git_pass(git_futils_find_global_file(&found, testfile));
+
+ {
+ int root = git_path_root(path.ptr);
+ char old;
+
+ if (root >= 0) {
+ setenv_and_check("USERPROFILE", NULL);
+ reset_global_search_path();
+
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_futils_find_global_file(&found, testfile));
+
+ old = path.ptr[root];
+ path.ptr[root] = '\0';
+ setenv_and_check("HOMEDRIVE", path.ptr);
+ path.ptr[root] = old;
+ setenv_and_check("HOMEPATH", &path.ptr[root]);
+ reset_global_search_path();
+
+ cl_git_pass(git_futils_find_global_file(&found, testfile));
+ }
+ }
+#endif
+
+ (void)p_rmdir(*val);
+ }
+
+ git_buf_free(&path);
+ git_buf_free(&found);
+}
+
+
+void test_core_env__1(void)
+{
+ git_buf path = GIT_BUF_INIT;
+
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_futils_find_global_file(&path, "nonexistentfile"));
+
+ cl_git_pass(cl_setenv("HOME", "doesnotexist"));
+#ifdef GIT_WIN32
+ cl_git_pass(cl_setenv("HOMEPATH", "doesnotexist"));
+ cl_git_pass(cl_setenv("USERPROFILE", "doesnotexist"));
+#endif
+ reset_global_search_path();
+
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_futils_find_global_file(&path, "nonexistentfile"));
+
+ cl_git_pass(cl_setenv("HOME", NULL));
+#ifdef GIT_WIN32
+ cl_git_pass(cl_setenv("HOMEPATH", NULL));
+ cl_git_pass(cl_setenv("USERPROFILE", NULL));
+#endif
+ reset_global_search_path();
+ reset_system_search_path();
+
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_futils_find_global_file(&path, "nonexistentfile"));
+
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_futils_find_system_file(&path, "nonexistentfile"));
+
+#ifdef GIT_WIN32
+ cl_git_pass(cl_setenv("PROGRAMFILES", NULL));
+ reset_system_search_path();
+
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_futils_find_system_file(&path, "nonexistentfile"));
+#endif
+
+ git_buf_free(&path);
+}
+
+static void check_global_searchpath(
+ const char *path, int position, const char *file, git_buf *temp)
+{
+ char out[GIT_PATH_MAX];
+
+ /* build and set new path */
+ if (position < 0)
+ cl_git_pass(git_buf_join(temp, GIT_PATH_LIST_SEPARATOR, path, "$PATH"));
+ else if (position > 0)
+ cl_git_pass(git_buf_join(temp, GIT_PATH_LIST_SEPARATOR, "$PATH", path));
+ else
+ cl_git_pass(git_buf_sets(temp, path));
+
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, temp->ptr));
+
+ /* get path and make sure $PATH expansion worked */
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, out, sizeof(out)));
+
+ if (position < 0)
+ cl_assert(git__prefixcmp(out, path) == 0);
+ else if (position > 0)
+ cl_assert(git__suffixcmp(out, path) == 0);
+ else
+ cl_assert_equal_s(out, path);
+
+ /* find file using new path */
+ cl_git_pass(git_futils_find_global_file(temp, file));
+
+ /* reset path and confirm file not found */
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, NULL));
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_futils_find_global_file(temp, file));
+}
+
+void test_core_env__2(void)
+{
+ git_buf path = GIT_BUF_INIT, found = GIT_BUF_INIT;
+ char testfile[16], tidx = '0';
+ char **val;
+ const char *testname = "alternate";
+ size_t testlen = strlen(testname);
+
+ strncpy(testfile, testname, sizeof(testfile));
+ cl_assert_equal_s(testname, testfile);
+
+ for (val = home_values; *val != NULL; val++) {
+
+ /* if we can't make the directory, let's just assume
+ * we are on a filesystem that doesn't support the
+ * characters in question and skip this test...
+ */
+ if (p_mkdir(*val, 0777) != 0 && errno != EEXIST) {
+ *val = ""; /* mark as not created */
+ continue;
+ }
+
+ cl_git_pass(git_path_prettify(&path, *val, NULL));
+
+ /* vary testfile name so any sloppiness is resetting variables or
+ * deleting files won't accidentally make a test pass.
+ */
+ testfile[testlen] = tidx++;
+ cl_git_pass(git_buf_joinpath(&path, path.ptr, testfile));
+ cl_git_mkfile(path.ptr, "find me");
+ git_buf_rtruncate_at_char(&path, '/');
+
+ /* default should be NOTFOUND */
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_futils_find_global_file(&found, testfile));
+
+ /* try plain, append $PATH, and prepend $PATH */
+ check_global_searchpath(path.ptr, 0, testfile, &found);
+ check_global_searchpath(path.ptr, -1, testfile, &found);
+ check_global_searchpath(path.ptr, 1, testfile, &found);
+
+ /* cleanup */
+ cl_git_pass(git_buf_joinpath(&path, path.ptr, testfile));
+ (void)p_unlink(path.ptr);
+ (void)p_rmdir(*val);
+ }
+
+ git_buf_free(&path);
+ git_buf_free(&found);
+}
diff --git a/tests/core/errors.c b/tests/core/errors.c
new file mode 100644
index 000000000..512a4134d
--- /dev/null
+++ b/tests/core/errors.c
@@ -0,0 +1,87 @@
+#include "clar_libgit2.h"
+
+void test_core_errors__public_api(void)
+{
+ char *str_in_error;
+
+ giterr_clear();
+ cl_assert(giterr_last() == NULL);
+
+ giterr_set_oom();
+
+ cl_assert(giterr_last() != NULL);
+ cl_assert(giterr_last()->klass == GITERR_NOMEMORY);
+ str_in_error = strstr(giterr_last()->message, "memory");
+ cl_assert(str_in_error != NULL);
+
+ giterr_clear();
+
+ giterr_set_str(GITERR_REPOSITORY, "This is a test");
+
+ cl_assert(giterr_last() != NULL);
+ str_in_error = strstr(giterr_last()->message, "This is a test");
+ cl_assert(str_in_error != NULL);
+
+ giterr_clear();
+ cl_assert(giterr_last() == NULL);
+}
+
+#include "common.h"
+#include "util.h"
+#include "posix.h"
+
+void test_core_errors__new_school(void)
+{
+ char *str_in_error;
+
+ giterr_clear();
+ cl_assert(giterr_last() == NULL);
+
+ giterr_set_oom(); /* internal fn */
+
+ cl_assert(giterr_last() != NULL);
+ cl_assert(giterr_last()->klass == GITERR_NOMEMORY);
+ str_in_error = strstr(giterr_last()->message, "memory");
+ cl_assert(str_in_error != NULL);
+
+ giterr_clear();
+
+ giterr_set(GITERR_REPOSITORY, "This is a test"); /* internal fn */
+
+ cl_assert(giterr_last() != NULL);
+ str_in_error = strstr(giterr_last()->message, "This is a test");
+ cl_assert(str_in_error != NULL);
+
+ giterr_clear();
+ cl_assert(giterr_last() == NULL);
+
+ do {
+ struct stat st;
+ memset(&st, 0, sizeof(st));
+ cl_assert(p_lstat("this_file_does_not_exist", &st) < 0);
+ GIT_UNUSED(st);
+ } while (false);
+ giterr_set(GITERR_OS, "stat failed"); /* internal fn */
+
+ cl_assert(giterr_last() != NULL);
+ str_in_error = strstr(giterr_last()->message, "stat failed");
+ cl_assert(str_in_error != NULL);
+ cl_assert(git__prefixcmp(str_in_error, "stat failed: ") == 0);
+ cl_assert(strlen(str_in_error) > strlen("stat failed: "));
+
+#ifdef GIT_WIN32
+ giterr_clear();
+
+ /* The MSDN docs use this to generate a sample error */
+ cl_assert(GetProcessId(NULL) == 0);
+ giterr_set(GITERR_OS, "GetProcessId failed"); /* internal fn */
+
+ cl_assert(giterr_last() != NULL);
+ str_in_error = strstr(giterr_last()->message, "GetProcessId failed");
+ cl_assert(str_in_error != NULL);
+ cl_assert(git__prefixcmp(str_in_error, "GetProcessId failed: ") == 0);
+ cl_assert(strlen(str_in_error) > strlen("GetProcessId failed: "));
+#endif
+
+ giterr_clear();
+}
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/core/hex.c b/tests/core/hex.c
new file mode 100644
index 000000000..930af1670
--- /dev/null
+++ b/tests/core/hex.c
@@ -0,0 +1,22 @@
+#include "clar_libgit2.h"
+#include "util.h"
+
+void test_core_hex__fromhex(void)
+{
+ /* Passing cases */
+ cl_assert(git__fromhex('0') == 0x0);
+ cl_assert(git__fromhex('1') == 0x1);
+ cl_assert(git__fromhex('3') == 0x3);
+ cl_assert(git__fromhex('9') == 0x9);
+ cl_assert(git__fromhex('A') == 0xa);
+ cl_assert(git__fromhex('C') == 0xc);
+ cl_assert(git__fromhex('F') == 0xf);
+ cl_assert(git__fromhex('a') == 0xa);
+ cl_assert(git__fromhex('c') == 0xc);
+ cl_assert(git__fromhex('f') == 0xf);
+
+ /* Failing cases */
+ cl_assert(git__fromhex('g') == -1);
+ cl_assert(git__fromhex('z') == -1);
+ cl_assert(git__fromhex('X') == -1);
+}
diff --git a/tests/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/core/oid.c b/tests/core/oid.c
new file mode 100644
index 000000000..7ee6fb67d
--- /dev/null
+++ b/tests/core/oid.c
@@ -0,0 +1,70 @@
+#include "clar_libgit2.h"
+
+static git_oid id;
+static git_oid idp;
+static git_oid idm;
+const char *str_oid = "ae90f12eea699729ed24555e40b9fd669da12a12";
+const char *str_oid_p = "ae90f12eea699729ed";
+const char *str_oid_m = "ae90f12eea699729ed24555e40b9fd669da12a12THIS IS EXTRA TEXT THAT SHOULD GET IGNORED";
+
+void test_core_oid__initialize(void)
+{
+ cl_git_pass(git_oid_fromstr(&id, str_oid));
+ cl_git_pass(git_oid_fromstrp(&idp, str_oid_p));
+ cl_git_fail(git_oid_fromstrp(&idm, str_oid_m));
+}
+
+void test_core_oid__streq(void)
+{
+ cl_assert_equal_i(0, git_oid_streq(&id, str_oid));
+ cl_assert_equal_i(-1, git_oid_streq(&id, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
+
+ cl_assert_equal_i(-1, git_oid_streq(&id, "deadbeef"));
+ cl_assert_equal_i(-1, git_oid_streq(&id, "I'm not an oid.... :)"));
+
+ cl_assert_equal_i(0, git_oid_streq(&idp, "ae90f12eea699729ed0000000000000000000000"));
+ cl_assert_equal_i(0, git_oid_streq(&idp, "ae90f12eea699729ed"));
+ cl_assert_equal_i(-1, git_oid_streq(&idp, "ae90f12eea699729ed1"));
+ cl_assert_equal_i(-1, git_oid_streq(&idp, "ae90f12eea699729ec"));
+ cl_assert_equal_i(-1, git_oid_streq(&idp, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
+
+ cl_assert_equal_i(-1, git_oid_streq(&idp, "deadbeef"));
+ cl_assert_equal_i(-1, git_oid_streq(&idp, "I'm not an oid.... :)"));
+}
+
+void test_core_oid__strcmp(void)
+{
+ cl_assert_equal_i(0, git_oid_strcmp(&id, str_oid));
+ cl_assert(git_oid_strcmp(&id, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef") < 0);
+
+ cl_assert(git_oid_strcmp(&id, "deadbeef") < 0);
+ cl_assert_equal_i(-1, git_oid_strcmp(&id, "I'm not an oid.... :)"));
+
+ cl_assert_equal_i(0, git_oid_strcmp(&idp, "ae90f12eea699729ed0000000000000000000000"));
+ cl_assert_equal_i(0, git_oid_strcmp(&idp, "ae90f12eea699729ed"));
+ cl_assert(git_oid_strcmp(&idp, "ae90f12eea699729ed1") < 0);
+ cl_assert(git_oid_strcmp(&idp, "ae90f12eea699729ec") > 0);
+ cl_assert(git_oid_strcmp(&idp, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef") < 0);
+
+ cl_assert(git_oid_strcmp(&idp, "deadbeef") < 0);
+ cl_assert_equal_i(-1, git_oid_strcmp(&idp, "I'm not an oid.... :)"));
+}
+
+void test_core_oid__ncmp(void)
+{
+ cl_assert(!git_oid_ncmp(&id, &idp, 0));
+ cl_assert(!git_oid_ncmp(&id, &idp, 1));
+ cl_assert(!git_oid_ncmp(&id, &idp, 2));
+ cl_assert(!git_oid_ncmp(&id, &idp, 17));
+ cl_assert(!git_oid_ncmp(&id, &idp, 18));
+ cl_assert(git_oid_ncmp(&id, &idp, 19));
+ cl_assert(git_oid_ncmp(&id, &idp, 40));
+ cl_assert(git_oid_ncmp(&id, &idp, 41));
+ cl_assert(git_oid_ncmp(&id, &idp, 42));
+
+ cl_assert(!git_oid_ncmp(&id, &id, 0));
+ cl_assert(!git_oid_ncmp(&id, &id, 1));
+ cl_assert(!git_oid_ncmp(&id, &id, 39));
+ cl_assert(!git_oid_ncmp(&id, &id, 40));
+ cl_assert(!git_oid_ncmp(&id, &id, 41));
+}
diff --git a/tests/core/oidmap.c b/tests/core/oidmap.c
new file mode 100644
index 000000000..ec4b5e775
--- /dev/null
+++ b/tests/core/oidmap.c
@@ -0,0 +1,110 @@
+#include "clar_libgit2.h"
+#include "oidmap.h"
+
+GIT__USE_OIDMAP;
+
+typedef struct {
+ git_oid oid;
+ size_t extra;
+} oidmap_item;
+
+#define NITEMS 0x0fff
+
+void test_core_oidmap__basic(void)
+{
+ git_oidmap *map;
+ oidmap_item items[NITEMS];
+ uint32_t i, j;
+
+ for (i = 0; i < NITEMS; ++i) {
+ items[i].extra = i;
+ for (j = 0; j < GIT_OID_RAWSZ / 4; ++j) {
+ items[i].oid.id[j * 4 ] = (unsigned char)i;
+ items[i].oid.id[j * 4 + 1] = (unsigned char)(i >> 8);
+ items[i].oid.id[j * 4 + 2] = (unsigned char)(i >> 16);
+ items[i].oid.id[j * 4 + 3] = (unsigned char)(i >> 24);
+ }
+ }
+
+ map = git_oidmap_alloc();
+ cl_assert(map != NULL);
+
+ for (i = 0; i < NITEMS; ++i) {
+ khiter_t pos;
+ int ret;
+
+ pos = kh_get(oid, map, &items[i].oid);
+ cl_assert(pos == kh_end(map));
+
+ pos = kh_put(oid, map, &items[i].oid, &ret);
+ cl_assert(ret != 0);
+
+ kh_val(map, pos) = &items[i];
+ }
+
+
+ for (i = 0; i < NITEMS; ++i) {
+ khiter_t pos;
+
+ pos = kh_get(oid, map, &items[i].oid);
+ cl_assert(pos != kh_end(map));
+
+ cl_assert_equal_p(kh_val(map, pos), &items[i]);
+ }
+
+ git_oidmap_free(map);
+}
+
+void test_core_oidmap__hash_collision(void)
+{
+ git_oidmap *map;
+ oidmap_item items[NITEMS];
+ uint32_t i, j;
+
+ for (i = 0; i < NITEMS; ++i) {
+ uint32_t segment = i / 8;
+ int modi = i - (segment * 8);
+
+ items[i].extra = i;
+
+ for (j = 0; j < GIT_OID_RAWSZ / 4; ++j) {
+ items[i].oid.id[j * 4 ] = (unsigned char)modi;
+ items[i].oid.id[j * 4 + 1] = (unsigned char)(modi >> 8);
+ items[i].oid.id[j * 4 + 2] = (unsigned char)(modi >> 16);
+ items[i].oid.id[j * 4 + 3] = (unsigned char)(modi >> 24);
+ }
+
+ items[i].oid.id[ 8] = (unsigned char)i;
+ items[i].oid.id[ 9] = (unsigned char)(i >> 8);
+ items[i].oid.id[10] = (unsigned char)(i >> 16);
+ items[i].oid.id[11] = (unsigned char)(i >> 24);
+ }
+
+ map = git_oidmap_alloc();
+ cl_assert(map != NULL);
+
+ for (i = 0; i < NITEMS; ++i) {
+ khiter_t pos;
+ int ret;
+
+ pos = kh_get(oid, map, &items[i].oid);
+ cl_assert(pos == kh_end(map));
+
+ pos = kh_put(oid, map, &items[i].oid, &ret);
+ cl_assert(ret != 0);
+
+ kh_val(map, pos) = &items[i];
+ }
+
+
+ for (i = 0; i < NITEMS; ++i) {
+ khiter_t pos;
+
+ pos = kh_get(oid, map, &items[i].oid);
+ cl_assert(pos != kh_end(map));
+
+ cl_assert_equal_p(kh_val(map, pos), &items[i]);
+ }
+
+ git_oidmap_free(map);
+}
diff --git a/tests/core/opts.c b/tests/core/opts.c
new file mode 100644
index 000000000..3173c648b
--- /dev/null
+++ b/tests/core/opts.c
@@ -0,0 +1,19 @@
+#include "clar_libgit2.h"
+#include "cache.h"
+
+void test_core_opts__readwrite(void)
+{
+ size_t old_val = 0;
+ size_t new_val = 0;
+
+ git_libgit2_opts(GIT_OPT_GET_MWINDOW_SIZE, &old_val);
+ git_libgit2_opts(GIT_OPT_SET_MWINDOW_SIZE, (size_t)1234);
+ git_libgit2_opts(GIT_OPT_GET_MWINDOW_SIZE, &new_val);
+
+ cl_assert(new_val == 1234);
+
+ git_libgit2_opts(GIT_OPT_SET_MWINDOW_SIZE, old_val);
+ git_libgit2_opts(GIT_OPT_GET_MWINDOW_SIZE, &new_val);
+
+ cl_assert(new_val == old_val);
+}
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/core/pool.c b/tests/core/pool.c
new file mode 100644
index 000000000..3073c4a45
--- /dev/null
+++ b/tests/core/pool.c
@@ -0,0 +1,145 @@
+#include "clar_libgit2.h"
+#include "pool.h"
+#include "git2/oid.h"
+
+void test_core_pool__0(void)
+{
+ int i;
+ git_pool p;
+ void *ptr;
+
+ cl_git_pass(git_pool_init(&p, 1, 4000));
+
+ for (i = 1; i < 10000; i *= 2) {
+ ptr = git_pool_malloc(&p, i);
+ cl_assert(ptr != NULL);
+ cl_assert(git_pool__ptr_in_pool(&p, ptr));
+ cl_assert(!git_pool__ptr_in_pool(&p, &i));
+ }
+
+ /* 1+2+4+8+16+32+64+128+256+512+1024 -> original block */
+ /* 2048 -> 1 block */
+ /* 4096 -> 1 block */
+ /* 8192 -> 1 block */
+
+ cl_assert(git_pool__open_pages(&p) + git_pool__full_pages(&p) == 4);
+
+ git_pool_clear(&p);
+}
+
+void test_core_pool__1(void)
+{
+ int i;
+ git_pool p;
+
+ cl_git_pass(git_pool_init(&p, 1, 4000));
+
+ for (i = 2010; i > 0; i--)
+ cl_assert(git_pool_malloc(&p, i) != NULL);
+
+ /* with fixed page size, allocation must end up with these values */
+ cl_assert(git_pool__open_pages(&p) == 1);
+ cl_assert(git_pool__full_pages(&p) == 505);
+
+ git_pool_clear(&p);
+
+ cl_git_pass(git_pool_init(&p, 1, 4100));
+
+ for (i = 2010; i > 0; i--)
+ cl_assert(git_pool_malloc(&p, i) != NULL);
+
+ /* with fixed page size, allocation must end up with these values */
+ cl_assert(git_pool__open_pages(&p) == 1);
+ cl_assert(git_pool__full_pages(&p) == 492);
+
+ git_pool_clear(&p);
+}
+
+static char to_hex[] = "0123456789abcdef";
+
+void test_core_pool__2(void)
+{
+ git_pool p;
+ char oid_hex[GIT_OID_HEXSZ];
+ git_oid *oid;
+ int i, j;
+
+ memset(oid_hex, '0', sizeof(oid_hex));
+
+ cl_git_pass(git_pool_init(&p, sizeof(git_oid), 100));
+
+ for (i = 1000; i < 10000; i++) {
+ oid = git_pool_malloc(&p, 1);
+ cl_assert(oid != NULL);
+
+ for (j = 0; j < 8; j++)
+ oid_hex[j] = to_hex[(i >> (4 * j)) & 0x0f];
+ cl_git_pass(git_oid_fromstr(oid, oid_hex));
+ }
+
+ /* with fixed page size, allocation must end up with these values */
+ cl_assert(git_pool__open_pages(&p) == 0);
+ cl_assert(git_pool__full_pages(&p) == 90);
+
+ git_pool_clear(&p);
+}
+
+void test_core_pool__free_list(void)
+{
+ int i;
+ git_pool p;
+ void *ptr, *ptrs[50];
+
+ cl_git_pass(git_pool_init(&p, 100, 100));
+
+ for (i = 0; i < 10; ++i) {
+ ptr = git_pool_malloc(&p, 1);
+ cl_assert(ptr != NULL);
+ }
+ cl_assert_equal_i(10, (int)p.items);
+
+ for (i = 0; i < 50; ++i) {
+ ptrs[i] = git_pool_malloc(&p, 1);
+ cl_assert(ptrs[i] != NULL);
+ }
+ cl_assert_equal_i(60, (int)p.items);
+
+ git_pool_free(&p, ptr);
+ cl_assert_equal_i(60, (int)p.items);
+
+ git_pool_free_array(&p, 50, ptrs);
+ cl_assert_equal_i(60, (int)p.items);
+
+ for (i = 0; i < 50; ++i) {
+ ptrs[i] = git_pool_malloc(&p, 1);
+ cl_assert(ptrs[i] != NULL);
+ }
+ cl_assert_equal_i(60, (int)p.items);
+
+ for (i = 0; i < 111; ++i) {
+ ptr = git_pool_malloc(&p, 1);
+ cl_assert(ptr != NULL);
+ }
+ cl_assert_equal_i(170, (int)p.items);
+
+ git_pool_free_array(&p, 50, ptrs);
+ cl_assert_equal_i(170, (int)p.items);
+
+ for (i = 0; i < 50; ++i) {
+ ptrs[i] = git_pool_malloc(&p, 1);
+ cl_assert(ptrs[i] != NULL);
+ }
+ cl_assert_equal_i(170, (int)p.items);
+
+ git_pool_clear(&p);
+}
+
+void test_core_pool__strndup_limit(void)
+{
+ git_pool p;
+
+ cl_git_pass(git_pool_init(&p, 1, 100));
+ cl_assert(git_pool_strndup(&p, "foo", -1) == NULL);
+ git_pool_clear(&p);
+}
+
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/core/rmdir.c b/tests/core/rmdir.c
new file mode 100644
index 000000000..f0b0bfa42
--- /dev/null
+++ b/tests/core/rmdir.c
@@ -0,0 +1,98 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+
+static const char *empty_tmp_dir = "test_gitfo_rmdir_recurs_test";
+
+void test_core_rmdir__initialize(void)
+{
+ git_buf path = GIT_BUF_INIT;
+
+ cl_must_pass(p_mkdir(empty_tmp_dir, 0777));
+
+ cl_git_pass(git_buf_joinpath(&path, empty_tmp_dir, "/one"));
+ cl_must_pass(p_mkdir(path.ptr, 0777));
+
+ cl_git_pass(git_buf_joinpath(&path, empty_tmp_dir, "/one/two_one"));
+ cl_must_pass(p_mkdir(path.ptr, 0777));
+
+ cl_git_pass(git_buf_joinpath(&path, empty_tmp_dir, "/one/two_two"));
+ cl_must_pass(p_mkdir(path.ptr, 0777));
+
+ cl_git_pass(git_buf_joinpath(&path, empty_tmp_dir, "/one/two_two/three"));
+ cl_must_pass(p_mkdir(path.ptr, 0777));
+
+ cl_git_pass(git_buf_joinpath(&path, empty_tmp_dir, "/two"));
+ cl_must_pass(p_mkdir(path.ptr, 0777));
+
+ git_buf_free(&path);
+}
+
+/* make sure empty dir can be deleted recusively */
+void test_core_rmdir__delete_recursive(void)
+{
+ cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_EMPTY_HIERARCHY));
+}
+
+/* make sure non-empty dir cannot be deleted recusively */
+void test_core_rmdir__fail_to_delete_non_empty_dir(void)
+{
+ git_buf file = GIT_BUF_INIT;
+
+ cl_git_pass(git_buf_joinpath(&file, empty_tmp_dir, "/two/file.txt"));
+
+ cl_git_mkfile(git_buf_cstr(&file), "dummy");
+
+ cl_git_fail(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_EMPTY_HIERARCHY));
+
+ cl_must_pass(p_unlink(file.ptr));
+ cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_EMPTY_HIERARCHY));
+
+ git_buf_free(&file);
+}
+
+void test_core_rmdir__can_skip_non_empty_dir(void)
+{
+ git_buf file = GIT_BUF_INIT;
+
+ cl_git_pass(git_buf_joinpath(&file, empty_tmp_dir, "/two/file.txt"));
+
+ cl_git_mkfile(git_buf_cstr(&file), "dummy");
+
+ cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_SKIP_NONEMPTY));
+ cl_assert(git_path_exists(git_buf_cstr(&file)) == true);
+
+ cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_assert(git_path_exists(empty_tmp_dir) == false);
+
+ git_buf_free(&file);
+}
+
+void test_core_rmdir__can_remove_empty_parents(void)
+{
+ git_buf file = GIT_BUF_INIT;
+
+ cl_git_pass(
+ git_buf_joinpath(&file, empty_tmp_dir, "/one/two_two/three/file.txt"));
+ cl_git_mkfile(git_buf_cstr(&file), "dummy");
+ cl_assert(git_path_isfile(git_buf_cstr(&file)));
+
+ cl_git_pass(git_futils_rmdir_r("one/two_two/three/file.txt", empty_tmp_dir,
+ GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_EMPTY_PARENTS));
+
+ cl_assert(!git_path_exists(git_buf_cstr(&file)));
+
+ git_buf_rtruncate_at_char(&file, '/'); /* three (only contained file.txt) */
+ cl_assert(!git_path_exists(git_buf_cstr(&file)));
+
+ git_buf_rtruncate_at_char(&file, '/'); /* two_two (only contained three) */
+ cl_assert(!git_path_exists(git_buf_cstr(&file)));
+
+ git_buf_rtruncate_at_char(&file, '/'); /* one (contained two_one also) */
+ cl_assert(git_path_exists(git_buf_cstr(&file)));
+
+ cl_assert(git_path_exists(empty_tmp_dir) == true);
+
+ git_buf_free(&file);
+
+ cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_EMPTY_HIERARCHY));
+}
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/core/stat.c b/tests/core/stat.c
new file mode 100644
index 000000000..2e4abfb79
--- /dev/null
+++ b/tests/core/stat.c
@@ -0,0 +1,97 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+#include "path.h"
+#include "posix.h"
+
+void test_core_stat__initialize(void)
+{
+ cl_git_pass(git_futils_mkdir("root/d1/d2", NULL, 0755, GIT_MKDIR_PATH));
+ cl_git_mkfile("root/file", "whatever\n");
+ cl_git_mkfile("root/d1/file", "whatever\n");
+}
+
+void test_core_stat__cleanup(void)
+{
+ git_futils_rmdir_r("root", NULL, GIT_RMDIR_REMOVE_FILES);
+}
+
+#define cl_assert_error(val) \
+ do { err = errno; cl_assert_equal_i((val), err); } while (0)
+
+void test_core_stat__0(void)
+{
+ struct stat st;
+ int err;
+
+ cl_assert_equal_i(0, p_lstat("root", &st));
+ cl_assert(S_ISDIR(st.st_mode));
+ cl_assert_error(0);
+
+ cl_assert_equal_i(0, p_lstat("root/", &st));
+ cl_assert(S_ISDIR(st.st_mode));
+ cl_assert_error(0);
+
+ cl_assert_equal_i(0, p_lstat("root/file", &st));
+ cl_assert(S_ISREG(st.st_mode));
+ cl_assert_error(0);
+
+ cl_assert_equal_i(0, p_lstat("root/d1", &st));
+ cl_assert(S_ISDIR(st.st_mode));
+ cl_assert_error(0);
+
+ cl_assert_equal_i(0, p_lstat("root/d1/", &st));
+ cl_assert(S_ISDIR(st.st_mode));
+ cl_assert_error(0);
+
+ cl_assert_equal_i(0, p_lstat("root/d1/file", &st));
+ cl_assert(S_ISREG(st.st_mode));
+ cl_assert_error(0);
+
+ cl_assert(p_lstat("root/missing", &st) < 0);
+ cl_assert_error(ENOENT);
+
+ cl_assert(p_lstat("root/missing/but/could/be/created", &st) < 0);
+ cl_assert_error(ENOENT);
+
+ cl_assert(p_lstat_posixly("root/missing/but/could/be/created", &st) < 0);
+ cl_assert_error(ENOENT);
+
+ cl_assert(p_lstat("root/d1/missing", &st) < 0);
+ cl_assert_error(ENOENT);
+
+ cl_assert(p_lstat("root/d1/missing/deeper/path", &st) < 0);
+ cl_assert_error(ENOENT);
+
+ cl_assert(p_lstat_posixly("root/d1/missing/deeper/path", &st) < 0);
+ cl_assert_error(ENOENT);
+
+ cl_assert(p_lstat_posixly("root/d1/file/deeper/path", &st) < 0);
+ cl_assert_error(ENOTDIR);
+
+ cl_assert(p_lstat("root/file/invalid", &st) < 0);
+#ifdef GIT_WIN32
+ cl_assert_error(ENOENT);
+#else
+ cl_assert_error(ENOTDIR);
+#endif
+
+ cl_assert(p_lstat_posixly("root/file/invalid", &st) < 0);
+ cl_assert_error(ENOTDIR);
+
+ cl_assert(p_lstat("root/file/invalid/deeper_path", &st) < 0);
+#ifdef GIT_WIN32
+ cl_assert_error(ENOENT);
+#else
+ cl_assert_error(ENOTDIR);
+#endif
+
+ cl_assert(p_lstat_posixly("root/file/invalid/deeper_path", &st) < 0);
+ cl_assert_error(ENOTDIR);
+
+ cl_assert(p_lstat_posixly("root/d1/file/extra", &st) < 0);
+ cl_assert_error(ENOTDIR);
+
+ cl_assert(p_lstat_posixly("root/d1/file/further/invalid/items", &st) < 0);
+ cl_assert_error(ENOTDIR);
+}
+
diff --git a/tests/core/string.c b/tests/core/string.c
new file mode 100644
index 000000000..ec9575685
--- /dev/null
+++ b/tests/core/string.c
@@ -0,0 +1,41 @@
+#include "clar_libgit2.h"
+
+/* compare prefixes */
+void test_core_string__0(void)
+{
+ cl_assert(git__prefixcmp("", "") == 0);
+ cl_assert(git__prefixcmp("a", "") == 0);
+ cl_assert(git__prefixcmp("", "a") < 0);
+ cl_assert(git__prefixcmp("a", "b") < 0);
+ cl_assert(git__prefixcmp("b", "a") > 0);
+ cl_assert(git__prefixcmp("ab", "a") == 0);
+ cl_assert(git__prefixcmp("ab", "ac") < 0);
+ cl_assert(git__prefixcmp("ab", "aa") > 0);
+}
+
+/* compare suffixes */
+void test_core_string__1(void)
+{
+ cl_assert(git__suffixcmp("", "") == 0);
+ cl_assert(git__suffixcmp("a", "") == 0);
+ cl_assert(git__suffixcmp("", "a") < 0);
+ cl_assert(git__suffixcmp("a", "b") < 0);
+ cl_assert(git__suffixcmp("b", "a") > 0);
+ cl_assert(git__suffixcmp("ba", "a") == 0);
+ cl_assert(git__suffixcmp("zaa", "ac") < 0);
+ cl_assert(git__suffixcmp("zaz", "ac") > 0);
+}
+
+/* compare icase sorting with case equality */
+void test_core_string__2(void)
+{
+ cl_assert(git__strcasesort_cmp("", "") == 0);
+ cl_assert(git__strcasesort_cmp("foo", "foo") == 0);
+ cl_assert(git__strcasesort_cmp("foo", "bar") > 0);
+ cl_assert(git__strcasesort_cmp("bar", "foo") < 0);
+ cl_assert(git__strcasesort_cmp("foo", "FOO") > 0);
+ cl_assert(git__strcasesort_cmp("FOO", "foo") < 0);
+ cl_assert(git__strcasesort_cmp("foo", "BAR") > 0);
+ cl_assert(git__strcasesort_cmp("BAR", "foo") < 0);
+ cl_assert(git__strcasesort_cmp("fooBar", "foobar") < 0);
+}
diff --git a/tests/core/strmap.c b/tests/core/strmap.c
new file mode 100644
index 000000000..f34a4f89f
--- /dev/null
+++ b/tests/core/strmap.c
@@ -0,0 +1,102 @@
+#include "clar_libgit2.h"
+#include "strmap.h"
+
+GIT__USE_STRMAP;
+
+void test_core_strmap__0(void)
+{
+ git_strmap *table = git_strmap_alloc();
+ cl_assert(table != NULL);
+ cl_assert(git_strmap_num_entries(table) == 0);
+ git_strmap_free(table);
+}
+
+static void insert_strings(git_strmap *table, int count)
+{
+ int i, j, over, err;
+ char *str;
+
+ for (i = 0; i < count; ++i) {
+ str = malloc(10);
+ for (j = 0; j < 10; ++j)
+ str[j] = 'a' + (i % 26);
+ str[9] = '\0';
+
+ /* if > 26, then encode larger value in first letters */
+ for (j = 0, over = i / 26; over > 0; j++, over = over / 26)
+ str[j] = 'A' + (over % 26);
+
+ git_strmap_insert(table, str, str, err);
+ cl_assert(err >= 0);
+ }
+
+ cl_assert((int)git_strmap_num_entries(table) == count);
+}
+
+void test_core_strmap__1(void)
+{
+ int i;
+ char *str;
+ git_strmap *table = git_strmap_alloc();
+ cl_assert(table != NULL);
+
+ insert_strings(table, 20);
+
+ cl_assert(git_strmap_exists(table, "aaaaaaaaa"));
+ cl_assert(git_strmap_exists(table, "ggggggggg"));
+ cl_assert(!git_strmap_exists(table, "aaaaaaaab"));
+ cl_assert(!git_strmap_exists(table, "abcdefghi"));
+
+ i = 0;
+ git_strmap_foreach_value(table, str, { i++; free(str); });
+ cl_assert(i == 20);
+
+ git_strmap_free(table);
+}
+
+void test_core_strmap__2(void)
+{
+ khiter_t pos;
+ int i;
+ char *str;
+ git_strmap *table = git_strmap_alloc();
+ cl_assert(table != NULL);
+
+ insert_strings(table, 20);
+
+ cl_assert(git_strmap_exists(table, "aaaaaaaaa"));
+ cl_assert(git_strmap_exists(table, "ggggggggg"));
+ cl_assert(!git_strmap_exists(table, "aaaaaaaab"));
+ cl_assert(!git_strmap_exists(table, "abcdefghi"));
+
+ cl_assert(git_strmap_exists(table, "bbbbbbbbb"));
+ pos = git_strmap_lookup_index(table, "bbbbbbbbb");
+ cl_assert(git_strmap_valid_index(table, pos));
+ cl_assert_equal_s(git_strmap_value_at(table, pos), "bbbbbbbbb");
+ free(git_strmap_value_at(table, pos));
+ git_strmap_delete_at(table, pos);
+
+ cl_assert(!git_strmap_exists(table, "bbbbbbbbb"));
+
+ i = 0;
+ git_strmap_foreach_value(table, str, { i++; free(str); });
+ cl_assert(i == 19);
+
+ git_strmap_free(table);
+}
+
+void test_core_strmap__3(void)
+{
+ int i;
+ char *str;
+ git_strmap *table = git_strmap_alloc();
+ cl_assert(table != NULL);
+
+ insert_strings(table, 10000);
+
+ i = 0;
+ git_strmap_foreach_value(table, str, { i++; free(str); });
+ cl_assert(i == 10000);
+
+ git_strmap_free(table);
+}
diff --git a/tests/core/strtol.c b/tests/core/strtol.c
new file mode 100644
index 000000000..8765e042b
--- /dev/null
+++ b/tests/core/strtol.c
@@ -0,0 +1,37 @@
+#include "clar_libgit2.h"
+
+void test_core_strtol__int32(void)
+{
+ int32_t i;
+
+ cl_git_pass(git__strtol32(&i, "123", NULL, 10));
+ cl_assert(i == 123);
+ cl_git_pass(git__strtol32(&i, " +123 ", NULL, 10));
+ cl_assert(i == 123);
+ cl_git_pass(git__strtol32(&i, " +2147483647 ", NULL, 10));
+ cl_assert(i == 2147483647);
+ cl_git_pass(git__strtol32(&i, " -2147483648 ", NULL, 10));
+ cl_assert(i == -2147483648LL);
+
+ cl_git_fail(git__strtol32(&i, " 2147483657 ", NULL, 10));
+ cl_git_fail(git__strtol32(&i, " -2147483657 ", NULL, 10));
+}
+
+void test_core_strtol__int64(void)
+{
+ int64_t i;
+
+ cl_git_pass(git__strtol64(&i, "123", NULL, 10));
+ cl_assert(i == 123);
+ cl_git_pass(git__strtol64(&i, " +123 ", NULL, 10));
+ cl_assert(i == 123);
+ cl_git_pass(git__strtol64(&i, " +2147483647 ", NULL, 10));
+ cl_assert(i == 2147483647);
+ cl_git_pass(git__strtol64(&i, " -2147483648 ", NULL, 10));
+ cl_assert(i == -2147483648LL);
+ cl_git_pass(git__strtol64(&i, " 2147483657 ", NULL, 10));
+ cl_assert(i == 2147483657LL);
+ cl_git_pass(git__strtol64(&i, " -2147483657 ", NULL, 10));
+ cl_assert(i == -2147483657LL);
+}
+
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/date/date.c b/tests/date/date.c
new file mode 100644
index 000000000..88881d1e1
--- /dev/null
+++ b/tests/date/date.c
@@ -0,0 +1,15 @@
+#include "clar_libgit2.h"
+
+#include "util.h"
+
+void test_date_date__overflow(void)
+{
+#ifdef __LP64__
+ git_time_t d2038, d2039;
+
+ /* This is expected to fail on a 32-bit machine. */
+ cl_git_pass(git__date_parse(&d2038, "2038-1-1"));
+ cl_git_pass(git__date_parse(&d2039, "2039-1-1"));
+ cl_assert(d2038 < d2039);
+#endif
+}
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/diff/iterator.c b/tests/diff/iterator.c
new file mode 100644
index 000000000..bbdae8ad1
--- /dev/null
+++ b/tests/diff/iterator.c
@@ -0,0 +1,937 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+#include "iterator.h"
+#include "tree.h"
+
+void test_diff_iterator__initialize(void)
+{
+ /* since we are doing tests with different sandboxes, defer setup
+ * to the actual tests. cleanup will still be done in the global
+ * cleanup function so that assertion failures don't result in a
+ * missed cleanup.
+ */
+}
+
+void test_diff_iterator__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+
+/* -- TREE ITERATOR TESTS -- */
+
+static void tree_iterator_test(
+ const char *sandbox,
+ const char *treeish,
+ const char *start,
+ const char *end,
+ int expected_count,
+ const char **expected_values)
+{
+ git_tree *t;
+ git_iterator *i;
+ const git_index_entry *entry;
+ int error, count = 0, count_post_reset = 0;
+ git_repository *repo = cl_git_sandbox_init(sandbox);
+
+ cl_assert(t = resolve_commit_oid_to_tree(repo, treeish));
+ cl_git_pass(git_iterator_for_tree(
+ &i, t, GIT_ITERATOR_DONT_IGNORE_CASE, start, end));
+
+ /* test loop */
+ while (!(error = git_iterator_advance(&entry, i))) {
+ cl_assert(entry);
+ if (expected_values != NULL)
+ cl_assert_equal_s(expected_values[count], entry->path);
+ count++;
+ }
+ cl_assert_equal_i(GIT_ITEROVER, error);
+ cl_assert(!entry);
+ cl_assert_equal_i(expected_count, count);
+
+ /* test reset */
+ cl_git_pass(git_iterator_reset(i, NULL, NULL));
+
+ while (!(error = git_iterator_advance(&entry, i))) {
+ cl_assert(entry);
+ if (expected_values != NULL)
+ cl_assert_equal_s(expected_values[count_post_reset], entry->path);
+ count_post_reset++;
+ }
+ cl_assert_equal_i(GIT_ITEROVER, error);
+ cl_assert(!entry);
+ cl_assert_equal_i(count, count_post_reset);
+
+ git_iterator_free(i);
+ git_tree_free(t);
+}
+
+/* results of: git ls-tree -r --name-only 605812a */
+const char *expected_tree_0[] = {
+ ".gitattributes",
+ "attr0",
+ "attr1",
+ "attr2",
+ "attr3",
+ "binfile",
+ "macro_test",
+ "root_test1",
+ "root_test2",
+ "root_test3",
+ "root_test4.txt",
+ "subdir/.gitattributes",
+ "subdir/abc",
+ "subdir/subdir_test1",
+ "subdir/subdir_test2.txt",
+ "subdir2/subdir2_test1",
+ NULL
+};
+
+void test_diff_iterator__tree_0(void)
+{
+ tree_iterator_test("attr", "605812a", NULL, NULL, 16, expected_tree_0);
+}
+
+/* results of: git ls-tree -r --name-only 6bab5c79 */
+const char *expected_tree_1[] = {
+ ".gitattributes",
+ "attr0",
+ "attr1",
+ "attr2",
+ "attr3",
+ "root_test1",
+ "root_test2",
+ "root_test3",
+ "root_test4.txt",
+ "subdir/.gitattributes",
+ "subdir/subdir_test1",
+ "subdir/subdir_test2.txt",
+ "subdir2/subdir2_test1",
+ NULL
+};
+
+void test_diff_iterator__tree_1(void)
+{
+ tree_iterator_test("attr", "6bab5c79cd5", NULL, NULL, 13, expected_tree_1);
+}
+
+/* results of: git ls-tree -r --name-only 26a125ee1 */
+const char *expected_tree_2[] = {
+ "current_file",
+ "file_deleted",
+ "modified_file",
+ "staged_changes",
+ "staged_changes_file_deleted",
+ "staged_changes_modified_file",
+ "staged_delete_file_deleted",
+ "staged_delete_modified_file",
+ "subdir.txt",
+ "subdir/current_file",
+ "subdir/deleted_file",
+ "subdir/modified_file",
+ NULL
+};
+
+void test_diff_iterator__tree_2(void)
+{
+ tree_iterator_test("status", "26a125ee1", NULL, NULL, 12, expected_tree_2);
+}
+
+/* $ git ls-tree -r --name-only 0017bd4ab1e */
+const char *expected_tree_3[] = {
+ "current_file",
+ "file_deleted",
+ "modified_file",
+ "staged_changes",
+ "staged_changes_file_deleted",
+ "staged_changes_modified_file",
+ "staged_delete_file_deleted",
+ "staged_delete_modified_file"
+};
+
+void test_diff_iterator__tree_3(void)
+{
+ tree_iterator_test("status", "0017bd4ab1e", NULL, NULL, 8, expected_tree_3);
+}
+
+/* $ git ls-tree -r --name-only 24fa9a9fc4e202313e24b648087495441dab432b */
+const char *expected_tree_4[] = {
+ "attr0",
+ "attr1",
+ "attr2",
+ "attr3",
+ "binfile",
+ "gitattributes",
+ "macro_bad",
+ "macro_test",
+ "root_test1",
+ "root_test2",
+ "root_test3",
+ "root_test4.txt",
+ "sub/abc",
+ "sub/file",
+ "sub/sub/file",
+ "sub/sub/subsub.txt",
+ "sub/subdir_test1",
+ "sub/subdir_test2.txt",
+ "subdir/.gitattributes",
+ "subdir/abc",
+ "subdir/subdir_test1",
+ "subdir/subdir_test2.txt",
+ "subdir2/subdir2_test1",
+ NULL
+};
+
+void test_diff_iterator__tree_4(void)
+{
+ tree_iterator_test(
+ "attr", "24fa9a9fc4e202313e24b648087495441dab432b", NULL, NULL,
+ 23, expected_tree_4);
+}
+
+void test_diff_iterator__tree_4_ranged(void)
+{
+ tree_iterator_test(
+ "attr", "24fa9a9fc4e202313e24b648087495441dab432b",
+ "sub", "sub",
+ 11, &expected_tree_4[12]);
+}
+
+const char *expected_tree_ranged_0[] = {
+ "gitattributes",
+ "macro_bad",
+ "macro_test",
+ "root_test1",
+ "root_test2",
+ "root_test3",
+ "root_test4.txt",
+ NULL
+};
+
+void test_diff_iterator__tree_ranged_0(void)
+{
+ tree_iterator_test(
+ "attr", "24fa9a9fc4e202313e24b648087495441dab432b",
+ "git", "root",
+ 7, expected_tree_ranged_0);
+}
+
+const char *expected_tree_ranged_1[] = {
+ "sub/subdir_test2.txt",
+ NULL
+};
+
+void test_diff_iterator__tree_ranged_1(void)
+{
+ tree_iterator_test(
+ "attr", "24fa9a9fc4e202313e24b648087495441dab432b",
+ "sub/subdir_test2.txt", "sub/subdir_test2.txt",
+ 1, expected_tree_ranged_1);
+}
+
+void test_diff_iterator__tree_range_empty_0(void)
+{
+ tree_iterator_test(
+ "attr", "24fa9a9fc4e202313e24b648087495441dab432b",
+ "empty", "empty", 0, NULL);
+}
+
+void test_diff_iterator__tree_range_empty_1(void)
+{
+ tree_iterator_test(
+ "attr", "24fa9a9fc4e202313e24b648087495441dab432b",
+ "z_empty_after", NULL, 0, NULL);
+}
+
+void test_diff_iterator__tree_range_empty_2(void)
+{
+ tree_iterator_test(
+ "attr", "24fa9a9fc4e202313e24b648087495441dab432b",
+ NULL, ".aaa_empty_before", 0, NULL);
+}
+
+static void check_tree_entry(
+ git_iterator *i,
+ const char *oid,
+ const char *oid_p,
+ const char *oid_pp,
+ const char *oid_ppp)
+{
+ const git_index_entry *ie;
+ const git_tree_entry *te;
+ const git_tree *tree;
+ git_buf path = GIT_BUF_INIT;
+
+ cl_git_pass(git_iterator_current_tree_entry(&te, i));
+ cl_assert(te);
+ cl_assert(git_oid_streq(&te->oid, oid) == 0);
+
+ cl_git_pass(git_iterator_current(&ie, i));
+ cl_git_pass(git_buf_sets(&path, ie->path));
+
+ if (oid_p) {
+ git_buf_rtruncate_at_char(&path, '/');
+ cl_git_pass(git_iterator_current_parent_tree(&tree, i, path.ptr));
+ cl_assert(tree);
+ cl_assert(git_oid_streq(git_tree_id(tree), oid_p) == 0);
+ }
+
+ if (oid_pp) {
+ git_buf_rtruncate_at_char(&path, '/');
+ cl_git_pass(git_iterator_current_parent_tree(&tree, i, path.ptr));
+ cl_assert(tree);
+ cl_assert(git_oid_streq(git_tree_id(tree), oid_pp) == 0);
+ }
+
+ if (oid_ppp) {
+ git_buf_rtruncate_at_char(&path, '/');
+ cl_git_pass(git_iterator_current_parent_tree(&tree, i, path.ptr));
+ cl_assert(tree);
+ cl_assert(git_oid_streq(git_tree_id(tree), oid_ppp) == 0);
+ }
+
+ git_buf_free(&path);
+}
+
+void test_diff_iterator__tree_special_functions(void)
+{
+ git_tree *t;
+ git_iterator *i;
+ const git_index_entry *entry;
+ git_repository *repo = cl_git_sandbox_init("attr");
+ int error, cases = 0;
+ const char *rootoid = "ce39a97a7fb1fa90bcf5e711249c1e507476ae0e";
+
+ t = resolve_commit_oid_to_tree(
+ repo, "24fa9a9fc4e202313e24b648087495441dab432b");
+ cl_assert(t != NULL);
+
+ cl_git_pass(git_iterator_for_tree(
+ &i, t, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL));
+
+ while (!(error = git_iterator_advance(&entry, i))) {
+ cl_assert(entry);
+
+ if (strcmp(entry->path, "sub/file") == 0) {
+ cases++;
+ check_tree_entry(
+ i, "45b983be36b73c0788dc9cbcb76cbb80fc7bb057",
+ "ecb97df2a174987475ac816e3847fc8e9f6c596b",
+ rootoid, NULL);
+ }
+ else if (strcmp(entry->path, "sub/sub/subsub.txt") == 0) {
+ cases++;
+ check_tree_entry(
+ i, "9e5bdc47d6a80f2be0ea3049ad74231b94609242",
+ "4e49ba8c5b6c32ff28cd9dcb60be34df50fcc485",
+ "ecb97df2a174987475ac816e3847fc8e9f6c596b", rootoid);
+ }
+ else if (strcmp(entry->path, "subdir/.gitattributes") == 0) {
+ cases++;
+ check_tree_entry(
+ i, "99eae476896f4907224978b88e5ecaa6c5bb67a9",
+ "9fb40b6675dde60b5697afceae91b66d908c02d9",
+ rootoid, NULL);
+ }
+ else if (strcmp(entry->path, "subdir2/subdir2_test1") == 0) {
+ cases++;
+ check_tree_entry(
+ i, "dccada462d3df8ac6de596fb8c896aba9344f941",
+ "2929de282ce999e95183aedac6451d3384559c4b",
+ rootoid, NULL);
+ }
+ }
+ cl_assert_equal_i(GIT_ITEROVER, error);
+ cl_assert(!entry);
+ cl_assert_equal_i(4, cases);
+
+ git_iterator_free(i);
+ git_tree_free(t);
+}
+
+/* -- INDEX ITERATOR TESTS -- */
+
+static void index_iterator_test(
+ const char *sandbox,
+ const char *start,
+ const char *end,
+ int expected_count,
+ const char **expected_names,
+ const char **expected_oids)
+{
+ git_index *index;
+ git_iterator *i;
+ const git_index_entry *entry;
+ int error, count = 0;
+ git_repository *repo = cl_git_sandbox_init(sandbox);
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_iterator_for_index(&i, index, 0, start, end));
+
+ while (!(error = git_iterator_advance(&entry, i))) {
+ cl_assert(entry);
+
+ if (expected_names != NULL)
+ cl_assert_equal_s(expected_names[count], entry->path);
+
+ if (expected_oids != NULL) {
+ git_oid oid;
+ cl_git_pass(git_oid_fromstr(&oid, expected_oids[count]));
+ cl_assert_equal_i(git_oid_cmp(&oid, &entry->oid), 0);
+ }
+
+ count++;
+ }
+
+ cl_assert_equal_i(GIT_ITEROVER, error);
+ cl_assert(!entry);
+ cl_assert_equal_i(expected_count, count);
+
+ git_iterator_free(i);
+ git_index_free(index);
+}
+
+static const char *expected_index_0[] = {
+ "attr0",
+ "attr1",
+ "attr2",
+ "attr3",
+ "binfile",
+ "gitattributes",
+ "macro_bad",
+ "macro_test",
+ "root_test1",
+ "root_test2",
+ "root_test3",
+ "root_test4.txt",
+ "sub/abc",
+ "sub/file",
+ "sub/sub/file",
+ "sub/sub/subsub.txt",
+ "sub/subdir_test1",
+ "sub/subdir_test2.txt",
+ "subdir/.gitattributes",
+ "subdir/abc",
+ "subdir/subdir_test1",
+ "subdir/subdir_test2.txt",
+ "subdir2/subdir2_test1",
+};
+
+static const char *expected_index_oids_0[] = {
+ "556f8c827b8e4a02ad5cab77dca2bcb3e226b0b3",
+ "3b74db7ab381105dc0d28f8295a77f6a82989292",
+ "2c66e14f77196ea763fb1e41612c1aa2bc2d8ed2",
+ "c485abe35abd4aa6fd83b076a78bbea9e2e7e06c",
+ "d800886d9c86731ae5c4a62b0b77c437015e00d2",
+ "2b40c5aca159b04ea8d20ffe36cdf8b09369b14a",
+ "5819a185d77b03325aaf87cafc771db36f6ddca7",
+ "ff69f8639ce2e6010b3f33a74160aad98b48da2b",
+ "45141a79a77842c59a63229403220a4e4be74e3d",
+ "4d713dc48e6b1bd75b0d61ad078ba9ca3a56745d",
+ "108bb4e7fd7b16490dc33ff7d972151e73d7166e",
+ "a0f7217ae99f5ac3e88534f5cea267febc5fa85b",
+ "3e42ffc54a663f9401cc25843d6c0e71a33e4249",
+ "45b983be36b73c0788dc9cbcb76cbb80fc7bb057",
+ "45b983be36b73c0788dc9cbcb76cbb80fc7bb057",
+ "9e5bdc47d6a80f2be0ea3049ad74231b94609242",
+ "e563cf4758f0d646f1b14b76016aa17fa9e549a4",
+ "fb5067b1aef3ac1ada4b379dbcb7d17255df7d78",
+ "99eae476896f4907224978b88e5ecaa6c5bb67a9",
+ "3e42ffc54a663f9401cc25843d6c0e71a33e4249",
+ "e563cf4758f0d646f1b14b76016aa17fa9e549a4",
+ "fb5067b1aef3ac1ada4b379dbcb7d17255df7d78",
+ "dccada462d3df8ac6de596fb8c896aba9344f941"
+};
+
+void test_diff_iterator__index_0(void)
+{
+ index_iterator_test(
+ "attr", NULL, NULL, 23, expected_index_0, expected_index_oids_0);
+}
+
+static const char *expected_index_range[] = {
+ "root_test1",
+ "root_test2",
+ "root_test3",
+ "root_test4.txt",
+};
+
+static const char *expected_index_oids_range[] = {
+ "45141a79a77842c59a63229403220a4e4be74e3d",
+ "4d713dc48e6b1bd75b0d61ad078ba9ca3a56745d",
+ "108bb4e7fd7b16490dc33ff7d972151e73d7166e",
+ "a0f7217ae99f5ac3e88534f5cea267febc5fa85b",
+};
+
+void test_diff_iterator__index_range(void)
+{
+ index_iterator_test(
+ "attr", "root", "root", 4, expected_index_range, expected_index_oids_range);
+}
+
+void test_diff_iterator__index_range_empty_0(void)
+{
+ index_iterator_test(
+ "attr", "empty", "empty", 0, NULL, NULL);
+}
+
+void test_diff_iterator__index_range_empty_1(void)
+{
+ index_iterator_test(
+ "attr", "z_empty_after", NULL, 0, NULL, NULL);
+}
+
+void test_diff_iterator__index_range_empty_2(void)
+{
+ index_iterator_test(
+ "attr", NULL, ".aaa_empty_before", 0, NULL, NULL);
+}
+
+static const char *expected_index_1[] = {
+ "current_file",
+ "file_deleted",
+ "modified_file",
+ "staged_changes",
+ "staged_changes_file_deleted",
+ "staged_changes_modified_file",
+ "staged_new_file",
+ "staged_new_file_deleted_file",
+ "staged_new_file_modified_file",
+ "subdir.txt",
+ "subdir/current_file",
+ "subdir/deleted_file",
+ "subdir/modified_file",
+};
+
+static const char* expected_index_oids_1[] = {
+ "a0de7e0ac200c489c41c59dfa910154a70264e6e",
+ "5452d32f1dd538eb0405e8a83cc185f79e25e80f",
+ "452e4244b5d083ddf0460acf1ecc74db9dcfa11a",
+ "55d316c9ba708999f1918e9677d01dfcae69c6b9",
+ "a6be623522ce87a1d862128ac42672604f7b468b",
+ "906ee7711f4f4928ddcb2a5f8fbc500deba0d2a8",
+ "529a16e8e762d4acb7b9636ff540a00831f9155a",
+ "90b8c29d8ba39434d1c63e1b093daaa26e5bd972",
+ "ed062903b8f6f3dccb2fa81117ba6590944ef9bd",
+ "e8ee89e15bbe9b20137715232387b3de5b28972e",
+ "53ace0d1cc1145a5f4fe4f78a186a60263190733",
+ "1888c805345ba265b0ee9449b8877b6064592058",
+ "a6191982709b746d5650e93c2acf34ef74e11504"
+};
+
+void test_diff_iterator__index_1(void)
+{
+ index_iterator_test(
+ "status", NULL, NULL, 13, expected_index_1, expected_index_oids_1);
+}
+
+
+/* -- WORKDIR ITERATOR TESTS -- */
+
+static void workdir_iterator_test(
+ const char *sandbox,
+ const char *start,
+ const char *end,
+ int expected_count,
+ int expected_ignores,
+ const char **expected_names,
+ const char *an_ignored_name)
+{
+ git_iterator *i;
+ const git_index_entry *entry;
+ int error, count = 0, count_all = 0, count_all_post_reset = 0;
+ git_repository *repo = cl_git_sandbox_init(sandbox);
+
+ cl_git_pass(git_iterator_for_workdir(
+ &i, repo, GIT_ITERATOR_DONT_AUTOEXPAND, start, end));
+
+ error = git_iterator_current(&entry, i);
+ cl_assert((error == 0 && entry != NULL) ||
+ (error == GIT_ITEROVER && entry == NULL));
+
+ while (entry != NULL) {
+ int ignored = git_iterator_current_is_ignored(i);
+
+ if (S_ISDIR(entry->mode)) {
+ cl_git_pass(git_iterator_advance_into(&entry, i));
+ continue;
+ }
+
+ if (expected_names != NULL)
+ cl_assert_equal_s(expected_names[count_all], entry->path);
+
+ if (an_ignored_name && strcmp(an_ignored_name,entry->path)==0)
+ cl_assert(ignored);
+
+ if (!ignored)
+ count++;
+ count_all++;
+
+ error = git_iterator_advance(&entry, i);
+
+ cl_assert((error == 0 && entry != NULL) ||
+ (error == GIT_ITEROVER && entry == NULL));
+ }
+
+ cl_assert_equal_i(expected_count, count);
+ cl_assert_equal_i(expected_count + expected_ignores, count_all);
+
+ cl_git_pass(git_iterator_reset(i, NULL, NULL));
+
+ error = git_iterator_current(&entry, i);
+ cl_assert((error == 0 && entry != NULL) ||
+ (error == GIT_ITEROVER && entry == NULL));
+
+ while (entry != NULL) {
+ if (S_ISDIR(entry->mode)) {
+ cl_git_pass(git_iterator_advance_into(&entry, i));
+ continue;
+ }
+
+ if (expected_names != NULL)
+ cl_assert_equal_s(
+ expected_names[count_all_post_reset], entry->path);
+ count_all_post_reset++;
+
+ error = git_iterator_advance(&entry, i);
+ cl_assert(error == 0 || error == GIT_ITEROVER);
+ }
+
+ cl_assert_equal_i(count_all, count_all_post_reset);
+
+ git_iterator_free(i);
+}
+
+void test_diff_iterator__workdir_0(void)
+{
+ workdir_iterator_test("attr", NULL, NULL, 27, 1, NULL, "ign");
+}
+
+static const char *status_paths[] = {
+ "current_file",
+ "ignored_file",
+ "modified_file",
+ "new_file",
+ "staged_changes",
+ "staged_changes_modified_file",
+ "staged_delete_modified_file",
+ "staged_new_file",
+ "staged_new_file_modified_file",
+ "subdir.txt",
+ "subdir/current_file",
+ "subdir/modified_file",
+ "subdir/new_file",
+ "\xe8\xbf\x99",
+ NULL
+};
+
+void test_diff_iterator__workdir_1(void)
+{
+ workdir_iterator_test(
+ "status", NULL, NULL, 13, 1, status_paths, "ignored_file");
+}
+
+static const char *status_paths_range_0[] = {
+ "staged_changes",
+ "staged_changes_modified_file",
+ "staged_delete_modified_file",
+ "staged_new_file",
+ "staged_new_file_modified_file",
+ NULL
+};
+
+void test_diff_iterator__workdir_1_ranged_0(void)
+{
+ workdir_iterator_test(
+ "status", "staged", "staged", 5, 0, status_paths_range_0, NULL);
+}
+
+static const char *status_paths_range_1[] = {
+ "modified_file", NULL
+};
+
+void test_diff_iterator__workdir_1_ranged_1(void)
+{
+ workdir_iterator_test(
+ "status", "modified_file", "modified_file",
+ 1, 0, status_paths_range_1, NULL);
+}
+
+static const char *status_paths_range_3[] = {
+ "subdir.txt",
+ "subdir/current_file",
+ "subdir/modified_file",
+ NULL
+};
+
+void test_diff_iterator__workdir_1_ranged_3(void)
+{
+ workdir_iterator_test(
+ "status", "subdir", "subdir/modified_file",
+ 3, 0, status_paths_range_3, NULL);
+}
+
+static const char *status_paths_range_4[] = {
+ "subdir/current_file",
+ "subdir/modified_file",
+ "subdir/new_file",
+ "\xe8\xbf\x99",
+ NULL
+};
+
+void test_diff_iterator__workdir_1_ranged_4(void)
+{
+ workdir_iterator_test(
+ "status", "subdir/", NULL, 4, 0, status_paths_range_4, NULL);
+}
+
+static const char *status_paths_range_5[] = {
+ "subdir/modified_file",
+ NULL
+};
+
+void test_diff_iterator__workdir_1_ranged_5(void)
+{
+ workdir_iterator_test(
+ "status", "subdir/modified_file", "subdir/modified_file",
+ 1, 0, status_paths_range_5, NULL);
+}
+
+void test_diff_iterator__workdir_1_ranged_empty_0(void)
+{
+ workdir_iterator_test(
+ "status", "\xff_does_not_exist", NULL,
+ 0, 0, NULL, NULL);
+}
+
+void test_diff_iterator__workdir_1_ranged_empty_1(void)
+{
+ workdir_iterator_test(
+ "status", "empty", "empty",
+ 0, 0, NULL, NULL);
+}
+
+void test_diff_iterator__workdir_1_ranged_empty_2(void)
+{
+ workdir_iterator_test(
+ "status", NULL, "aaaa_empty_before",
+ 0, 0, NULL, NULL);
+}
+
+void test_diff_iterator__workdir_builtin_ignores(void)
+{
+ git_repository *repo = cl_git_sandbox_init("attr");
+ git_iterator *i;
+ const git_index_entry *entry;
+ int idx;
+ static struct {
+ const char *path;
+ bool ignored;
+ } expected[] = {
+ { "dir/", true },
+ { "file", false },
+ { "ign", true },
+ { "macro_bad", false },
+ { "macro_test", false },
+ { "root_test1", false },
+ { "root_test2", false },
+ { "root_test3", false },
+ { "root_test4.txt", false },
+ { "sub", false },
+ { "sub/.gitattributes", false },
+ { "sub/abc", false },
+ { "sub/dir/", true },
+ { "sub/file", false },
+ { "sub/ign/", true },
+ { "sub/sub", false },
+ { "sub/sub/.gitattributes", false },
+ { "sub/sub/dir", false }, /* file is not actually a dir */
+ { "sub/sub/file", false },
+ { NULL, false }
+ };
+
+ cl_git_pass(p_mkdir("attr/sub/sub/.git", 0777));
+ cl_git_mkfile("attr/sub/.git", "whatever");
+
+ cl_git_pass(git_iterator_for_workdir(
+ &i, repo, GIT_ITERATOR_DONT_AUTOEXPAND, "dir", "sub/sub/file"));
+ cl_git_pass(git_iterator_current(&entry, i));
+
+ for (idx = 0; entry != NULL; ++idx) {
+ int ignored = git_iterator_current_is_ignored(i);
+
+ cl_assert_equal_s(expected[idx].path, entry->path);
+ cl_assert_(ignored == expected[idx].ignored, expected[idx].path);
+
+ if (!ignored &&
+ (entry->mode == GIT_FILEMODE_TREE ||
+ entry->mode == GIT_FILEMODE_COMMIT))
+ {
+ /* it is possible to advance "into" a submodule */
+ cl_git_pass(git_iterator_advance_into(&entry, i));
+ } else {
+ int error = git_iterator_advance(&entry, i);
+ cl_assert(!error || error == GIT_ITEROVER);
+ }
+ }
+
+ cl_assert(expected[idx].path == NULL);
+
+ git_iterator_free(i);
+}
+
+static void check_wd_first_through_third_range(
+ git_repository *repo, const char *start, const char *end)
+{
+ git_iterator *i;
+ const git_index_entry *entry;
+ int error, idx;
+ static const char *expected[] = { "FIRST", "second", "THIRD", NULL };
+
+ cl_git_pass(git_iterator_for_workdir(
+ &i, repo, GIT_ITERATOR_IGNORE_CASE, start, end));
+ cl_git_pass(git_iterator_current(&entry, i));
+
+ for (idx = 0; entry != NULL; ++idx) {
+ cl_assert_equal_s(expected[idx], entry->path);
+
+ error = git_iterator_advance(&entry, i);
+ cl_assert(!error || error == GIT_ITEROVER);
+ }
+
+ cl_assert(expected[idx] == NULL);
+
+ git_iterator_free(i);
+}
+
+void test_diff_iterator__workdir_handles_icase_range(void)
+{
+ git_repository *repo;
+
+ repo = cl_git_sandbox_init("empty_standard_repo");
+ cl_git_remove_placeholders(git_repository_path(repo), "dummy-marker.txt");
+
+ cl_git_mkfile("empty_standard_repo/before", "whatever\n");
+ cl_git_mkfile("empty_standard_repo/FIRST", "whatever\n");
+ cl_git_mkfile("empty_standard_repo/second", "whatever\n");
+ cl_git_mkfile("empty_standard_repo/THIRD", "whatever\n");
+ cl_git_mkfile("empty_standard_repo/zafter", "whatever\n");
+ cl_git_mkfile("empty_standard_repo/Zlast", "whatever\n");
+
+ check_wd_first_through_third_range(repo, "first", "third");
+ check_wd_first_through_third_range(repo, "FIRST", "THIRD");
+ check_wd_first_through_third_range(repo, "first", "THIRD");
+ check_wd_first_through_third_range(repo, "FIRST", "third");
+ check_wd_first_through_third_range(repo, "FirSt", "tHiRd");
+}
+
+static void check_tree_range(
+ git_repository *repo,
+ const char *start,
+ const char *end,
+ bool ignore_case,
+ int expected_count)
+{
+ git_tree *head;
+ git_iterator *i;
+ int error, count;
+
+ cl_git_pass(git_repository_head_tree(&head, repo));
+
+ cl_git_pass(git_iterator_for_tree(
+ &i, head,
+ ignore_case ? GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE,
+ start, end));
+
+ for (count = 0; !(error = git_iterator_advance(NULL, i)); ++count)
+ /* count em up */;
+
+ cl_assert_equal_i(GIT_ITEROVER, error);
+ cl_assert_equal_i(expected_count, count);
+
+ git_iterator_free(i);
+ git_tree_free(head);
+}
+
+void test_diff_iterator__tree_handles_icase_range(void)
+{
+ git_repository *repo;
+
+ repo = cl_git_sandbox_init("testrepo");
+
+ check_tree_range(repo, "B", "C", false, 0);
+ check_tree_range(repo, "B", "C", true, 1);
+ check_tree_range(repo, "b", "c", false, 1);
+ check_tree_range(repo, "b", "c", true, 1);
+
+ check_tree_range(repo, "a", "z", false, 3);
+ check_tree_range(repo, "a", "z", true, 4);
+ check_tree_range(repo, "A", "Z", false, 1);
+ check_tree_range(repo, "A", "Z", true, 4);
+ check_tree_range(repo, "a", "Z", false, 0);
+ check_tree_range(repo, "a", "Z", true, 4);
+ check_tree_range(repo, "A", "z", false, 4);
+ check_tree_range(repo, "A", "z", true, 4);
+
+ check_tree_range(repo, "new.txt", "new.txt", true, 1);
+ check_tree_range(repo, "new.txt", "new.txt", false, 1);
+ check_tree_range(repo, "README", "README", true, 1);
+ check_tree_range(repo, "README", "README", false, 1);
+}
+
+static void check_index_range(
+ git_repository *repo,
+ const char *start,
+ const char *end,
+ bool ignore_case,
+ int expected_count)
+{
+ git_index *index;
+ git_iterator *i;
+ int error, count, caps;
+ bool is_ignoring_case;
+
+ cl_git_pass(git_repository_index(&index, repo));
+
+ caps = git_index_caps(index);
+ is_ignoring_case = ((caps & GIT_INDEXCAP_IGNORE_CASE) != 0);
+
+ if (ignore_case != is_ignoring_case)
+ cl_git_pass(git_index_set_caps(index, caps ^ GIT_INDEXCAP_IGNORE_CASE));
+
+ cl_git_pass(git_iterator_for_index(&i, index, 0, start, end));
+
+ cl_assert(git_iterator_ignore_case(i) == ignore_case);
+
+ for (count = 0; !(error = git_iterator_advance(NULL, i)); ++count)
+ /* count em up */;
+
+ cl_assert_equal_i(GIT_ITEROVER, error);
+ cl_assert_equal_i(expected_count, count);
+
+ git_iterator_free(i);
+ git_index_free(index);
+}
+
+void test_diff_iterator__index_handles_icase_range(void)
+{
+ git_repository *repo;
+ git_index *index;
+ git_tree *head;
+
+ repo = cl_git_sandbox_init("testrepo");
+
+ /* reset index to match HEAD */
+ cl_git_pass(git_repository_head_tree(&head, repo));
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_read_tree(index, head));
+ cl_git_pass(git_index_write(index));
+ git_tree_free(head);
+ git_index_free(index);
+
+ /* do some ranged iterator checks toggling case sensitivity */
+ check_index_range(repo, "B", "C", false, 0);
+ check_index_range(repo, "B", "C", true, 1);
+ check_index_range(repo, "a", "z", false, 3);
+ check_index_range(repo, "a", "z", true, 4);
+}
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..366e5da58
--- /dev/null
+++ b/tests/diff/patch.c
@@ -0,0 +1,571 @@
+#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_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_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);
+
+ /* 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_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_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);
+
+ 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/fetchhead/fetchhead_data.h b/tests/fetchhead/fetchhead_data.h
new file mode 100644
index 000000000..294c9fb01
--- /dev/null
+++ b/tests/fetchhead/fetchhead_data.h
@@ -0,0 +1,31 @@
+
+#define FETCH_HEAD_WILDCARD_DATA_LOCAL \
+ "49322bb17d3acc9146f98c97d078513228bbf3c0\t\tbranch 'master' of git://github.com/libgit2/TestGitRepository\n" \
+ "0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of git://github.com/libgit2/TestGitRepository\n" \
+ "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of git://github.com/libgit2/TestGitRepository\n" \
+ "d96c4e80345534eccee5ac7b07fc7603b56124cb\tnot-for-merge\ttag 'annotated_tag' of git://github.com/libgit2/TestGitRepository\n" \
+ "55a1a760df4b86a02094a904dfa511deb5655905\tnot-for-merge\ttag 'blob' of git://github.com/libgit2/TestGitRepository\n" \
+ "8f50ba15d49353813cc6e20298002c0d17b0a9ee\tnot-for-merge\ttag 'commit_tree' of git://github.com/libgit2/TestGitRepository\n"
+
+#define FETCH_HEAD_WILDCARD_DATA \
+ "49322bb17d3acc9146f98c97d078513228bbf3c0\t\tbranch 'master' of git://github.com/libgit2/TestGitRepository\n" \
+ "0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of git://github.com/libgit2/TestGitRepository\n" \
+ "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of git://github.com/libgit2/TestGitRepository\n" \
+ "d96c4e80345534eccee5ac7b07fc7603b56124cb\tnot-for-merge\ttag 'annotated_tag' of git://github.com/libgit2/TestGitRepository\n" \
+ "55a1a760df4b86a02094a904dfa511deb5655905\tnot-for-merge\ttag 'blob' of git://github.com/libgit2/TestGitRepository\n" \
+ "8f50ba15d49353813cc6e20298002c0d17b0a9ee\tnot-for-merge\ttag 'commit_tree' of git://github.com/libgit2/TestGitRepository\n" \
+ "6e0c7bdb9b4ed93212491ee778ca1c65047cab4e\tnot-for-merge\ttag 'nearly-dangling' of git://github.com/libgit2/TestGitRepository\n"
+
+#define FETCH_HEAD_NO_MERGE_DATA \
+ "0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of git://github.com/libgit2/TestGitRepository\n" \
+ "49322bb17d3acc9146f98c97d078513228bbf3c0\tnot-for-merge\tbranch 'master' of git://github.com/libgit2/TestGitRepository\n" \
+ "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of git://github.com/libgit2/TestGitRepository\n" \
+ "d96c4e80345534eccee5ac7b07fc7603b56124cb\tnot-for-merge\ttag 'annotated_tag' of git://github.com/libgit2/TestGitRepository\n" \
+ "55a1a760df4b86a02094a904dfa511deb5655905\tnot-for-merge\ttag 'blob' of git://github.com/libgit2/TestGitRepository\n" \
+ "8f50ba15d49353813cc6e20298002c0d17b0a9ee\tnot-for-merge\ttag 'commit_tree' of git://github.com/libgit2/TestGitRepository\n" \
+ "6e0c7bdb9b4ed93212491ee778ca1c65047cab4e\tnot-for-merge\ttag 'nearly-dangling' of git://github.com/libgit2/TestGitRepository\n"
+
+
+#define FETCH_HEAD_EXPLICIT_DATA \
+ "0966a434eb1a025db6b71485ab63a3bfbea520b6\t\tbranch 'first-merge' of git://github.com/libgit2/TestGitRepository\n"
+
diff --git a/tests/fetchhead/nonetwork.c b/tests/fetchhead/nonetwork.c
new file mode 100644
index 000000000..a68ebb0b7
--- /dev/null
+++ b/tests/fetchhead/nonetwork.c
@@ -0,0 +1,309 @@
+#include "clar_libgit2.h"
+
+#include "fileops.h"
+#include "fetchhead.h"
+
+#include "fetchhead_data.h"
+
+#define DO_LOCAL_TEST 0
+
+static git_repository *g_repo;
+
+void test_fetchhead_nonetwork__initialize(void)
+{
+ g_repo = NULL;
+}
+
+static void cleanup_repository(void *path)
+{
+ if (g_repo) {
+ git_repository_free(g_repo);
+ g_repo = NULL;
+ }
+
+ cl_fixture_cleanup((const char *)path);
+}
+
+static void populate_fetchhead(git_vector *out, git_repository *repo)
+{
+ git_fetchhead_ref *fetchhead_ref;
+ git_oid oid;
+
+ cl_git_pass(git_oid_fromstr(&oid,
+ "49322bb17d3acc9146f98c97d078513228bbf3c0"));
+ cl_git_pass(git_fetchhead_ref_create(&fetchhead_ref, &oid, 1,
+ "refs/heads/master",
+ "git://github.com/libgit2/TestGitRepository"));
+ cl_git_pass(git_vector_insert(out, fetchhead_ref));
+
+ cl_git_pass(git_oid_fromstr(&oid,
+ "0966a434eb1a025db6b71485ab63a3bfbea520b6"));
+ cl_git_pass(git_fetchhead_ref_create(&fetchhead_ref, &oid, 0,
+ "refs/heads/first-merge",
+ "git://github.com/libgit2/TestGitRepository"));
+ cl_git_pass(git_vector_insert(out, fetchhead_ref));
+
+ cl_git_pass(git_oid_fromstr(&oid,
+ "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1"));
+ cl_git_pass(git_fetchhead_ref_create(&fetchhead_ref, &oid, 0,
+ "refs/heads/no-parent",
+ "git://github.com/libgit2/TestGitRepository"));
+ cl_git_pass(git_vector_insert(out, fetchhead_ref));
+
+ cl_git_pass(git_oid_fromstr(&oid,
+ "d96c4e80345534eccee5ac7b07fc7603b56124cb"));
+ cl_git_pass(git_fetchhead_ref_create(&fetchhead_ref, &oid, 0,
+ "refs/tags/annotated_tag",
+ "git://github.com/libgit2/TestGitRepository"));
+ cl_git_pass(git_vector_insert(out, fetchhead_ref));
+
+ cl_git_pass(git_oid_fromstr(&oid,
+ "55a1a760df4b86a02094a904dfa511deb5655905"));
+ cl_git_pass(git_fetchhead_ref_create(&fetchhead_ref, &oid, 0,
+ "refs/tags/blob",
+ "git://github.com/libgit2/TestGitRepository"));
+ cl_git_pass(git_vector_insert(out, fetchhead_ref));
+
+ cl_git_pass(git_oid_fromstr(&oid,
+ "8f50ba15d49353813cc6e20298002c0d17b0a9ee"));
+ cl_git_pass(git_fetchhead_ref_create(&fetchhead_ref, &oid, 0,
+ "refs/tags/commit_tree",
+ "git://github.com/libgit2/TestGitRepository"));
+ cl_git_pass(git_vector_insert(out, fetchhead_ref));
+
+ cl_git_pass(git_fetchhead_write(repo, out));
+}
+
+void test_fetchhead_nonetwork__write(void)
+{
+ git_vector fetchhead_vector = GIT_VECTOR_INIT;
+ git_fetchhead_ref *fetchhead_ref;
+ git_buf fetchhead_buf = GIT_BUF_INIT;
+ int equals = 0;
+ size_t i;
+
+ git_vector_init(&fetchhead_vector, 6, NULL);
+
+ cl_set_cleanup(&cleanup_repository, "./test1");
+ cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
+
+ populate_fetchhead(&fetchhead_vector, g_repo);
+
+ cl_git_pass(git_futils_readbuffer(&fetchhead_buf,
+ "./test1/.git/FETCH_HEAD"));
+
+ equals = (strcmp(fetchhead_buf.ptr, FETCH_HEAD_WILDCARD_DATA_LOCAL) == 0);
+
+ git_buf_free(&fetchhead_buf);
+
+ git_vector_foreach(&fetchhead_vector, i, fetchhead_ref) {
+ git_fetchhead_ref_free(fetchhead_ref);
+ }
+
+ git_vector_free(&fetchhead_vector);
+
+ cl_assert(equals);
+}
+
+typedef struct {
+ git_vector *fetchhead_vector;
+ size_t idx;
+} fetchhead_ref_cb_data;
+
+static int fetchhead_ref_cb(const char *name, const char *url,
+ const git_oid *oid, unsigned int is_merge, void *payload)
+{
+ fetchhead_ref_cb_data *cb_data = payload;
+ git_fetchhead_ref *expected;
+
+ cl_assert(payload);
+
+ expected = git_vector_get(cb_data->fetchhead_vector, cb_data->idx);
+
+ cl_assert(git_oid_cmp(&expected->oid, oid) == 0);
+ cl_assert(expected->is_merge == is_merge);
+
+ if (expected->ref_name)
+ cl_assert_equal_s(expected->ref_name, name);
+ else
+ cl_assert(name == NULL);
+
+ if (expected->remote_url)
+ cl_assert_equal_s(expected->remote_url, url);
+ else
+ cl_assert(url == NULL);
+
+ cb_data->idx++;
+
+ return 0;
+}
+
+void test_fetchhead_nonetwork__read(void)
+{
+ git_vector fetchhead_vector = GIT_VECTOR_INIT;
+ git_fetchhead_ref *fetchhead_ref;
+ fetchhead_ref_cb_data cb_data;
+ size_t i;
+
+ memset(&cb_data, 0x0, sizeof(fetchhead_ref_cb_data));
+
+ cl_set_cleanup(&cleanup_repository, "./test1");
+ cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
+
+ populate_fetchhead(&fetchhead_vector, g_repo);
+
+ cb_data.fetchhead_vector = &fetchhead_vector;
+
+ cl_git_pass(git_repository_fetchhead_foreach(g_repo, fetchhead_ref_cb, &cb_data));
+
+ git_vector_foreach(&fetchhead_vector, i, fetchhead_ref) {
+ git_fetchhead_ref_free(fetchhead_ref);
+ }
+
+ git_vector_free(&fetchhead_vector);
+}
+
+static int read_old_style_cb(const char *name, const char *url,
+ const git_oid *oid, unsigned int is_merge, void *payload)
+{
+ git_oid expected;
+
+ GIT_UNUSED(payload);
+
+ git_oid_fromstr(&expected, "49322bb17d3acc9146f98c97d078513228bbf3c0");
+
+ cl_assert(name == NULL);
+ cl_assert(url == NULL);
+ cl_assert(git_oid_cmp(&expected, oid) == 0);
+ cl_assert(is_merge == 1);
+
+ return 0;
+}
+
+void test_fetchhead_nonetwork__read_old_style(void)
+{
+ cl_set_cleanup(&cleanup_repository, "./test1");
+ cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
+
+ cl_git_rewritefile("./test1/.git/FETCH_HEAD", "49322bb17d3acc9146f98c97d078513228bbf3c0\n");
+
+ cl_git_pass(git_repository_fetchhead_foreach(g_repo, read_old_style_cb, NULL));
+}
+
+static int read_type_missing(const char *ref_name, const char *remote_url,
+ const git_oid *oid, unsigned int is_merge, void *payload)
+{
+ git_oid expected;
+
+ GIT_UNUSED(payload);
+
+ git_oid_fromstr(&expected, "49322bb17d3acc9146f98c97d078513228bbf3c0");
+
+ cl_assert_equal_s("name", ref_name);
+ cl_assert_equal_s("remote_url", remote_url);
+ cl_assert(git_oid_cmp(&expected, oid) == 0);
+ cl_assert(is_merge == 0);
+
+ return 0;
+}
+
+void test_fetchhead_nonetwork__type_missing(void)
+{
+ cl_set_cleanup(&cleanup_repository, "./test1");
+ cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
+
+ cl_git_rewritefile("./test1/.git/FETCH_HEAD", "49322bb17d3acc9146f98c97d078513228bbf3c0\tnot-for-merge\t'name' of remote_url\n");
+
+ cl_git_pass(git_repository_fetchhead_foreach(g_repo, read_type_missing, NULL));
+}
+
+static int read_name_missing(const char *ref_name, const char *remote_url,
+ const git_oid *oid, unsigned int is_merge, void *payload)
+{
+ git_oid expected;
+
+ GIT_UNUSED(payload);
+
+ git_oid_fromstr(&expected, "49322bb17d3acc9146f98c97d078513228bbf3c0");
+
+ cl_assert(ref_name == NULL);
+ cl_assert_equal_s("remote_url", remote_url);
+ cl_assert(git_oid_cmp(&expected, oid) == 0);
+ cl_assert(is_merge == 0);
+
+ return 0;
+}
+
+void test_fetchhead_nonetwork__name_missing(void)
+{
+ cl_set_cleanup(&cleanup_repository, "./test1");
+ cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
+
+ cl_git_rewritefile("./test1/.git/FETCH_HEAD", "49322bb17d3acc9146f98c97d078513228bbf3c0\tnot-for-merge\tremote_url\n");
+
+ cl_git_pass(git_repository_fetchhead_foreach(g_repo, read_name_missing, NULL));
+}
+
+static int read_noop(const char *ref_name, const char *remote_url,
+ const git_oid *oid, unsigned int is_merge, void *payload)
+{
+ GIT_UNUSED(ref_name);
+ GIT_UNUSED(remote_url);
+ GIT_UNUSED(oid);
+ GIT_UNUSED(is_merge);
+ GIT_UNUSED(payload);
+
+ return 0;
+}
+
+void test_fetchhead_nonetwork__nonexistent(void)
+{
+ int error;
+
+ cl_set_cleanup(&cleanup_repository, "./test1");
+ cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
+
+ cl_git_fail((error = git_repository_fetchhead_foreach(g_repo, read_noop, NULL)));
+ cl_assert(error == GIT_ENOTFOUND);
+}
+
+void test_fetchhead_nonetwork__invalid_unterminated_last_line(void)
+{
+ cl_set_cleanup(&cleanup_repository, "./test1");
+ cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
+
+ cl_git_rewritefile("./test1/.git/FETCH_HEAD", "unterminated");
+ cl_git_fail(git_repository_fetchhead_foreach(g_repo, read_noop, NULL));
+}
+
+void test_fetchhead_nonetwork__invalid_oid(void)
+{
+ cl_set_cleanup(&cleanup_repository, "./test1");
+ cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
+
+ cl_git_rewritefile("./test1/.git/FETCH_HEAD", "shortoid\n");
+ cl_git_fail(git_repository_fetchhead_foreach(g_repo, read_noop, NULL));
+}
+
+void test_fetchhead_nonetwork__invalid_for_merge(void)
+{
+ cl_set_cleanup(&cleanup_repository, "./test1");
+ cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
+
+ cl_git_rewritefile("./test1/.git/FETCH_HEAD", "49322bb17d3acc9146f98c97d078513228bbf3c0\tinvalid-merge\t\n");
+ cl_git_fail(git_repository_fetchhead_foreach(g_repo, read_noop, NULL));
+
+ cl_assert(git__prefixcmp(giterr_last()->message, "Invalid for-merge") == 0);
+}
+
+void test_fetchhead_nonetwork__invalid_description(void)
+{
+ cl_set_cleanup(&cleanup_repository, "./test1");
+ cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
+
+ cl_git_rewritefile("./test1/.git/FETCH_HEAD", "49322bb17d3acc9146f98c97d078513228bbf3c0\tnot-for-merge\n");
+ cl_git_fail(git_repository_fetchhead_foreach(g_repo, read_noop, NULL));
+
+ cl_assert(git__prefixcmp(giterr_last()->message, "Invalid description") == 0);
+}
+
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/generate.py b/tests/generate.py
new file mode 100644
index 000000000..d4fe8f2a3
--- /dev/null
+++ b/tests/generate.py
@@ -0,0 +1,244 @@
+#!/usr/bin/env python
+#
+# Copyright (c) Vicent Marti. All rights reserved.
+#
+# This file is part of clar, distributed under the ISC license.
+# For full terms see the included COPYING file.
+#
+
+from __future__ import with_statement
+from string import Template
+import re, fnmatch, os, codecs, pickle
+
+class Module(object):
+ class Template(object):
+ def __init__(self, module):
+ self.module = module
+
+ def _render_callback(self, cb):
+ if not cb:
+ return ' { NULL, NULL }'
+ return ' { "%s", &%s }' % (cb['short_name'], cb['symbol'])
+
+ class DeclarationTemplate(Template):
+ def render(self):
+ out = "\n".join("extern %s;" % cb['declaration'] for cb in self.module.callbacks) + "\n"
+
+ if self.module.initialize:
+ out += "extern %s;\n" % self.module.initialize['declaration']
+
+ if self.module.cleanup:
+ out += "extern %s;\n" % self.module.cleanup['declaration']
+
+ return out
+
+ class CallbacksTemplate(Template):
+ def render(self):
+ out = "static const struct clar_func _clar_cb_%s[] = {\n" % self.module.name
+ out += ",\n".join(self._render_callback(cb) for cb in self.module.callbacks)
+ out += "\n};\n"
+ return out
+
+ class InfoTemplate(Template):
+ def render(self):
+ return Template(
+ r"""
+ {
+ "${clean_name}",
+ ${initialize},
+ ${cleanup},
+ ${cb_ptr}, ${cb_count}, ${enabled}
+ }"""
+ ).substitute(
+ clean_name = self.module.clean_name(),
+ initialize = self._render_callback(self.module.initialize),
+ cleanup = self._render_callback(self.module.cleanup),
+ cb_ptr = "_clar_cb_%s" % self.module.name,
+ cb_count = len(self.module.callbacks),
+ enabled = int(self.module.enabled)
+ )
+
+ def __init__(self, name):
+ self.name = name
+
+ self.mtime = 0
+ self.enabled = True
+ self.modified = False
+
+ def clean_name(self):
+ return self.name.replace("_", "::")
+
+ def _skip_comments(self, text):
+ SKIP_COMMENTS_REGEX = re.compile(
+ r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
+ re.DOTALL | re.MULTILINE)
+
+ def _replacer(match):
+ s = match.group(0)
+ return "" if s.startswith('/') else s
+
+ return re.sub(SKIP_COMMENTS_REGEX, _replacer, text)
+
+ def parse(self, contents):
+ TEST_FUNC_REGEX = r"^(void\s+(test_%s__(\w+))\(\s*void\s*\))\s*\{"
+
+ contents = self._skip_comments(contents)
+ regex = re.compile(TEST_FUNC_REGEX % self.name, re.MULTILINE)
+
+ self.callbacks = []
+ self.initialize = None
+ self.cleanup = None
+
+ for (declaration, symbol, short_name) in regex.findall(contents):
+ data = {
+ "short_name" : short_name,
+ "declaration" : declaration,
+ "symbol" : symbol
+ }
+
+ if short_name == 'initialize':
+ self.initialize = data
+ elif short_name == 'cleanup':
+ self.cleanup = data
+ else:
+ self.callbacks.append(data)
+
+ return self.callbacks != []
+
+ def refresh(self, path):
+ self.modified = False
+
+ try:
+ st = os.stat(path)
+
+ # Not modified
+ if st.st_mtime == self.mtime:
+ return True
+
+ self.modified = True
+ self.mtime = st.st_mtime
+
+ with open(path) as fp:
+ raw_content = fp.read()
+
+ except IOError:
+ return False
+
+ return self.parse(raw_content)
+
+class TestSuite(object):
+
+ def __init__(self, path):
+ self.path = path
+
+ def should_generate(self, path):
+ if not os.path.isfile(path):
+ return True
+
+ if any(module.modified for module in self.modules.values()):
+ return True
+
+ return False
+
+ def find_modules(self):
+ modules = []
+ for root, _, files in os.walk(self.path):
+ module_root = root[len(self.path):]
+ module_root = [c for c in module_root.split(os.sep) if c]
+
+ tests_in_module = fnmatch.filter(files, "*.c")
+
+ for test_file in tests_in_module:
+ full_path = os.path.join(root, test_file)
+ module_name = "_".join(module_root + [test_file[:-2]])
+
+ modules.append((full_path, module_name))
+
+ return modules
+
+ def load_cache(self):
+ path = os.path.join(self.path, '.clarcache')
+ cache = {}
+
+ try:
+ fp = open(path, 'rb')
+ cache = pickle.load(fp)
+ fp.close()
+ except (IOError, ValueError):
+ pass
+
+ return cache
+
+ def save_cache(self):
+ path = os.path.join(self.path, '.clarcache')
+ with open(path, 'wb') as cache:
+ pickle.dump(self.modules, cache)
+
+ def load(self, force = False):
+ module_data = self.find_modules()
+ self.modules = {} if force else self.load_cache()
+
+ for path, name in module_data:
+ if name not in self.modules:
+ self.modules[name] = Module(name)
+
+ if not self.modules[name].refresh(path):
+ del self.modules[name]
+
+ def disable(self, excluded):
+ for exclude in excluded:
+ for module in self.modules.values():
+ name = module.clean_name()
+ if name.startswith(exclude):
+ module.enabled = False
+ module.modified = True
+
+ def suite_count(self):
+ return len(self.modules)
+
+ def callback_count(self):
+ return sum(len(module.callbacks) for module in self.modules.values())
+
+ def write(self):
+ output = os.path.join(self.path, 'clar.suite')
+
+ if not self.should_generate(output):
+ return False
+
+ with open(output, 'w') as data:
+ for module in self.modules.values():
+ t = Module.DeclarationTemplate(module)
+ data.write(t.render())
+
+ for module in self.modules.values():
+ t = Module.CallbacksTemplate(module)
+ data.write(t.render())
+
+ suites = "static struct clar_suite _clar_suites[] = {" + ','.join(
+ Module.InfoTemplate(module).render() for module in sorted(self.modules.values(), key=lambda module: module.name)
+ ) + "\n};\n"
+
+ data.write(suites)
+
+ data.write("static const size_t _clar_suite_count = %d;\n" % self.suite_count())
+ data.write("static const size_t _clar_callback_count = %d;\n" % self.callback_count())
+
+ suite.save_cache()
+ return True
+
+if __name__ == '__main__':
+ from optparse import OptionParser
+
+ parser = OptionParser()
+ parser.add_option('-f', '--force', dest='force', default=False)
+ parser.add_option('-x', '--exclude', dest='excluded', action='append', default=[])
+
+ options, args = parser.parse_args()
+
+ for path in args or ['.']:
+ suite = TestSuite(path)
+ suite.load(options.force)
+ suite.disable(options.excluded)
+ if suite.write():
+ print("Written `clar.suite` (%d tests in %d suites)" % (suite.callback_count(), suite.suite_count()))
+
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/index/conflicts.c b/tests/index/conflicts.c
new file mode 100644
index 000000000..6311b3a75
--- /dev/null
+++ b/tests/index/conflicts.c
@@ -0,0 +1,287 @@
+#include "clar_libgit2.h"
+#include "index.h"
+#include "git2/repository.h"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+#define TEST_REPO_PATH "mergedrepo"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+#define CONFLICTS_ONE_ANCESTOR_OID "1f85ca51b8e0aac893a621b61a9c2661d6aa6d81"
+#define CONFLICTS_ONE_OUR_OID "6aea5f295304c36144ad6e9247a291b7f8112399"
+#define CONFLICTS_ONE_THEIR_OID "516bd85f78061e09ccc714561d7b504672cb52da"
+
+#define CONFLICTS_TWO_ANCESTOR_OID "84af62840be1b1c47b778a8a249f3ff45155038c"
+#define CONFLICTS_TWO_OUR_OID "8b3f43d2402825c200f835ca1762413e386fd0b2"
+#define CONFLICTS_TWO_THEIR_OID "220bd62631c8cf7a83ef39c6b94595f00517211e"
+
+#define TEST_ANCESTOR_OID "f00ff00ff00ff00ff00ff00ff00ff00ff00ff00f"
+#define TEST_OUR_OID "b44bb44bb44bb44bb44bb44bb44bb44bb44bb44b"
+#define TEST_THEIR_OID "0123456789abcdef0123456789abcdef01234567"
+
+// Fixture setup and teardown
+void test_index_conflicts__initialize(void)
+{
+ repo = cl_git_sandbox_init("mergedrepo");
+ git_repository_index(&repo_index, repo);
+}
+
+void test_index_conflicts__cleanup(void)
+{
+ git_index_free(repo_index);
+ repo_index = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_index_conflicts__add(void)
+{
+ git_index_entry ancestor_entry, our_entry, their_entry;
+
+ cl_assert(git_index_entrycount(repo_index) == 8);
+
+ memset(&ancestor_entry, 0x0, sizeof(git_index_entry));
+ memset(&our_entry, 0x0, sizeof(git_index_entry));
+ memset(&their_entry, 0x0, sizeof(git_index_entry));
+
+ ancestor_entry.path = "test-one.txt";
+ ancestor_entry.flags |= (1 << GIT_IDXENTRY_STAGESHIFT);
+ git_oid_fromstr(&ancestor_entry.oid, TEST_ANCESTOR_OID);
+
+ our_entry.path = "test-one.txt";
+ ancestor_entry.flags |= (2 << GIT_IDXENTRY_STAGESHIFT);
+ git_oid_fromstr(&our_entry.oid, TEST_OUR_OID);
+
+ their_entry.path = "test-one.txt";
+ ancestor_entry.flags |= (3 << GIT_IDXENTRY_STAGESHIFT);
+ git_oid_fromstr(&their_entry.oid, TEST_THEIR_OID);
+
+ cl_git_pass(git_index_conflict_add(repo_index, &ancestor_entry, &our_entry, &their_entry));
+
+ cl_assert(git_index_entrycount(repo_index) == 11);
+}
+
+void test_index_conflicts__add_fixes_incorrect_stage(void)
+{
+ git_index_entry ancestor_entry, our_entry, their_entry;
+ const git_index_entry *conflict_entry[3];
+
+ cl_assert(git_index_entrycount(repo_index) == 8);
+
+ memset(&ancestor_entry, 0x0, sizeof(git_index_entry));
+ memset(&our_entry, 0x0, sizeof(git_index_entry));
+ memset(&their_entry, 0x0, sizeof(git_index_entry));
+
+ ancestor_entry.path = "test-one.txt";
+ ancestor_entry.flags |= (3 << GIT_IDXENTRY_STAGESHIFT);
+ git_oid_fromstr(&ancestor_entry.oid, TEST_ANCESTOR_OID);
+
+ our_entry.path = "test-one.txt";
+ ancestor_entry.flags |= (1 << GIT_IDXENTRY_STAGESHIFT);
+ git_oid_fromstr(&our_entry.oid, TEST_OUR_OID);
+
+ their_entry.path = "test-one.txt";
+ ancestor_entry.flags |= (2 << GIT_IDXENTRY_STAGESHIFT);
+ git_oid_fromstr(&their_entry.oid, TEST_THEIR_OID);
+
+ cl_git_pass(git_index_conflict_add(repo_index, &ancestor_entry, &our_entry, &their_entry));
+
+ cl_assert(git_index_entrycount(repo_index) == 11);
+
+ cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1], &conflict_entry[2], repo_index, "test-one.txt"));
+
+ cl_assert(git_index_entry_stage(conflict_entry[0]) == 1);
+ cl_assert(git_index_entry_stage(conflict_entry[1]) == 2);
+ cl_assert(git_index_entry_stage(conflict_entry[2]) == 3);
+}
+
+void test_index_conflicts__get(void)
+{
+ const git_index_entry *conflict_entry[3];
+ git_oid oid;
+
+ cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1],
+ &conflict_entry[2], repo_index, "conflicts-one.txt"));
+
+ cl_assert_equal_s("conflicts-one.txt", conflict_entry[0]->path);
+
+ git_oid_fromstr(&oid, CONFLICTS_ONE_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&conflict_entry[0]->oid, &oid) == 0);
+
+ git_oid_fromstr(&oid, CONFLICTS_ONE_OUR_OID);
+ cl_assert(git_oid_cmp(&conflict_entry[1]->oid, &oid) == 0);
+
+ git_oid_fromstr(&oid, CONFLICTS_ONE_THEIR_OID);
+ cl_assert(git_oid_cmp(&conflict_entry[2]->oid, &oid) == 0);
+
+ cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1],
+ &conflict_entry[2], repo_index, "conflicts-two.txt"));
+
+ cl_assert_equal_s("conflicts-two.txt", conflict_entry[0]->path);
+
+ git_oid_fromstr(&oid, CONFLICTS_TWO_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&conflict_entry[0]->oid, &oid) == 0);
+
+ git_oid_fromstr(&oid, CONFLICTS_TWO_OUR_OID);
+ cl_assert(git_oid_cmp(&conflict_entry[1]->oid, &oid) == 0);
+
+ git_oid_fromstr(&oid, CONFLICTS_TWO_THEIR_OID);
+ cl_assert(git_oid_cmp(&conflict_entry[2]->oid, &oid) == 0);
+}
+
+void test_index_conflicts__iterate(void)
+{
+ git_index_conflict_iterator *iterator;
+ const git_index_entry *conflict_entry[3];
+ git_oid oid;
+
+ cl_git_pass(git_index_conflict_iterator_new(&iterator, repo_index));
+
+ cl_git_pass(git_index_conflict_next(&conflict_entry[0], &conflict_entry[1], &conflict_entry[2], iterator));
+
+ git_oid_fromstr(&oid, CONFLICTS_ONE_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&conflict_entry[0]->oid, &oid) == 0);
+ cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-one.txt") == 0);
+
+ git_oid_fromstr(&oid, CONFLICTS_ONE_OUR_OID);
+ cl_assert(git_oid_cmp(&conflict_entry[1]->oid, &oid) == 0);
+ cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-one.txt") == 0);
+
+ git_oid_fromstr(&oid, CONFLICTS_ONE_THEIR_OID);
+ cl_assert(git_oid_cmp(&conflict_entry[2]->oid, &oid) == 0);
+ cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-one.txt") == 0);
+
+ cl_git_pass(git_index_conflict_next(&conflict_entry[0], &conflict_entry[1], &conflict_entry[2], iterator));
+
+ git_oid_fromstr(&oid, CONFLICTS_TWO_ANCESTOR_OID);
+ cl_assert(git_oid_cmp(&conflict_entry[0]->oid, &oid) == 0);
+ cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-two.txt") == 0);
+
+ git_oid_fromstr(&oid, CONFLICTS_TWO_OUR_OID);
+ cl_assert(git_oid_cmp(&conflict_entry[1]->oid, &oid) == 0);
+ cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-two.txt") == 0);
+
+ git_oid_fromstr(&oid, CONFLICTS_TWO_THEIR_OID);
+ cl_assert(git_oid_cmp(&conflict_entry[2]->oid, &oid) == 0);
+ cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-two.txt") == 0);
+
+ cl_assert(git_index_conflict_next(&conflict_entry[0], &conflict_entry[1], &conflict_entry[2], iterator) == GIT_ITEROVER);
+
+ cl_assert(conflict_entry[0] == NULL);
+ cl_assert(conflict_entry[2] == NULL);
+ cl_assert(conflict_entry[2] == NULL);
+
+ git_index_conflict_iterator_free(iterator);
+}
+
+void test_index_conflicts__remove(void)
+{
+ const git_index_entry *entry;
+ size_t i;
+
+ cl_assert(git_index_entrycount(repo_index) == 8);
+
+ cl_git_pass(git_index_conflict_remove(repo_index, "conflicts-one.txt"));
+ cl_assert(git_index_entrycount(repo_index) == 5);
+
+ for (i = 0; i < git_index_entrycount(repo_index); i++) {
+ cl_assert(entry = git_index_get_byindex(repo_index, i));
+ cl_assert(strcmp(entry->path, "conflicts-one.txt") != 0);
+ }
+
+ cl_git_pass(git_index_conflict_remove(repo_index, "conflicts-two.txt"));
+ cl_assert(git_index_entrycount(repo_index) == 2);
+
+ for (i = 0; i < git_index_entrycount(repo_index); i++) {
+ cl_assert(entry = git_index_get_byindex(repo_index, i));
+ cl_assert(strcmp(entry->path, "conflicts-two.txt") != 0);
+ }
+}
+
+void test_index_conflicts__moved_to_reuc_on_add(void)
+{
+ const git_index_entry *entry;
+ size_t i;
+
+ cl_assert(git_index_entrycount(repo_index) == 8);
+
+ cl_git_mkfile("./mergedrepo/conflicts-one.txt", "new-file\n");
+
+ cl_git_pass(git_index_add_bypath(repo_index, "conflicts-one.txt"));
+
+ cl_assert(git_index_entrycount(repo_index) == 6);
+
+ for (i = 0; i < git_index_entrycount(repo_index); i++) {
+ cl_assert(entry = git_index_get_byindex(repo_index, i));
+
+ if (strcmp(entry->path, "conflicts-one.txt") == 0)
+ cl_assert(git_index_entry_stage(entry) == 0);
+ }
+}
+
+void test_index_conflicts__moved_to_reuc_on_remove(void)
+{
+ const git_index_entry *entry;
+ size_t i;
+
+ cl_assert(git_index_entrycount(repo_index) == 8);
+
+ cl_git_pass(p_unlink("./mergedrepo/conflicts-one.txt"));
+
+ cl_git_pass(git_index_remove_bypath(repo_index, "conflicts-one.txt"));
+
+ cl_assert(git_index_entrycount(repo_index) == 5);
+
+ for (i = 0; i < git_index_entrycount(repo_index); i++) {
+ cl_assert(entry = git_index_get_byindex(repo_index, i));
+ cl_assert(strcmp(entry->path, "conflicts-one.txt") != 0);
+ }
+}
+
+void test_index_conflicts__remove_all_conflicts(void)
+{
+ size_t i;
+ const git_index_entry *entry;
+
+ cl_assert(git_index_entrycount(repo_index) == 8);
+
+ cl_assert_equal_i(true, git_index_has_conflicts(repo_index));
+
+ git_index_conflict_cleanup(repo_index);
+
+ cl_assert_equal_i(false, git_index_has_conflicts(repo_index));
+
+ cl_assert(git_index_entrycount(repo_index) == 2);
+
+ for (i = 0; i < git_index_entrycount(repo_index); i++) {
+ cl_assert(entry = git_index_get_byindex(repo_index, i));
+ cl_assert(git_index_entry_stage(entry) == 0);
+ }
+}
+
+void test_index_conflicts__partial(void)
+{
+ git_index_entry ancestor_entry, our_entry, their_entry;
+ const git_index_entry *conflict_entry[3];
+
+ cl_assert(git_index_entrycount(repo_index) == 8);
+
+ memset(&ancestor_entry, 0x0, sizeof(git_index_entry));
+ memset(&our_entry, 0x0, sizeof(git_index_entry));
+ memset(&their_entry, 0x0, sizeof(git_index_entry));
+
+ ancestor_entry.path = "test-one.txt";
+ ancestor_entry.flags |= (1 << GIT_IDXENTRY_STAGESHIFT);
+ git_oid_fromstr(&ancestor_entry.oid, TEST_ANCESTOR_OID);
+
+ cl_git_pass(git_index_conflict_add(repo_index, &ancestor_entry, NULL, NULL));
+ cl_assert(git_index_entrycount(repo_index) == 9);
+
+ cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1],
+ &conflict_entry[2], repo_index, "test-one.txt"));
+
+ cl_assert(git_oid_cmp(&ancestor_entry.oid, &conflict_entry[0]->oid) == 0);
+ cl_assert(conflict_entry[1] == NULL);
+ cl_assert(conflict_entry[2] == NULL);
+}
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/index/inmemory.c b/tests/index/inmemory.c
new file mode 100644
index 000000000..38e91e0fd
--- /dev/null
+++ b/tests/index/inmemory.c
@@ -0,0 +1,22 @@
+#include "clar_libgit2.h"
+
+void test_index_inmemory__can_create_an_inmemory_index(void)
+{
+ git_index *index;
+
+ cl_git_pass(git_index_new(&index));
+ cl_assert_equal_i(0, (int)git_index_entrycount(index));
+
+ git_index_free(index);
+}
+
+void test_index_inmemory__cannot_add_bypath_to_an_inmemory_index(void)
+{
+ git_index *index;
+
+ cl_git_pass(git_index_new(&index));
+
+ cl_assert_equal_i(GIT_ERROR, git_index_add_bypath(index, "test.txt"));
+
+ git_index_free(index);
+}
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/index/read_tree.c b/tests/index/read_tree.c
new file mode 100644
index 000000000..6c6b40121
--- /dev/null
+++ b/tests/index/read_tree.c
@@ -0,0 +1,46 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+
+/* Test that reading and writing a tree is a no-op */
+void test_index_read_tree__read_write_involution(void)
+{
+ git_repository *repo;
+ git_index *index;
+ git_oid tree_oid;
+ git_tree *tree;
+ git_oid expected;
+
+ p_mkdir("read_tree", 0700);
+
+ cl_git_pass(git_repository_init(&repo, "./read_tree", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+
+ cl_assert(git_index_entrycount(index) == 0);
+
+ p_mkdir("./read_tree/abc", 0700);
+
+ /* Sort order: '-' < '/' < '_' */
+ cl_git_mkfile("./read_tree/abc-d", NULL);
+ cl_git_mkfile("./read_tree/abc/d", NULL);
+ cl_git_mkfile("./read_tree/abc_d", NULL);
+
+ cl_git_pass(git_index_add_bypath(index, "abc-d"));
+ cl_git_pass(git_index_add_bypath(index, "abc_d"));
+ cl_git_pass(git_index_add_bypath(index, "abc/d"));
+
+ /* write-tree */
+ cl_git_pass(git_index_write_tree(&expected, index));
+
+ /* read-tree */
+ git_tree_lookup(&tree, repo, &expected);
+ cl_git_pass(git_index_read_tree(index, tree));
+ git_tree_free(tree);
+
+ cl_git_pass(git_index_write_tree(&tree_oid, index));
+ cl_assert(git_oid_cmp(&expected, &tree_oid) == 0);
+
+ git_index_free(index);
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("read_tree");
+}
diff --git a/tests/index/rename.c b/tests/index/rename.c
new file mode 100644
index 000000000..4deef1332
--- /dev/null
+++ b/tests/index/rename.c
@@ -0,0 +1,50 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+
+void test_index_rename__single_file(void)
+{
+ git_repository *repo;
+ git_index *index;
+ size_t position;
+ git_oid expected;
+ const git_index_entry *entry;
+
+ p_mkdir("rename", 0700);
+
+ cl_git_pass(git_repository_init(&repo, "./rename", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+
+ cl_assert(git_index_entrycount(index) == 0);
+
+ cl_git_mkfile("./rename/lame.name.txt", "new_file\n");
+
+ /* This should add a new blob to the object database in 'd4/fa8600b4f37d7516bef4816ae2c64dbf029e3a' */
+ cl_git_pass(git_index_add_bypath(index, "lame.name.txt"));
+ cl_assert(git_index_entrycount(index) == 1);
+
+ cl_git_pass(git_oid_fromstr(&expected, "d4fa8600b4f37d7516bef4816ae2c64dbf029e3a"));
+
+ cl_assert(!git_index_find(&position, index, "lame.name.txt"));
+
+ entry = git_index_get_byindex(index, position);
+ cl_assert(git_oid_cmp(&expected, &entry->oid) == 0);
+
+ /* This removes the entry from the index, but not from the object database */
+ cl_git_pass(git_index_remove(index, "lame.name.txt", 0));
+ cl_assert(git_index_entrycount(index) == 0);
+
+ p_rename("./rename/lame.name.txt", "./rename/fancy.name.txt");
+
+ cl_git_pass(git_index_add_bypath(index, "fancy.name.txt"));
+ cl_assert(git_index_entrycount(index) == 1);
+
+ cl_assert(!git_index_find(&position, index, "fancy.name.txt"));
+
+ entry = git_index_get_byindex(index, position);
+ cl_assert(git_oid_cmp(&expected, &entry->oid) == 0);
+
+ git_index_free(index);
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("rename");
+}
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/index/stage.c b/tests/index/stage.c
new file mode 100644
index 000000000..58dc1fb5e
--- /dev/null
+++ b/tests/index/stage.c
@@ -0,0 +1,62 @@
+#include "clar_libgit2.h"
+#include "index.h"
+#include "git2/repository.h"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+#define TEST_REPO_PATH "mergedrepo"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+// Fixture setup and teardown
+void test_index_stage__initialize(void)
+{
+ repo = cl_git_sandbox_init("mergedrepo");
+ git_repository_index(&repo_index, repo);
+}
+
+void test_index_stage__cleanup(void)
+{
+ git_index_free(repo_index);
+ repo_index = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+
+void test_index_stage__add_always_adds_stage_0(void)
+{
+ size_t entry_idx;
+ const git_index_entry *entry;
+
+ cl_git_mkfile("./mergedrepo/new-file.txt", "new-file\n");
+
+ cl_git_pass(git_index_add_bypath(repo_index, "new-file.txt"));
+
+ cl_assert(!git_index_find(&entry_idx, repo_index, "new-file.txt"));
+ cl_assert((entry = git_index_get_byindex(repo_index, entry_idx)) != NULL);
+ cl_assert(git_index_entry_stage(entry) == 0);
+}
+
+void test_index_stage__find_gets_first_stage(void)
+{
+ size_t entry_idx;
+ const git_index_entry *entry;
+
+ cl_assert(!git_index_find(&entry_idx, repo_index, "one.txt"));
+ cl_assert((entry = git_index_get_byindex(repo_index, entry_idx)) != NULL);
+ cl_assert(git_index_entry_stage(entry) == 0);
+
+ cl_assert(!git_index_find(&entry_idx, repo_index, "two.txt"));
+ cl_assert((entry = git_index_get_byindex(repo_index, entry_idx)) != NULL);
+ cl_assert(git_index_entry_stage(entry) == 0);
+
+ cl_assert(!git_index_find(&entry_idx, repo_index, "conflicts-one.txt"));
+ cl_assert((entry = git_index_get_byindex(repo_index, entry_idx)) != NULL);
+ cl_assert(git_index_entry_stage(entry) == 1);
+
+ cl_assert(!git_index_find(&entry_idx, repo_index, "conflicts-two.txt"));
+ cl_assert((entry = git_index_get_byindex(repo_index, entry_idx)) != NULL);
+ cl_assert(git_index_entry_stage(entry) == 1);
+}
+
diff --git a/tests/index/tests.c b/tests/index/tests.c
new file mode 100644
index 000000000..e5202980c
--- /dev/null
+++ b/tests/index/tests.c
@@ -0,0 +1,537 @@
+#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, 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);
+}
diff --git a/tests/main.c b/tests/main.c
new file mode 100644
index 000000000..6b498939d
--- /dev/null
+++ b/tests/main.c
@@ -0,0 +1,20 @@
+#include "clar_libgit2.h"
+
+#ifdef _WIN32
+int __cdecl main(int argc, char *argv[])
+#else
+int main(int argc, char *argv[])
+#endif
+{
+ int res;
+
+ git_threads_init();
+
+ /* Run the test suite */
+ res = clar_test(argc, argv);
+
+ giterr_clear();
+ git_threads_shutdown();
+
+ return res;
+}
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/merge/trees/modeconflict.c b/tests/merge/trees/modeconflict.c
new file mode 100644
index 000000000..d858b8f66
--- /dev/null
+++ b/tests/merge/trees/modeconflict.c
@@ -0,0 +1,59 @@
+#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 DF_SIDE1_BRANCH "df_side1"
+#define DF_SIDE2_BRANCH "df_side2"
+
+// Fixture setup and teardown
+void test_merge_trees_modeconflict__initialize(void)
+{
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+}
+
+void test_merge_trees_modeconflict__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_merge_trees_modeconflict__df_conflict(void)
+{
+ git_index *index;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "49130a28ef567af9a6a6104c38773fedfa5f9742", 2, "dir-10" },
+ { 0100644, "6c06dcd163587c2cc18be44857e0b71116382aeb", 3, "dir-10" },
+ { 0100644, "43aafd43bea779ec74317dc361f45ae3f532a505", 0, "dir-6" },
+ { 0100644, "a031a28ae70e33a641ce4b8a8f6317f1ab79dee4", 3, "dir-7" },
+ { 0100644, "5012fd565b1393bdfda1805d4ec38ce6619e1fd1", 1, "dir-7/file.txt" },
+ { 0100644, "a5563304ddf6caba25cb50323a2ea6f7dbfcadca", 2, "dir-7/file.txt" },
+ { 0100644, "e9ad6ec3e38364a3d07feda7c4197d4d845c53b5", 0, "dir-8" },
+ { 0100644, "3ef4d30382ca33fdeba9fda895a99e0891ba37aa", 2, "dir-9" },
+ { 0100644, "fc4c636d6515e9e261f9260dbcf3cc6eca97ea08", 1, "dir-9/file.txt" },
+ { 0100644, "76ab0e2868197ec158ddd6c78d8a0d2fd73d38f9", 3, "dir-9/file.txt" },
+ { 0100644, "5c2411f8075f48a6b2fdb85ebc0d371747c4df15", 0, "file-1/new" },
+ { 0100644, "a39a620dae5bc8b4e771cd4d251b7d080401a21e", 1, "file-2" },
+ { 0100644, "d963979c237d08b6ba39062ee7bf64c7d34a27f8", 2, "file-2" },
+ { 0100644, "5c341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d", 0, "file-2/new" },
+ { 0100644, "9efe7723802d4305142eee177e018fee1572c4f4", 0, "file-3/new" },
+ { 0100644, "bacac9b3493509aa15e1730e1545fc0919d1dae0", 1, "file-4" },
+ { 0100644, "7663fce0130db092936b137cabd693ec234eb060", 3, "file-4" },
+ { 0100644, "e49f917b448d1340b31d76e54ba388268fd4c922", 0, "file-4/new" },
+ { 0100644, "cab2cf23998b40f1af2d9d9a756dc9e285a8df4b", 2, "file-5/new" },
+ { 0100644, "f5504f36e6f4eb797a56fc5bac6c6c7f32969bf2", 3, "file-5/new" },
+ };
+
+ cl_git_pass(merge_trees_from_branches(&index, repo, DF_SIDE1_BRANCH, DF_SIDE2_BRANCH, NULL));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 20));
+
+ git_index_free(index);
+}
diff --git a/tests/merge/trees/renames.c b/tests/merge/trees/renames.c
new file mode 100644
index 000000000..427b6bd8f
--- /dev/null
+++ b/tests/merge/trees/renames.c
@@ -0,0 +1,252 @@
+#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 BRANCH_RENAME_OURS "rename_conflict_ours"
+#define BRANCH_RENAME_THEIRS "rename_conflict_theirs"
+
+// Fixture setup and teardown
+void test_merge_trees_renames__initialize(void)
+{
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+}
+
+void test_merge_trees_renames__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_merge_trees_renames__index(void)
+{
+ git_index *index;
+ git_merge_tree_opts *opts = NULL;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" },
+ { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt" },
+ { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 1, "0b-rewritten-in-ours.txt" },
+ { 0100644, "e376fbdd06ebf021c92724da9f26f44212734e3e", 2, "0b-rewritten-in-ours.txt" },
+ { 0100644, "b2d399ae15224e1d58066e3c8df70ce37de7a656", 3, "0b-rewritten-in-ours.txt" },
+ { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt" },
+ { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 1, "0c-rewritten-in-theirs.txt" },
+ { 0100644, "efc9121fdedaf08ba180b53ebfbcf71bd488ed09", 2, "0c-rewritten-in-theirs.txt" },
+ { 0100644, "712ebba6669ea847d9829e4f1059d6c830c8b531", 3, "0c-rewritten-in-theirs.txt" },
+ { 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 0, "1a-newname-in-ours-edited-in-theirs.txt" },
+ { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt" },
+ { 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 0, "1b-newname-in-theirs-edited-in-ours.txt" },
+ { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt" },
+ { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" },
+ { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 2, "3a-newname-in-ours-deleted-in-theirs.txt" },
+ { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 1, "3a-renamed-in-ours-deleted-in-theirs.txt" },
+ { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 3, "3b-newname-in-theirs-deleted-in-ours.txt" },
+ { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 1, "3b-renamed-in-theirs-deleted-in-ours.txt" },
+ { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 2, "4a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a", 3, "4a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 1, "4a-renamed-in-ours-added-in-theirs.txt" },
+ { 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 2, "4b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 3, "4b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 1, "4b-renamed-in-theirs-added-in-ours.txt" },
+ { 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 2, "5a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "98ba4205fcf31f5dd93c916d35fe3f3b3d0e6714", 3, "5a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 1, "5a-renamed-in-ours-added-in-theirs.txt" },
+ { 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 3, "5a-renamed-in-ours-added-in-theirs.txt" },
+ { 0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced", 2, "5b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 3, "5b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 1, "5b-renamed-in-theirs-added-in-ours.txt" },
+ { 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 2, "5b-renamed-in-theirs-added-in-ours.txt" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 2, "6-both-renamed-1-to-2-ours.txt" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 3, "6-both-renamed-1-to-2-theirs.txt" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 1, "6-both-renamed-1-to-2.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 1, "7-both-renamed-side-1.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 3, "7-both-renamed-side-1.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 1, "7-both-renamed-side-2.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 2, "7-both-renamed-side-2.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 2, "7-both-renamed.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 3, "7-both-renamed.txt" },
+ };
+
+ struct merge_name_entry merge_name_entries[] = {
+ {
+ "3a-renamed-in-ours-deleted-in-theirs.txt",
+ "3a-newname-in-ours-deleted-in-theirs.txt",
+ ""
+ },
+
+ {
+ "3b-renamed-in-theirs-deleted-in-ours.txt",
+ "",
+ "3b-newname-in-theirs-deleted-in-ours.txt",
+ },
+
+ {
+ "4a-renamed-in-ours-added-in-theirs.txt",
+ "4a-newname-in-ours-added-in-theirs.txt",
+ "",
+ },
+
+ {
+ "4b-renamed-in-theirs-added-in-ours.txt",
+ "",
+ "4b-newname-in-theirs-added-in-ours.txt",
+ },
+
+ {
+ "5a-renamed-in-ours-added-in-theirs.txt",
+ "5a-newname-in-ours-added-in-theirs.txt",
+ "5a-renamed-in-ours-added-in-theirs.txt",
+ },
+
+ {
+ "5b-renamed-in-theirs-added-in-ours.txt",
+ "5b-renamed-in-theirs-added-in-ours.txt",
+ "5b-newname-in-theirs-added-in-ours.txt",
+ },
+
+ {
+ "6-both-renamed-1-to-2.txt",
+ "6-both-renamed-1-to-2-ours.txt",
+ "6-both-renamed-1-to-2-theirs.txt",
+ },
+
+ {
+ "7-both-renamed-side-1.txt",
+ "7-both-renamed.txt",
+ "7-both-renamed-side-1.txt",
+ },
+
+ {
+ "7-both-renamed-side-2.txt",
+ "7-both-renamed-side-2.txt",
+ "7-both-renamed.txt",
+ },
+ };
+
+ struct merge_reuc_entry merge_reuc_entries[] = {
+ { "1a-newname-in-ours-edited-in-theirs.txt",
+ 0, 0100644, 0,
+ "",
+ "c3d02eeef75183df7584d8d13ac03053910c1301",
+ "" },
+
+ { "1a-newname-in-ours.txt",
+ 0, 0100644, 0,
+ "",
+ "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb",
+ "" },
+
+ { "1a-renamed-in-ours-edited-in-theirs.txt",
+ 0100644, 0, 0100644,
+ "c3d02eeef75183df7584d8d13ac03053910c1301",
+ "",
+ "0d872f8e871a30208305978ecbf9e66d864f1638" },
+
+ { "1a-renamed-in-ours.txt",
+ 0100644, 0, 0100644,
+ "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb",
+ "",
+ "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb" },
+
+ { "1b-newname-in-theirs-edited-in-ours.txt",
+ 0, 0, 0100644,
+ "",
+ "",
+ "241a1005cd9b980732741b74385b891142bcba28" },
+
+ { "1b-newname-in-theirs.txt",
+ 0, 0, 0100644,
+ "",
+ "",
+ "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136" },
+
+ { "1b-renamed-in-theirs-edited-in-ours.txt",
+ 0100644, 0100644, 0,
+ "241a1005cd9b980732741b74385b891142bcba28",
+ "ed9523e62e453e50dd9be1606af19399b96e397a",
+ "" },
+
+ { "1b-renamed-in-theirs.txt",
+ 0100644, 0100644, 0,
+ "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136",
+ "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136",
+ "" },
+
+ { "2-newname-in-both.txt",
+ 0, 0100644, 0100644,
+ "",
+ "178940b450f238a56c0d75b7955cb57b38191982",
+ "178940b450f238a56c0d75b7955cb57b38191982" },
+
+ { "2-renamed-in-both.txt",
+ 0100644, 0, 0,
+ "178940b450f238a56c0d75b7955cb57b38191982",
+ "",
+ "" },
+ };
+
+ cl_git_pass(merge_trees_from_branches(&index, repo,
+ BRANCH_RENAME_OURS, BRANCH_RENAME_THEIRS,
+ opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 41));
+ cl_assert(merge_test_names(index, merge_name_entries, 9));
+ cl_assert(merge_test_reuc(index, merge_reuc_entries, 10));
+
+ git_index_free(index);
+}
+
+void test_merge_trees_renames__no_rename_index(void)
+{
+ git_index *index;
+ git_merge_tree_opts opts = GIT_MERGE_TREE_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, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 1, "0b-rewritten-in-ours.txt" },
+ { 0100644, "e376fbdd06ebf021c92724da9f26f44212734e3e", 2, "0b-rewritten-in-ours.txt" },
+ { 0100644, "b2d399ae15224e1d58066e3c8df70ce37de7a656", 3, "0b-rewritten-in-ours.txt" },
+ { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt" },
+ { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 1, "0c-rewritten-in-theirs.txt" },
+ { 0100644, "efc9121fdedaf08ba180b53ebfbcf71bd488ed09", 2, "0c-rewritten-in-theirs.txt" },
+ { 0100644, "712ebba6669ea847d9829e4f1059d6c830c8b531", 3, "0c-rewritten-in-theirs.txt" },
+ { 0100644, "c3d02eeef75183df7584d8d13ac03053910c1301", 0, "1a-newname-in-ours-edited-in-theirs.txt" },
+ { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt" },
+ { 0100644, "c3d02eeef75183df7584d8d13ac03053910c1301", 1, "1a-renamed-in-ours-edited-in-theirs.txt" },
+ { 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 3, "1a-renamed-in-ours-edited-in-theirs.txt" },
+ { 0100644, "241a1005cd9b980732741b74385b891142bcba28", 0, "1b-newname-in-theirs-edited-in-ours.txt" },
+ { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt" },
+ { 0100644, "241a1005cd9b980732741b74385b891142bcba28", 1, "1b-renamed-in-theirs-edited-in-ours.txt" },
+ { 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 2, "1b-renamed-in-theirs-edited-in-ours.txt" },
+ { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" },
+ { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 0, "3a-newname-in-ours-deleted-in-theirs.txt" },
+ { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 0, "3b-newname-in-theirs-deleted-in-ours.txt" },
+ { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 2, "4a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a", 3, "4a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 2, "4b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 3, "4b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 2, "5a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "98ba4205fcf31f5dd93c916d35fe3f3b3d0e6714", 3, "5a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced", 2, "5b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 3, "5b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "6-both-renamed-1-to-2-ours.txt" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "6-both-renamed-1-to-2-theirs.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 2, "7-both-renamed.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 3, "7-both-renamed.txt" },
+ };
+
+ cl_git_pass(merge_trees_from_branches(&index, repo,
+ BRANCH_RENAME_OURS, BRANCH_RENAME_THEIRS,
+ &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 32));
+
+ git_index_free(index);
+}
diff --git a/tests/merge/trees/treediff.c b/tests/merge/trees/treediff.c
new file mode 100644
index 000000000..357859df3
--- /dev/null
+++ b/tests/merge/trees/treediff.c
@@ -0,0 +1,542 @@
+#include "clar_libgit2.h"
+#include "git2/tree.h"
+#include "merge.h"
+#include "../merge_helpers.h"
+#include "diff.h"
+#include "hashsig.h"
+
+static git_repository *repo;
+
+#define TEST_REPO_PATH "merge-resolve"
+
+#define TREE_OID_ANCESTOR "0d52e3a556e189ba0948ae56780918011c1b167d"
+#define TREE_OID_MASTER "1f81433e3161efbf250576c58fede7f6b836f3d3"
+#define TREE_OID_BRANCH "eea9286df54245fea72c5b557291470eb825f38f"
+#define TREE_OID_RENAMES1 "f5f9dd5886a6ee20272be0aafc790cba43b31931"
+#define TREE_OID_RENAMES2 "5fbfbdc04b4eca46f54f4853a3c5a1dce28f5165"
+
+#define TREE_OID_DF_ANCESTOR "b8a3a806d3950e8c0a03a34f234a92eff0e2c68d"
+#define TREE_OID_DF_SIDE1 "ee1d6f164893c1866a323f072eeed36b855656be"
+#define TREE_OID_DF_SIDE2 "6178885b38fe96e825ac0f492c0a941f288b37f6"
+
+#define TREE_OID_RENAME_CONFLICT_ANCESTOR "476dbb3e207313d1d8aaa120c6ad204bf1295e53"
+#define TREE_OID_RENAME_CONFLICT_OURS "c4efe31e9decccc8b2b4d3df9aac2cdfe2995618"
+#define TREE_OID_RENAME_CONFLICT_THEIRS "9e7f4359c469f309b6057febf4c6e80742cbed5b"
+
+void test_merge_trees_treediff__initialize(void)
+{
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+}
+
+void test_merge_trees_treediff__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static void test_find_differences(
+ const char *ancestor_oidstr,
+ const char *ours_oidstr,
+ const char *theirs_oidstr,
+ struct merge_index_conflict_data *treediff_conflict_data,
+ size_t treediff_conflict_data_len)
+{
+ git_merge_diff_list *merge_diff_list = git_merge_diff_list__alloc(repo);
+ git_oid ancestor_oid, ours_oid, theirs_oid;
+ git_tree *ancestor_tree, *ours_tree, *theirs_tree;
+
+ git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT;
+ opts.flags |= GIT_MERGE_TREE_FIND_RENAMES;
+ opts.target_limit = 1000;
+ opts.rename_threshold = 50;
+
+ opts.metric = git__malloc(sizeof(git_diff_similarity_metric));
+ cl_assert(opts.metric != NULL);
+
+ opts.metric->file_signature = git_diff_find_similar__hashsig_for_file;
+ opts.metric->buffer_signature = git_diff_find_similar__hashsig_for_buf;
+ opts.metric->free_signature = git_diff_find_similar__hashsig_free;
+ opts.metric->similarity = git_diff_find_similar__calc_similarity;
+ opts.metric->payload = (void *)GIT_HASHSIG_SMART_WHITESPACE;
+
+ cl_git_pass(git_oid_fromstr(&ancestor_oid, ancestor_oidstr));
+ cl_git_pass(git_oid_fromstr(&ours_oid, ours_oidstr));
+ cl_git_pass(git_oid_fromstr(&theirs_oid, theirs_oidstr));
+
+ cl_git_pass(git_tree_lookup(&ancestor_tree, repo, &ancestor_oid));
+ cl_git_pass(git_tree_lookup(&ours_tree, repo, &ours_oid));
+ cl_git_pass(git_tree_lookup(&theirs_tree, repo, &theirs_oid));
+
+ cl_git_pass(git_merge_diff_list__find_differences(merge_diff_list, ancestor_tree, ours_tree, theirs_tree));
+ cl_git_pass(git_merge_diff_list__find_renames(repo, merge_diff_list, &opts));
+
+ /*
+ dump_merge_index(merge_index);
+ */
+
+ cl_assert(treediff_conflict_data_len == merge_diff_list->conflicts.length);
+
+ cl_assert(merge_test_merge_conflicts(&merge_diff_list->conflicts, treediff_conflict_data, treediff_conflict_data_len));
+
+ git_tree_free(ancestor_tree);
+ git_tree_free(ours_tree);
+ git_tree_free(theirs_tree);
+
+ git_merge_diff_list__free(merge_diff_list);
+
+ git__free(opts.metric);
+}
+
+void test_merge_trees_treediff__simple(void)
+{
+ struct merge_index_conflict_data treediff_conflict_data[] = {
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" }, GIT_DELTA_ADDED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE
+ },
+
+ {
+ { { 0100644, "6212c31dab5e482247d7977e4f0dd3601decf13b", 0, "automergeable.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 0, "automergeable.txt" }, GIT_DELTA_MODIFIED },
+ { { 0100644, "058541fc37114bfc1dddf6bd6bffc7fae5c2e6fe", 0, "automergeable.txt" }, GIT_DELTA_MODIFIED },
+ GIT_MERGE_DIFF_BOTH_MODIFIED
+ },
+
+ {
+ { { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "4eb04c9e79e88f6640d01ff5b25ca2a60764f216", 0, "changed-in-branch.txt" }, GIT_DELTA_MODIFIED },
+ GIT_MERGE_DIFF_NONE
+ },
+
+ {
+ { { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" }, GIT_DELTA_MODIFIED },
+ { { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE
+ },
+
+ {
+ { { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 0, "conflicting.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt" }, GIT_DELTA_MODIFIED },
+ { { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 0, "conflicting.txt" }, GIT_DELTA_MODIFIED },
+ GIT_MERGE_DIFF_BOTH_MODIFIED
+ },
+
+ {
+ { { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ GIT_MERGE_DIFF_NONE
+ },
+
+ {
+ { { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ { { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE
+ },
+ };
+
+ test_find_differences(TREE_OID_ANCESTOR, TREE_OID_MASTER, TREE_OID_BRANCH, treediff_conflict_data, 7);
+}
+
+void test_merge_trees_treediff__df_conflicts(void)
+{
+ struct merge_index_conflict_data treediff_conflict_data[] = {
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "49130a28ef567af9a6a6104c38773fedfa5f9742", 0, "dir-10" }, GIT_DELTA_ADDED },
+ { { 0100644, "6c06dcd163587c2cc18be44857e0b71116382aeb", 0, "dir-10" }, GIT_DELTA_ADDED },
+ GIT_MERGE_DIFF_BOTH_ADDED,
+ },
+
+ {
+ { { 0100644, "242591eb280ee9eeb2ce63524b9a8b9bc4cb515d", 0, "dir-10/file.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ GIT_MERGE_DIFF_BOTH_DELETED,
+ },
+
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "43aafd43bea779ec74317dc361f45ae3f532a505", 0, "dir-6" }, GIT_DELTA_ADDED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "cf8c5cc8a85a1ff5a4ba51e0bc7cf5665669924d", 0, "dir-6/file.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "cf8c5cc8a85a1ff5a4ba51e0bc7cf5665669924d", 0, "dir-6/file.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "a031a28ae70e33a641ce4b8a8f6317f1ab79dee4", 0, "dir-7" }, GIT_DELTA_ADDED },
+ GIT_MERGE_DIFF_DIRECTORY_FILE,
+ },
+
+ {
+ { { 0100644, "5012fd565b1393bdfda1805d4ec38ce6619e1fd1", 0, "dir-7/file.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "a5563304ddf6caba25cb50323a2ea6f7dbfcadca", 0, "dir-7/file.txt" }, GIT_DELTA_MODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ GIT_MERGE_DIFF_DF_CHILD,
+ },
+
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "e9ad6ec3e38364a3d07feda7c4197d4d845c53b5", 0, "dir-8" }, GIT_DELTA_ADDED },
+ { {0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "f20c9063fa0bda9a397c96947a7b687305c49753", 0, "dir-8/file.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ { { 0100644, "f20c9063fa0bda9a397c96947a7b687305c49753", 0, "dir-8/file.txt" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "3ef4d30382ca33fdeba9fda895a99e0891ba37aa", 0, "dir-9" }, GIT_DELTA_ADDED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_DIRECTORY_FILE,
+ },
+
+ {
+ { { 0100644, "fc4c636d6515e9e261f9260dbcf3cc6eca97ea08", 0, "dir-9/file.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ { { 0100644, "76ab0e2868197ec158ddd6c78d8a0d2fd73d38f9", 0, "dir-9/file.txt" }, GIT_DELTA_MODIFIED },
+ GIT_MERGE_DIFF_DF_CHILD,
+ },
+
+ {
+ { { 0100644, "1e4ff029aee68d0d69ef9eb6efa6cbf1ec732f99", 0, "file-1" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "1e4ff029aee68d0d69ef9eb6efa6cbf1ec732f99", 0, "file-1" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "5c2411f8075f48a6b2fdb85ebc0d371747c4df15", 0, "file-1/new" }, GIT_DELTA_ADDED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "a39a620dae5bc8b4e771cd4d251b7d080401a21e", 0, "file-2" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "d963979c237d08b6ba39062ee7bf64c7d34a27f8", 0, "file-2" }, GIT_DELTA_MODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ GIT_MERGE_DIFF_DIRECTORY_FILE,
+ },
+
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "5c341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d", 0, "file-2/new" }, GIT_DELTA_ADDED },
+ GIT_MERGE_DIFF_DF_CHILD,
+ },
+
+ {
+ { { 0100644, "032ebc5ab85d9553bb187d3cd40875ff23a63ed0", 0, "file-3" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ { { 0100644, "032ebc5ab85d9553bb187d3cd40875ff23a63ed0", 0, "file-3" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "9efe7723802d4305142eee177e018fee1572c4f4", 0, "file-3/new" }, GIT_DELTA_ADDED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "bacac9b3493509aa15e1730e1545fc0919d1dae0", 0, "file-4" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ { { 0100644, "7663fce0130db092936b137cabd693ec234eb060", 0, "file-4" }, GIT_DELTA_MODIFIED },
+ GIT_MERGE_DIFF_DIRECTORY_FILE,
+ },
+
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "e49f917b448d1340b31d76e54ba388268fd4c922", 0, "file-4/new" }, GIT_DELTA_ADDED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_DF_CHILD,
+ },
+
+ {
+ { { 0100644, "ac4045f965119e6998f4340ed0f411decfb3ec05", 0, "file-5" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ GIT_MERGE_DIFF_BOTH_DELETED,
+ },
+
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "cab2cf23998b40f1af2d9d9a756dc9e285a8df4b", 0, "file-5/new" }, GIT_DELTA_ADDED },
+ { { 0100644, "f5504f36e6f4eb797a56fc5bac6c6c7f32969bf2", 0, "file-5/new" }, GIT_DELTA_ADDED },
+ GIT_MERGE_DIFF_BOTH_ADDED,
+ },
+ };
+
+ test_find_differences(TREE_OID_DF_ANCESTOR, TREE_OID_DF_SIDE1, TREE_OID_DF_SIDE2, treediff_conflict_data, 20);
+}
+
+void test_merge_trees_treediff__strict_renames(void)
+{
+ struct merge_index_conflict_data treediff_conflict_data[] = {
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" }, GIT_DELTA_ADDED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "6212c31dab5e482247d7977e4f0dd3601decf13b", 0, "automergeable.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 0, "automergeable.txt" }, GIT_DELTA_MODIFIED },
+ { { 0100644, "6212c31dab5e482247d7977e4f0dd3601decf13b", 0, "automergeable.txt" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" }, GIT_DELTA_MODIFIED },
+ { { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 0, "conflicting.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt" }, GIT_DELTA_MODIFIED },
+ { { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 0, "conflicting.txt" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "renamed-in-branch.txt" }, GIT_DELTA_RENAMED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ { { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "renamed.txt" }, GIT_DELTA_ADDED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "copied.txt" }, GIT_DELTA_RENAMED },
+ GIT_MERGE_DIFF_NONE,
+ },
+ };
+
+ test_find_differences(TREE_OID_ANCESTOR, TREE_OID_MASTER, TREE_OID_RENAMES1, treediff_conflict_data, 8);
+}
+
+void test_merge_trees_treediff__rename_conflicts(void)
+{
+ struct merge_index_conflict_data treediff_conflict_data[] = {
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt" }, GIT_DELTA_ADDED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-rewritten-in-ours.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "e376fbdd06ebf021c92724da9f26f44212734e3e", 0, "0b-rewritten-in-ours.txt" }, GIT_DELTA_MODIFIED },
+ { { 0100644, "b2d399ae15224e1d58066e3c8df70ce37de7a656", 0, "0b-rewritten-in-ours.txt" }, GIT_DELTA_MODIFIED },
+ GIT_MERGE_DIFF_BOTH_MODIFIED,
+ },
+
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt" }, GIT_DELTA_ADDED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-rewritten-in-theirs.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "efc9121fdedaf08ba180b53ebfbcf71bd488ed09", 0, "0c-rewritten-in-theirs.txt" }, GIT_DELTA_MODIFIED },
+ { { 0100644, "712ebba6669ea847d9829e4f1059d6c830c8b531", 0, "0c-rewritten-in-theirs.txt" }, GIT_DELTA_MODIFIED },
+ GIT_MERGE_DIFF_BOTH_MODIFIED,
+ },
+
+ {
+ { { 0100644, "c3d02eeef75183df7584d8d13ac03053910c1301", 0, "1a-renamed-in-ours-edited-in-theirs.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "c3d02eeef75183df7584d8d13ac03053910c1301", 0, "1a-newname-in-ours-edited-in-theirs.txt" }, GIT_DELTA_RENAMED },
+ { { 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 0, "1a-renamed-in-ours-edited-in-theirs.txt" }, GIT_DELTA_MODIFIED },
+ GIT_MERGE_DIFF_RENAMED_MODIFIED,
+ },
+
+ {
+ { { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-renamed-in-ours.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt" }, GIT_DELTA_RENAMED },
+ { { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-renamed-in-ours.txt" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "241a1005cd9b980732741b74385b891142bcba28", 0, "1b-renamed-in-theirs-edited-in-ours.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 0, "1b-renamed-in-theirs-edited-in-ours.txt" }, GIT_DELTA_MODIFIED },
+ { { 0100644, "241a1005cd9b980732741b74385b891142bcba28", 0, "1b-newname-in-theirs-edited-in-ours.txt" }, GIT_DELTA_RENAMED },
+ GIT_MERGE_DIFF_RENAMED_MODIFIED,
+ },
+
+ {
+ { { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-renamed-in-theirs.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-renamed-in-theirs.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt" }, GIT_DELTA_RENAMED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-renamed-in-both.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" }, GIT_DELTA_RENAMED },
+ { { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" }, GIT_DELTA_RENAMED },
+ GIT_MERGE_DIFF_BOTH_RENAMED,
+ },
+
+ {
+ { { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 0, "3a-renamed-in-ours-deleted-in-theirs.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 0, "3a-newname-in-ours-deleted-in-theirs.txt" }, GIT_DELTA_RENAMED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ GIT_MERGE_DIFF_RENAMED_DELETED,
+ },
+
+ {
+ { { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 0, "3b-renamed-in-theirs-deleted-in-ours.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ { { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 0, "3b-newname-in-theirs-deleted-in-ours.txt" }, GIT_DELTA_RENAMED },
+ GIT_MERGE_DIFF_RENAMED_DELETED,
+ },
+
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a", 0, "4a-newname-in-ours-added-in-theirs.txt" }, GIT_DELTA_ADDED },
+ GIT_MERGE_DIFF_RENAMED_ADDED,
+ },
+
+ {
+ { { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 0, "4a-renamed-in-ours-added-in-theirs.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 0, "4a-newname-in-ours-added-in-theirs.txt" }, GIT_DELTA_RENAMED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ GIT_MERGE_DIFF_RENAMED_ADDED,
+ },
+
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 0, "4b-newname-in-theirs-added-in-ours.txt" }, GIT_DELTA_ADDED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_RENAMED_ADDED,
+ },
+
+ {
+ { { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 0, "4b-renamed-in-theirs-added-in-ours.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ { { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 0, "4b-newname-in-theirs-added-in-ours.txt" }, GIT_DELTA_RENAMED },
+ GIT_MERGE_DIFF_RENAMED_ADDED,
+ },
+
+ {
+ { { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "5-both-renamed-1-to-2.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "5-both-renamed-1-to-2-ours.txt" }, GIT_DELTA_RENAMED },
+ { { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "5-both-renamed-1-to-2-theirs.txt" }, GIT_DELTA_RENAMED },
+ GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2,
+ },
+
+ {
+ { { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "6-both-renamed-side-1.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "6-both-renamed.txt" }, GIT_DELTA_RENAMED },
+ { { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "6-both-renamed-side-1.txt" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1,
+ },
+
+ {
+ { { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "6-both-renamed-side-2.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "6-both-renamed-side-2.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "6-both-renamed.txt" }, GIT_DELTA_RENAMED },
+ GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1,
+ },
+ };
+ test_find_differences(TREE_OID_RENAME_CONFLICT_ANCESTOR,
+ TREE_OID_RENAME_CONFLICT_OURS, TREE_OID_RENAME_CONFLICT_THEIRS, treediff_conflict_data, 18);
+}
+
+void test_merge_trees_treediff__best_renames(void)
+{
+ struct merge_index_conflict_data treediff_conflict_data[] = {
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" }, GIT_DELTA_ADDED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "6212c31dab5e482247d7977e4f0dd3601decf13b", 0, "automergeable.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 0, "automergeable.txt" }, GIT_DELTA_MODIFIED },
+ { { 0100644, "45299c1ca5e07bba1fd90843056fb559f96b1f5a", 0, "renamed-90.txt" }, GIT_DELTA_RENAMED },
+ GIT_MERGE_DIFF_RENAMED_MODIFIED,
+ },
+
+ {
+ { { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" }, GIT_DELTA_MODIFIED },
+ { { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 0, "conflicting.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt" }, GIT_DELTA_MODIFIED },
+ { { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 0, "conflicting.txt" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt" },GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ { { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_MODIFIED_DELETED,
+ },
+
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "5843febcb23480df0b5edb22a21c59c772bb8e29", 0, "renamed-50.txt" }, GIT_DELTA_ADDED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "a77a56a49f8f3ae242e02717f18ebbc60c5cc543", 0, "renamed-75.txt" }, GIT_DELTA_ADDED },
+ GIT_MERGE_DIFF_NONE,
+ },
+ };
+
+ test_find_differences(TREE_OID_ANCESTOR, TREE_OID_MASTER, TREE_OID_RENAMES2, treediff_conflict_data, 7);
+}
diff --git a/tests/merge/trees/trivial.c b/tests/merge/trees/trivial.c
new file mode 100644
index 000000000..bfd5dfed3
--- /dev/null
+++ b/tests/merge/trees/trivial.c
@@ -0,0 +1,397 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/merge.h"
+#include "merge.h"
+#include "../merge_helpers.h"
+#include "refs.h"
+#include "fileops.h"
+#include "git2/sys/index.h"
+
+static git_repository *repo;
+
+#define TEST_REPO_PATH "merge-resolve"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+
+// Fixture setup and teardown
+void test_merge_trees_trivial__initialize(void)
+{
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+}
+
+void test_merge_trees_trivial__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+
+static int merge_trivial(git_index **index, const char *ours, const char *theirs, bool automerge)
+{
+ git_commit *our_commit, *their_commit, *ancestor_commit;
+ git_tree *our_tree, *their_tree, *ancestor_tree;
+ git_oid our_oid, their_oid, ancestor_oid;
+ git_buf branch_buf = GIT_BUF_INIT;
+ git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT;
+
+ 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_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);
+ cl_git_pass(git_reference_name_to_id(&their_oid, repo, branch_buf.ptr));
+ cl_git_pass(git_commit_lookup(&their_commit, repo, &their_oid));
+
+ cl_git_pass(git_merge_base(&ancestor_oid, repo, git_commit_id(our_commit), git_commit_id(their_commit)));
+ cl_git_pass(git_commit_lookup(&ancestor_commit, repo, &ancestor_oid));
+
+ cl_git_pass(git_commit_tree(&ancestor_tree, ancestor_commit));
+ cl_git_pass(git_commit_tree(&our_tree, our_commit));
+ cl_git_pass(git_commit_tree(&their_tree, their_commit));
+
+ cl_git_pass(git_merge_trees(index, repo, ancestor_tree, our_tree, their_tree, &opts));
+
+ git_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;
+}
+
+static int merge_trivial_conflict_entrycount(git_index *index)
+{
+ const git_index_entry *entry;
+ int count = 0;
+ size_t i;
+
+ for (i = 0; i < git_index_entrycount(index); i++) {
+ cl_assert(entry = git_index_get_byindex(index, i));
+
+ if (git_index_entry_stage(entry) > 0)
+ count++;
+ }
+
+ return count;
+}
+
+/* 2ALT: ancest:(empty)+, head:*empty*, remote:remote = result:remote */
+void test_merge_trees_trivial__2alt(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial(&result, "trivial-2alt", "trivial-2alt-branch", 0));
+
+ cl_assert(entry = git_index_get_bypath(result, "new-in-branch.txt", 0));
+ cl_assert(git_index_reuc_entrycount(result) == 0);
+ cl_assert(merge_trivial_conflict_entrycount(result) == 0);
+
+ git_index_free(result);
+}
+
+/* 3ALT: ancest:(empty)+, head:head, remote:*empty* = result:head */
+void test_merge_trees_trivial__3alt(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial(&result, "trivial-3alt", "trivial-3alt-branch", 0));
+
+ cl_assert(entry = git_index_get_bypath(result, "new-in-3alt.txt", 0));
+ cl_assert(git_index_reuc_entrycount(result) == 0);
+ cl_assert(merge_trivial_conflict_entrycount(result) == 0);
+
+ git_index_free(result);
+}
+
+/* 4: ancest:(empty)^, head:head, remote:remote = result:no merge */
+void test_merge_trees_trivial__4(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial(&result, "trivial-4", "trivial-4-branch", 0));
+
+ cl_assert((entry = git_index_get_bypath(result, "new-and-different.txt", 0)) == NULL);
+ cl_assert(git_index_reuc_entrycount(result) == 0);
+
+ cl_assert(merge_trivial_conflict_entrycount(result) == 2);
+ cl_assert(entry = git_index_get_bypath(result, "new-and-different.txt", 2));
+ cl_assert(entry = git_index_get_bypath(result, "new-and-different.txt", 3));
+
+ git_index_free(result);
+}
+
+/* 5ALT: ancest:*, head:head, remote:head = result:head */
+void test_merge_trees_trivial__5alt_1(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial(&result, "trivial-5alt-1", "trivial-5alt-1-branch", 0));
+
+ cl_assert(entry = git_index_get_bypath(result, "new-and-same.txt", 0));
+ cl_assert(git_index_reuc_entrycount(result) == 0);
+ cl_assert(merge_trivial_conflict_entrycount(result) == 0);
+
+ git_index_free(result);
+}
+
+/* 5ALT: ancest:*, head:head, remote:head = result:head */
+void test_merge_trees_trivial__5alt_2(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial(&result, "trivial-5alt-2", "trivial-5alt-2-branch", 0));
+
+ cl_assert(entry = git_index_get_bypath(result, "modified-to-same.txt", 0));
+ cl_assert(git_index_reuc_entrycount(result) == 0);
+ cl_assert(merge_trivial_conflict_entrycount(result) == 0);
+
+ git_index_free(result);
+}
+
+/* 6: ancest:ancest+, head:(empty), remote:(empty) = result:no merge */
+void test_merge_trees_trivial__6(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial(&result, "trivial-6", "trivial-6-branch", 0));
+
+ cl_assert((entry = git_index_get_bypath(result, "removed-in-both.txt", 0)) == NULL);
+ cl_assert(git_index_reuc_entrycount(result) == 0);
+
+ cl_assert(merge_trivial_conflict_entrycount(result) == 1);
+ cl_assert(entry = git_index_get_bypath(result, "removed-in-both.txt", 1));
+
+ git_index_free(result);
+}
+
+/* 6: ancest:ancest+, head:(empty), remote:(empty) = result:no merge */
+void test_merge_trees_trivial__6_automerge(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+ const git_index_reuc_entry *reuc;
+
+ cl_git_pass(merge_trivial(&result, "trivial-6", "trivial-6-branch", 1));
+
+ cl_assert((entry = git_index_get_bypath(result, "removed-in-both.txt", 0)) == NULL);
+ cl_assert(git_index_reuc_entrycount(result) == 1);
+ cl_assert(reuc = git_index_reuc_get_bypath(result, "removed-in-both.txt"));
+
+ cl_assert(merge_trivial_conflict_entrycount(result) == 0);
+
+ git_index_free(result);
+}
+
+/* 8: ancest:ancest^, head:(empty), remote:ancest = result:no merge */
+void test_merge_trees_trivial__8(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial(&result, "trivial-8", "trivial-8-branch", 0));
+
+ cl_assert((entry = git_index_get_bypath(result, "removed-in-8.txt", 0)) == NULL);
+ cl_assert(git_index_reuc_entrycount(result) == 0);
+
+ cl_assert(merge_trivial_conflict_entrycount(result) == 2);
+ cl_assert(entry = git_index_get_bypath(result, "removed-in-8.txt", 1));
+ cl_assert(entry = git_index_get_bypath(result, "removed-in-8.txt", 3));
+
+ git_index_free(result);
+}
+
+/* 8: ancest:ancest^, head:(empty), remote:ancest = result:no merge */
+void test_merge_trees_trivial__8_automerge(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+ const git_index_reuc_entry *reuc;
+
+ cl_git_pass(merge_trivial(&result, "trivial-8", "trivial-8-branch", 1));
+
+ cl_assert((entry = git_index_get_bypath(result, "removed-in-8.txt", 0)) == NULL);
+
+ cl_assert(git_index_reuc_entrycount(result) == 1);
+ cl_assert(reuc = git_index_reuc_get_bypath(result, "removed-in-8.txt"));
+
+ cl_assert(merge_trivial_conflict_entrycount(result) == 0);
+
+ git_index_free(result);
+}
+
+/* 7: ancest:ancest+, head:(empty), remote:remote = result:no merge */
+void test_merge_trees_trivial__7(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial(&result, "trivial-7", "trivial-7-branch", 0));
+
+ cl_assert((entry = git_index_get_bypath(result, "removed-in-7.txt", 0)) == NULL);
+ cl_assert(git_index_reuc_entrycount(result) == 0);
+
+ cl_assert(merge_trivial_conflict_entrycount(result) == 2);
+ cl_assert(entry = git_index_get_bypath(result, "removed-in-7.txt", 1));
+ cl_assert(entry = git_index_get_bypath(result, "removed-in-7.txt", 3));
+
+ git_index_free(result);
+}
+
+/* 7: ancest:ancest+, head:(empty), remote:remote = result:no merge */
+void test_merge_trees_trivial__7_automerge(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial(&result, "trivial-7", "trivial-7-branch", 0));
+
+ cl_assert((entry = git_index_get_bypath(result, "removed-in-7.txt", 0)) == NULL);
+ cl_assert(git_index_reuc_entrycount(result) == 0);
+
+ cl_assert(merge_trivial_conflict_entrycount(result) == 2);
+ cl_assert(entry = git_index_get_bypath(result, "removed-in-7.txt", 1));
+ cl_assert(entry = git_index_get_bypath(result, "removed-in-7.txt", 3));
+
+ git_index_free(result);
+}
+
+/* 10: ancest:ancest^, head:ancest, remote:(empty) = result:no merge */
+void test_merge_trees_trivial__10(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial(&result, "trivial-10", "trivial-10-branch", 0));
+
+ cl_assert((entry = git_index_get_bypath(result, "removed-in-10-branch.txt", 0)) == NULL);
+ cl_assert(git_index_reuc_entrycount(result) == 0);
+
+ cl_assert(merge_trivial_conflict_entrycount(result) == 2);
+ cl_assert(entry = git_index_get_bypath(result, "removed-in-10-branch.txt", 1));
+ cl_assert(entry = git_index_get_bypath(result, "removed-in-10-branch.txt", 2));
+
+ git_index_free(result);
+}
+
+/* 10: ancest:ancest^, head:ancest, remote:(empty) = result:no merge */
+void test_merge_trees_trivial__10_automerge(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+ const git_index_reuc_entry *reuc;
+
+ cl_git_pass(merge_trivial(&result, "trivial-10", "trivial-10-branch", 1));
+
+ cl_assert((entry = git_index_get_bypath(result, "removed-in-10-branch.txt", 0)) == NULL);
+
+ cl_assert(git_index_reuc_entrycount(result) == 1);
+ cl_assert(reuc = git_index_reuc_get_bypath(result, "removed-in-10-branch.txt"));
+
+ cl_assert(merge_trivial_conflict_entrycount(result) == 0);
+
+ git_index_free(result);
+}
+
+/* 9: ancest:ancest+, head:head, remote:(empty) = result:no merge */
+void test_merge_trees_trivial__9(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial(&result, "trivial-9", "trivial-9-branch", 0));
+
+ cl_assert((entry = git_index_get_bypath(result, "removed-in-9-branch.txt", 0)) == NULL);
+ cl_assert(git_index_reuc_entrycount(result) == 0);
+
+ cl_assert(merge_trivial_conflict_entrycount(result) == 2);
+ cl_assert(entry = git_index_get_bypath(result, "removed-in-9-branch.txt", 1));
+ cl_assert(entry = git_index_get_bypath(result, "removed-in-9-branch.txt", 2));
+
+ git_index_free(result);
+}
+
+/* 9: ancest:ancest+, head:head, remote:(empty) = result:no merge */
+void test_merge_trees_trivial__9_automerge(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial(&result, "trivial-9", "trivial-9-branch", 1));
+
+ cl_assert((entry = git_index_get_bypath(result, "removed-in-9-branch.txt", 0)) == NULL);
+ cl_assert(git_index_reuc_entrycount(result) == 0);
+
+ cl_assert(merge_trivial_conflict_entrycount(result) == 2);
+ cl_assert(entry = git_index_get_bypath(result, "removed-in-9-branch.txt", 1));
+ cl_assert(entry = git_index_get_bypath(result, "removed-in-9-branch.txt", 2));
+
+ git_index_free(result);
+}
+
+/* 13: ancest:ancest+, head:head, remote:ancest = result:head */
+void test_merge_trees_trivial__13(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+ git_oid expected_oid;
+
+ cl_git_pass(merge_trivial(&result, "trivial-13", "trivial-13-branch", 0));
+
+ cl_assert(entry = git_index_get_bypath(result, "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(result) == 0);
+ cl_assert(merge_trivial_conflict_entrycount(result) == 0);
+
+ git_index_free(result);
+}
+
+/* 14: ancest:ancest+, head:ancest, remote:remote = result:remote */
+void test_merge_trees_trivial__14(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+ git_oid expected_oid;
+
+ cl_git_pass(merge_trivial(&result, "trivial-14", "trivial-14-branch", 0));
+
+ cl_assert(entry = git_index_get_bypath(result, "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(result) == 0);
+ cl_assert(merge_trivial_conflict_entrycount(result) == 0);
+
+ git_index_free(result);
+}
+
+/* 11: ancest:ancest+, head:head, remote:remote = result:no merge */
+void test_merge_trees_trivial__11(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial(&result, "trivial-11", "trivial-11-branch", 0));
+
+ cl_assert((entry = git_index_get_bypath(result, "modified-in-both.txt", 0)) == NULL);
+ cl_assert(git_index_reuc_entrycount(result) == 0);
+
+ cl_assert(merge_trivial_conflict_entrycount(result) == 3);
+ cl_assert(entry = git_index_get_bypath(result, "modified-in-both.txt", 1));
+ cl_assert(entry = git_index_get_bypath(result, "modified-in-both.txt", 2));
+ cl_assert(entry = git_index_get_bypath(result, "modified-in-both.txt", 3));
+
+ git_index_free(result);
+}
diff --git a/tests/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/network/cred.c b/tests/network/cred.c
new file mode 100644
index 000000000..6994cc0c3
--- /dev/null
+++ b/tests/network/cred.c
@@ -0,0 +1,50 @@
+#include "clar_libgit2.h"
+
+#include "git2/cred_helpers.h"
+
+void test_network_cred__stock_userpass_validates_args(void)
+{
+ git_cred_userpass_payload payload = {0};
+
+ cl_git_fail(git_cred_userpass(NULL, NULL, NULL, 0, NULL));
+
+ payload.username = "user";
+ cl_git_fail(git_cred_userpass(NULL, NULL, NULL, 0, &payload));
+
+ payload.username = NULL;
+ payload.username = "pass";
+ cl_git_fail(git_cred_userpass(NULL, NULL, NULL, 0, &payload));
+}
+
+void test_network_cred__stock_userpass_validates_that_method_is_allowed(void)
+{
+ git_cred *cred;
+ git_cred_userpass_payload payload = {"user", "pass"};
+
+ cl_git_fail(git_cred_userpass(&cred, NULL, NULL, 0, &payload));
+ cl_git_pass(git_cred_userpass(&cred, NULL, NULL, GIT_CREDTYPE_USERPASS_PLAINTEXT, &payload));
+ cred->free(cred);
+}
+
+void test_network_cred__stock_userpass_properly_handles_username_in_url(void)
+{
+ git_cred *cred;
+ git_cred_userpass_plaintext *plain;
+ git_cred_userpass_payload payload = {"alice", "password"};
+
+ cl_git_pass(git_cred_userpass(&cred, NULL, NULL, GIT_CREDTYPE_USERPASS_PLAINTEXT, &payload));
+ plain = (git_cred_userpass_plaintext*)cred;
+ cl_assert_equal_s(plain->username, "alice");
+ cred->free(cred);
+
+ cl_git_pass(git_cred_userpass(&cred, NULL, "bob", GIT_CREDTYPE_USERPASS_PLAINTEXT, &payload));
+ plain = (git_cred_userpass_plaintext*)cred;
+ cl_assert_equal_s(plain->username, "alice");
+ cred->free(cred);
+
+ payload.username = NULL;
+ cl_git_pass(git_cred_userpass(&cred, NULL, "bob", GIT_CREDTYPE_USERPASS_PLAINTEXT, &payload));
+ plain = (git_cred_userpass_plaintext*)cred;
+ cl_assert_equal_s(plain->username, "bob");
+ cred->free(cred);
+}
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/network/refspecs.c b/tests/network/refspecs.c
new file mode 100644
index 000000000..676a1fa99
--- /dev/null
+++ b/tests/network/refspecs.c
@@ -0,0 +1,87 @@
+#include "clar_libgit2.h"
+#include "refspec.h"
+#include "remote.h"
+
+static void assert_refspec(unsigned int direction, const char *input, bool is_expected_to_be_valid)
+{
+ git_refspec refspec;
+ int error;
+
+ error = git_refspec__parse(&refspec, input, direction == GIT_DIRECTION_FETCH);
+ git_refspec__free(&refspec);
+
+ if (is_expected_to_be_valid)
+ cl_assert_equal_i(0, error);
+ else
+ cl_assert_equal_i(GIT_ERROR, error);
+}
+
+void test_network_refspecs__parsing(void)
+{
+ // Ported from https://github.com/git/git/blob/abd2bde78bd994166900290434a2048e660dabed/t/t5511-refspec.sh
+
+ assert_refspec(GIT_DIRECTION_PUSH, "", false);
+ assert_refspec(GIT_DIRECTION_PUSH, ":", true);
+ assert_refspec(GIT_DIRECTION_PUSH, "::", false);
+ assert_refspec(GIT_DIRECTION_PUSH, "+:", true);
+
+ assert_refspec(GIT_DIRECTION_FETCH, "", true);
+ assert_refspec(GIT_DIRECTION_PUSH, ":", true);
+ assert_refspec(GIT_DIRECTION_FETCH, "::", false);
+
+ assert_refspec(GIT_DIRECTION_PUSH, "refs/heads/*:refs/remotes/frotz/*", true);
+ assert_refspec(GIT_DIRECTION_PUSH, "refs/heads/*:refs/remotes/frotz", false);
+ assert_refspec(GIT_DIRECTION_PUSH, "refs/heads:refs/remotes/frotz/*", false);
+ assert_refspec(GIT_DIRECTION_PUSH, "refs/heads/master:refs/remotes/frotz/xyzzy", true);
+
+ /*
+ * These have invalid LHS, but we do not have a formal "valid sha-1
+ * expression syntax checker" so they are not checked with the current
+ * code. They will be caught downstream anyway, but we may want to
+ * have tighter check later...
+ */
+ //assert_refspec(GIT_DIRECTION_PUSH, "refs/heads/master::refs/remotes/frotz/xyzzy", false);
+ //assert_refspec(GIT_DIRECTION_PUSH, "refs/heads/maste :refs/remotes/frotz/xyzzy", false);
+
+ assert_refspec(GIT_DIRECTION_FETCH, "refs/heads/*:refs/remotes/frotz/*", true);
+ assert_refspec(GIT_DIRECTION_FETCH, "refs/heads/*:refs/remotes/frotz", false);
+ assert_refspec(GIT_DIRECTION_FETCH, "refs/heads:refs/remotes/frotz/*", false);
+ assert_refspec(GIT_DIRECTION_FETCH, "refs/heads/master:refs/remotes/frotz/xyzzy", true);
+ assert_refspec(GIT_DIRECTION_FETCH, "refs/heads/master::refs/remotes/frotz/xyzzy", false);
+ assert_refspec(GIT_DIRECTION_FETCH, "refs/heads/maste :refs/remotes/frotz/xyzzy", false);
+
+ assert_refspec(GIT_DIRECTION_PUSH, "master~1:refs/remotes/frotz/backup", true);
+ assert_refspec(GIT_DIRECTION_FETCH, "master~1:refs/remotes/frotz/backup", false);
+ assert_refspec(GIT_DIRECTION_PUSH, "HEAD~4:refs/remotes/frotz/new", true);
+ assert_refspec(GIT_DIRECTION_FETCH, "HEAD~4:refs/remotes/frotz/new", false);
+
+ assert_refspec(GIT_DIRECTION_PUSH, "HEAD", true);
+ assert_refspec(GIT_DIRECTION_FETCH, "HEAD", true);
+ assert_refspec(GIT_DIRECTION_PUSH, "refs/heads/ nitfol", false);
+ assert_refspec(GIT_DIRECTION_FETCH, "refs/heads/ nitfol", false);
+
+ assert_refspec(GIT_DIRECTION_PUSH, "HEAD:", false);
+ assert_refspec(GIT_DIRECTION_FETCH, "HEAD:", true);
+ assert_refspec(GIT_DIRECTION_PUSH, "refs/heads/ nitfol:", false);
+ assert_refspec(GIT_DIRECTION_FETCH, "refs/heads/ nitfol:", false);
+
+ assert_refspec(GIT_DIRECTION_PUSH, ":refs/remotes/frotz/deleteme", true);
+ assert_refspec(GIT_DIRECTION_FETCH, ":refs/remotes/frotz/HEAD-to-me", true);
+ assert_refspec(GIT_DIRECTION_PUSH, ":refs/remotes/frotz/delete me", false);
+ assert_refspec(GIT_DIRECTION_FETCH, ":refs/remotes/frotz/HEAD to me", false);
+
+ assert_refspec(GIT_DIRECTION_FETCH, "refs/heads/*/for-linus:refs/remotes/mine/*-blah", false);
+ assert_refspec(GIT_DIRECTION_PUSH, "refs/heads/*/for-linus:refs/remotes/mine/*-blah", false);
+
+ assert_refspec(GIT_DIRECTION_FETCH, "refs/heads*/for-linus:refs/remotes/mine/*", false);
+ assert_refspec(GIT_DIRECTION_PUSH, "refs/heads*/for-linus:refs/remotes/mine/*", false);
+
+ assert_refspec(GIT_DIRECTION_FETCH, "refs/heads/*/*/for-linus:refs/remotes/mine/*", false);
+ assert_refspec(GIT_DIRECTION_PUSH, "refs/heads/*/*/for-linus:refs/remotes/mine/*", false);
+
+ assert_refspec(GIT_DIRECTION_FETCH, "refs/heads/*/for-linus:refs/remotes/mine/*", true);
+ assert_refspec(GIT_DIRECTION_PUSH, "refs/heads/*/for-linus:refs/remotes/mine/*", true);
+
+ assert_refspec(GIT_DIRECTION_FETCH, "master", true);
+ assert_refspec(GIT_DIRECTION_PUSH, "master", true);
+}
diff --git a/tests/network/remote/createthenload.c b/tests/network/remote/createthenload.c
new file mode 100644
index 000000000..ac6cfccd3
--- /dev/null
+++ b/tests/network/remote/createthenload.c
@@ -0,0 +1,37 @@
+#include "clar_libgit2.h"
+
+static git_remote *_remote;
+static git_repository *_repo;
+static git_config *_config;
+static char url[] = "http://github.com/libgit2/libgit2.git";
+
+void test_network_remote_createthenload__initialize(void)
+{
+ cl_fixture_sandbox("testrepo.git");
+
+ cl_git_pass(git_repository_open(&_repo, "testrepo.git"));
+
+ cl_git_pass(git_repository_config(&_config, _repo));
+ cl_git_pass(git_config_set_string(_config, "remote.origin.fetch", "+refs/heads/*:refs/remotes/origin/*"));
+ cl_git_pass(git_config_set_string(_config, "remote.origin.url", url));
+ git_config_free(_config);
+
+ cl_git_pass(git_remote_load(&_remote, _repo, "origin"));
+}
+
+void test_network_remote_createthenload__cleanup(void)
+{
+ git_remote_free(_remote);
+ _remote = NULL;
+
+ git_repository_free(_repo);
+ _repo = NULL;
+
+ cl_fixture_cleanup("testrepo.git");
+}
+
+void test_network_remote_createthenload__parsing(void)
+{
+ cl_assert_equal_s(git_remote_name(_remote), "origin");
+ cl_assert_equal_s(git_remote_url(_remote), url);
+}
diff --git a/tests/network/remote/isvalidname.c b/tests/network/remote/isvalidname.c
new file mode 100644
index 000000000..c26fbd0a5
--- /dev/null
+++ b/tests/network/remote/isvalidname.c
@@ -0,0 +1,17 @@
+#include "clar_libgit2.h"
+
+void test_network_remote_isvalidname__can_detect_invalid_formats(void)
+{
+ cl_assert_equal_i(false, git_remote_is_valid_name("/"));
+ cl_assert_equal_i(false, git_remote_is_valid_name("//"));
+ cl_assert_equal_i(false, git_remote_is_valid_name(".lock"));
+ cl_assert_equal_i(false, git_remote_is_valid_name("a.lock"));
+ cl_assert_equal_i(false, git_remote_is_valid_name("/no/leading/slash"));
+ cl_assert_equal_i(false, git_remote_is_valid_name("no/trailing/slash/"));
+}
+
+void test_network_remote_isvalidname__wont_hopefully_choke_on_valid_formats(void)
+{
+ cl_assert_equal_i(true, git_remote_is_valid_name("webmatrix"));
+ cl_assert_equal_i(true, git_remote_is_valid_name("yishaigalatzer/rules"));
+}
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/network/remote/rename.c b/tests/network/remote/rename.c
new file mode 100644
index 000000000..ed98ee811
--- /dev/null
+++ b/tests/network/remote/rename.c
@@ -0,0 +1,174 @@
+#include "clar_libgit2.h"
+#include "config/config_helpers.h"
+
+#include "repository.h"
+
+static git_remote *_remote;
+static git_repository *_repo;
+
+void test_network_remote_rename__initialize(void)
+{
+ _repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_git_pass(git_remote_load(&_remote, _repo, "test"));
+}
+
+void test_network_remote_rename__cleanup(void)
+{
+ git_remote_free(_remote);
+ _remote = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+static int dont_call_me_cb(const char *fetch_refspec, void *payload)
+{
+ GIT_UNUSED(fetch_refspec);
+ GIT_UNUSED(payload);
+
+ cl_assert(false);
+
+ return -1;
+}
+
+void test_network_remote_rename__renaming_a_remote_moves_related_configuration_section(void)
+{
+ assert_config_entry_existence(_repo, "remote.test.fetch", true);
+ assert_config_entry_existence(_repo, "remote.just/renamed.fetch", false);
+
+ cl_git_pass(git_remote_rename(_remote, "just/renamed", dont_call_me_cb, NULL));
+
+ assert_config_entry_existence(_repo, "remote.test.fetch", false);
+ assert_config_entry_existence(_repo, "remote.just/renamed.fetch", true);
+}
+
+void test_network_remote_rename__renaming_a_remote_updates_branch_related_configuration_entries(void)
+{
+ assert_config_entry_value(_repo, "branch.master.remote", "test");
+
+ cl_git_pass(git_remote_rename(_remote, "just/renamed", dont_call_me_cb, NULL));
+
+ assert_config_entry_value(_repo, "branch.master.remote", "just/renamed");
+}
+
+void test_network_remote_rename__renaming_a_remote_updates_default_fetchrefspec(void)
+{
+ cl_git_pass(git_remote_rename(_remote, "just/renamed", dont_call_me_cb, NULL));
+
+ assert_config_entry_value(_repo, "remote.just/renamed.fetch", "+refs/heads/*:refs/remotes/just/renamed/*");
+}
+
+void test_network_remote_rename__renaming_a_remote_without_a_fetchrefspec_doesnt_create_one(void)
+{
+ git_config *config;
+
+ git_remote_free(_remote);
+ cl_git_pass(git_repository_config__weakptr(&config, _repo));
+ cl_git_pass(git_config_delete_entry(config, "remote.test.fetch"));
+
+ cl_git_pass(git_remote_load(&_remote, _repo, "test"));
+
+ assert_config_entry_existence(_repo, "remote.test.fetch", false);
+
+ cl_git_pass(git_remote_rename(_remote, "just/renamed", dont_call_me_cb, NULL));
+
+ assert_config_entry_existence(_repo, "remote.just/renamed.fetch", false);
+}
+
+static int ensure_refspecs(const char* refspec_name, void *payload)
+{
+ int i = 0;
+ bool found = false;
+ const char ** exp = (const char **)payload;
+
+ while (exp[i]) {
+ if (strcmp(exp[i++], refspec_name))
+ continue;
+
+ found = true;
+ break;
+ }
+
+ cl_assert(found);
+
+ return 0;
+}
+
+void test_network_remote_rename__renaming_a_remote_notifies_of_non_default_fetchrefspec(void)
+{
+ git_config *config;
+
+ char *expected_refspecs[] = {
+ "+refs/*:refs/*",
+ NULL
+ };
+
+ git_remote_free(_remote);
+ cl_git_pass(git_repository_config__weakptr(&config, _repo));
+ cl_git_pass(git_config_set_string(config, "remote.test.fetch", "+refs/*:refs/*"));
+ cl_git_pass(git_remote_load(&_remote, _repo, "test"));
+
+ cl_git_pass(git_remote_rename(_remote, "just/renamed", ensure_refspecs, &expected_refspecs));
+
+ assert_config_entry_value(_repo, "remote.just/renamed.fetch", "+refs/*:refs/*");
+}
+
+void test_network_remote_rename__new_name_can_contain_dots(void)
+{
+ cl_git_pass(git_remote_rename(_remote, "just.renamed", dont_call_me_cb, NULL));
+ cl_assert_equal_s("just.renamed", git_remote_name(_remote));
+}
+
+void test_network_remote_rename__new_name_must_conform_to_reference_naming_conventions(void)
+{
+ cl_assert_equal_i(
+ GIT_EINVALIDSPEC,
+ git_remote_rename(_remote, "new@{name", dont_call_me_cb, NULL));
+}
+
+void test_network_remote_rename__renamed_name_is_persisted(void)
+{
+ git_remote *renamed;
+ git_repository *another_repo;
+
+ cl_git_fail(git_remote_load(&renamed, _repo, "just/renamed"));
+
+ cl_git_pass(git_remote_rename(_remote, "just/renamed", dont_call_me_cb, NULL));
+
+ cl_git_pass(git_repository_open(&another_repo, "testrepo.git"));
+ cl_git_pass(git_remote_load(&renamed, _repo, "just/renamed"));
+
+ git_remote_free(renamed);
+ git_repository_free(another_repo);
+}
+
+void test_network_remote_rename__cannot_overwrite_an_existing_remote(void)
+{
+ cl_assert_equal_i(GIT_EEXISTS, git_remote_rename(_remote, "test", dont_call_me_cb, NULL));
+ cl_assert_equal_i(GIT_EEXISTS, git_remote_rename(_remote, "test_with_pushurl", dont_call_me_cb, NULL));
+}
+
+void test_network_remote_rename__renaming_a_remote_moves_the_underlying_reference(void)
+{
+ git_reference *underlying;
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&underlying, _repo, "refs/remotes/just/renamed"));
+ cl_git_pass(git_reference_lookup(&underlying, _repo, "refs/remotes/test/master"));
+ git_reference_free(underlying);
+
+ cl_git_pass(git_remote_rename(_remote, "just/renamed", dont_call_me_cb, NULL));
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&underlying, _repo, "refs/remotes/test/master"));
+ cl_git_pass(git_reference_lookup(&underlying, _repo, "refs/remotes/just/renamed/master"));
+ git_reference_free(underlying);
+}
+
+void test_network_remote_rename__cannot_rename_an_inmemory_remote(void)
+{
+ git_remote *remote;
+
+ cl_git_pass(git_remote_create_inmemory(&remote, _repo, NULL, "file:///blah"));
+ cl_git_fail(git_remote_rename(remote, "newname", NULL, NULL));
+
+ git_remote_free(remote);
+}
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/notes/notes.c b/tests/notes/notes.c
new file mode 100644
index 000000000..82dcaf8ca
--- /dev/null
+++ b/tests/notes/notes.c
@@ -0,0 +1,387 @@
+#include "clar_libgit2.h"
+
+static git_repository *_repo;
+static git_signature *_sig;
+
+void test_notes_notes__initialize(void)
+{
+ _repo = cl_git_sandbox_init("testrepo.git");
+ cl_git_pass(git_signature_now(&_sig, "alice", "alice@example.com"));
+}
+
+void test_notes_notes__cleanup(void)
+{
+ git_signature_free(_sig);
+ _sig = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+static void assert_note_equal(git_note *note, char *message, git_oid *note_oid) {
+ git_blob *blob;
+
+ cl_assert_equal_s(git_note_message(note), message);
+ cl_assert(!git_oid_cmp(git_note_oid(note), note_oid));
+
+ cl_git_pass(git_blob_lookup(&blob, _repo, note_oid));
+ cl_assert_equal_s(git_note_message(note), (const char *)git_blob_rawcontent(blob));
+
+ git_blob_free(blob);
+}
+
+static void create_note(git_oid *note_oid, const char *canonical_namespace, const char *target_sha, const char *message)
+{
+ git_oid oid;
+
+ cl_git_pass(git_oid_fromstr(&oid, target_sha));
+ cl_git_pass(git_note_create(note_oid, _repo, _sig, _sig, canonical_namespace, &oid, message, 0));
+}
+
+static struct {
+ const char *note_sha;
+ const char *annotated_object_sha;
+}
+list_expectations[] = {
+ { "1c73b1f51762155d357bcd1fd4f2c409ef80065b", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045" },
+ { "1c73b1f51762155d357bcd1fd4f2c409ef80065b", "9fd738e8f7967c078dceed8190330fc8648ee56a" },
+ { "257b43746b6b46caa4aa788376c647cce0a33e2b", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750" },
+ { "1ec1c8e03f461f4f5d3f3702172483662e7223f3", "c47800c7266a2be04c571c04d5a6614691ea99bd" },
+ { NULL, NULL }
+};
+
+#define EXPECTATIONS_COUNT (sizeof(list_expectations)/sizeof(list_expectations[0])) - 1
+
+static int note_list_cb(
+ const git_oid *blob_id, const git_oid *annotated_obj_id, void *payload)
+{
+ git_oid expected_note_oid, expected_target_oid;
+
+ unsigned int *count = (unsigned int *)payload;
+
+ cl_assert(*count < EXPECTATIONS_COUNT);
+
+ cl_git_pass(git_oid_fromstr(&expected_note_oid, list_expectations[*count].note_sha));
+ cl_assert(git_oid_cmp(&expected_note_oid, blob_id) == 0);
+
+ cl_git_pass(git_oid_fromstr(&expected_target_oid, list_expectations[*count].annotated_object_sha));
+ cl_assert(git_oid_cmp(&expected_target_oid, annotated_obj_id) == 0);
+
+ (*count)++;
+
+ return 0;
+}
+
+/*
+ * $ git notes --ref i-can-see-dead-notes add -m "I decorate a65f" a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+ * $ git notes --ref i-can-see-dead-notes add -m "I decorate c478" c47800c7266a2be04c571c04d5a6614691ea99bd
+ * $ git notes --ref i-can-see-dead-notes add -m "I decorate 9fd7 and 4a20" 9fd738e8f7967c078dceed8190330fc8648ee56a
+ * $ git notes --ref i-can-see-dead-notes add -m "I decorate 9fd7 and 4a20" 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+ *
+ * $ git notes --ref i-can-see-dead-notes list
+ * 1c73b1f51762155d357bcd1fd4f2c409ef80065b 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+ * 1c73b1f51762155d357bcd1fd4f2c409ef80065b 9fd738e8f7967c078dceed8190330fc8648ee56a
+ * 257b43746b6b46caa4aa788376c647cce0a33e2b a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+ * 1ec1c8e03f461f4f5d3f3702172483662e7223f3 c47800c7266a2be04c571c04d5a6614691ea99bd
+ *
+ * $ git ls-tree refs/notes/i-can-see-dead-notes
+ * 100644 blob 1c73b1f51762155d357bcd1fd4f2c409ef80065b 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+ * 100644 blob 1c73b1f51762155d357bcd1fd4f2c409ef80065b 9fd738e8f7967c078dceed8190330fc8648ee56a
+ * 100644 blob 257b43746b6b46caa4aa788376c647cce0a33e2b a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+ * 100644 blob 1ec1c8e03f461f4f5d3f3702172483662e7223f3 c47800c7266a2be04c571c04d5a6614691ea99bd
+*/
+void test_notes_notes__can_retrieve_a_list_of_notes_for_a_given_namespace(void)
+{
+ git_oid note_oid1, note_oid2, note_oid3, note_oid4;
+ unsigned int retrieved_notes = 0;
+
+ create_note(&note_oid1, "refs/notes/i-can-see-dead-notes", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", "I decorate a65f\n");
+ create_note(&note_oid2, "refs/notes/i-can-see-dead-notes", "c47800c7266a2be04c571c04d5a6614691ea99bd", "I decorate c478\n");
+ create_note(&note_oid3, "refs/notes/i-can-see-dead-notes", "9fd738e8f7967c078dceed8190330fc8648ee56a", "I decorate 9fd7 and 4a20\n");
+ create_note(&note_oid4, "refs/notes/i-can-see-dead-notes", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", "I decorate 9fd7 and 4a20\n");
+
+ cl_git_pass(git_note_foreach
+(_repo, "refs/notes/i-can-see-dead-notes", note_list_cb, &retrieved_notes));
+
+ cl_assert_equal_i(4, retrieved_notes);
+}
+
+static int note_cancel_cb(
+ const git_oid *blob_id, const git_oid *annotated_obj_id, void *payload)
+{
+ unsigned int *count = (unsigned int *)payload;
+
+ GIT_UNUSED(blob_id);
+ GIT_UNUSED(annotated_obj_id);
+
+ (*count)++;
+
+ return (*count > 2);
+}
+
+void test_notes_notes__can_cancel_foreach(void)
+{
+ git_oid note_oid1, note_oid2, note_oid3, note_oid4;
+ unsigned int retrieved_notes = 0;
+
+ create_note(&note_oid1, "refs/notes/i-can-see-dead-notes", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", "I decorate a65f\n");
+ create_note(&note_oid2, "refs/notes/i-can-see-dead-notes", "c47800c7266a2be04c571c04d5a6614691ea99bd", "I decorate c478\n");
+ create_note(&note_oid3, "refs/notes/i-can-see-dead-notes", "9fd738e8f7967c078dceed8190330fc8648ee56a", "I decorate 9fd7 and 4a20\n");
+ create_note(&note_oid4, "refs/notes/i-can-see-dead-notes", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", "I decorate 9fd7 and 4a20\n");
+
+ cl_assert_equal_i(
+ GIT_EUSER,
+ git_note_foreach(_repo, "refs/notes/i-can-see-dead-notes",
+ note_cancel_cb, &retrieved_notes));
+}
+
+void test_notes_notes__retrieving_a_list_of_notes_for_an_unknown_namespace_returns_ENOTFOUND(void)
+{
+ int error;
+ unsigned int retrieved_notes = 0;
+
+ error = git_note_foreach(_repo, "refs/notes/i-am-not", note_list_cb, &retrieved_notes);
+ cl_git_fail(error);
+ cl_assert_equal_i(GIT_ENOTFOUND, error);
+
+ cl_assert_equal_i(0, retrieved_notes);
+}
+
+void test_notes_notes__inserting_a_note_without_passing_a_namespace_uses_the_default_namespace(void)
+{
+ git_oid note_oid, target_oid;
+ git_note *note, *default_namespace_note;
+ const char *default_ref;
+
+ cl_git_pass(git_oid_fromstr(&target_oid, "08b041783f40edfe12bb406c9c9a8a040177c125"));
+ cl_git_pass(git_note_default_ref(&default_ref, _repo));
+
+ create_note(&note_oid, NULL, "08b041783f40edfe12bb406c9c9a8a040177c125", "hello world\n");
+
+ cl_git_pass(git_note_read(&note, _repo, NULL, &target_oid));
+ cl_git_pass(git_note_read(&default_namespace_note, _repo, default_ref, &target_oid));
+
+ assert_note_equal(note, "hello world\n", &note_oid);
+ assert_note_equal(default_namespace_note, "hello world\n", &note_oid);
+
+ git_note_free(note);
+ git_note_free(default_namespace_note);
+}
+
+void test_notes_notes__can_insert_a_note_with_a_custom_namespace(void)
+{
+ git_oid note_oid, target_oid;
+ git_note *note;
+
+ cl_git_pass(git_oid_fromstr(&target_oid, "08b041783f40edfe12bb406c9c9a8a040177c125"));
+
+ create_note(&note_oid, "refs/notes/some/namespace", "08b041783f40edfe12bb406c9c9a8a040177c125", "hello world on a custom namespace\n");
+
+ cl_git_pass(git_note_read(&note, _repo, "refs/notes/some/namespace", &target_oid));
+
+ assert_note_equal(note, "hello world on a custom namespace\n", &note_oid);
+
+ git_note_free(note);
+}
+
+/*
+ * $ git notes --ref fanout list 8496071c1b46c854b31185ea97743be6a8774479
+ * 08b041783f40edfe12bb406c9c9a8a040177c125
+ */
+void test_notes_notes__creating_a_note_on_a_target_which_already_has_one_returns_EEXISTS(void)
+{
+ int error;
+ git_oid note_oid, target_oid;
+
+ cl_git_pass(git_oid_fromstr(&target_oid, "08b041783f40edfe12bb406c9c9a8a040177c125"));
+
+ create_note(&note_oid, NULL, "08b041783f40edfe12bb406c9c9a8a040177c125", "hello world\n");
+ error = git_note_create(&note_oid, _repo, _sig, _sig, NULL, &target_oid, "hello world\n", 0);
+ cl_git_fail(error);
+ cl_assert_equal_i(GIT_EEXISTS, error);
+
+ create_note(&note_oid, "refs/notes/some/namespace", "08b041783f40edfe12bb406c9c9a8a040177c125", "hello world\n");
+ error = git_note_create(&note_oid, _repo, _sig, _sig, "refs/notes/some/namespace", &target_oid, "hello world\n", 0);
+ cl_git_fail(error);
+ cl_assert_equal_i(GIT_EEXISTS, error);
+}
+
+
+void test_notes_notes__creating_a_note_on_a_target_can_overwrite_existing_note(void)
+{
+ git_oid note_oid, target_oid;
+ git_note *note, *namespace_note;
+
+ cl_git_pass(git_oid_fromstr(&target_oid, "08b041783f40edfe12bb406c9c9a8a040177c125"));
+
+ create_note(&note_oid, NULL, "08b041783f40edfe12bb406c9c9a8a040177c125", "hello old world\n");
+ cl_git_pass(git_note_create(&note_oid, _repo, _sig, _sig, NULL, &target_oid, "hello new world\n", 1));
+
+ cl_git_pass(git_note_read(&note, _repo, NULL, &target_oid));
+ assert_note_equal(note, "hello new world\n", &note_oid);
+
+ create_note(&note_oid, "refs/notes/some/namespace", "08b041783f40edfe12bb406c9c9a8a040177c125", "hello old world\n");
+ cl_git_pass(git_note_create(&note_oid, _repo, _sig, _sig, "refs/notes/some/namespace", &target_oid, "hello new ref world\n", 1));
+
+ cl_git_pass(git_note_read(&namespace_note, _repo, "refs/notes/some/namespace", &target_oid));
+ assert_note_equal(namespace_note, "hello new ref world\n", &note_oid);
+
+ git_note_free(note);
+ git_note_free(namespace_note);
+}
+
+static char *messages[] = {
+ "08c041783f40edfe12bb406c9c9a8a040177c125",
+ "96c45fbe09ab7445fc7c60fd8d17f32494399343",
+ "48cc7e38dcfc1ec87e70ec03e08c3e83d7a16aa1",
+ "24c3eaafb681c3df668f9df96f58e7b8c756eb04",
+ "96ca1b6ccc7858ae94684777f85ac0e7447f7040",
+ "7ac2db4378a08bb244a427c357e0082ee0d57ac6",
+ "e6cba23dbf4ef84fe35e884f017f4e24dc228572",
+ "c8cf3462c7d8feba716deeb2ebe6583bd54589e2",
+ "39c16b9834c2d665ac5f68ad91dc5b933bad8549",
+ "f3c582b1397df6a664224ebbaf9d4cc952706597",
+ "29cec67037fe8e89977474988219016ae7f342a6",
+ "36c4cd238bf8e82e27b740e0741b025f2e8c79ab",
+ "f1c45a47c02e01d5a9a326f1d9f7f756373387f8",
+ "4aca84406f5daee34ab513a60717c8d7b1763ead",
+ "84ce167da452552f63ed8407b55d5ece4901845f",
+ NULL
+};
+
+#define MESSAGES_COUNT (sizeof(messages)/sizeof(messages[0])) - 1
+
+/*
+ * $ git ls-tree refs/notes/fanout
+ * 040000 tree 4b22b35d44b5a4f589edf3dc89196399771796ea 84
+ *
+ * $ git ls-tree 4b22b35
+ * 040000 tree d71aab4f9b04b45ce09bcaa636a9be6231474759 96
+ *
+ * $ git ls-tree d71aab4
+ * 100644 blob 08b041783f40edfe12bb406c9c9a8a040177c125 071c1b46c854b31185ea97743be6a8774479
+ */
+void test_notes_notes__can_insert_a_note_in_an_existing_fanout(void)
+{
+ size_t i;
+ git_oid note_oid, target_oid;
+ git_note *_note;
+
+ cl_git_pass(git_oid_fromstr(&target_oid, "08b041783f40edfe12bb406c9c9a8a040177c125"));
+
+ for (i = 0; i < MESSAGES_COUNT; i++) {
+ cl_git_pass(git_note_create(&note_oid, _repo, _sig, _sig, "refs/notes/fanout", &target_oid, messages[i], 0));
+ cl_git_pass(git_note_read(&_note, _repo, "refs/notes/fanout", &target_oid));
+ git_note_free(_note);
+
+ git_oid_cpy(&target_oid, &note_oid);
+ }
+}
+
+/*
+ * $ git notes --ref fanout list 8496071c1b46c854b31185ea97743be6a8774479
+ * 08b041783f40edfe12bb406c9c9a8a040177c125
+ */
+void test_notes_notes__can_read_a_note_in_an_existing_fanout(void)
+{
+ git_oid note_oid, target_oid;
+ git_note *note;
+
+ cl_git_pass(git_oid_fromstr(&target_oid, "8496071c1b46c854b31185ea97743be6a8774479"));
+ cl_git_pass(git_note_read(&note, _repo, "refs/notes/fanout", &target_oid));
+
+ cl_git_pass(git_oid_fromstr(&note_oid, "08b041783f40edfe12bb406c9c9a8a040177c125"));
+ cl_assert(!git_oid_cmp(git_note_oid(note), &note_oid));
+
+ git_note_free(note);
+}
+
+void test_notes_notes__can_remove_a_note_in_an_existing_fanout(void)
+{
+ git_oid target_oid;
+ git_note *note;
+
+ cl_git_pass(git_oid_fromstr(&target_oid, "8496071c1b46c854b31185ea97743be6a8774479"));
+ cl_git_pass(git_note_remove(_repo, "refs/notes/fanout", _sig, _sig, &target_oid));
+
+ cl_git_fail(git_note_read(&note, _repo, "refs/notes/fanout", &target_oid));
+}
+
+void test_notes_notes__removing_a_note_which_doesnt_exists_returns_ENOTFOUND(void)
+{
+ int error;
+ git_oid target_oid;
+
+ cl_git_pass(git_oid_fromstr(&target_oid, "8496071c1b46c854b31185ea97743be6a8774479"));
+ cl_git_pass(git_note_remove(_repo, "refs/notes/fanout", _sig, _sig, &target_oid));
+
+ error = git_note_remove(_repo, "refs/notes/fanout", _sig, _sig, &target_oid);
+ cl_git_fail(error);
+ cl_assert_equal_i(GIT_ENOTFOUND, error);
+}
+
+void test_notes_notes__can_iterate_default_namespace(void)
+{
+ git_note_iterator *iter;
+ git_note *note;
+ git_oid note_id, annotated_id;
+ git_oid note_created[2];
+ const char* note_message[] = {
+ "I decorate a65f\n",
+ "I decorate c478\n"
+ };
+ int i, err;
+
+ create_note(&note_created[0], "refs/notes/commits",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", note_message[0]);
+ create_note(&note_created[1], "refs/notes/commits",
+ "c47800c7266a2be04c571c04d5a6614691ea99bd", note_message[1]);
+
+ cl_git_pass(git_note_iterator_new(&iter, _repo, NULL));
+
+ for (i = 0; (err = git_note_next(&note_id, &annotated_id, iter)) >= 0; ++i) {
+ cl_git_pass(git_note_read(&note, _repo, NULL, &annotated_id));
+ cl_assert_equal_s(git_note_message(note), note_message[i]);
+ git_note_free(note);
+ }
+
+ cl_assert_equal_i(GIT_ITEROVER, err);
+ cl_assert_equal_i(2, i);
+ git_note_iterator_free(iter);
+}
+
+void test_notes_notes__can_iterate_custom_namespace(void)
+{
+ git_note_iterator *iter;
+ git_note *note;
+ git_oid note_id, annotated_id;
+ git_oid note_created[2];
+ const char* note_message[] = {
+ "I decorate a65f\n",
+ "I decorate c478\n"
+ };
+ int i, err;
+
+ create_note(&note_created[0], "refs/notes/beer",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", note_message[0]);
+ create_note(&note_created[1], "refs/notes/beer",
+ "c47800c7266a2be04c571c04d5a6614691ea99bd", note_message[1]);
+
+ cl_git_pass(git_note_iterator_new(&iter, _repo, "refs/notes/beer"));
+
+ for (i = 0; (err = git_note_next(&note_id, &annotated_id, iter)) >= 0; ++i) {
+ cl_git_pass(git_note_read(&note, _repo, "refs/notes/beer", &annotated_id));
+ cl_assert_equal_s(git_note_message(note), note_message[i]);
+ git_note_free(note);
+ }
+
+ cl_assert_equal_i(GIT_ITEROVER, err);
+ cl_assert_equal_i(2, i);
+ git_note_iterator_free(iter);
+}
+
+void test_notes_notes__empty_iterate(void)
+{
+ git_note_iterator *iter;
+
+ cl_git_fail(git_note_iterator_new(&iter, _repo, "refs/notes/commits"));
+}
diff --git a/tests/notes/notesref.c b/tests/notes/notesref.c
new file mode 100644
index 000000000..c89b71ba5
--- /dev/null
+++ b/tests/notes/notesref.c
@@ -0,0 +1,64 @@
+#include "clar_libgit2.h"
+
+#include "notes.h"
+
+static git_repository *_repo;
+static git_note *_note;
+static git_signature *_sig;
+static git_config *_cfg;
+
+void test_notes_notesref__initialize(void)
+{
+ cl_fixture_sandbox("testrepo.git");
+ cl_git_pass(git_repository_open(&_repo, "testrepo.git"));
+}
+
+void test_notes_notesref__cleanup(void)
+{
+ git_note_free(_note);
+ _note = NULL;
+
+ git_signature_free(_sig);
+ _sig = NULL;
+
+ git_config_free(_cfg);
+ _cfg = NULL;
+
+ git_repository_free(_repo);
+ _repo = NULL;
+
+ cl_fixture_cleanup("testrepo.git");
+}
+
+void test_notes_notesref__config_corenotesref(void)
+{
+ git_oid oid, note_oid;
+ const char *default_ref;
+
+ cl_git_pass(git_signature_now(&_sig, "alice", "alice@example.com"));
+ cl_git_pass(git_oid_fromstr(&oid, "8496071c1b46c854b31185ea97743be6a8774479"));
+
+ cl_git_pass(git_repository_config(&_cfg, _repo));
+
+ cl_git_pass(git_config_set_string(_cfg, "core.notesRef", "refs/notes/mydefaultnotesref"));
+
+ cl_git_pass(git_note_create(&note_oid, _repo, _sig, _sig, NULL, &oid, "test123test\n", 0));
+
+ cl_git_pass(git_note_read(&_note, _repo, NULL, &oid));
+ cl_assert_equal_s("test123test\n", git_note_message(_note));
+ cl_assert(!git_oid_cmp(git_note_oid(_note), &note_oid));
+
+ git_note_free(_note);
+
+ cl_git_pass(git_note_read(&_note, _repo, "refs/notes/mydefaultnotesref", &oid));
+ cl_assert_equal_s("test123test\n", git_note_message(_note));
+ cl_assert(!git_oid_cmp(git_note_oid(_note), &note_oid));
+
+ cl_git_pass(git_note_default_ref(&default_ref, _repo));
+ cl_assert_equal_s("refs/notes/mydefaultnotesref", default_ref);
+
+ cl_git_pass(git_config_delete_entry(_cfg, "core.notesRef"));
+
+ cl_git_pass(git_note_default_ref(&default_ref, _repo));
+ cl_assert_equal_s(GIT_NOTES_DEFAULT_REF, default_ref);
+}
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/object/blob/write.c b/tests/object/blob/write.c
new file mode 100644
index 000000000..203bc67c1
--- /dev/null
+++ b/tests/object/blob/write.c
@@ -0,0 +1,69 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "posix.h"
+#include "path.h"
+#include "fileops.h"
+
+static git_repository *repo;
+
+#define WORKDIR "empty_standard_repo"
+#define BARE_REPO "testrepo.git"
+#define ELSEWHERE "elsewhere"
+
+typedef int (*blob_creator_fn)(
+ git_oid *,
+ git_repository *,
+ const char *);
+
+void test_object_blob_write__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static void assert_blob_creation(const char *path_to_file, const char *blob_from_path, blob_creator_fn creator)
+{
+ git_oid oid;
+ cl_git_mkfile(path_to_file, "1..2...3... Can you hear me?\n");
+
+ cl_must_pass(creator(&oid, repo, blob_from_path));
+ cl_assert(git_oid_streq(&oid, "da5e4f20c91c81b44a7e298f3d3fb3fe2f178e32") == 0);
+}
+
+void test_object_blob_write__can_create_a_blob_in_a_standard_repo_from_a_file_located_in_the_working_directory(void)
+{
+ repo = cl_git_sandbox_init(WORKDIR);
+
+ assert_blob_creation(WORKDIR "/test.txt", "test.txt", &git_blob_create_fromworkdir);
+}
+
+void test_object_blob_write__can_create_a_blob_in_a_standard_repo_from_a_absolute_filepath_pointing_outside_of_the_working_directory(void)
+{
+ git_buf full_path = GIT_BUF_INIT;
+
+ repo = cl_git_sandbox_init(WORKDIR);
+
+ cl_must_pass(p_mkdir(ELSEWHERE, 0777));
+ cl_must_pass(git_path_prettify_dir(&full_path, ELSEWHERE, NULL));
+ cl_must_pass(git_buf_puts(&full_path, "test.txt"));
+
+ assert_blob_creation(ELSEWHERE "/test.txt", git_buf_cstr(&full_path), &git_blob_create_fromdisk);
+
+ git_buf_free(&full_path);
+ cl_must_pass(git_futils_rmdir_r(ELSEWHERE, NULL, GIT_RMDIR_REMOVE_FILES));
+}
+
+void test_object_blob_write__can_create_a_blob_in_a_bare_repo_from_a_absolute_filepath(void)
+{
+ git_buf full_path = GIT_BUF_INIT;
+
+ repo = cl_git_sandbox_init(BARE_REPO);
+
+ cl_must_pass(p_mkdir(ELSEWHERE, 0777));
+ cl_must_pass(git_path_prettify_dir(&full_path, ELSEWHERE, NULL));
+ cl_must_pass(git_buf_puts(&full_path, "test.txt"));
+
+ assert_blob_creation(ELSEWHERE "/test.txt", git_buf_cstr(&full_path), &git_blob_create_fromdisk);
+
+ git_buf_free(&full_path);
+ cl_must_pass(git_futils_rmdir_r(ELSEWHERE, NULL, GIT_RMDIR_REMOVE_FILES));
+}
diff --git a/tests/object/cache.c b/tests/object/cache.c
new file mode 100644
index 000000000..b927b2514
--- /dev/null
+++ b/tests/object/cache.c
@@ -0,0 +1,287 @@
+#include "clar_libgit2.h"
+#include "repository.h"
+
+static git_repository *g_repo;
+
+void test_object_cache__initialize(void)
+{
+ g_repo = NULL;
+}
+
+void test_object_cache__cleanup(void)
+{
+ git_repository_free(g_repo);
+ g_repo = NULL;
+
+ git_libgit2_opts(GIT_OPT_SET_CACHE_OBJECT_LIMIT, (int)GIT_OBJ_BLOB, (size_t)0);
+}
+
+static struct {
+ git_otype type;
+ const char *sha;
+} g_data[] = {
+ /* HEAD */
+ { GIT_OBJ_BLOB, "a8233120f6ad708f843d861ce2b7228ec4e3dec6" }, /* README */
+ { GIT_OBJ_BLOB, "3697d64be941a53d4ae8f6a271e4e3fa56b022cc" }, /* branch_file.txt */
+ { GIT_OBJ_BLOB, "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd" }, /* new.txt */
+
+ /* refs/heads/subtrees */
+ { GIT_OBJ_BLOB, "1385f264afb75a56a5bec74243be9b367ba4ca08" }, /* README */
+ { GIT_OBJ_TREE, "f1425cef211cc08caa31e7b545ffb232acb098c3" }, /* ab */
+ { GIT_OBJ_BLOB, "d6c93164c249c8000205dd4ec5cbca1b516d487f" }, /* ab/4.txt */
+ { GIT_OBJ_TREE, "9a03079b8a8ee85a0bee58bf9be3da8b62414ed4" }, /* ab/c */
+ { GIT_OBJ_BLOB, "270b8ea76056d5cad83af921837702d3e3c2924d" }, /* ab/c/3.txt */
+ { GIT_OBJ_TREE, "b6361fc6a97178d8fc8639fdeed71c775ab52593" }, /* ab/de */
+ { GIT_OBJ_BLOB, "e7b4ad382349ff96dd8199000580b9b1e2042eb0" }, /* ab/de/2.txt */
+ { GIT_OBJ_TREE, "3259a6bd5b57fb9c1281bb7ed3167b50f224cb54" }, /* ab/de/fgh */
+ { GIT_OBJ_BLOB, "1f67fc4386b2d171e0d21be1c447e12660561f9b" }, /* ab/de/fgh/1.txt */
+ { GIT_OBJ_BLOB, "45b983be36b73c0788dc9cbcb76cbb80fc7bb057" }, /* branch_file.txt */
+ { GIT_OBJ_BLOB, "fa49b077972391ad58037050f2a75f74e3671e92" }, /* new.txt */
+
+ /* refs/heads/chomped */
+ { GIT_OBJ_BLOB, "0266163a49e280c4f5ed1e08facd36a2bd716bcf" }, /* readme.txt */
+
+ { 0, NULL },
+ { 0, NULL }
+};
+
+void test_object_cache__cache_everything(void)
+{
+ int i, start;
+ git_oid oid;
+ git_odb_object *odb_obj;
+ git_object *obj;
+ git_odb *odb;
+
+ git_libgit2_opts(
+ GIT_OPT_SET_CACHE_OBJECT_LIMIT, (int)GIT_OBJ_BLOB, (size_t)32767);
+
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_repository_odb(&odb, g_repo));
+
+ start = (int)git_cache_size(&g_repo->objects);
+
+ for (i = 0; g_data[i].sha != NULL; ++i) {
+ int count = (int)git_cache_size(&g_repo->objects);
+
+ cl_git_pass(git_oid_fromstr(&oid, g_data[i].sha));
+
+ /* alternate between loading raw and parsed objects */
+ if ((i & 1) == 0) {
+ cl_git_pass(git_odb_read(&odb_obj, odb, &oid));
+ cl_assert(g_data[i].type == git_odb_object_type(odb_obj));
+ git_odb_object_free(odb_obj);
+ } else {
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+ cl_assert(g_data[i].type == git_object_type(obj));
+ git_object_free(obj);
+ }
+
+ cl_assert_equal_i(count + 1, (int)git_cache_size(&g_repo->objects));
+ }
+
+ cl_assert_equal_i(i, (int)git_cache_size(&g_repo->objects) - start);
+
+ git_odb_free(odb);
+
+ for (i = 0; g_data[i].sha != NULL; ++i) {
+ int count = (int)git_cache_size(&g_repo->objects);
+
+ cl_git_pass(git_oid_fromstr(&oid, g_data[i].sha));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+ cl_assert(g_data[i].type == git_object_type(obj));
+ git_object_free(obj);
+
+ cl_assert_equal_i(count, (int)git_cache_size(&g_repo->objects));
+ }
+}
+
+void test_object_cache__cache_no_blobs(void)
+{
+ int i, start, nonblobs = 0;
+ git_oid oid;
+ git_odb_object *odb_obj;
+ git_object *obj;
+ git_odb *odb;
+
+ git_libgit2_opts(GIT_OPT_SET_CACHE_OBJECT_LIMIT, (int)GIT_OBJ_BLOB, (size_t)0);
+
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_repository_odb(&odb, g_repo));
+
+ start = (int)git_cache_size(&g_repo->objects);
+
+ for (i = 0; g_data[i].sha != NULL; ++i) {
+ int count = (int)git_cache_size(&g_repo->objects);
+
+ cl_git_pass(git_oid_fromstr(&oid, g_data[i].sha));
+
+ /* alternate between loading raw and parsed objects */
+ if ((i & 1) == 0) {
+ cl_git_pass(git_odb_read(&odb_obj, odb, &oid));
+ cl_assert(g_data[i].type == git_odb_object_type(odb_obj));
+ git_odb_object_free(odb_obj);
+ } else {
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+ cl_assert(g_data[i].type == git_object_type(obj));
+ git_object_free(obj);
+ }
+
+ if (g_data[i].type == GIT_OBJ_BLOB)
+ cl_assert_equal_i(count, (int)git_cache_size(&g_repo->objects));
+ else {
+ cl_assert_equal_i(count + 1, (int)git_cache_size(&g_repo->objects));
+ nonblobs++;
+ }
+ }
+
+ cl_assert_equal_i(nonblobs, (int)git_cache_size(&g_repo->objects) - start);
+
+ git_odb_free(odb);
+}
+
+static void *cache_parsed(void *arg)
+{
+ int i;
+ git_oid oid;
+ git_object *obj;
+
+ for (i = ((int *)arg)[1]; g_data[i].sha != NULL; i += 2) {
+ cl_git_pass(git_oid_fromstr(&oid, g_data[i].sha));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+ cl_assert(g_data[i].type == git_object_type(obj));
+ git_object_free(obj);
+ }
+
+ for (i = 0; i < ((int *)arg)[1]; i += 2) {
+ cl_git_pass(git_oid_fromstr(&oid, g_data[i].sha));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+ cl_assert(g_data[i].type == git_object_type(obj));
+ git_object_free(obj);
+ }
+
+ return arg;
+}
+
+static void *cache_raw(void *arg)
+{
+ int i;
+ git_oid oid;
+ git_odb *odb;
+ git_odb_object *odb_obj;
+
+ cl_git_pass(git_repository_odb(&odb, g_repo));
+
+ for (i = ((int *)arg)[1]; g_data[i].sha != NULL; i += 2) {
+ cl_git_pass(git_oid_fromstr(&oid, g_data[i].sha));
+ cl_git_pass(git_odb_read(&odb_obj, odb, &oid));
+ cl_assert(g_data[i].type == git_odb_object_type(odb_obj));
+ git_odb_object_free(odb_obj);
+ }
+
+ for (i = 0; i < ((int *)arg)[1]; i += 2) {
+ cl_git_pass(git_oid_fromstr(&oid, g_data[i].sha));
+ cl_git_pass(git_odb_read(&odb_obj, odb, &oid));
+ cl_assert(g_data[i].type == git_odb_object_type(odb_obj));
+ git_odb_object_free(odb_obj);
+ }
+
+ git_odb_free(odb);
+
+ return arg;
+}
+
+#define REPEAT 20
+#define THREADCOUNT 50
+
+void test_object_cache__threadmania(void)
+{
+ int try, th, max_i;
+ void *data;
+ void *(*fn)(void *);
+
+#ifdef GIT_THREADS
+ git_thread t[THREADCOUNT];
+#endif
+
+ for (max_i = 0; g_data[max_i].sha != NULL; ++max_i)
+ /* count up */;
+
+ for (try = 0; try < REPEAT; ++try) {
+
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
+
+ for (th = 0; th < THREADCOUNT; ++th) {
+ data = git__malloc(2 * sizeof(int));
+
+ ((int *)data)[0] = th;
+ ((int *)data)[1] = th % max_i;
+
+ fn = (th & 1) ? cache_parsed : cache_raw;
+
+#ifdef GIT_THREADS
+ cl_git_pass(git_thread_create(&t[th], NULL, fn, data));
+#else
+ cl_assert(fn(data) == data);
+ git__free(data);
+#endif
+ }
+
+#ifdef GIT_THREADS
+ for (th = 0; th < THREADCOUNT; ++th) {
+ cl_git_pass(git_thread_join(t[th], &data));
+ cl_assert_equal_i(th, ((int *)data)[0]);
+ git__free(data);
+ }
+#endif
+
+ git_repository_free(g_repo);
+ g_repo = NULL;
+ }
+}
+
+static void *cache_quick(void *arg)
+{
+ git_oid oid;
+ git_object *obj;
+
+ cl_git_pass(git_oid_fromstr(&oid, g_data[4].sha));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+ cl_assert(g_data[4].type == git_object_type(obj));
+ git_object_free(obj);
+
+ return arg;
+}
+
+void test_object_cache__fast_thread_rush(void)
+{
+ int try, th, data[THREADCOUNT*2];
+#ifdef GIT_THREADS
+ git_thread t[THREADCOUNT*2];
+#endif
+
+ for (try = 0; try < REPEAT; ++try) {
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
+
+ for (th = 0; th < THREADCOUNT*2; ++th) {
+ data[th] = th;
+#ifdef GIT_THREADS
+ cl_git_pass(
+ git_thread_create(&t[th], NULL, cache_quick, &data[th]));
+#else
+ cl_assert(cache_quick(&data[th]) == &data[th]);
+#endif
+ }
+
+#ifdef GIT_THREADS
+ for (th = 0; th < THREADCOUNT*2; ++th) {
+ void *rval;
+ cl_git_pass(git_thread_join(t[th], &rval));
+ cl_assert_equal_i(th, *((int *)rval));
+ }
+#endif
+
+ git_repository_free(g_repo);
+ g_repo = NULL;
+ }
+}
diff --git a/tests/object/commit/commitstagedfile.c b/tests/object/commit/commitstagedfile.c
new file mode 100644
index 000000000..9867ab418
--- /dev/null
+++ b/tests/object/commit/commitstagedfile.c
@@ -0,0 +1,132 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+
+static git_repository *repo;
+
+void test_object_commit_commitstagedfile__initialize(void)
+{
+ cl_fixture("treebuilder");
+ cl_git_pass(git_repository_init(&repo, "treebuilder/", 0));
+ cl_assert(repo != NULL);
+}
+
+void test_object_commit_commitstagedfile__cleanup(void)
+{
+ git_repository_free(repo);
+ repo = NULL;
+
+ cl_fixture_cleanup("treebuilder");
+}
+
+void test_object_commit_commitstagedfile__generate_predictable_object_ids(void)
+{
+ git_index *index;
+ const git_index_entry *entry;
+ git_oid expected_blob_oid, tree_oid, expected_tree_oid, commit_oid, expected_commit_oid;
+ git_signature *signature;
+ git_tree *tree;
+ char buffer[128];
+
+ /*
+ * The test below replicates the following git scenario
+ *
+ * $ echo "test" > test.txt
+ * $ git hash-object test.txt
+ * 9daeafb9864cf43055ae93beb0afd6c7d144bfa4
+ *
+ * $ git add .
+ * $ git commit -m "Initial commit"
+ *
+ * $ git log
+ * commit 1fe3126578fc4eca68c193e4a3a0a14a0704624d
+ * Author: nulltoken <emeric.fermas@gmail.com>
+ * Date: Wed Dec 14 08:29:03 2011 +0100
+ *
+ * Initial commit
+ *
+ * $ git show 1fe3 --format=raw
+ * commit 1fe3126578fc4eca68c193e4a3a0a14a0704624d
+ * tree 2b297e643c551e76cfa1f93810c50811382f9117
+ * author nulltoken <emeric.fermas@gmail.com> 1323847743 +0100
+ * committer nulltoken <emeric.fermas@gmail.com> 1323847743 +0100
+ *
+ * Initial commit
+ *
+ * diff --git a/test.txt b/test.txt
+ * new file mode 100644
+ * index 0000000..9daeafb
+ * --- /dev/null
+ * +++ b/test.txt
+ * @@ -0,0 +1 @@
+ * +test
+ *
+ * $ git ls-tree 2b297
+ * 100644 blob 9daeafb9864cf43055ae93beb0afd6c7d144bfa4 test.txt
+ */
+
+ cl_git_pass(git_oid_fromstr(&expected_commit_oid, "1fe3126578fc4eca68c193e4a3a0a14a0704624d"));
+ cl_git_pass(git_oid_fromstr(&expected_tree_oid, "2b297e643c551e76cfa1f93810c50811382f9117"));
+ cl_git_pass(git_oid_fromstr(&expected_blob_oid, "9daeafb9864cf43055ae93beb0afd6c7d144bfa4"));
+
+ /*
+ * Add a new file to the index
+ */
+ cl_git_mkfile("treebuilder/test.txt", "test\n");
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_add_bypath(index, "test.txt"));
+
+ entry = git_index_get_byindex(index, 0);
+
+ cl_assert(git_oid_cmp(&expected_blob_oid, &entry->oid) == 0);
+
+ /*
+ * Information about index entry should match test file
+ */
+ {
+ struct stat st;
+ cl_must_pass(p_lstat("treebuilder/test.txt", &st));
+ cl_assert(entry->file_size == st.st_size);
+#ifndef _WIN32
+ /*
+ * Windows doesn't populate these fields, and the signage is
+ * wrong in the Windows version of the struct, so lets avoid
+ * the "comparing signed and unsigned" compilation warning in
+ * that case.
+ */
+ cl_assert(entry->uid == st.st_uid);
+ cl_assert(entry->gid == st.st_gid);
+#endif
+ }
+
+ /*
+ * Build the tree from the index
+ */
+ cl_git_pass(git_index_write_tree(&tree_oid, index));
+
+ cl_assert(git_oid_cmp(&expected_tree_oid, &tree_oid) == 0);
+
+ /*
+ * Commit the staged file
+ */
+ cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60));
+ cl_git_pass(git_tree_lookup(&tree, repo, &tree_oid));
+
+ cl_assert_equal_i(16, git_message_prettify(buffer, 128, "Initial commit", 0));
+
+ cl_git_pass(git_commit_create_v(
+ &commit_oid,
+ repo,
+ "HEAD",
+ signature,
+ signature,
+ NULL,
+ buffer,
+ tree,
+ 0));
+
+ cl_assert(git_oid_cmp(&expected_commit_oid, &commit_oid) == 0);
+
+ git_signature_free(signature);
+ git_tree_free(tree);
+ git_index_free(index);
+}
diff --git a/tests/object/lookup.c b/tests/object/lookup.c
new file mode 100644
index 000000000..cfa6d4678
--- /dev/null
+++ b/tests/object/lookup.c
@@ -0,0 +1,65 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+
+static git_repository *g_repo;
+
+void test_object_lookup__initialize(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
+}
+
+void test_object_lookup__cleanup(void)
+{
+ git_repository_free(g_repo);
+ g_repo = NULL;
+}
+
+void test_object_lookup__lookup_wrong_type_returns_enotfound(void)
+{
+ const char *commit = "e90810b8df3e80c413d903f631643c716887138d";
+ git_oid oid;
+ git_object *object;
+
+ cl_git_pass(git_oid_fromstr(&oid, commit));
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_object_lookup(&object, g_repo, &oid, GIT_OBJ_TAG));
+}
+
+void test_object_lookup__lookup_nonexisting_returns_enotfound(void)
+{
+ const char *unknown = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
+ git_oid oid;
+ git_object *object;
+
+ cl_git_pass(git_oid_fromstr(&oid, unknown));
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_object_lookup(&object, g_repo, &oid, GIT_OBJ_ANY));
+}
+
+void test_object_lookup__lookup_wrong_type_by_abbreviated_id_returns_enotfound(void)
+{
+ const char *commit = "e90810b";
+ git_oid oid;
+ git_object *object;
+
+ cl_git_pass(git_oid_fromstrn(&oid, commit, strlen(commit)));
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_object_lookup_prefix(&object, g_repo, &oid, strlen(commit), GIT_OBJ_TAG));
+}
+
+void test_object_lookup__lookup_wrong_type_eventually_returns_enotfound(void)
+{
+ const char *commit = "e90810b8df3e80c413d903f631643c716887138d";
+ git_oid oid;
+ git_object *object;
+
+ cl_git_pass(git_oid_fromstr(&oid, commit));
+
+ cl_git_pass(git_object_lookup(&object, g_repo, &oid, GIT_OBJ_COMMIT));
+ git_object_free(object);
+
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_object_lookup(&object, g_repo, &oid, GIT_OBJ_TAG));
+}
+
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/object/message.c b/tests/object/message.c
new file mode 100644
index 000000000..7ef6374b3
--- /dev/null
+++ b/tests/object/message.c
@@ -0,0 +1,236 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "message.h"
+
+static void assert_message_prettifying(char *expected_output, char *input, int strip_comments)
+{
+ git_buf prettified_message = GIT_BUF_INIT;
+
+ git_message__prettify(&prettified_message, input, strip_comments);
+ cl_assert_equal_s(expected_output, git_buf_cstr(&prettified_message));
+
+ git_buf_free(&prettified_message);
+}
+
+#define t40 "A quick brown fox jumps over the lazy do"
+#define s40 " "
+#define sss s40 s40 s40 s40 s40 s40 s40 s40 s40 s40 // # 400
+#define ttt t40 t40 t40 t40 t40 t40 t40 t40 t40 t40 // # 400
+
+/* Ported from git.git */
+/* see https://github.com/git/git/blob/master/t/t0030-stripspace.sh */
+void test_object_message__long_lines_without_spaces_should_be_unchanged(void)
+{
+ assert_message_prettifying(ttt "\n", ttt, 0);
+ assert_message_prettifying(ttt ttt "\n", ttt ttt, 0);
+ assert_message_prettifying(ttt ttt ttt "\n", ttt ttt ttt, 0);
+ assert_message_prettifying(ttt ttt ttt ttt "\n", ttt ttt ttt ttt, 0);
+}
+
+void test_object_message__lines_with_spaces_at_the_beginning_should_be_unchanged(void)
+{
+ assert_message_prettifying(sss ttt "\n", sss ttt, 0);
+ assert_message_prettifying(sss sss ttt "\n", sss sss ttt, 0);
+ assert_message_prettifying(sss sss sss ttt "\n", sss sss sss ttt, 0);
+}
+
+void test_object_message__lines_with_intermediate_spaces_should_be_unchanged(void)
+{
+ assert_message_prettifying(ttt sss ttt "\n", ttt sss ttt, 0);
+ assert_message_prettifying(ttt sss sss ttt "\n", ttt sss sss ttt, 0);
+}
+
+void test_object_message__consecutive_blank_lines_should_be_unified(void)
+{
+ assert_message_prettifying(ttt "\n\n" ttt "\n", ttt "\n\n\n\n\n" ttt "\n", 0);
+ assert_message_prettifying(ttt ttt "\n\n" ttt "\n", ttt ttt "\n\n\n\n\n" ttt "\n", 0);
+ assert_message_prettifying(ttt ttt ttt "\n\n" ttt "\n", ttt ttt ttt "\n\n\n\n\n" ttt "\n", 0);
+
+ assert_message_prettifying(ttt "\n\n" ttt ttt "\n", ttt "\n\n\n\n\n" ttt ttt "\n", 0);
+ assert_message_prettifying(ttt "\n\n" ttt ttt ttt "\n", ttt "\n\n\n\n\n" ttt ttt ttt "\n", 0);
+
+ assert_message_prettifying(ttt "\n\n" ttt "\n", ttt "\n\t\n \n\n \t\t\n" ttt "\n", 0);
+ assert_message_prettifying(ttt ttt "\n\n" ttt "\n", ttt ttt "\n\t\n \n\n \t\t\n" ttt "\n", 0);
+ assert_message_prettifying(ttt ttt ttt "\n\n" ttt "\n", ttt ttt ttt "\n\t\n \n\n \t\t\n" ttt "\n", 0);
+
+ assert_message_prettifying(ttt "\n\n" ttt ttt "\n", ttt "\n\t\n \n\n \t\t\n" ttt ttt "\n", 0);
+ assert_message_prettifying(ttt "\n\n" ttt ttt ttt "\n", ttt "\n\t\n \n\n \t\t\n" ttt ttt ttt "\n", 0);
+}
+
+void test_object_message__only_consecutive_blank_lines_should_be_completely_removed(void)
+{
+ assert_message_prettifying("", "\n", 0);
+ assert_message_prettifying("", "\n\n\n", 0);
+ assert_message_prettifying("", sss "\n" sss "\n" sss "\n", 0);
+ assert_message_prettifying("", sss sss "\n" sss "\n\n", 0);
+}
+
+void test_object_message__consecutive_blank_lines_at_the_beginning_should_be_removed(void)
+{
+ assert_message_prettifying(ttt "\n", "\n" ttt "\n", 0);
+ assert_message_prettifying(ttt "\n", "\n\n\n" ttt "\n", 0);
+ assert_message_prettifying(ttt ttt "\n", "\n\n\n" ttt ttt "\n", 0);
+ assert_message_prettifying(ttt ttt ttt "\n", "\n\n\n" ttt ttt ttt "\n", 0);
+ assert_message_prettifying(ttt ttt ttt ttt "\n", "\n\n\n" ttt ttt ttt ttt "\n", 0);
+ assert_message_prettifying(ttt "\n", sss "\n" sss "\n" sss "\n" ttt "\n", 0);
+ assert_message_prettifying(ttt "\n", "\n" sss "\n" sss sss "\n" ttt "\n", 0);
+ assert_message_prettifying(ttt "\n", sss sss "\n" sss "\n\n" ttt "\n", 0);
+ assert_message_prettifying(ttt "\n", sss sss sss "\n\n\n" ttt "\n", 0);
+ assert_message_prettifying(ttt "\n", "\n" sss sss sss "\n\n" ttt "\n", 0);
+ assert_message_prettifying(ttt "\n", "\n\n" sss sss sss "\n" ttt "\n", 0);
+}
+
+void test_object_message__consecutive_blank_lines_at_the_end_should_be_removed(void)
+{
+ assert_message_prettifying(ttt "\n", ttt "\n\n", 0);
+ assert_message_prettifying(ttt "\n", ttt "\n\n\n\n", 0);
+ assert_message_prettifying(ttt ttt "\n", ttt ttt "\n\n\n\n", 0);
+ assert_message_prettifying(ttt ttt ttt "\n", ttt ttt ttt "\n\n\n\n", 0);
+ assert_message_prettifying(ttt ttt ttt ttt "\n", ttt ttt ttt ttt "\n\n\n\n", 0);
+ assert_message_prettifying(ttt "\n", ttt "\n" sss "\n" sss "\n" sss "\n", 0);
+ assert_message_prettifying(ttt "\n", ttt "\n\n" sss "\n" sss sss "\n", 0);
+ assert_message_prettifying(ttt "\n", ttt "\n" sss sss "\n" sss "\n\n", 0);
+ assert_message_prettifying(ttt "\n", ttt "\n" sss sss sss "\n\n\n", 0);
+ assert_message_prettifying(ttt "\n", ttt "\n\n" sss sss sss "\n\n", 0);
+ assert_message_prettifying(ttt "\n", ttt "\n\n\n" sss sss sss "\n\n", 0);
+}
+
+void test_object_message__text_without_newline_at_end_should_end_with_newline(void)
+{
+ assert_message_prettifying(ttt "\n", ttt, 0);
+ assert_message_prettifying(ttt ttt "\n", ttt ttt, 0);
+ assert_message_prettifying(ttt ttt ttt "\n", ttt ttt ttt, 0);
+ assert_message_prettifying(ttt ttt ttt ttt "\n", ttt ttt ttt ttt, 0);
+}
+
+void test_object_message__text_plus_spaces_without_newline_should_not_show_spaces_and_end_with_newline(void)
+{
+ assert_message_prettifying(ttt "\n", ttt sss, 0);
+ assert_message_prettifying(ttt ttt "\n", ttt ttt sss, 0);
+ assert_message_prettifying(ttt ttt ttt "\n", ttt ttt ttt sss, 0);
+ assert_message_prettifying(ttt "\n", ttt sss sss, 0);
+ assert_message_prettifying(ttt ttt "\n", ttt ttt sss sss, 0);
+ assert_message_prettifying(ttt "\n", ttt sss sss sss, 0);
+}
+
+void test_object_message__text_plus_spaces_ending_with_newline_should_be_cleaned_and_newline_must_remain(void){
+ assert_message_prettifying(ttt "\n", ttt sss "\n", 0);
+ assert_message_prettifying(ttt "\n", ttt sss sss "\n", 0);
+ assert_message_prettifying(ttt "\n", ttt sss sss sss "\n", 0);
+ assert_message_prettifying(ttt ttt "\n", ttt ttt sss "\n", 0);
+ assert_message_prettifying(ttt ttt "\n", ttt ttt sss sss "\n", 0);
+ assert_message_prettifying(ttt ttt ttt "\n", ttt ttt ttt sss "\n", 0);
+}
+
+void test_object_message__spaces_with_newline_at_end_should_be_replaced_with_empty_string(void)
+{
+ assert_message_prettifying("", sss "\n", 0);
+ assert_message_prettifying("", sss sss "\n", 0);
+ assert_message_prettifying("", sss sss sss "\n", 0);
+ assert_message_prettifying("", sss sss sss sss "\n", 0);
+}
+
+void test_object_message__spaces_without_newline_at_end_should_be_replaced_with_empty_string(void)
+{
+ assert_message_prettifying("", "", 0);
+ assert_message_prettifying("", sss sss, 0);
+ assert_message_prettifying("", sss sss sss, 0);
+ assert_message_prettifying("", sss sss sss sss, 0);
+}
+
+void test_object_message__consecutive_text_lines_should_be_unchanged(void)
+{
+ assert_message_prettifying(ttt ttt "\n" ttt "\n", ttt ttt "\n" ttt "\n", 0);
+ assert_message_prettifying(ttt "\n" ttt ttt "\n" ttt "\n", ttt "\n" ttt ttt "\n" ttt "\n", 0);
+ assert_message_prettifying(ttt "\n" ttt "\n" ttt "\n" ttt ttt "\n", ttt "\n" ttt "\n" ttt "\n" ttt ttt "\n", 0);
+ assert_message_prettifying(ttt "\n" ttt "\n\n" ttt ttt "\n" ttt "\n", ttt "\n" ttt "\n\n" ttt ttt "\n" ttt "\n", 0);
+ assert_message_prettifying(ttt ttt "\n\n" ttt "\n" ttt ttt "\n", ttt ttt "\n\n" ttt "\n" ttt ttt "\n", 0);
+ assert_message_prettifying(ttt "\n" ttt ttt "\n\n" ttt "\n", ttt "\n" ttt ttt "\n\n" ttt "\n", 0);
+}
+
+void test_object_message__strip_comments(void)
+{
+ assert_message_prettifying("", "# comment", 1);
+ assert_message_prettifying("", "# comment\n", 1);
+ assert_message_prettifying("", "# comment \n", 1);
+
+ assert_message_prettifying(ttt "\n", ttt "\n" "# comment\n", 1);
+ assert_message_prettifying(ttt "\n", "# comment\n" ttt "\n", 1);
+ assert_message_prettifying(ttt "\n" ttt "\n", ttt "\n" "# comment\n" ttt "\n", 1);
+}
+
+void test_object_message__keep_comments(void)
+{
+ assert_message_prettifying("# comment\n", "# comment", 0);
+ assert_message_prettifying("# comment\n", "# comment\n", 0);
+ assert_message_prettifying("# comment\n", "# comment \n", 0);
+
+ assert_message_prettifying(ttt "\n" "# comment\n", ttt "\n" "# comment\n", 0);
+ assert_message_prettifying("# comment\n" ttt "\n", "# comment\n" ttt "\n", 0);
+ assert_message_prettifying(ttt "\n" "# comment\n" ttt "\n", ttt "\n" "# comment\n" ttt "\n", 0);
+}
+
+void test_object_message__message_prettify(void)
+{
+ char buffer[100];
+
+ cl_assert(git_message_prettify(buffer, sizeof(buffer), "", 0) == 1);
+ cl_assert_equal_s(buffer, "");
+ cl_assert(git_message_prettify(buffer, sizeof(buffer), "", 1) == 1);
+ cl_assert_equal_s(buffer, "");
+
+ cl_assert_equal_i(7, git_message_prettify(buffer, sizeof(buffer), "Short", 0));
+ cl_assert_equal_s("Short\n", buffer);
+ cl_assert_equal_i(7, git_message_prettify(buffer, sizeof(buffer), "Short", 1));
+ cl_assert_equal_s("Short\n", buffer);
+
+ cl_assert(git_message_prettify(buffer, sizeof(buffer), "This is longer\nAnd multiline\n# with some comments still in\n", 0) > 0);
+ cl_assert_equal_s(buffer, "This is longer\nAnd multiline\n# with some comments still in\n");
+
+ cl_assert(git_message_prettify(buffer, sizeof(buffer), "This is longer\nAnd multiline\n# with some comments still in\n", 1) > 0);
+ cl_assert_equal_s(buffer, "This is longer\nAnd multiline\n");
+
+ /* try out overflow */
+ cl_assert(git_message_prettify(buffer, sizeof(buffer),
+ "1234567890" "1234567890" "1234567890" "1234567890" "1234567890"
+ "1234567890" "1234567890" "1234567890" "1234567890" "12345678",
+ 0) > 0);
+ cl_assert_equal_s(buffer,
+ "1234567890" "1234567890" "1234567890" "1234567890" "1234567890"
+ "1234567890" "1234567890" "1234567890" "1234567890" "12345678\n");
+
+ cl_assert(git_message_prettify(buffer, sizeof(buffer),
+ "1234567890" "1234567890" "1234567890" "1234567890" "1234567890"
+ "1234567890" "1234567890" "1234567890" "1234567890" "12345678\n",
+ 0) > 0);
+ cl_assert_equal_s(buffer,
+ "1234567890" "1234567890" "1234567890" "1234567890" "1234567890"
+ "1234567890" "1234567890" "1234567890" "1234567890" "12345678\n");
+
+ cl_git_fail(git_message_prettify(buffer, sizeof(buffer),
+ "1234567890" "1234567890" "1234567890" "1234567890" "1234567890"
+ "1234567890" "1234567890" "1234567890" "1234567890" "123456789",
+ 0));
+ cl_git_fail(git_message_prettify(buffer, sizeof(buffer),
+ "1234567890" "1234567890" "1234567890" "1234567890" "1234567890"
+ "1234567890" "1234567890" "1234567890" "1234567890" "123456789\n",
+ 0));
+ cl_git_fail(git_message_prettify(buffer, sizeof(buffer),
+ "1234567890" "1234567890" "1234567890" "1234567890" "1234567890"
+ "1234567890" "1234567890" "1234567890" "1234567890" "1234567890",
+ 0));
+ cl_git_fail(git_message_prettify(buffer, sizeof(buffer),
+ "1234567890" "1234567890" "1234567890" "1234567890" "1234567890"
+ "1234567890" "1234567890" "1234567890" "1234567890" "1234567890""x",
+ 0));
+
+ cl_assert(git_message_prettify(buffer, sizeof(buffer),
+ "1234567890" "1234567890" "1234567890" "1234567890" "1234567890\n"
+ "# 1234567890" "1234567890" "1234567890" "1234567890" "1234567890\n"
+ "1234567890",
+ 1) > 0);
+
+ cl_assert(git_message_prettify(NULL, 0, "", 0) == 1);
+ cl_assert(git_message_prettify(NULL, 0, "Short test", 0) == 12);
+ cl_assert(git_message_prettify(NULL, 0, "Test\n# with\nComments", 1) == 15);
+}
diff --git a/tests/object/peel.c b/tests/object/peel.c
new file mode 100644
index 000000000..b6c9c7a3b
--- /dev/null
+++ b/tests/object/peel.c
@@ -0,0 +1,105 @@
+#include "clar_libgit2.h"
+
+static git_repository *g_repo;
+
+void test_object_peel__initialize(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
+}
+
+void test_object_peel__cleanup(void)
+{
+ git_repository_free(g_repo);
+ g_repo = NULL;
+}
+
+static void assert_peel(
+ const char *sha,
+ git_otype requested_type,
+ const char* expected_sha,
+ git_otype expected_type)
+{
+ git_oid oid, expected_oid;
+ git_object *obj;
+ git_object *peeled;
+
+ cl_git_pass(git_oid_fromstr(&oid, sha));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+
+ cl_git_pass(git_object_peel(&peeled, obj, requested_type));
+
+ cl_git_pass(git_oid_fromstr(&expected_oid, expected_sha));
+ cl_assert_equal_i(0, git_oid_cmp(&expected_oid, git_object_id(peeled)));
+
+ cl_assert_equal_i(expected_type, git_object_type(peeled));
+
+ git_object_free(peeled);
+ git_object_free(obj);
+}
+
+static void assert_peel_error(int error, const char *sha, git_otype requested_type)
+{
+ git_oid oid;
+ git_object *obj;
+ git_object *peeled;
+
+ cl_git_pass(git_oid_fromstr(&oid, sha));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+
+ cl_assert_equal_i(error, git_object_peel(&peeled, obj, requested_type));
+
+ git_object_free(obj);
+}
+
+void test_object_peel__peeling_an_object_into_its_own_type_returns_another_instance_of_it(void)
+{
+ assert_peel("e90810b8df3e80c413d903f631643c716887138d", GIT_OBJ_COMMIT,
+ "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJ_COMMIT);
+ assert_peel("7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJ_TAG,
+ "7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJ_TAG);
+ assert_peel("53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_TREE,
+ "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_TREE);
+ assert_peel("0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJ_BLOB,
+ "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJ_BLOB);
+}
+
+void test_object_peel__can_peel_a_tag(void)
+{
+ assert_peel("7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJ_COMMIT,
+ "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJ_COMMIT);
+ assert_peel("7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJ_TREE,
+ "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_TREE);
+}
+
+void test_object_peel__can_peel_a_commit(void)
+{
+ assert_peel("e90810b8df3e80c413d903f631643c716887138d", GIT_OBJ_TREE,
+ "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_TREE);
+}
+
+void test_object_peel__cannot_peel_a_tree(void)
+{
+ assert_peel_error(GIT_EAMBIGUOUS, "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_BLOB);
+}
+
+void test_object_peel__cannot_peel_a_blob(void)
+{
+ assert_peel_error(GIT_ENOTFOUND, "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJ_COMMIT);
+}
+
+void test_object_peel__target_any_object_for_type_change(void)
+{
+ /* tag to commit */
+ assert_peel("7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJ_ANY,
+ "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJ_COMMIT);
+
+ /* commit to tree */
+ assert_peel("e90810b8df3e80c413d903f631643c716887138d", GIT_OBJ_ANY,
+ "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_TREE);
+
+ /* fail to peel tree */
+ assert_peel_error(GIT_EAMBIGUOUS, "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_ANY);
+
+ /* fail to peel blob */
+ assert_peel_error(GIT_ENOTFOUND, "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJ_ANY);
+}
diff --git a/tests/object/raw/chars.c b/tests/object/raw/chars.c
new file mode 100644
index 000000000..206bf7119
--- /dev/null
+++ b/tests/object/raw/chars.c
@@ -0,0 +1,41 @@
+
+#include "clar_libgit2.h"
+
+#include "odb.h"
+
+void test_object_raw_chars__find_invalid_chars_in_oid(void)
+{
+ git_oid out;
+ unsigned char exp[] = {
+ 0x16, 0xa6, 0x77, 0x70, 0xb7,
+ 0xd8, 0xd7, 0x23, 0x17, 0xc4,
+ 0xb7, 0x75, 0x21, 0x3c, 0x23,
+ 0xa8, 0xbd, 0x74, 0xf5, 0xe0,
+ };
+ char in[41] = "16a67770b7d8d72317c4b775213c23a8bd74f5e0";
+ unsigned int i;
+
+ for (i = 0; i < 256; i++) {
+ in[38] = (char)i;
+ if (git__fromhex(i) >= 0) {
+ exp[19] = (unsigned char)(git__fromhex(i) << 4);
+ cl_git_pass(git_oid_fromstr(&out, in));
+ cl_assert(memcmp(out.id, exp, sizeof(out.id)) == 0);
+ } else {
+ cl_git_fail(git_oid_fromstr(&out, in));
+ }
+ }
+}
+
+void test_object_raw_chars__build_valid_oid_from_raw_bytes(void)
+{
+ git_oid out;
+ unsigned char exp[] = {
+ 0x16, 0xa6, 0x77, 0x70, 0xb7,
+ 0xd8, 0xd7, 0x23, 0x17, 0xc4,
+ 0xb7, 0x75, 0x21, 0x3c, 0x23,
+ 0xa8, 0xbd, 0x74, 0xf5, 0xe0,
+ };
+ git_oid_fromraw(&out, exp);
+ cl_git_pass(memcmp(out.id, exp, sizeof(out.id)));
+}
diff --git a/tests/object/raw/compare.c b/tests/object/raw/compare.c
new file mode 100644
index 000000000..1c9ce4b81
--- /dev/null
+++ b/tests/object/raw/compare.c
@@ -0,0 +1,124 @@
+
+#include "clar_libgit2.h"
+
+#include "odb.h"
+
+void test_object_raw_compare__succeed_on_copy_oid(void)
+{
+ git_oid a, b;
+ unsigned char exp[] = {
+ 0x16, 0xa6, 0x77, 0x70, 0xb7,
+ 0xd8, 0xd7, 0x23, 0x17, 0xc4,
+ 0xb7, 0x75, 0x21, 0x3c, 0x23,
+ 0xa8, 0xbd, 0x74, 0xf5, 0xe0,
+ };
+ memset(&b, 0, sizeof(b));
+ git_oid_fromraw(&a, exp);
+ git_oid_cpy(&b, &a);
+ cl_git_pass(memcmp(a.id, exp, sizeof(a.id)));
+}
+
+void test_object_raw_compare__succeed_on_oid_comparison_lesser(void)
+{
+ git_oid a, b;
+ unsigned char a_in[] = {
+ 0x16, 0xa6, 0x77, 0x70, 0xb7,
+ 0xd8, 0xd7, 0x23, 0x17, 0xc4,
+ 0xb7, 0x75, 0x21, 0x3c, 0x23,
+ 0xa8, 0xbd, 0x74, 0xf5, 0xe0,
+ };
+ unsigned char b_in[] = {
+ 0x16, 0xa6, 0x77, 0x70, 0xb7,
+ 0xd8, 0xd7, 0x23, 0x17, 0xc4,
+ 0xb7, 0x75, 0x21, 0x3c, 0x23,
+ 0xa8, 0xbd, 0x74, 0xf5, 0xf0,
+ };
+ git_oid_fromraw(&a, a_in);
+ git_oid_fromraw(&b, b_in);
+ cl_assert(git_oid_cmp(&a, &b) < 0);
+}
+
+void test_object_raw_compare__succeed_on_oid_comparison_equal(void)
+{
+ git_oid a, b;
+ unsigned char a_in[] = {
+ 0x16, 0xa6, 0x77, 0x70, 0xb7,
+ 0xd8, 0xd7, 0x23, 0x17, 0xc4,
+ 0xb7, 0x75, 0x21, 0x3c, 0x23,
+ 0xa8, 0xbd, 0x74, 0xf5, 0xe0,
+ };
+ git_oid_fromraw(&a, a_in);
+ git_oid_fromraw(&b, a_in);
+ cl_assert(git_oid_cmp(&a, &b) == 0);
+}
+
+void test_object_raw_compare__succeed_on_oid_comparison_greater(void)
+{
+ git_oid a, b;
+ unsigned char a_in[] = {
+ 0x16, 0xa6, 0x77, 0x70, 0xb7,
+ 0xd8, 0xd7, 0x23, 0x17, 0xc4,
+ 0xb7, 0x75, 0x21, 0x3c, 0x23,
+ 0xa8, 0xbd, 0x74, 0xf5, 0xe0,
+ };
+ unsigned char b_in[] = {
+ 0x16, 0xa6, 0x77, 0x70, 0xb7,
+ 0xd8, 0xd7, 0x23, 0x17, 0xc4,
+ 0xb7, 0x75, 0x21, 0x3c, 0x23,
+ 0xa8, 0xbd, 0x74, 0xf5, 0xd0,
+ };
+ git_oid_fromraw(&a, a_in);
+ git_oid_fromraw(&b, b_in);
+ cl_assert(git_oid_cmp(&a, &b) > 0);
+}
+
+void test_object_raw_compare__compare_fmt_oids(void)
+{
+ const char *exp = "16a0123456789abcdef4b775213c23a8bd74f5e0";
+ git_oid in;
+ char out[GIT_OID_HEXSZ + 1];
+
+ cl_git_pass(git_oid_fromstr(&in, exp));
+
+ /* Format doesn't touch the last byte */
+ out[GIT_OID_HEXSZ] = 'Z';
+ git_oid_fmt(out, &in);
+ cl_assert(out[GIT_OID_HEXSZ] == 'Z');
+
+ /* Format produced the right result */
+ out[GIT_OID_HEXSZ] = '\0';
+ cl_assert_equal_s(exp, out);
+}
+
+void test_object_raw_compare__compare_allocfmt_oids(void)
+{
+ const char *exp = "16a0123456789abcdef4b775213c23a8bd74f5e0";
+ git_oid in;
+ char *out;
+
+ cl_git_pass(git_oid_fromstr(&in, exp));
+
+ out = git_oid_allocfmt(&in);
+ cl_assert(out);
+ cl_assert_equal_s(exp, out);
+ git__free(out);
+}
+
+void test_object_raw_compare__compare_pathfmt_oids(void)
+{
+ const char *exp1 = "16a0123456789abcdef4b775213c23a8bd74f5e0";
+ const char *exp2 = "16/a0123456789abcdef4b775213c23a8bd74f5e0";
+ git_oid in;
+ char out[GIT_OID_HEXSZ + 2];
+
+ cl_git_pass(git_oid_fromstr(&in, exp1));
+
+ /* Format doesn't touch the last byte */
+ out[GIT_OID_HEXSZ + 1] = 'Z';
+ git_oid_pathfmt(out, &in);
+ cl_assert(out[GIT_OID_HEXSZ + 1] == 'Z');
+
+ /* Format produced the right result */
+ out[GIT_OID_HEXSZ + 1] = '\0';
+ cl_assert_equal_s(exp2, out);
+}
diff --git a/tests/object/raw/convert.c b/tests/object/raw/convert.c
new file mode 100644
index 000000000..88b1380a4
--- /dev/null
+++ b/tests/object/raw/convert.c
@@ -0,0 +1,112 @@
+
+#include "clar_libgit2.h"
+
+#include "odb.h"
+
+void test_object_raw_convert__succeed_on_oid_to_string_conversion(void)
+{
+ const char *exp = "16a0123456789abcdef4b775213c23a8bd74f5e0";
+ git_oid in;
+ char out[GIT_OID_HEXSZ + 1];
+ char *str;
+ int i;
+
+ cl_git_pass(git_oid_fromstr(&in, exp));
+
+ /* NULL buffer pointer, returns static empty string */
+ str = git_oid_tostr(NULL, sizeof(out), &in);
+ cl_assert(str && *str == '\0' && str != out);
+
+ /* zero buffer size, returns static empty string */
+ str = git_oid_tostr(out, 0, &in);
+ cl_assert(str && *str == '\0' && str != out);
+
+ /* NULL oid pointer, sets existing buffer to empty string */
+ str = git_oid_tostr(out, sizeof(out), NULL);
+ cl_assert(str && *str == '\0' && str == out);
+
+ /* n == 1, returns out as an empty string */
+ str = git_oid_tostr(out, 1, &in);
+ cl_assert(str && *str == '\0' && str == out);
+
+ for (i = 1; i < GIT_OID_HEXSZ; i++) {
+ out[i+1] = 'Z';
+ str = git_oid_tostr(out, i+1, &in);
+ /* returns out containing c-string */
+ cl_assert(str && str == out);
+ /* must be '\0' terminated */
+ cl_assert(*(str+i) == '\0');
+ /* must not touch bytes past end of string */
+ cl_assert(*(str+(i+1)) == 'Z');
+ /* i == n-1 charaters of string */
+ cl_git_pass(strncmp(exp, out, i));
+ }
+
+ /* returns out as hex formatted c-string */
+ str = git_oid_tostr(out, sizeof(out), &in);
+ cl_assert(str && str == out && *(str+GIT_OID_HEXSZ) == '\0');
+ cl_assert_equal_s(exp, out);
+}
+
+void test_object_raw_convert__succeed_on_oid_to_string_conversion_big(void)
+{
+ const char *exp = "16a0123456789abcdef4b775213c23a8bd74f5e0";
+ git_oid in;
+ char big[GIT_OID_HEXSZ + 1 + 3]; /* note + 4 => big buffer */
+ char *str;
+
+ cl_git_pass(git_oid_fromstr(&in, exp));
+
+ /* place some tail material */
+ big[GIT_OID_HEXSZ+0] = 'W'; /* should be '\0' afterwards */
+ big[GIT_OID_HEXSZ+1] = 'X'; /* should remain untouched */
+ big[GIT_OID_HEXSZ+2] = 'Y'; /* ditto */
+ big[GIT_OID_HEXSZ+3] = 'Z'; /* ditto */
+
+ /* returns big as hex formatted c-string */
+ str = git_oid_tostr(big, sizeof(big), &in);
+ cl_assert(str && str == big && *(str+GIT_OID_HEXSZ) == '\0');
+ cl_assert_equal_s(exp, big);
+
+ /* check tail material is untouched */
+ cl_assert(str && str == big && *(str+GIT_OID_HEXSZ+1) == 'X');
+ cl_assert(str && str == big && *(str+GIT_OID_HEXSZ+2) == 'Y');
+ cl_assert(str && str == big && *(str+GIT_OID_HEXSZ+3) == 'Z');
+}
+
+static void check_partial_oid(
+ char *buffer, size_t count, const git_oid *oid, const char *expected)
+{
+ git_oid_nfmt(buffer, count, oid);
+ buffer[count] = '\0';
+ cl_assert_equal_s(expected, buffer);
+}
+
+void test_object_raw_convert__convert_oid_partially(void)
+{
+ const char *exp = "16a0123456789abcdef4b775213c23a8bd74f5e0";
+ git_oid in;
+ char big[GIT_OID_HEXSZ + 1 + 3]; /* note + 4 => big buffer */
+
+ cl_git_pass(git_oid_fromstr(&in, exp));
+
+ git_oid_nfmt(big, sizeof(big), &in);
+ cl_assert_equal_s(exp, big);
+
+ git_oid_nfmt(big, GIT_OID_HEXSZ + 1, &in);
+ cl_assert_equal_s(exp, big);
+
+ check_partial_oid(big, 1, &in, "1");
+ check_partial_oid(big, 2, &in, "16");
+ check_partial_oid(big, 3, &in, "16a");
+ check_partial_oid(big, 4, &in, "16a0");
+ check_partial_oid(big, 5, &in, "16a01");
+
+ check_partial_oid(big, GIT_OID_HEXSZ, &in, exp);
+ check_partial_oid(
+ big, GIT_OID_HEXSZ - 1, &in, "16a0123456789abcdef4b775213c23a8bd74f5e");
+ check_partial_oid(
+ big, GIT_OID_HEXSZ - 2, &in, "16a0123456789abcdef4b775213c23a8bd74f5");
+ check_partial_oid(
+ big, GIT_OID_HEXSZ - 3, &in, "16a0123456789abcdef4b775213c23a8bd74f");
+}
diff --git a/tests/object/raw/data.h b/tests/object/raw/data.h
new file mode 100644
index 000000000..cf23819f1
--- /dev/null
+++ b/tests/object/raw/data.h
@@ -0,0 +1,323 @@
+
+/*
+ * Raw data
+ */
+static unsigned char commit_data[] = {
+ 0x74, 0x72, 0x65, 0x65, 0x20, 0x64, 0x66, 0x66,
+ 0x32, 0x64, 0x61, 0x39, 0x30, 0x62, 0x32, 0x35,
+ 0x34, 0x65, 0x31, 0x62, 0x65, 0x62, 0x38, 0x38,
+ 0x39, 0x64, 0x31, 0x66, 0x31, 0x66, 0x31, 0x32,
+ 0x38, 0x38, 0x62, 0x65, 0x31, 0x38, 0x30, 0x33,
+ 0x37, 0x38, 0x32, 0x64, 0x66, 0x0a, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x20, 0x41, 0x20, 0x55,
+ 0x20, 0x54, 0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x40, 0x65, 0x78,
+ 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x3e, 0x20, 0x31, 0x32, 0x32, 0x37, 0x38,
+ 0x31, 0x34, 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30,
+ 0x30, 0x30, 0x30, 0x0a, 0x63, 0x6f, 0x6d, 0x6d,
+ 0x69, 0x74, 0x74, 0x65, 0x72, 0x20, 0x43, 0x20,
+ 0x4f, 0x20, 0x4d, 0x69, 0x74, 0x74, 0x65, 0x72,
+ 0x20, 0x3c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+ 0x74, 0x65, 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d,
+ 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e,
+ 0x20, 0x31, 0x32, 0x32, 0x37, 0x38, 0x31, 0x34,
+ 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30, 0x30, 0x30,
+ 0x30, 0x0a, 0x0a, 0x41, 0x20, 0x6f, 0x6e, 0x65,
+ 0x2d, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x63, 0x6f,
+ 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x73, 0x75, 0x6d,
+ 0x6d, 0x61, 0x72, 0x79, 0x0a, 0x0a, 0x54, 0x68,
+ 0x65, 0x20, 0x62, 0x6f, 0x64, 0x79, 0x20, 0x6f,
+ 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f,
+ 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x6d, 0x65, 0x73,
+ 0x73, 0x61, 0x67, 0x65, 0x2c, 0x20, 0x63, 0x6f,
+ 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67,
+ 0x20, 0x66, 0x75, 0x72, 0x74, 0x68, 0x65, 0x72,
+ 0x20, 0x65, 0x78, 0x70, 0x6c, 0x61, 0x6e, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x6f, 0x66, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x70, 0x75, 0x72, 0x70,
+ 0x6f, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67,
+ 0x65, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x72, 0x6f,
+ 0x64, 0x75, 0x63, 0x65, 0x64, 0x20, 0x62, 0x79,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d,
+ 0x6d, 0x69, 0x74, 0x2e, 0x0a, 0x0a, 0x53, 0x69,
+ 0x67, 0x6e, 0x65, 0x64, 0x2d, 0x6f, 0x66, 0x2d,
+ 0x62, 0x79, 0x3a, 0x20, 0x41, 0x20, 0x55, 0x20,
+ 0x54, 0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x40, 0x65, 0x78, 0x61,
+ 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x3e, 0x0a,
+};
+
+
+static unsigned char tree_data[] = {
+ 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x6f,
+ 0x6e, 0x65, 0x00, 0x8b, 0x13, 0x78, 0x91, 0x79,
+ 0x1f, 0xe9, 0x69, 0x27, 0xad, 0x78, 0xe6, 0x4b,
+ 0x0a, 0xad, 0x7b, 0xde, 0xd0, 0x8b, 0xdc, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x73, 0x6f,
+ 0x6d, 0x65, 0x00, 0xfd, 0x84, 0x30, 0xbc, 0x86,
+ 0x4c, 0xfc, 0xd5, 0xf1, 0x0e, 0x55, 0x90, 0xf8,
+ 0xa4, 0x47, 0xe0, 0x1b, 0x94, 0x2b, 0xfe, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x77,
+ 0x6f, 0x00, 0x78, 0x98, 0x19, 0x22, 0x61, 0x3b,
+ 0x2a, 0xfb, 0x60, 0x25, 0x04, 0x2f, 0xf6, 0xbd,
+ 0x87, 0x8a, 0xc1, 0x99, 0x4e, 0x85, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x7a, 0x65, 0x72,
+ 0x6f, 0x00, 0xe6, 0x9d, 0xe2, 0x9b, 0xb2, 0xd1,
+ 0xd6, 0x43, 0x4b, 0x8b, 0x29, 0xae, 0x77, 0x5a,
+ 0xd8, 0xc2, 0xe4, 0x8c, 0x53, 0x91,
+};
+
+static unsigned char tag_data[] = {
+ 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x33,
+ 0x64, 0x37, 0x66, 0x38, 0x61, 0x36, 0x61, 0x66,
+ 0x30, 0x37, 0x36, 0x63, 0x38, 0x63, 0x33, 0x66,
+ 0x32, 0x30, 0x30, 0x37, 0x31, 0x61, 0x38, 0x39,
+ 0x33, 0x35, 0x63, 0x64, 0x62, 0x65, 0x38, 0x32,
+ 0x32, 0x38, 0x35, 0x39, 0x34, 0x64, 0x31, 0x0a,
+ 0x74, 0x79, 0x70, 0x65, 0x20, 0x63, 0x6f, 0x6d,
+ 0x6d, 0x69, 0x74, 0x0a, 0x74, 0x61, 0x67, 0x20,
+ 0x76, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x0a, 0x74,
+ 0x61, 0x67, 0x67, 0x65, 0x72, 0x20, 0x43, 0x20,
+ 0x4f, 0x20, 0x4d, 0x69, 0x74, 0x74, 0x65, 0x72,
+ 0x20, 0x3c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+ 0x74, 0x65, 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d,
+ 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e,
+ 0x20, 0x31, 0x32, 0x32, 0x37, 0x38, 0x31, 0x34,
+ 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30, 0x30, 0x30,
+ 0x30, 0x0a, 0x0a, 0x54, 0x68, 0x69, 0x73, 0x20,
+ 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74,
+ 0x61, 0x67, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63,
+ 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x72, 0x65,
+ 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20, 0x76, 0x30,
+ 0x2e, 0x30, 0x2e, 0x31, 0x0a,
+};
+
+/*
+ * Dummy data
+ */
+static unsigned char zero_data[] = {
+ 0x00,
+};
+
+static unsigned char one_data[] = {
+ 0x0a,
+};
+
+static unsigned char two_data[] = {
+ 0x61, 0x0a,
+};
+
+static unsigned char some_data[] = {
+ 0x2f, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x54, 0x68,
+ 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20,
+ 0x69, 0x73, 0x20, 0x66, 0x72, 0x65, 0x65, 0x20,
+ 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
+ 0x3b, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x63, 0x61,
+ 0x6e, 0x20, 0x72, 0x65, 0x64, 0x69, 0x73, 0x74,
+ 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x69,
+ 0x74, 0x20, 0x61, 0x6e, 0x64, 0x2f, 0x6f, 0x72,
+ 0x20, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0a,
+ 0x20, 0x2a, 0x20, 0x69, 0x74, 0x20, 0x75, 0x6e,
+ 0x64, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x74, 0x65, 0x72, 0x6d, 0x73, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x47, 0x4e, 0x55,
+ 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
+ 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x2c,
+ 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+ 0x20, 0x32, 0x2c, 0x0a, 0x20, 0x2a, 0x20, 0x61,
+ 0x73, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73,
+ 0x68, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x46, 0x72, 0x65, 0x65, 0x20,
+ 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
+ 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x2a, 0x0a,
+ 0x20, 0x2a, 0x20, 0x49, 0x6e, 0x20, 0x61, 0x64,
+ 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74,
+ 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x65,
+ 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e,
+ 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x47, 0x4e, 0x55, 0x20, 0x47, 0x65, 0x6e,
+ 0x65, 0x72, 0x61, 0x6c, 0x20, 0x50, 0x75, 0x62,
+ 0x6c, 0x69, 0x63, 0x20, 0x4c, 0x69, 0x63, 0x65,
+ 0x6e, 0x73, 0x65, 0x2c, 0x0a, 0x20, 0x2a, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x61, 0x75, 0x74, 0x68,
+ 0x6f, 0x72, 0x73, 0x20, 0x67, 0x69, 0x76, 0x65,
+ 0x20, 0x79, 0x6f, 0x75, 0x20, 0x75, 0x6e, 0x6c,
+ 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x20, 0x70,
+ 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f,
+ 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x6c, 0x69, 0x6e,
+ 0x6b, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f,
+ 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x64, 0x0a, 0x20,
+ 0x2a, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
+ 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69,
+ 0x73, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x69,
+ 0x6e, 0x74, 0x6f, 0x20, 0x63, 0x6f, 0x6d, 0x62,
+ 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6f, 0x74,
+ 0x68, 0x65, 0x72, 0x20, 0x70, 0x72, 0x6f, 0x67,
+ 0x72, 0x61, 0x6d, 0x73, 0x2c, 0x0a, 0x20, 0x2a,
+ 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x6f, 0x20,
+ 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75,
+ 0x74, 0x65, 0x20, 0x74, 0x68, 0x6f, 0x73, 0x65,
+ 0x20, 0x63, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x77, 0x69,
+ 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x61, 0x6e,
+ 0x79, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x2a,
+ 0x20, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x20,
+ 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c,
+ 0x65, 0x2e, 0x20, 0x20, 0x28, 0x54, 0x68, 0x65,
+ 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
+ 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x0a,
+ 0x20, 0x2a, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72,
+ 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20,
+ 0x64, 0x6f, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x79,
+ 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x74, 0x68, 0x65,
+ 0x72, 0x20, 0x72, 0x65, 0x73, 0x70, 0x65, 0x63,
+ 0x74, 0x73, 0x3b, 0x20, 0x66, 0x6f, 0x72, 0x20,
+ 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c,
+ 0x20, 0x74, 0x68, 0x65, 0x79, 0x20, 0x63, 0x6f,
+ 0x76, 0x65, 0x72, 0x0a, 0x20, 0x2a, 0x20, 0x6d,
+ 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x2c,
+ 0x20, 0x61, 0x6e, 0x64, 0x20, 0x64, 0x69, 0x73,
+ 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x6e,
+ 0x6f, 0x74, 0x20, 0x6c, 0x69, 0x6e, 0x6b, 0x65,
+ 0x64, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x0a, 0x20,
+ 0x2a, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x62,
+ 0x69, 0x6e, 0x65, 0x64, 0x20, 0x65, 0x78, 0x65,
+ 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e,
+ 0x29, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20,
+ 0x54, 0x68, 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c,
+ 0x65, 0x20, 0x69, 0x73, 0x20, 0x64, 0x69, 0x73,
+ 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x64,
+ 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x68, 0x6f, 0x70, 0x65, 0x20, 0x74, 0x68, 0x61,
+ 0x74, 0x20, 0x69, 0x74, 0x20, 0x77, 0x69, 0x6c,
+ 0x6c, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65,
+ 0x66, 0x75, 0x6c, 0x2c, 0x20, 0x62, 0x75, 0x74,
+ 0x0a, 0x20, 0x2a, 0x20, 0x57, 0x49, 0x54, 0x48,
+ 0x4f, 0x55, 0x54, 0x20, 0x41, 0x4e, 0x59, 0x20,
+ 0x57, 0x41, 0x52, 0x52, 0x41, 0x4e, 0x54, 0x59,
+ 0x3b, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75,
+ 0x74, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x69,
+ 0x65, 0x64, 0x20, 0x77, 0x61, 0x72, 0x72, 0x61,
+ 0x6e, 0x74, 0x79, 0x20, 0x6f, 0x66, 0x0a, 0x20,
+ 0x2a, 0x20, 0x4d, 0x45, 0x52, 0x43, 0x48, 0x41,
+ 0x4e, 0x54, 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54,
+ 0x59, 0x20, 0x6f, 0x72, 0x20, 0x46, 0x49, 0x54,
+ 0x4e, 0x45, 0x53, 0x53, 0x20, 0x46, 0x4f, 0x52,
+ 0x20, 0x41, 0x20, 0x50, 0x41, 0x52, 0x54, 0x49,
+ 0x43, 0x55, 0x4c, 0x41, 0x52, 0x20, 0x50, 0x55,
+ 0x52, 0x50, 0x4f, 0x53, 0x45, 0x2e, 0x20, 0x20,
+ 0x53, 0x65, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x47, 0x4e, 0x55, 0x0a, 0x20, 0x2a, 0x20, 0x47,
+ 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x20, 0x50,
+ 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x4c, 0x69,
+ 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x66, 0x6f,
+ 0x72, 0x20, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x64,
+ 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x0a,
+ 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x59, 0x6f,
+ 0x75, 0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64,
+ 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x72, 0x65,
+ 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x20, 0x61,
+ 0x20, 0x63, 0x6f, 0x70, 0x79, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x47, 0x4e, 0x55,
+ 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
+ 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x0a,
+ 0x20, 0x2a, 0x20, 0x61, 0x6c, 0x6f, 0x6e, 0x67,
+ 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68,
+ 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72,
+ 0x61, 0x6d, 0x3b, 0x20, 0x73, 0x65, 0x65, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x65,
+ 0x20, 0x43, 0x4f, 0x50, 0x59, 0x49, 0x4e, 0x47,
+ 0x2e, 0x20, 0x20, 0x49, 0x66, 0x20, 0x6e, 0x6f,
+ 0x74, 0x2c, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65,
+ 0x20, 0x74, 0x6f, 0x0a, 0x20, 0x2a, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x46, 0x72, 0x65, 0x65, 0x20,
+ 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
+ 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x35, 0x31, 0x20,
+ 0x46, 0x72, 0x61, 0x6e, 0x6b, 0x6c, 0x69, 0x6e,
+ 0x20, 0x53, 0x74, 0x72, 0x65, 0x65, 0x74, 0x2c,
+ 0x20, 0x46, 0x69, 0x66, 0x74, 0x68, 0x20, 0x46,
+ 0x6c, 0x6f, 0x6f, 0x72, 0x2c, 0x0a, 0x20, 0x2a,
+ 0x20, 0x42, 0x6f, 0x73, 0x74, 0x6f, 0x6e, 0x2c,
+ 0x20, 0x4d, 0x41, 0x20, 0x30, 0x32, 0x31, 0x31,
+ 0x30, 0x2d, 0x31, 0x33, 0x30, 0x31, 0x2c, 0x20,
+ 0x55, 0x53, 0x41, 0x2e, 0x0a, 0x20, 0x2a, 0x2f,
+ 0x0a,
+};
+
+/*
+ * SHA1 Hashes
+ */
+static char *commit_id = "3d7f8a6af076c8c3f20071a8935cdbe8228594d1";
+static char *tree_id = "dff2da90b254e1beb889d1f1f1288be1803782df";
+static char *tag_id = "09d373e1dfdc16b129ceec6dd649739911541e05";
+static char *zero_id = "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391";
+static char *one_id = "8b137891791fe96927ad78e64b0aad7bded08bdc";
+static char *two_id = "78981922613b2afb6025042ff6bd878ac1994e85";
+static char *some_id = "fd8430bc864cfcd5f10e5590f8a447e01b942bfe";
+
+/*
+ * In-memory objects
+ */
+static git_rawobj tree_obj = {
+ tree_data,
+ sizeof(tree_data),
+ GIT_OBJ_TREE
+};
+
+static git_rawobj tag_obj = {
+ tag_data,
+ sizeof(tag_data),
+ GIT_OBJ_TAG
+};
+
+static git_rawobj zero_obj = {
+ zero_data,
+ 0,
+ GIT_OBJ_BLOB
+};
+
+static git_rawobj one_obj = {
+ one_data,
+ sizeof(one_data),
+ GIT_OBJ_BLOB
+};
+
+static git_rawobj two_obj = {
+ two_data,
+ sizeof(two_data),
+ GIT_OBJ_BLOB
+};
+
+static git_rawobj commit_obj = {
+ commit_data,
+ sizeof(commit_data),
+ GIT_OBJ_COMMIT
+};
+
+static git_rawobj some_obj = {
+ some_data,
+ sizeof(some_data),
+ GIT_OBJ_BLOB
+};
+
+static git_rawobj junk_obj = {
+ NULL,
+ 0,
+ GIT_OBJ_BAD
+};
diff --git a/tests/object/raw/fromstr.c b/tests/object/raw/fromstr.c
new file mode 100644
index 000000000..8c11c105f
--- /dev/null
+++ b/tests/object/raw/fromstr.c
@@ -0,0 +1,30 @@
+
+#include "clar_libgit2.h"
+
+#include "odb.h"
+
+void test_object_raw_fromstr__fail_on_invalid_oid_string(void)
+{
+ git_oid out;
+ cl_git_fail(git_oid_fromstr(&out, ""));
+ cl_git_fail(git_oid_fromstr(&out, "moo"));
+ cl_git_fail(git_oid_fromstr(&out, "16a67770b7d8d72317c4b775213c23a8bd74f5ez"));
+}
+
+void test_object_raw_fromstr__succeed_on_valid_oid_string(void)
+{
+ git_oid out;
+ unsigned char exp[] = {
+ 0x16, 0xa6, 0x77, 0x70, 0xb7,
+ 0xd8, 0xd7, 0x23, 0x17, 0xc4,
+ 0xb7, 0x75, 0x21, 0x3c, 0x23,
+ 0xa8, 0xbd, 0x74, 0xf5, 0xe0,
+ };
+
+ cl_git_pass(git_oid_fromstr(&out, "16a67770b7d8d72317c4b775213c23a8bd74f5e0"));
+ cl_git_pass(memcmp(out.id, exp, sizeof(out.id)));
+
+ cl_git_pass(git_oid_fromstr(&out, "16A67770B7D8D72317C4b775213C23A8BD74F5E0"));
+ cl_git_pass(memcmp(out.id, exp, sizeof(out.id)));
+
+}
diff --git a/tests/object/raw/hash.c b/tests/object/raw/hash.c
new file mode 100644
index 000000000..ede31e145
--- /dev/null
+++ b/tests/object/raw/hash.c
@@ -0,0 +1,166 @@
+
+#include "clar_libgit2.h"
+
+#include "odb.h"
+#include "hash.h"
+
+#include "data.h"
+
+static void hash_object_pass(git_oid *oid, git_rawobj *obj)
+{
+ cl_git_pass(git_odb_hash(oid, obj->data, obj->len, obj->type));
+}
+static void hash_object_fail(git_oid *oid, git_rawobj *obj)
+{
+ cl_git_fail(git_odb_hash(oid, obj->data, obj->len, obj->type));
+}
+
+static char *hello_id = "22596363b3de40b06f981fb85d82312e8c0ed511";
+static char *hello_text = "hello world\n";
+
+static char *bye_id = "ce08fe4884650f067bd5703b6a59a8b3b3c99a09";
+static char *bye_text = "bye world\n";
+
+void test_object_raw_hash__hash_by_blocks(void)
+{
+ git_hash_ctx ctx;
+ git_oid id1, id2;
+
+ cl_git_pass(git_hash_ctx_init(&ctx));
+
+ /* should already be init'd */
+ cl_git_pass(git_hash_update(&ctx, hello_text, strlen(hello_text)));
+ cl_git_pass(git_hash_final(&id2, &ctx));
+ cl_git_pass(git_oid_fromstr(&id1, hello_id));
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+
+ /* reinit should permit reuse */
+ cl_git_pass(git_hash_init(&ctx));
+ cl_git_pass(git_hash_update(&ctx, bye_text, strlen(bye_text)));
+ cl_git_pass(git_hash_final(&id2, &ctx));
+ cl_git_pass(git_oid_fromstr(&id1, bye_id));
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+
+ git_hash_ctx_cleanup(&ctx);
+}
+
+void test_object_raw_hash__hash_buffer_in_single_call(void)
+{
+ git_oid id1, id2;
+
+ cl_git_pass(git_oid_fromstr(&id1, hello_id));
+ git_hash_buf(&id2, hello_text, strlen(hello_text));
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+}
+
+void test_object_raw_hash__hash_vector(void)
+{
+ git_oid id1, id2;
+ git_buf_vec vec[2];
+
+ cl_git_pass(git_oid_fromstr(&id1, hello_id));
+
+ vec[0].data = hello_text;
+ vec[0].len = 4;
+ vec[1].data = hello_text+4;
+ vec[1].len = strlen(hello_text)-4;
+
+ git_hash_vec(&id2, vec, 2);
+
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+}
+
+void test_object_raw_hash__hash_junk_data(void)
+{
+ git_oid id, id_zero;
+
+ cl_git_pass(git_oid_fromstr(&id_zero, zero_id));
+
+ /* invalid types: */
+ junk_obj.data = some_data;
+ hash_object_fail(&id, &junk_obj);
+
+ junk_obj.type = GIT_OBJ__EXT1;
+ hash_object_fail(&id, &junk_obj);
+
+ junk_obj.type = GIT_OBJ__EXT2;
+ hash_object_fail(&id, &junk_obj);
+
+ junk_obj.type = GIT_OBJ_OFS_DELTA;
+ hash_object_fail(&id, &junk_obj);
+
+ junk_obj.type = GIT_OBJ_REF_DELTA;
+ hash_object_fail(&id, &junk_obj);
+
+ /* data can be NULL only if len is zero: */
+ junk_obj.type = GIT_OBJ_BLOB;
+ junk_obj.data = NULL;
+ hash_object_pass(&id, &junk_obj);
+ cl_assert(git_oid_cmp(&id, &id_zero) == 0);
+
+ junk_obj.len = 1;
+ hash_object_fail(&id, &junk_obj);
+}
+
+void test_object_raw_hash__hash_commit_object(void)
+{
+ git_oid id1, id2;
+
+ cl_git_pass(git_oid_fromstr(&id1, commit_id));
+ hash_object_pass(&id2, &commit_obj);
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+}
+
+void test_object_raw_hash__hash_tree_object(void)
+{
+ git_oid id1, id2;
+
+ cl_git_pass(git_oid_fromstr(&id1, tree_id));
+ hash_object_pass(&id2, &tree_obj);
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+}
+
+void test_object_raw_hash__hash_tag_object(void)
+{
+ git_oid id1, id2;
+
+ cl_git_pass(git_oid_fromstr(&id1, tag_id));
+ hash_object_pass(&id2, &tag_obj);
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+}
+
+void test_object_raw_hash__hash_zero_length_object(void)
+{
+ git_oid id1, id2;
+
+ cl_git_pass(git_oid_fromstr(&id1, zero_id));
+ hash_object_pass(&id2, &zero_obj);
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+}
+
+void test_object_raw_hash__hash_one_byte_object(void)
+{
+ git_oid id1, id2;
+
+ cl_git_pass(git_oid_fromstr(&id1, one_id));
+ hash_object_pass(&id2, &one_obj);
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+}
+
+void test_object_raw_hash__hash_two_byte_object(void)
+{
+ git_oid id1, id2;
+
+ cl_git_pass(git_oid_fromstr(&id1, two_id));
+ hash_object_pass(&id2, &two_obj);
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+}
+
+void test_object_raw_hash__hash_multi_byte_object(void)
+{
+ git_oid id1, id2;
+
+ cl_git_pass(git_oid_fromstr(&id1, some_id));
+ hash_object_pass(&id2, &some_obj);
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+}
diff --git a/tests/object/raw/short.c b/tests/object/raw/short.c
new file mode 100644
index 000000000..813cd86b6
--- /dev/null
+++ b/tests/object/raw/short.c
@@ -0,0 +1,137 @@
+
+#include "clar_libgit2.h"
+
+#include "odb.h"
+#include "hash.h"
+
+void test_object_raw_short__oid_shortener_no_duplicates(void)
+{
+ git_oid_shorten *os;
+ int min_len;
+
+ os = git_oid_shorten_new(0);
+ cl_assert(os != NULL);
+
+ git_oid_shorten_add(os, "22596363b3de40b06f981fb85d82312e8c0ed511");
+ git_oid_shorten_add(os, "ce08fe4884650f067bd5703b6a59a8b3b3c99a09");
+ git_oid_shorten_add(os, "16a0123456789abcdef4b775213c23a8bd74f5e0");
+ min_len = git_oid_shorten_add(os, "ce08fe4884650f067bd5703b6a59a8b3b3c99a09");
+
+ cl_assert(min_len == GIT_OID_HEXSZ + 1);
+
+ git_oid_shorten_free(os);
+}
+
+static int insert_sequential_oids(
+ char ***out, git_oid_shorten *os, int n, int fail)
+{
+ int i, min_len = 0;
+ char numbuf[16];
+ git_oid oid;
+ char **oids = git__calloc(n, sizeof(char *));
+ cl_assert(oids != NULL);
+
+ for (i = 0; i < n; ++i) {
+ p_snprintf(numbuf, sizeof(numbuf), "%u", (unsigned int)i);
+ git_hash_buf(&oid, numbuf, strlen(numbuf));
+
+ oids[i] = git__malloc(GIT_OID_HEXSZ + 1);
+ cl_assert(oids[i]);
+ git_oid_nfmt(oids[i], GIT_OID_HEXSZ + 1, &oid);
+
+ min_len = git_oid_shorten_add(os, oids[i]);
+
+ /* After "fail", we expect git_oid_shorten_add to fail */
+ if (fail >= 0 && i >= fail)
+ cl_assert(min_len < 0);
+ else
+ cl_assert(min_len >= 0);
+ }
+
+ *out = oids;
+
+ return min_len;
+}
+
+static void free_oids(int n, char **oids)
+{
+ int i;
+
+ for (i = 0; i < n; ++i) {
+ git__free(oids[i]);
+ }
+ git__free(oids);
+}
+
+void test_object_raw_short__oid_shortener_stresstest_git_oid_shorten(void)
+{
+#define MAX_OIDS 1000
+
+ git_oid_shorten *os;
+ size_t i, j;
+ int min_len = 0, found_collision;
+ char **oids;
+
+ os = git_oid_shorten_new(0);
+ cl_assert(os != NULL);
+
+ /*
+ * Insert in the shortener 1000 unique SHA1 ids
+ */
+ min_len = insert_sequential_oids(&oids, os, MAX_OIDS, MAX_OIDS);
+ cl_assert(min_len > 0);
+
+ /*
+ * Compare the first `min_char - 1` characters of each
+ * SHA1 OID. If the minimizer worked, we should find at
+ * least one collision
+ */
+ found_collision = 0;
+ for (i = 0; i < MAX_OIDS; ++i) {
+ for (j = i + 1; j < MAX_OIDS; ++j) {
+ if (memcmp(oids[i], oids[j], min_len - 1) == 0)
+ found_collision = 1;
+ }
+ }
+ cl_assert_equal_b(true, found_collision);
+
+ /*
+ * Compare the first `min_char` characters of each
+ * SHA1 OID. If the minimizer worked, every single preffix
+ * should be unique.
+ */
+ found_collision = 0;
+ for (i = 0; i < MAX_OIDS; ++i) {
+ for (j = i + 1; j < MAX_OIDS; ++j) {
+ if (memcmp(oids[i], oids[j], min_len) == 0)
+ found_collision = 1;
+ }
+ }
+ cl_assert_equal_b(false, found_collision);
+
+ /* cleanup */
+ free_oids(MAX_OIDS, oids);
+ git_oid_shorten_free(os);
+
+#undef MAX_OIDS
+}
+
+void test_object_raw_short__oid_shortener_too_much_oids(void)
+{
+ /* The magic number of oids at which an oid_shortener will fail.
+ * This was experimentally established. */
+#define MAX_OIDS 24556
+
+ git_oid_shorten *os;
+ char **oids;
+
+ os = git_oid_shorten_new(0);
+ cl_assert(os != NULL);
+
+ cl_assert(insert_sequential_oids(&oids, os, MAX_OIDS, MAX_OIDS - 1) < 0);
+
+ free_oids(MAX_OIDS, oids);
+ git_oid_shorten_free(os);
+
+#undef MAX_OIDS
+}
diff --git a/tests/object/raw/size.c b/tests/object/raw/size.c
new file mode 100644
index 000000000..930c6de23
--- /dev/null
+++ b/tests/object/raw/size.c
@@ -0,0 +1,13 @@
+
+#include "clar_libgit2.h"
+
+#include "odb.h"
+
+void test_object_raw_size__validate_oid_size(void)
+{
+ git_oid out;
+ cl_assert(20 == GIT_OID_RAWSZ);
+ cl_assert(40 == GIT_OID_HEXSZ);
+ cl_assert(sizeof(out) == GIT_OID_RAWSZ);
+ cl_assert(sizeof(out.id) == GIT_OID_RAWSZ);
+}
diff --git a/tests/object/raw/type2string.c b/tests/object/raw/type2string.c
new file mode 100644
index 000000000..a3585487f
--- /dev/null
+++ b/tests/object/raw/type2string.c
@@ -0,0 +1,54 @@
+
+#include "clar_libgit2.h"
+
+#include "odb.h"
+#include "hash.h"
+
+void test_object_raw_type2string__convert_type_to_string(void)
+{
+ cl_assert_equal_s(git_object_type2string(GIT_OBJ_BAD), "");
+ cl_assert_equal_s(git_object_type2string(GIT_OBJ__EXT1), "");
+ cl_assert_equal_s(git_object_type2string(GIT_OBJ_COMMIT), "commit");
+ cl_assert_equal_s(git_object_type2string(GIT_OBJ_TREE), "tree");
+ cl_assert_equal_s(git_object_type2string(GIT_OBJ_BLOB), "blob");
+ cl_assert_equal_s(git_object_type2string(GIT_OBJ_TAG), "tag");
+ cl_assert_equal_s(git_object_type2string(GIT_OBJ__EXT2), "");
+ cl_assert_equal_s(git_object_type2string(GIT_OBJ_OFS_DELTA), "OFS_DELTA");
+ cl_assert_equal_s(git_object_type2string(GIT_OBJ_REF_DELTA), "REF_DELTA");
+
+ cl_assert_equal_s(git_object_type2string(-2), "");
+ cl_assert_equal_s(git_object_type2string(8), "");
+ cl_assert_equal_s(git_object_type2string(1234), "");
+}
+
+void test_object_raw_type2string__convert_string_to_type(void)
+{
+ cl_assert(git_object_string2type(NULL) == GIT_OBJ_BAD);
+ cl_assert(git_object_string2type("") == GIT_OBJ_BAD);
+ cl_assert(git_object_string2type("commit") == GIT_OBJ_COMMIT);
+ cl_assert(git_object_string2type("tree") == GIT_OBJ_TREE);
+ cl_assert(git_object_string2type("blob") == GIT_OBJ_BLOB);
+ cl_assert(git_object_string2type("tag") == GIT_OBJ_TAG);
+ cl_assert(git_object_string2type("OFS_DELTA") == GIT_OBJ_OFS_DELTA);
+ cl_assert(git_object_string2type("REF_DELTA") == GIT_OBJ_REF_DELTA);
+
+ cl_assert(git_object_string2type("CoMmIt") == GIT_OBJ_BAD);
+ cl_assert(git_object_string2type("hohoho") == GIT_OBJ_BAD);
+}
+
+void test_object_raw_type2string__check_type_is_loose(void)
+{
+ cl_assert(git_object_typeisloose(GIT_OBJ_BAD) == 0);
+ cl_assert(git_object_typeisloose(GIT_OBJ__EXT1) == 0);
+ cl_assert(git_object_typeisloose(GIT_OBJ_COMMIT) == 1);
+ cl_assert(git_object_typeisloose(GIT_OBJ_TREE) == 1);
+ cl_assert(git_object_typeisloose(GIT_OBJ_BLOB) == 1);
+ cl_assert(git_object_typeisloose(GIT_OBJ_TAG) == 1);
+ cl_assert(git_object_typeisloose(GIT_OBJ__EXT2) == 0);
+ cl_assert(git_object_typeisloose(GIT_OBJ_OFS_DELTA) == 0);
+ cl_assert(git_object_typeisloose(GIT_OBJ_REF_DELTA) == 0);
+
+ cl_assert(git_object_typeisloose(-2) == 0);
+ cl_assert(git_object_typeisloose(8) == 0);
+ cl_assert(git_object_typeisloose(1234) == 0);
+}
diff --git a/tests/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/object/tag/list.c b/tests/object/tag/list.c
new file mode 100644
index 000000000..6d5a24347
--- /dev/null
+++ b/tests/object/tag/list.c
@@ -0,0 +1,115 @@
+#include "clar_libgit2.h"
+
+#include "tag.h"
+
+static git_repository *g_repo;
+
+#define MAX_USED_TAGS 6
+
+struct pattern_match_t
+{
+ const char* pattern;
+ const size_t expected_matches;
+ const char* expected_results[MAX_USED_TAGS];
+};
+
+// Helpers
+static void ensure_tag_pattern_match(git_repository *repo,
+ const struct pattern_match_t* data)
+{
+ int already_found[MAX_USED_TAGS] = { 0 };
+ git_strarray tag_list;
+ int error = 0;
+ size_t sucessfully_found = 0;
+ size_t i, j;
+
+ cl_assert(data->expected_matches <= MAX_USED_TAGS);
+
+ if ((error = git_tag_list_match(&tag_list, data->pattern, repo)) < 0)
+ goto exit;
+
+ if (tag_list.count != data->expected_matches)
+ {
+ error = GIT_ERROR;
+ goto exit;
+ }
+
+ // we have to be prepared that tags come in any order.
+ for (i = 0; i < tag_list.count; i++)
+ {
+ for (j = 0; j < data->expected_matches; j++)
+ {
+ if (!already_found[j] && !strcmp(data->expected_results[j], tag_list.strings[i]))
+ {
+ already_found[j] = 1;
+ sucessfully_found++;
+ break;
+ }
+ }
+ }
+ cl_assert_equal_i((int)sucessfully_found, (int)data->expected_matches);
+
+exit:
+ git_strarray_free(&tag_list);
+ cl_git_pass(error);
+}
+
+// Fixture setup and teardown
+void test_object_tag_list__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_object_tag_list__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_object_tag_list__list_all(void)
+{
+ // list all tag names from the repository
+ git_strarray tag_list;
+
+ cl_git_pass(git_tag_list(&tag_list, g_repo));
+
+ cl_assert_equal_i((int)tag_list.count, 6);
+
+ git_strarray_free(&tag_list);
+}
+
+static const struct pattern_match_t matches[] = {
+ // All tags, including a packed one and two namespaced ones.
+ { "", 6, { "e90810b", "point_to_blob", "test", "packed-tag", "foo/bar", "foo/foo/bar" } },
+
+ // beginning with
+ { "t*", 1, { "test" } },
+
+ // ending with
+ { "*b", 2, { "e90810b", "point_to_blob" } },
+
+ // exact match
+ { "e", 0 },
+ { "e90810b", 1, { "e90810b" } },
+
+ // either or
+ { "e90810[ab]", 1, { "e90810b" } },
+
+ // glob in the middle
+ { "foo/*/bar", 1, { "foo/foo/bar" } },
+
+ // The matching of '*' is based on plain string matching analog to the regular expression ".*"
+ // => a '/' in the tag name has no special meaning.
+ // Compare to `git tag -l "*bar"`
+ { "*bar", 2, { "foo/bar", "foo/foo/bar" } },
+
+ // End of list
+ { NULL }
+};
+
+void test_object_tag_list__list_by_pattern(void)
+{
+ // list all tag names from the repository matching a specified pattern
+ size_t i = 0;
+ while (matches[i].pattern)
+ ensure_tag_pattern_match(g_repo, &matches[i++]);
+}
diff --git a/tests/object/tag/peel.c b/tests/object/tag/peel.c
new file mode 100644
index 000000000..e2cd8d6a8
--- /dev/null
+++ b/tests/object/tag/peel.c
@@ -0,0 +1,61 @@
+#include "clar_libgit2.h"
+#include "tag.h"
+
+static git_repository *repo;
+static git_tag *tag;
+static git_object *target;
+
+void test_object_tag_peel__initialize(void)
+{
+ cl_fixture_sandbox("testrepo.git");
+ cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+}
+
+void test_object_tag_peel__cleanup(void)
+{
+ git_tag_free(tag);
+ tag = NULL;
+
+ git_object_free(target);
+ target = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+
+ cl_fixture_cleanup("testrepo.git");
+}
+
+static void retrieve_tag_from_oid(git_tag **tag_out, git_repository *repo, const char *sha)
+{
+ git_oid oid;
+
+ cl_git_pass(git_oid_fromstr(&oid, sha));
+ cl_git_pass(git_tag_lookup(tag_out, repo, &oid));
+}
+
+void test_object_tag_peel__can_peel_to_a_commit(void)
+{
+ retrieve_tag_from_oid(&tag, repo, "7b4384978d2493e851f9cca7858815fac9b10980");
+
+ cl_git_pass(git_tag_peel(&target, tag));
+ cl_assert(git_object_type(target) == GIT_OBJ_COMMIT);
+ cl_git_pass(git_oid_streq(git_object_id(target), "e90810b8df3e80c413d903f631643c716887138d"));
+}
+
+void test_object_tag_peel__can_peel_several_nested_tags_to_a_commit(void)
+{
+ retrieve_tag_from_oid(&tag, repo, "b25fa35b38051e4ae45d4222e795f9df2e43f1d1");
+
+ cl_git_pass(git_tag_peel(&target, tag));
+ cl_assert(git_object_type(target) == GIT_OBJ_COMMIT);
+ cl_git_pass(git_oid_streq(git_object_id(target), "e90810b8df3e80c413d903f631643c716887138d"));
+}
+
+void test_object_tag_peel__can_peel_to_a_non_commit(void)
+{
+ retrieve_tag_from_oid(&tag, repo, "521d87c1ec3aef9824daf6d96cc0ae3710766d91");
+
+ cl_git_pass(git_tag_peel(&target, tag));
+ cl_assert(git_object_type(target) == GIT_OBJ_BLOB);
+ cl_git_pass(git_oid_streq(git_object_id(target), "1385f264afb75a56a5bec74243be9b367ba4ca08"));
+}
diff --git a/tests/object/tag/read.c b/tests/object/tag/read.c
new file mode 100644
index 000000000..c9787a413
--- /dev/null
+++ b/tests/object/tag/read.c
@@ -0,0 +1,142 @@
+#include "clar_libgit2.h"
+
+#include "tag.h"
+
+static const char *tag1_id = "b25fa35b38051e4ae45d4222e795f9df2e43f1d1";
+static const char *tag2_id = "7b4384978d2493e851f9cca7858815fac9b10980";
+static const char *tagged_commit = "e90810b8df3e80c413d903f631643c716887138d";
+static const char *bad_tag_id = "eda9f45a2a98d4c17a09d681d88569fa4ea91755";
+static const char *badly_tagged_commit = "e90810b8df3e80c413d903f631643c716887138d";
+static const char *short_tag_id = "5da7760512a953e3c7c4e47e4392c7a4338fb729";
+static const char *short_tagged_commit = "4a5ed60bafcf4638b7c8356bd4ce1916bfede93c";
+static const char *taggerless = "4a23e2e65ad4e31c4c9db7dc746650bfad082679";
+
+static git_repository *g_repo;
+
+// Fixture setup and teardown
+void test_object_tag_read__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_object_tag_read__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+
+void test_object_tag_read__parse(void)
+{
+ // read and parse a tag from the repository
+ git_tag *tag1, *tag2;
+ git_commit *commit;
+ git_oid id1, id2, id_commit;
+
+ git_oid_fromstr(&id1, tag1_id);
+ git_oid_fromstr(&id2, tag2_id);
+ git_oid_fromstr(&id_commit, tagged_commit);
+
+ cl_git_pass(git_tag_lookup(&tag1, g_repo, &id1));
+
+ cl_assert_equal_s(git_tag_name(tag1), "test");
+ cl_assert(git_tag_target_type(tag1) == GIT_OBJ_TAG);
+
+ cl_git_pass(git_tag_target((git_object **)&tag2, tag1));
+ cl_assert(tag2 != NULL);
+
+ cl_assert(git_oid_cmp(&id2, git_tag_id(tag2)) == 0);
+
+ cl_git_pass(git_tag_target((git_object **)&commit, tag2));
+ cl_assert(commit != NULL);
+
+ cl_assert(git_oid_cmp(&id_commit, git_commit_id(commit)) == 0);
+
+ git_tag_free(tag1);
+ git_tag_free(tag2);
+ git_commit_free(commit);
+}
+
+void test_object_tag_read__parse_without_tagger(void)
+{
+ // read and parse a tag without a tagger field
+ git_repository *bad_tag_repo;
+ git_tag *bad_tag;
+ git_commit *commit;
+ git_oid id, id_commit;
+
+ // TODO: This is a little messy
+ cl_git_pass(git_repository_open(&bad_tag_repo, cl_fixture("bad_tag.git")));
+
+ git_oid_fromstr(&id, bad_tag_id);
+ git_oid_fromstr(&id_commit, badly_tagged_commit);
+
+ cl_git_pass(git_tag_lookup(&bad_tag, bad_tag_repo, &id));
+ cl_assert(bad_tag != NULL);
+
+ cl_assert_equal_s(git_tag_name(bad_tag), "e90810b");
+ cl_assert(git_oid_cmp(&id, git_tag_id(bad_tag)) == 0);
+ cl_assert(bad_tag->tagger == NULL);
+
+ cl_git_pass(git_tag_target((git_object **)&commit, bad_tag));
+ cl_assert(commit != NULL);
+
+ cl_assert(git_oid_cmp(&id_commit, git_commit_id(commit)) == 0);
+
+
+ git_tag_free(bad_tag);
+ git_commit_free(commit);
+ git_repository_free(bad_tag_repo);
+}
+
+void test_object_tag_read__parse_without_message(void)
+{
+ // read and parse a tag without a message field
+ git_repository *short_tag_repo;
+ git_tag *short_tag;
+ git_commit *commit;
+ git_oid id, id_commit;
+
+ // TODO: This is a little messy
+ cl_git_pass(git_repository_open(&short_tag_repo, cl_fixture("short_tag.git")));
+
+ git_oid_fromstr(&id, short_tag_id);
+ git_oid_fromstr(&id_commit, short_tagged_commit);
+
+ cl_git_pass(git_tag_lookup(&short_tag, short_tag_repo, &id));
+ cl_assert(short_tag != NULL);
+
+ cl_assert_equal_s(git_tag_name(short_tag), "no_description");
+ cl_assert(git_oid_cmp(&id, git_tag_id(short_tag)) == 0);
+ cl_assert(short_tag->message == NULL);
+
+ cl_git_pass(git_tag_target((git_object **)&commit, short_tag));
+ cl_assert(commit != NULL);
+
+ cl_assert(git_oid_cmp(&id_commit, git_commit_id(commit)) == 0);
+
+ git_tag_free(short_tag);
+ git_commit_free(commit);
+ git_repository_free(short_tag_repo);
+}
+
+void test_object_tag_read__without_tagger_nor_message(void)
+{
+ git_tag *tag;
+ git_oid id;
+ git_repository *repo;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+ cl_git_pass(git_oid_fromstr(&id, taggerless));
+
+ cl_git_pass(git_tag_lookup(&tag, repo, &id));
+
+ cl_assert_equal_s(git_tag_name(tag), "taggerless");
+ cl_assert(git_tag_target_type(tag) == GIT_OBJ_COMMIT);
+
+ cl_assert(tag->message == NULL);
+ cl_assert(tag->tagger == NULL);
+
+ git_tag_free(tag);
+ git_repository_free(repo);
+}
diff --git a/tests/object/tag/write.c b/tests/object/tag/write.c
new file mode 100644
index 000000000..68e4b6c61
--- /dev/null
+++ b/tests/object/tag/write.c
@@ -0,0 +1,260 @@
+#include "clar_libgit2.h"
+
+static const char* tagger_name = "Vicent Marti";
+static const char* tagger_email = "vicent@github.com";
+static const char* tagger_message = "This is my tag.\n\nThere are many tags, but this one is mine\n";
+
+static const char *tag2_id = "7b4384978d2493e851f9cca7858815fac9b10980";
+static const char *tagged_commit = "e90810b8df3e80c413d903f631643c716887138d";
+
+static git_repository *g_repo;
+
+// Fixture setup and teardown
+void test_object_tag_write__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_object_tag_write__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_object_tag_write__basic(void)
+{
+ // write a tag to the repository and read it again
+ git_tag *tag;
+ git_oid target_id, tag_id;
+ git_signature *tagger;
+ const git_signature *tagger1;
+ git_reference *ref_tag;
+ git_object *target;
+
+ git_oid_fromstr(&target_id, tagged_commit);
+ cl_git_pass(git_object_lookup(&target, g_repo, &target_id, GIT_OBJ_COMMIT));
+
+ /* create signature */
+ cl_git_pass(git_signature_new(&tagger, tagger_name, tagger_email, 123456789, 60));
+
+ cl_git_pass(
+ git_tag_create(&tag_id, g_repo,
+ "the-tag", target, tagger, tagger_message, 0)
+ );
+
+ git_object_free(target);
+ git_signature_free(tagger);
+
+ cl_git_pass(git_tag_lookup(&tag, g_repo, &tag_id));
+ cl_assert(git_oid_cmp(git_tag_target_id(tag), &target_id) == 0);
+
+ /* Check attributes were set correctly */
+ tagger1 = git_tag_tagger(tag);
+ cl_assert(tagger1 != NULL);
+ cl_assert_equal_s(tagger1->name, tagger_name);
+ cl_assert_equal_s(tagger1->email, tagger_email);
+ cl_assert(tagger1->when.time == 123456789);
+ cl_assert(tagger1->when.offset == 60);
+
+ cl_assert_equal_s(git_tag_message(tag), tagger_message);
+
+ cl_git_pass(git_reference_lookup(&ref_tag, g_repo, "refs/tags/the-tag"));
+ cl_assert(git_oid_cmp(git_reference_target(ref_tag), &tag_id) == 0);
+ cl_git_pass(git_reference_delete(ref_tag));
+ git_reference_free(ref_tag);
+
+ git_tag_free(tag);
+}
+
+void test_object_tag_write__overwrite(void)
+{
+ // Attempt to write a tag bearing the same name than an already existing tag
+ git_oid target_id, tag_id;
+ git_signature *tagger;
+ git_object *target;
+
+ git_oid_fromstr(&target_id, tagged_commit);
+ cl_git_pass(git_object_lookup(&target, g_repo, &target_id, GIT_OBJ_COMMIT));
+
+ /* create signature */
+ cl_git_pass(git_signature_new(&tagger, tagger_name, tagger_email, 123456789, 60));
+
+ cl_assert_equal_i(GIT_EEXISTS, git_tag_create(
+ &tag_id, /* out id */
+ g_repo,
+ "e90810b",
+ target,
+ tagger,
+ tagger_message,
+ 0));
+
+ git_object_free(target);
+ git_signature_free(tagger);
+}
+
+void test_object_tag_write__replace(void)
+{
+ // Replace an already existing tag
+ git_oid target_id, tag_id, old_tag_id;
+ git_signature *tagger;
+ git_reference *ref_tag;
+ git_object *target;
+
+ git_oid_fromstr(&target_id, tagged_commit);
+ cl_git_pass(git_object_lookup(&target, g_repo, &target_id, GIT_OBJ_COMMIT));
+
+ cl_git_pass(git_reference_lookup(&ref_tag, g_repo, "refs/tags/e90810b"));
+ git_oid_cpy(&old_tag_id, git_reference_target(ref_tag));
+ git_reference_free(ref_tag);
+
+ /* create signature */
+ cl_git_pass(git_signature_new(&tagger, tagger_name, tagger_email, 123456789, 60));
+
+ cl_git_pass(git_tag_create(
+ &tag_id, /* out id */
+ g_repo,
+ "e90810b",
+ target,
+ tagger,
+ tagger_message,
+ 1));
+
+ git_object_free(target);
+ git_signature_free(tagger);
+
+ cl_git_pass(git_reference_lookup(&ref_tag, g_repo, "refs/tags/e90810b"));
+ cl_assert(git_oid_cmp(git_reference_target(ref_tag), &tag_id) == 0);
+ cl_assert(git_oid_cmp(git_reference_target(ref_tag), &old_tag_id) != 0);
+
+ git_reference_free(ref_tag);
+}
+
+void test_object_tag_write__lightweight(void)
+{
+ // write a lightweight tag to the repository and read it again
+ git_oid target_id, object_id;
+ git_reference *ref_tag;
+ git_object *target;
+
+ git_oid_fromstr(&target_id, tagged_commit);
+ cl_git_pass(git_object_lookup(&target, g_repo, &target_id, GIT_OBJ_COMMIT));
+
+ cl_git_pass(git_tag_create_lightweight(
+ &object_id,
+ g_repo,
+ "light-tag",
+ target,
+ 0));
+
+ git_object_free(target);
+
+ cl_assert(git_oid_cmp(&object_id, &target_id) == 0);
+
+ cl_git_pass(git_reference_lookup(&ref_tag, g_repo, "refs/tags/light-tag"));
+ cl_assert(git_oid_cmp(git_reference_target(ref_tag), &target_id) == 0);
+
+ cl_git_pass(git_tag_delete(g_repo, "light-tag"));
+
+ git_reference_free(ref_tag);
+}
+
+void test_object_tag_write__lightweight_over_existing(void)
+{
+ // Attempt to write a lightweight tag bearing the same name than an already existing tag
+ git_oid target_id, object_id, existing_object_id;
+ git_object *target;
+
+ git_oid_fromstr(&target_id, tagged_commit);
+ cl_git_pass(git_object_lookup(&target, g_repo, &target_id, GIT_OBJ_COMMIT));
+
+ cl_assert_equal_i(GIT_EEXISTS, git_tag_create_lightweight(
+ &object_id,
+ g_repo,
+ "e90810b",
+ target,
+ 0));
+
+ git_oid_fromstr(&existing_object_id, tag2_id);
+ cl_assert(git_oid_cmp(&object_id, &existing_object_id) == 0);
+
+ git_object_free(target);
+}
+
+void test_object_tag_write__delete(void)
+{
+ // Delete an already existing tag
+ git_reference *ref_tag;
+
+ cl_git_pass(git_tag_delete(g_repo, "e90810b"));
+
+ cl_git_fail(git_reference_lookup(&ref_tag, g_repo, "refs/tags/e90810b"));
+
+ git_reference_free(ref_tag);
+}
+
+void test_object_tag_write__creating_with_an_invalid_name_returns_EINVALIDSPEC(void)
+{
+ git_oid target_id, tag_id;
+ git_signature *tagger;
+ git_object *target;
+
+ git_oid_fromstr(&target_id, tagged_commit);
+ cl_git_pass(git_object_lookup(&target, g_repo, &target_id, GIT_OBJ_COMMIT));
+
+ cl_git_pass(git_signature_new(&tagger, tagger_name, tagger_email, 123456789, 60));
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC,
+ git_tag_create(&tag_id, g_repo,
+ "Inv@{id", target, tagger, tagger_message, 0)
+ );
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC,
+ git_tag_create_lightweight(&tag_id, g_repo,
+ "Inv@{id", target, 0)
+ );
+
+ git_object_free(target);
+ git_signature_free(tagger);
+}
+
+void test_object_tag_write__deleting_with_an_invalid_name_returns_EINVALIDSPEC(void)
+{
+ cl_assert_equal_i(GIT_EINVALIDSPEC, git_tag_delete(g_repo, "Inv@{id"));
+}
+
+void create_annotation(git_oid *tag_id, const char *name)
+{
+ git_object *target;
+ git_oid target_id;
+ git_signature *tagger;
+
+ cl_git_pass(git_signature_new(&tagger, tagger_name, tagger_email, 123456789, 60));
+
+ git_oid_fromstr(&target_id, tagged_commit);
+ cl_git_pass(git_object_lookup(&target, g_repo, &target_id, GIT_OBJ_COMMIT));
+
+ cl_git_pass(git_tag_annotation_create(tag_id, g_repo, name, target, tagger, "boom!"));
+ git_object_free(target);
+ git_signature_free(tagger);
+}
+
+void test_object_tag_write__creating_an_annotation_stores_the_new_object_in_the_odb(void)
+{
+ git_oid tag_id;
+ git_tag *tag;
+
+ create_annotation(&tag_id, "new_tag");
+
+ cl_git_pass(git_tag_lookup(&tag, g_repo, &tag_id));
+ cl_assert_equal_s("new_tag", git_tag_name(tag));
+
+ git_tag_free(tag);
+}
+
+void test_object_tag_write__creating_an_annotation_does_not_create_a_reference(void)
+{
+ git_oid tag_id;
+ git_reference *tag_ref;
+
+ create_annotation(&tag_id, "new_tag");
+ cl_git_fail_with(git_reference_lookup(&tag_ref, g_repo, "refs/tags/new_tag"), GIT_ENOTFOUND);
+}
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/object/tree/duplicateentries.c b/tests/object/tree/duplicateentries.c
new file mode 100644
index 000000000..9262f9a1a
--- /dev/null
+++ b/tests/object/tree/duplicateentries.c
@@ -0,0 +1,157 @@
+#include "clar_libgit2.h"
+#include "tree.h"
+
+static git_repository *_repo;
+
+void test_object_tree_duplicateentries__initialize(void) {
+ _repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_object_tree_duplicateentries__cleanup(void) {
+ cl_git_sandbox_cleanup();
+}
+
+/*
+ * $ git show --format=raw refs/heads/dir
+ * commit 144344043ba4d4a405da03de3844aa829ae8be0e
+ * tree d52a8fe84ceedf260afe4f0287bbfca04a117e83
+ * parent cf80f8de9f1185bf3a05f993f6121880dd0cfbc9
+ * author Ben Straub <bstraub@github.com> 1343755506 -0700
+ * committer Ben Straub <bstraub@github.com> 1343755506 -0700
+ *
+ * Change a file mode
+ *
+ * diff --git a/a/b.txt b/a/b.txt
+ * old mode 100644
+ * new mode 100755
+ *
+ * $ git ls-tree d52a8fe84ceedf260afe4f0287bbfca04a117e83
+ * 100644 blob a8233120f6ad708f843d861ce2b7228ec4e3dec6 README
+ * 040000 tree 4e0883eeeeebc1fb1735161cea82f7cb5fab7e63 a
+ * 100644 blob 45b983be36b73c0788dc9cbcb76cbb80fc7bb057 branch_file.txt
+ * 100644 blob a71586c1dfe8a71c6cbf6c129f404c5642ff31bd new.txt
+ */
+
+static void tree_checker(
+ git_oid *tid,
+ const char *expected_sha,
+ git_filemode_t expected_filemode)
+{
+ git_tree *tree;
+ const git_tree_entry *entry;
+ git_oid oid;
+
+ cl_git_pass(git_tree_lookup(&tree, _repo, tid));
+ cl_assert_equal_i(1, (int)git_tree_entrycount(tree));
+ entry = git_tree_entry_byindex(tree, 0);
+
+ cl_git_pass(git_oid_fromstr(&oid, expected_sha));
+
+ cl_assert_equal_i(0, git_oid_cmp(&oid, git_tree_entry_id(entry)));
+ cl_assert_equal_i(expected_filemode, git_tree_entry_filemode(entry));
+
+ git_tree_free(tree);
+}
+
+static void tree_creator(git_oid *out, void (*fn)(git_treebuilder *))
+{
+ git_treebuilder *builder;
+
+ cl_git_pass(git_treebuilder_create(&builder, NULL));
+
+ fn(builder);
+
+ cl_git_pass(git_treebuilder_write(out, _repo, builder));
+ git_treebuilder_free(builder);
+}
+
+static void two_blobs(git_treebuilder *bld)
+{
+ git_oid oid;
+ const git_tree_entry *entry;
+
+ cl_git_pass(git_oid_fromstr(&oid,
+ "a8233120f6ad708f843d861ce2b7228ec4e3dec6")); /* blob oid (README) */
+
+ cl_git_pass(git_treebuilder_insert(
+ &entry, bld, "duplicate", &oid,
+ GIT_FILEMODE_BLOB));
+
+ cl_git_pass(git_oid_fromstr(&oid,
+ "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd")); /* blob oid (new.txt) */
+
+ cl_git_pass(git_treebuilder_insert(
+ &entry, bld, "duplicate", &oid,
+ GIT_FILEMODE_BLOB));
+}
+
+static void one_blob_and_one_tree(git_treebuilder *bld)
+{
+ git_oid oid;
+ const git_tree_entry *entry;
+
+ cl_git_pass(git_oid_fromstr(&oid,
+ "a8233120f6ad708f843d861ce2b7228ec4e3dec6")); /* blob oid (README) */
+
+ cl_git_pass(git_treebuilder_insert(
+ &entry, bld, "duplicate", &oid,
+ GIT_FILEMODE_BLOB));
+
+ cl_git_pass(git_oid_fromstr(&oid,
+ "4e0883eeeeebc1fb1735161cea82f7cb5fab7e63")); /* tree oid (a) */
+
+ cl_git_pass(git_treebuilder_insert(
+ &entry, bld, "duplicate", &oid,
+ GIT_FILEMODE_TREE));
+}
+
+void test_object_tree_duplicateentries__cannot_create_a_duplicate_entry_through_the_treebuilder(void)
+{
+ git_oid tid;
+
+ tree_creator(&tid, two_blobs);
+ tree_checker(&tid, "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd", GIT_FILEMODE_BLOB);
+
+ tree_creator(&tid, one_blob_and_one_tree);
+ tree_checker(&tid, "4e0883eeeeebc1fb1735161cea82f7cb5fab7e63", GIT_FILEMODE_TREE);
+}
+
+static void add_fake_conflicts(git_index *index)
+{
+ git_index_entry ancestor_entry, our_entry, their_entry;
+
+ memset(&ancestor_entry, 0x0, sizeof(git_index_entry));
+ memset(&our_entry, 0x0, sizeof(git_index_entry));
+ memset(&their_entry, 0x0, sizeof(git_index_entry));
+
+ ancestor_entry.path = "duplicate";
+ ancestor_entry.mode = GIT_FILEMODE_BLOB;
+ ancestor_entry.flags |= (1 << GIT_IDXENTRY_STAGESHIFT);
+ git_oid_fromstr(&ancestor_entry.oid, "a8233120f6ad708f843d861ce2b7228ec4e3dec6");
+
+ our_entry.path = "duplicate";
+ our_entry.mode = GIT_FILEMODE_BLOB;
+ ancestor_entry.flags |= (2 << GIT_IDXENTRY_STAGESHIFT);
+ git_oid_fromstr(&our_entry.oid, "45b983be36b73c0788dc9cbcb76cbb80fc7bb057");
+
+ their_entry.path = "duplicate";
+ their_entry.mode = GIT_FILEMODE_BLOB;
+ ancestor_entry.flags |= (3 << GIT_IDXENTRY_STAGESHIFT);
+ git_oid_fromstr(&their_entry.oid, "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd");
+
+ cl_git_pass(git_index_conflict_add(index, &ancestor_entry, &our_entry, &their_entry));
+}
+
+void test_object_tree_duplicateentries__cannot_create_a_duplicate_entry_building_a_tree_from_a_index_with_conflicts(void)
+{
+ git_index *index;
+ git_oid tid;
+
+ cl_git_pass(git_repository_index(&index, _repo));
+
+ add_fake_conflicts(index);
+
+ cl_assert_equal_i(GIT_EUNMERGED, git_index_write_tree(&tid, index));
+
+ git_index_free(index);
+}
diff --git a/tests/object/tree/frompath.c b/tests/object/tree/frompath.c
new file mode 100644
index 000000000..86ca47e94
--- /dev/null
+++ b/tests/object/tree/frompath.c
@@ -0,0 +1,68 @@
+#include "clar_libgit2.h"
+
+static git_repository *repo;
+static git_tree *tree;
+
+void test_object_tree_frompath__initialize(void)
+{
+ git_oid id;
+ const char *tree_with_subtrees_oid = "ae90f12eea699729ed24555e40b9fd669da12a12";
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+ cl_assert(repo != NULL);
+
+ cl_git_pass(git_oid_fromstr(&id, tree_with_subtrees_oid));
+ cl_git_pass(git_tree_lookup(&tree, repo, &id));
+ cl_assert(tree != NULL);
+}
+
+void test_object_tree_frompath__cleanup(void)
+{
+ git_tree_free(tree);
+ tree = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+}
+
+static void assert_tree_from_path(
+ git_tree *root,
+ const char *path,
+ const char *expected_entry_name)
+{
+ git_tree_entry *entry;
+
+ cl_git_pass(git_tree_entry_bypath(&entry, root, path));
+ cl_assert_equal_s(git_tree_entry_name(entry), expected_entry_name);
+ git_tree_entry_free(entry);
+}
+
+void test_object_tree_frompath__retrieve_tree_from_path_to_treeentry(void)
+{
+ git_tree_entry *e;
+
+ assert_tree_from_path(tree, "README", "README");
+ assert_tree_from_path(tree, "ab/de/fgh/1.txt", "1.txt");
+ assert_tree_from_path(tree, "ab/de/fgh", "fgh");
+ assert_tree_from_path(tree, "ab/de/fgh/", "fgh");
+ assert_tree_from_path(tree, "ab/de", "de");
+ assert_tree_from_path(tree, "ab/", "ab");
+ assert_tree_from_path(tree, "ab/de/", "de");
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_tree_entry_bypath(&e, tree, "i-do-not-exist.txt"));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_tree_entry_bypath(&e, tree, "README/"));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_tree_entry_bypath(&e, tree, "ab/de/fgh/i-do-not-exist.txt"));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_tree_entry_bypath(&e, tree, "nope/de/fgh/1.txt"));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_tree_entry_bypath(&e, tree, "ab/me-neither/fgh/2.txt"));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_tree_entry_bypath(&e, tree, "ab/me-neither/fgh/2.txt/"));
+}
+
+void test_object_tree_frompath__fail_when_processing_an_invalid_path(void)
+{
+ git_tree_entry *e;
+
+ cl_must_fail(git_tree_entry_bypath(&e, tree, "/"));
+ cl_must_fail(git_tree_entry_bypath(&e, tree, "/ab"));
+ cl_must_fail(git_tree_entry_bypath(&e, tree, "/ab/de"));
+ cl_must_fail(git_tree_entry_bypath(&e, tree, "ab//de"));
+}
diff --git a/tests/object/tree/read.c b/tests/object/tree/read.c
new file mode 100644
index 000000000..59a809bf1
--- /dev/null
+++ b/tests/object/tree/read.c
@@ -0,0 +1,75 @@
+#include "clar_libgit2.h"
+
+#include "tree.h"
+
+static const char *tree_oid = "1810dff58d8a660512d4832e740f692884338ccd";
+
+static git_repository *g_repo;
+
+// Fixture setup and teardown
+void test_object_tree_read__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_object_tree_read__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+
+
+void test_object_tree_read__loaded(void)
+{
+ // acces randomly the entries on a loaded tree
+ git_oid id;
+ git_tree *tree;
+
+ git_oid_fromstr(&id, tree_oid);
+
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+
+ cl_assert(git_tree_entry_byname(tree, "README") != NULL);
+ cl_assert(git_tree_entry_byname(tree, "NOTEXISTS") == NULL);
+ cl_assert(git_tree_entry_byname(tree, "") == NULL);
+ cl_assert(git_tree_entry_byindex(tree, 0) != NULL);
+ cl_assert(git_tree_entry_byindex(tree, 2) != NULL);
+ cl_assert(git_tree_entry_byindex(tree, 3) == NULL);
+ cl_assert(git_tree_entry_byindex(tree, (unsigned int)-1) == NULL);
+
+ git_tree_free(tree);
+}
+
+void test_object_tree_read__two(void)
+{
+ // read a tree from the repository
+ git_oid id;
+ git_tree *tree;
+ const git_tree_entry *entry;
+ git_object *obj;
+
+ git_oid_fromstr(&id, tree_oid);
+
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+
+ cl_assert(git_tree_entrycount(tree) == 3);
+
+ /* GH-86: git_object_lookup() should also check the type if the object comes from the cache */
+ cl_assert(git_object_lookup(&obj, g_repo, &id, GIT_OBJ_TREE) == 0);
+ cl_assert(obj != NULL);
+ git_object_free(obj);
+ obj = NULL;
+ cl_git_fail(git_object_lookup(&obj, g_repo, &id, GIT_OBJ_BLOB));
+ cl_assert(obj == NULL);
+
+ entry = git_tree_entry_byname(tree, "README");
+ cl_assert(entry != NULL);
+
+ cl_assert_equal_s(git_tree_entry_name(entry), "README");
+
+ cl_git_pass(git_tree_entry_to_object(&obj, g_repo, entry));
+ cl_assert(obj != NULL);
+
+ git_object_free(obj);
+ git_tree_free(tree);
+}
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/object/tree/write.c b/tests/object/tree/write.c
new file mode 100644
index 000000000..468c0ccd1
--- /dev/null
+++ b/tests/object/tree/write.c
@@ -0,0 +1,262 @@
+#include "clar_libgit2.h"
+
+#include "tree.h"
+
+static const char *blob_oid = "fa49b077972391ad58037050f2a75f74e3671e92";
+static const char *first_tree = "181037049a54a1eb5fab404658a3a250b44335d7";
+static const char *second_tree = "f60079018b664e4e79329a7ef9559c8d9e0378d1";
+static const char *third_tree = "eb86d8b81d6adbd5290a935d6c9976882de98488";
+
+static git_repository *g_repo;
+
+// Fixture setup and teardown
+void test_object_tree_write__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_object_tree_write__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_object_tree_write__from_memory(void)
+{
+ // write a tree from a memory
+ git_treebuilder *builder;
+ git_tree *tree;
+ git_oid id, bid, rid, id2;
+
+ git_oid_fromstr(&id, first_tree);
+ git_oid_fromstr(&id2, second_tree);
+ git_oid_fromstr(&bid, blob_oid);
+
+ //create a second tree from first tree using `git_treebuilder_insert` on REPOSITORY_FOLDER.
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+ cl_git_pass(git_treebuilder_create(&builder, tree));
+
+ cl_git_fail(git_treebuilder_insert(NULL, builder, "",
+ &bid, GIT_FILEMODE_BLOB));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, "/",
+ &bid, GIT_FILEMODE_BLOB));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, ".git",
+ &bid, GIT_FILEMODE_BLOB));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, "..",
+ &bid, GIT_FILEMODE_BLOB));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, ".",
+ &bid, GIT_FILEMODE_BLOB));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, "folder/new.txt",
+ &bid, GIT_FILEMODE_BLOB));
+
+ cl_git_pass(git_treebuilder_insert(
+ NULL, builder, "new.txt", &bid, GIT_FILEMODE_BLOB));
+
+ cl_git_pass(git_treebuilder_write(&rid, g_repo, builder));
+
+ cl_assert(git_oid_cmp(&rid, &id2) == 0);
+
+ git_treebuilder_free(builder);
+ git_tree_free(tree);
+}
+
+void test_object_tree_write__subtree(void)
+{
+ // write a hierarchical tree from a memory
+ git_treebuilder *builder;
+ git_tree *tree;
+ git_oid id, bid, subtree_id, id2, id3;
+ git_oid id_hiearar;
+
+ git_oid_fromstr(&id, first_tree);
+ git_oid_fromstr(&id2, second_tree);
+ git_oid_fromstr(&id3, third_tree);
+ git_oid_fromstr(&bid, blob_oid);
+
+ //create subtree
+ cl_git_pass(git_treebuilder_create(&builder, NULL));
+ cl_git_pass(git_treebuilder_insert(
+ NULL, builder, "new.txt", &bid, GIT_FILEMODE_BLOB)); //-V536
+ cl_git_pass(git_treebuilder_write(&subtree_id, g_repo, builder));
+ git_treebuilder_free(builder);
+
+ // create parent tree
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+ cl_git_pass(git_treebuilder_create(&builder, tree));
+ cl_git_pass(git_treebuilder_insert(
+ NULL, builder, "new", &subtree_id, GIT_FILEMODE_TREE)); //-V536
+ cl_git_pass(git_treebuilder_write(&id_hiearar, g_repo, builder));
+ git_treebuilder_free(builder);
+ git_tree_free(tree);
+
+ cl_assert(git_oid_cmp(&id_hiearar, &id3) == 0);
+
+ // check data is correct
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id_hiearar));
+ cl_assert(2 == git_tree_entrycount(tree));
+ git_tree_free(tree);
+}
+
+/*
+ * And the Lord said: Is this tree properly sorted?
+ */
+void test_object_tree_write__sorted_subtrees(void)
+{
+ git_treebuilder *builder;
+ unsigned int i;
+ int position_c = -1, position_cake = -1, position_config = -1;
+
+ struct {
+ unsigned int attr;
+ const char *filename;
+ } entries[] = {
+ { GIT_FILEMODE_BLOB, ".gitattributes" },
+ { GIT_FILEMODE_BLOB, ".gitignore" },
+ { GIT_FILEMODE_BLOB, ".htaccess" },
+ { GIT_FILEMODE_BLOB, "Capfile" },
+ { GIT_FILEMODE_BLOB, "Makefile"},
+ { GIT_FILEMODE_BLOB, "README"},
+ { GIT_FILEMODE_TREE, "app"},
+ { GIT_FILEMODE_TREE, "cake"},
+ { GIT_FILEMODE_TREE, "config"},
+ { GIT_FILEMODE_BLOB, "c"},
+ { GIT_FILEMODE_BLOB, "git_test.txt"},
+ { GIT_FILEMODE_BLOB, "htaccess.htaccess"},
+ { GIT_FILEMODE_BLOB, "index.php"},
+ { GIT_FILEMODE_TREE, "plugins"},
+ { GIT_FILEMODE_TREE, "schemas"},
+ { GIT_FILEMODE_TREE, "ssl-certs"},
+ { GIT_FILEMODE_TREE, "vendors"}
+ };
+
+ git_oid blank_oid, tree_oid;
+
+ memset(&blank_oid, 0x0, sizeof(blank_oid));
+
+ cl_git_pass(git_treebuilder_create(&builder, NULL));
+
+ for (i = 0; i < ARRAY_SIZE(entries); ++i) {
+ cl_git_pass(git_treebuilder_insert(NULL,
+ builder, entries[i].filename, &blank_oid, entries[i].attr));
+ }
+
+ cl_git_pass(git_treebuilder_write(&tree_oid, g_repo, builder));
+
+ for (i = 0; i < builder->entries.length; ++i) {
+ git_tree_entry *entry = git_vector_get(&builder->entries, i);
+
+ if (strcmp(entry->filename, "c") == 0)
+ position_c = i;
+
+ if (strcmp(entry->filename, "cake") == 0)
+ position_cake = i;
+
+ if (strcmp(entry->filename, "config") == 0)
+ position_config = i;
+ }
+
+ cl_assert(position_c != -1);
+ cl_assert(position_cake != -1);
+ cl_assert(position_config != -1);
+
+ cl_assert(position_c < position_cake);
+ cl_assert(position_cake < position_config);
+
+ git_treebuilder_free(builder);
+}
+
+void test_object_tree_write__removing_and_re_adding_in_treebuilder(void)
+{
+ git_treebuilder *builder;
+ int i, aardvark_i, apple_i, apple_after_i, apple_extra_i, last_i;
+ git_oid blank_oid, tree_oid;
+ git_tree *tree;
+ struct {
+ unsigned int attr;
+ const char *filename;
+ } entries[] = {
+ { GIT_FILEMODE_BLOB, "aardvark" },
+ { GIT_FILEMODE_BLOB, ".first" },
+ { GIT_FILEMODE_BLOB, "apple" },
+ { GIT_FILEMODE_BLOB, "last"},
+ { GIT_FILEMODE_BLOB, "apple_after"},
+ { GIT_FILEMODE_BLOB, "after_aardvark"},
+ { 0, NULL },
+ };
+
+ memset(&blank_oid, 0x0, sizeof(blank_oid));
+
+ cl_git_pass(git_treebuilder_create(&builder, NULL));
+
+ cl_assert_equal_i(0, (int)git_treebuilder_entrycount(builder));
+
+ for (i = 0; entries[i].filename; ++i)
+ cl_git_pass(git_treebuilder_insert(NULL,
+ builder, entries[i].filename, &blank_oid, entries[i].attr));
+
+ cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder));
+
+ cl_git_pass(git_treebuilder_remove(builder, "apple"));
+ cl_assert_equal_i(5, (int)git_treebuilder_entrycount(builder));
+
+ cl_git_pass(git_treebuilder_remove(builder, "apple_after"));
+ cl_assert_equal_i(4, (int)git_treebuilder_entrycount(builder));
+
+ cl_git_pass(git_treebuilder_insert(
+ NULL, builder, "before_last", &blank_oid, GIT_FILEMODE_BLOB));
+ cl_assert_equal_i(5, (int)git_treebuilder_entrycount(builder));
+
+ /* reinsert apple_after */
+ cl_git_pass(git_treebuilder_insert(
+ NULL, builder, "apple_after", &blank_oid, GIT_FILEMODE_BLOB));
+ cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder));
+
+ cl_git_pass(git_treebuilder_remove(builder, "last"));
+ cl_assert_equal_i(5, (int)git_treebuilder_entrycount(builder));
+
+ /* reinsert last */
+ cl_git_pass(git_treebuilder_insert(
+ NULL, builder, "last", &blank_oid, GIT_FILEMODE_BLOB));
+ cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder));
+
+ cl_git_pass(git_treebuilder_insert(
+ NULL, builder, "apple_extra", &blank_oid, GIT_FILEMODE_BLOB));
+ cl_assert_equal_i(7, (int)git_treebuilder_entrycount(builder));
+
+ cl_git_pass(git_treebuilder_write(&tree_oid, g_repo, builder));
+
+ git_treebuilder_free(builder);
+
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_oid));
+
+ cl_assert_equal_i(7, (int)git_tree_entrycount(tree));
+
+ cl_assert(git_tree_entry_byname(tree, ".first") != NULL);
+ cl_assert(git_tree_entry_byname(tree, "apple") == NULL);
+ cl_assert(git_tree_entry_byname(tree, "apple_after") != NULL);
+ cl_assert(git_tree_entry_byname(tree, "apple_extra") != NULL);
+ cl_assert(git_tree_entry_byname(tree, "last") != NULL);
+
+ aardvark_i = apple_i = apple_after_i = apple_extra_i = last_i = -1;
+
+ for (i = 0; i < 7; ++i) {
+ const git_tree_entry *entry = git_tree_entry_byindex(tree, i);
+
+ if (!strcmp(entry->filename, "aardvark"))
+ aardvark_i = i;
+ else if (!strcmp(entry->filename, "apple"))
+ apple_i = i;
+ else if (!strcmp(entry->filename, "apple_after"))
+ apple_after_i = i;
+ else if (!strcmp(entry->filename, "apple_extra"))
+ apple_extra_i = i;
+ else if (!strcmp(entry->filename, "last"))
+ last_i = i;
+ }
+
+ cl_assert_equal_i(-1, apple_i);
+ cl_assert_equal_i(6, last_i);
+ cl_assert(aardvark_i < apple_after_i);
+ cl_assert(apple_after_i < apple_extra_i);
+
+ git_tree_free(tree);
+}
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..f643d9621
--- /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-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/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/odb/loose_data.h b/tests/odb/loose_data.h
new file mode 100644
index 000000000..c10c9bc7f
--- /dev/null
+++ b/tests/odb/loose_data.h
@@ -0,0 +1,522 @@
+typedef struct object_data {
+ unsigned char *bytes; /* (compressed) bytes stored in object store */
+ size_t blen; /* length of data in object store */
+ char *id; /* object id (sha1) */
+ char *type; /* object type */
+ char *dir; /* object store (fan-out) directory name */
+ char *file; /* object store filename */
+ unsigned char *data; /* (uncompressed) object data */
+ size_t dlen; /* length of (uncompressed) object data */
+} object_data;
+
+/* one == 8b137891791fe96927ad78e64b0aad7bded08bdc */
+static unsigned char one_bytes[] = {
+ 0x31, 0x78, 0x9c, 0xe3, 0x02, 0x00, 0x00, 0x0b,
+ 0x00, 0x0b,
+};
+
+static unsigned char one_data[] = {
+ 0x0a,
+};
+
+static object_data one = {
+ one_bytes,
+ sizeof(one_bytes),
+ "8b137891791fe96927ad78e64b0aad7bded08bdc",
+ "blob",
+ "test-objects/8b",
+ "test-objects/8b/137891791fe96927ad78e64b0aad7bded08bdc",
+ one_data,
+ sizeof(one_data),
+};
+
+
+/* commit == 3d7f8a6af076c8c3f20071a8935cdbe8228594d1 */
+static unsigned char commit_bytes[] = {
+ 0x78, 0x01, 0x85, 0x50, 0xc1, 0x6a, 0xc3, 0x30,
+ 0x0c, 0xdd, 0xd9, 0x5f, 0xa1, 0xfb, 0x96, 0x12,
+ 0xbb, 0x29, 0x71, 0x46, 0x19, 0x2b, 0x3d, 0x97,
+ 0x1d, 0xd6, 0x7d, 0x80, 0x1d, 0xcb, 0x89, 0x21,
+ 0xb6, 0x82, 0xed, 0x40, 0xf3, 0xf7, 0xf3, 0x48,
+ 0x29, 0x3b, 0x6d, 0xd2, 0xe5, 0xbd, 0x27, 0xbd,
+ 0x27, 0x50, 0x4f, 0xde, 0xbb, 0x0c, 0xfb, 0x43,
+ 0xf3, 0x94, 0x23, 0x22, 0x18, 0x6b, 0x85, 0x51,
+ 0x5d, 0xad, 0xc5, 0xa1, 0x41, 0xae, 0x51, 0x4b,
+ 0xd9, 0x19, 0x6e, 0x4b, 0x0b, 0x29, 0x35, 0x72,
+ 0x59, 0xef, 0x5b, 0x29, 0x8c, 0x65, 0x6a, 0xc9,
+ 0x23, 0x45, 0x38, 0xc1, 0x17, 0x5c, 0x7f, 0xc0,
+ 0x71, 0x13, 0xde, 0xf1, 0xa6, 0xfc, 0x3c, 0xe1,
+ 0xae, 0x27, 0xff, 0x06, 0x5c, 0x88, 0x56, 0xf2,
+ 0x46, 0x74, 0x2d, 0x3c, 0xd7, 0xa5, 0x58, 0x51,
+ 0xcb, 0xb9, 0x8c, 0x11, 0xce, 0xf0, 0x01, 0x97,
+ 0x0d, 0x1e, 0x1f, 0xea, 0x3f, 0x6e, 0x76, 0x02,
+ 0x0a, 0x58, 0x4d, 0x2e, 0x20, 0x6c, 0x1e, 0x48,
+ 0x8b, 0xf7, 0x2a, 0xae, 0x8c, 0x5d, 0x47, 0x04,
+ 0x4d, 0x66, 0x05, 0xb2, 0x90, 0x0b, 0xbe, 0xcf,
+ 0x3d, 0xa6, 0xa4, 0x06, 0x7c, 0x29, 0x3c, 0x64,
+ 0xe5, 0x82, 0x0b, 0x03, 0xd8, 0x25, 0x96, 0x8d,
+ 0x08, 0x78, 0x9b, 0x27, 0x15, 0x54, 0x76, 0x14,
+ 0xd8, 0xdd, 0x35, 0x2f, 0x71, 0xa6, 0x84, 0x8f,
+ 0x90, 0x51, 0x85, 0x01, 0x13, 0xb8, 0x90, 0x23,
+ 0x99, 0xa5, 0x47, 0x03, 0x7a, 0xfd, 0x15, 0xbf,
+ 0x63, 0xec, 0xd3, 0x0d, 0x01, 0x4d, 0x45, 0xb6,
+ 0xd2, 0xeb, 0xeb, 0xdf, 0xef, 0x60, 0xdf, 0xef,
+ 0x1f, 0x78, 0x35,
+};
+
+static unsigned char commit_data[] = {
+ 0x74, 0x72, 0x65, 0x65, 0x20, 0x64, 0x66, 0x66,
+ 0x32, 0x64, 0x61, 0x39, 0x30, 0x62, 0x32, 0x35,
+ 0x34, 0x65, 0x31, 0x62, 0x65, 0x62, 0x38, 0x38,
+ 0x39, 0x64, 0x31, 0x66, 0x31, 0x66, 0x31, 0x32,
+ 0x38, 0x38, 0x62, 0x65, 0x31, 0x38, 0x30, 0x33,
+ 0x37, 0x38, 0x32, 0x64, 0x66, 0x0a, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x20, 0x41, 0x20, 0x55,
+ 0x20, 0x54, 0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x40, 0x65, 0x78,
+ 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x3e, 0x20, 0x31, 0x32, 0x32, 0x37, 0x38,
+ 0x31, 0x34, 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30,
+ 0x30, 0x30, 0x30, 0x0a, 0x63, 0x6f, 0x6d, 0x6d,
+ 0x69, 0x74, 0x74, 0x65, 0x72, 0x20, 0x43, 0x20,
+ 0x4f, 0x20, 0x4d, 0x69, 0x74, 0x74, 0x65, 0x72,
+ 0x20, 0x3c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+ 0x74, 0x65, 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d,
+ 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e,
+ 0x20, 0x31, 0x32, 0x32, 0x37, 0x38, 0x31, 0x34,
+ 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30, 0x30, 0x30,
+ 0x30, 0x0a, 0x0a, 0x41, 0x20, 0x6f, 0x6e, 0x65,
+ 0x2d, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x63, 0x6f,
+ 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x73, 0x75, 0x6d,
+ 0x6d, 0x61, 0x72, 0x79, 0x0a, 0x0a, 0x54, 0x68,
+ 0x65, 0x20, 0x62, 0x6f, 0x64, 0x79, 0x20, 0x6f,
+ 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f,
+ 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x6d, 0x65, 0x73,
+ 0x73, 0x61, 0x67, 0x65, 0x2c, 0x20, 0x63, 0x6f,
+ 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67,
+ 0x20, 0x66, 0x75, 0x72, 0x74, 0x68, 0x65, 0x72,
+ 0x20, 0x65, 0x78, 0x70, 0x6c, 0x61, 0x6e, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x6f, 0x66, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x70, 0x75, 0x72, 0x70,
+ 0x6f, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67,
+ 0x65, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x72, 0x6f,
+ 0x64, 0x75, 0x63, 0x65, 0x64, 0x20, 0x62, 0x79,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d,
+ 0x6d, 0x69, 0x74, 0x2e, 0x0a, 0x0a, 0x53, 0x69,
+ 0x67, 0x6e, 0x65, 0x64, 0x2d, 0x6f, 0x66, 0x2d,
+ 0x62, 0x79, 0x3a, 0x20, 0x41, 0x20, 0x55, 0x20,
+ 0x54, 0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x40, 0x65, 0x78, 0x61,
+ 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x3e, 0x0a,
+};
+
+static object_data commit = {
+ commit_bytes,
+ sizeof(commit_bytes),
+ "3d7f8a6af076c8c3f20071a8935cdbe8228594d1",
+ "commit",
+ "test-objects/3d",
+ "test-objects/3d/7f8a6af076c8c3f20071a8935cdbe8228594d1",
+ commit_data,
+ sizeof(commit_data),
+};
+
+/* tree == dff2da90b254e1beb889d1f1f1288be1803782df */
+static unsigned char tree_bytes[] = {
+ 0x78, 0x01, 0x2b, 0x29, 0x4a, 0x4d, 0x55, 0x30,
+ 0x34, 0x32, 0x63, 0x30, 0x34, 0x30, 0x30, 0x33,
+ 0x31, 0x51, 0xc8, 0xcf, 0x4b, 0x65, 0xe8, 0x16,
+ 0xae, 0x98, 0x58, 0x29, 0xff, 0x32, 0x53, 0x7d,
+ 0x6d, 0xc5, 0x33, 0x6f, 0xae, 0xb5, 0xd5, 0xf7,
+ 0x2e, 0x74, 0xdf, 0x81, 0x4a, 0x17, 0xe7, 0xe7,
+ 0xa6, 0x32, 0xfc, 0x6d, 0x31, 0xd8, 0xd3, 0xe6,
+ 0xf3, 0xe7, 0xea, 0x47, 0xbe, 0xd0, 0x09, 0x3f,
+ 0x96, 0xb8, 0x3f, 0x90, 0x9e, 0xa2, 0xfd, 0x0f,
+ 0x2a, 0x5f, 0x52, 0x9e, 0xcf, 0x50, 0x31, 0x43,
+ 0x52, 0x29, 0xd1, 0x5a, 0xeb, 0x77, 0x82, 0x2a,
+ 0x8b, 0xfe, 0xb7, 0xbd, 0xed, 0x5d, 0x07, 0x67,
+ 0xfa, 0xb5, 0x42, 0xa5, 0xab, 0x52, 0x8b, 0xf2,
+ 0x19, 0x9e, 0xcd, 0x7d, 0x34, 0x7b, 0xd3, 0xc5,
+ 0x6b, 0xce, 0xde, 0xdd, 0x9a, 0xeb, 0xca, 0xa3,
+ 0x6e, 0x1c, 0x7a, 0xd2, 0x13, 0x3c, 0x11, 0x00,
+ 0xe2, 0xaa, 0x38, 0x57,
+};
+
+static unsigned char tree_data[] = {
+ 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x6f,
+ 0x6e, 0x65, 0x00, 0x8b, 0x13, 0x78, 0x91, 0x79,
+ 0x1f, 0xe9, 0x69, 0x27, 0xad, 0x78, 0xe6, 0x4b,
+ 0x0a, 0xad, 0x7b, 0xde, 0xd0, 0x8b, 0xdc, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x73, 0x6f,
+ 0x6d, 0x65, 0x00, 0xfd, 0x84, 0x30, 0xbc, 0x86,
+ 0x4c, 0xfc, 0xd5, 0xf1, 0x0e, 0x55, 0x90, 0xf8,
+ 0xa4, 0x47, 0xe0, 0x1b, 0x94, 0x2b, 0xfe, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x77,
+ 0x6f, 0x00, 0x78, 0x98, 0x19, 0x22, 0x61, 0x3b,
+ 0x2a, 0xfb, 0x60, 0x25, 0x04, 0x2f, 0xf6, 0xbd,
+ 0x87, 0x8a, 0xc1, 0x99, 0x4e, 0x85, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x7a, 0x65, 0x72,
+ 0x6f, 0x00, 0xe6, 0x9d, 0xe2, 0x9b, 0xb2, 0xd1,
+ 0xd6, 0x43, 0x4b, 0x8b, 0x29, 0xae, 0x77, 0x5a,
+ 0xd8, 0xc2, 0xe4, 0x8c, 0x53, 0x91,
+};
+
+static object_data tree = {
+ tree_bytes,
+ sizeof(tree_bytes),
+ "dff2da90b254e1beb889d1f1f1288be1803782df",
+ "tree",
+ "test-objects/df",
+ "test-objects/df/f2da90b254e1beb889d1f1f1288be1803782df",
+ tree_data,
+ sizeof(tree_data),
+};
+
+/* tag == 09d373e1dfdc16b129ceec6dd649739911541e05 */
+static unsigned char tag_bytes[] = {
+ 0x78, 0x01, 0x35, 0x4e, 0xcb, 0x0a, 0xc2, 0x40,
+ 0x10, 0xf3, 0xbc, 0x5f, 0x31, 0x77, 0xa1, 0xec,
+ 0xa3, 0xed, 0x6e, 0x41, 0x44, 0xf0, 0x2c, 0x5e,
+ 0xfc, 0x81, 0xe9, 0x76, 0xb6, 0xad, 0xb4, 0xb4,
+ 0x6c, 0x07, 0xd1, 0xbf, 0x77, 0x44, 0x0d, 0x39,
+ 0x84, 0x10, 0x92, 0x30, 0xf6, 0x60, 0xbc, 0xdb,
+ 0x2d, 0xed, 0x9d, 0x22, 0x83, 0xeb, 0x7c, 0x0a,
+ 0x58, 0x63, 0xd2, 0xbe, 0x8e, 0x21, 0xba, 0x64,
+ 0xb5, 0xf6, 0x06, 0x43, 0xe3, 0xaa, 0xd8, 0xb5,
+ 0x14, 0xac, 0x0d, 0x55, 0x53, 0x76, 0x46, 0xf1,
+ 0x6b, 0x25, 0x88, 0xcb, 0x3c, 0x8f, 0xac, 0x58,
+ 0x3a, 0x1e, 0xba, 0xd0, 0x85, 0xd8, 0xd8, 0xf7,
+ 0x94, 0xe1, 0x0c, 0x57, 0xb8, 0x8c, 0xcc, 0x22,
+ 0x0f, 0xdf, 0x90, 0xc8, 0x13, 0x3d, 0x71, 0x5e,
+ 0x27, 0x2a, 0xc4, 0x39, 0x82, 0xb1, 0xd6, 0x07,
+ 0x53, 0xda, 0xc6, 0xc3, 0x5e, 0x0b, 0x94, 0xba,
+ 0x0d, 0xe3, 0x06, 0x42, 0x1e, 0x08, 0x3e, 0x95,
+ 0xbf, 0x4b, 0x69, 0xc9, 0x90, 0x69, 0x22, 0xdc,
+ 0xe8, 0xbf, 0xf2, 0x06, 0x42, 0x9a, 0x36, 0xb1,
+};
+
+static unsigned char tag_data[] = {
+ 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x33,
+ 0x64, 0x37, 0x66, 0x38, 0x61, 0x36, 0x61, 0x66,
+ 0x30, 0x37, 0x36, 0x63, 0x38, 0x63, 0x33, 0x66,
+ 0x32, 0x30, 0x30, 0x37, 0x31, 0x61, 0x38, 0x39,
+ 0x33, 0x35, 0x63, 0x64, 0x62, 0x65, 0x38, 0x32,
+ 0x32, 0x38, 0x35, 0x39, 0x34, 0x64, 0x31, 0x0a,
+ 0x74, 0x79, 0x70, 0x65, 0x20, 0x63, 0x6f, 0x6d,
+ 0x6d, 0x69, 0x74, 0x0a, 0x74, 0x61, 0x67, 0x20,
+ 0x76, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x0a, 0x74,
+ 0x61, 0x67, 0x67, 0x65, 0x72, 0x20, 0x43, 0x20,
+ 0x4f, 0x20, 0x4d, 0x69, 0x74, 0x74, 0x65, 0x72,
+ 0x20, 0x3c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+ 0x74, 0x65, 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d,
+ 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e,
+ 0x20, 0x31, 0x32, 0x32, 0x37, 0x38, 0x31, 0x34,
+ 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30, 0x30, 0x30,
+ 0x30, 0x0a, 0x0a, 0x54, 0x68, 0x69, 0x73, 0x20,
+ 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74,
+ 0x61, 0x67, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63,
+ 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x72, 0x65,
+ 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20, 0x76, 0x30,
+ 0x2e, 0x30, 0x2e, 0x31, 0x0a,
+};
+
+static object_data tag = {
+ tag_bytes,
+ sizeof(tag_bytes),
+ "09d373e1dfdc16b129ceec6dd649739911541e05",
+ "tag",
+ "test-objects/09",
+ "test-objects/09/d373e1dfdc16b129ceec6dd649739911541e05",
+ tag_data,
+ sizeof(tag_data),
+};
+
+/* zero == e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 */
+static unsigned char zero_bytes[] = {
+ 0x78, 0x01, 0x4b, 0xca, 0xc9, 0x4f, 0x52, 0x30,
+ 0x60, 0x00, 0x00, 0x09, 0xb0, 0x01, 0xf0,
+};
+
+static unsigned char zero_data[] = {
+ 0x00 /* dummy data */
+};
+
+static object_data zero = {
+ zero_bytes,
+ sizeof(zero_bytes),
+ "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
+ "blob",
+ "test-objects/e6",
+ "test-objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391",
+ zero_data,
+ 0,
+};
+
+/* two == 78981922613b2afb6025042ff6bd878ac1994e85 */
+static unsigned char two_bytes[] = {
+ 0x78, 0x01, 0x4b, 0xca, 0xc9, 0x4f, 0x52, 0x30,
+ 0x62, 0x48, 0xe4, 0x02, 0x00, 0x0e, 0x64, 0x02,
+ 0x5d,
+};
+
+static unsigned char two_data[] = {
+ 0x61, 0x0a,
+};
+
+static object_data two = {
+ two_bytes,
+ sizeof(two_bytes),
+ "78981922613b2afb6025042ff6bd878ac1994e85",
+ "blob",
+ "test-objects/78",
+ "test-objects/78/981922613b2afb6025042ff6bd878ac1994e85",
+ two_data,
+ sizeof(two_data),
+};
+
+/* some == fd8430bc864cfcd5f10e5590f8a447e01b942bfe */
+static unsigned char some_bytes[] = {
+ 0x78, 0x01, 0x7d, 0x54, 0xc1, 0x4e, 0xe3, 0x30,
+ 0x10, 0xdd, 0x33, 0x5f, 0x31, 0xc7, 0x5d, 0x94,
+ 0xa5, 0x84, 0xd5, 0x22, 0xad, 0x7a, 0x0a, 0x15,
+ 0x85, 0x48, 0xd0, 0x56, 0x49, 0x2a, 0xd4, 0xa3,
+ 0x13, 0x4f, 0x88, 0x85, 0x63, 0x47, 0xb6, 0x43,
+ 0xc9, 0xdf, 0xef, 0x8c, 0x69, 0x17, 0x56, 0x0b,
+ 0x7b, 0xaa, 0x62, 0x7b, 0xde, 0xbc, 0xf7, 0xe6,
+ 0x4d, 0x6b, 0x6d, 0x6b, 0x48, 0xd3, 0xcb, 0x5f,
+ 0x5f, 0x66, 0xa7, 0x27, 0x70, 0x0a, 0x55, 0xa7,
+ 0x3c, 0xb4, 0x4a, 0x23, 0xf0, 0xaf, 0x43, 0x04,
+ 0x6f, 0xdb, 0xb0, 0x17, 0x0e, 0xe7, 0x30, 0xd9,
+ 0x11, 0x1a, 0x61, 0xc0, 0xa1, 0x54, 0x3e, 0x38,
+ 0x55, 0x8f, 0x81, 0x9e, 0x05, 0x10, 0x46, 0xce,
+ 0xac, 0x83, 0xde, 0x4a, 0xd5, 0x4e, 0x0c, 0x42,
+ 0x67, 0xa3, 0x91, 0xe8, 0x20, 0x74, 0x08, 0x01,
+ 0x5d, 0xef, 0xc1, 0xb6, 0xf1, 0xe3, 0x66, 0xb5,
+ 0x85, 0x1b, 0x34, 0xe8, 0x84, 0x86, 0xcd, 0x58,
+ 0x6b, 0xd5, 0xc0, 0x9d, 0x6a, 0xd0, 0x78, 0x4c,
+ 0xe0, 0x19, 0x9d, 0x57, 0xd6, 0xc0, 0x45, 0xc2,
+ 0x18, 0xc2, 0xc3, 0xc0, 0x0f, 0x7c, 0x87, 0x12,
+ 0xea, 0x29, 0x56, 0x2f, 0x99, 0x4f, 0x79, 0xe0,
+ 0x03, 0x4b, 0x4b, 0x4d, 0x44, 0xa0, 0x92, 0x33,
+ 0x2a, 0xe0, 0x9a, 0xdc, 0x80, 0x90, 0x52, 0xf1,
+ 0x11, 0x04, 0x1b, 0x4b, 0x06, 0xea, 0xae, 0x3c,
+ 0xe3, 0x7a, 0x50, 0x74, 0x4a, 0x84, 0xfe, 0xc3,
+ 0x81, 0x41, 0xf8, 0x89, 0x18, 0x43, 0x67, 0x9d,
+ 0x87, 0x47, 0xf5, 0x8c, 0x51, 0xf6, 0x68, 0xb4,
+ 0xea, 0x55, 0x20, 0x2a, 0x6f, 0x80, 0xdc, 0x42,
+ 0x2b, 0xf3, 0x14, 0x2b, 0x1a, 0xdb, 0x0f, 0xe4,
+ 0x9a, 0x64, 0x84, 0xa3, 0x90, 0xa8, 0xf9, 0x8f,
+ 0x9d, 0x86, 0x9e, 0xd3, 0xab, 0x5a, 0x99, 0xc8,
+ 0xd9, 0xc3, 0x5e, 0x85, 0x0e, 0x2c, 0xb5, 0x73,
+ 0x30, 0x38, 0xfb, 0xe8, 0x44, 0xef, 0x5f, 0x95,
+ 0x1b, 0xc9, 0xd0, 0xef, 0x3c, 0x26, 0x32, 0x1e,
+ 0xff, 0x2d, 0xb6, 0x23, 0x7b, 0x3f, 0xd1, 0x3c,
+ 0x78, 0x1a, 0x0d, 0xcb, 0xe6, 0xf6, 0xd4, 0x44,
+ 0x99, 0x47, 0x1a, 0x9e, 0xed, 0x23, 0xb5, 0x91,
+ 0x6a, 0xdf, 0x53, 0x39, 0x03, 0xf8, 0x5a, 0xb1,
+ 0x0f, 0x1f, 0xce, 0x81, 0x11, 0xde, 0x01, 0x7a,
+ 0x90, 0x16, 0xc4, 0x30, 0xe8, 0x89, 0xed, 0x7b,
+ 0x65, 0x4b, 0xd7, 0x03, 0x36, 0xc1, 0xcf, 0xa1,
+ 0xa5, 0xb1, 0xe3, 0x8b, 0xe8, 0x07, 0x4d, 0xf3,
+ 0x23, 0x25, 0x13, 0x35, 0x27, 0xf5, 0x8c, 0x11,
+ 0xd3, 0xa0, 0x9a, 0xa8, 0xf5, 0x38, 0x7d, 0xce,
+ 0x55, 0xc2, 0x71, 0x79, 0x13, 0xc7, 0xa3, 0xda,
+ 0x77, 0x68, 0xc0, 0xd8, 0x10, 0xdd, 0x24, 0x8b,
+ 0x15, 0x59, 0xc5, 0x10, 0xe2, 0x20, 0x99, 0x8e,
+ 0xf0, 0x05, 0x9b, 0x31, 0x88, 0x5a, 0xe3, 0xd9,
+ 0x37, 0xba, 0xe2, 0xdb, 0xbf, 0x92, 0xfa, 0x66,
+ 0x16, 0x97, 0x47, 0xd9, 0x9d, 0x1d, 0x28, 0x7c,
+ 0x9d, 0x08, 0x1c, 0xc7, 0xbd, 0xd2, 0x1a, 0x6a,
+ 0x04, 0xf2, 0xa2, 0x1d, 0x75, 0x02, 0x14, 0x5d,
+ 0xc6, 0x78, 0xc8, 0xab, 0xdb, 0xf5, 0xb6, 0x82,
+ 0x6c, 0xb5, 0x83, 0x87, 0xac, 0x28, 0xb2, 0x55,
+ 0xb5, 0x9b, 0xc7, 0xc1, 0xb0, 0xb7, 0xf8, 0x4c,
+ 0xbc, 0x38, 0x0e, 0x8a, 0x04, 0x2a, 0x62, 0x41,
+ 0x6b, 0xe0, 0x84, 0x09, 0x13, 0xe9, 0xe1, 0xea,
+ 0xfb, 0xeb, 0x62, 0x71, 0x4b, 0x25, 0xd9, 0x55,
+ 0x7e, 0x97, 0x57, 0x3b, 0x20, 0x33, 0x96, 0x79,
+ 0xb5, 0xba, 0x2e, 0x4b, 0x58, 0xae, 0x0b, 0xc8,
+ 0x60, 0x93, 0x15, 0x55, 0xbe, 0xd8, 0xde, 0x65,
+ 0x05, 0x6c, 0xb6, 0xc5, 0x66, 0x5d, 0x5e, 0x93,
+ 0xf7, 0x25, 0x65, 0x98, 0x41, 0x29, 0x86, 0x0c,
+ 0xf2, 0xf1, 0x14, 0xa2, 0xb3, 0xbd, 0x75, 0x08,
+ 0x12, 0x83, 0x50, 0xda, 0x1f, 0x23, 0xbe, 0xa3,
+ 0x1d, 0xf4, 0x9d, 0x1d, 0xb5, 0x84, 0x4e, 0x50,
+ 0x38, 0x1d, 0x36, 0x48, 0x21, 0x95, 0xd1, 0xac,
+ 0x81, 0x99, 0x1d, 0xc1, 0x3f, 0x41, 0xe6, 0x9e,
+ 0x42, 0x5b, 0x0a, 0x48, 0xcc, 0x5f, 0xe0, 0x7d,
+ 0x3f, 0xc4, 0x6f, 0x0e, 0xfe, 0xc0, 0x2d, 0xfe,
+ 0x01, 0x2c, 0xd6, 0x9b, 0x5d, 0xbe, 0xba, 0x21,
+ 0xca, 0x79, 0xcb, 0xe3, 0x49, 0x60, 0xef, 0x68,
+ 0x05, 0x28, 0x9b, 0x8c, 0xc1, 0x12, 0x3e, 0xdb,
+ 0xc7, 0x04, 0x7e, 0xa6, 0x74, 0x29, 0xcc, 0x13,
+ 0xed, 0x07, 0x94, 0x81, 0xd6, 0x96, 0xaa, 0x97,
+ 0xaa, 0xa5, 0xc0, 0x2f, 0xb5, 0xb5, 0x2e, 0xe6,
+ 0xfc, 0xca, 0xfa, 0x60, 0x4d, 0x02, 0xf7, 0x19,
+ 0x9c, 0x5f, 0xa4, 0xe9, 0xf9, 0xf7, 0xf4, 0xc7,
+ 0x79, 0x9a, 0xc0, 0xb6, 0xcc, 0x58, 0xec, 0xec,
+ 0xe4, 0x37, 0x22, 0xfa, 0x8b, 0x53,
+};
+
+static unsigned char some_data[] = {
+ 0x2f, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x54, 0x68,
+ 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20,
+ 0x69, 0x73, 0x20, 0x66, 0x72, 0x65, 0x65, 0x20,
+ 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
+ 0x3b, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x63, 0x61,
+ 0x6e, 0x20, 0x72, 0x65, 0x64, 0x69, 0x73, 0x74,
+ 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x69,
+ 0x74, 0x20, 0x61, 0x6e, 0x64, 0x2f, 0x6f, 0x72,
+ 0x20, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0a,
+ 0x20, 0x2a, 0x20, 0x69, 0x74, 0x20, 0x75, 0x6e,
+ 0x64, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x74, 0x65, 0x72, 0x6d, 0x73, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x47, 0x4e, 0x55,
+ 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
+ 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x2c,
+ 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+ 0x20, 0x32, 0x2c, 0x0a, 0x20, 0x2a, 0x20, 0x61,
+ 0x73, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73,
+ 0x68, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x46, 0x72, 0x65, 0x65, 0x20,
+ 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
+ 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x2a, 0x0a,
+ 0x20, 0x2a, 0x20, 0x49, 0x6e, 0x20, 0x61, 0x64,
+ 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74,
+ 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x65,
+ 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e,
+ 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x47, 0x4e, 0x55, 0x20, 0x47, 0x65, 0x6e,
+ 0x65, 0x72, 0x61, 0x6c, 0x20, 0x50, 0x75, 0x62,
+ 0x6c, 0x69, 0x63, 0x20, 0x4c, 0x69, 0x63, 0x65,
+ 0x6e, 0x73, 0x65, 0x2c, 0x0a, 0x20, 0x2a, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x61, 0x75, 0x74, 0x68,
+ 0x6f, 0x72, 0x73, 0x20, 0x67, 0x69, 0x76, 0x65,
+ 0x20, 0x79, 0x6f, 0x75, 0x20, 0x75, 0x6e, 0x6c,
+ 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x20, 0x70,
+ 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f,
+ 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x6c, 0x69, 0x6e,
+ 0x6b, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f,
+ 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x64, 0x0a, 0x20,
+ 0x2a, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
+ 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69,
+ 0x73, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x69,
+ 0x6e, 0x74, 0x6f, 0x20, 0x63, 0x6f, 0x6d, 0x62,
+ 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6f, 0x74,
+ 0x68, 0x65, 0x72, 0x20, 0x70, 0x72, 0x6f, 0x67,
+ 0x72, 0x61, 0x6d, 0x73, 0x2c, 0x0a, 0x20, 0x2a,
+ 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x6f, 0x20,
+ 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75,
+ 0x74, 0x65, 0x20, 0x74, 0x68, 0x6f, 0x73, 0x65,
+ 0x20, 0x63, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x77, 0x69,
+ 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x61, 0x6e,
+ 0x79, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x2a,
+ 0x20, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x20,
+ 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c,
+ 0x65, 0x2e, 0x20, 0x20, 0x28, 0x54, 0x68, 0x65,
+ 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
+ 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x0a,
+ 0x20, 0x2a, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72,
+ 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20,
+ 0x64, 0x6f, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x79,
+ 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x74, 0x68, 0x65,
+ 0x72, 0x20, 0x72, 0x65, 0x73, 0x70, 0x65, 0x63,
+ 0x74, 0x73, 0x3b, 0x20, 0x66, 0x6f, 0x72, 0x20,
+ 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c,
+ 0x20, 0x74, 0x68, 0x65, 0x79, 0x20, 0x63, 0x6f,
+ 0x76, 0x65, 0x72, 0x0a, 0x20, 0x2a, 0x20, 0x6d,
+ 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x2c,
+ 0x20, 0x61, 0x6e, 0x64, 0x20, 0x64, 0x69, 0x73,
+ 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x6e,
+ 0x6f, 0x74, 0x20, 0x6c, 0x69, 0x6e, 0x6b, 0x65,
+ 0x64, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x0a, 0x20,
+ 0x2a, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x62,
+ 0x69, 0x6e, 0x65, 0x64, 0x20, 0x65, 0x78, 0x65,
+ 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e,
+ 0x29, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20,
+ 0x54, 0x68, 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c,
+ 0x65, 0x20, 0x69, 0x73, 0x20, 0x64, 0x69, 0x73,
+ 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x64,
+ 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x68, 0x6f, 0x70, 0x65, 0x20, 0x74, 0x68, 0x61,
+ 0x74, 0x20, 0x69, 0x74, 0x20, 0x77, 0x69, 0x6c,
+ 0x6c, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65,
+ 0x66, 0x75, 0x6c, 0x2c, 0x20, 0x62, 0x75, 0x74,
+ 0x0a, 0x20, 0x2a, 0x20, 0x57, 0x49, 0x54, 0x48,
+ 0x4f, 0x55, 0x54, 0x20, 0x41, 0x4e, 0x59, 0x20,
+ 0x57, 0x41, 0x52, 0x52, 0x41, 0x4e, 0x54, 0x59,
+ 0x3b, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75,
+ 0x74, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x69,
+ 0x65, 0x64, 0x20, 0x77, 0x61, 0x72, 0x72, 0x61,
+ 0x6e, 0x74, 0x79, 0x20, 0x6f, 0x66, 0x0a, 0x20,
+ 0x2a, 0x20, 0x4d, 0x45, 0x52, 0x43, 0x48, 0x41,
+ 0x4e, 0x54, 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54,
+ 0x59, 0x20, 0x6f, 0x72, 0x20, 0x46, 0x49, 0x54,
+ 0x4e, 0x45, 0x53, 0x53, 0x20, 0x46, 0x4f, 0x52,
+ 0x20, 0x41, 0x20, 0x50, 0x41, 0x52, 0x54, 0x49,
+ 0x43, 0x55, 0x4c, 0x41, 0x52, 0x20, 0x50, 0x55,
+ 0x52, 0x50, 0x4f, 0x53, 0x45, 0x2e, 0x20, 0x20,
+ 0x53, 0x65, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x47, 0x4e, 0x55, 0x0a, 0x20, 0x2a, 0x20, 0x47,
+ 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x20, 0x50,
+ 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x4c, 0x69,
+ 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x66, 0x6f,
+ 0x72, 0x20, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x64,
+ 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x0a,
+ 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x59, 0x6f,
+ 0x75, 0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64,
+ 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x72, 0x65,
+ 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x20, 0x61,
+ 0x20, 0x63, 0x6f, 0x70, 0x79, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x47, 0x4e, 0x55,
+ 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
+ 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x0a,
+ 0x20, 0x2a, 0x20, 0x61, 0x6c, 0x6f, 0x6e, 0x67,
+ 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68,
+ 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72,
+ 0x61, 0x6d, 0x3b, 0x20, 0x73, 0x65, 0x65, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x65,
+ 0x20, 0x43, 0x4f, 0x50, 0x59, 0x49, 0x4e, 0x47,
+ 0x2e, 0x20, 0x20, 0x49, 0x66, 0x20, 0x6e, 0x6f,
+ 0x74, 0x2c, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65,
+ 0x20, 0x74, 0x6f, 0x0a, 0x20, 0x2a, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x46, 0x72, 0x65, 0x65, 0x20,
+ 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
+ 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x35, 0x31, 0x20,
+ 0x46, 0x72, 0x61, 0x6e, 0x6b, 0x6c, 0x69, 0x6e,
+ 0x20, 0x53, 0x74, 0x72, 0x65, 0x65, 0x74, 0x2c,
+ 0x20, 0x46, 0x69, 0x66, 0x74, 0x68, 0x20, 0x46,
+ 0x6c, 0x6f, 0x6f, 0x72, 0x2c, 0x0a, 0x20, 0x2a,
+ 0x20, 0x42, 0x6f, 0x73, 0x74, 0x6f, 0x6e, 0x2c,
+ 0x20, 0x4d, 0x41, 0x20, 0x30, 0x32, 0x31, 0x31,
+ 0x30, 0x2d, 0x31, 0x33, 0x30, 0x31, 0x2c, 0x20,
+ 0x55, 0x53, 0x41, 0x2e, 0x0a, 0x20, 0x2a, 0x2f,
+ 0x0a,
+};
+
+static object_data some = {
+ some_bytes,
+ sizeof(some_bytes),
+ "fd8430bc864cfcd5f10e5590f8a447e01b942bfe",
+ "blob",
+ "test-objects/fd",
+ "test-objects/fd/8430bc864cfcd5f10e5590f8a447e01b942bfe",
+ some_data,
+ sizeof(some_data),
+};
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/odb/pack_data.h b/tests/odb/pack_data.h
new file mode 100644
index 000000000..e6371beb1
--- /dev/null
+++ b/tests/odb/pack_data.h
@@ -0,0 +1,151 @@
+
+static const char *packed_objects[] = {
+ "0266163a49e280c4f5ed1e08facd36a2bd716bcf",
+ "53fc32d17276939fc79ed05badaef2db09990016",
+ "6336846bd5c88d32f93ae57d846683e61ab5c530",
+ "6dcf9bf7541ee10456529833502442f385010c3d",
+ "bed08a0b30b72a9d4aed7f1af8c8ca124e8d64b9",
+ "e90810b8df3e80c413d903f631643c716887138d",
+ "fc3c3a2083e9f6f89e6bd53e9420e70d1e357c9b",
+ "fc58168adf502d0c0ef614c3111a7038fc8c09c8",
+ "fd0ec0333948dfe23265ac46be0205a436a8c3a5",
+ "fd8430bc864cfcd5f10e5590f8a447e01b942bfe",
+ "fd899f45951c15c1c5f7c34b1c864e91bd6556c6",
+ "fda23b974899e7e1f938619099280bfda13bdca9",
+ "fdbec189efb657c8325962b494875987881a356b",
+ "fe1ca6bd22b5d8353ce6c2f3aba80805c438a7a5",
+ "fe3a6a42c87ff1239370c741a265f3997add87c1",
+ "deb106bfd2d36ecf9f0079224c12022201a39ad1",
+ "dec93efc79e60f2680de3e666755d335967eec30",
+ "def425bf8568b9c1e20879bf5be6f9c52b7361c4",
+ "df48000ac4f48570054e3a71a81916357997b680",
+ "dfae6ed8f6dd8acc3b40a31811ea316239223559",
+ "dff79e27d3d2cdc09790ded80fe2ea8ff5d61034",
+ "e00e46abe4c542e17c8bc83d72cf5be8018d7b0e",
+ "e01b107b4f77f8f98645adac0206a504f2d29d7c",
+ "e032d863f512c47b479bd984f8b6c8061f66b7d4",
+ "e044baa468a1c74f9f9da36805445f6888358b49",
+ "e04529998989ba8ae3419538dd57969af819b241",
+ "e0637ddfbea67c8d7f557c709e095af8906e9176",
+ "e0743ad4031231e71700abdc6fdbe94f189d20e5",
+ "cf33ac7a3d8b2b8f6bb266518aadbf59de397608",
+ "cf5f7235b9c9689b133f6ea12015720b411329bd",
+ "cf6cccf1297284833a9a03138a1f5738fa1c6c94",
+ "cf7992bde17ce7a79cab5f0c1fcbe8a0108721ed",
+ "cfe3a027ab12506d4144ee8a35669ae8fc4b7ab1",
+ "cfe96f31dfad7bab49977aa1df7302f7fafcb025",
+ "cff54d138945ef4de384e9d2759291d0c13ea90a",
+ "d01f7573ac34c2f502bd1cf18cde73480c741151",
+ "d03f567593f346a1ca96a57f8191def098d126e3",
+ "d047b47aadf88501238f36f5c17dd0a50dc62087",
+ "d0a0d63086fae3b0682af7261df21f7d0f7f066d",
+ "d0a44bd6ed0be21b725a96c0891bbc79bc1a540c",
+ "d0d7e736e536a41bcb885005f8bf258c61cad682",
+ "d0e7959d4b95ffec6198df6f5a7ae259b23a5f50",
+ "bf2fe2acca17d13356ce802ba9dc8343f710dfb7",
+ "bf55f407d6d9418e51f42ea7a3a6aadf17388349",
+ "bf92206f8b633b88a66dca4a911777630b06fbac",
+ "bfaf8c42eb8842abe206179fee864cfba87e3ca9",
+ "bfe05675d4e8f6b59d50932add8790f1a06b10ee",
+ "bff8618112330763327cfa6ce6e914db84f51ddf",
+ "bff873e9853ed99fed52c25f7ad29f78b27dcec2",
+ "c01c3fae7251098d7af1b459bcd0786e81d4616d",
+ "c0220fca67f48b8a5d4163d53b1486224be3a198",
+ "c02d0b160b82ee72469c269f13de4c26a7ea09cb",
+ "c059510ad1b45ab58390e042d7dee1ac46703854",
+ "c07204a1897aeeaa3c248d29dbfa9b033baf9755",
+ "c073337a4dd7276931b4b3fdbc3f0040e9441793",
+ "0fd7e4bfba5b3a82be88d1057757ca8b2c5e6d26",
+ "100746511cc45c9f1ad6721c4ef5be49222fee4d",
+ "1088490171d9b984d68b8b9be9ca003f4eafff59",
+ "1093c8ff4cb78fcf5f79dbbeedcb6e824bd4e253",
+ "10aa3fa72afab7ee31e116ae06442fe0f7b79df2",
+ "10b759e734e8299aa0dca08be935d95d886127b6",
+ "111d5ccf0bb010c4e8d7af3eedfa12ef4c5e265b",
+ "11261fbff21758444d426356ff6327ee01e90752",
+ "112998d425717bb922ce74e8f6f0f831d8dc4510",
+ "2ef4e5d838b6507bd61d457cf6466662b791c5c0",
+ "2ef4faa0f82efa00eeac6cae9e8b2abccc8566ee",
+ "2f06098183b0d7be350acbe39cdbaccff2df0c4a",
+ "2f1c5d509ac5bffb3c62f710a1c2c542e126dfd1",
+ "2f205b20fc16423c42b3ba51b2ea78d7b9ff3578",
+ "2f9b6b6e3d9250ba09360734aa47973a993b59d1",
+ "30c62a2d5a8d644f1311d4f7fe3f6a788e4c8188",
+ "31438e245492d85fd6da4d1406eba0fbde8332a4",
+ "3184a3abdfea231992254929ff4e275898e5bbf6",
+ "3188ffdbb3a3d52e0f78f30c484533899224436e",
+ "32581d0093429770d044a60eb0e9cc0462bedb13",
+ "32679a9544d83e5403202c4d5efb61ad02492847",
+ "4e7e9f60b7e2049b7f5697daf133161a18ef688f",
+ "4e8cda27ddc8be7db875ceb0f360c37734724c6d",
+ "4ea481c61c59ab55169b7cbaae536ad50b49d6f0",
+ "4f0adcd0e61eabe06fe32be66b16559537124b7a",
+ "4f1355c91100d12f9e7202f91b245df0c110867c",
+ "4f6eadeb08b9d0d1e8b1b3eac8a34940adf29a2d",
+ "4f9339df943c53117a5fc8e86e2f38716ff3a668",
+ "4fc3874b118752e40de556b1c3e7b4a9f1737d00",
+ "4ff1dd0992dd6baafdb5e166be6f9f23b59bdf87",
+ "5018a35e0b7e2eec7ce5050baf9c7343f3f74164",
+ "50298f44a45eda3a29dae82dbe911b5aa176ac07",
+ "502acd164fb115768d723144da2e7bb5a24891bb",
+ "50330c02bd4fd95c9db1fcf2f97f4218e42b7226",
+ "5052bf355d9f8c52446561a39733a8767bf31e37",
+ "6f2cd729ae42988c1dd43588d3a6661ba48ad7a0",
+ "6f4e2c42d9138bfbf3e0f908f1308828cc6f2178",
+ "6f6a17db05a83620cef4572761831c20a70ba9b9",
+ "6faad60901e36538634f0d8b8ff3f21f83503c71",
+ "6fc72e46de3df0c3842dab302bbacf697a63abab",
+ "6fdccd49f442a7204399ca9b418f017322dbded8",
+ "6fe7568fc3861c334cb008fd85d57d9647249ef5",
+ "700f55d91d7b55665594676a4bada1f1457a0598",
+ "702bd70595a7b19afc48a1f784a6505be68469d4",
+ "7033f9ee0e52b08cb5679cd49b7b7999eaf9eaf8",
+ "70957110ce446c4e250f865760fb3da513cdcc92",
+ "8ec696a4734f16479d091bc70574d23dd9fe7443",
+ "8ed341c55ed4d6f4cdc8bf4f0ca18a08c93f6962",
+ "8edc2805f1f11b63e44bf81f4557f8b473612b69",
+ "8ef9060a954118a698fc10e20acdc430566a100f",
+ "8f0c4b543f4bb6eb1518ecfc3d4699e43108d393",
+ "8fac94df3035405c2e60b3799153ce7c428af6b9",
+ "904c0ac12b23548de524adae712241b423d765a3",
+ "90bbaa9a809c3a768d873a9cc7d52b4f3bf3d1b9",
+ "90d4d2f0fc362beabbbf76b4ffda0828229c198d",
+ "90f9ff6755330b685feff6c3d81782ee3592ab04",
+ "91822c50ebe4f9bf5bbb8308ecf9f6557062775c",
+ "91d973263a55708fa8255867b3202d81ef9c2868",
+ "af292c99c6148d772af3315a1c74e83330e7ead7",
+ "af3b99d5be330dbbce0b9250c3a5fb05911908cc",
+ "af55d0cdeb280af2db8697e5afa506e081012719",
+ "af795e498d411142ddb073e8ca2c5447c3295a4c",
+ "afadc73a392f8cc8e2cc77dd62a7433dd3bafa8c",
+ "affd84ed8ec7ce67612fe3c12a80f8164b101f6a",
+ "b0941f9c70ffe67f0387a827b338e64ecf3190f0",
+ "b0a3077f9ef6e093f8d9869bdb0c07095bd722cb",
+ "b0a8568a7614806378a54db5706ee3b06ae58693",
+ "b0fb7372f242233d1d35ce7d8e74d3990cbc5841",
+ "b10489944b9ead17427551759d180d10203e06ba",
+ "b196a807b323f2748ffc6b1d42cd0812d04c9a40",
+ "b1bb1d888f0c5e19278536d49fa77db035fac7ae"
+};
+
+static const char *loose_objects[] = {
+ "45b983be36b73c0788dc9cbcb76cbb80fc7bb057",
+ "a8233120f6ad708f843d861ce2b7228ec4e3dec6",
+ "fd093bff70906175335656e6ce6ae05783708765",
+ "c47800c7266a2be04c571c04d5a6614691ea99bd",
+ "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd",
+ "8496071c1b46c854b31185ea97743be6a8774479",
+ "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
+ "814889a078c031f61ed08ab5fa863aea9314344d",
+ "5b5b025afb0b4c913b4c338a42934a3863bf3644",
+ "1385f264afb75a56a5bec74243be9b367ba4ca08",
+ "f60079018b664e4e79329a7ef9559c8d9e0378d1",
+ "be3563ae3f795b2b4353bcce3a527ad0a4f7f644",
+ "75057dd4114e74cca1d750d0aee1647c903cb60a",
+ "fa49b077972391ad58037050f2a75f74e3671e92",
+ "9fd738e8f7967c078dceed8190330fc8648ee56a",
+ "1810dff58d8a660512d4832e740f692884338ccd",
+ "181037049a54a1eb5fab404658a3a250b44335d7",
+ "a4a7dce85cf63874e984719f4fdd239f5145052f",
+ "4a202b346bb0fb0db7eff3cffeb3c70babbd2045"
+};
diff --git a/tests/odb/pack_data_one.h b/tests/odb/pack_data_one.h
new file mode 100644
index 000000000..13570ba78
--- /dev/null
+++ b/tests/odb/pack_data_one.h
@@ -0,0 +1,19 @@
+/* Just a few to make sure it's working, the rest is tested already */
+static const char *packed_objects_one[] = {
+ "9fcf811e00fa469688943a9152c16d4ee90fb9a9",
+ "a93f42a5b5e9de40fa645a9ff1e276a021c9542b",
+ "12bf5f3e3470d90db177ccf1b5e8126409377fc6",
+ "ed1ea164cdbe3c4b200fb4fa19861ea90eaee222",
+ "dfae6ed8f6dd8acc3b40a31811ea316239223559",
+ "aefe66d192771201e369fde830530f4475beec30",
+ "775e4b4c1296e9e3104f2a36ca9cf9356a130959",
+ "412ec4e4a6a7419bc1be00561fe474e54cb499fe",
+ "236e7579fed7763be77209efb8708960982f3cb3",
+ "09fe9364461cf60dd1c46b0e9545b1e47bb1a297",
+ "d76d8a6390d1cf32138d98a91b1eb7e0275a12f5",
+ "d0fdf2dcff2f548952eec536ccc6d266550041bc",
+ "a20d733a9fa79fa5b4cbb9639864f93325ec27a6",
+ "785d3fe8e7db5ade2c2242fecd46c32a7f4dc59f",
+ "4d8d0fd9cb6045075385701c3f933ec13345e9c4",
+ "0cfd861bd547b6520d1fc2e190e8359e0a9c9b90"
+};
diff --git a/tests/odb/packed.c b/tests/odb/packed.c
new file mode 100644
index 000000000..b4f549b58
--- /dev/null
+++ b/tests/odb/packed.c
@@ -0,0 +1,79 @@
+#include "clar_libgit2.h"
+#include "odb.h"
+#include "pack_data.h"
+
+static git_odb *_odb;
+
+void test_odb_packed__initialize(void)
+{
+ cl_git_pass(git_odb_open(&_odb, cl_fixture("testrepo.git/objects")));
+}
+
+void test_odb_packed__cleanup(void)
+{
+ git_odb_free(_odb);
+ _odb = NULL;
+}
+
+void test_odb_packed__mass_read(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(packed_objects); ++i) {
+ git_oid id;
+ git_odb_object *obj;
+
+ cl_git_pass(git_oid_fromstr(&id, packed_objects[i]));
+ cl_assert(git_odb_exists(_odb, &id) == 1);
+ cl_git_pass(git_odb_read(&obj, _odb, &id));
+
+ git_odb_object_free(obj);
+ }
+}
+
+void test_odb_packed__read_header_0(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(packed_objects); ++i) {
+ git_oid id;
+ git_odb_object *obj;
+ size_t len;
+ git_otype type;
+
+ cl_git_pass(git_oid_fromstr(&id, packed_objects[i]));
+
+ cl_git_pass(git_odb_read(&obj, _odb, &id));
+ cl_git_pass(git_odb_read_header(&len, &type, _odb, &id));
+
+ cl_assert(obj->cached.size == len);
+ cl_assert(obj->cached.type == type);
+
+ git_odb_object_free(obj);
+ }
+}
+
+void test_odb_packed__read_header_1(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(loose_objects); ++i) {
+ git_oid id;
+ git_odb_object *obj;
+ size_t len;
+ git_otype type;
+
+ cl_git_pass(git_oid_fromstr(&id, loose_objects[i]));
+
+ cl_assert(git_odb_exists(_odb, &id) == 1);
+
+ cl_git_pass(git_odb_read(&obj, _odb, &id));
+ cl_git_pass(git_odb_read_header(&len, &type, _odb, &id));
+
+ cl_assert(obj->cached.size == len);
+ cl_assert(obj->cached.type == type);
+
+ git_odb_object_free(obj);
+ }
+}
+
diff --git a/tests/odb/packed_one.c b/tests/odb/packed_one.c
new file mode 100644
index 000000000..0c6ed387b
--- /dev/null
+++ b/tests/odb/packed_one.c
@@ -0,0 +1,60 @@
+#include "clar_libgit2.h"
+#include "git2/odb_backend.h"
+
+#include "pack_data_one.h"
+#include "pack.h"
+
+static git_odb *_odb;
+
+void test_odb_packed_one__initialize(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));
+}
+
+void test_odb_packed_one__cleanup(void)
+{
+ git_odb_free(_odb);
+ _odb = NULL;
+}
+
+void test_odb_packed_one__mass_read(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(packed_objects_one); ++i) {
+ git_oid id;
+ git_odb_object *obj;
+
+ cl_git_pass(git_oid_fromstr(&id, packed_objects_one[i]));
+ cl_assert(git_odb_exists(_odb, &id) == 1);
+ cl_git_pass(git_odb_read(&obj, _odb, &id));
+
+ git_odb_object_free(obj);
+ }
+}
+
+void test_odb_packed_one__read_header_0(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(packed_objects_one); ++i) {
+ git_oid id;
+ git_odb_object *obj;
+ size_t len;
+ git_otype type;
+
+ cl_git_pass(git_oid_fromstr(&id, packed_objects_one[i]));
+
+ cl_git_pass(git_odb_read(&obj, _odb, &id));
+ cl_git_pass(git_odb_read_header(&len, &type, _odb, &id));
+
+ cl_assert(obj->cached.size == len);
+ cl_assert(obj->cached.type == type);
+
+ git_odb_object_free(obj);
+ }
+}
diff --git a/tests/odb/sorting.c b/tests/odb/sorting.c
new file mode 100644
index 000000000..147a160c8
--- /dev/null
+++ b/tests/odb/sorting.c
@@ -0,0 +1,69 @@
+#include "clar_libgit2.h"
+#include "git2/sys/odb_backend.h"
+
+typedef struct {
+ git_odb_backend base;
+ size_t position;
+} fake_backend;
+
+static git_odb_backend *new_backend(size_t position)
+{
+ fake_backend *b;
+
+ b = git__calloc(1, sizeof(fake_backend));
+ if (b == NULL)
+ return NULL;
+
+ b->base.version = GIT_ODB_BACKEND_VERSION;
+ b->position = position;
+ return (git_odb_backend *)b;
+}
+
+static void check_backend_sorting(git_odb *odb)
+{
+ size_t i, max_i = git_odb_num_backends(odb);
+ fake_backend *internal;
+
+ for (i = 0; i < max_i; ++i) {
+ cl_git_pass(git_odb_get_backend((git_odb_backend **)&internal, odb, i));
+ cl_assert(internal != NULL);
+ cl_assert_equal_sz(i, internal->position);
+ }
+}
+
+static git_odb *_odb;
+
+void test_odb_sorting__initialize(void)
+{
+ cl_git_pass(git_odb_new(&_odb));
+}
+
+void test_odb_sorting__cleanup(void)
+{
+ git_odb_free(_odb);
+ _odb = NULL;
+}
+
+void test_odb_sorting__basic_backends_sorting(void)
+{
+ cl_git_pass(git_odb_add_backend(_odb, new_backend(0), 5));
+ cl_git_pass(git_odb_add_backend(_odb, new_backend(2), 3));
+ cl_git_pass(git_odb_add_backend(_odb, new_backend(1), 4));
+ cl_git_pass(git_odb_add_backend(_odb, new_backend(3), 1));
+
+ check_backend_sorting(_odb);
+}
+
+void test_odb_sorting__alternate_backends_sorting(void)
+{
+ cl_git_pass(git_odb_add_backend(_odb, new_backend(0), 5));
+ cl_git_pass(git_odb_add_backend(_odb, new_backend(2), 3));
+ cl_git_pass(git_odb_add_backend(_odb, new_backend(1), 4));
+ cl_git_pass(git_odb_add_backend(_odb, new_backend(3), 1));
+ cl_git_pass(git_odb_add_alternate(_odb, new_backend(4), 5));
+ cl_git_pass(git_odb_add_alternate(_odb, new_backend(6), 3));
+ cl_git_pass(git_odb_add_alternate(_odb, new_backend(5), 4));
+ cl_git_pass(git_odb_add_alternate(_odb, new_backend(7), 1));
+
+ check_backend_sorting(_odb);
+}
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..aa3d6b26a
--- /dev/null
+++ b/tests/online/clone.c
@@ -0,0 +1,256 @@
+#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);
+}
+
+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..aeb1ab47d
--- /dev/null
+++ b/tests/online/push.c
@@ -0,0 +1,770 @@
+#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_ssh_key;
+static char *_remote_ssh_pubkey;
+static char *_remote_ssh_passphrase;
+
+static char *_remote_url;
+static char *_remote_user;
+static char *_remote_pass;
+
+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_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 = 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..54d1e8022
--- /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-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");
+}
+
+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/refs/branches/create.c b/tests/refs/branches/create.c
new file mode 100644
index 000000000..693a592a3
--- /dev/null
+++ b/tests/refs/branches/create.c
@@ -0,0 +1,76 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+
+static git_repository *repo;
+static git_commit *target;
+static git_reference *branch;
+
+void test_refs_branches_create__initialize(void)
+{
+ cl_fixture_sandbox("testrepo.git");
+ cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+
+ branch = NULL;
+}
+
+void test_refs_branches_create__cleanup(void)
+{
+ git_reference_free(branch);
+ branch = NULL;
+
+ git_commit_free(target);
+ target = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+
+ cl_fixture_cleanup("testrepo.git");
+}
+
+static void retrieve_target_from_oid(git_commit **out, git_repository *repo, const char *sha)
+{
+ git_oid oid;
+
+ cl_git_pass(git_oid_fromstr(&oid, sha));
+ cl_git_pass(git_commit_lookup(out, repo, &oid));
+}
+
+static void retrieve_known_commit(git_commit **commit, git_repository *repo)
+{
+ retrieve_target_from_oid(commit, repo, "e90810b8df3e80c413d903f631643c716887138d");
+}
+
+#define NEW_BRANCH_NAME "new-branch-on-the-block"
+
+void test_refs_branches_create__can_create_a_local_branch(void)
+{
+ retrieve_known_commit(&target, repo);
+
+ cl_git_pass(git_branch_create(&branch, repo, NEW_BRANCH_NAME, target, 0));
+ cl_git_pass(git_oid_cmp(git_reference_target(branch), git_commit_id(target)));
+}
+
+void test_refs_branches_create__can_not_create_a_branch_if_its_name_collide_with_an_existing_one(void)
+{
+ retrieve_known_commit(&target, repo);
+
+ cl_assert_equal_i(GIT_EEXISTS, git_branch_create(&branch, repo, "br2", target, 0));
+}
+
+void test_refs_branches_create__can_force_create_over_an_existing_branch(void)
+{
+ retrieve_known_commit(&target, repo);
+
+ cl_git_pass(git_branch_create(&branch, repo, "br2", target, 1));
+ cl_git_pass(git_oid_cmp(git_reference_target(branch), git_commit_id(target)));
+ cl_assert_equal_s("refs/heads/br2", git_reference_name(branch));
+}
+
+
+void test_refs_branches_create__creating_a_branch_with_an_invalid_name_returns_EINVALIDSPEC(void)
+{
+ retrieve_known_commit(&target, repo);
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC,
+ git_branch_create(&branch, repo, "inv@{id", target, 0));
+} \ No newline at end of file
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/refs/branches/lookup.c b/tests/refs/branches/lookup.c
new file mode 100644
index 000000000..95d49a4b3
--- /dev/null
+++ b/tests/refs/branches/lookup.c
@@ -0,0 +1,45 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+
+static git_repository *repo;
+static git_reference *branch;
+
+void test_refs_branches_lookup__initialize(void)
+{
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+ branch = NULL;
+}
+
+void test_refs_branches_lookup__cleanup(void)
+{
+ git_reference_free(branch);
+ branch = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+}
+
+void test_refs_branches_lookup__can_retrieve_a_local_branch(void)
+{
+ cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_LOCAL));
+}
+
+void test_refs_branches_lookup__can_retrieve_a_remote_tracking_branch(void)
+{
+ cl_git_pass(git_branch_lookup(&branch, repo, "test/master", GIT_BRANCH_REMOTE));
+}
+
+void test_refs_branches_lookup__trying_to_retrieve_an_unknown_branch_returns_ENOTFOUND(void)
+{
+ cl_assert_equal_i(GIT_ENOTFOUND, git_branch_lookup(&branch, repo, "where/are/you", GIT_BRANCH_LOCAL));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_branch_lookup(&branch, repo, "over/here", GIT_BRANCH_REMOTE));
+}
+
+void test_refs_branches_lookup__trying_to_retrieve_a_branch_with_an_invalid_name_returns_EINVALIDSPEC(void)
+{
+ cl_assert_equal_i(GIT_EINVALIDSPEC,
+ git_branch_lookup(&branch, repo, "are/you/inv@{id", GIT_BRANCH_LOCAL));
+ cl_assert_equal_i(GIT_EINVALIDSPEC,
+ git_branch_lookup(&branch, repo, "yes/i am", GIT_BRANCH_REMOTE));
+}
diff --git a/tests/refs/branches/move.c b/tests/refs/branches/move.c
new file mode 100644
index 000000000..ecf14e006
--- /dev/null
+++ b/tests/refs/branches/move.c
@@ -0,0 +1,146 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+#include "config/config_helpers.h"
+
+static git_repository *repo;
+
+void test_refs_branches_move__initialize(void)
+{
+ repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_refs_branches_move__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+#define NEW_BRANCH_NAME "new-branch-on-the-block"
+
+void test_refs_branches_move__can_move_a_local_branch(void)
+{
+ git_reference *original_ref, *new_ref;
+
+ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2"));
+
+ cl_git_pass(git_branch_move(&new_ref, original_ref, NEW_BRANCH_NAME, 0));
+ cl_assert_equal_s(GIT_REFS_HEADS_DIR NEW_BRANCH_NAME, git_reference_name(new_ref));
+
+ git_reference_free(original_ref);
+ git_reference_free(new_ref);
+}
+
+void test_refs_branches_move__can_move_a_local_branch_to_a_different_namespace(void)
+{
+ git_reference *original_ref, *new_ref, *newer_ref;
+
+ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2"));
+
+ /* Downward */
+ cl_git_pass(git_branch_move(&new_ref, original_ref, "somewhere/" NEW_BRANCH_NAME, 0));
+ git_reference_free(original_ref);
+
+ /* Upward */
+ cl_git_pass(git_branch_move(&newer_ref, new_ref, "br2", 0));
+ git_reference_free(new_ref);
+
+ git_reference_free(newer_ref);
+}
+
+void test_refs_branches_move__can_move_a_local_branch_to_a_partially_colliding_namespace(void)
+{
+ git_reference *original_ref, *new_ref, *newer_ref;
+
+ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2"));
+
+ /* Downward */
+ cl_git_pass(git_branch_move(&new_ref, original_ref, "br2/" NEW_BRANCH_NAME, 0));
+ git_reference_free(original_ref);
+
+ /* Upward */
+ cl_git_pass(git_branch_move(&newer_ref, new_ref, "br2", 0));
+ git_reference_free(new_ref);
+
+ git_reference_free(newer_ref);
+}
+
+void test_refs_branches_move__can_not_move_a_branch_if_its_destination_name_collide_with_an_existing_one(void)
+{
+ git_reference *original_ref, *new_ref;
+
+ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2"));
+
+ cl_assert_equal_i(GIT_EEXISTS, git_branch_move(&new_ref, original_ref, "master", 0));
+
+ git_reference_free(original_ref);
+}
+
+void test_refs_branches_move__moving_a_branch_with_an_invalid_name_returns_EINVALIDSPEC(void)
+{
+ git_reference *original_ref, *new_ref;
+
+ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2"));
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC, git_branch_move(&new_ref, original_ref, "Inv@{id", 0));
+
+ git_reference_free(original_ref);
+}
+
+void test_refs_branches_move__can_not_move_a_non_branch(void)
+{
+ git_reference *tag, *new_ref;
+
+ cl_git_pass(git_reference_lookup(&tag, repo, "refs/tags/e90810b"));
+ cl_git_fail(git_branch_move(&new_ref, tag, NEW_BRANCH_NAME, 0));
+
+ git_reference_free(tag);
+}
+
+void test_refs_branches_move__can_force_move_over_an_existing_branch(void)
+{
+ git_reference *original_ref, *new_ref;
+
+ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2"));
+
+ cl_git_pass(git_branch_move(&new_ref, original_ref, "master", 1));
+
+ git_reference_free(original_ref);
+ git_reference_free(new_ref);
+}
+
+void test_refs_branches_move__moving_a_branch_moves_related_configuration_data(void)
+{
+ git_reference *branch;
+ git_reference *new_branch;
+
+ cl_git_pass(git_branch_lookup(&branch, repo, "track-local", GIT_BRANCH_LOCAL));
+
+ assert_config_entry_existence(repo, "branch.track-local.remote", true);
+ assert_config_entry_existence(repo, "branch.track-local.merge", true);
+ assert_config_entry_existence(repo, "branch.moved.remote", false);
+ assert_config_entry_existence(repo, "branch.moved.merge", false);
+
+ cl_git_pass(git_branch_move(&new_branch, branch, "moved", 0));
+ git_reference_free(branch);
+
+ assert_config_entry_existence(repo, "branch.track-local.remote", false);
+ assert_config_entry_existence(repo, "branch.track-local.merge", false);
+ assert_config_entry_existence(repo, "branch.moved.remote", true);
+ assert_config_entry_existence(repo, "branch.moved.merge", true);
+
+ git_reference_free(new_branch);
+}
+
+void test_refs_branches_move__moving_the_branch_pointed_at_by_HEAD_updates_HEAD(void)
+{
+ git_reference *branch;
+ git_reference *new_branch;
+
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master"));
+ cl_git_pass(git_branch_move(&new_branch, branch, "master2", 0));
+ git_reference_free(branch);
+ git_reference_free(new_branch);
+
+ cl_git_pass(git_repository_head(&branch, repo));
+ cl_assert_equal_s("refs/heads/master2", git_reference_name(branch));
+ git_reference_free(branch);
+}
diff --git a/tests/refs/branches/name.c b/tests/refs/branches/name.c
new file mode 100644
index 000000000..176f836a4
--- /dev/null
+++ b/tests/refs/branches/name.c
@@ -0,0 +1,45 @@
+#include "clar_libgit2.h"
+#include "branch.h"
+
+static git_repository *repo;
+static git_reference *ref;
+
+void test_refs_branches_name__initialize(void)
+{
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+}
+
+void test_refs_branches_name__cleanup(void)
+{
+ git_reference_free(ref);
+ ref = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+}
+
+void test_refs_branches_name__can_get_local_branch_name(void)
+{
+ const char *name;
+
+ cl_git_pass(git_branch_lookup(&ref,repo,"master",GIT_BRANCH_LOCAL));
+ cl_git_pass(git_branch_name(&name,ref));
+ cl_assert_equal_s("master",name);
+}
+
+void test_refs_branches_name__can_get_remote_branch_name(void)
+{
+ const char *name;
+
+ cl_git_pass(git_branch_lookup(&ref,repo,"test/master",GIT_BRANCH_REMOTE));
+ cl_git_pass(git_branch_name(&name,ref));
+ cl_assert_equal_s("test/master",name);
+}
+
+void test_refs_branches_name__error_when_ref_is_no_branch(void)
+{
+ const char *name;
+
+ cl_git_pass(git_reference_lookup(&ref,repo,"refs/notes/fanout"));
+ cl_git_fail(git_branch_name(&name,ref));
+}
diff --git a/tests/refs/branches/remote.c b/tests/refs/branches/remote.c
new file mode 100644
index 000000000..c110adb33
--- /dev/null
+++ b/tests/refs/branches/remote.c
@@ -0,0 +1,86 @@
+#include "clar_libgit2.h"
+#include "branch.h"
+#include "remote.h"
+
+static git_repository *g_repo;
+static const char *remote_tracking_branch_name = "refs/remotes/test/master";
+static const char *expected_remote_name = "test";
+static int expected_remote_name_length;
+
+void test_refs_branches_remote__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+
+ expected_remote_name_length = (int)strlen(expected_remote_name) + 1;
+}
+
+void test_refs_branches_remote__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_branches_remote__can_get_remote_for_branch(void)
+{
+ char remotename[1024] = {0};
+
+ cl_assert_equal_i(expected_remote_name_length,
+ git_branch_remote_name(NULL, 0, g_repo, remote_tracking_branch_name));
+
+ cl_assert_equal_i(expected_remote_name_length,
+ git_branch_remote_name(remotename, expected_remote_name_length, g_repo,
+ remote_tracking_branch_name));
+
+ cl_assert_equal_s("test", remotename);
+}
+
+void test_refs_branches_remote__insufficient_buffer_returns_error(void)
+{
+ char remotename[1024] = {0};
+
+ cl_assert_equal_i(expected_remote_name_length,
+ git_branch_remote_name(NULL, 0, g_repo, remote_tracking_branch_name));
+
+ cl_git_fail_with(git_branch_remote_name(remotename,
+ expected_remote_name_length - 1, g_repo, remote_tracking_branch_name),
+ expected_remote_name_length);
+}
+
+void test_refs_branches_remote__no_matching_remote_returns_error(void)
+{
+ const char *unknown = "refs/remotes/nonexistent/master";
+
+ giterr_clear();
+ cl_git_fail_with(git_branch_remote_name(
+ NULL, 0, g_repo, unknown), GIT_ENOTFOUND);
+ cl_assert(giterr_last() != NULL);
+}
+
+void test_refs_branches_remote__local_remote_returns_error(void)
+{
+ const char *local = "refs/heads/master";
+
+ giterr_clear();
+ cl_git_fail_with(git_branch_remote_name(
+ NULL, 0, g_repo, local), GIT_ERROR);
+ cl_assert(giterr_last() != NULL);
+}
+
+void test_refs_branches_remote__ambiguous_remote_returns_error(void)
+{
+ git_remote *remote;
+
+ /* Create the remote */
+ cl_git_pass(git_remote_create(&remote, g_repo, "addtest", "http://github.com/libgit2/libgit2"));
+
+ /* Update the remote fetch spec */
+ git_remote_clear_refspecs(remote);
+ cl_git_pass(git_remote_add_fetch(remote, "refs/heads/*:refs/remotes/test/*"));
+ cl_git_pass(git_remote_save(remote));
+
+ git_remote_free(remote);
+
+ giterr_clear();
+ cl_git_fail_with(git_branch_remote_name(NULL, 0, g_repo,
+ remote_tracking_branch_name), GIT_EAMBIGUOUS);
+ cl_assert(giterr_last() != NULL);
+}
diff --git a/tests/refs/branches/upstream.c b/tests/refs/branches/upstream.c
new file mode 100644
index 000000000..69e55a0c5
--- /dev/null
+++ b/tests/refs/branches/upstream.c
@@ -0,0 +1,143 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+
+static git_repository *repo;
+static git_reference *branch, *upstream;
+
+void test_refs_branches_upstream__initialize(void)
+{
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+ branch = NULL;
+ upstream = NULL;
+}
+
+void test_refs_branches_upstream__cleanup(void)
+{
+ git_reference_free(upstream);
+ git_reference_free(branch);
+ branch = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+}
+
+void test_refs_branches_upstream__can_retrieve_the_remote_tracking_reference_of_a_local_branch(void)
+{
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master"));
+
+ cl_git_pass(git_branch_upstream(&upstream, branch));
+
+ cl_assert_equal_s("refs/remotes/test/master", git_reference_name(upstream));
+}
+
+void test_refs_branches_upstream__can_retrieve_the_local_upstream_reference_of_a_local_branch(void)
+{
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/track-local"));
+
+ cl_git_pass(git_branch_upstream(&upstream, branch));
+
+ cl_assert_equal_s("refs/heads/master", git_reference_name(upstream));
+}
+
+void test_refs_branches_upstream__cannot_retrieve_a_remote_upstream_reference_from_a_non_branch(void)
+{
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/tags/e90810b"));
+
+ cl_git_fail(git_branch_upstream(&upstream, branch));
+}
+
+void test_refs_branches_upstream__trying_to_retrieve_a_remote_tracking_reference_from_a_plain_local_branch_returns_GIT_ENOTFOUND(void)
+{
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/subtrees"));
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_branch_upstream(&upstream, branch));
+}
+
+void test_refs_branches_upstream__trying_to_retrieve_a_remote_tracking_reference_from_a_branch_with_no_fetchspec_returns_GIT_ENOTFOUND(void)
+{
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/cannot-fetch"));
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_branch_upstream(&upstream, branch));
+}
+
+static void assert_merge_and_or_remote_key_missing(git_repository *repository, const git_commit *target, const char *entry_name)
+{
+ git_reference *branch;
+
+ cl_assert_equal_i(GIT_OBJ_COMMIT, git_object_type((git_object*)target));
+ cl_git_pass(git_branch_create(&branch, repository, entry_name, (git_commit*)target, 0));
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_branch_upstream(&upstream, branch));
+
+ git_reference_free(branch);
+}
+
+void test_refs_branches_upstream__retrieve_a_remote_tracking_reference_from_a_branch_with_no_remote_returns_GIT_ENOTFOUND(void)
+{
+ git_reference *head;
+ git_repository *repository;
+ git_commit *target;
+
+ repository = cl_git_sandbox_init("testrepo.git");
+
+ cl_git_pass(git_repository_head(&head, repository));
+ cl_git_pass(git_reference_peel(((git_object **)&target), head, GIT_OBJ_COMMIT));
+ git_reference_free(head);
+
+ assert_merge_and_or_remote_key_missing(repository, target, "remoteless");
+ assert_merge_and_or_remote_key_missing(repository, target, "mergeless");
+ assert_merge_and_or_remote_key_missing(repository, target, "mergeandremoteless");
+
+ git_commit_free(target);
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_branches_upstream__set_unset_upstream(void)
+{
+ git_reference *branch;
+ git_repository *repository;
+ const char *value;
+ git_config *config;
+
+ repository = cl_git_sandbox_init("testrepo.git");
+
+ /* remote */
+ cl_git_pass(git_reference_lookup(&branch, repository, "refs/heads/test"));
+ cl_git_pass(git_branch_set_upstream(branch, "test/master"));
+
+ cl_git_pass(git_repository_config(&config, repository));
+ cl_git_pass(git_config_get_string(&value, config, "branch.test.remote"));
+ cl_assert_equal_s(value, "test");
+ cl_git_pass(git_config_get_string(&value, config, "branch.test.merge"));
+ cl_assert_equal_s(value, "refs/heads/master");
+
+ git_reference_free(branch);
+
+ /* local */
+ cl_git_pass(git_reference_lookup(&branch, repository, "refs/heads/test"));
+ cl_git_pass(git_branch_set_upstream(branch, "master"));
+
+ cl_git_pass(git_config_get_string(&value, config, "branch.test.remote"));
+ cl_assert_equal_s(value, ".");
+ cl_git_pass(git_config_get_string(&value, config, "branch.test.merge"));
+ cl_assert_equal_s(value, "refs/heads/master");
+
+ /* unset */
+ cl_git_pass(git_branch_set_upstream(branch, NULL));
+ cl_git_fail_with(git_config_get_string(&value, config, "branch.test.merge"), GIT_ENOTFOUND);
+ cl_git_fail_with(git_config_get_string(&value, config, "branch.test.remote"), GIT_ENOTFOUND);
+
+ git_reference_free(branch);
+
+ cl_git_pass(git_reference_lookup(&branch, repository, "refs/heads/master"));
+ cl_git_pass(git_branch_set_upstream(branch, NULL));
+ cl_git_fail_with(git_config_get_string(&value, config, "branch.master.merge"), GIT_ENOTFOUND);
+ cl_git_fail_with(git_config_get_string(&value, config, "branch.master.remote"), GIT_ENOTFOUND);
+
+ git_reference_free(branch);
+
+ git_config_free(config);
+ cl_git_sandbox_cleanup();
+}
diff --git a/tests/refs/branches/upstreamname.c b/tests/refs/branches/upstreamname.c
new file mode 100644
index 000000000..f05607d44
--- /dev/null
+++ b/tests/refs/branches/upstreamname.c
@@ -0,0 +1,42 @@
+#include "clar_libgit2.h"
+#include "branch.h"
+
+static git_repository *repo;
+static git_buf upstream_name;
+
+void test_refs_branches_upstreamname__initialize(void)
+{
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+ git_buf_init(&upstream_name, 0);
+}
+
+void test_refs_branches_upstreamname__cleanup(void)
+{
+ git_buf_free(&upstream_name);
+
+ git_repository_free(repo);
+ repo = NULL;
+}
+
+void test_refs_branches_upstreamname__can_retrieve_the_remote_tracking_reference_name_of_a_local_branch(void)
+{
+ cl_git_pass(git_branch_upstream__name(
+ &upstream_name, repo, "refs/heads/master"));
+
+ cl_assert_equal_s("refs/remotes/test/master", git_buf_cstr(&upstream_name));
+}
+
+void test_refs_branches_upstreamname__can_retrieve_the_local_upstream_reference_name_of_a_local_branch(void)
+{
+ cl_git_pass(git_branch_upstream__name(
+ &upstream_name, repo, "refs/heads/track-local"));
+
+ cl_assert_equal_s("refs/heads/master", git_buf_cstr(&upstream_name));
+}
+
+void test_refs_branches_upstreamname__can_return_the_size_of_thelocal_upstream_reference_name_of_a_local_branch(void)
+{
+ cl_assert_equal_i((int)strlen("refs/heads/master") + 1,
+ git_branch_upstream_name(NULL, 0, repo, "refs/heads/track-local"));
+}
diff --git a/tests/refs/crashes.c b/tests/refs/crashes.c
new file mode 100644
index 000000000..5a1885a7a
--- /dev/null
+++ b/tests/refs/crashes.c
@@ -0,0 +1,20 @@
+#include "clar_libgit2.h"
+
+void test_refs_crashes__double_free(void)
+{
+ git_repository *repo;
+ git_reference *ref, *ref2;
+ const char *REFNAME = "refs/heads/xxx";
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_reference_symbolic_create(&ref, repo, REFNAME, "refs/heads/master", 0));
+ cl_git_pass(git_reference_lookup(&ref2, repo, REFNAME));
+ cl_git_pass(git_reference_delete(ref));
+ git_reference_free(ref);
+ git_reference_free(ref2);
+
+ /* reference is gone from disk, so reloading it will fail */
+ cl_git_fail(git_reference_lookup(&ref2, repo, REFNAME));
+
+ git_repository_free(repo);
+}
diff --git a/tests/refs/create.c b/tests/refs/create.c
new file mode 100644
index 000000000..85ff05aa9
--- /dev/null
+++ b/tests/refs/create.c
@@ -0,0 +1,168 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "git2/reflog.h"
+#include "reflog.h"
+#include "ref_helpers.h"
+
+static const char *current_master_tip = "099fabac3a9ea935598528c27f866e34089c2eff";
+static const char *current_head_target = "refs/heads/master";
+
+static git_repository *g_repo;
+
+void test_refs_create__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_refs_create__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_create__symbolic(void)
+{
+ // create a new symbolic reference
+ git_reference *new_reference, *looked_up_ref, *resolved_ref;
+ git_repository *repo2;
+ git_oid id;
+
+ const char *new_head_tracker = "ANOTHER_HEAD_TRACKER";
+
+ git_oid_fromstr(&id, current_master_tip);
+
+ /* Create and write the new symbolic reference */
+ cl_git_pass(git_reference_symbolic_create(&new_reference, g_repo, new_head_tracker, current_head_target, 0));
+
+ /* Ensure the reference can be looked-up... */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, new_head_tracker));
+ cl_assert(git_reference_type(looked_up_ref) & GIT_REF_SYMBOLIC);
+ cl_assert(reference_is_packed(looked_up_ref) == 0);
+ cl_assert_equal_s(looked_up_ref->name, new_head_tracker);
+
+ /* ...peeled.. */
+ cl_git_pass(git_reference_resolve(&resolved_ref, looked_up_ref));
+ cl_assert(git_reference_type(resolved_ref) == GIT_REF_OID);
+
+ /* ...and that it points to the current master tip */
+ cl_assert(git_oid_cmp(&id, git_reference_target(resolved_ref)) == 0);
+ git_reference_free(looked_up_ref);
+ git_reference_free(resolved_ref);
+
+ /* Similar test with a fresh new repository */
+ cl_git_pass(git_repository_open(&repo2, "testrepo"));
+
+ cl_git_pass(git_reference_lookup(&looked_up_ref, repo2, new_head_tracker));
+ cl_git_pass(git_reference_resolve(&resolved_ref, looked_up_ref));
+ cl_assert(git_oid_cmp(&id, git_reference_target(resolved_ref)) == 0);
+
+ git_repository_free(repo2);
+
+ git_reference_free(new_reference);
+ git_reference_free(looked_up_ref);
+ git_reference_free(resolved_ref);
+}
+
+void test_refs_create__deep_symbolic(void)
+{
+ // create a deep symbolic reference
+ git_reference *new_reference, *looked_up_ref, *resolved_ref;
+ git_oid id;
+
+ const char *new_head_tracker = "deep/rooted/tracker";
+
+ git_oid_fromstr(&id, current_master_tip);
+
+ cl_git_pass(git_reference_symbolic_create(&new_reference, g_repo, new_head_tracker, current_head_target, 0));
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, new_head_tracker));
+ cl_git_pass(git_reference_resolve(&resolved_ref, looked_up_ref));
+ cl_assert(git_oid_cmp(&id, git_reference_target(resolved_ref)) == 0);
+
+ git_reference_free(new_reference);
+ git_reference_free(looked_up_ref);
+ git_reference_free(resolved_ref);
+}
+
+void test_refs_create__oid(void)
+{
+ // create a new OID reference
+ git_reference *new_reference, *looked_up_ref;
+ git_repository *repo2;
+ git_oid id;
+
+ const char *new_head = "refs/heads/new-head";
+
+ git_oid_fromstr(&id, current_master_tip);
+
+ /* Create and write the new object id reference */
+ cl_git_pass(git_reference_create(&new_reference, g_repo, new_head, &id, 0));
+
+ /* Ensure the reference can be looked-up... */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, new_head));
+ cl_assert(git_reference_type(looked_up_ref) & GIT_REF_OID);
+ cl_assert(reference_is_packed(looked_up_ref) == 0);
+ cl_assert_equal_s(looked_up_ref->name, new_head);
+
+ /* ...and that it points to the current master tip */
+ cl_assert(git_oid_cmp(&id, git_reference_target(looked_up_ref)) == 0);
+ git_reference_free(looked_up_ref);
+
+ /* Similar test with a fresh new repository */
+ cl_git_pass(git_repository_open(&repo2, "testrepo"));
+
+ cl_git_pass(git_reference_lookup(&looked_up_ref, repo2, new_head));
+ cl_assert(git_oid_cmp(&id, git_reference_target(looked_up_ref)) == 0);
+
+ git_repository_free(repo2);
+
+ git_reference_free(new_reference);
+ git_reference_free(looked_up_ref);
+}
+
+void test_refs_create__oid_unknown(void)
+{
+ // Can not create a new OID reference which targets at an unknown id
+ git_reference *new_reference, *looked_up_ref;
+ git_oid id;
+
+ const char *new_head = "refs/heads/new-head";
+
+ git_oid_fromstr(&id, "deadbeef3f795b2b4353bcce3a527ad0a4f7f644");
+
+ /* Create and write the new object id reference */
+ cl_git_fail(git_reference_create(&new_reference, g_repo, new_head, &id, 0));
+
+ /* Ensure the reference can't be looked-up... */
+ cl_git_fail(git_reference_lookup(&looked_up_ref, g_repo, new_head));
+}
+
+void test_refs_create__propagate_eexists(void)
+{
+ int error;
+ git_oid oid;
+ git_reference *ref;
+
+ /* Make sure it works for oid and for symbolic both */
+ git_oid_fromstr(&oid, current_master_tip);
+ error = git_reference_create(&ref, g_repo, current_head_target, &oid, false);
+ cl_assert(error == GIT_EEXISTS);
+
+ error = git_reference_symbolic_create(&ref, g_repo, "HEAD", current_head_target, false);
+ cl_assert(error == GIT_EEXISTS);
+}
+
+void test_refs_create__creating_a_reference_with_an_invalid_name_returns_EINVALIDSPEC(void)
+{
+ git_reference *new_reference;
+ git_oid id;
+
+ const char *name = "refs/heads/inv@{id";
+
+ git_oid_fromstr(&id, current_master_tip);
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_create(
+ &new_reference, g_repo, name, &id, 0));
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_symbolic_create(
+ &new_reference, g_repo, name, current_head_target, 0));
+}
diff --git a/tests/refs/delete.c b/tests/refs/delete.c
new file mode 100644
index 000000000..973768aeb
--- /dev/null
+++ b/tests/refs/delete.c
@@ -0,0 +1,93 @@
+#include "clar_libgit2.h"
+
+#include "fileops.h"
+#include "git2/reflog.h"
+#include "git2/refdb.h"
+#include "reflog.h"
+#include "ref_helpers.h"
+
+static const char *packed_test_head_name = "refs/heads/packed-test";
+static const char *current_master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750";
+
+static git_repository *g_repo;
+
+
+
+void test_refs_delete__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_refs_delete__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+
+
+void test_refs_delete__packed_loose(void)
+{
+ // deleting a ref which is both packed and loose should remove both tracks in the filesystem
+ git_reference *looked_up_ref, *another_looked_up_ref;
+ git_buf temp_path = GIT_BUF_INIT;
+
+ /* Ensure the loose reference exists on the file system */
+ cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), packed_test_head_name));
+ cl_assert(git_path_exists(temp_path.ptr));
+
+ /* Lookup the reference */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_test_head_name));
+
+ /* Ensure it's the loose version that has been found */
+ cl_assert(reference_is_packed(looked_up_ref) == 0);
+
+ /* Now that the reference is deleted... */
+ cl_git_pass(git_reference_delete(looked_up_ref));
+ git_reference_free(looked_up_ref);
+
+ /* Looking up the reference once again should not retrieve it */
+ cl_git_fail(git_reference_lookup(&another_looked_up_ref, g_repo, packed_test_head_name));
+
+ /* Ensure the loose reference doesn't exist any longer on the file system */
+ cl_assert(!git_path_exists(temp_path.ptr));
+
+ git_reference_free(another_looked_up_ref);
+ git_buf_free(&temp_path);
+}
+
+void test_refs_delete__packed_only(void)
+{
+ // can delete a just packed reference
+ git_reference *ref;
+ git_refdb *refdb;
+ git_oid id;
+ const char *new_ref = "refs/heads/new_ref";
+
+ git_oid_fromstr(&id, current_master_tip);
+
+ /* Create and write the new object id reference */
+ cl_git_pass(git_reference_create(&ref, g_repo, new_ref, &id, 0));
+ git_reference_free(ref);
+
+ /* Lookup the reference */
+ cl_git_pass(git_reference_lookup(&ref, g_repo, new_ref));
+
+ /* Ensure it's a loose reference */
+ cl_assert(reference_is_packed(ref) == 0);
+
+ /* Pack all existing references */
+ cl_git_pass(git_repository_refdb(&refdb, g_repo));
+ cl_git_pass(git_refdb_compress(refdb));
+
+ /* Reload the reference from disk */
+ git_reference_free(ref);
+ cl_git_pass(git_reference_lookup(&ref, g_repo, new_ref));
+
+ /* Ensure it's a packed reference */
+ cl_assert(reference_is_packed(ref) == 1);
+
+ /* This should pass */
+ cl_git_pass(git_reference_delete(ref));
+ git_reference_free(ref);
+ git_refdb_free(refdb);
+}
diff --git a/tests/refs/foreachglob.c b/tests/refs/foreachglob.c
new file mode 100644
index 000000000..2c458082f
--- /dev/null
+++ b/tests/refs/foreachglob.c
@@ -0,0 +1,95 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+
+static git_repository *repo;
+static git_reference *fake_remote;
+
+void test_refs_foreachglob__initialize(void)
+{
+ git_oid id;
+
+ cl_fixture_sandbox("testrepo.git");
+ cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+
+ cl_git_pass(git_oid_fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
+ cl_git_pass(git_reference_create(&fake_remote, repo, "refs/remotes/nulltoken/master", &id, 0));
+}
+
+void test_refs_foreachglob__cleanup(void)
+{
+ git_reference_free(fake_remote);
+ fake_remote = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+
+ cl_fixture_cleanup("testrepo.git");
+}
+
+static int count_cb(const char *reference_name, void *payload)
+{
+ int *count = (int *)payload;
+
+ GIT_UNUSED(reference_name);
+
+ (*count)++;
+
+ return 0;
+}
+
+static void assert_retrieval(const char *glob, int expected_count)
+{
+ int count = 0;
+
+ cl_git_pass(git_reference_foreach_glob(repo, glob, count_cb, &count));
+
+ cl_assert_equal_i(expected_count, count);
+}
+
+void test_refs_foreachglob__retrieve_all_refs(void)
+{
+ /* 12 heads (including one packed head) + 1 note + 2 remotes + 7 tags */
+ assert_retrieval("*", 22);
+}
+
+void test_refs_foreachglob__retrieve_remote_branches(void)
+{
+ assert_retrieval("refs/remotes/*", 2);
+}
+
+void test_refs_foreachglob__retrieve_local_branches(void)
+{
+ assert_retrieval("refs/heads/*", 12);
+}
+
+void test_refs_foreachglob__retrieve_partially_named_references(void)
+{
+ /*
+ * refs/heads/packed-test, refs/heads/test
+ * refs/remotes/test/master, refs/tags/test
+ */
+
+ assert_retrieval("*test*", 4);
+}
+
+
+static int interrupt_cb(const char *reference_name, void *payload)
+{
+ int *count = (int *)payload;
+
+ GIT_UNUSED(reference_name);
+
+ (*count)++;
+
+ return (*count == 11);
+}
+
+void test_refs_foreachglob__can_cancel(void)
+{
+ int count = 0;
+
+ cl_assert_equal_i(GIT_EUSER, git_reference_foreach_glob(
+ repo, "*", interrupt_cb, &count) );
+
+ cl_assert_equal_i(11, count);
+}
diff --git a/tests/refs/isvalidname.c b/tests/refs/isvalidname.c
new file mode 100644
index 000000000..65c70ba4d
--- /dev/null
+++ b/tests/refs/isvalidname.c
@@ -0,0 +1,31 @@
+#include "clar_libgit2.h"
+
+void test_refs_isvalidname__can_detect_invalid_formats(void)
+{
+ cl_assert_equal_i(false, git_reference_is_valid_name("refs/tags/0.17.0^{}"));
+ cl_assert_equal_i(false, git_reference_is_valid_name("TWO/LEVELS"));
+ cl_assert_equal_i(false, git_reference_is_valid_name("ONE.LEVEL"));
+ cl_assert_equal_i(false, git_reference_is_valid_name("HEAD/"));
+ cl_assert_equal_i(false, git_reference_is_valid_name("NO_TRAILING_UNDERSCORE_"));
+ cl_assert_equal_i(false, git_reference_is_valid_name("_NO_LEADING_UNDERSCORE"));
+ cl_assert_equal_i(false, git_reference_is_valid_name("HEAD/aa"));
+ cl_assert_equal_i(false, git_reference_is_valid_name("lower_case"));
+ cl_assert_equal_i(false, git_reference_is_valid_name("/stupid/name/master"));
+ cl_assert_equal_i(false, git_reference_is_valid_name("/"));
+ cl_assert_equal_i(false, git_reference_is_valid_name("//"));
+ cl_assert_equal_i(false, git_reference_is_valid_name(""));
+ cl_assert_equal_i(false, git_reference_is_valid_name("refs/heads/sub.lock/webmatrix"));
+}
+
+void test_refs_isvalidname__wont_hopefully_choke_on_valid_formats(void)
+{
+ cl_assert_equal_i(true, git_reference_is_valid_name("refs/tags/0.17.0"));
+ cl_assert_equal_i(true, git_reference_is_valid_name("refs/LEVELS"));
+ cl_assert_equal_i(true, git_reference_is_valid_name("HEAD"));
+ cl_assert_equal_i(true, git_reference_is_valid_name("ONE_LEVEL"));
+ cl_assert_equal_i(true, git_reference_is_valid_name("refs/stash"));
+ cl_assert_equal_i(true, git_reference_is_valid_name("refs/remotes/origin/bim_with_3d@11296"));
+ cl_assert_equal_i(true, git_reference_is_valid_name("refs/master{yesterday"));
+ cl_assert_equal_i(true, git_reference_is_valid_name("refs/master}yesterday"));
+ cl_assert_equal_i(true, git_reference_is_valid_name("refs/master{yesterday}"));
+}
diff --git a/tests/refs/iterator.c b/tests/refs/iterator.c
new file mode 100644
index 000000000..266410fdf
--- /dev/null
+++ b/tests/refs/iterator.c
@@ -0,0 +1,97 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+#include "vector.h"
+
+static git_repository *repo;
+
+void test_refs_iterator__initialize(void)
+{
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+}
+
+void test_refs_iterator__cleanup(void)
+{
+ git_repository_free(repo);
+}
+
+static const char *refnames[] = {
+ "refs/heads/br2",
+ "refs/heads/cannot-fetch",
+ "refs/heads/chomped",
+ "refs/heads/haacked",
+ "refs/heads/master",
+ "refs/heads/not-good",
+ "refs/heads/packed",
+ "refs/heads/packed-test",
+ "refs/heads/subtrees",
+ "refs/heads/test",
+ "refs/heads/track-local",
+ "refs/heads/trailing",
+ "refs/notes/fanout",
+ "refs/remotes/test/master",
+ "refs/tags/annotated_tag_to_blob",
+ "refs/tags/e90810b",
+ "refs/tags/hard_tag",
+ "refs/tags/point_to_blob",
+ "refs/tags/taggerless",
+ "refs/tags/test",
+ "refs/tags/wrapped_tag",
+};
+
+static int refcmp_cb(const void *a, const void *b)
+{
+ const git_reference *refa = (const git_reference *)a;
+ const git_reference *refb = (const git_reference *)b;
+
+ return strcmp(refa->name, refb->name);
+}
+
+void test_refs_iterator__list(void)
+{
+ git_reference_iterator *iter;
+ git_vector output;
+ git_reference *ref;
+ int error;
+ size_t i;
+
+ cl_git_pass(git_vector_init(&output, 32, &refcmp_cb));
+ cl_git_pass(git_reference_iterator_new(&iter, repo));
+
+ do {
+ error = git_reference_next(&ref, iter);
+ cl_assert(error == 0 || error == GIT_ITEROVER);
+ if (error != GIT_ITEROVER) {
+ cl_git_pass(git_vector_insert(&output, ref));
+ }
+ } while (!error);
+
+ git_reference_iterator_free(iter);
+ cl_assert_equal_sz(output.length, ARRAY_SIZE(refnames));
+
+ git_vector_sort(&output);
+
+ git_vector_foreach(&output, i, ref) {
+ cl_assert_equal_s(ref->name, refnames[i]);
+ git_reference_free(ref);
+ }
+
+ git_vector_free(&output);
+}
+
+void test_refs_iterator__empty(void)
+{
+ git_reference_iterator *iter;
+ git_odb *odb;
+ git_reference *ref;
+ git_repository *empty;
+
+ cl_git_pass(git_odb_new(&odb));
+ cl_git_pass(git_repository_wrap_odb(&empty, odb));
+
+ cl_git_pass(git_reference_iterator_new(&iter, empty));
+ cl_assert_equal_i(GIT_ITEROVER, git_reference_next(&ref, iter));
+
+ git_reference_iterator_free(iter);
+ git_odb_free(odb);
+ git_repository_free(empty);
+}
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/refs/listall.c b/tests/refs/listall.c
new file mode 100644
index 000000000..c696fbb2e
--- /dev/null
+++ b/tests/refs/listall.c
@@ -0,0 +1,47 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+
+static git_repository *repo;
+static git_strarray ref_list;
+
+static void ensure_no_refname_starts_with_a_forward_slash(const char *path)
+{
+ size_t i;
+
+ cl_git_pass(git_repository_open(&repo, path));
+ cl_git_pass(git_reference_list(&ref_list, repo));
+
+ cl_assert(ref_list.count > 0);
+
+ for (i = 0; i < ref_list.count; i++)
+ cl_assert(git__prefixcmp(ref_list.strings[i], "/") != 0);
+
+ git_strarray_free(&ref_list);
+ git_repository_free(repo);
+}
+
+void test_refs_listall__from_repository_opened_through_workdir_path(void)
+{
+ cl_fixture_sandbox("status");
+ cl_git_pass(p_rename("status/.gitted", "status/.git"));
+
+ ensure_no_refname_starts_with_a_forward_slash("status");
+
+ cl_fixture_cleanup("status");
+}
+
+void test_refs_listall__from_repository_opened_through_gitdir_path(void)
+{
+ ensure_no_refname_starts_with_a_forward_slash(cl_fixture("testrepo.git"));
+}
+
+void test_refs_listall__from_repository_with_no_trailing_newline(void)
+{
+ cl_git_pass(git_repository_open(&repo, cl_fixture("bad_tag.git")));
+ cl_git_pass(git_reference_list(&ref_list, repo));
+
+ cl_assert(ref_list.count > 0);
+
+ git_strarray_free(&ref_list);
+ git_repository_free(repo);
+}
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/refs/normalize.c b/tests/refs/normalize.c
new file mode 100644
index 000000000..7f313ef38
--- /dev/null
+++ b/tests/refs/normalize.c
@@ -0,0 +1,403 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "git2/reflog.h"
+#include "reflog.h"
+
+// Helpers
+static void ensure_refname_normalized(
+ unsigned int flags,
+ const char *input_refname,
+ const char *expected_refname)
+{
+ char buffer_out[GIT_REFNAME_MAX];
+
+ cl_git_pass(git_reference_normalize_name(buffer_out, sizeof(buffer_out), input_refname, flags));
+
+ cl_assert_equal_s(expected_refname, buffer_out);
+}
+
+static void ensure_refname_invalid(unsigned int flags, const char *input_refname)
+{
+ char buffer_out[GIT_REFNAME_MAX];
+
+ cl_assert_equal_i(
+ GIT_EINVALIDSPEC,
+ git_reference_normalize_name(buffer_out, sizeof(buffer_out), input_refname, flags));
+}
+
+void test_refs_normalize__can_normalize_a_direct_reference_name(void)
+{
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_NORMAL, "refs/dummy/a", "refs/dummy/a");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_NORMAL, "refs/stash", "refs/stash");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_NORMAL, "refs/tags/a", "refs/tags/a");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_NORMAL, "refs/heads/a/b", "refs/heads/a/b");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_NORMAL, "refs/heads/a./b", "refs/heads/a./b");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_NORMAL, "refs/heads/v@ation", "refs/heads/v@ation");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_NORMAL, "refs///heads///a", "refs/heads/a");
+}
+
+void test_refs_normalize__cannot_normalize_any_direct_reference_name(void)
+{
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "a");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "/a");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "//a");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "/refs/heads/a/");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "refs/heads/a/");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "refs/heads/a.");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "refs/heads/a.lock");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "refs/heads/foo?bar");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "refs/heads\foo");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_NORMAL, "refs/heads/v@ation", "refs/heads/v@ation");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_NORMAL, "refs///heads///a", "refs/heads/a");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "refs/heads/.a/b");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "refs/heads/foo/../bar");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "refs/heads/foo..bar");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "refs/heads/./foo");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "refs/heads/v@{ation");
+}
+
+void test_refs_normalize__symbolic(void)
+{
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "heads\foo");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "/");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "///");
+
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "ALL_CAPS_AND_UNDERSCORES", "ALL_CAPS_AND_UNDERSCORES");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/MixedCasing", "refs/MixedCasing");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs///heads///a", "refs/heads/a");
+
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "HEAD", "HEAD");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "MERGE_HEAD", "MERGE_HEAD");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "FETCH_HEAD", "FETCH_HEAD");
+}
+
+/* Ported from JGit, BSD licence.
+ * See https://github.com/spearce/JGit/commit/e4bf8f6957bbb29362575d641d1e77a02d906739
+ *
+ * Copyright (C) 2009, Google Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+void test_refs_normalize__jgit_suite(void)
+{
+ // tests borrowed from JGit
+
+/* EmptyString */
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "/");
+
+/* MustHaveTwoComponents */
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_NORMAL, "master");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_NORMAL, "heads/master", "heads/master");
+
+/* ValidHead */
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master", "refs/heads/master");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/pu", "refs/heads/pu");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/z", "refs/heads/z");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/FoO", "refs/heads/FoO");
+
+/* ValidTag */
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/tags/v1.0", "refs/tags/v1.0");
+
+/* NoLockSuffix */
+ ensure_refname_invalid(GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master.lock");
+
+/* NoDirectorySuffix */
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master/");
+
+/* NoSpace */
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/i haz space");
+
+/* NoAsciiControlCharacters */
+ {
+ char c;
+ char buffer[GIT_REFNAME_MAX];
+ for (c = '\1'; c < ' '; c++) {
+ strncpy(buffer, "refs/heads/mast", 15);
+ strncpy(buffer + 15, (const char *)&c, 1);
+ strncpy(buffer + 16, "er", 2);
+ buffer[18 - 1] = '\0';
+ ensure_refname_invalid(GIT_REF_FORMAT_ALLOW_ONELEVEL, buffer);
+ }
+ }
+
+/* NoBareDot */
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/.");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/..");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/./master");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/../master");
+
+/* NoLeadingOrTrailingDot */
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, ".");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/.bar");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/..bar");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/bar.");
+
+/* ContainsDot */
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/m.a.s.t.e.r", "refs/heads/m.a.s.t.e.r");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master..pu");
+
+/* NoMagicRefCharacters */
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master^");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/^master");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "^refs/heads/master");
+
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master~");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/~master");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "~refs/heads/master");
+
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master:");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/:master");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, ":refs/heads/master");
+
+/* ShellGlob */
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master?");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/?master");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "?refs/heads/master");
+
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master[");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/[master");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "[refs/heads/master");
+
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master*");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/*master");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "*refs/heads/master");
+
+/* ValidSpecialCharacters */
+ ensure_refname_normalized
+ (GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/!", "refs/heads/!");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/\"", "refs/heads/\"");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/#", "refs/heads/#");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/$", "refs/heads/$");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/%", "refs/heads/%");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/&", "refs/heads/&");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/'", "refs/heads/'");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/(", "refs/heads/(");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/)", "refs/heads/)");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/+", "refs/heads/+");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/,", "refs/heads/,");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/-", "refs/heads/-");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/;", "refs/heads/;");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/<", "refs/heads/<");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/=", "refs/heads/=");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/>", "refs/heads/>");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/@", "refs/heads/@");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/]", "refs/heads/]");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/_", "refs/heads/_");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/`", "refs/heads/`");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/{", "refs/heads/{");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/|", "refs/heads/|");
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/}", "refs/heads/}");
+
+ // This is valid on UNIX, but not on Windows
+ // hence we make in invalid due to non-portability
+ //
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/\\");
+
+/* UnicodeNames */
+ /*
+ * Currently this fails.
+ * ensure_refname_normalized(GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/\u00e5ngstr\u00f6m", "refs/heads/\u00e5ngstr\u00f6m");
+ */
+
+/* RefLogQueryIsValidRef */
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master@{1}");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_ALLOW_ONELEVEL, "refs/heads/master@{1.hour.ago}");
+}
+
+void test_refs_normalize__buffer_has_to_be_big_enough_to_hold_the_normalized_version(void)
+{
+ char buffer_out[21];
+
+ cl_git_pass(git_reference_normalize_name(
+ buffer_out, 21, "refs//heads///long///name", GIT_REF_FORMAT_NORMAL));
+ cl_git_fail(git_reference_normalize_name(
+ buffer_out, 20, "refs//heads///long///name", GIT_REF_FORMAT_NORMAL));
+}
+
+#define ONE_LEVEL_AND_REFSPEC \
+ GIT_REF_FORMAT_ALLOW_ONELEVEL \
+ | GIT_REF_FORMAT_REFSPEC_PATTERN
+
+void test_refs_normalize__refspec_pattern(void)
+{
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_REFSPEC_PATTERN, "heads/*foo/bar");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_REFSPEC_PATTERN, "heads/foo*/bar");
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_REFSPEC_PATTERN, "heads/f*o/bar");
+
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_REFSPEC_PATTERN, "foo");
+ ensure_refname_normalized(
+ ONE_LEVEL_AND_REFSPEC, "FOO", "FOO");
+
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_REFSPEC_PATTERN, "foo/bar", "foo/bar");
+ ensure_refname_normalized(
+ ONE_LEVEL_AND_REFSPEC, "foo/bar", "foo/bar");
+
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_REFSPEC_PATTERN, "*/foo", "*/foo");
+ ensure_refname_normalized(
+ ONE_LEVEL_AND_REFSPEC, "*/foo", "*/foo");
+
+ ensure_refname_normalized(
+ GIT_REF_FORMAT_REFSPEC_PATTERN, "foo/*/bar", "foo/*/bar");
+ ensure_refname_normalized(
+ ONE_LEVEL_AND_REFSPEC, "foo/*/bar", "foo/*/bar");
+
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_REFSPEC_PATTERN, "*");
+ ensure_refname_normalized(
+ ONE_LEVEL_AND_REFSPEC, "*", "*");
+
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_REFSPEC_PATTERN, "foo/*/*");
+ ensure_refname_invalid(
+ ONE_LEVEL_AND_REFSPEC, "foo/*/*");
+
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_REFSPEC_PATTERN, "*/foo/*");
+ ensure_refname_invalid(
+ ONE_LEVEL_AND_REFSPEC, "*/foo/*");
+
+ ensure_refname_invalid(
+ GIT_REF_FORMAT_REFSPEC_PATTERN, "*/*/foo");
+ ensure_refname_invalid(
+ ONE_LEVEL_AND_REFSPEC, "*/*/foo");
+}
diff --git a/tests/refs/overwrite.c b/tests/refs/overwrite.c
new file mode 100644
index 000000000..ebe72069c
--- /dev/null
+++ b/tests/refs/overwrite.c
@@ -0,0 +1,136 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "git2/reflog.h"
+#include "reflog.h"
+
+static const char *ref_name = "refs/heads/other";
+static const char *ref_master_name = "refs/heads/master";
+static const char *ref_branch_name = "refs/heads/branch";
+static const char *ref_test_name = "refs/heads/test";
+
+static git_repository *g_repo;
+
+void test_refs_overwrite__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_refs_overwrite__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_overwrite__symbolic(void)
+{
+ // Overwrite an existing symbolic reference
+ git_reference *ref, *branch_ref;
+
+ /* The target needds to exist and we need to check the name has changed */
+ cl_git_pass(git_reference_symbolic_create(&branch_ref, g_repo, ref_branch_name, ref_master_name, 0));
+ cl_git_pass(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_branch_name, 0));
+ git_reference_free(ref);
+
+ /* Ensure it points to the right place*/
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_name));
+ cl_assert(git_reference_type(ref) & GIT_REF_SYMBOLIC);
+ cl_assert_equal_s(git_reference_symbolic_target(ref), ref_branch_name);
+ git_reference_free(ref);
+
+ /* Ensure we can't create it unless we force it to */
+ cl_git_fail(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 0));
+ cl_git_pass(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 1));
+ git_reference_free(ref);
+
+ /* Ensure it points to the right place */
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_name));
+ cl_assert(git_reference_type(ref) & GIT_REF_SYMBOLIC);
+ cl_assert_equal_s(git_reference_symbolic_target(ref), ref_master_name);
+
+ git_reference_free(ref);
+ git_reference_free(branch_ref);
+}
+
+void test_refs_overwrite__object_id(void)
+{
+ // Overwrite an existing object id reference
+ git_reference *ref;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
+ cl_assert(git_reference_type(ref) & GIT_REF_OID);
+ git_oid_cpy(&id, git_reference_target(ref));
+ git_reference_free(ref);
+
+ /* Create it */
+ cl_git_pass(git_reference_create(&ref, g_repo, ref_name, &id, 0));
+ git_reference_free(ref);
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_test_name));
+ cl_assert(git_reference_type(ref) & GIT_REF_OID);
+ git_oid_cpy(&id, git_reference_target(ref));
+ git_reference_free(ref);
+
+ /* Ensure we can't overwrite unless we force it */
+ cl_git_fail(git_reference_create(&ref, g_repo, ref_name, &id, 0));
+ cl_git_pass(git_reference_create(&ref, g_repo, ref_name, &id, 1));
+ git_reference_free(ref);
+
+ /* Ensure it has been overwritten */
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_name));
+ cl_assert(!git_oid_cmp(&id, git_reference_target(ref)));
+
+ git_reference_free(ref);
+}
+
+void test_refs_overwrite__object_id_with_symbolic(void)
+{
+ // Overwrite an existing object id reference with a symbolic one
+ git_reference *ref;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
+ cl_assert(git_reference_type(ref) & GIT_REF_OID);
+ git_oid_cpy(&id, git_reference_target(ref));
+ git_reference_free(ref);
+
+ cl_git_pass(git_reference_create(&ref, g_repo, ref_name, &id, 0));
+ git_reference_free(ref);
+ cl_git_fail(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 0));
+ cl_git_pass(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 1));
+ git_reference_free(ref);
+
+ /* Ensure it points to the right place */
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_name));
+ cl_assert(git_reference_type(ref) & GIT_REF_SYMBOLIC);
+ cl_assert_equal_s(git_reference_symbolic_target(ref), ref_master_name);
+
+ git_reference_free(ref);
+}
+
+void test_refs_overwrite__symbolic_with_object_id(void)
+{
+ // Overwrite an existing symbolic reference with an object id one
+ git_reference *ref;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
+ cl_assert(git_reference_type(ref) & GIT_REF_OID);
+ git_oid_cpy(&id, git_reference_target(ref));
+ git_reference_free(ref);
+
+ /* Create the symbolic ref */
+ cl_git_pass(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 0));
+ git_reference_free(ref);
+ /* It shouldn't overwrite unless we tell it to */
+ cl_git_fail(git_reference_create(&ref, g_repo, ref_name, &id, 0));
+ cl_git_pass(git_reference_create(&ref, g_repo, ref_name, &id, 1));
+ git_reference_free(ref);
+
+ /* Ensure it points to the right place */
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_name));
+ cl_assert(git_reference_type(ref) & GIT_REF_OID);
+ cl_assert(!git_oid_cmp(git_reference_target(ref), &id));
+
+ git_reference_free(ref);
+}
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/refs/peel.c b/tests/refs/peel.c
new file mode 100644
index 000000000..f2fb6e259
--- /dev/null
+++ b/tests/refs/peel.c
@@ -0,0 +1,119 @@
+#include "clar_libgit2.h"
+
+static git_repository *g_repo;
+static git_repository *g_peel_repo;
+
+void test_refs_peel__initialize(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_repository_open(&g_peel_repo, cl_fixture("peeled.git")));
+}
+
+void test_refs_peel__cleanup(void)
+{
+ git_repository_free(g_repo);
+ g_repo = NULL;
+ git_repository_free(g_peel_repo);
+ g_peel_repo = NULL;
+}
+
+static void assert_peel_generic(
+ git_repository *repo,
+ const char *ref_name,
+ git_otype requested_type,
+ const char* expected_sha,
+ git_otype expected_type)
+{
+ git_oid expected_oid;
+ git_reference *ref;
+ git_object *peeled;
+
+ cl_git_pass(git_reference_lookup(&ref, repo, ref_name));
+
+ cl_git_pass(git_reference_peel(&peeled, ref, requested_type));
+
+ cl_git_pass(git_oid_fromstr(&expected_oid, expected_sha));
+ cl_assert_equal_i(0, git_oid_cmp(&expected_oid, git_object_id(peeled)));
+
+ cl_assert_equal_i(expected_type, git_object_type(peeled));
+
+ git_object_free(peeled);
+ git_reference_free(ref);
+}
+
+static void assert_peel(
+ const char *ref_name,
+ git_otype requested_type,
+ const char* expected_sha,
+ git_otype expected_type)
+{
+ assert_peel_generic(g_repo, ref_name, requested_type,
+ expected_sha, expected_type);
+}
+
+static void assert_peel_error(int error, const char *ref_name, git_otype requested_type)
+{
+ git_reference *ref;
+ git_object *peeled;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_name));
+
+ cl_assert_equal_i(error, git_reference_peel(&peeled, ref, requested_type));
+
+ git_reference_free(ref);
+}
+
+void test_refs_peel__can_peel_a_tag(void)
+{
+ assert_peel("refs/tags/test", GIT_OBJ_TAG,
+ "b25fa35b38051e4ae45d4222e795f9df2e43f1d1", GIT_OBJ_TAG);
+ assert_peel("refs/tags/test", GIT_OBJ_COMMIT,
+ "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJ_COMMIT);
+ assert_peel("refs/tags/test", GIT_OBJ_TREE,
+ "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_TREE);
+ assert_peel("refs/tags/point_to_blob", GIT_OBJ_BLOB,
+ "1385f264afb75a56a5bec74243be9b367ba4ca08", GIT_OBJ_BLOB);
+}
+
+void test_refs_peel__can_peel_a_branch(void)
+{
+ assert_peel("refs/heads/master", GIT_OBJ_COMMIT,
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OBJ_COMMIT);
+ assert_peel("refs/heads/master", GIT_OBJ_TREE,
+ "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162", GIT_OBJ_TREE);
+}
+
+void test_refs_peel__can_peel_a_symbolic_reference(void)
+{
+ assert_peel("HEAD", GIT_OBJ_COMMIT,
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OBJ_COMMIT);
+ assert_peel("HEAD", GIT_OBJ_TREE,
+ "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162", GIT_OBJ_TREE);
+}
+
+void test_refs_peel__cannot_peel_into_a_non_existing_target(void)
+{
+ assert_peel_error(GIT_ENOTFOUND, "refs/tags/point_to_blob", GIT_OBJ_TAG);
+}
+
+void test_refs_peel__can_peel_into_any_non_tag_object(void)
+{
+ assert_peel("refs/heads/master", GIT_OBJ_ANY,
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OBJ_COMMIT);
+ assert_peel("refs/tags/point_to_blob", GIT_OBJ_ANY,
+ "1385f264afb75a56a5bec74243be9b367ba4ca08", GIT_OBJ_BLOB);
+ assert_peel("refs/tags/test", GIT_OBJ_ANY,
+ "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJ_COMMIT);
+}
+
+void test_refs_peel__can_peel_fully_peeled_packed_refs(void)
+{
+ assert_peel_generic(g_peel_repo,
+ "refs/tags/tag-inside-tags", GIT_OBJ_ANY,
+ "0df1a5865c8abfc09f1f2182e6a31be550e99f07",
+ GIT_OBJ_COMMIT);
+ assert_peel_generic(g_peel_repo,
+ "refs/foo/tag-outside-tags", GIT_OBJ_ANY,
+ "0df1a5865c8abfc09f1f2182e6a31be550e99f07",
+ GIT_OBJ_COMMIT);
+}
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/refs/ref_helpers.c b/tests/refs/ref_helpers.c
new file mode 100644
index 000000000..7676e65a7
--- /dev/null
+++ b/tests/refs/ref_helpers.c
@@ -0,0 +1,25 @@
+#include "git2/repository.h"
+#include "git2/refs.h"
+#include "common.h"
+#include "util.h"
+#include "buffer.h"
+#include "path.h"
+
+int reference_is_packed(git_reference *ref)
+{
+ git_buf ref_path = GIT_BUF_INIT;
+ int packed;
+
+ assert(ref);
+
+ if (git_buf_joinpath(&ref_path,
+ git_repository_path(git_reference_owner(ref)),
+ git_reference_name(ref)) < 0)
+ return -1;
+
+ packed = !git_path_isfile(ref_path.ptr);
+
+ git_buf_free(&ref_path);
+
+ return packed;
+}
diff --git a/tests/refs/ref_helpers.h b/tests/refs/ref_helpers.h
new file mode 100644
index 000000000..0ef55bfce
--- /dev/null
+++ b/tests/refs/ref_helpers.h
@@ -0,0 +1 @@
+int reference_is_packed(git_reference *ref);
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/refs/rename.c b/tests/refs/rename.c
new file mode 100644
index 000000000..543bc4d62
--- /dev/null
+++ b/tests/refs/rename.c
@@ -0,0 +1,367 @@
+#include "clar_libgit2.h"
+
+#include "fileops.h"
+#include "git2/reflog.h"
+#include "reflog.h"
+#include "refs.h"
+#include "ref_helpers.h"
+
+static const char *loose_tag_ref_name = "refs/tags/e90810b";
+static const char *packed_head_name = "refs/heads/packed";
+static const char *packed_test_head_name = "refs/heads/packed-test";
+static const char *ref_one_name = "refs/heads/one/branch";
+static const char *ref_one_name_new = "refs/heads/two/branch";
+static const char *ref_two_name = "refs/heads/two";
+static const char *ref_master_name = "refs/heads/master";
+static const char *ref_two_name_new = "refs/heads/two/two";
+
+static git_repository *g_repo;
+
+
+
+void test_refs_rename__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_refs_rename__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+
+
+void test_refs_rename__loose(void)
+{
+ // rename a loose reference
+ git_reference *looked_up_ref, *new_ref, *another_looked_up_ref;
+ git_buf temp_path = GIT_BUF_INIT;
+ const char *new_name = "refs/tags/Nemo/knows/refs.kung-fu";
+
+ /* Ensure the ref doesn't exist on the file system */
+ cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), new_name));
+ cl_assert(!git_path_exists(temp_path.ptr));
+
+ /* Retrieval of the reference to rename */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, loose_tag_ref_name));
+
+ /* ... which is indeed loose */
+ cl_assert(reference_is_packed(looked_up_ref) == 0);
+
+ /* Now that the reference is renamed... */
+ cl_git_pass(git_reference_rename(&new_ref, looked_up_ref, new_name, 0));
+ cl_assert_equal_s(new_ref->name, new_name);
+ git_reference_free(looked_up_ref);
+
+ /* ...It can't be looked-up with the old name... */
+ cl_git_fail(git_reference_lookup(&another_looked_up_ref, g_repo, loose_tag_ref_name));
+
+ /* ...but the new name works ok... */
+ cl_git_pass(git_reference_lookup(&another_looked_up_ref, g_repo, new_name));
+ cl_assert_equal_s(new_ref->name, new_name);
+
+ /* .. the new ref is loose... */
+ cl_assert(reference_is_packed(another_looked_up_ref) == 0);
+ cl_assert(reference_is_packed(new_ref) == 0);
+
+ /* ...and the ref can be found in the file system */
+ cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), new_name));
+ cl_assert(git_path_exists(temp_path.ptr));
+
+ git_reference_free(new_ref);
+ git_reference_free(another_looked_up_ref);
+ git_buf_free(&temp_path);
+}
+
+void test_refs_rename__packed(void)
+{
+ // rename a packed reference (should make it loose)
+ git_reference *looked_up_ref, *new_ref, *another_looked_up_ref;
+ git_buf temp_path = GIT_BUF_INIT;
+ const char *brand_new_name = "refs/heads/brand_new_name";
+
+ /* Ensure the ref doesn't exist on the file system */
+ cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), packed_head_name));
+ cl_assert(!git_path_exists(temp_path.ptr));
+
+ /* The reference can however be looked-up... */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_head_name));
+
+ /* .. and it's packed */
+ cl_assert(reference_is_packed(looked_up_ref) != 0);
+
+ /* Now that the reference is renamed... */
+ cl_git_pass(git_reference_rename(&new_ref, looked_up_ref, brand_new_name, 0));
+ cl_assert_equal_s(new_ref->name, brand_new_name);
+ git_reference_free(looked_up_ref);
+
+ /* ...It can't be looked-up with the old name... */
+ cl_git_fail(git_reference_lookup(&another_looked_up_ref, g_repo, packed_head_name));
+
+ /* ...but the new name works ok... */
+ cl_git_pass(git_reference_lookup(&another_looked_up_ref, g_repo, brand_new_name));
+ cl_assert_equal_s(another_looked_up_ref->name, brand_new_name);
+
+ /* .. the ref is no longer packed... */
+ cl_assert(reference_is_packed(another_looked_up_ref) == 0);
+ cl_assert(reference_is_packed(new_ref) == 0);
+
+ /* ...and the ref now happily lives in the file system */
+ cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), brand_new_name));
+ cl_assert(git_path_exists(temp_path.ptr));
+
+ git_reference_free(new_ref);
+ git_reference_free(another_looked_up_ref);
+ git_buf_free(&temp_path);
+}
+
+void test_refs_rename__packed_doesnt_pack_others(void)
+{
+ // renaming a packed reference does not pack another reference which happens to be in both loose and pack state
+ git_reference *looked_up_ref, *another_looked_up_ref, *renamed_ref;
+ git_buf temp_path = GIT_BUF_INIT;
+ const char *brand_new_name = "refs/heads/brand_new_name";
+
+ /* Ensure the other reference exists on the file system */
+ cl_git_pass(git_buf_joinpath(&temp_path, git_repository_path(g_repo), packed_test_head_name));
+ cl_assert(git_path_exists(temp_path.ptr));
+
+ /* Lookup the other reference */
+ cl_git_pass(git_reference_lookup(&another_looked_up_ref, g_repo, packed_test_head_name));
+
+ /* Ensure it's loose */
+ cl_assert(reference_is_packed(another_looked_up_ref) == 0);
+ git_reference_free(another_looked_up_ref);
+
+ /* Lookup the reference to rename */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_head_name));
+
+ /* Ensure it's packed */
+ cl_assert(reference_is_packed(looked_up_ref) != 0);
+
+ /* Now that the reference is renamed... */
+ cl_git_pass(git_reference_rename(&renamed_ref, looked_up_ref, brand_new_name, 0));
+ git_reference_free(looked_up_ref);
+
+ /* Lookup the other reference */
+ cl_git_pass(git_reference_lookup(&another_looked_up_ref, g_repo, packed_test_head_name));
+
+ /* Ensure it's loose */
+ cl_assert(reference_is_packed(another_looked_up_ref) == 0);
+
+ /* Ensure the other ref still exists on the file system */
+ cl_assert(git_path_exists(temp_path.ptr));
+
+ git_reference_free(renamed_ref);
+ git_reference_free(another_looked_up_ref);
+ git_buf_free(&temp_path);
+}
+
+void test_refs_rename__name_collision(void)
+{
+ // can not rename a reference with the name of an existing reference
+ git_reference *looked_up_ref, *renamed_ref;
+
+ /* An existing reference... */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_head_name));
+
+ /* Can not be renamed to the name of another existing reference. */
+ cl_git_fail(git_reference_rename(&renamed_ref, looked_up_ref, packed_test_head_name, 0));
+ git_reference_free(looked_up_ref);
+
+ /* Failure to rename it hasn't corrupted its state */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_head_name));
+ cl_assert_equal_s(looked_up_ref->name, packed_head_name);
+
+ git_reference_free(looked_up_ref);
+}
+
+void test_refs_rename__invalid_name(void)
+{
+ // can not rename a reference with an invalid name
+ git_reference *looked_up_ref, *renamed_ref;
+
+ /* An existing oid reference... */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_test_head_name));
+
+ /* Can not be renamed with an invalid name. */
+ cl_assert_equal_i(
+ GIT_EINVALIDSPEC,
+ git_reference_rename(&renamed_ref, looked_up_ref, "Hello! I'm a very invalid name.", 0));
+
+ /* Can not be renamed outside of the refs hierarchy
+ * unless it's ALL_CAPS_AND_UNDERSCORES.
+ */
+ cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_rename(&renamed_ref, looked_up_ref, "i-will-sudo-you", 0));
+
+ /* Failure to rename it hasn't corrupted its state */
+ git_reference_free(looked_up_ref);
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_test_head_name));
+ cl_assert_equal_s(looked_up_ref->name, packed_test_head_name);
+
+ git_reference_free(looked_up_ref);
+}
+
+void test_refs_rename__force_loose_packed(void)
+{
+ // can force-rename a packed reference with the name of an existing loose and packed reference
+ git_reference *looked_up_ref, *renamed_ref;
+ git_oid oid;
+
+ /* An existing reference... */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_head_name));
+ git_oid_cpy(&oid, git_reference_target(looked_up_ref));
+
+ /* Can be force-renamed to the name of another existing reference. */
+ cl_git_pass(git_reference_rename(&renamed_ref, looked_up_ref, packed_test_head_name, 1));
+ git_reference_free(looked_up_ref);
+ git_reference_free(renamed_ref);
+
+ /* Check we actually renamed it */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_test_head_name));
+ cl_assert_equal_s(looked_up_ref->name, packed_test_head_name);
+ cl_assert(!git_oid_cmp(&oid, git_reference_target(looked_up_ref)));
+ git_reference_free(looked_up_ref);
+
+ /* And that the previous one doesn't exist any longer */
+ cl_git_fail(git_reference_lookup(&looked_up_ref, g_repo, packed_head_name));
+}
+
+void test_refs_rename__force_loose(void)
+{
+ // can force-rename a loose reference with the name of an existing loose reference
+ git_reference *looked_up_ref, *renamed_ref;
+ git_oid oid;
+
+ /* An existing reference... */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, "refs/heads/br2"));
+ git_oid_cpy(&oid, git_reference_target(looked_up_ref));
+
+ /* Can be force-renamed to the name of another existing reference. */
+ cl_git_pass(git_reference_rename(&renamed_ref, looked_up_ref, "refs/heads/test", 1));
+ git_reference_free(looked_up_ref);
+ git_reference_free(renamed_ref);
+
+ /* Check we actually renamed it */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, "refs/heads/test"));
+ cl_assert_equal_s(looked_up_ref->name, "refs/heads/test");
+ cl_assert(!git_oid_cmp(&oid, git_reference_target(looked_up_ref)));
+ git_reference_free(looked_up_ref);
+
+ /* And that the previous one doesn't exist any longer */
+ cl_git_fail(git_reference_lookup(&looked_up_ref, g_repo, "refs/heads/br2"));
+
+ git_reference_free(looked_up_ref);
+}
+
+
+void test_refs_rename__overwrite(void)
+{
+ // can not overwrite name of existing reference
+ git_reference *ref, *ref_one, *ref_one_new, *ref_two;
+ git_refdb *refdb;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
+ cl_assert(git_reference_type(ref) & GIT_REF_OID);
+
+ git_oid_cpy(&id, git_reference_target(ref));
+
+ /* Create loose references */
+ cl_git_pass(git_reference_create(&ref_one, g_repo, ref_one_name, &id, 0));
+ cl_git_pass(git_reference_create(&ref_two, g_repo, ref_two_name, &id, 0));
+
+ /* Pack everything */
+ cl_git_pass(git_repository_refdb(&refdb, g_repo));
+ cl_git_pass(git_refdb_compress(refdb));
+
+ /* Attempt to create illegal reference */
+ cl_git_fail(git_reference_create(&ref_one_new, g_repo, ref_one_name_new, &id, 0));
+
+ /* Illegal reference couldn't be created so this is supposed to fail */
+ cl_git_fail(git_reference_lookup(&ref_one_new, g_repo, ref_one_name_new));
+
+ git_reference_free(ref);
+ git_reference_free(ref_one);
+ git_reference_free(ref_one_new);
+ git_reference_free(ref_two);
+ git_refdb_free(refdb);
+}
+
+
+void test_refs_rename__prefix(void)
+{
+ // can be renamed to a new name prefixed with the old name
+ git_reference *ref, *ref_two, *looked_up_ref, *renamed_ref;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
+ cl_assert(git_reference_type(ref) & GIT_REF_OID);
+
+ git_oid_cpy(&id, git_reference_target(ref));
+
+ /* Create loose references */
+ cl_git_pass(git_reference_create(&ref_two, g_repo, ref_two_name, &id, 0));
+
+ /* An existing reference... */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name));
+
+ /* Can be rename to a new name starting with the old name. */
+ cl_git_pass(git_reference_rename(&renamed_ref, looked_up_ref, ref_two_name_new, 0));
+ git_reference_free(looked_up_ref);
+ git_reference_free(renamed_ref);
+
+ /* Check we actually renamed it */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name_new));
+ cl_assert_equal_s(looked_up_ref->name, ref_two_name_new);
+ git_reference_free(looked_up_ref);
+ cl_git_fail(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name));
+
+ git_reference_free(ref);
+ git_reference_free(ref_two);
+ git_reference_free(looked_up_ref);
+}
+
+void test_refs_rename__move_up(void)
+{
+ // can move a reference to a upper reference hierarchy
+ git_reference *ref, *ref_two, *looked_up_ref, *renamed_ref;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
+ cl_assert(git_reference_type(ref) & GIT_REF_OID);
+
+ git_oid_cpy(&id, git_reference_target(ref));
+
+ /* Create loose references */
+ cl_git_pass(git_reference_create(&ref_two, g_repo, ref_two_name_new, &id, 0));
+ git_reference_free(ref_two);
+
+ /* An existing reference... */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name_new));
+
+ /* Can be renamed upward the reference tree. */
+ cl_git_pass(git_reference_rename(&renamed_ref, looked_up_ref, ref_two_name, 0));
+ git_reference_free(looked_up_ref);
+ git_reference_free(renamed_ref);
+
+ /* Check we actually renamed it */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name));
+ cl_assert_equal_s(looked_up_ref->name, ref_two_name);
+ git_reference_free(looked_up_ref);
+
+ cl_git_fail(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name_new));
+ git_reference_free(ref);
+ git_reference_free(looked_up_ref);
+}
+
+void test_refs_rename__propagate_eexists(void)
+{
+ git_reference *ref, *new_ref;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, packed_head_name));
+
+ cl_assert_equal_i(GIT_EEXISTS, git_reference_rename(&new_ref, ref, packed_test_head_name, 0));
+
+ git_reference_free(ref);
+}
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/refs/setter.c b/tests/refs/setter.c
new file mode 100644
index 000000000..6d875f9b6
--- /dev/null
+++ b/tests/refs/setter.c
@@ -0,0 +1,99 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "git2/reflog.h"
+#include "reflog.h"
+#include "git2/refs.h"
+
+static const char *ref_name = "refs/heads/other";
+static const char *ref_master_name = "refs/heads/master";
+static const char *ref_test_name = "refs/heads/test";
+
+static git_repository *g_repo;
+
+void test_refs_setter__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_refs_setter__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_setter__update_direct(void)
+{
+ git_reference *ref, *test_ref, *new_ref;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
+ cl_assert(git_reference_type(ref) == GIT_REF_OID);
+ git_oid_cpy(&id, git_reference_target(ref));
+ git_reference_free(ref);
+
+ cl_git_pass(git_reference_lookup(&test_ref, g_repo, ref_test_name));
+ cl_assert(git_reference_type(test_ref) == GIT_REF_OID);
+
+ cl_git_pass(git_reference_set_target(&new_ref, test_ref, &id));
+
+ git_reference_free(test_ref);
+ git_reference_free(new_ref);
+
+ cl_git_pass(git_reference_lookup(&test_ref, g_repo, ref_test_name));
+ cl_assert(git_reference_type(test_ref) == GIT_REF_OID);
+ cl_assert(git_oid_cmp(&id, git_reference_target(test_ref)) == 0);
+ git_reference_free(test_ref);
+}
+
+void test_refs_setter__update_symbolic(void)
+{
+ git_reference *head, *new_head;
+
+ cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
+ cl_assert(git_reference_type(head) == GIT_REF_SYMBOLIC);
+ cl_assert(strcmp(git_reference_symbolic_target(head), ref_master_name) == 0);
+
+ cl_git_pass(git_reference_symbolic_set_target(&new_head, head, ref_test_name));
+ git_reference_free(new_head);
+ git_reference_free(head);
+
+ cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
+ cl_assert(git_reference_type(head) == GIT_REF_SYMBOLIC);
+ cl_assert(strcmp(git_reference_symbolic_target(head), ref_test_name) == 0);
+ git_reference_free(head);
+}
+
+void test_refs_setter__cant_update_direct_with_symbolic(void)
+{
+ // Overwrite an existing object id reference with a symbolic one
+ git_reference *ref, *new;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
+ cl_assert(git_reference_type(ref) == GIT_REF_OID);
+ git_oid_cpy(&id, git_reference_target(ref));
+
+ cl_git_fail(git_reference_symbolic_set_target(&new, ref, ref_name));
+
+ git_reference_free(ref);
+}
+
+void test_refs_setter__cant_update_symbolic_with_direct(void)
+{
+ // Overwrite an existing symbolic reference with an object id one
+ git_reference *ref, *new;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
+ cl_assert(git_reference_type(ref) == GIT_REF_OID);
+ git_oid_cpy(&id, git_reference_target(ref));
+ git_reference_free(ref);
+
+ /* Create the symbolic ref */
+ cl_git_pass(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 0));
+
+ /* Can't set an OID on a direct ref */
+ cl_git_fail(git_reference_set_target(&new, ref, &id));
+
+ git_reference_free(ref);
+}
diff --git a/tests/refs/shorthand.c b/tests/refs/shorthand.c
new file mode 100644
index 000000000..f995d26ca
--- /dev/null
+++ b/tests/refs/shorthand.c
@@ -0,0 +1,27 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+
+void assert_shorthand(git_repository *repo, const char *refname, const char *shorthand)
+{
+ git_reference *ref;
+
+ cl_git_pass(git_reference_lookup(&ref, repo, refname));
+ cl_assert_equal_s(git_reference_shorthand(ref), shorthand);
+ git_reference_free(ref);
+}
+
+void test_refs_shorthand__0(void)
+{
+ git_repository *repo;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+
+ assert_shorthand(repo, "refs/heads/master", "master");
+ assert_shorthand(repo, "refs/tags/test", "test");
+ assert_shorthand(repo, "refs/remotes/test/master", "test/master");
+ assert_shorthand(repo, "refs/notes/fanout", "notes/fanout");
+
+ git_repository_free(repo);
+}
diff --git a/tests/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/refs/update.c b/tests/refs/update.c
new file mode 100644
index 000000000..205b526a2
--- /dev/null
+++ b/tests/refs/update.c
@@ -0,0 +1,26 @@
+#include "clar_libgit2.h"
+
+#include "refs.h"
+
+static git_repository *g_repo;
+
+void test_refs_update__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_refs_update__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_update__updating_the_target_of_a_symref_with_an_invalid_name_returns_EINVALIDSPEC(void)
+{
+ git_reference *head;
+
+ cl_git_pass(git_reference_lookup(&head, g_repo, GIT_HEAD_FILE));
+ cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head));
+ git_reference_free(head);
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_symbolic_create(&head, g_repo, GIT_HEAD_FILE, "refs/heads/inv@{id", 1));
+}
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/repo/discover.c b/tests/repo/discover.c
new file mode 100644
index 000000000..f93ff2462
--- /dev/null
+++ b/tests/repo/discover.c
@@ -0,0 +1,142 @@
+#include "clar_libgit2.h"
+
+#include "odb.h"
+#include "fileops.h"
+#include "repository.h"
+
+#define TEMP_REPO_FOLDER "temprepo/"
+#define DISCOVER_FOLDER TEMP_REPO_FOLDER "discover.git"
+
+#define SUB_REPOSITORY_FOLDER_NAME "sub_repo"
+#define SUB_REPOSITORY_FOLDER DISCOVER_FOLDER "/" SUB_REPOSITORY_FOLDER_NAME
+#define SUB_REPOSITORY_FOLDER_SUB SUB_REPOSITORY_FOLDER "/sub"
+#define SUB_REPOSITORY_FOLDER_SUB_SUB SUB_REPOSITORY_FOLDER_SUB "/subsub"
+#define SUB_REPOSITORY_FOLDER_SUB_SUB_SUB SUB_REPOSITORY_FOLDER_SUB_SUB "/subsubsub"
+
+#define REPOSITORY_ALTERNATE_FOLDER DISCOVER_FOLDER "/alternate_sub_repo"
+#define REPOSITORY_ALTERNATE_FOLDER_SUB REPOSITORY_ALTERNATE_FOLDER "/sub"
+#define REPOSITORY_ALTERNATE_FOLDER_SUB_SUB REPOSITORY_ALTERNATE_FOLDER_SUB "/subsub"
+#define REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB REPOSITORY_ALTERNATE_FOLDER_SUB_SUB "/subsubsub"
+
+#define ALTERNATE_MALFORMED_FOLDER1 DISCOVER_FOLDER "/alternate_malformed_repo1"
+#define ALTERNATE_MALFORMED_FOLDER2 DISCOVER_FOLDER "/alternate_malformed_repo2"
+#define ALTERNATE_MALFORMED_FOLDER3 DISCOVER_FOLDER "/alternate_malformed_repo3"
+#define ALTERNATE_NOT_FOUND_FOLDER DISCOVER_FOLDER "/alternate_not_found_repo"
+
+static void ensure_repository_discover(const char *start_path,
+ const char *ceiling_dirs,
+ const char *expected_path)
+{
+ char found_path[GIT_PATH_MAX];
+ cl_git_pass(git_repository_discover(found_path, sizeof(found_path), start_path, 0, ceiling_dirs));
+ //across_fs is always 0 as we can't automate the filesystem change tests
+ cl_assert_equal_s(found_path, expected_path);
+}
+
+static void write_file(const char *path, const char *content)
+{
+ git_file file;
+ int error;
+
+ if (git_path_exists(path)) {
+ cl_git_pass(p_unlink(path));
+ }
+
+ file = git_futils_creat_withpath(path, 0777, 0666);
+ cl_assert(file >= 0);
+
+ error = p_write(file, content, strlen(content) * sizeof(char));
+ p_close(file);
+ cl_git_pass(error);
+}
+
+//no check is performed on ceiling_dirs length, so be sure it's long enough
+static void append_ceiling_dir(git_buf *ceiling_dirs, const char *path)
+{
+ git_buf pretty_path = GIT_BUF_INIT;
+ char ceiling_separator[2] = { GIT_PATH_LIST_SEPARATOR, '\0' };
+
+ cl_git_pass(git_path_prettify_dir(&pretty_path, path, NULL));
+
+ if (ceiling_dirs->size > 0)
+ git_buf_puts(ceiling_dirs, ceiling_separator);
+
+ git_buf_puts(ceiling_dirs, pretty_path.ptr);
+
+ git_buf_free(&pretty_path);
+ cl_assert(git_buf_oom(ceiling_dirs) == 0);
+}
+
+void test_repo_discover__0(void)
+{
+ // test discover
+ git_repository *repo;
+ git_buf ceiling_dirs_buf = GIT_BUF_INIT;
+ const char *ceiling_dirs;
+ char repository_path[GIT_PATH_MAX];
+ char sub_repository_path[GIT_PATH_MAX];
+ char found_path[GIT_PATH_MAX];
+ const mode_t mode = 0777;
+
+ git_futils_mkdir_r(DISCOVER_FOLDER, NULL, mode);
+ append_ceiling_dir(&ceiling_dirs_buf, TEMP_REPO_FOLDER);
+ ceiling_dirs = git_buf_cstr(&ceiling_dirs_buf);
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(repository_path, sizeof(repository_path), DISCOVER_FOLDER, 0, ceiling_dirs));
+
+ cl_git_pass(git_repository_init(&repo, DISCOVER_FOLDER, 1));
+ cl_git_pass(git_repository_discover(repository_path, sizeof(repository_path), DISCOVER_FOLDER, 0, ceiling_dirs));
+ git_repository_free(repo);
+
+ cl_git_pass(git_repository_init(&repo, SUB_REPOSITORY_FOLDER, 0));
+ cl_git_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, NULL, mode));
+ cl_git_pass(git_repository_discover(sub_repository_path, sizeof(sub_repository_path), SUB_REPOSITORY_FOLDER, 0, ceiling_dirs));
+
+ cl_git_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, NULL, mode));
+ ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB, ceiling_dirs, sub_repository_path);
+ ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB_SUB, ceiling_dirs, sub_repository_path);
+ ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, ceiling_dirs, sub_repository_path);
+
+ cl_git_pass(git_futils_mkdir_r(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, NULL, mode));
+ write_file(REPOSITORY_ALTERNATE_FOLDER "/" DOT_GIT, "gitdir: ../" SUB_REPOSITORY_FOLDER_NAME "/" DOT_GIT);
+ write_file(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB "/" DOT_GIT, "gitdir: ../../../" SUB_REPOSITORY_FOLDER_NAME "/" DOT_GIT);
+ write_file(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB "/" DOT_GIT, "gitdir: ../../../../");
+ ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER, ceiling_dirs, sub_repository_path);
+ ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB, ceiling_dirs, sub_repository_path);
+ ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB, ceiling_dirs, sub_repository_path);
+ ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, ceiling_dirs, repository_path);
+
+ cl_git_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER1, NULL, mode));
+ write_file(ALTERNATE_MALFORMED_FOLDER1 "/" DOT_GIT, "Anything but not gitdir:");
+ cl_git_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER2, NULL, mode));
+ write_file(ALTERNATE_MALFORMED_FOLDER2 "/" DOT_GIT, "gitdir:");
+ cl_git_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER3, NULL, mode));
+ write_file(ALTERNATE_MALFORMED_FOLDER3 "/" DOT_GIT, "gitdir: \n\n\n");
+ cl_git_pass(git_futils_mkdir_r(ALTERNATE_NOT_FOUND_FOLDER, NULL, mode));
+ write_file(ALTERNATE_NOT_FOUND_FOLDER "/" DOT_GIT, "gitdir: a_repository_that_surely_does_not_exist");
+ cl_git_fail(git_repository_discover(found_path, sizeof(found_path), ALTERNATE_MALFORMED_FOLDER1, 0, ceiling_dirs));
+ cl_git_fail(git_repository_discover(found_path, sizeof(found_path), ALTERNATE_MALFORMED_FOLDER2, 0, ceiling_dirs));
+ cl_git_fail(git_repository_discover(found_path, sizeof(found_path), ALTERNATE_MALFORMED_FOLDER3, 0, ceiling_dirs));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(found_path, sizeof(found_path), ALTERNATE_NOT_FOUND_FOLDER, 0, ceiling_dirs));
+
+ append_ceiling_dir(&ceiling_dirs_buf, SUB_REPOSITORY_FOLDER);
+ ceiling_dirs = git_buf_cstr(&ceiling_dirs_buf);
+
+ //this must pass as ceiling_directories cannot predent the current
+ //working directory to be checked
+ cl_git_pass(git_repository_discover(found_path, sizeof(found_path), SUB_REPOSITORY_FOLDER, 0, ceiling_dirs));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(found_path, sizeof(found_path), SUB_REPOSITORY_FOLDER_SUB, 0, ceiling_dirs));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(found_path, sizeof(found_path), SUB_REPOSITORY_FOLDER_SUB_SUB, 0, ceiling_dirs));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(found_path, sizeof(found_path), SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, 0, ceiling_dirs));
+
+ //.gitfile redirection should not be affected by ceiling directories
+ ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER, ceiling_dirs, sub_repository_path);
+ ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB, ceiling_dirs, sub_repository_path);
+ ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB, ceiling_dirs, sub_repository_path);
+ ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, ceiling_dirs, repository_path);
+
+ cl_git_pass(git_futils_rmdir_r(TEMP_REPO_FOLDER, NULL, GIT_RMDIR_REMOVE_FILES));
+ git_repository_free(repo);
+ git_buf_free(&ceiling_dirs_buf);
+}
+
diff --git a/tests/repo/getters.c b/tests/repo/getters.c
new file mode 100644
index 000000000..b8ede126c
--- /dev/null
+++ b/tests/repo/getters.c
@@ -0,0 +1,40 @@
+#include "clar_libgit2.h"
+
+void test_repo_getters__is_empty_correctly_deals_with_pristine_looking_repos(void)
+{
+ git_repository *repo;
+
+ repo = cl_git_sandbox_init("empty_bare.git");
+ cl_git_remove_placeholders(git_repository_path(repo), "dummy-marker.txt");
+
+ cl_assert_equal_i(true, git_repository_is_empty(repo));
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_repo_getters__is_empty_can_detect_used_repositories(void)
+{
+ git_repository *repo;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+ cl_assert_equal_i(false, git_repository_is_empty(repo));
+
+ git_repository_free(repo);
+}
+
+void test_repo_getters__retrieving_the_odb_honors_the_refcount(void)
+{
+ git_odb *odb;
+ git_repository *repo;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+ cl_git_pass(git_repository_odb(&odb, repo));
+ cl_assert(((git_refcount *)odb)->refcount.val == 2);
+
+ git_repository_free(repo);
+ cl_assert(((git_refcount *)odb)->refcount.val == 1);
+
+ git_odb_free(odb);
+}
diff --git a/tests/repo/hashfile.c b/tests/repo/hashfile.c
new file mode 100644
index 000000000..4cc9f18b4
--- /dev/null
+++ b/tests/repo/hashfile.c
@@ -0,0 +1,85 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+
+static git_repository *_repo;
+
+void test_repo_hashfile__initialize(void)
+{
+ _repo = cl_git_sandbox_init("status");
+}
+
+void test_repo_hashfile__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ _repo = NULL;
+}
+
+void test_repo_hashfile__simple(void)
+{
+ git_oid a, b;
+ git_buf full = GIT_BUF_INIT;
+
+ /* hash with repo relative path */
+ cl_git_pass(git_odb_hashfile(&a, "status/current_file", GIT_OBJ_BLOB));
+ cl_git_pass(git_repository_hashfile(&b, _repo, "current_file", GIT_OBJ_BLOB, NULL));
+ cl_assert(git_oid_equal(&a, &b));
+
+ cl_git_pass(git_buf_joinpath(&full, git_repository_workdir(_repo), "current_file"));
+
+ /* hash with full path */
+ cl_git_pass(git_odb_hashfile(&a, full.ptr, GIT_OBJ_BLOB));
+ cl_git_pass(git_repository_hashfile(&b, _repo, full.ptr, GIT_OBJ_BLOB, NULL));
+ cl_assert(git_oid_equal(&a, &b));
+
+ /* hash with invalid type */
+ cl_git_fail(git_odb_hashfile(&a, full.ptr, GIT_OBJ_ANY));
+ cl_git_fail(git_repository_hashfile(&b, _repo, full.ptr, GIT_OBJ_OFS_DELTA, NULL));
+
+ git_buf_free(&full);
+}
+
+void test_repo_hashfile__filtered(void)
+{
+ git_oid a, b;
+
+ cl_repo_set_bool(_repo, "core.autocrlf", true);
+
+ cl_git_append2file("status/.gitattributes", "*.txt text\n*.bin binary\n\n");
+
+ /* create some sample content with CRLF in it */
+ cl_git_mkfile("status/testfile.txt", "content\r\n");
+ cl_git_mkfile("status/testfile.bin", "other\r\nstuff\r\n");
+
+ /* not equal hashes because of filtering */
+ cl_git_pass(git_odb_hashfile(&a, "status/testfile.txt", GIT_OBJ_BLOB));
+ cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.txt", GIT_OBJ_BLOB, NULL));
+ cl_assert(git_oid_cmp(&a, &b));
+
+ /* equal hashes because filter is binary */
+ cl_git_pass(git_odb_hashfile(&a, "status/testfile.bin", GIT_OBJ_BLOB));
+ cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.bin", GIT_OBJ_BLOB, NULL));
+ cl_assert(git_oid_equal(&a, &b));
+
+ /* equal hashes when 'as_file' points to binary filtering */
+ cl_git_pass(git_odb_hashfile(&a, "status/testfile.txt", GIT_OBJ_BLOB));
+ cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.txt", GIT_OBJ_BLOB, "foo.bin"));
+ cl_assert(git_oid_equal(&a, &b));
+
+ /* not equal hashes when 'as_file' points to text filtering */
+ cl_git_pass(git_odb_hashfile(&a, "status/testfile.bin", GIT_OBJ_BLOB));
+ cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.bin", GIT_OBJ_BLOB, "foo.txt"));
+ cl_assert(git_oid_cmp(&a, &b));
+
+ /* equal hashes when 'as_file' is empty and turns off filtering */
+ cl_git_pass(git_odb_hashfile(&a, "status/testfile.txt", GIT_OBJ_BLOB));
+ cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.txt", GIT_OBJ_BLOB, ""));
+ cl_assert(git_oid_equal(&a, &b));
+
+ cl_git_pass(git_odb_hashfile(&a, "status/testfile.bin", GIT_OBJ_BLOB));
+ cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.bin", GIT_OBJ_BLOB, ""));
+ cl_assert(git_oid_equal(&a, &b));
+
+ /* some hash type failures */
+ cl_git_fail(git_odb_hashfile(&a, "status/testfile.txt", 0));
+ cl_git_fail(git_repository_hashfile(&b, _repo, "testfile.txt", GIT_OBJ_ANY, NULL));
+}
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/repo/message.c b/tests/repo/message.c
new file mode 100644
index 000000000..629d40c12
--- /dev/null
+++ b/tests/repo/message.c
@@ -0,0 +1,52 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "refs.h"
+#include "posix.h"
+
+static git_repository *_repo;
+static git_buf _path;
+static char *_actual;
+
+void test_repo_message__initialize(void)
+{
+ _repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_repo_message__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ git_buf_free(&_path);
+ git__free(_actual);
+ _actual = NULL;
+}
+
+void test_repo_message__none(void)
+{
+ cl_assert_equal_i(GIT_ENOTFOUND, git_repository_message(NULL, 0, _repo));
+}
+
+void test_repo_message__message(void)
+{
+ const char expected[] = "Test\n\nThis is a test of the emergency broadcast system\n";
+ ssize_t len;
+
+ cl_git_pass(git_buf_joinpath(&_path, git_repository_path(_repo), "MERGE_MSG"));
+ cl_git_mkfile(git_buf_cstr(&_path), expected);
+
+ len = git_repository_message(NULL, 0, _repo);
+ cl_assert(len > 0);
+
+ _actual = git__malloc(len + 1);
+ cl_assert(_actual != NULL);
+
+ /* Test non truncation */
+ cl_assert(git_repository_message(_actual, len, _repo) > 0);
+ cl_assert_equal_s(expected, _actual);
+
+ /* Test truncation and that trailing NUL is inserted */
+ cl_assert(git_repository_message(_actual, 6, _repo) > 0);
+ cl_assert_equal_s("Test\n", _actual);
+
+ cl_git_pass(p_unlink(git_buf_cstr(&_path)));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_repository_message(NULL, 0, _repo));
+}
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/repo/setters.c b/tests/repo/setters.c
new file mode 100644
index 000000000..f34f1e471
--- /dev/null
+++ b/tests/repo/setters.c
@@ -0,0 +1,107 @@
+#include "clar_libgit2.h"
+#include "git2/sys/repository.h"
+
+#include "buffer.h"
+#include "posix.h"
+#include "util.h"
+#include "path.h"
+#include "fileops.h"
+
+static git_repository *repo;
+
+void test_repo_setters__initialize(void)
+{
+ cl_fixture_sandbox("testrepo.git");
+ cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+ cl_must_pass(p_mkdir("new_workdir", 0777));
+}
+
+void test_repo_setters__cleanup(void)
+{
+ git_repository_free(repo);
+ repo = NULL;
+
+ cl_fixture_cleanup("testrepo.git");
+ cl_fixture_cleanup("new_workdir");
+}
+
+void test_repo_setters__setting_a_workdir_turns_a_bare_repository_into_a_standard_one(void)
+{
+ cl_assert(git_repository_is_bare(repo) == 1);
+
+ cl_assert(git_repository_workdir(repo) == NULL);
+ cl_git_pass(git_repository_set_workdir(repo, "./new_workdir", false));
+
+ cl_assert(git_repository_workdir(repo) != NULL);
+ cl_assert(git_repository_is_bare(repo) == 0);
+}
+
+void test_repo_setters__setting_a_workdir_prettifies_its_path(void)
+{
+ cl_git_pass(git_repository_set_workdir(repo, "./new_workdir", false));
+
+ cl_assert(git__suffixcmp(git_repository_workdir(repo), "new_workdir/") == 0);
+}
+
+void test_repo_setters__setting_a_workdir_creates_a_gitlink(void)
+{
+ git_config *cfg;
+ const char *val;
+ git_buf content = GIT_BUF_INIT;
+
+ cl_git_pass(git_repository_set_workdir(repo, "./new_workdir", true));
+
+ cl_assert(git_path_isfile("./new_workdir/.git"));
+
+ cl_git_pass(git_futils_readbuffer(&content, "./new_workdir/.git"));
+ cl_assert(git__prefixcmp(git_buf_cstr(&content), "gitdir: ") == 0);
+ cl_assert(git__suffixcmp(git_buf_cstr(&content), "testrepo.git/") == 0);
+ git_buf_free(&content);
+
+ cl_git_pass(git_repository_config(&cfg, repo));
+ cl_git_pass(git_config_get_string(&val, cfg, "core.worktree"));
+ cl_assert(git__suffixcmp(val, "new_workdir/") == 0);
+ git_config_free(cfg);
+}
+
+void test_repo_setters__setting_a_new_index_on_a_repo_which_has_already_loaded_one_properly_honors_the_refcount(void)
+{
+ git_index *new_index;
+
+ cl_git_pass(git_index_open(&new_index, "./my-index"));
+ cl_assert(((git_refcount *)new_index)->refcount.val == 1);
+
+ git_repository_set_index(repo, new_index);
+ cl_assert(((git_refcount *)new_index)->refcount.val == 2);
+
+ git_repository_free(repo);
+ cl_assert(((git_refcount *)new_index)->refcount.val == 1);
+
+ git_index_free(new_index);
+
+ /*
+ * Ensure the cleanup method won't try to free the repo as it's already been taken care of
+ */
+ repo = NULL;
+}
+
+void test_repo_setters__setting_a_new_odb_on_a_repo_which_already_loaded_one_properly_honors_the_refcount(void)
+{
+ git_odb *new_odb;
+
+ cl_git_pass(git_odb_open(&new_odb, "./testrepo.git/objects"));
+ cl_assert(((git_refcount *)new_odb)->refcount.val == 1);
+
+ git_repository_set_odb(repo, new_odb);
+ cl_assert(((git_refcount *)new_odb)->refcount.val == 2);
+
+ git_repository_free(repo);
+ cl_assert(((git_refcount *)new_odb)->refcount.val == 1);
+
+ git_odb_free(new_odb);
+
+ /*
+ * Ensure the cleanup method won't try to free the repo as it's already been taken care of
+ */
+ repo = NULL;
+}
diff --git a/tests/repo/shallow.c b/tests/repo/shallow.c
new file mode 100644
index 000000000..1cc66ae40
--- /dev/null
+++ b/tests/repo/shallow.c
@@ -0,0 +1,33 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+
+static git_repository *g_repo;
+
+void test_repo_shallow__initialize(void)
+{
+}
+
+void test_repo_shallow__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_repo_shallow__no_shallow_file(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+ cl_assert_equal_i(0, git_repository_is_shallow(g_repo));
+}
+
+void test_repo_shallow__empty_shallow_file(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+ cl_git_mkfile("testrepo.git/shallow", "");
+ cl_assert_equal_i(0, git_repository_is_shallow(g_repo));
+}
+
+void test_repo_shallow__shallow_repo(void)
+{
+ g_repo = cl_git_sandbox_init("shallow.git");
+ cl_assert_equal_i(1, git_repository_is_shallow(g_repo));
+}
+
diff --git a/tests/repo/state.c b/tests/repo/state.c
new file mode 100644
index 000000000..5a0a5f360
--- /dev/null
+++ b/tests/repo/state.c
@@ -0,0 +1,96 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "refs.h"
+#include "posix.h"
+#include "fileops.h"
+
+static git_repository *_repo;
+static git_buf _path;
+
+void test_repo_state__initialize(void)
+{
+ _repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_repo_state__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ git_buf_free(&_path);
+}
+
+static void setup_simple_state(const char *filename)
+{
+ cl_git_pass(git_buf_joinpath(&_path, git_repository_path(_repo), filename));
+ git_futils_mkpath2file(git_buf_cstr(&_path), 0777);
+ cl_git_mkfile(git_buf_cstr(&_path), "dummy");
+}
+
+static void assert_repo_state(git_repository_state_t state)
+{
+ cl_assert_equal_i(state, git_repository_state(_repo));
+}
+
+void test_repo_state__none_with_HEAD_attached(void)
+{
+ assert_repo_state(GIT_REPOSITORY_STATE_NONE);
+}
+
+void test_repo_state__none_with_HEAD_detached(void)
+{
+ cl_git_pass(git_repository_detach_head(_repo));
+ assert_repo_state(GIT_REPOSITORY_STATE_NONE);
+}
+
+void test_repo_state__merge(void)
+{
+ setup_simple_state(GIT_MERGE_HEAD_FILE);
+ assert_repo_state(GIT_REPOSITORY_STATE_MERGE);
+}
+
+void test_repo_state__revert(void)
+{
+ setup_simple_state(GIT_REVERT_HEAD_FILE);
+ assert_repo_state(GIT_REPOSITORY_STATE_REVERT);
+}
+
+void test_repo_state__cherry_pick(void)
+{
+ setup_simple_state(GIT_CHERRY_PICK_HEAD_FILE);
+ assert_repo_state(GIT_REPOSITORY_STATE_CHERRY_PICK);
+}
+
+void test_repo_state__bisect(void)
+{
+ setup_simple_state(GIT_BISECT_LOG_FILE);
+ assert_repo_state(GIT_REPOSITORY_STATE_BISECT);
+}
+
+void test_repo_state__rebase_interactive(void)
+{
+ setup_simple_state(GIT_REBASE_MERGE_INTERACTIVE_FILE);
+ assert_repo_state(GIT_REPOSITORY_STATE_REBASE_INTERACTIVE);
+}
+
+void test_repo_state__rebase_merge(void)
+{
+ setup_simple_state(GIT_REBASE_MERGE_DIR "whatever");
+ assert_repo_state(GIT_REPOSITORY_STATE_REBASE_MERGE);
+}
+
+void test_repo_state__rebase(void)
+{
+ setup_simple_state(GIT_REBASE_APPLY_REBASING_FILE);
+ assert_repo_state(GIT_REPOSITORY_STATE_REBASE);
+}
+
+void test_repo_state__apply_mailbox(void)
+{
+ setup_simple_state(GIT_REBASE_APPLY_APPLYING_FILE);
+ assert_repo_state(GIT_REPOSITORY_STATE_APPLY_MAILBOX);
+}
+
+void test_repo_state__apply_mailbox_or_rebase(void)
+{
+ setup_simple_state(GIT_REBASE_APPLY_DIR "whatever");
+ assert_repo_state(GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE);
+}
diff --git a/tests/reset/default.c b/tests/reset/default.c
new file mode 100644
index 000000000..e29e63550
--- /dev/null
+++ b/tests/reset/default.c
@@ -0,0 +1,180 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "reset_helpers.h"
+#include "path.h"
+
+static git_repository *_repo;
+static git_object *_target;
+static git_strarray _pathspecs;
+static git_index *_index;
+
+static void initialize(const char *repo_name)
+{
+ _repo = cl_git_sandbox_init(repo_name);
+ cl_git_pass(git_repository_index(&_index, _repo));
+
+ _target = NULL;
+
+ _pathspecs.strings = NULL;
+ _pathspecs.count = 0;
+}
+
+void test_reset_default__initialize(void)
+{
+ initialize("status");
+}
+
+void test_reset_default__cleanup(void)
+{
+ git_object_free(_target);
+ _target = NULL;
+
+ git_index_free(_index);
+ _index = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+static void assert_content_in_index(
+ git_strarray *pathspecs,
+ bool should_exist,
+ git_strarray *expected_shas)
+{
+ size_t i, pos;
+ int error;
+
+ for (i = 0; i < pathspecs->count; i++) {
+ error = git_index_find(&pos, _index, pathspecs->strings[i]);
+
+ if (should_exist) {
+ const git_index_entry *entry;
+
+ cl_assert(error != GIT_ENOTFOUND);
+
+ entry = git_index_get_byindex(_index, pos);
+ cl_assert(entry != NULL);
+
+ if (!expected_shas)
+ continue;
+
+ cl_git_pass(git_oid_streq(&entry->oid, expected_shas->strings[i]));
+ } else
+ cl_assert_equal_i(should_exist, error != GIT_ENOTFOUND);
+ }
+}
+
+void test_reset_default__resetting_filepaths_against_a_null_target_removes_them_from_the_index(void)
+{
+ char *paths[] = { "staged_changes", "staged_new_file" };
+
+ _pathspecs.strings = paths;
+ _pathspecs.count = 2;
+
+ assert_content_in_index(&_pathspecs, true, NULL);
+
+ cl_git_pass(git_reset_default(_repo, NULL, &_pathspecs));
+
+ assert_content_in_index(&_pathspecs, false, NULL);
+}
+
+/*
+ * $ git ls-files --cached -s --abbrev=7 -- "staged*"
+ * 100644 55d316c 0 staged_changes
+ * 100644 a6be623 0 staged_changes_file_deleted
+ * ...
+ *
+ * $ git reset 0017bd4 -- staged_changes staged_changes_file_deleted
+ * Unstaged changes after reset:
+ * ...
+ *
+ * $ git ls-files --cached -s --abbrev=7 -- "staged*"
+ * 100644 32504b7 0 staged_changes
+ * 100644 061d42a 0 staged_changes_file_deleted
+ * ...
+ */
+void test_reset_default__resetting_filepaths_replaces_their_corresponding_index_entries(void)
+{
+ git_strarray before, after;
+
+ char *paths[] = { "staged_changes", "staged_changes_file_deleted" };
+ char *before_shas[] = { "55d316c9ba708999f1918e9677d01dfcae69c6b9",
+ "a6be623522ce87a1d862128ac42672604f7b468b" };
+ char *after_shas[] = { "32504b727382542f9f089e24fddac5e78533e96c",
+ "061d42a44cacde5726057b67558821d95db96f19" };
+
+ _pathspecs.strings = paths;
+ _pathspecs.count = 2;
+ before.strings = before_shas;
+ before.count = 2;
+ after.strings = after_shas;
+ after.count = 2;
+
+ cl_git_pass(git_revparse_single(&_target, _repo, "0017bd4"));
+ assert_content_in_index(&_pathspecs, true, &before);
+
+ cl_git_pass(git_reset_default(_repo, _target, &_pathspecs));
+
+ assert_content_in_index(&_pathspecs, true, &after);
+}
+
+/*
+ * $ git ls-files --cached -s --abbrev=7 -- conflicts-one.txt
+ * 100644 1f85ca5 1 conflicts-one.txt
+ * 100644 6aea5f2 2 conflicts-one.txt
+ * 100644 516bd85 3 conflicts-one.txt
+ *
+ * $ git reset 9a05ccb -- conflicts-one.txt
+ * Unstaged changes after reset:
+ * ...
+ *
+ * $ git ls-files --cached -s --abbrev=7 -- conflicts-one.txt
+ * 100644 1f85ca5 0 conflicts-one.txt
+ *
+ */
+void test_reset_default__resetting_filepaths_clears_previous_conflicts(void)
+{
+ const git_index_entry *conflict_entry[3];
+ git_strarray after;
+
+ char *paths[] = { "conflicts-one.txt" };
+ char *after_shas[] = { "1f85ca51b8e0aac893a621b61a9c2661d6aa6d81" };
+
+ test_reset_default__cleanup();
+ initialize("mergedrepo");
+
+ _pathspecs.strings = paths;
+ _pathspecs.count = 1;
+ after.strings = after_shas;
+ after.count = 1;
+
+ cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1],
+ &conflict_entry[2], _index, "conflicts-one.txt"));
+
+ cl_git_pass(git_revparse_single(&_target, _repo, "9a05ccb"));
+ cl_git_pass(git_reset_default(_repo, _target, &_pathspecs));
+
+ assert_content_in_index(&_pathspecs, true, &after);
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_index_conflict_get(&conflict_entry[0],
+ &conflict_entry[1], &conflict_entry[2], _index, "conflicts-one.txt"));
+}
+
+/*
+$ git reset HEAD -- "I_am_not_there.txt" "me_neither.txt"
+Unstaged changes after reset:
+...
+*/
+void test_reset_default__resetting_unknown_filepaths_does_not_fail(void)
+{
+ char *paths[] = { "I_am_not_there.txt", "me_neither.txt" };
+
+ _pathspecs.strings = paths;
+ _pathspecs.count = 2;
+
+ assert_content_in_index(&_pathspecs, false, NULL);
+
+ cl_git_pass(git_revparse_single(&_target, _repo, "HEAD"));
+ cl_git_pass(git_reset_default(_repo, _target, &_pathspecs));
+
+ assert_content_in_index(&_pathspecs, false, NULL);
+}
diff --git a/tests/reset/hard.c b/tests/reset/hard.c
new file mode 100644
index 000000000..1c0c84135
--- /dev/null
+++ b/tests/reset/hard.c
@@ -0,0 +1,200 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "reset_helpers.h"
+#include "path.h"
+#include "fileops.h"
+
+static git_repository *repo;
+static git_object *target;
+
+void test_reset_hard__initialize(void)
+{
+ repo = cl_git_sandbox_init("status");
+ target = NULL;
+}
+
+void test_reset_hard__cleanup(void)
+{
+ if (target != NULL) {
+ git_object_free(target);
+ target = NULL;
+ }
+
+ cl_git_sandbox_cleanup();
+}
+
+static int strequal_ignore_eol(const char *exp, const char *str)
+{
+ while (*exp && *str) {
+ if (*exp != *str) {
+ while (*exp == '\r' || *exp == '\n') ++exp;
+ while (*str == '\r' || *str == '\n') ++str;
+ if (*exp != *str)
+ return false;
+ } else {
+ exp++; str++;
+ }
+ }
+ return (!*exp && !*str);
+}
+
+void test_reset_hard__resetting_reverts_modified_files(void)
+{
+ git_buf path = GIT_BUF_INIT, content = GIT_BUF_INIT;
+ int i;
+ static const char *files[4] = {
+ "current_file",
+ "modified_file",
+ "staged_new_file",
+ "staged_changes_modified_file" };
+ static const char *before[4] = {
+ "current_file\n",
+ "modified_file\nmodified_file\n",
+ "staged_new_file\n",
+ "staged_changes_modified_file\nstaged_changes_modified_file\nstaged_changes_modified_file\n"
+ };
+ static const char *after[4] = {
+ "current_file\n",
+ "modified_file\n",
+ NULL,
+ "staged_changes_modified_file\n"
+ };
+ const char *wd = git_repository_workdir(repo);
+
+ cl_assert(wd);
+
+ for (i = 0; i < 4; ++i) {
+ cl_git_pass(git_buf_joinpath(&path, wd, files[i]));
+ cl_git_pass(git_futils_readbuffer(&content, path.ptr));
+ cl_assert_equal_s(before[i], content.ptr);
+ }
+
+ retrieve_target_from_oid(
+ &target, repo, "26a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f");
+
+ cl_git_pass(git_reset(repo, target, GIT_RESET_HARD));
+
+ for (i = 0; i < 4; ++i) {
+ cl_git_pass(git_buf_joinpath(&path, wd, files[i]));
+ if (after[i]) {
+ cl_git_pass(git_futils_readbuffer(&content, path.ptr));
+ cl_assert(strequal_ignore_eol(after[i], content.ptr));
+ } else {
+ cl_assert(!git_path_exists(path.ptr));
+ }
+ }
+
+ git_buf_free(&content);
+ git_buf_free(&path);
+}
+
+void test_reset_hard__cannot_reset_in_a_bare_repository(void)
+{
+ git_repository *bare;
+
+ cl_git_pass(git_repository_open(&bare, cl_fixture("testrepo.git")));
+ cl_assert(git_repository_is_bare(bare) == true);
+
+ retrieve_target_from_oid(&target, bare, KNOWN_COMMIT_IN_BARE_REPO);
+
+ cl_assert_equal_i(GIT_EBAREREPO, git_reset(bare, target, GIT_RESET_HARD));
+
+ git_repository_free(bare);
+}
+
+static void index_entry_init(git_index *index, int side, git_oid *oid)
+{
+ git_index_entry entry;
+
+ memset(&entry, 0x0, sizeof(git_index_entry));
+
+ entry.path = "conflicting_file";
+ entry.flags = (side << GIT_IDXENTRY_STAGESHIFT);
+ entry.mode = 0100644;
+ git_oid_cpy(&entry.oid, oid);
+
+ cl_git_pass(git_index_add(index, &entry));
+}
+
+static void unmerged_index_init(git_index *index, int entries)
+{
+ int write_ancestor = 1;
+ int write_ours = 2;
+ int write_theirs = 4;
+ git_oid ancestor, ours, theirs;
+
+ git_oid_fromstr(&ancestor, "6bb0d9f700543ba3d318ba7075fc3bd696b4287b");
+ git_oid_fromstr(&ours, "b19a1e93bec1317dc6097229e12afaffbfa74dc2");
+ git_oid_fromstr(&theirs, "950b81b7eee953d050aa05a641f8e056c85dd1bd");
+
+ cl_git_rewritefile("status/conflicting_file", "conflicting file\n");
+
+ if (entries & write_ancestor)
+ index_entry_init(index, 1, &ancestor);
+
+ if (entries & write_ours)
+ index_entry_init(index, 2, &ours);
+
+ if (entries & write_theirs)
+ index_entry_init(index, 3, &theirs);
+}
+
+void test_reset_hard__resetting_reverts_unmerged(void)
+{
+ git_index *index;
+ int entries;
+
+ /* Ensure every permutation of non-zero stage entries results in the
+ * path being cleaned up. */
+ for (entries = 1; entries < 8; entries++) {
+ cl_git_pass(git_repository_index(&index, repo));
+
+ unmerged_index_init(index, entries);
+ cl_git_pass(git_index_write(index));
+
+ retrieve_target_from_oid(&target, repo, "26a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f");
+ cl_git_pass(git_reset(repo, target, GIT_RESET_HARD));
+
+ cl_assert(git_path_exists("status/conflicting_file") == 0);
+
+ git_object_free(target);
+ target = NULL;
+
+ git_index_free(index);
+ }
+}
+
+void test_reset_hard__cleans_up_merge(void)
+{
+ git_buf merge_head_path = GIT_BUF_INIT,
+ merge_msg_path = GIT_BUF_INIT,
+ merge_mode_path = GIT_BUF_INIT,
+ orig_head_path = GIT_BUF_INIT;
+
+ 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");
+
+ cl_git_pass(git_buf_joinpath(&merge_msg_path, git_repository_path(repo), "MERGE_MSG"));
+ cl_git_mkfile(git_buf_cstr(&merge_msg_path), "Merge commit 0017bd4ab1ec30440b17bae1680cff124ab5f1f6\n");
+
+ cl_git_pass(git_buf_joinpath(&merge_mode_path, git_repository_path(repo), "MERGE_MODE"));
+ cl_git_mkfile(git_buf_cstr(&merge_mode_path), "");
+
+ cl_git_pass(git_buf_joinpath(&orig_head_path, git_repository_path(repo), "ORIG_HEAD"));
+ cl_git_mkfile(git_buf_cstr(&orig_head_path), "0017bd4ab1ec30440b17bae1680cff124ab5f1f6");
+
+ retrieve_target_from_oid(&target, repo, "0017bd4ab1ec30440b17bae1680cff124ab5f1f6");
+ cl_git_pass(git_reset(repo, target, GIT_RESET_HARD));
+
+ cl_assert(!git_path_exists(git_buf_cstr(&merge_head_path)));
+ cl_assert(!git_path_exists(git_buf_cstr(&merge_msg_path)));
+ cl_assert(!git_path_exists(git_buf_cstr(&merge_mode_path)));
+
+ cl_assert(git_path_exists(git_buf_cstr(&orig_head_path)));
+ cl_git_pass(p_unlink(git_buf_cstr(&orig_head_path)));
+
+ git_buf_free(&merge_head_path);
+ git_buf_free(&merge_msg_path);
+ git_buf_free(&merge_mode_path);
+ git_buf_free(&orig_head_path);
+}
diff --git a/tests/reset/mixed.c b/tests/reset/mixed.c
new file mode 100644
index 000000000..7b90c23f1
--- /dev/null
+++ b/tests/reset/mixed.c
@@ -0,0 +1,49 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "reset_helpers.h"
+#include "path.h"
+
+static git_repository *repo;
+static git_object *target;
+
+void test_reset_mixed__initialize(void)
+{
+ repo = cl_git_sandbox_init("attr");
+ target = NULL;
+}
+
+void test_reset_mixed__cleanup(void)
+{
+ git_object_free(target);
+ target = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_reset_mixed__cannot_reset_in_a_bare_repository(void)
+{
+ git_repository *bare;
+
+ cl_git_pass(git_repository_open(&bare, cl_fixture("testrepo.git")));
+ cl_assert(git_repository_is_bare(bare) == true);
+
+ retrieve_target_from_oid(&target, bare, KNOWN_COMMIT_IN_BARE_REPO);
+
+ cl_assert_equal_i(GIT_EBAREREPO, git_reset(bare, target, GIT_RESET_MIXED));
+
+ git_repository_free(bare);
+}
+
+void test_reset_mixed__resetting_refreshes_the_index_to_the_commit_tree(void)
+{
+ unsigned int status;
+
+ cl_git_pass(git_status_file(&status, repo, "macro_bad"));
+ cl_assert(status == GIT_STATUS_CURRENT);
+ retrieve_target_from_oid(&target, repo, "605812ab7fe421fdd325a935d35cb06a9234a7d7");
+
+ cl_git_pass(git_reset(repo, target, GIT_RESET_MIXED));
+
+ cl_git_pass(git_status_file(&status, repo, "macro_bad"));
+ cl_assert(status == GIT_STATUS_WT_NEW);
+}
diff --git a/tests/reset/reset_helpers.c b/tests/reset/reset_helpers.c
new file mode 100644
index 000000000..17edca4e9
--- /dev/null
+++ b/tests/reset/reset_helpers.c
@@ -0,0 +1,10 @@
+#include "clar_libgit2.h"
+#include "reset_helpers.h"
+
+void retrieve_target_from_oid(git_object **object_out, git_repository *repo, const char *sha)
+{
+ git_oid oid;
+
+ cl_git_pass(git_oid_fromstr(&oid, sha));
+ cl_git_pass(git_object_lookup(object_out, repo, &oid, GIT_OBJ_ANY));
+}
diff --git a/tests/reset/reset_helpers.h b/tests/reset/reset_helpers.h
new file mode 100644
index 000000000..5dbe9d2c7
--- /dev/null
+++ b/tests/reset/reset_helpers.h
@@ -0,0 +1,6 @@
+#include "common.h"
+
+#define KNOWN_COMMIT_IN_BARE_REPO "e90810b8df3e80c413d903f631643c716887138d"
+#define KNOWN_COMMIT_IN_ATTR_REPO "217878ab49e1314388ea2e32dc6fdb58a1b969e0"
+
+extern void retrieve_target_from_oid(git_object **object_out, git_repository *repo, const char *sha);
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/resources/.gitattributes b/tests/resources/.gitattributes
new file mode 100644
index 000000000..556f8c827
--- /dev/null
+++ b/tests/resources/.gitattributes
@@ -0,0 +1 @@
+* binary
diff --git a/tests/resources/.gitignore b/tests/resources/.gitignore
new file mode 100644
index 000000000..43a19cc9d
--- /dev/null
+++ b/tests/resources/.gitignore
@@ -0,0 +1 @@
+discover.git
diff --git a/tests/resources/attr/.gitted/HEAD b/tests/resources/attr/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/attr/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/attr/.gitted/config b/tests/resources/attr/.gitted/config
new file mode 100644
index 000000000..af107929f
--- /dev/null
+++ b/tests/resources/attr/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests/resources/attr/.gitted/description b/tests/resources/attr/.gitted/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/attr/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/attr/.gitted/index b/tests/resources/attr/.gitted/index
new file mode 100644
index 000000000..439ffb151
--- /dev/null
+++ b/tests/resources/attr/.gitted/index
Binary files differ
diff --git a/tests/resources/attr/.gitted/info/attributes b/tests/resources/attr/.gitted/info/attributes
new file mode 100644
index 000000000..5fe62a37a
--- /dev/null
+++ b/tests/resources/attr/.gitted/info/attributes
@@ -0,0 +1,4 @@
+* repoattr
+a* foo !bar -baz
+sub/*.txt reposub
+sub/sub/*.txt reposubsub
diff --git a/tests/resources/attr/.gitted/info/exclude b/tests/resources/attr/.gitted/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/attr/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/attr/.gitted/logs/HEAD b/tests/resources/attr/.gitted/logs/HEAD
new file mode 100644
index 000000000..8ece39f37
--- /dev/null
+++ b/tests/resources/attr/.gitted/logs/HEAD
@@ -0,0 +1,9 @@
+0000000000000000000000000000000000000000 6bab5c79cd5140d0f800917f550eb2a3dc32b0da Russell Belfer <arrbee@arrbee.com> 1324416995 -0800 commit (initial): initial test data
+6bab5c79cd5140d0f800917f550eb2a3dc32b0da 605812ab7fe421fdd325a935d35cb06a9234a7d7 Russell Belfer <arrbee@arrbee.com> 1325143098 -0800 commit: latest test updates
+605812ab7fe421fdd325a935d35cb06a9234a7d7 a5d76cad53f66f1312bd995909a5bab3c0820770 Russell Belfer <arrbee@arrbee.com> 1325281762 -0800 commit: more macro tests
+a5d76cad53f66f1312bd995909a5bab3c0820770 370fe9ec224ce33e71f9e5ec2bd1142ce9937a6a Russell Belfer <arrbee@arrbee.com> 1327611749 -0800 commit: Updating files so we can do diffs
+370fe9ec224ce33e71f9e5ec2bd1142ce9937a6a f5b0af1fb4f5c0cd7aad880711d368a07333c307 Russell Belfer <arrbee@arrbee.com> 1327621027 -0800 commit: Updating test data
+f5b0af1fb4f5c0cd7aad880711d368a07333c307 a97cc019851d401a4f1d091cb91a15890a0dd1ba Russell Belfer <arrbee@arrbee.com> 1328653313 -0800 commit: Some whitespace only changes for testing purposes
+a97cc019851d401a4f1d091cb91a15890a0dd1ba 217878ab49e1314388ea2e32dc6fdb58a1b969e0 Russell Belfer <arrbee@arrbee.com> 1332734901 -0700 commit: added files in sub/sub
+217878ab49e1314388ea2e32dc6fdb58a1b969e0 24fa9a9fc4e202313e24b648087495441dab432b Russell Belfer <arrbee@arrbee.com> 1332735555 -0700 commit: adding more files in sub for tree status
+24fa9a9fc4e202313e24b648087495441dab432b 8d0b9df9bd30be7910ddda60548d485bc302b911 yorah <yoram.harmelin@gmail.com> 1341230701 +0200 commit: Updating test data so we can test inter-hunk-context
diff --git a/tests/resources/attr/.gitted/logs/refs/heads/master b/tests/resources/attr/.gitted/logs/refs/heads/master
new file mode 100644
index 000000000..8ece39f37
--- /dev/null
+++ b/tests/resources/attr/.gitted/logs/refs/heads/master
@@ -0,0 +1,9 @@
+0000000000000000000000000000000000000000 6bab5c79cd5140d0f800917f550eb2a3dc32b0da Russell Belfer <arrbee@arrbee.com> 1324416995 -0800 commit (initial): initial test data
+6bab5c79cd5140d0f800917f550eb2a3dc32b0da 605812ab7fe421fdd325a935d35cb06a9234a7d7 Russell Belfer <arrbee@arrbee.com> 1325143098 -0800 commit: latest test updates
+605812ab7fe421fdd325a935d35cb06a9234a7d7 a5d76cad53f66f1312bd995909a5bab3c0820770 Russell Belfer <arrbee@arrbee.com> 1325281762 -0800 commit: more macro tests
+a5d76cad53f66f1312bd995909a5bab3c0820770 370fe9ec224ce33e71f9e5ec2bd1142ce9937a6a Russell Belfer <arrbee@arrbee.com> 1327611749 -0800 commit: Updating files so we can do diffs
+370fe9ec224ce33e71f9e5ec2bd1142ce9937a6a f5b0af1fb4f5c0cd7aad880711d368a07333c307 Russell Belfer <arrbee@arrbee.com> 1327621027 -0800 commit: Updating test data
+f5b0af1fb4f5c0cd7aad880711d368a07333c307 a97cc019851d401a4f1d091cb91a15890a0dd1ba Russell Belfer <arrbee@arrbee.com> 1328653313 -0800 commit: Some whitespace only changes for testing purposes
+a97cc019851d401a4f1d091cb91a15890a0dd1ba 217878ab49e1314388ea2e32dc6fdb58a1b969e0 Russell Belfer <arrbee@arrbee.com> 1332734901 -0700 commit: added files in sub/sub
+217878ab49e1314388ea2e32dc6fdb58a1b969e0 24fa9a9fc4e202313e24b648087495441dab432b Russell Belfer <arrbee@arrbee.com> 1332735555 -0700 commit: adding more files in sub for tree status
+24fa9a9fc4e202313e24b648087495441dab432b 8d0b9df9bd30be7910ddda60548d485bc302b911 yorah <yoram.harmelin@gmail.com> 1341230701 +0200 commit: Updating test data so we can test inter-hunk-context
diff --git a/tests/resources/attr/.gitted/objects/10/8bb4e7fd7b16490dc33ff7d972151e73d7166e b/tests/resources/attr/.gitted/objects/10/8bb4e7fd7b16490dc33ff7d972151e73d7166e
new file mode 100644
index 000000000..edcf7520c
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/10/8bb4e7fd7b16490dc33ff7d972151e73d7166e
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/16/983da6643656bb44c43965ecb6855c6d574512 b/tests/resources/attr/.gitted/objects/16/983da6643656bb44c43965ecb6855c6d574512
new file mode 100644
index 000000000..e49c94acd
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/16/983da6643656bb44c43965ecb6855c6d574512
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/21/7878ab49e1314388ea2e32dc6fdb58a1b969e0 b/tests/resources/attr/.gitted/objects/21/7878ab49e1314388ea2e32dc6fdb58a1b969e0
new file mode 100644
index 000000000..b537899f2
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/21/7878ab49e1314388ea2e32dc6fdb58a1b969e0
@@ -0,0 +1,4 @@
+xŽQ
+Â0DýÎ)öên“ØDÄ#xƒmvƒ…ÖJ’Þ߀7ðcx0¼IۺΠ­¨‚óž-¹ÌÁñ+e"¼vù‚Á‡œâ˜ùpÑwŽcJH1x‡Ô%Œ”¦HL>Dd¡‰ ïíµxîµê²ÀC—¬®\ʤzÿᔶõdí0Z‘àˆ#¢émÿغþÏÚ°ˆ
+äyÑ
+óê>{Ì–qK² \ No newline at end of file
diff --git a/tests/resources/attr/.gitted/objects/24/fa9a9fc4e202313e24b648087495441dab432b b/tests/resources/attr/.gitted/objects/24/fa9a9fc4e202313e24b648087495441dab432b
new file mode 100644
index 000000000..e7099bbaa
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/24/fa9a9fc4e202313e24b648087495441dab432b
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/29/29de282ce999e95183aedac6451d3384559c4b b/tests/resources/attr/.gitted/objects/29/29de282ce999e95183aedac6451d3384559c4b
new file mode 100644
index 000000000..ad84f0854
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/29/29de282ce999e95183aedac6451d3384559c4b
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/2b/40c5aca159b04ea8d20ffe36cdf8b09369b14a b/tests/resources/attr/.gitted/objects/2b/40c5aca159b04ea8d20ffe36cdf8b09369b14a
new file mode 100644
index 000000000..0e2368069
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/2b/40c5aca159b04ea8d20ffe36cdf8b09369b14a
@@ -0,0 +1 @@
+xmPÑj„0ì«ùŠ=úP8Z…ÞS¾¤”c£ñ hR6{=¼¯obâ™Ò"šÙafvŒšœ‚÷×æéä#3‰ά=7Pÿ%[8<Heì`&]@?aFZª®ªç@!…÷.ÊÈù:±ù½§…•ldðÚLG­|K’7~XÃN8¤˜·IÏdìê}¿øˆqŸó2cG¾œ7l¾5ÔV_pEûŸ®’#lZ´ŽGMòt[JÌÈͽ¥&©h¸±uÌ][‰Öß4‰-3;ËCˆg˜4¿x`ZÀ»YÃŒ“錻ú€bÝ^>ï yNlÍ£¡>c¯;gÓ•¥kÇYXÄ9b|Dª~VØ—)…v¿øñÎÜ• \ No newline at end of file
diff --git a/tests/resources/attr/.gitted/objects/2c/66e14f77196ea763fb1e41612c1aa2bc2d8ed2 b/tests/resources/attr/.gitted/objects/2c/66e14f77196ea763fb1e41612c1aa2bc2d8ed2
new file mode 100644
index 000000000..4b75d50eb
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/2c/66e14f77196ea763fb1e41612c1aa2bc2d8ed2
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/2d/e7dfe3588f3c7e9ad59e7d50ba90e3329df9d9 b/tests/resources/attr/.gitted/objects/2d/e7dfe3588f3c7e9ad59e7d50ba90e3329df9d9
new file mode 100644
index 000000000..e0fd0468e
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/2d/e7dfe3588f3c7e9ad59e7d50ba90e3329df9d9
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/37/0fe9ec224ce33e71f9e5ec2bd1142ce9937a6a b/tests/resources/attr/.gitted/objects/37/0fe9ec224ce33e71f9e5ec2bd1142ce9937a6a
new file mode 100644
index 000000000..9c37c5946
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/37/0fe9ec224ce33e71f9e5ec2bd1142ce9937a6a
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/3a/6df026462ebafe455af9867d27eda20a9e0974 b/tests/resources/attr/.gitted/objects/3a/6df026462ebafe455af9867d27eda20a9e0974
new file mode 100644
index 000000000..c74add826
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/3a/6df026462ebafe455af9867d27eda20a9e0974
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/3b/74db7ab381105dc0d28f8295a77f6a82989292 b/tests/resources/attr/.gitted/objects/3b/74db7ab381105dc0d28f8295a77f6a82989292
new file mode 100644
index 000000000..e5cef35fa
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/3b/74db7ab381105dc0d28f8295a77f6a82989292
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/3e/42ffc54a663f9401cc25843d6c0e71a33e4249 b/tests/resources/attr/.gitted/objects/3e/42ffc54a663f9401cc25843d6c0e71a33e4249
new file mode 100644
index 000000000..091d79b14
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/3e/42ffc54a663f9401cc25843d6c0e71a33e4249
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/45/141a79a77842c59a63229403220a4e4be74e3d b/tests/resources/attr/.gitted/objects/45/141a79a77842c59a63229403220a4e4be74e3d
new file mode 100644
index 000000000..5b58ef024
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/45/141a79a77842c59a63229403220a4e4be74e3d
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/45/5a314fa848d52ae1f11d254da4f60858fc97f4 b/tests/resources/attr/.gitted/objects/45/5a314fa848d52ae1f11d254da4f60858fc97f4
new file mode 100644
index 000000000..f90f0d79c
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/45/5a314fa848d52ae1f11d254da4f60858fc97f4
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/attr/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
new file mode 100644
index 000000000..7ca4ceed5
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/4d/713dc48e6b1bd75b0d61ad078ba9ca3a56745d b/tests/resources/attr/.gitted/objects/4d/713dc48e6b1bd75b0d61ad078ba9ca3a56745d
new file mode 100644
index 000000000..eb1e8d0c5
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/4d/713dc48e6b1bd75b0d61ad078ba9ca3a56745d
@@ -0,0 +1,2 @@
+xÁÁ € @ßWŶàÇ
+|ø§k 9n$¡}gŠ«à:‡îÂ;5°1¥e–4ˆ\k_]‘ÞƒŸÙ­hœD¡k›ý'~ \ No newline at end of file
diff --git a/tests/resources/attr/.gitted/objects/4e/49ba8c5b6c32ff28cd9dcb60be34df50fcc485 b/tests/resources/attr/.gitted/objects/4e/49ba8c5b6c32ff28cd9dcb60be34df50fcc485
new file mode 100644
index 000000000..6fcc549b4
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/4e/49ba8c5b6c32ff28cd9dcb60be34df50fcc485
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/55/6f8c827b8e4a02ad5cab77dca2bcb3e226b0b3 b/tests/resources/attr/.gitted/objects/55/6f8c827b8e4a02ad5cab77dca2bcb3e226b0b3
new file mode 100644
index 000000000..4bcff1faa
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/55/6f8c827b8e4a02ad5cab77dca2bcb3e226b0b3
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/58/19a185d77b03325aaf87cafc771db36f6ddca7 b/tests/resources/attr/.gitted/objects/58/19a185d77b03325aaf87cafc771db36f6ddca7
new file mode 100644
index 000000000..fe34eb63a
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/58/19a185d77b03325aaf87cafc771db36f6ddca7
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/60/5812ab7fe421fdd325a935d35cb06a9234a7d7 b/tests/resources/attr/.gitted/objects/60/5812ab7fe421fdd325a935d35cb06a9234a7d7
new file mode 100644
index 000000000..b0cc51ee6
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/60/5812ab7fe421fdd325a935d35cb06a9234a7d7
@@ -0,0 +1,2 @@
+xNÛ Â0ã;SÜ Ë»•BŒÀ—ËU ¥´JÓý °?¶lÙ–y™çgcáU RbaâÙcG;¸l²ã DqÖ Z©Ê«AH”<Ç‘³×3Nâ¨ãä=J2d3[“0“¢½=–
+÷}Û¤¸I™¤Â™jM"×x™/ ­é[ÇŽØçTwûÇÖãÿ´U¡&[ƒ/ìkþ(õtJL \ No newline at end of file
diff --git a/tests/resources/attr/.gitted/objects/6b/ab5c79cd5140d0f800917f550eb2a3dc32b0da b/tests/resources/attr/.gitted/objects/6b/ab5c79cd5140d0f800917f550eb2a3dc32b0da
new file mode 100644
index 000000000..f51e11ccc
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/6b/ab5c79cd5140d0f800917f550eb2a3dc32b0da
@@ -0,0 +1,3 @@
+xÛ Ã0 Eûí)´@‹d'~@(¥#tÅQ¨ÁiÀQö¯¡ôëÂánÞ·­(Pôm"Ř2æh°s L+d{—"{Zœ“`øÔ÷Þàu‡Ô
+O©«4˜¸µYäñ›[Þ·;³Ã@>¥®M§ýS»þOmʧhá
+*‡ÂÂÊæ ¿<- \ No newline at end of file
diff --git a/tests/resources/attr/.gitted/objects/6d/968d62c89c7d9ea23a4c9a7b665d017c3d8ffd b/tests/resources/attr/.gitted/objects/6d/968d62c89c7d9ea23a4c9a7b665d017c3d8ffd
new file mode 100644
index 000000000..e832241c9
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/6d/968d62c89c7d9ea23a4c9a7b665d017c3d8ffd
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/71/7fc31f6b84f9d6fc3a4edbca259d7fc92beee2 b/tests/resources/attr/.gitted/objects/71/7fc31f6b84f9d6fc3a4edbca259d7fc92beee2
new file mode 100644
index 000000000..a80265cac
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/71/7fc31f6b84f9d6fc3a4edbca259d7fc92beee2
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/8d/0b9df9bd30be7910ddda60548d485bc302b911 b/tests/resources/attr/.gitted/objects/8d/0b9df9bd30be7910ddda60548d485bc302b911
new file mode 100644
index 000000000..3dcf088e4
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/8d/0b9df9bd30be7910ddda60548d485bc302b911
@@ -0,0 +1 @@
+xŽKj1D³Ö)zolôiõŒ _"hiÚK2²L’ÛG!7Ȫ¯¨ÔJÉ,ù—ÑEÀPXÝÆDèÈSŒˆ ] /)Òê}¢Í/èUwîR§ˆ. Åj댋‘pÕë‚Á#š#:?ÇÞ:|·Î;¼þF9íÜ‹Ür=_ çÛ)µòÆ¡±N/ÚÀA[­ÕlçÃ!ÿqÕû}ã‘ë†<Lfx4øH\ÿº\çôqÖcj“¿†úƒTè \ No newline at end of file
diff --git a/tests/resources/attr/.gitted/objects/93/61f40bb97239cf55811892e14de2e344168ba1 b/tests/resources/attr/.gitted/objects/93/61f40bb97239cf55811892e14de2e344168ba1
new file mode 100644
index 000000000..4b57836cd
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/93/61f40bb97239cf55811892e14de2e344168ba1
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/94/da4faa0a6bfb8ee6ccf7153801a69202b31857 b/tests/resources/attr/.gitted/objects/94/da4faa0a6bfb8ee6ccf7153801a69202b31857
new file mode 100644
index 000000000..a9ddf5d20
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/94/da4faa0a6bfb8ee6ccf7153801a69202b31857
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/96/089fd31ce1d3ee2afb0ba09ba063066932f027 b/tests/resources/attr/.gitted/objects/96/089fd31ce1d3ee2afb0ba09ba063066932f027
new file mode 100644
index 000000000..efa62f912
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/96/089fd31ce1d3ee2afb0ba09ba063066932f027
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9 b/tests/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9
new file mode 100644
index 000000000..8f5acc70a
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/9e/5bdc47d6a80f2be0ea3049ad74231b94609242 b/tests/resources/attr/.gitted/objects/9e/5bdc47d6a80f2be0ea3049ad74231b94609242
new file mode 100644
index 000000000..d6385ec8d
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/9e/5bdc47d6a80f2be0ea3049ad74231b94609242
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9 b/tests/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9
new file mode 100644
index 000000000..7663ad0ad
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/a0/f7217ae99f5ac3e88534f5cea267febc5fa85b b/tests/resources/attr/.gitted/objects/a0/f7217ae99f5ac3e88534f5cea267febc5fa85b
new file mode 100644
index 000000000..985c2e281
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/a0/f7217ae99f5ac3e88534f5cea267febc5fa85b
@@ -0,0 +1 @@
+x5Ž1Â0 E™}Š?–΀;•˜SâˆÔ®’”ŠÛ“Ðv´ýߢ8ŸO‡'FÈÈ:2r™ƒ)(¾ &¢Þ·«×9Z¼A Âð³¼Ñ¹r9Ýl¬ %¨˜ˆ„3ÑEo‚£.ÿV­Õi<Bñà F­©MÌb‰®+ÂÙŸ*vµªÛþìÖmõ÷¾¢ÞLK†Ý­D?+­N \ No newline at end of file
diff --git a/tests/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320 b/tests/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320
new file mode 100644
index 000000000..d898ae9b8
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/a5/d76cad53f66f1312bd995909a5bab3c0820770 b/tests/resources/attr/.gitted/objects/a5/d76cad53f66f1312bd995909a5bab3c0820770
new file mode 100644
index 000000000..cd6a389f9
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/a5/d76cad53f66f1312bd995909a5bab3c0820770
@@ -0,0 +1,4 @@
+xŽ]
+‚!E{v³B>!"ZB;u¤à3Cmÿ í § ‡{.7µZŸ4âavfÈÖgBLÊEeP;NQÚ¬BŒLAnŲIÆç ÞÔù5ÁI»)MÑ6Z•œQ[
+h3Úe:
+ ùì}æ£u¸Æà}‡ï…;œ©÷È|ýÅ)µzµ&ô¦¼Óp”›”bÑõq®ú?¶¨­3TJ½Áä1‡ø3ÙJX \ No newline at end of file
diff --git a/tests/resources/attr/.gitted/objects/a9/7cc019851d401a4f1d091cb91a15890a0dd1ba b/tests/resources/attr/.gitted/objects/a9/7cc019851d401a4f1d091cb91a15890a0dd1ba
new file mode 100644
index 000000000..1a7ec0c55
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/a9/7cc019851d401a4f1d091cb91a15890a0dd1ba
@@ -0,0 +1,2 @@
+xŽQjÄ0 DûíSè[ähc;PJéÚ(²¼ $q°–Þ¾†Þ _3oàIÞ÷µÁàÜK+ªàâäBtƒ„I|œ”â»LìgçÆˆÖ ÅR4'=¤qFN6Í÷4
+JôÌ1ôÖFrÑ‘zÃW[r¯«VÝ6øÔ-i7.eVýø‹WÉû;X‚‰,Á ¢émwlÿÏÛ|ç]ṬMëÉ¢ídáã¡RwêC[œW9sÕj~’Wy \ No newline at end of file
diff --git a/tests/resources/attr/.gitted/objects/b4/35cd5689a0fb54afbeda4ac20368aa480e8f04 b/tests/resources/attr/.gitted/objects/b4/35cd5689a0fb54afbeda4ac20368aa480e8f04
new file mode 100644
index 000000000..ffe3473f4
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/b4/35cd5689a0fb54afbeda4ac20368aa480e8f04
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/c0/091889c0c77142b87a1fa5123a6398a61d33e7 b/tests/resources/attr/.gitted/objects/c0/091889c0c77142b87a1fa5123a6398a61d33e7
new file mode 100644
index 000000000..11dc63c79
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/c0/091889c0c77142b87a1fa5123a6398a61d33e7
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/c4/85abe35abd4aa6fd83b076a78bbea9e2e7e06c b/tests/resources/attr/.gitted/objects/c4/85abe35abd4aa6fd83b076a78bbea9e2e7e06c
new file mode 100644
index 000000000..58569ca0e
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/c4/85abe35abd4aa6fd83b076a78bbea9e2e7e06c
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/c7/aadd770d5907a8475c29e9ee21a27b88bf675d b/tests/resources/attr/.gitted/objects/c7/aadd770d5907a8475c29e9ee21a27b88bf675d
new file mode 100644
index 000000000..39aedb7d9
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/c7/aadd770d5907a8475c29e9ee21a27b88bf675d
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/c9/6bbb2c2557a8325ae1559e3ba79cdcecb23076 b/tests/resources/attr/.gitted/objects/c9/6bbb2c2557a8325ae1559e3ba79cdcecb23076
new file mode 100644
index 000000000..589f9ad31
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/c9/6bbb2c2557a8325ae1559e3ba79cdcecb23076
@@ -0,0 +1,2 @@
+x5A
+Â0D]ÿSÌεèoàÂuJ~L0ýͯ¡··)¸xÃcfªœp¹]OOΊcñB µ˜6‘»!뢘´²Ã³‚{,áU<C¿j˜¹[ÁE-ŠÜ-¢Ò˜ð&#š¯)~ëÇäÆ=˜;;{.öùe"3A \ No newline at end of file
diff --git a/tests/resources/attr/.gitted/objects/ce/39a97a7fb1fa90bcf5e711249c1e507476ae0e b/tests/resources/attr/.gitted/objects/ce/39a97a7fb1fa90bcf5e711249c1e507476ae0e
new file mode 100644
index 000000000..1005f944a
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/ce/39a97a7fb1fa90bcf5e711249c1e507476ae0e
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/d5/7da33c16b14326ecb05d19bbea908f5e4c47d9 b/tests/resources/attr/.gitted/objects/d5/7da33c16b14326ecb05d19bbea908f5e4c47d9
new file mode 100644
index 000000000..b96d40c24
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/d5/7da33c16b14326ecb05d19bbea908f5e4c47d9
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2 b/tests/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2
new file mode 100644
index 000000000..83f3b726d
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/dc/cada462d3df8ac6de596fb8c896aba9344f941 b/tests/resources/attr/.gitted/objects/dc/cada462d3df8ac6de596fb8c896aba9344f941
new file mode 100644
index 000000000..ef62f8b9d
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/dc/cada462d3df8ac6de596fb8c896aba9344f941
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/de/863bff4976c9ed7e17a4da0fd524908dc84049 b/tests/resources/attr/.gitted/objects/de/863bff4976c9ed7e17a4da0fd524908dc84049
new file mode 100644
index 000000000..7d9b8551f
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/de/863bff4976c9ed7e17a4da0fd524908dc84049
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/e5/63cf4758f0d646f1b14b76016aa17fa9e549a4 b/tests/resources/attr/.gitted/objects/e5/63cf4758f0d646f1b14b76016aa17fa9e549a4
new file mode 100644
index 000000000..1bc1f0f0b
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/e5/63cf4758f0d646f1b14b76016aa17fa9e549a4
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/ec/b97df2a174987475ac816e3847fc8e9f6c596b b/tests/resources/attr/.gitted/objects/ec/b97df2a174987475ac816e3847fc8e9f6c596b
new file mode 100644
index 000000000..44d703b2e
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/ec/b97df2a174987475ac816e3847fc8e9f6c596b
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/ed/f3dcee4003d71f139777898882ccd097e34c53 b/tests/resources/attr/.gitted/objects/ed/f3dcee4003d71f139777898882ccd097e34c53
new file mode 100644
index 000000000..d28184670
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/ed/f3dcee4003d71f139777898882ccd097e34c53
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/f2/c6d717cf4a5a3e6b02684155ab07b766982165 b/tests/resources/attr/.gitted/objects/f2/c6d717cf4a5a3e6b02684155ab07b766982165
new file mode 100644
index 000000000..27a25dc86
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/f2/c6d717cf4a5a3e6b02684155ab07b766982165
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/f5/b0af1fb4f5c0cd7aad880711d368a07333c307 b/tests/resources/attr/.gitted/objects/f5/b0af1fb4f5c0cd7aad880711d368a07333c307
new file mode 100644
index 000000000..21faeb8a2
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/f5/b0af1fb4f5c0cd7aad880711d368a07333c307
@@ -0,0 +1,2 @@
+xN[j1 Ì·O¡ 4ÈRbÇPJÈ
+=€Ö;NûÂë½ ½A?†y 1“×y~7½žZ(¾¥2ªÏð£beàÁ8uå’Ja‰n³Š¥‘F.HÈ"— UD_®Ý£÷ÉHI£sv´×ZéûØwL=0Tú´ZàþGç¼Î_äUbßKèƒoÌ®§}cëçÿùv?Ûhí½<©aoÔµ¹_áEK \ No newline at end of file
diff --git a/tests/resources/attr/.gitted/objects/fb/5067b1aef3ac1ada4b379dbcb7d17255df7d78 b/tests/resources/attr/.gitted/objects/fb/5067b1aef3ac1ada4b379dbcb7d17255df7d78
new file mode 100644
index 000000000..6c8ff837e
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/fb/5067b1aef3ac1ada4b379dbcb7d17255df7d78
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/fe/773770c5a6cc7185580c9204b1ff18a33ff3fc b/tests/resources/attr/.gitted/objects/fe/773770c5a6cc7185580c9204b1ff18a33ff3fc
new file mode 100644
index 000000000..e6fcbc0b3
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/fe/773770c5a6cc7185580c9204b1ff18a33ff3fc
@@ -0,0 +1 @@
+x5ŽAÂ0 9ûû„xBÜAâœG¤vÕ¤Tüž¤Ð£åõÙ<ûãîÂ#¥Î1ÂUT釛*ÑMúWlÎOCR˜2dÖѵC.„T“©ËÈI¹lQH/öœmYÛ¬UN[àžª€ß¬¬¥þBÖ@t¶Üð8~˜†Õ‹¿}}R#Ä#kAØdD_=-H– \ No newline at end of file
diff --git a/tests/resources/attr/.gitted/objects/ff/69f8639ce2e6010b3f33a74160aad98b48da2b b/tests/resources/attr/.gitted/objects/ff/69f8639ce2e6010b3f33a74160aad98b48da2b
new file mode 100644
index 000000000..b736c0b2b
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/ff/69f8639ce2e6010b3f33a74160aad98b48da2b
Binary files differ
diff --git a/tests/resources/attr/.gitted/refs/heads/master b/tests/resources/attr/.gitted/refs/heads/master
new file mode 100644
index 000000000..b3abfff7d
--- /dev/null
+++ b/tests/resources/attr/.gitted/refs/heads/master
@@ -0,0 +1 @@
+8d0b9df9bd30be7910ddda60548d485bc302b911
diff --git a/tests/resources/attr/attr0 b/tests/resources/attr/attr0
new file mode 100644
index 000000000..556f8c827
--- /dev/null
+++ b/tests/resources/attr/attr0
@@ -0,0 +1 @@
+* binary
diff --git a/tests/resources/attr/attr1 b/tests/resources/attr/attr1
new file mode 100644
index 000000000..3b74db7ab
--- /dev/null
+++ b/tests/resources/attr/attr1
@@ -0,0 +1,29 @@
+# a comment followed by some blank lines
+
+
+
+ # another comment that is indented
+
+# variations on fnmatch
+
+pat0 attr0
+!pat1 attr1
+pat2/ attr2
+pat3dir/pat3file attr3
+pat4.* attr4
+ *.pat5 attr5
+pat6/pat6/*.pat6 attr6
+
+pat7[a-e]??[xyz] attr7 # with a comment on the line
+
+pat8\ with\ spaces attr8
+
+ invalid # attr with no assignments doesn't count
+
+also/invalid
+
+invalid.again/
+
+# next attr is at eof
+
+ pat9 attr9 \ No newline at end of file
diff --git a/tests/resources/attr/attr2 b/tests/resources/attr/attr2
new file mode 100644
index 000000000..2c66e14f7
--- /dev/null
+++ b/tests/resources/attr/attr2
@@ -0,0 +1,21 @@
+
+# variations on assignments
+
+pat0 simple
+pat1 -neg
+* notundef
+pat2 !notundef
+pat3 assigned=test-value
+pat4 rule-with-more-chars=value-with-more-chars
+pat5 empty=
+pat6 -negempty=
+pat7 multiple -single values=1 also=a-really-long-value/* happy=yes!
+# the next line has trailing spaces
+pat8 again= another=12321
+patbad0 # empty assignment does not count
+# next line will be another simple empty assign that should not count
+ patbad1
+
+# BTW I think there are 11 valid rules and two "invalid" empty ones
+
+pat9 -at-eof \ No newline at end of file
diff --git a/tests/resources/attr/attr3 b/tests/resources/attr/attr3
new file mode 100644
index 000000000..c485abe35
--- /dev/null
+++ b/tests/resources/attr/attr3
@@ -0,0 +1,4 @@
+# These are examples from the git-check-attr.1 man page
+*.java diff=java -crlf myAttr
+NoMyAttr.java !myAttr
+README caveat=unspecified
diff --git a/tests/resources/attr/binfile b/tests/resources/attr/binfile
new file mode 100644
index 000000000..d800886d9
--- /dev/null
+++ b/tests/resources/attr/binfile
@@ -0,0 +1 @@
+123 \ No newline at end of file
diff --git a/tests/resources/attr/dir/file b/tests/resources/attr/dir/file
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/resources/attr/dir/file
diff --git a/tests/resources/attr/file b/tests/resources/attr/file
new file mode 100644
index 000000000..45b983be3
--- /dev/null
+++ b/tests/resources/attr/file
@@ -0,0 +1 @@
+hi
diff --git a/tests/resources/attr/gitattributes b/tests/resources/attr/gitattributes
new file mode 100644
index 000000000..e038983ec
--- /dev/null
+++ b/tests/resources/attr/gitattributes
@@ -0,0 +1,29 @@
+* rootattr
+root_test2 -rootattr
+root_test3 !rootattr
+binfile binary
+abc foo bar baz
+does-not-exist foo=yes
+
+root_test2 multiattr
+root_test3 multi2=foo
+
+root_test3 multiattr=1 multiattr=2 multiattr=3 multi2=abc !multi2
+root_test2 multiattr=string -multiattr
+
+[attr]mymacro positive -negative !rootattr
+macro* mymacro another=77
+
+[attr]macro2 multi2 -multi2 multi3 !multi3 multi3=answer
+macro* macro2 macro2 macro2
+
+# let's try some malicious macro defs
+[attr]firstmacro -thirdmacro -secondmacro
+[attr]secondmacro firstmacro -firstmacro
+[attr]thirdmacro secondmacro=hahaha
+
+macro_bad firstmacro secondmacro thirdmacro
+
+# another test that Peff found was failing
+[attr]notest !test
+
diff --git a/tests/resources/attr/gitignore b/tests/resources/attr/gitignore
new file mode 100644
index 000000000..192967012
--- /dev/null
+++ b/tests/resources/attr/gitignore
@@ -0,0 +1,2 @@
+ign
+dir/
diff --git a/tests/resources/attr/ign b/tests/resources/attr/ign
new file mode 100644
index 000000000..592fd2594
--- /dev/null
+++ b/tests/resources/attr/ign
@@ -0,0 +1 @@
+ignore me
diff --git a/tests/resources/attr/macro_bad b/tests/resources/attr/macro_bad
new file mode 100644
index 000000000..5819a185d
--- /dev/null
+++ b/tests/resources/attr/macro_bad
@@ -0,0 +1 @@
+boo
diff --git a/tests/resources/attr/macro_test b/tests/resources/attr/macro_test
new file mode 100644
index 000000000..ff69f8639
--- /dev/null
+++ b/tests/resources/attr/macro_test
@@ -0,0 +1 @@
+Yo
diff --git a/tests/resources/attr/root_test1 b/tests/resources/attr/root_test1
new file mode 100644
index 000000000..45141a79a
--- /dev/null
+++ b/tests/resources/attr/root_test1
@@ -0,0 +1 @@
+Hello from the root
diff --git a/tests/resources/attr/root_test2 b/tests/resources/attr/root_test2
new file mode 100644
index 000000000..4d713dc48
--- /dev/null
+++ b/tests/resources/attr/root_test2
@@ -0,0 +1,6 @@
+Hello from the root
+
+Some additional lines
+
+Down here below
+
diff --git a/tests/resources/attr/root_test3 b/tests/resources/attr/root_test3
new file mode 100644
index 000000000..108bb4e7f
--- /dev/null
+++ b/tests/resources/attr/root_test3
@@ -0,0 +1,19 @@
+Some additional lines
+
+
+ Down here below the other lines
+
+
+With even more at the end
+
+
+And lots of good stuff
+
+
+Anywhere you want
+
+
+Don't you think
+
+
+
diff --git a/tests/resources/attr/root_test4.txt b/tests/resources/attr/root_test4.txt
new file mode 100644
index 000000000..a0f7217ae
--- /dev/null
+++ b/tests/resources/attr/root_test4.txt
@@ -0,0 +1,14 @@
+Here is some stuff at the start
+
+This should go in one hunk (first)
+
+Some additional lines
+
+Down here below the other lines
+
+With even more at the end
+
+Followed by a second hunk of stuff (second)
+
+That happens down here
+
diff --git a/tests/resources/attr/sub/.gitattributes b/tests/resources/attr/sub/.gitattributes
new file mode 100644
index 000000000..329c1c5b8
--- /dev/null
+++ b/tests/resources/attr/sub/.gitattributes
@@ -0,0 +1,7 @@
+* subattr=yes -negattr
+*.txt another=zero
+sub/*.txt another=one
+ab* merge=filfre
+abc -foo -bar
+*.c frotz
+deep/file deepdeep
diff --git a/tests/resources/attr/sub/abc b/tests/resources/attr/sub/abc
new file mode 100644
index 000000000..3e42ffc54
--- /dev/null
+++ b/tests/resources/attr/sub/abc
@@ -0,0 +1,37 @@
+# Test file from gitattributes(5) example:
+
+If you have these three gitattributes file:
+
+ (in $GIT_DIR/info/attributes)
+
+ a* foo !bar -baz
+
+ (in .gitattributes)
+ abc foo bar baz
+
+ (in t/.gitattributes)
+ ab* merge=filfre
+ abc -foo -bar
+ *.c frotz
+
+the attributes given to path t/abc are computed as follows:
+
+1. By examining t/.gitattributes (which is in the same directory as the path
+ in question), git finds that the first line matches. merge attribute is
+ set. It also finds that the second line matches, and attributes foo and
+ bar are unset.
+2. Then it examines .gitattributes (which is in the parent directory), and
+ finds that the first line matches, but t/.gitattributes file already
+ decided how merge, foo and bar attributes should be given to this path,
+ so it leaves foo and bar unset. Attribute baz is set.
+3. Finally it examines $GIT_DIR/info/attributes. This file is used to
+ override the in-tree settings. The first line is a match, and foo is set,
+ bar is reverted to unspecified state, and baz is unset.
+
+As the result, the attributes assignment to t/abc becomes:
+
+ foo set to true
+ bar unspecified
+ baz set to false
+ merge set to string value "filfre"
+ frotz unspecified
diff --git a/tests/resources/attr/sub/dir/file b/tests/resources/attr/sub/dir/file
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/resources/attr/sub/dir/file
diff --git a/tests/resources/attr/sub/file b/tests/resources/attr/sub/file
new file mode 100644
index 000000000..45b983be3
--- /dev/null
+++ b/tests/resources/attr/sub/file
@@ -0,0 +1 @@
+hi
diff --git a/tests/resources/attr/sub/ign/file b/tests/resources/attr/sub/ign/file
new file mode 100644
index 000000000..4dcd992e1
--- /dev/null
+++ b/tests/resources/attr/sub/ign/file
@@ -0,0 +1 @@
+in ignored dir
diff --git a/tests/resources/attr/sub/ign/sub/file b/tests/resources/attr/sub/ign/sub/file
new file mode 100644
index 000000000..88aca0164
--- /dev/null
+++ b/tests/resources/attr/sub/ign/sub/file
@@ -0,0 +1 @@
+below ignored dir
diff --git a/tests/resources/attr/sub/sub/.gitattributes b/tests/resources/attr/sub/sub/.gitattributes
new file mode 100644
index 000000000..55225e4d6
--- /dev/null
+++ b/tests/resources/attr/sub/sub/.gitattributes
@@ -0,0 +1,3 @@
+d/* test=a/b/d/*
+d/yes notest
+
diff --git a/tests/resources/attr/sub/sub/dir b/tests/resources/attr/sub/sub/dir
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/resources/attr/sub/sub/dir
diff --git a/tests/resources/attr/sub/sub/file b/tests/resources/attr/sub/sub/file
new file mode 100644
index 000000000..45b983be3
--- /dev/null
+++ b/tests/resources/attr/sub/sub/file
@@ -0,0 +1 @@
+hi
diff --git a/tests/resources/attr/sub/sub/subsub.txt b/tests/resources/attr/sub/sub/subsub.txt
new file mode 100644
index 000000000..9e5bdc47d
--- /dev/null
+++ b/tests/resources/attr/sub/sub/subsub.txt
@@ -0,0 +1 @@
+subsub
diff --git a/tests/resources/attr/sub/subdir_test1 b/tests/resources/attr/sub/subdir_test1
new file mode 100644
index 000000000..e563cf475
--- /dev/null
+++ b/tests/resources/attr/sub/subdir_test1
@@ -0,0 +1,2 @@
+Hello from the subdir
+
diff --git a/tests/resources/attr/sub/subdir_test2.txt b/tests/resources/attr/sub/subdir_test2.txt
new file mode 100644
index 000000000..fb5067b1a
--- /dev/null
+++ b/tests/resources/attr/sub/subdir_test2.txt
@@ -0,0 +1 @@
+Hello again
diff --git a/tests/resources/attr_index/.gitted/HEAD b/tests/resources/attr_index/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/attr_index/.gitted/config b/tests/resources/attr_index/.gitted/config
new file mode 100644
index 000000000..af107929f
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests/resources/attr_index/.gitted/description b/tests/resources/attr_index/.gitted/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/attr_index/.gitted/index b/tests/resources/attr_index/.gitted/index
new file mode 100644
index 000000000..d87480332
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/index
Binary files differ
diff --git a/tests/resources/attr_index/.gitted/info/exclude b/tests/resources/attr_index/.gitted/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/attr_index/.gitted/info/refs b/tests/resources/attr_index/.gitted/info/refs
new file mode 100644
index 000000000..60feca293
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/info/refs
@@ -0,0 +1 @@
+58f7cf825b553ef7c26e5b9f8a23599c1a9ca296 refs/heads/master
diff --git a/tests/resources/attr_index/.gitted/logs/HEAD b/tests/resources/attr_index/.gitted/logs/HEAD
new file mode 100644
index 000000000..ffd298c04
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/logs/HEAD
@@ -0,0 +1,4 @@
+0000000000000000000000000000000000000000 67c1640e91ccbaf0793591be09bf572cf40c9a53 Russell Belfer <rb@github.com> 1335817070 -0700 commit (initial): Initial commit
+67c1640e91ccbaf0793591be09bf572cf40c9a53 d441d7d88f52c28c2b23940ce4c33756748425f9 Russell Belfer <rb@github.com> 1335817296 -0700 commit: Adding some files in subtrees
+d441d7d88f52c28c2b23940ce4c33756748425f9 67c1640e91ccbaf0793591be09bf572cf40c9a53 Russell Belfer <rb@github.com> 1335817353 -0700 HEAD^: updating HEAD
+67c1640e91ccbaf0793591be09bf572cf40c9a53 58f7cf825b553ef7c26e5b9f8a23599c1a9ca296 Russell Belfer <rb@github.com> 1335817372 -0700 commit: Adding subtree data
diff --git a/tests/resources/attr_index/.gitted/logs/refs/heads/master b/tests/resources/attr_index/.gitted/logs/refs/heads/master
new file mode 100644
index 000000000..ffd298c04
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/logs/refs/heads/master
@@ -0,0 +1,4 @@
+0000000000000000000000000000000000000000 67c1640e91ccbaf0793591be09bf572cf40c9a53 Russell Belfer <rb@github.com> 1335817070 -0700 commit (initial): Initial commit
+67c1640e91ccbaf0793591be09bf572cf40c9a53 d441d7d88f52c28c2b23940ce4c33756748425f9 Russell Belfer <rb@github.com> 1335817296 -0700 commit: Adding some files in subtrees
+d441d7d88f52c28c2b23940ce4c33756748425f9 67c1640e91ccbaf0793591be09bf572cf40c9a53 Russell Belfer <rb@github.com> 1335817353 -0700 HEAD^: updating HEAD
+67c1640e91ccbaf0793591be09bf572cf40c9a53 58f7cf825b553ef7c26e5b9f8a23599c1a9ca296 Russell Belfer <rb@github.com> 1335817372 -0700 commit: Adding subtree data
diff --git a/tests/resources/attr_index/.gitted/objects/38/12cfef36615db1788d4e63f90028007e17a348 b/tests/resources/attr_index/.gitted/objects/38/12cfef36615db1788d4e63f90028007e17a348
new file mode 100644
index 000000000..ee2991571
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/objects/38/12cfef36615db1788d4e63f90028007e17a348
@@ -0,0 +1,3 @@
+x•Ž[
+Ã Eûí*Ü@‹ŽPJé
+]€š™&“`Ìþëúw¸œ 'o¥ÌM¸K«D’ ‚q•4¤ÊËì5DFË#šä!!ˆ=VZ›DÏ.³Lˆ†:ƒ%L}ƒ!dCŽ¬ˆg›¶*ßçqвÈ-LUÞkz~ç6é–·òÚ«íàWå”}í}­«ÿ>Åg˾Õ{fžâú%ñ ¡Gò \ No newline at end of file
diff --git a/tests/resources/attr_index/.gitted/objects/59/d942b8be2784bc96db9b22202c10815c9a077b b/tests/resources/attr_index/.gitted/objects/59/d942b8be2784bc96db9b22202c10815c9a077b
new file mode 100644
index 000000000..ff33737db
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/objects/59/d942b8be2784bc96db9b22202c10815c9a077b
@@ -0,0 +1 @@
+x ÃÑ €0 @¿âÍà‡“¸@kR”’@ßÂ]½ã<¶K4±ÜnÕÔÅY‰á)l(a¨hF˜Hcƒcÿ^Ô \ No newline at end of file
diff --git a/tests/resources/attr_index/.gitted/objects/cd/f17ea3fe625ef812f4dce7f423f4f299287505 b/tests/resources/attr_index/.gitted/objects/cd/f17ea3fe625ef812f4dce7f423f4f299287505
new file mode 100644
index 000000000..2a410057e
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/objects/cd/f17ea3fe625ef812f4dce7f423f4f299287505
Binary files differ
diff --git a/tests/resources/attr_index/.gitted/objects/f7/2502ddd01412bb20796ff812af56fd53b82b52 b/tests/resources/attr_index/.gitted/objects/f7/2502ddd01412bb20796ff812af56fd53b82b52
new file mode 100644
index 000000000..048928000
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/objects/f7/2502ddd01412bb20796ff812af56fd53b82b52
Binary files differ
diff --git a/tests/resources/attr_index/.gitted/objects/info/packs b/tests/resources/attr_index/.gitted/objects/info/packs
new file mode 100644
index 000000000..559dc741c
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/objects/info/packs
@@ -0,0 +1,2 @@
+P pack-4e6438607204ce78827e3885594b2c0bb4f13895.pack
+
diff --git a/tests/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.idx b/tests/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.idx
new file mode 100644
index 000000000..fbef4aa1d
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.idx
Binary files differ
diff --git a/tests/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.pack b/tests/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.pack
new file mode 100644
index 000000000..09c9e06d9
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.pack
Binary files differ
diff --git a/tests/resources/attr_index/.gitted/packed-refs b/tests/resources/attr_index/.gitted/packed-refs
new file mode 100644
index 000000000..6b3e4decf
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+58f7cf825b553ef7c26e5b9f8a23599c1a9ca296 refs/heads/master
diff --git a/tests/resources/attr_index/.gitted/refs/heads/master b/tests/resources/attr_index/.gitted/refs/heads/master
new file mode 100644
index 000000000..9b7562931
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/refs/heads/master
@@ -0,0 +1 @@
+3812cfef36615db1788d4e63f90028007e17a348
diff --git a/tests/resources/attr_index/README.md b/tests/resources/attr_index/README.md
new file mode 100644
index 000000000..59d942b8b
--- /dev/null
+++ b/tests/resources/attr_index/README.md
@@ -0,0 +1 @@
+This is contains tests for when the index and work dir differ
diff --git a/tests/resources/attr_index/README.txt b/tests/resources/attr_index/README.txt
new file mode 100644
index 000000000..874c12b79
--- /dev/null
+++ b/tests/resources/attr_index/README.txt
@@ -0,0 +1 @@
+This contains files for testing when the index and the workdir differ
diff --git a/tests/resources/attr_index/gitattributes b/tests/resources/attr_index/gitattributes
new file mode 100644
index 000000000..cdf17ea3f
--- /dev/null
+++ b/tests/resources/attr_index/gitattributes
@@ -0,0 +1,4 @@
+* bar
+*.txt -foo beep=10
+*.md blargh=goop -bar
+
diff --git a/tests/resources/attr_index/sub/sub/.gitattributes b/tests/resources/attr_index/sub/sub/.gitattributes
new file mode 100644
index 000000000..060c9a261
--- /dev/null
+++ b/tests/resources/attr_index/sub/sub/.gitattributes
@@ -0,0 +1,3 @@
+*.txt another=one again
+*.md bar=1234
+
diff --git a/tests/resources/attr_index/sub/sub/README.md b/tests/resources/attr_index/sub/sub/README.md
new file mode 100644
index 000000000..59652e349
--- /dev/null
+++ b/tests/resources/attr_index/sub/sub/README.md
@@ -0,0 +1 @@
+More testing
diff --git a/tests/resources/attr_index/sub/sub/README.txt b/tests/resources/attr_index/sub/sub/README.txt
new file mode 100644
index 000000000..59652e349
--- /dev/null
+++ b/tests/resources/attr_index/sub/sub/README.txt
@@ -0,0 +1 @@
+More testing
diff --git a/tests/resources/bad_tag.git/HEAD b/tests/resources/bad_tag.git/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/bad_tag.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/bad_tag.git/config b/tests/resources/bad_tag.git/config
new file mode 100644
index 000000000..2f8958058
--- /dev/null
+++ b/tests/resources/bad_tag.git/config
@@ -0,0 +1,5 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = true
+ logallrefupdates = true
diff --git a/tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.idx b/tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.idx
new file mode 100644
index 000000000..c404aa15b
--- /dev/null
+++ b/tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.idx
Binary files differ
diff --git a/tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.pack b/tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.pack
new file mode 100644
index 000000000..90eac5032
--- /dev/null
+++ b/tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.pack
Binary files differ
diff --git a/tests/resources/bad_tag.git/packed-refs b/tests/resources/bad_tag.git/packed-refs
new file mode 100644
index 000000000..9da16459b
--- /dev/null
+++ b/tests/resources/bad_tag.git/packed-refs
@@ -0,0 +1,5 @@
+# pack-refs with: peeled
+eda9f45a2a98d4c17a09d681d88569fa4ea91755 refs/tags/e90810b
+^e90810b8df3e80c413d903f631643c716887138d
+d3bacb8d3ff25876a961b1963b6515170d0151ab refs/tags/hello
+^6dcf9bf7541ee10456529833502442f385010c3d \ No newline at end of file
diff --git a/tests/resources/bad_tag.git/refs/dummy-marker.txt b/tests/resources/bad_tag.git/refs/dummy-marker.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/resources/bad_tag.git/refs/dummy-marker.txt
diff --git a/tests/resources/big.index b/tests/resources/big.index
new file mode 100644
index 000000000..66932f14b
--- /dev/null
+++ b/tests/resources/big.index
Binary files differ
diff --git a/tests/resources/binaryunicode/.gitted/HEAD b/tests/resources/binaryunicode/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/binaryunicode/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/binaryunicode/.gitted/config b/tests/resources/binaryunicode/.gitted/config
new file mode 100644
index 000000000..f9845fe7e
--- /dev/null
+++ b/tests/resources/binaryunicode/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ autocrlf = true
+ logallrefupdates = true
diff --git a/tests/resources/binaryunicode/.gitted/description b/tests/resources/binaryunicode/.gitted/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/binaryunicode/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/binaryunicode/.gitted/index b/tests/resources/binaryunicode/.gitted/index
new file mode 100644
index 000000000..a216d2219
--- /dev/null
+++ b/tests/resources/binaryunicode/.gitted/index
Binary files differ
diff --git a/tests/resources/binaryunicode/.gitted/info/exclude b/tests/resources/binaryunicode/.gitted/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/binaryunicode/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/binaryunicode/.gitted/info/refs b/tests/resources/binaryunicode/.gitted/info/refs
new file mode 100644
index 000000000..128eea7c9
--- /dev/null
+++ b/tests/resources/binaryunicode/.gitted/info/refs
@@ -0,0 +1,3 @@
+39e046d1416a208265b754124d0d197b4c9c0c47 refs/heads/branch1
+9e7d8bcd4d24dd57e3f1179aaf7afe648ff50e80 refs/heads/branch2
+d2a291469f4c11f387600d189313b927ddfe891c refs/heads/master
diff --git a/tests/resources/binaryunicode/.gitted/objects/info/packs b/tests/resources/binaryunicode/.gitted/objects/info/packs
new file mode 100644
index 000000000..c2de8f5cb
--- /dev/null
+++ b/tests/resources/binaryunicode/.gitted/objects/info/packs
@@ -0,0 +1,2 @@
+P pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.pack
+
diff --git a/tests/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.idx b/tests/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.idx
new file mode 100644
index 000000000..8a05b2beb
--- /dev/null
+++ b/tests/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.idx
Binary files differ
diff --git a/tests/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.pack b/tests/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.pack
new file mode 100644
index 000000000..6b5ddc414
--- /dev/null
+++ b/tests/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.pack
Binary files differ
diff --git a/tests/resources/binaryunicode/.gitted/refs/heads/branch1 b/tests/resources/binaryunicode/.gitted/refs/heads/branch1
new file mode 100644
index 000000000..0595fbd31
--- /dev/null
+++ b/tests/resources/binaryunicode/.gitted/refs/heads/branch1
@@ -0,0 +1 @@
+39e046d1416a208265b754124d0d197b4c9c0c47
diff --git a/tests/resources/binaryunicode/.gitted/refs/heads/branch2 b/tests/resources/binaryunicode/.gitted/refs/heads/branch2
new file mode 100644
index 000000000..d86856687
--- /dev/null
+++ b/tests/resources/binaryunicode/.gitted/refs/heads/branch2
@@ -0,0 +1 @@
+9e7d8bcd4d24dd57e3f1179aaf7afe648ff50e80
diff --git a/tests/resources/binaryunicode/.gitted/refs/heads/master b/tests/resources/binaryunicode/.gitted/refs/heads/master
new file mode 100644
index 000000000..552d166da
--- /dev/null
+++ b/tests/resources/binaryunicode/.gitted/refs/heads/master
@@ -0,0 +1 @@
+d2a291469f4c11f387600d189313b927ddfe891c
diff --git a/tests/resources/binaryunicode/file.txt b/tests/resources/binaryunicode/file.txt
new file mode 100644
index 000000000..2255035d4
--- /dev/null
+++ b/tests/resources/binaryunicode/file.txt
@@ -0,0 +1 @@
+Master branch.
diff --git a/tests/resources/blametest.git/HEAD b/tests/resources/blametest.git/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/blametest.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/blametest.git/config b/tests/resources/blametest.git/config
new file mode 100644
index 000000000..c53d818dd
--- /dev/null
+++ b/tests/resources/blametest.git/config
@@ -0,0 +1,5 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = true
+ ignorecase = true
diff --git a/tests/resources/blametest.git/description b/tests/resources/blametest.git/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/blametest.git/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/blametest.git/objects/0c/bab4d45fd61e55a1c9697f9f9cb07a12e15448 b/tests/resources/blametest.git/objects/0c/bab4d45fd61e55a1c9697f9f9cb07a12e15448
new file mode 100644
index 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/resources/config/.gitconfig b/tests/resources/config/.gitconfig
new file mode 100644
index 000000000..fa72bddfc
--- /dev/null
+++ b/tests/resources/config/.gitconfig
@@ -0,0 +1,3 @@
+[core]
+ repositoryformatversion = 5
+ something = 2 \ No newline at end of file
diff --git a/tests/resources/config/config-include b/tests/resources/config/config-include
new file mode 100644
index 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/resources/config/config0 b/tests/resources/config/config0
new file mode 100644
index 000000000..85235c501
--- /dev/null
+++ b/tests/resources/config/config0
@@ -0,0 +1,7 @@
+# This is a test
+; of different comments
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true \ No newline at end of file
diff --git a/tests/resources/config/config1 b/tests/resources/config/config1
new file mode 100644
index 000000000..211dc9e7d
--- /dev/null
+++ b/tests/resources/config/config1
@@ -0,0 +1,5 @@
+# This one checks for case sensitivity
+[this "that"]
+ other = true
+[this "That"]
+ other = yes
diff --git a/tests/resources/config/config10 b/tests/resources/config/config10
new file mode 100644
index 000000000..dde17911b
--- /dev/null
+++ b/tests/resources/config/config10
@@ -0,0 +1 @@
+[empty]
diff --git a/tests/resources/config/config11 b/tests/resources/config/config11
new file mode 100644
index 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/resources/config/config12 b/tests/resources/config/config12
new file mode 100644
index 000000000..b57a81b08
--- /dev/null
+++ b/tests/resources/config/config12
@@ -0,0 +1,7 @@
+[some "section"]
+ test = hi ; comment
+ other = "hello! \" ; ; ; " ; more test
+ multi = "hi, this is a ; \
+multiline comment # with ;\n special chars \
+and other stuff !@#"
+ back = "this is \ba phrase"
diff --git a/tests/resources/config/config13 b/tests/resources/config/config13
new file mode 100644
index 000000000..c1e0c5647
--- /dev/null
+++ b/tests/resources/config/config13
@@ -0,0 +1,2 @@
+[core]
+ editor = \"C:/Program Files/Nonsense/bah.exe\" \"--some option\"
diff --git a/tests/resources/config/config14 b/tests/resources/config/config14
new file mode 100644
index 000000000..ef2198c45
--- /dev/null
+++ b/tests/resources/config/config14
@@ -0,0 +1,4 @@
+[a]
+ b=c
+[d]
+ e = f
diff --git a/tests/resources/config/config15 b/tests/resources/config/config15
new file mode 100644
index 000000000..6d34f817b
--- /dev/null
+++ b/tests/resources/config/config15
@@ -0,0 +1,3 @@
+[core]
+ dummy2 = 7
+ global = 17
diff --git a/tests/resources/config/config16 b/tests/resources/config/config16
new file mode 100644
index 000000000..f25cdb728
--- /dev/null
+++ b/tests/resources/config/config16
@@ -0,0 +1,3 @@
+[core]
+ dummy2 = 28
+ system = 11
diff --git a/tests/resources/config/config17 b/tests/resources/config/config17
new file mode 100644
index 000000000..ca25a86af
--- /dev/null
+++ b/tests/resources/config/config17
@@ -0,0 +1,3 @@
+[core]
+ dummy2 = 7
+ global = 17 \ No newline at end of file
diff --git a/tests/resources/config/config18 b/tests/resources/config/config18
new file mode 100644
index 000000000..cb6fd5ebc
--- /dev/null
+++ b/tests/resources/config/config18
@@ -0,0 +1,5 @@
+[core]
+ int32global = 28
+ int64global = 9223372036854775803
+ boolglobal = true
+ stringglobal = I'm a global config value! \ No newline at end of file
diff --git a/tests/resources/config/config19 b/tests/resources/config/config19
new file mode 100644
index 000000000..f3ae5a640
--- /dev/null
+++ b/tests/resources/config/config19
@@ -0,0 +1,5 @@
+[core]
+ int32global = -1
+ int64global = -2
+ boolglobal = false
+ stringglobal = don't find me! \ No newline at end of file
diff --git a/tests/resources/config/config2 b/tests/resources/config/config2
new file mode 100644
index 000000000..60a389827
--- /dev/null
+++ b/tests/resources/config/config2
@@ -0,0 +1,5 @@
+; This one tests for multiline values
+[this "That"]
+ and = one one one \
+two two \
+three three \ No newline at end of file
diff --git a/tests/resources/config/config20 b/tests/resources/config/config20
new file mode 100644
index 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/resources/config/config3 b/tests/resources/config/config3
new file mode 100644
index 000000000..44a5e50ea
--- /dev/null
+++ b/tests/resources/config/config3
@@ -0,0 +1,3 @@
+# A [section.subsection] header is case-insensitive
+[section.SuBsection]
+ var = hello
diff --git a/tests/resources/config/config4 b/tests/resources/config/config4
new file mode 100644
index 000000000..9dd40419e
--- /dev/null
+++ b/tests/resources/config/config4
@@ -0,0 +1,5 @@
+# A variable name on its own is valid
+[some.section]
+ variable
+# A variable and '=' is accepted, but it's not considered true
+ variableeq =
diff --git a/tests/resources/config/config5 b/tests/resources/config/config5
new file mode 100644
index 000000000..8ab60ccec
--- /dev/null
+++ b/tests/resources/config/config5
@@ -0,0 +1,9 @@
+# Test for number suffixes
+[number]
+ simple = 1
+ k = 1k
+ kk = 1K
+ m = 1m
+ mm = 1M
+ g = 1g
+ gg = 1G
diff --git a/tests/resources/config/config6 b/tests/resources/config/config6
new file mode 100644
index 000000000..0f8f90ac9
--- /dev/null
+++ b/tests/resources/config/config6
@@ -0,0 +1,5 @@
+[valid "subsection"]
+ something = true
+
+[something "else"]
+ something = false
diff --git a/tests/resources/config/config7 b/tests/resources/config/config7
new file mode 100644
index 000000000..6af6fcf25
--- /dev/null
+++ b/tests/resources/config/config7
@@ -0,0 +1,5 @@
+[valid "subsection"]
+ something = a
+; we don't allow anything after closing "
+[sec "subsec"x]
+ bleh = blah
diff --git a/tests/resources/config/config8 b/tests/resources/config/config8
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/resources/config/config8
diff --git a/tests/resources/config/config9 b/tests/resources/config/config9
new file mode 100644
index 000000000..fcaac424e
--- /dev/null
+++ b/tests/resources/config/config9
@@ -0,0 +1,9 @@
+[core]
+ dummy2 = 42
+ verylong = 1
+ dummy = 1
+
+[remote "ab"]
+ url = http://example.com/git/ab
+[remote "abba"]
+ url = http://example.com/git/abba
diff --git a/tests/resources/crlf/.gitted/HEAD b/tests/resources/crlf/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/crlf/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/crlf/.gitted/config b/tests/resources/crlf/.gitted/config
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/resources/crlf/.gitted/config
diff --git a/tests/resources/crlf/.gitted/objects/04/de00b358f13389948756732158eaaaefa1448c b/tests/resources/crlf/.gitted/objects/04/de00b358f13389948756732158eaaaefa1448c
new file mode 100644
index 000000000..c3b7598c0
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/04/de00b358f13389948756732158eaaaefa1448c
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/0a/a76e474d259bd7c13eb726a1396c381db55c88 b/tests/resources/crlf/.gitted/objects/0a/a76e474d259bd7c13eb726a1396c381db55c88
new file mode 100644
index 000000000..e118d6656
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/0a/a76e474d259bd7c13eb726a1396c381db55c88
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/0d/06894e14df22e066763ae906e0ed3eb79c205f b/tests/resources/crlf/.gitted/objects/0d/06894e14df22e066763ae906e0ed3eb79c205f
new file mode 100644
index 000000000..b7a1f3290
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/0d/06894e14df22e066763ae906e0ed3eb79c205f
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/0f/f5a53f19bfd2b5eea1ba550295c47515678987 b/tests/resources/crlf/.gitted/objects/0f/f5a53f19bfd2b5eea1ba550295c47515678987
new file mode 100644
index 000000000..5366acd8c
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/0f/f5a53f19bfd2b5eea1ba550295c47515678987
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/12/faf3c1ea55f572473cec9052fca468c3584ccb b/tests/resources/crlf/.gitted/objects/12/faf3c1ea55f572473cec9052fca468c3584ccb
new file mode 100644
index 000000000..96d5b2f91
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/12/faf3c1ea55f572473cec9052fca468c3584ccb
@@ -0,0 +1 @@
+x¥ŽÝ 1„}NÛ€²ù½,ˆøb6K6œ`.’‹Ø¾QìÀ·™o†ab-åÖA»ë0¡ódXš”•btnr:0¡cä¤yž(*´Y<Bãµå0‡¨q m-y«|TSöα6èGŸsáÙ—Úà’^¡%¸.µlu…#úQgþ?wˆµœ@jëµ”DöèÅ ãlç?gDl÷ ·¾‰7kP \ No newline at end of file
diff --git a/tests/resources/crlf/.gitted/objects/38/1cfe630df902bc29271a202d3277981180e4a6 b/tests/resources/crlf/.gitted/objects/38/1cfe630df902bc29271a202d3277981180e4a6
new file mode 100644
index 000000000..0cf707296
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/38/1cfe630df902bc29271a202d3277981180e4a6
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/79/9770d1cff46753a57db7a066159b5610da6e3a b/tests/resources/crlf/.gitted/objects/79/9770d1cff46753a57db7a066159b5610da6e3a
new file mode 100644
index 000000000..5c701b867
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/79/9770d1cff46753a57db7a066159b5610da6e3a
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/7c/ce67e58173e2b01f7db124ceaabe3183d19c49 b/tests/resources/crlf/.gitted/objects/7c/ce67e58173e2b01f7db124ceaabe3183d19c49
new file mode 100644
index 000000000..8e836aba1
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/7c/ce67e58173e2b01f7db124ceaabe3183d19c49
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/a9/a2e8913c1dbe2812fac5e6b4e0a4bd5d0d5966 b/tests/resources/crlf/.gitted/objects/a9/a2e8913c1dbe2812fac5e6b4e0a4bd5d0d5966
new file mode 100644
index 000000000..33d59f1f1
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/a9/a2e8913c1dbe2812fac5e6b4e0a4bd5d0d5966
@@ -0,0 +1 @@
+xKÊÉOR02aH.ÊIãåÂ$œž  \ No newline at end of file
diff --git a/tests/resources/crlf/.gitted/objects/ba/aa042ab2976f8264e467988e6112ee518ec62e b/tests/resources/crlf/.gitted/objects/ba/aa042ab2976f8264e467988e6112ee518ec62e
new file mode 100644
index 000000000..4c544d5ef
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/ba/aa042ab2976f8264e467988e6112ee518ec62e
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/dc/88e3b917de821e25962bea7ec1f55c4ce2112c b/tests/resources/crlf/.gitted/objects/dc/88e3b917de821e25962bea7ec1f55c4ce2112c
new file mode 100644
index 000000000..3db13aa79
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/dc/88e3b917de821e25962bea7ec1f55c4ce2112c
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/ea/030d3c6cec212069eca698cabaa5b4550f1511 b/tests/resources/crlf/.gitted/objects/ea/030d3c6cec212069eca698cabaa5b4550f1511
new file mode 100644
index 000000000..117dc725a
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/ea/030d3c6cec212069eca698cabaa5b4550f1511
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/fe/085d9ace90cc675b87df15e1aeed0c3a31407f b/tests/resources/crlf/.gitted/objects/fe/085d9ace90cc675b87df15e1aeed0c3a31407f
new file mode 100644
index 000000000..2e8d10b76
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/fe/085d9ace90cc675b87df15e1aeed0c3a31407f
Binary files differ
diff --git a/tests/resources/crlf/.gitted/refs/heads/master b/tests/resources/crlf/.gitted/refs/heads/master
new file mode 100644
index 000000000..a2dbe0c2d
--- /dev/null
+++ b/tests/resources/crlf/.gitted/refs/heads/master
@@ -0,0 +1 @@
+12faf3c1ea55f572473cec9052fca468c3584ccb
diff --git a/tests/resources/crlf/.gitted/refs/heads/utf8 b/tests/resources/crlf/.gitted/refs/heads/utf8
new file mode 100644
index 000000000..4b32f7f91
--- /dev/null
+++ b/tests/resources/crlf/.gitted/refs/heads/utf8
@@ -0,0 +1 @@
+baaa042ab2976f8264e467988e6112ee518ec62e
diff --git a/tests/resources/deprecated-mode.git/HEAD b/tests/resources/deprecated-mode.git/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/deprecated-mode.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/deprecated-mode.git/config b/tests/resources/deprecated-mode.git/config
new file mode 100644
index 000000000..f57351fd5
--- /dev/null
+++ b/tests/resources/deprecated-mode.git/config
@@ -0,0 +1,6 @@
+[core]
+ bare = true
+ repositoryformatversion = 0
+ filemode = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests/resources/deprecated-mode.git/description b/tests/resources/deprecated-mode.git/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/deprecated-mode.git/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/deprecated-mode.git/index b/tests/resources/deprecated-mode.git/index
new file mode 100644
index 000000000..682740603
--- /dev/null
+++ b/tests/resources/deprecated-mode.git/index
Binary files differ
diff --git a/tests/resources/deprecated-mode.git/info/exclude b/tests/resources/deprecated-mode.git/info/exclude
new file mode 100644
index 000000000..6d05881d3
--- /dev/null
+++ b/tests/resources/deprecated-mode.git/info/exclude
@@ -0,0 +1,2 @@
+# File patterns to ignore; see `git help ignore` for more information.
+# Lines that start with '#' are comments.
diff --git a/tests/resources/deprecated-mode.git/objects/06/262edc257418e9987caf999f9a7a3e1547adff b/tests/resources/deprecated-mode.git/objects/06/262edc257418e9987caf999f9a7a3e1547adff
new file mode 100644
index 000000000..a030cf7a7
--- /dev/null
+++ b/tests/resources/deprecated-mode.git/objects/06/262edc257418e9987caf999f9a7a3e1547adff
Binary files differ
diff --git a/tests/resources/deprecated-mode.git/objects/08/10fb7818088ff5ac41ee49199b51473b1bd6c7 b/tests/resources/deprecated-mode.git/objects/08/10fb7818088ff5ac41ee49199b51473b1bd6c7
new file mode 100644
index 000000000..52d56936a
--- /dev/null
+++ b/tests/resources/deprecated-mode.git/objects/08/10fb7818088ff5ac41ee49199b51473b1bd6c7
Binary files differ
diff --git a/tests/resources/deprecated-mode.git/objects/1b/05fdaa881ee45b48cbaa5e9b037d667a47745e b/tests/resources/deprecated-mode.git/objects/1b/05fdaa881ee45b48cbaa5e9b037d667a47745e
new file mode 100644
index 000000000..ae7765a70
--- /dev/null
+++ b/tests/resources/deprecated-mode.git/objects/1b/05fdaa881ee45b48cbaa5e9b037d667a47745e
Binary files differ
diff --git a/tests/resources/deprecated-mode.git/objects/3d/0970ec547fc41ef8a5882dde99c6adce65b021 b/tests/resources/deprecated-mode.git/objects/3d/0970ec547fc41ef8a5882dde99c6adce65b021
new file mode 100644
index 000000000..595283e89
--- /dev/null
+++ b/tests/resources/deprecated-mode.git/objects/3d/0970ec547fc41ef8a5882dde99c6adce65b021
Binary files differ
diff --git a/tests/resources/deprecated-mode.git/refs/heads/master b/tests/resources/deprecated-mode.git/refs/heads/master
new file mode 100644
index 000000000..1e106dfa4
--- /dev/null
+++ b/tests/resources/deprecated-mode.git/refs/heads/master
@@ -0,0 +1 @@
+06262edc257418e9987caf999f9a7a3e1547adff
diff --git a/tests/resources/diff/.gitted/HEAD b/tests/resources/diff/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/diff/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/diff/.gitted/config b/tests/resources/diff/.gitted/config
new file mode 100644
index 000000000..77a27ef1d
--- /dev/null
+++ b/tests/resources/diff/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = false
diff --git a/tests/resources/diff/.gitted/description b/tests/resources/diff/.gitted/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/diff/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/diff/.gitted/index b/tests/resources/diff/.gitted/index
new file mode 100644
index 000000000..e1071874e
--- /dev/null
+++ b/tests/resources/diff/.gitted/index
Binary files differ
diff --git a/tests/resources/diff/.gitted/info/exclude b/tests/resources/diff/.gitted/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/diff/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/diff/.gitted/logs/HEAD b/tests/resources/diff/.gitted/logs/HEAD
new file mode 100644
index 000000000..8c6f6fd18
--- /dev/null
+++ b/tests/resources/diff/.gitted/logs/HEAD
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 d70d245ed97ed2aa596dd1af6536e4bfdb047b69 Russell Belfer <rb@github.com> 1347559804 -0700 commit (initial): initial commit
+d70d245ed97ed2aa596dd1af6536e4bfdb047b69 7a9e0b02e63179929fed24f0a3e0f19168114d10 Russell Belfer <rb@github.com> 1347560491 -0700 commit: some changes
diff --git a/tests/resources/diff/.gitted/logs/refs/heads/master b/tests/resources/diff/.gitted/logs/refs/heads/master
new file mode 100644
index 000000000..8c6f6fd18
--- /dev/null
+++ b/tests/resources/diff/.gitted/logs/refs/heads/master
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 d70d245ed97ed2aa596dd1af6536e4bfdb047b69 Russell Belfer <rb@github.com> 1347559804 -0700 commit (initial): initial commit
+d70d245ed97ed2aa596dd1af6536e4bfdb047b69 7a9e0b02e63179929fed24f0a3e0f19168114d10 Russell Belfer <rb@github.com> 1347560491 -0700 commit: some changes
diff --git a/tests/resources/diff/.gitted/objects/29/ab7053bb4dde0298e03e2c179e890b7dd465a7 b/tests/resources/diff/.gitted/objects/29/ab7053bb4dde0298e03e2c179e890b7dd465a7
new file mode 100644
index 000000000..94f9a676d
--- /dev/null
+++ b/tests/resources/diff/.gitted/objects/29/ab7053bb4dde0298e03e2c179e890b7dd465a7
Binary files differ
diff --git a/tests/resources/diff/.gitted/objects/3e/5bcbad2a68e5bc60a53b8388eea53a1a7ab847 b/tests/resources/diff/.gitted/objects/3e/5bcbad2a68e5bc60a53b8388eea53a1a7ab847
new file mode 100644
index 000000000..9fed523dc
--- /dev/null
+++ b/tests/resources/diff/.gitted/objects/3e/5bcbad2a68e5bc60a53b8388eea53a1a7ab847
Binary files differ
diff --git a/tests/resources/diff/.gitted/objects/54/6c735f16a3b44d9784075c2c0dab2ac9bf1989 b/tests/resources/diff/.gitted/objects/54/6c735f16a3b44d9784075c2c0dab2ac9bf1989
new file mode 100644
index 000000000..d7df4d6a1
--- /dev/null
+++ b/tests/resources/diff/.gitted/objects/54/6c735f16a3b44d9784075c2c0dab2ac9bf1989
Binary files differ
diff --git a/tests/resources/diff/.gitted/objects/7a/9e0b02e63179929fed24f0a3e0f19168114d10 b/tests/resources/diff/.gitted/objects/7a/9e0b02e63179929fed24f0a3e0f19168114d10
new file mode 100644
index 000000000..9bc25eb34
--- /dev/null
+++ b/tests/resources/diff/.gitted/objects/7a/9e0b02e63179929fed24f0a3e0f19168114d10
Binary files differ
diff --git a/tests/resources/diff/.gitted/objects/7b/808f723a8ca90df319682c221187235af76693 b/tests/resources/diff/.gitted/objects/7b/808f723a8ca90df319682c221187235af76693
new file mode 100644
index 000000000..2fd266be6
--- /dev/null
+++ b/tests/resources/diff/.gitted/objects/7b/808f723a8ca90df319682c221187235af76693
Binary files differ
diff --git a/tests/resources/diff/.gitted/objects/88/789109439c1e1c3cd45224001edee5304ed53c b/tests/resources/diff/.gitted/objects/88/789109439c1e1c3cd45224001edee5304ed53c
new file mode 100644
index 000000000..7598b5914
--- /dev/null
+++ b/tests/resources/diff/.gitted/objects/88/789109439c1e1c3cd45224001edee5304ed53c
@@ -0,0 +1 @@
+x+)JMU07g040031QHÌË/ÉH-Ò+©(aÉ)Ž[¼Åwz {Œïj­“û%;¡ÊŠRSrSÁª4Wïö½Ç4ãŽø¼NîÚ+©Ë¶a \ No newline at end of file
diff --git a/tests/resources/diff/.gitted/objects/cb/8294e696339863df760b2ff5d1e275bee72455 b/tests/resources/diff/.gitted/objects/cb/8294e696339863df760b2ff5d1e275bee72455
new file mode 100644
index 000000000..86ebe04fe
--- /dev/null
+++ b/tests/resources/diff/.gitted/objects/cb/8294e696339863df760b2ff5d1e275bee72455
Binary files differ
diff --git a/tests/resources/diff/.gitted/objects/d7/0d245ed97ed2aa596dd1af6536e4bfdb047b69 b/tests/resources/diff/.gitted/objects/d7/0d245ed97ed2aa596dd1af6536e4bfdb047b69
new file mode 100644
index 000000000..99304c4aa
--- /dev/null
+++ b/tests/resources/diff/.gitted/objects/d7/0d245ed97ed2aa596dd1af6536e4bfdb047b69
@@ -0,0 +1 @@
+x•Û !óm·_ׄRB:XÝkVpWpµÿ© ¿‡™9±î{î ,^z#‚œôšŒ7JygÔš¬áA¦„« i1Y©Ù2úV¼ÇyR)𢒨Á½…ç'÷m„[¬û„ÒÑ;®áÊ-çl®ó¯Oô_“å#÷¼ø%Øœv8¤ \ No newline at end of file
diff --git a/tests/resources/diff/.gitted/refs/heads/master b/tests/resources/diff/.gitted/refs/heads/master
new file mode 100644
index 000000000..a83afc38b
--- /dev/null
+++ b/tests/resources/diff/.gitted/refs/heads/master
@@ -0,0 +1 @@
+7a9e0b02e63179929fed24f0a3e0f19168114d10
diff --git a/tests/resources/diff/another.txt b/tests/resources/diff/another.txt
new file mode 100644
index 000000000..d0e0bae4d
--- /dev/null
+++ b/tests/resources/diff/another.txt
@@ -0,0 +1,38 @@
+Git is fast. With Git, nearly all operations are performed locally, giving
+it an huge speed advantage on centralized systems that constantly have to
+communicate with a server somewh3r3.
+
+For testing, large AWS instances were set up in the same availability
+zone. Git and SVN were installed on both machines, the Ruby repository was
+copied to both Git and SVN servers, and common operations were performed on
+both.
+
+In some cases the commands don't match up exactly. Here, matching on the
+lowest common denominator was attempted. For example, the 'commit' tests
+also include the time to push for Git, though most of the time you would not
+actually be pushing to the server immediately after a commit where the two
+commands cannot be separated in SVN.
+
+Note that this is the best case scenario for SVN - a server with no load
+with an 80MB/s bandwidth connection to the client machine. Nearly all of
+these times would be even worse for SVN if that connection was slower, while
+many of the Git times would not be affected.
+
+Clearly, in many of these common version control operations, Git is one or
+two orders of magnitude faster than SVN, even under ideal conditions for
+SVN.
+
+Let's see how common operations stack up against Subversion, a common
+centralized version control system that is similar to CVS or
+Perforce. Smaller is faster.
+
+One place where Git is slower is in the initial clone operation. Here, Git
+One place where Git is slower is in the initial clone operation. Here, Git
+One place where Git is slower is in the initial clone operation. Here, Git
+seen in the above charts, it's not considerably slower for an operation that
+is only performed once.
+
+It's also interesting to note that the size of the data on the client side
+is very similar even though Git also has every version of every file for the
+entire history of the project. This illustrates how efficient it is at
+compressing and storing data on the client side. \ No newline at end of file
diff --git a/tests/resources/diff/readme.txt b/tests/resources/diff/readme.txt
new file mode 100644
index 000000000..beedf288d
--- /dev/null
+++ b/tests/resources/diff/readme.txt
@@ -0,0 +1,36 @@
+The Git feature that r3ally mak3s it stand apart from n3arly 3v3ry other SCM
+out there is its branching model.
+
+Git allows and encourages you to have multiple local branches that can be
+entirely independent of each other. The creation, merging, and deletion of
+those lines of development takes seconds.
+
+Git allows and encourages you to have multiple local branches that can be
+entirely independent of each other. The creation, merging, and deletion of
+those lines of development takes seconds.
+
+This means that you can do things like:
+
+Role-Bas3d Codelin3s. Have a branch that always contains only what goes to
+production, another that you merge work into for testing, and several
+smaller ones for day to day work.
+
+Feature Based Workflow. Create new branches for each new feature you're
+working on so you can seamlessly switch back and forth between them, then
+delete each branch when that feature gets merged into your main line.
+
+Disposable Experimentation. Create a branch to experiment in, realize it's
+not going to work, and just delete it - abandoning the work—with nobody else
+ever seeing it (even if you've pushed other branches in the meantime).
+
+Notably, when you push to a remote repository, you do not have to push all
+share it with others.
+
+Git allows and encourages you to have multiple local branches that can be
+entirely independent of each other. The creation, merging, and deletion of
+those lines of development takes seconds.
+
+There are ways to accomplish some of this with other systems, but the work
+involved is much more difficult and error-prone. Git makes this process
+incredibly easy and it changes the way most developers work when they learn
+it.!
diff --git a/tests/resources/duplicate.git/COMMIT_EDITMSG b/tests/resources/duplicate.git/COMMIT_EDITMSG
new file mode 100644
index 000000000..01f9a2aac
--- /dev/null
+++ b/tests/resources/duplicate.git/COMMIT_EDITMSG
@@ -0,0 +1 @@
+commit
diff --git a/tests/resources/duplicate.git/HEAD b/tests/resources/duplicate.git/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/duplicate.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/duplicate.git/config b/tests/resources/duplicate.git/config
new file mode 100644
index 000000000..a4ef456cb
--- /dev/null
+++ b/tests/resources/duplicate.git/config
@@ -0,0 +1,5 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = true
+ logallrefupdates = true
diff --git a/tests/resources/duplicate.git/description b/tests/resources/duplicate.git/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/duplicate.git/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/duplicate.git/index b/tests/resources/duplicate.git/index
new file mode 100644
index 000000000..a61e1c5ca
--- /dev/null
+++ b/tests/resources/duplicate.git/index
Binary files differ
diff --git a/tests/resources/duplicate.git/info/exclude b/tests/resources/duplicate.git/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/duplicate.git/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/duplicate.git/info/refs b/tests/resources/duplicate.git/info/refs
new file mode 100644
index 000000000..3b5bb03be
--- /dev/null
+++ b/tests/resources/duplicate.git/info/refs
@@ -0,0 +1 @@
+8d2f05c97ef29a4697b37c30fe81c248ef411a23 refs/heads/master
diff --git a/tests/resources/duplicate.git/logs/HEAD b/tests/resources/duplicate.git/logs/HEAD
new file mode 100644
index 000000000..be9b4c6cb
--- /dev/null
+++ b/tests/resources/duplicate.git/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 8d2f05c97ef29a4697b37c30fe81c248ef411a23 Han-Wen Nienhuys <hanwen@xs4all.nl> 1336844322 -0300 commit (initial): commit
diff --git a/tests/resources/duplicate.git/logs/refs/heads/master b/tests/resources/duplicate.git/logs/refs/heads/master
new file mode 100644
index 000000000..be9b4c6cb
--- /dev/null
+++ b/tests/resources/duplicate.git/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 8d2f05c97ef29a4697b37c30fe81c248ef411a23 Han-Wen Nienhuys <hanwen@xs4all.nl> 1336844322 -0300 commit (initial): commit
diff --git a/tests/resources/duplicate.git/objects/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/resources/duplicate.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a b/tests/resources/duplicate.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a
new file mode 100644
index 000000000..6802d4949
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a
Binary files differ
diff --git a/tests/resources/duplicate.git/objects/info/packs b/tests/resources/duplicate.git/objects/info/packs
new file mode 100644
index 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/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.idx b/tests/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.idx
new file mode 100644
index 000000000..fd8abee98
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.idx
Binary files differ
diff --git a/tests/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.pack b/tests/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.pack
new file mode 100644
index 000000000..9879e0869
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.pack
Binary files differ
diff --git a/tests/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.idx b/tests/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.idx
new file mode 100644
index 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/resources/duplicate.git/packed-refs b/tests/resources/duplicate.git/packed-refs
new file mode 100644
index 000000000..9f0d4e434
--- /dev/null
+++ b/tests/resources/duplicate.git/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+8d2f05c97ef29a4697b37c30fe81c248ef411a23 refs/heads/master
diff --git a/tests/resources/duplicate.git/refs/heads/dummy-marker.txt b/tests/resources/duplicate.git/refs/heads/dummy-marker.txt
new file mode 100644
index 000000000..421376db9
--- /dev/null
+++ b/tests/resources/duplicate.git/refs/heads/dummy-marker.txt
@@ -0,0 +1 @@
+dummy
diff --git a/tests/resources/empty_bare.git/HEAD b/tests/resources/empty_bare.git/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/empty_bare.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/empty_bare.git/config b/tests/resources/empty_bare.git/config
new file mode 100644
index 000000000..90e16477b
--- /dev/null
+++ b/tests/resources/empty_bare.git/config
@@ -0,0 +1,7 @@
+[core]
+ repositoryformatversion = 0
+ filemode = false
+ bare = true
+ symlinks = false
+ ignorecase = true
+ hideDotFiles = dotGitOnly
diff --git a/tests/resources/empty_bare.git/description b/tests/resources/empty_bare.git/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/empty_bare.git/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/empty_bare.git/info/exclude b/tests/resources/empty_bare.git/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/empty_bare.git/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/empty_bare.git/objects/info/dummy-marker.txt b/tests/resources/empty_bare.git/objects/info/dummy-marker.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/resources/empty_bare.git/objects/info/dummy-marker.txt
diff --git a/tests/resources/empty_bare.git/objects/pack/dummy-marker.txt b/tests/resources/empty_bare.git/objects/pack/dummy-marker.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/resources/empty_bare.git/objects/pack/dummy-marker.txt
diff --git a/tests/resources/empty_bare.git/refs/heads/dummy-marker.txt b/tests/resources/empty_bare.git/refs/heads/dummy-marker.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/resources/empty_bare.git/refs/heads/dummy-marker.txt
diff --git a/tests/resources/empty_bare.git/refs/tags/dummy-marker.txt b/tests/resources/empty_bare.git/refs/tags/dummy-marker.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/resources/empty_bare.git/refs/tags/dummy-marker.txt
diff --git a/tests/resources/empty_standard_repo/.gitted/HEAD b/tests/resources/empty_standard_repo/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/empty_standard_repo/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/empty_standard_repo/.gitted/config b/tests/resources/empty_standard_repo/.gitted/config
new file mode 100644
index 000000000..78387c50b
--- /dev/null
+++ b/tests/resources/empty_standard_repo/.gitted/config
@@ -0,0 +1,8 @@
+[core]
+ repositoryformatversion = 0
+ filemode = false
+ bare = false
+ logallrefupdates = true
+ symlinks = false
+ ignorecase = true
+ hideDotFiles = dotGitOnly
diff --git a/tests/resources/empty_standard_repo/.gitted/description b/tests/resources/empty_standard_repo/.gitted/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/empty_standard_repo/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/empty_standard_repo/.gitted/info/exclude b/tests/resources/empty_standard_repo/.gitted/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/empty_standard_repo/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/empty_standard_repo/.gitted/objects/info/dummy-marker.txt b/tests/resources/empty_standard_repo/.gitted/objects/info/dummy-marker.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/resources/empty_standard_repo/.gitted/objects/info/dummy-marker.txt
diff --git a/tests/resources/empty_standard_repo/.gitted/objects/pack/dummy-marker.txt b/tests/resources/empty_standard_repo/.gitted/objects/pack/dummy-marker.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/resources/empty_standard_repo/.gitted/objects/pack/dummy-marker.txt
diff --git a/tests/resources/empty_standard_repo/.gitted/refs/heads/dummy-marker.txt b/tests/resources/empty_standard_repo/.gitted/refs/heads/dummy-marker.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/resources/empty_standard_repo/.gitted/refs/heads/dummy-marker.txt
diff --git a/tests/resources/empty_standard_repo/.gitted/refs/tags/dummy-marker.txt b/tests/resources/empty_standard_repo/.gitted/refs/tags/dummy-marker.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/resources/empty_standard_repo/.gitted/refs/tags/dummy-marker.txt
diff --git a/tests/resources/filemodes/.gitted/HEAD b/tests/resources/filemodes/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/filemodes/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/filemodes/.gitted/config b/tests/resources/filemodes/.gitted/config
new file mode 100644
index 000000000..af107929f
--- /dev/null
+++ b/tests/resources/filemodes/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests/resources/filemodes/.gitted/description b/tests/resources/filemodes/.gitted/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/filemodes/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/filemodes/.gitted/index b/tests/resources/filemodes/.gitted/index
new file mode 100644
index 000000000..b1b175a9b
--- /dev/null
+++ b/tests/resources/filemodes/.gitted/index
Binary files differ
diff --git a/tests/resources/filemodes/.gitted/info/exclude b/tests/resources/filemodes/.gitted/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/filemodes/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/filemodes/.gitted/logs/HEAD b/tests/resources/filemodes/.gitted/logs/HEAD
new file mode 100644
index 000000000..1cb6a84c1
--- /dev/null
+++ b/tests/resources/filemodes/.gitted/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 9962c8453ba6f0cf8dac7c5dcc2fa2897fa9964a Russell Belfer <rb@github.com> 1338847682 -0700 commit (initial): Initial commit of test data
diff --git a/tests/resources/filemodes/.gitted/logs/refs/heads/master b/tests/resources/filemodes/.gitted/logs/refs/heads/master
new file mode 100644
index 000000000..1cb6a84c1
--- /dev/null
+++ b/tests/resources/filemodes/.gitted/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 9962c8453ba6f0cf8dac7c5dcc2fa2897fa9964a Russell Belfer <rb@github.com> 1338847682 -0700 commit (initial): Initial commit of test data
diff --git a/tests/resources/filemodes/.gitted/objects/99/62c8453ba6f0cf8dac7c5dcc2fa2897fa9964a b/tests/resources/filemodes/.gitted/objects/99/62c8453ba6f0cf8dac7c5dcc2fa2897fa9964a
new file mode 100644
index 000000000..cbd2b557a
--- /dev/null
+++ b/tests/resources/filemodes/.gitted/objects/99/62c8453ba6f0cf8dac7c5dcc2fa2897fa9964a
Binary files differ
diff --git a/tests/resources/filemodes/.gitted/objects/a5/c5dd0fc6c313159a69b1d19d7f61a9f978e8f1 b/tests/resources/filemodes/.gitted/objects/a5/c5dd0fc6c313159a69b1d19d7f61a9f978e8f1
new file mode 100644
index 000000000..a9eaf2cba
--- /dev/null
+++ b/tests/resources/filemodes/.gitted/objects/a5/c5dd0fc6c313159a69b1d19d7f61a9f978e8f1
Binary files differ
diff --git a/tests/resources/filemodes/.gitted/objects/e7/48d196331bcb20267eaaee4ff3326cb73b8182 b/tests/resources/filemodes/.gitted/objects/e7/48d196331bcb20267eaaee4ff3326cb73b8182
new file mode 100644
index 000000000..98066029c
--- /dev/null
+++ b/tests/resources/filemodes/.gitted/objects/e7/48d196331bcb20267eaaee4ff3326cb73b8182
Binary files differ
diff --git a/tests/resources/filemodes/.gitted/refs/heads/master b/tests/resources/filemodes/.gitted/refs/heads/master
new file mode 100644
index 000000000..9822d2d3f
--- /dev/null
+++ b/tests/resources/filemodes/.gitted/refs/heads/master
@@ -0,0 +1 @@
+9962c8453ba6f0cf8dac7c5dcc2fa2897fa9964a
diff --git a/tests/resources/filemodes/exec_off b/tests/resources/filemodes/exec_off
new file mode 100644
index 000000000..a5c5dd0fc
--- /dev/null
+++ b/tests/resources/filemodes/exec_off
@@ -0,0 +1 @@
+Howdy
diff --git a/tests/resources/filemodes/exec_off2on_staged b/tests/resources/filemodes/exec_off2on_staged
new file mode 100755
index 000000000..a5c5dd0fc
--- /dev/null
+++ b/tests/resources/filemodes/exec_off2on_staged
@@ -0,0 +1 @@
+Howdy
diff --git a/tests/resources/filemodes/exec_off2on_workdir b/tests/resources/filemodes/exec_off2on_workdir
new file mode 100755
index 000000000..a5c5dd0fc
--- /dev/null
+++ b/tests/resources/filemodes/exec_off2on_workdir
@@ -0,0 +1 @@
+Howdy
diff --git a/tests/resources/filemodes/exec_off_untracked b/tests/resources/filemodes/exec_off_untracked
new file mode 100644
index 000000000..a5c5dd0fc
--- /dev/null
+++ b/tests/resources/filemodes/exec_off_untracked
@@ -0,0 +1 @@
+Howdy
diff --git a/tests/resources/filemodes/exec_on b/tests/resources/filemodes/exec_on
new file mode 100755
index 000000000..a5c5dd0fc
--- /dev/null
+++ b/tests/resources/filemodes/exec_on
@@ -0,0 +1 @@
+Howdy
diff --git a/tests/resources/filemodes/exec_on2off_staged b/tests/resources/filemodes/exec_on2off_staged
new file mode 100644
index 000000000..a5c5dd0fc
--- /dev/null
+++ b/tests/resources/filemodes/exec_on2off_staged
@@ -0,0 +1 @@
+Howdy
diff --git a/tests/resources/filemodes/exec_on2off_workdir b/tests/resources/filemodes/exec_on2off_workdir
new file mode 100644
index 000000000..a5c5dd0fc
--- /dev/null
+++ b/tests/resources/filemodes/exec_on2off_workdir
@@ -0,0 +1 @@
+Howdy
diff --git a/tests/resources/filemodes/exec_on_untracked b/tests/resources/filemodes/exec_on_untracked
new file mode 100755
index 000000000..a5c5dd0fc
--- /dev/null
+++ b/tests/resources/filemodes/exec_on_untracked
@@ -0,0 +1 @@
+Howdy
diff --git a/tests/resources/gitgit.index b/tests/resources/gitgit.index
new file mode 100644
index 000000000..215da649e
--- /dev/null
+++ b/tests/resources/gitgit.index
Binary files differ
diff --git a/tests/resources/icase/.gitted/HEAD b/tests/resources/icase/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/icase/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/icase/.gitted/config b/tests/resources/icase/.gitted/config
new file mode 100644
index 000000000..bb4d11c1f
--- /dev/null
+++ b/tests/resources/icase/.gitted/config
@@ -0,0 +1,7 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
+ precomposeunicode = false
diff --git a/tests/resources/icase/.gitted/description b/tests/resources/icase/.gitted/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/icase/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/icase/.gitted/index b/tests/resources/icase/.gitted/index
new file mode 100644
index 000000000..f8288ec13
--- /dev/null
+++ b/tests/resources/icase/.gitted/index
Binary files differ
diff --git a/tests/resources/icase/.gitted/info/exclude b/tests/resources/icase/.gitted/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/icase/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/icase/.gitted/logs/HEAD b/tests/resources/icase/.gitted/logs/HEAD
new file mode 100644
index 000000000..3b16bd163
--- /dev/null
+++ b/tests/resources/icase/.gitted/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 76d6e1d231b1085fcce151427e9899335de74be6 Russell Belfer <rb@github.com> 1359157123 -0800 commit (initial): initial commit
diff --git a/tests/resources/icase/.gitted/logs/refs/heads/master b/tests/resources/icase/.gitted/logs/refs/heads/master
new file mode 100644
index 000000000..3b16bd163
--- /dev/null
+++ b/tests/resources/icase/.gitted/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 76d6e1d231b1085fcce151427e9899335de74be6 Russell Belfer <rb@github.com> 1359157123 -0800 commit (initial): initial commit
diff --git a/tests/resources/icase/.gitted/objects/3e/257c57f136a1cb8f2b8e9a2e5bc8ec0258bdce b/tests/resources/icase/.gitted/objects/3e/257c57f136a1cb8f2b8e9a2e5bc8ec0258bdce
new file mode 100644
index 000000000..10691c788
--- /dev/null
+++ b/tests/resources/icase/.gitted/objects/3e/257c57f136a1cb8f2b8e9a2e5bc8ec0258bdce
Binary files differ
diff --git a/tests/resources/icase/.gitted/objects/4d/d6027d083575c7431396dc2a3174afeb393c93 b/tests/resources/icase/.gitted/objects/4d/d6027d083575c7431396dc2a3174afeb393c93
new file mode 100644
index 000000000..8ca70df17
--- /dev/null
+++ b/tests/resources/icase/.gitted/objects/4d/d6027d083575c7431396dc2a3174afeb393c93
Binary files differ
diff --git a/tests/resources/icase/.gitted/objects/62/e0af52c199ec731fe4ad230041cd3286192d49 b/tests/resources/icase/.gitted/objects/62/e0af52c199ec731fe4ad230041cd3286192d49
new file mode 100644
index 000000000..e264aeab3
--- /dev/null
+++ b/tests/resources/icase/.gitted/objects/62/e0af52c199ec731fe4ad230041cd3286192d49
Binary files differ
diff --git a/tests/resources/icase/.gitted/objects/76/d6e1d231b1085fcce151427e9899335de74be6 b/tests/resources/icase/.gitted/objects/76/d6e1d231b1085fcce151427e9899335de74be6
new file mode 100644
index 000000000..24a4b3ee3
--- /dev/null
+++ b/tests/resources/icase/.gitted/objects/76/d6e1d231b1085fcce151427e9899335de74be6
@@ -0,0 +1,3 @@
+x•Û 1EýNÓ€’c² ‹X‚dƉÈÈ&ý°/çœËußsãñÔ›8±è}2î SH–‚,Ñ
+am1ЋEÅÑ·Úà9ŽCJ‡”$ nîïÜ·A®û
+ÆábÐëଃÖj®ó¯Oô_SåOî9ø%Ô)Œ9š \ No newline at end of file
diff --git a/tests/resources/icase/.gitted/objects/d4/4e18fb93b7107b5cd1b95d601591d77869a1b6 b/tests/resources/icase/.gitted/objects/d4/4e18fb93b7107b5cd1b95d601591d77869a1b6
new file mode 100644
index 000000000..32d8c499f
--- /dev/null
+++ b/tests/resources/icase/.gitted/objects/d4/4e18fb93b7107b5cd1b95d601591d77869a1b6
Binary files differ
diff --git a/tests/resources/icase/.gitted/refs/heads/master b/tests/resources/icase/.gitted/refs/heads/master
new file mode 100644
index 000000000..37410ec2a
--- /dev/null
+++ b/tests/resources/icase/.gitted/refs/heads/master
@@ -0,0 +1 @@
+76d6e1d231b1085fcce151427e9899335de74be6
diff --git a/tests/resources/icase/B b/tests/resources/icase/B
new file mode 100644
index 000000000..d44e18fb9
--- /dev/null
+++ b/tests/resources/icase/B
@@ -0,0 +1 @@
+start
diff --git a/tests/resources/icase/D b/tests/resources/icase/D
new file mode 100644
index 000000000..d44e18fb9
--- /dev/null
+++ b/tests/resources/icase/D
@@ -0,0 +1 @@
+start
diff --git a/tests/resources/icase/F b/tests/resources/icase/F
new file mode 100644
index 000000000..d44e18fb9
--- /dev/null
+++ b/tests/resources/icase/F
@@ -0,0 +1 @@
+start
diff --git a/tests/resources/icase/H b/tests/resources/icase/H
new file mode 100644
index 000000000..d44e18fb9
--- /dev/null
+++ b/tests/resources/icase/H
@@ -0,0 +1 @@
+start
diff --git a/tests/resources/icase/J b/tests/resources/icase/J
new file mode 100644
index 000000000..d44e18fb9
--- /dev/null
+++ b/tests/resources/icase/J
@@ -0,0 +1 @@
+start
diff --git a/tests/resources/icase/L/1 b/tests/resources/icase/L/1
new file mode 100644
index 000000000..62e0af52c
--- /dev/null
+++ b/tests/resources/icase/L/1
@@ -0,0 +1 @@
+sub
diff --git a/tests/resources/icase/L/B b/tests/resources/icase/L/B
new file mode 100644
index 000000000..62e0af52c
--- /dev/null
+++ b/tests/resources/icase/L/B
@@ -0,0 +1 @@
+sub
diff --git a/tests/resources/icase/L/D b/tests/resources/icase/L/D
new file mode 100644
index 000000000..62e0af52c
--- /dev/null
+++ b/tests/resources/icase/L/D
@@ -0,0 +1 @@
+sub
diff --git a/tests/resources/icase/L/a b/tests/resources/icase/L/a
new file mode 100644
index 000000000..62e0af52c
--- /dev/null
+++ b/tests/resources/icase/L/a
@@ -0,0 +1 @@
+sub
diff --git a/tests/resources/icase/L/c b/tests/resources/icase/L/c
new file mode 100644
index 000000000..62e0af52c
--- /dev/null
+++ b/tests/resources/icase/L/c
@@ -0,0 +1 @@
+sub
diff --git a/tests/resources/icase/a b/tests/resources/icase/a
new file mode 100644
index 000000000..d44e18fb9
--- /dev/null
+++ b/tests/resources/icase/a
@@ -0,0 +1 @@
+start
diff --git a/tests/resources/icase/c b/tests/resources/icase/c
new file mode 100644
index 000000000..d44e18fb9
--- /dev/null
+++ b/tests/resources/icase/c
@@ -0,0 +1 @@
+start
diff --git a/tests/resources/icase/e b/tests/resources/icase/e
new file mode 100644
index 000000000..d44e18fb9
--- /dev/null
+++ b/tests/resources/icase/e
@@ -0,0 +1 @@
+start
diff --git a/tests/resources/icase/g b/tests/resources/icase/g
new file mode 100644
index 000000000..d44e18fb9
--- /dev/null
+++ b/tests/resources/icase/g
@@ -0,0 +1 @@
+start
diff --git a/tests/resources/icase/i b/tests/resources/icase/i
new file mode 100644
index 000000000..d44e18fb9
--- /dev/null
+++ b/tests/resources/icase/i
@@ -0,0 +1 @@
+start
diff --git a/tests/resources/icase/k/1 b/tests/resources/icase/k/1
new file mode 100644
index 000000000..62e0af52c
--- /dev/null
+++ b/tests/resources/icase/k/1
@@ -0,0 +1 @@
+sub
diff --git a/tests/resources/icase/k/B b/tests/resources/icase/k/B
new file mode 100644
index 000000000..62e0af52c
--- /dev/null
+++ b/tests/resources/icase/k/B
@@ -0,0 +1 @@
+sub
diff --git a/tests/resources/icase/k/D b/tests/resources/icase/k/D
new file mode 100644
index 000000000..62e0af52c
--- /dev/null
+++ b/tests/resources/icase/k/D
@@ -0,0 +1 @@
+sub
diff --git a/tests/resources/icase/k/a b/tests/resources/icase/k/a
new file mode 100644
index 000000000..62e0af52c
--- /dev/null
+++ b/tests/resources/icase/k/a
@@ -0,0 +1 @@
+sub
diff --git a/tests/resources/icase/k/c b/tests/resources/icase/k/c
new file mode 100644
index 000000000..62e0af52c
--- /dev/null
+++ b/tests/resources/icase/k/c
@@ -0,0 +1 @@
+sub
diff --git a/tests/resources/issue_1397/.gitted/HEAD b/tests/resources/issue_1397/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/issue_1397/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/issue_1397/.gitted/config b/tests/resources/issue_1397/.gitted/config
new file mode 100644
index 000000000..ba5bbde24
--- /dev/null
+++ b/tests/resources/issue_1397/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ bare = false
+ repositoryformatversion = 0
+ filemode = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests/resources/issue_1397/.gitted/index b/tests/resources/issue_1397/.gitted/index
new file mode 100644
index 000000000..fa0f541d6
--- /dev/null
+++ b/tests/resources/issue_1397/.gitted/index
Binary files differ
diff --git a/tests/resources/issue_1397/.gitted/objects/7f/483a738f867e5b21c8f377d70311f011eb48b5 b/tests/resources/issue_1397/.gitted/objects/7f/483a738f867e5b21c8f377d70311f011eb48b5
new file mode 100644
index 000000000..63bcb5d76
--- /dev/null
+++ b/tests/resources/issue_1397/.gitted/objects/7f/483a738f867e5b21c8f377d70311f011eb48b5
@@ -0,0 +1,3 @@
+xÎQNÃ0 €ažs
+¿£!'nâTBÓî°¸ŽC+Öe\Äv€ÿӯǾoÑËèfPƒ¦PL8FΆ‘ÒÌ%ª_Ä‹b4æIÜ—tk²°Uœ¸êLdeIÁòd<3/˜|0¥šjÈää1Ö£ÃõÛ\Gßô³c…wÛe»]ô~úùߊÁS
+)ÏGxEèôÿqØsµÓUÚ‡8šÁmkæ~yIæ \ No newline at end of file
diff --git a/tests/resources/issue_1397/.gitted/objects/83/12e0889a9cbab77c732b6bc39b51a683e3a318 b/tests/resources/issue_1397/.gitted/objects/83/12e0889a9cbab77c732b6bc39b51a683e3a318
new file mode 100644
index 000000000..06b59fede
--- /dev/null
+++ b/tests/resources/issue_1397/.gitted/objects/83/12e0889a9cbab77c732b6bc39b51a683e3a318
Binary files differ
diff --git a/tests/resources/issue_1397/.gitted/objects/8a/7ef047fc933edb62e84e7977b0612ec3f6f283 b/tests/resources/issue_1397/.gitted/objects/8a/7ef047fc933edb62e84e7977b0612ec3f6f283
new file mode 100644
index 000000000..19cfbeae2
--- /dev/null
+++ b/tests/resources/issue_1397/.gitted/objects/8a/7ef047fc933edb62e84e7977b0612ec3f6f283
Binary files differ
diff --git a/tests/resources/issue_1397/.gitted/objects/8e/8f80088a9274fd23584992f587083ca1bcbbac b/tests/resources/issue_1397/.gitted/objects/8e/8f80088a9274fd23584992f587083ca1bcbbac
new file mode 100644
index 000000000..f5c776b17
--- /dev/null
+++ b/tests/resources/issue_1397/.gitted/objects/8e/8f80088a9274fd23584992f587083ca1bcbbac
Binary files differ
diff --git a/tests/resources/issue_1397/.gitted/objects/f2/c62dea0372a0578e053697d5c1ba1ac05e774a b/tests/resources/issue_1397/.gitted/objects/f2/c62dea0372a0578e053697d5c1ba1ac05e774a
new file mode 100644
index 000000000..f932f3618
--- /dev/null
+++ b/tests/resources/issue_1397/.gitted/objects/f2/c62dea0372a0578e053697d5c1ba1ac05e774a
Binary files differ
diff --git a/tests/resources/issue_1397/.gitted/objects/ff/3578d64d199d5b48d92bbb569e0a273e411741 b/tests/resources/issue_1397/.gitted/objects/ff/3578d64d199d5b48d92bbb569e0a273e411741
new file mode 100644
index 000000000..fbd731727
--- /dev/null
+++ b/tests/resources/issue_1397/.gitted/objects/ff/3578d64d199d5b48d92bbb569e0a273e411741
Binary files differ
diff --git a/tests/resources/issue_1397/.gitted/refs/heads/master b/tests/resources/issue_1397/.gitted/refs/heads/master
new file mode 100644
index 000000000..285bc08ff
--- /dev/null
+++ b/tests/resources/issue_1397/.gitted/refs/heads/master
@@ -0,0 +1 @@
+7f483a738f867e5b21c8f377d70311f011eb48b5
diff --git a/tests/resources/issue_1397/crlf_file.txt b/tests/resources/issue_1397/crlf_file.txt
new file mode 100644
index 000000000..8312e0889
--- /dev/null
+++ b/tests/resources/issue_1397/crlf_file.txt
@@ -0,0 +1,3 @@
+first line
+second line
+both with crlf \ No newline at end of file
diff --git a/tests/resources/issue_1397/some_other_crlf_file.txt b/tests/resources/issue_1397/some_other_crlf_file.txt
new file mode 100644
index 000000000..8e8f80088
--- /dev/null
+++ b/tests/resources/issue_1397/some_other_crlf_file.txt
@@ -0,0 +1,3 @@
+first line
+second line with some change
+both with crlf \ No newline at end of file
diff --git a/tests/resources/issue_592/.gitted/COMMIT_EDITMSG b/tests/resources/issue_592/.gitted/COMMIT_EDITMSG
new file mode 100644
index 000000000..5852f4463
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/COMMIT_EDITMSG
@@ -0,0 +1 @@
+Initial commit
diff --git a/tests/resources/issue_592/.gitted/HEAD b/tests/resources/issue_592/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/issue_592/.gitted/config b/tests/resources/issue_592/.gitted/config
new file mode 100644
index 000000000..78387c50b
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/config
@@ -0,0 +1,8 @@
+[core]
+ repositoryformatversion = 0
+ filemode = false
+ bare = false
+ logallrefupdates = true
+ symlinks = false
+ ignorecase = true
+ hideDotFiles = dotGitOnly
diff --git a/tests/resources/issue_592/.gitted/index b/tests/resources/issue_592/.gitted/index
new file mode 100644
index 000000000..be7a29d99
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/index
Binary files differ
diff --git a/tests/resources/issue_592/.gitted/info/exclude b/tests/resources/issue_592/.gitted/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/issue_592/.gitted/logs/HEAD b/tests/resources/issue_592/.gitted/logs/HEAD
new file mode 100644
index 000000000..f19fe35a6
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/logs/HEAD
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 4d383e87f0371ba8fa353f3912db6862b2625e85 nulltoken <emeric.fermas@gmail.com> 1331989635 +0100 commit (initial): Initial commit
+4d383e87f0371ba8fa353f3912db6862b2625e85 e38fcc7a6060f5eb5b876e836b52ae4769363f21 nulltoken <emeric.fermas@gmail.com> 1332227062 +0100 commit (amend): Initial commit
diff --git a/tests/resources/issue_592/.gitted/logs/refs/heads/master b/tests/resources/issue_592/.gitted/logs/refs/heads/master
new file mode 100644
index 000000000..f19fe35a6
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/logs/refs/heads/master
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 4d383e87f0371ba8fa353f3912db6862b2625e85 nulltoken <emeric.fermas@gmail.com> 1331989635 +0100 commit (initial): Initial commit
+4d383e87f0371ba8fa353f3912db6862b2625e85 e38fcc7a6060f5eb5b876e836b52ae4769363f21 nulltoken <emeric.fermas@gmail.com> 1332227062 +0100 commit (amend): Initial commit
diff --git a/tests/resources/issue_592/.gitted/objects/06/07ee9d4ccce8e4c4fa13c2c7d727e7faba4e0e b/tests/resources/issue_592/.gitted/objects/06/07ee9d4ccce8e4c4fa13c2c7d727e7faba4e0e
new file mode 100644
index 000000000..05dec10f7
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/objects/06/07ee9d4ccce8e4c4fa13c2c7d727e7faba4e0e
Binary files differ
diff --git a/tests/resources/issue_592/.gitted/objects/49/363a72a90d9424240258cd3759f23788ecf1d8 b/tests/resources/issue_592/.gitted/objects/49/363a72a90d9424240258cd3759f23788ecf1d8
new file mode 100644
index 000000000..e997e1b49
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/objects/49/363a72a90d9424240258cd3759f23788ecf1d8
Binary files differ
diff --git a/tests/resources/issue_592/.gitted/objects/4d/383e87f0371ba8fa353f3912db6862b2625e85 b/tests/resources/issue_592/.gitted/objects/4d/383e87f0371ba8fa353f3912db6862b2625e85
new file mode 100644
index 000000000..c49a8be58
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/objects/4d/383e87f0371ba8fa353f3912db6862b2625e85
@@ -0,0 +1,2 @@
+xM
+Â0]ço/”¤I›DÜzŒçë æbz ÞÀíÀÌHÍ9v2Ëtê =k¬›,pâ+£øÍ>ðƒ4ïýU•=¥^ß(tAF‹2´ÌŸÛ3sLƒÔ|%c­Y—u¶µÑZô˜vü©«{‰=r¢_G}KÈ>ˆ \ No newline at end of file
diff --git a/tests/resources/issue_592/.gitted/objects/71/44be264b61825fbff68046fe999bdfe96a1792 b/tests/resources/issue_592/.gitted/objects/71/44be264b61825fbff68046fe999bdfe96a1792
new file mode 100644
index 000000000..25d44d938
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/objects/71/44be264b61825fbff68046fe999bdfe96a1792
Binary files differ
diff --git a/tests/resources/issue_592/.gitted/objects/be/de83ee10b5b3f00239660b00acec2d55fd0b84 b/tests/resources/issue_592/.gitted/objects/be/de83ee10b5b3f00239660b00acec2d55fd0b84
new file mode 100644
index 000000000..1d6e38d37
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/objects/be/de83ee10b5b3f00239660b00acec2d55fd0b84
Binary files differ
diff --git a/tests/resources/issue_592/.gitted/objects/e3/8fcc7a6060f5eb5b876e836b52ae4769363f21 b/tests/resources/issue_592/.gitted/objects/e3/8fcc7a6060f5eb5b876e836b52ae4769363f21
new file mode 100644
index 000000000..36c5b9aab
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/objects/e3/8fcc7a6060f5eb5b876e836b52ae4769363f21
Binary files differ
diff --git a/tests/resources/issue_592/.gitted/objects/f1/adef63cb08891a0942b76fc4b9c50c6c494bc7 b/tests/resources/issue_592/.gitted/objects/f1/adef63cb08891a0942b76fc4b9c50c6c494bc7
new file mode 100644
index 000000000..c08ecd5ed
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/objects/f1/adef63cb08891a0942b76fc4b9c50c6c494bc7
Binary files differ
diff --git a/tests/resources/issue_592/.gitted/refs/heads/master b/tests/resources/issue_592/.gitted/refs/heads/master
new file mode 100644
index 000000000..1f6669628
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/refs/heads/master
@@ -0,0 +1 @@
+e38fcc7a6060f5eb5b876e836b52ae4769363f21
diff --git a/tests/resources/issue_592/a.txt b/tests/resources/issue_592/a.txt
new file mode 100644
index 000000000..f1adef63c
--- /dev/null
+++ b/tests/resources/issue_592/a.txt
@@ -0,0 +1 @@
+nothing here
diff --git a/tests/resources/issue_592/c/a.txt b/tests/resources/issue_592/c/a.txt
new file mode 100644
index 000000000..f1adef63c
--- /dev/null
+++ b/tests/resources/issue_592/c/a.txt
@@ -0,0 +1 @@
+nothing here
diff --git a/tests/resources/issue_592/l.txt b/tests/resources/issue_592/l.txt
new file mode 100644
index 000000000..f1adef63c
--- /dev/null
+++ b/tests/resources/issue_592/l.txt
@@ -0,0 +1 @@
+nothing here
diff --git a/tests/resources/issue_592/t/a.txt b/tests/resources/issue_592/t/a.txt
new file mode 100644
index 000000000..f1adef63c
--- /dev/null
+++ b/tests/resources/issue_592/t/a.txt
@@ -0,0 +1 @@
+nothing here
diff --git a/tests/resources/issue_592/t/b.txt b/tests/resources/issue_592/t/b.txt
new file mode 100644
index 000000000..f1adef63c
--- /dev/null
+++ b/tests/resources/issue_592/t/b.txt
@@ -0,0 +1 @@
+nothing here
diff --git a/tests/resources/issue_592b/.gitted/HEAD b/tests/resources/issue_592b/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/issue_592b/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/issue_592b/.gitted/config b/tests/resources/issue_592b/.gitted/config
new file mode 100644
index 000000000..af107929f
--- /dev/null
+++ b/tests/resources/issue_592b/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests/resources/issue_592b/.gitted/description b/tests/resources/issue_592b/.gitted/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/issue_592b/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/issue_592b/.gitted/index b/tests/resources/issue_592b/.gitted/index
new file mode 100644
index 000000000..596438216
--- /dev/null
+++ b/tests/resources/issue_592b/.gitted/index
Binary files differ
diff --git a/tests/resources/issue_592b/.gitted/info/exclude b/tests/resources/issue_592b/.gitted/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/issue_592b/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/issue_592b/.gitted/logs/HEAD b/tests/resources/issue_592b/.gitted/logs/HEAD
new file mode 100644
index 000000000..6f3ba90cc
--- /dev/null
+++ b/tests/resources/issue_592b/.gitted/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 3fbf1852f72fd268e36457b13a18cdd9a4c9ea35 Russell Belfer <rb@github.com> 1337205933 -0700 commit (initial): Initial commit
diff --git a/tests/resources/issue_592b/.gitted/logs/refs/heads/master b/tests/resources/issue_592b/.gitted/logs/refs/heads/master
new file mode 100644
index 000000000..6f3ba90cc
--- /dev/null
+++ b/tests/resources/issue_592b/.gitted/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 3fbf1852f72fd268e36457b13a18cdd9a4c9ea35 Russell Belfer <rb@github.com> 1337205933 -0700 commit (initial): Initial commit
diff --git a/tests/resources/issue_592b/.gitted/objects/3f/bf1852f72fd268e36457b13a18cdd9a4c9ea35 b/tests/resources/issue_592b/.gitted/objects/3f/bf1852f72fd268e36457b13a18cdd9a4c9ea35
new file mode 100644
index 000000000..6eaf64b46
--- /dev/null
+++ b/tests/resources/issue_592b/.gitted/objects/3f/bf1852f72fd268e36457b13a18cdd9a4c9ea35
@@ -0,0 +1,2 @@
+x•K
+1]ç}%Bwn½A§íq‰™Îý x·ªz¼µVƃv É‚còžÑ&”%9¦@˜9x¤dÝëŒìÙÐÐuëðû.µÂ]ê".=ßÞEבO¼µ+¸ÐÛ˜B€£EkÍ\çŸNô_Ó<>E Uø%Ìû•9 \ No newline at end of file
diff --git a/tests/resources/issue_592b/.gitted/objects/6f/a891d3e578c83e1c03bdb9e0fdd8e6e934157f b/tests/resources/issue_592b/.gitted/objects/6f/a891d3e578c83e1c03bdb9e0fdd8e6e934157f
new file mode 100644
index 000000000..c4becfe2f
--- /dev/null
+++ b/tests/resources/issue_592b/.gitted/objects/6f/a891d3e578c83e1c03bdb9e0fdd8e6e934157f
Binary files differ
diff --git a/tests/resources/issue_592b/.gitted/objects/80/07d41d5794e6ce4d4d2c97e370d5a9aa6d5213 b/tests/resources/issue_592b/.gitted/objects/80/07d41d5794e6ce4d4d2c97e370d5a9aa6d5213
new file mode 100644
index 000000000..aea14f2af
--- /dev/null
+++ b/tests/resources/issue_592b/.gitted/objects/80/07d41d5794e6ce4d4d2c97e370d5a9aa6d5213
Binary files differ
diff --git a/tests/resources/issue_592b/.gitted/objects/a6/5fb6583a7c425284142f285bc359a2d6565513 b/tests/resources/issue_592b/.gitted/objects/a6/5fb6583a7c425284142f285bc359a2d6565513
new file mode 100644
index 000000000..9b7407221
--- /dev/null
+++ b/tests/resources/issue_592b/.gitted/objects/a6/5fb6583a7c425284142f285bc359a2d6565513
Binary files differ
diff --git a/tests/resources/issue_592b/.gitted/objects/ae/be7a55922c7097ef91ca3a7bc327a901d87c2c b/tests/resources/issue_592b/.gitted/objects/ae/be7a55922c7097ef91ca3a7bc327a901d87c2c
new file mode 100644
index 000000000..1494ed822
--- /dev/null
+++ b/tests/resources/issue_592b/.gitted/objects/ae/be7a55922c7097ef91ca3a7bc327a901d87c2c
Binary files differ
diff --git a/tests/resources/issue_592b/.gitted/objects/b3/44b055867fcdc1f01eaa75056a43e868eb4fbc b/tests/resources/issue_592b/.gitted/objects/b3/44b055867fcdc1f01eaa75056a43e868eb4fbc
new file mode 100644
index 000000000..7a6626636
--- /dev/null
+++ b/tests/resources/issue_592b/.gitted/objects/b3/44b055867fcdc1f01eaa75056a43e868eb4fbc
Binary files differ
diff --git a/tests/resources/issue_592b/.gitted/objects/f7/d75fbfad8b1d2e307ced287ea78aad403cdce3 b/tests/resources/issue_592b/.gitted/objects/f7/d75fbfad8b1d2e307ced287ea78aad403cdce3
new file mode 100644
index 000000000..65a1fd0d0
--- /dev/null
+++ b/tests/resources/issue_592b/.gitted/objects/f7/d75fbfad8b1d2e307ced287ea78aad403cdce3
Binary files differ
diff --git a/tests/resources/issue_592b/.gitted/refs/heads/master b/tests/resources/issue_592b/.gitted/refs/heads/master
new file mode 100644
index 000000000..c0a9ab495
--- /dev/null
+++ b/tests/resources/issue_592b/.gitted/refs/heads/master
@@ -0,0 +1 @@
+3fbf1852f72fd268e36457b13a18cdd9a4c9ea35
diff --git a/tests/resources/issue_592b/gitignore b/tests/resources/issue_592b/gitignore
new file mode 100644
index 000000000..8007d41d5
--- /dev/null
+++ b/tests/resources/issue_592b/gitignore
@@ -0,0 +1 @@
+ignored/
diff --git a/tests/resources/issue_592b/ignored/contained/ignored3.txt b/tests/resources/issue_592b/ignored/contained/ignored3.txt
new file mode 100644
index 000000000..b5dc7b073
--- /dev/null
+++ b/tests/resources/issue_592b/ignored/contained/ignored3.txt
@@ -0,0 +1 @@
+I'm ignored
diff --git a/tests/resources/issue_592b/ignored/contained/tracked3.txt b/tests/resources/issue_592b/ignored/contained/tracked3.txt
new file mode 100644
index 000000000..b344b0558
--- /dev/null
+++ b/tests/resources/issue_592b/ignored/contained/tracked3.txt
@@ -0,0 +1 @@
+You added me anyhow
diff --git a/tests/resources/issue_592b/ignored/ignored2.txt b/tests/resources/issue_592b/ignored/ignored2.txt
new file mode 100644
index 000000000..b5dc7b073
--- /dev/null
+++ b/tests/resources/issue_592b/ignored/ignored2.txt
@@ -0,0 +1 @@
+I'm ignored
diff --git a/tests/resources/issue_592b/ignored/tracked2.txt b/tests/resources/issue_592b/ignored/tracked2.txt
new file mode 100644
index 000000000..6fa891d3e
--- /dev/null
+++ b/tests/resources/issue_592b/ignored/tracked2.txt
@@ -0,0 +1 @@
+You like me
diff --git a/tests/resources/issue_592b/ignored1.txt b/tests/resources/issue_592b/ignored1.txt
new file mode 100644
index 000000000..b5dc7b073
--- /dev/null
+++ b/tests/resources/issue_592b/ignored1.txt
@@ -0,0 +1 @@
+I'm ignored
diff --git a/tests/resources/issue_592b/tracked1.txt b/tests/resources/issue_592b/tracked1.txt
new file mode 100644
index 000000000..6fa891d3e
--- /dev/null
+++ b/tests/resources/issue_592b/tracked1.txt
@@ -0,0 +1 @@
+You like me
diff --git a/tests/resources/merge-resolve/.gitted/COMMIT_EDITMSG b/tests/resources/merge-resolve/.gitted/COMMIT_EDITMSG
new file mode 100644
index 000000000..245b18a2c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/COMMIT_EDITMSG
@@ -0,0 +1 @@
+rename conflict theirs
diff --git a/tests/resources/merge-resolve/.gitted/HEAD b/tests/resources/merge-resolve/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/merge-resolve/.gitted/ORIG_HEAD b/tests/resources/merge-resolve/.gitted/ORIG_HEAD
new file mode 100644
index 000000000..4092d428f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/ORIG_HEAD
@@ -0,0 +1 @@
+2392a2dacc9efb562b8635d6579fb458751c7c5b
diff --git a/tests/resources/merge-resolve/.gitted/config b/tests/resources/merge-resolve/.gitted/config
new file mode 100644
index 000000000..af107929f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests/resources/merge-resolve/.gitted/description b/tests/resources/merge-resolve/.gitted/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/merge-resolve/.gitted/index b/tests/resources/merge-resolve/.gitted/index
new file mode 100644
index 000000000..230eba9eb
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/index
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/logs/HEAD b/tests/resources/merge-resolve/.gitted/logs/HEAD
new file mode 100644
index 000000000..96cdb337e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/HEAD
@@ -0,0 +1,236 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1351563869 -0500 commit (initial): initial
+c607fc30883e335def28cd686b51f6cfa02b06ec c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1351563886 -0500 checkout: moving from master to branch
+c607fc30883e335def28cd686b51f6cfa02b06ec 7cb63eed597130ba4abb87b3e544b85021905520 Edward Thomson <ethomson@edwardthomson.com> 1351563965 -0500 commit: branch
+7cb63eed597130ba4abb87b3e544b85021905520 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1351563968 -0500 checkout: moving from branch to master
+c607fc30883e335def28cd686b51f6cfa02b06ec 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351564033 -0500 commit: master
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351605785 -0500 checkout: moving from master to ff_branch
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 33d500f588fbbe65901d82b4e6b008e549064be0 Edward Thomson <ethomson@edwardthomson.com> 1351605830 -0500 commit: fastforward
+33d500f588fbbe65901d82b4e6b008e549064be0 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351605889 -0500 checkout: moving from ff_branch to master
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351874933 -0500 checkout: moving from master to octo1
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 16f825815cfd20a07a75c71554e82d8eede0b061 Edward Thomson <ethomson@edwardthomson.com> 1351874954 -0500 commit: octo1
+16f825815cfd20a07a75c71554e82d8eede0b061 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351874957 -0500 checkout: moving from octo1 to master
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351874960 -0500 checkout: moving from master to octo2
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 158dc7bedb202f5b26502bf3574faa7f4238d56c Edward Thomson <ethomson@edwardthomson.com> 1351874974 -0500 commit: octo2
+158dc7bedb202f5b26502bf3574faa7f4238d56c 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351874976 -0500 checkout: moving from octo2 to master
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351874980 -0500 checkout: moving from master to octo3
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 50ce7d7d01217679e26c55939eef119e0c93e272 Edward Thomson <ethomson@edwardthomson.com> 1351874998 -0500 commit: octo3
+50ce7d7d01217679e26c55939eef119e0c93e272 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875006 -0500 checkout: moving from octo3 to master
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875010 -0500 checkout: moving from master to octo4
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 54269b3f6ec3d7d4ede24dd350dd5d605495c3ae Edward Thomson <ethomson@edwardthomson.com> 1351875023 -0500 commit: octo4
+54269b3f6ec3d7d4ede24dd350dd5d605495c3ae 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875031 -0500 checkout: moving from octo4 to master
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875031 -0500 checkout: moving from master to octo5
+977c696519c5a3004c5f1d15d60c89dbeb8f235f e4f618a2c3ed0669308735727df5ebf2447f022f Edward Thomson <ethomson@edwardthomson.com> 1351875041 -0500 commit: octo5
+e4f618a2c3ed0669308735727df5ebf2447f022f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875046 -0500 checkout: moving from octo5 to master
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875046 -0500 checkout: moving from master to octo6
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 4ca408a8c88655f7586a1b580be6fad138121e98 Edward Thomson <ethomson@edwardthomson.com> 1351875057 -0500 commit: octo5
+4ca408a8c88655f7586a1b580be6fad138121e98 b6f610aef53bd343e6c96227de874c66f00ee8e8 Edward Thomson <ethomson@edwardthomson.com> 1351875065 -0500 commit (amend): octo6
+b6f610aef53bd343e6c96227de874c66f00ee8e8 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875071 -0500 checkout: moving from octo6 to master
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 4e0d9401aee78eb345a8685a859d37c8c3c0bbed Edward Thomson <ethomson@edwardthomson.com> 1351875091 -0500 merge octo1 octo2 octo3 octo4: Merge made by the 'octopus' strategy.
+4e0d9401aee78eb345a8685a859d37c8c3c0bbed 54269b3f6ec3d7d4ede24dd350dd5d605495c3ae Edward Thomson <ethomson@edwardthomson.com> 1351875108 -0500 reset: moving to 54269b3f6ec3d7d4ede24dd350dd5d605495c3ae
+54269b3f6ec3d7d4ede24dd350dd5d605495c3ae 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875584 -0500 reset: moving to 977c696519c5a3004c5f1d15d60c89dbeb8f235f
+bd593285fc7fe4ca18ccdbabf027f5d689101452 33d500f588fbbe65901d82b4e6b008e549064be0 Edward Thomson <ethomson@edwardthomson.com> 1351990193 -0500 checkout: moving from master to ff_branch
+33d500f588fbbe65901d82b4e6b008e549064be0 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1351990202 -0500 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1351990205 -0500 merge master: Fast-forward
+bd593285fc7fe4ca18ccdbabf027f5d689101452 fd89f8cffb663ac89095a0f9764902e93ceaca6a Edward Thomson <ethomson@edwardthomson.com> 1351990229 -0500 commit: fastforward
+fd89f8cffb663ac89095a0f9764902e93ceaca6a bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1351990233 -0500 checkout: moving from ff_branch to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352091703 -0600 checkout: moving from master to trivial-2alt
+c607fc30883e335def28cd686b51f6cfa02b06ec c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352092411 -0600 checkout: moving from trivial-2alt to trivial-2alt-branch
+c607fc30883e335def28cd686b51f6cfa02b06ec c9174cef549ec94ecbc43ef03cdc775b4950becb Edward Thomson <ethomson@edwardthomson.com> 1352092434 -0600 commit: 2alt-branch
+c9174cef549ec94ecbc43ef03cdc775b4950becb c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352092440 -0600 checkout: moving from trivial-2alt-branch to trivial-2alt
+c607fc30883e335def28cd686b51f6cfa02b06ec 566ab53c220a2eafc1212af1a024513230280ab9 Edward Thomson <ethomson@edwardthomson.com> 1352092452 -0600 commit: 2alt
+bd593285fc7fe4ca18ccdbabf027f5d689101452 566ab53c220a2eafc1212af1a024513230280ab9 Edward Thomson <ethomson@edwardthomson.com> 1352094476 -0600 checkout: moving from master to trivial-3alt
+566ab53c220a2eafc1212af1a024513230280ab9 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352094547 -0600 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec 5459c89aa0026d543ce8343bd89871bce543f9c2 Edward Thomson <ethomson@edwardthomson.com> 1352094580 -0600 commit: 3alt
+5459c89aa0026d543ce8343bd89871bce543f9c2 4c9fac0707f8d4195037ae5a681aa48626491541 Edward Thomson <ethomson@edwardthomson.com> 1352094610 -0600 commit: 3alt-branch
+4c9fac0707f8d4195037ae5a681aa48626491541 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352094620 -0600 checkout: moving from trivial-3alt to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 566ab53c220a2eafc1212af1a024513230280ab9 Edward Thomson <ethomson@edwardthomson.com> 1352094752 -0600 checkout: moving from master to trivial-4
+566ab53c220a2eafc1212af1a024513230280ab9 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352094764 -0600 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec cc3e3009134cb88014129fc8858d1101359e5e2f Edward Thomson <ethomson@edwardthomson.com> 1352094815 -0600 commit: trivial-4
+cc3e3009134cb88014129fc8858d1101359e5e2f c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352094843 -0600 checkout: moving from trivial-4 to trivial-4-branch
+c607fc30883e335def28cd686b51f6cfa02b06ec 183310e30fb1499af8c619108ffea4d300b5e778 Edward Thomson <ethomson@edwardthomson.com> 1352094856 -0600 commit: trivial-4-branch
+183310e30fb1499af8c619108ffea4d300b5e778 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352094860 -0600 checkout: moving from trivial-4-branch to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 cc3e3009134cb88014129fc8858d1101359e5e2f Edward Thomson <ethomson@edwardthomson.com> 1352096588 -0600 checkout: moving from master to trivial-4
+cc3e3009134cb88014129fc8858d1101359e5e2f c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352096612 -0600 checkout: moving from trivial-4 to trivial-5alt-1
+c607fc30883e335def28cd686b51f6cfa02b06ec 4fe93c0ec83eb6305cbace3dace88ecee1b63cb6 Edward Thomson <ethomson@edwardthomson.com> 1352096643 -0600 commit: 5alt-1
+4fe93c0ec83eb6305cbace3dace88ecee1b63cb6 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352096661 -0600 checkout: moving from trivial-5alt-1 to trivial-5alt-1-branch
+c607fc30883e335def28cd686b51f6cfa02b06ec 4fe93c0ec83eb6305cbace3dace88ecee1b63cb6 Edward Thomson <ethomson@edwardthomson.com> 1352096671 -0600 checkout: moving from trivial-5alt-1-branch to trivial-5alt-1
+4fe93c0ec83eb6305cbace3dace88ecee1b63cb6 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352096678 -0600 checkout: moving from trivial-5alt-1 to trivial-5alt-1-branch
+c607fc30883e335def28cd686b51f6cfa02b06ec 478172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2 Edward Thomson <ethomson@edwardthomson.com> 1352096689 -0600 commit: 5alt-1-branch
+478172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2 4fe93c0ec83eb6305cbace3dace88ecee1b63cb6 Edward Thomson <ethomson@edwardthomson.com> 1352096701 -0600 checkout: moving from trivial-5alt-1-branch to trivial-5alt-1
+4fe93c0ec83eb6305cbace3dace88ecee1b63cb6 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352096715 -0600 checkout: moving from trivial-5alt-1 to trivial-5alt-2
+c607fc30883e335def28cd686b51f6cfa02b06ec ebc09d0137cfb0c26697aed0109fb943ad906f3f Edward Thomson <ethomson@edwardthomson.com> 1352096764 -0600 commit: existing file
+ebc09d0137cfb0c26697aed0109fb943ad906f3f 3b47b031b3e55ae11e14a05260b1c3ffd6838d55 Edward Thomson <ethomson@edwardthomson.com> 1352096815 -0600 commit: 5alt-2
+3b47b031b3e55ae11e14a05260b1c3ffd6838d55 ebc09d0137cfb0c26697aed0109fb943ad906f3f Edward Thomson <ethomson@edwardthomson.com> 1352096840 -0600 checkout: moving from trivial-5alt-2 to trivial-5alt-2-branch
+ebc09d0137cfb0c26697aed0109fb943ad906f3f f48097eb340dc5a7cae55aabcf1faf4548aa821f Edward Thomson <ethomson@edwardthomson.com> 1352096855 -0600 commit: 5alt-2-branch
+f48097eb340dc5a7cae55aabcf1faf4548aa821f bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352096858 -0600 checkout: moving from trivial-5alt-2-branch to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352097377 -0600 checkout: moving from master to trivial-6
+c607fc30883e335def28cd686b51f6cfa02b06ec f7c332bd4d4d4b777366cae4d24d1687477576bf Edward Thomson <ethomson@edwardthomson.com> 1352097389 -0600 commit: 6
+f7c332bd4d4d4b777366cae4d24d1687477576bf 99b4f7e4f24470fa06b980bc21f1095c2a9425c0 Edward Thomson <ethomson@edwardthomson.com> 1352097404 -0600 commit: trivial-6
+99b4f7e4f24470fa06b980bc21f1095c2a9425c0 f7c332bd4d4d4b777366cae4d24d1687477576bf Edward Thomson <ethomson@edwardthomson.com> 1352097420 -0600 checkout: moving from trivial-6 to trivial-6-branch
+f7c332bd4d4d4b777366cae4d24d1687477576bf a43150a738849c59376cf30bb2a68348a83c8f48 Edward Thomson <ethomson@edwardthomson.com> 1352097431 -0600 commit: 6-branch
+a43150a738849c59376cf30bb2a68348a83c8f48 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352097442 -0600 checkout: moving from trivial-6-branch to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 99b4f7e4f24470fa06b980bc21f1095c2a9425c0 Edward Thomson <ethomson@edwardthomson.com> 1352098040 -0600 checkout: moving from master to trivial-6
+99b4f7e4f24470fa06b980bc21f1095c2a9425c0 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352098057 -0600 checkout: moving from trivial-6 to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 cc3e3009134cb88014129fc8858d1101359e5e2f Edward Thomson <ethomson@edwardthomson.com> 1352098792 -0600 checkout: moving from master to trivial-4
+cc3e3009134cb88014129fc8858d1101359e5e2f c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352098818 -0600 checkout: moving from trivial-4 to trivial-8
+c607fc30883e335def28cd686b51f6cfa02b06ec 75a811bf6bc57694adb3fe604786f3a4efd1cd1b Edward Thomson <ethomson@edwardthomson.com> 1352098884 -0600 commit: trivial-8
+75a811bf6bc57694adb3fe604786f3a4efd1cd1b 75a811bf6bc57694adb3fe604786f3a4efd1cd1b Edward Thomson <ethomson@edwardthomson.com> 1352098947 -0600 checkout: moving from trivial-8 to trivial-8-branch
+75a811bf6bc57694adb3fe604786f3a4efd1cd1b 52d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9 Edward Thomson <ethomson@edwardthomson.com> 1352098979 -0600 commit: trivial-8-branch
+52d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9 75a811bf6bc57694adb3fe604786f3a4efd1cd1b Edward Thomson <ethomson@edwardthomson.com> 1352098982 -0600 checkout: moving from trivial-8-branch to trivial-8
+75a811bf6bc57694adb3fe604786f3a4efd1cd1b 3575826c96a975031d2c14368529cc5c4353a8fd Edward Thomson <ethomson@edwardthomson.com> 1352099000 -0600 commit: trivial-8
+3575826c96a975031d2c14368529cc5c4353a8fd bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352099008 -0600 checkout: moving from trivial-8 to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352099776 -0600 checkout: moving from master to trivial-7
+c607fc30883e335def28cd686b51f6cfa02b06ec 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson <ethomson@edwardthomson.com> 1352099790 -0600 commit: trivial-7
+092ce8682d7f3a2a3a769a6daca58950168ba5c4 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson <ethomson@edwardthomson.com> 1352099799 -0600 checkout: moving from trivial-7 to trivial-7-branch
+092ce8682d7f3a2a3a769a6daca58950168ba5c4 73cbfdc4fe843169e5b2af8dcad03cbf3acf306c Edward Thomson <ethomson@edwardthomson.com> 1352099812 -0600 commit: trivial-7-branch
+73cbfdc4fe843169e5b2af8dcad03cbf3acf306c 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson <ethomson@edwardthomson.com> 1352099815 -0600 checkout: moving from trivial-7-branch to trivial-7
+092ce8682d7f3a2a3a769a6daca58950168ba5c4 73cbfdc4fe843169e5b2af8dcad03cbf3acf306c Edward Thomson <ethomson@edwardthomson.com> 1352099838 -0600 checkout: moving from trivial-7 to trivial-7-branch
+73cbfdc4fe843169e5b2af8dcad03cbf3acf306c 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson <ethomson@edwardthomson.com> 1352099874 -0600 reset: moving to 092ce8682d7f3a2a3a769a6daca58950168ba5c4
+092ce8682d7f3a2a3a769a6daca58950168ba5c4 009b9cab6fdac02915a88ecd078b7a792ed802d8 Edward Thomson <ethomson@edwardthomson.com> 1352099921 -0600 commit: removed in 7
+009b9cab6fdac02915a88ecd078b7a792ed802d8 5195a1b480f66691b667f10a9e41e70115a78351 Edward Thomson <ethomson@edwardthomson.com> 1352099927 -0600 commit (amend): trivial-7-branch
+5195a1b480f66691b667f10a9e41e70115a78351 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson <ethomson@edwardthomson.com> 1352099937 -0600 checkout: moving from trivial-7-branch to trivial-7
+092ce8682d7f3a2a3a769a6daca58950168ba5c4 d874671ef5b20184836cb983bb273e5280384d0b Edward Thomson <ethomson@edwardthomson.com> 1352099947 -0600 commit: trivial-7
+d874671ef5b20184836cb983bb273e5280384d0b bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352099949 -0600 checkout: moving from trivial-7 to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352100174 -0600 checkout: moving from master to trivial-10
+c607fc30883e335def28cd686b51f6cfa02b06ec 53825f41ac8d640612f9423a2f03a69f3d96809a Edward Thomson <ethomson@edwardthomson.com> 1352100193 -0600 commit: trivial-10
+53825f41ac8d640612f9423a2f03a69f3d96809a 53825f41ac8d640612f9423a2f03a69f3d96809a Edward Thomson <ethomson@edwardthomson.com> 1352100200 -0600 checkout: moving from trivial-10 to trivial-10-branch
+53825f41ac8d640612f9423a2f03a69f3d96809a 11f4f3c08b737f5fd896cbefa1425ee63b21b2fa Edward Thomson <ethomson@edwardthomson.com> 1352100211 -0600 commit: trivial-10-branch
+11f4f3c08b737f5fd896cbefa1425ee63b21b2fa 53825f41ac8d640612f9423a2f03a69f3d96809a Edward Thomson <ethomson@edwardthomson.com> 1352100214 -0600 checkout: moving from trivial-10-branch to trivial-10
+53825f41ac8d640612f9423a2f03a69f3d96809a 0ec5f433959cd46177f745903353efb5be08d151 Edward Thomson <ethomson@edwardthomson.com> 1352100223 -0600 commit: trivial-10
+0ec5f433959cd46177f745903353efb5be08d151 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352100225 -0600 checkout: moving from trivial-10 to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352100270 -0600 checkout: moving from master to trivial-9
+c607fc30883e335def28cd686b51f6cfa02b06ec f0053b8060bb3f0be5cbcc3147a07ece26bf097e Edward Thomson <ethomson@edwardthomson.com> 1352100304 -0600 commit: trivial-9
+f0053b8060bb3f0be5cbcc3147a07ece26bf097e f0053b8060bb3f0be5cbcc3147a07ece26bf097e Edward Thomson <ethomson@edwardthomson.com> 1352100310 -0600 checkout: moving from trivial-9 to trivial-9-branch
+f0053b8060bb3f0be5cbcc3147a07ece26bf097e 13d1be4ea52a6ced1d7a1d832f0ee3c399348e5e Edward Thomson <ethomson@edwardthomson.com> 1352100317 -0600 commit: trivial-9-branch
+13d1be4ea52a6ced1d7a1d832f0ee3c399348e5e f0053b8060bb3f0be5cbcc3147a07ece26bf097e Edward Thomson <ethomson@edwardthomson.com> 1352100319 -0600 checkout: moving from trivial-9-branch to trivial-9
+f0053b8060bb3f0be5cbcc3147a07ece26bf097e c35dee9bcc0e989f3b0c40f68372a9a51b6c4e6a Edward Thomson <ethomson@edwardthomson.com> 1352100333 -0600 commit: trivial-9
+c35dee9bcc0e989f3b0c40f68372a9a51b6c4e6a bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352100335 -0600 checkout: moving from trivial-9 to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352100576 -0600 checkout: moving from master to trivial-13
+c607fc30883e335def28cd686b51f6cfa02b06ec 8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa Edward Thomson <ethomson@edwardthomson.com> 1352100589 -0600 commit: trivial-13
+8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa 8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa Edward Thomson <ethomson@edwardthomson.com> 1352100604 -0600 checkout: moving from trivial-13 to trivial-13-branch
+8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa 05f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c Edward Thomson <ethomson@edwardthomson.com> 1352100610 -0600 commit: trivial-13-branch
+05f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c 8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa Edward Thomson <ethomson@edwardthomson.com> 1352100612 -0600 checkout: moving from trivial-13-branch to trivial-13
+8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa a3fabece9eb8748da810e1e08266fef9b7136ad4 Edward Thomson <ethomson@edwardthomson.com> 1352100625 -0600 commit: trivial-13
+a3fabece9eb8748da810e1e08266fef9b7136ad4 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352100627 -0600 checkout: moving from trivial-13 to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352100936 -0600 checkout: moving from master to trivial-11
+c607fc30883e335def28cd686b51f6cfa02b06ec 35632e43612c06a3ea924bfbacd48333da874c29 Edward Thomson <ethomson@edwardthomson.com> 1352100958 -0600 commit: trivial-11
+35632e43612c06a3ea924bfbacd48333da874c29 35632e43612c06a3ea924bfbacd48333da874c29 Edward Thomson <ethomson@edwardthomson.com> 1352100964 -0600 checkout: moving from trivial-11 to trivial-11-branch
+35632e43612c06a3ea924bfbacd48333da874c29 6718a45909532d1fcf5600d0877f7fe7e78f0b86 Edward Thomson <ethomson@edwardthomson.com> 1352100978 -0600 commit: trivial-11-branch
+6718a45909532d1fcf5600d0877f7fe7e78f0b86 35632e43612c06a3ea924bfbacd48333da874c29 Edward Thomson <ethomson@edwardthomson.com> 1352100981 -0600 checkout: moving from trivial-11-branch to trivial-11
+35632e43612c06a3ea924bfbacd48333da874c29 3168dca1a561889b045a6441909f4c56145e666d Edward Thomson <ethomson@edwardthomson.com> 1352100992 -0600 commit: trivial-11
+3168dca1a561889b045a6441909f4c56145e666d bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352100996 -0600 checkout: moving from trivial-11 to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352101098 -0600 checkout: moving from master to trivial-14
+c607fc30883e335def28cd686b51f6cfa02b06ec 596803b523203a4851c824c07366906f8353f4ad Edward Thomson <ethomson@edwardthomson.com> 1352101113 -0600 commit: trivial-14
+596803b523203a4851c824c07366906f8353f4ad 596803b523203a4851c824c07366906f8353f4ad Edward Thomson <ethomson@edwardthomson.com> 1352101117 -0600 checkout: moving from trivial-14 to trivial-14-branch
+596803b523203a4851c824c07366906f8353f4ad 8187117062b750eed4f93fd7e899f17b52ce554d Edward Thomson <ethomson@edwardthomson.com> 1352101132 -0600 commit: trivial-14-branch
+8187117062b750eed4f93fd7e899f17b52ce554d 596803b523203a4851c824c07366906f8353f4ad Edward Thomson <ethomson@edwardthomson.com> 1352101135 -0600 checkout: moving from trivial-14-branch to trivial-14
+596803b523203a4851c824c07366906f8353f4ad 7e2d058d5fedf8329db44db4fac610d6b1a89159 Edward Thomson <ethomson@edwardthomson.com> 1352101141 -0600 commit: trivial-14
+7e2d058d5fedf8329db44db4fac610d6b1a89159 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352101145 -0600 checkout: moving from trivial-14 to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1353177749 -0600 checkout: moving from master to renames1
+c607fc30883e335def28cd686b51f6cfa02b06ec 412b32fb66137366147f1801ecc962452757d48a Edward Thomson <ethomson@edwardthomson.com> 1353177886 -0600 commit: renames
+412b32fb66137366147f1801ecc962452757d48a bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1353794607 -0600 checkout: moving from renames1 to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1353794647 -0600 checkout: moving from master to renames2
+bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1353794677 -0600 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec ab40af3cb8a3ed2e2843e96d9aa7871336b94573 Edward Thomson <ethomson@edwardthomson.com> 1353794852 -0600 commit: renames2
+ab40af3cb8a3ed2e2843e96d9aa7871336b94573 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1353794883 -0600 checkout: moving from renames2 to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1354574697 -0600 checkout: moving from master to df_side1
+bd593285fc7fe4ca18ccdbabf027f5d689101452 d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1354574962 -0600 commit: df_ancestor
+d4207f77243500bec335ab477f9227fcdb1e271a c94b27e41064c521120627e07e2035cca1d24ffa Edward Thomson <ethomson@edwardthomson.com> 1354575027 -0600 commit: df_side1
+c94b27e41064c521120627e07e2035cca1d24ffa d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1354575070 -0600 checkout: moving from df_side1 to df_side2
+d4207f77243500bec335ab477f9227fcdb1e271a f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 Edward Thomson <ethomson@edwardthomson.com> 1354575206 -0600 commit: df_side2
+f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1354575381 -0600 checkout: moving from df_side2 to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 c94b27e41064c521120627e07e2035cca1d24ffa Edward Thomson <ethomson@edwardthomson.com> 1355017614 -0600 checkout: moving from master to df_side1
+c94b27e41064c521120627e07e2035cca1d24ffa a90bc3fb6f15181972a2959a921429efbd81a473 Edward Thomson <ethomson@edwardthomson.com> 1355017650 -0600 commit: df_added
+a90bc3fb6f15181972a2959a921429efbd81a473 c94b27e41064c521120627e07e2035cca1d24ffa Edward Thomson <ethomson@edwardthomson.com> 1355017673 -0600 checkout: moving from df_side1 to c94b27e
+c94b27e41064c521120627e07e2035cca1d24ffa d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1355017673 -0600 rebase -i (squash): updating HEAD
+d4207f77243500bec335ab477f9227fcdb1e271a 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson <ethomson@edwardthomson.com> 1355017673 -0600 rebase -i (squash): df_side1
+005b6fcc8fec71d2550bef8462d169b3c26aa14b 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson <ethomson@edwardthomson.com> 1355017676 -0600 rebase -i (finish): returning to refs/heads/df_side1
+005b6fcc8fec71d2550bef8462d169b3c26aa14b f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 Edward Thomson <ethomson@edwardthomson.com> 1355017715 -0600 reset: moving to df_side2
+f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 8c749d9968d4b10dcfb06c9f97d0e5d92d337071 Edward Thomson <ethomson@edwardthomson.com> 1355017744 -0600 commit: df_added
+8c749d9968d4b10dcfb06c9f97d0e5d92d337071 f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 Edward Thomson <ethomson@edwardthomson.com> 1355017754 -0600 checkout: moving from df_side1 to f8958bd
+f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1355017754 -0600 rebase -i (squash): updating HEAD
+d4207f77243500bec335ab477f9227fcdb1e271a 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson <ethomson@edwardthomson.com> 1355017754 -0600 rebase -i (squash): df_side2
+0204a84f822acbf6386b36d33f1f6bc68bbbf858 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson <ethomson@edwardthomson.com> 1355017756 -0600 rebase -i (finish): returning to refs/heads/df_side1
+0204a84f822acbf6386b36d33f1f6bc68bbbf858 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson <ethomson@edwardthomson.com> 1355017793 -0600 reset: moving to 005b6fcc8fec71d2550bef8462d169b3c26aa14b
+005b6fcc8fec71d2550bef8462d169b3c26aa14b 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson <ethomson@edwardthomson.com> 1355017826 -0600 reset: moving to 0204a84
+0204a84f822acbf6386b36d33f1f6bc68bbbf858 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1355017847 -0600 checkout: moving from df_side1 to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson <ethomson@edwardthomson.com> 1355168677 -0600 checkout: moving from master to df_side1
+005b6fcc8fec71d2550bef8462d169b3c26aa14b 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson <ethomson@edwardthomson.com> 1355168829 -0600 checkout: moving from df_side1 to df_side1
+005b6fcc8fec71d2550bef8462d169b3c26aa14b 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson <ethomson@edwardthomson.com> 1355168838 -0600 checkout: moving from df_side1 to df_side1
+005b6fcc8fec71d2550bef8462d169b3c26aa14b e8107f24196736b870a318a0e28f048e29f6feff Edward Thomson <ethomson@edwardthomson.com> 1355169065 -0600 commit: df_side1
+e8107f24196736b870a318a0e28f048e29f6feff 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson <ethomson@edwardthomson.com> 1355169081 -0600 checkout: moving from df_side1 to 005b6fc
+005b6fcc8fec71d2550bef8462d169b3c26aa14b d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1355169081 -0600 rebase -i (squash): updating HEAD
+d4207f77243500bec335ab477f9227fcdb1e271a 80a8fbb3abb1ba423d554e9630b8fc2e5698f86b Edward Thomson <ethomson@edwardthomson.com> 1355169081 -0600 rebase -i (squash): df_side1
+80a8fbb3abb1ba423d554e9630b8fc2e5698f86b 80a8fbb3abb1ba423d554e9630b8fc2e5698f86b Edward Thomson <ethomson@edwardthomson.com> 1355169084 -0600 rebase -i (finish): returning to refs/heads/df_side1
+80a8fbb3abb1ba423d554e9630b8fc2e5698f86b 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson <ethomson@edwardthomson.com> 1355169141 -0600 checkout: moving from df_side1 to df_side2
+0204a84f822acbf6386b36d33f1f6bc68bbbf858 944f5dd1a867cab4c2bbcb896493435cae1dcc1a Edward Thomson <ethomson@edwardthomson.com> 1355169174 -0600 commit: both
+944f5dd1a867cab4c2bbcb896493435cae1dcc1a 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson <ethomson@edwardthomson.com> 1355169182 -0600 checkout: moving from df_side2 to 0204a84
+0204a84f822acbf6386b36d33f1f6bc68bbbf858 d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1355169182 -0600 rebase -i (squash): updating HEAD
+d4207f77243500bec335ab477f9227fcdb1e271a 57079a46233ae2b6df62e9ade71c4948512abefb Edward Thomson <ethomson@edwardthomson.com> 1355169182 -0600 rebase -i (squash): df_side2
+57079a46233ae2b6df62e9ade71c4948512abefb 57079a46233ae2b6df62e9ade71c4948512abefb Edward Thomson <ethomson@edwardthomson.com> 1355169185 -0600 rebase -i (finish): returning to refs/heads/df_side2
+57079a46233ae2b6df62e9ade71c4948512abefb 80a8fbb3abb1ba423d554e9630b8fc2e5698f86b Edward Thomson <ethomson@edwardthomson.com> 1355169241 -0600 checkout: moving from df_side2 to df_side1
+80a8fbb3abb1ba423d554e9630b8fc2e5698f86b e65a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812 Edward Thomson <ethomson@edwardthomson.com> 1355169419 -0600 commit: side1
+e65a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812 80a8fbb3abb1ba423d554e9630b8fc2e5698f86b Edward Thomson <ethomson@edwardthomson.com> 1355169431 -0600 checkout: moving from df_side1 to 80a8fbb
+80a8fbb3abb1ba423d554e9630b8fc2e5698f86b d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1355169431 -0600 rebase -i (squash): updating HEAD
+d4207f77243500bec335ab477f9227fcdb1e271a 5dc1018e90b19654bee986b7a0c268804d39659d Edward Thomson <ethomson@edwardthomson.com> 1355169431 -0600 rebase -i (squash): df_side1
+5dc1018e90b19654bee986b7a0c268804d39659d 5dc1018e90b19654bee986b7a0c268804d39659d Edward Thomson <ethomson@edwardthomson.com> 1355169435 -0600 rebase -i (finish): returning to refs/heads/df_side1
+5dc1018e90b19654bee986b7a0c268804d39659d 57079a46233ae2b6df62e9ade71c4948512abefb Edward Thomson <ethomson@edwardthomson.com> 1355169439 -0600 checkout: moving from df_side1 to df_side2
+57079a46233ae2b6df62e9ade71c4948512abefb 58e853f66699fd02629fd50bde08082bc005933a Edward Thomson <ethomson@edwardthomson.com> 1355169460 -0600 commit: side2
+58e853f66699fd02629fd50bde08082bc005933a 57079a46233ae2b6df62e9ade71c4948512abefb Edward Thomson <ethomson@edwardthomson.com> 1355169469 -0600 checkout: moving from df_side2 to 57079a4
+57079a46233ae2b6df62e9ade71c4948512abefb d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1355169469 -0600 rebase -i (squash): updating HEAD
+d4207f77243500bec335ab477f9227fcdb1e271a fada9356aa3f74622327a3038ae9c6f92e1c5c1d Edward Thomson <ethomson@edwardthomson.com> 1355169469 -0600 rebase -i (squash): df_side2
+fada9356aa3f74622327a3038ae9c6f92e1c5c1d fada9356aa3f74622327a3038ae9c6f92e1c5c1d Edward Thomson <ethomson@edwardthomson.com> 1355169471 -0600 rebase -i (finish): returning to refs/heads/df_side2
+fada9356aa3f74622327a3038ae9c6f92e1c5c1d 5dc1018e90b19654bee986b7a0c268804d39659d Edward Thomson <ethomson@edwardthomson.com> 1355169494 -0600 checkout: moving from df_side2 to df_side1
+5dc1018e90b19654bee986b7a0c268804d39659d d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1355169663 -0600 checkout: moving from df_side1 to d4207f77243500bec335ab477f9227fcdb1e271a
+d4207f77243500bec335ab477f9227fcdb1e271a 849619b03ae540acee4d1edec96b86993da6b497 Edward Thomson <ethomson@edwardthomson.com> 1355169683 -0600 commit: both_dirs
+849619b03ae540acee4d1edec96b86993da6b497 d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1355169691 -0600 checkout: moving from 849619b03ae540acee4d1edec96b86993da6b497 to d4207f7
+d4207f77243500bec335ab477f9227fcdb1e271a bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1355169691 -0600 rebase -i (squash): updating HEAD
+bd593285fc7fe4ca18ccdbabf027f5d689101452 a765fb87eb2f7a1920b73b2d5a057f8f8476a42b Edward Thomson <ethomson@edwardthomson.com> 1355169691 -0600 rebase -i (squash): df_ancestor
+a765fb87eb2f7a1920b73b2d5a057f8f8476a42b 5dc1018e90b19654bee986b7a0c268804d39659d Edward Thomson <ethomson@edwardthomson.com> 1355169706 -0600 checkout: moving from a765fb87eb2f7a1920b73b2d5a057f8f8476a42b to df_side1
+5dc1018e90b19654bee986b7a0c268804d39659d a765fb87eb2f7a1920b73b2d5a057f8f8476a42b Edward Thomson <ethomson@edwardthomson.com> 1355169715 -0600 checkout: moving from df_side1 to a765fb87eb2f7a1920b73b2d5a057f8f8476a42b^0
+a765fb87eb2f7a1920b73b2d5a057f8f8476a42b bc744705e1d8a019993cf88f62bc4020f1b80919 Edward Thomson <ethomson@edwardthomson.com> 1355169801 -0600 commit: df_side1
+bc744705e1d8a019993cf88f62bc4020f1b80919 bc744705e1d8a019993cf88f62bc4020f1b80919 Edward Thomson <ethomson@edwardthomson.com> 1355169822 -0600 checkout: moving from bc744705e1d8a019993cf88f62bc4020f1b80919 to df_side1
+bc744705e1d8a019993cf88f62bc4020f1b80919 fada9356aa3f74622327a3038ae9c6f92e1c5c1d Edward Thomson <ethomson@edwardthomson.com> 1355169826 -0600 checkout: moving from df_side1 to df_side2
+fada9356aa3f74622327a3038ae9c6f92e1c5c1d a765fb87eb2f7a1920b73b2d5a057f8f8476a42b Edward Thomson <ethomson@edwardthomson.com> 1355169866 -0600 checkout: moving from df_side2 to a765fb87eb2f7a1920b73b2d5a057f8f8476a42b^0
+a765fb87eb2f7a1920b73b2d5a057f8f8476a42b 95646149ab6b6ba6edc83cff678582538b457b2b Edward Thomson <ethomson@edwardthomson.com> 1355169897 -0600 rebase: df_side2
+95646149ab6b6ba6edc83cff678582538b457b2b 95646149ab6b6ba6edc83cff678582538b457b2b Edward Thomson <ethomson@edwardthomson.com> 1355169897 -0600 rebase finished: returning to refs/heads/df_side2
+95646149ab6b6ba6edc83cff678582538b457b2b bc744705e1d8a019993cf88f62bc4020f1b80919 Edward Thomson <ethomson@edwardthomson.com> 1355169949 -0600 checkout: moving from df_side2 to df_side1
+bc744705e1d8a019993cf88f62bc4020f1b80919 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1355170046 -0600 checkout: moving from df_side1 to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1355181639 -0600 checkout: moving from master to df_ancestor
+bd593285fc7fe4ca18ccdbabf027f5d689101452 2da538570bc1e5b2c3e855bf702f35248ad0735f Edward Thomson <ethomson@edwardthomson.com> 1355181673 -0600 commit: df_ancestor
+2da538570bc1e5b2c3e855bf702f35248ad0735f a7dbfcbfc1a60709cb80b5ca24539008456531d0 Edward Thomson <ethomson@edwardthomson.com> 1355181715 -0600 commit: df_side1
+a7dbfcbfc1a60709cb80b5ca24539008456531d0 a7dbfcbfc1a60709cb80b5ca24539008456531d0 Edward Thomson <ethomson@edwardthomson.com> 1355181743 -0600 checkout: moving from df_ancestor to df_ancestor
+a7dbfcbfc1a60709cb80b5ca24539008456531d0 9a301fbe6fada7dcb74fcd7c20269b5c743459a7 Edward Thomson <ethomson@edwardthomson.com> 1355181775 -0600 commit: df_side2
+9a301fbe6fada7dcb74fcd7c20269b5c743459a7 a7dbfcbfc1a60709cb80b5ca24539008456531d0 Edward Thomson <ethomson@edwardthomson.com> 1355181793 -0600 checkout: moving from df_ancestor to df_side1
+a7dbfcbfc1a60709cb80b5ca24539008456531d0 9a301fbe6fada7dcb74fcd7c20269b5c743459a7 Edward Thomson <ethomson@edwardthomson.com> 1355181797 -0600 checkout: moving from df_side1 to df_side2
+9a301fbe6fada7dcb74fcd7c20269b5c743459a7 9a301fbe6fada7dcb74fcd7c20269b5c743459a7 Edward Thomson <ethomson@edwardthomson.com> 1355182062 -0600 checkout: moving from df_side2 to df_ancestor
+9a301fbe6fada7dcb74fcd7c20269b5c743459a7 2da538570bc1e5b2c3e855bf702f35248ad0735f Edward Thomson <ethomson@edwardthomson.com> 1355182067 -0600 reset: moving to 2da538570bc1e5b2c3e855bf702f35248ad0735f
+2da538570bc1e5b2c3e855bf702f35248ad0735f 2da538570bc1e5b2c3e855bf702f35248ad0735f Edward Thomson <ethomson@edwardthomson.com> 1355182087 -0600 checkout: moving from df_ancestor to df_side2
+2da538570bc1e5b2c3e855bf702f35248ad0735f fc90237dc4891fa6c69827fc465632225e391618 Edward Thomson <ethomson@edwardthomson.com> 1355182104 -0600 commit: df_side2
+fc90237dc4891fa6c69827fc465632225e391618 a7dbfcbfc1a60709cb80b5ca24539008456531d0 Edward Thomson <ethomson@edwardthomson.com> 1355182111 -0600 checkout: moving from df_side2 to df_side1
+a7dbfcbfc1a60709cb80b5ca24539008456531d0 fc90237dc4891fa6c69827fc465632225e391618 Edward Thomson <ethomson@edwardthomson.com> 1355182115 -0600 checkout: moving from df_side1 to df_side2
+fc90237dc4891fa6c69827fc465632225e391618 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1355182122 -0600 checkout: moving from df_side2 to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 d6cf6c7741b3316826af1314042550c97ded1d50 Edward Thomson <ethomson@edwardthomson.com> 1358997543 -0600 checkout: moving from master to unrelated
+d6cf6c7741b3316826af1314042550c97ded1d50 55b4e4687e7a0d9ca367016ed930f385d4022e6f Edward Thomson <ethomson@edwardthomson.com> 1358997664 -0600 commit: conflicting changes
+55b4e4687e7a0d9ca367016ed930f385d4022e6f bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1358997675 -0600 checkout: moving from unrelated to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 88e185910a15cd13bdf44854ad037f4842b03b29 Edward Thomson <ethomson@microsoft.com> 1365714471 -0500 checkout: moving from master to rename_conflict_ours
+88e185910a15cd13bdf44854ad037f4842b03b29 bef6e37b3ee632ba74159168836f382fed21d77d Edward Thomson <ethomson@microsoft.com> 1365714516 -0500 checkout: moving from rename_conflict_ours to bef6e37b3ee632ba74159168836f382fed21d77d
+bef6e37b3ee632ba74159168836f382fed21d77d 01f149e1b8f84bd8896aaff6d6b22af88459ded0 Edward Thomson <ethomson@microsoft.com> 1365714831 -0500 commit: rename ancestor
+0000000000000000000000000000000000000000 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson <ethomson@microsoft.com> 1365714958 -0500 commit (initial): rename conflict ancestor
+2392a2dacc9efb562b8635d6579fb458751c7c5b 88e185910a15cd13bdf44854ad037f4842b03b29 Edward Thomson <ethomson@microsoft.com> 1365714980 -0500 checkout: moving from rename_conflict_ancestor to rename_conflict_ours
+88e185910a15cd13bdf44854ad037f4842b03b29 7c2c5228c9e90170d4a35e6558e47163daf092e5 Edward Thomson <ethomson@microsoft.com> 1365715250 -0500 commit: rename conflict ours
+7c2c5228c9e90170d4a35e6558e47163daf092e5 2f4024ce528d36d8670c289cce5a7963e625bb0c Edward Thomson <ethomson@microsoft.com> 1365715274 -0500 checkout: moving from rename_conflict_ours to rename_conflict_theirs
+2f4024ce528d36d8670c289cce5a7963e625bb0c 56a638b76b75e068590ac999c2f8621e7f3e264c Edward Thomson <ethomson@microsoft.com> 1365715362 -0500 commit: rename conflict theirs
+56a638b76b75e068590ac999c2f8621e7f3e264c 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson <ethomson@microsoft.com> 1365715368 -0500 checkout: moving from rename_conflict_theirs to rename_conflict_ancestor
+2392a2dacc9efb562b8635d6579fb458751c7c5b 56a638b76b75e068590ac999c2f8621e7f3e264c Edward Thomson <ethomson@microsoft.com> 1365715371 -0500 checkout: moving from rename_conflict_ancestor to rename_conflict_theirs
+56a638b76b75e068590ac999c2f8621e7f3e264c 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson <ethomson@microsoft.com> 1365715404 -0500 checkout: moving from rename_conflict_theirs to rename_conflict_ancestor
+2392a2dacc9efb562b8635d6579fb458751c7c5b 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson <ethomson@microsoft.com> 1365715438 -0500 checkout: moving from rename_conflict_ancestor to rename_conflict_ours
+2392a2dacc9efb562b8635d6579fb458751c7c5b 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson <ethomson@microsoft.com> 1365715480 -0500 checkout: moving from rename_conflict_ours to rename_conflict_ancestor
+2392a2dacc9efb562b8635d6579fb458751c7c5b 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson <ethomson@microsoft.com> 1365715486 -0500 checkout: moving from rename_conflict_ancestor to rename_conflict_ours
+2392a2dacc9efb562b8635d6579fb458751c7c5b f3293571dcd708b6a3faf03818cd2844d000e198 Edward Thomson <ethomson@microsoft.com> 1365715538 -0500 commit: rename conflict ours
+f3293571dcd708b6a3faf03818cd2844d000e198 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson <ethomson@microsoft.com> 1365715546 -0500 checkout: moving from rename_conflict_ours to rename_conflict_ancestor
+2392a2dacc9efb562b8635d6579fb458751c7c5b 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson <ethomson@microsoft.com> 1365715550 -0500 checkout: moving from rename_conflict_ancestor to rename_conflict_thiers
+2392a2dacc9efb562b8635d6579fb458751c7c5b 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson <ethomson@microsoft.com> 1365715554 -0500 checkout: moving from rename_conflict_thiers to rename_conflict_ancestor
+2392a2dacc9efb562b8635d6579fb458751c7c5b 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson <ethomson@microsoft.com> 1365715557 -0500 checkout: moving from rename_conflict_ancestor to rename_conflict_theirs
+2392a2dacc9efb562b8635d6579fb458751c7c5b a802e06f1782a9645b9851bc7202cee74a8a4972 Edward Thomson <ethomson@microsoft.com> 1365715572 -0500 commit: rename conflict theirs
+a802e06f1782a9645b9851bc7202cee74a8a4972 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@microsoft.com> 1365715620 -0500 checkout: moving from rename_conflict_theirs to master
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/branch
new file mode 100644
index 000000000..8b0acb702
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/branch
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1351563886 -0500 branch: Created from HEAD
+c607fc30883e335def28cd686b51f6cfa02b06ec 7cb63eed597130ba4abb87b3e544b85021905520 Edward Thomson <ethomson@edwardthomson.com> 1351563965 -0500 commit: branch
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/df_ancestor b/tests/resources/merge-resolve/.gitted/logs/refs/heads/df_ancestor
new file mode 100644
index 000000000..df7695a66
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/df_ancestor
@@ -0,0 +1,5 @@
+0000000000000000000000000000000000000000 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1355181639 -0600 branch: Created from HEAD
+bd593285fc7fe4ca18ccdbabf027f5d689101452 2da538570bc1e5b2c3e855bf702f35248ad0735f Edward Thomson <ethomson@edwardthomson.com> 1355181673 -0600 commit: df_ancestor
+2da538570bc1e5b2c3e855bf702f35248ad0735f a7dbfcbfc1a60709cb80b5ca24539008456531d0 Edward Thomson <ethomson@edwardthomson.com> 1355181715 -0600 commit: df_side1
+a7dbfcbfc1a60709cb80b5ca24539008456531d0 9a301fbe6fada7dcb74fcd7c20269b5c743459a7 Edward Thomson <ethomson@edwardthomson.com> 1355181775 -0600 commit: df_side2
+9a301fbe6fada7dcb74fcd7c20269b5c743459a7 2da538570bc1e5b2c3e855bf702f35248ad0735f Edward Thomson <ethomson@edwardthomson.com> 1355182067 -0600 reset: moving to 2da538570bc1e5b2c3e855bf702f35248ad0735f
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/df_side1 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/df_side1
new file mode 100644
index 000000000..a504ad610
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/df_side1
@@ -0,0 +1,14 @@
+0000000000000000000000000000000000000000 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1354574697 -0600 branch: Created from HEAD
+bd593285fc7fe4ca18ccdbabf027f5d689101452 d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1354574962 -0600 commit: df_ancestor
+d4207f77243500bec335ab477f9227fcdb1e271a c94b27e41064c521120627e07e2035cca1d24ffa Edward Thomson <ethomson@edwardthomson.com> 1354575027 -0600 commit: df_side1
+c94b27e41064c521120627e07e2035cca1d24ffa a90bc3fb6f15181972a2959a921429efbd81a473 Edward Thomson <ethomson@edwardthomson.com> 1355017650 -0600 commit: df_added
+a90bc3fb6f15181972a2959a921429efbd81a473 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson <ethomson@edwardthomson.com> 1355017676 -0600 rebase -i (finish): refs/heads/df_side1 onto c94b27e
+005b6fcc8fec71d2550bef8462d169b3c26aa14b f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 Edward Thomson <ethomson@edwardthomson.com> 1355017715 -0600 reset: moving to df_side2
+f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 8c749d9968d4b10dcfb06c9f97d0e5d92d337071 Edward Thomson <ethomson@edwardthomson.com> 1355017744 -0600 commit: df_added
+8c749d9968d4b10dcfb06c9f97d0e5d92d337071 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson <ethomson@edwardthomson.com> 1355017756 -0600 rebase -i (finish): refs/heads/df_side1 onto f8958bd
+0204a84f822acbf6386b36d33f1f6bc68bbbf858 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson <ethomson@edwardthomson.com> 1355017793 -0600 reset: moving to 005b6fcc8fec71d2550bef8462d169b3c26aa14b
+005b6fcc8fec71d2550bef8462d169b3c26aa14b 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson <ethomson@edwardthomson.com> 1355017826 -0600 reset: moving to 0204a84
+005b6fcc8fec71d2550bef8462d169b3c26aa14b e8107f24196736b870a318a0e28f048e29f6feff Edward Thomson <ethomson@edwardthomson.com> 1355169065 -0600 commit: df_side1
+e8107f24196736b870a318a0e28f048e29f6feff 80a8fbb3abb1ba423d554e9630b8fc2e5698f86b Edward Thomson <ethomson@edwardthomson.com> 1355169084 -0600 rebase -i (finish): refs/heads/df_side1 onto 005b6fc
+80a8fbb3abb1ba423d554e9630b8fc2e5698f86b e65a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812 Edward Thomson <ethomson@edwardthomson.com> 1355169419 -0600 commit: side1
+e65a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812 5dc1018e90b19654bee986b7a0c268804d39659d Edward Thomson <ethomson@edwardthomson.com> 1355169435 -0600 rebase -i (finish): refs/heads/df_side1 onto 80a8fbb
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/df_side2 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/df_side2
new file mode 100644
index 000000000..27d833eda
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/df_side2
@@ -0,0 +1,9 @@
+0000000000000000000000000000000000000000 d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1354575051 -0600 branch: Created from d4207f77243500bec335ab477f9227fcdb1e271a
+d4207f77243500bec335ab477f9227fcdb1e271a f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 Edward Thomson <ethomson@edwardthomson.com> 1354575206 -0600 commit: df_side2
+0204a84f822acbf6386b36d33f1f6bc68bbbf858 944f5dd1a867cab4c2bbcb896493435cae1dcc1a Edward Thomson <ethomson@edwardthomson.com> 1355169174 -0600 commit: both
+944f5dd1a867cab4c2bbcb896493435cae1dcc1a 57079a46233ae2b6df62e9ade71c4948512abefb Edward Thomson <ethomson@edwardthomson.com> 1355169185 -0600 rebase -i (finish): refs/heads/df_side2 onto 0204a84
+57079a46233ae2b6df62e9ade71c4948512abefb 58e853f66699fd02629fd50bde08082bc005933a Edward Thomson <ethomson@edwardthomson.com> 1355169460 -0600 commit: side2
+58e853f66699fd02629fd50bde08082bc005933a fada9356aa3f74622327a3038ae9c6f92e1c5c1d Edward Thomson <ethomson@edwardthomson.com> 1355169471 -0600 rebase -i (finish): refs/heads/df_side2 onto 57079a4
+fada9356aa3f74622327a3038ae9c6f92e1c5c1d 95646149ab6b6ba6edc83cff678582538b457b2b Edward Thomson <ethomson@edwardthomson.com> 1355169897 -0600 rebase finished: refs/heads/df_side2 onto a765fb87eb2f7a1920b73b2d5a057f8f8476a42b
+0000000000000000000000000000000000000000 2da538570bc1e5b2c3e855bf702f35248ad0735f Edward Thomson <ethomson@edwardthomson.com> 1355182087 -0600 branch: Created from HEAD
+2da538570bc1e5b2c3e855bf702f35248ad0735f fc90237dc4891fa6c69827fc465632225e391618 Edward Thomson <ethomson@edwardthomson.com> 1355182104 -0600 commit: df_side2
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/ff_branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/ff_branch
new file mode 100644
index 000000000..c4706175d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/ff_branch
@@ -0,0 +1,5 @@
+0000000000000000000000000000000000000000 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351605785 -0500 branch: Created from HEAD
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 33d500f588fbbe65901d82b4e6b008e549064be0 Edward Thomson <ethomson@edwardthomson.com> 1351605830 -0500 commit: fastforward
+33d500f588fbbe65901d82b4e6b008e549064be0 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1351990202 -0500 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1351990205 -0500 merge master: Fast-forward
+bd593285fc7fe4ca18ccdbabf027f5d689101452 fd89f8cffb663ac89095a0f9764902e93ceaca6a Edward Thomson <ethomson@edwardthomson.com> 1351990229 -0500 commit: fastforward
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/master b/tests/resources/merge-resolve/.gitted/logs/refs/heads/master
new file mode 100644
index 000000000..60475992a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/master
@@ -0,0 +1,5 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1351563869 -0500 commit (initial): initial
+c607fc30883e335def28cd686b51f6cfa02b06ec 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351564033 -0500 commit: master
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 4e0d9401aee78eb345a8685a859d37c8c3c0bbed Edward Thomson <ethomson@edwardthomson.com> 1351875091 -0500 merge octo1 octo2 octo3 octo4: Merge made by the 'octopus' strategy.
+4e0d9401aee78eb345a8685a859d37c8c3c0bbed 54269b3f6ec3d7d4ede24dd350dd5d605495c3ae Edward Thomson <ethomson@edwardthomson.com> 1351875108 -0500 reset: moving to 54269b3f6ec3d7d4ede24dd350dd5d605495c3ae
+54269b3f6ec3d7d4ede24dd350dd5d605495c3ae 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875584 -0500 reset: moving to 977c696519c5a3004c5f1d15d60c89dbeb8f235f
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo1 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo1
new file mode 100644
index 000000000..0b6c9214a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo1
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351874933 -0500 branch: Created from HEAD
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 16f825815cfd20a07a75c71554e82d8eede0b061 Edward Thomson <ethomson@edwardthomson.com> 1351874954 -0500 commit: octo1
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo2 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo2
new file mode 100644
index 000000000..5392a4f86
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo2
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351874960 -0500 branch: Created from HEAD
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 158dc7bedb202f5b26502bf3574faa7f4238d56c Edward Thomson <ethomson@edwardthomson.com> 1351874974 -0500 commit: octo2
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo3 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo3
new file mode 100644
index 000000000..7db5617c8
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo3
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351874980 -0500 branch: Created from HEAD
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 50ce7d7d01217679e26c55939eef119e0c93e272 Edward Thomson <ethomson@edwardthomson.com> 1351874998 -0500 commit: octo3
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo4 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo4
new file mode 100644
index 000000000..b0f9e42ef
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo4
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875010 -0500 branch: Created from HEAD
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 54269b3f6ec3d7d4ede24dd350dd5d605495c3ae Edward Thomson <ethomson@edwardthomson.com> 1351875023 -0500 commit: octo4
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo5 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo5
new file mode 100644
index 000000000..614563edf
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo5
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875031 -0500 branch: Created from HEAD
+977c696519c5a3004c5f1d15d60c89dbeb8f235f e4f618a2c3ed0669308735727df5ebf2447f022f Edward Thomson <ethomson@edwardthomson.com> 1351875041 -0500 commit: octo5
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo6 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo6
new file mode 100644
index 000000000..4c812eacc
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo6
@@ -0,0 +1,3 @@
+0000000000000000000000000000000000000000 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875046 -0500 branch: Created from HEAD
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 4ca408a8c88655f7586a1b580be6fad138121e98 Edward Thomson <ethomson@edwardthomson.com> 1351875057 -0500 commit: octo5
+4ca408a8c88655f7586a1b580be6fad138121e98 b6f610aef53bd343e6c96227de874c66f00ee8e8 Edward Thomson <ethomson@edwardthomson.com> 1351875065 -0500 commit (amend): octo6
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/renames1 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/renames1
new file mode 100644
index 000000000..58a7e0565
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/renames1
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1353177745 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec 412b32fb66137366147f1801ecc962452757d48a Edward Thomson <ethomson@edwardthomson.com> 1353177886 -0600 commit: renames
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/renames2 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/renames2
new file mode 100644
index 000000000..5645ecee7
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/renames2
@@ -0,0 +1,3 @@
+0000000000000000000000000000000000000000 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1353794647 -0600 branch: Created from HEAD
+bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1353794677 -0600 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec ab40af3cb8a3ed2e2843e96d9aa7871336b94573 Edward Thomson <ethomson@edwardthomson.com> 1353794852 -0600 commit: renames2
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10
new file mode 100644
index 000000000..b6bd247e7
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10
@@ -0,0 +1,3 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352100171 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec 53825f41ac8d640612f9423a2f03a69f3d96809a Edward Thomson <ethomson@edwardthomson.com> 1352100193 -0600 commit: trivial-10
+53825f41ac8d640612f9423a2f03a69f3d96809a 0ec5f433959cd46177f745903353efb5be08d151 Edward Thomson <ethomson@edwardthomson.com> 1352100223 -0600 commit: trivial-10
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10-branch
new file mode 100644
index 000000000..14ce9e545
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10-branch
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 53825f41ac8d640612f9423a2f03a69f3d96809a Edward Thomson <ethomson@edwardthomson.com> 1352100200 -0600 branch: Created from HEAD
+53825f41ac8d640612f9423a2f03a69f3d96809a 11f4f3c08b737f5fd896cbefa1425ee63b21b2fa Edward Thomson <ethomson@edwardthomson.com> 1352100211 -0600 commit: trivial-10-branch
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11
new file mode 100644
index 000000000..3e6b77437
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11
@@ -0,0 +1,3 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352100930 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec 35632e43612c06a3ea924bfbacd48333da874c29 Edward Thomson <ethomson@edwardthomson.com> 1352100958 -0600 commit: trivial-11
+35632e43612c06a3ea924bfbacd48333da874c29 3168dca1a561889b045a6441909f4c56145e666d Edward Thomson <ethomson@edwardthomson.com> 1352100992 -0600 commit: trivial-11
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11-branch
new file mode 100644
index 000000000..30d5ec7a3
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11-branch
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 35632e43612c06a3ea924bfbacd48333da874c29 Edward Thomson <ethomson@edwardthomson.com> 1352100964 -0600 branch: Created from HEAD
+35632e43612c06a3ea924bfbacd48333da874c29 6718a45909532d1fcf5600d0877f7fe7e78f0b86 Edward Thomson <ethomson@edwardthomson.com> 1352100978 -0600 commit: trivial-11-branch
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13
new file mode 100644
index 000000000..3a7302dea
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13
@@ -0,0 +1,3 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352100559 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec 8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa Edward Thomson <ethomson@edwardthomson.com> 1352100589 -0600 commit: trivial-13
+8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa a3fabece9eb8748da810e1e08266fef9b7136ad4 Edward Thomson <ethomson@edwardthomson.com> 1352100625 -0600 commit: trivial-13
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13-branch
new file mode 100644
index 000000000..bb2604244
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13-branch
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa Edward Thomson <ethomson@edwardthomson.com> 1352100604 -0600 branch: Created from HEAD
+8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa 05f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c Edward Thomson <ethomson@edwardthomson.com> 1352100610 -0600 commit: trivial-13-branch
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14
new file mode 100644
index 000000000..4b70d2898
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14
@@ -0,0 +1,3 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352101083 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec 596803b523203a4851c824c07366906f8353f4ad Edward Thomson <ethomson@edwardthomson.com> 1352101113 -0600 commit: trivial-14
+596803b523203a4851c824c07366906f8353f4ad 7e2d058d5fedf8329db44db4fac610d6b1a89159 Edward Thomson <ethomson@edwardthomson.com> 1352101141 -0600 commit: trivial-14
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14-branch
new file mode 100644
index 000000000..8e491ca68
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14-branch
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 596803b523203a4851c824c07366906f8353f4ad Edward Thomson <ethomson@edwardthomson.com> 1352101117 -0600 branch: Created from HEAD
+596803b523203a4851c824c07366906f8353f4ad 8187117062b750eed4f93fd7e899f17b52ce554d Edward Thomson <ethomson@edwardthomson.com> 1352101132 -0600 commit: trivial-14-branch
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt
new file mode 100644
index 000000000..a2a28d401
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352091695 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec 566ab53c220a2eafc1212af1a024513230280ab9 Edward Thomson <ethomson@edwardthomson.com> 1352092452 -0600 commit: 2alt
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt-branch
new file mode 100644
index 000000000..a0a48ae35
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt-branch
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352092411 -0600 branch: Created from HEAD
+c607fc30883e335def28cd686b51f6cfa02b06ec c9174cef549ec94ecbc43ef03cdc775b4950becb Edward Thomson <ethomson@edwardthomson.com> 1352092434 -0600 commit: 2alt-branch
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt
new file mode 100644
index 000000000..4374d3888
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt
@@ -0,0 +1,3 @@
+566ab53c220a2eafc1212af1a024513230280ab9 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352094547 -0600 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec 5459c89aa0026d543ce8343bd89871bce543f9c2 Edward Thomson <ethomson@edwardthomson.com> 1352094580 -0600 commit: 3alt
+5459c89aa0026d543ce8343bd89871bce543f9c2 4c9fac0707f8d4195037ae5a681aa48626491541 Edward Thomson <ethomson@edwardthomson.com> 1352094610 -0600 commit: 3alt-branch
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt-branch
new file mode 100644
index 000000000..7a2e6f822
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt-branch
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352094594 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4
new file mode 100644
index 000000000..3ee6d2503
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4
@@ -0,0 +1,2 @@
+566ab53c220a2eafc1212af1a024513230280ab9 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352094764 -0600 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec cc3e3009134cb88014129fc8858d1101359e5e2f Edward Thomson <ethomson@edwardthomson.com> 1352094815 -0600 commit: trivial-4
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4-branch
new file mode 100644
index 000000000..51f8a9290
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4-branch
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352094830 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec 183310e30fb1499af8c619108ffea4d300b5e778 Edward Thomson <ethomson@edwardthomson.com> 1352094856 -0600 commit: trivial-4-branch
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1
new file mode 100644
index 000000000..14497029a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352096606 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec 4fe93c0ec83eb6305cbace3dace88ecee1b63cb6 Edward Thomson <ethomson@edwardthomson.com> 1352096643 -0600 commit: 5alt-1
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1-branch
new file mode 100644
index 000000000..4cff83526
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1-branch
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352096657 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec 478172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2 Edward Thomson <ethomson@edwardthomson.com> 1352096689 -0600 commit: 5alt-1-branch
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2
new file mode 100644
index 000000000..3ca077b29
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2
@@ -0,0 +1,3 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352096711 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec ebc09d0137cfb0c26697aed0109fb943ad906f3f Edward Thomson <ethomson@edwardthomson.com> 1352096764 -0600 commit: existing file
+ebc09d0137cfb0c26697aed0109fb943ad906f3f 3b47b031b3e55ae11e14a05260b1c3ffd6838d55 Edward Thomson <ethomson@edwardthomson.com> 1352096815 -0600 commit: 5alt-2
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2-branch
new file mode 100644
index 000000000..e7bb901f2
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2-branch
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 ebc09d0137cfb0c26697aed0109fb943ad906f3f Edward Thomson <ethomson@edwardthomson.com> 1352096833 -0600 branch: Created from ebc09d0
+ebc09d0137cfb0c26697aed0109fb943ad906f3f f48097eb340dc5a7cae55aabcf1faf4548aa821f Edward Thomson <ethomson@edwardthomson.com> 1352096855 -0600 commit: 5alt-2-branch
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6
new file mode 100644
index 000000000..7c717a210
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6
@@ -0,0 +1,3 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352097371 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec f7c332bd4d4d4b777366cae4d24d1687477576bf Edward Thomson <ethomson@edwardthomson.com> 1352097389 -0600 commit: 6
+f7c332bd4d4d4b777366cae4d24d1687477576bf 99b4f7e4f24470fa06b980bc21f1095c2a9425c0 Edward Thomson <ethomson@edwardthomson.com> 1352097404 -0600 commit: trivial-6
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6-branch
new file mode 100644
index 000000000..715f3ae1c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6-branch
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 f7c332bd4d4d4b777366cae4d24d1687477576bf Edward Thomson <ethomson@edwardthomson.com> 1352097414 -0600 branch: Created from f7c332bd4d4d4b777366cae4d24d1687477576bf
+f7c332bd4d4d4b777366cae4d24d1687477576bf a43150a738849c59376cf30bb2a68348a83c8f48 Edward Thomson <ethomson@edwardthomson.com> 1352097431 -0600 commit: 6-branch
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7
new file mode 100644
index 000000000..a014f1722
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7
@@ -0,0 +1,3 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352099765 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson <ethomson@edwardthomson.com> 1352099790 -0600 commit: trivial-7
+092ce8682d7f3a2a3a769a6daca58950168ba5c4 d874671ef5b20184836cb983bb273e5280384d0b Edward Thomson <ethomson@edwardthomson.com> 1352099947 -0600 commit: trivial-7
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7-branch
new file mode 100644
index 000000000..22331d78c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7-branch
@@ -0,0 +1,5 @@
+0000000000000000000000000000000000000000 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson <ethomson@edwardthomson.com> 1352099799 -0600 branch: Created from HEAD
+092ce8682d7f3a2a3a769a6daca58950168ba5c4 73cbfdc4fe843169e5b2af8dcad03cbf3acf306c Edward Thomson <ethomson@edwardthomson.com> 1352099812 -0600 commit: trivial-7-branch
+73cbfdc4fe843169e5b2af8dcad03cbf3acf306c 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson <ethomson@edwardthomson.com> 1352099874 -0600 reset: moving to 092ce8682d7f3a2a3a769a6daca58950168ba5c4
+092ce8682d7f3a2a3a769a6daca58950168ba5c4 009b9cab6fdac02915a88ecd078b7a792ed802d8 Edward Thomson <ethomson@edwardthomson.com> 1352099921 -0600 commit: removed in 7
+009b9cab6fdac02915a88ecd078b7a792ed802d8 5195a1b480f66691b667f10a9e41e70115a78351 Edward Thomson <ethomson@edwardthomson.com> 1352099927 -0600 commit (amend): trivial-7-branch
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8
new file mode 100644
index 000000000..7670c3506
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8
@@ -0,0 +1,3 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352098816 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec 75a811bf6bc57694adb3fe604786f3a4efd1cd1b Edward Thomson <ethomson@edwardthomson.com> 1352098884 -0600 commit: trivial-8
+75a811bf6bc57694adb3fe604786f3a4efd1cd1b 3575826c96a975031d2c14368529cc5c4353a8fd Edward Thomson <ethomson@edwardthomson.com> 1352099000 -0600 commit: trivial-8
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8-branch
new file mode 100644
index 000000000..c4d68edcf
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8-branch
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 75a811bf6bc57694adb3fe604786f3a4efd1cd1b Edward Thomson <ethomson@edwardthomson.com> 1352098947 -0600 branch: Created from HEAD
+75a811bf6bc57694adb3fe604786f3a4efd1cd1b 52d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9 Edward Thomson <ethomson@edwardthomson.com> 1352098979 -0600 commit: trivial-8-branch
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9
new file mode 100644
index 000000000..09a343bdb
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9
@@ -0,0 +1,3 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352100268 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec f0053b8060bb3f0be5cbcc3147a07ece26bf097e Edward Thomson <ethomson@edwardthomson.com> 1352100304 -0600 commit: trivial-9
+f0053b8060bb3f0be5cbcc3147a07ece26bf097e c35dee9bcc0e989f3b0c40f68372a9a51b6c4e6a Edward Thomson <ethomson@edwardthomson.com> 1352100333 -0600 commit: trivial-9
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9-branch
new file mode 100644
index 000000000..1b126fb7b
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9-branch
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 f0053b8060bb3f0be5cbcc3147a07ece26bf097e Edward Thomson <ethomson@edwardthomson.com> 1352100310 -0600 branch: Created from HEAD
+f0053b8060bb3f0be5cbcc3147a07ece26bf097e 13d1be4ea52a6ced1d7a1d832f0ee3c399348e5e Edward Thomson <ethomson@edwardthomson.com> 1352100317 -0600 commit: trivial-9-branch
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/unrelated b/tests/resources/merge-resolve/.gitted/logs/refs/heads/unrelated
new file mode 100644
index 000000000..a83ffc26a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/unrelated
@@ -0,0 +1 @@
+d6cf6c7741b3316826af1314042550c97ded1d50 55b4e4687e7a0d9ca367016ed930f385d4022e6f Edward Thomson <ethomson@edwardthomson.com> 1358997664 -0600 commit: conflicting changes
diff --git a/tests/resources/merge-resolve/.gitted/objects/00/5b6fcc8fec71d2550bef8462d169b3c26aa14b b/tests/resources/merge-resolve/.gitted/objects/00/5b6fcc8fec71d2550bef8462d169b3c26aa14b
new file mode 100644
index 000000000..82a8da597
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/00/5b6fcc8fec71d2550bef8462d169b3c26aa14b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/00/9b9cab6fdac02915a88ecd078b7a792ed802d8 b/tests/resources/merge-resolve/.gitted/objects/00/9b9cab6fdac02915a88ecd078b7a792ed802d8
new file mode 100644
index 000000000..f663a3c51
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/00/9b9cab6fdac02915a88ecd078b7a792ed802d8
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/00/c7d33f1ffa79d19c2272b370fcaeaadba49c08 b/tests/resources/merge-resolve/.gitted/objects/00/c7d33f1ffa79d19c2272b370fcaeaadba49c08
new file mode 100644
index 000000000..72698dc3d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/00/c7d33f1ffa79d19c2272b370fcaeaadba49c08
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/01/f149e1b8f84bd8896aaff6d6b22af88459ded0 b/tests/resources/merge-resolve/.gitted/objects/01/f149e1b8f84bd8896aaff6d6b22af88459ded0
new file mode 100644
index 000000000..aa6336d3f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/01/f149e1b8f84bd8896aaff6d6b22af88459ded0
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/02/04a84f822acbf6386b36d33f1f6bc68bbbf858 b/tests/resources/merge-resolve/.gitted/objects/02/04a84f822acbf6386b36d33f1f6bc68bbbf858
new file mode 100644
index 000000000..2f0a0e1bb
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/02/04a84f822acbf6386b36d33f1f6bc68bbbf858
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/02/251f990ca8e92e7ae61d3426163fa821c64001 b/tests/resources/merge-resolve/.gitted/objects/02/251f990ca8e92e7ae61d3426163fa821c64001
new file mode 100644
index 000000000..d623117c5
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/02/251f990ca8e92e7ae61d3426163fa821c64001
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/03/21415405cb906c46869919af56d51dbbe5e85c b/tests/resources/merge-resolve/.gitted/objects/03/21415405cb906c46869919af56d51dbbe5e85c
new file mode 100644
index 000000000..277bdcff5
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/03/21415405cb906c46869919af56d51dbbe5e85c
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/03/2ebc5ab85d9553bb187d3cd40875ff23a63ed0 b/tests/resources/merge-resolve/.gitted/objects/03/2ebc5ab85d9553bb187d3cd40875ff23a63ed0
new file mode 100644
index 000000000..e5404d838
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/03/2ebc5ab85d9553bb187d3cd40875ff23a63ed0
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/03/b87706555accbf874ccd410dbda01e8e70a67f b/tests/resources/merge-resolve/.gitted/objects/03/b87706555accbf874ccd410dbda01e8e70a67f
new file mode 100644
index 000000000..0befcd735
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/03/b87706555accbf874ccd410dbda01e8e70a67f
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/03/dad1005e5d06d418f50b12e0bcd48ff2306a03 b/tests/resources/merge-resolve/.gitted/objects/03/dad1005e5d06d418f50b12e0bcd48ff2306a03
new file mode 100644
index 000000000..04011a2ce
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/03/dad1005e5d06d418f50b12e0bcd48ff2306a03
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/05/1ffd7901a442faf56b226161649074f15c7c47 b/tests/resources/merge-resolve/.gitted/objects/05/1ffd7901a442faf56b226161649074f15c7c47
new file mode 100644
index 000000000..65fa6894f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/05/1ffd7901a442faf56b226161649074f15c7c47
@@ -0,0 +1 @@
+x+)JMU06·`040031QH,-ÉÏM-JOMLÊIÕ+©(aH:,»:ÎCÉýúô:ÞË ²o>ZC'g$楧¦èfæé&%æ%g€5¬ÎqYôÂeÒZoÇÝÙkÚMíæ2­éÆÔ›X\’ZDPC~^ZNfrIf^:Xéõ›ZHÙŠž1O(_œ,'º× jvn~JfZ&Ä5†&ȚؽùÁ„ +gÆz¬Ÿ¥•4íú3Ž^¨¦¢ÔÜü2 ÜüI{•|þ¹÷ 2m»gÜ˾‹©É1ÖËåüŠ5Ó¿Ü,\“µ})TC)0XÀ¡vÿ‰ùzÖ›¦9–¤×Mü°úÕ…'6óbó#— \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/05/8541fc37114bfc1dddf6bd6bffc7fae5c2e6fe b/tests/resources/merge-resolve/.gitted/objects/05/8541fc37114bfc1dddf6bd6bffc7fae5c2e6fe
new file mode 100644
index 000000000..d79dc30ba
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/05/8541fc37114bfc1dddf6bd6bffc7fae5c2e6fe
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/05/f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c b/tests/resources/merge-resolve/.gitted/objects/05/f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c
new file mode 100644
index 000000000..7b4b152f3
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/05/f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/07/a759da919f737221791d542f176ab49c88837f b/tests/resources/merge-resolve/.gitted/objects/07/a759da919f737221791d542f176ab49c88837f
new file mode 100644
index 000000000..a34b6c235
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/07/a759da919f737221791d542f176ab49c88837f
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/07/c514b04698e068892b31c8d352b85813b99c6e b/tests/resources/merge-resolve/.gitted/objects/07/c514b04698e068892b31c8d352b85813b99c6e
new file mode 100644
index 000000000..23ab92171
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/07/c514b04698e068892b31c8d352b85813b99c6e
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/09/055301463b7f2f8ee5d368f8ed5c0a40ad8515 b/tests/resources/merge-resolve/.gitted/objects/09/055301463b7f2f8ee5d368f8ed5c0a40ad8515
new file mode 100644
index 000000000..bf5b0fcc5
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/09/055301463b7f2f8ee5d368f8ed5c0a40ad8515
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/09/17bb159596aea4d295f4857da77e8f96b3c7dc b/tests/resources/merge-resolve/.gitted/objects/09/17bb159596aea4d295f4857da77e8f96b3c7dc
new file mode 100644
index 000000000..9fb640dd5
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/09/17bb159596aea4d295f4857da77e8f96b3c7dc
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/09/2ce8682d7f3a2a3a769a6daca58950168ba5c4 b/tests/resources/merge-resolve/.gitted/objects/09/2ce8682d7f3a2a3a769a6daca58950168ba5c4
new file mode 100644
index 000000000..b709cf461
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/09/2ce8682d7f3a2a3a769a6daca58950168ba5c4
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/09/3bebf072dd4bbba88833667d6ffe454df199e1 b/tests/resources/merge-resolve/.gitted/objects/09/3bebf072dd4bbba88833667d6ffe454df199e1
new file mode 100644
index 000000000..ae13207d7
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/09/3bebf072dd4bbba88833667d6ffe454df199e1
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/09/768bed22680cdb0859683fa9677ccc8d5a25c1 b/tests/resources/merge-resolve/.gitted/objects/09/768bed22680cdb0859683fa9677ccc8d5a25c1
new file mode 100644
index 000000000..5f4b4dab1
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/09/768bed22680cdb0859683fa9677ccc8d5a25c1
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/0a/75d9aac1dc84fb5aa51f7325c0ab53242ddef7 b/tests/resources/merge-resolve/.gitted/objects/0a/75d9aac1dc84fb5aa51f7325c0ab53242ddef7
new file mode 100644
index 000000000..d5377341a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/0a/75d9aac1dc84fb5aa51f7325c0ab53242ddef7
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/0c/fd6c54ef6532d862408f562309dc9c74a401e8 b/tests/resources/merge-resolve/.gitted/objects/0c/fd6c54ef6532d862408f562309dc9c74a401e8
new file mode 100644
index 000000000..40f628f89
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/0c/fd6c54ef6532d862408f562309dc9c74a401e8
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/0d/52e3a556e189ba0948ae56780918011c1b167d b/tests/resources/merge-resolve/.gitted/objects/0d/52e3a556e189ba0948ae56780918011c1b167d
new file mode 100644
index 000000000..4b633e504
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/0d/52e3a556e189ba0948ae56780918011c1b167d
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/0d/872f8e871a30208305978ecbf9e66d864f1638 b/tests/resources/merge-resolve/.gitted/objects/0d/872f8e871a30208305978ecbf9e66d864f1638
new file mode 100644
index 000000000..4cbc18e84
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/0d/872f8e871a30208305978ecbf9e66d864f1638
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/0e/c5f433959cd46177f745903353efb5be08d151 b/tests/resources/merge-resolve/.gitted/objects/0e/c5f433959cd46177f745903353efb5be08d151
new file mode 100644
index 000000000..1bee56c14
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/0e/c5f433959cd46177f745903353efb5be08d151
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/11/deab00b2d3a6f5a3073988ac050c2d7b6655e2 b/tests/resources/merge-resolve/.gitted/objects/11/deab00b2d3a6f5a3073988ac050c2d7b6655e2
new file mode 100644
index 000000000..857b23686
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/11/deab00b2d3a6f5a3073988ac050c2d7b6655e2
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/11/f4f3c08b737f5fd896cbefa1425ee63b21b2fa b/tests/resources/merge-resolve/.gitted/objects/11/f4f3c08b737f5fd896cbefa1425ee63b21b2fa
new file mode 100644
index 000000000..6555194cb
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/11/f4f3c08b737f5fd896cbefa1425ee63b21b2fa
@@ -0,0 +1 @@
+x¥ŽQ Dýæ\ fw)cüñ^` Û´‰-Q¯/oàßÌ›Ìdb^×¥j²´«EDC²$†­u‚>Œ ¡÷,Ö z@Œ8¢’ºq‘­jk<Ù©GŽ>¹Òz2Lva2)¸VeÅ:ç¢ÏéÅ%éËœ×{ÞôAý¨“|ƒŸÛǼ5K@ˆº mg«ü9£jYž _;„n,¼ÅY½y²P” \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/13/d1be4ea52a6ced1d7a1d832f0ee3c399348e5e b/tests/resources/merge-resolve/.gitted/objects/13/d1be4ea52a6ced1d7a1d832f0ee3c399348e5e
new file mode 100644
index 000000000..4e4e175e8
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/13/d1be4ea52a6ced1d7a1d832f0ee3c399348e5e
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/14/39088f509b79b1535b64193137d3ce4b240734 b/tests/resources/merge-resolve/.gitted/objects/14/39088f509b79b1535b64193137d3ce4b240734
new file mode 100644
index 000000000..51ddf6dcb
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/14/39088f509b79b1535b64193137d3ce4b240734
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/15/8dc7bedb202f5b26502bf3574faa7f4238d56c b/tests/resources/merge-resolve/.gitted/objects/15/8dc7bedb202f5b26502bf3574faa7f4238d56c
new file mode 100644
index 000000000..064423d0c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/15/8dc7bedb202f5b26502bf3574faa7f4238d56c
@@ -0,0 +1,2 @@
+x¥ŽK!D]sŠ¾€¦ùCbŒoà i2. b¼¾£ñîªÞK*E½µÛep73UƒÓ¾*NYYôI²Ô”)–j¼ŠL:8§<‹{¼NˆÞ“‹ÎÊH6iDC¶Ê"mqH!–Ì9T¥mé9—>àR^i¸.½=ú
+GÞè'ù+~í@½@j+ƒ7ÑØ£EÝÎNþsFtš]‰7bN) \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/16/f825815cfd20a07a75c71554e82d8eede0b061 b/tests/resources/merge-resolve/.gitted/objects/16/f825815cfd20a07a75c71554e82d8eede0b061
new file mode 100644
index 000000000..82d65253b
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/16/f825815cfd20a07a75c71554e82d8eede0b061
@@ -0,0 +1 @@
+x¥ŽK!D]sŠ¾€†šObŒoà Œ ƒ¯ïh¼»ª÷’Jqoí6A¹›£°JÙ€ˆ¬“T1h¢3§£·Î'LÕ ó.‰{eœc,a`ŠZJÃT1#e+Ù‡œJòUiª">çÒ\ò+Ž ×¥·G_áX6úIçò¿vàÞN€šÐ;ÈÀ^’”b£ÛÙYþœgGñ—MMµ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/17/8940b450f238a56c0d75b7955cb57b38191982 b/tests/resources/merge-resolve/.gitted/objects/17/8940b450f238a56c0d75b7955cb57b38191982
new file mode 100644
index 000000000..94e571e65
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/17/8940b450f238a56c0d75b7955cb57b38191982
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/18/3310e30fb1499af8c619108ffea4d300b5e778 b/tests/resources/merge-resolve/.gitted/objects/18/3310e30fb1499af8c619108ffea4d300b5e778
new file mode 100644
index 000000000..1c4010d04
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/18/3310e30fb1499af8c619108ffea4d300b5e778
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/18/cb316b1cefa0f8a6946f0e201a8e1a6f845ab9 b/tests/resources/merge-resolve/.gitted/objects/18/cb316b1cefa0f8a6946f0e201a8e1a6f845ab9
new file mode 100644
index 000000000..30f3110f1
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/18/cb316b1cefa0f8a6946f0e201a8e1a6f845ab9
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/19/b7ac485269b672a101060894de3ba9c2a24dd1 b/tests/resources/merge-resolve/.gitted/objects/19/b7ac485269b672a101060894de3ba9c2a24dd1
new file mode 100644
index 000000000..e34ccb855
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/19/b7ac485269b672a101060894de3ba9c2a24dd1
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/1a/010b1c0f081b2e8901d55307a15c29ff30af0e b/tests/resources/merge-resolve/.gitted/objects/1a/010b1c0f081b2e8901d55307a15c29ff30af0e
new file mode 100644
index 000000000..6039df00e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/1a/010b1c0f081b2e8901d55307a15c29ff30af0e
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/1c/ff9ec6a47a537380dedfdd17c9e76d74259a2b b/tests/resources/merge-resolve/.gitted/objects/1c/ff9ec6a47a537380dedfdd17c9e76d74259a2b
new file mode 100644
index 000000000..30802bcec
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/1c/ff9ec6a47a537380dedfdd17c9e76d74259a2b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/1e/4ff029aee68d0d69ef9eb6efa6cbf1ec732f99 b/tests/resources/merge-resolve/.gitted/objects/1e/4ff029aee68d0d69ef9eb6efa6cbf1ec732f99
new file mode 100644
index 000000000..5183b8360
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/1e/4ff029aee68d0d69ef9eb6efa6cbf1ec732f99
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/1f/81433e3161efbf250576c58fede7f6b836f3d3 b/tests/resources/merge-resolve/.gitted/objects/1f/81433e3161efbf250576c58fede7f6b836f3d3
new file mode 100644
index 000000000..970855675
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/1f/81433e3161efbf250576c58fede7f6b836f3d3
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/20/91d94c8bd3eb0835dc5220de5e8bb310fa1513 b/tests/resources/merge-resolve/.gitted/objects/20/91d94c8bd3eb0835dc5220de5e8bb310fa1513
new file mode 100644
index 000000000..a843890c0
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/20/91d94c8bd3eb0835dc5220de5e8bb310fa1513
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/21/671e290278286fb2ce4c63d01699b67adce331 b/tests/resources/merge-resolve/.gitted/objects/21/671e290278286fb2ce4c63d01699b67adce331
new file mode 100644
index 000000000..b656d0001
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/21/671e290278286fb2ce4c63d01699b67adce331
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/22/7792b52aaa0b238bea00ec7e509b02623f168c b/tests/resources/merge-resolve/.gitted/objects/22/7792b52aaa0b238bea00ec7e509b02623f168c
new file mode 100644
index 000000000..3bb19bb77
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/22/7792b52aaa0b238bea00ec7e509b02623f168c
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/23/3c0919c998ed110a4b6ff36f353aec8b713487 b/tests/resources/merge-resolve/.gitted/objects/23/3c0919c998ed110a4b6ff36f353aec8b713487
new file mode 100644
index 000000000..d0c8c9e1d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/23/3c0919c998ed110a4b6ff36f353aec8b713487
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/23/92a2dacc9efb562b8635d6579fb458751c7c5b b/tests/resources/merge-resolve/.gitted/objects/23/92a2dacc9efb562b8635d6579fb458751c7c5b
new file mode 100644
index 000000000..86127a344
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/23/92a2dacc9efb562b8635d6579fb458751c7c5b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/24/1a1005cd9b980732741b74385b891142bcba28 b/tests/resources/merge-resolve/.gitted/objects/24/1a1005cd9b980732741b74385b891142bcba28
new file mode 100644
index 000000000..9b65f666f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/24/1a1005cd9b980732741b74385b891142bcba28
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/24/2591eb280ee9eeb2ce63524b9a8b9bc4cb515d b/tests/resources/merge-resolve/.gitted/objects/24/2591eb280ee9eeb2ce63524b9a8b9bc4cb515d
new file mode 100644
index 000000000..74a01373f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/24/2591eb280ee9eeb2ce63524b9a8b9bc4cb515d
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/24/90b9f1a079420870027deefb49f51d6656cf74 b/tests/resources/merge-resolve/.gitted/objects/24/90b9f1a079420870027deefb49f51d6656cf74
new file mode 100644
index 000000000..60497caa5
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/24/90b9f1a079420870027deefb49f51d6656cf74
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/25/9d08ca43af9200e9ea9a098e44a5a350ebd9b3 b/tests/resources/merge-resolve/.gitted/objects/25/9d08ca43af9200e9ea9a098e44a5a350ebd9b3
new file mode 100644
index 000000000..2bae66998
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/25/9d08ca43af9200e9ea9a098e44a5a350ebd9b3
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/25/c40b7660c08c8fb581f770312f41b9b03119d1 b/tests/resources/merge-resolve/.gitted/objects/25/c40b7660c08c8fb581f770312f41b9b03119d1
new file mode 100644
index 000000000..185214727
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/25/c40b7660c08c8fb581f770312f41b9b03119d1
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/26/153a3ff3649b6c2bb652d3f06878c6e0a172f9 b/tests/resources/merge-resolve/.gitted/objects/26/153a3ff3649b6c2bb652d3f06878c6e0a172f9
new file mode 100644
index 000000000..4fcaa07e2
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/26/153a3ff3649b6c2bb652d3f06878c6e0a172f9
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/27/133da702ba3c60af2a01e96c2555ff4045d692 b/tests/resources/merge-resolve/.gitted/objects/27/133da702ba3c60af2a01e96c2555ff4045d692
new file mode 100644
index 000000000..08e61f844
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/27/133da702ba3c60af2a01e96c2555ff4045d692
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/2b/0de5dc27505dcdd83a75c8bf1fcd9462cd7add b/tests/resources/merge-resolve/.gitted/objects/2b/0de5dc27505dcdd83a75c8bf1fcd9462cd7add
new file mode 100644
index 000000000..a95f926f8
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/2b/0de5dc27505dcdd83a75c8bf1fcd9462cd7add
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/2b/5f1f181ee3b58ea751f5dd5d8f9b445520a136 b/tests/resources/merge-resolve/.gitted/objects/2b/5f1f181ee3b58ea751f5dd5d8f9b445520a136
new file mode 100644
index 000000000..d24231eda
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/2b/5f1f181ee3b58ea751f5dd5d8f9b445520a136
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863 b/tests/resources/merge-resolve/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863
new file mode 100644
index 000000000..d10ca636b
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/2d/a538570bc1e5b2c3e855bf702f35248ad0735f b/tests/resources/merge-resolve/.gitted/objects/2d/a538570bc1e5b2c3e855bf702f35248ad0735f
new file mode 100644
index 000000000..83253f81c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/2d/a538570bc1e5b2c3e855bf702f35248ad0735f
@@ -0,0 +1,2 @@
+x¥ŽK
+1D]ç¹€ÒùN"n¼{ét:Œ‹L$ñúŽâ ÜU½EQ«õ>¤¶~7:³L ðÙD [´±5—¬É‡,Øy2e®ÐTت@”¦z*.û(ë´Àç˜[——üžåunum‹<òF?éÌ_ñkjõ$•qNå'#÷àÄF·³ƒÿœ¹Üp!^Gëâ 9+Q. \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/2f/2e37b7ebbae467978610896ca3aafcdad2ee67 b/tests/resources/merge-resolve/.gitted/objects/2f/2e37b7ebbae467978610896ca3aafcdad2ee67
new file mode 100644
index 000000000..7adffb165
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/2f/2e37b7ebbae467978610896ca3aafcdad2ee67
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/2f/4024ce528d36d8670c289cce5a7963e625bb0c b/tests/resources/merge-resolve/.gitted/objects/2f/4024ce528d36d8670c289cce5a7963e625bb0c
new file mode 100644
index 000000000..0100fd70e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/2f/4024ce528d36d8670c289cce5a7963e625bb0c
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/2f/56120107d680129a5d9791b521cb1e73a2ed31 b/tests/resources/merge-resolve/.gitted/objects/2f/56120107d680129a5d9791b521cb1e73a2ed31
new file mode 100644
index 000000000..1f5f597b9
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/2f/56120107d680129a5d9791b521cb1e73a2ed31
@@ -0,0 +1,3 @@
+xí[
+Â@ EýžUdR臸žy¤öBÌ”LJ·oÔnBð/pî#Ü"½Ðí>_|Å Ât@„
+“ñapg%hò•aãJYÁÕ®Aâ8Õ©í› fçà²ûN©ì¦¯4À;œ”h[º%cÍO¦ÆÎÕÑuJÑ÷çWÖyÎ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/2f/598248eeccfc27e5ca44d9d96383f6dfea7b16 b/tests/resources/merge-resolve/.gitted/objects/2f/598248eeccfc27e5ca44d9d96383f6dfea7b16
new file mode 100644
index 000000000..1d9f226e2
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/2f/598248eeccfc27e5ca44d9d96383f6dfea7b16
@@ -0,0 +1 @@
+x+)JMU067c040031QH,-ÉÏM-JOMLÊIÕ+©(aH:,»:ÎCÉýúô:ÞË ²o>ZC'g$楧¦èfæé&%æ%g€5¬ÎqYôÂeÒZoÇÝÙkÚMíæ2­éÆÔ›X\’ZDPC~^ZNfrIf^:Xéõ›ZHÙŠž1O(_œ,'º× jvQjn~Ä1–ÈÎÑ×3ßþzדôém9‹Wý¹ué]:¦$÷ßüI{•|þ¹÷ 2m»gÜ˾‹©Éý1ÖËåüŠ5Ó¿Ü,\“µ})TC)0PÀavý‰ùzÖ›¦9–¤×Mü°úÕ…'6óbˆ—¢ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/31/68dca1a561889b045a6441909f4c56145e666d b/tests/resources/merge-resolve/.gitted/objects/31/68dca1a561889b045a6441909f4c56145e666d
new file mode 100644
index 000000000..2de1c5a79
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/31/68dca1a561889b045a6441909f4c56145e666d
@@ -0,0 +1,2 @@
+x¥ŽQ
+Â0DýÎ)rJ²›MñÇxM²¥ÛHŒz}«xÿfÞƒaRY–¹ip´kUD $Ï1f¢Q2qê-Ó=Y£ëÁ3R7®²6äÄ¡·Œgàâ9e7 bæ¡w ‚âG›JÕçüâšõe*˽¬ú ý¤“|ůíSYŽÚ"5&Ðñƨng›ü9£ZŸ3_;kÕ¬dO¼ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/31/d5472536041a83d986829240bbbdc897c6f8a6 b/tests/resources/merge-resolve/.gitted/objects/31/d5472536041a83d986829240bbbdc897c6f8a6
new file mode 100644
index 000000000..5ec5acb59
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/31/d5472536041a83d986829240bbbdc897c6f8a6
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/32/21dd512b7e2dc4b5bd03046df6c81b2ab2070b b/tests/resources/merge-resolve/.gitted/objects/32/21dd512b7e2dc4b5bd03046df6c81b2ab2070b
new file mode 100644
index 000000000..d36138d79
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/32/21dd512b7e2dc4b5bd03046df6c81b2ab2070b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/33/46d64325b39e5323733492cd55f808994a2475 b/tests/resources/merge-resolve/.gitted/objects/33/46d64325b39e5323733492cd55f808994a2475
new file mode 100644
index 000000000..11546cea4
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/33/46d64325b39e5323733492cd55f808994a2475
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/33/d500f588fbbe65901d82b4e6b008e549064be0 b/tests/resources/merge-resolve/.gitted/objects/33/d500f588fbbe65901d82b4e6b008e549064be0
new file mode 100644
index 000000000..061a031b6
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/33/d500f588fbbe65901d82b4e6b008e549064be0
@@ -0,0 +1,2 @@
+x¥ŽA E]s
+. hbŒoত.Z Åx}[ã Üýÿ~òó¤Îó£këü¡·œµCAŒž<:km`d”„Ì‘d,!:𦳤žÜòÒõ‚øÁ“„œP1ÉPò qHcc±HEñ«Oµé[zsKú>Õy­‹>çî隿ï¤ÎmŒŠú 6ºÉöüç*¼öRÛn¢>úåOÇ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/34/bfafff88eaf118402b44e6f3e2dbbf1a582b05 b/tests/resources/merge-resolve/.gitted/objects/34/bfafff88eaf118402b44e6f3e2dbbf1a582b05
new file mode 100644
index 000000000..c653cec50
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/34/bfafff88eaf118402b44e6f3e2dbbf1a582b05
@@ -0,0 +1 @@
+x•ŽKj1D½Ö)ú úµ>`Œ7¾A. µ$<`MŒ¯orlŠ¢ ¸÷m‚¶þ4G­›·&údVdƒ[j2ÖµËJÉâ›C«ÑŠgu_GuÒ%ÅÚ2:ƒ3XúزÅàQ‘'Ì"½æÜÊ;?wîïp®kým×¾ÑàƒÛü&îPf!¢ ð%QJ±Ö%:ëûCˆeœzâ½=6šÀ¯qˆ;iOè \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/35/0c6eb3010efc403a6bed682332635314e9ed58 b/tests/resources/merge-resolve/.gitted/objects/35/0c6eb3010efc403a6bed682332635314e9ed58
new file mode 100644
index 000000000..2eee60233
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/35/0c6eb3010efc403a6bed682332635314e9ed58
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/35/411bfb77cd2cc431f3a03a2b4976ed94b5d241 b/tests/resources/merge-resolve/.gitted/objects/35/411bfb77cd2cc431f3a03a2b4976ed94b5d241
new file mode 100644
index 000000000..ea024ccd9
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/35/411bfb77cd2cc431f3a03a2b4976ed94b5d241
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/35/4704d3613ad4228e4786fc76656b11e98236c4 b/tests/resources/merge-resolve/.gitted/objects/35/4704d3613ad4228e4786fc76656b11e98236c4
new file mode 100644
index 000000000..1dd13c44a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/35/4704d3613ad4228e4786fc76656b11e98236c4
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/35/632e43612c06a3ea924bfbacd48333da874c29 b/tests/resources/merge-resolve/.gitted/objects/35/632e43612c06a3ea924bfbacd48333da874c29
new file mode 100644
index 000000000..be7684f19
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/35/632e43612c06a3ea924bfbacd48333da874c29
@@ -0,0 +1 @@
+x¥NË !õLÓÀšd–MŒñb60ÀÝă¨í‹Æ¼½^,ëº40;·iUFf+)›³ˆ1vòBÁ939fG–(ôDIݸʵA$s´è½k]’l|Lä{IgŠ™Ñ$‰Šm.NéÅ5Áy.ë½\a/]ý £|ÛƲ@[g4âä< Hˆª«ýl“?gT«ËsáË µzCøP˜ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/35/75826c96a975031d2c14368529cc5c4353a8fd b/tests/resources/merge-resolve/.gitted/objects/35/75826c96a975031d2c14368529cc5c4353a8fd
new file mode 100644
index 000000000..24e33bc41
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/35/75826c96a975031d2c14368529cc5c4353a8fd
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/36/219b49367146cb2e6a1555b5a9ebd4d0328495 b/tests/resources/merge-resolve/.gitted/objects/36/219b49367146cb2e6a1555b5a9ebd4d0328495
new file mode 100644
index 000000000..7f8044372
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/36/219b49367146cb2e6a1555b5a9ebd4d0328495
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/36/4bbe4ce80c7bd31e6307dce77d46e3e1759fb3 b/tests/resources/merge-resolve/.gitted/objects/36/4bbe4ce80c7bd31e6307dce77d46e3e1759fb3
new file mode 100644
index 000000000..90fd9651f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/36/4bbe4ce80c7bd31e6307dce77d46e3e1759fb3
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/37/48859b001c6e627e712a07951aee40afd19b41 b/tests/resources/merge-resolve/.gitted/objects/37/48859b001c6e627e712a07951aee40afd19b41
new file mode 100644
index 000000000..6a0c389e4
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/37/48859b001c6e627e712a07951aee40afd19b41
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/38/5c8a0f26ddf79e9041e15e17dc352ed2c4cced b/tests/resources/merge-resolve/.gitted/objects/38/5c8a0f26ddf79e9041e15e17dc352ed2c4cced
new file mode 100644
index 000000000..e95ff3a88
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/38/5c8a0f26ddf79e9041e15e17dc352ed2c4cced
@@ -0,0 +1,2 @@
+x-MK
+1 uS¼YÍRÄ…ñ™6C±…6Ò뛪ð¼okn÷ÇåYt àEpª iÅDûØCd§Éœ³dLõB+8ø%¨q‚±ƒk µÿú+Þe™Þ6¢ï‡©fHüBü·¯1J©ÕÓ4ùFà1l \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/3b/47b031b3e55ae11e14a05260b1c3ffd6838d55 b/tests/resources/merge-resolve/.gitted/objects/3b/47b031b3e55ae11e14a05260b1c3ffd6838d55
new file mode 100644
index 000000000..82086466f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/3b/47b031b3e55ae11e14a05260b1c3ffd6838d55
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/3b/bf0bf59b20df5d5fc58b9fc1dc07be637c301f b/tests/resources/merge-resolve/.gitted/objects/3b/bf0bf59b20df5d5fc58b9fc1dc07be637c301f
new file mode 100644
index 000000000..723a9ae4c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/3b/bf0bf59b20df5d5fc58b9fc1dc07be637c301f
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/3e/f4d30382ca33fdeba9fda895a99e0891ba37aa b/tests/resources/merge-resolve/.gitted/objects/3e/f4d30382ca33fdeba9fda895a99e0891ba37aa
new file mode 100644
index 000000000..49ee15239
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/3e/f4d30382ca33fdeba9fda895a99e0891ba37aa
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/3e/f9bfe82f9635518ae89152322f3b46fd4ba25b b/tests/resources/merge-resolve/.gitted/objects/3e/f9bfe82f9635518ae89152322f3b46fd4ba25b
new file mode 100644
index 000000000..3b5998ca6
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/3e/f9bfe82f9635518ae89152322f3b46fd4ba25b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/40/2784a46a4a3982294231594cbeb431f506d22c b/tests/resources/merge-resolve/.gitted/objects/40/2784a46a4a3982294231594cbeb431f506d22c
new file mode 100644
index 000000000..a17e05d0f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/40/2784a46a4a3982294231594cbeb431f506d22c
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/41/2b32fb66137366147f1801ecc962452757d48a b/tests/resources/merge-resolve/.gitted/objects/41/2b32fb66137366147f1801ecc962452757d48a
new file mode 100644
index 000000000..b183dd782
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/41/2b32fb66137366147f1801ecc962452757d48a
@@ -0,0 +1,2 @@
+x¥ŽK
+1D]ç¹€ÒIO>"n¼èt:Œ‹™‰x}£xwU¯ xÜj½um'»ë«ˆ.®Ì9»=y 6Ø$@T8ÌÀ‰&Lhf4êA«Ü»f¡0BŒ(ˆ.K±‘³>9S<›À +zö¥­ú’_´f}]ZÝÚ]eÐO:Ëwøµ·zÒš†‹ÞƒPƒÙ.Þ¨aNU6õÎOÖ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/43/aafd43bea779ec74317dc361f45ae3f532a505 b/tests/resources/merge-resolve/.gitted/objects/43/aafd43bea779ec74317dc361f45ae3f532a505
new file mode 100644
index 000000000..ac86823b6
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/43/aafd43bea779ec74317dc361f45ae3f532a505
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/43/c338656342227a3a3cd3aa85cbf784061f5425 b/tests/resources/merge-resolve/.gitted/objects/43/c338656342227a3a3cd3aa85cbf784061f5425
new file mode 100644
index 000000000..d9773118b
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/43/c338656342227a3a3cd3aa85cbf784061f5425
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/45/299c1ca5e07bba1fd90843056fb559f96b1f5a b/tests/resources/merge-resolve/.gitted/objects/45/299c1ca5e07bba1fd90843056fb559f96b1f5a
new file mode 100644
index 000000000..2093b4410
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/45/299c1ca5e07bba1fd90843056fb559f96b1f5a
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/46/6daf8552b891e5c22bc58c9d7fc1a2eb8f0289 b/tests/resources/merge-resolve/.gitted/objects/46/6daf8552b891e5c22bc58c9d7fc1a2eb8f0289
new file mode 100644
index 000000000..c39b53aa8
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/46/6daf8552b891e5c22bc58c9d7fc1a2eb8f0289
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/47/6dbb3e207313d1d8aaa120c6ad204bf1295e53 b/tests/resources/merge-resolve/.gitted/objects/47/6dbb3e207313d1d8aaa120c6ad204bf1295e53
new file mode 100644
index 000000000..3e5f66e55
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/47/6dbb3e207313d1d8aaa120c6ad204bf1295e53
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/47/8172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2 b/tests/resources/merge-resolve/.gitted/objects/47/8172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2
new file mode 100644
index 000000000..d9e250e66
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/47/8172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/49/130a28ef567af9a6a6104c38773fedfa5f9742 b/tests/resources/merge-resolve/.gitted/objects/49/130a28ef567af9a6a6104c38773fedfa5f9742
new file mode 100644
index 000000000..e2c49f5c4
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/49/130a28ef567af9a6a6104c38773fedfa5f9742
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/49/9df817155e4bdd3c6ee192a72c52f481818230 b/tests/resources/merge-resolve/.gitted/objects/49/9df817155e4bdd3c6ee192a72c52f481818230
new file mode 100644
index 000000000..9c7e471dd
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/49/9df817155e4bdd3c6ee192a72c52f481818230
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/4a/9550ebcc97ce22b22f45af7b829bb030d003f5 b/tests/resources/merge-resolve/.gitted/objects/4a/9550ebcc97ce22b22f45af7b829bb030d003f5
new file mode 100644
index 000000000..6ec674adc
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/4a/9550ebcc97ce22b22f45af7b829bb030d003f5
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/4b/253da36a0ae8bfce63aeabd8c5b58429925594 b/tests/resources/merge-resolve/.gitted/objects/4b/253da36a0ae8bfce63aeabd8c5b58429925594
new file mode 100644
index 000000000..1a4072794
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/4b/253da36a0ae8bfce63aeabd8c5b58429925594
@@ -0,0 +1,2 @@
+x ÅA
+€0 @ÏûŠ¾AðAILm l¡¦ÿ×¹ŒæÔvGxÑ#ÿ6Ù3¬‚tW BØ6—§”ßÐ%´h” \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/4b/48deed3a433909bfd6b6ab3d4b91348b6af464 b/tests/resources/merge-resolve/.gitted/objects/4b/48deed3a433909bfd6b6ab3d4b91348b6af464
new file mode 100644
index 000000000..328c8506e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/4b/48deed3a433909bfd6b6ab3d4b91348b6af464
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 b/tests/resources/merge-resolve/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904
new file mode 100644
index 000000000..adf64119a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/4c/9fac0707f8d4195037ae5a681aa48626491541 b/tests/resources/merge-resolve/.gitted/objects/4c/9fac0707f8d4195037ae5a681aa48626491541
new file mode 100644
index 000000000..6b8c85e2b
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/4c/9fac0707f8d4195037ae5a681aa48626491541
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/4c/a408a8c88655f7586a1b580be6fad138121e98 b/tests/resources/merge-resolve/.gitted/objects/4c/a408a8c88655f7586a1b580be6fad138121e98
new file mode 100644
index 000000000..15cb7f29a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/4c/a408a8c88655f7586a1b580be6fad138121e98
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/4e/0d9401aee78eb345a8685a859d37c8c3c0bbed b/tests/resources/merge-resolve/.gitted/objects/4e/0d9401aee78eb345a8685a859d37c8c3c0bbed
new file mode 100644
index 000000000..57f7eb68c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/4e/0d9401aee78eb345a8685a859d37c8c3c0bbed
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10 b/tests/resources/merge-resolve/.gitted/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10
new file mode 100644
index 000000000..53168a038
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/4e/b04c9e79e88f6640d01ff5b25ca2a60764f216 b/tests/resources/merge-resolve/.gitted/objects/4e/b04c9e79e88f6640d01ff5b25ca2a60764f216
new file mode 100644
index 000000000..f4ec0efec
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/4e/b04c9e79e88f6640d01ff5b25ca2a60764f216
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/4f/e93c0ec83eb6305cbace3dace88ecee1b63cb6 b/tests/resources/merge-resolve/.gitted/objects/4f/e93c0ec83eb6305cbace3dace88ecee1b63cb6
new file mode 100644
index 000000000..67dc6842f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/4f/e93c0ec83eb6305cbace3dace88ecee1b63cb6
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/50/12fd565b1393bdfda1805d4ec38ce6619e1fd1 b/tests/resources/merge-resolve/.gitted/objects/50/12fd565b1393bdfda1805d4ec38ce6619e1fd1
new file mode 100644
index 000000000..d629a23a1
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/50/12fd565b1393bdfda1805d4ec38ce6619e1fd1
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/50/4f75ac95a71ef98051817618576a68505b92f9 b/tests/resources/merge-resolve/.gitted/objects/50/4f75ac95a71ef98051817618576a68505b92f9
new file mode 100644
index 000000000..1b24c721a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/50/4f75ac95a71ef98051817618576a68505b92f9
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/50/84fc2a88b6bdba8db93bd3953a8f4fdb470238 b/tests/resources/merge-resolve/.gitted/objects/50/84fc2a88b6bdba8db93bd3953a8f4fdb470238
new file mode 100644
index 000000000..84c9987ce
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/50/84fc2a88b6bdba8db93bd3953a8f4fdb470238
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/50/ce7d7d01217679e26c55939eef119e0c93e272 b/tests/resources/merge-resolve/.gitted/objects/50/ce7d7d01217679e26c55939eef119e0c93e272
new file mode 100644
index 000000000..e2f9f67fd
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/50/ce7d7d01217679e26c55939eef119e0c93e272
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/51/95a1b480f66691b667f10a9e41e70115a78351 b/tests/resources/merge-resolve/.gitted/objects/51/95a1b480f66691b667f10a9e41e70115a78351
new file mode 100644
index 000000000..088ee5498
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/51/95a1b480f66691b667f10a9e41e70115a78351
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/52/d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9 b/tests/resources/merge-resolve/.gitted/objects/52/d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9
new file mode 100644
index 000000000..6522209bd
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/52/d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9
@@ -0,0 +1,3 @@
+x¥Ž]
+Â0„}Î)rÊnóÓD|ñ^`“ÝЂm$F½¾U¼o3ßä²,sÓ½Ã]«" #€ÇàÙbÈ"ö1± 9÷<Q7ª²6=8
+ˆ1û˜ÜàGKMv>²’cTôhS©úÌ/ª¬/SYîeÕÙè'ä+~mŸÊrÔh\c‡QwàÔF·³MþœQ­ÎÏ™®]èb¥5Mê ¨ÚRŸ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/53/825f41ac8d640612f9423a2f03a69f3d96809a b/tests/resources/merge-resolve/.gitted/objects/53/825f41ac8d640612f9423a2f03a69f3d96809a
new file mode 100644
index 000000000..08cb0b66f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/53/825f41ac8d640612f9423a2f03a69f3d96809a
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/54/269b3f6ec3d7d4ede24dd350dd5d605495c3ae b/tests/resources/merge-resolve/.gitted/objects/54/269b3f6ec3d7d4ede24dd350dd5d605495c3ae
new file mode 100644
index 000000000..4a2415339
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/54/269b3f6ec3d7d4ede24dd350dd5d605495c3ae
@@ -0,0 +1,2 @@
+x¥ŽK
+1D]ç¹€ÒI¦ó7ÞÀ LºÓŒ‹L$F¼¾£xwUïAQÔj½ m'Ø^Šv9 dÁ”-° £Æœ„ „\¼ äÀˆºÏ½¬C§È'&Î`"ÃÙÅĹä(Ö¡¨ù9–Öõ…_sg}]Z}´UËF?é\¾â×ÔêI‡&ëô@mt;;ÊŸ3ªÑh“z“ÎNÀ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/54/59c89aa0026d543ce8343bd89871bce543f9c2 b/tests/resources/merge-resolve/.gitted/objects/54/59c89aa0026d543ce8343bd89871bce543f9c2
new file mode 100644
index 000000000..178b833e8
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/54/59c89aa0026d543ce8343bd89871bce543f9c2
@@ -0,0 +1,3 @@
+x¥ŽÑ !Dý¦
+Ð,,,ãØÀ,9O blßÓØ3ï%“ÉmY.C[L›ÑEtd–`\ %aBH%TƒvB G%QphÐqªêÎ]nCg‚P3BŒ(ˆ¾Hµ1Š4yS)W;IVüsëúT^Ü‹>Ïmy´›ÞËJ?é(_ñk»Ü–ƒ6è-$ç#è-€ZézvÈŸ3
+ù:ÔNqMB \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/54/7607c690372fe81fab8e3bb44c530e129118fd b/tests/resources/merge-resolve/.gitted/objects/54/7607c690372fe81fab8e3bb44c530e129118fd
new file mode 100644
index 000000000..dccd22006
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/54/7607c690372fe81fab8e3bb44c530e129118fd
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/55/b4e4687e7a0d9ca367016ed930f385d4022e6f b/tests/resources/merge-resolve/.gitted/objects/55/b4e4687e7a0d9ca367016ed930f385d4022e6f
new file mode 100644
index 000000000..fb157a214
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/55/b4e4687e7a0d9ca367016ed930f385d4022e6f
@@ -0,0 +1 @@
+x¥ŽQjÃ0DûíSìZv¥ÕJ‚ú“ä²´Š µ•\¿Né ú7ó“û¶­Œã·±«‚Ôd-{á¹LìXçÀÖ'ÅŒ ÊçéžvmŠä*Ù{¦ÙZ’`$U²ÄÈÆ9ÌÑ-TNé{,}‡Ky¦½ÀuéÛ£78éA_éS‡¿ö‘ûv².ÄèEÞQ§ƒg‡þSsxZýZóXÛ ò’ÚMÓNRi \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/56/6ab53c220a2eafc1212af1a024513230280ab9 b/tests/resources/merge-resolve/.gitted/objects/56/6ab53c220a2eafc1212af1a024513230280ab9
new file mode 100644
index 000000000..a8855ae67
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/56/6ab53c220a2eafc1212af1a024513230280ab9
@@ -0,0 +1,3 @@
+x¥ŽÑ !Dý¦
+Ð,˱‰1þØ ,°äLD blßÓØ3ï%“I­ÖËÐhÃft Ù¡XvŽÄøÂäYÍ‚ñ`L2ÑМջ܆NsI¼·b­ËRЧLž¢3…RaÀ$Iñs,­ëS~qÏú¼´úh7½—•~ÒQ¾â×v©Õƒ6Ö!œê-€ZézvÈŸ3
+ù:Ô9âM& \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/56/a638b76b75e068590ac999c2f8621e7f3e264c b/tests/resources/merge-resolve/.gitted/objects/56/a638b76b75e068590ac999c2f8621e7f3e264c
new file mode 100644
index 000000000..36289bf7a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/56/a638b76b75e068590ac999c2f8621e7f3e264c
@@ -0,0 +1 @@
+x¥ŽAj!E³öu vuW©B6sƒ¹€–%ÝÛÁ6äúqÎÝçÁ<iµp o£«BNŒ¼D'LâJfç ­„”¸ lŽ<Æ4“yÆ®ç<–Íâ&JèóÊÙ³³‚>È$Ñ^•‘R²bâÏØ[‡{þ=Ãcoõj'|褯õUéíje¼K«Ÿ°¬Ln¡•n–¬5“ÎСÿP˜Y«‚´³|2`ìzôËü—zQ{ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/57/079a46233ae2b6df62e9ade71c4948512abefb b/tests/resources/merge-resolve/.gitted/objects/57/079a46233ae2b6df62e9ade71c4948512abefb
new file mode 100644
index 000000000..c7eabc46b
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/57/079a46233ae2b6df62e9ade71c4948512abefb
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/58/43febcb23480df0b5edb22a21c59c772bb8e29 b/tests/resources/merge-resolve/.gitted/objects/58/43febcb23480df0b5edb22a21c59c772bb8e29
new file mode 100644
index 000000000..f6b2a2bfe
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/58/43febcb23480df0b5edb22a21c59c772bb8e29
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/58/e853f66699fd02629fd50bde08082bc005933a b/tests/resources/merge-resolve/.gitted/objects/58/e853f66699fd02629fd50bde08082bc005933a
new file mode 100644
index 000000000..cf6db633c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/58/e853f66699fd02629fd50bde08082bc005933a
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/59/6803b523203a4851c824c07366906f8353f4ad b/tests/resources/merge-resolve/.gitted/objects/59/6803b523203a4851c824c07366906f8353f4ad
new file mode 100644
index 000000000..cbc8cbef3
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/59/6803b523203a4851c824c07366906f8353f4ad
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/5c/2411f8075f48a6b2fdb85ebc0d371747c4df15 b/tests/resources/merge-resolve/.gitted/objects/5c/2411f8075f48a6b2fdb85ebc0d371747c4df15
new file mode 100644
index 000000000..7b41413da
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/5c/2411f8075f48a6b2fdb85ebc0d371747c4df15
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/5c/341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d b/tests/resources/merge-resolve/.gitted/objects/5c/341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d
new file mode 100644
index 000000000..63c86bd33
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/5c/341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/5c/3b68a71fc4fa5d362fd3875e53137c6a5ab7a5 b/tests/resources/merge-resolve/.gitted/objects/5c/3b68a71fc4fa5d362fd3875e53137c6a5ab7a5
new file mode 100644
index 000000000..541001456
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/5c/3b68a71fc4fa5d362fd3875e53137c6a5ab7a5
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/5d/c1018e90b19654bee986b7a0c268804d39659d b/tests/resources/merge-resolve/.gitted/objects/5d/c1018e90b19654bee986b7a0c268804d39659d
new file mode 100644
index 000000000..7500b9914
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/5d/c1018e90b19654bee986b7a0c268804d39659d
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/5d/dd0fe66f990dc0e5cf9fec6d9b465240e9537f b/tests/resources/merge-resolve/.gitted/objects/5d/dd0fe66f990dc0e5cf9fec6d9b465240e9537f
new file mode 100644
index 000000000..9d8691eb2
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/5d/dd0fe66f990dc0e5cf9fec6d9b465240e9537f
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/5e/b7bb6a146eb3c7fd3990b240a2308eceb1cf8d b/tests/resources/merge-resolve/.gitted/objects/5e/b7bb6a146eb3c7fd3990b240a2308eceb1cf8d
new file mode 100644
index 000000000..aca2666cf
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/5e/b7bb6a146eb3c7fd3990b240a2308eceb1cf8d
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/5f/bfbdc04b4eca46f54f4853a3c5a1dce28f5165 b/tests/resources/merge-resolve/.gitted/objects/5f/bfbdc04b4eca46f54f4853a3c5a1dce28f5165
new file mode 100644
index 000000000..aec3867c8
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/5f/bfbdc04b4eca46f54f4853a3c5a1dce28f5165
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/60/91fc2c036a382a69489e3f518ee5aae9a4e567 b/tests/resources/merge-resolve/.gitted/objects/60/91fc2c036a382a69489e3f518ee5aae9a4e567
new file mode 100644
index 000000000..fa63afba1
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/60/91fc2c036a382a69489e3f518ee5aae9a4e567
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/61/340eeed7340fa6a8792def9a5938bb5d4434bb b/tests/resources/merge-resolve/.gitted/objects/61/340eeed7340fa6a8792def9a5938bb5d4434bb
new file mode 100644
index 000000000..e830cafe5
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/61/340eeed7340fa6a8792def9a5938bb5d4434bb
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/61/78885b38fe96e825ac0f492c0a941f288b37f6 b/tests/resources/merge-resolve/.gitted/objects/61/78885b38fe96e825ac0f492c0a941f288b37f6
new file mode 100644
index 000000000..bedc5f27e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/61/78885b38fe96e825ac0f492c0a941f288b37f6
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/62/12c31dab5e482247d7977e4f0dd3601decf13b b/tests/resources/merge-resolve/.gitted/objects/62/12c31dab5e482247d7977e4f0dd3601decf13b
new file mode 100644
index 000000000..b6f0607bb
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/62/12c31dab5e482247d7977e4f0dd3601decf13b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/62/269111c3b02a9355badcb9da8678b1bf41787b b/tests/resources/merge-resolve/.gitted/objects/62/269111c3b02a9355badcb9da8678b1bf41787b
new file mode 100644
index 000000000..0edf65994
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/62/269111c3b02a9355badcb9da8678b1bf41787b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/62/c4f6533c9a3894191fdcb96a3be935ade63f1a b/tests/resources/merge-resolve/.gitted/objects/62/c4f6533c9a3894191fdcb96a3be935ade63f1a
new file mode 100644
index 000000000..c0f822d2c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/62/c4f6533c9a3894191fdcb96a3be935ade63f1a
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/63/247125386de9ec90a27ad36169307bf8a11a38 b/tests/resources/merge-resolve/.gitted/objects/63/247125386de9ec90a27ad36169307bf8a11a38
new file mode 100644
index 000000000..bc2d7384d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/63/247125386de9ec90a27ad36169307bf8a11a38
@@ -0,0 +1 @@
+xÝ;1 D©}Šé¶A‹Vâô\ÀÙ8¬¥àHIVp|²?‰‚ÐLãyO“ÃuN7C] Í¥ƒËlãt¦:iAÐ(xiŒp‚,ÆOñÄæ¡;•æo†7 …UYZ B‡½ß÷ý]ÆdUmÔyk©ô[…½Úc©ñþÍ¥)©ñ!êX{¢¿Zó±ö \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/67/18a45909532d1fcf5600d0877f7fe7e78f0b86 b/tests/resources/merge-resolve/.gitted/objects/67/18a45909532d1fcf5600d0877f7fe7e78f0b86
new file mode 100644
index 000000000..ffda698f0
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/67/18a45909532d1fcf5600d0877f7fe7e78f0b86
@@ -0,0 +1 @@
+x¥ŽÑM1 DùNi`‘ǹDBè~è€ǧ]‰Ý ƒöYð7óž4íû¾M8<ÌaæF䬵@Ò˜r*‹Ü85ÆV«±eV÷.Î鉋”0($!“b½UÑ35É—¨¡8¹ÏµÿÒ¾d4ÿºöý£þÉNú“®ö+þÚ£öýÙ#q@€rÉ~àNzžöÏ7Çö¹ÉÛ‚¸Ô!‡®î÷uQu \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/68/c6c84b091926c7d90aa6a79b2bc3bb6adccd8e b/tests/resources/merge-resolve/.gitted/objects/68/c6c84b091926c7d90aa6a79b2bc3bb6adccd8e
new file mode 100644
index 000000000..1e4b07574
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/68/c6c84b091926c7d90aa6a79b2bc3bb6adccd8e
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/69/f570c57b24ea7c086e94c5e574964798321435 b/tests/resources/merge-resolve/.gitted/objects/69/f570c57b24ea7c086e94c5e574964798321435
new file mode 100644
index 000000000..6975f0bab
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/69/f570c57b24ea7c086e94c5e574964798321435
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/6a/e1a3967031a42cf955d9d5c2395211ac82f6cf b/tests/resources/merge-resolve/.gitted/objects/6a/e1a3967031a42cf955d9d5c2395211ac82f6cf
new file mode 100644
index 000000000..3b5713cbc
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/6a/e1a3967031a42cf955d9d5c2395211ac82f6cf
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/6b/7e37be8ce0b897093f2878a9dcd8f396beda2c b/tests/resources/merge-resolve/.gitted/objects/6b/7e37be8ce0b897093f2878a9dcd8f396beda2c
new file mode 100644
index 000000000..c39318683
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/6b/7e37be8ce0b897093f2878a9dcd8f396beda2c
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/6c/06dcd163587c2cc18be44857e0b71116382aeb b/tests/resources/merge-resolve/.gitted/objects/6c/06dcd163587c2cc18be44857e0b71116382aeb
new file mode 100644
index 000000000..2f54be818
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/6c/06dcd163587c2cc18be44857e0b71116382aeb
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/6f/32739c3724d1d5f855299309f388606f407468 b/tests/resources/merge-resolve/.gitted/objects/6f/32739c3724d1d5f855299309f388606f407468
new file mode 100644
index 000000000..6741aa4d5
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/6f/32739c3724d1d5f855299309f388606f407468
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/6f/a33014764bf1120a454eb8437ae098238e409b b/tests/resources/merge-resolve/.gitted/objects/6f/a33014764bf1120a454eb8437ae098238e409b
new file mode 100644
index 000000000..973a4f646
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/6f/a33014764bf1120a454eb8437ae098238e409b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/6f/be9fb85c86d7d1435f728da418bdff52c640a9 b/tests/resources/merge-resolve/.gitted/objects/6f/be9fb85c86d7d1435f728da418bdff52c640a9
new file mode 100644
index 000000000..a2c8d93ad
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/6f/be9fb85c86d7d1435f728da418bdff52c640a9
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/71/17467b18605a660ebe5586df69e2311ed5609f b/tests/resources/merge-resolve/.gitted/objects/71/17467b18605a660ebe5586df69e2311ed5609f
new file mode 100644
index 000000000..02e183144
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/71/17467b18605a660ebe5586df69e2311ed5609f
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/71/2ebba6669ea847d9829e4f1059d6c830c8b531 b/tests/resources/merge-resolve/.gitted/objects/71/2ebba6669ea847d9829e4f1059d6c830c8b531
new file mode 100644
index 000000000..dd7d58f1f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/71/2ebba6669ea847d9829e4f1059d6c830c8b531
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/71/add2d7b93d55bf3600f8a1582beceebbd050c8 b/tests/resources/merge-resolve/.gitted/objects/71/add2d7b93d55bf3600f8a1582beceebbd050c8
new file mode 100644
index 000000000..221afa3c8
--- /dev/null
+++ 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/resources/merge-resolve/.gitted/objects/74/df13f0793afdaa972150bba976f7de8284914e b/tests/resources/merge-resolve/.gitted/objects/74/df13f0793afdaa972150bba976f7de8284914e
new file mode 100644
index 000000000..cb50e6757
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/74/df13f0793afdaa972150bba976f7de8284914e
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/75/a811bf6bc57694adb3fe604786f3a4efd1cd1b b/tests/resources/merge-resolve/.gitted/objects/75/a811bf6bc57694adb3fe604786f3a4efd1cd1b
new file mode 100644
index 000000000..477fd87ec
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/75/a811bf6bc57694adb3fe604786f3a4efd1cd1b
@@ -0,0 +1,2 @@
+x¥ŽK
+1D]ç¹ÀHçk "n¼Hº;Ì€3‘õúŽâ ÜU½EQç©këý¦7 Ž€ !²7Xdc2±ÇR¬ƒ˜À©[j²tMv… :q.°‹Äc¦D* l†(¤Ò£µé3¿Rc}ë|¯‹>ÈJ?é$_ñk[ªóQ,ìÑë"€Zéz¶ËŸ3ª·é9¥ë€ê LŽOÊ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/76/63fce0130db092936b137cabd693ec234eb060 b/tests/resources/merge-resolve/.gitted/objects/76/63fce0130db092936b137cabd693ec234eb060
new file mode 100644
index 000000000..f578a4a68
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/76/63fce0130db092936b137cabd693ec234eb060
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/76/ab0e2868197ec158ddd6c78d8a0d2fd73d38f9 b/tests/resources/merge-resolve/.gitted/objects/76/ab0e2868197ec158ddd6c78d8a0d2fd73d38f9
new file mode 100644
index 000000000..4d41ad8cd
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/76/ab0e2868197ec158ddd6c78d8a0d2fd73d38f9
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/7a/a3edf2bcfee22398e6b55295aa56366b7aaf76 b/tests/resources/merge-resolve/.gitted/objects/7a/a3edf2bcfee22398e6b55295aa56366b7aaf76
new file mode 100644
index 000000000..09f1e4d3a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/7a/a3edf2bcfee22398e6b55295aa56366b7aaf76
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/7c/2c5228c9e90170d4a35e6558e47163daf092e5 b/tests/resources/merge-resolve/.gitted/objects/7c/2c5228c9e90170d4a35e6558e47163daf092e5
new file mode 100644
index 000000000..52fde92a1
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/7c/2c5228c9e90170d4a35e6558e47163daf092e5
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/7c/b63eed597130ba4abb87b3e544b85021905520 b/tests/resources/merge-resolve/.gitted/objects/7c/b63eed597130ba4abb87b3e544b85021905520
new file mode 100644
index 000000000..769f29c6e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/7c/b63eed597130ba4abb87b3e544b85021905520
@@ -0,0 +1,3 @@
+x¥ŽK
+1D]ç¹€Òùt'"n¼H:ÆÅL$F¼¾£xwUïAQÜ–å6´õf7ºˆI“T*zë±J
+–1#;@r´X]¬êžº¬C3A¨ì F'Îa‘j#Š”ÑTâšÀf a•žcn]_Ê+õ¢¯s[mÕGÙè'å+~íÀm9iãÐ ¹‰PïÔF·³CþœQ¹§•gõe"NÕ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/7e/2d058d5fedf8329db44db4fac610d6b1a89159 b/tests/resources/merge-resolve/.gitted/objects/7e/2d058d5fedf8329db44db4fac610d6b1a89159
new file mode 100644
index 000000000..d12d7b4a7
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/7e/2d058d5fedf8329db44db4fac610d6b1a89159
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/7f/7a2da58126226986d71c6ddfab4afba693280d b/tests/resources/merge-resolve/.gitted/objects/7f/7a2da58126226986d71c6ddfab4afba693280d
new file mode 100644
index 000000000..2f833c292
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/7f/7a2da58126226986d71c6ddfab4afba693280d
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/80/a8fbb3abb1ba423d554e9630b8fc2e5698f86b b/tests/resources/merge-resolve/.gitted/objects/80/a8fbb3abb1ba423d554e9630b8fc2e5698f86b
new file mode 100644
index 000000000..3daf6c3e0
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/80/a8fbb3abb1ba423d554e9630b8fc2e5698f86b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/81/87117062b750eed4f93fd7e899f17b52ce554d b/tests/resources/merge-resolve/.gitted/objects/81/87117062b750eed4f93fd7e899f17b52ce554d
new file mode 100644
index 000000000..19cac9faf
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/81/87117062b750eed4f93fd7e899f17b52ce554d
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/83/07d93a155903a5c49576583f0ce1f6ff897c0e b/tests/resources/merge-resolve/.gitted/objects/83/07d93a155903a5c49576583f0ce1f6ff897c0e
new file mode 100644
index 000000000..5a96a4e4e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/83/07d93a155903a5c49576583f0ce1f6ff897c0e
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/83/824a8c6658768e2013905219cc8c64cc3d9a2e b/tests/resources/merge-resolve/.gitted/objects/83/824a8c6658768e2013905219cc8c64cc3d9a2e
new file mode 100644
index 000000000..066190fb8
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/83/824a8c6658768e2013905219cc8c64cc3d9a2e
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/84/9619b03ae540acee4d1edec96b86993da6b497 b/tests/resources/merge-resolve/.gitted/objects/84/9619b03ae540acee4d1edec96b86993da6b497
new file mode 100644
index 000000000..67271ac50
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/84/9619b03ae540acee4d1edec96b86993da6b497
@@ -0,0 +1,3 @@
+x¥ŽK
+1D]ç¹€Òùv7ÞÀ½t’3‹L$ñúŽâ ÜU½E¥Vë<¤¶v7:³"xç¥K@›R¶
+rÌŠ#Ç"Ôy2[ Xµ5 r2ÆQ´ˆå¨5–”£bŠ=ÇÔº¼æõ,oS«k[ä‰7úIþŠ_;¤VÏRç”?ú`ä<€ØèvvðŸ3"¶1ÝóÜWñEÇP \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/84/de84f8f3a6d63e636ee9ad81f4b80512fa9bbe b/tests/resources/merge-resolve/.gitted/objects/84/de84f8f3a6d63e636ee9ad81f4b80512fa9bbe
new file mode 100644
index 000000000..32f1461d4
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/84/de84f8f3a6d63e636ee9ad81f4b80512fa9bbe
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/86/088dae8bade454995b21a1c88107b0e1accdab b/tests/resources/merge-resolve/.gitted/objects/86/088dae8bade454995b21a1c88107b0e1accdab
new file mode 100644
index 000000000..623a747f0
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/86/088dae8bade454995b21a1c88107b0e1accdab
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/87/b4926260d77a3b851e71ecce06839bd650b231 b/tests/resources/merge-resolve/.gitted/objects/87/b4926260d77a3b851e71ecce06839bd650b231
new file mode 100644
index 000000000..91944ffb5
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/87/b4926260d77a3b851e71ecce06839bd650b231
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/88/e185910a15cd13bdf44854ad037f4842b03b29 b/tests/resources/merge-resolve/.gitted/objects/88/e185910a15cd13bdf44854ad037f4842b03b29
new file mode 100644
index 000000000..ae1c5e242
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/88/e185910a15cd13bdf44854ad037f4842b03b29
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/8a/ad9d0ea334951da47b621a475b39cc6ed759bf b/tests/resources/merge-resolve/.gitted/objects/8a/ad9d0ea334951da47b621a475b39cc6ed759bf
new file mode 100644
index 000000000..5e2c94321
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/8a/ad9d0ea334951da47b621a475b39cc6ed759bf
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/8a/ae714f7d939309d7f132b30646d96743134a9f b/tests/resources/merge-resolve/.gitted/objects/8a/ae714f7d939309d7f132b30646d96743134a9f
new file mode 100644
index 000000000..34ff560e3
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/8a/ae714f7d939309d7f132b30646d96743134a9f
@@ -0,0 +1 @@
+x+)JMU06³`040031QH,-ÉÏM-JOMLÊIÕ+©(aH:,»:ÎCÉýúô:ÞË ²o>ZC'g$楧¦èfæé&%æ%g€5¬ÎqYôÂeÒZoÇÝÙkÚMíæ2­éÆÔ›X\’ZDPC~^ZNfrIf^:Xéõ›ZHÙŠž1O(_œ,'º× jv^j9È!Ɖ9%`¥<sBÞ§ÝHrèSæ¼3§d ã ¨Ò¢ÔÜü2 wßüI{•|þ¹÷ 2m»gÜ˾‹©ÉÝ1ÖËåüŠ5Ó¿Ü,\“µ})TC)00ÀavʉùzÖ›¦9–¤×Mü°úÕ…'6óbÊå’G \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/8b/095d8fd01594f4d14454d073e3ac57b9ce485f b/tests/resources/merge-resolve/.gitted/objects/8b/095d8fd01594f4d14454d073e3ac57b9ce485f
new file mode 100644
index 000000000..4ec013881
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/8b/095d8fd01594f4d14454d073e3ac57b9ce485f
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/8b/5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a b/tests/resources/merge-resolve/.gitted/objects/8b/5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a
new file mode 100644
index 000000000..f4249c23d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/8b/5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a
@@ -0,0 +1 @@
+xí± À0 S{Š"2ŽŒd,0²‘^?&S¤HóÕÝŸ[Ï8ï눪E›`Ñ„ËrƒZŠ*êdŒ¥­ÆrlŒ,©± ÙcbF/ ·“¶÷'¿ûågв \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/8b/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/resources/merge-resolve/.gitted/objects/8c/749d9968d4b10dcfb06c9f97d0e5d92d337071 b/tests/resources/merge-resolve/.gitted/objects/8c/749d9968d4b10dcfb06c9f97d0e5d92d337071
new file mode 100644
index 000000000..e42393cf7
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/8c/749d9968d4b10dcfb06c9f97d0e5d92d337071
@@ -0,0 +1,2 @@
+x¥ŽAB!C]s
+. a€ùcÜx÷f`†|_ b¼¾h¼»ö5mšëº^»¶ÞmzÑÙL“`ð”}$26#"8°ÅÆ`s.`Ԛܺ.!bH\<» i´"Á,K¦œ8¯èÙ—Úô‰_ÔXŸ—º>êMïeÐ:Ê7ø¹]®ëAƒC40ÏÞë­™ŒQƒŽ³]þœQ\.Ä,¬ÞVO  \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/8f/4433f8593ddd65b7dd43dd4564d841f4d9c8aa b/tests/resources/merge-resolve/.gitted/objects/8f/4433f8593ddd65b7dd43dd4564d841f4d9c8aa
new file mode 100644
index 000000000..d2de777cc
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/8f/4433f8593ddd65b7dd43dd4564d841f4d9c8aa
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/90/a336c7dacbe295159413559b0043b8bdc60d57 b/tests/resources/merge-resolve/.gitted/objects/90/a336c7dacbe295159413559b0043b8bdc60d57
new file mode 100644
index 000000000..35453ebfd
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/90/a336c7dacbe295159413559b0043b8bdc60d57
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/91/2b2d7819cf9c1029e414883857ed61d597a1a5 b/tests/resources/merge-resolve/.gitted/objects/91/2b2d7819cf9c1029e414883857ed61d597a1a5
new file mode 100644
index 000000000..d5df393e9
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/91/2b2d7819cf9c1029e414883857ed61d597a1a5
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/91/8bb3e09090a9995d48af9a2a6296d7e6088d1c b/tests/resources/merge-resolve/.gitted/objects/91/8bb3e09090a9995d48af9a2a6296d7e6088d1c
new file mode 100644
index 000000000..c214ab206
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/91/8bb3e09090a9995d48af9a2a6296d7e6088d1c
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/92/7d4943cdbdc9a667db8e62cfd0a41870235c51 b/tests/resources/merge-resolve/.gitted/objects/92/7d4943cdbdc9a667db8e62cfd0a41870235c51
new file mode 100644
index 000000000..b6b92c842
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/92/7d4943cdbdc9a667db8e62cfd0a41870235c51
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/93/77fccdb210540b8c0520cc6e80eb632c20bd25 b/tests/resources/merge-resolve/.gitted/objects/93/77fccdb210540b8c0520cc6e80eb632c20bd25
new file mode 100644
index 000000000..4b2d93b07
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/93/77fccdb210540b8c0520cc6e80eb632c20bd25
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/94/4f5dd1a867cab4c2bbcb896493435cae1dcc1a b/tests/resources/merge-resolve/.gitted/objects/94/4f5dd1a867cab4c2bbcb896493435cae1dcc1a
new file mode 100644
index 000000000..143093831
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/94/4f5dd1a867cab4c2bbcb896493435cae1dcc1a
@@ -0,0 +1,2 @@
+x¥ŽK!D]s
+. zšÄ7ÞÀ Ð|2. b¼¾h¼»ªWÉKÅVëmH0~7zÎÒ"¦PÈ9`ò:»ÀÑQôi)QÈLEyq=oC*P6-"4„l0StAŽHÌ<u$Âs¬­ËKz…žäumõÑ6yÌ“~Ò9‡_;ÄVORç4z½X¹W¨”˜tžùOà6Vññ4NF \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/94/8ba6e701c1edab0c2d394fb7c5538334129793 b/tests/resources/merge-resolve/.gitted/objects/94/8ba6e701c1edab0c2d394fb7c5538334129793
new file mode 100644
index 000000000..b3e3ef985
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/94/8ba6e701c1edab0c2d394fb7c5538334129793
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/95/646149ab6b6ba6edc83cff678582538b457b2b b/tests/resources/merge-resolve/.gitted/objects/95/646149ab6b6ba6edc83cff678582538b457b2b
new file mode 100644
index 000000000..de9ba2894
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/95/646149ab6b6ba6edc83cff678582538b457b2b
@@ -0,0 +1,3 @@
+x•ŽMÂ …]s
+. ™ÃObŒoàÞ eH»h1ãõEÓ ¸{?y_ÞX–enR}h•Y* üHFS
+€S ž!$À1…¨Å“*¯M’³˜£wUv4ôIt:ª„è²ÏÞ8KFEA¯6•*oéM5ÉûT–­¬òÌ=ýª+ÿŠÝƲ\ä Ñ CVÁˆžö³ÿÆà`ƒnLj”ÛœX‰iO\ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/95/9de65e568274120fdf9e3af9f77b1550122149 b/tests/resources/merge-resolve/.gitted/objects/95/9de65e568274120fdf9e3af9f77b1550122149
new file mode 100644
index 000000000..e998de849
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/95/9de65e568274120fdf9e3af9f77b1550122149
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/96/8ca794a4597f7f6abbb2b8d940b4078a0f3fd4 b/tests/resources/merge-resolve/.gitted/objects/96/8ca794a4597f7f6abbb2b8d940b4078a0f3fd4
new file mode 100644
index 000000000..359e43a88
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/96/8ca794a4597f7f6abbb2b8d940b4078a0f3fd4
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/97/7c696519c5a3004c5f1d15d60c89dbeb8f235f b/tests/resources/merge-resolve/.gitted/objects/97/7c696519c5a3004c5f1d15d60c89dbeb8f235f
new file mode 100644
index 000000000..e561b473f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/97/7c696519c5a3004c5f1d15d60c89dbeb8f235f
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/98/ba4205fcf31f5dd93c916d35fe3f3b3d0e6714 b/tests/resources/merge-resolve/.gitted/objects/98/ba4205fcf31f5dd93c916d35fe3f3b3d0e6714
new file mode 100644
index 000000000..6f5e97978
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/98/ba4205fcf31f5dd93c916d35fe3f3b3d0e6714
@@ -0,0 +1 @@
+x-ŒÁ Ã0 ûÖ ÐþºG% È…­"ë×Mú!@yÉjÂýñ¼½Šv¬j‚¢:ïAÜÁ‹ÂAÇèM~dú¹­ãÐ{.3Ñ);ÔlÂç]vi›ú6Á„D%þ «¯¦9fú|.z \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/98/d52d07c0b0bbf2b46548f6aa521295c2cb55db b/tests/resources/merge-resolve/.gitted/objects/98/d52d07c0b0bbf2b46548f6aa521295c2cb55db
new file mode 100644
index 000000000..c8d636e8b
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/98/d52d07c0b0bbf2b46548f6aa521295c2cb55db
@@ -0,0 +1,3 @@
+xíA
+€0=÷y„}NË®taÝJ[ï
+ú/^r“’´$ŒÓ<ô, ‡¨"1*[\™ †žYj Ñ‹(;Ôóm£9ƒ oNŒxcëz"1ï(»7„áy÷Û—.øõ®þ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/99/b4f7e4f24470fa06b980bc21f1095c2a9425c0 b/tests/resources/merge-resolve/.gitted/objects/99/b4f7e4f24470fa06b980bc21f1095c2a9425c0
new file mode 100644
index 000000000..01ad66eaa
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/99/b4f7e4f24470fa06b980bc21f1095c2a9425c0
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/9a/301fbe6fada7dcb74fcd7c20269b5c743459a7 b/tests/resources/merge-resolve/.gitted/objects/9a/301fbe6fada7dcb74fcd7c20269b5c743459a7
new file mode 100644
index 000000000..f413cc5f7
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/9a/301fbe6fada7dcb74fcd7c20269b5c743459a7
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/9a/f731fa116d1eb9a6c0109562472cfee6f5a979 b/tests/resources/merge-resolve/.gitted/objects/9a/f731fa116d1eb9a6c0109562472cfee6f5a979
new file mode 100644
index 000000000..53233c4f1
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/9a/f731fa116d1eb9a6c0109562472cfee6f5a979
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/9c/0b6c34ef379a42d858f03fef38630f476b9102 b/tests/resources/merge-resolve/.gitted/objects/9c/0b6c34ef379a42d858f03fef38630f476b9102
new file mode 100644
index 000000000..e6f850079
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/9c/0b6c34ef379a42d858f03fef38630f476b9102
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/9e/7f4359c469f309b6057febf4c6e80742cbed5b b/tests/resources/merge-resolve/.gitted/objects/9e/7f4359c469f309b6057febf4c6e80742cbed5b
new file mode 100644
index 000000000..72b7c49fc
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/9e/7f4359c469f309b6057febf4c6e80742cbed5b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/9e/fe7723802d4305142eee177e018fee1572c4f4 b/tests/resources/merge-resolve/.gitted/objects/9e/fe7723802d4305142eee177e018fee1572c4f4
new file mode 100644
index 000000000..c63fc2c96
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/9e/fe7723802d4305142eee177e018fee1572c4f4
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/9f/74397a3397b3585faf09e9926b110d7f654254 b/tests/resources/merge-resolve/.gitted/objects/9f/74397a3397b3585faf09e9926b110d7f654254
new file mode 100644
index 000000000..e7ec3973a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/9f/74397a3397b3585faf09e9926b110d7f654254
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/a0/31a28ae70e33a641ce4b8a8f6317f1ab79dee4 b/tests/resources/merge-resolve/.gitted/objects/a0/31a28ae70e33a641ce4b8a8f6317f1ab79dee4
new file mode 100644
index 000000000..a6c05d182
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/a0/31a28ae70e33a641ce4b8a8f6317f1ab79dee4
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/a3/9a620dae5bc8b4e771cd4d251b7d080401a21e b/tests/resources/merge-resolve/.gitted/objects/a3/9a620dae5bc8b4e771cd4d251b7d080401a21e
new file mode 100644
index 000000000..4d22586eb
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/a3/9a620dae5bc8b4e771cd4d251b7d080401a21e
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/a3/fabece9eb8748da810e1e08266fef9b7136ad4 b/tests/resources/merge-resolve/.gitted/objects/a3/fabece9eb8748da810e1e08266fef9b7136ad4
new file mode 100644
index 000000000..24d7dbc2e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/a3/fabece9eb8748da810e1e08266fef9b7136ad4
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/a4/1b1bb6d0be3c22fb654234c33b428e15c8cc27 b/tests/resources/merge-resolve/.gitted/objects/a4/1b1bb6d0be3c22fb654234c33b428e15c8cc27
new file mode 100644
index 000000000..60789ee36
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/a4/1b1bb6d0be3c22fb654234c33b428e15c8cc27
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/a4/3150a738849c59376cf30bb2a68348a83c8f48 b/tests/resources/merge-resolve/.gitted/objects/a4/3150a738849c59376cf30bb2a68348a83c8f48
new file mode 100644
index 000000000..06ae09eb6
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/a4/3150a738849c59376cf30bb2a68348a83c8f48
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/a5/563304ddf6caba25cb50323a2ea6f7dbfcadca b/tests/resources/merge-resolve/.gitted/objects/a5/563304ddf6caba25cb50323a2ea6f7dbfcadca
new file mode 100644
index 000000000..a831878f8
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/a5/563304ddf6caba25cb50323a2ea6f7dbfcadca
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/a7/08b253bd507417ec42d1467a7fd2d7519c4956 b/tests/resources/merge-resolve/.gitted/objects/a7/08b253bd507417ec42d1467a7fd2d7519c4956
new file mode 100644
index 000000000..bae752a09
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/a7/08b253bd507417ec42d1467a7fd2d7519c4956
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/a7/65fb87eb2f7a1920b73b2d5a057f8f8476a42b b/tests/resources/merge-resolve/.gitted/objects/a7/65fb87eb2f7a1920b73b2d5a057f8f8476a42b
new file mode 100644
index 000000000..30abd8b4d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/a7/65fb87eb2f7a1920b73b2d5a057f8f8476a42b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/a7/7a56a49f8f3ae242e02717f18ebbc60c5cc543 b/tests/resources/merge-resolve/.gitted/objects/a7/7a56a49f8f3ae242e02717f18ebbc60c5cc543
new file mode 100644
index 000000000..76dd5f91b
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/a7/7a56a49f8f3ae242e02717f18ebbc60c5cc543
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/a7/dbfcbfc1a60709cb80b5ca24539008456531d0 b/tests/resources/merge-resolve/.gitted/objects/a7/dbfcbfc1a60709cb80b5ca24539008456531d0
new file mode 100644
index 000000000..67126c90b
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/a7/dbfcbfc1a60709cb80b5ca24539008456531d0
@@ -0,0 +1 @@
+x¥NË !õL4 a˜ÀÄ/vàÝð²{`1,ÆöEcÞÞ?/ÖR–.õ„»Þ˜%3$“ÁL15fe53'4Á2ÅÃ7^G1yBGV…LAGä Ù*‘ôä|R) ÿìsmòš^¾%y›kÙê*O<Ôºð×ø±C¬å,‰À’{e”Cg;ÿ9#R¾oKboœ³NÀ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/a8/02e06f1782a9645b9851bc7202cee74a8a4972 b/tests/resources/merge-resolve/.gitted/objects/a8/02e06f1782a9645b9851bc7202cee74a8a4972
new file mode 100644
index 000000000..d39034b82
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/a8/02e06f1782a9645b9851bc7202cee74a8a4972
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/a8/87dd39ad3edd610fc9083dcb61e40ab50673d1 b/tests/resources/merge-resolve/.gitted/objects/a8/87dd39ad3edd610fc9083dcb61e40ab50673d1
new file mode 100644
index 000000000..968c42ae2
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/a8/87dd39ad3edd610fc9083dcb61e40ab50673d1
@@ -0,0 +1 @@
+x+)JMU067a040031QH,-ÉÏM-JOMLÊIÕ+©(aH:,»:ÎCÉýúô:ÞË ²o>ZC'g$楧¦èfæé&%æ%g€5¬ÎqYôÂeÒZoÇÝÙkÚMíæ2­éÆÔ›X\’ZDPC~^ZNfrIf^:Xéõ›ZHÙŠž1O(_œ,'º× jv^j¹nb^ŠnJfZZjQj^ XÃû#3ƒ|>²^Uó:þ'äAÝÔ2¿†R¨†¢ÔÜü2 ×ßüI{•|þ¹÷ 2m»gÜ˾‹©Éõ1ÖËåüŠ5Ó¿Ü,\“µ})TC)0HÀ!vʉùzÖ›¦9–¤×Mü°úÕ…'6óbG–x \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/a9/0bc3fb6f15181972a2959a921429efbd81a473 b/tests/resources/merge-resolve/.gitted/objects/a9/0bc3fb6f15181972a2959a921429efbd81a473
new file mode 100644
index 000000000..91113ee8e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/a9/0bc3fb6f15181972a2959a921429efbd81a473
@@ -0,0 +1,2 @@
+x¥ŽK
+1D]ç}¥;ÿ7ÞÀ½dÒ=Œ‹‰¯ooà®^UQT®¥\;hk6½‰@¦™Â‰gŒ §5r’èƒÑ“–Œ]uOMnòdgÄz›&ÒècÆ圈õ¨'•ž}­ NüJá¼Öò¨7ØËp?ê(ßàG»\ËÈ8‡¼CØ¢GTÃg»ü9£x¹$faõxN" \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/ab/40af3cb8a3ed2e2843e96d9aa7871336b94573 b/tests/resources/merge-resolve/.gitted/objects/ab/40af3cb8a3ed2e2843e96d9aa7871336b94573
new file mode 100644
index 000000000..7da1da656
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ab/40af3cb8a3ed2e2843e96d9aa7871336b94573
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ab/6c44a2e84492ad4b41bb6bac87353e9d02ac8b b/tests/resources/merge-resolve/.gitted/objects/ab/6c44a2e84492ad4b41bb6bac87353e9d02ac8b
new file mode 100644
index 000000000..d840c1a57
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ab/6c44a2e84492ad4b41bb6bac87353e9d02ac8b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ab/929391ac42572f92110f3deeb4f0844a951e22 b/tests/resources/merge-resolve/.gitted/objects/ab/929391ac42572f92110f3deeb4f0844a951e22
new file mode 100644
index 000000000..8840d00c5
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ab/929391ac42572f92110f3deeb4f0844a951e22
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ac/4045f965119e6998f4340ed0f411decfb3ec05 b/tests/resources/merge-resolve/.gitted/objects/ac/4045f965119e6998f4340ed0f411decfb3ec05
new file mode 100644
index 000000000..4c32d63f8
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ac/4045f965119e6998f4340ed0f411decfb3ec05
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ad/a14492498136771f69dd451866cabcb0e9ef9a b/tests/resources/merge-resolve/.gitted/objects/ad/a14492498136771f69dd451866cabcb0e9ef9a
new file mode 100644
index 000000000..71023de39
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ad/a14492498136771f69dd451866cabcb0e9ef9a
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ad/a55a45d14527dc3dfc714ea1c65d2e1e6fbe87 b/tests/resources/merge-resolve/.gitted/objects/ad/a55a45d14527dc3dfc714ea1c65d2e1e6fbe87
new file mode 100644
index 000000000..3091b8f3d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ad/a55a45d14527dc3dfc714ea1c65d2e1e6fbe87
@@ -0,0 +1 @@
+x+)JMU067d040031QH,-ÉÏM-JOMLÊIÕ+©(aH:,»:ÎCÉýúô:ÞË ²o>ZC'g$楧¦èfæé&%æ%g€5¬ÎqYôÂeÒZoÇÝÙkÚMíæ2­éÆÔ›X\’ZDPC~^ZNfrIf^:Xéõ›ZHÙŠž1O(_œ,'º× jvn~JfZ&Ä5†Æ`ÕÆnלU7Ï V.6™t6ôÇL/•R¨ê¢ÔÜü2 §ßüI{•|þ¹÷ 2m»gÜ˾‹©Éé1ÖËåüŠ5Ó¿Ü,\“µ})TC)0<ÀÁvʉùzÖ›¦9–¤×Mü°úÕ…'6ób±N’* \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/b2/d399ae15224e1d58066e3c8df70ce37de7a656 b/tests/resources/merge-resolve/.gitted/objects/b2/d399ae15224e1d58066e3c8df70ce37de7a656
new file mode 100644
index 000000000..20fa838f2
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/b2/d399ae15224e1d58066e3c8df70ce37de7a656
@@ -0,0 +1,2 @@
+xíQA1ôÌ+x€ÙÄ‹‰Ï¡-kI*5”f¿/»zõîÁ af˜!¤Ö^/·“W¸Jcܤ5LŒÆ›‰;+ŠBŸ6ÎHZP|`îóh>\(óÙ$“sà´î•íX·@š¢75€}57¹K
+¯µ+= ;g—® @нÒ!¬4Úè!Œ,\$\ \Âb/±ÉHsø©#þa¾¼÷QÄß \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/b4/2712cfe99a1a500b2a51fe984e0b8a7702ba11 b/tests/resources/merge-resolve/.gitted/objects/b4/2712cfe99a1a500b2a51fe984e0b8a7702ba11
new file mode 100644
index 000000000..2820b46cc
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/b4/2712cfe99a1a500b2a51fe984e0b8a7702ba11
@@ -0,0 +1,5 @@
+xíA
+À {öû†Bé{M1  ¨¥ß¯>£PØËË–‹3ŽýÜFÖŽ7Á¥E02 <Z
+XÐĨJ
+ÃAˆ^«Ú
+ündim=p#‹Yz¿÷“?¼ \»š \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/b6/9fe837e4cecfd4c9a40cdca7c138468687df07 b/tests/resources/merge-resolve/.gitted/objects/b6/9fe837e4cecfd4c9a40cdca7c138468687df07
new file mode 100644
index 000000000..6dbcb05ea
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/b6/9fe837e4cecfd4c9a40cdca7c138468687df07
@@ -0,0 +1,2 @@
+xí’A
+1E»Î)þf7í}ÌŒ!‚QHzýZºí\|üOùU½âµ?Ñe!‹pJk<ÙM”oQ-¤ËQéÑàáI>†Ûý¶‹XŽ0üš¹Ì’ê,)ë$;:¯‚­Ü·îþÍÆ(óä: \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/b6/f610aef53bd343e6c96227de874c66f00ee8e8 b/tests/resources/merge-resolve/.gitted/objects/b6/f610aef53bd343e6c96227de874c66f00ee8e8
new file mode 100644
index 000000000..fb102f15d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/b6/f610aef53bd343e6c96227de874c66f00ee8e8
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/b7/a2576f9fc20024ac9ef17cb134acbd1ac73127 b/tests/resources/merge-resolve/.gitted/objects/b7/a2576f9fc20024ac9ef17cb134acbd1ac73127
new file mode 100644
index 000000000..22f2d137d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/b7/a2576f9fc20024ac9ef17cb134acbd1ac73127
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/b8/a3a806d3950e8c0a03a34f234a92eff0e2c68d b/tests/resources/merge-resolve/.gitted/objects/b8/a3a806d3950e8c0a03a34f234a92eff0e2c68d
new file mode 100644
index 000000000..24f029900
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/b8/a3a806d3950e8c0a03a34f234a92eff0e2c68d
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ba/cac9b3493509aa15e1730e1545fc0919d1dae0 b/tests/resources/merge-resolve/.gitted/objects/ba/cac9b3493509aa15e1730e1545fc0919d1dae0
new file mode 100644
index 000000000..f35586f7f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ba/cac9b3493509aa15e1730e1545fc0919d1dae0
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/bc/744705e1d8a019993cf88f62bc4020f1b80919 b/tests/resources/merge-resolve/.gitted/objects/bc/744705e1d8a019993cf88f62bc4020f1b80919
new file mode 100644
index 000000000..0d4bdb323
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/bc/744705e1d8a019993cf88f62bc4020f1b80919
@@ -0,0 +1,2 @@
+x¥ŽK
+1D]ç}%ÿˆ¸ñî%t˜YÄH&âõâ ÜU½¢ŠJ­Öu€Ôj7:P(Ú#¥”¬ÌF§Ì„Ä£1”°Ù+kÙ#vºˆÎš‚ÞÊ⢒£S8«‘W|ñÚÙ¨%²øKëpɯØ3\—V·v‡#MúQgú?wH­ž@(c„ ž ØsË9›tžôç Ë嶭™{ŸkOó \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/bc/95c75d59386147d1e79a87c33068d8dbfd71f2 b/tests/resources/merge-resolve/.gitted/objects/bc/95c75d59386147d1e79a87c33068d8dbfd71f2
new file mode 100644
index 000000000..436d5a076
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/bc/95c75d59386147d1e79a87c33068d8dbfd71f2
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/bd/593285fc7fe4ca18ccdbabf027f5d689101452 b/tests/resources/merge-resolve/.gitted/objects/bd/593285fc7fe4ca18ccdbabf027f5d689101452
new file mode 100644
index 000000000..75ab1f0f3
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/bd/593285fc7fe4ca18ccdbabf027f5d689101452
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/bd/867fbae2faa80b920b002b80b1c91bcade7784 b/tests/resources/merge-resolve/.gitted/objects/bd/867fbae2faa80b920b002b80b1c91bcade7784
new file mode 100644
index 000000000..0f7421963
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/bd/867fbae2faa80b920b002b80b1c91bcade7784
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/bd/9cb4cd0a770cb9adcb5fce212142ef40ea1c35 b/tests/resources/merge-resolve/.gitted/objects/bd/9cb4cd0a770cb9adcb5fce212142ef40ea1c35
new file mode 100644
index 000000000..2aafdc64f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/bd/9cb4cd0a770cb9adcb5fce212142ef40ea1c35
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/be/f6e37b3ee632ba74159168836f382fed21d77d b/tests/resources/merge-resolve/.gitted/objects/be/f6e37b3ee632ba74159168836f382fed21d77d
new file mode 100644
index 000000000..6c243150d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/be/f6e37b3ee632ba74159168836f382fed21d77d
@@ -0,0 +1,2 @@
+x¥K
+1D]ç}¥»3?AÄ7p?ô$=Œ`Ò‰x}GñîªÞƒª`)Ý+0Ñ®Uhú.N“WÆÞ“!ÆÐIdl¦™øØjë<ëb®ñ%%Âm±´Z†“nô“.ú¿v–Î@¾Cdjy€=ˆn£ÛyÕ?g\Ñ,IÇ`y~ÜC%]«÷½¿H& \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/c0/6a9be584ac49aa02c5551312d9e2982c91df10 b/tests/resources/merge-resolve/.gitted/objects/c0/6a9be584ac49aa02c5551312d9e2982c91df10
new file mode 100644
index 000000000..963ef23ac
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/c0/6a9be584ac49aa02c5551312d9e2982c91df10
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/c1/b17981db0840109a820dae8674ee29684134ff b/tests/resources/merge-resolve/.gitted/objects/c1/b17981db0840109a820dae8674ee29684134ff
new file mode 100644
index 000000000..fdcf28cb0
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/c1/b17981db0840109a820dae8674ee29684134ff
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/c1/b6a51bbb87c2f82b161412c3d20b59fc69b090 b/tests/resources/merge-resolve/.gitted/objects/c1/b6a51bbb87c2f82b161412c3d20b59fc69b090
new file mode 100644
index 000000000..3b369f8fe
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/c1/b6a51bbb87c2f82b161412c3d20b59fc69b090
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/c3/5dee9bcc0e989f3b0c40f68372a9a51b6c4e6a b/tests/resources/merge-resolve/.gitted/objects/c3/5dee9bcc0e989f3b0c40f68372a9a51b6c4e6a
new file mode 100644
index 000000000..d22b3b23c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/c3/5dee9bcc0e989f3b0c40f68372a9a51b6c4e6a
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/c3/d02eeef75183df7584d8d13ac03053910c1301 b/tests/resources/merge-resolve/.gitted/objects/c3/d02eeef75183df7584d8d13ac03053910c1301
new file mode 100644
index 000000000..2294f018d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/c3/d02eeef75183df7584d8d13ac03053910c1301
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/c4/efe31e9decccc8b2b4d3df9aac2cdfe2995618 b/tests/resources/merge-resolve/.gitted/objects/c4/efe31e9decccc8b2b4d3df9aac2cdfe2995618
new file mode 100644
index 000000000..c7572d5bc
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/c4/efe31e9decccc8b2b4d3df9aac2cdfe2995618
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/c5/0d0f1cb60b8b0fe1615ad20ace557e9d68d7bd b/tests/resources/merge-resolve/.gitted/objects/c5/0d0f1cb60b8b0fe1615ad20ace557e9d68d7bd
new file mode 100644
index 000000000..a1d5321e8
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/c5/0d0f1cb60b8b0fe1615ad20ace557e9d68d7bd
@@ -0,0 +1 @@
+x¥ŽKj1D³Ö)t˜–4ê–À˜l|ï>-f ‚¯9äÞU½‚â¥Vë6¤¶ô1:³Ì¸èè‰ !ÆüÀå>Z.¼P…0“x„Îû‘ ²¡h˜ÑèhQÖ+tÎ`1NÎZe¢,ÂÏX[—×ü =ËÛÚêÑvyæI_é‹ÿ†ÿvJ­^¤2 í$?ÁˆI§ìà7oÄ4•ï©íå{Kã>VÞú!~|”U= \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/c5/bbe550b9f09444bdddd3ecf3d97c0b42aa786c b/tests/resources/merge-resolve/.gitted/objects/c5/bbe550b9f09444bdddd3ecf3d97c0b42aa786c
new file mode 100644
index 000000000..2f2ada732
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/c5/bbe550b9f09444bdddd3ecf3d97c0b42aa786c
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/c6/07fc30883e335def28cd686b51f6cfa02b06ec b/tests/resources/merge-resolve/.gitted/objects/c6/07fc30883e335def28cd686b51f6cfa02b06ec
new file mode 100644
index 000000000..475b87ef9
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/c6/07fc30883e335def28cd686b51f6cfa02b06ec
@@ -0,0 +1,2 @@
+x¥Q
+1 Dýî)r%±›n "þx/m[°[¨¯ooàßÌ{0k)¹…i×›*`â£ZavJ>,‚aò¢ìf<EZÈÍÉȳ¯µÁ5½¤%¸­µ<ê'ô“.ú¿vˆµœ,;ë]€=2¢tœwýsÆä-÷,wóÊ8@° \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/c6/92ecf62007c0ac9fb26e2aa884de2933de15ed b/tests/resources/merge-resolve/.gitted/objects/c6/92ecf62007c0ac9fb26e2aa884de2933de15ed
new file mode 100644
index 000000000..ae430bd4a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/c6/92ecf62007c0ac9fb26e2aa884de2933de15ed
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/c8/f06f2e3bb2964174677e91f0abead0e43c9e5d b/tests/resources/merge-resolve/.gitted/objects/c8/f06f2e3bb2964174677e91f0abead0e43c9e5d
new file mode 100644
index 000000000..5dae4c3ac
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/c8/f06f2e3bb2964174677e91f0abead0e43c9e5d
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/c9/174cef549ec94ecbc43ef03cdc775b4950becb b/tests/resources/merge-resolve/.gitted/objects/c9/174cef549ec94ecbc43ef03cdc775b4950becb
new file mode 100644
index 000000000..da8dba244
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/c9/174cef549ec94ecbc43ef03cdc775b4950becb
@@ -0,0 +1,2 @@
+x¥ŽQ
+Â0DýÎ)rÊf“nSñÇxífCÛHŒx}«xÿfÞÀc¤,˵Y ´kUÕbïò8‚pÔu`%—|@rä3GtBÀ™;W]›‚!‹‡½zß'Í%Q¤iÓdœ€T ?Û\ª=§×d/sYeµÝè'ô;üÚ^Êr´Î÷#l`6ºmú§Æ ßZ7U^e6oVòO´ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/c9/4b27e41064c521120627e07e2035cca1d24ffa b/tests/resources/merge-resolve/.gitted/objects/c9/4b27e41064c521120627e07e2035cca1d24ffa
new file mode 100644
index 000000000..fd1ec9fab
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/c9/4b27e41064c521120627e07e2035cca1d24ffa
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ca/b2cf23998b40f1af2d9d9a756dc9e285a8df4b b/tests/resources/merge-resolve/.gitted/objects/ca/b2cf23998b40f1af2d9d9a756dc9e285a8df4b
new file mode 100644
index 000000000..32ba2aa53
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ca/b2cf23998b40f1af2d9d9a756dc9e285a8df4b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/cb/491780d82e46dc88a065b965ab307a038f2bc2 b/tests/resources/merge-resolve/.gitted/objects/cb/491780d82e46dc88a065b965ab307a038f2bc2
new file mode 100644
index 000000000..cf9cd7d39
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/cb/491780d82e46dc88a065b965ab307a038f2bc2
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/cb/6693a788715b82440a54e0eacd19ba9f6ec559 b/tests/resources/merge-resolve/.gitted/objects/cb/6693a788715b82440a54e0eacd19ba9f6ec559
new file mode 100644
index 000000000..e11181a96
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/cb/6693a788715b82440a54e0eacd19ba9f6ec559
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/cc/3e3009134cb88014129fc8858d1101359e5e2f b/tests/resources/merge-resolve/.gitted/objects/cc/3e3009134cb88014129fc8858d1101359e5e2f
new file mode 100644
index 000000000..9a0cb7a0c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/cc/3e3009134cb88014129fc8858d1101359e5e2f
@@ -0,0 +1,2 @@
+x¥Ž]
+Â0„}Î)ö•Íß&_¼H“ -ØFbÔë[Åø6ó}0LªË2wPÆìzc†’­Ë*“sXbö‚ Rt”®#Gë$‰[l¼vH„®$ÞkÖÚf.ʧLžF+ ¥QHœD|ô©68çWl.S]îu…oô“Nü¿¶Ou9‚ÔVa0^ZÅF·³ÿœ½ÍÏ9^#Þ ØOd \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/ce/8860d49e3bea6fd745874a01b7c3e46da8cbc3 b/tests/resources/merge-resolve/.gitted/objects/ce/8860d49e3bea6fd745874a01b7c3e46da8cbc3
new file mode 100644
index 000000000..860f9952f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ce/8860d49e3bea6fd745874a01b7c3e46da8cbc3
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ce/e656c392ad0557b3aae0fb411475c206e2926f b/tests/resources/merge-resolve/.gitted/objects/ce/e656c392ad0557b3aae0fb411475c206e2926f
new file mode 100644
index 000000000..ff0624ccb
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ce/e656c392ad0557b3aae0fb411475c206e2926f
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/cf/8c5cc8a85a1ff5a4ba51e0bc7cf5665669924d b/tests/resources/merge-resolve/.gitted/objects/cf/8c5cc8a85a1ff5a4ba51e0bc7cf5665669924d
new file mode 100644
index 000000000..36b0289e6
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/cf/8c5cc8a85a1ff5a4ba51e0bc7cf5665669924d
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/d0/7ec190c306ec690bac349e87d01c4358e49bb2 b/tests/resources/merge-resolve/.gitted/objects/d0/7ec190c306ec690bac349e87d01c4358e49bb2
new file mode 100644
index 000000000..d52a56ffe
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d0/7ec190c306ec690bac349e87d01c4358e49bb2
@@ -0,0 +1,2 @@
+xÕ± €0 ©™Â3¤ažObˆ%Ç‘S°=HTŒ@u:]uYG¦´¥%šLÚE™–;¸’uÌ`_?gŒÎ~0²òßÒ”.
+׋Pëû£ÓœñxvXië Ó­f! \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/d0/d4594e16f2e19107e3fa7ea63e7aaaff305ffb b/tests/resources/merge-resolve/.gitted/objects/d0/d4594e16f2e19107e3fa7ea63e7aaaff305ffb
new file mode 100644
index 000000000..5f7e286ff
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d0/d4594e16f2e19107e3fa7ea63e7aaaff305ffb
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/d2/f8637f2eab2507a1e13cbc9df4729ec386627e b/tests/resources/merge-resolve/.gitted/objects/d2/f8637f2eab2507a1e13cbc9df4729ec386627e
new file mode 100644
index 000000000..558a8513f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d2/f8637f2eab2507a1e13cbc9df4729ec386627e
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/d3/719a5ae8e4d92276b5313ce976f6ee5af2b436 b/tests/resources/merge-resolve/.gitted/objects/d3/719a5ae8e4d92276b5313ce976f6ee5af2b436
new file mode 100644
index 000000000..930bf5a5e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d3/719a5ae8e4d92276b5313ce976f6ee5af2b436
@@ -0,0 +1,2 @@
+x=1Â0 E™}Š¿uAH…Ø¨Ä z‡¸ÔRHPÔŸ´T –¿÷ìBrh/ç]?ªaÐ ˜48¡,‘_â¡MúdköÄуTPF!«÷›TïZµQ?
+RÖ§F«Në™ÕÊÒ_J͆ÃÔ Øh’ª{Ÿßò(kÒËLKV1p–+Q‹êÑÕÝÑ® A× \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/d3/7aa3bbfe1c0c49b909781251b956dbabe85f96 b/tests/resources/merge-resolve/.gitted/objects/d3/7aa3bbfe1c0c49b909781251b956dbabe85f96
new file mode 100644
index 000000000..5902e0f32
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d3/7aa3bbfe1c0c49b909781251b956dbabe85f96
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/d3/7ad72a2052685fc6201c2af90103ad42d2079b b/tests/resources/merge-resolve/.gitted/objects/d3/7ad72a2052685fc6201c2af90103ad42d2079b
new file mode 100644
index 000000000..b2f39bff4
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d3/7ad72a2052685fc6201c2af90103ad42d2079b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/d4/207f77243500bec335ab477f9227fcdb1e271a b/tests/resources/merge-resolve/.gitted/objects/d4/207f77243500bec335ab477f9227fcdb1e271a
new file mode 100644
index 000000000..862e4e5bc
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d4/207f77243500bec335ab477f9227fcdb1e271a
@@ -0,0 +1,2 @@
+x¥ŽK
+1D]ç¹€’O'™7ÞÀ½t:ÝŒ‹™H&âõŸ«zŠ¢:Ï÷®Ä]oÌZÀBñBXlô(ƒƒ“昭+d<"«6^ºÎ%ŒÞ A( ¡ˆJÆ,Æ% %£5‚SøìSmúR^ØŠ¾Nu^뢼ÑO:óWüÚê|ÒÖ ÆèôÞDcÔF·³ÿœQEn¸¯½6õÔ#Qç \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46 b/tests/resources/merge-resolve/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46
new file mode 100644
index 000000000..0b3611ae4
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/d5/093787ef302b941b6aab081b99fb4880038bd8 b/tests/resources/merge-resolve/.gitted/objects/d5/093787ef302b941b6aab081b99fb4880038bd8
new file mode 100644
index 000000000..7d73449eb
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d5/093787ef302b941b6aab081b99fb4880038bd8
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/d5/a61b0b4992a4f0caa887fa08b52431e727bb6f b/tests/resources/merge-resolve/.gitted/objects/d5/a61b0b4992a4f0caa887fa08b52431e727bb6f
new file mode 100644
index 000000000..a7921de43
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d5/a61b0b4992a4f0caa887fa08b52431e727bb6f
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/d5/b6fc965c926a1bfc9ee456042b94088b5c5d21 b/tests/resources/merge-resolve/.gitted/objects/d5/b6fc965c926a1bfc9ee456042b94088b5c5d21
new file mode 100644
index 000000000..924bdbbb5
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d5/b6fc965c926a1bfc9ee456042b94088b5c5d21
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/d5/ec1152fe25e9fec00189eb00b3db71db24c218 b/tests/resources/merge-resolve/.gitted/objects/d5/ec1152fe25e9fec00189eb00b3db71db24c218
new file mode 100644
index 000000000..0d2534bc9
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d5/ec1152fe25e9fec00189eb00b3db71db24c218
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/d6/42b9770c66bba94a08df09b5efb095001f76d7 b/tests/resources/merge-resolve/.gitted/objects/d6/42b9770c66bba94a08df09b5efb095001f76d7
new file mode 100644
index 000000000..1671f9f2c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d6/42b9770c66bba94a08df09b5efb095001f76d7
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/d6/462fa3f5292857db599c54aea2bf91616230c5 b/tests/resources/merge-resolve/.gitted/objects/d6/462fa3f5292857db599c54aea2bf91616230c5
new file mode 100644
index 000000000..baae3f0e0
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d6/462fa3f5292857db599c54aea2bf91616230c5
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/d6/cf6c7741b3316826af1314042550c97ded1d50 b/tests/resources/merge-resolve/.gitted/objects/d6/cf6c7741b3316826af1314042550c97ded1d50
new file mode 100644
index 000000000..8f9ae1fc6
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d6/cf6c7741b3316826af1314042550c97ded1d50
@@ -0,0 +1,2 @@
+x¥Q
+1 Dýî)r%µ¦i@Äoàj›e»…ÚÅë[Åø7ó̤Zʽƒ¿éMSLB‘­Nl°ìm öÑBºÉ~×>×—üŠ-Ãu®åY8ê ŸtÖ¯øµ]ªåÖQaǶèÍ ã¼ëŸ3f]š>b×lÞ(„A] \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/d8/74671ef5b20184836cb983bb273e5280384d0b b/tests/resources/merge-resolve/.gitted/objects/d8/74671ef5b20184836cb983bb273e5280384d0b
new file mode 100644
index 000000000..1d8037895
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d8/74671ef5b20184836cb983bb273e5280384d0b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/d8/fa77b6833082c1ea36b7828a582d4c43882450 b/tests/resources/merge-resolve/.gitted/objects/d8/fa77b6833082c1ea36b7828a582d4c43882450
new file mode 100644
index 000000000..988145322
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d8/fa77b6833082c1ea36b7828a582d4c43882450
@@ -0,0 +1 @@
+xíÐ1€ DQkN1¥–&6‡•%l²BkŒ·naa1Åk¦ø¤…°íëdI¢(ãU£rö'7‰‘»LŸ’AÅ,±+Wmð9 ŒI'UŸÄ͹ÿñ£_ÛÜ°N \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/d9/63979c237d08b6ba39062ee7bf64c7d34a27f8 b/tests/resources/merge-resolve/.gitted/objects/d9/63979c237d08b6ba39062ee7bf64c7d34a27f8
new file mode 100644
index 000000000..5fa10405c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d9/63979c237d08b6ba39062ee7bf64c7d34a27f8
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/da/178208145ef585a1bd5ca5f4c9785d738df2cf b/tests/resources/merge-resolve/.gitted/objects/da/178208145ef585a1bd5ca5f4c9785d738df2cf
new file mode 100644
index 000000000..6292118e0
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/da/178208145ef585a1bd5ca5f4c9785d738df2cf
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/db/6261a7c65c7fd678520c9bb6f2c47582ab9ed5 b/tests/resources/merge-resolve/.gitted/objects/db/6261a7c65c7fd678520c9bb6f2c47582ab9ed5
new file mode 100644
index 000000000..b82e7fcaf
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/db/6261a7c65c7fd678520c9bb6f2c47582ab9ed5
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/dd/9a570c3400e6e07bc4d7651d6e20b08926b3d9 b/tests/resources/merge-resolve/.gitted/objects/dd/9a570c3400e6e07bc4d7651d6e20b08926b3d9
new file mode 100644
index 000000000..8fd60cbe8
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/dd/9a570c3400e6e07bc4d7651d6e20b08926b3d9
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/de/872ee3618b894992e9d1e18ba2ebe256a112f9 b/tests/resources/merge-resolve/.gitted/objects/de/872ee3618b894992e9d1e18ba2ebe256a112f9
new file mode 100644
index 000000000..04dda4a75
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/de/872ee3618b894992e9d1e18ba2ebe256a112f9
@@ -0,0 +1 @@
+xí± À S3Å‚…ŒlK–A†ˆõƒ²BÚ4W®¸b­ Ÿù˜—T5Á¢:§8ÔS»c€œ±Ô Ô»P`KäI¥Ë†O3Z½•”þàç‡&ØÝ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/df/e3f22baa1f6fce5447901c3086bae368de6bdd b/tests/resources/merge-resolve/.gitted/objects/df/e3f22baa1f6fce5447901c3086bae368de6bdd
new file mode 100644
index 000000000..e13569440
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/df/e3f22baa1f6fce5447901c3086bae368de6bdd
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/e0/67f9361140f19391472df8a82d6610813c73b7 b/tests/resources/merge-resolve/.gitted/objects/e0/67f9361140f19391472df8a82d6610813c73b7
new file mode 100644
index 000000000..955431dd7
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e0/67f9361140f19391472df8a82d6610813c73b7
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/e1/129b3cfb5898e0fbd606e0cb80b2755e50d161 b/tests/resources/merge-resolve/.gitted/objects/e1/129b3cfb5898e0fbd606e0cb80b2755e50d161
new file mode 100644
index 000000000..751f1dd33
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e1/129b3cfb5898e0fbd606e0cb80b2755e50d161
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/e1/7ace1492648c9dc5701bad5c47af9d1b60c4e9 b/tests/resources/merge-resolve/.gitted/objects/e1/7ace1492648c9dc5701bad5c47af9d1b60c4e9
new file mode 100644
index 000000000..4a812e5df
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e1/7ace1492648c9dc5701bad5c47af9d1b60c4e9
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/e2/c6abbd55fed5ac71a5f2751e29b4a34726a595 b/tests/resources/merge-resolve/.gitted/objects/e2/c6abbd55fed5ac71a5f2751e29b4a34726a595
new file mode 100644
index 000000000..7b84ce966
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e2/c6abbd55fed5ac71a5f2751e29b4a34726a595
@@ -0,0 +1 @@
+x+)JMU067f040031QH,-ÉÏM-JOMLÊIÕ+©(aH:,»:ÎCÉýúô:ÞË ²o>ZC'g$楧¦èfæé&%æ%g€5¬ÎqYôÂeÒZoÇÝÙkÚMíæ2­éÆÔ›X\’ZDPC~^ZNfrIf^:Xéõ›ZHÙŠž1O(_œ,'º× jvn~JfZ&Ô5ù%·˜º³\N´º¢ÔçÞö§,5[ðe“Ù¨ú¢ÔÜü2 ÇßüI{•|þ¹÷ 2m»gÜ˾‹©Éñ1ÖËåüŠ5Ó¿Ü,\“µ})TC)0DÀvú‰ùzÖ›¦9–¤×Mü°úÕ…'6ób‹” \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/e3/1e7ad3ed298f24e383c4950f4671993ec078e4 b/tests/resources/merge-resolve/.gitted/objects/e3/1e7ad3ed298f24e383c4950f4671993ec078e4
new file mode 100644
index 000000000..a28ded3fb
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e3/1e7ad3ed298f24e383c4950f4671993ec078e4
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/e3/76fbdd06ebf021c92724da9f26f44212734e3e b/tests/resources/merge-resolve/.gitted/objects/e3/76fbdd06ebf021c92724da9f26f44212734e3e
new file mode 100644
index 000000000..8da234114
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e3/76fbdd06ebf021c92724da9f26f44212734e3e
@@ -0,0 +1,3 @@
+xíAÂ@E]s
+`ö@Ì uHš)M=¾Scô®Üþü’zÂé:¢ÊŠ³(ãN+6Þ›D°¡Feð­­˜Y®g$+GˆÞä&F
+Ÿ½ì‹p‡þâG —4”mQÉ\±á85Æ#FìCð¥ï~QEóÀÄÀÚR—š½u)£;cáàâ6ü­öë'ÍjÄÇ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/e4/9f917b448d1340b31d76e54ba388268fd4c922 b/tests/resources/merge-resolve/.gitted/objects/e4/9f917b448d1340b31d76e54ba388268fd4c922
new file mode 100644
index 000000000..870c3e732
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e4/9f917b448d1340b31d76e54ba388268fd4c922
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/e4/f618a2c3ed0669308735727df5ebf2447f022f b/tests/resources/merge-resolve/.gitted/objects/e4/f618a2c3ed0669308735727df5ebf2447f022f
new file mode 100644
index 000000000..c7e1ee9d7
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e4/f618a2c3ed0669308735727df5ebf2447f022f
@@ -0,0 +1,2 @@
+x¥ŽK!D]s
+. išObŒoà i2. b¼¾h¼»ª÷’JQ«õ6äb`7:³DN.%•±œ4¹’u¦‹ÏÄIQñYÜcçmÈàÙ`Q¨ aQYa¶@>äÄÉ—Ecñ9ÖÖå%¿bÏòº¶úh›<ò¤Ÿtæ¯øµµz’J£òÁ(¹“γƒÿœFCñ_‹NŒ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/e6/5a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812 b/tests/resources/merge-resolve/.gitted/objects/e6/5a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812
new file mode 100644
index 000000000..72f1cbcd9
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e6/5a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/e8/107f24196736b870a318a0e28f048e29f6feff b/tests/resources/merge-resolve/.gitted/objects/e8/107f24196736b870a318a0e28f048e29f6feff
new file mode 100644
index 000000000..ffcf843c2
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e8/107f24196736b870a318a0e28f048e29f6feff
@@ -0,0 +1,3 @@
+x¥ŽK!D]s
+. i~=LbŒoàÞ@ÓdfãõEã ÜU½—TŠj)k—Úš]oÌÒ¯mð„èü„ž5(3ƒÓj&Ô™4Íâß»póp™iRI;‘³·¨“Â9Ò‚²Q„g_j“—ô
+-ÉëRËVïòȃ~Ò™¿â×TËI*ãÜtr g;ÿ9#R¾mkb%ÞrLNÁ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/e9/2cdb7017dc6c5aed25cb4202c5b0104b872246 b/tests/resources/merge-resolve/.gitted/objects/e9/2cdb7017dc6c5aed25cb4202c5b0104b872246
new file mode 100644
index 000000000..cb1260eb8
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e9/2cdb7017dc6c5aed25cb4202c5b0104b872246
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/e9/ad6ec3e38364a3d07feda7c4197d4d845c53b5 b/tests/resources/merge-resolve/.gitted/objects/e9/ad6ec3e38364a3d07feda7c4197d4d845c53b5
new file mode 100644
index 000000000..da4a5edd1
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e9/ad6ec3e38364a3d07feda7c4197d4d845c53b5
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/e9/f48beccc62d535739bfbdebe0a55ed716d8366 b/tests/resources/merge-resolve/.gitted/objects/e9/f48beccc62d535739bfbdebe0a55ed716d8366
new file mode 100644
index 000000000..23c59e4c9
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e9/f48beccc62d535739bfbdebe0a55ed716d8366
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/eb/c09d0137cfb0c26697aed0109fb943ad906f3f b/tests/resources/merge-resolve/.gitted/objects/eb/c09d0137cfb0c26697aed0109fb943ad906f3f
new file mode 100644
index 000000000..83b489d3a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/eb/c09d0137cfb0c26697aed0109fb943ad906f3f
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ec/67e5a86adff465359f1c8f995e12dbdfa08d8a b/tests/resources/merge-resolve/.gitted/objects/ec/67e5a86adff465359f1c8f995e12dbdfa08d8a
new file mode 100644
index 000000000..8490346e1
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ec/67e5a86adff465359f1c8f995e12dbdfa08d8a
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ed/9523e62e453e50dd9be1606af19399b96e397a b/tests/resources/merge-resolve/.gitted/objects/ed/9523e62e453e50dd9be1606af19399b96e397a
new file mode 100644
index 000000000..7853e235c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ed/9523e62e453e50dd9be1606af19399b96e397a
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ee/1d6f164893c1866a323f072eeed36b855656be b/tests/resources/merge-resolve/.gitted/objects/ee/1d6f164893c1866a323f072eeed36b855656be
new file mode 100644
index 000000000..87d808007
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ee/1d6f164893c1866a323f072eeed36b855656be
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf b/tests/resources/merge-resolve/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf
new file mode 100644
index 000000000..974b72dfd
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ee/a9286df54245fea72c5b557291470eb825f38f b/tests/resources/merge-resolve/.gitted/objects/ee/a9286df54245fea72c5b557291470eb825f38f
new file mode 100644
index 000000000..ead0b2cda
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ee/a9286df54245fea72c5b557291470eb825f38f
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ef/58fdd8086c243bdc81f99e379acacfd21d32d6 b/tests/resources/merge-resolve/.gitted/objects/ef/58fdd8086c243bdc81f99e379acacfd21d32d6
new file mode 100644
index 000000000..55f79e066
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ef/58fdd8086c243bdc81f99e379acacfd21d32d6
@@ -0,0 +1,2 @@
+x ÉÁ À0Оâï:JŠB¢ÝŸæôOV
+Þñ´yáó5éê†5jã†q!’4÷Î{¡³:ýp;¼ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/ef/c499524cf105d5264ac7fc54e07e95764e8075 b/tests/resources/merge-resolve/.gitted/objects/ef/c499524cf105d5264ac7fc54e07e95764e8075
new file mode 100644
index 000000000..bc9350bc0
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ef/c499524cf105d5264ac7fc54e07e95764e8075
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ef/c9121fdedaf08ba180b53ebfbcf71bd488ed09 b/tests/resources/merge-resolve/.gitted/objects/ef/c9121fdedaf08ba180b53ebfbcf71bd488ed09
new file mode 100644
index 000000000..5f9cd3012
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ef/c9121fdedaf08ba180b53ebfbcf71bd488ed09
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/f0/053b8060bb3f0be5cbcc3147a07ece26bf097e b/tests/resources/merge-resolve/.gitted/objects/f0/053b8060bb3f0be5cbcc3147a07ece26bf097e
new file mode 100644
index 000000000..c63d37fb0
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f0/053b8060bb3f0be5cbcc3147a07ece26bf097e
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/f0/ce2b8e4986084d9b308fb72709e414c23eb5e6 b/tests/resources/merge-resolve/.gitted/objects/f0/ce2b8e4986084d9b308fb72709e414c23eb5e6
new file mode 100644
index 000000000..e78c19f1a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f0/ce2b8e4986084d9b308fb72709e414c23eb5e6
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/f2/0c9063fa0bda9a397c96947a7b687305c49753 b/tests/resources/merge-resolve/.gitted/objects/f2/0c9063fa0bda9a397c96947a7b687305c49753
new file mode 100644
index 000000000..34d9aed20
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f2/0c9063fa0bda9a397c96947a7b687305c49753
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/f2/9e7fb590551095230c6149cbe72f2e9104a796 b/tests/resources/merge-resolve/.gitted/objects/f2/9e7fb590551095230c6149cbe72f2e9104a796
new file mode 100644
index 000000000..663f6aeb7
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f2/9e7fb590551095230c6149cbe72f2e9104a796
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/f3/293571dcd708b6a3faf03818cd2844d000e198 b/tests/resources/merge-resolve/.gitted/objects/f3/293571dcd708b6a3faf03818cd2844d000e198
new file mode 100644
index 000000000..f748743b8
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f3/293571dcd708b6a3faf03818cd2844d000e198
@@ -0,0 +1 @@
+x¥ŽKjÄ0³Ö)údµZ!›¹Á\@nIعƒ,3×Ͳ{¼¢XZÛë?F/\Eã12zcó’©"#êX1§]µÚ[Ô_êå˜GŒ&™œ˜c©+9³‡”ùXWKÁÓžiUé›t¸çgê›´Sø*“¾×OÛ¹Ë)u|²´oXp*" pÓ¤µšt†Žò…šÅ©`9êïÎäê§zÅKO\ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/f3/f1164b68b57b1995b658a828320e6df3081fae b/tests/resources/merge-resolve/.gitted/objects/f3/f1164b68b57b1995b658a828320e6df3081fae
new file mode 100644
index 000000000..5f0b4e424
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f3/f1164b68b57b1995b658a828320e6df3081fae
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/f4/15caf3fcad16304cb424b67f0ee6b12dc03aae b/tests/resources/merge-resolve/.gitted/objects/f4/15caf3fcad16304cb424b67f0ee6b12dc03aae
new file mode 100644
index 000000000..21ce1a0fc
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f4/15caf3fcad16304cb424b67f0ee6b12dc03aae
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/f4/8097eb340dc5a7cae55aabcf1faf4548aa821f b/tests/resources/merge-resolve/.gitted/objects/f4/8097eb340dc5a7cae55aabcf1faf4548aa821f
new file mode 100644
index 000000000..5a4a9a54f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f4/8097eb340dc5a7cae55aabcf1faf4548aa821f
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/f5/504f36e6f4eb797a56fc5bac6c6c7f32969bf2 b/tests/resources/merge-resolve/.gitted/objects/f5/504f36e6f4eb797a56fc5bac6c6c7f32969bf2
new file mode 100644
index 000000000..2aa0c3b9a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f5/504f36e6f4eb797a56fc5bac6c6c7f32969bf2
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/f5/b50c85a87cac64d7eb3254cdd1aec9564c0293 b/tests/resources/merge-resolve/.gitted/objects/f5/b50c85a87cac64d7eb3254cdd1aec9564c0293
new file mode 100644
index 000000000..c1885cbe7
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f5/b50c85a87cac64d7eb3254cdd1aec9564c0293
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/f5/f9dd5886a6ee20272be0aafc790cba43b31931 b/tests/resources/merge-resolve/.gitted/objects/f5/f9dd5886a6ee20272be0aafc790cba43b31931
new file mode 100644
index 000000000..17ad5063d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f5/f9dd5886a6ee20272be0aafc790cba43b31931
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/f6/be049e284c0f9dcbbc745543885be3502ea521 b/tests/resources/merge-resolve/.gitted/objects/f6/be049e284c0f9dcbbc745543885be3502ea521
new file mode 100644
index 000000000..12d3c25c2
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f6/be049e284c0f9dcbbc745543885be3502ea521
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/f7/c332bd4d4d4b777366cae4d24d1687477576bf b/tests/resources/merge-resolve/.gitted/objects/f7/c332bd4d4d4b777366cae4d24d1687477576bf
new file mode 100644
index 000000000..b36bceabf
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f7/c332bd4d4d4b777366cae4d24d1687477576bf
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/f8/958bdf4d365a84a9a178b1f5f35ff1dacbd884 b/tests/resources/merge-resolve/.gitted/objects/f8/958bdf4d365a84a9a178b1f5f35ff1dacbd884
new file mode 100644
index 000000000..5dbbef276
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f8/958bdf4d365a84a9a178b1f5f35ff1dacbd884
@@ -0,0 +1,2 @@
+x¥ŽK
+1D]ç}¥ÓI¦qã ÜK>f1’‰x}£xwU¯àQ±–²v kv½‰@`OŽ§<çHˆd}œ%kŽA›‘CÒ>²ÑÄêá›Ü;$KÈ™ybhŒóÁ2癈sLA ±öÊ?ûR\ÒË·×¥–­Þá(ƒ~ÒY¾Ã¯b-'ÐÆYÇŽp‚=NˆjÐq¶ËŸ•òm[“zí O+ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/fa/c03f2c5139618d87d53614c153823bf1f31396 b/tests/resources/merge-resolve/.gitted/objects/fa/c03f2c5139618d87d53614c153823bf1f31396
new file mode 100644
index 000000000..30e07e5b7
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/fa/c03f2c5139618d87d53614c153823bf1f31396
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/fa/da9356aa3f74622327a3038ae9c6f92e1c5c1d b/tests/resources/merge-resolve/.gitted/objects/fa/da9356aa3f74622327a3038ae9c6f92e1c5c1d
new file mode 100644
index 000000000..16ce49a1b
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/fa/da9356aa3f74622327a3038ae9c6f92e1c5c1d
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/fb/738a106cfd097a4acb96ce132ecb1ad6c46b03 b/tests/resources/merge-resolve/.gitted/objects/fb/738a106cfd097a4acb96ce132ecb1ad6c46b03
new file mode 100644
index 000000000..4f1e72688
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/fb/738a106cfd097a4acb96ce132ecb1ad6c46b03
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/fc/4c636d6515e9e261f9260dbcf3cc6eca97ea08 b/tests/resources/merge-resolve/.gitted/objects/fc/4c636d6515e9e261f9260dbcf3cc6eca97ea08
new file mode 100644
index 000000000..be8a810cd
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/fc/4c636d6515e9e261f9260dbcf3cc6eca97ea08
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/fc/7d7b805f7a9428574f4f802b2e34cd20ab9d99 b/tests/resources/merge-resolve/.gitted/objects/fc/7d7b805f7a9428574f4f802b2e34cd20ab9d99
new file mode 100644
index 000000000..20493e68c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/fc/7d7b805f7a9428574f4f802b2e34cd20ab9d99
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/fc/90237dc4891fa6c69827fc465632225e391618 b/tests/resources/merge-resolve/.gitted/objects/fc/90237dc4891fa6c69827fc465632225e391618
new file mode 100644
index 000000000..961814bae
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/fc/90237dc4891fa6c69827fc465632225e391618
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/fd/57d2d6770fad8e9959124793a17f441b571e66 b/tests/resources/merge-resolve/.gitted/objects/fd/57d2d6770fad8e9959124793a17f441b571e66
new file mode 100644
index 000000000..21e6b2c55
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/fd/57d2d6770fad8e9959124793a17f441b571e66
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/fd/89f8cffb663ac89095a0f9764902e93ceaca6a b/tests/resources/merge-resolve/.gitted/objects/fd/89f8cffb663ac89095a0f9764902e93ceaca6a
new file mode 100644
index 000000000..2f9d83b26
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/fd/89f8cffb663ac89095a0f9764902e93ceaca6a
@@ -0,0 +1,2 @@
+x¥ŽK!D]s
+. ¡{` cÜx/À§É¸`0 ÆëËoà®êURy±–òèõ|èY`ÀdPÌA!±íÜäŒå4C2d=x#ž¾ñÚe`Bgr´™uôàbLÁ‡¬Ðf“fG @þÕ—Úä-½}Kò¾Ô²ÕUžyÐ=]ù;üÚ)Ör‘0 Rˆ$Ê(%²ÿ¼Ùo=׶›ˆ‡OPw \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/fe/5407fc50a53aecb41d1a6e9ea7b612e581af87 b/tests/resources/merge-resolve/.gitted/objects/fe/5407fc50a53aecb41d1a6e9ea7b612e581af87
new file mode 100644
index 000000000..4ce7d2297
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/fe/5407fc50a53aecb41d1a6e9ea7b612e581af87
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ff/49d07869831ad761bbdaea026086f8789bcb00 b/tests/resources/merge-resolve/.gitted/objects/ff/49d07869831ad761bbdaea026086f8789bcb00
new file mode 100644
index 000000000..eada39b77
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ff/49d07869831ad761bbdaea026086f8789bcb00
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ff/b312248d607284c290023f9502eea010d34efd b/tests/resources/merge-resolve/.gitted/objects/ff/b312248d607284c290023f9502eea010d34efd
new file mode 100644
index 000000000..7e46c4fe3
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ff/b312248d607284c290023f9502eea010d34efd
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/branch b/tests/resources/merge-resolve/.gitted/refs/heads/branch
new file mode 100644
index 000000000..03f79a3dc
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/branch
@@ -0,0 +1 @@
+7cb63eed597130ba4abb87b3e544b85021905520
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/df_ancestor b/tests/resources/merge-resolve/.gitted/refs/heads/df_ancestor
new file mode 100644
index 000000000..4bc37ac60
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/df_ancestor
@@ -0,0 +1 @@
+2da538570bc1e5b2c3e855bf702f35248ad0735f
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/df_side1 b/tests/resources/merge-resolve/.gitted/refs/heads/df_side1
new file mode 100644
index 000000000..ca6dd679d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/df_side1
@@ -0,0 +1 @@
+a7dbfcbfc1a60709cb80b5ca24539008456531d0
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/df_side2 b/tests/resources/merge-resolve/.gitted/refs/heads/df_side2
new file mode 100644
index 000000000..b8160f80e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/df_side2
@@ -0,0 +1 @@
+fc90237dc4891fa6c69827fc465632225e391618
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/ff_branch b/tests/resources/merge-resolve/.gitted/refs/heads/ff_branch
new file mode 100644
index 000000000..e9e90512f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/ff_branch
@@ -0,0 +1 @@
+fd89f8cffb663ac89095a0f9764902e93ceaca6a
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/master b/tests/resources/merge-resolve/.gitted/refs/heads/master
new file mode 100644
index 000000000..8a329ae5f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/master
@@ -0,0 +1 @@
+bd593285fc7fe4ca18ccdbabf027f5d689101452
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/octo1 b/tests/resources/merge-resolve/.gitted/refs/heads/octo1
new file mode 100644
index 000000000..4d2c66902
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/octo1
@@ -0,0 +1 @@
+16f825815cfd20a07a75c71554e82d8eede0b061
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/octo2 b/tests/resources/merge-resolve/.gitted/refs/heads/octo2
new file mode 100644
index 000000000..f503977a7
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/octo2
@@ -0,0 +1 @@
+158dc7bedb202f5b26502bf3574faa7f4238d56c
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/octo3 b/tests/resources/merge-resolve/.gitted/refs/heads/octo3
new file mode 100644
index 000000000..b92994f10
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/octo3
@@ -0,0 +1 @@
+50ce7d7d01217679e26c55939eef119e0c93e272
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/octo4 b/tests/resources/merge-resolve/.gitted/refs/heads/octo4
new file mode 100644
index 000000000..f33d57cbc
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/octo4
@@ -0,0 +1 @@
+54269b3f6ec3d7d4ede24dd350dd5d605495c3ae
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/octo5 b/tests/resources/merge-resolve/.gitted/refs/heads/octo5
new file mode 100644
index 000000000..e9f943385
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/octo5
@@ -0,0 +1 @@
+e4f618a2c3ed0669308735727df5ebf2447f022f
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/octo6 b/tests/resources/merge-resolve/.gitted/refs/heads/octo6
new file mode 100644
index 000000000..4c5a98ad9
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/octo6
@@ -0,0 +1 @@
+b6f610aef53bd343e6c96227de874c66f00ee8e8
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ancestor b/tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ancestor
new file mode 100644
index 000000000..4092d428f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ancestor
@@ -0,0 +1 @@
+2392a2dacc9efb562b8635d6579fb458751c7c5b
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ours b/tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ours
new file mode 100644
index 000000000..a1c50dce8
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ours
@@ -0,0 +1 @@
+34bfafff88eaf118402b44e6f3e2dbbf1a582b05
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_theirs b/tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_theirs
new file mode 100644
index 000000000..130989399
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_theirs
@@ -0,0 +1 @@
+a802e06f1782a9645b9851bc7202cee74a8a4972
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/renames1 b/tests/resources/merge-resolve/.gitted/refs/heads/renames1
new file mode 100644
index 000000000..3d248102c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/renames1
@@ -0,0 +1 @@
+412b32fb66137366147f1801ecc962452757d48a
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/renames2 b/tests/resources/merge-resolve/.gitted/refs/heads/renames2
new file mode 100644
index 000000000..d22621561
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/renames2
@@ -0,0 +1 @@
+ab40af3cb8a3ed2e2843e96d9aa7871336b94573
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-10 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-10
new file mode 100644
index 000000000..5b378cd88
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-10
@@ -0,0 +1 @@
+0ec5f433959cd46177f745903353efb5be08d151
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-10-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-10-branch
new file mode 100644
index 000000000..b3db6c892
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-10-branch
@@ -0,0 +1 @@
+11f4f3c08b737f5fd896cbefa1425ee63b21b2fa
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-11 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-11
new file mode 100644
index 000000000..154de9a64
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-11
@@ -0,0 +1 @@
+3168dca1a561889b045a6441909f4c56145e666d
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-11-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-11-branch
new file mode 100644
index 000000000..2e4118029
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-11-branch
@@ -0,0 +1 @@
+6718a45909532d1fcf5600d0877f7fe7e78f0b86
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-13 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-13
new file mode 100644
index 000000000..297573a57
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-13
@@ -0,0 +1 @@
+a3fabece9eb8748da810e1e08266fef9b7136ad4
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-13-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-13-branch
new file mode 100644
index 000000000..22e429a61
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-13-branch
@@ -0,0 +1 @@
+05f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-14 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-14
new file mode 100644
index 000000000..89051853a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-14
@@ -0,0 +1 @@
+7e2d058d5fedf8329db44db4fac610d6b1a89159
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-14-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-14-branch
new file mode 100644
index 000000000..0158f950c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-14-branch
@@ -0,0 +1 @@
+8187117062b750eed4f93fd7e899f17b52ce554d
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-2alt b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-2alt
new file mode 100644
index 000000000..474074120
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-2alt
@@ -0,0 +1 @@
+566ab53c220a2eafc1212af1a024513230280ab9
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-2alt-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-2alt-branch
new file mode 100644
index 000000000..2f5f1a4af
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-2alt-branch
@@ -0,0 +1 @@
+c9174cef549ec94ecbc43ef03cdc775b4950becb
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-3alt b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-3alt
new file mode 100644
index 000000000..18e50ae12
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-3alt
@@ -0,0 +1 @@
+4c9fac0707f8d4195037ae5a681aa48626491541
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-3alt-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-3alt-branch
new file mode 100644
index 000000000..7bc1a8d15
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-3alt-branch
@@ -0,0 +1 @@
+c607fc30883e335def28cd686b51f6cfa02b06ec
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-4 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-4
new file mode 100644
index 000000000..f49bbf956
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-4
@@ -0,0 +1 @@
+cc3e3009134cb88014129fc8858d1101359e5e2f
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-4-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-4-branch
new file mode 100644
index 000000000..bff519ef1
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-4-branch
@@ -0,0 +1 @@
+183310e30fb1499af8c619108ffea4d300b5e778
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1
new file mode 100644
index 000000000..963a7b336
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1
@@ -0,0 +1 @@
+4fe93c0ec83eb6305cbace3dace88ecee1b63cb6
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1-branch
new file mode 100644
index 000000000..4a22138e7
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1-branch
@@ -0,0 +1 @@
+478172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2
new file mode 100644
index 000000000..aa4ada17e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2
@@ -0,0 +1 @@
+3b47b031b3e55ae11e14a05260b1c3ffd6838d55
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2-branch
new file mode 100644
index 000000000..5553cdba1
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2-branch
@@ -0,0 +1 @@
+f48097eb340dc5a7cae55aabcf1faf4548aa821f
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-6 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-6
new file mode 100644
index 000000000..fb685bb63
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-6
@@ -0,0 +1 @@
+99b4f7e4f24470fa06b980bc21f1095c2a9425c0
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-6-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-6-branch
new file mode 100644
index 000000000..efc4c55ac
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-6-branch
@@ -0,0 +1 @@
+a43150a738849c59376cf30bb2a68348a83c8f48
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-7 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-7
new file mode 100644
index 000000000..9c9424346
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-7
@@ -0,0 +1 @@
+d874671ef5b20184836cb983bb273e5280384d0b
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-7-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-7-branch
new file mode 100644
index 000000000..1762bb5db
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-7-branch
@@ -0,0 +1 @@
+5195a1b480f66691b667f10a9e41e70115a78351
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-8 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-8
new file mode 100644
index 000000000..837c4915a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-8
@@ -0,0 +1 @@
+3575826c96a975031d2c14368529cc5c4353a8fd
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-8-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-8-branch
new file mode 100644
index 000000000..874230eff
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-8-branch
@@ -0,0 +1 @@
+52d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-9 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-9
new file mode 100644
index 000000000..b968a3efb
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-9
@@ -0,0 +1 @@
+c35dee9bcc0e989f3b0c40f68372a9a51b6c4e6a
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-9-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-9-branch
new file mode 100644
index 000000000..7f3097b69
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-9-branch
@@ -0,0 +1 @@
+13d1be4ea52a6ced1d7a1d832f0ee3c399348e5e
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/unrelated b/tests/resources/merge-resolve/.gitted/refs/heads/unrelated
new file mode 100644
index 000000000..bb877be2e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/unrelated
@@ -0,0 +1 @@
+55b4e4687e7a0d9ca367016ed930f385d4022e6f
diff --git a/tests/resources/merge-resolve/added-in-master.txt b/tests/resources/merge-resolve/added-in-master.txt
new file mode 100644
index 000000000..233c0919c
--- /dev/null
+++ b/tests/resources/merge-resolve/added-in-master.txt
@@ -0,0 +1 @@
+this file is added in master
diff --git a/tests/resources/merge-resolve/automergeable.txt b/tests/resources/merge-resolve/automergeable.txt
new file mode 100644
index 000000000..ee3fa1b8c
--- /dev/null
+++ b/tests/resources/merge-resolve/automergeable.txt
@@ -0,0 +1,9 @@
+this file is changed in master
+this file is automergeable
+this file is automergeable
+this file is automergeable
+this file is automergeable
+this file is automergeable
+this file is automergeable
+this file is automergeable
+this file is automergeable
diff --git a/tests/resources/merge-resolve/changed-in-branch.txt b/tests/resources/merge-resolve/changed-in-branch.txt
new file mode 100644
index 000000000..ab6c44a2e
--- /dev/null
+++ b/tests/resources/merge-resolve/changed-in-branch.txt
@@ -0,0 +1 @@
+initial revision
diff --git a/tests/resources/merge-resolve/changed-in-master.txt b/tests/resources/merge-resolve/changed-in-master.txt
new file mode 100644
index 000000000..11deab00b
--- /dev/null
+++ b/tests/resources/merge-resolve/changed-in-master.txt
@@ -0,0 +1 @@
+changed in master
diff --git a/tests/resources/merge-resolve/conflicting.txt b/tests/resources/merge-resolve/conflicting.txt
new file mode 100644
index 000000000..4e886e602
--- /dev/null
+++ b/tests/resources/merge-resolve/conflicting.txt
@@ -0,0 +1 @@
+this file is changed in master and branch
diff --git a/tests/resources/merge-resolve/removed-in-branch.txt b/tests/resources/merge-resolve/removed-in-branch.txt
new file mode 100644
index 000000000..dfe3f22ba
--- /dev/null
+++ b/tests/resources/merge-resolve/removed-in-branch.txt
@@ -0,0 +1 @@
+this is removed in branch
diff --git a/tests/resources/merge-resolve/unchanged.txt b/tests/resources/merge-resolve/unchanged.txt
new file mode 100644
index 000000000..c8f06f2e3
--- /dev/null
+++ b/tests/resources/merge-resolve/unchanged.txt
@@ -0,0 +1 @@
+this file is unchanged in both
diff --git a/tests/resources/mergedrepo/.gitted/COMMIT_EDITMSG b/tests/resources/mergedrepo/.gitted/COMMIT_EDITMSG
new file mode 100644
index 000000000..1f7391f92
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/COMMIT_EDITMSG
@@ -0,0 +1 @@
+master
diff --git a/tests/resources/mergedrepo/.gitted/HEAD b/tests/resources/mergedrepo/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/mergedrepo/.gitted/MERGE_HEAD b/tests/resources/mergedrepo/.gitted/MERGE_HEAD
new file mode 100644
index 000000000..a5bdf6e40
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/MERGE_HEAD
@@ -0,0 +1 @@
+e2809157a7766f272e4cfe26e61ef2678a5357ff
diff --git a/tests/resources/mergedrepo/.gitted/MERGE_MODE b/tests/resources/mergedrepo/.gitted/MERGE_MODE
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/MERGE_MODE
diff --git a/tests/resources/mergedrepo/.gitted/MERGE_MSG b/tests/resources/mergedrepo/.gitted/MERGE_MSG
new file mode 100644
index 000000000..7c4d1f5a9
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/MERGE_MSG
@@ -0,0 +1,5 @@
+Merge branch 'branch'
+
+Conflicts:
+ conflicts-one.txt
+ conflicts-two.txt
diff --git a/tests/resources/mergedrepo/.gitted/ORIG_HEAD b/tests/resources/mergedrepo/.gitted/ORIG_HEAD
new file mode 100644
index 000000000..13d4d6721
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/ORIG_HEAD
@@ -0,0 +1 @@
+3a34580a35add43a4cf361e8e9a30060a905c876
diff --git a/tests/resources/mergedrepo/.gitted/config b/tests/resources/mergedrepo/.gitted/config
new file mode 100644
index 000000000..af107929f
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests/resources/mergedrepo/.gitted/description b/tests/resources/mergedrepo/.gitted/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/mergedrepo/.gitted/index b/tests/resources/mergedrepo/.gitted/index
new file mode 100644
index 000000000..3d29f78e7
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/index
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/info/exclude b/tests/resources/mergedrepo/.gitted/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/mergedrepo/.gitted/logs/HEAD b/tests/resources/mergedrepo/.gitted/logs/HEAD
new file mode 100644
index 000000000..a385da67b
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/logs/HEAD
@@ -0,0 +1,5 @@
+0000000000000000000000000000000000000000 9a05ccb4e0f948de03128e095f39dae6976751c5 Edward Thomson <ethomson@edwardthomson.com> 1351371828 -0500 commit (initial): initial
+9a05ccb4e0f948de03128e095f39dae6976751c5 9a05ccb4e0f948de03128e095f39dae6976751c5 Edward Thomson <ethomson@edwardthomson.com> 1351371835 -0500 checkout: moving from master to branch
+9a05ccb4e0f948de03128e095f39dae6976751c5 e2809157a7766f272e4cfe26e61ef2678a5357ff Edward Thomson <ethomson@edwardthomson.com> 1351371872 -0500 commit: branch
+e2809157a7766f272e4cfe26e61ef2678a5357ff 9a05ccb4e0f948de03128e095f39dae6976751c5 Edward Thomson <ethomson@edwardthomson.com> 1351371873 -0500 checkout: moving from branch to master
+9a05ccb4e0f948de03128e095f39dae6976751c5 3a34580a35add43a4cf361e8e9a30060a905c876 Edward Thomson <ethomson@edwardthomson.com> 1351372106 -0500 commit: master
diff --git a/tests/resources/mergedrepo/.gitted/logs/refs/heads/branch b/tests/resources/mergedrepo/.gitted/logs/refs/heads/branch
new file mode 100644
index 000000000..26a5e8dc5
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/logs/refs/heads/branch
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 9a05ccb4e0f948de03128e095f39dae6976751c5 Edward Thomson <ethomson@edwardthomson.com> 1351371835 -0500 branch: Created from HEAD
+9a05ccb4e0f948de03128e095f39dae6976751c5 e2809157a7766f272e4cfe26e61ef2678a5357ff Edward Thomson <ethomson@edwardthomson.com> 1351371872 -0500 commit: branch
diff --git a/tests/resources/mergedrepo/.gitted/logs/refs/heads/master b/tests/resources/mergedrepo/.gitted/logs/refs/heads/master
new file mode 100644
index 000000000..425f7bd89
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/logs/refs/heads/master
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 9a05ccb4e0f948de03128e095f39dae6976751c5 Edward Thomson <ethomson@edwardthomson.com> 1351371828 -0500 commit (initial): initial
+9a05ccb4e0f948de03128e095f39dae6976751c5 3a34580a35add43a4cf361e8e9a30060a905c876 Edward Thomson <ethomson@edwardthomson.com> 1351372106 -0500 commit: master
diff --git a/tests/resources/mergedrepo/.gitted/objects/03/db1d37504ca0c4f7c26d7776b0e28bdea08712 b/tests/resources/mergedrepo/.gitted/objects/03/db1d37504ca0c4f7c26d7776b0e28bdea08712
new file mode 100644
index 000000000..9232f79d9
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/03/db1d37504ca0c4f7c26d7776b0e28bdea08712
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/17/0efc1023e0ed2390150bb4469c8456b63e8f91 b/tests/resources/mergedrepo/.gitted/objects/17/0efc1023e0ed2390150bb4469c8456b63e8f91
new file mode 100644
index 000000000..3e124d9a4
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/17/0efc1023e0ed2390150bb4469c8456b63e8f91
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/1f/85ca51b8e0aac893a621b61a9c2661d6aa6d81 b/tests/resources/mergedrepo/.gitted/objects/1f/85ca51b8e0aac893a621b61a9c2661d6aa6d81
new file mode 100644
index 000000000..7bb19c873
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/1f/85ca51b8e0aac893a621b61a9c2661d6aa6d81
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/22/0bd62631c8cf7a83ef39c6b94595f00517211e b/tests/resources/mergedrepo/.gitted/objects/22/0bd62631c8cf7a83ef39c6b94595f00517211e
new file mode 100644
index 000000000..487bcffb1
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/22/0bd62631c8cf7a83ef39c6b94595f00517211e
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/32/d55d59265db86dd690f0a7fc563db43e2bc6a6 b/tests/resources/mergedrepo/.gitted/objects/32/d55d59265db86dd690f0a7fc563db43e2bc6a6
new file mode 100644
index 000000000..2eb3954fc
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/32/d55d59265db86dd690f0a7fc563db43e2bc6a6
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/38/e2d82b9065a237904af4b780b4d68da6950534 b/tests/resources/mergedrepo/.gitted/objects/38/e2d82b9065a237904af4b780b4d68da6950534
new file mode 100644
index 000000000..ebe83ccb2
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/38/e2d82b9065a237904af4b780b4d68da6950534
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/3a/34580a35add43a4cf361e8e9a30060a905c876 b/tests/resources/mergedrepo/.gitted/objects/3a/34580a35add43a4cf361e8e9a30060a905c876
new file mode 100644
index 000000000..0d4095ffc
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/3a/34580a35add43a4cf361e8e9a30060a905c876
@@ -0,0 +1,2 @@
+x¥ŽK
+1D]ç}¥óíDÜx/Oã"F2¯ooà®ê<*·Zo”‘»Ñ™uI²h²hrÄlÊÊ"r YùT8¢'©Ä#v¾mÎÉ0.Áø¨¥òŒÁ.:”È.#+³ñ9ÖÖáR^±¸®­níGžô“Îü~í[=ÔVjRìÑ"ŠIçÙÁjDÛ”ˆ7|N` \ No newline at end of file
diff --git a/tests/resources/mergedrepo/.gitted/objects/44/58b8bc9e72b6c8755ae456f60e9844d0538d8c b/tests/resources/mergedrepo/.gitted/objects/44/58b8bc9e72b6c8755ae456f60e9844d0538d8c
new file mode 100644
index 000000000..33389c302
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/44/58b8bc9e72b6c8755ae456f60e9844d0538d8c
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/47/8871385b9cd03908c5383acfd568bef023c6b3 b/tests/resources/mergedrepo/.gitted/objects/47/8871385b9cd03908c5383acfd568bef023c6b3
new file mode 100644
index 000000000..5361ea685
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/47/8871385b9cd03908c5383acfd568bef023c6b3
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/51/6bd85f78061e09ccc714561d7b504672cb52da b/tests/resources/mergedrepo/.gitted/objects/51/6bd85f78061e09ccc714561d7b504672cb52da
new file mode 100644
index 000000000..a60da877c
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/51/6bd85f78061e09ccc714561d7b504672cb52da
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/53/c1d95a01f4514b162066fc98564500c96c46ad b/tests/resources/mergedrepo/.gitted/objects/53/c1d95a01f4514b162066fc98564500c96c46ad
new file mode 100644
index 000000000..85e84d71e
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/53/c1d95a01f4514b162066fc98564500c96c46ad
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/6a/ea5f295304c36144ad6e9247a291b7f8112399 b/tests/resources/mergedrepo/.gitted/objects/6a/ea5f295304c36144ad6e9247a291b7f8112399
new file mode 100644
index 000000000..b16b521e6
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/6a/ea5f295304c36144ad6e9247a291b7f8112399
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/70/68e30a7f0090ae32db35dfa1e4189d8780fcb8 b/tests/resources/mergedrepo/.gitted/objects/70/68e30a7f0090ae32db35dfa1e4189d8780fcb8
new file mode 100644
index 000000000..7c4e85ffb
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/70/68e30a7f0090ae32db35dfa1e4189d8780fcb8
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/75/938de1e367098b3e9a7b1ec3c4ac4548afffe4 b/tests/resources/mergedrepo/.gitted/objects/75/938de1e367098b3e9a7b1ec3c4ac4548afffe4
new file mode 100644
index 000000000..65173fc4d
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/75/938de1e367098b3e9a7b1ec3c4ac4548afffe4
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/7b/26923aaf452b1977eb08617c59475fb3f74b71 b/tests/resources/mergedrepo/.gitted/objects/7b/26923aaf452b1977eb08617c59475fb3f74b71
new file mode 100644
index 000000000..162fa4455
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/7b/26923aaf452b1977eb08617c59475fb3f74b71
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/84/af62840be1b1c47b778a8a249f3ff45155038c b/tests/resources/mergedrepo/.gitted/objects/84/af62840be1b1c47b778a8a249f3ff45155038c
new file mode 100644
index 000000000..77a519f55
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/84/af62840be1b1c47b778a8a249f3ff45155038c
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/88/71f7a2ee3addfc4ba39fbd0783c8e738d04cda b/tests/resources/mergedrepo/.gitted/objects/88/71f7a2ee3addfc4ba39fbd0783c8e738d04cda
new file mode 100644
index 000000000..f624cd4f1
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/88/71f7a2ee3addfc4ba39fbd0783c8e738d04cda
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/88/7b153b165d32409c70163e0f734c090f12f673 b/tests/resources/mergedrepo/.gitted/objects/88/7b153b165d32409c70163e0f734c090f12f673
new file mode 100644
index 000000000..096474c03
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/88/7b153b165d32409c70163e0f734c090f12f673
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/8a/ad34cc83733590e74b93d0f7cf00375e2a735a b/tests/resources/mergedrepo/.gitted/objects/8a/ad34cc83733590e74b93d0f7cf00375e2a735a
new file mode 100644
index 000000000..a413bc6b0
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/8a/ad34cc83733590e74b93d0f7cf00375e2a735a
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/8b/3f43d2402825c200f835ca1762413e386fd0b2 b/tests/resources/mergedrepo/.gitted/objects/8b/3f43d2402825c200f835ca1762413e386fd0b2
new file mode 100644
index 000000000..3ac8f6018
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/8b/3f43d2402825c200f835ca1762413e386fd0b2
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/8b/72416545c7e761b64cecad4f1686eae4078aa8 b/tests/resources/mergedrepo/.gitted/objects/8b/72416545c7e761b64cecad4f1686eae4078aa8
new file mode 100644
index 000000000..589a5ae9b
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/8b/72416545c7e761b64cecad4f1686eae4078aa8
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/8f/3c06cff9a83757cec40c80bc9bf31a2582bde9 b/tests/resources/mergedrepo/.gitted/objects/8f/3c06cff9a83757cec40c80bc9bf31a2582bde9
new file mode 100644
index 000000000..6503985e3
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/8f/3c06cff9a83757cec40c80bc9bf31a2582bde9
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/8f/fcc405925511824a2240a6d3686aa7f8c7ac50 b/tests/resources/mergedrepo/.gitted/objects/8f/fcc405925511824a2240a6d3686aa7f8c7ac50
new file mode 100644
index 000000000..2eaa80838
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/8f/fcc405925511824a2240a6d3686aa7f8c7ac50
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/9a/05ccb4e0f948de03128e095f39dae6976751c5 b/tests/resources/mergedrepo/.gitted/objects/9a/05ccb4e0f948de03128e095f39dae6976751c5
new file mode 100644
index 000000000..7373a80d8
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/9a/05ccb4e0f948de03128e095f39dae6976751c5
@@ -0,0 +1 @@
+x¥Ñ !Dý¦Šm@³Ë ÉÅøc6Àq#‘#AŒí‹Æü›y/™ µ”Üœ:ô#$–l•tH:é—„*D³XÖh¬VœáŸ}« ®ëË·n[-ºÃý¤KüŠ_;…ZÎ@“¦‰ÉJ GÔˆbÐqÞãŸ3"ï¹goŒ«@I \ No newline at end of file
diff --git a/tests/resources/mergedrepo/.gitted/objects/9d/81f82fccc7dcd7de7a1ffead1815294c2e092c b/tests/resources/mergedrepo/.gitted/objects/9d/81f82fccc7dcd7de7a1ffead1815294c2e092c
new file mode 100644
index 000000000..c5a651f97
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/9d/81f82fccc7dcd7de7a1ffead1815294c2e092c
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/b7/cedb8ad4cbb22b6363f9578cbd749797f7ef0d b/tests/resources/mergedrepo/.gitted/objects/b7/cedb8ad4cbb22b6363f9578cbd749797f7ef0d
new file mode 100644
index 000000000..3e14b5dc8
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/b7/cedb8ad4cbb22b6363f9578cbd749797f7ef0d
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/d0/1885ea594926eae9ba5b54ad76692af5969f51 b/tests/resources/mergedrepo/.gitted/objects/d0/1885ea594926eae9ba5b54ad76692af5969f51
new file mode 100644
index 000000000..a641adc2e
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/d0/1885ea594926eae9ba5b54ad76692af5969f51
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/e2/809157a7766f272e4cfe26e61ef2678a5357ff b/tests/resources/mergedrepo/.gitted/objects/e2/809157a7766f272e4cfe26e61ef2678a5357ff
new file mode 100644
index 000000000..fa86662e0
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/e2/809157a7766f272e4cfe26e61ef2678a5357ff
@@ -0,0 +1,3 @@
+x¥ŽK
+1D]ç¹€Òùt> âÆxžN‡q1‰¯ï(ÞÀ]Õ{P·e¹ m½Ù.¢S­Ì0[Dc’õd­
+Å…ˆbM‰Ôº¬Cgdž¼@Í>glÈX].$!ÇÑ0*zŽ¹u})/êE_ç¶<Úª²ÑO:ËWüÚÛrÒÆ¡qѤhõ@mt;;äÏ5uZyVoÓ\Mÿ \ No newline at end of file
diff --git a/tests/resources/mergedrepo/.gitted/objects/e6/2cac5c88b9928f2695b934c70efa4285324478 b/tests/resources/mergedrepo/.gitted/objects/e6/2cac5c88b9928f2695b934c70efa4285324478
new file mode 100644
index 000000000..c9841c698
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/e6/2cac5c88b9928f2695b934c70efa4285324478
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/f7/2784290c151092abf04ce6b875068547f70406 b/tests/resources/mergedrepo/.gitted/objects/f7/2784290c151092abf04ce6b875068547f70406
new file mode 100644
index 000000000..cd587dbec
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/f7/2784290c151092abf04ce6b875068547f70406
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/refs/heads/branch b/tests/resources/mergedrepo/.gitted/refs/heads/branch
new file mode 100644
index 000000000..a5bdf6e40
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/refs/heads/branch
@@ -0,0 +1 @@
+e2809157a7766f272e4cfe26e61ef2678a5357ff
diff --git a/tests/resources/mergedrepo/.gitted/refs/heads/master b/tests/resources/mergedrepo/.gitted/refs/heads/master
new file mode 100644
index 000000000..13d4d6721
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/refs/heads/master
@@ -0,0 +1 @@
+3a34580a35add43a4cf361e8e9a30060a905c876
diff --git a/tests/resources/mergedrepo/conflicts-one.txt b/tests/resources/mergedrepo/conflicts-one.txt
new file mode 100644
index 000000000..8aad34cc8
--- /dev/null
+++ b/tests/resources/mergedrepo/conflicts-one.txt
@@ -0,0 +1,5 @@
+<<<<<<< HEAD
+This is most certainly a conflict!
+=======
+This is a conflict!!!
+>>>>>>> branch
diff --git a/tests/resources/mergedrepo/conflicts-two.txt b/tests/resources/mergedrepo/conflicts-two.txt
new file mode 100644
index 000000000..e62cac5c8
--- /dev/null
+++ b/tests/resources/mergedrepo/conflicts-two.txt
@@ -0,0 +1,5 @@
+<<<<<<< HEAD
+This is without question another conflict!
+=======
+This is another conflict!!!
+>>>>>>> branch
diff --git a/tests/resources/mergedrepo/one.txt b/tests/resources/mergedrepo/one.txt
new file mode 100644
index 000000000..75938de1e
--- /dev/null
+++ b/tests/resources/mergedrepo/one.txt
@@ -0,0 +1,10 @@
+This is file one!
+This is file one.
+This is file one.
+This is file one.
+This is file one.
+This is file one.
+This is file one.
+This is file one.
+This is file one.
+This is file one!
diff --git a/tests/resources/mergedrepo/two.txt b/tests/resources/mergedrepo/two.txt
new file mode 100644
index 000000000..7b26923aa
--- /dev/null
+++ b/tests/resources/mergedrepo/two.txt
@@ -0,0 +1,12 @@
+This is file two!
+This is file two.
+This is file two.
+This is file two.
+This is file two.
+This is file two.
+This is file two.
+This is file two.
+This is file two.
+This is file two.
+This is file two.
+This is file two!
diff --git a/tests/resources/partial-testrepo/.gitted/HEAD b/tests/resources/partial-testrepo/.gitted/HEAD
new file mode 100644
index 000000000..4bfb9c93f
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/dir
diff --git a/tests/resources/partial-testrepo/.gitted/config b/tests/resources/partial-testrepo/.gitted/config
new file mode 100644
index 000000000..99abaab97
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/config
@@ -0,0 +1,7 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
+[branch "dir"]
diff --git a/tests/resources/partial-testrepo/.gitted/index b/tests/resources/partial-testrepo/.gitted/index
new file mode 100644
index 000000000..4f241f914
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/index
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/tests/resources/partial-testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
new file mode 100644
index 000000000..cedb2a22e
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e b/tests/resources/partial-testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e
new file mode 100644
index 000000000..b7d944fa1
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925 b/tests/resources/partial-testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925
new file mode 100644
index 000000000..d37b93e4f
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/tests/resources/partial-testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
new file mode 100644
index 000000000..93a16f146
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd b/tests/resources/partial-testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
new file mode 100644
index 000000000..ba0bfb30c
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/partial-testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
new file mode 100644
index 000000000..7ca4ceed5
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/tests/resources/partial-testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
new file mode 100644
index 000000000..8953b6cef
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
@@ -0,0 +1,2 @@
+xŽQ
+Â0DýÎ)öÊ6›Í¦ "xO°‰‰-ØFb¼¿EoàÏ0 ¼Ç¤º,ske×[ÎPn8R,EpD?±gŸ}Ê^3² âÙ<µåµGŽhYKÄèÒ8ЖDAÉ)¿ÉÈ;gôݧÚàšjïp™4ÕŽ¯ô-çû¢óãêr‚ÁŠ;°s°GA4Ûº=ìùÖ(ôin7øIÌKÍFE \ No newline at end of file
diff --git a/tests/resources/partial-testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63 b/tests/resources/partial-testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63
new file mode 100644
index 000000000..e9150214b
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/tests/resources/partial-testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
new file mode 100644
index 000000000..c1f22c54f
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
@@ -0,0 +1,2 @@
+xŽÛ 1EýNi@™Ék2 "X‚$ÙYW0YcÿíÀ¿Ã…s¸¥ÕzïÚÚõMDÏ€0æœ8!¶†ÉÌÞs‰ XŠªgÚdí::@X0»P¢wÙ"F/‰‰œÍRàˆUz÷¥múZZïú²¤ÒV}|•/œo5݇ÒêI£!¬1z Æ:vùÇUim}ê/¢>
+öF- \ No newline at end of file
diff --git a/tests/resources/partial-testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc b/tests/resources/partial-testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc
new file mode 100644
index 000000000..b669961d8
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735 b/tests/resources/partial-testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735
new file mode 100644
index 000000000..9ff5eb2b5
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/tests/resources/partial-testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
new file mode 100644
index 000000000..2ef4faa0f
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/tests/resources/partial-testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
new file mode 100644
index 000000000..2f9b6b6e3
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/tests/resources/partial-testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
new file mode 100644
index 000000000..5df58dda5
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/tests/resources/partial-testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
new file mode 100644
index 000000000..a79612435
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
@@ -0,0 +1,3 @@
+xŽ[
+Â0EýÎ*fÊäÕ¤ "¸W0“‡-ØFâtÿÝ—çpS[–YÀ˜x^
+Díb CLhutɉ}¥8X*4Zí¬sY½¨—UÀ‘AÃÖ ÌX3‡R«Mµ¶) s6è¼¢M¦ÖážšÜ&Jm…ó;}Çõ±Ðü<¥¶\@›à‚ÑÞpÄ€¨vº?”ò«jÛºLð«¨Ø?Hå \ No newline at end of file
diff --git a/tests/resources/partial-testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/tests/resources/partial-testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
new file mode 100644
index 000000000..f8588696b
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
@@ -0,0 +1,2 @@
+x;j1DëmdÓú·À˜ÇŽ|M«µ3`ŒV{ >€³âQ¯ ¸·vL0I?Í!š4–Z=Ê! ×¦8²F¢Ã’!rÖsQßyÈ9]$DŽ&„l6AÇ>jFWüÒµ IKNiûë§Z¢%¡SˆŒ‘
+‹Ò ­ÅʉøU~̽øä>'¼ï™û ¯wþ ×[ËÇ× ÷öÚDGÚ¡±ðŒQ-ºMù«>dܶ‘OÞáÒò}í\à8g_ШÂoYr \ No newline at end of file
diff --git a/tests/resources/partial-testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/tests/resources/partial-testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
new file mode 100644
index 000000000..d0d7e736e
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/tests/resources/partial-testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
new file mode 100644
index 000000000..18a7f61c2
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd b/tests/resources/partial-testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
new file mode 100644
index 000000000..75f541f10
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
@@ -0,0 +1,3 @@
+xŽQ
+Â0DýÎ)öʦ»I<‚'ØlR+˜Fj¼¿EoàÏ0<xÃh«õÞa Üõµ]È™­åXUlÞPF)Åz‘4yó”µ,\r 'SÂÄ-mI4
+‘Xhô”&òÌFÞ}n+\µõ—Y´-p|é·œoUz;-‘aÑlt{ØË?®I«,:ÃoÚR̳cHK \ No newline at end of file
diff --git a/tests/resources/partial-testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9 b/tests/resources/partial-testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9
new file mode 100644
index 000000000..7620c514f
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83 b/tests/resources/partial-testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83
new file mode 100644
index 000000000..00940f0f2
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 b/tests/resources/partial-testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
new file mode 100644
index 000000000..03770969a
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/tests/resources/partial-testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
new file mode 100644
index 000000000..112998d42
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765 b/tests/resources/partial-testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
new file mode 100644
index 000000000..12bf5f3e3
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/pack/.gitkeep b/tests/resources/partial-testrepo/.gitted/objects/pack/.gitkeep
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/pack/.gitkeep
diff --git a/tests/resources/partial-testrepo/.gitted/refs/heads/dir b/tests/resources/partial-testrepo/.gitted/refs/heads/dir
new file mode 100644
index 000000000..4567d37fa
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/refs/heads/dir
@@ -0,0 +1 @@
+144344043ba4d4a405da03de3844aa829ae8be0e
diff --git a/tests/resources/peeled.git/HEAD b/tests/resources/peeled.git/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/peeled.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/peeled.git/config b/tests/resources/peeled.git/config
new file mode 100644
index 000000000..88300524a
--- /dev/null
+++ b/tests/resources/peeled.git/config
@@ -0,0 +1,8 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = true
+[remote "origin"]
+ url = /home/peff/compile/libgit2/tests-clar/resources/peeled
+ fetch = +refs/*:refs/*
+ mirror = true
diff --git a/tests/resources/peeled.git/objects/info/packs b/tests/resources/peeled.git/objects/info/packs
new file mode 100644
index 000000000..0d88b32e5
--- /dev/null
+++ b/tests/resources/peeled.git/objects/info/packs
@@ -0,0 +1,2 @@
+P pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.pack
+
diff --git a/tests/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.idx b/tests/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.idx
new file mode 100644
index 000000000..9b79e9b85
--- /dev/null
+++ b/tests/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.idx
Binary files differ
diff --git a/tests/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.pack b/tests/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.pack
new file mode 100644
index 000000000..2459ca287
--- /dev/null
+++ b/tests/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.pack
Binary files differ
diff --git a/tests/resources/peeled.git/packed-refs b/tests/resources/peeled.git/packed-refs
new file mode 100644
index 000000000..ad053d550
--- /dev/null
+++ b/tests/resources/peeled.git/packed-refs
@@ -0,0 +1,6 @@
+# pack-refs with: peeled fully-peeled
+c2596aa0151888587ec5c0187f261e63412d9e11 refs/foo/tag-outside-tags
+^0df1a5865c8abfc09f1f2182e6a31be550e99f07
+0df1a5865c8abfc09f1f2182e6a31be550e99f07 refs/heads/master
+c2596aa0151888587ec5c0187f261e63412d9e11 refs/tags/tag-inside-tags
+^0df1a5865c8abfc09f1f2182e6a31be550e99f07
diff --git a/tests/resources/peeled.git/refs/heads/master b/tests/resources/peeled.git/refs/heads/master
new file mode 100644
index 000000000..76c15e203
--- /dev/null
+++ b/tests/resources/peeled.git/refs/heads/master
@@ -0,0 +1 @@
+0df1a5865c8abfc09f1f2182e6a31be550e99f07
diff --git a/tests/resources/push.sh b/tests/resources/push.sh
new file mode 100644
index 000000000..607117675
--- /dev/null
+++ b/tests/resources/push.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+#creates push_src repo for libgit2 push tests.
+set -eu
+
+#Create src repo for push
+mkdir push_src
+pushd push_src
+ git init
+
+ echo a > a.txt
+ git add .
+ git commit -m 'added a.txt'
+
+ mkdir fold
+ echo b > fold/b.txt
+ git add .
+ git commit -m 'added fold and fold/b.txt'
+
+ git branch b1 #b1 and b2 are the same
+ git branch b2
+
+ git checkout -b b3
+ echo edit >> a.txt
+ git add .
+ git commit -m 'edited a.txt'
+
+ git checkout -b b4 master
+ echo edit >> fold\b.txt
+ git add .
+ git commit -m 'edited fold\b.txt'
+
+ git checkout -b b5 master
+ git submodule add ../testrepo.git submodule
+ git commit -m "added submodule named 'submodule' pointing to '../testrepo.git'"
+
+ git checkout master
+ git merge -m "merge b3, b4, and b5 to master" b3 b4 b5
+
+ #Log commits to include in testcase
+ git log --format=oneline --decorate --graph
+ #*-. 951bbbb90e2259a4c8950db78946784fb53fcbce (HEAD, master) merge b3, b4, and b5 to master
+ #|\ \
+ #| | * fa38b91f199934685819bea316186d8b008c52a2 (b5) added submodule named 'submodule' pointing to '../testrepo.git'
+ #| * | 27b7ce66243eb1403862d05f958c002312df173d (b4) edited fold\b.txt
+ #| |/
+ #* | d9b63a88223d8367516f50bd131a5f7349b7f3e4 (b3) edited a.txt
+ #|/
+ #* a78705c3b2725f931d3ee05348d83cc26700f247 (b2, b1) added fold and fold/b.txt
+ #* 5c0bb3d1b9449d1cc69d7519fd05166f01840915 added a.txt
+
+ #fix paths so that we can add repo folders under libgit2 repo
+ #rename .git to .gitted
+ find . -name .git -exec mv -i '{}' '{}ted' \;
+ mv -i .gitmodules gitmodules
+popd
diff --git a/tests/resources/push_src/.gitted/COMMIT_EDITMSG b/tests/resources/push_src/.gitted/COMMIT_EDITMSG
new file mode 100644
index 000000000..b1295084c
--- /dev/null
+++ b/tests/resources/push_src/.gitted/COMMIT_EDITMSG
@@ -0,0 +1 @@
+added submodule named 'submodule' pointing to '../testrepo.git'
diff --git a/tests/resources/push_src/.gitted/HEAD b/tests/resources/push_src/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/push_src/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/push_src/.gitted/ORIG_HEAD b/tests/resources/push_src/.gitted/ORIG_HEAD
new file mode 100644
index 000000000..afadf9d26
--- /dev/null
+++ b/tests/resources/push_src/.gitted/ORIG_HEAD
@@ -0,0 +1 @@
+a78705c3b2725f931d3ee05348d83cc26700f247
diff --git a/tests/resources/push_src/.gitted/config b/tests/resources/push_src/.gitted/config
new file mode 100644
index 000000000..51de0311b
--- /dev/null
+++ b/tests/resources/push_src/.gitted/config
@@ -0,0 +1,10 @@
+[core]
+ repositoryformatversion = 0
+ filemode = false
+ bare = false
+ logallrefupdates = true
+ symlinks = false
+ ignorecase = true
+ hideDotFiles = dotGitOnly
+[submodule "submodule"]
+ url = m:/dd/libgit2/tests-clar/resources/testrepo.git
diff --git a/tests/resources/push_src/.gitted/description b/tests/resources/push_src/.gitted/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/push_src/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/push_src/.gitted/index b/tests/resources/push_src/.gitted/index
new file mode 100644
index 000000000..0ef6594b3
--- /dev/null
+++ b/tests/resources/push_src/.gitted/index
Binary files differ
diff --git a/tests/resources/push_src/.gitted/info/exclude b/tests/resources/push_src/.gitted/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/push_src/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/push_src/.gitted/logs/HEAD b/tests/resources/push_src/.gitted/logs/HEAD
new file mode 100644
index 000000000..4ef336f84
--- /dev/null
+++ b/tests/resources/push_src/.gitted/logs/HEAD
@@ -0,0 +1,10 @@
+0000000000000000000000000000000000000000 5c0bb3d1b9449d1cc69d7519fd05166f01840915 Congyi Wu <congyiwu@gmail.com> 1352923200 -0500 commit (initial): added a.txt
+5c0bb3d1b9449d1cc69d7519fd05166f01840915 a78705c3b2725f931d3ee05348d83cc26700f247 Congyi Wu <congyiwu@gmail.com> 1352923200 -0500 commit: added fold and fold/b.txt
+a78705c3b2725f931d3ee05348d83cc26700f247 a78705c3b2725f931d3ee05348d83cc26700f247 Congyi Wu <congyiwu@gmail.com> 1352923201 -0500 checkout: moving from master to b3
+a78705c3b2725f931d3ee05348d83cc26700f247 d9b63a88223d8367516f50bd131a5f7349b7f3e4 Congyi Wu <congyiwu@gmail.com> 1352923201 -0500 commit: edited a.txt
+d9b63a88223d8367516f50bd131a5f7349b7f3e4 a78705c3b2725f931d3ee05348d83cc26700f247 Congyi Wu <congyiwu@gmail.com> 1352923201 -0500 checkout: moving from b3 to b4
+a78705c3b2725f931d3ee05348d83cc26700f247 27b7ce66243eb1403862d05f958c002312df173d Congyi Wu <congyiwu@gmail.com> 1352923201 -0500 commit: edited fold\b.txt
+27b7ce66243eb1403862d05f958c002312df173d a78705c3b2725f931d3ee05348d83cc26700f247 Congyi Wu <congyiwu@gmail.com> 1352923201 -0500 checkout: moving from b4 to b5
+a78705c3b2725f931d3ee05348d83cc26700f247 fa38b91f199934685819bea316186d8b008c52a2 Congyi Wu <congyiwu@gmail.com> 1352923206 -0500 commit: added submodule named 'submodule' pointing to '../testrepo.git'
+fa38b91f199934685819bea316186d8b008c52a2 a78705c3b2725f931d3ee05348d83cc26700f247 Congyi Wu <congyiwu@gmail.com> 1352923207 -0500 checkout: moving from b5 to master
+a78705c3b2725f931d3ee05348d83cc26700f247 951bbbb90e2259a4c8950db78946784fb53fcbce Congyi Wu <congyiwu@gmail.com> 1352923207 -0500 merge b3 b4 b5: Merge made by the 'octopus' strategy.
diff --git a/tests/resources/push_src/.gitted/logs/refs/heads/b1 b/tests/resources/push_src/.gitted/logs/refs/heads/b1
new file mode 100644
index 000000000..390a03d5c
--- /dev/null
+++ b/tests/resources/push_src/.gitted/logs/refs/heads/b1
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 a78705c3b2725f931d3ee05348d83cc26700f247 Congyi Wu <congyiwu@gmail.com> 1352923200 -0500 branch: Created from master
diff --git a/tests/resources/push_src/.gitted/logs/refs/heads/b2 b/tests/resources/push_src/.gitted/logs/refs/heads/b2
new file mode 100644
index 000000000..390a03d5c
--- /dev/null
+++ b/tests/resources/push_src/.gitted/logs/refs/heads/b2
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 a78705c3b2725f931d3ee05348d83cc26700f247 Congyi Wu <congyiwu@gmail.com> 1352923200 -0500 branch: Created from master
diff --git a/tests/resources/push_src/.gitted/logs/refs/heads/b3 b/tests/resources/push_src/.gitted/logs/refs/heads/b3
new file mode 100644
index 000000000..01e302c44
--- /dev/null
+++ b/tests/resources/push_src/.gitted/logs/refs/heads/b3
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 a78705c3b2725f931d3ee05348d83cc26700f247 Congyi Wu <congyiwu@gmail.com> 1352923201 -0500 branch: Created from HEAD
+a78705c3b2725f931d3ee05348d83cc26700f247 d9b63a88223d8367516f50bd131a5f7349b7f3e4 Congyi Wu <congyiwu@gmail.com> 1352923201 -0500 commit: edited a.txt
diff --git a/tests/resources/push_src/.gitted/logs/refs/heads/b4 b/tests/resources/push_src/.gitted/logs/refs/heads/b4
new file mode 100644
index 000000000..7afddc54e
--- /dev/null
+++ b/tests/resources/push_src/.gitted/logs/refs/heads/b4
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 a78705c3b2725f931d3ee05348d83cc26700f247 Congyi Wu <congyiwu@gmail.com> 1352923201 -0500 branch: Created from master
+a78705c3b2725f931d3ee05348d83cc26700f247 27b7ce66243eb1403862d05f958c002312df173d Congyi Wu <congyiwu@gmail.com> 1352923201 -0500 commit: edited fold\b.txt
diff --git a/tests/resources/push_src/.gitted/logs/refs/heads/b5 b/tests/resources/push_src/.gitted/logs/refs/heads/b5
new file mode 100644
index 000000000..bc22567f7
--- /dev/null
+++ b/tests/resources/push_src/.gitted/logs/refs/heads/b5
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 a78705c3b2725f931d3ee05348d83cc26700f247 Congyi Wu <congyiwu@gmail.com> 1352923201 -0500 branch: Created from master
+a78705c3b2725f931d3ee05348d83cc26700f247 fa38b91f199934685819bea316186d8b008c52a2 Congyi Wu <congyiwu@gmail.com> 1352923206 -0500 commit: added submodule named 'submodule' pointing to '../testrepo.git'
diff --git a/tests/resources/push_src/.gitted/logs/refs/heads/master b/tests/resources/push_src/.gitted/logs/refs/heads/master
new file mode 100644
index 000000000..8aafa9ca4
--- /dev/null
+++ b/tests/resources/push_src/.gitted/logs/refs/heads/master
@@ -0,0 +1,3 @@
+0000000000000000000000000000000000000000 5c0bb3d1b9449d1cc69d7519fd05166f01840915 Congyi Wu <congyiwu@gmail.com> 1352923200 -0500 commit (initial): added a.txt
+5c0bb3d1b9449d1cc69d7519fd05166f01840915 a78705c3b2725f931d3ee05348d83cc26700f247 Congyi Wu <congyiwu@gmail.com> 1352923200 -0500 commit: added fold and fold/b.txt
+a78705c3b2725f931d3ee05348d83cc26700f247 951bbbb90e2259a4c8950db78946784fb53fcbce Congyi Wu <congyiwu@gmail.com> 1352923207 -0500 merge b3 b4 b5: Merge made by the 'octopus' strategy.
diff --git a/tests/resources/push_src/.gitted/modules/submodule/HEAD b/tests/resources/push_src/.gitted/modules/submodule/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/push_src/.gitted/modules/submodule/config b/tests/resources/push_src/.gitted/modules/submodule/config
new file mode 100644
index 000000000..59810077d
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/config
@@ -0,0 +1,15 @@
+[core]
+ repositoryformatversion = 0
+ filemode = false
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../submodule
+ symlinks = false
+ ignorecase = true
+ hideDotFiles = dotGitOnly
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = m:/dd/libgit2/tests-clar/resources/testrepo.git
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/push_src/.gitted/modules/submodule/description b/tests/resources/push_src/.gitted/modules/submodule/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/push_src/.gitted/modules/submodule/index b/tests/resources/push_src/.gitted/modules/submodule/index
new file mode 100644
index 000000000..8e44080f3
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/index
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/info/exclude b/tests/resources/push_src/.gitted/modules/submodule/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/push_src/.gitted/modules/submodule/logs/HEAD b/tests/resources/push_src/.gitted/modules/submodule/logs/HEAD
new file mode 100644
index 000000000..aedcdf295
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Congyi Wu <congyiwu@gmail.com> 1352923205 -0500 clone: from m:/dd/libgit2/tests-clar/resources/testrepo.git
diff --git a/tests/resources/push_src/.gitted/modules/submodule/logs/refs/heads/master b/tests/resources/push_src/.gitted/modules/submodule/logs/refs/heads/master
new file mode 100644
index 000000000..aedcdf295
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Congyi Wu <congyiwu@gmail.com> 1352923205 -0500 clone: from m:/dd/libgit2/tests-clar/resources/testrepo.git
diff --git a/tests/resources/push_src/.gitted/modules/submodule/logs/refs/remotes/origin/HEAD b/tests/resources/push_src/.gitted/modules/submodule/logs/refs/remotes/origin/HEAD
new file mode 100644
index 000000000..aedcdf295
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Congyi Wu <congyiwu@gmail.com> 1352923205 -0500 clone: from m:/dd/libgit2/tests-clar/resources/testrepo.git
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/08/b041783f40edfe12bb406c9c9a8a040177c125 b/tests/resources/push_src/.gitted/modules/submodule/objects/08/b041783f40edfe12bb406c9c9a8a040177c125
new file mode 100644
index 000000000..d1c032fce
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/08/b041783f40edfe12bb406c9c9a8a040177c125
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/tests/resources/push_src/.gitted/modules/submodule/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
new file mode 100644
index 000000000..cedb2a22e
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/tests/resources/push_src/.gitted/modules/submodule/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
new file mode 100644
index 000000000..93a16f146
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/18/10dff58d8a660512d4832e740f692884338ccd b/tests/resources/push_src/.gitted/modules/submodule/objects/18/10dff58d8a660512d4832e740f692884338ccd
new file mode 100644
index 000000000..ba0bfb30c
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/18/10dff58d8a660512d4832e740f692884338ccd
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd b/tests/resources/push_src/.gitted/modules/submodule/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd
new file mode 100644
index 000000000..3ec541288
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/1b/8cbad43e867676df601306689fe7c3def5e689 b/tests/resources/push_src/.gitted/modules/submodule/objects/1b/8cbad43e867676df601306689fe7c3def5e689
new file mode 100644
index 000000000..6048d4bad
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/1b/8cbad43e867676df601306689fe7c3def5e689
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b b/tests/resources/push_src/.gitted/modules/submodule/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
new file mode 100644
index 000000000..225c45734
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10 b/tests/resources/push_src/.gitted/modules/submodule/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10
new file mode 100644
index 000000000..cb1ed5712
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d b/tests/resources/push_src/.gitted/modules/submodule/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
new file mode 100644
index 000000000..df40d99af
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7 b/tests/resources/push_src/.gitted/modules/submodule/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7
new file mode 100644
index 000000000..0a1500a6f
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54 b/tests/resources/push_src/.gitted/modules/submodule/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
new file mode 100644
index 000000000..321eaa867
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc b/tests/resources/push_src/.gitted/modules/submodule/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
new file mode 100644
index 000000000..9bb5b623b
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/push_src/.gitted/modules/submodule/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
new file mode 100644
index 000000000..7ca4ceed5
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/tests/resources/push_src/.gitted/modules/submodule/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
new file mode 100644
index 000000000..8953b6cef
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
@@ -0,0 +1,2 @@
+xŽQ
+Â0DýÎ)öÊ6›Í¦ "xO°‰‰-ØFb¼¿EoàÏ0 ¼Ç¤º,ske×[ÎPn8R,EpD?±gŸ}Ê^3² âÙ<µåµGŽhYKÄèÒ8ЖDAÉ)¿ÉÈ;gôݧÚàšjïp™4ÕŽ¯ô-çû¢óãêr‚ÁŠ;°s°GA4Ûº=ìùÖ(ôin7øIÌKÍFE \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea b/tests/resources/push_src/.gitted/modules/submodule/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea
new file mode 100644
index 000000000..b4e5aa186
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91 b/tests/resources/push_src/.gitted/modules/submodule/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91
new file mode 100644
index 000000000..351cff823
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/tests/resources/push_src/.gitted/modules/submodule/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
new file mode 100644
index 000000000..c1f22c54f
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
@@ -0,0 +1,2 @@
+xŽÛ 1EýNi@™Ék2 "X‚$ÙYW0YcÿíÀ¿Ã…s¸¥ÕzïÚÚõMDÏ€0æœ8!¶†ÉÌÞs‰ XŠªgÚdí::@X0»P¢wÙ"F/‰‰œÍRàˆUz÷¥múZZïú²¤ÒV}|•/œo5݇ÒêI£!¬1z Æ:vùÇUim}ê/¢>
+öF- \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/tests/resources/push_src/.gitted/modules/submodule/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
new file mode 100644
index 000000000..2ef4faa0f
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af b/tests/resources/push_src/.gitted/modules/submodule/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
new file mode 100644
index 000000000..716b0c64b
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
@@ -0,0 +1 @@
+xŽAj!³ö?0¨£ßÂ09Êo}HÚ6¨}ÿôjUPP©ÕZ&Yÿø˜ AÔ›±€pŒÁFdë¼÷pz[fŽYŒ½PÒqLJ.,Z§`™Å®Ð.ù`’vÙ ³q $Æ5+9çOëtœû>Û/úDE/龡W¯ï*e¿§VŸdf1>ð覭Öê²×äÄ›¹úÊ™F« ­ìTŽÙhœk.i¶^0Ô?P¼R, \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/7b/4384978d2493e851f9cca7858815fac9b10980 b/tests/resources/push_src/.gitted/modules/submodule/objects/7b/4384978d2493e851f9cca7858815fac9b10980
new file mode 100644
index 000000000..23c462f34
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/7b/4384978d2493e851f9cca7858815fac9b10980
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/tests/resources/push_src/.gitted/modules/submodule/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
new file mode 100644
index 000000000..2f9b6b6e3
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/tests/resources/push_src/.gitted/modules/submodule/objects/84/96071c1b46c854b31185ea97743be6a8774479
new file mode 100644
index 000000000..5df58dda5
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/84/96071c1b46c854b31185ea97743be6a8774479
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe b/tests/resources/push_src/.gitted/modules/submodule/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe
new file mode 100644
index 000000000..71019a636
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe
@@ -0,0 +1,2 @@
+xŒM F]sŠ¹€†Ÿ41ÆxÝ(­I‹ÁéÂÛKݽ/_ÞãP@¡ÚÕø¢!8›)es
+” ¥N&FGSÆ„¹hÑ{+ßCç‰÷ÆZzvØF¡7ZàÎ-¬Îñó‡k™x\ã¡[PÆ8ï´ôGØK/¥^© lÊ>.4 \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162 b/tests/resources/push_src/.gitted/modules/submodule/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
new file mode 100644
index 000000000..4cc3f4dff
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
@@ -0,0 +1 @@
+x+)JMU044b040031QrutñueX¡l¨ðmmA‹m›Ì£íJ}Gß;U‘T”˜—œŸ–™“ªWRQÂ`6ýš÷KÇ¥¶^/¾-*|òøWØ¥3P¥y©å`%ËEÛÞ±\&gŽÐ|Ÿ0§ÿ†{Ó1X \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4 b/tests/resources/push_src/.gitted/modules/submodule/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
new file mode 100644
index 000000000..bf7b2bb68
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2 b/tests/resources/push_src/.gitted/modules/submodule/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2
new file mode 100644
index 000000000..7f1cfb23c
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2
@@ -0,0 +1,2 @@
+xŽM
+Â0F]ç³d2¤ñ®<A~&´`­ÄôþVàæãmïËë²ÌÈÒ¡7Uà$äJöL9yM!¢GuœªH¤&UÈæ›>;ÔÂÁ…¬£³X†ÂEÈŽ5R±£ ÛAÑE &n}ZÜæ<E}À=O[ÒÖáüÞéúÓ¼^À,ã^†#¢É¿ƒ]ÿPÍ`>™A¹ \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/tests/resources/push_src/.gitted/modules/submodule/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
new file mode 100644
index 000000000..a79612435
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
@@ -0,0 +1,3 @@
+xŽ[
+Â0EýÎ*fÊäÕ¤ "¸W0“‡-ØFâtÿÝ—çpS[–YÀ˜x^
+Díb CLhutɉ}¥8X*4Zí¬sY½¨—UÀ‘AÃÖ ÌX3‡R«Mµ¶) s6è¼¢M¦ÖážšÜ&Jm…ó;}Çõ±Ðü<¥¶\@›à‚ÑÞpÄ€¨vº?”ò«jÛºLð«¨Ø?Hå \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/tests/resources/push_src/.gitted/modules/submodule/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
new file mode 100644
index 000000000..f8588696b
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
@@ -0,0 +1,2 @@
+x;j1DëmdÓú·À˜ÇŽ|M«µ3`ŒV{ >€³âQ¯ ¸·vL0I?Í!š4–Z=Ê! ×¦8²F¢Ã’!rÖsQßyÈ9]$DŽ&„l6AÇ>jFWüÒµ IKNiûë§Z¢%¡SˆŒ‘
+‹Ò ­ÅʉøU~̽øä>'¼ï™û ¯wþ ×[ËÇ× ÷öÚDGÚ¡±ðŒQ-ºMù«>dܶ‘OÞáÒò}í\à8g_ШÂoYr \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750 b/tests/resources/push_src/.gitted/modules/submodule/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
new file mode 100644
index 000000000..29c8e824d
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
@@ -0,0 +1,3 @@
+xŽQ
+!@ûösBQ"‚ŽÐ ÆÙ± rÍîßÒú{<xð¤·öàîƪ
+™HlJSer!ZPTe*Žj°UÝÑEo^¼ê2 (†ˆ¬XSÅ€ED‘ƒO<Y¦šj$2üs_á&} ¸Î,}Ó[~p¹7~<ÒÛ:Ÿ £°·ÉZ³Ùípè?­1_ûåC0 \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/tests/resources/push_src/.gitted/modules/submodule/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
new file mode 100644
index 000000000..d0d7e736e
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/tests/resources/push_src/.gitted/modules/submodule/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
new file mode 100644
index 000000000..18a7f61c2
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/ae/90f12eea699729ed24555e40b9fd669da12a12 b/tests/resources/push_src/.gitted/modules/submodule/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
new file mode 100644
index 000000000..d95254674
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 b/tests/resources/push_src/.gitted/modules/submodule/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
new file mode 100644
index 000000000..f460f2547
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
@@ -0,0 +1,2 @@
+xÌA
+Â0…a×9ÅìIÒ ™€ˆp'î§1Ѷ‘v\x{cáýðVŸpƒvWûgŠ¾ÇŽ0xº[ ]"g†#{rDÆ Cot ­äûN œU $­ò?9-p+1Í^¤ÀQx®¯Ï9O\ÆC¬Ó Œm'D {mµVêú(+´ñælè¶,Þ \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593 b/tests/resources/push_src/.gitted/modules/submodule/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
new file mode 100644
index 000000000..f613670e2
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 b/tests/resources/push_src/.gitted/modules/submodule/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
new file mode 100644
index 000000000..0817229bc
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
@@ -0,0 +1,3 @@
+xKj1D³Ö)zçUBëÛ-0ÁuV9¦Õò<#£È÷ÏȲ+ŠW<Jú¶Ý&8Ê/s¨‚e‹µµÈ•KJ­«½S
+ØRvÌÁ{©æQ†îr«äY¹QN$H\Eµ²Íè=6áX5¦òÇK Fr)·(‰dC‡Î†”­–œ—jÊs®}À—ô9ác-Òw8Ëo¸\·r»¿IßÞÁ:
+l}F‚W$Ds´Ç£©ÿÙšOW…e”]V8-ÃÌÈ"U \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd b/tests/resources/push_src/.gitted/modules/submodule/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
new file mode 100644
index 000000000..75f541f10
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
@@ -0,0 +1,3 @@
+xŽQ
+Â0DýÎ)öʦ»I<‚'ØlR+˜Fj¼¿EoàÏ0<xÃh«õÞa Üõµ]È™­åXUlÞPF)Åz‘4yó”µ,\r 'SÂÄ-mI4
+‘Xhô”&òÌFÞ}n+\µõ—Y´-p|é·œoUz;-‘aÑlt{ØË?®I«,:ÃoÚR̳cHK \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659 b/tests/resources/push_src/.gitted/modules/submodule/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659
new file mode 100644
index 000000000..f3b46b3ca
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f b/tests/resources/push_src/.gitted/modules/submodule/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
new file mode 100644
index 000000000..a67d6e647
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759 b/tests/resources/push_src/.gitted/modules/submodule/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759
new file mode 100644
index 000000000..2d47e6faf
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/push_src/.gitted/modules/submodule/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644
index 000000000..711223894
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0 b/tests/resources/push_src/.gitted/modules/submodule/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
new file mode 100644
index 000000000..b135eccda
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3 b/tests/resources/push_src/.gitted/modules/submodule/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
new file mode 100644
index 000000000..82e2790e8
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 b/tests/resources/push_src/.gitted/modules/submodule/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
new file mode 100644
index 000000000..697c94c92
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/tests/resources/push_src/.gitted/modules/submodule/objects/fa/49b077972391ad58037050f2a75f74e3671e92
new file mode 100644
index 000000000..112998d42
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/fa/49b077972391ad58037050f2a75f74e3671e92
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/fd/093bff70906175335656e6ce6ae05783708765 b/tests/resources/push_src/.gitted/modules/submodule/objects/fd/093bff70906175335656e6ce6ae05783708765
new file mode 100644
index 000000000..12bf5f3e3
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/fd/093bff70906175335656e6ce6ae05783708765
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09 b/tests/resources/push_src/.gitted/modules/submodule/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09
new file mode 100644
index 000000000..158aef21f
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
new file mode 100644
index 000000000..5068f2818
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
new file mode 100644
index 000000000..a6a1f3020
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
new file mode 100644
index 000000000..94c3c71da
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
new file mode 100644
index 000000000..74c7fe4f3
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
new file mode 100644
index 000000000..555cfa977
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
new file mode 100644
index 000000000..4d539ed0a
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/packed-refs b/tests/resources/push_src/.gitted/modules/submodule/packed-refs
new file mode 100644
index 000000000..506a8607c
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/packed-refs
@@ -0,0 +1,24 @@
+# pack-refs with: peeled
+a4a7dce85cf63874e984719f4fdd239f5145052f refs/remotes/origin/br2
+a4a7dce85cf63874e984719f4fdd239f5145052f refs/remotes/origin/cannot-fetch
+e90810b8df3e80c413d903f631643c716887138d refs/remotes/origin/chomped
+258f0e2a959a364e40ed6603d5d44fbb24765b10 refs/remotes/origin/haacked
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750 refs/remotes/origin/master
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750 refs/remotes/origin/not-good
+41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9 refs/remotes/origin/packed
+4a202b346bb0fb0db7eff3cffeb3c70babbd2045 refs/remotes/origin/packed-test
+763d71aadf09a7951596c9746c024e7eece7c7af refs/remotes/origin/subtrees
+e90810b8df3e80c413d903f631643c716887138d refs/remotes/origin/test
+9fd738e8f7967c078dceed8190330fc8648ee56a refs/remotes/origin/track-local
+e90810b8df3e80c413d903f631643c716887138d refs/remotes/origin/trailing
+521d87c1ec3aef9824daf6d96cc0ae3710766d91 refs/tags/annotated_tag_to_blob
+^1385f264afb75a56a5bec74243be9b367ba4ca08
+7b4384978d2493e851f9cca7858815fac9b10980 refs/tags/e90810b
+^e90810b8df3e80c413d903f631643c716887138d
+849a5e34a26815e821f865b8479f5815a47af0fe refs/tags/hard_tag
+^a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+1385f264afb75a56a5bec74243be9b367ba4ca08 refs/tags/point_to_blob
+b25fa35b38051e4ae45d4222e795f9df2e43f1d1 refs/tags/test
+^e90810b8df3e80c413d903f631643c716887138d
+849a5e34a26815e821f865b8479f5815a47af0fe refs/tags/wrapped_tag
+^a65fedf39aefe402d3bb6e24df4d4f5fe4547750
diff --git a/tests/resources/push_src/.gitted/modules/submodule/refs/heads/master b/tests/resources/push_src/.gitted/modules/submodule/refs/heads/master
new file mode 100644
index 000000000..3d8f0a402
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/refs/heads/master
@@ -0,0 +1 @@
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750
diff --git a/tests/resources/push_src/.gitted/modules/submodule/refs/remotes/origin/HEAD b/tests/resources/push_src/.gitted/modules/submodule/refs/remotes/origin/HEAD
new file mode 100644
index 000000000..6efe28fff
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/push_src/.gitted/objects/08/585692ce06452da6f82ae66b90d98b55536fca b/tests/resources/push_src/.gitted/objects/08/585692ce06452da6f82ae66b90d98b55536fca
new file mode 100644
index 000000000..39d126b2b
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/08/585692ce06452da6f82ae66b90d98b55536fca
@@ -0,0 +1 @@
+x+)JMU06f040031QHÔ+©(a¨˜!©”h­õ;A•EÿÛÞö®ƒ3ýZüÝ* \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/objects/27/b7ce66243eb1403862d05f958c002312df173d b/tests/resources/push_src/.gitted/objects/27/b7ce66243eb1403862d05f958c002312df173d
new file mode 100644
index 000000000..01d63b5c4
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/27/b7ce66243eb1403862d05f958c002312df173d
@@ -0,0 +1,4 @@
+x•ŽM
+Â0F]çsËt¦i<„7ù™Ñ‚m¤¤¨··xw÷àKešÆ
+Dý®." ªâmrš1°Ó@’’ê9Ú>RëûÈž¬y†Eæ
+Á mâHŽì&µ™EÐr7äS¢Þ!*u΄µÞËç2ß>#\V8¤ß|­§ÛÆG“Êt„–-ybÂöhÍF·Uþ/ä±J-|M}Wóã+GK \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/objects/28/905c54ea45a4bed8d7b90f51bd8bd81eec8840 b/tests/resources/push_src/.gitted/objects/28/905c54ea45a4bed8d7b90f51bd8bd81eec8840
new file mode 100644
index 000000000..dc10f6831
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/28/905c54ea45a4bed8d7b90f51bd8bd81eec8840
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/36/6226fb970ac0caa9d3f55967ab01334a548f60 b/tests/resources/push_src/.gitted/objects/36/6226fb970ac0caa9d3f55967ab01334a548f60
new file mode 100644
index 000000000..45c4d9208
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/36/6226fb970ac0caa9d3f55967ab01334a548f60
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/36/f79b2846017d3761e0a02d0bccd573e0f90c57 b/tests/resources/push_src/.gitted/objects/36/f79b2846017d3761e0a02d0bccd573e0f90c57
new file mode 100644
index 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/resources/push_src/.gitted/objects/5c/0bb3d1b9449d1cc69d7519fd05166f01840915 b/tests/resources/push_src/.gitted/objects/5c/0bb3d1b9449d1cc69d7519fd05166f01840915
new file mode 100644
index 000000000..883182138
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/5c/0bb3d1b9449d1cc69d7519fd05166f01840915
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/61/780798228d17af2d34fce4cfbdf35556832472 b/tests/resources/push_src/.gitted/objects/61/780798228d17af2d34fce4cfbdf35556832472
new file mode 100644
index 000000000..586bf17a4
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/61/780798228d17af2d34fce4cfbdf35556832472
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/64/fd55f9b6390202db5e5666fd1fb339089fba4d b/tests/resources/push_src/.gitted/objects/64/fd55f9b6390202db5e5666fd1fb339089fba4d
new file mode 100644
index 000000000..bcaaa91c2
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/64/fd55f9b6390202db5e5666fd1fb339089fba4d
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/78/981922613b2afb6025042ff6bd878ac1994e85 b/tests/resources/push_src/.gitted/objects/78/981922613b2afb6025042ff6bd878ac1994e85
new file mode 100644
index 000000000..e814d07b0
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/78/981922613b2afb6025042ff6bd878ac1994e85
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/80/5c54522e614f29f70d2413a0470247d8b424ac b/tests/resources/push_src/.gitted/objects/80/5c54522e614f29f70d2413a0470247d8b424ac
new file mode 100644
index 000000000..552670c06
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/80/5c54522e614f29f70d2413a0470247d8b424ac
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/95/1bbbb90e2259a4c8950db78946784fb53fcbce b/tests/resources/push_src/.gitted/objects/95/1bbbb90e2259a4c8950db78946784fb53fcbce
new file mode 100644
index 000000000..596cd43de
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/95/1bbbb90e2259a4c8950db78946784fb53fcbce
@@ -0,0 +1,2 @@
+x•[JÆ0…}î*f¿2—Ì$ÁEøœi’úƒm¥¶ˆ»·
+úîÛáã\8ã:Ï×DôfßZ ½ªöì&¹º65³^©»œ,åî%Ôá­lmÙ¡~;KJÌR“XT²®è•„Šö(!{ìÒ¯Ÿ£Ç±™qæP’qÅsPÓˆÈB\;EùëïE’gê”s–`IeoEÈ(YMŽ˜FåÂC9ö—uƒ§u™>¯ð|Àýø#?ŽÇi.××»q€D9³0F¸EENzþßÛÿ“Ãܶ©Ë<\ ,\a_a.ïgßðëd \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/objects/a7/8705c3b2725f931d3ee05348d83cc26700f247 b/tests/resources/push_src/.gitted/objects/a7/8705c3b2725f931d3ee05348d83cc26700f247
new file mode 100644
index 000000000..6ad835e86
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/a7/8705c3b2725f931d3ee05348d83cc26700f247
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/b4/83ae7ba66decee9aee971f501221dea84b1498 b/tests/resources/push_src/.gitted/objects/b4/83ae7ba66decee9aee971f501221dea84b1498
new file mode 100644
index 000000000..1e0bd3b05
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/b4/83ae7ba66decee9aee971f501221dea84b1498
@@ -0,0 +1,3 @@
+x5K
+1D]ç½¥;1i"^À•'èÎô|d`dŒ ooqQE½Í«*PàÝ¢+
+á 3…$, }ì%¢Rßw¬É+sç9»úy輨«ÍÐrøÃ`+ܦ2ŠÍp/ã[m­p~µuÝê8-—öSˆ™r„=¢Û,?ÃZ+g \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/objects/b4/e1f2b375a64c1ccd40c5ff6aa8bc96839ba4fd b/tests/resources/push_src/.gitted/objects/b4/e1f2b375a64c1ccd40c5ff6aa8bc96839ba4fd
new file mode 100644
index 000000000..4e650aaa1
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/b4/e1f2b375a64c1ccd40c5ff6aa8bc96839ba4fd
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/c1/0409136a7a75e025fa502a1b2fd7b62b77d279 b/tests/resources/push_src/.gitted/objects/c1/0409136a7a75e025fa502a1b2fd7b62b77d279
new file mode 100644
index 000000000..fcb2b32f3
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/c1/0409136a7a75e025fa502a1b2fd7b62b77d279
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/cd/881f90f2933db2e4cc26b8c71fe6037ac7fe4c b/tests/resources/push_src/.gitted/objects/cd/881f90f2933db2e4cc26b8c71fe6037ac7fe4c
new file mode 100644
index 000000000..ad4272638
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/cd/881f90f2933db2e4cc26b8c71fe6037ac7fe4c
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/d9/b63a88223d8367516f50bd131a5f7349b7f3e4 b/tests/resources/push_src/.gitted/objects/d9/b63a88223d8367516f50bd131a5f7349b7f3e4
new file mode 100644
index 000000000..b471e2155
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/d9/b63a88223d8367516f50bd131a5f7349b7f3e4
@@ -0,0 +1,2 @@
+x•ŽA
+Â0E]çsËd¦Ó$ "x×i2Õ‚m¤¤¨··zwŸ÷S™¦±‘ÝÕErŠ½gjÃÐ ![ÍŽ%wbY(z˜C/ÁšG\t®w(‰{r$C`›Y…[Ÿ=§DC¨u&®õV8—ùúá²Â!ýæs=]§8Þ›T¦#|;˜ÐÂÑltûWõÓh«fˆM}Uó¼QDM \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/objects/dc/ab83249f6f9d1ed735d651352a80519339b591 b/tests/resources/push_src/.gitted/objects/dc/ab83249f6f9d1ed735d651352a80519339b591
new file mode 100644
index 000000000..9f6b1502f
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/dc/ab83249f6f9d1ed735d651352a80519339b591
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/ee/a4f2705eeec2db3813f2430829afce99cd00b5 b/tests/resources/push_src/.gitted/objects/ee/a4f2705eeec2db3813f2430829afce99cd00b5
new file mode 100644
index 000000000..b7b81d5e3
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/ee/a4f2705eeec2db3813f2430829afce99cd00b5
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/f7/8a3106c85fb549c65198b2a2086276c6174928 b/tests/resources/push_src/.gitted/objects/f7/8a3106c85fb549c65198b2a2086276c6174928
new file mode 100644
index 000000000..b9813576d
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/f7/8a3106c85fb549c65198b2a2086276c6174928
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/f8/f7aefc2900a3d737cea9eee45729fd55761e1a b/tests/resources/push_src/.gitted/objects/f8/f7aefc2900a3d737cea9eee45729fd55761e1a
new file mode 100644
index 000000000..888354fc2
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/f8/f7aefc2900a3d737cea9eee45729fd55761e1a
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/fa/38b91f199934685819bea316186d8b008c52a2 b/tests/resources/push_src/.gitted/objects/fa/38b91f199934685819bea316186d8b008c52a2
new file mode 100644
index 000000000..13d9bca20
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/fa/38b91f199934685819bea316186d8b008c52a2
@@ -0,0 +1,2 @@
+x•½J1„­ó§ÛÊ5›ÿ…‹>„urr²n’eo‚øö {»™f[)¹ƒ°â©_DmIiµ7
+7Ĩ8ꔌ÷.ànœÜƒW)²Ó_T;xë,×(ÃlÐi—[”D\K墓ˆÂXΓP–ùÑ?Ûï­ß>ÜðW~·£ø|_±•Wؤ»‚xæšs6éÜ×éÿIæc¤J‹ãNP}™~ù œ-מë½Á²®/ó„³­Gî û§X \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/objects/ff/83aa4c5e5d28e3bcba2f5c6e2adc61286a4e5e b/tests/resources/push_src/.gitted/objects/ff/83aa4c5e5d28e3bcba2f5c6e2adc61286a4e5e
new file mode 100644
index 000000000..10f25eb7c
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/ff/83aa4c5e5d28e3bcba2f5c6e2adc61286a4e5e
@@ -0,0 +1,4 @@
+x5ÍM
+1 `×=Eö¢4ÓNAÄ ¸òýÉüÈÀH ooq‘Ç{›/G@ò»5=$+”SOÝ) n¥xâ≻Ø[Æ@4úy
+h1Ú„v‡ÿ¥ÂmÎS”îyz'©
+çWk×-ŽóziÙQc<ÃÞ¢µfS~Âpv+… \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/objects/ff/fe95c7fd0a37fa2ed702f8f93b56b2196b3925 b/tests/resources/push_src/.gitted/objects/ff/fe95c7fd0a37fa2ed702f8f93b56b2196b3925
new file mode 100644
index 000000000..1cdc048c0
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/ff/fe95c7fd0a37fa2ed702f8f93b56b2196b3925
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/pack/dummy b/tests/resources/push_src/.gitted/objects/pack/dummy
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/pack/dummy
diff --git a/tests/resources/push_src/.gitted/refs/heads/b1 b/tests/resources/push_src/.gitted/refs/heads/b1
new file mode 100644
index 000000000..afadf9d26
--- /dev/null
+++ b/tests/resources/push_src/.gitted/refs/heads/b1
@@ -0,0 +1 @@
+a78705c3b2725f931d3ee05348d83cc26700f247
diff --git a/tests/resources/push_src/.gitted/refs/heads/b2 b/tests/resources/push_src/.gitted/refs/heads/b2
new file mode 100644
index 000000000..afadf9d26
--- /dev/null
+++ b/tests/resources/push_src/.gitted/refs/heads/b2
@@ -0,0 +1 @@
+a78705c3b2725f931d3ee05348d83cc26700f247
diff --git a/tests/resources/push_src/.gitted/refs/heads/b3 b/tests/resources/push_src/.gitted/refs/heads/b3
new file mode 100644
index 000000000..3056bb436
--- /dev/null
+++ b/tests/resources/push_src/.gitted/refs/heads/b3
@@ -0,0 +1 @@
+d9b63a88223d8367516f50bd131a5f7349b7f3e4
diff --git a/tests/resources/push_src/.gitted/refs/heads/b4 b/tests/resources/push_src/.gitted/refs/heads/b4
new file mode 100644
index 000000000..efed6f064
--- /dev/null
+++ b/tests/resources/push_src/.gitted/refs/heads/b4
@@ -0,0 +1 @@
+27b7ce66243eb1403862d05f958c002312df173d
diff --git a/tests/resources/push_src/.gitted/refs/heads/b5 b/tests/resources/push_src/.gitted/refs/heads/b5
new file mode 100644
index 000000000..cf313ad05
--- /dev/null
+++ b/tests/resources/push_src/.gitted/refs/heads/b5
@@ -0,0 +1 @@
+fa38b91f199934685819bea316186d8b008c52a2
diff --git a/tests/resources/push_src/.gitted/refs/heads/b6 b/tests/resources/push_src/.gitted/refs/heads/b6
new file mode 100644
index 000000000..711e466ae
--- /dev/null
+++ b/tests/resources/push_src/.gitted/refs/heads/b6
@@ -0,0 +1 @@
+951bbbb90e2259a4c8950db78946784fb53fcbce
diff --git a/tests/resources/push_src/.gitted/refs/tags/tag-blob b/tests/resources/push_src/.gitted/refs/tags/tag-blob
new file mode 100644
index 000000000..abfebf22e
--- /dev/null
+++ b/tests/resources/push_src/.gitted/refs/tags/tag-blob
@@ -0,0 +1 @@
+b483ae7ba66decee9aee971f501221dea84b1498
diff --git a/tests/resources/push_src/.gitted/refs/tags/tag-commit b/tests/resources/push_src/.gitted/refs/tags/tag-commit
new file mode 100644
index 000000000..c023b8452
--- /dev/null
+++ b/tests/resources/push_src/.gitted/refs/tags/tag-commit
@@ -0,0 +1 @@
+805c54522e614f29f70d2413a0470247d8b424ac
diff --git a/tests/resources/push_src/.gitted/refs/tags/tag-commit-two b/tests/resources/push_src/.gitted/refs/tags/tag-commit-two
new file mode 100644
index 000000000..abb365080
--- /dev/null
+++ b/tests/resources/push_src/.gitted/refs/tags/tag-commit-two
@@ -0,0 +1 @@
+36f79b2846017d3761e0a02d0bccd573e0f90c57
diff --git a/tests/resources/push_src/.gitted/refs/tags/tag-lightweight b/tests/resources/push_src/.gitted/refs/tags/tag-lightweight
new file mode 100644
index 000000000..711e466ae
--- /dev/null
+++ b/tests/resources/push_src/.gitted/refs/tags/tag-lightweight
@@ -0,0 +1 @@
+951bbbb90e2259a4c8950db78946784fb53fcbce
diff --git a/tests/resources/push_src/.gitted/refs/tags/tag-tag b/tests/resources/push_src/.gitted/refs/tags/tag-tag
new file mode 100644
index 000000000..d6cd74804
--- /dev/null
+++ b/tests/resources/push_src/.gitted/refs/tags/tag-tag
@@ -0,0 +1 @@
+eea4f2705eeec2db3813f2430829afce99cd00b5
diff --git a/tests/resources/push_src/.gitted/refs/tags/tag-tree b/tests/resources/push_src/.gitted/refs/tags/tag-tree
new file mode 100644
index 000000000..7a530d381
--- /dev/null
+++ b/tests/resources/push_src/.gitted/refs/tags/tag-tree
@@ -0,0 +1 @@
+ff83aa4c5e5d28e3bcba2f5c6e2adc61286a4e5e
diff --git a/tests/resources/push_src/a.txt b/tests/resources/push_src/a.txt
new file mode 100644
index 000000000..f7eac1c51
--- /dev/null
+++ b/tests/resources/push_src/a.txt
@@ -0,0 +1,2 @@
+a
+edit
diff --git a/tests/resources/push_src/fold/b.txt b/tests/resources/push_src/fold/b.txt
new file mode 100644
index 000000000..617807982
--- /dev/null
+++ b/tests/resources/push_src/fold/b.txt
@@ -0,0 +1 @@
+b
diff --git a/tests/resources/push_src/foldb.txt b/tests/resources/push_src/foldb.txt
new file mode 100644
index 000000000..5b38718be
--- /dev/null
+++ b/tests/resources/push_src/foldb.txt
@@ -0,0 +1 @@
+edit
diff --git a/tests/resources/push_src/gitmodules b/tests/resources/push_src/gitmodules
new file mode 100644
index 000000000..f1734dfc1
--- /dev/null
+++ b/tests/resources/push_src/gitmodules
@@ -0,0 +1,3 @@
+[submodule "submodule"]
+ path = submodule
+ url = ../testrepo.git
diff --git a/tests/resources/push_src/submodule/.gitted b/tests/resources/push_src/submodule/.gitted
new file mode 100644
index 000000000..3ffcf960a
--- /dev/null
+++ b/tests/resources/push_src/submodule/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/submodule
diff --git a/tests/resources/push_src/submodule/README b/tests/resources/push_src/submodule/README
new file mode 100644
index 000000000..ca8c64728
--- /dev/null
+++ b/tests/resources/push_src/submodule/README
@@ -0,0 +1 @@
+hey there
diff --git a/tests/resources/push_src/submodule/branch_file.txt b/tests/resources/push_src/submodule/branch_file.txt
new file mode 100644
index 000000000..a26902575
--- /dev/null
+++ b/tests/resources/push_src/submodule/branch_file.txt
@@ -0,0 +1,2 @@
+hi
+bye!
diff --git a/tests/resources/push_src/submodule/new.txt b/tests/resources/push_src/submodule/new.txt
new file mode 100644
index 000000000..8e0884e36
--- /dev/null
+++ b/tests/resources/push_src/submodule/new.txt
@@ -0,0 +1 @@
+my new file
diff --git a/tests/resources/renames/.gitted/HEAD b/tests/resources/renames/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/renames/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/renames/.gitted/config b/tests/resources/renames/.gitted/config
new file mode 100644
index 000000000..bb4d11c1f
--- /dev/null
+++ b/tests/resources/renames/.gitted/config
@@ -0,0 +1,7 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
+ precomposeunicode = false
diff --git a/tests/resources/renames/.gitted/description b/tests/resources/renames/.gitted/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/renames/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/renames/.gitted/index b/tests/resources/renames/.gitted/index
new file mode 100644
index 000000000..72363c0f5
--- /dev/null
+++ b/tests/resources/renames/.gitted/index
Binary files differ
diff --git a/tests/resources/renames/.gitted/info/exclude b/tests/resources/renames/.gitted/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/renames/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/renames/.gitted/logs/HEAD b/tests/resources/renames/.gitted/logs/HEAD
new file mode 100644
index 000000000..e69792263
--- /dev/null
+++ b/tests/resources/renames/.gitted/logs/HEAD
@@ -0,0 +1,4 @@
+0000000000000000000000000000000000000000 31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 Russell Belfer <rb@github.com> 1351024687 -0700 commit (initial): Initial commit
+31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 2bc7f351d20b53f1c72c16c4b036e491c478c49a Russell Belfer <rb@github.com> 1351024817 -0700 commit: copy and rename with no change
+2bc7f351d20b53f1c72c16c4b036e491c478c49a 1c068dee5790ef1580cfc4cd670915b48d790084 Russell Belfer <rb@github.com> 1361485758 -0800 commit: rewrites, copies with changes, etc.
+1c068dee5790ef1580cfc4cd670915b48d790084 19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13 Russell Belfer <rb@github.com> 1361486360 -0800 commit: more renames and smallish modifications
diff --git a/tests/resources/renames/.gitted/logs/refs/heads/master b/tests/resources/renames/.gitted/logs/refs/heads/master
new file mode 100644
index 000000000..e69792263
--- /dev/null
+++ b/tests/resources/renames/.gitted/logs/refs/heads/master
@@ -0,0 +1,4 @@
+0000000000000000000000000000000000000000 31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 Russell Belfer <rb@github.com> 1351024687 -0700 commit (initial): Initial commit
+31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 2bc7f351d20b53f1c72c16c4b036e491c478c49a Russell Belfer <rb@github.com> 1351024817 -0700 commit: copy and rename with no change
+2bc7f351d20b53f1c72c16c4b036e491c478c49a 1c068dee5790ef1580cfc4cd670915b48d790084 Russell Belfer <rb@github.com> 1361485758 -0800 commit: rewrites, copies with changes, etc.
+1c068dee5790ef1580cfc4cd670915b48d790084 19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13 Russell Belfer <rb@github.com> 1361486360 -0800 commit: more renames and smallish modifications
diff --git a/tests/resources/renames/.gitted/objects/03/da7ad872536bd448da8d88eb7165338bf923a7 b/tests/resources/renames/.gitted/objects/03/da7ad872536bd448da8d88eb7165338bf923a7
new file mode 100644
index 000000000..2ee86444d
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/03/da7ad872536bd448da8d88eb7165338bf923a7
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/17/58bdd7c16a72ff7c17d8de0c957ced3ccad645 b/tests/resources/renames/.gitted/objects/17/58bdd7c16a72ff7c17d8de0c957ced3ccad645
new file mode 100644
index 000000000..01801ed11
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/17/58bdd7c16a72ff7c17d8de0c957ced3ccad645
@@ -0,0 +1,5 @@
+xEͱ Â@QbWq ÿ®—ÃÅHôŸ¡_ž‰&{ëó]ãðy›û¸•¶Y¬X³û`Ÿì‹=¯Ý'í¶=Zo´Þh½Ñz£õFëÖí¿­­Ð
+­Ð
+­Ð
+­Ð
+MhBšÐ„&4¡ MhB3šÑŒf4£ÍhF3šÑŽKûxŒŒ \ No newline at end of file
diff --git a/tests/resources/renames/.gitted/objects/19/dd32dfb1520a64e5bbaae8dce6ef423dfa2f13 b/tests/resources/renames/.gitted/objects/19/dd32dfb1520a64e5bbaae8dce6ef423dfa2f13
new file mode 100644
index 000000000..4be4c6952
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/19/dd32dfb1520a64e5bbaae8dce6ef423dfa2f13
@@ -0,0 +1 @@
+x•ŽÑM!Eý¦Ši@3¼Þc,Á\X °ýK þÞ{Nrbo­,xzYC¬<‡h[&“È?=fcvÎyƒèC£W¿<äZ #:J"vs’µ%Œ9š˜Ü½¶ÁPÚ’Q|¯³ø¾ç”ZáKj–ï#|þ”uÞá-ööúpÚ;Â+¢Úëî[ý¯©Z;’›Là+Ál\k™'´žJ.‘Wé×T¯;O
diff --git a/tests/resources/renames/.gitted/objects/1c/068dee5790ef1580cfc4cd670915b48d790084 b/tests/resources/renames/.gitted/objects/1c/068dee5790ef1580cfc4cd670915b48d790084
new file mode 100644
index 000000000..d65ab0a9b
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/1c/068dee5790ef1580cfc4cd670915b48d790084
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/2b/c7f351d20b53f1c72c16c4b036e491c478c49a b/tests/resources/renames/.gitted/objects/2b/c7f351d20b53f1c72c16c4b036e491c478c49a
new file mode 100644
index 000000000..93f1ccb3f
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/2b/c7f351d20b53f1c72c16c4b036e491c478c49a
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/31/e47d8c1fa36d7f8d537b96158e3f024de0a9f2 b/tests/resources/renames/.gitted/objects/31/e47d8c1fa36d7f8d537b96158e3f024de0a9f2
new file mode 100644
index 000000000..00ce53212
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/31/e47d8c1fa36d7f8d537b96158e3f024de0a9f2
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/35/92953ff3ea5e8ba700c429f3aefe33c8806754 b/tests/resources/renames/.gitted/objects/35/92953ff3ea5e8ba700c429f3aefe33c8806754
new file mode 100644
index 000000000..886271d32
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/35/92953ff3ea5e8ba700c429f3aefe33c8806754
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/36/020db6cdacaa93497f31edcd8f242ff9bc366d b/tests/resources/renames/.gitted/objects/36/020db6cdacaa93497f31edcd8f242ff9bc366d
new file mode 100644
index 000000000..f4f9303ed
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/36/020db6cdacaa93497f31edcd8f242ff9bc366d
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/3c/04741dd4b96c4ae4b00ec0f6e10c816a30aad2 b/tests/resources/renames/.gitted/objects/3c/04741dd4b96c4ae4b00ec0f6e10c816a30aad2
new file mode 100644
index 000000000..c23602262
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/3c/04741dd4b96c4ae4b00ec0f6e10c816a30aad2
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/42/10ffd5c390b21dd5483375e75288dea9ede512 b/tests/resources/renames/.gitted/objects/42/10ffd5c390b21dd5483375e75288dea9ede512
new file mode 100644
index 000000000..d351a6d13
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/42/10ffd5c390b21dd5483375e75288dea9ede512
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/44/4a76ed3e45b183753f49376af30da8c3fe276a b/tests/resources/renames/.gitted/objects/44/4a76ed3e45b183753f49376af30da8c3fe276a
new file mode 100644
index 000000000..5ce12a3c5
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/44/4a76ed3e45b183753f49376af30da8c3fe276a
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/47/184c1e7eb22abcbed2bf4ee87d4e38096f7951 b/tests/resources/renames/.gitted/objects/47/184c1e7eb22abcbed2bf4ee87d4e38096f7951
new file mode 100644
index 000000000..aa9192699
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/47/184c1e7eb22abcbed2bf4ee87d4e38096f7951
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/4e/4cae3e7dd56ed74bff39526d0469e554432953 b/tests/resources/renames/.gitted/objects/4e/4cae3e7dd56ed74bff39526d0469e554432953
new file mode 100644
index 000000000..5e6ebd5e0
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/4e/4cae3e7dd56ed74bff39526d0469e554432953
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/50/e90273af7d826ff0a95865bcd3ba8412c447d9 b/tests/resources/renames/.gitted/objects/50/e90273af7d826ff0a95865bcd3ba8412c447d9
new file mode 100644
index 000000000..a98d14ee7
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/50/e90273af7d826ff0a95865bcd3ba8412c447d9
@@ -0,0 +1,3 @@
+xmÍM
+Â0`×9Å\@ÉOÛd ˆîE¼@š&4hLI"âíî]Ícà}Ïå”bÉÕ®ïaBÙORvΡ5çÂ"¢
+“óbÀ0[kLo³ï¶ä·èpÍ­U˺ÝSŠ®äšC;¸œŽ ”æ8 ÜhØóŽsF_šl¾ÀeþØ2Ã}ɩ挞È-ý!Dg4*IDO;Ûê!Ð~)>m‰í ä®”¨Ï~*ùD‰ \ No newline at end of file
diff --git a/tests/resources/renames/.gitted/objects/5e/26abc56a5a84d89790f45416648899cbe13109 b/tests/resources/renames/.gitted/objects/5e/26abc56a5a84d89790f45416648899cbe13109
new file mode 100644
index 000000000..2acd3d583
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/5e/26abc56a5a84d89790f45416648899cbe13109
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/61/8c6f2f8740bd6049b2fb9eb93fc15726462745 b/tests/resources/renames/.gitted/objects/61/8c6f2f8740bd6049b2fb9eb93fc15726462745
new file mode 100644
index 000000000..24eac54c5
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/61/8c6f2f8740bd6049b2fb9eb93fc15726462745
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/66/311f5cfbe7836c27510a3ba2f43e282e2c8bba b/tests/resources/renames/.gitted/objects/66/311f5cfbe7836c27510a3ba2f43e282e2c8bba
new file mode 100644
index 000000000..5ee28a76a
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/66/311f5cfbe7836c27510a3ba2f43e282e2c8bba
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/93/f538c45a57a87eb4c1e86f91c6ee41d66c7ba7 b/tests/resources/renames/.gitted/objects/93/f538c45a57a87eb4c1e86f91c6ee41d66c7ba7
new file mode 100644
index 000000000..39105f94d
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/93/f538c45a57a87eb4c1e86f91c6ee41d66c7ba7
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/9a/69d960ae94b060f56c2a8702545e2bb1abb935 b/tests/resources/renames/.gitted/objects/9a/69d960ae94b060f56c2a8702545e2bb1abb935
new file mode 100644
index 000000000..f75178c59
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/9a/69d960ae94b060f56c2a8702545e2bb1abb935
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/ad/0a8e55a104ac54a8a29ed4b84b49e76837a113 b/tests/resources/renames/.gitted/objects/ad/0a8e55a104ac54a8a29ed4b84b49e76837a113
new file mode 100644
index 000000000..440b7bec3
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/ad/0a8e55a104ac54a8a29ed4b84b49e76837a113
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/b9/25b224cc91f897001a9993fbce169fdaa8858f b/tests/resources/renames/.gitted/objects/b9/25b224cc91f897001a9993fbce169fdaa8858f
new file mode 100644
index 000000000..90e107fa2
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/b9/25b224cc91f897001a9993fbce169fdaa8858f
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/d7/9b202de198fa61b02424b9e25e840dc75e1323 b/tests/resources/renames/.gitted/objects/d7/9b202de198fa61b02424b9e25e840dc75e1323
new file mode 100644
index 000000000..daa2b3997
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/d7/9b202de198fa61b02424b9e25e840dc75e1323
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/ea/c43f5195a2cee53b7458d8dad16aedde10711b b/tests/resources/renames/.gitted/objects/ea/c43f5195a2cee53b7458d8dad16aedde10711b
new file mode 100644
index 000000000..2fb025080
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/ea/c43f5195a2cee53b7458d8dad16aedde10711b
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/ea/f4a3e3bfe68585e90cada20736ace491cd100b b/tests/resources/renames/.gitted/objects/ea/f4a3e3bfe68585e90cada20736ace491cd100b
new file mode 100644
index 000000000..f72df8d82
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/ea/f4a3e3bfe68585e90cada20736ace491cd100b
@@ -0,0 +1,5 @@
+x}RÁªÔ@ô<_QžTÈ.¼“ÂâAáÉ[–‡â
+{ž¼t2C’žez’˜›áú%öLžº'!aº»ª«jê!Ôx{÷îÅ'¢+Îþ;“$œ)Ξ»Ý#±yÿ¿Ç˜³#q#¯h„­c° ÃQDX¶m­R|ŠaD*ÝOˆþ†+±”^ZI~ýøi>3aôÃàã!,R!-áïÉEaIÁ>äyš‰o*«¼4æˆRf¡ m&e¯ IAÑú™ò*!â;¢ždÍݬ‚…´Å
+êH¶o­¤
+ÃÄO®‚U¾DöyTVØHpwqÅH¼7§„Æ·­.ÈʆÎÎts6{Zä +öX\)Šª”ÑC–žì5QªÂä9
+%©ÌÅt*Ã&Ï&è°êv;|šÕÆ'4½ìÆéþþ Dƒu[°7h¯¿e!ÉNK*"C©-=Óòæ`´æ#ØŽ$EëÅe2õáâT|ùêå@NBsús¢¦lµ°Wö|/¶0¬÷aÈ¥üJ±ò¶Nêv)-šÚ¡˜iÛ¤3ÅëbäbO:uWMâˆNÓÜàóæ¶X²7¿Tóº \ No newline at end of file
diff --git a/tests/resources/renames/.gitted/objects/f9/0d4fc20ecddf21eebe6a37e9225d244339d2b5 b/tests/resources/renames/.gitted/objects/f9/0d4fc20ecddf21eebe6a37e9225d244339d2b5
new file mode 100644
index 000000000..f6d933be9
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/f9/0d4fc20ecddf21eebe6a37e9225d244339d2b5
Binary files differ
diff --git a/tests/resources/renames/.gitted/refs/heads/master b/tests/resources/renames/.gitted/refs/heads/master
new file mode 100644
index 000000000..642c3198d
--- /dev/null
+++ b/tests/resources/renames/.gitted/refs/heads/master
@@ -0,0 +1 @@
+19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13
diff --git a/tests/resources/renames/.gitted/refs/heads/renames_similar b/tests/resources/renames/.gitted/refs/heads/renames_similar
new file mode 100644
index 000000000..8ffb6d94b
--- /dev/null
+++ b/tests/resources/renames/.gitted/refs/heads/renames_similar
@@ -0,0 +1 @@
+444a76ed3e45b183753f49376af30da8c3fe276a
diff --git a/tests/resources/renames/.gitted/refs/heads/renames_similar_two b/tests/resources/renames/.gitted/refs/heads/renames_similar_two
new file mode 100644
index 000000000..4ee5d04e1
--- /dev/null
+++ b/tests/resources/renames/.gitted/refs/heads/renames_similar_two
@@ -0,0 +1 @@
+50e90273af7d826ff0a95865bcd3ba8412c447d9
diff --git a/tests/resources/renames/ikeepsix.txt b/tests/resources/renames/ikeepsix.txt
new file mode 100644
index 000000000..eaf4a3e3b
--- /dev/null
+++ b/tests/resources/renames/ikeepsix.txt
@@ -0,0 +1,27 @@
+I Keep Six Honest Serving-Men
+=============================
+
+She sends'em abroad on her own affairs,
+ From the second she opens her eyes—
+One million Hows, two million Wheres,
+And seven million Whys!
+
+I let them rest from nine till five,
+ For I am busy then,
+As well as breakfast, lunch, and tea,
+ For they are hungry men.
+But different folk have different views;
+I know a person small—
+She keeps ten million serving-men,
+Who get no rest at all!
+
+ -- Rudyard Kipling
+
+I KEEP six honest serving-men
+ (They taught me all I knew);
+Their names are What and Why and When
+ And How and Where and Who.
+I send them over land and sea,
+ I send them east and west;
+But after they have worked for me,
+ I give them all a rest.
diff --git a/tests/resources/renames/sixserving.txt b/tests/resources/renames/sixserving.txt
new file mode 100644
index 000000000..f90d4fc20
--- /dev/null
+++ b/tests/resources/renames/sixserving.txt
@@ -0,0 +1,25 @@
+I KEEP six honest serving-men
+ (They taught me all I knew);
+Their names are What and Why and When
+ And How and Where and Who.
+I send them over land and sea,
+ I send them east and west;
+But after they have worked for me,
+ I give them all a rest.
+
+I let them rest from nine till five,
+ For I am busy then,
+As well as breakfast, lunch, and tea,
+ For they are hungry men.
+But different folk have different views;
+I know a person small—
+She keeps ten million serving-men,
+Who get no rest at all!
+
+She sends'em abroad on her own affairs,
+ From the second she opens her eyes—
+One million Hows, two million Wheres,
+And seven million Whys!
+
+ -- Rudyard Kipling
+
diff --git a/tests/resources/renames/songof7cities.txt b/tests/resources/renames/songof7cities.txt
new file mode 100644
index 000000000..4210ffd5c
--- /dev/null
+++ b/tests/resources/renames/songof7cities.txt
@@ -0,0 +1,49 @@
+The Song of Seven Cities
+------------------------
+
+I WAS Lord of Cities very sumptuously builded.
+Seven roaring Cities paid me tribute from afar.
+Ivory their outposts were--the guardrooms of them gilded,
+And garrisoned with Amazons invincible in war.
+
+All the world went softly when it walked before my Cities--
+Neither King nor Army vexed my peoples at their toil,
+Never horse nor chariot irked or overbore my Cities,
+Never Mob nor Ruler questioned whence they drew their spoil.
+
+Banded, mailed and arrogant from sunrise unto sunset;
+Singing while they sacked it, they possessed the land at large.
+Yet when men would rob them, they resisted, they made onset
+And pierced the smoke of battle with a thousand-sabred charge.
+
+So they warred and trafficked only yesterday, my Cities.
+To-day there is no mark or mound of where my Cities stood.
+For the River rose at midnight and it washed away my Cities.
+They are evened with Atlantis and the towns before the Flood.
+
+Rain on rain-gorged channels raised the water-levels round them,
+Freshet backed on freshet swelled and swept their world from sight,
+Till the emboldened floods linked arms and, flashing forward, drowned them--
+Drowned my Seven Cities and their peoples in one night!
+
+Low among the alders lie their derelict foundations,
+The beams wherein they trusted and the plinths whereon they built--
+My rulers and their treasure and their unborn populations,
+Dead, destroyed, aborted, and defiled with mud and silt!
+
+The Daughters of the Palace whom they cherished in my Cities,
+My silver-tongued Princesses, and the promise of their May--
+Their bridegrooms of the June-tide--all have perished in my Cities,
+With the harsh envenomed virgins that can neither love nor play.
+
+I was Lord of Cities--I will build anew my Cities,
+Seven, set on rocks, above the wrath of any flood.
+Nor will I rest from search till I have filled anew my Cities
+With peoples undefeated of the dark, enduring blood.
+
+To the sound of trumpets shall their seed restore my Cities
+Wealthy and well-weaponed, that once more may I behold
+All the world go softly when it walks before my Cities,
+And the horses and the chariots fleeing from them as of old!
+
+ -- Rudyard Kipling
diff --git a/tests/resources/renames/untimely.txt b/tests/resources/renames/untimely.txt
new file mode 100644
index 000000000..9a69d960a
--- /dev/null
+++ b/tests/resources/renames/untimely.txt
@@ -0,0 +1,24 @@
+Untimely
+========
+
+Nothing in life has been made by man for man's using
+But it was shown long since to man in ages
+Lost as the name of the maker of it,
+Who received oppression and shame for his wages--
+Hate, avoidance, and scorn in his daily dealings--
+Until he perished, wholly confounded
+
+More to be pitied than he are the wise
+Souls which foresaw the evil of loosing
+Knowledge or Art before time, and aborted
+Noble devices and deep-wrought healings,
+Lest offense should arise.
+
+Heaven delivers on earth the Hour that cannot be
+ thwarted,
+Neither advanced, at the price of a world nor a soul,
+ and its Prophet
+Comes through the blood of the vanguards who
+ dreamed--too soon--it had sounded.
+
+ -- Rudyard Kipling
diff --git a/tests/resources/shallow.git/HEAD b/tests/resources/shallow.git/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/shallow.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/shallow.git/config b/tests/resources/shallow.git/config
new file mode 100644
index 000000000..a88b74b69
--- /dev/null
+++ b/tests/resources/shallow.git/config
@@ -0,0 +1,8 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = true
+ ignorecase = true
+ precomposeunicode = false
+[remote "origin"]
+ url = file://testrepo.git
diff --git a/tests/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.idx b/tests/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.idx
new file mode 100644
index 000000000..bfc7d24ff
--- /dev/null
+++ b/tests/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.idx
Binary files differ
diff --git a/tests/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.pack b/tests/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.pack
new file mode 100644
index 000000000..ccc6932fc
--- /dev/null
+++ b/tests/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.pack
Binary files differ
diff --git a/tests/resources/shallow.git/packed-refs b/tests/resources/shallow.git/packed-refs
new file mode 100644
index 000000000..97eed743b
--- /dev/null
+++ b/tests/resources/shallow.git/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750 refs/heads/master
diff --git a/tests/resources/shallow.git/refs/.gitkeep b/tests/resources/shallow.git/refs/.gitkeep
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/resources/shallow.git/refs/.gitkeep
diff --git a/tests/resources/shallow.git/shallow b/tests/resources/shallow.git/shallow
new file mode 100644
index 000000000..9536ad89c
--- /dev/null
+++ b/tests/resources/shallow.git/shallow
@@ -0,0 +1 @@
+be3563ae3f795b2b4353bcce3a527ad0a4f7f644
diff --git a/tests/resources/short_tag.git/HEAD b/tests/resources/short_tag.git/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/short_tag.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/short_tag.git/config b/tests/resources/short_tag.git/config
new file mode 100644
index 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/resources/short_tag.git/index b/tests/resources/short_tag.git/index
new file mode 100644
index 000000000..87fef7847
--- /dev/null
+++ b/tests/resources/short_tag.git/index
Binary files differ
diff --git a/tests/resources/short_tag.git/objects/4a/5ed60bafcf4638b7c8356bd4ce1916bfede93c b/tests/resources/short_tag.git/objects/4a/5ed60bafcf4638b7c8356bd4ce1916bfede93c
new file mode 100644
index 000000000..aeb4e4b0b
--- /dev/null
+++ b/tests/resources/short_tag.git/objects/4a/5ed60bafcf4638b7c8356bd4ce1916bfede93c
Binary files differ
diff --git a/tests/resources/short_tag.git/objects/4d/5fcadc293a348e88f777dc0920f11e7d71441c b/tests/resources/short_tag.git/objects/4d/5fcadc293a348e88f777dc0920f11e7d71441c
new file mode 100644
index 000000000..806ce71a5
--- /dev/null
+++ b/tests/resources/short_tag.git/objects/4d/5fcadc293a348e88f777dc0920f11e7d71441c
Binary files differ
diff --git a/tests/resources/short_tag.git/objects/5d/a7760512a953e3c7c4e47e4392c7a4338fb729 b/tests/resources/short_tag.git/objects/5d/a7760512a953e3c7c4e47e4392c7a4338fb729
new file mode 100644
index 000000000..1192707c9
--- /dev/null
+++ b/tests/resources/short_tag.git/objects/5d/a7760512a953e3c7c4e47e4392c7a4338fb729
@@ -0,0 +1 @@
+xÌM‚0@aלb. ií%1Æ—pcÚé@Š”˜˜èé-Ëï-û¤6§&Bí E+‚pÐV¹Ð¡SƆ¨‘d/m(R¯°áïJ€%çÄ ×ÇR^‘vÜÒÊ©,GiƒÇ–Þðñ <Ó´3\©º­n‡ïcöinëåRé„SögÑ Ñüëu1 \ No newline at end of file
diff --git a/tests/resources/short_tag.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/short_tag.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644
index 000000000..711223894
--- /dev/null
+++ b/tests/resources/short_tag.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests/resources/short_tag.git/packed-refs b/tests/resources/short_tag.git/packed-refs
new file mode 100644
index 000000000..ca5197e3c
--- /dev/null
+++ b/tests/resources/short_tag.git/packed-refs
@@ -0,0 +1 @@
+5da7760512a953e3c7c4e47e4392c7a4338fb729 refs/tags/no_description
diff --git a/tests/resources/short_tag.git/refs/heads/master b/tests/resources/short_tag.git/refs/heads/master
new file mode 100644
index 000000000..fcefd1ef0
--- /dev/null
+++ b/tests/resources/short_tag.git/refs/heads/master
@@ -0,0 +1 @@
+4a5ed60bafcf4638b7c8356bd4ce1916bfede93c
diff --git a/tests/resources/status/.gitted/COMMIT_EDITMSG b/tests/resources/status/.gitted/COMMIT_EDITMSG
new file mode 100644
index 000000000..1a25cd4a6
--- /dev/null
+++ b/tests/resources/status/.gitted/COMMIT_EDITMSG
@@ -0,0 +1 @@
+Add a file which name should appear before the "subdir/" folder while being dealt with by the treewalker
diff --git a/tests/resources/status/.gitted/HEAD b/tests/resources/status/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/status/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/status/.gitted/ORIG_HEAD b/tests/resources/status/.gitted/ORIG_HEAD
new file mode 100644
index 000000000..b46871fd6
--- /dev/null
+++ b/tests/resources/status/.gitted/ORIG_HEAD
@@ -0,0 +1 @@
+735b6a258cd196a8f7c9428419b02c1dca93fd75
diff --git a/tests/resources/status/.gitted/config b/tests/resources/status/.gitted/config
new file mode 100644
index 000000000..af107929f
--- /dev/null
+++ b/tests/resources/status/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests/resources/status/.gitted/description b/tests/resources/status/.gitted/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/status/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/status/.gitted/index b/tests/resources/status/.gitted/index
new file mode 100644
index 000000000..2af99a183
--- /dev/null
+++ b/tests/resources/status/.gitted/index
Binary files differ
diff --git a/tests/resources/status/.gitted/info/exclude b/tests/resources/status/.gitted/info/exclude
new file mode 100644
index 000000000..0c4042a6a
--- /dev/null
+++ b/tests/resources/status/.gitted/info/exclude
@@ -0,0 +1,8 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
+ignored*
+
diff --git a/tests/resources/status/.gitted/logs/HEAD b/tests/resources/status/.gitted/logs/HEAD
new file mode 100644
index 000000000..7b95b3cf1
--- /dev/null
+++ b/tests/resources/status/.gitted/logs/HEAD
@@ -0,0 +1,3 @@
+0000000000000000000000000000000000000000 0017bd4ab1ec30440b17bae1680cff124ab5f1f6 Jason Penny <jasonpenny4@gmail.com> 1308050070 -0400 commit (initial): initial
+0017bd4ab1ec30440b17bae1680cff124ab5f1f6 735b6a258cd196a8f7c9428419b02c1dca93fd75 Jason Penny <jasonpenny4@gmail.com> 1308954538 -0400 commit: add subdir
+735b6a258cd196a8f7c9428419b02c1dca93fd75 26a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f nulltoken <emeric.fermas@gmail.com> 1319911544 +0200 commit: Add a file which name should appear before the "subdir/" folder while being dealt with by the treewalker
diff --git a/tests/resources/status/.gitted/logs/refs/heads/master b/tests/resources/status/.gitted/logs/refs/heads/master
new file mode 100644
index 000000000..7b95b3cf1
--- /dev/null
+++ b/tests/resources/status/.gitted/logs/refs/heads/master
@@ -0,0 +1,3 @@
+0000000000000000000000000000000000000000 0017bd4ab1ec30440b17bae1680cff124ab5f1f6 Jason Penny <jasonpenny4@gmail.com> 1308050070 -0400 commit (initial): initial
+0017bd4ab1ec30440b17bae1680cff124ab5f1f6 735b6a258cd196a8f7c9428419b02c1dca93fd75 Jason Penny <jasonpenny4@gmail.com> 1308954538 -0400 commit: add subdir
+735b6a258cd196a8f7c9428419b02c1dca93fd75 26a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f nulltoken <emeric.fermas@gmail.com> 1319911544 +0200 commit: Add a file which name should appear before the "subdir/" folder while being dealt with by the treewalker
diff --git a/tests/resources/status/.gitted/objects/00/17bd4ab1ec30440b17bae1680cff124ab5f1f6 b/tests/resources/status/.gitted/objects/00/17bd4ab1ec30440b17bae1680cff124ab5f1f6
new file mode 100644
index 000000000..b256d95a3
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/00/17bd4ab1ec30440b17bae1680cff124ab5f1f6
@@ -0,0 +1,2 @@
+xA E]sŠ¹€fh)‰1]»ò
+#SÀTºðö¶Wp÷ßK^~¨9§šÜ¡-"àC'Ø…)FvõbƒvÉ Þ"¶wŽŽ¼EÅk{Ö®ü©nRÊίÞû6ã#sšO¡æ 舄pDƒ¨6»6ù3W©¤–xV?¨Å9é \ No newline at end of file
diff --git a/tests/resources/status/.gitted/objects/06/1d42a44cacde5726057b67558821d95db96f19 b/tests/resources/status/.gitted/objects/06/1d42a44cacde5726057b67558821d95db96f19
new file mode 100644
index 000000000..82e02cb0e
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/06/1d42a44cacde5726057b67558821d95db96f19
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/18/88c805345ba265b0ee9449b8877b6064592058 b/tests/resources/status/.gitted/objects/18/88c805345ba265b0ee9449b8877b6064592058
new file mode 100644
index 000000000..e3cad2f02
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/18/88c805345ba265b0ee9449b8877b6064592058
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/19/d9cc8584ac2c7dcf57d2680375e80f099dc481 b/tests/resources/status/.gitted/objects/19/d9cc8584ac2c7dcf57d2680375e80f099dc481
new file mode 100644
index 000000000..2d5e711b9
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/19/d9cc8584ac2c7dcf57d2680375e80f099dc481
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/26/a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f b/tests/resources/status/.gitted/objects/26/a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f
new file mode 100644
index 000000000..f7dddc4ff
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/26/a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f
@@ -0,0 +1,2 @@
+xMnÄ …»Î)¬ÙVšò† ªö(Ì€BDˆ¢Þ¾LÐöûüžž«¥¤RÈ·Þˆ@êà,Î9Ž‹òÜÌœtàìæ•ðNj6f`žM6Z;h©ì …ZÜÐÞp Ú™Y,37¯/Ü;42x­&<z¬ Ö#ç^Ÿ´Â
+µä®ZÁýëQ0å««å¸äÆp®æÞ™`lÛ²Ó?ñéÛ{@)œ1¹+‚=Ö#ö6°j#è‘à²Ö§öqP³®¨¥´>ÀægêìÏŸüÕÛ‰ùImú|½jñ \ No newline at end of file
diff --git a/tests/resources/status/.gitted/objects/32/504b727382542f9f089e24fddac5e78533e96c b/tests/resources/status/.gitted/objects/32/504b727382542f9f089e24fddac5e78533e96c
new file mode 100644
index 000000000..7fca67be8
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/32/504b727382542f9f089e24fddac5e78533e96c
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/37/fcb02ccc1a85d1941e7f106d52dc3702dcf0d0 b/tests/resources/status/.gitted/objects/37/fcb02ccc1a85d1941e7f106d52dc3702dcf0d0
new file mode 100644
index 000000000..b75481b51
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/37/fcb02ccc1a85d1941e7f106d52dc3702dcf0d0
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/45/2e4244b5d083ddf0460acf1ecc74db9dcfa11a b/tests/resources/status/.gitted/objects/45/2e4244b5d083ddf0460acf1ecc74db9dcfa11a
new file mode 100644
index 000000000..5b47461e9
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/45/2e4244b5d083ddf0460acf1ecc74db9dcfa11a
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/52/9a16e8e762d4acb7b9636ff540a00831f9155a b/tests/resources/status/.gitted/objects/52/9a16e8e762d4acb7b9636ff540a00831f9155a
new file mode 100644
index 000000000..615009ad0
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/52/9a16e8e762d4acb7b9636ff540a00831f9155a
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/53/ace0d1cc1145a5f4fe4f78a186a60263190733 b/tests/resources/status/.gitted/objects/53/ace0d1cc1145a5f4fe4f78a186a60263190733
new file mode 100644
index 000000000..cdb7e961a
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/53/ace0d1cc1145a5f4fe4f78a186a60263190733
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/54/52d32f1dd538eb0405e8a83cc185f79e25e80f b/tests/resources/status/.gitted/objects/54/52d32f1dd538eb0405e8a83cc185f79e25e80f
new file mode 100644
index 000000000..a72dff646
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/54/52d32f1dd538eb0405e8a83cc185f79e25e80f
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/55/d316c9ba708999f1918e9677d01dfcae69c6b9 b/tests/resources/status/.gitted/objects/55/d316c9ba708999f1918e9677d01dfcae69c6b9
new file mode 100644
index 000000000..72807f3d0
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/55/d316c9ba708999f1918e9677d01dfcae69c6b9
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/70/bd9443ada07063e7fbf0b3ff5c13f7494d89c2 b/tests/resources/status/.gitted/objects/70/bd9443ada07063e7fbf0b3ff5c13f7494d89c2
new file mode 100644
index 000000000..3665a8f7c
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/70/bd9443ada07063e7fbf0b3ff5c13f7494d89c2
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/73/5b6a258cd196a8f7c9428419b02c1dca93fd75 b/tests/resources/status/.gitted/objects/73/5b6a258cd196a8f7c9428419b02c1dca93fd75
new file mode 100644
index 000000000..08e6fd246
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/73/5b6a258cd196a8f7c9428419b02c1dca93fd75
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/75/6e27627e67bfbc048d01ece5819c6de733d7ea b/tests/resources/status/.gitted/objects/75/6e27627e67bfbc048d01ece5819c6de733d7ea
new file mode 100644
index 000000000..8f3fa89e5
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/75/6e27627e67bfbc048d01ece5819c6de733d7ea
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/90/6ee7711f4f4928ddcb2a5f8fbc500deba0d2a8 b/tests/resources/status/.gitted/objects/90/6ee7711f4f4928ddcb2a5f8fbc500deba0d2a8
new file mode 100644
index 000000000..bb732b08e
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/90/6ee7711f4f4928ddcb2a5f8fbc500deba0d2a8
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/90/b8c29d8ba39434d1c63e1b093daaa26e5bd972 b/tests/resources/status/.gitted/objects/90/b8c29d8ba39434d1c63e1b093daaa26e5bd972
new file mode 100644
index 000000000..7a96618ff
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/90/b8c29d8ba39434d1c63e1b093daaa26e5bd972
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/9c/2e02cdffa8d73e6c189074594477a6baf87960 b/tests/resources/status/.gitted/objects/9c/2e02cdffa8d73e6c189074594477a6baf87960
new file mode 100644
index 000000000..20a3c497e
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/9c/2e02cdffa8d73e6c189074594477a6baf87960
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/a0/de7e0ac200c489c41c59dfa910154a70264e6e b/tests/resources/status/.gitted/objects/a0/de7e0ac200c489c41c59dfa910154a70264e6e
new file mode 100644
index 000000000..a1789c9a6
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/a0/de7e0ac200c489c41c59dfa910154a70264e6e
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/a6/191982709b746d5650e93c2acf34ef74e11504 b/tests/resources/status/.gitted/objects/a6/191982709b746d5650e93c2acf34ef74e11504
new file mode 100644
index 000000000..cc1f377b3
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/a6/191982709b746d5650e93c2acf34ef74e11504
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/a6/be623522ce87a1d862128ac42672604f7b468b b/tests/resources/status/.gitted/objects/a6/be623522ce87a1d862128ac42672604f7b468b
new file mode 100644
index 000000000..c47298347
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/a6/be623522ce87a1d862128ac42672604f7b468b
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/aa/27a641456848200fdb7f7c99ba36f8a0952877 b/tests/resources/status/.gitted/objects/aa/27a641456848200fdb7f7c99ba36f8a0952877
new file mode 100644
index 000000000..a4669ccbb
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/aa/27a641456848200fdb7f7c99ba36f8a0952877
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/da/bc8af9bd6e9f5bbe96a176f1a24baf3d1f8916 b/tests/resources/status/.gitted/objects/da/bc8af9bd6e9f5bbe96a176f1a24baf3d1f8916
new file mode 100644
index 000000000..3e3c03c96
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/da/bc8af9bd6e9f5bbe96a176f1a24baf3d1f8916
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/e8/ee89e15bbe9b20137715232387b3de5b28972e b/tests/resources/status/.gitted/objects/e8/ee89e15bbe9b20137715232387b3de5b28972e
new file mode 100644
index 000000000..cfc2413d5
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/e8/ee89e15bbe9b20137715232387b3de5b28972e
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/e9/b9107f290627c04d097733a10055af941f6bca b/tests/resources/status/.gitted/objects/e9/b9107f290627c04d097733a10055af941f6bca
new file mode 100644
index 000000000..1266d3eac
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/e9/b9107f290627c04d097733a10055af941f6bca
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/ed/062903b8f6f3dccb2fa81117ba6590944ef9bd b/tests/resources/status/.gitted/objects/ed/062903b8f6f3dccb2fa81117ba6590944ef9bd
new file mode 100644
index 000000000..8fa8c1707
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/ed/062903b8f6f3dccb2fa81117ba6590944ef9bd
Binary files differ
diff --git a/tests/resources/status/.gitted/refs/heads/master b/tests/resources/status/.gitted/refs/heads/master
new file mode 100644
index 000000000..3e2e2a07a
--- /dev/null
+++ b/tests/resources/status/.gitted/refs/heads/master
@@ -0,0 +1 @@
+26a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f
diff --git a/tests/resources/status/current_file b/tests/resources/status/current_file
new file mode 100644
index 000000000..a0de7e0ac
--- /dev/null
+++ b/tests/resources/status/current_file
@@ -0,0 +1 @@
+current_file
diff --git a/tests/resources/status/ignored_file b/tests/resources/status/ignored_file
new file mode 100644
index 000000000..6a79f808a
--- /dev/null
+++ b/tests/resources/status/ignored_file
@@ -0,0 +1 @@
+ignored_file
diff --git a/tests/resources/status/modified_file b/tests/resources/status/modified_file
new file mode 100644
index 000000000..0a5396305
--- /dev/null
+++ b/tests/resources/status/modified_file
@@ -0,0 +1,2 @@
+modified_file
+modified_file
diff --git a/tests/resources/status/new_file b/tests/resources/status/new_file
new file mode 100644
index 000000000..d4fa8600b
--- /dev/null
+++ b/tests/resources/status/new_file
@@ -0,0 +1 @@
+new_file
diff --git a/tests/resources/status/staged_changes b/tests/resources/status/staged_changes
new file mode 100644
index 000000000..55d316c9b
--- /dev/null
+++ b/tests/resources/status/staged_changes
@@ -0,0 +1,2 @@
+staged_changes
+staged_changes
diff --git a/tests/resources/status/staged_changes_modified_file b/tests/resources/status/staged_changes_modified_file
new file mode 100644
index 000000000..011c3440d
--- /dev/null
+++ b/tests/resources/status/staged_changes_modified_file
@@ -0,0 +1,3 @@
+staged_changes_modified_file
+staged_changes_modified_file
+staged_changes_modified_file
diff --git a/tests/resources/status/staged_delete_modified_file b/tests/resources/status/staged_delete_modified_file
new file mode 100644
index 000000000..dabc8af9b
--- /dev/null
+++ b/tests/resources/status/staged_delete_modified_file
@@ -0,0 +1 @@
+staged_delete_modified_file
diff --git a/tests/resources/status/staged_new_file b/tests/resources/status/staged_new_file
new file mode 100644
index 000000000..529a16e8e
--- /dev/null
+++ b/tests/resources/status/staged_new_file
@@ -0,0 +1 @@
+staged_new_file
diff --git a/tests/resources/status/staged_new_file_modified_file b/tests/resources/status/staged_new_file_modified_file
new file mode 100644
index 000000000..8b090c06d
--- /dev/null
+++ b/tests/resources/status/staged_new_file_modified_file
@@ -0,0 +1,2 @@
+staged_new_file_modified_file
+staged_new_file_modified_file
diff --git a/tests/resources/status/subdir.txt b/tests/resources/status/subdir.txt
new file mode 100644
index 000000000..e8ee89e15
--- /dev/null
+++ b/tests/resources/status/subdir.txt
@@ -0,0 +1,2 @@
+Is it a bird?
+Is it a plane?
diff --git a/tests/resources/status/subdir/current_file b/tests/resources/status/subdir/current_file
new file mode 100644
index 000000000..53ace0d1c
--- /dev/null
+++ b/tests/resources/status/subdir/current_file
@@ -0,0 +1 @@
+subdir/current_file
diff --git a/tests/resources/status/subdir/modified_file b/tests/resources/status/subdir/modified_file
new file mode 100644
index 000000000..57274b75e
--- /dev/null
+++ b/tests/resources/status/subdir/modified_file
@@ -0,0 +1,2 @@
+subdir/modified_file
+subdir/modified_file
diff --git a/tests/resources/status/subdir/new_file b/tests/resources/status/subdir/new_file
new file mode 100644
index 000000000..80a86a693
--- /dev/null
+++ b/tests/resources/status/subdir/new_file
@@ -0,0 +1 @@
+subdir/new_file
diff --git a/tests/resources/status/è¿™ b/tests/resources/status/è¿™
new file mode 100644
index 000000000..f0ff9a197
--- /dev/null
+++ b/tests/resources/status/è¿™
@@ -0,0 +1 @@
+This
diff --git a/tests/resources/submod2/.gitted/HEAD b/tests/resources/submod2/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/submod2/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod2/.gitted/config b/tests/resources/submod2/.gitted/config
new file mode 100644
index 000000000..abc420734
--- /dev/null
+++ b/tests/resources/submod2/.gitted/config
@@ -0,0 +1,20 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
+[submodule "sm_missing_commits"]
+ url = ../submod2_target
+[submodule "sm_unchanged"]
+ url = ../submod2_target
+[submodule "sm_changed_file"]
+ url = ../submod2_target
+[submodule "sm_changed_index"]
+ url = ../submod2_target
+[submodule "sm_changed_head"]
+ url = ../submod2_target
+[submodule "sm_changed_untracked_file"]
+ url = ../submod2_target
+[submodule "sm_added_and_uncommited"]
+ url = ../submod2_target
diff --git a/tests/resources/submod2/.gitted/description b/tests/resources/submod2/.gitted/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/submod2/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/submod2/.gitted/index b/tests/resources/submod2/.gitted/index
new file mode 100644
index 000000000..0c17e8629
--- /dev/null
+++ b/tests/resources/submod2/.gitted/index
Binary files differ
diff --git a/tests/resources/submod2/.gitted/info/exclude b/tests/resources/submod2/.gitted/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/submod2/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/submod2/.gitted/logs/HEAD b/tests/resources/submod2/.gitted/logs/HEAD
new file mode 100644
index 000000000..2cf2ca74d
--- /dev/null
+++ b/tests/resources/submod2/.gitted/logs/HEAD
@@ -0,0 +1,4 @@
+0000000000000000000000000000000000000000 14fe9ccf104058df25e0a08361c4494e167ef243 Russell Belfer <rb@github.com> 1342559771 -0700 commit (initial): Initial commit
+14fe9ccf104058df25e0a08361c4494e167ef243 a9104bf89e911387244ef499413960ba472066d9 Russell Belfer <rb@github.com> 1342559831 -0700 commit: Adding a submodule
+a9104bf89e911387244ef499413960ba472066d9 5901da4f1c67756eeadc5121d206bec2431f253b Russell Belfer <rb@github.com> 1342560036 -0700 commit: Updating submodule
+5901da4f1c67756eeadc5121d206bec2431f253b 7484482eb8db738cafa696993664607500a3f2b9 Russell Belfer <rb@github.com> 1342560288 -0700 commit: Adding a bunch more test content
diff --git a/tests/resources/submod2/.gitted/logs/refs/heads/master b/tests/resources/submod2/.gitted/logs/refs/heads/master
new file mode 100644
index 000000000..2cf2ca74d
--- /dev/null
+++ b/tests/resources/submod2/.gitted/logs/refs/heads/master
@@ -0,0 +1,4 @@
+0000000000000000000000000000000000000000 14fe9ccf104058df25e0a08361c4494e167ef243 Russell Belfer <rb@github.com> 1342559771 -0700 commit (initial): Initial commit
+14fe9ccf104058df25e0a08361c4494e167ef243 a9104bf89e911387244ef499413960ba472066d9 Russell Belfer <rb@github.com> 1342559831 -0700 commit: Adding a submodule
+a9104bf89e911387244ef499413960ba472066d9 5901da4f1c67756eeadc5121d206bec2431f253b Russell Belfer <rb@github.com> 1342560036 -0700 commit: Updating submodule
+5901da4f1c67756eeadc5121d206bec2431f253b 7484482eb8db738cafa696993664607500a3f2b9 Russell Belfer <rb@github.com> 1342560288 -0700 commit: Adding a bunch more test content
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/HEAD b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/config b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/config
new file mode 100644
index 000000000..2d0583e99
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/config
@@ -0,0 +1,13 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../sm_added_and_uncommited
+ ignorecase = true
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/description b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/index b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/index
new file mode 100644
index 000000000..65140a510
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/index
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/info/exclude b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/HEAD b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/HEAD
new file mode 100644
index 000000000..53753e7dd
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560316 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/heads/master
new file mode 100644
index 000000000..53753e7dd
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560316 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/remotes/origin/HEAD
new file mode 100644
index 000000000..53753e7dd
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560316 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 000000000..f4b7094c5
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 000000000..56c845e49
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 000000000..bd179b5f5
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 000000000..ccf49bd15
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 000000000..53029069a
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 000000000..38c791eba
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 000000000..a26d29993
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 000000000..83d1ba481
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 000000000..6d27af8a8
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 000000000..17458840b
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 000000000..83cc29fb1
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 000000000..55bda40ef
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/packed-refs b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/packed-refs
new file mode 100644
index 000000000..5a4ebc47c
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/heads/master
new file mode 100644
index 000000000..e12c44d7a
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/remotes/origin/HEAD
new file mode 100644
index 000000000..6efe28fff
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_file/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/config b/tests/resources/submod2/.gitted/modules/sm_changed_file/config
new file mode 100644
index 000000000..10cc2508e
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/config
@@ -0,0 +1,13 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../sm_changed_file
+ ignorecase = true
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/description b/tests/resources/submod2/.gitted/modules/sm_changed_file/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/index b/tests/resources/submod2/.gitted/modules/sm_changed_file/index
new file mode 100644
index 000000000..6914a3b6e
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/index
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/info/exclude b/tests/resources/submod2/.gitted/modules/sm_changed_file/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/logs/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_file/logs/HEAD
new file mode 100644
index 000000000..e5cb63f8d
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560173 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/heads/master
new file mode 100644
index 000000000..e5cb63f8d
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560173 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/remotes/origin/HEAD
new file mode 100644
index 000000000..e5cb63f8d
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560173 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 000000000..f4b7094c5
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 000000000..56c845e49
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 000000000..bd179b5f5
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 000000000..ccf49bd15
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 000000000..53029069a
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 000000000..38c791eba
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 000000000..a26d29993
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 000000000..83d1ba481
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 000000000..6d27af8a8
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 000000000..17458840b
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 000000000..83cc29fb1
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 000000000..55bda40ef
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/packed-refs b/tests/resources/submod2/.gitted/modules/sm_changed_file/packed-refs
new file mode 100644
index 000000000..5a4ebc47c
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_file/refs/heads/master
new file mode 100644
index 000000000..e12c44d7a
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_file/refs/remotes/origin/HEAD
new file mode 100644
index 000000000..6efe28fff
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/COMMIT_EDITMSG b/tests/resources/submod2/.gitted/modules/sm_changed_head/COMMIT_EDITMSG
new file mode 100644
index 000000000..6b8d1e3fc
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/COMMIT_EDITMSG
@@ -0,0 +1 @@
+Making a change in a submodule
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_head/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/config b/tests/resources/submod2/.gitted/modules/sm_changed_head/config
new file mode 100644
index 000000000..7d002536a
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/config
@@ -0,0 +1,13 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../sm_changed_head
+ ignorecase = true
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/description b/tests/resources/submod2/.gitted/modules/sm_changed_head/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/index b/tests/resources/submod2/.gitted/modules/sm_changed_head/index
new file mode 100644
index 000000000..728fa292f
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/index
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/info/exclude b/tests/resources/submod2/.gitted/modules/sm_changed_head/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/logs/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_head/logs/HEAD
new file mode 100644
index 000000000..cabdeb2b5
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/logs/HEAD
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560179 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
+480095882d281ed676fe5b863569520e54a7d5c0 3d9386c507f6b093471a3e324085657a3c2b4247 Russell Belfer <rb@github.com> 1342560431 -0700 commit: Making a change in a submodule
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/heads/master
new file mode 100644
index 000000000..cabdeb2b5
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/heads/master
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560179 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
+480095882d281ed676fe5b863569520e54a7d5c0 3d9386c507f6b093471a3e324085657a3c2b4247 Russell Belfer <rb@github.com> 1342560431 -0700 commit: Making a change in a submodule
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/remotes/origin/HEAD
new file mode 100644
index 000000000..257ca21d1
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560179 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 000000000..f4b7094c5
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 000000000..56c845e49
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 000000000..bd179b5f5
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/3d/9386c507f6b093471a3e324085657a3c2b4247 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/3d/9386c507f6b093471a3e324085657a3c2b4247
new file mode 100644
index 000000000..a2c371642
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/3d/9386c507f6b093471a3e324085657a3c2b4247
@@ -0,0 +1,3 @@
+x•ŽKj!E3vµ„jµüÀ#<Þ<“ì@­êéO°uÿq ™.çÂ)×ql ´‰o­Š€÷sFa#Èv‰ÓÅ )g#{':ªßTål`b¤4ë0 ;ïf¡ár‘4
+Ùä™
+ªÔÛzUøî÷-û/Ùg©ð¨ù¹lmíù£\Ç'LÆjrhÍïèÕXG_êŸê+ýlç ÊšÎE`;ß=÷]ÔÞJç \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 000000000..ccf49bd15
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 000000000..53029069a
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 000000000..38c791eba
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 000000000..a26d29993
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 000000000..83d1ba481
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/77/fb0ed3e58568d6ad362c78de08ab8649d76e29 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/77/fb0ed3e58568d6ad362c78de08ab8649d76e29
new file mode 100644
index 000000000..f8a236f3d
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/77/fb0ed3e58568d6ad362c78de08ab8649d76e29
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 000000000..6d27af8a8
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 000000000..17458840b
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 000000000..83cc29fb1
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/8e/b1e637ed9fc8e5454fa20d38f809091f9395f4 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/8e/b1e637ed9fc8e5454fa20d38f809091f9395f4
new file mode 100644
index 000000000..8155b3e87
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/8e/b1e637ed9fc8e5454fa20d38f809091f9395f4
@@ -0,0 +1,2 @@
+xMM;
+1µÎ)Þ ÁZPÐÞÆr²3kÉ l²En¿ƒl!¼æýc±ˆóõrz§Üà ,¹º¡çe +ÚlEZxuPY…x QC³*ðf·uLácfR3ŠÍT0'Ò¯øjƒŠ°ð~G¦^s1Šèb2z’ƒÿùVkî]Ü5<·ûv¨'>ã \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 000000000..55bda40ef
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/packed-refs b/tests/resources/submod2/.gitted/modules/sm_changed_head/packed-refs
new file mode 100644
index 000000000..5a4ebc47c
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_head/refs/heads/master
new file mode 100644
index 000000000..ae079bd79
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/refs/heads/master
@@ -0,0 +1 @@
+3d9386c507f6b093471a3e324085657a3c2b4247
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_head/refs/remotes/origin/HEAD
new file mode 100644
index 000000000..6efe28fff
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_index/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/config b/tests/resources/submod2/.gitted/modules/sm_changed_index/config
new file mode 100644
index 000000000..0274ff7e3
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/config
@@ -0,0 +1,13 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../sm_changed_index
+ ignorecase = true
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/description b/tests/resources/submod2/.gitted/modules/sm_changed_index/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/index b/tests/resources/submod2/.gitted/modules/sm_changed_index/index
new file mode 100644
index 000000000..6fad3b43e
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/index
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/info/exclude b/tests/resources/submod2/.gitted/modules/sm_changed_index/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/logs/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_index/logs/HEAD
new file mode 100644
index 000000000..80eb54102
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560175 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/heads/master
new file mode 100644
index 000000000..80eb54102
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560175 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/remotes/origin/HEAD
new file mode 100644
index 000000000..80eb54102
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560175 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 000000000..f4b7094c5
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 000000000..56c845e49
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 000000000..bd179b5f5
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 000000000..ccf49bd15
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 000000000..53029069a
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 000000000..38c791eba
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 000000000..a26d29993
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 000000000..83d1ba481
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 000000000..6d27af8a8
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 000000000..17458840b
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 000000000..83cc29fb1
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/a0/2d31770687965547ab7a04cee199b29ee458d6 b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/a0/2d31770687965547ab7a04cee199b29ee458d6
new file mode 100644
index 000000000..cb3f5a002
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/a0/2d31770687965547ab7a04cee199b29ee458d6
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 000000000..55bda40ef
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/packed-refs b/tests/resources/submod2/.gitted/modules/sm_changed_index/packed-refs
new file mode 100644
index 000000000..5a4ebc47c
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_index/refs/heads/master
new file mode 100644
index 000000000..e12c44d7a
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_index/refs/remotes/origin/HEAD
new file mode 100644
index 000000000..6efe28fff
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/config b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/config
new file mode 100644
index 000000000..7f2584476
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/config
@@ -0,0 +1,13 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../sm_changed_untracked_file
+ ignorecase = true
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/description b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/index b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/index
new file mode 100644
index 000000000..598e30a32
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/index
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/info/exclude b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/HEAD
new file mode 100644
index 000000000..d1beafbd6
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560186 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/heads/master
new file mode 100644
index 000000000..d1beafbd6
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560186 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/remotes/origin/HEAD
new file mode 100644
index 000000000..d1beafbd6
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560186 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 000000000..f4b7094c5
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 000000000..56c845e49
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 000000000..bd179b5f5
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 000000000..ccf49bd15
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 000000000..53029069a
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 000000000..38c791eba
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 000000000..a26d29993
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 000000000..83d1ba481
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 000000000..6d27af8a8
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 000000000..17458840b
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 000000000..83cc29fb1
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 000000000..55bda40ef
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/packed-refs b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/packed-refs
new file mode 100644
index 000000000..5a4ebc47c
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/heads/master
new file mode 100644
index 000000000..e12c44d7a
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/remotes/origin/HEAD
new file mode 100644
index 000000000..6efe28fff
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/HEAD b/tests/resources/submod2/.gitted/modules/sm_missing_commits/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/config b/tests/resources/submod2/.gitted/modules/sm_missing_commits/config
new file mode 100644
index 000000000..45fbb30cf
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/config
@@ -0,0 +1,13 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../sm_missing_commits
+ ignorecase = true
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/description b/tests/resources/submod2/.gitted/modules/sm_missing_commits/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/index b/tests/resources/submod2/.gitted/modules/sm_missing_commits/index
new file mode 100644
index 000000000..490356524
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/index
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/info/exclude b/tests/resources/submod2/.gitted/modules/sm_missing_commits/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/HEAD b/tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/HEAD
new file mode 100644
index 000000000..ee08c9706
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 5e4963595a9774b90524d35a807169049de8ccad Russell Belfer <rb@github.com> 1342559796 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/heads/master
new file mode 100644
index 000000000..ee08c9706
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 5e4963595a9774b90524d35a807169049de8ccad Russell Belfer <rb@github.com> 1342559796 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/remotes/origin/HEAD
new file mode 100644
index 000000000..ee08c9706
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 5e4963595a9774b90524d35a807169049de8ccad Russell Belfer <rb@github.com> 1342559796 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 000000000..f4b7094c5
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 000000000..56c845e49
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 000000000..bd179b5f5
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 000000000..ccf49bd15
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 000000000..38c791eba
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 000000000..a26d29993
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 000000000..6d27af8a8
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 000000000..83cc29fb1
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 000000000..55bda40ef
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/packed-refs b/tests/resources/submod2/.gitted/modules/sm_missing_commits/packed-refs
new file mode 100644
index 000000000..66fbf5daf
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+5e4963595a9774b90524d35a807169049de8ccad refs/remotes/origin/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_missing_commits/refs/heads/master
new file mode 100644
index 000000000..3913aca5d
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/refs/heads/master
@@ -0,0 +1 @@
+5e4963595a9774b90524d35a807169049de8ccad
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_missing_commits/refs/remotes/origin/HEAD
new file mode 100644
index 000000000..6efe28fff
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/HEAD b/tests/resources/submod2/.gitted/modules/sm_unchanged/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/config b/tests/resources/submod2/.gitted/modules/sm_unchanged/config
new file mode 100644
index 000000000..fc706c9dd
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/config
@@ -0,0 +1,13 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../sm_unchanged
+ ignorecase = true
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/description b/tests/resources/submod2/.gitted/modules/sm_unchanged/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/index b/tests/resources/submod2/.gitted/modules/sm_unchanged/index
new file mode 100644
index 000000000..629c849ec
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/index
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/info/exclude b/tests/resources/submod2/.gitted/modules/sm_unchanged/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/logs/HEAD b/tests/resources/submod2/.gitted/modules/sm_unchanged/logs/HEAD
new file mode 100644
index 000000000..72653286a
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560169 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/heads/master
new file mode 100644
index 000000000..72653286a
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560169 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/remotes/origin/HEAD
new file mode 100644
index 000000000..72653286a
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560169 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 000000000..f4b7094c5
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 000000000..56c845e49
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 000000000..bd179b5f5
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 000000000..ccf49bd15
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 000000000..53029069a
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 000000000..38c791eba
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 000000000..a26d29993
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 000000000..83d1ba481
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 000000000..6d27af8a8
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 000000000..17458840b
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 000000000..83cc29fb1
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 000000000..55bda40ef
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/packed-refs b/tests/resources/submod2/.gitted/modules/sm_unchanged/packed-refs
new file mode 100644
index 000000000..5a4ebc47c
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_unchanged/refs/heads/master
new file mode 100644
index 000000000..e12c44d7a
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_unchanged/refs/remotes/origin/HEAD
new file mode 100644
index 000000000..6efe28fff
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/submod2/.gitted/objects/09/460e5b6cbcb05a3e404593c32a3aa7221eca0e b/tests/resources/submod2/.gitted/objects/09/460e5b6cbcb05a3e404593c32a3aa7221eca0e
new file mode 100644
index 000000000..f1ea5f4c8
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/09/460e5b6cbcb05a3e404593c32a3aa7221eca0e
Binary files differ
diff --git a/tests/resources/submod2/.gitted/objects/14/fe9ccf104058df25e0a08361c4494e167ef243 b/tests/resources/submod2/.gitted/objects/14/fe9ccf104058df25e0a08361c4494e167ef243
new file mode 100644
index 000000000..d3c8582e3
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/14/fe9ccf104058df25e0a08361c4494e167ef243
@@ -0,0 +1 @@
+x•M F]sŠ¹€fh¡ccŒ;·Þ€ŸÁ’@I(Ü_OàöË{ïs%çØ@’>µÊ ^!¹²F'½‘!諲l£_¼q4Íä´ÇE˜Þ¶Rá݃S‚'§ÀnÕ>>±mÝ^\Éw³š´^‰$œ‘ÅXÇ_迦xí±E“à—_.à9} \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/objects/22/ce3e0311dda73a5992d54a4a595518d3876ea7 b/tests/resources/submod2/.gitted/objects/22/ce3e0311dda73a5992d54a4a595518d3876ea7
new file mode 100644
index 000000000..fce6a94b5
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/22/ce3e0311dda73a5992d54a4a595518d3876ea7
@@ -0,0 +1,4 @@
+xµË
+Â0Eݶ_Qº·.
+.ü W"!1 æ!3 øù>+¶Š.¤Û9Ã=3Wº(«nÕ-¶”¥:;¨jòÜ[" WÑ{›¨Þ•ÅQ¤¾ZWï°,2º iviyh •“ÐT/‚=Ž{Ž‡ ¶!@b(¡bÎJcSËP¢¥rÅŒ
+è‡ð¡ã{ë`ì|%³imÐpú콡ÙÄ=ˆIÇÿW2›6‡„B@)|¼óÿ)g£ý™ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/objects/25/5546424b0efb847b1bfc91dbf7348b277f8970 b/tests/resources/submod2/.gitted/objects/25/5546424b0efb847b1bfc91dbf7348b277f8970
new file mode 100644
index 000000000..2965becf6
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/25/5546424b0efb847b1bfc91dbf7348b277f8970
Binary files differ
diff --git a/tests/resources/submod2/.gitted/objects/2a/30f1e6f94b20917005a21273f65b406d0f8bad b/tests/resources/submod2/.gitted/objects/2a/30f1e6f94b20917005a21273f65b406d0f8bad
new file mode 100644
index 000000000..08faf0fa8
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/2a/30f1e6f94b20917005a21273f65b406d0f8bad
Binary files differ
diff --git a/tests/resources/submod2/.gitted/objects/42/cfb95cd01bf9225b659b5ee3edcc78e8eeb478 b/tests/resources/submod2/.gitted/objects/42/cfb95cd01bf9225b659b5ee3edcc78e8eeb478
new file mode 100644
index 000000000..ee7848ae6
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/42/cfb95cd01bf9225b659b5ee3edcc78e8eeb478
Binary files differ
diff --git a/tests/resources/submod2/.gitted/objects/57/958699c2dc394f81cfc76950e9c3ac3025c398 b/tests/resources/submod2/.gitted/objects/57/958699c2dc394f81cfc76950e9c3ac3025c398
new file mode 100644
index 000000000..ca9203a6e
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/57/958699c2dc394f81cfc76950e9c3ac3025c398
Binary files differ
diff --git a/tests/resources/submod2/.gitted/objects/59/01da4f1c67756eeadc5121d206bec2431f253b b/tests/resources/submod2/.gitted/objects/59/01da4f1c67756eeadc5121d206bec2431f253b
new file mode 100644
index 000000000..9f88f6bdf
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/59/01da4f1c67756eeadc5121d206bec2431f253b
@@ -0,0 +1,2 @@
+x•ŽÛ 1EýNÓ€2yg@D,A°€$;YöE6éßmÁß{Λ·e™(å/­2ƒõdƒ#ÊjÈšL 2—ìYdÊ:fÊž ˆ=V^D’hR Ä$¥^ÃÅ©ÉaŠÆ+tn {ûnÞý8xžáÅsá
+÷šžãÔ¾=Ýò¶<@j£¬CÔ®èŹžÿÚ©þ[ŠÏ>Ä6­#=-ÛÐg?,¯FŒ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/objects/60/7d96653d4d0a4f733107f7890c2e67b55b620d b/tests/resources/submod2/.gitted/objects/60/7d96653d4d0a4f733107f7890c2e67b55b620d
new file mode 100644
index 000000000..30bee40e9
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/60/7d96653d4d0a4f733107f7890c2e67b55b620d
Binary files differ
diff --git a/tests/resources/submod2/.gitted/objects/74/84482eb8db738cafa696993664607500a3f2b9 b/tests/resources/submod2/.gitted/objects/74/84482eb8db738cafa696993664607500a3f2b9
new file mode 100644
index 000000000..79018042d
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/74/84482eb8db738cafa696993664607500a3f2b9
Binary files differ
diff --git a/tests/resources/submod2/.gitted/objects/7b/a4c5c3561daa5ab1a86215cfb0587e96d404d6 b/tests/resources/submod2/.gitted/objects/7b/a4c5c3561daa5ab1a86215cfb0587e96d404d6
new file mode 100644
index 000000000..cde89e5bb
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/7b/a4c5c3561daa5ab1a86215cfb0587e96d404d6
Binary files differ
diff --git a/tests/resources/submod2/.gitted/objects/87/3585b94bdeabccea991ea5e3ec1a277895b698 b/tests/resources/submod2/.gitted/objects/87/3585b94bdeabccea991ea5e3ec1a277895b698
new file mode 100644
index 000000000..41af98aa9
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/87/3585b94bdeabccea991ea5e3ec1a277895b698
Binary files differ
diff --git a/tests/resources/submod2/.gitted/objects/97/4cf7c73de336b0c4e019f918f3cee367d72e84 b/tests/resources/submod2/.gitted/objects/97/4cf7c73de336b0c4e019f918f3cee367d72e84
new file mode 100644
index 000000000..160f1caf4
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/97/4cf7c73de336b0c4e019f918f3cee367d72e84
@@ -0,0 +1,2 @@
+xµË
+Â0Eݶ_º·Bqåg¸ yŒi ™IÀÏ÷Y±Up!ÝÎs¸£|R¬ï7«=’)XCAGä¢:…à25‡º:É<°-û„uUÐ_IÛò‡¤Y¢…\Ϥ%êAF fª{Gß qTœPsï”u¹ã(ÓZ{‰RA ô#ø̉£ó0m¾“Ų.8ïÞÑbáäìÇãÞù?{vÊŒ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/objects/9d/bc299bc013ea253583b40bf327b5a6e4037b89 b/tests/resources/submod2/.gitted/objects/9d/bc299bc013ea253583b40bf327b5a6e4037b89
new file mode 100644
index 000000000..1ee52218d
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/9d/bc299bc013ea253583b40bf327b5a6e4037b89
Binary files differ
diff --git a/tests/resources/submod2/.gitted/objects/a9/104bf89e911387244ef499413960ba472066d9 b/tests/resources/submod2/.gitted/objects/a9/104bf89e911387244ef499413960ba472066d9
new file mode 100644
index 000000000..2239e14a8
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/a9/104bf89e911387244ef499413960ba472066d9
Binary files differ
diff --git a/tests/resources/submod2/.gitted/objects/b6/14088620bbdc1d29549d223ceba0f4419fd4cb b/tests/resources/submod2/.gitted/objects/b6/14088620bbdc1d29549d223ceba0f4419fd4cb
new file mode 100644
index 000000000..a03ea66e4
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/b6/14088620bbdc1d29549d223ceba0f4419fd4cb
Binary files differ
diff --git a/tests/resources/submod2/.gitted/objects/d4/07f19e50c1da1ff584beafe0d6dac7237c5d06 b/tests/resources/submod2/.gitted/objects/d4/07f19e50c1da1ff584beafe0d6dac7237c5d06
new file mode 100644
index 000000000..292303eb9
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/d4/07f19e50c1da1ff584beafe0d6dac7237c5d06
Binary files differ
diff --git a/tests/resources/submod2/.gitted/objects/d9/3e95571d92cceb5de28c205f1d5f3cc8b88bc8 b/tests/resources/submod2/.gitted/objects/d9/3e95571d92cceb5de28c205f1d5f3cc8b88bc8
new file mode 100644
index 000000000..b92c7eebd
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/d9/3e95571d92cceb5de28c205f1d5f3cc8b88bc8
@@ -0,0 +1,2 @@
+x•ÏÛ
+!€ánק}€ "‚.z’uRÉCx€}üΑۼøt¸ œ.׫Ù6î‚,iŸs&%ãÁ9“S¿#ݲ¦úIW¢=—a˜ßËf2A‹¼BYsÏñßÐa{c±¶^K3g¼Äñ³wMÍ F˜Üúøߥ4sÅçâ€òÇáõÎ÷'Nê°I \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/objects/e3/b83bf274ee065eee48734cf8c6dfaf5e81471c b/tests/resources/submod2/.gitted/objects/e3/b83bf274ee065eee48734cf8c6dfaf5e81471c
new file mode 100644
index 000000000..3c7750b12
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/e3/b83bf274ee065eee48734cf8c6dfaf5e81471c
Binary files differ
diff --git a/tests/resources/submod2/.gitted/objects/f5/4414c25e6d24fe39f5c3f128d7c8a17bc23833 b/tests/resources/submod2/.gitted/objects/f5/4414c25e6d24fe39f5c3f128d7c8a17bc23833
new file mode 100644
index 000000000..219620b25
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/f5/4414c25e6d24fe39f5c3f128d7c8a17bc23833
@@ -0,0 +1,2 @@
+xeÍÁ
+Â0„a¯íS„ÞíbOzð1<I Iº¤¤‘Íû+ˆ‚õ:?|ãsõæt9îh¾Ô¥e6Š- H[´¡–’ÃÜw§«¹šÿØwMò«Œ#½‘ɪ“ÈÚïж…Õm‘—_î; º$ž rò1éDÊPCvB¨Mcø‡ýI^ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/objects/f9/90a25a74d1a8281ce2ab018ea8df66795cd60b b/tests/resources/submod2/.gitted/objects/f9/90a25a74d1a8281ce2ab018ea8df66795cd60b
new file mode 100644
index 000000000..883a40bfc
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/f9/90a25a74d1a8281ce2ab018ea8df66795cd60b
@@ -0,0 +1 @@
+xEŒA€ =óŠý„ÿáZ)¤RE¿/ñb2·É «1¶uÙsé˜xôÁ§Å¡—îˆä>ßä2<E™nG=2,ýÉœTÄ’’4©Žî4!¼N¬$` \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/refs/heads/master b/tests/resources/submod2/.gitted/refs/heads/master
new file mode 100644
index 000000000..d1d38aa49
--- /dev/null
+++ b/tests/resources/submod2/.gitted/refs/heads/master
@@ -0,0 +1 @@
+7484482eb8db738cafa696993664607500a3f2b9
diff --git a/tests/resources/submod2/README.txt b/tests/resources/submod2/README.txt
new file mode 100644
index 000000000..f990a25a7
--- /dev/null
+++ b/tests/resources/submod2/README.txt
@@ -0,0 +1,3 @@
+This is the submodule test data
+This repo will have a bunch of submodules in different states
+
diff --git a/tests/resources/submod2/gitmodules b/tests/resources/submod2/gitmodules
new file mode 100644
index 000000000..4c31108ed
--- /dev/null
+++ b/tests/resources/submod2/gitmodules
@@ -0,0 +1,24 @@
+[submodule "sm_missing_commits"]
+ path = sm_missing_commits
+ url = ../submod2_target
+[submodule "sm_unchanged"]
+ path = sm_unchanged
+ url = ../submod2_target
+[submodule "sm_changed_file"]
+ path = sm_changed_file
+ url = ../submod2_target
+[submodule "sm_changed_index"]
+ path = sm_changed_index
+ url = ../submod2_target
+[submodule "sm_changed_head"]
+ path = sm_changed_head
+ url = ../submod2_target
+[submodule "sm_changed_untracked_file"]
+ path = sm_changed_untracked_file
+ url = ../submod2_target
+[submodule "sm_added_and_uncommited"]
+ path = sm_added_and_uncommited
+ url = ../submod2_target
+[submodule "sm_gitmodules_only"]
+ path = sm_gitmodules_only
+ url = ../submod2_target
diff --git a/tests/resources/submod2/just_a_dir/contents b/tests/resources/submod2/just_a_dir/contents
new file mode 100644
index 000000000..7ba4c5c35
--- /dev/null
+++ b/tests/resources/submod2/just_a_dir/contents
@@ -0,0 +1 @@
+This is a file in a plain directory
diff --git a/tests/resources/submod2/just_a_file b/tests/resources/submod2/just_a_file
new file mode 100644
index 000000000..42cfb95cd
--- /dev/null
+++ b/tests/resources/submod2/just_a_file
@@ -0,0 +1 @@
+This is just a plain file
diff --git a/tests/resources/submod2/not-submodule/.gitted/HEAD b/tests/resources/submod2/not-submodule/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/submod2/not-submodule/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod2/not-submodule/.gitted/config b/tests/resources/submod2/not-submodule/.gitted/config
new file mode 100644
index 000000000..af107929f
--- /dev/null
+++ b/tests/resources/submod2/not-submodule/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests/resources/submod2/not-submodule/.gitted/description b/tests/resources/submod2/not-submodule/.gitted/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/submod2/not-submodule/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/submod2/not-submodule/.gitted/index b/tests/resources/submod2/not-submodule/.gitted/index
new file mode 100644
index 000000000..f3fafa536
--- /dev/null
+++ b/tests/resources/submod2/not-submodule/.gitted/index
Binary files differ
diff --git a/tests/resources/submod2/not-submodule/.gitted/info/exclude b/tests/resources/submod2/not-submodule/.gitted/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/submod2/not-submodule/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/submod2/not-submodule/.gitted/logs/HEAD b/tests/resources/submod2/not-submodule/.gitted/logs/HEAD
new file mode 100644
index 000000000..1749e7dff
--- /dev/null
+++ b/tests/resources/submod2/not-submodule/.gitted/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 68e92c611b80ee1ed8f38314ff9577f0d15b2444 Russell Belfer <rb@github.com> 1342560358 -0700 commit (initial): Initial commit
diff --git a/tests/resources/submod2/not-submodule/.gitted/logs/refs/heads/master b/tests/resources/submod2/not-submodule/.gitted/logs/refs/heads/master
new file mode 100644
index 000000000..1749e7dff
--- /dev/null
+++ b/tests/resources/submod2/not-submodule/.gitted/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 68e92c611b80ee1ed8f38314ff9577f0d15b2444 Russell Belfer <rb@github.com> 1342560358 -0700 commit (initial): Initial commit
diff --git a/tests/resources/submod2/not-submodule/.gitted/objects/68/e92c611b80ee1ed8f38314ff9577f0d15b2444 b/tests/resources/submod2/not-submodule/.gitted/objects/68/e92c611b80ee1ed8f38314ff9577f0d15b2444
new file mode 100644
index 000000000..8892531a7
--- /dev/null
+++ b/tests/resources/submod2/not-submodule/.gitted/objects/68/e92c611b80ee1ed8f38314ff9577f0d15b2444
Binary files differ
diff --git a/tests/resources/submod2/not-submodule/.gitted/objects/71/ff9927d7c8a5639e062c38a7d35c433c424627 b/tests/resources/submod2/not-submodule/.gitted/objects/71/ff9927d7c8a5639e062c38a7d35c433c424627
new file mode 100644
index 000000000..c4e1a77d7
--- /dev/null
+++ b/tests/resources/submod2/not-submodule/.gitted/objects/71/ff9927d7c8a5639e062c38a7d35c433c424627
Binary files differ
diff --git a/tests/resources/submod2/not-submodule/.gitted/objects/f0/1d56b18efd353ef2bb93a4585d590a0847195e b/tests/resources/submod2/not-submodule/.gitted/objects/f0/1d56b18efd353ef2bb93a4585d590a0847195e
new file mode 100644
index 000000000..e9f1942a9
--- /dev/null
+++ b/tests/resources/submod2/not-submodule/.gitted/objects/f0/1d56b18efd353ef2bb93a4585d590a0847195e
Binary files differ
diff --git a/tests/resources/submod2/not-submodule/.gitted/refs/heads/master b/tests/resources/submod2/not-submodule/.gitted/refs/heads/master
new file mode 100644
index 000000000..0bd8514bd
--- /dev/null
+++ b/tests/resources/submod2/not-submodule/.gitted/refs/heads/master
@@ -0,0 +1 @@
+68e92c611b80ee1ed8f38314ff9577f0d15b2444
diff --git a/tests/resources/submod2/not-submodule/README.txt b/tests/resources/submod2/not-submodule/README.txt
new file mode 100644
index 000000000..71ff9927d
--- /dev/null
+++ b/tests/resources/submod2/not-submodule/README.txt
@@ -0,0 +1 @@
+This is a git repo but not a submodule
diff --git a/tests/resources/submod2/not/.gitted/notempty b/tests/resources/submod2/not/.gitted/notempty
new file mode 100644
index 000000000..9b33ac4e4
--- /dev/null
+++ b/tests/resources/submod2/not/.gitted/notempty
@@ -0,0 +1 @@
+fooled you
diff --git a/tests/resources/submod2/not/README.txt b/tests/resources/submod2/not/README.txt
new file mode 100644
index 000000000..4f6935b98
--- /dev/null
+++ b/tests/resources/submod2/not/README.txt
@@ -0,0 +1 @@
+what am I really
diff --git a/tests/resources/submod2/sm_added_and_uncommited/.gitted b/tests/resources/submod2/sm_added_and_uncommited/.gitted
new file mode 100644
index 000000000..2b2a4cf90
--- /dev/null
+++ b/tests/resources/submod2/sm_added_and_uncommited/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/sm_added_and_uncommited
diff --git a/tests/resources/submod2/sm_added_and_uncommited/README.txt b/tests/resources/submod2/sm_added_and_uncommited/README.txt
new file mode 100644
index 000000000..780d7397f
--- /dev/null
+++ b/tests/resources/submod2/sm_added_and_uncommited/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests/resources/submod2/sm_added_and_uncommited/file_to_modify b/tests/resources/submod2/sm_added_and_uncommited/file_to_modify
new file mode 100644
index 000000000..789efbdad
--- /dev/null
+++ b/tests/resources/submod2/sm_added_and_uncommited/file_to_modify
@@ -0,0 +1,3 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
diff --git a/tests/resources/submod2/sm_changed_file/.gitted b/tests/resources/submod2/sm_changed_file/.gitted
new file mode 100644
index 000000000..dc98b1674
--- /dev/null
+++ b/tests/resources/submod2/sm_changed_file/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/sm_changed_file
diff --git a/tests/resources/submod2/sm_changed_file/README.txt b/tests/resources/submod2/sm_changed_file/README.txt
new file mode 100644
index 000000000..780d7397f
--- /dev/null
+++ b/tests/resources/submod2/sm_changed_file/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests/resources/submod2/sm_changed_file/file_to_modify b/tests/resources/submod2/sm_changed_file/file_to_modify
new file mode 100644
index 000000000..e5ba67168
--- /dev/null
+++ b/tests/resources/submod2/sm_changed_file/file_to_modify
@@ -0,0 +1,4 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
+In this case, the file is changed in the workdir
diff --git a/tests/resources/submod2/sm_changed_head/.gitted b/tests/resources/submod2/sm_changed_head/.gitted
new file mode 100644
index 000000000..d5419b62d
--- /dev/null
+++ b/tests/resources/submod2/sm_changed_head/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/sm_changed_head
diff --git a/tests/resources/submod2/sm_changed_head/README.txt b/tests/resources/submod2/sm_changed_head/README.txt
new file mode 100644
index 000000000..780d7397f
--- /dev/null
+++ b/tests/resources/submod2/sm_changed_head/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests/resources/submod2/sm_changed_head/file_to_modify b/tests/resources/submod2/sm_changed_head/file_to_modify
new file mode 100644
index 000000000..8eb1e637e
--- /dev/null
+++ b/tests/resources/submod2/sm_changed_head/file_to_modify
@@ -0,0 +1,4 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
+This one has been changed and the change has been committed to HEAD.
diff --git a/tests/resources/submod2/sm_changed_index/.gitted b/tests/resources/submod2/sm_changed_index/.gitted
new file mode 100644
index 000000000..2c7a5b271
--- /dev/null
+++ b/tests/resources/submod2/sm_changed_index/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/sm_changed_index
diff --git a/tests/resources/submod2/sm_changed_index/README.txt b/tests/resources/submod2/sm_changed_index/README.txt
new file mode 100644
index 000000000..780d7397f
--- /dev/null
+++ b/tests/resources/submod2/sm_changed_index/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests/resources/submod2/sm_changed_index/file_to_modify b/tests/resources/submod2/sm_changed_index/file_to_modify
new file mode 100644
index 000000000..a02d31770
--- /dev/null
+++ b/tests/resources/submod2/sm_changed_index/file_to_modify
@@ -0,0 +1,4 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
+Here the file is changed in the index and the workdir
diff --git a/tests/resources/submod2/sm_changed_untracked_file/.gitted b/tests/resources/submod2/sm_changed_untracked_file/.gitted
new file mode 100644
index 000000000..9a1070647
--- /dev/null
+++ b/tests/resources/submod2/sm_changed_untracked_file/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/sm_changed_untracked_file
diff --git a/tests/resources/submod2/sm_changed_untracked_file/README.txt b/tests/resources/submod2/sm_changed_untracked_file/README.txt
new file mode 100644
index 000000000..780d7397f
--- /dev/null
+++ b/tests/resources/submod2/sm_changed_untracked_file/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests/resources/submod2/sm_changed_untracked_file/file_to_modify b/tests/resources/submod2/sm_changed_untracked_file/file_to_modify
new file mode 100644
index 000000000..789efbdad
--- /dev/null
+++ b/tests/resources/submod2/sm_changed_untracked_file/file_to_modify
@@ -0,0 +1,3 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
diff --git a/tests/resources/submod2/sm_changed_untracked_file/i_am_untracked b/tests/resources/submod2/sm_changed_untracked_file/i_am_untracked
new file mode 100644
index 000000000..d2bae6167
--- /dev/null
+++ b/tests/resources/submod2/sm_changed_untracked_file/i_am_untracked
@@ -0,0 +1 @@
+This file is untracked, but in a submodule
diff --git a/tests/resources/submod2/sm_missing_commits/.gitted b/tests/resources/submod2/sm_missing_commits/.gitted
new file mode 100644
index 000000000..70193be84
--- /dev/null
+++ b/tests/resources/submod2/sm_missing_commits/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/sm_missing_commits
diff --git a/tests/resources/submod2/sm_missing_commits/README.txt b/tests/resources/submod2/sm_missing_commits/README.txt
new file mode 100644
index 000000000..780d7397f
--- /dev/null
+++ b/tests/resources/submod2/sm_missing_commits/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests/resources/submod2/sm_missing_commits/file_to_modify b/tests/resources/submod2/sm_missing_commits/file_to_modify
new file mode 100644
index 000000000..8834b635d
--- /dev/null
+++ b/tests/resources/submod2/sm_missing_commits/file_to_modify
@@ -0,0 +1,3 @@
+This is a file to modify in submodules
+It already has some history.
+
diff --git a/tests/resources/submod2/sm_unchanged/.gitted b/tests/resources/submod2/sm_unchanged/.gitted
new file mode 100644
index 000000000..51a679c80
--- /dev/null
+++ b/tests/resources/submod2/sm_unchanged/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/sm_unchanged
diff --git a/tests/resources/submod2/sm_unchanged/README.txt b/tests/resources/submod2/sm_unchanged/README.txt
new file mode 100644
index 000000000..780d7397f
--- /dev/null
+++ b/tests/resources/submod2/sm_unchanged/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests/resources/submod2/sm_unchanged/file_to_modify b/tests/resources/submod2/sm_unchanged/file_to_modify
new file mode 100644
index 000000000..789efbdad
--- /dev/null
+++ b/tests/resources/submod2/sm_unchanged/file_to_modify
@@ -0,0 +1,3 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
diff --git a/tests/resources/submod2_target/.gitted/HEAD b/tests/resources/submod2_target/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod2_target/.gitted/config b/tests/resources/submod2_target/.gitted/config
new file mode 100644
index 000000000..af107929f
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests/resources/submod2_target/.gitted/description b/tests/resources/submod2_target/.gitted/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/submod2_target/.gitted/index b/tests/resources/submod2_target/.gitted/index
new file mode 100644
index 000000000..eb3ff8c10
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/index
Binary files differ
diff --git a/tests/resources/submod2_target/.gitted/info/exclude b/tests/resources/submod2_target/.gitted/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/submod2_target/.gitted/logs/HEAD b/tests/resources/submod2_target/.gitted/logs/HEAD
new file mode 100644
index 000000000..0ecd1113f
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/logs/HEAD
@@ -0,0 +1,4 @@
+0000000000000000000000000000000000000000 6b31c659545507c381e9cd34ec508f16c04e149e Russell Belfer <rb@github.com> 1342559662 -0700 commit (initial): Initial commit
+6b31c659545507c381e9cd34ec508f16c04e149e 41bd4bc3df978de695f67ace64c560913da11653 Russell Belfer <rb@github.com> 1342559709 -0700 commit: Adding test file
+41bd4bc3df978de695f67ace64c560913da11653 5e4963595a9774b90524d35a807169049de8ccad Russell Belfer <rb@github.com> 1342559726 -0700 commit: Updating test file
+5e4963595a9774b90524d35a807169049de8ccad 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342559925 -0700 commit: One more update
diff --git a/tests/resources/submod2_target/.gitted/logs/refs/heads/master b/tests/resources/submod2_target/.gitted/logs/refs/heads/master
new file mode 100644
index 000000000..0ecd1113f
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/logs/refs/heads/master
@@ -0,0 +1,4 @@
+0000000000000000000000000000000000000000 6b31c659545507c381e9cd34ec508f16c04e149e Russell Belfer <rb@github.com> 1342559662 -0700 commit (initial): Initial commit
+6b31c659545507c381e9cd34ec508f16c04e149e 41bd4bc3df978de695f67ace64c560913da11653 Russell Belfer <rb@github.com> 1342559709 -0700 commit: Adding test file
+41bd4bc3df978de695f67ace64c560913da11653 5e4963595a9774b90524d35a807169049de8ccad Russell Belfer <rb@github.com> 1342559726 -0700 commit: Updating test file
+5e4963595a9774b90524d35a807169049de8ccad 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342559925 -0700 commit: One more update
diff --git a/tests/resources/submod2_target/.gitted/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2_target/.gitted/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 000000000..f4b7094c5
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/submod2_target/.gitted/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2_target/.gitted/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 000000000..56c845e49
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/submod2_target/.gitted/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2_target/.gitted/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 000000000..bd179b5f5
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/submod2_target/.gitted/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2_target/.gitted/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 000000000..ccf49bd15
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/submod2_target/.gitted/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod2_target/.gitted/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 000000000..53029069a
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests/resources/submod2_target/.gitted/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2_target/.gitted/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 000000000..38c791eba
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/submod2_target/.gitted/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2_target/.gitted/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 000000000..a26d29993
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/submod2_target/.gitted/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod2_target/.gitted/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 000000000..83d1ba481
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests/resources/submod2_target/.gitted/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2_target/.gitted/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 000000000..6d27af8a8
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/submod2_target/.gitted/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod2_target/.gitted/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 000000000..17458840b
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests/resources/submod2_target/.gitted/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2_target/.gitted/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 000000000..83cc29fb1
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/submod2_target/.gitted/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2_target/.gitted/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 000000000..55bda40ef
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/submod2_target/.gitted/refs/heads/master b/tests/resources/submod2_target/.gitted/refs/heads/master
new file mode 100644
index 000000000..e12c44d7a
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests/resources/submod2_target/README.txt b/tests/resources/submod2_target/README.txt
new file mode 100644
index 000000000..780d7397f
--- /dev/null
+++ b/tests/resources/submod2_target/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests/resources/submod2_target/file_to_modify b/tests/resources/submod2_target/file_to_modify
new file mode 100644
index 000000000..789efbdad
--- /dev/null
+++ b/tests/resources/submod2_target/file_to_modify
@@ -0,0 +1,3 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
diff --git a/tests/resources/submodules/.gitted/HEAD b/tests/resources/submodules/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/submodules/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submodules/.gitted/config b/tests/resources/submodules/.gitted/config
new file mode 100644
index 000000000..af107929f
--- /dev/null
+++ b/tests/resources/submodules/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests/resources/submodules/.gitted/description b/tests/resources/submodules/.gitted/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/submodules/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/submodules/.gitted/index b/tests/resources/submodules/.gitted/index
new file mode 100644
index 000000000..97bf8ef51
--- /dev/null
+++ b/tests/resources/submodules/.gitted/index
Binary files differ
diff --git a/tests/resources/submodules/.gitted/info/exclude b/tests/resources/submodules/.gitted/info/exclude
new file mode 100644
index 000000000..dfc411579
--- /dev/null
+++ b/tests/resources/submodules/.gitted/info/exclude
@@ -0,0 +1,8 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
+ignored
+
diff --git a/tests/resources/submodules/.gitted/info/refs b/tests/resources/submodules/.gitted/info/refs
new file mode 100644
index 000000000..ba17abdde
--- /dev/null
+++ b/tests/resources/submodules/.gitted/info/refs
@@ -0,0 +1 @@
+09176a980273d801a3e37cc45c84af1366501ed9 refs/heads/master
diff --git a/tests/resources/submodules/.gitted/logs/HEAD b/tests/resources/submodules/.gitted/logs/HEAD
new file mode 100644
index 000000000..87a7bdafc
--- /dev/null
+++ b/tests/resources/submodules/.gitted/logs/HEAD
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 09176a980273d801a3e37cc45c84af1366501ed9 Russell Belfer <arrbee@arrbee.com> 1332365253 -0700 commit (initial): initial commit
+09176a980273d801a3e37cc45c84af1366501ed9 97896810b3210244a62a82458b8e0819ecfc6850 Russell Belfer <arrbee@arrbee.com> 1332780781 -0700 commit: Setting up gitmodules
diff --git a/tests/resources/submodules/.gitted/logs/refs/heads/master b/tests/resources/submodules/.gitted/logs/refs/heads/master
new file mode 100644
index 000000000..87a7bdafc
--- /dev/null
+++ b/tests/resources/submodules/.gitted/logs/refs/heads/master
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 09176a980273d801a3e37cc45c84af1366501ed9 Russell Belfer <arrbee@arrbee.com> 1332365253 -0700 commit (initial): initial commit
+09176a980273d801a3e37cc45c84af1366501ed9 97896810b3210244a62a82458b8e0819ecfc6850 Russell Belfer <arrbee@arrbee.com> 1332780781 -0700 commit: Setting up gitmodules
diff --git a/tests/resources/submodules/.gitted/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e b/tests/resources/submodules/.gitted/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e
new file mode 100644
index 000000000..2c3c2cb61
--- /dev/null
+++ b/tests/resources/submodules/.gitted/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e
@@ -0,0 +1,2 @@
+x%‰=
+€0 F]í)Š0à"ÃIŒ*•|Éý-t{?œ2ÇilV8¿ùô$±«Øm¡ýv»ãk­k*F DAÊ=(=|=6 ¬DAv=ÛÍA}™&'…Oò$= \ No newline at end of file
diff --git a/tests/resources/submodules/.gitted/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357 b/tests/resources/submodules/.gitted/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357
new file mode 100644
index 000000000..c85fb5512
--- /dev/null
+++ b/tests/resources/submodules/.gitted/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357
Binary files differ
diff --git a/tests/resources/submodules/.gitted/objects/97/896810b3210244a62a82458b8e0819ecfc6850 b/tests/resources/submodules/.gitted/objects/97/896810b3210244a62a82458b8e0819ecfc6850
new file mode 100644
index 000000000..1c8dbdf9f
--- /dev/null
+++ b/tests/resources/submodules/.gitted/objects/97/896810b3210244a62a82458b8e0819ecfc6850
@@ -0,0 +1,3 @@
+xŽ[
+Â0EýÎ*fʤS“ ˆˆKФéú4Ý¿wà×…Ã9pÓ2MC¥FôP @ãÜu.„.¶pÚ!²OYáƒdiYUÍ'Ì•8XïbPn¼ôÊ6
+ħԞ“¶1[qîÌ}0q«ï¥Ðc[WŒ#Ý1fºÄR:àö›SZ¦+Y‘Æ+{µtdÏlvº¬»þOmž¨u˜_´}è5Ôié·«ù` Kæ \ No newline at end of file
diff --git a/tests/resources/submodules/.gitted/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888 b/tests/resources/submodules/.gitted/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888
new file mode 100644
index 000000000..3d78bd6be
--- /dev/null
+++ b/tests/resources/submodules/.gitted/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888
Binary files differ
diff --git a/tests/resources/submodules/.gitted/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818 b/tests/resources/submodules/.gitted/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818
new file mode 100644
index 000000000..6e0b49e86
--- /dev/null
+++ b/tests/resources/submodules/.gitted/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818
Binary files differ
diff --git a/tests/resources/submodules/.gitted/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae b/tests/resources/submodules/.gitted/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae
new file mode 100644
index 000000000..082a58941
--- /dev/null
+++ b/tests/resources/submodules/.gitted/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae
Binary files differ
diff --git a/tests/resources/submodules/.gitted/objects/info/packs b/tests/resources/submodules/.gitted/objects/info/packs
new file mode 100644
index 000000000..0785ef698
--- /dev/null
+++ b/tests/resources/submodules/.gitted/objects/info/packs
@@ -0,0 +1,2 @@
+P pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack
+
diff --git a/tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idx b/tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idx
new file mode 100644
index 000000000..810fc3181
--- /dev/null
+++ b/tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idx
Binary files differ
diff --git a/tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack b/tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack
new file mode 100644
index 000000000..c25c4a73f
--- /dev/null
+++ b/tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack
Binary files differ
diff --git a/tests/resources/submodules/.gitted/packed-refs b/tests/resources/submodules/.gitted/packed-refs
new file mode 100644
index 000000000..a6450691e
--- /dev/null
+++ b/tests/resources/submodules/.gitted/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+09176a980273d801a3e37cc45c84af1366501ed9 refs/heads/master
diff --git a/tests/resources/submodules/.gitted/refs/heads/master b/tests/resources/submodules/.gitted/refs/heads/master
new file mode 100644
index 000000000..32b935853
--- /dev/null
+++ b/tests/resources/submodules/.gitted/refs/heads/master
@@ -0,0 +1 @@
+97896810b3210244a62a82458b8e0819ecfc6850
diff --git a/tests/resources/submodules/added b/tests/resources/submodules/added
new file mode 100644
index 000000000..d5f7fc3f7
--- /dev/null
+++ b/tests/resources/submodules/added
@@ -0,0 +1 @@
+added
diff --git a/tests/resources/submodules/gitmodules b/tests/resources/submodules/gitmodules
new file mode 100644
index 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/resources/submodules/ignored b/tests/resources/submodules/ignored
new file mode 100644
index 000000000..092bfb9bd
--- /dev/null
+++ b/tests/resources/submodules/ignored
@@ -0,0 +1 @@
+yo
diff --git a/tests/resources/submodules/modified b/tests/resources/submodules/modified
new file mode 100644
index 000000000..452216e1d
--- /dev/null
+++ b/tests/resources/submodules/modified
@@ -0,0 +1,2 @@
+changed
+
diff --git a/tests/resources/submodules/testrepo/.gitted/HEAD b/tests/resources/submodules/testrepo/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submodules/testrepo/.gitted/config b/tests/resources/submodules/testrepo/.gitted/config
new file mode 100644
index 000000000..d6dcad12b
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/config
@@ -0,0 +1,12 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = /Users/rb/src/libgit2/tests/resources/testrepo.git
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/submodules/testrepo/.gitted/description b/tests/resources/submodules/testrepo/.gitted/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/submodules/testrepo/.gitted/index b/tests/resources/submodules/testrepo/.gitted/index
new file mode 100644
index 000000000..3eb8d84fe
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/index
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/info/exclude b/tests/resources/submodules/testrepo/.gitted/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/submodules/testrepo/.gitted/logs/HEAD b/tests/resources/submodules/testrepo/.gitted/logs/HEAD
new file mode 100644
index 000000000..147643a30
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Russell Belfer <arrbee@arrbee.com> 1332366307 -0700 clone: from /Users/rb/src/libgit2/tests/resources/testrepo.git
diff --git a/tests/resources/submodules/testrepo/.gitted/logs/refs/heads/master b/tests/resources/submodules/testrepo/.gitted/logs/refs/heads/master
new file mode 100644
index 000000000..147643a30
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Russell Belfer <arrbee@arrbee.com> 1332366307 -0700 clone: from /Users/rb/src/libgit2/tests/resources/testrepo.git
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/tests/resources/submodules/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
new file mode 100644
index 000000000..cedb2a22e
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/tests/resources/submodules/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
new file mode 100644
index 000000000..93a16f146
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd b/tests/resources/submodules/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
new file mode 100644
index 000000000..ba0bfb30c
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b b/tests/resources/submodules/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
new file mode 100644
index 000000000..225c45734
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d b/tests/resources/submodules/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
new file mode 100644
index 000000000..df40d99af
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54 b/tests/resources/submodules/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
new file mode 100644
index 000000000..321eaa867
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc b/tests/resources/submodules/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
new file mode 100644
index 000000000..9bb5b623b
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/submodules/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
new file mode 100644
index 000000000..7ca4ceed5
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/tests/resources/submodules/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
new file mode 100644
index 000000000..8953b6cef
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
@@ -0,0 +1,2 @@
+xŽQ
+Â0DýÎ)öÊ6›Í¦ "xO°‰‰-ØFb¼¿EoàÏ0 ¼Ç¤º,ske×[ÎPn8R,EpD?±gŸ}Ê^3² âÙ<µåµGŽhYKÄèÒ8ЖDAÉ)¿ÉÈ;gôݧÚàšjïp™4ÕŽ¯ô-çû¢óãêr‚ÁŠ;°s°GA4Ûº=ìùÖ(ôin7øIÌKÍFE \ No newline at end of file
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/tests/resources/submodules/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
new file mode 100644
index 000000000..c1f22c54f
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
@@ -0,0 +1,2 @@
+xŽÛ 1EýNi@™Ék2 "X‚$ÙYW0YcÿíÀ¿Ã…s¸¥ÕzïÚÚõMDÏ€0æœ8!¶†ÉÌÞs‰ XŠªgÚdí::@X0»P¢wÙ"F/‰‰œÍRàˆUz÷¥múZZïú²¤ÒV}|•/œo5݇ÒêI£!¬1z Æ:vùÇUim}ê/¢>
+öF- \ No newline at end of file
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/tests/resources/submodules/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
new file mode 100644
index 000000000..2ef4faa0f
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af b/tests/resources/submodules/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
new file mode 100644
index 000000000..716b0c64b
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
@@ -0,0 +1 @@
+xŽAj!³ö?0¨£ßÂ09Êo}HÚ6¨}ÿôjUPP©ÕZ&Yÿø˜ AÔ›±€pŒÁFdë¼÷pz[fŽYŒ½PÒqLJ.,Z§`™Å®Ð.ù`’vÙ ³q $Æ5+9çOëtœû>Û/úDE/龡W¯ï*e¿§VŸdf1>ð覭Öê²×äÄ›¹úÊ™F« ­ìTŽÙhœk.i¶^0Ô?P¼R, \ No newline at end of file
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980 b/tests/resources/submodules/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980
new file mode 100644
index 000000000..23c462f34
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/tests/resources/submodules/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
new file mode 100644
index 000000000..2f9b6b6e3
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/tests/resources/submodules/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
new file mode 100644
index 000000000..5df58dda5
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162 b/tests/resources/submodules/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
new file mode 100644
index 000000000..4cc3f4dff
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
@@ -0,0 +1 @@
+x+)JMU044b040031QrutñueX¡l¨ðmmA‹m›Ì£íJ}Gß;U‘T”˜—œŸ–™“ªWRQÂ`6ýš÷KÇ¥¶^/¾-*|òøWØ¥3P¥y©å`%ËEÛÞ±\&gŽÐ|Ÿ0§ÿ†{Ó1X \ No newline at end of file
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4 b/tests/resources/submodules/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
new file mode 100644
index 000000000..bf7b2bb68
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/tests/resources/submodules/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
new file mode 100644
index 000000000..a79612435
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
@@ -0,0 +1,3 @@
+xŽ[
+Â0EýÎ*fÊäÕ¤ "¸W0“‡-ØFâtÿÝ—çpS[–YÀ˜x^
+Díb CLhutɉ}¥8X*4Zí¬sY½¨—UÀ‘AÃÖ ÌX3‡R«Mµ¶) s6è¼¢M¦ÖážšÜ&Jm…ó;}Çõ±Ðü<¥¶\@›à‚ÑÞpÄ€¨vº?”ò«jÛºLð«¨Ø?Hå \ No newline at end of file
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/tests/resources/submodules/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
new file mode 100644
index 000000000..f8588696b
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
@@ -0,0 +1,2 @@
+x;j1DëmdÓú·À˜ÇŽ|M«µ3`ŒV{ >€³âQ¯ ¸·vL0I?Í!š4–Z=Ê! ×¦8²F¢Ã’!rÖsQßyÈ9]$DŽ&„l6AÇ>jFWüÒµ IKNiûë§Z¢%¡SˆŒ‘
+‹Ò ­ÅʉøU~̽øä>'¼ï™û ¯wþ ×[ËÇ× ÷öÚDGÚ¡±ðŒQ-ºMù«>dܶ‘OÞáÒò}í\à8g_ШÂoYr \ No newline at end of file
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750 b/tests/resources/submodules/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
new file mode 100644
index 000000000..29c8e824d
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
@@ -0,0 +1,3 @@
+xŽQ
+!@ûösBQ"‚ŽÐ ÆÙ± rÍîßÒú{<xð¤·öàîƪ
+™HlJSer!ZPTe*Žj°UÝÑEo^¼ê2 (†ˆ¬XSÅ€ED‘ƒO<Y¦šj$2üs_á&} ¸Î,}Ó[~p¹7~<ÒÛ:Ÿ £°·ÉZ³Ùípè?­1_ûåC0 \ No newline at end of file
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/tests/resources/submodules/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
new file mode 100644
index 000000000..d0d7e736e
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/tests/resources/submodules/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
new file mode 100644
index 000000000..18a7f61c2
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12 b/tests/resources/submodules/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
new file mode 100644
index 000000000..d95254674
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 b/tests/resources/submodules/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
new file mode 100644
index 000000000..f460f2547
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
@@ -0,0 +1,2 @@
+xÌA
+Â0…a×9ÅìIÒ ™€ˆp'î§1Ѷ‘v\x{cáýðVŸpƒvWûgŠ¾ÇŽ0xº[ ]"g†#{rDÆ Cot ­äûN œU $­ò?9-p+1Í^¤ÀQx®¯Ï9O\ÆC¬Ó Œm'D {mµVêú(+´ñælè¶,Þ \ No newline at end of file
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593 b/tests/resources/submodules/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
new file mode 100644
index 000000000..f613670e2
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 b/tests/resources/submodules/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
new file mode 100644
index 000000000..0817229bc
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
@@ -0,0 +1,3 @@
+xKj1D³Ö)zçUBëÛ-0ÁuV9¦Õò<#£È÷ÏȲ+ŠW<Jú¶Ý&8Ê/s¨‚e‹µµÈ•KJ­«½S
+ØRvÌÁ{©æQ†îr«äY¹QN$H\Eµ²Íè=6áX5¦òÇK Fr)·(‰dC‡Î†”­–œ—jÊs®}À—ô9ác-Òw8Ëo¸\·r»¿IßÞÁ:
+l}F‚W$Ds´Ç£©ÿÙšOW…e”]V8-ÃÌÈ"U \ No newline at end of file
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd b/tests/resources/submodules/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
new file mode 100644
index 000000000..75f541f10
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
@@ -0,0 +1,3 @@
+xŽQ
+Â0DýÎ)öʦ»I<‚'ØlR+˜Fj¼¿EoàÏ0<xÃh«õÞa Üõµ]È™­åXUlÞPF)Åz‘4yó”µ,\r 'SÂÄ-mI4
+‘Xhô”&òÌFÞ}n+\µõ—Y´-p|é·œoUz;-‘aÑlt{ØË?®I«,:ÃoÚR̳cHK \ No newline at end of file
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f b/tests/resources/submodules/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
new file mode 100644
index 000000000..a67d6e647
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/submodules/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644
index 000000000..711223894
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0 b/tests/resources/submodules/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
new file mode 100644
index 000000000..b135eccda
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3 b/tests/resources/submodules/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
new file mode 100644
index 000000000..82e2790e8
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 b/tests/resources/submodules/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
new file mode 100644
index 000000000..697c94c92
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/tests/resources/submodules/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
new file mode 100644
index 000000000..112998d42
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765 b/tests/resources/submodules/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
new file mode 100644
index 000000000..12bf5f3e3
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
new file mode 100644
index 000000000..5068f2818
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
new file mode 100644
index 000000000..a6a1f3020
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
new file mode 100644
index 000000000..94c3c71da
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
new file mode 100644
index 000000000..74c7fe4f3
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
new file mode 100644
index 000000000..555cfa977
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
new file mode 100644
index 000000000..4d539ed0a
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/packed-refs b/tests/resources/submodules/testrepo/.gitted/packed-refs
new file mode 100644
index 000000000..9c0433e1c
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/packed-refs
@@ -0,0 +1,12 @@
+# pack-refs with: peeled
+b25fa35b38051e4ae45d4222e795f9df2e43f1d1 refs/tags/test
+^e90810b8df3e80c413d903f631643c716887138d
+1385f264afb75a56a5bec74243be9b367ba4ca08 refs/tags/point_to_blob
+7b4384978d2493e851f9cca7858815fac9b10980 refs/tags/e90810b
+^e90810b8df3e80c413d903f631643c716887138d
+e90810b8df3e80c413d903f631643c716887138d refs/remotes/origin/test
+763d71aadf09a7951596c9746c024e7eece7c7af refs/remotes/origin/subtrees
+4a202b346bb0fb0db7eff3cffeb3c70babbd2045 refs/remotes/origin/packed-test
+41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9 refs/remotes/origin/packed
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750 refs/remotes/origin/master
+a4a7dce85cf63874e984719f4fdd239f5145052f refs/remotes/origin/br2
diff --git a/tests/resources/submodules/testrepo/.gitted/refs/heads/master b/tests/resources/submodules/testrepo/.gitted/refs/heads/master
new file mode 100644
index 000000000..3d8f0a402
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/refs/heads/master
@@ -0,0 +1 @@
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750
diff --git a/tests/resources/submodules/testrepo/.gitted/refs/remotes/origin/HEAD b/tests/resources/submodules/testrepo/.gitted/refs/remotes/origin/HEAD
new file mode 100644
index 000000000..6efe28fff
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/submodules/testrepo/README b/tests/resources/submodules/testrepo/README
new file mode 100644
index 000000000..a8233120f
--- /dev/null
+++ b/tests/resources/submodules/testrepo/README
@@ -0,0 +1 @@
+hey there
diff --git a/tests/resources/submodules/testrepo/branch_file.txt b/tests/resources/submodules/testrepo/branch_file.txt
new file mode 100644
index 000000000..3697d64be
--- /dev/null
+++ b/tests/resources/submodules/testrepo/branch_file.txt
@@ -0,0 +1,2 @@
+hi
+bye!
diff --git a/tests/resources/submodules/testrepo/new.txt b/tests/resources/submodules/testrepo/new.txt
new file mode 100644
index 000000000..a71586c1d
--- /dev/null
+++ b/tests/resources/submodules/testrepo/new.txt
@@ -0,0 +1 @@
+my new file
diff --git a/tests/resources/submodules/unmodified b/tests/resources/submodules/unmodified
new file mode 100644
index 000000000..092bfb9bd
--- /dev/null
+++ b/tests/resources/submodules/unmodified
@@ -0,0 +1 @@
+yo
diff --git a/tests/resources/submodules/untracked b/tests/resources/submodules/untracked
new file mode 100644
index 000000000..092bfb9bd
--- /dev/null
+++ b/tests/resources/submodules/untracked
@@ -0,0 +1 @@
+yo
diff --git a/tests/resources/template/branches/.gitignore b/tests/resources/template/branches/.gitignore
new file mode 100644
index 000000000..16868cedb
--- /dev/null
+++ b/tests/resources/template/branches/.gitignore
@@ -0,0 +1,2 @@
+# This file should not be copied, nor should the
+# containing directory, since it is effectively "empty"
diff --git a/tests/resources/template/description b/tests/resources/template/description
new file mode 100644
index 000000000..ff04c4c13
--- /dev/null
+++ b/tests/resources/template/description
@@ -0,0 +1 @@
+Edit this file 'description' to name the repository.
diff --git a/tests/resources/template/hooks/link.sample b/tests/resources/template/hooks/link.sample
new file mode 120000
index 000000000..771acc43b
--- /dev/null
+++ b/tests/resources/template/hooks/link.sample
@@ -0,0 +1 @@
+update.sample \ No newline at end of file
diff --git a/tests/resources/template/hooks/update.sample b/tests/resources/template/hooks/update.sample
new file mode 100755
index 000000000..3b5f41202
--- /dev/null
+++ b/tests/resources/template/hooks/update.sample
@@ -0,0 +1,9 @@
+#!/bin/sh
+#
+# A sample hook to make sure that the `git_repository_init_ext()` function
+# can correctly copy a hook over and set it up with the correct permissions.
+#
+# To enable a hook, you copy the file and remove the ".sample" suffix, but
+# in this case, we're just making sure it gets copied correctly.
+
+echo "$GIT_DIR"
diff --git a/tests/resources/template/info/exclude b/tests/resources/template/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/template/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/testrepo.git/FETCH_HEAD b/tests/resources/testrepo.git/FETCH_HEAD
new file mode 100644
index 000000000..48446265c
--- /dev/null
+++ b/tests/resources/testrepo.git/FETCH_HEAD
@@ -0,0 +1,2 @@
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750 branch 'master' of git://example.com/git/testrepo.git
+258f0e2a959a364e40ed6603d5d44fbb24765b10 not-for-merge branch 'haacked' of git://example.com/git/testrepo.git
diff --git a/tests/resources/testrepo.git/HEAD b/tests/resources/testrepo.git/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/testrepo.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/testrepo.git/HEAD_TRACKER b/tests/resources/testrepo.git/HEAD_TRACKER
new file mode 100644
index 000000000..40d876b4c
--- /dev/null
+++ b/tests/resources/testrepo.git/HEAD_TRACKER
@@ -0,0 +1 @@
+ref: HEAD
diff --git a/tests/resources/testrepo.git/config b/tests/resources/testrepo.git/config
new file mode 100644
index 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/resources/testrepo.git/index b/tests/resources/testrepo.git/index
new file mode 100644
index 000000000..a27fb9c96
--- /dev/null
+++ b/tests/resources/testrepo.git/index
Binary files differ
diff --git a/tests/resources/testrepo.git/logs/HEAD b/tests/resources/testrepo.git/logs/HEAD
new file mode 100644
index 000000000..9413b72cc
--- /dev/null
+++ b/tests/resources/testrepo.git/logs/HEAD
@@ -0,0 +1,7 @@
+0000000000000000000000000000000000000000 be3563ae3f795b2b4353bcce3a527ad0a4f7f644 Ben Straub <bstraub@github.com> 1335806563 -0700 clone: from /Users/ben/src/libgit2/tests/resources/testrepo.git
+be3563ae3f795b2b4353bcce3a527ad0a4f7f644 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Ben Straub <bstraub@github.com> 1335806603 -0900 commit:
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750 5b5b025afb0b4c913b4c338a42934a3863bf3644 Ben Straub <bstraub@github.com> 1335806604 -0900 checkout: moving from master to 5b5b025
+5b5b025afb0b4c913b4c338a42934a3863bf3644 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Ben Straub <bstraub@github.com> 1335806605 -0900 checkout: moving from 5b5b025 to master
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750 c47800c7266a2be04c571c04d5a6614691ea99bd Ben Straub <bstraub@github.com> 1335806608 -0900 checkout: moving from master to br2
+c47800c7266a2be04c571c04d5a6614691ea99bd a4a7dce85cf63874e984719f4fdd239f5145052f Ben Straub <bstraub@github.com> 1335806617 -0900 commit: checking in
+a4a7dce85cf63874e984719f4fdd239f5145052f a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Ben Straub <bstraub@github.com> 1335806621 -0900 checkout: moving from br2 to master
diff --git a/tests/resources/testrepo.git/logs/refs/heads/br2 b/tests/resources/testrepo.git/logs/refs/heads/br2
new file mode 100644
index 000000000..4e27f6b8d
--- /dev/null
+++ b/tests/resources/testrepo.git/logs/refs/heads/br2
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 c47800c7266a2be04c571c04d5a6614691ea99bd Ben Straub <bstraub@github.com> 1335806608 -0700 branch: Created from refs/remotes/origin/br2
+a4a7dce85cf63874e984719f4fdd239f5145052f a4a7dce85cf63874e984719f4fdd239f5145052f Ben Straub <bstraub@github.com> 1335806617 -0700 commit: checking in
diff --git a/tests/resources/testrepo.git/logs/refs/heads/master b/tests/resources/testrepo.git/logs/refs/heads/master
new file mode 100644
index 000000000..e1c729a45
--- /dev/null
+++ b/tests/resources/testrepo.git/logs/refs/heads/master
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 be3563ae3f795b2b4353bcce3a527ad0a4f7f644 Ben Straub <bstraub@github.com> 1335806563 -0800 clone: from /Users/ben/src/libgit2/tests/resources/testrepo.git
+be3563ae3f795b2b4353bcce3a527ad0a4f7f644 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Ben Straub <bstraub@github.com> 1335806603 -0800 commit: checking in
diff --git a/tests/resources/testrepo.git/logs/refs/heads/not-good b/tests/resources/testrepo.git/logs/refs/heads/not-good
new file mode 100644
index 000000000..bfbeacb8a
--- /dev/null
+++ b/tests/resources/testrepo.git/logs/refs/heads/not-good
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Ben Straub <bstraub@github.com> 1336761944 -0700 branch: Created from master
diff --git a/tests/resources/testrepo.git/logs/refs/remotes/origin/HEAD b/tests/resources/testrepo.git/logs/refs/remotes/origin/HEAD
new file mode 100644
index 000000000..f1aac6d0f
--- /dev/null
+++ b/tests/resources/testrepo.git/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 be3563ae3f795b2b4353bcce3a527ad0a4f7f644 Ben Straub <bstraub@github.com> 1335806563 -0700 clone: from /Users/ben/src/libgit2/tests/resources/testrepo.git
diff --git a/tests/resources/testrepo.git/logs/refs/remotes/test/master b/tests/resources/testrepo.git/logs/refs/remotes/test/master
new file mode 100644
index 000000000..8d49ba3e0
--- /dev/null
+++ b/tests/resources/testrepo.git/logs/refs/remotes/test/master
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Ben Straub <bstraub@github.com> 1335806565 -0800 update by push
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750 be3563ae3f795b2b4353bcce3a527ad0a4f7f644 Ben Straub <bstraub@github.com> 1335806688 -0800 update by push
diff --git a/tests/resources/testrepo.git/objects/08/b041783f40edfe12bb406c9c9a8a040177c125 b/tests/resources/testrepo.git/objects/08/b041783f40edfe12bb406c9c9a8a040177c125
new file mode 100644
index 000000000..d1c032fce
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/08/b041783f40edfe12bb406c9c9a8a040177c125
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/tests/resources/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
new file mode 100644
index 000000000..cedb2a22e
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/tests/resources/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
new file mode 100644
index 000000000..93a16f146
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccd b/tests/resources/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccd
new file mode 100644
index 000000000..ba0bfb30c
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccd
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd b/tests/resources/testrepo.git/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd
new file mode 100644
index 000000000..3ec541288
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/1b/8cbad43e867676df601306689fe7c3def5e689 b/tests/resources/testrepo.git/objects/1b/8cbad43e867676df601306689fe7c3def5e689
new file mode 100644
index 000000000..6048d4bad
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/1b/8cbad43e867676df601306689fe7c3def5e689
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b b/tests/resources/testrepo.git/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
new file mode 100644
index 000000000..225c45734
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10 b/tests/resources/testrepo.git/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10
new file mode 100644
index 000000000..cb1ed5712
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d b/tests/resources/testrepo.git/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
new file mode 100644
index 000000000..df40d99af
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7 b/tests/resources/testrepo.git/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7
new file mode 100644
index 000000000..0a1500a6f
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54 b/tests/resources/testrepo.git/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
new file mode 100644
index 000000000..321eaa867
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc b/tests/resources/testrepo.git/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
new file mode 100644
index 000000000..9bb5b623b
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
new file mode 100644
index 000000000..7ca4ceed5
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/tests/resources/testrepo.git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
new file mode 100644
index 000000000..8953b6cef
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
@@ -0,0 +1,2 @@
+xŽQ
+Â0DýÎ)öÊ6›Í¦ "xO°‰‰-ØFb¼¿EoàÏ0 ¼Ç¤º,ske×[ÎPn8R,EpD?±gŸ}Ê^3² âÙ<µåµGŽhYKÄèÒ8ЖDAÉ)¿ÉÈ;gôݧÚàšjïp™4ÕŽ¯ô-çû¢óãêr‚ÁŠ;°s°GA4Ûº=ìùÖ(ôin7øIÌKÍFE \ No newline at end of file
diff --git a/tests/resources/testrepo.git/objects/4a/23e2e65ad4e31c4c9db7dc746650bfad082679 b/tests/resources/testrepo.git/objects/4a/23e2e65ad4e31c4c9db7dc746650bfad082679
new file mode 100644
index 000000000..18e3964b3
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/4a/23e2e65ad4e31c4c9db7dc746650bfad082679
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea b/tests/resources/testrepo.git/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea
new file mode 100644
index 000000000..b4e5aa186
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91 b/tests/resources/testrepo.git/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91
new file mode 100644
index 000000000..351cff823
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/tests/resources/testrepo.git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
new file mode 100644
index 000000000..c1f22c54f
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
@@ -0,0 +1,2 @@
+xŽÛ 1EýNi@™Ék2 "X‚$ÙYW0YcÿíÀ¿Ã…s¸¥ÕzïÚÚõMDÏ€0æœ8!¶†ÉÌÞs‰ XŠªgÚdí::@X0»P¢wÙ"F/‰‰œÍRàˆUz÷¥múZZïú²¤ÒV}|•/œo5݇ÒêI£!¬1z Æ:vùÇUim}ê/¢>
+öF- \ No newline at end of file
diff --git a/tests/resources/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/tests/resources/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
new file mode 100644
index 000000000..2ef4faa0f
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af b/tests/resources/testrepo.git/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
new file mode 100644
index 000000000..716b0c64b
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
@@ -0,0 +1 @@
+xŽAj!³ö?0¨£ßÂ09Êo}HÚ6¨}ÿôjUPP©ÕZ&Yÿø˜ AÔ›±€pŒÁFdë¼÷pz[fŽYŒ½PÒqLJ.,Z§`™Å®Ð.ù`’vÙ ³q $Æ5+9çOëtœû>Û/úDE/龡W¯ï*e¿§VŸdf1>ð覭Öê²×äÄ›¹úÊ™F« ­ìTŽÙhœk.i¶^0Ô?P¼R, \ No newline at end of file
diff --git a/tests/resources/testrepo.git/objects/7b/4384978d2493e851f9cca7858815fac9b10980 b/tests/resources/testrepo.git/objects/7b/4384978d2493e851f9cca7858815fac9b10980
new file mode 100644
index 000000000..23c462f34
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/7b/4384978d2493e851f9cca7858815fac9b10980
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/tests/resources/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
new file mode 100644
index 000000000..2f9b6b6e3
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/tests/resources/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479
new file mode 100644
index 000000000..5df58dda5
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe b/tests/resources/testrepo.git/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe
new file mode 100644
index 000000000..71019a636
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe
@@ -0,0 +1,2 @@
+xŒM F]sŠ¹€†Ÿ41ÆxÝ(­I‹ÁéÂÛKݽ/_ÞãP@¡ÚÕø¢!8›)es
+” ¥N&FGSÆ„¹hÑ{+ßCç‰÷ÆZzvØF¡7ZàÎ-¬Îñó‡k™x\ã¡[PÆ8ï´ôGØK/¥^© lÊ>.4 \ No newline at end of file
diff --git a/tests/resources/testrepo.git/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162 b/tests/resources/testrepo.git/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
new file mode 100644
index 000000000..4cc3f4dff
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
@@ -0,0 +1 @@
+x+)JMU044b040031QrutñueX¡l¨ðmmA‹m›Ì£íJ}Gß;U‘T”˜—œŸ–™“ªWRQÂ`6ýš÷KÇ¥¶^/¾-*|òøWØ¥3P¥y©å`%ËEÛÞ±\&gŽÐ|Ÿ0§ÿ†{Ó1X \ No newline at end of file
diff --git a/tests/resources/testrepo.git/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4 b/tests/resources/testrepo.git/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
new file mode 100644
index 000000000..bf7b2bb68
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2 b/tests/resources/testrepo.git/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2
new file mode 100644
index 000000000..7f1cfb23c
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2
@@ -0,0 +1,2 @@
+xŽM
+Â0F]ç³d2¤ñ®<A~&´`­ÄôþVàæãmïËë²ÌÈÒ¡7Uà$äJöL9yM!¢GuœªH¤&UÈæ›>;ÔÂÁ…¬£³X†ÂEÈŽ5R±£ ÛAÑE &n}ZÜæ<E}À=O[ÒÖáüÞéúÓ¼^À,ã^†#¢É¿ƒ]ÿPÍ`>™A¹ \ No newline at end of file
diff --git a/tests/resources/testrepo.git/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/tests/resources/testrepo.git/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
new file mode 100644
index 000000000..a79612435
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
@@ -0,0 +1,3 @@
+xŽ[
+Â0EýÎ*fÊäÕ¤ "¸W0“‡-ØFâtÿÝ—çpS[–YÀ˜x^
+Díb CLhutɉ}¥8X*4Zí¬sY½¨—UÀ‘AÃÖ ÌX3‡R«Mµ¶) s6è¼¢M¦ÖážšÜ&Jm…ó;}Çõ±Ðü<¥¶\@›à‚ÑÞpÄ€¨vº?”ò«jÛºLð«¨Ø?Hå \ No newline at end of file
diff --git a/tests/resources/testrepo.git/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/tests/resources/testrepo.git/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
new file mode 100644
index 000000000..f8588696b
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
@@ -0,0 +1,2 @@
+x;j1DëmdÓú·À˜ÇŽ|M«µ3`ŒV{ >€³âQ¯ ¸·vL0I?Í!š4–Z=Ê! ×¦8²F¢Ã’!rÖsQßyÈ9]$DŽ&„l6AÇ>jFWüÒµ IKNiûë§Z¢%¡SˆŒ‘
+‹Ò ­ÅʉøU~̽øä>'¼ï™û ¯wþ ×[ËÇ× ÷öÚDGÚ¡±ðŒQ-ºMù«>dܶ‘OÞáÒò}í\à8g_ШÂoYr \ No newline at end of file
diff --git a/tests/resources/testrepo.git/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750 b/tests/resources/testrepo.git/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
new file mode 100644
index 000000000..29c8e824d
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
@@ -0,0 +1,3 @@
+xŽQ
+!@ûösBQ"‚ŽÐ ÆÙ± rÍîßÒú{<xð¤·öàîƪ
+™HlJSer!ZPTe*Žj°UÝÑEo^¼ê2 (†ˆ¬XSÅ€ED‘ƒO<Y¦šj$2üs_á&} ¸Î,}Ó[~p¹7~<ÒÛ:Ÿ £°·ÉZ³Ùípè?­1_ûåC0 \ No newline at end of file
diff --git a/tests/resources/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/tests/resources/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
new file mode 100644
index 000000000..d0d7e736e
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/tests/resources/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
new file mode 100644
index 000000000..18a7f61c2
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/ae/90f12eea699729ed24555e40b9fd669da12a12 b/tests/resources/testrepo.git/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
new file mode 100644
index 000000000..d95254674
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 b/tests/resources/testrepo.git/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
new file mode 100644
index 000000000..f460f2547
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
@@ -0,0 +1,2 @@
+xÌA
+Â0…a×9ÅìIÒ ™€ˆp'î§1Ѷ‘v\x{cáýðVŸpƒvWûgŠ¾ÇŽ0xº[ ]"g†#{rDÆ Cot ­äûN œU $­ò?9-p+1Í^¤ÀQx®¯Ï9O\ÆC¬Ó Œm'D {mµVêú(+´ñælè¶,Þ \ No newline at end of file
diff --git a/tests/resources/testrepo.git/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593 b/tests/resources/testrepo.git/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
new file mode 100644
index 000000000..f613670e2
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 b/tests/resources/testrepo.git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
new file mode 100644
index 000000000..0817229bc
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
@@ -0,0 +1,3 @@
+xKj1D³Ö)zçUBëÛ-0ÁuV9¦Õò<#£È÷ÏȲ+ŠW<Jú¶Ý&8Ê/s¨‚e‹µµÈ•KJ­«½S
+ØRvÌÁ{©æQ†îr«äY¹QN$H\Eµ²Íè=6áX5¦òÇK Fr)·(‰dC‡Î†”­–œ—jÊs®}À—ô9ác-Òw8Ëo¸\·r»¿IßÞÁ:
+l}F‚W$Ds´Ç£©ÿÙšOW…e”]V8-ÃÌÈ"U \ No newline at end of file
diff --git a/tests/resources/testrepo.git/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd b/tests/resources/testrepo.git/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
new file mode 100644
index 000000000..75f541f10
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
@@ -0,0 +1,3 @@
+xŽQ
+Â0DýÎ)öʦ»I<‚'ØlR+˜Fj¼¿EoàÏ0<xÃh«õÞa Üõµ]È™­åXUlÞPF)Åz‘4yó”µ,\r 'SÂÄ-mI4
+‘Xhô”&òÌFÞ}n+\µõ—Y´-p|é·œoUz;-‘aÑlt{ØË?®I«,:ÃoÚR̳cHK \ No newline at end of file
diff --git a/tests/resources/testrepo.git/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659 b/tests/resources/testrepo.git/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659
new file mode 100644
index 000000000..f3b46b3ca
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f b/tests/resources/testrepo.git/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
new file mode 100644
index 000000000..a67d6e647
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759 b/tests/resources/testrepo.git/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759
new file mode 100644
index 000000000..2d47e6faf
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644
index 000000000..711223894
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0 b/tests/resources/testrepo.git/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
new file mode 100644
index 000000000..b135eccda
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3 b/tests/resources/testrepo.git/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
new file mode 100644
index 000000000..82e2790e8
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 b/tests/resources/testrepo.git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
new file mode 100644
index 000000000..697c94c92
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/tests/resources/testrepo.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92
new file mode 100644
index 000000000..112998d42
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/fd/093bff70906175335656e6ce6ae05783708765 b/tests/resources/testrepo.git/objects/fd/093bff70906175335656e6ce6ae05783708765
new file mode 100644
index 000000000..12bf5f3e3
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/fd/093bff70906175335656e6ce6ae05783708765
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09 b/tests/resources/testrepo.git/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09
new file mode 100644
index 000000000..158aef21f
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx b/tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
new file mode 100644
index 000000000..5068f2818
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack b/tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
new file mode 100644
index 000000000..a6a1f3020
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx b/tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
new file mode 100644
index 000000000..94c3c71da
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack b/tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
new file mode 100644
index 000000000..74c7fe4f3
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx b/tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
new file mode 100644
index 000000000..555cfa977
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack b/tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
new file mode 100644
index 000000000..4d539ed0a
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
Binary files differ
diff --git a/tests/resources/testrepo.git/packed-refs b/tests/resources/testrepo.git/packed-refs
new file mode 100644
index 000000000..52f5e876f
--- /dev/null
+++ b/tests/resources/testrepo.git/packed-refs
@@ -0,0 +1,3 @@
+# pack-refs with: peeled
+41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9 refs/heads/packed
+5b5b025afb0b4c913b4c338a42934a3863bf3644 refs/heads/packed-test
diff --git a/tests/resources/testrepo.git/refs/heads/br2 b/tests/resources/testrepo.git/refs/heads/br2
new file mode 100644
index 000000000..aab87e5e7
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/heads/br2
@@ -0,0 +1 @@
+a4a7dce85cf63874e984719f4fdd239f5145052f
diff --git a/tests/resources/testrepo.git/refs/heads/cannot-fetch b/tests/resources/testrepo.git/refs/heads/cannot-fetch
new file mode 100644
index 000000000..aab87e5e7
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/heads/cannot-fetch
@@ -0,0 +1 @@
+a4a7dce85cf63874e984719f4fdd239f5145052f
diff --git a/tests/resources/testrepo.git/refs/heads/chomped b/tests/resources/testrepo.git/refs/heads/chomped
new file mode 100644
index 000000000..0166a7f92
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/heads/chomped
@@ -0,0 +1 @@
+e90810b8df3e80c413d903f631643c716887138d \ No newline at end of file
diff --git a/tests/resources/testrepo.git/refs/heads/haacked b/tests/resources/testrepo.git/refs/heads/haacked
new file mode 100644
index 000000000..17f591222
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/heads/haacked
@@ -0,0 +1 @@
+258f0e2a959a364e40ed6603d5d44fbb24765b10
diff --git a/tests/resources/testrepo.git/refs/heads/master b/tests/resources/testrepo.git/refs/heads/master
new file mode 100644
index 000000000..3d8f0a402
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/heads/master
@@ -0,0 +1 @@
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750
diff --git a/tests/resources/testrepo.git/refs/heads/not-good b/tests/resources/testrepo.git/refs/heads/not-good
new file mode 100644
index 000000000..3d8f0a402
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/heads/not-good
@@ -0,0 +1 @@
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750
diff --git a/tests/resources/testrepo.git/refs/heads/packed-test b/tests/resources/testrepo.git/refs/heads/packed-test
new file mode 100644
index 000000000..f2c14ad83
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/heads/packed-test
@@ -0,0 +1 @@
+4a202b346bb0fb0db7eff3cffeb3c70babbd2045
diff --git a/tests/resources/testrepo.git/refs/heads/subtrees b/tests/resources/testrepo.git/refs/heads/subtrees
new file mode 100644
index 000000000..ad27e0b13
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/heads/subtrees
@@ -0,0 +1 @@
+763d71aadf09a7951596c9746c024e7eece7c7af
diff --git a/tests/resources/testrepo.git/refs/heads/test b/tests/resources/testrepo.git/refs/heads/test
new file mode 100644
index 000000000..399c4c73e
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/heads/test
@@ -0,0 +1 @@
+e90810b8df3e80c413d903f631643c716887138d
diff --git a/tests/resources/testrepo.git/refs/heads/track-local b/tests/resources/testrepo.git/refs/heads/track-local
new file mode 100644
index 000000000..f37febb2c
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/heads/track-local
@@ -0,0 +1 @@
+9fd738e8f7967c078dceed8190330fc8648ee56a
diff --git a/tests/resources/testrepo.git/refs/heads/trailing b/tests/resources/testrepo.git/refs/heads/trailing
new file mode 100644
index 000000000..2a4a6e62f
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/heads/trailing
@@ -0,0 +1 @@
+e90810b8df3e80c413d903f631643c716887138d
diff --git a/tests/resources/testrepo.git/refs/notes/fanout b/tests/resources/testrepo.git/refs/notes/fanout
new file mode 100644
index 000000000..1f1703631
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/notes/fanout
@@ -0,0 +1 @@
+d07b0f9a8c89f1d9e74dc4fce6421dec5ef8a659
diff --git a/tests/resources/testrepo.git/refs/remotes/test/master b/tests/resources/testrepo.git/refs/remotes/test/master
new file mode 100644
index 000000000..9536ad89c
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/remotes/test/master
@@ -0,0 +1 @@
+be3563ae3f795b2b4353bcce3a527ad0a4f7f644
diff --git a/tests/resources/testrepo.git/refs/tags/annotated_tag_to_blob b/tests/resources/testrepo.git/refs/tags/annotated_tag_to_blob
new file mode 100644
index 000000000..6c146d6e3
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/tags/annotated_tag_to_blob
@@ -0,0 +1 @@
+521d87c1ec3aef9824daf6d96cc0ae3710766d91
diff --git a/tests/resources/testrepo.git/refs/tags/e90810b b/tests/resources/testrepo.git/refs/tags/e90810b
new file mode 100644
index 000000000..584495d3c
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/tags/e90810b
@@ -0,0 +1 @@
+7b4384978d2493e851f9cca7858815fac9b10980
diff --git a/tests/resources/testrepo.git/refs/tags/hard_tag b/tests/resources/testrepo.git/refs/tags/hard_tag
new file mode 100644
index 000000000..59ce65649
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/tags/hard_tag
@@ -0,0 +1 @@
+849a5e34a26815e821f865b8479f5815a47af0fe
diff --git a/tests/resources/testrepo.git/refs/tags/point_to_blob b/tests/resources/testrepo.git/refs/tags/point_to_blob
new file mode 100644
index 000000000..f874a3ffc
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/tags/point_to_blob
@@ -0,0 +1 @@
+1385f264afb75a56a5bec74243be9b367ba4ca08
diff --git a/tests/resources/testrepo.git/refs/tags/taggerless b/tests/resources/testrepo.git/refs/tags/taggerless
new file mode 100644
index 000000000..f960c7d62
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/tags/taggerless
@@ -0,0 +1 @@
+4a23e2e65ad4e31c4c9db7dc746650bfad082679
diff --git a/tests/resources/testrepo.git/refs/tags/test b/tests/resources/testrepo.git/refs/tags/test
new file mode 100644
index 000000000..6ee952a03
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/tags/test
@@ -0,0 +1 @@
+b25fa35b38051e4ae45d4222e795f9df2e43f1d1
diff --git a/tests/resources/testrepo.git/refs/tags/wrapped_tag b/tests/resources/testrepo.git/refs/tags/wrapped_tag
new file mode 100644
index 000000000..59ce65649
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/tags/wrapped_tag
@@ -0,0 +1 @@
+849a5e34a26815e821f865b8479f5815a47af0fe
diff --git a/tests/resources/testrepo/.gitted/HEAD b/tests/resources/testrepo/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/testrepo/.gitted/HEAD_TRACKER b/tests/resources/testrepo/.gitted/HEAD_TRACKER
new file mode 100644
index 000000000..40d876b4c
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/HEAD_TRACKER
@@ -0,0 +1 @@
+ref: HEAD
diff --git a/tests/resources/testrepo/.gitted/config b/tests/resources/testrepo/.gitted/config
new file mode 100644
index 000000000..d0114012f
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/config
@@ -0,0 +1,8 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+[remote "test"]
+ url = git://github.com/libgit2/libgit2
+ fetch = +refs/heads/*:refs/remotes/test/*
diff --git a/tests/resources/testrepo/.gitted/index b/tests/resources/testrepo/.gitted/index
new file mode 100644
index 000000000..a27fb9c96
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/index
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/09/9fabac3a9ea935598528c27f866e34089c2eff b/tests/resources/testrepo/.gitted/objects/09/9fabac3a9ea935598528c27f866e34089c2eff
new file mode 100644
index 000000000..c60c78fb5
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/09/9fabac3a9ea935598528c27f866e34089c2eff
@@ -0,0 +1 @@
+xÎQ P¿9Å^@³ÂB!1F½‚'€î¢ÒJ?¼½Õ#ø7™ÉK¦ŸJhM›VE€,³·.3û¼§Þ¦ˆ‚ÔuVsHè-;õŠUÆÑÙ,œMˆ’…P³Iɉ&ÎÄ”×ìסŠK»O.2µո$8¤ùN·¡Ý—´ë§r„½!½l±CTk»lòUgfˆ0¿ËsêÓG( \ No newline at end of file
diff --git a/tests/resources/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/tests/resources/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
new file mode 100644
index 000000000..cedb2a22e
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e b/tests/resources/testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e
new file mode 100644
index 000000000..b7d944fa1
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925 b/tests/resources/testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925
new file mode 100644
index 000000000..d37b93e4f
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/tests/resources/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
new file mode 100644
index 000000000..93a16f146
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd b/tests/resources/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
new file mode 100644
index 000000000..ba0bfb30c
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b b/tests/resources/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
new file mode 100644
index 000000000..225c45734
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d b/tests/resources/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
new file mode 100644
index 000000000..df40d99af
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54 b/tests/resources/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
new file mode 100644
index 000000000..321eaa867
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc b/tests/resources/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
new file mode 100644
index 000000000..9bb5b623b
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
new file mode 100644
index 000000000..7ca4ceed5
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/45/dd856fdd4d89b884c340ba0e047752d9b085d6 b/tests/resources/testrepo/.gitted/objects/45/dd856fdd4d89b884c340ba0e047752d9b085d6
new file mode 100644
index 000000000..a83ed9763
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/45/dd856fdd4d89b884c340ba0e047752d9b085d6
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/tests/resources/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
new file mode 100644
index 000000000..8953b6cef
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
@@ -0,0 +1,2 @@
+xŽQ
+Â0DýÎ)öÊ6›Í¦ "xO°‰‰-ØFb¼¿EoàÏ0 ¼Ç¤º,ske×[ÎPn8R,EpD?±gŸ}Ê^3² âÙ<µåµGŽhYKÄèÒ8ЖDAÉ)¿ÉÈ;gôݧÚàšjïp™4ÕŽ¯ô-çû¢óãêr‚ÁŠ;°s°GA4Ûº=ìùÖ(ôin7øIÌKÍFE \ No newline at end of file
diff --git a/tests/resources/testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63 b/tests/resources/testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63
new file mode 100644
index 000000000..e9150214b
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/tests/resources/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
new file mode 100644
index 000000000..c1f22c54f
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
@@ -0,0 +1,2 @@
+xŽÛ 1EýNi@™Ék2 "X‚$ÙYW0YcÿíÀ¿Ã…s¸¥ÕzïÚÚõMDÏ€0æœ8!¶†ÉÌÞs‰ XŠªgÚdí::@X0»P¢wÙ"F/‰‰œÍRàˆUz÷¥múZZïú²¤ÒV}|•/œo5݇ÒêI£!¬1z Æ:vùÇUim}ê/¢>
+öF- \ No newline at end of file
diff --git a/tests/resources/testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc b/tests/resources/testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc
new file mode 100644
index 000000000..b669961d8
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735 b/tests/resources/testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735
new file mode 100644
index 000000000..9ff5eb2b5
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/6b/377958d8c6a4906e8573b53672a1a23a4e8ce6 b/tests/resources/testrepo/.gitted/objects/6b/377958d8c6a4906e8573b53672a1a23a4e8ce6
new file mode 100644
index 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/resources/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/tests/resources/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
new file mode 100644
index 000000000..2ef4faa0f
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af b/tests/resources/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
new file mode 100644
index 000000000..716b0c64b
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
@@ -0,0 +1 @@
+xŽAj!³ö?0¨£ßÂ09Êo}HÚ6¨}ÿôjUPP©ÕZ&Yÿø˜ AÔ›±€pŒÁFdë¼÷pz[fŽYŒ½PÒqLJ.,Z§`™Å®Ð.ù`’vÙ ³q $Æ5+9çOëtœû>Û/úDE/龡W¯ï*e¿§VŸdf1>ð覭Öê²×äÄ›¹úÊ™F« ­ìTŽÙhœk.i¶^0Ô?P¼R, \ No newline at end of file
diff --git a/tests/resources/testrepo/.gitted/objects/7b/2417a23b63e1fdde88c80e14b33247c6e5785a b/tests/resources/testrepo/.gitted/objects/7b/2417a23b63e1fdde88c80e14b33247c6e5785a
new file mode 100644
index 000000000..db778aaae
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/7b/2417a23b63e1fdde88c80e14b33247c6e5785a
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980 b/tests/resources/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980
new file mode 100644
index 000000000..23c462f34
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/tests/resources/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
new file mode 100644
index 000000000..2f9b6b6e3
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/tests/resources/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
new file mode 100644
index 000000000..5df58dda5
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/87/380ae84009e9c503506c2f6143a4fc6c60bf80 b/tests/resources/testrepo/.gitted/objects/87/380ae84009e9c503506c2f6143a4fc6c60bf80
new file mode 100644
index 000000000..3042f5790
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/87/380ae84009e9c503506c2f6143a4fc6c60bf80
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162 b/tests/resources/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
new file mode 100644
index 000000000..4cc3f4dff
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
@@ -0,0 +1 @@
+x+)JMU044b040031QrutñueX¡l¨ðmmA‹m›Ì£íJ}Gß;U‘T”˜—œŸ–™“ªWRQÂ`6ýš÷KÇ¥¶^/¾-*|òøWØ¥3P¥y©å`%ËEÛÞ±\&gŽÐ|Ÿ0§ÿ†{Ó1X \ No newline at end of file
diff --git a/tests/resources/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4 b/tests/resources/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
new file mode 100644
index 000000000..bf7b2bb68
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/tests/resources/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
new file mode 100644
index 000000000..a79612435
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
@@ -0,0 +1,3 @@
+xŽ[
+Â0EýÎ*fÊäÕ¤ "¸W0“‡-ØFâtÿÝ—çpS[–YÀ˜x^
+Díb CLhutɉ}¥8X*4Zí¬sY½¨—UÀ‘AÃÖ ÌX3‡R«Mµ¶) s6è¼¢M¦ÖážšÜ&Jm…ó;}Çõ±Ðü<¥¶\@›à‚ÑÞpÄ€¨vº?”ò«jÛºLð«¨Ø?Hå \ No newline at end of file
diff --git a/tests/resources/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/tests/resources/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
new file mode 100644
index 000000000..f8588696b
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
@@ -0,0 +1,2 @@
+x;j1DëmdÓú·À˜ÇŽ|M«µ3`ŒV{ >€³âQ¯ ¸·vL0I?Í!š4–Z=Ê! ×¦8²F¢Ã’!rÖsQßyÈ9]$DŽ&„l6AÇ>jFWüÒµ IKNiûë§Z¢%¡SˆŒ‘
+‹Ò ­ÅʉøU~̽øä>'¼ï™û ¯wþ ×[ËÇ× ÷öÚDGÚ¡±ðŒQ-ºMù«>dܶ‘OÞáÒò}í\à8g_ШÂoYr \ No newline at end of file
diff --git a/tests/resources/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750 b/tests/resources/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
new file mode 100644
index 000000000..29c8e824d
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
@@ -0,0 +1,3 @@
+xŽQ
+!@ûösBQ"‚ŽÐ ÆÙ± rÍîßÒú{<xð¤·öàîƪ
+™HlJSer!ZPTe*Žj°UÝÑEo^¼ê2 (†ˆ¬XSÅ€ED‘ƒO<Y¦šj$2üs_á&} ¸Î,}Ó[~p¹7~<ÒÛ:Ÿ £°·ÉZ³Ùípè?­1_ûåC0 \ No newline at end of file
diff --git a/tests/resources/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/tests/resources/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
new file mode 100644
index 000000000..d0d7e736e
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/tests/resources/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
new file mode 100644
index 000000000..18a7f61c2
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12 b/tests/resources/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
new file mode 100644
index 000000000..d95254674
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 b/tests/resources/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
new file mode 100644
index 000000000..f460f2547
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
@@ -0,0 +1,2 @@
+xÌA
+Â0…a×9ÅìIÒ ™€ˆp'î§1Ѷ‘v\x{cáýðVŸpƒvWûgŠ¾ÇŽ0xº[ ]"g†#{rDÆ Cot ­äûN œU $­ò?9-p+1Í^¤ÀQx®¯Ï9O\ÆC¬Ó Œm'D {mµVêú(+´ñælè¶,Þ \ No newline at end of file
diff --git a/tests/resources/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593 b/tests/resources/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
new file mode 100644
index 000000000..f613670e2
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 b/tests/resources/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
new file mode 100644
index 000000000..0817229bc
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
@@ -0,0 +1,3 @@
+xKj1D³Ö)zçUBëÛ-0ÁuV9¦Õò<#£È÷ÏȲ+ŠW<Jú¶Ý&8Ê/s¨‚e‹µµÈ•KJ­«½S
+ØRvÌÁ{©æQ†îr«äY¹QN$H\Eµ²Íè=6áX5¦òÇK Fr)·(‰dC‡Î†”­–œ—jÊs®}À—ô9ác-Òw8Ëo¸\·r»¿IßÞÁ:
+l}F‚W$Ds´Ç£©ÿÙšOW…e”]V8-ÃÌÈ"U \ No newline at end of file
diff --git a/tests/resources/testrepo/.gitted/objects/c0/528fd6cc988c0a40ce0be11bc192fc8dc5346e b/tests/resources/testrepo/.gitted/objects/c0/528fd6cc988c0a40ce0be11bc192fc8dc5346e
new file mode 100644
index 000000000..0401ab489
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/c0/528fd6cc988c0a40ce0be11bc192fc8dc5346e
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd b/tests/resources/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
new file mode 100644
index 000000000..75f541f10
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
@@ -0,0 +1,3 @@
+xŽQ
+Â0DýÎ)öʦ»I<‚'ØlR+˜Fj¼¿EoàÏ0<xÃh«õÞa Üõµ]È™­åXUlÞPF)Åz‘4yó”µ,\r 'SÂÄ-mI4
+‘Xhô”&òÌFÞ}n+\µõ—Y´-p|é·œoUz;-‘aÑlt{ØË?®I«,:ÃoÚR̳cHK \ No newline at end of file
diff --git a/tests/resources/testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9 b/tests/resources/testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9
new file mode 100644
index 000000000..7620c514f
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83 b/tests/resources/testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83
new file mode 100644
index 000000000..00940f0f2
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f b/tests/resources/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
new file mode 100644
index 000000000..a67d6e647
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644
index 000000000..711223894
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0 b/tests/resources/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
new file mode 100644
index 000000000..b135eccda
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3 b/tests/resources/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
new file mode 100644
index 000000000..82e2790e8
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 b/tests/resources/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
new file mode 100644
index 000000000..697c94c92
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/tests/resources/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
new file mode 100644
index 000000000..112998d42
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765 b/tests/resources/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
new file mode 100644
index 000000000..12bf5f3e3
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx b/tests/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
new file mode 100644
index 000000000..5068f2818
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack b/tests/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
new file mode 100644
index 000000000..a6a1f3020
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx b/tests/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
new file mode 100644
index 000000000..94c3c71da
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack b/tests/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
new file mode 100644
index 000000000..74c7fe4f3
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx b/tests/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
new file mode 100644
index 000000000..555cfa977
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack b/tests/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
new file mode 100644
index 000000000..4d539ed0a
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/packed-refs b/tests/resources/testrepo/.gitted/packed-refs
new file mode 100644
index 000000000..6018a19d2
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/packed-refs
@@ -0,0 +1,4 @@
+# pack-refs with: peeled
+41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9 refs/heads/packed
+5b5b025afb0b4c913b4c338a42934a3863bf3644 refs/heads/packed-test
+b25fa35b38051e4ae45d4222e795f9df2e43f1d1 refs/tags/packed-tag
diff --git a/tests/resources/testrepo/.gitted/refs/heads/br2 b/tests/resources/testrepo/.gitted/refs/heads/br2
new file mode 100644
index 000000000..aab87e5e7
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/heads/br2
@@ -0,0 +1 @@
+a4a7dce85cf63874e984719f4fdd239f5145052f
diff --git a/tests/resources/testrepo/.gitted/refs/heads/dir b/tests/resources/testrepo/.gitted/refs/heads/dir
new file mode 100644
index 000000000..4567d37fa
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/heads/dir
@@ -0,0 +1 @@
+144344043ba4d4a405da03de3844aa829ae8be0e
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/resources/testrepo/.gitted/refs/heads/master b/tests/resources/testrepo/.gitted/refs/heads/master
new file mode 100644
index 000000000..f31fe781b
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/heads/master
@@ -0,0 +1 @@
+099fabac3a9ea935598528c27f866e34089c2eff
diff --git a/tests/resources/testrepo/.gitted/refs/heads/packed-test b/tests/resources/testrepo/.gitted/refs/heads/packed-test
new file mode 100644
index 000000000..f2c14ad83
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/heads/packed-test
@@ -0,0 +1 @@
+4a202b346bb0fb0db7eff3cffeb3c70babbd2045
diff --git a/tests/resources/testrepo/.gitted/refs/heads/subtrees b/tests/resources/testrepo/.gitted/refs/heads/subtrees
new file mode 100644
index 000000000..ad27e0b13
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/heads/subtrees
@@ -0,0 +1 @@
+763d71aadf09a7951596c9746c024e7eece7c7af
diff --git a/tests/resources/testrepo/.gitted/refs/heads/test b/tests/resources/testrepo/.gitted/refs/heads/test
new file mode 100644
index 000000000..399c4c73e
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/heads/test
@@ -0,0 +1 @@
+e90810b8df3e80c413d903f631643c716887138d
diff --git a/tests/resources/testrepo/.gitted/refs/tags/e90810b b/tests/resources/testrepo/.gitted/refs/tags/e90810b
new file mode 100644
index 000000000..584495d3c
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/tags/e90810b
@@ -0,0 +1 @@
+7b4384978d2493e851f9cca7858815fac9b10980
diff --git a/tests/resources/testrepo/.gitted/refs/tags/foo/bar b/tests/resources/testrepo/.gitted/refs/tags/foo/bar
new file mode 100644
index 000000000..6ee952a03
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/tags/foo/bar
@@ -0,0 +1 @@
+b25fa35b38051e4ae45d4222e795f9df2e43f1d1
diff --git a/tests/resources/testrepo/.gitted/refs/tags/foo/foo/bar b/tests/resources/testrepo/.gitted/refs/tags/foo/foo/bar
new file mode 100644
index 000000000..6ee952a03
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/tags/foo/foo/bar
@@ -0,0 +1 @@
+b25fa35b38051e4ae45d4222e795f9df2e43f1d1
diff --git a/tests/resources/testrepo/.gitted/refs/tags/point_to_blob b/tests/resources/testrepo/.gitted/refs/tags/point_to_blob
new file mode 100644
index 000000000..f874a3ffc
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/tags/point_to_blob
@@ -0,0 +1 @@
+1385f264afb75a56a5bec74243be9b367ba4ca08
diff --git a/tests/resources/testrepo/.gitted/refs/tags/test b/tests/resources/testrepo/.gitted/refs/tags/test
new file mode 100644
index 000000000..6ee952a03
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/tags/test
@@ -0,0 +1 @@
+b25fa35b38051e4ae45d4222e795f9df2e43f1d1
diff --git a/tests/resources/testrepo2/.gitted/HEAD b/tests/resources/testrepo2/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/testrepo2/.gitted/config b/tests/resources/testrepo2/.gitted/config
new file mode 100644
index 000000000..fc2433caf
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/config
@@ -0,0 +1,14 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
+ precomposeunicode = false
+[remote "origin"]
+ url = https://github.com/libgit2/false.git
+ fetch = +refs/heads/*:refs/remotes/origin/*
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
+ rebase = true
diff --git a/tests/resources/testrepo2/.gitted/description b/tests/resources/testrepo2/.gitted/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/testrepo2/.gitted/index b/tests/resources/testrepo2/.gitted/index
new file mode 100644
index 000000000..b614d0727
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/index
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/info/exclude b/tests/resources/testrepo2/.gitted/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/testrepo2/.gitted/logs/HEAD b/tests/resources/testrepo2/.gitted/logs/HEAD
new file mode 100644
index 000000000..4e80c69fa
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 36060c58702ed4c2a40832c51758d5344201d89a Russell Belfer <rb@github.com> 1368278260 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/../../../rugged/test/fixtures/testrepo.git
diff --git a/tests/resources/testrepo2/.gitted/logs/refs/heads/master b/tests/resources/testrepo2/.gitted/logs/refs/heads/master
new file mode 100644
index 000000000..4e80c69fa
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 36060c58702ed4c2a40832c51758d5344201d89a Russell Belfer <rb@github.com> 1368278260 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/../../../rugged/test/fixtures/testrepo.git
diff --git a/tests/resources/testrepo2/.gitted/logs/refs/remotes/origin/HEAD b/tests/resources/testrepo2/.gitted/logs/refs/remotes/origin/HEAD
new file mode 100644
index 000000000..4e80c69fa
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 36060c58702ed4c2a40832c51758d5344201d89a Russell Belfer <rb@github.com> 1368278260 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/../../../rugged/test/fixtures/testrepo.git
diff --git a/tests/resources/testrepo2/.gitted/objects/0c/37a5391bbff43c37f0d0371823a5509eed5b1d b/tests/resources/testrepo2/.gitted/objects/0c/37a5391bbff43c37f0d0371823a5509eed5b1d
new file mode 100644
index 000000000..bfe146a5a
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/0c/37a5391bbff43c37f0d0371823a5509eed5b1d
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/tests/resources/testrepo2/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
new file mode 100644
index 000000000..cedb2a22e
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/tests/resources/testrepo2/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
new file mode 100644
index 000000000..93a16f146
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd b/tests/resources/testrepo2/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
new file mode 100644
index 000000000..ba0bfb30c
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/2d/2eff63372b08adf0a9eb84109ccf7d19e2f3a2 b/tests/resources/testrepo2/.gitted/objects/2d/2eff63372b08adf0a9eb84109ccf7d19e2f3a2
new file mode 100644
index 000000000..3cd240db5
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/2d/2eff63372b08adf0a9eb84109ccf7d19e2f3a2
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/36/060c58702ed4c2a40832c51758d5344201d89a b/tests/resources/testrepo2/.gitted/objects/36/060c58702ed4c2a40832c51758d5344201d89a
new file mode 100644
index 000000000..0c6246061
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/36/060c58702ed4c2a40832c51758d5344201d89a
@@ -0,0 +1,2 @@
+xŽQ
+Â0ýÎ)re“Ý´ ˆÁ$Û­-˜FÒíý-ÁŸÇ00𸖲¨õ¾?i±L#»‚ÐHSŽŒS×#qÏ2²DÈæ“š¬jC|HS†L‹8$ò)áÐaž°#2i×¹6ûäªjsâºÚëÆ?¸¿JZÞ®åfçtΞÁ˜ÃUþiͶçqiÂZÛ"›ù_/H6 \ No newline at end of file
diff --git a/tests/resources/testrepo2/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/testrepo2/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
new file mode 100644
index 000000000..7ca4ceed5
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/tests/resources/testrepo2/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
new file mode 100644
index 000000000..8953b6cef
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
@@ -0,0 +1,2 @@
+xŽQ
+Â0DýÎ)öÊ6›Í¦ "xO°‰‰-ØFb¼¿EoàÏ0 ¼Ç¤º,ske×[ÎPn8R,EpD?±gŸ}Ê^3² âÙ<µåµGŽhYKÄèÒ8ЖDAÉ)¿ÉÈ;gôݧÚàšjïp™4ÕŽ¯ô-çû¢óãêr‚ÁŠ;°s°GA4Ûº=ìùÖ(ôin7øIÌKÍFE \ No newline at end of file
diff --git a/tests/resources/testrepo2/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/tests/resources/testrepo2/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
new file mode 100644
index 000000000..c1f22c54f
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
@@ -0,0 +1,2 @@
+xŽÛ 1EýNi@™Ék2 "X‚$ÙYW0YcÿíÀ¿Ã…s¸¥ÕzïÚÚõMDÏ€0æœ8!¶†ÉÌÞs‰ XŠªgÚdí::@X0»P¢wÙ"F/‰‰œÍRàˆUz÷¥múZZïú²¤ÒV}|•/œo5݇ÒêI£!¬1z Æ:vùÇUim}ê/¢>
+öF- \ No newline at end of file
diff --git a/tests/resources/testrepo2/.gitted/objects/61/9f9935957e010c419cb9d15621916ddfcc0b96 b/tests/resources/testrepo2/.gitted/objects/61/9f9935957e010c419cb9d15621916ddfcc0b96
new file mode 100644
index 000000000..1fd79b477
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/61/9f9935957e010c419cb9d15621916ddfcc0b96
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/tests/resources/testrepo2/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
new file mode 100644
index 000000000..2ef4faa0f
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/7f/043268ea43ce18e3540acaabf9e090c91965b0 b/tests/resources/testrepo2/.gitted/objects/7f/043268ea43ce18e3540acaabf9e090c91965b0
new file mode 100644
index 000000000..3d1016daa
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/7f/043268ea43ce18e3540acaabf9e090c91965b0
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/tests/resources/testrepo2/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
new file mode 100644
index 000000000..2f9b6b6e3
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/tests/resources/testrepo2/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
new file mode 100644
index 000000000..5df58dda5
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/tests/resources/testrepo2/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
new file mode 100644
index 000000000..a79612435
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
@@ -0,0 +1,3 @@
+xŽ[
+Â0EýÎ*fÊäÕ¤ "¸W0“‡-ØFâtÿÝ—çpS[–YÀ˜x^
+Díb CLhutɉ}¥8X*4Zí¬sY½¨—UÀ‘AÃÖ ÌX3‡R«Mµ¶) s6è¼¢M¦ÖážšÜ&Jm…ó;}Çõ±Ðü<¥¶\@›à‚ÑÞpÄ€¨vº?”ò«jÛºLð«¨Ø?Hå \ No newline at end of file
diff --git a/tests/resources/testrepo2/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/tests/resources/testrepo2/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
new file mode 100644
index 000000000..f8588696b
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
@@ -0,0 +1,2 @@
+x;j1DëmdÓú·À˜ÇŽ|M«µ3`ŒV{ >€³âQ¯ ¸·vL0I?Í!š4–Z=Ê! ×¦8²F¢Ã’!rÖsQßyÈ9]$DŽ&„l6AÇ>jFWüÒµ IKNiûë§Z¢%¡SˆŒ‘
+‹Ò ­ÅʉøU~̽øä>'¼ï™û ¯wþ ×[ËÇ× ÷öÚDGÚ¡±ðŒQ-ºMù«>dܶ‘OÞáÒò}í\à8g_ШÂoYr \ No newline at end of file
diff --git a/tests/resources/testrepo2/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/tests/resources/testrepo2/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
new file mode 100644
index 000000000..d0d7e736e
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/tests/resources/testrepo2/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
new file mode 100644
index 000000000..18a7f61c2
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 b/tests/resources/testrepo2/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
new file mode 100644
index 000000000..0817229bc
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
@@ -0,0 +1,3 @@
+xKj1D³Ö)zçUBëÛ-0ÁuV9¦Õò<#£È÷ÏȲ+ŠW<Jú¶Ý&8Ê/s¨‚e‹µµÈ•KJ­«½S
+ØRvÌÁ{©æQ†îr«äY¹QN$H\Eµ²Íè=6áX5¦òÇK Fr)·(‰dC‡Î†”­–œ—jÊs®}À—ô9ác-Òw8Ëo¸\·r»¿IßÞÁ:
+l}F‚W$Ds´Ç£©ÿÙšOW…e”]V8-ÃÌÈ"U \ No newline at end of file
diff --git a/tests/resources/testrepo2/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd b/tests/resources/testrepo2/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
new file mode 100644
index 000000000..75f541f10
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
@@ -0,0 +1,3 @@
+xŽQ
+Â0DýÎ)öʦ»I<‚'ØlR+˜Fj¼¿EoàÏ0<xÃh«õÞa Üõµ]È™­åXUlÞPF)Åz‘4yó”µ,\r 'SÂÄ-mI4
+‘Xhô”&òÌFÞ}n+\µõ—Y´-p|é·œoUz;-‘aÑlt{ØË?®I«,:ÃoÚR̳cHK \ No newline at end of file
diff --git a/tests/resources/testrepo2/.gitted/objects/c4/dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b b/tests/resources/testrepo2/.gitted/objects/c4/dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b
new file mode 100644
index 000000000..599e16084
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/c4/dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/testrepo2/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644
index 000000000..711223894
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 b/tests/resources/testrepo2/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
new file mode 100644
index 000000000..03770969a
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/tests/resources/testrepo2/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
new file mode 100644
index 000000000..112998d42
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765 b/tests/resources/testrepo2/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
new file mode 100644
index 000000000..12bf5f3e3
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx b/tests/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
new file mode 100644
index 000000000..94c3c71da
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack b/tests/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
new file mode 100644
index 000000000..74c7fe4f3
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/packed-refs b/tests/resources/testrepo2/.gitted/packed-refs
new file mode 100644
index 000000000..97ea6a848
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/packed-refs
@@ -0,0 +1,6 @@
+# pack-refs with: peeled fully-peeled
+36060c58702ed4c2a40832c51758d5344201d89a refs/remotes/origin/master
+41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9 refs/remotes/origin/packed
+5b5b025afb0b4c913b4c338a42934a3863bf3644 refs/tags/v0.9
+0c37a5391bbff43c37f0d0371823a5509eed5b1d refs/tags/v1.0
+^5b5b025afb0b4c913b4c338a42934a3863bf3644
diff --git a/tests/resources/testrepo2/.gitted/refs/heads/master b/tests/resources/testrepo2/.gitted/refs/heads/master
new file mode 100644
index 000000000..a7eafce3c
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/refs/heads/master
@@ -0,0 +1 @@
+36060c58702ed4c2a40832c51758d5344201d89a
diff --git a/tests/resources/testrepo2/.gitted/refs/remotes/origin/HEAD b/tests/resources/testrepo2/.gitted/refs/remotes/origin/HEAD
new file mode 100644
index 000000000..6efe28fff
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/testrepo2/README b/tests/resources/testrepo2/README
new file mode 100644
index 000000000..1385f264a
--- /dev/null
+++ b/tests/resources/testrepo2/README
@@ -0,0 +1 @@
+hey
diff --git a/tests/resources/testrepo2/new.txt b/tests/resources/testrepo2/new.txt
new file mode 100644
index 000000000..fa49b0779
--- /dev/null
+++ b/tests/resources/testrepo2/new.txt
@@ -0,0 +1 @@
+new file
diff --git a/tests/resources/testrepo2/subdir/README b/tests/resources/testrepo2/subdir/README
new file mode 100644
index 000000000..1385f264a
--- /dev/null
+++ b/tests/resources/testrepo2/subdir/README
@@ -0,0 +1 @@
+hey
diff --git a/tests/resources/testrepo2/subdir/new.txt b/tests/resources/testrepo2/subdir/new.txt
new file mode 100644
index 000000000..fa49b0779
--- /dev/null
+++ b/tests/resources/testrepo2/subdir/new.txt
@@ -0,0 +1 @@
+new file
diff --git a/tests/resources/testrepo2/subdir/subdir2/README b/tests/resources/testrepo2/subdir/subdir2/README
new file mode 100644
index 000000000..1385f264a
--- /dev/null
+++ b/tests/resources/testrepo2/subdir/subdir2/README
@@ -0,0 +1 @@
+hey
diff --git a/tests/resources/testrepo2/subdir/subdir2/new.txt b/tests/resources/testrepo2/subdir/subdir2/new.txt
new file mode 100644
index 000000000..fa49b0779
--- /dev/null
+++ b/tests/resources/testrepo2/subdir/subdir2/new.txt
@@ -0,0 +1 @@
+new file
diff --git a/tests/resources/twowaymerge.git/HEAD b/tests/resources/twowaymerge.git/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/twowaymerge.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/twowaymerge.git/config b/tests/resources/twowaymerge.git/config
new file mode 100644
index 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/resources/twowaymerge.git/description b/tests/resources/twowaymerge.git/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/twowaymerge.git/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/twowaymerge.git/info/exclude b/tests/resources/twowaymerge.git/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/twowaymerge.git/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/twowaymerge.git/objects/0c/8a3f1f3d5f421cf83048c7c73ee3b55a5e0f29 b/tests/resources/twowaymerge.git/objects/0c/8a3f1f3d5f421cf83048c7c73ee3b55a5e0f29
new file mode 100644
index 000000000..12698affa
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/0c/8a3f1f3d5f421cf83048c7c73ee3b55a5e0f29
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/10/2dce8e3081f398e4bdd9fd894dc85ac3ca6a67 b/tests/resources/twowaymerge.git/objects/10/2dce8e3081f398e4bdd9fd894dc85ac3ca6a67
new file mode 100644
index 000000000..3806ee74c
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/10/2dce8e3081f398e4bdd9fd894dc85ac3ca6a67
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/17/7d8634a28e26ec7819284752757ebe01a479d5 b/tests/resources/twowaymerge.git/objects/17/7d8634a28e26ec7819284752757ebe01a479d5
new file mode 100644
index 000000000..e91e06db2
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/17/7d8634a28e26ec7819284752757ebe01a479d5
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/1c/30b88f5f3ee66d78df6520a7de9e89b890818b b/tests/resources/twowaymerge.git/objects/1c/30b88f5f3ee66d78df6520a7de9e89b890818b
new file mode 100644
index 000000000..57d1a0f1e
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/1c/30b88f5f3ee66d78df6520a7de9e89b890818b
@@ -0,0 +1,3 @@
+xŽM
+Â0F]çsËÌä§ ˆ¸Üz‚Iš¶Ši¤¦÷·^Áå{<>¾TKy4`6‡¶æ š,y’9j§GJì8ÁÇÞb¢‘\Œfõ–5/ Ç^‰8v¹'ö‚ÙËœì`SÆ%[›ë
+÷T[ƒ[×úŠ,púüÌsºL6o±Kµœ´5Ø;ŽèÕn÷›-ÿ= ²úÿDà \ No newline at end of file
diff --git a/tests/resources/twowaymerge.git/objects/1f/4c0311a24b63f6fc209a59a1e404942d4a5006 b/tests/resources/twowaymerge.git/objects/1f/4c0311a24b63f6fc209a59a1e404942d4a5006
new file mode 100644
index 000000000..99288fdd7
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/1f/4c0311a24b63f6fc209a59a1e404942d4a5006
@@ -0,0 +1,2 @@
+xÍ=Â0 @aæœÂ ²Mê6BlH¬œ ¿m!RqïO¹ë7¼[­‹ rÐ5g°N’Xƒ‹Å±)Eg]ÏDY2c R8xã7Û
+ØTáÞÁ­½Rõo8~òœ®Ó¢óºØêèÔ[”™àˆ#¢Ùußjþ;`¼ùÔÙ7ó \ No newline at end of file
diff --git a/tests/resources/twowaymerge.git/objects/22/24e191514cb4bd8c566d80dac22dfcb1e9bb83 b/tests/resources/twowaymerge.git/objects/22/24e191514cb4bd8c566d80dac22dfcb1e9bb83
new file mode 100644
index 000000000..48466ea51
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/22/24e191514cb4bd8c566d80dac22dfcb1e9bb83
@@ -0,0 +1,3 @@
+xŽK
+Â0@]çsK&¤ ˆ¸Üz‚™4éÛHMïo½‚Û÷àñbY–©‚1tª[JàbŽlɈµâ¥í4vÉ¡±Lâ³ ì'—Õ›·´V`B¦û .
+ëÎIöm ï1õZ¨Ç x¯cÙàK­ðhà^^ýÂ+\>?2·aªã.M,Ë°µtTB‹pÖ^kuÐc³¦¿jV_«sFh \ No newline at end of file
diff --git a/tests/resources/twowaymerge.git/objects/29/6e56023cdc034d2735fee8c0d85a659d1b07f4 b/tests/resources/twowaymerge.git/objects/29/6e56023cdc034d2735fee8c0d85a659d1b07f4
new file mode 100644
index 000000000..aa3fccdf0
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/29/6e56023cdc034d2735fee8c0d85a659d1b07f4
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/31/51880ae2b363f1c262cf98b750c1f169a0d432 b/tests/resources/twowaymerge.git/objects/31/51880ae2b363f1c262cf98b750c1f169a0d432
new file mode 100644
index 000000000..235d42bff
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/31/51880ae2b363f1c262cf98b750c1f169a0d432
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/3b/287f8730c81d0b763c2d294618a5e32b67b4f8 b/tests/resources/twowaymerge.git/objects/3b/287f8730c81d0b763c2d294618a5e32b67b4f8
new file mode 100644
index 000000000..56ddac5ee
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/3b/287f8730c81d0b763c2d294618a5e32b67b4f8
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/42/b7311aa626e712891940c1ec5d5cba201946a4 b/tests/resources/twowaymerge.git/objects/42/b7311aa626e712891940c1ec5d5cba201946a4
new file mode 100644
index 000000000..a8e6581f8
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/42/b7311aa626e712891940c1ec5d5cba201946a4
@@ -0,0 +1,3 @@
+xν Â0@ajOq cßù"!D‡DËþKÂ1
+Îþ„h_ñéÅZÊ£AßÛC[s†aÌÈžp´I³±‰#‹„lB…œqÈêí×¼4ðZ"¡Ç(œyGF¢¢ød#y«ò[›ë
+÷X[ƒ[×úJÅ/púüÊsºL6o¡‹µœA²èX„ሂ¨öºo¶ü7 ’úÔ¸Ec \ No newline at end of file
diff --git a/tests/resources/twowaymerge.git/objects/49/6d6428b9cf92981dc9495211e6e1120fb6f2ba b/tests/resources/twowaymerge.git/objects/49/6d6428b9cf92981dc9495211e6e1120fb6f2ba
new file mode 100644
index 000000000..978bc3448
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/49/6d6428b9cf92981dc9495211e6e1120fb6f2ba
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/59/b0cf7d74659e1cdb13305319d6d4ce2733c118 b/tests/resources/twowaymerge.git/objects/59/b0cf7d74659e1cdb13305319d6d4ce2733c118
new file mode 100644
index 000000000..30b507c06
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/59/b0cf7d74659e1cdb13305319d6d4ce2733c118
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/6a/b5d28acbf3c3bdff276f7ccfdf29c1520e542f b/tests/resources/twowaymerge.git/objects/6a/b5d28acbf3c3bdff276f7ccfdf29c1520e542f
new file mode 100644
index 000000000..ff6a386ac
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/6a/b5d28acbf3c3bdff276f7ccfdf29c1520e542f
@@ -0,0 +1 @@
+xÎM‚0@a×=Å\@2ýcÜ™¸õeÚ†Rƒåþâܾŗǵ”¹RæÔ¶”@¢Šœ(i$™uOÉ 1ö9Ro"“ ¬9¸à¼x‡-­ ü@¬µcc3;ê-KvHÊ+‡9ÙèÁFe¼{›êO®­Á£ƒ{]b +\>¿òoãܦ}踖+Hm zšàŒ„(Žzl¶ô7 ñ•œF- \ No newline at end of file
diff --git a/tests/resources/twowaymerge.git/objects/6c/fca542b55b8b37017e6125a4b8f59a6eae6f11 b/tests/resources/twowaymerge.git/objects/6c/fca542b55b8b37017e6125a4b8f59a6eae6f11
new file mode 100644
index 000000000..9a969a279
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/6c/fca542b55b8b37017e6125a4b8f59a6eae6f11
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/76/5b32c65d38f04c4f287abda055818ec0f26912 b/tests/resources/twowaymerge.git/objects/76/5b32c65d38f04c4f287abda055818ec0f26912
new file mode 100644
index 000000000..493bbc076
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/76/5b32c65d38f04c4f287abda055818ec0f26912
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/7b/8c336c45fc6895c1c60827260fe5d798e5d247 b/tests/resources/twowaymerge.git/objects/7b/8c336c45fc6895c1c60827260fe5d798e5d247
new file mode 100644
index 000000000..19e7ef463
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/7b/8c336c45fc6895c1c60827260fe5d798e5d247
@@ -0,0 +1,3 @@
+xÎA @Qלb.`3 S(‰1îLÜz‚B[µÅTzëܾÅÏež§
+D|¨kJCß3f‰´íȵœ‚uÙ ›L>YGMÌV½eMK9¢ÑZˆƒ5ÙæHè¥õ¢#{¦ž¥E´J¶:–î±Ô
+·®åÕϲÀéó“Çp¦:n¡‰e>ƒ6-£sH GìÕ®ûfMÔS}ZE² \ No newline at end of file
diff --git a/tests/resources/twowaymerge.git/objects/82/bf9a1a10a4b25c1f14c9607b60970705e92545 b/tests/resources/twowaymerge.git/objects/82/bf9a1a10a4b25c1f14c9607b60970705e92545
new file mode 100644
index 000000000..89b0b9f9b
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/82/bf9a1a10a4b25c1f14c9607b60970705e92545
@@ -0,0 +1 @@
+xŽË Â09»Šm€hã_ÖBܸRן„cœþ1-p§yšPKy4RÚ–D›GŒF»ÀJvÉFE>‡1#q²Ž j§ÅÛoimbvSŽŠYSbEr²Š¸»Q"eÓÑ{+üÞ–ºÁ=ÔÖà6Àµ¾bñ+œ>?òœ/ó£-;¡–3ŒÊhœ¬C‚#¢è´g¶ô÷XÄÌyF¤ \ No newline at end of file
diff --git a/tests/resources/twowaymerge.git/objects/8b/82fb1794cb1c8c7f172ec730a4c2db0ae3e650 b/tests/resources/twowaymerge.git/objects/8b/82fb1794cb1c8c7f172ec730a4c2db0ae3e650
new file mode 100644
index 000000000..8e9b758ea
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/8b/82fb1794cb1c8c7f172ec730a4c2db0ae3e650
@@ -0,0 +1,3 @@
+xν Â0@ajOq ù7BtH´Lp¾ó%A8FÁÙŸ°íW<=ª¥Ì ¬õ‡¶æ x"ÊŽØ$—%1†dÄcÏDNLˆ:Yv=©7®yiÐc
+l¤$Ž\b{‰DÂbOd‚Õ9x+
+·6ÕT[ƒ{·úâ‚ œ??yŽ×qnÓ–:ªåƯcÔÞÁQZ«]÷Í–ÿ¨¢¾7 H† \ No newline at end of file
diff --git a/tests/resources/twowaymerge.git/objects/9a/40a2f11c191f180c47e54b11567cb3c1e89b30 b/tests/resources/twowaymerge.git/objects/9a/40a2f11c191f180c47e54b11567cb3c1e89b30
new file mode 100644
index 000000000..1de1224f7
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/9a/40a2f11c191f180c47e54b11567cb3c1e89b30
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/9b/219343610c88a1187c996d0dc58330b55cee28 b/tests/resources/twowaymerge.git/objects/9b/219343610c88a1187c996d0dc58330b55cee28
new file mode 100644
index 000000000..8b64b4381
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/9b/219343610c88a1187c996d0dc58330b55cee28
@@ -0,0 +1,2 @@
+xÏKj1Ьç½óÊFj}Z‚¼3²Ê ¤VÏÇdFA#ß?’\ ËzÅu]—FÓSo" ‰JðÆ& ‚^˜‚Ž,9$G’Eéd)7|¦&[6”(FU"&Žh< ¯FÉc4AÆ¿>"ZÑQ;m9Û\;ïKP%1b9k‰93¤GŸkƒw®½Ãënõ£¬iƒçý[îÓuZúüÈ®ë hã¬"RÞÂY¥†C[]þ=0¼I›rKÏp—¶÷óO:Á²õ
+»pÝʯ _¾(c‡ \ No newline at end of file
diff --git a/tests/resources/twowaymerge.git/objects/9f/e06a50f4d1634d6c6879854d01d80857388706 b/tests/resources/twowaymerge.git/objects/9f/e06a50f4d1634d6c6879854d01d80857388706
new file mode 100644
index 000000000..055de0158
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/9f/e06a50f4d1634d6c6879854d01d80857388706
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/a4/1a49f8f5cd9b6cb14a076bf8394881ed0b4d19 b/tests/resources/twowaymerge.git/objects/a4/1a49f8f5cd9b6cb14a076bf8394881ed0b4d19
new file mode 100644
index 000000000..cb4d34e77
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/a4/1a49f8f5cd9b6cb14a076bf8394881ed0b4d19
@@ -0,0 +1,3 @@
+xν Â0@ajOq Ýù7'!D‡DËöÙ A$FÁÙŸ°íW<=©ó<5ÐÚÚZ
+8N(CÈÁzÇ…$'2!Î>[):#D½zǵ, zŽ £MÚ d…=†ä‘t…µ³NÅ­=ê
+w©­Á­ƒk}å9.púüä9^Æ©=¶ÔIÏ@ÆY ž‰áˆ=¢ÚußlåÔ°Dâ \ No newline at end of file
diff --git a/tests/resources/twowaymerge.git/objects/a9/53a018c5b10b20c86e69fef55ebc8ad4c5a417 b/tests/resources/twowaymerge.git/objects/a9/53a018c5b10b20c86e69fef55ebc8ad4c5a417
new file mode 100644
index 000000000..8235f1839
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/a9/53a018c5b10b20c86e69fef55ebc8ad4c5a417
@@ -0,0 +1 @@
+xÍJÄ0…]÷)înV3$¹is"îÁ•Oû“NŶ’ɼ¿ñ\žï|8²¯ëÒ!dzèÍ ªÔdXG/ޫϹp*‰¢C³X³ˆº@ZÂ8|•f[VŸ0HD™H“E]6¯”g¶I#g«*ñÏ­9UEæHÆH!MḦÕñh‚ºR¦¡Üûuoð.{ïðz—ýSײÁãí‡|ÌÏóÒ¯w¾È¾>Ç1º4‘C8;rn8èq«Û¿†7k³·²ÉNui·~þM§áÜ^­ \ No newline at end of file
diff --git a/tests/resources/twowaymerge.git/objects/a9/cce3cd1b3efbda5b1f4a6dcc3f1570b2d3d74c b/tests/resources/twowaymerge.git/objects/a9/cce3cd1b3efbda5b1f4a6dcc3f1570b2d3d74c
new file mode 100644
index 000000000..4da7e826a
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/a9/cce3cd1b3efbda5b1f4a6dcc3f1570b2d3d74c
@@ -0,0 +1 @@
+x+)JMU044c040031QHdx6÷ÑìM¯9{wk®+ºqèIOðD¨d6>É|’¹X%>½9j \ No newline at end of file
diff --git a/tests/resources/twowaymerge.git/objects/bd/1732c43c68d712ad09e1d872b9be6d4b9efdc4 b/tests/resources/twowaymerge.git/objects/bd/1732c43c68d712ad09e1d872b9be6d4b9efdc4
new file mode 100644
index 000000000..b9b60122d
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/bd/1732c43c68d712ad09e1d872b9be6d4b9efdc4
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/c3/7a783c20d92ac92362a78a32860f7eebf938ef b/tests/resources/twowaymerge.git/objects/c3/7a783c20d92ac92362a78a32860f7eebf938ef
new file mode 100644
index 000000000..041e890ab
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/c3/7a783c20d92ac92362a78a32860f7eebf938ef
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/cb/dd40facab1682754eb67f7a43f29e672903cf6 b/tests/resources/twowaymerge.git/objects/cb/dd40facab1682754eb67f7a43f29e672903cf6
new file mode 100644
index 000000000..ccb156d88
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/cb/dd40facab1682754eb67f7a43f29e672903cf6
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/cd/f97fd3bb48eb3827638bb33d208f5fd32d0aa6 b/tests/resources/twowaymerge.git/objects/cd/f97fd3bb48eb3827638bb33d208f5fd32d0aa6
new file mode 100644
index 000000000..0e028dc01
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/cd/f97fd3bb48eb3827638bb33d208f5fd32d0aa6
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/d6/f10d549cb335b9e6d38afc1f0088be69b50494 b/tests/resources/twowaymerge.git/objects/d6/f10d549cb335b9e6d38afc1f0088be69b50494
new file mode 100644
index 000000000..b298c520e
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/d6/f10d549cb335b9e6d38afc1f0088be69b50494
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/d9/acdc7ae7632adfeec67fa73c1e343cf4d1f47e b/tests/resources/twowaymerge.git/objects/d9/acdc7ae7632adfeec67fa73c1e343cf4d1f47e
new file mode 100644
index 000000000..de94528a4
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/d9/acdc7ae7632adfeec67fa73c1e343cf4d1f47e
@@ -0,0 +1 @@
+x+)JMU044c040031QHdx6÷ÑìM¯9{wk®+ºqèIOðD¨d>É4|’éX%:79U \ No newline at end of file
diff --git a/tests/resources/twowaymerge.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/twowaymerge.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644
index 000000000..711223894
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/ef/0488f0b722f0be8bcb90a7730ac7efafd1d694 b/tests/resources/twowaymerge.git/objects/ef/0488f0b722f0be8bcb90a7730ac7efafd1d694
new file mode 100644
index 000000000..00f7d3615
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/ef/0488f0b722f0be8bcb90a7730ac7efafd1d694
@@ -0,0 +1 @@
+xŽM F]sŠ¹€Í0Ã_cÜ™¸õ@¡ÕH1•Þ_¼‚Û—ï}y±–òh@¤mK 8³ÙYÆèä„ÁŽ4ѨŒt^'¦`lPÙ‰·ßÒÚ Ï<g™yÒY‘ŒÙ1*m´œ­»„™Fá÷¶Ô ·®õ5¿Âéó#Ïù2?Ú²‡!ÖrÉZ¡5ÆŽpD‡(:í™-ý} ²ø#E¸ \ No newline at end of file
diff --git a/tests/resources/twowaymerge.git/objects/fc/f7e3f51c11d199ab7a78403ee4f9ccd028da25 b/tests/resources/twowaymerge.git/objects/fc/f7e3f51c11d199ab7a78403ee4f9ccd028da25
new file mode 100644
index 000000000..54989ea87
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/fc/f7e3f51c11d199ab7a78403ee4f9ccd028da25
Binary files differ
diff --git a/tests/resources/twowaymerge.git/refs/heads/first-branch b/tests/resources/twowaymerge.git/refs/heads/first-branch
new file mode 100644
index 000000000..ef0dead7f
--- /dev/null
+++ b/tests/resources/twowaymerge.git/refs/heads/first-branch
@@ -0,0 +1 @@
+2224e191514cb4bd8c566d80dac22dfcb1e9bb83
diff --git a/tests/resources/twowaymerge.git/refs/heads/master b/tests/resources/twowaymerge.git/refs/heads/master
new file mode 100644
index 000000000..ebf18f58e
--- /dev/null
+++ b/tests/resources/twowaymerge.git/refs/heads/master
@@ -0,0 +1 @@
+1c30b88f5f3ee66d78df6520a7de9e89b890818b
diff --git a/tests/resources/twowaymerge.git/refs/heads/second-branch b/tests/resources/twowaymerge.git/refs/heads/second-branch
new file mode 100644
index 000000000..586a14a84
--- /dev/null
+++ b/tests/resources/twowaymerge.git/refs/heads/second-branch
@@ -0,0 +1 @@
+9b219343610c88a1187c996d0dc58330b55cee28
diff --git a/tests/resources/typechanges/.gitted/HEAD b/tests/resources/typechanges/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/typechanges/.gitted/config b/tests/resources/typechanges/.gitted/config
new file mode 100644
index 000000000..4cc6e1ddf
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/config
@@ -0,0 +1,12 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
+[submodule "e"]
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target/.git
+[submodule "d"]
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target/.git
+[submodule "b"]
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target/.git
diff --git a/tests/resources/typechanges/.gitted/description b/tests/resources/typechanges/.gitted/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/typechanges/.gitted/index b/tests/resources/typechanges/.gitted/index
new file mode 100644
index 000000000..4f6d12a3b
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/index
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/info/exclude b/tests/resources/typechanges/.gitted/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/typechanges/.gitted/modules/b/HEAD b/tests/resources/typechanges/.gitted/modules/b/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/typechanges/.gitted/modules/b/config b/tests/resources/typechanges/.gitted/modules/b/config
new file mode 100644
index 000000000..f57cd4a6f
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/config
@@ -0,0 +1,13 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../b
+ ignorecase = true
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target/.git
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/typechanges/.gitted/modules/b/description b/tests/resources/typechanges/.gitted/modules/b/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/typechanges/.gitted/modules/b/index b/tests/resources/typechanges/.gitted/modules/b/index
new file mode 100644
index 000000000..c16a026b7
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/index
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/b/info/exclude b/tests/resources/typechanges/.gitted/modules/b/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/typechanges/.gitted/modules/b/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/typechanges/.gitted/modules/b/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 000000000..f4b7094c5
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/b/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/typechanges/.gitted/modules/b/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 000000000..56c845e49
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/b/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/typechanges/.gitted/modules/b/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 000000000..bd179b5f5
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/b/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/typechanges/.gitted/modules/b/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 000000000..ccf49bd15
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/b/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/typechanges/.gitted/modules/b/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 000000000..53029069a
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/b/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/typechanges/.gitted/modules/b/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 000000000..38c791eba
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/b/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/typechanges/.gitted/modules/b/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 000000000..a26d29993
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/typechanges/.gitted/modules/b/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/typechanges/.gitted/modules/b/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 000000000..83d1ba481
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/b/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/typechanges/.gitted/modules/b/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 000000000..6d27af8a8
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/typechanges/.gitted/modules/b/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/typechanges/.gitted/modules/b/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 000000000..17458840b
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests/resources/typechanges/.gitted/modules/b/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/typechanges/.gitted/modules/b/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 000000000..83cc29fb1
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/b/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/typechanges/.gitted/modules/b/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 000000000..55bda40ef
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/b/packed-refs b/tests/resources/typechanges/.gitted/modules/b/packed-refs
new file mode 100644
index 000000000..5a4ebc47c
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests/resources/typechanges/.gitted/modules/b/refs/heads/master b/tests/resources/typechanges/.gitted/modules/b/refs/heads/master
new file mode 100644
index 000000000..e12c44d7a
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests/resources/typechanges/.gitted/modules/b/refs/remotes/origin/HEAD b/tests/resources/typechanges/.gitted/modules/b/refs/remotes/origin/HEAD
new file mode 100644
index 000000000..6efe28fff
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/typechanges/.gitted/modules/d/HEAD b/tests/resources/typechanges/.gitted/modules/d/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/typechanges/.gitted/modules/d/config b/tests/resources/typechanges/.gitted/modules/d/config
new file mode 100644
index 000000000..42e1bddda
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/config
@@ -0,0 +1,13 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../d
+ ignorecase = true
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target/.git
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/typechanges/.gitted/modules/d/description b/tests/resources/typechanges/.gitted/modules/d/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/typechanges/.gitted/modules/d/index b/tests/resources/typechanges/.gitted/modules/d/index
new file mode 100644
index 000000000..86d0266e8
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/index
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/d/info/exclude b/tests/resources/typechanges/.gitted/modules/d/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/typechanges/.gitted/modules/d/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/typechanges/.gitted/modules/d/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 000000000..f4b7094c5
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/d/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/typechanges/.gitted/modules/d/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 000000000..56c845e49
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/d/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/typechanges/.gitted/modules/d/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 000000000..bd179b5f5
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/d/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/typechanges/.gitted/modules/d/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 000000000..ccf49bd15
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/d/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/typechanges/.gitted/modules/d/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 000000000..53029069a
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/d/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/typechanges/.gitted/modules/d/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 000000000..38c791eba
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/d/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/typechanges/.gitted/modules/d/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 000000000..a26d29993
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/typechanges/.gitted/modules/d/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/typechanges/.gitted/modules/d/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 000000000..83d1ba481
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/d/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/typechanges/.gitted/modules/d/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 000000000..6d27af8a8
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/typechanges/.gitted/modules/d/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/typechanges/.gitted/modules/d/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 000000000..17458840b
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests/resources/typechanges/.gitted/modules/d/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/typechanges/.gitted/modules/d/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 000000000..83cc29fb1
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/d/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/typechanges/.gitted/modules/d/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 000000000..55bda40ef
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/d/packed-refs b/tests/resources/typechanges/.gitted/modules/d/packed-refs
new file mode 100644
index 000000000..5a4ebc47c
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests/resources/typechanges/.gitted/modules/d/refs/heads/master b/tests/resources/typechanges/.gitted/modules/d/refs/heads/master
new file mode 100644
index 000000000..e12c44d7a
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests/resources/typechanges/.gitted/modules/d/refs/remotes/origin/HEAD b/tests/resources/typechanges/.gitted/modules/d/refs/remotes/origin/HEAD
new file mode 100644
index 000000000..6efe28fff
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/typechanges/.gitted/modules/e/HEAD b/tests/resources/typechanges/.gitted/modules/e/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/typechanges/.gitted/modules/e/config b/tests/resources/typechanges/.gitted/modules/e/config
new file mode 100644
index 000000000..89b3b9b4f
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/config
@@ -0,0 +1,13 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../e
+ ignorecase = true
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target/.git
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/typechanges/.gitted/modules/e/description b/tests/resources/typechanges/.gitted/modules/e/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/typechanges/.gitted/modules/e/index b/tests/resources/typechanges/.gitted/modules/e/index
new file mode 100644
index 000000000..cd6e2da6c
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/index
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/e/info/exclude b/tests/resources/typechanges/.gitted/modules/e/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/typechanges/.gitted/modules/e/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/typechanges/.gitted/modules/e/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 000000000..f4b7094c5
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/e/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/typechanges/.gitted/modules/e/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 000000000..56c845e49
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/e/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/typechanges/.gitted/modules/e/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 000000000..bd179b5f5
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/e/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/typechanges/.gitted/modules/e/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 000000000..ccf49bd15
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/e/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/typechanges/.gitted/modules/e/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 000000000..53029069a
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/e/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/typechanges/.gitted/modules/e/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 000000000..38c791eba
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/e/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/typechanges/.gitted/modules/e/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 000000000..a26d29993
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/typechanges/.gitted/modules/e/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/typechanges/.gitted/modules/e/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 000000000..83d1ba481
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/e/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/typechanges/.gitted/modules/e/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 000000000..6d27af8a8
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/typechanges/.gitted/modules/e/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/typechanges/.gitted/modules/e/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 000000000..17458840b
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests/resources/typechanges/.gitted/modules/e/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/typechanges/.gitted/modules/e/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 000000000..83cc29fb1
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/e/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/typechanges/.gitted/modules/e/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 000000000..55bda40ef
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/e/packed-refs b/tests/resources/typechanges/.gitted/modules/e/packed-refs
new file mode 100644
index 000000000..5a4ebc47c
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests/resources/typechanges/.gitted/modules/e/refs/heads/master b/tests/resources/typechanges/.gitted/modules/e/refs/heads/master
new file mode 100644
index 000000000..e12c44d7a
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests/resources/typechanges/.gitted/modules/e/refs/remotes/origin/HEAD b/tests/resources/typechanges/.gitted/modules/e/refs/remotes/origin/HEAD
new file mode 100644
index 000000000..6efe28fff
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/typechanges/.gitted/objects/0d/78578795b7ca49fd8df6c4b6d27c5c02d991d8 b/tests/resources/typechanges/.gitted/objects/0d/78578795b7ca49fd8df6c4b6d27c5c02d991d8
new file mode 100644
index 000000000..f2d02f4f7
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/0d/78578795b7ca49fd8df6c4b6d27c5c02d991d8
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/0e/7ed140b514b8cae23254cb8656fe1674403aff b/tests/resources/typechanges/.gitted/objects/0e/7ed140b514b8cae23254cb8656fe1674403aff
new file mode 100644
index 000000000..527964c92
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/0e/7ed140b514b8cae23254cb8656fe1674403aff
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/0f/f461da9689266f482d8f6654a4400b4e33c586 b/tests/resources/typechanges/.gitted/objects/0f/f461da9689266f482d8f6654a4400b4e33c586
new file mode 100644
index 000000000..2694e4fa0
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/0f/f461da9689266f482d8f6654a4400b4e33c586
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/18/aa7e45bbe4c3cc24a0b079696c59d36675af97 b/tests/resources/typechanges/.gitted/objects/18/aa7e45bbe4c3cc24a0b079696c59d36675af97
new file mode 100644
index 000000000..032a960b4
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/18/aa7e45bbe4c3cc24a0b079696c59d36675af97
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/1b/63caae4a5ca96f78e8dfefc376c6a39a142475 b/tests/resources/typechanges/.gitted/objects/1b/63caae4a5ca96f78e8dfefc376c6a39a142475
new file mode 100644
index 000000000..d32622e67
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/1b/63caae4a5ca96f78e8dfefc376c6a39a142475
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/1e/abe82aa3b2365a394f6108f24435df6e193d02 b/tests/resources/typechanges/.gitted/objects/1e/abe82aa3b2365a394f6108f24435df6e193d02
new file mode 100644
index 000000000..42d5f92f3
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/1e/abe82aa3b2365a394f6108f24435df6e193d02
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/42/061c01a1c70097d1e4579f29a5adf40abdec95 b/tests/resources/typechanges/.gitted/objects/42/061c01a1c70097d1e4579f29a5adf40abdec95
new file mode 100644
index 000000000..0a8f32e15
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/42/061c01a1c70097d1e4579f29a5adf40abdec95
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/46/2838cee476a87e7cff32196b66fa18ed756592 b/tests/resources/typechanges/.gitted/objects/46/2838cee476a87e7cff32196b66fa18ed756592
new file mode 100644
index 000000000..52af51f74
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/46/2838cee476a87e7cff32196b66fa18ed756592
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/63/499e4ea8e096b831515ceb1d5a7593e4d87ae5 b/tests/resources/typechanges/.gitted/objects/63/499e4ea8e096b831515ceb1d5a7593e4d87ae5
new file mode 100644
index 000000000..afafa89f4
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/63/499e4ea8e096b831515ceb1d5a7593e4d87ae5
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/68/1af94e10eaf262f3ab7cb9b8fd5f4158ba4d3e b/tests/resources/typechanges/.gitted/objects/68/1af94e10eaf262f3ab7cb9b8fd5f4158ba4d3e
new file mode 100644
index 000000000..9e518fc28
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/68/1af94e10eaf262f3ab7cb9b8fd5f4158ba4d3e
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/6a/9008602b811e69a9b7a2d83496f39a794fdeeb b/tests/resources/typechanges/.gitted/objects/6a/9008602b811e69a9b7a2d83496f39a794fdeeb
new file mode 100644
index 000000000..a245727a1
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/6a/9008602b811e69a9b7a2d83496f39a794fdeeb
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/6e/ae26c90e8ccc4d16208972119c40635489c6f0 b/tests/resources/typechanges/.gitted/objects/6e/ae26c90e8ccc4d16208972119c40635489c6f0
new file mode 100644
index 000000000..ea35cd311
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/6e/ae26c90e8ccc4d16208972119c40635489c6f0
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/6f/39eabbb8a7541515e0d35971078bccb502e7e0 b/tests/resources/typechanges/.gitted/objects/6f/39eabbb8a7541515e0d35971078bccb502e7e0
new file mode 100644
index 000000000..c54817598
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/6f/39eabbb8a7541515e0d35971078bccb502e7e0
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/71/54d3083461536dfc71ad5542f3e65e723a06c4 b/tests/resources/typechanges/.gitted/objects/71/54d3083461536dfc71ad5542f3e65e723a06c4
new file mode 100644
index 000000000..9fdd8f245
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/71/54d3083461536dfc71ad5542f3e65e723a06c4
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/75/56c1d893a4c0ca85ac8ac51de47ff399758729 b/tests/resources/typechanges/.gitted/objects/75/56c1d893a4c0ca85ac8ac51de47ff399758729
new file mode 100644
index 000000000..d43630f44
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/75/56c1d893a4c0ca85ac8ac51de47ff399758729
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/76/fef844064c26d5e06c2508240dae661e7231b2 b/tests/resources/typechanges/.gitted/objects/76/fef844064c26d5e06c2508240dae661e7231b2
new file mode 100644
index 000000000..355ce4b5b
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/76/fef844064c26d5e06c2508240dae661e7231b2
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/79/b9f23e85f55ea36a472a902e875bc1121a94cb b/tests/resources/typechanges/.gitted/objects/79/b9f23e85f55ea36a472a902e875bc1121a94cb
new file mode 100644
index 000000000..2b07ad256
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/79/b9f23e85f55ea36a472a902e875bc1121a94cb
@@ -0,0 +1,2 @@
+x•A E]sŠ¹€f€JbŒqçÖ í`I@
+÷—ĸýyïýµäH;ŸZeBrž6L˜P«Yº%8½²&v‹4JmÖ¢ÔÛ^*¼úqpJðà¸Âµúû;¶½ûËZò ¤žœ’Æ 3ZD1Öñ×ú¯)žŸØ"%ø%Ä–38_ \ No newline at end of file
diff --git a/tests/resources/typechanges/.gitted/objects/85/28da0ea65eacf1f74f9ed6696adbac547963ad b/tests/resources/typechanges/.gitted/objects/85/28da0ea65eacf1f74f9ed6696adbac547963ad
new file mode 100644
index 000000000..6d2da6c93
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/85/28da0ea65eacf1f74f9ed6696adbac547963ad
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/8b/3726b365824ad5a07c537247f4bc73ed7d37ea b/tests/resources/typechanges/.gitted/objects/8b/3726b365824ad5a07c537247f4bc73ed7d37ea
new file mode 100644
index 000000000..3dc333bc6
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/8b/3726b365824ad5a07c537247f4bc73ed7d37ea
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/93/3e28c1c8a68838a763d250bdf0b2c6068289c3 b/tests/resources/typechanges/.gitted/objects/93/3e28c1c8a68838a763d250bdf0b2c6068289c3
new file mode 100644
index 000000000..02ad0e97a
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/93/3e28c1c8a68838a763d250bdf0b2c6068289c3
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/96/2710fe5b4e453e9e827945b3487c525968ec4a b/tests/resources/typechanges/.gitted/objects/96/2710fe5b4e453e9e827945b3487c525968ec4a
new file mode 100644
index 000000000..d06b06a52
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/96/2710fe5b4e453e9e827945b3487c525968ec4a
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/96/6cf1b3598e195b31b2cde3784f9a19f0728a6f b/tests/resources/typechanges/.gitted/objects/96/6cf1b3598e195b31b2cde3784f9a19f0728a6f
new file mode 100644
index 000000000..5f9ffd4ed
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/96/6cf1b3598e195b31b2cde3784f9a19f0728a6f
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/99/e8bab9ece009f0fba7eb41f850f4c12bedb9b7 b/tests/resources/typechanges/.gitted/objects/99/e8bab9ece009f0fba7eb41f850f4c12bedb9b7
new file mode 100644
index 000000000..ac17defac
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/99/e8bab9ece009f0fba7eb41f850f4c12bedb9b7
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/9b/19edf33a03a0c59cdfc113bfa5c06179bf9b1a b/tests/resources/typechanges/.gitted/objects/9b/19edf33a03a0c59cdfc113bfa5c06179bf9b1a
new file mode 100644
index 000000000..7ab83aefe
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/9b/19edf33a03a0c59cdfc113bfa5c06179bf9b1a
@@ -0,0 +1,5 @@
+x•ŽI
+1E]箕LTˆè ¼A†J·Ðƒ¤Ó÷7WpûyïñÓ¶,ŸZÑ©Uf cXcR ƒC4¼3Y2æ"£NN:ÔHɈo¨¼6 ,µ’žs’ˆòÁjf—#îk½G›¶
+ïcßyžáÉsá
+·ã§MG¼¦m¹ƒ2–´Bò
+.ÒK)úÚÿµŽþkŠ×Ö‘w8ñžCCà \ No newline at end of file
diff --git a/tests/resources/typechanges/.gitted/objects/9b/db75b73836a99e3dbeea640a81de81031fdc29 b/tests/resources/typechanges/.gitted/objects/9b/db75b73836a99e3dbeea640a81de81031fdc29
new file mode 100644
index 000000000..aed4d8165
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/9b/db75b73836a99e3dbeea640a81de81031fdc29
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/9d/0235c7a7edc0889a18f97a42ee6db9fe688447 b/tests/resources/typechanges/.gitted/objects/9d/0235c7a7edc0889a18f97a42ee6db9fe688447
new file mode 100644
index 000000000..3e02a41b2
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/9d/0235c7a7edc0889a18f97a42ee6db9fe688447
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/9e/ffc457877f109b2a4319e14bee613a15f2a00d b/tests/resources/typechanges/.gitted/objects/9e/ffc457877f109b2a4319e14bee613a15f2a00d
new file mode 100644
index 000000000..fb24100fc
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/9e/ffc457877f109b2a4319e14bee613a15f2a00d
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/a0/a9bad6f6f40325198f938a0e3ae981622d7707 b/tests/resources/typechanges/.gitted/objects/a0/a9bad6f6f40325198f938a0e3ae981622d7707
new file mode 100644
index 000000000..b6b7db785
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/a0/a9bad6f6f40325198f938a0e3ae981622d7707
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/b1/977dc4e573b812d4619754c98138c56999dc0d b/tests/resources/typechanges/.gitted/objects/b1/977dc4e573b812d4619754c98138c56999dc0d
new file mode 100644
index 000000000..e1334057c
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/b1/977dc4e573b812d4619754c98138c56999dc0d
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/d7/5992dd02391e128dac332dcc78d649dd9ab095 b/tests/resources/typechanges/.gitted/objects/d7/5992dd02391e128dac332dcc78d649dd9ab095
new file mode 100644
index 000000000..65f1f530f
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/d7/5992dd02391e128dac332dcc78d649dd9ab095
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/da/e2709d638df52212b1f43ff61797ebfedfcc7c b/tests/resources/typechanges/.gitted/objects/da/e2709d638df52212b1f43ff61797ebfedfcc7c
new file mode 100644
index 000000000..355faa61f
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/da/e2709d638df52212b1f43ff61797ebfedfcc7c
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/e1/152adcb9adf37ec551ada9ba377ab53aec3bad b/tests/resources/typechanges/.gitted/objects/e1/152adcb9adf37ec551ada9ba377ab53aec3bad
new file mode 100644
index 000000000..c68fdcfab
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/e1/152adcb9adf37ec551ada9ba377ab53aec3bad
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/e4/ed436a9eb0f198cda722886a5f8d6d6c836b7b b/tests/resources/typechanges/.gitted/objects/e4/ed436a9eb0f198cda722886a5f8d6d6c836b7b
new file mode 100644
index 000000000..c9229ba25
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/e4/ed436a9eb0f198cda722886a5f8d6d6c836b7b
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/typechanges/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644
index 000000000..711223894
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/f2/0b79342712e0b2315647cd8227a573fd3bc46e b/tests/resources/typechanges/.gitted/objects/f2/0b79342712e0b2315647cd8227a573fd3bc46e
new file mode 100644
index 000000000..3962ba6b4
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/f2/0b79342712e0b2315647cd8227a573fd3bc46e
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/fd/e0147e3b59f381635a3b016e3fe6dacb70779d b/tests/resources/typechanges/.gitted/objects/fd/e0147e3b59f381635a3b016e3fe6dacb70779d
new file mode 100644
index 000000000..e3663da9f
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/fd/e0147e3b59f381635a3b016e3fe6dacb70779d
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/refs/heads/master b/tests/resources/typechanges/.gitted/refs/heads/master
new file mode 100644
index 000000000..546481a33
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/refs/heads/master
@@ -0,0 +1 @@
+6eae26c90e8ccc4d16208972119c40635489c6f0
diff --git a/tests/resources/typechanges/README.md b/tests/resources/typechanges/README.md
new file mode 100644
index 000000000..1f5a95a9f
--- /dev/null
+++ b/tests/resources/typechanges/README.md
@@ -0,0 +1,43 @@
+This is a test repo for libgit2 where tree entries have type changes
+
+Types
+-----
+
+The key types that could be found in tree entries are:
+
+1. GIT_FILEMODE_NEW = 0000000 (i.e. file does not exist)
+2. GIT_FILEMODE_TREE = 0040000
+3. GIT_FILEMODE_BLOB = 0100644
+4. GIT_FILEMODE_BLOB_EXECUTABLE = 0100755
+5. GIT_FILEMODE_LINK = 0120000
+6. GIT_FILEMODE_COMMIT = 0160000
+
+I will try to have every type of transition somewhere in the history
+of this repo.
+
+Commits
+-------
+
+* `a(1--1) b(1--1) c(1--1) d(1--1) e(1--1)`
+ **Initial commit**<br>
+ `79b9f23e85f55ea36a472a902e875bc1121a94cb`
+* `a(1->2) b(1->3) c(1->4) d(1->5) e(1->6)`
+ **Create content**<br>
+ `9bdb75b73836a99e3dbeea640a81de81031fdc29`
+* `a(2->3) b(3->4) c(4->5) d(5->6) e(6->2)`
+ **Changes #1**<br>
+ `0e7ed140b514b8cae23254cb8656fe1674403aff`
+* `a(3->5) b(4->6) c(5->2) d(6->3) e(2->4)`
+ **Changes #2**<br>
+ `9d0235c7a7edc0889a18f97a42ee6db9fe688447`
+* `a(5->3) b(6->4) c(2->5) d(3->6) e(4->2)`
+ **Changes #3**<br>
+ `9b19edf33a03a0c59cdfc113bfa5c06179bf9b1a`
+* `a(3->2) b(4->3) c(5->4) d(6->5) e(2->6)`
+ **Changes #4**<br>
+ `1b63caae4a5ca96f78e8dfefc376c6a39a142475`<br>
+ Matches **Changes #1** except README.md
+* `a(2->1) b(3->1) c(4->1) d(5->1) e(6->1)`
+ **Changes #5**<br>
+ `6eae26c90e8ccc4d16208972119c40635489c6f0`<br>
+ Matches **Initial commit** except README.md and .gitmodules
diff --git a/tests/resources/typechanges/gitmodules b/tests/resources/typechanges/gitmodules
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/resources/typechanges/gitmodules
diff --git a/tests/resources/unsymlinked.git/HEAD b/tests/resources/unsymlinked.git/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/unsymlinked.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/unsymlinked.git/config b/tests/resources/unsymlinked.git/config
new file mode 100644
index 000000000..f57351fd5
--- /dev/null
+++ b/tests/resources/unsymlinked.git/config
@@ -0,0 +1,6 @@
+[core]
+ bare = true
+ repositoryformatversion = 0
+ filemode = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests/resources/unsymlinked.git/description b/tests/resources/unsymlinked.git/description
new file mode 100644
index 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/resources/unsymlinked.git/info/exclude b/tests/resources/unsymlinked.git/info/exclude
new file mode 100644
index 000000000..6d05881d3
--- /dev/null
+++ b/tests/resources/unsymlinked.git/info/exclude
@@ -0,0 +1,2 @@
+# File patterns to ignore; see `git help ignore` for more information.
+# Lines that start with '#' are comments.
diff --git a/tests/resources/unsymlinked.git/objects/08/8b64704e0d6b8bd061dea879418cb5442a3fbf b/tests/resources/unsymlinked.git/objects/08/8b64704e0d6b8bd061dea879418cb5442a3fbf
new file mode 100644
index 000000000..953262fa9
--- /dev/null
+++ b/tests/resources/unsymlinked.git/objects/08/8b64704e0d6b8bd061dea879418cb5442a3fbf
Binary files differ
diff --git a/tests/resources/unsymlinked.git/objects/13/a5e939bca25940c069fd2169d993dba328e30b b/tests/resources/unsymlinked.git/objects/13/a5e939bca25940c069fd2169d993dba328e30b
new file mode 100644
index 000000000..91ec8fa5e
--- /dev/null
+++ b/tests/resources/unsymlinked.git/objects/13/a5e939bca25940c069fd2169d993dba328e30b
Binary files differ
diff --git a/tests/resources/unsymlinked.git/objects/19/bf568e59e3a0b363cafb4106226e62d4a4c41c b/tests/resources/unsymlinked.git/objects/19/bf568e59e3a0b363cafb4106226e62d4a4c41c
new file mode 100644
index 000000000..94afd01e8
--- /dev/null
+++ b/tests/resources/unsymlinked.git/objects/19/bf568e59e3a0b363cafb4106226e62d4a4c41c
Binary files differ
diff --git a/tests/resources/unsymlinked.git/objects/58/1fadd35b4cf320d102a152f918729011604773 b/tests/resources/unsymlinked.git/objects/58/1fadd35b4cf320d102a152f918729011604773
new file mode 100644
index 000000000..5b33d027c
--- /dev/null
+++ b/tests/resources/unsymlinked.git/objects/58/1fadd35b4cf320d102a152f918729011604773
Binary files differ
diff --git a/tests/resources/unsymlinked.git/objects/5c/87b6791e8b13da658a14d1ef7e09b5dc3bac8c b/tests/resources/unsymlinked.git/objects/5c/87b6791e8b13da658a14d1ef7e09b5dc3bac8c
new file mode 100644
index 000000000..67eb14930
--- /dev/null
+++ b/tests/resources/unsymlinked.git/objects/5c/87b6791e8b13da658a14d1ef7e09b5dc3bac8c
Binary files differ
diff --git a/tests/resources/unsymlinked.git/objects/6f/e5f5398af85fb3de8a6aba0339b6d3bfa26a27 b/tests/resources/unsymlinked.git/objects/6f/e5f5398af85fb3de8a6aba0339b6d3bfa26a27
new file mode 100644
index 000000000..c1ea0de75
--- /dev/null
+++ b/tests/resources/unsymlinked.git/objects/6f/e5f5398af85fb3de8a6aba0339b6d3bfa26a27
Binary files differ
diff --git a/tests/resources/unsymlinked.git/objects/7f/ccd75616ec188b8f1b23d67506a334cc34a49d b/tests/resources/unsymlinked.git/objects/7f/ccd75616ec188b8f1b23d67506a334cc34a49d
new file mode 100644
index 000000000..028505563
--- /dev/null
+++ b/tests/resources/unsymlinked.git/objects/7f/ccd75616ec188b8f1b23d67506a334cc34a49d
Binary files differ
diff --git a/tests/resources/unsymlinked.git/objects/80/6999882bf91d24241e4077906b9017605eb1f3 b/tests/resources/unsymlinked.git/objects/80/6999882bf91d24241e4077906b9017605eb1f3
new file mode 100644
index 000000000..e866a75a6
--- /dev/null
+++ b/tests/resources/unsymlinked.git/objects/80/6999882bf91d24241e4077906b9017605eb1f3
Binary files differ
diff --git a/tests/resources/unsymlinked.git/objects/83/7d176303c5005505ec1e4a30231c40930c0230 b/tests/resources/unsymlinked.git/objects/83/7d176303c5005505ec1e4a30231c40930c0230
new file mode 100644
index 000000000..189ab044d
--- /dev/null
+++ b/tests/resources/unsymlinked.git/objects/83/7d176303c5005505ec1e4a30231c40930c0230
Binary files differ
diff --git a/tests/resources/unsymlinked.git/objects/a8/595ccca04f40818ae0155c8f9c77a230e597b6 b/tests/resources/unsymlinked.git/objects/a8/595ccca04f40818ae0155c8f9c77a230e597b6
new file mode 100644
index 000000000..a2ef6be45
--- /dev/null
+++ b/tests/resources/unsymlinked.git/objects/a8/595ccca04f40818ae0155c8f9c77a230e597b6
@@ -0,0 +1,2 @@
+x•Ž[
+Â0EýÎ*æ_“¤yˆÝ€{˜4ShZ))¸| ®À¯ ÎáN[ks=èKß™Á¶Åš¨[’ÉÈQ"4&&—M*¤i/Þ´óÚ! ‹1† S‰*Ÿ™Añ€ÞGt)¢ò-'UŒ £×m‡ñ7O cc¸Õ¹=zåå´µ;(ãPY«‡+*DqÒó^ç¿E!¥œ*­/Î0¯}Z?<ÒÂPæ…¥øšÈJp \ No newline at end of file
diff --git a/tests/resources/unsymlinked.git/objects/cf/8f1cf5cce859c438d6cc067284cb5e161206e7 b/tests/resources/unsymlinked.git/objects/cf/8f1cf5cce859c438d6cc067284cb5e161206e7
new file mode 100644
index 000000000..ec274cb1d
--- /dev/null
+++ b/tests/resources/unsymlinked.git/objects/cf/8f1cf5cce859c438d6cc067284cb5e161206e7
Binary files differ
diff --git a/tests/resources/unsymlinked.git/objects/d5/278d05c8607ec420bfee4cf219fbc0eeebfd6a b/tests/resources/unsymlinked.git/objects/d5/278d05c8607ec420bfee4cf219fbc0eeebfd6a
new file mode 100644
index 000000000..c1b6a5101
--- /dev/null
+++ b/tests/resources/unsymlinked.git/objects/d5/278d05c8607ec420bfee4cf219fbc0eeebfd6a
Binary files differ
diff --git a/tests/resources/unsymlinked.git/objects/f4/e16fb76536591a41454194058d048d8e4dd2e9 b/tests/resources/unsymlinked.git/objects/f4/e16fb76536591a41454194058d048d8e4dd2e9
new file mode 100644
index 000000000..ad751adbe
--- /dev/null
+++ b/tests/resources/unsymlinked.git/objects/f4/e16fb76536591a41454194058d048d8e4dd2e9
Binary files differ
diff --git a/tests/resources/unsymlinked.git/objects/f9/e65619d93fdf2673882e0a261c5e93b1a84006 b/tests/resources/unsymlinked.git/objects/f9/e65619d93fdf2673882e0a261c5e93b1a84006
new file mode 100644
index 000000000..f87cd42fb
--- /dev/null
+++ b/tests/resources/unsymlinked.git/objects/f9/e65619d93fdf2673882e0a261c5e93b1a84006
Binary files differ
diff --git a/tests/resources/unsymlinked.git/refs/heads/exe-file b/tests/resources/unsymlinked.git/refs/heads/exe-file
new file mode 100644
index 000000000..b96ef46ca
--- /dev/null
+++ b/tests/resources/unsymlinked.git/refs/heads/exe-file
@@ -0,0 +1 @@
+a8595ccca04f40818ae0155c8f9c77a230e597b6
diff --git a/tests/resources/unsymlinked.git/refs/heads/master b/tests/resources/unsymlinked.git/refs/heads/master
new file mode 100644
index 000000000..96c17ab17
--- /dev/null
+++ b/tests/resources/unsymlinked.git/refs/heads/master
@@ -0,0 +1 @@
+7fccd75616ec188b8f1b23d67506a334cc34a49d
diff --git a/tests/resources/unsymlinked.git/refs/heads/reg-file b/tests/resources/unsymlinked.git/refs/heads/reg-file
new file mode 100644
index 000000000..b428c00d5
--- /dev/null
+++ b/tests/resources/unsymlinked.git/refs/heads/reg-file
@@ -0,0 +1 @@
+806999882bf91d24241e4077906b9017605eb1f3
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/revwalk/signatureparsing.c b/tests/revwalk/signatureparsing.c
new file mode 100644
index 000000000..5c7d8813d
--- /dev/null
+++ b/tests/revwalk/signatureparsing.c
@@ -0,0 +1,47 @@
+#include "clar_libgit2.h"
+
+static git_repository *_repo;
+static git_revwalk *_walk;
+
+void test_revwalk_signatureparsing__initialize(void)
+{
+ cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_revwalk_new(&_walk, _repo));
+}
+
+void test_revwalk_signatureparsing__cleanup(void)
+{
+ git_revwalk_free(_walk);
+ _walk = NULL;
+
+ git_repository_free(_repo);
+ _repo = NULL;
+}
+
+void test_revwalk_signatureparsing__do_not_choke_when_name_contains_angle_brackets(void)
+{
+ git_reference *ref;
+ git_oid commit_oid;
+ git_commit *commit;
+ const git_signature *signature;
+
+ /*
+ * The branch below points at a commit with angle brackets in the committer/author name
+ * committer <Yu V. Bin Haacked> <foo@example.com> 1323847743 +0100
+ */
+ cl_git_pass(git_reference_lookup(&ref, _repo, "refs/heads/haacked"));
+
+ git_revwalk_push(_walk, git_reference_target(ref));
+ cl_git_pass(git_revwalk_next(&commit_oid, _walk));
+
+ cl_git_pass(git_commit_lookup(&commit, _repo, git_reference_target(ref)));
+
+ signature = git_commit_committer(commit);
+ cl_assert_equal_s("foo@example.com", signature->email);
+ cl_assert_equal_s("<Yu V. Bin Haacked>", signature->name);
+ cl_assert_equal_i(1323847743, (int)signature->when.time);
+ cl_assert_equal_i(60, signature->when.offset);
+
+ git_commit_free(commit);
+ git_reference_free(ref);
+}
diff --git a/tests/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/stash/foreach.c b/tests/stash/foreach.c
new file mode 100644
index 000000000..f1983625f
--- /dev/null
+++ b/tests/stash/foreach.c
@@ -0,0 +1,124 @@
+#include "clar_libgit2.h"
+#include "fileops.h"
+#include "stash_helpers.h"
+
+struct callback_data
+{
+ char **oids;
+ int invokes;
+};
+
+static git_repository *repo;
+static git_signature *signature;
+static git_oid stash_tip_oid;
+struct callback_data data;
+
+#define REPO_NAME "stash"
+
+void test_stash_foreach__initialize(void)
+{
+ cl_git_pass(git_signature_new(
+ &signature,
+ "nulltoken",
+ "emeric.fermas@gmail.com",
+ 1323847743, 60)); /* Wed Dec 14 08:29:03 2011 +0100 */
+
+ memset(&data, 0, sizeof(struct callback_data));
+}
+
+void test_stash_foreach__cleanup(void)
+{
+ git_signature_free(signature);
+ signature = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+
+ cl_git_pass(git_futils_rmdir_r(REPO_NAME, NULL, GIT_RMDIR_REMOVE_FILES));
+}
+
+static int callback_cb(
+ size_t index,
+ const char* message,
+ const git_oid *stash_oid,
+ void *payload)
+{
+ struct callback_data *data = (struct callback_data *)payload;
+
+ GIT_UNUSED(index);
+ GIT_UNUSED(message);
+
+ cl_assert_equal_i(0, git_oid_streq(stash_oid, data->oids[data->invokes++]));
+
+ return 0;
+}
+
+void test_stash_foreach__enumerating_a_empty_repository_doesnt_fail(void)
+{
+ char *oids[] = { NULL };
+
+ data.oids = oids;
+
+ cl_git_pass(git_repository_init(&repo, REPO_NAME, 0));
+
+ cl_git_pass(git_stash_foreach(repo, callback_cb, &data));
+
+ cl_assert_equal_i(0, data.invokes);
+}
+
+void test_stash_foreach__can_enumerate_a_repository(void)
+{
+ char *oids_default[] = {
+ "1d91c842a7cdfc25872b3a763e5c31add8816c25", NULL };
+
+ char *oids_untracked[] = {
+ "7f89a8b15c878809c5c54d1ff8f8c9674154017b",
+ "1d91c842a7cdfc25872b3a763e5c31add8816c25", NULL };
+
+ char *oids_ignored[] = {
+ "c95599a8fef20a7e57582c6727b1a0d02e0a5828",
+ "7f89a8b15c878809c5c54d1ff8f8c9674154017b",
+ "1d91c842a7cdfc25872b3a763e5c31add8816c25", NULL };
+
+ cl_git_pass(git_repository_init(&repo, REPO_NAME, 0));
+
+ setup_stash(repo, signature);
+
+ cl_git_pass(git_stash_save(
+ &stash_tip_oid,
+ repo,
+ signature,
+ NULL,
+ GIT_STASH_DEFAULT));
+
+ data.oids = oids_default;
+
+ cl_git_pass(git_stash_foreach(repo, callback_cb, &data));
+ cl_assert_equal_i(1, data.invokes);
+
+ data.oids = oids_untracked;
+ data.invokes = 0;
+
+ cl_git_pass(git_stash_save(
+ &stash_tip_oid,
+ repo,
+ signature,
+ NULL,
+ GIT_STASH_INCLUDE_UNTRACKED));
+
+ cl_git_pass(git_stash_foreach(repo, callback_cb, &data));
+ cl_assert_equal_i(2, data.invokes);
+
+ data.oids = oids_ignored;
+ data.invokes = 0;
+
+ cl_git_pass(git_stash_save(
+ &stash_tip_oid,
+ repo,
+ signature,
+ NULL,
+ GIT_STASH_INCLUDE_IGNORED));
+
+ cl_git_pass(git_stash_foreach(repo, callback_cb, &data));
+ cl_assert_equal_i(3, data.invokes);
+}
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/status/single.c b/tests/status/single.c
new file mode 100644
index 000000000..292c9120a
--- /dev/null
+++ b/tests/status/single.c
@@ -0,0 +1,45 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+
+static void
+cleanup__remove_file(void *_file)
+{
+ cl_must_pass(p_unlink((char *)_file));
+}
+
+/* test retrieving OID from a file apart from the ODB */
+void test_status_single__hash_single_file(void)
+{
+ static const char file_name[] = "new_file";
+ static const char file_contents[] = "new_file\n";
+ static const char file_hash[] = "d4fa8600b4f37d7516bef4816ae2c64dbf029e3a";
+
+ git_oid expected_id, actual_id;
+
+ /* initialization */
+ git_oid_fromstr(&expected_id, file_hash);
+ cl_git_mkfile(file_name, file_contents);
+ cl_set_cleanup(&cleanup__remove_file, (void *)file_name);
+
+ cl_git_pass(git_odb_hashfile(&actual_id, file_name, GIT_OBJ_BLOB));
+ cl_assert(git_oid_cmp(&expected_id, &actual_id) == 0);
+}
+
+/* test retrieving OID from an empty file apart from the ODB */
+void test_status_single__hash_single_empty_file(void)
+{
+ static const char file_name[] = "new_empty_file";
+ static const char file_contents[] = "";
+ static const char file_hash[] = "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391";
+
+ git_oid expected_id, actual_id;
+
+ /* initialization */
+ git_oid_fromstr(&expected_id, file_hash);
+ cl_git_mkfile(file_name, file_contents);
+ cl_set_cleanup(&cleanup__remove_file, (void *)file_name);
+
+ cl_git_pass(git_odb_hashfile(&actual_id, file_name, GIT_OBJ_BLOB));
+ cl_assert(git_oid_cmp(&expected_id, &actual_id) == 0);
+}
+
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/status/status_helpers.c b/tests/status/status_helpers.c
new file mode 100644
index 000000000..902b65c4f
--- /dev/null
+++ b/tests/status/status_helpers.c
@@ -0,0 +1,101 @@
+#include "clar_libgit2.h"
+#include "status_helpers.h"
+
+int cb_status__normal(
+ const char *path, unsigned int status_flags, void *payload)
+{
+ status_entry_counts *counts = payload;
+
+ if (counts->debug)
+ cb_status__print(path, status_flags, NULL);
+
+ if (counts->entry_count >= counts->expected_entry_count) {
+ counts->wrong_status_flags_count++;
+ goto exit;
+ }
+
+ if (strcmp(path, counts->expected_paths[counts->entry_count])) {
+ counts->wrong_sorted_path++;
+ goto exit;
+ }
+
+ if (status_flags != counts->expected_statuses[counts->entry_count])
+ counts->wrong_status_flags_count++;
+
+exit:
+ counts->entry_count++;
+ return 0;
+}
+
+int cb_status__count(const char *p, unsigned int s, void *payload)
+{
+ volatile int *count = (int *)payload;
+
+ GIT_UNUSED(p);
+ GIT_UNUSED(s);
+
+ (*count)++;
+
+ return 0;
+}
+
+int cb_status__single(const char *p, unsigned int s, void *payload)
+{
+ status_entry_single *data = (status_entry_single *)payload;
+
+ if (data->debug)
+ fprintf(stderr, "%02d: %s (%04x)\n", data->count, p, s);
+
+ data->count++;
+ data->status = s;
+
+ return 0;
+}
+
+int cb_status__print(
+ const char *path, unsigned int status_flags, void *payload)
+{
+ char istatus = ' ', wstatus = ' ';
+ int icount = 0, wcount = 0;
+
+ if (status_flags & GIT_STATUS_INDEX_NEW) {
+ istatus = 'A'; icount++;
+ }
+ if (status_flags & GIT_STATUS_INDEX_MODIFIED) {
+ istatus = 'M'; icount++;
+ }
+ if (status_flags & GIT_STATUS_INDEX_DELETED) {
+ istatus = 'D'; icount++;
+ }
+ if (status_flags & GIT_STATUS_INDEX_RENAMED) {
+ istatus = 'R'; icount++;
+ }
+ if (status_flags & GIT_STATUS_INDEX_TYPECHANGE) {
+ istatus = 'T'; icount++;
+ }
+
+ if (status_flags & GIT_STATUS_WT_NEW) {
+ wstatus = 'A'; wcount++;
+ }
+ if (status_flags & GIT_STATUS_WT_MODIFIED) {
+ wstatus = 'M'; wcount++;
+ }
+ if (status_flags & GIT_STATUS_WT_DELETED) {
+ wstatus = 'D'; wcount++;
+ }
+ if (status_flags & GIT_STATUS_WT_TYPECHANGE) {
+ wstatus = 'T'; wcount++;
+ }
+ if (status_flags & GIT_STATUS_IGNORED) {
+ wstatus = 'I'; wcount++;
+ }
+
+ fprintf(stderr, "%c%c %s (%d/%d%s)\n",
+ istatus, wstatus, path, icount, wcount,
+ (icount > 1 || wcount > 1) ? " INVALID COMBO" : "");
+
+ if (payload)
+ *((int *)payload) += 1;
+
+ return 0;
+}
diff --git a/tests/status/status_helpers.h b/tests/status/status_helpers.h
new file mode 100644
index 000000000..f1f009e02
--- /dev/null
+++ b/tests/status/status_helpers.h
@@ -0,0 +1,39 @@
+#ifndef INCLUDE_cl_status_helpers_h__
+#define INCLUDE_cl_status_helpers_h__
+
+typedef struct {
+ int wrong_status_flags_count;
+ int wrong_sorted_path;
+ int entry_count;
+ const unsigned int* expected_statuses;
+ const char** expected_paths;
+ int expected_entry_count;
+ bool debug;
+} status_entry_counts;
+
+/* cb_status__normal takes payload of "status_entry_counts *" */
+
+extern int cb_status__normal(
+ const char *path, unsigned int status_flags, void *payload);
+
+
+/* cb_status__count takes payload of "int *" */
+
+extern int cb_status__count(const char *p, unsigned int s, void *payload);
+
+
+typedef struct {
+ int count;
+ unsigned int status;
+ bool debug;
+} status_entry_single;
+
+/* cb_status__single takes payload of "status_entry_single *" */
+
+extern int cb_status__single(const char *p, unsigned int s, void *payload);
+
+/* cb_status__print takes optional payload of "int *" */
+
+extern int cb_status__print(const char *p, unsigned int s, void *payload);
+
+#endif
diff --git a/tests/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/status/worktree_init.c b/tests/status/worktree_init.c
new file mode 100644
index 000000000..296c27c86
--- /dev/null
+++ b/tests/status/worktree_init.c
@@ -0,0 +1,341 @@
+#include "clar_libgit2.h"
+#include "git2/sys/repository.h"
+
+#include "fileops.h"
+#include "ignore.h"
+#include "status_helpers.h"
+#include "posix.h"
+#include "util.h"
+#include "path.h"
+
+static void cleanup_new_repo(void *path)
+{
+ cl_fixture_cleanup((char *)path);
+}
+
+void test_status_worktree_init__cannot_retrieve_the_status_of_a_bare_repository(void)
+{
+ git_repository *repo;
+ unsigned int status = 0;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+ cl_assert_equal_i(GIT_EBAREREPO, git_status_file(&status, repo, "dummy"));
+ git_repository_free(repo);
+}
+
+void test_status_worktree_init__first_commit_in_progress(void)
+{
+ git_repository *repo;
+ git_index *index;
+ status_entry_single result;
+
+ cl_set_cleanup(&cleanup_new_repo, "getting_started");
+
+ cl_git_pass(git_repository_init(&repo, "getting_started", 0));
+ cl_git_mkfile("getting_started/testfile.txt", "content\n");
+
+ memset(&result, 0, sizeof(result));
+ cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
+ cl_assert_equal_i(1, result.count);
+ cl_assert(result.status == GIT_STATUS_WT_NEW);
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_add_bypath(index, "testfile.txt"));
+ cl_git_pass(git_index_write(index));
+
+ memset(&result, 0, sizeof(result));
+ cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
+ cl_assert_equal_i(1, result.count);
+ cl_assert(result.status == GIT_STATUS_INDEX_NEW);
+
+ git_index_free(index);
+ git_repository_free(repo);
+}
+
+
+
+void test_status_worktree_init__status_file_without_index_or_workdir(void)
+{
+ git_repository *repo;
+ unsigned int status = 0;
+ git_index *index;
+
+ cl_git_pass(p_mkdir("wd", 0777));
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_repository_set_workdir(repo, "wd", false));
+
+ cl_git_pass(git_index_open(&index, "empty-index"));
+ cl_assert_equal_i(0, (int)git_index_entrycount(index));
+ git_repository_set_index(repo, index);
+
+ cl_git_pass(git_status_file(&status, repo, "branch_file.txt"));
+
+ cl_assert_equal_i(GIT_STATUS_INDEX_DELETED, status);
+
+ git_repository_free(repo);
+ git_index_free(index);
+ cl_git_pass(p_rmdir("wd"));
+}
+
+static void fill_index_wth_head_entries(git_repository *repo, git_index *index)
+{
+ git_oid oid;
+ git_commit *commit;
+ git_tree *tree;
+
+ cl_git_pass(git_reference_name_to_id(&oid, repo, "HEAD"));
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+ cl_git_pass(git_commit_tree(&tree, commit));
+
+ cl_git_pass(git_index_read_tree(index, tree));
+ cl_git_pass(git_index_write(index));
+
+ git_tree_free(tree);
+ git_commit_free(commit);
+}
+
+void test_status_worktree_init__status_file_with_clean_index_and_empty_workdir(void)
+{
+ git_repository *repo;
+ unsigned int status = 0;
+ git_index *index;
+
+ cl_git_pass(p_mkdir("wd", 0777));
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_repository_set_workdir(repo, "wd", false));
+
+ cl_git_pass(git_index_open(&index, "my-index"));
+ fill_index_wth_head_entries(repo, index);
+
+ git_repository_set_index(repo, index);
+
+ cl_git_pass(git_status_file(&status, repo, "branch_file.txt"));
+
+ cl_assert_equal_i(GIT_STATUS_WT_DELETED, status);
+
+ git_repository_free(repo);
+ git_index_free(index);
+ cl_git_pass(p_rmdir("wd"));
+ cl_git_pass(p_unlink("my-index"));
+}
+
+void test_status_worktree_init__bracket_in_filename(void)
+{
+ git_repository *repo;
+ git_index *index;
+ status_entry_single result;
+ unsigned int status_flags;
+ int error;
+
+ #define FILE_WITH_BRACKET "LICENSE[1].md"
+ #define FILE_WITHOUT_BRACKET "LICENSE1.md"
+
+ cl_set_cleanup(&cleanup_new_repo, "with_bracket");
+
+ cl_git_pass(git_repository_init(&repo, "with_bracket", 0));
+ cl_git_mkfile("with_bracket/" FILE_WITH_BRACKET, "I have a bracket in my name\n");
+
+ /* file is new to working directory */
+
+ memset(&result, 0, sizeof(result));
+ cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
+ cl_assert_equal_i(1, result.count);
+ cl_assert(result.status == GIT_STATUS_WT_NEW);
+
+ cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_BRACKET));
+ cl_assert(status_flags == GIT_STATUS_WT_NEW);
+
+ /* ignore the file */
+
+ cl_git_rewritefile("with_bracket/.gitignore", "*.md\n.gitignore\n");
+
+ memset(&result, 0, sizeof(result));
+ cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
+ cl_assert_equal_i(2, result.count);
+ cl_assert(result.status == GIT_STATUS_IGNORED);
+
+ cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_BRACKET));
+ cl_assert(status_flags == GIT_STATUS_IGNORED);
+
+ /* don't ignore the file */
+
+ cl_git_rewritefile("with_bracket/.gitignore", ".gitignore\n");
+
+ memset(&result, 0, sizeof(result));
+ cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
+ cl_assert_equal_i(2, result.count);
+ cl_assert(result.status == GIT_STATUS_WT_NEW);
+
+ cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_BRACKET));
+ cl_assert(status_flags == GIT_STATUS_WT_NEW);
+
+ /* add the file to the index */
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_add_bypath(index, FILE_WITH_BRACKET));
+ cl_git_pass(git_index_write(index));
+
+ memset(&result, 0, sizeof(result));
+ cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
+ cl_assert_equal_i(2, result.count);
+ cl_assert(result.status == GIT_STATUS_INDEX_NEW);
+
+ cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_BRACKET));
+ cl_assert(status_flags == GIT_STATUS_INDEX_NEW);
+
+ /* Create file without bracket */
+
+ cl_git_mkfile("with_bracket/" FILE_WITHOUT_BRACKET, "I have no bracket in my name!\n");
+
+ cl_git_pass(git_status_file(&status_flags, repo, FILE_WITHOUT_BRACKET));
+ cl_assert(status_flags == GIT_STATUS_WT_NEW);
+
+ cl_git_pass(git_status_file(&status_flags, repo, "LICENSE\\[1\\].md"));
+ cl_assert(status_flags == GIT_STATUS_INDEX_NEW);
+
+ error = git_status_file(&status_flags, repo, FILE_WITH_BRACKET);
+ cl_git_fail(error);
+ cl_assert_equal_i(GIT_EAMBIGUOUS, error);
+
+ git_index_free(index);
+ git_repository_free(repo);
+}
+
+void test_status_worktree_init__space_in_filename(void)
+{
+ git_repository *repo;
+ git_index *index;
+ status_entry_single result;
+ unsigned int status_flags;
+
+#define FILE_WITH_SPACE "LICENSE - copy.md"
+
+ cl_set_cleanup(&cleanup_new_repo, "with_space");
+ cl_git_pass(git_repository_init(&repo, "with_space", 0));
+ cl_git_mkfile("with_space/" FILE_WITH_SPACE, "I have a space in my name\n");
+
+ /* file is new to working directory */
+
+ memset(&result, 0, sizeof(result));
+ cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
+ cl_assert_equal_i(1, result.count);
+ cl_assert(result.status == GIT_STATUS_WT_NEW);
+
+ cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_SPACE));
+ cl_assert(status_flags == GIT_STATUS_WT_NEW);
+
+ /* ignore the file */
+
+ cl_git_rewritefile("with_space/.gitignore", "*.md\n.gitignore\n");
+
+ memset(&result, 0, sizeof(result));
+ cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
+ cl_assert_equal_i(2, result.count);
+ cl_assert(result.status == GIT_STATUS_IGNORED);
+
+ cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_SPACE));
+ cl_assert(status_flags == GIT_STATUS_IGNORED);
+
+ /* don't ignore the file */
+
+ cl_git_rewritefile("with_space/.gitignore", ".gitignore\n");
+
+ memset(&result, 0, sizeof(result));
+ cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
+ cl_assert_equal_i(2, result.count);
+ cl_assert(result.status == GIT_STATUS_WT_NEW);
+
+ cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_SPACE));
+ cl_assert(status_flags == GIT_STATUS_WT_NEW);
+
+ /* add the file to the index */
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_add_bypath(index, FILE_WITH_SPACE));
+ cl_git_pass(git_index_write(index));
+
+ memset(&result, 0, sizeof(result));
+ cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
+ cl_assert_equal_i(2, result.count);
+ cl_assert(result.status == GIT_STATUS_INDEX_NEW);
+
+ cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_SPACE));
+ cl_assert(status_flags == GIT_STATUS_INDEX_NEW);
+
+ git_index_free(index);
+ git_repository_free(repo);
+}
+
+static int cb_status__expected_path(const char *p, unsigned int s, void *payload)
+{
+ const char *expected_path = (const char *)payload;
+
+ GIT_UNUSED(s);
+
+ if (payload == NULL)
+ cl_fail("Unexpected path");
+
+ cl_assert_equal_s(expected_path, p);
+
+ return 0;
+}
+
+void test_status_worktree_init__disable_pathspec_match(void)
+{
+ git_repository *repo;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ char *file_with_bracket = "LICENSE[1].md",
+ *imaginary_file_with_bracket = "LICENSE[1-2].md";
+
+ cl_set_cleanup(&cleanup_new_repo, "pathspec");
+ cl_git_pass(git_repository_init(&repo, "pathspec", 0));
+ cl_git_mkfile("pathspec/LICENSE[1].md", "screaming bracket\n");
+ cl_git_mkfile("pathspec/LICENSE1.md", "no bracket\n");
+
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH;
+ opts.pathspec.count = 1;
+ opts.pathspec.strings = &file_with_bracket;
+
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__expected_path,
+ file_with_bracket)
+ );
+
+ /* Test passing a pathspec matching files in the workdir. */
+ /* Must not match because pathspecs are disabled. */
+ opts.pathspec.strings = &imaginary_file_with_bracket;
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__expected_path, NULL)
+ );
+
+ git_repository_free(repo);
+}
+
+void test_status_worktree_init__new_staged_file_must_handle_crlf(void)
+{
+ git_repository *repo;
+ git_index *index;
+ unsigned int status;
+
+ cl_set_cleanup(&cleanup_new_repo, "getting_started");
+ cl_git_pass(git_repository_init(&repo, "getting_started", 0));
+
+ /* Ensure that repo has core.autocrlf=true */
+ cl_repo_set_bool(repo, "core.autocrlf", true);
+
+ cl_git_mkfile("getting_started/testfile.txt", "content\r\n"); /* Content with CRLF */
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_add_bypath(index, "testfile.txt"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_status_file(&status, repo, "testfile.txt"));
+ cl_assert_equal_i(GIT_STATUS_INDEX_NEW, status);
+
+ git_index_free(index);
+ git_repository_free(repo);
+}
+
diff --git a/tests/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/trace/trace.c b/tests/trace/trace.c
new file mode 100644
index 000000000..87b325378
--- /dev/null
+++ b/tests/trace/trace.c
@@ -0,0 +1,87 @@
+#include "clar_libgit2.h"
+#include "trace.h"
+
+static int written = 0;
+
+static void trace_callback(git_trace_level_t level, const char *message)
+{
+ GIT_UNUSED(level);
+
+ cl_assert(strcmp(message, "Hello world!") == 0);
+
+ written = 1;
+}
+
+void test_trace_trace__initialize(void)
+{
+ git_trace_set(GIT_TRACE_INFO, trace_callback);
+ written = 0;
+}
+
+void test_trace_trace__cleanup(void)
+{
+ git_trace_set(GIT_TRACE_NONE, NULL);
+}
+
+void test_trace_trace__sets(void)
+{
+#ifdef GIT_TRACE
+ cl_assert(git_trace_level() == GIT_TRACE_INFO);
+#endif
+}
+
+void test_trace_trace__can_reset(void)
+{
+#ifdef GIT_TRACE
+ cl_assert(git_trace_level() == GIT_TRACE_INFO);
+ cl_git_pass(git_trace_set(GIT_TRACE_ERROR, trace_callback));
+
+ cl_assert(written == 0);
+ git_trace(GIT_TRACE_INFO, "Hello %s!", "world");
+ cl_assert(written == 0);
+
+ git_trace(GIT_TRACE_ERROR, "Hello %s!", "world");
+ cl_assert(written == 1);
+#endif
+}
+
+void test_trace_trace__can_unset(void)
+{
+#ifdef GIT_TRACE
+ cl_assert(git_trace_level() == GIT_TRACE_INFO);
+ cl_git_pass(git_trace_set(GIT_TRACE_NONE, NULL));
+
+ cl_assert(git_trace_level() == GIT_TRACE_NONE);
+
+ cl_assert(written == 0);
+ git_trace(GIT_TRACE_FATAL, "Hello %s!", "world");
+ cl_assert(written == 0);
+#endif
+}
+
+void test_trace_trace__skips_higher_level(void)
+{
+#ifdef GIT_TRACE
+ cl_assert(written == 0);
+ git_trace(GIT_TRACE_DEBUG, "Hello %s!", "world");
+ cl_assert(written == 0);
+#endif
+}
+
+void test_trace_trace__writes(void)
+{
+#ifdef GIT_TRACE
+ cl_assert(written == 0);
+ git_trace(GIT_TRACE_INFO, "Hello %s!", "world");
+ cl_assert(written == 1);
+#endif
+}
+
+void test_trace_trace__writes_lower_level(void)
+{
+#ifdef GIT_TRACE
+ cl_assert(written == 0);
+ git_trace(GIT_TRACE_ERROR, "Hello %s!", "world");
+ cl_assert(written == 1);
+#endif
+}
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
+}