Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/libgit2.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVicent Marti <tanoku@gmail.com>2012-05-19 03:46:11 +0400
committerVicent Marti <tanoku@gmail.com>2012-05-19 03:46:11 +0400
commit5b9fac39d8a76b9139667c26a63e6b3f204b3977 (patch)
treee6ba28025f92c16563c4ffa8bc60b95f17d69691
parent7ef9f1b5606c2672105ecbbf34c022a71ef212fe (diff)
parentad5df35a47d56c3d716d7a56eac4aeb611987c11 (diff)
Merge branch 'development'v0.17.0
Conflicts: .travis.yml
-rw-r--r--.travis.yml2
-rw-r--r--AUTHORS2
-rw-r--r--CMakeLists.txt57
-rw-r--r--COPYING507
-rw-r--r--Makefile.embed2
-rw-r--r--README.md63
-rw-r--r--deps/regex/config.h7
-rw-r--r--deps/regex/regcomp.c3856
-rw-r--r--deps/regex/regex.c85
-rw-r--r--deps/regex/regex.h582
-rw-r--r--deps/regex/regex_internal.c1744
-rw-r--r--deps/regex/regex_internal.h810
-rw-r--r--deps/regex/regexec.c4369
-rw-r--r--docs/error-handling.md111
-rw-r--r--examples/Makefile8
-rw-r--r--examples/diff.c240
-rw-r--r--examples/general.c4
-rw-r--r--examples/network/Makefile2
-rw-r--r--examples/network/fetch.c160
-rw-r--r--examples/network/git2.c10
-rw-r--r--examples/network/index-pack.c61
-rw-r--r--examples/network/ls-remote.c12
-rw-r--r--include/git2.h7
-rw-r--r--include/git2/attr.h180
-rw-r--r--include/git2/blob.h22
-rw-r--r--include/git2/branch.h121
-rw-r--r--include/git2/commit.h21
-rw-r--r--include/git2/common.h7
-rw-r--r--include/git2/config.h131
-rw-r--r--include/git2/diff.h362
-rw-r--r--include/git2/errors.h128
-rw-r--r--include/git2/index.h20
-rw-r--r--include/git2/indexer.h47
-rw-r--r--include/git2/merge.h35
-rw-r--r--include/git2/net.h2
-rw-r--r--include/git2/notes.h139
-rw-r--r--include/git2/object.h4
-rw-r--r--include/git2/odb.h18
-rw-r--r--include/git2/odb_backend.h16
-rw-r--r--include/git2/oid.h21
-rw-r--r--include/git2/reflog.h10
-rw-r--r--include/git2/refs.h50
-rw-r--r--include/git2/refspec.h18
-rw-r--r--include/git2/remote.h84
-rw-r--r--include/git2/repository.h30
-rw-r--r--include/git2/revwalk.h80
-rw-r--r--include/git2/signature.h6
-rw-r--r--include/git2/status.h131
-rw-r--r--include/git2/submodule.h103
-rw-r--r--include/git2/tag.h36
-rw-r--r--include/git2/threads.h2
-rw-r--r--include/git2/tree.h55
-rw-r--r--include/git2/types.h12
-rw-r--r--include/git2/version.h6
-rw-r--r--include/git2/windows.h2
-rw-r--r--include/git2/zlib.h40
-rw-r--r--packaging/rpm/README6
-rw-r--r--packaging/rpm/libgit2.spec106
-rw-r--r--src/attr.c560
-rw-r--r--src/attr.h41
-rw-r--r--src/attr_file.c304
-rw-r--r--src/attr_file.h60
-rw-r--r--src/blob.c233
-rw-r--r--src/blob.h3
-rw-r--r--src/branch.c208
-rw-r--r--src/branch.h17
-rw-r--r--src/bswap.h2
-rw-r--r--src/buffer.c209
-rw-r--r--src/buffer.h68
-rw-r--r--src/cache.c18
-rw-r--r--src/cache.h2
-rw-r--r--src/cc-compat.h34
-rw-r--r--src/commit.c192
-rw-r--r--src/commit.h2
-rw-r--r--src/common.h14
-rw-r--r--src/compat/fnmatch.c (renamed from src/win32/fnmatch.c)2
-rw-r--r--src/compat/fnmatch.h (renamed from src/win32/fnmatch.h)6
-rw-r--r--src/config.c356
-rw-r--r--src/config.h7
-rw-r--r--src/config_cache.c94
-rw-r--r--src/config_file.c1110
-rw-r--r--src/config_file.h31
-rw-r--r--src/crlf.c228
-rw-r--r--src/delta-apply.c24
-rw-r--r--src/delta-apply.h4
-rw-r--r--src/diff.c783
-rw-r--r--src/diff.h40
-rw-r--r--src/diff_output.c786
-rw-r--r--src/errors.c163
-rw-r--r--src/fetch.c152
-rw-r--r--src/fetch.h9
-rw-r--r--src/filebuf.c245
-rw-r--r--src/filebuf.h29
-rw-r--r--src/fileops.c331
-rw-r--r--src/fileops.h74
-rw-r--r--src/filter.c165
-rw-r--r--src/filter.h119
-rw-r--r--src/global.c8
-rw-r--r--src/global.h5
-rw-r--r--src/hash.c2
-rw-r--r--src/hash.h2
-rw-r--r--src/hashtable.c258
-rw-r--r--src/hashtable.h95
-rw-r--r--src/ignore.c207
-rw-r--r--src/ignore.h27
-rw-r--r--src/index.c345
-rw-r--r--src/index.h6
-rw-r--r--src/indexer.c672
-rw-r--r--src/iterator.c748
-rw-r--r--src/iterator.h151
-rw-r--r--src/khash.h608
-rw-r--r--src/map.h7
-rw-r--r--src/message.c61
-rw-r--r--src/message.h14
-rw-r--r--src/mwindow.c58
-rw-r--r--src/mwindow.h8
-rw-r--r--src/netops.c112
-rw-r--r--src/netops.h12
-rw-r--r--src/notes.c548
-rw-r--r--src/notes.h28
-rw-r--r--src/object.c80
-rw-r--r--src/odb.c374
-rw-r--r--src/odb.h41
-rw-r--r--src/odb_loose.c376
-rw-r--r--src/odb_pack.c180
-rw-r--r--src/oid.c91
-rw-r--r--src/oidmap.h42
-rw-r--r--src/pack.c359
-rw-r--r--src/pack.h25
-rw-r--r--src/path.c382
-rw-r--r--src/path.h87
-rw-r--r--src/pkt.c230
-rw-r--r--src/pkt.h14
-rw-r--r--src/pool.c294
-rw-r--r--src/pool.h125
-rw-r--r--src/posix.c55
-rw-r--r--src/posix.h14
-rw-r--r--src/ppc/sha1.c2
-rw-r--r--src/ppc/sha1.h2
-rw-r--r--src/pqueue.c14
-rw-r--r--src/pqueue.h2
-rw-r--r--src/protocol.c34
-rw-r--r--src/protocol.h2
-rw-r--r--src/reflog.c187
-rw-r--r--src/reflog.h2
-rw-r--r--src/refs.c1250
-rw-r--r--src/refs.h29
-rw-r--r--src/refspec.c55
-rw-r--r--src/refspec.h4
-rw-r--r--src/remote.c400
-rw-r--r--src/remote.h2
-rw-r--r--src/repository.c1033
-rw-r--r--src/repository.h80
-rw-r--r--src/revwalk.c592
-rw-r--r--src/sha1.c6
-rw-r--r--src/sha1.h4
-rw-r--r--src/sha1_lookup.c5
-rw-r--r--src/sha1_lookup.h2
-rw-r--r--src/signature.c131
-rw-r--r--src/signature.h2
-rw-r--r--src/status.c876
-rw-r--r--src/strmap.h64
-rw-r--r--src/submodule.c387
-rw-r--r--src/tag.c273
-rw-r--r--src/tag.h3
-rw-r--r--src/thread-utils.c2
-rw-r--r--src/thread-utils.h2
-rw-r--r--src/transport.c46
-rw-r--r--src/transport.h18
-rw-r--r--src/transports/git.c350
-rw-r--r--src/transports/http.c379
-rw-r--r--src/transports/local.c160
-rw-r--r--src/tree-cache.c79
-rw-r--r--src/tree-cache.h2
-rw-r--r--src/tree.c534
-rw-r--r--src/tree.h16
-rw-r--r--src/tsort.c30
-rw-r--r--src/unix/map.c32
-rw-r--r--src/unix/posix.h12
-rw-r--r--src/util.c108
-rw-r--r--src/util.h78
-rw-r--r--src/vector.c103
-rw-r--r--src/vector.h33
-rw-r--r--src/win32/dir.c122
-rw-r--r--src/win32/dir.h5
-rw-r--r--src/win32/git2.rc2
-rw-r--r--src/win32/map.c54
-rw-r--r--src/win32/mingw-compat.h2
-rw-r--r--src/win32/msvc-compat.h2
-rw-r--r--src/win32/posix.h18
-rw-r--r--src/win32/posix_w32.c273
-rw-r--r--src/win32/pthread.c21
-rw-r--r--src/win32/pthread.h2
-rw-r--r--src/win32/utf-conv.c44
-rw-r--r--src/win32/utf-conv.h2
-rw-r--r--src/xdiff/xdiff.h135
-rw-r--r--src/xdiff/xdiffi.c572
-rw-r--r--src/xdiff/xdiffi.h63
-rw-r--r--src/xdiff/xemit.c253
-rw-r--r--src/xdiff/xemit.h36
-rw-r--r--src/xdiff/xhistogram.c371
-rw-r--r--src/xdiff/xinclude.h46
-rw-r--r--src/xdiff/xmacros.h54
-rw-r--r--src/xdiff/xmerge.c619
-rw-r--r--src/xdiff/xpatience.c358
-rw-r--r--src/xdiff/xprepare.c483
-rw-r--r--src/xdiff/xprepare.h34
-rw-r--r--src/xdiff/xtypes.h67
-rw-r--r--src/xdiff/xutils.c419
-rw-r--r--src/xdiff/xutils.h49
-rw-r--r--tests-clar/attr/attr_expect.h42
-rw-r--r--tests-clar/attr/file.c155
-rw-r--r--tests-clar/attr/flags.c108
-rw-r--r--tests-clar/attr/lookup.c312
-rw-r--r--tests-clar/attr/repo.c278
-rw-r--r--tests-clar/buf/basic.c6
-rwxr-xr-xtests-clar/clar22
-rw-r--r--tests-clar/clar_helpers.c71
-rw-r--r--tests-clar/clar_libgit2.h31
-rw-r--r--tests-clar/commit/commit.c44
-rw-r--r--tests-clar/commit/parse.c350
-rw-r--r--tests-clar/commit/signature.c65
-rw-r--r--tests-clar/commit/write.c140
-rw-r--r--tests-clar/config/add.c4
-rw-r--r--tests-clar/config/multivar.c151
-rw-r--r--tests-clar/config/new.c8
-rw-r--r--tests-clar/config/read.c74
-rw-r--r--tests-clar/config/stress.c28
-rw-r--r--tests-clar/config/write.c23
-rw-r--r--tests-clar/core/buffer.c191
-rw-r--r--tests-clar/core/dirent.c17
-rw-r--r--tests-clar/core/errors.c60
-rw-r--r--tests-clar/core/filebuf.c16
-rw-r--r--tests-clar/core/oid.c8
-rw-r--r--tests-clar/core/path.c74
-rw-r--r--tests-clar/core/pool.c85
-rw-r--r--tests-clar/core/rmdir.c28
-rw-r--r--tests-clar/core/strmap.c102
-rw-r--r--tests-clar/diff/blob.c254
-rw-r--r--tests-clar/diff/diff_helpers.c99
-rw-r--r--tests-clar/diff/diff_helpers.h47
-rw-r--r--tests-clar/diff/index.c92
-rw-r--r--tests-clar/diff/iterator.c570
-rw-r--r--tests-clar/diff/patch.c99
-rw-r--r--tests-clar/diff/tree.c210
-rw-r--r--tests-clar/diff/workdir.c301
-rw-r--r--tests-clar/index/tests.c246
-rw-r--r--tests-clar/network/createremotethenload.c4
-rw-r--r--tests-clar/network/remotelocal.c28
-rw-r--r--tests-clar/network/remotes.c127
-rw-r--r--tests-clar/notes/notes.c133
-rw-r--r--tests-clar/notes/notesref.c57
-rw-r--r--tests-clar/object/blob/filter.c125
-rw-r--r--tests-clar/object/blob/write.c69
-rw-r--r--tests-clar/object/commit/commitstagedfile.c10
-rw-r--r--tests-clar/object/lookup.c63
-rw-r--r--tests-clar/object/message.c171
-rw-r--r--tests-clar/object/raw/compare.c6
-rw-r--r--tests-clar/object/raw/convert.c18
-rw-r--r--tests-clar/object/raw/hash.c36
-rw-r--r--tests-clar/object/raw/type2string.c26
-rw-r--r--tests-clar/object/raw/write.c455
-rw-r--r--tests-clar/object/tag/peel.c56
-rw-r--r--tests-clar/object/tag/read.c130
-rw-r--r--tests-clar/object/tag/write.c192
-rw-r--r--tests-clar/object/tree/diff.c168
-rw-r--r--tests-clar/object/tree/frompath.c36
-rw-r--r--tests-clar/object/tree/read.c75
-rw-r--r--tests-clar/object/tree/write.c84
-rw-r--r--tests-clar/odb/mixed.c24
-rw-r--r--tests-clar/refs/branches/create.c113
-rw-r--r--tests-clar/refs/branches/delete.c91
-rw-r--r--tests-clar/refs/branches/listall.c78
-rw-r--r--tests-clar/refs/branches/move.c72
-rw-r--r--tests-clar/refs/crashes.c2
-rw-r--r--tests-clar/refs/create.c149
-rw-r--r--tests-clar/refs/delete.c85
-rw-r--r--tests-clar/refs/list.c53
-rw-r--r--tests-clar/refs/listall.c4
-rw-r--r--tests-clar/refs/lookup.c42
-rw-r--r--tests-clar/refs/normalize.c200
-rw-r--r--tests-clar/refs/overwrite.c136
-rw-r--r--tests-clar/refs/pack.c67
-rw-r--r--tests-clar/refs/read.c194
-rw-r--r--tests-clar/refs/reflog.c123
-rw-r--r--tests-clar/refs/rename.c339
-rw-r--r--tests-clar/refs/unicode.c42
-rw-r--r--tests-clar/repo/discover.c142
-rw-r--r--tests-clar/repo/getters.c16
-rw-r--r--tests-clar/repo/init.c24
-rw-r--r--tests-clar/repo/open.c258
-rw-r--r--tests-clar/repo/setters.c80
-rw-r--r--tests-clar/resources/.gitattributes (renamed from tests/resources/.gitattributes)0
-rw-r--r--tests-clar/resources/.gitignore (renamed from tests/resources/.gitignore)0
-rw-r--r--tests-clar/resources/attr/.gitted/HEAD (renamed from tests/resources/attr/.gitted/HEAD)0
-rw-r--r--tests-clar/resources/attr/.gitted/config (renamed from tests/resources/attr/.gitted/config)0
-rw-r--r--tests-clar/resources/attr/.gitted/description (renamed from tests/resources/attr/.gitted/description)0
-rw-r--r--tests-clar/resources/attr/.gitted/indexbin0 -> 1856 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/info/attributes (renamed from tests/resources/attr/.gitted/info/attributes)0
-rw-r--r--tests-clar/resources/attr/.gitted/info/exclude (renamed from tests/resources/attr/.gitted/info/exclude)0
-rw-r--r--tests-clar/resources/attr/.gitted/logs/HEAD8
-rw-r--r--tests-clar/resources/attr/.gitted/logs/refs/heads/master8
-rw-r--r--tests-clar/resources/attr/.gitted/objects/10/8bb4e7fd7b16490dc33ff7d972151e73d7166ebin0 -> 130 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/21/7878ab49e1314388ea2e32dc6fdb58a1b969e04
-rw-r--r--tests-clar/resources/attr/.gitted/objects/24/fa9a9fc4e202313e24b648087495441dab432bbin0 -> 180 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/29/29de282ce999e95183aedac6451d3384559c4b (renamed from tests/resources/attr/.gitted/objects/29/29de282ce999e95183aedac6451d3384559c4b)bin58 -> 58 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/2b/40c5aca159b04ea8d20ffe36cdf8b09369b14a (renamed from tests/resources/attr/.gitted/objects/2b/40c5aca159b04ea8d20ffe36cdf8b09369b14a)0
-rw-r--r--tests-clar/resources/attr/.gitted/objects/2c/66e14f77196ea763fb1e41612c1aa2bc2d8ed2 (renamed from tests/resources/attr/.gitted/objects/2c/66e14f77196ea763fb1e41612c1aa2bc2d8ed2)bin316 -> 316 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/2d/e7dfe3588f3c7e9ad59e7d50ba90e3329df9d9 (renamed from tests/resources/attr/.gitted/objects/2d/e7dfe3588f3c7e9ad59e7d50ba90e3329df9d9)bin124 -> 124 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/37/0fe9ec224ce33e71f9e5ec2bd1142ce9937a6abin0 -> 177 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/3a/6df026462ebafe455af9867d27eda20a9e0974bin0 -> 84 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/3b/74db7ab381105dc0d28f8295a77f6a82989292 (renamed from tests/resources/attr/.gitted/objects/3b/74db7ab381105dc0d28f8295a77f6a82989292)bin276 -> 276 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/3e/42ffc54a663f9401cc25843d6c0e71a33e4249 (renamed from tests/resources/attr/.gitted/objects/3e/42ffc54a663f9401cc25843d6c0e71a33e4249)bin596 -> 596 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/45/141a79a77842c59a63229403220a4e4be74e3d (renamed from tests/resources/attr/.gitted/objects/45/141a79a77842c59a63229403220a4e4be74e3d)bin36 -> 36 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/45/5a314fa848d52ae1f11d254da4f60858fc97f4bin0 -> 446 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 (renamed from tests/resources/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057)bin18 -> 18 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/4d/713dc48e6b1bd75b0d61ad078ba9ca3a56745d2
-rw-r--r--tests-clar/resources/attr/.gitted/objects/4e/49ba8c5b6c32ff28cd9dcb60be34df50fcc485bin0 -> 81 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/55/6f8c827b8e4a02ad5cab77dca2bcb3e226b0b3 (renamed from tests/resources/attr/.gitted/objects/55/6f8c827b8e4a02ad5cab77dca2bcb3e226b0b3)bin24 -> 24 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/58/19a185d77b03325aaf87cafc771db36f6ddca7 (renamed from tests/resources/attr/.gitted/objects/58/19a185d77b03325aaf87cafc771db36f6ddca7)bin19 -> 19 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/60/5812ab7fe421fdd325a935d35cb06a9234a7d7 (renamed from tests/resources/attr/.gitted/objects/60/5812ab7fe421fdd325a935d35cb06a9234a7d7)0
-rw-r--r--tests-clar/resources/attr/.gitted/objects/6b/ab5c79cd5140d0f800917f550eb2a3dc32b0da (renamed from tests/resources/attr/.gitted/objects/6b/ab5c79cd5140d0f800917f550eb2a3dc32b0da)0
-rw-r--r--tests-clar/resources/attr/.gitted/objects/6d/968d62c89c7d9ea23a4c9a7b665d017c3d8ffdbin0 -> 422 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/71/7fc31f6b84f9d6fc3a4edbca259d7fc92beee2bin0 -> 422 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/93/61f40bb97239cf55811892e14de2e344168ba1bin0 -> 45 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/94/da4faa0a6bfb8ee6ccf7153801a69202b31857 (renamed from tests/resources/attr/.gitted/objects/94/da4faa0a6bfb8ee6ccf7153801a69202b31857)bin124 -> 124 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/96/089fd31ce1d3ee2afb0ba09ba063066932f027bin0 -> 422 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9 (renamed from tests/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9)bin95 -> 95 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/9e/5bdc47d6a80f2be0ea3049ad74231b94609242bin0 -> 20 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9 (renamed from tests/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9)bin151 -> 151 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320 (renamed from tests/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320)bin351 -> 351 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/a5/d76cad53f66f1312bd995909a5bab3c0820770 (renamed from tests/resources/attr/.gitted/objects/a5/d76cad53f66f1312bd995909a5bab3c0820770)0
-rw-r--r--tests-clar/resources/attr/.gitted/objects/a9/7cc019851d401a4f1d091cb91a15890a0dd1ba2
-rw-r--r--tests-clar/resources/attr/.gitted/objects/c0/091889c0c77142b87a1fa5123a6398a61d33e7 (renamed from tests/resources/attr/.gitted/objects/c0/091889c0c77142b87a1fa5123a6398a61d33e7)bin290 -> 290 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/c4/85abe35abd4aa6fd83b076a78bbea9e2e7e06c (renamed from tests/resources/attr/.gitted/objects/c4/85abe35abd4aa6fd83b076a78bbea9e2e7e06c)bin129 -> 129 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/c7/aadd770d5907a8475c29e9ee21a27b88bf675d (renamed from tests/resources/attr/.gitted/objects/c7/aadd770d5907a8475c29e9ee21a27b88bf675d)bin60 -> 60 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/c9/6bbb2c2557a8325ae1559e3ba79cdcecb230762
-rw-r--r--tests-clar/resources/attr/.gitted/objects/ce/39a97a7fb1fa90bcf5e711249c1e507476ae0ebin0 -> 446 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/d5/7da33c16b14326ecb05d19bbea908f5e4c47d9 (renamed from tests/resources/attr/.gitted/objects/d5/7da33c16b14326ecb05d19bbea908f5e4c47d9)bin379 -> 379 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2 (renamed from tests/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2)bin18 -> 18 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/dc/cada462d3df8ac6de596fb8c896aba9344f941 (renamed from tests/resources/attr/.gitted/objects/dc/cada462d3df8ac6de596fb8c896aba9344f941)bin35 -> 35 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/de/863bff4976c9ed7e17a4da0fd524908dc84049bin0 -> 4115 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/e5/63cf4758f0d646f1b14b76016aa17fa9e549a4 (renamed from tests/resources/attr/.gitted/objects/e5/63cf4758f0d646f1b14b76016aa17fa9e549a4)bin39 -> 39 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/ec/b97df2a174987475ac816e3847fc8e9f6c596bbin0 -> 171 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/ed/f3dcee4003d71f139777898882ccd097e34c53bin0 -> 6289 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/f2/c6d717cf4a5a3e6b02684155ab07b766982165 (renamed from tests/resources/attr/.gitted/objects/f2/c6d717cf4a5a3e6b02684155ab07b766982165)bin44 -> 44 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/f5/b0af1fb4f5c0cd7aad880711d368a07333c3072
-rw-r--r--tests-clar/resources/attr/.gitted/objects/fb/5067b1aef3ac1ada4b379dbcb7d17255df7d78 (renamed from tests/resources/attr/.gitted/objects/fb/5067b1aef3ac1ada4b379dbcb7d17255df7d78)bin28 -> 28 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/objects/fe/773770c5a6cc7185580c9204b1ff18a33ff3fc1
-rw-r--r--tests-clar/resources/attr/.gitted/objects/ff/69f8639ce2e6010b3f33a74160aad98b48da2b (renamed from tests/resources/attr/.gitted/objects/ff/69f8639ce2e6010b3f33a74160aad98b48da2b)bin18 -> 18 bytes
-rw-r--r--tests-clar/resources/attr/.gitted/refs/heads/master1
-rw-r--r--tests-clar/resources/attr/attr0 (renamed from tests/resources/attr/attr0)0
-rw-r--r--tests-clar/resources/attr/attr1 (renamed from tests/resources/attr/attr1)0
-rw-r--r--tests-clar/resources/attr/attr2 (renamed from tests/resources/attr/attr2)0
-rw-r--r--tests-clar/resources/attr/attr3 (renamed from tests/resources/attr/attr3)0
-rw-r--r--tests-clar/resources/attr/binfile (renamed from tests/resources/attr/binfile)0
-rw-r--r--tests-clar/resources/attr/dir/file (renamed from tests/resources/attr/dir/file)0
-rw-r--r--tests-clar/resources/attr/file (renamed from tests/resources/attr/file)0
-rw-r--r--tests-clar/resources/attr/gitattributes (renamed from tests/resources/attr/gitattributes)4
-rw-r--r--tests-clar/resources/attr/gitignore (renamed from tests/resources/attr/gitignore)0
-rw-r--r--tests-clar/resources/attr/ign (renamed from tests/resources/attr/ign)0
-rw-r--r--tests-clar/resources/attr/macro_bad (renamed from tests/resources/attr/macro_bad)0
-rw-r--r--tests-clar/resources/attr/macro_test (renamed from tests/resources/attr/macro_test)0
-rw-r--r--tests-clar/resources/attr/root_test1 (renamed from tests/resources/attr/root_test1)0
-rw-r--r--tests-clar/resources/attr/root_test26
-rw-r--r--tests-clar/resources/attr/root_test319
-rw-r--r--tests-clar/resources/attr/root_test4.txt14
-rw-r--r--tests-clar/resources/attr/sub/.gitattributes (renamed from tests/resources/attr/sub/.gitattributes)0
-rw-r--r--tests-clar/resources/attr/sub/abc (renamed from tests/resources/attr/sub/abc)0
-rw-r--r--tests-clar/resources/attr/sub/dir/file (renamed from tests/resources/attr/sub/dir/file)0
-rw-r--r--tests-clar/resources/attr/sub/file (renamed from tests/resources/attr/sub/file)0
-rw-r--r--tests-clar/resources/attr/sub/ign (renamed from tests/resources/attr/sub/ign)0
-rw-r--r--tests-clar/resources/attr/sub/sub/.gitattributes3
-rw-r--r--tests-clar/resources/attr/sub/sub/dir (renamed from tests/resources/attr/sub/sub/dir)0
-rw-r--r--tests-clar/resources/attr/sub/sub/file (renamed from tests/resources/attr/sub/sub/file)0
-rw-r--r--tests-clar/resources/attr/sub/sub/subsub.txt (renamed from tests/resources/attr/sub/sub/subsub.txt)0
-rw-r--r--tests-clar/resources/attr/sub/subdir_test1 (renamed from tests/resources/attr/sub/subdir_test1)0
-rw-r--r--tests-clar/resources/attr/sub/subdir_test2.txt (renamed from tests/resources/attr/sub/subdir_test2.txt)0
-rw-r--r--tests-clar/resources/attr_index/.gitted/HEAD (renamed from tests/resources/bad_tag.git/HEAD)0
-rw-r--r--tests-clar/resources/attr_index/.gitted/config (renamed from tests/resources/status/.gitted/config)0
-rw-r--r--tests-clar/resources/attr_index/.gitted/description (renamed from tests/resources/empty_bare.git/description)0
-rw-r--r--tests-clar/resources/attr_index/.gitted/indexbin0 -> 520 bytes
-rw-r--r--tests-clar/resources/attr_index/.gitted/info/exclude (renamed from tests/resources/empty_bare.git/info/exclude)0
-rw-r--r--tests-clar/resources/attr_index/.gitted/info/refs1
-rw-r--r--tests-clar/resources/attr_index/.gitted/logs/HEAD4
-rw-r--r--tests-clar/resources/attr_index/.gitted/logs/refs/heads/master4
-rw-r--r--tests-clar/resources/attr_index/.gitted/objects/38/12cfef36615db1788d4e63f90028007e17a3483
-rw-r--r--tests-clar/resources/attr_index/.gitted/objects/59/d942b8be2784bc96db9b22202c10815c9a077b1
-rw-r--r--tests-clar/resources/attr_index/.gitted/objects/cd/f17ea3fe625ef812f4dce7f423f4f299287505bin0 -> 61 bytes
-rw-r--r--tests-clar/resources/attr_index/.gitted/objects/f7/2502ddd01412bb20796ff812af56fd53b82b52bin0 -> 149 bytes
-rw-r--r--tests-clar/resources/attr_index/.gitted/objects/info/packs2
-rw-r--r--tests-clar/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.idxbin0 -> 1492 bytes
-rw-r--r--tests-clar/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.packbin0 -> 1106 bytes
-rw-r--r--tests-clar/resources/attr_index/.gitted/packed-refs2
-rw-r--r--tests-clar/resources/attr_index/.gitted/refs/heads/master1
-rw-r--r--tests-clar/resources/attr_index/README.md1
-rw-r--r--tests-clar/resources/attr_index/README.txt1
-rw-r--r--tests-clar/resources/attr_index/gitattributes4
-rw-r--r--tests-clar/resources/attr_index/sub/sub/.gitattributes3
-rw-r--r--tests-clar/resources/attr_index/sub/sub/README.md1
-rw-r--r--tests-clar/resources/attr_index/sub/sub/README.txt1
-rw-r--r--tests-clar/resources/bad_tag.git/HEAD (renamed from tests/resources/empty_bare.git/HEAD)0
-rw-r--r--tests-clar/resources/bad_tag.git/config (renamed from tests/resources/bad_tag.git/config)0
-rw-r--r--tests-clar/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.idx (renamed from tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.idx)bin1268 -> 1268 bytes
-rw-r--r--tests-clar/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.pack (renamed from tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.pack)bin596 -> 596 bytes
-rw-r--r--tests-clar/resources/bad_tag.git/packed-refs (renamed from tests/resources/bad_tag.git/packed-refs)0
-rw-r--r--tests-clar/resources/bad_tag.git/refs/dummy-marker.txt (renamed from tests/resources/bad_tag.git/refs/dummy-marker.txt)0
-rw-r--r--tests-clar/resources/big.index (renamed from tests/resources/big.index)bin335272 -> 335272 bytes
-rw-r--r--tests-clar/resources/config/.gitconfig (renamed from tests/resources/config/.gitconfig)0
-rw-r--r--tests-clar/resources/config/config0 (renamed from tests/resources/config/config0)0
-rw-r--r--tests-clar/resources/config/config1 (renamed from tests/resources/config/config1)0
-rw-r--r--tests-clar/resources/config/config10 (renamed from tests/resources/config/config10)0
-rw-r--r--tests-clar/resources/config/config113
-rw-r--r--tests-clar/resources/config/config127
-rw-r--r--tests-clar/resources/config/config132
-rw-r--r--tests-clar/resources/config/config2 (renamed from tests/resources/config/config2)0
-rw-r--r--tests-clar/resources/config/config3 (renamed from tests/resources/config/config3)0
-rw-r--r--tests-clar/resources/config/config4 (renamed from tests/resources/config/config4)0
-rw-r--r--tests-clar/resources/config/config5 (renamed from tests/resources/config/config5)0
-rw-r--r--tests-clar/resources/config/config6 (renamed from tests/resources/config/config6)0
-rw-r--r--tests-clar/resources/config/config7 (renamed from tests/resources/config/config7)0
-rw-r--r--tests-clar/resources/config/config8 (renamed from tests/resources/config/config8)0
-rw-r--r--tests-clar/resources/config/config9 (renamed from tests/resources/config/config9)0
-rw-r--r--tests-clar/resources/duplicate.git/COMMIT_EDITMSG1
-rw-r--r--tests-clar/resources/duplicate.git/HEAD (renamed from tests/resources/empty_standard_repo/.gitted/HEAD)0
-rw-r--r--tests-clar/resources/duplicate.git/config5
-rw-r--r--tests-clar/resources/duplicate.git/description (renamed from tests/resources/empty_standard_repo/.gitted/description)0
-rwxr-xr-xtests-clar/resources/duplicate.git/hooks/applypatch-msg.sample15
-rwxr-xr-xtests-clar/resources/duplicate.git/hooks/commit-msg.sample24
-rwxr-xr-xtests-clar/resources/duplicate.git/hooks/post-update.sample8
-rwxr-xr-xtests-clar/resources/duplicate.git/hooks/pre-applypatch.sample14
-rwxr-xr-xtests-clar/resources/duplicate.git/hooks/pre-commit.sample50
-rwxr-xr-xtests-clar/resources/duplicate.git/hooks/pre-rebase.sample169
-rwxr-xr-xtests-clar/resources/duplicate.git/hooks/prepare-commit-msg.sample36
-rwxr-xr-xtests-clar/resources/duplicate.git/hooks/update.sample128
-rw-r--r--tests-clar/resources/duplicate.git/indexbin0 -> 104 bytes
-rw-r--r--tests-clar/resources/duplicate.git/info/exclude (renamed from tests/resources/empty_standard_repo/.gitted/info/exclude)0
-rw-r--r--tests-clar/resources/duplicate.git/info/refs1
-rw-r--r--tests-clar/resources/duplicate.git/logs/HEAD1
-rw-r--r--tests-clar/resources/duplicate.git/logs/refs/heads/master1
-rw-r--r--tests-clar/resources/duplicate.git/objects/ce/013625030ba8dba906f756967f9e9ca394464abin0 -> 21 bytes
-rw-r--r--tests-clar/resources/duplicate.git/objects/info/packs2
-rw-r--r--tests-clar/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.idxbin0 -> 1156 bytes
-rw-r--r--tests-clar/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.packbin0 -> 213 bytes
-rw-r--r--tests-clar/resources/duplicate.git/packed-refs2
-rw-r--r--tests-clar/resources/empty_bare.git/HEAD (renamed from tests/resources/status/.gitted/HEAD)0
-rw-r--r--tests-clar/resources/empty_bare.git/config (renamed from tests/resources/empty_bare.git/config)0
-rw-r--r--tests-clar/resources/empty_bare.git/description (renamed from tests/resources/status/.gitted/description)0
-rw-r--r--tests-clar/resources/empty_bare.git/info/exclude6
-rw-r--r--tests-clar/resources/empty_bare.git/objects/info/dummy-marker.txt (renamed from tests/resources/empty_bare.git/objects/info/dummy-marker.txt)0
-rw-r--r--tests-clar/resources/empty_bare.git/objects/pack/dummy-marker.txt (renamed from tests/resources/empty_bare.git/objects/pack/dummy-marker.txt)0
-rw-r--r--tests-clar/resources/empty_bare.git/refs/heads/dummy-marker.txt (renamed from tests/resources/empty_bare.git/refs/heads/dummy-marker.txt)0
-rw-r--r--tests-clar/resources/empty_bare.git/refs/tags/dummy-marker.txt (renamed from tests/resources/empty_bare.git/refs/tags/dummy-marker.txt)0
-rw-r--r--tests-clar/resources/empty_standard_repo/.gitted/HEAD (renamed from tests/resources/testrepo.git/HEAD)0
-rw-r--r--tests-clar/resources/empty_standard_repo/.gitted/config (renamed from tests/resources/empty_standard_repo/.gitted/config)0
-rw-r--r--tests-clar/resources/empty_standard_repo/.gitted/description1
-rw-r--r--tests-clar/resources/empty_standard_repo/.gitted/info/exclude6
-rw-r--r--tests-clar/resources/empty_standard_repo/.gitted/objects/info/dummy-marker.txt (renamed from tests/resources/empty_standard_repo/.gitted/objects/info/dummy-marker.txt)0
-rw-r--r--tests-clar/resources/empty_standard_repo/.gitted/objects/pack/dummy-marker.txt (renamed from tests/resources/empty_standard_repo/.gitted/objects/pack/dummy-marker.txt)0
-rw-r--r--tests-clar/resources/empty_standard_repo/.gitted/refs/heads/dummy-marker.txt (renamed from tests/resources/empty_standard_repo/.gitted/refs/heads/dummy-marker.txt)0
-rw-r--r--tests-clar/resources/empty_standard_repo/.gitted/refs/tags/dummy-marker.txt (renamed from tests/resources/empty_standard_repo/.gitted/refs/tags/dummy-marker.txt)0
-rw-r--r--tests-clar/resources/gitgit.index (renamed from tests/resources/gitgit.index)bin134799 -> 134799 bytes
-rw-r--r--tests-clar/resources/issue_592/.gitted/COMMIT_EDITMSG1
-rw-r--r--tests-clar/resources/issue_592/.gitted/HEAD1
-rw-r--r--tests-clar/resources/issue_592/.gitted/config8
-rw-r--r--tests-clar/resources/issue_592/.gitted/indexbin0 -> 392 bytes
-rw-r--r--tests-clar/resources/issue_592/.gitted/info/exclude6
-rw-r--r--tests-clar/resources/issue_592/.gitted/logs/HEAD2
-rw-r--r--tests-clar/resources/issue_592/.gitted/logs/refs/heads/master2
-rw-r--r--tests-clar/resources/issue_592/.gitted/objects/06/07ee9d4ccce8e4c4fa13c2c7d727e7faba4e0ebin0 -> 87 bytes
-rw-r--r--tests-clar/resources/issue_592/.gitted/objects/49/363a72a90d9424240258cd3759f23788ecf1d8bin0 -> 55 bytes
-rw-r--r--tests-clar/resources/issue_592/.gitted/objects/4d/383e87f0371ba8fa353f3912db6862b2625e852
-rw-r--r--tests-clar/resources/issue_592/.gitted/objects/71/44be264b61825fbff68046fe999bdfe96a1792bin0 -> 50 bytes
-rw-r--r--tests-clar/resources/issue_592/.gitted/objects/be/de83ee10b5b3f00239660b00acec2d55fd0b84bin0 -> 107 bytes
-rw-r--r--tests-clar/resources/issue_592/.gitted/objects/e3/8fcc7a6060f5eb5b876e836b52ae4769363f21bin0 -> 137 bytes
-rw-r--r--tests-clar/resources/issue_592/.gitted/objects/f1/adef63cb08891a0942b76fc4b9c50c6c494bc7bin0 -> 29 bytes
-rw-r--r--tests-clar/resources/issue_592/.gitted/refs/heads/master1
-rw-r--r--tests-clar/resources/issue_592/a.txt1
-rw-r--r--tests-clar/resources/issue_592/c/a.txt1
-rw-r--r--tests-clar/resources/issue_592/l.txt1
-rw-r--r--tests-clar/resources/issue_592/t/a.txt1
-rw-r--r--tests-clar/resources/issue_592/t/b.txt1
-rw-r--r--tests-clar/resources/issue_592b/.gitted/HEAD1
-rw-r--r--tests-clar/resources/issue_592b/.gitted/config6
-rw-r--r--tests-clar/resources/issue_592b/.gitted/description1
-rwxr-xr-xtests-clar/resources/issue_592b/.gitted/hooks/post-update.sample8
-rw-r--r--tests-clar/resources/issue_592b/.gitted/indexbin0 -> 376 bytes
-rw-r--r--tests-clar/resources/issue_592b/.gitted/info/exclude6
-rw-r--r--tests-clar/resources/issue_592b/.gitted/logs/HEAD1
-rw-r--r--tests-clar/resources/issue_592b/.gitted/logs/refs/heads/master1
-rw-r--r--tests-clar/resources/issue_592b/.gitted/objects/3f/bf1852f72fd268e36457b13a18cdd9a4c9ea352
-rw-r--r--tests-clar/resources/issue_592b/.gitted/objects/6f/a891d3e578c83e1c03bdb9e0fdd8e6e934157fbin0 -> 28 bytes
-rw-r--r--tests-clar/resources/issue_592b/.gitted/objects/80/07d41d5794e6ce4d4d2c97e370d5a9aa6d5213bin0 -> 24 bytes
-rw-r--r--tests-clar/resources/issue_592b/.gitted/objects/a6/5fb6583a7c425284142f285bc359a2d6565513bin0 -> 93 bytes
-rw-r--r--tests-clar/resources/issue_592b/.gitted/objects/ae/be7a55922c7097ef91ca3a7bc327a901d87c2cbin0 -> 122 bytes
-rw-r--r--tests-clar/resources/issue_592b/.gitted/objects/b3/44b055867fcdc1f01eaa75056a43e868eb4fbcbin0 -> 36 bytes
-rw-r--r--tests-clar/resources/issue_592b/.gitted/objects/f7/d75fbfad8b1d2e307ced287ea78aad403cdce3bin0 -> 57 bytes
-rw-r--r--tests-clar/resources/issue_592b/.gitted/refs/heads/master1
-rw-r--r--tests-clar/resources/issue_592b/gitignore1
-rw-r--r--tests-clar/resources/issue_592b/ignored/contained/ignored3.txt1
-rw-r--r--tests-clar/resources/issue_592b/ignored/contained/tracked3.txt1
-rw-r--r--tests-clar/resources/issue_592b/ignored/ignored2.txt1
-rw-r--r--tests-clar/resources/issue_592b/ignored/tracked2.txt1
-rw-r--r--tests-clar/resources/issue_592b/ignored1.txt1
-rw-r--r--tests-clar/resources/issue_592b/tracked1.txt1
-rw-r--r--tests-clar/resources/status/.gitted/COMMIT_EDITMSG (renamed from tests/resources/status/.gitted/COMMIT_EDITMSG)0
-rw-r--r--tests-clar/resources/status/.gitted/HEAD1
-rw-r--r--tests-clar/resources/status/.gitted/ORIG_HEAD (renamed from tests/resources/status/.gitted/ORIG_HEAD)0
-rw-r--r--tests-clar/resources/status/.gitted/config6
-rw-r--r--tests-clar/resources/status/.gitted/description1
-rw-r--r--tests-clar/resources/status/.gitted/index (renamed from tests/resources/status/.gitted/index)bin1160 -> 1160 bytes
-rw-r--r--tests-clar/resources/status/.gitted/info/exclude (renamed from tests/resources/status/.gitted/info/exclude)0
-rw-r--r--tests-clar/resources/status/.gitted/logs/HEAD (renamed from tests/resources/status/.gitted/logs/HEAD)0
-rw-r--r--tests-clar/resources/status/.gitted/logs/refs/heads/master (renamed from tests/resources/status/.gitted/logs/refs/heads/master)0
-rw-r--r--tests-clar/resources/status/.gitted/objects/00/17bd4ab1ec30440b17bae1680cff124ab5f1f6 (renamed from tests/resources/status/.gitted/objects/00/17bd4ab1ec30440b17bae1680cff124ab5f1f6)0
-rw-r--r--tests-clar/resources/status/.gitted/objects/06/1d42a44cacde5726057b67558821d95db96f19 (renamed from tests/resources/status/.gitted/objects/06/1d42a44cacde5726057b67558821d95db96f19)bin44 -> 44 bytes
-rw-r--r--tests-clar/resources/status/.gitted/objects/18/88c805345ba265b0ee9449b8877b6064592058 (renamed from tests/resources/status/.gitted/objects/18/88c805345ba265b0ee9449b8877b6064592058)bin36 -> 36 bytes
-rw-r--r--tests-clar/resources/status/.gitted/objects/19/d9cc8584ac2c7dcf57d2680375e80f099dc481 (renamed from tests/resources/status/.gitted/objects/19/d9cc8584ac2c7dcf57d2680375e80f099dc481)bin22 -> 22 bytes
-rw-r--r--tests-clar/resources/status/.gitted/objects/26/a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f (renamed from tests/resources/status/.gitted/objects/26/a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f)0
-rw-r--r--tests-clar/resources/status/.gitted/objects/32/504b727382542f9f089e24fddac5e78533e96c (renamed from tests/resources/status/.gitted/objects/32/504b727382542f9f089e24fddac5e78533e96c)bin31 -> 31 bytes
-rw-r--r--tests-clar/resources/status/.gitted/objects/37/fcb02ccc1a85d1941e7f106d52dc3702dcf0d0 (renamed from tests/resources/status/.gitted/objects/37/fcb02ccc1a85d1941e7f106d52dc3702dcf0d0)bin331 -> 331 bytes
-rw-r--r--tests-clar/resources/status/.gitted/objects/45/2e4244b5d083ddf0460acf1ecc74db9dcfa11a (renamed from tests/resources/status/.gitted/objects/45/2e4244b5d083ddf0460acf1ecc74db9dcfa11a)bin30 -> 30 bytes
-rw-r--r--tests-clar/resources/status/.gitted/objects/52/9a16e8e762d4acb7b9636ff540a00831f9155a (renamed from tests/resources/status/.gitted/objects/52/9a16e8e762d4acb7b9636ff540a00831f9155a)bin32 -> 32 bytes
-rw-r--r--tests-clar/resources/status/.gitted/objects/53/ace0d1cc1145a5f4fe4f78a186a60263190733 (renamed from tests/resources/status/.gitted/objects/53/ace0d1cc1145a5f4fe4f78a186a60263190733)bin36 -> 36 bytes
-rw-r--r--tests-clar/resources/status/.gitted/objects/54/52d32f1dd538eb0405e8a83cc185f79e25e80f (renamed from tests/resources/status/.gitted/objects/54/52d32f1dd538eb0405e8a83cc185f79e25e80f)bin29 -> 29 bytes
-rw-r--r--tests-clar/resources/status/.gitted/objects/55/d316c9ba708999f1918e9677d01dfcae69c6b9 (renamed from tests/resources/status/.gitted/objects/55/d316c9ba708999f1918e9677d01dfcae69c6b9)bin33 -> 33 bytes
-rw-r--r--tests-clar/resources/status/.gitted/objects/70/bd9443ada07063e7fbf0b3ff5c13f7494d89c2 (renamed from tests/resources/status/.gitted/objects/70/bd9443ada07063e7fbf0b3ff5c13f7494d89c2)bin44 -> 44 bytes
-rw-r--r--tests-clar/resources/status/.gitted/objects/73/5b6a258cd196a8f7c9428419b02c1dca93fd75 (renamed from tests/resources/status/.gitted/objects/73/5b6a258cd196a8f7c9428419b02c1dca93fd75)bin160 -> 160 bytes
-rw-r--r--tests-clar/resources/status/.gitted/objects/75/6e27627e67bfbc048d01ece5819c6de733d7ea (renamed from tests/resources/status/.gitted/objects/75/6e27627e67bfbc048d01ece5819c6de733d7ea)bin301 -> 301 bytes
-rw-r--r--tests-clar/resources/status/.gitted/objects/90/6ee7711f4f4928ddcb2a5f8fbc500deba0d2a8 (renamed from tests/resources/status/.gitted/objects/90/6ee7711f4f4928ddcb2a5f8fbc500deba0d2a8)bin46 -> 46 bytes
-rw-r--r--tests-clar/resources/status/.gitted/objects/90/b8c29d8ba39434d1c63e1b093daaa26e5bd972 (renamed from tests/resources/status/.gitted/objects/90/b8c29d8ba39434d1c63e1b093daaa26e5bd972)bin41 -> 41 bytes
-rw-r--r--tests-clar/resources/status/.gitted/objects/9c/2e02cdffa8d73e6c189074594477a6baf87960 (renamed from tests/resources/status/.gitted/objects/9c/2e02cdffa8d73e6c189074594477a6baf87960)bin268 -> 268 bytes
-rw-r--r--tests-clar/resources/status/.gitted/objects/a0/de7e0ac200c489c41c59dfa910154a70264e6e (renamed from tests/resources/status/.gitted/objects/a0/de7e0ac200c489c41c59dfa910154a70264e6e)bin29 -> 29 bytes
-rw-r--r--tests-clar/resources/status/.gitted/objects/a6/191982709b746d5650e93c2acf34ef74e11504 (renamed from tests/resources/status/.gitted/objects/a6/191982709b746d5650e93c2acf34ef74e11504)bin37 -> 37 bytes
-rw-r--r--tests-clar/resources/status/.gitted/objects/a6/be623522ce87a1d862128ac42672604f7b468b (renamed from tests/resources/status/.gitted/objects/a6/be623522ce87a1d862128ac42672604f7b468b)bin46 -> 46 bytes
-rw-r--r--tests-clar/resources/status/.gitted/objects/aa/27a641456848200fdb7f7c99ba36f8a0952877 (renamed from tests/resources/status/.gitted/objects/aa/27a641456848200fdb7f7c99ba36f8a0952877)bin120 -> 120 bytes
-rw-r--r--tests-clar/resources/status/.gitted/objects/da/bc8af9bd6e9f5bbe96a176f1a24baf3d1f8916 (renamed from tests/resources/status/.gitted/objects/da/bc8af9bd6e9f5bbe96a176f1a24baf3d1f8916)bin42 -> 42 bytes
-rw-r--r--tests-clar/resources/status/.gitted/objects/e8/ee89e15bbe9b20137715232387b3de5b28972e (renamed from tests/resources/status/.gitted/objects/e8/ee89e15bbe9b20137715232387b3de5b28972e)bin38 -> 38 bytes
-rw-r--r--tests-clar/resources/status/.gitted/objects/e9/b9107f290627c04d097733a10055af941f6bca (renamed from tests/resources/status/.gitted/objects/e9/b9107f290627c04d097733a10055af941f6bca)bin37 -> 37 bytes
-rw-r--r--tests-clar/resources/status/.gitted/objects/ed/062903b8f6f3dccb2fa81117ba6590944ef9bd (renamed from tests/resources/status/.gitted/objects/ed/062903b8f6f3dccb2fa81117ba6590944ef9bd)bin42 -> 42 bytes
-rw-r--r--tests-clar/resources/status/.gitted/refs/heads/master (renamed from tests/resources/status/.gitted/refs/heads/master)0
-rw-r--r--tests-clar/resources/status/current_file (renamed from tests/resources/status/current_file)0
-rw-r--r--tests-clar/resources/status/ignored_file (renamed from tests/resources/status/ignored_file)0
-rw-r--r--tests-clar/resources/status/modified_file (renamed from tests/resources/status/modified_file)0
-rw-r--r--tests-clar/resources/status/new_file (renamed from tests/resources/status/new_file)0
-rw-r--r--tests-clar/resources/status/staged_changes (renamed from tests/resources/status/staged_changes)0
-rw-r--r--tests-clar/resources/status/staged_changes_modified_file (renamed from tests/resources/status/staged_changes_modified_file)0
-rw-r--r--tests-clar/resources/status/staged_delete_modified_file (renamed from tests/resources/status/staged_delete_modified_file)0
-rw-r--r--tests-clar/resources/status/staged_new_file (renamed from tests/resources/status/staged_new_file)0
-rw-r--r--tests-clar/resources/status/staged_new_file_modified_file (renamed from tests/resources/status/staged_new_file_modified_file)0
-rw-r--r--tests-clar/resources/status/subdir.txt (renamed from tests/resources/status/subdir.txt)0
-rw-r--r--tests-clar/resources/status/subdir/current_file (renamed from tests/resources/status/subdir/current_file)0
-rw-r--r--tests-clar/resources/status/subdir/modified_file (renamed from tests/resources/status/subdir/modified_file)0
-rw-r--r--tests-clar/resources/status/subdir/new_file (renamed from tests/resources/status/subdir/new_file)0
-rw-r--r--tests-clar/resources/submodules/.gitted/HEAD1
-rw-r--r--tests-clar/resources/submodules/.gitted/config6
-rw-r--r--tests-clar/resources/submodules/.gitted/description1
-rw-r--r--tests-clar/resources/submodules/.gitted/indexbin0 -> 408 bytes
-rw-r--r--tests-clar/resources/submodules/.gitted/info/exclude8
-rw-r--r--tests-clar/resources/submodules/.gitted/info/refs1
-rw-r--r--tests-clar/resources/submodules/.gitted/logs/HEAD2
-rw-r--r--tests-clar/resources/submodules/.gitted/logs/refs/heads/master2
-rw-r--r--tests-clar/resources/submodules/.gitted/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e2
-rw-r--r--tests-clar/resources/submodules/.gitted/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357bin0 -> 97 bytes
-rw-r--r--tests-clar/resources/submodules/.gitted/objects/97/896810b3210244a62a82458b8e0819ecfc68503
-rw-r--r--tests-clar/resources/submodules/.gitted/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888bin0 -> 138 bytes
-rw-r--r--tests-clar/resources/submodules/.gitted/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818bin0 -> 21 bytes
-rw-r--r--tests-clar/resources/submodules/.gitted/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4aebin0 -> 120 bytes
-rw-r--r--tests-clar/resources/submodules/.gitted/objects/info/packs2
-rw-r--r--tests-clar/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idxbin0 -> 1156 bytes
-rw-r--r--tests-clar/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.packbin0 -> 228 bytes
-rw-r--r--tests-clar/resources/submodules/.gitted/packed-refs2
-rw-r--r--tests-clar/resources/submodules/.gitted/refs/heads/master1
-rw-r--r--tests-clar/resources/submodules/added1
-rw-r--r--tests-clar/resources/submodules/gitmodules3
-rw-r--r--tests-clar/resources/submodules/ignored1
-rw-r--r--tests-clar/resources/submodules/modified2
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/HEAD1
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/config12
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/description1
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/indexbin0 -> 256 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/info/exclude6
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/logs/HEAD1
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/logs/refs/heads/master1
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 (renamed from tests/resources/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08)bin19 -> 19 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 (renamed from tests/resources/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7)bin51 -> 51 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd (renamed from tests/resources/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccd)bin119 -> 119 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b (renamed from tests/resources/testrepo.git/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b)bin21 -> 21 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d (renamed from tests/resources/testrepo.git/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d)bin21 -> 21 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54 (renamed from tests/resources/testrepo.git/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54)bin50 -> 50 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc (renamed from tests/resources/testrepo.git/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc)bin23 -> 23 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057bin0 -> 18 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 (renamed from tests/resources/testrepo.git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045)0
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 (renamed from tests/resources/testrepo.git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644)0
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a (renamed from tests/resources/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a)bin119 -> 119 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af (renamed from tests/resources/testrepo.git/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af)0
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980 (renamed from tests/resources/testrepo.git/objects/7b/4384978d2493e851f9cca7858815fac9b10980)bin145 -> 145 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d (renamed from tests/resources/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d)bin82 -> 82 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479 (renamed from tests/resources/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479)bin126 -> 126 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162 (renamed from tests/resources/testrepo.git/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162)0
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4 (renamed from tests/resources/testrepo.git/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4)bin50 -> 50 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a (renamed from tests/resources/testrepo.git/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a)0
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f (renamed from tests/resources/testrepo.git/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f)0
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750 (renamed from tests/resources/testrepo.git/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750)0
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd (renamed from tests/resources/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd)bin28 -> 28 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 (renamed from tests/resources/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6)bin26 -> 26 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12 (renamed from tests/resources/testrepo.git/objects/ae/90f12eea699729ed24555e40b9fd669da12a12)bin148 -> 148 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 (renamed from tests/resources/testrepo.git/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1)0
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593 (renamed from tests/resources/testrepo.git/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593)bin80 -> 80 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 (renamed from tests/resources/testrepo.git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644)0
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd (renamed from tests/resources/testrepo.git/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd)0
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f (renamed from tests/resources/testrepo.git/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f)bin21 -> 21 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 (renamed from tests/resources/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391)bin15 -> 15 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0 (renamed from tests/resources/testrepo.git/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0)bin21 -> 21 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3 (renamed from tests/resources/testrepo.git/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3)bin103 -> 103 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 (renamed from tests/resources/testrepo.git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1)bin82 -> 82 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92 (renamed from tests/resources/testrepo.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92)bin24 -> 24 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765 (renamed from tests/resources/testrepo.git/objects/fd/093bff70906175335656e6ce6ae05783708765)bin82 -> 82 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx (renamed from tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx)bin46656 -> 46656 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack (renamed from tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack)bin386089 -> 386089 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx (renamed from tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx)bin1240 -> 1240 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack (renamed from tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack)bin491 -> 491 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx (renamed from tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx)bin1240 -> 1240 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack (renamed from tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack)bin498 -> 498 bytes
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/packed-refs12
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/refs/heads/master (renamed from tests/resources/testrepo.git/refs/heads/master)0
-rw-r--r--tests-clar/resources/submodules/testrepo/.gitted/refs/remotes/origin/HEAD1
-rw-r--r--tests-clar/resources/submodules/testrepo/README1
-rw-r--r--tests-clar/resources/submodules/testrepo/branch_file.txt2
-rw-r--r--tests-clar/resources/submodules/testrepo/new.txt1
-rw-r--r--tests-clar/resources/submodules/unmodified1
-rw-r--r--tests-clar/resources/submodules/untracked1
-rw-r--r--tests-clar/resources/testrepo.git/HEAD1
-rw-r--r--tests-clar/resources/testrepo.git/config (renamed from tests/resources/testrepo.git/config)0
-rw-r--r--tests-clar/resources/testrepo.git/head-tracker (renamed from tests/resources/testrepo.git/head-tracker)0
-rw-r--r--tests-clar/resources/testrepo.git/index (renamed from tests/resources/testrepo.git/index)bin10041 -> 10041 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08bin0 -> 19 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7bin0 -> 51 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccdbin0 -> 119 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9bbin0 -> 21 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/27/0b8ea76056d5cad83af921837702d3e3c2924dbin0 -> 21 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54bin0 -> 50 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022ccbin0 -> 23 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057bin0 -> 18 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd20452
-rw-r--r--tests-clar/resources/testrepo.git/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91bin0 -> 152 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf36442
-rw-r--r--tests-clar/resources/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60abin0 -> 119 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af1
-rw-r--r--tests-clar/resources/testrepo.git/objects/7b/4384978d2493e851f9cca7858815fac9b10980bin0 -> 145 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344dbin0 -> 82 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479bin0 -> 126 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe181621
-rw-r--r--tests-clar/resources/testrepo.git/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4bin0 -> 50 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a3
-rw-r--r--tests-clar/resources/testrepo.git/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f2
-rw-r--r--tests-clar/resources/testrepo.git/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe45477503
-rw-r--r--tests-clar/resources/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bdbin0 -> 28 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6bin0 -> 26 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/ae/90f12eea699729ed24555e40b9fd669da12a12bin0 -> 148 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d12
-rw-r--r--tests-clar/resources/testrepo.git/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593bin0 -> 80 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f6443
-rw-r--r--tests-clar/resources/testrepo.git/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd3
-rw-r--r--tests-clar/resources/testrepo.git/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487fbin0 -> 21 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391bin0 -> 15 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0bin0 -> 21 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3bin0 -> 103 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1bin0 -> 82 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92bin0 -> 24 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/fd/093bff70906175335656e6ce6ae05783708765bin0 -> 82 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idxbin0 -> 46656 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.packbin0 -> 386089 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idxbin0 -> 1240 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.packbin0 -> 491 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idxbin0 -> 1240 bytes
-rw-r--r--tests-clar/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.packbin0 -> 498 bytes
-rw-r--r--tests-clar/resources/testrepo.git/packed-refs (renamed from tests/resources/testrepo.git/packed-refs)0
-rw-r--r--tests-clar/resources/testrepo.git/refs/heads/br2 (renamed from tests/resources/testrepo.git/refs/heads/br2)0
-rw-r--r--tests-clar/resources/testrepo.git/refs/heads/master1
-rw-r--r--tests-clar/resources/testrepo.git/refs/heads/packed-test (renamed from tests/resources/testrepo.git/refs/heads/packed-test)0
-rw-r--r--tests-clar/resources/testrepo.git/refs/heads/subtrees (renamed from tests/resources/testrepo.git/refs/heads/subtrees)0
-rw-r--r--tests-clar/resources/testrepo.git/refs/heads/test (renamed from tests/resources/testrepo.git/refs/heads/test)0
-rw-r--r--tests-clar/resources/testrepo.git/refs/tags/annotated_tag_to_blob1
-rw-r--r--tests-clar/resources/testrepo.git/refs/tags/e90810b (renamed from tests/resources/testrepo.git/refs/tags/e90810b)0
-rw-r--r--tests-clar/resources/testrepo.git/refs/tags/point_to_blob (renamed from tests/resources/testrepo.git/refs/tags/point_to_blob)0
-rw-r--r--tests-clar/resources/testrepo.git/refs/tags/test (renamed from tests/resources/testrepo.git/refs/tags/test)0
-rw-r--r--tests-clar/resources/testrepo/.gitted/HEAD1
-rw-r--r--tests-clar/resources/testrepo/.gitted/config8
-rw-r--r--tests-clar/resources/testrepo/.gitted/head-tracker1
-rw-r--r--tests-clar/resources/testrepo/.gitted/indexbin0 -> 10041 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08bin0 -> 19 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7bin0 -> 51 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccdbin0 -> 119 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9bbin0 -> 21 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924dbin0 -> 21 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54bin0 -> 50 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022ccbin0 -> 23 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057bin0 -> 18 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd20452
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf36442
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60abin0 -> 119 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af1
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980bin0 -> 145 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344dbin0 -> 82 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479bin0 -> 126 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe181621
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4bin0 -> 50 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a3
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f2
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe45477503
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bdbin0 -> 28 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6bin0 -> 26 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12bin0 -> 148 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d12
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593bin0 -> 80 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f6443
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd3
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487fbin0 -> 21 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391bin0 -> 15 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0bin0 -> 21 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3bin0 -> 103 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1bin0 -> 82 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92bin0 -> 24 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765bin0 -> 82 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idxbin0 -> 46656 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.packbin0 -> 386089 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idxbin0 -> 1240 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.packbin0 -> 491 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idxbin0 -> 1240 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.packbin0 -> 498 bytes
-rw-r--r--tests-clar/resources/testrepo/.gitted/packed-refs3
-rw-r--r--tests-clar/resources/testrepo/.gitted/refs/heads/br21
-rw-r--r--tests-clar/resources/testrepo/.gitted/refs/heads/master1
-rw-r--r--tests-clar/resources/testrepo/.gitted/refs/heads/packed-test1
-rw-r--r--tests-clar/resources/testrepo/.gitted/refs/heads/subtrees1
-rw-r--r--tests-clar/resources/testrepo/.gitted/refs/heads/test1
-rw-r--r--tests-clar/resources/testrepo/.gitted/refs/tags/e90810b1
-rw-r--r--tests-clar/resources/testrepo/.gitted/refs/tags/point_to_blob1
-rw-r--r--tests-clar/resources/testrepo/.gitted/refs/tags/test1
-rw-r--r--tests-clar/revwalk/basic.c (renamed from tests/t05-revwalk.c)129
-rw-r--r--tests-clar/revwalk/mergebase.c148
-rw-r--r--tests-clar/status/ignore.c100
-rw-r--r--tests-clar/status/status_data.h160
-rw-r--r--tests-clar/status/status_helpers.c49
-rw-r--r--tests-clar/status/status_helpers.h33
-rw-r--r--tests-clar/status/submodules.c112
-rw-r--r--tests-clar/status/worktree.c532
-rw-r--r--tests-clar/threads/basic.c20
-rw-r--r--tests/.gitignore1
-rw-r--r--tests/NAMING52
-rw-r--r--tests/resources/attr/.gitted/indexbin1376 -> 0 bytes
-rw-r--r--tests/resources/attr/.gitted/logs/HEAD3
-rw-r--r--tests/resources/attr/.gitted/logs/refs/heads/master3
-rw-r--r--tests/resources/attr/.gitted/refs/heads/master1
-rw-r--r--tests/resources/attr/root_test21
-rw-r--r--tests/resources/attr/root_test31
-rw-r--r--tests/resources/attr/root_test4.txt1
-rw-r--r--tests/t00-core.c636
-rw-r--r--tests/t01-data.h322
-rw-r--r--tests/t01-rawobj.c627
-rw-r--r--tests/t03-data.h344
-rw-r--r--tests/t03-objwrite.c255
-rw-r--r--tests/t04-commit.c789
-rw-r--r--tests/t06-index.c219
-rw-r--r--tests/t07-hashtable.c192
-rw-r--r--tests/t08-tag.c358
-rw-r--r--tests/t09-tree.c221
-rw-r--r--tests/t10-refs.c1338
-rw-r--r--tests/t12-repo.c183
-rw-r--r--tests/t13-threads.c41
-rw-r--r--tests/t17-bufs.c61
-rw-r--r--tests/t18-status.c455
-rw-r--r--tests/test_helpers.c338
-rw-r--r--tests/test_helpers.h83
-rwxr-xr-xtests/test_lib.c198
-rwxr-xr-xtests/test_lib.h54
-rw-r--r--tests/test_main.c93
-rw-r--r--tests/tests.supp6
779 files changed, 41894 insertions, 15644 deletions
diff --git a/.travis.yml b/.travis.yml
index 4c8c42aaa..b9a08dc59 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -22,7 +22,7 @@ script:
# Run Tests
after_script:
- - ctest .
+ - ctest -V .
# Only watch the development branch
branches:
diff --git a/AUTHORS b/AUTHORS
index 1d6235cf1..03904ff55 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -36,7 +36,6 @@ Marc Pegon
Marcel Groothuis
Marco Villegas
Olivier Ramonat
-Peter Drahos
Peter Drahoš
Pierre Habouzit
Przemyslaw Pawelczyk
@@ -44,6 +43,7 @@ Ramsay Jones
Robert G. Jakabosky
Romain Geissler
Romain Muller
+Russell Belfer
Sakari Jokinen
Sam
Sarath Lakshman
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3a09605a7..bfbabc0a5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -28,6 +28,10 @@ FILE(GLOB SRC_HTTP deps/http-parser/*.c)
IF (NOT WIN32)
FIND_PACKAGE(ZLIB)
+ELSE()
+ # Windows doesn't understand POSIX regex on its own
+ INCLUDE_DIRECTORIES(deps/regex)
+ SET(SRC_REGEX deps/regex/regex.c)
ENDIF()
IF (ZLIB_FOUND)
@@ -47,29 +51,34 @@ SET(INSTALL_INC include CACHE PATH "Where to install headers to.")
# Build options
OPTION (BUILD_SHARED_LIBS "Build Shared Library (OFF for Static)" ON)
OPTION (THREADSAFE "Build libgit2 as threadsafe" OFF)
-OPTION (BUILD_TESTS "Build Tests" ON)
-OPTION (BUILD_CLAR "Build Tests using the Clar suite" OFF)
+OPTION (BUILD_CLAR "Build Tests using the Clar suite" ON)
OPTION (TAGS "Generate tags" OFF)
+OPTION (PROFILE "Generate profiling information" OFF)
# Platform specific compilation flags
IF (MSVC)
# Not using __stdcall with the CRT causes problems
OPTION (STDCALL "Buildl libgit2 with the __stdcall convention" ON)
- SET(CMAKE_C_FLAGS "/W4 /nologo /Zi ${CMAKE_C_FLAGS}")
+ SET(CMAKE_C_FLAGS "/W4 /MP /nologo /Zi ${CMAKE_C_FLAGS}")
IF (STDCALL)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Gz")
ENDIF ()
- # TODO: bring back /RTC1 /RTCc
- SET(CMAKE_C_FLAGS_DEBUG "/Od /DEBUG /MTd")
+ SET(CMAKE_C_FLAGS_DEBUG "/Od /DEBUG /MTd /RTC1 /RTCs /RTCu")
SET(CMAKE_C_FLAGS_RELEASE "/MT /O2")
SET(WIN_RC "src/win32/git2.rc")
+
+ # Precompiled headers
ELSE ()
- SET(CMAKE_C_FLAGS "-O2 -g -D_GNU_SOURCE -Wall -Wextra -Wno-missing-field-initializers -Wstrict-aliasing=2 -Wstrict-prototypes -Wmissing-prototypes ${CMAKE_C_FLAGS}")
+ SET(CMAKE_C_FLAGS "-O2 -g -D_GNU_SOURCE -fvisibility=hidden -Wall -Wextra -Wno-missing-field-initializers -Wstrict-aliasing=2 -Wstrict-prototypes -Wmissing-prototypes ${CMAKE_C_FLAGS}")
SET(CMAKE_C_FLAGS_DEBUG "-O0 -g")
IF (NOT MINGW) # MinGW always does PIC and complains if we tell it to
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
ENDIF ()
+ IF (PROFILE)
+ SET(CMAKE_C_FLAGS "-pg ${CMAKE_C_FLAGS}")
+ SET(CMAKE_EXE_LINKER_FLAGS "-pg ${CMAKE_EXE_LINKER_FLAGS}")
+ ENDIF ()
ENDIF()
# Build Debug by default
@@ -92,14 +101,16 @@ FILE(GLOB SRC_H include/git2/*.h)
# On Windows use specific platform sources
IF (WIN32 AND NOT CYGWIN)
- ADD_DEFINITIONS(-DWIN32 -D_DEBUG)
- FILE(GLOB SRC src/*.c src/transports/*.c src/win32/*.c)
+ ADD_DEFINITIONS(-DWIN32 -D_DEBUG -D_WIN32_WINNT=0x0501)
+ FILE(GLOB SRC src/*.c src/transports/*.c src/xdiff/*.c src/win32/*.c src/compat/*.c)
+ELSEIF (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
+ FILE(GLOB SRC src/*.c src/transports/*.c src/xdiff/*.c src/unix/*.c src/compat/*.c)
ELSE()
- FILE(GLOB SRC src/*.c src/transports/*.c src/unix/*.c)
+ FILE(GLOB SRC src/*.c src/transports/*.c src/xdiff/*.c src/unix/*.c)
ENDIF ()
# Compile and link libgit2
-ADD_LIBRARY(git2 ${SRC} ${SRC_ZLIB} ${SRC_HTTP} ${WIN_RC})
+ADD_LIBRARY(git2 ${SRC} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${WIN_RC})
IF (WIN32)
TARGET_LINK_LIBRARIES(git2 ws2_32)
@@ -123,31 +134,15 @@ INSTALL(DIRECTORY include/git2 DESTINATION ${INSTALL_INC} )
INSTALL(FILES include/git2.h DESTINATION ${INSTALL_INC} )
# Tests
-IF (BUILD_TESTS)
- SET(TEST_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/tests/resources" CACHE PATH "Path to test resources.")
- ADD_DEFINITIONS(-DTEST_RESOURCES=\"${TEST_RESOURCES}\")
-
- INCLUDE_DIRECTORIES(tests)
- FILE(GLOB SRC_TEST tests/t??-*.c)
-
- ADD_EXECUTABLE(libgit2_test tests/test_main.c tests/test_lib.c tests/test_helpers.c ${SRC} ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP})
- TARGET_LINK_LIBRARIES(libgit2_test ${CMAKE_THREAD_LIBS_INIT})
- IF (WIN32)
- TARGET_LINK_LIBRARIES(libgit2_test ws2_32)
- ELSEIF (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
- TARGET_LINK_LIBRARIES(libgit2_test socket nsl)
- ENDIF ()
-
- ENABLE_TESTING()
- ADD_TEST(libgit2_test libgit2_test)
-ENDIF ()
-
IF (BUILD_CLAR)
+
FIND_PACKAGE(PythonInterp REQUIRED)
- SET(CLAR_FIXTURES "${CMAKE_CURRENT_SOURCE_DIR}/tests/resources/")
+ SET(CLAR_FIXTURES "${CMAKE_CURRENT_SOURCE_DIR}/tests-clar/resources/")
SET(CLAR_PATH "${CMAKE_CURRENT_SOURCE_DIR}/tests-clar")
+ SET(CLAR_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/tests-clar/resources" CACHE PATH "Path to test resources.")
ADD_DEFINITIONS(-DCLAR_FIXTURE_PATH=\"${CLAR_FIXTURES}\")
+ ADD_DEFINITIONS(-DCLAR_RESOURCES=\"${TEST_RESOURCES}\")
INCLUDE_DIRECTORIES(${CLAR_PATH})
FILE(GLOB_RECURSE SRC_TEST ${CLAR_PATH}/*/*.c ${CLAR_PATH}/clar_helpers.c ${CLAR_PATH}/testlib.c)
@@ -158,7 +153,7 @@ IF (BUILD_CLAR)
DEPENDS ${CLAR_PATH}/clar ${SRC_TEST}
WORKING_DIRECTORY ${CLAR_PATH}
)
- ADD_EXECUTABLE(libgit2_clar ${SRC} ${CLAR_PATH}/clar_main.c ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP})
+ ADD_EXECUTABLE(libgit2_clar ${SRC} ${CLAR_PATH}/clar_main.c ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX})
TARGET_LINK_LIBRARIES(libgit2_clar ${CMAKE_THREAD_LIBS_INIT})
IF (WIN32)
TARGET_LINK_LIBRARIES(libgit2_clar ws2_32)
diff --git a/COPYING b/COPYING
index 4c02dbcf3..e3f9969d0 100644
--- a/COPYING
+++ b/COPYING
@@ -1,4 +1,4 @@
- libgit2 is Copyright (C) 2009-2011 the libgit2 contributors,
+ libgit2 is Copyright (C) 2009-2012 the libgit2 contributors,
unless otherwise stated. See the AUTHORS file for details.
Note that the only valid version of the GPL as far as this project
@@ -422,4 +422,509 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+----------------------------------------------------------------------
+
+The regex library (deps/regex/) is licensed under the GNU LGPL
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+That's all there is to it!
diff --git a/Makefile.embed b/Makefile.embed
index 83dc281b4..fb6b01bee 100644
--- a/Makefile.embed
+++ b/Makefile.embed
@@ -6,7 +6,7 @@ LIBNAME=libgit2.a
INCLUDES= -I. -Isrc -Iinclude -Ideps/http-parser -Ideps/zlib
-DEFINES= $(INCLUDES) -DNO_VIZ -DSTDC -DNO_GZIP -D_FILE_OFFSET_BITS=64
+DEFINES= $(INCLUDES) -DNO_VIZ -DSTDC -DNO_GZIP -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE
CFLAGS= -g $(DEFINES) -Wall -Wextra -fPIC -O2
SRCS = $(wildcard src/*.c) $(wildcard src/transports/*.c) $(wildcard src/unix/*.c) $(wildcard deps/http-parser/*.c) $(wildcard deps/zlib/*.c)
diff --git a/README.md b/README.md
index 5493b8879..b0c0db194 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,8 @@
libgit2 - the Git linkable library
======================
+[![Build Status](https://secure.travis-ci.org/libgit2/libgit2.png?branch=development)](http://travis-ci.org/libgit2/libgit2)
+
libgit2 is a portable, pure C implementation of the Git core methods provided as a
re-entrant linkable library with a solid API, allowing you to write native
speed custom Git applications in any language with bindings.
@@ -10,6 +12,7 @@ This basically means that you can link it (unmodified) with any kind of software
release its source code.
* Mailing list: <libgit2@librelist.org>
+ * Archives: <http://librelist.com/browser/libgit2/>
* Website: <http://libgit2.github.com>
* API documentation: <http://libgit2.github.com/libgit2>
* Usage guide: <http://libgit2.github.com/api.html>
@@ -67,7 +70,7 @@ The following CMake variables are declared:
- `INSTALL_LIB`: Where to install libraries to.
- `INSTALL_INC`: Where to install headers to.
- `BUILD_SHARED_LIBS`: Build libgit2 as a Shared Library (defaults to ON)
-- `BUILD_TESTS`: Build the libgit2 test suite (defaults to ON)
+- `BUILD_CLAR`: Build [Clar](https://github.com/tanoku/clar)-based test suite (defaults to ON)
- `THREADSAFE`: Build libgit2 with threading support (defaults to OFF)
Language Bindings
@@ -75,29 +78,49 @@ Language Bindings
Here are the bindings to libgit2 that are currently available:
-* Rugged (Ruby bindings) <https://github.com/libgit2/rugged>
-* objective-git (Objective-C bindings) <https://github.com/libgit2/objective-git>
-* pygit2 (Python bindings) <https://github.com/libgit2/pygit2>
-* libgit2sharp (.NET bindings) <https://github.com/libgit2/libgit2sharp>
-* php-git (PHP bindings) <https://github.com/libgit2/php-git>
-* luagit2 (Lua bindings) <https://github.com/libgit2/luagit2>
-* GitForDelphi (Delphi bindings) <https://github.com/libgit2/GitForDelphi>
-* node-gitteh (Node.js bindings) <https://github.com/libgit2/node-gitteh>
-* nodegit (Node.js bindings) <https://github.com/tbranyen/nodegit>
-* go-git (Go bindings) <https://github.com/str1ngs/go-git>
-* libqgit2 (C++ Qt bindings) <https://projects.kde.org/projects/playground/libs/libqgit2/>
-* libgit2-ocaml (ocaml bindings) <https://github.com/burdges/libgit2-ocaml>
-* Geef (Erlang bindings) <https://github.com/schacon/geef>
-* libgit2net (.NET bindings, low level) <https://github.com/txdv/libgit2net>
-* parrot-libgit2 (Parrot Virtual Machine bindings) <https://github.com/letolabs/parrot-libgit2>
-* hgit2 (Haskell bindings) <https://github.com/norm2782/hgit2>
-* git-xs-pm (Perl bindings) <https://github.com/ingydotnet/git-xs-pm>
-* libgit2.vapi (Vala bindings) <https://github.com/apmasell/vapis/blob/master/libgit2.vapi>
+* C++
+ * libqgit2, Qt bindings <https://projects.kde.org/projects/playground/libs/libqgit2/>
+* Chicken Scheme
+ * chicken-git <https://wiki.call-cc.org/egg/git>
+* Delphi
+ * GitForDelphi <https://github.com/libgit2/GitForDelphi>
+* Erlang
+ * Geef <https://github.com/schacon/geef>
+* Go
+ * go-git <https://github.com/str1ngs/go-git>
+* GObject
+ * libgit2-glib <https://github.com/nacho/libgit2-glib>
+* Haskell
+ * hgit2 <https://github.com/norm2782/hgit2>
+* Lua
+ * luagit2 <https://github.com/libgit2/luagit2>
+* .NET
+ * libgit2net, low level bindings <https://github.com/txdv/libgit2net>
+ * libgit2sharp <https://github.com/libgit2/libgit2sharp>
+* Node.js
+ * node-gitteh <https://github.com/libgit2/node-gitteh>
+ * nodegit <https://github.com/tbranyen/nodegit>
+* Objective-C
+ * objective-git <https://github.com/libgit2/objective-git>
+* OCaml
+ * libgit2-ocaml <https://github.com/burdges/libgit2-ocaml>
+* Parrot Virtual Machine
+ * parrot-libgit2 <https://github.com/letolabs/parrot-libgit2>
+* Perl
+ * git-xs-pm <https://github.com/ingydotnet/git-xs-pm>
+* PHP
+ * php-git <https://github.com/libgit2/php-git>
+* Python
+ * pygit2 <https://github.com/libgit2/pygit2>
+* Ruby
+ * Rugged <https://github.com/libgit2/rugged>
+* Vala
+ * libgit2.vapi <https://github.com/apmasell/vapis/blob/master/libgit2.vapi>
If you start another language binding to libgit2, please let us know so
we can add it to the list.
-How Can I Contribute
+How Can I Contribute?
==================================
Fork libgit2/libgit2 on GitHub, add your improvement, push it to a branch
diff --git a/deps/regex/config.h b/deps/regex/config.h
new file mode 100644
index 000000000..95370690e
--- /dev/null
+++ b/deps/regex/config.h
@@ -0,0 +1,7 @@
+#ifndef _REGEX_CONFIG_H_
+#define _REGEX_CONFIG_H_
+
+# define GAWK
+# define NO_MBSUPPORT
+
+#endif
diff --git a/deps/regex/regcomp.c b/deps/regex/regcomp.c
new file mode 100644
index 000000000..7373fbc22
--- /dev/null
+++ b/deps/regex/regcomp.c
@@ -0,0 +1,3856 @@
+/* Extended regular expression matching and search library.
+ Copyright (C) 2002-2007,2009,2010 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA. */
+
+static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern,
+ size_t length, reg_syntax_t syntax);
+static void re_compile_fastmap_iter (regex_t *bufp,
+ const re_dfastate_t *init_state,
+ char *fastmap);
+static reg_errcode_t init_dfa (re_dfa_t *dfa, size_t pat_len);
+#ifdef RE_ENABLE_I18N
+static void free_charset (re_charset_t *cset);
+#endif /* RE_ENABLE_I18N */
+static void free_workarea_compile (regex_t *preg);
+static reg_errcode_t create_initial_state (re_dfa_t *dfa);
+#ifdef RE_ENABLE_I18N
+static void optimize_utf8 (re_dfa_t *dfa);
+#endif
+static reg_errcode_t analyze (regex_t *preg);
+static reg_errcode_t preorder (bin_tree_t *root,
+ reg_errcode_t (fn (void *, bin_tree_t *)),
+ void *extra);
+static reg_errcode_t postorder (bin_tree_t *root,
+ reg_errcode_t (fn (void *, bin_tree_t *)),
+ void *extra);
+static reg_errcode_t optimize_subexps (void *extra, bin_tree_t *node);
+static reg_errcode_t lower_subexps (void *extra, bin_tree_t *node);
+static bin_tree_t *lower_subexp (reg_errcode_t *err, regex_t *preg,
+ bin_tree_t *node);
+static reg_errcode_t calc_first (void *extra, bin_tree_t *node);
+static reg_errcode_t calc_next (void *extra, bin_tree_t *node);
+static reg_errcode_t link_nfa_nodes (void *extra, bin_tree_t *node);
+static int duplicate_node (re_dfa_t *dfa, int org_idx, unsigned int constraint);
+static int search_duplicated_node (const re_dfa_t *dfa, int org_node,
+ unsigned int constraint);
+static reg_errcode_t calc_eclosure (re_dfa_t *dfa);
+static reg_errcode_t calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa,
+ int node, int root);
+static reg_errcode_t calc_inveclosure (re_dfa_t *dfa);
+static int fetch_number (re_string_t *input, re_token_t *token,
+ reg_syntax_t syntax);
+static int peek_token (re_token_t *token, re_string_t *input,
+ reg_syntax_t syntax) internal_function;
+static bin_tree_t *parse (re_string_t *regexp, regex_t *preg,
+ reg_syntax_t syntax, reg_errcode_t *err);
+static bin_tree_t *parse_reg_exp (re_string_t *regexp, regex_t *preg,
+ re_token_t *token, reg_syntax_t syntax,
+ int nest, reg_errcode_t *err);
+static bin_tree_t *parse_branch (re_string_t *regexp, regex_t *preg,
+ re_token_t *token, reg_syntax_t syntax,
+ int nest, reg_errcode_t *err);
+static bin_tree_t *parse_expression (re_string_t *regexp, regex_t *preg,
+ re_token_t *token, reg_syntax_t syntax,
+ int nest, reg_errcode_t *err);
+static bin_tree_t *parse_sub_exp (re_string_t *regexp, regex_t *preg,
+ re_token_t *token, reg_syntax_t syntax,
+ int nest, reg_errcode_t *err);
+static bin_tree_t *parse_dup_op (bin_tree_t *dup_elem, re_string_t *regexp,
+ re_dfa_t *dfa, re_token_t *token,
+ reg_syntax_t syntax, reg_errcode_t *err);
+static bin_tree_t *parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa,
+ re_token_t *token, reg_syntax_t syntax,
+ reg_errcode_t *err);
+static reg_errcode_t parse_bracket_element (bracket_elem_t *elem,
+ re_string_t *regexp,
+ re_token_t *token, int token_len,
+ re_dfa_t *dfa,
+ reg_syntax_t syntax,
+ int accept_hyphen);
+static reg_errcode_t parse_bracket_symbol (bracket_elem_t *elem,
+ re_string_t *regexp,
+ re_token_t *token);
+#ifdef RE_ENABLE_I18N
+static reg_errcode_t build_equiv_class (bitset_t sbcset,
+ re_charset_t *mbcset,
+ int *equiv_class_alloc,
+ const unsigned char *name);
+static reg_errcode_t build_charclass (RE_TRANSLATE_TYPE trans,
+ bitset_t sbcset,
+ re_charset_t *mbcset,
+ int *char_class_alloc,
+ const char *class_name,
+ reg_syntax_t syntax);
+#else /* not RE_ENABLE_I18N */
+static reg_errcode_t build_equiv_class (bitset_t sbcset,
+ const unsigned char *name);
+static reg_errcode_t build_charclass (RE_TRANSLATE_TYPE trans,
+ bitset_t sbcset,
+ const char *class_name,
+ reg_syntax_t syntax);
+#endif /* not RE_ENABLE_I18N */
+static bin_tree_t *build_charclass_op (re_dfa_t *dfa,
+ RE_TRANSLATE_TYPE trans,
+ const char *class_name,
+ const char *extra,
+ int non_match, reg_errcode_t *err);
+static bin_tree_t *create_tree (re_dfa_t *dfa,
+ bin_tree_t *left, bin_tree_t *right,
+ re_token_type_t type);
+static bin_tree_t *create_token_tree (re_dfa_t *dfa,
+ bin_tree_t *left, bin_tree_t *right,
+ const re_token_t *token);
+static bin_tree_t *duplicate_tree (const bin_tree_t *src, re_dfa_t *dfa);
+static void free_token (re_token_t *node);
+static reg_errcode_t free_tree (void *extra, bin_tree_t *node);
+static reg_errcode_t mark_opt_subexp (void *extra, bin_tree_t *node);
+
+/* This table gives an error message for each of the error codes listed
+ in regex.h. Obviously the order here has to be same as there.
+ POSIX doesn't require that we do anything for REG_NOERROR,
+ but why not be nice? */
+
+const char __re_error_msgid[] attribute_hidden =
+ {
+#define REG_NOERROR_IDX 0
+ gettext_noop ("Success") /* REG_NOERROR */
+ "\0"
+#define REG_NOMATCH_IDX (REG_NOERROR_IDX + sizeof "Success")
+ gettext_noop ("No match") /* REG_NOMATCH */
+ "\0"
+#define REG_BADPAT_IDX (REG_NOMATCH_IDX + sizeof "No match")
+ gettext_noop ("Invalid regular expression") /* REG_BADPAT */
+ "\0"
+#define REG_ECOLLATE_IDX (REG_BADPAT_IDX + sizeof "Invalid regular expression")
+ gettext_noop ("Invalid collation character") /* REG_ECOLLATE */
+ "\0"
+#define REG_ECTYPE_IDX (REG_ECOLLATE_IDX + sizeof "Invalid collation character")
+ gettext_noop ("Invalid character class name") /* REG_ECTYPE */
+ "\0"
+#define REG_EESCAPE_IDX (REG_ECTYPE_IDX + sizeof "Invalid character class name")
+ gettext_noop ("Trailing backslash") /* REG_EESCAPE */
+ "\0"
+#define REG_ESUBREG_IDX (REG_EESCAPE_IDX + sizeof "Trailing backslash")
+ gettext_noop ("Invalid back reference") /* REG_ESUBREG */
+ "\0"
+#define REG_EBRACK_IDX (REG_ESUBREG_IDX + sizeof "Invalid back reference")
+ gettext_noop ("Unmatched [ or [^") /* REG_EBRACK */
+ "\0"
+#define REG_EPAREN_IDX (REG_EBRACK_IDX + sizeof "Unmatched [ or [^")
+ gettext_noop ("Unmatched ( or \\(") /* REG_EPAREN */
+ "\0"
+#define REG_EBRACE_IDX (REG_EPAREN_IDX + sizeof "Unmatched ( or \\(")
+ gettext_noop ("Unmatched \\{") /* REG_EBRACE */
+ "\0"
+#define REG_BADBR_IDX (REG_EBRACE_IDX + sizeof "Unmatched \\{")
+ gettext_noop ("Invalid content of \\{\\}") /* REG_BADBR */
+ "\0"
+#define REG_ERANGE_IDX (REG_BADBR_IDX + sizeof "Invalid content of \\{\\}")
+ gettext_noop ("Invalid range end") /* REG_ERANGE */
+ "\0"
+#define REG_ESPACE_IDX (REG_ERANGE_IDX + sizeof "Invalid range end")
+ gettext_noop ("Memory exhausted") /* REG_ESPACE */
+ "\0"
+#define REG_BADRPT_IDX (REG_ESPACE_IDX + sizeof "Memory exhausted")
+ gettext_noop ("Invalid preceding regular expression") /* REG_BADRPT */
+ "\0"
+#define REG_EEND_IDX (REG_BADRPT_IDX + sizeof "Invalid preceding regular expression")
+ gettext_noop ("Premature end of regular expression") /* REG_EEND */
+ "\0"
+#define REG_ESIZE_IDX (REG_EEND_IDX + sizeof "Premature end of regular expression")
+ gettext_noop ("Regular expression too big") /* REG_ESIZE */
+ "\0"
+#define REG_ERPAREN_IDX (REG_ESIZE_IDX + sizeof "Regular expression too big")
+ gettext_noop ("Unmatched ) or \\)") /* REG_ERPAREN */
+ };
+
+const size_t __re_error_msgid_idx[] attribute_hidden =
+ {
+ REG_NOERROR_IDX,
+ REG_NOMATCH_IDX,
+ REG_BADPAT_IDX,
+ REG_ECOLLATE_IDX,
+ REG_ECTYPE_IDX,
+ REG_EESCAPE_IDX,
+ REG_ESUBREG_IDX,
+ REG_EBRACK_IDX,
+ REG_EPAREN_IDX,
+ REG_EBRACE_IDX,
+ REG_BADBR_IDX,
+ REG_ERANGE_IDX,
+ REG_ESPACE_IDX,
+ REG_BADRPT_IDX,
+ REG_EEND_IDX,
+ REG_ESIZE_IDX,
+ REG_ERPAREN_IDX
+ };
+
+/* Entry points for GNU code. */
+
+
+#ifdef ZOS_USS
+
+/* For ZOS USS we must define btowc */
+
+wchar_t
+btowc (int c)
+{
+ wchar_t wtmp[2];
+ char tmp[2];
+
+ tmp[0] = c;
+ tmp[1] = 0;
+
+ mbtowc (wtmp, tmp, 1);
+ return wtmp[0];
+}
+#endif
+
+/* re_compile_pattern is the GNU regular expression compiler: it
+ compiles PATTERN (of length LENGTH) and puts the result in BUFP.
+ Returns 0 if the pattern was valid, otherwise an error string.
+
+ Assumes the `allocated' (and perhaps `buffer') and `translate' fields
+ are set in BUFP on entry. */
+
+const char *
+re_compile_pattern (const char *pattern,
+ size_t length,
+ struct re_pattern_buffer *bufp)
+{
+ reg_errcode_t ret;
+
+ /* And GNU code determines whether or not to get register information
+ by passing null for the REGS argument to re_match, etc., not by
+ setting no_sub, unless RE_NO_SUB is set. */
+ bufp->no_sub = !!(re_syntax_options & RE_NO_SUB);
+
+ /* Match anchors at newline. */
+ bufp->newline_anchor = 1;
+
+ ret = re_compile_internal (bufp, pattern, length, re_syntax_options);
+
+ if (!ret)
+ return NULL;
+ return gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]);
+}
+#ifdef _LIBC
+weak_alias (__re_compile_pattern, re_compile_pattern)
+#endif
+
+/* Set by `re_set_syntax' to the current regexp syntax to recognize. Can
+ also be assigned to arbitrarily: each pattern buffer stores its own
+ syntax, so it can be changed between regex compilations. */
+/* This has no initializer because initialized variables in Emacs
+ become read-only after dumping. */
+reg_syntax_t re_syntax_options;
+
+
+/* Specify the precise syntax of regexps for compilation. This provides
+ for compatibility for various utilities which historically have
+ different, incompatible syntaxes.
+
+ The argument SYNTAX is a bit mask comprised of the various bits
+ defined in regex.h. We return the old syntax. */
+
+reg_syntax_t
+re_set_syntax (reg_syntax_t syntax)
+{
+ reg_syntax_t ret = re_syntax_options;
+
+ re_syntax_options = syntax;
+ return ret;
+}
+#ifdef _LIBC
+weak_alias (__re_set_syntax, re_set_syntax)
+#endif
+
+int
+re_compile_fastmap (struct re_pattern_buffer *bufp)
+{
+ re_dfa_t *dfa = (re_dfa_t *) bufp->buffer;
+ char *fastmap = bufp->fastmap;
+
+ memset (fastmap, '\0', sizeof (char) * SBC_MAX);
+ re_compile_fastmap_iter (bufp, dfa->init_state, fastmap);
+ if (dfa->init_state != dfa->init_state_word)
+ re_compile_fastmap_iter (bufp, dfa->init_state_word, fastmap);
+ if (dfa->init_state != dfa->init_state_nl)
+ re_compile_fastmap_iter (bufp, dfa->init_state_nl, fastmap);
+ if (dfa->init_state != dfa->init_state_begbuf)
+ re_compile_fastmap_iter (bufp, dfa->init_state_begbuf, fastmap);
+ bufp->fastmap_accurate = 1;
+ return 0;
+}
+#ifdef _LIBC
+weak_alias (__re_compile_fastmap, re_compile_fastmap)
+#endif
+
+static inline void
+__attribute ((always_inline))
+re_set_fastmap (char *fastmap, int icase, int ch)
+{
+ fastmap[ch] = 1;
+ if (icase)
+ fastmap[tolower (ch)] = 1;
+}
+
+/* Helper function for re_compile_fastmap.
+ Compile fastmap for the initial_state INIT_STATE. */
+
+static void
+re_compile_fastmap_iter (regex_t *bufp, const re_dfastate_t *init_state,
+ char *fastmap)
+{
+ volatile re_dfa_t *dfa = (re_dfa_t *) bufp->buffer;
+ int node_cnt;
+ int icase = (dfa->mb_cur_max == 1 && (bufp->syntax & RE_ICASE));
+ for (node_cnt = 0; node_cnt < init_state->nodes.nelem; ++node_cnt)
+ {
+ int node = init_state->nodes.elems[node_cnt];
+ re_token_type_t type = dfa->nodes[node].type;
+
+ if (type == CHARACTER)
+ {
+ re_set_fastmap (fastmap, icase, dfa->nodes[node].opr.c);
+#ifdef RE_ENABLE_I18N
+ if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1)
+ {
+ unsigned char *buf = re_malloc (unsigned char, dfa->mb_cur_max), *p;
+ wchar_t wc;
+ mbstate_t state;
+
+ p = buf;
+ *p++ = dfa->nodes[node].opr.c;
+ while (++node < dfa->nodes_len
+ && dfa->nodes[node].type == CHARACTER
+ && dfa->nodes[node].mb_partial)
+ *p++ = dfa->nodes[node].opr.c;
+ memset (&state, '\0', sizeof (state));
+ if (__mbrtowc (&wc, (const char *) buf, p - buf,
+ &state) == p - buf
+ && (__wcrtomb ((char *) buf, towlower (wc), &state)
+ != (size_t) -1))
+ re_set_fastmap (fastmap, 0, buf[0]);
+ re_free (buf);
+ }
+#endif
+ }
+ else if (type == SIMPLE_BRACKET)
+ {
+ int i, ch;
+ for (i = 0, ch = 0; i < BITSET_WORDS; ++i)
+ {
+ int j;
+ bitset_word_t w = dfa->nodes[node].opr.sbcset[i];
+ for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch)
+ if (w & ((bitset_word_t) 1 << j))
+ re_set_fastmap (fastmap, icase, ch);
+ }
+ }
+#ifdef RE_ENABLE_I18N
+ else if (type == COMPLEX_BRACKET)
+ {
+ re_charset_t *cset = dfa->nodes[node].opr.mbcset;
+ int i;
+
+# ifdef _LIBC
+ /* See if we have to try all bytes which start multiple collation
+ elements.
+ e.g. In da_DK, we want to catch 'a' since "aa" is a valid
+ collation element, and don't catch 'b' since 'b' is
+ the only collation element which starts from 'b' (and
+ it is caught by SIMPLE_BRACKET). */
+ if (_NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES) != 0
+ && (cset->ncoll_syms || cset->nranges))
+ {
+ const int32_t *table = (const int32_t *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+ for (i = 0; i < SBC_MAX; ++i)
+ if (table[i] < 0)
+ re_set_fastmap (fastmap, icase, i);
+ }
+# endif /* _LIBC */
+
+ /* See if we have to start the match at all multibyte characters,
+ i.e. where we would not find an invalid sequence. This only
+ applies to multibyte character sets; for single byte character
+ sets, the SIMPLE_BRACKET again suffices. */
+ if (dfa->mb_cur_max > 1
+ && (cset->nchar_classes || cset->non_match || cset->nranges
+# ifdef _LIBC
+ || cset->nequiv_classes
+# endif /* _LIBC */
+ ))
+ {
+ unsigned char c = 0;
+ do
+ {
+ mbstate_t mbs;
+ memset (&mbs, 0, sizeof (mbs));
+ if (__mbrtowc (NULL, (char *) &c, 1, &mbs) == (size_t) -2)
+ re_set_fastmap (fastmap, false, (int) c);
+ }
+ while (++c != 0);
+ }
+
+ else
+ {
+ /* ... Else catch all bytes which can start the mbchars. */
+ for (i = 0; i < cset->nmbchars; ++i)
+ {
+ char buf[256];
+ mbstate_t state;
+ memset (&state, '\0', sizeof (state));
+ if (__wcrtomb (buf, cset->mbchars[i], &state) != (size_t) -1)
+ re_set_fastmap (fastmap, icase, *(unsigned char *) buf);
+ if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1)
+ {
+ if (__wcrtomb (buf, towlower (cset->mbchars[i]), &state)
+ != (size_t) -1)
+ re_set_fastmap (fastmap, false, *(unsigned char *) buf);
+ }
+ }
+ }
+ }
+#endif /* RE_ENABLE_I18N */
+ else if (type == OP_PERIOD
+#ifdef RE_ENABLE_I18N
+ || type == OP_UTF8_PERIOD
+#endif /* RE_ENABLE_I18N */
+ || type == END_OF_RE)
+ {
+ memset (fastmap, '\1', sizeof (char) * SBC_MAX);
+ if (type == END_OF_RE)
+ bufp->can_be_null = 1;
+ return;
+ }
+ }
+}
+
+/* Entry point for POSIX code. */
+/* regcomp takes a regular expression as a string and compiles it.
+
+ PREG is a regex_t *. We do not expect any fields to be initialized,
+ since POSIX says we shouldn't. Thus, we set
+
+ `buffer' to the compiled pattern;
+ `used' to the length of the compiled pattern;
+ `syntax' to RE_SYNTAX_POSIX_EXTENDED if the
+ REG_EXTENDED bit in CFLAGS is set; otherwise, to
+ RE_SYNTAX_POSIX_BASIC;
+ `newline_anchor' to REG_NEWLINE being set in CFLAGS;
+ `fastmap' to an allocated space for the fastmap;
+ `fastmap_accurate' to zero;
+ `re_nsub' to the number of subexpressions in PATTERN.
+
+ PATTERN is the address of the pattern string.
+
+ CFLAGS is a series of bits which affect compilation.
+
+ If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we
+ use POSIX basic syntax.
+
+ If REG_NEWLINE is set, then . and [^...] don't match newline.
+ Also, regexec will try a match beginning after every newline.
+
+ If REG_ICASE is set, then we considers upper- and lowercase
+ versions of letters to be equivalent when matching.
+
+ If REG_NOSUB is set, then when PREG is passed to regexec, that
+ routine will report only success or failure, and nothing about the
+ registers.
+
+ It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for
+ the return codes and their meanings.) */
+
+int
+regcomp (regex_t *__restrict preg,
+ const char *__restrict pattern,
+ int cflags)
+{
+ reg_errcode_t ret;
+ reg_syntax_t syntax = ((cflags & REG_EXTENDED) ? RE_SYNTAX_POSIX_EXTENDED
+ : RE_SYNTAX_POSIX_BASIC);
+
+ preg->buffer = NULL;
+ preg->allocated = 0;
+ preg->used = 0;
+
+ /* Try to allocate space for the fastmap. */
+ preg->fastmap = re_malloc (char, SBC_MAX);
+ if (BE (preg->fastmap == NULL, 0))
+ return REG_ESPACE;
+
+ syntax |= (cflags & REG_ICASE) ? RE_ICASE : 0;
+
+ /* If REG_NEWLINE is set, newlines are treated differently. */
+ if (cflags & REG_NEWLINE)
+ { /* REG_NEWLINE implies neither . nor [^...] match newline. */
+ syntax &= ~RE_DOT_NEWLINE;
+ syntax |= RE_HAT_LISTS_NOT_NEWLINE;
+ /* It also changes the matching behavior. */
+ preg->newline_anchor = 1;
+ }
+ else
+ preg->newline_anchor = 0;
+ preg->no_sub = !!(cflags & REG_NOSUB);
+ preg->translate = NULL;
+
+ ret = re_compile_internal (preg, pattern, strlen (pattern), syntax);
+
+ /* POSIX doesn't distinguish between an unmatched open-group and an
+ unmatched close-group: both are REG_EPAREN. */
+ if (ret == REG_ERPAREN)
+ ret = REG_EPAREN;
+
+ /* We have already checked preg->fastmap != NULL. */
+ if (BE (ret == REG_NOERROR, 1))
+ /* Compute the fastmap now, since regexec cannot modify the pattern
+ buffer. This function never fails in this implementation. */
+ (void) re_compile_fastmap (preg);
+ else
+ {
+ /* Some error occurred while compiling the expression. */
+ re_free (preg->fastmap);
+ preg->fastmap = NULL;
+ }
+
+ return (int) ret;
+}
+#ifdef _LIBC
+weak_alias (__regcomp, regcomp)
+#endif
+
+/* Returns a message corresponding to an error code, ERRCODE, returned
+ from either regcomp or regexec. We don't use PREG here. */
+
+size_t
+regerror(int errcode, const regex_t *__restrict preg,
+ char *__restrict errbuf, size_t errbuf_size)
+{
+ const char *msg;
+ size_t msg_size;
+
+ if (BE (errcode < 0
+ || errcode >= (int) (sizeof (__re_error_msgid_idx)
+ / sizeof (__re_error_msgid_idx[0])), 0))
+ /* Only error codes returned by the rest of the code should be passed
+ to this routine. If we are given anything else, or if other regex
+ code generates an invalid error code, then the program has a bug.
+ Dump core so we can fix it. */
+ abort ();
+
+ msg = gettext (__re_error_msgid + __re_error_msgid_idx[errcode]);
+
+ msg_size = strlen (msg) + 1; /* Includes the null. */
+
+ if (BE (errbuf_size != 0, 1))
+ {
+ if (BE (msg_size > errbuf_size, 0))
+ {
+ memcpy (errbuf, msg, errbuf_size - 1);
+ errbuf[errbuf_size - 1] = 0;
+ }
+ else
+ memcpy (errbuf, msg, msg_size);
+ }
+
+ return msg_size;
+}
+#ifdef _LIBC
+weak_alias (__regerror, regerror)
+#endif
+
+
+#ifdef RE_ENABLE_I18N
+/* This static array is used for the map to single-byte characters when
+ UTF-8 is used. Otherwise we would allocate memory just to initialize
+ it the same all the time. UTF-8 is the preferred encoding so this is
+ a worthwhile optimization. */
+#if __GNUC__ >= 3
+static const bitset_t utf8_sb_map = {
+ /* Set the first 128 bits. */
+ [0 ... 0x80 / BITSET_WORD_BITS - 1] = BITSET_WORD_MAX
+};
+#else /* ! (__GNUC__ >= 3) */
+static bitset_t utf8_sb_map;
+#endif /* __GNUC__ >= 3 */
+#endif /* RE_ENABLE_I18N */
+
+
+static void
+free_dfa_content (re_dfa_t *dfa)
+{
+ unsigned int i;
+ int j;
+
+ if (dfa->nodes)
+ for (i = 0; i < dfa->nodes_len; ++i)
+ free_token (dfa->nodes + i);
+ re_free (dfa->nexts);
+ for (i = 0; i < dfa->nodes_len; ++i)
+ {
+ if (dfa->eclosures != NULL)
+ re_node_set_free (dfa->eclosures + i);
+ if (dfa->inveclosures != NULL)
+ re_node_set_free (dfa->inveclosures + i);
+ if (dfa->edests != NULL)
+ re_node_set_free (dfa->edests + i);
+ }
+ re_free (dfa->edests);
+ re_free (dfa->eclosures);
+ re_free (dfa->inveclosures);
+ re_free (dfa->nodes);
+
+ if (dfa->state_table)
+ for (i = 0; i <= dfa->state_hash_mask; ++i)
+ {
+ struct re_state_table_entry *entry = dfa->state_table + i;
+ for (j = 0; j < entry->num; ++j)
+ {
+ re_dfastate_t *state = entry->array[j];
+ free_state (state);
+ }
+ re_free (entry->array);
+ }
+ re_free (dfa->state_table);
+#ifdef RE_ENABLE_I18N
+ if (dfa->sb_char != utf8_sb_map)
+ re_free (dfa->sb_char);
+#endif
+ re_free (dfa->subexp_map);
+#ifdef DEBUG
+ re_free (dfa->re_str);
+#endif
+
+ re_free (dfa);
+}
+
+
+/* Free dynamically allocated space used by PREG. */
+
+void
+regfree (regex_t *preg)
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ if (BE (dfa != NULL, 1))
+ free_dfa_content (dfa);
+ preg->buffer = NULL;
+ preg->allocated = 0;
+
+ re_free (preg->fastmap);
+ preg->fastmap = NULL;
+
+ re_free (preg->translate);
+ preg->translate = NULL;
+}
+#ifdef _LIBC
+weak_alias (__regfree, regfree)
+#endif
+
+/* Entry points compatible with 4.2 BSD regex library. We don't define
+ them unless specifically requested. */
+
+#if defined _REGEX_RE_COMP || defined _LIBC
+
+/* BSD has one and only one pattern buffer. */
+static struct re_pattern_buffer re_comp_buf;
+
+char *
+# ifdef _LIBC
+/* Make these definitions weak in libc, so POSIX programs can redefine
+ these names if they don't use our functions, and still use
+ regcomp/regexec above without link errors. */
+weak_function
+# endif
+re_comp (s)
+ const char *s;
+{
+ reg_errcode_t ret;
+ char *fastmap;
+
+ if (!s)
+ {
+ if (!re_comp_buf.buffer)
+ return gettext ("No previous regular expression");
+ return 0;
+ }
+
+ if (re_comp_buf.buffer)
+ {
+ fastmap = re_comp_buf.fastmap;
+ re_comp_buf.fastmap = NULL;
+ __regfree (&re_comp_buf);
+ memset (&re_comp_buf, '\0', sizeof (re_comp_buf));
+ re_comp_buf.fastmap = fastmap;
+ }
+
+ if (re_comp_buf.fastmap == NULL)
+ {
+ re_comp_buf.fastmap = (char *) malloc (SBC_MAX);
+ if (re_comp_buf.fastmap == NULL)
+ return (char *) gettext (__re_error_msgid
+ + __re_error_msgid_idx[(int) REG_ESPACE]);
+ }
+
+ /* Since `re_exec' always passes NULL for the `regs' argument, we
+ don't need to initialize the pattern buffer fields which affect it. */
+
+ /* Match anchors at newlines. */
+ re_comp_buf.newline_anchor = 1;
+
+ ret = re_compile_internal (&re_comp_buf, s, strlen (s), re_syntax_options);
+
+ if (!ret)
+ return NULL;
+
+ /* Yes, we're discarding `const' here if !HAVE_LIBINTL. */
+ return (char *) gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]);
+}
+
+#ifdef _LIBC
+libc_freeres_fn (free_mem)
+{
+ __regfree (&re_comp_buf);
+}
+#endif
+
+#endif /* _REGEX_RE_COMP */
+
+/* Internal entry point.
+ Compile the regular expression PATTERN, whose length is LENGTH.
+ SYNTAX indicate regular expression's syntax. */
+
+static reg_errcode_t
+re_compile_internal (regex_t *preg, const char * pattern, size_t length,
+ reg_syntax_t syntax)
+{
+ reg_errcode_t err = REG_NOERROR;
+ re_dfa_t *dfa;
+ re_string_t regexp;
+
+ /* Initialize the pattern buffer. */
+ preg->fastmap_accurate = 0;
+ preg->syntax = syntax;
+ preg->not_bol = preg->not_eol = 0;
+ preg->used = 0;
+ preg->re_nsub = 0;
+ preg->can_be_null = 0;
+ preg->regs_allocated = REGS_UNALLOCATED;
+
+ /* Initialize the dfa. */
+ dfa = (re_dfa_t *) preg->buffer;
+ if (BE (preg->allocated < sizeof (re_dfa_t), 0))
+ {
+ /* If zero allocated, but buffer is non-null, try to realloc
+ enough space. This loses if buffer's address is bogus, but
+ that is the user's responsibility. If ->buffer is NULL this
+ is a simple allocation. */
+ dfa = re_realloc (preg->buffer, re_dfa_t, 1);
+ if (dfa == NULL)
+ return REG_ESPACE;
+ preg->allocated = sizeof (re_dfa_t);
+ preg->buffer = (unsigned char *) dfa;
+ }
+ preg->used = sizeof (re_dfa_t);
+
+ err = init_dfa (dfa, length);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ free_dfa_content (dfa);
+ preg->buffer = NULL;
+ preg->allocated = 0;
+ return err;
+ }
+#ifdef DEBUG
+ /* Note: length+1 will not overflow since it is checked in init_dfa. */
+ dfa->re_str = re_malloc (char, length + 1);
+ strncpy (dfa->re_str, pattern, length + 1);
+#endif
+
+ __libc_lock_init (dfa->lock);
+
+ err = re_string_construct (&regexp, pattern, length, preg->translate,
+ syntax & RE_ICASE, dfa);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_compile_internal_free_return:
+ free_workarea_compile (preg);
+ re_string_destruct (&regexp);
+ free_dfa_content (dfa);
+ preg->buffer = NULL;
+ preg->allocated = 0;
+ return err;
+ }
+
+ /* Parse the regular expression, and build a structure tree. */
+ preg->re_nsub = 0;
+ dfa->str_tree = parse (&regexp, preg, syntax, &err);
+ if (BE (dfa->str_tree == NULL, 0))
+ goto re_compile_internal_free_return;
+
+ /* Analyze the tree and create the nfa. */
+ err = analyze (preg);
+ if (BE (err != REG_NOERROR, 0))
+ goto re_compile_internal_free_return;
+
+#ifdef RE_ENABLE_I18N
+ /* If possible, do searching in single byte encoding to speed things up. */
+ if (dfa->is_utf8 && !(syntax & RE_ICASE) && preg->translate == NULL)
+ optimize_utf8 (dfa);
+#endif
+
+ /* Then create the initial state of the dfa. */
+ err = create_initial_state (dfa);
+
+ /* Release work areas. */
+ free_workarea_compile (preg);
+ re_string_destruct (&regexp);
+
+ if (BE (err != REG_NOERROR, 0))
+ {
+ free_dfa_content (dfa);
+ preg->buffer = NULL;
+ preg->allocated = 0;
+ }
+
+ return err;
+}
+
+/* Initialize DFA. We use the length of the regular expression PAT_LEN
+ as the initial length of some arrays. */
+
+static reg_errcode_t
+init_dfa (re_dfa_t *dfa, size_t pat_len)
+{
+ unsigned int table_size;
+
+ memset (dfa, '\0', sizeof (re_dfa_t));
+
+ /* Force allocation of str_tree_storage the first time. */
+ dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE;
+
+ /* Avoid overflows. */
+ if (pat_len == SIZE_MAX)
+ return REG_ESPACE;
+
+ dfa->nodes_alloc = pat_len + 1;
+ dfa->nodes = re_malloc (re_token_t, dfa->nodes_alloc);
+
+ /* table_size = 2 ^ ceil(log pat_len) */
+ for (table_size = 1; ; table_size <<= 1)
+ if (table_size > pat_len)
+ break;
+
+ dfa->state_table = calloc (sizeof (struct re_state_table_entry), table_size);
+ dfa->state_hash_mask = table_size - 1;
+
+ dfa->mb_cur_max = MB_CUR_MAX;
+#ifdef _LIBC
+ if (dfa->mb_cur_max == 6
+ && strcmp (_NL_CURRENT (LC_CTYPE, _NL_CTYPE_CODESET_NAME), "UTF-8") == 0)
+ dfa->is_utf8 = 1;
+ dfa->map_notascii = (_NL_CURRENT_WORD (LC_CTYPE, _NL_CTYPE_MAP_TO_NONASCII)
+ != 0);
+#else
+ dfa->is_utf8 = 1;
+ /* We check exhaustively in the loop below if this charset is a
+ superset of ASCII. */
+ dfa->map_notascii = 0;
+#endif
+
+#ifdef RE_ENABLE_I18N
+ if (dfa->mb_cur_max > 1)
+ {
+ if (dfa->is_utf8)
+ {
+#if !defined(__GNUC__) || __GNUC__ < 3
+ static short utf8_sb_map_inited = 0;
+
+ if (! utf8_sb_map_inited)
+ {
+ int i;
+
+ utf8_sb_map_inited = 0;
+ for (i = 0; i <= 0x80 / BITSET_WORD_BITS - 1; i++)
+ utf8_sb_map[i] = BITSET_WORD_MAX;
+ }
+#endif
+ dfa->sb_char = (re_bitset_ptr_t) utf8_sb_map;
+ }
+ else
+ {
+ int i, j, ch;
+
+ dfa->sb_char = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
+ if (BE (dfa->sb_char == NULL, 0))
+ return REG_ESPACE;
+
+ /* Set the bits corresponding to single byte chars. */
+ for (i = 0, ch = 0; i < BITSET_WORDS; ++i)
+ for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch)
+ {
+ wint_t wch = __btowc (ch);
+ if (wch != WEOF)
+ dfa->sb_char[i] |= (bitset_word_t) 1 << j;
+# ifndef _LIBC
+ if (isascii (ch) && wch != ch)
+ dfa->map_notascii = 1;
+# endif
+ }
+ }
+ }
+#endif
+
+ if (BE (dfa->nodes == NULL || dfa->state_table == NULL, 0))
+ return REG_ESPACE;
+ return REG_NOERROR;
+}
+
+/* Initialize WORD_CHAR table, which indicate which character is
+ "word". In this case "word" means that it is the word construction
+ character used by some operators like "\<", "\>", etc. */
+
+static void
+internal_function
+init_word_char (re_dfa_t *dfa)
+{
+ int i, j, ch;
+ dfa->word_ops_used = 1;
+ for (i = 0, ch = 0; i < BITSET_WORDS; ++i)
+ for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch)
+ if (isalnum (ch) || ch == '_')
+ dfa->word_char[i] |= (bitset_word_t) 1 << j;
+}
+
+/* Free the work area which are only used while compiling. */
+
+static void
+free_workarea_compile (regex_t *preg)
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ bin_tree_storage_t *storage, *next;
+ for (storage = dfa->str_tree_storage; storage; storage = next)
+ {
+ next = storage->next;
+ re_free (storage);
+ }
+ dfa->str_tree_storage = NULL;
+ dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE;
+ dfa->str_tree = NULL;
+ re_free (dfa->org_indices);
+ dfa->org_indices = NULL;
+}
+
+/* Create initial states for all contexts. */
+
+static reg_errcode_t
+create_initial_state (re_dfa_t *dfa)
+{
+ int first, i;
+ reg_errcode_t err;
+ re_node_set init_nodes;
+
+ /* Initial states have the epsilon closure of the node which is
+ the first node of the regular expression. */
+ first = dfa->str_tree->first->node_idx;
+ dfa->init_node = first;
+ err = re_node_set_init_copy (&init_nodes, dfa->eclosures + first);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+
+ /* The back-references which are in initial states can epsilon transit,
+ since in this case all of the subexpressions can be null.
+ Then we add epsilon closures of the nodes which are the next nodes of
+ the back-references. */
+ if (dfa->nbackref > 0)
+ for (i = 0; i < init_nodes.nelem; ++i)
+ {
+ int node_idx = init_nodes.elems[i];
+ re_token_type_t type = dfa->nodes[node_idx].type;
+
+ int clexp_idx;
+ if (type != OP_BACK_REF)
+ continue;
+ for (clexp_idx = 0; clexp_idx < init_nodes.nelem; ++clexp_idx)
+ {
+ re_token_t *clexp_node;
+ clexp_node = dfa->nodes + init_nodes.elems[clexp_idx];
+ if (clexp_node->type == OP_CLOSE_SUBEXP
+ && clexp_node->opr.idx == dfa->nodes[node_idx].opr.idx)
+ break;
+ }
+ if (clexp_idx == init_nodes.nelem)
+ continue;
+
+ if (type == OP_BACK_REF)
+ {
+ int dest_idx = dfa->edests[node_idx].elems[0];
+ if (!re_node_set_contains (&init_nodes, dest_idx))
+ {
+ reg_errcode_t err = re_node_set_merge (&init_nodes,
+ dfa->eclosures
+ + dest_idx);
+ if (err != REG_NOERROR)
+ return err;
+ i = 0;
+ }
+ }
+ }
+
+ /* It must be the first time to invoke acquire_state. */
+ dfa->init_state = re_acquire_state_context (&err, dfa, &init_nodes, 0);
+ /* We don't check ERR here, since the initial state must not be NULL. */
+ if (BE (dfa->init_state == NULL, 0))
+ return err;
+ if (dfa->init_state->has_constraint)
+ {
+ dfa->init_state_word = re_acquire_state_context (&err, dfa, &init_nodes,
+ CONTEXT_WORD);
+ dfa->init_state_nl = re_acquire_state_context (&err, dfa, &init_nodes,
+ CONTEXT_NEWLINE);
+ dfa->init_state_begbuf = re_acquire_state_context (&err, dfa,
+ &init_nodes,
+ CONTEXT_NEWLINE
+ | CONTEXT_BEGBUF);
+ if (BE (dfa->init_state_word == NULL || dfa->init_state_nl == NULL
+ || dfa->init_state_begbuf == NULL, 0))
+ return err;
+ }
+ else
+ dfa->init_state_word = dfa->init_state_nl
+ = dfa->init_state_begbuf = dfa->init_state;
+
+ re_node_set_free (&init_nodes);
+ return REG_NOERROR;
+}
+
+#ifdef RE_ENABLE_I18N
+/* If it is possible to do searching in single byte encoding instead of UTF-8
+ to speed things up, set dfa->mb_cur_max to 1, clear is_utf8 and change
+ DFA nodes where needed. */
+
+static void
+optimize_utf8 (re_dfa_t *dfa)
+{
+ int node, i, mb_chars = 0, has_period = 0;
+
+ for (node = 0; node < dfa->nodes_len; ++node)
+ switch (dfa->nodes[node].type)
+ {
+ case CHARACTER:
+ if (dfa->nodes[node].opr.c >= 0x80)
+ mb_chars = 1;
+ break;
+ case ANCHOR:
+ switch (dfa->nodes[node].opr.ctx_type)
+ {
+ case LINE_FIRST:
+ case LINE_LAST:
+ case BUF_FIRST:
+ case BUF_LAST:
+ break;
+ default:
+ /* Word anchors etc. cannot be handled. It's okay to test
+ opr.ctx_type since constraints (for all DFA nodes) are
+ created by ORing one or more opr.ctx_type values. */
+ return;
+ }
+ break;
+ case OP_PERIOD:
+ has_period = 1;
+ break;
+ case OP_BACK_REF:
+ case OP_ALT:
+ case END_OF_RE:
+ case OP_DUP_ASTERISK:
+ case OP_OPEN_SUBEXP:
+ case OP_CLOSE_SUBEXP:
+ break;
+ case COMPLEX_BRACKET:
+ return;
+ case SIMPLE_BRACKET:
+ /* Just double check. The non-ASCII range starts at 0x80. */
+ assert (0x80 % BITSET_WORD_BITS == 0);
+ for (i = 0x80 / BITSET_WORD_BITS; i < BITSET_WORDS; ++i)
+ if (dfa->nodes[node].opr.sbcset[i])
+ return;
+ break;
+ default:
+ abort ();
+ }
+
+ if (mb_chars || has_period)
+ for (node = 0; node < dfa->nodes_len; ++node)
+ {
+ if (dfa->nodes[node].type == CHARACTER
+ && dfa->nodes[node].opr.c >= 0x80)
+ dfa->nodes[node].mb_partial = 0;
+ else if (dfa->nodes[node].type == OP_PERIOD)
+ dfa->nodes[node].type = OP_UTF8_PERIOD;
+ }
+
+ /* The search can be in single byte locale. */
+ dfa->mb_cur_max = 1;
+ dfa->is_utf8 = 0;
+ dfa->has_mb_node = dfa->nbackref > 0 || has_period;
+}
+#endif
+
+/* Analyze the structure tree, and calculate "first", "next", "edest",
+ "eclosure", and "inveclosure". */
+
+static reg_errcode_t
+analyze (regex_t *preg)
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ reg_errcode_t ret;
+
+ /* Allocate arrays. */
+ dfa->nexts = re_malloc (int, dfa->nodes_alloc);
+ dfa->org_indices = re_malloc (int, dfa->nodes_alloc);
+ dfa->edests = re_malloc (re_node_set, dfa->nodes_alloc);
+ dfa->eclosures = re_malloc (re_node_set, dfa->nodes_alloc);
+ if (BE (dfa->nexts == NULL || dfa->org_indices == NULL || dfa->edests == NULL
+ || dfa->eclosures == NULL, 0))
+ return REG_ESPACE;
+
+ dfa->subexp_map = re_malloc (int, preg->re_nsub);
+ if (dfa->subexp_map != NULL)
+ {
+ unsigned int i;
+ for (i = 0; i < preg->re_nsub; i++)
+ dfa->subexp_map[i] = i;
+ preorder (dfa->str_tree, optimize_subexps, dfa);
+ for (i = 0; i < preg->re_nsub; i++)
+ if (dfa->subexp_map[i] != i)
+ break;
+ if (i == preg->re_nsub)
+ {
+ free (dfa->subexp_map);
+ dfa->subexp_map = NULL;
+ }
+ }
+
+ ret = postorder (dfa->str_tree, lower_subexps, preg);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+ ret = postorder (dfa->str_tree, calc_first, dfa);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+ preorder (dfa->str_tree, calc_next, dfa);
+ ret = preorder (dfa->str_tree, link_nfa_nodes, dfa);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+ ret = calc_eclosure (dfa);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+
+ /* We only need this during the prune_impossible_nodes pass in regexec.c;
+ skip it if p_i_n will not run, as calc_inveclosure can be quadratic. */
+ if ((!preg->no_sub && preg->re_nsub > 0 && dfa->has_plural_match)
+ || dfa->nbackref)
+ {
+ dfa->inveclosures = re_malloc (re_node_set, dfa->nodes_len);
+ if (BE (dfa->inveclosures == NULL, 0))
+ return REG_ESPACE;
+ ret = calc_inveclosure (dfa);
+ }
+
+ return ret;
+}
+
+/* Our parse trees are very unbalanced, so we cannot use a stack to
+ implement parse tree visits. Instead, we use parent pointers and
+ some hairy code in these two functions. */
+static reg_errcode_t
+postorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)),
+ void *extra)
+{
+ bin_tree_t *node, *prev;
+
+ for (node = root; ; )
+ {
+ /* Descend down the tree, preferably to the left (or to the right
+ if that's the only child). */
+ while (node->left || node->right)
+ if (node->left)
+ node = node->left;
+ else
+ node = node->right;
+
+ do
+ {
+ reg_errcode_t err = fn (extra, node);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ if (node->parent == NULL)
+ return REG_NOERROR;
+ prev = node;
+ node = node->parent;
+ }
+ /* Go up while we have a node that is reached from the right. */
+ while (node->right == prev || node->right == NULL);
+ node = node->right;
+ }
+}
+
+static reg_errcode_t
+preorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)),
+ void *extra)
+{
+ bin_tree_t *node;
+
+ for (node = root; ; )
+ {
+ reg_errcode_t err = fn (extra, node);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+
+ /* Go to the left node, or up and to the right. */
+ if (node->left)
+ node = node->left;
+ else
+ {
+ bin_tree_t *prev = NULL;
+ while (node->right == prev || node->right == NULL)
+ {
+ prev = node;
+ node = node->parent;
+ if (!node)
+ return REG_NOERROR;
+ }
+ node = node->right;
+ }
+ }
+}
+
+/* Optimization pass: if a SUBEXP is entirely contained, strip it and tell
+ re_search_internal to map the inner one's opr.idx to this one's. Adjust
+ backreferences as well. Requires a preorder visit. */
+static reg_errcode_t
+optimize_subexps (void *extra, bin_tree_t *node)
+{
+ re_dfa_t *dfa = (re_dfa_t *) extra;
+
+ if (node->token.type == OP_BACK_REF && dfa->subexp_map)
+ {
+ int idx = node->token.opr.idx;
+ node->token.opr.idx = dfa->subexp_map[idx];
+ dfa->used_bkref_map |= 1 << node->token.opr.idx;
+ }
+
+ else if (node->token.type == SUBEXP
+ && node->left && node->left->token.type == SUBEXP)
+ {
+ int other_idx = node->left->token.opr.idx;
+
+ node->left = node->left->left;
+ if (node->left)
+ node->left->parent = node;
+
+ dfa->subexp_map[other_idx] = dfa->subexp_map[node->token.opr.idx];
+ if (other_idx < BITSET_WORD_BITS)
+ dfa->used_bkref_map &= ~((bitset_word_t) 1 << other_idx);
+ }
+
+ return REG_NOERROR;
+}
+
+/* Lowering pass: Turn each SUBEXP node into the appropriate concatenation
+ of OP_OPEN_SUBEXP, the body of the SUBEXP (if any) and OP_CLOSE_SUBEXP. */
+static reg_errcode_t
+lower_subexps (void *extra, bin_tree_t *node)
+{
+ regex_t *preg = (regex_t *) extra;
+ reg_errcode_t err = REG_NOERROR;
+
+ if (node->left && node->left->token.type == SUBEXP)
+ {
+ node->left = lower_subexp (&err, preg, node->left);
+ if (node->left)
+ node->left->parent = node;
+ }
+ if (node->right && node->right->token.type == SUBEXP)
+ {
+ node->right = lower_subexp (&err, preg, node->right);
+ if (node->right)
+ node->right->parent = node;
+ }
+
+ return err;
+}
+
+static bin_tree_t *
+lower_subexp (reg_errcode_t *err, regex_t *preg, bin_tree_t *node)
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ bin_tree_t *body = node->left;
+ bin_tree_t *op, *cls, *tree1, *tree;
+
+ if (preg->no_sub
+ /* We do not optimize empty subexpressions, because otherwise we may
+ have bad CONCAT nodes with NULL children. This is obviously not
+ very common, so we do not lose much. An example that triggers
+ this case is the sed "script" /\(\)/x. */
+ && node->left != NULL
+ && (node->token.opr.idx >= BITSET_WORD_BITS
+ || !(dfa->used_bkref_map
+ & ((bitset_word_t) 1 << node->token.opr.idx))))
+ return node->left;
+
+ /* Convert the SUBEXP node to the concatenation of an
+ OP_OPEN_SUBEXP, the contents, and an OP_CLOSE_SUBEXP. */
+ op = create_tree (dfa, NULL, NULL, OP_OPEN_SUBEXP);
+ cls = create_tree (dfa, NULL, NULL, OP_CLOSE_SUBEXP);
+ tree1 = body ? create_tree (dfa, body, cls, CONCAT) : cls;
+ tree = create_tree (dfa, op, tree1, CONCAT);
+ if (BE (tree == NULL || tree1 == NULL || op == NULL || cls == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+
+ op->token.opr.idx = cls->token.opr.idx = node->token.opr.idx;
+ op->token.opt_subexp = cls->token.opt_subexp = node->token.opt_subexp;
+ return tree;
+}
+
+/* Pass 1 in building the NFA: compute FIRST and create unlinked automaton
+ nodes. Requires a postorder visit. */
+static reg_errcode_t
+calc_first (void *extra, bin_tree_t *node)
+{
+ re_dfa_t *dfa = (re_dfa_t *) extra;
+ if (node->token.type == CONCAT)
+ {
+ node->first = node->left->first;
+ node->node_idx = node->left->node_idx;
+ }
+ else
+ {
+ node->first = node;
+ node->node_idx = re_dfa_add_node (dfa, node->token);
+ if (BE (node->node_idx == -1, 0))
+ return REG_ESPACE;
+ if (node->token.type == ANCHOR)
+ dfa->nodes[node->node_idx].constraint = node->token.opr.ctx_type;
+ }
+ return REG_NOERROR;
+}
+
+/* Pass 2: compute NEXT on the tree. Preorder visit. */
+static reg_errcode_t
+calc_next (void *extra, bin_tree_t *node)
+{
+ switch (node->token.type)
+ {
+ case OP_DUP_ASTERISK:
+ node->left->next = node;
+ break;
+ case CONCAT:
+ node->left->next = node->right->first;
+ node->right->next = node->next;
+ break;
+ default:
+ if (node->left)
+ node->left->next = node->next;
+ if (node->right)
+ node->right->next = node->next;
+ break;
+ }
+ return REG_NOERROR;
+}
+
+/* Pass 3: link all DFA nodes to their NEXT node (any order will do). */
+static reg_errcode_t
+link_nfa_nodes (void *extra, bin_tree_t *node)
+{
+ re_dfa_t *dfa = (re_dfa_t *) extra;
+ int idx = node->node_idx;
+ reg_errcode_t err = REG_NOERROR;
+
+ switch (node->token.type)
+ {
+ case CONCAT:
+ break;
+
+ case END_OF_RE:
+ assert (node->next == NULL);
+ break;
+
+ case OP_DUP_ASTERISK:
+ case OP_ALT:
+ {
+ int left, right;
+ dfa->has_plural_match = 1;
+ if (node->left != NULL)
+ left = node->left->first->node_idx;
+ else
+ left = node->next->node_idx;
+ if (node->right != NULL)
+ right = node->right->first->node_idx;
+ else
+ right = node->next->node_idx;
+ assert (left > -1);
+ assert (right > -1);
+ err = re_node_set_init_2 (dfa->edests + idx, left, right);
+ }
+ break;
+
+ case ANCHOR:
+ case OP_OPEN_SUBEXP:
+ case OP_CLOSE_SUBEXP:
+ err = re_node_set_init_1 (dfa->edests + idx, node->next->node_idx);
+ break;
+
+ case OP_BACK_REF:
+ dfa->nexts[idx] = node->next->node_idx;
+ if (node->token.type == OP_BACK_REF)
+ err = re_node_set_init_1 (dfa->edests + idx, dfa->nexts[idx]);
+ break;
+
+ default:
+ assert (!IS_EPSILON_NODE (node->token.type));
+ dfa->nexts[idx] = node->next->node_idx;
+ break;
+ }
+
+ return err;
+}
+
+/* Duplicate the epsilon closure of the node ROOT_NODE.
+ Note that duplicated nodes have constraint INIT_CONSTRAINT in addition
+ to their own constraint. */
+
+static reg_errcode_t
+internal_function
+duplicate_node_closure (re_dfa_t *dfa, int top_org_node, int top_clone_node,
+ int root_node, unsigned int init_constraint)
+{
+ int org_node, clone_node, ret;
+ unsigned int constraint = init_constraint;
+ for (org_node = top_org_node, clone_node = top_clone_node;;)
+ {
+ int org_dest, clone_dest;
+ if (dfa->nodes[org_node].type == OP_BACK_REF)
+ {
+ /* If the back reference epsilon-transit, its destination must
+ also have the constraint. Then duplicate the epsilon closure
+ of the destination of the back reference, and store it in
+ edests of the back reference. */
+ org_dest = dfa->nexts[org_node];
+ re_node_set_empty (dfa->edests + clone_node);
+ clone_dest = duplicate_node (dfa, org_dest, constraint);
+ if (BE (clone_dest == -1, 0))
+ return REG_ESPACE;
+ dfa->nexts[clone_node] = dfa->nexts[org_node];
+ ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+ if (BE (ret < 0, 0))
+ return REG_ESPACE;
+ }
+ else if (dfa->edests[org_node].nelem == 0)
+ {
+ /* In case of the node can't epsilon-transit, don't duplicate the
+ destination and store the original destination as the
+ destination of the node. */
+ dfa->nexts[clone_node] = dfa->nexts[org_node];
+ break;
+ }
+ else if (dfa->edests[org_node].nelem == 1)
+ {
+ /* In case of the node can epsilon-transit, and it has only one
+ destination. */
+ org_dest = dfa->edests[org_node].elems[0];
+ re_node_set_empty (dfa->edests + clone_node);
+ /* If the node is root_node itself, it means the epsilon clsoure
+ has a loop. Then tie it to the destination of the root_node. */
+ if (org_node == root_node && clone_node != org_node)
+ {
+ ret = re_node_set_insert (dfa->edests + clone_node, org_dest);
+ if (BE (ret < 0, 0))
+ return REG_ESPACE;
+ break;
+ }
+ /* In case of the node has another constraint, add it. */
+ constraint |= dfa->nodes[org_node].constraint;
+ clone_dest = duplicate_node (dfa, org_dest, constraint);
+ if (BE (clone_dest == -1, 0))
+ return REG_ESPACE;
+ ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+ if (BE (ret < 0, 0))
+ return REG_ESPACE;
+ }
+ else /* dfa->edests[org_node].nelem == 2 */
+ {
+ /* In case of the node can epsilon-transit, and it has two
+ destinations. In the bin_tree_t and DFA, that's '|' and '*'. */
+ org_dest = dfa->edests[org_node].elems[0];
+ re_node_set_empty (dfa->edests + clone_node);
+ /* Search for a duplicated node which satisfies the constraint. */
+ clone_dest = search_duplicated_node (dfa, org_dest, constraint);
+ if (clone_dest == -1)
+ {
+ /* There is no such duplicated node, create a new one. */
+ reg_errcode_t err;
+ clone_dest = duplicate_node (dfa, org_dest, constraint);
+ if (BE (clone_dest == -1, 0))
+ return REG_ESPACE;
+ ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+ if (BE (ret < 0, 0))
+ return REG_ESPACE;
+ err = duplicate_node_closure (dfa, org_dest, clone_dest,
+ root_node, constraint);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ else
+ {
+ /* There is a duplicated node which satisfies the constraint,
+ use it to avoid infinite loop. */
+ ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+ if (BE (ret < 0, 0))
+ return REG_ESPACE;
+ }
+
+ org_dest = dfa->edests[org_node].elems[1];
+ clone_dest = duplicate_node (dfa, org_dest, constraint);
+ if (BE (clone_dest == -1, 0))
+ return REG_ESPACE;
+ ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+ if (BE (ret < 0, 0))
+ return REG_ESPACE;
+ }
+ org_node = org_dest;
+ clone_node = clone_dest;
+ }
+ return REG_NOERROR;
+}
+
+/* Search for a node which is duplicated from the node ORG_NODE, and
+ satisfies the constraint CONSTRAINT. */
+
+static int
+search_duplicated_node (const re_dfa_t *dfa, int org_node,
+ unsigned int constraint)
+{
+ int idx;
+ for (idx = dfa->nodes_len - 1; dfa->nodes[idx].duplicated && idx > 0; --idx)
+ {
+ if (org_node == dfa->org_indices[idx]
+ && constraint == dfa->nodes[idx].constraint)
+ return idx; /* Found. */
+ }
+ return -1; /* Not found. */
+}
+
+/* Duplicate the node whose index is ORG_IDX and set the constraint CONSTRAINT.
+ Return the index of the new node, or -1 if insufficient storage is
+ available. */
+
+static int
+duplicate_node (re_dfa_t *dfa, int org_idx, unsigned int constraint)
+{
+ int dup_idx = re_dfa_add_node (dfa, dfa->nodes[org_idx]);
+ if (BE (dup_idx != -1, 1))
+ {
+ dfa->nodes[dup_idx].constraint = constraint;
+ dfa->nodes[dup_idx].constraint |= dfa->nodes[org_idx].constraint;
+ dfa->nodes[dup_idx].duplicated = 1;
+
+ /* Store the index of the original node. */
+ dfa->org_indices[dup_idx] = org_idx;
+ }
+ return dup_idx;
+}
+
+static reg_errcode_t
+calc_inveclosure (re_dfa_t *dfa)
+{
+ int ret;
+ unsigned int src, idx;
+ for (idx = 0; idx < dfa->nodes_len; ++idx)
+ re_node_set_init_empty (dfa->inveclosures + idx);
+
+ for (src = 0; src < dfa->nodes_len; ++src)
+ {
+ int *elems = dfa->eclosures[src].elems;
+ int idx;
+ for (idx = 0; idx < dfa->eclosures[src].nelem; ++idx)
+ {
+ ret = re_node_set_insert_last (dfa->inveclosures + elems[idx], src);
+ if (BE (ret == -1, 0))
+ return REG_ESPACE;
+ }
+ }
+
+ return REG_NOERROR;
+}
+
+/* Calculate "eclosure" for all the node in DFA. */
+
+static reg_errcode_t
+calc_eclosure (re_dfa_t *dfa)
+{
+ int node_idx, incomplete;
+#ifdef DEBUG
+ assert (dfa->nodes_len > 0);
+#endif
+ incomplete = 0;
+ /* For each nodes, calculate epsilon closure. */
+ for (node_idx = 0; ; ++node_idx)
+ {
+ reg_errcode_t err;
+ re_node_set eclosure_elem;
+ if (node_idx == dfa->nodes_len)
+ {
+ if (!incomplete)
+ break;
+ incomplete = 0;
+ node_idx = 0;
+ }
+
+#ifdef DEBUG
+ assert (dfa->eclosures[node_idx].nelem != -1);
+#endif
+
+ /* If we have already calculated, skip it. */
+ if (dfa->eclosures[node_idx].nelem != 0)
+ continue;
+ /* Calculate epsilon closure of `node_idx'. */
+ err = calc_eclosure_iter (&eclosure_elem, dfa, node_idx, 1);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+
+ if (dfa->eclosures[node_idx].nelem == 0)
+ {
+ incomplete = 1;
+ re_node_set_free (&eclosure_elem);
+ }
+ }
+ return REG_NOERROR;
+}
+
+/* Calculate epsilon closure of NODE. */
+
+static reg_errcode_t
+calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa, int node, int root)
+{
+ reg_errcode_t err;
+ int i;
+ re_node_set eclosure;
+ int ret;
+ int incomplete = 0;
+ err = re_node_set_alloc (&eclosure, dfa->edests[node].nelem + 1);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+
+ /* This indicates that we are calculating this node now.
+ We reference this value to avoid infinite loop. */
+ dfa->eclosures[node].nelem = -1;
+
+ /* If the current node has constraints, duplicate all nodes
+ since they must inherit the constraints. */
+ if (dfa->nodes[node].constraint
+ && dfa->edests[node].nelem
+ && !dfa->nodes[dfa->edests[node].elems[0]].duplicated)
+ {
+ err = duplicate_node_closure (dfa, node, node, node,
+ dfa->nodes[node].constraint);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+
+ /* Expand each epsilon destination nodes. */
+ if (IS_EPSILON_NODE(dfa->nodes[node].type))
+ for (i = 0; i < dfa->edests[node].nelem; ++i)
+ {
+ re_node_set eclosure_elem;
+ int edest = dfa->edests[node].elems[i];
+ /* If calculating the epsilon closure of `edest' is in progress,
+ return intermediate result. */
+ if (dfa->eclosures[edest].nelem == -1)
+ {
+ incomplete = 1;
+ continue;
+ }
+ /* If we haven't calculated the epsilon closure of `edest' yet,
+ calculate now. Otherwise use calculated epsilon closure. */
+ if (dfa->eclosures[edest].nelem == 0)
+ {
+ err = calc_eclosure_iter (&eclosure_elem, dfa, edest, 0);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ else
+ eclosure_elem = dfa->eclosures[edest];
+ /* Merge the epsilon closure of `edest'. */
+ err = re_node_set_merge (&eclosure, &eclosure_elem);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ /* If the epsilon closure of `edest' is incomplete,
+ the epsilon closure of this node is also incomplete. */
+ if (dfa->eclosures[edest].nelem == 0)
+ {
+ incomplete = 1;
+ re_node_set_free (&eclosure_elem);
+ }
+ }
+
+ /* An epsilon closure includes itself. */
+ ret = re_node_set_insert (&eclosure, node);
+ if (BE (ret < 0, 0))
+ return REG_ESPACE;
+ if (incomplete && !root)
+ dfa->eclosures[node].nelem = 0;
+ else
+ dfa->eclosures[node] = eclosure;
+ *new_set = eclosure;
+ return REG_NOERROR;
+}
+
+/* Functions for token which are used in the parser. */
+
+/* Fetch a token from INPUT.
+ We must not use this function inside bracket expressions. */
+
+static void
+internal_function
+fetch_token (re_token_t *result, re_string_t *input, reg_syntax_t syntax)
+{
+ re_string_skip_bytes (input, peek_token (result, input, syntax));
+}
+
+/* Peek a token from INPUT, and return the length of the token.
+ We must not use this function inside bracket expressions. */
+
+static int
+internal_function
+peek_token (re_token_t *token, re_string_t *input, reg_syntax_t syntax)
+{
+ unsigned char c;
+
+ if (re_string_eoi (input))
+ {
+ token->type = END_OF_RE;
+ return 0;
+ }
+
+ c = re_string_peek_byte (input, 0);
+ token->opr.c = c;
+
+ token->word_char = 0;
+#ifdef RE_ENABLE_I18N
+ token->mb_partial = 0;
+ if (input->mb_cur_max > 1 &&
+ !re_string_first_byte (input, re_string_cur_idx (input)))
+ {
+ token->type = CHARACTER;
+ token->mb_partial = 1;
+ return 1;
+ }
+#endif
+ if (c == '\\')
+ {
+ unsigned char c2;
+ if (re_string_cur_idx (input) + 1 >= re_string_length (input))
+ {
+ token->type = BACK_SLASH;
+ return 1;
+ }
+
+ c2 = re_string_peek_byte_case (input, 1);
+ token->opr.c = c2;
+ token->type = CHARACTER;
+#ifdef RE_ENABLE_I18N
+ if (input->mb_cur_max > 1)
+ {
+ wint_t wc = re_string_wchar_at (input,
+ re_string_cur_idx (input) + 1);
+ token->word_char = IS_WIDE_WORD_CHAR (wc) != 0;
+ }
+ else
+#endif
+ token->word_char = IS_WORD_CHAR (c2) != 0;
+
+ switch (c2)
+ {
+ case '|':
+ if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_NO_BK_VBAR))
+ token->type = OP_ALT;
+ break;
+ case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9':
+ if (!(syntax & RE_NO_BK_REFS))
+ {
+ token->type = OP_BACK_REF;
+ token->opr.idx = c2 - '1';
+ }
+ break;
+ case '<':
+ if (!(syntax & RE_NO_GNU_OPS))
+ {
+ token->type = ANCHOR;
+ token->opr.ctx_type = WORD_FIRST;
+ }
+ break;
+ case '>':
+ if (!(syntax & RE_NO_GNU_OPS))
+ {
+ token->type = ANCHOR;
+ token->opr.ctx_type = WORD_LAST;
+ }
+ break;
+ case 'b':
+ if (!(syntax & RE_NO_GNU_OPS))
+ {
+ token->type = ANCHOR;
+ token->opr.ctx_type = WORD_DELIM;
+ }
+ break;
+ case 'B':
+ if (!(syntax & RE_NO_GNU_OPS))
+ {
+ token->type = ANCHOR;
+ token->opr.ctx_type = NOT_WORD_DELIM;
+ }
+ break;
+ case 'w':
+ if (!(syntax & RE_NO_GNU_OPS))
+ token->type = OP_WORD;
+ break;
+ case 'W':
+ if (!(syntax & RE_NO_GNU_OPS))
+ token->type = OP_NOTWORD;
+ break;
+ case 's':
+ if (!(syntax & RE_NO_GNU_OPS))
+ token->type = OP_SPACE;
+ break;
+ case 'S':
+ if (!(syntax & RE_NO_GNU_OPS))
+ token->type = OP_NOTSPACE;
+ break;
+ case '`':
+ if (!(syntax & RE_NO_GNU_OPS))
+ {
+ token->type = ANCHOR;
+ token->opr.ctx_type = BUF_FIRST;
+ }
+ break;
+ case '\'':
+ if (!(syntax & RE_NO_GNU_OPS))
+ {
+ token->type = ANCHOR;
+ token->opr.ctx_type = BUF_LAST;
+ }
+ break;
+ case '(':
+ if (!(syntax & RE_NO_BK_PARENS))
+ token->type = OP_OPEN_SUBEXP;
+ break;
+ case ')':
+ if (!(syntax & RE_NO_BK_PARENS))
+ token->type = OP_CLOSE_SUBEXP;
+ break;
+ case '+':
+ if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM))
+ token->type = OP_DUP_PLUS;
+ break;
+ case '?':
+ if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM))
+ token->type = OP_DUP_QUESTION;
+ break;
+ case '{':
+ if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES)))
+ token->type = OP_OPEN_DUP_NUM;
+ break;
+ case '}':
+ if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES)))
+ token->type = OP_CLOSE_DUP_NUM;
+ break;
+ default:
+ break;
+ }
+ return 2;
+ }
+
+ token->type = CHARACTER;
+#ifdef RE_ENABLE_I18N
+ if (input->mb_cur_max > 1)
+ {
+ wint_t wc = re_string_wchar_at (input, re_string_cur_idx (input));
+ token->word_char = IS_WIDE_WORD_CHAR (wc) != 0;
+ }
+ else
+#endif
+ token->word_char = IS_WORD_CHAR (token->opr.c);
+
+ switch (c)
+ {
+ case '\n':
+ if (syntax & RE_NEWLINE_ALT)
+ token->type = OP_ALT;
+ break;
+ case '|':
+ if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_NO_BK_VBAR))
+ token->type = OP_ALT;
+ break;
+ case '*':
+ token->type = OP_DUP_ASTERISK;
+ break;
+ case '+':
+ if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM))
+ token->type = OP_DUP_PLUS;
+ break;
+ case '?':
+ if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM))
+ token->type = OP_DUP_QUESTION;
+ break;
+ case '{':
+ if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+ token->type = OP_OPEN_DUP_NUM;
+ break;
+ case '}':
+ if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+ token->type = OP_CLOSE_DUP_NUM;
+ break;
+ case '(':
+ if (syntax & RE_NO_BK_PARENS)
+ token->type = OP_OPEN_SUBEXP;
+ break;
+ case ')':
+ if (syntax & RE_NO_BK_PARENS)
+ token->type = OP_CLOSE_SUBEXP;
+ break;
+ case '[':
+ token->type = OP_OPEN_BRACKET;
+ break;
+ case '.':
+ token->type = OP_PERIOD;
+ break;
+ case '^':
+ if (!(syntax & (RE_CONTEXT_INDEP_ANCHORS | RE_CARET_ANCHORS_HERE)) &&
+ re_string_cur_idx (input) != 0)
+ {
+ char prev = re_string_peek_byte (input, -1);
+ if (!(syntax & RE_NEWLINE_ALT) || prev != '\n')
+ break;
+ }
+ token->type = ANCHOR;
+ token->opr.ctx_type = LINE_FIRST;
+ break;
+ case '$':
+ if (!(syntax & RE_CONTEXT_INDEP_ANCHORS) &&
+ re_string_cur_idx (input) + 1 != re_string_length (input))
+ {
+ re_token_t next;
+ re_string_skip_bytes (input, 1);
+ peek_token (&next, input, syntax);
+ re_string_skip_bytes (input, -1);
+ if (next.type != OP_ALT && next.type != OP_CLOSE_SUBEXP)
+ break;
+ }
+ token->type = ANCHOR;
+ token->opr.ctx_type = LINE_LAST;
+ break;
+ default:
+ break;
+ }
+ return 1;
+}
+
+/* Peek a token from INPUT, and return the length of the token.
+ We must not use this function out of bracket expressions. */
+
+static int
+internal_function
+peek_token_bracket (re_token_t *token, re_string_t *input, reg_syntax_t syntax)
+{
+ unsigned char c;
+ if (re_string_eoi (input))
+ {
+ token->type = END_OF_RE;
+ return 0;
+ }
+ c = re_string_peek_byte (input, 0);
+ token->opr.c = c;
+
+#ifdef RE_ENABLE_I18N
+ if (input->mb_cur_max > 1 &&
+ !re_string_first_byte (input, re_string_cur_idx (input)))
+ {
+ token->type = CHARACTER;
+ return 1;
+ }
+#endif /* RE_ENABLE_I18N */
+
+ if (c == '\\' && (syntax & RE_BACKSLASH_ESCAPE_IN_LISTS)
+ && re_string_cur_idx (input) + 1 < re_string_length (input))
+ {
+ /* In this case, '\' escape a character. */
+ unsigned char c2;
+ re_string_skip_bytes (input, 1);
+ c2 = re_string_peek_byte (input, 0);
+ token->opr.c = c2;
+ token->type = CHARACTER;
+ return 1;
+ }
+ if (c == '[') /* '[' is a special char in a bracket exps. */
+ {
+ unsigned char c2;
+ int token_len;
+ if (re_string_cur_idx (input) + 1 < re_string_length (input))
+ c2 = re_string_peek_byte (input, 1);
+ else
+ c2 = 0;
+ token->opr.c = c2;
+ token_len = 2;
+ switch (c2)
+ {
+ case '.':
+ token->type = OP_OPEN_COLL_ELEM;
+ break;
+ case '=':
+ token->type = OP_OPEN_EQUIV_CLASS;
+ break;
+ case ':':
+ if (syntax & RE_CHAR_CLASSES)
+ {
+ token->type = OP_OPEN_CHAR_CLASS;
+ break;
+ }
+ /* else fall through. */
+ default:
+ token->type = CHARACTER;
+ token->opr.c = c;
+ token_len = 1;
+ break;
+ }
+ return token_len;
+ }
+ switch (c)
+ {
+ case '-':
+ token->type = OP_CHARSET_RANGE;
+ break;
+ case ']':
+ token->type = OP_CLOSE_BRACKET;
+ break;
+ case '^':
+ token->type = OP_NON_MATCH_LIST;
+ break;
+ default:
+ token->type = CHARACTER;
+ }
+ return 1;
+}
+
+/* Functions for parser. */
+
+/* Entry point of the parser.
+ Parse the regular expression REGEXP and return the structure tree.
+ If an error is occured, ERR is set by error code, and return NULL.
+ This function build the following tree, from regular expression <reg_exp>:
+ CAT
+ / \
+ / \
+ <reg_exp> EOR
+
+ CAT means concatenation.
+ EOR means end of regular expression. */
+
+static bin_tree_t *
+parse (re_string_t *regexp, regex_t *preg, reg_syntax_t syntax,
+ reg_errcode_t *err)
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ bin_tree_t *tree, *eor, *root;
+ re_token_t current_token;
+ dfa->syntax = syntax;
+ fetch_token (&current_token, regexp, syntax | RE_CARET_ANCHORS_HERE);
+ tree = parse_reg_exp (regexp, preg, &current_token, syntax, 0, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+ eor = create_tree (dfa, NULL, NULL, END_OF_RE);
+ if (tree != NULL)
+ root = create_tree (dfa, tree, eor, CONCAT);
+ else
+ root = eor;
+ if (BE (eor == NULL || root == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ return root;
+}
+
+/* This function build the following tree, from regular expression
+ <branch1>|<branch2>:
+ ALT
+ / \
+ / \
+ <branch1> <branch2>
+
+ ALT means alternative, which represents the operator `|'. */
+
+static bin_tree_t *
+parse_reg_exp (re_string_t *regexp, regex_t *preg, re_token_t *token,
+ reg_syntax_t syntax, int nest, reg_errcode_t *err)
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ bin_tree_t *tree, *branch = NULL;
+ tree = parse_branch (regexp, preg, token, syntax, nest, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+
+ while (token->type == OP_ALT)
+ {
+ fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE);
+ if (token->type != OP_ALT && token->type != END_OF_RE
+ && (nest == 0 || token->type != OP_CLOSE_SUBEXP))
+ {
+ branch = parse_branch (regexp, preg, token, syntax, nest, err);
+ if (BE (*err != REG_NOERROR && branch == NULL, 0))
+ return NULL;
+ }
+ else
+ branch = NULL;
+ tree = create_tree (dfa, tree, branch, OP_ALT);
+ if (BE (tree == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ }
+ return tree;
+}
+
+/* This function build the following tree, from regular expression
+ <exp1><exp2>:
+ CAT
+ / \
+ / \
+ <exp1> <exp2>
+
+ CAT means concatenation. */
+
+static bin_tree_t *
+parse_branch (re_string_t *regexp, regex_t *preg, re_token_t *token,
+ reg_syntax_t syntax, int nest, reg_errcode_t *err)
+{
+ bin_tree_t *tree, *exp;
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ tree = parse_expression (regexp, preg, token, syntax, nest, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+
+ while (token->type != OP_ALT && token->type != END_OF_RE
+ && (nest == 0 || token->type != OP_CLOSE_SUBEXP))
+ {
+ exp = parse_expression (regexp, preg, token, syntax, nest, err);
+ if (BE (*err != REG_NOERROR && exp == NULL, 0))
+ {
+ return NULL;
+ }
+ if (tree != NULL && exp != NULL)
+ {
+ tree = create_tree (dfa, tree, exp, CONCAT);
+ if (tree == NULL)
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ }
+ else if (tree == NULL)
+ tree = exp;
+ /* Otherwise exp == NULL, we don't need to create new tree. */
+ }
+ return tree;
+}
+
+/* This function build the following tree, from regular expression a*:
+ *
+ |
+ a
+*/
+
+static bin_tree_t *
+parse_expression (re_string_t *regexp, regex_t *preg, re_token_t *token,
+ reg_syntax_t syntax, int nest, reg_errcode_t *err)
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ bin_tree_t *tree;
+ switch (token->type)
+ {
+ case CHARACTER:
+ tree = create_token_tree (dfa, NULL, NULL, token);
+ if (BE (tree == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+#ifdef RE_ENABLE_I18N
+ if (dfa->mb_cur_max > 1)
+ {
+ while (!re_string_eoi (regexp)
+ && !re_string_first_byte (regexp, re_string_cur_idx (regexp)))
+ {
+ bin_tree_t *mbc_remain;
+ fetch_token (token, regexp, syntax);
+ mbc_remain = create_token_tree (dfa, NULL, NULL, token);
+ tree = create_tree (dfa, tree, mbc_remain, CONCAT);
+ if (BE (mbc_remain == NULL || tree == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ }
+ }
+#endif
+ break;
+ case OP_OPEN_SUBEXP:
+ tree = parse_sub_exp (regexp, preg, token, syntax, nest + 1, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+ break;
+ case OP_OPEN_BRACKET:
+ tree = parse_bracket_exp (regexp, dfa, token, syntax, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+ break;
+ case OP_BACK_REF:
+ if (!BE (dfa->completed_bkref_map & (1 << token->opr.idx), 1))
+ {
+ *err = REG_ESUBREG;
+ return NULL;
+ }
+ dfa->used_bkref_map |= 1 << token->opr.idx;
+ tree = create_token_tree (dfa, NULL, NULL, token);
+ if (BE (tree == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ ++dfa->nbackref;
+ dfa->has_mb_node = 1;
+ break;
+ case OP_OPEN_DUP_NUM:
+ if (syntax & RE_CONTEXT_INVALID_DUP)
+ {
+ *err = REG_BADRPT;
+ return NULL;
+ }
+ /* FALLTHROUGH */
+ case OP_DUP_ASTERISK:
+ case OP_DUP_PLUS:
+ case OP_DUP_QUESTION:
+ if (syntax & RE_CONTEXT_INVALID_OPS)
+ {
+ *err = REG_BADRPT;
+ return NULL;
+ }
+ else if (syntax & RE_CONTEXT_INDEP_OPS)
+ {
+ fetch_token (token, regexp, syntax);
+ return parse_expression (regexp, preg, token, syntax, nest, err);
+ }
+ /* else fall through */
+ case OP_CLOSE_SUBEXP:
+ if ((token->type == OP_CLOSE_SUBEXP) &&
+ !(syntax & RE_UNMATCHED_RIGHT_PAREN_ORD))
+ {
+ *err = REG_ERPAREN;
+ return NULL;
+ }
+ /* else fall through */
+ case OP_CLOSE_DUP_NUM:
+ /* We treat it as a normal character. */
+
+ /* Then we can these characters as normal characters. */
+ token->type = CHARACTER;
+ /* mb_partial and word_char bits should be initialized already
+ by peek_token. */
+ tree = create_token_tree (dfa, NULL, NULL, token);
+ if (BE (tree == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ break;
+ case ANCHOR:
+ if ((token->opr.ctx_type
+ & (WORD_DELIM | NOT_WORD_DELIM | WORD_FIRST | WORD_LAST))
+ && dfa->word_ops_used == 0)
+ init_word_char (dfa);
+ if (token->opr.ctx_type == WORD_DELIM
+ || token->opr.ctx_type == NOT_WORD_DELIM)
+ {
+ bin_tree_t *tree_first, *tree_last;
+ if (token->opr.ctx_type == WORD_DELIM)
+ {
+ token->opr.ctx_type = WORD_FIRST;
+ tree_first = create_token_tree (dfa, NULL, NULL, token);
+ token->opr.ctx_type = WORD_LAST;
+ }
+ else
+ {
+ token->opr.ctx_type = INSIDE_WORD;
+ tree_first = create_token_tree (dfa, NULL, NULL, token);
+ token->opr.ctx_type = INSIDE_NOTWORD;
+ }
+ tree_last = create_token_tree (dfa, NULL, NULL, token);
+ tree = create_tree (dfa, tree_first, tree_last, OP_ALT);
+ if (BE (tree_first == NULL || tree_last == NULL || tree == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ }
+ else
+ {
+ tree = create_token_tree (dfa, NULL, NULL, token);
+ if (BE (tree == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ }
+ /* We must return here, since ANCHORs can't be followed
+ by repetition operators.
+ eg. RE"^*" is invalid or "<ANCHOR(^)><CHAR(*)>",
+ it must not be "<ANCHOR(^)><REPEAT(*)>". */
+ fetch_token (token, regexp, syntax);
+ return tree;
+ case OP_PERIOD:
+ tree = create_token_tree (dfa, NULL, NULL, token);
+ if (BE (tree == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ if (dfa->mb_cur_max > 1)
+ dfa->has_mb_node = 1;
+ break;
+ case OP_WORD:
+ case OP_NOTWORD:
+ tree = build_charclass_op (dfa, regexp->trans,
+ "alnum",
+ "_",
+ token->type == OP_NOTWORD, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+ break;
+ case OP_SPACE:
+ case OP_NOTSPACE:
+ tree = build_charclass_op (dfa, regexp->trans,
+ "space",
+ "",
+ token->type == OP_NOTSPACE, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+ break;
+ case OP_ALT:
+ case END_OF_RE:
+ return NULL;
+ case BACK_SLASH:
+ *err = REG_EESCAPE;
+ return NULL;
+ default:
+ /* Must not happen? */
+#ifdef DEBUG
+ assert (0);
+#endif
+ return NULL;
+ }
+ fetch_token (token, regexp, syntax);
+
+ while (token->type == OP_DUP_ASTERISK || token->type == OP_DUP_PLUS
+ || token->type == OP_DUP_QUESTION || token->type == OP_OPEN_DUP_NUM)
+ {
+ tree = parse_dup_op (tree, regexp, dfa, token, syntax, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+ /* In BRE consecutive duplications are not allowed. */
+ if ((syntax & RE_CONTEXT_INVALID_DUP)
+ && (token->type == OP_DUP_ASTERISK
+ || token->type == OP_OPEN_DUP_NUM))
+ {
+ *err = REG_BADRPT;
+ return NULL;
+ }
+ }
+
+ return tree;
+}
+
+/* This function build the following tree, from regular expression
+ (<reg_exp>):
+ SUBEXP
+ |
+ <reg_exp>
+*/
+
+static bin_tree_t *
+parse_sub_exp (re_string_t *regexp, regex_t *preg, re_token_t *token,
+ reg_syntax_t syntax, int nest, reg_errcode_t *err)
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ bin_tree_t *tree;
+ size_t cur_nsub;
+ cur_nsub = preg->re_nsub++;
+
+ fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE);
+
+ /* The subexpression may be a null string. */
+ if (token->type == OP_CLOSE_SUBEXP)
+ tree = NULL;
+ else
+ {
+ tree = parse_reg_exp (regexp, preg, token, syntax, nest, err);
+ if (BE (*err == REG_NOERROR && token->type != OP_CLOSE_SUBEXP, 0))
+ *err = REG_EPAREN;
+ if (BE (*err != REG_NOERROR, 0))
+ return NULL;
+ }
+
+ if (cur_nsub <= '9' - '1')
+ dfa->completed_bkref_map |= 1 << cur_nsub;
+
+ tree = create_tree (dfa, tree, NULL, SUBEXP);
+ if (BE (tree == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ tree->token.opr.idx = cur_nsub;
+ return tree;
+}
+
+/* This function parse repetition operators like "*", "+", "{1,3}" etc. */
+
+static bin_tree_t *
+parse_dup_op (bin_tree_t *elem, re_string_t *regexp, re_dfa_t *dfa,
+ re_token_t *token, reg_syntax_t syntax, reg_errcode_t *err)
+{
+ bin_tree_t *tree = NULL, *old_tree = NULL;
+ int i, start, end, start_idx = re_string_cur_idx (regexp);
+#ifndef RE_TOKEN_INIT_BUG
+ re_token_t start_token = *token;
+#else
+ re_token_t start_token;
+
+ memcpy ((void *) &start_token, (void *) token, sizeof start_token);
+#endif
+
+ if (token->type == OP_OPEN_DUP_NUM)
+ {
+ end = 0;
+ start = fetch_number (regexp, token, syntax);
+ if (start == -1)
+ {
+ if (token->type == CHARACTER && token->opr.c == ',')
+ start = 0; /* We treat "{,m}" as "{0,m}". */
+ else
+ {
+ *err = REG_BADBR; /* <re>{} is invalid. */
+ return NULL;
+ }
+ }
+ if (BE (start != -2, 1))
+ {
+ /* We treat "{n}" as "{n,n}". */
+ end = ((token->type == OP_CLOSE_DUP_NUM) ? start
+ : ((token->type == CHARACTER && token->opr.c == ',')
+ ? fetch_number (regexp, token, syntax) : -2));
+ }
+ if (BE (start == -2 || end == -2, 0))
+ {
+ /* Invalid sequence. */
+ if (BE (!(syntax & RE_INVALID_INTERVAL_ORD), 0))
+ {
+ if (token->type == END_OF_RE)
+ *err = REG_EBRACE;
+ else
+ *err = REG_BADBR;
+
+ return NULL;
+ }
+
+ /* If the syntax bit is set, rollback. */
+ re_string_set_index (regexp, start_idx);
+ *token = start_token;
+ token->type = CHARACTER;
+ /* mb_partial and word_char bits should be already initialized by
+ peek_token. */
+ return elem;
+ }
+
+ if (BE ((end != -1 && start > end) || token->type != OP_CLOSE_DUP_NUM, 0))
+ {
+ /* First number greater than second. */
+ *err = REG_BADBR;
+ return NULL;
+ }
+ }
+ else
+ {
+ start = (token->type == OP_DUP_PLUS) ? 1 : 0;
+ end = (token->type == OP_DUP_QUESTION) ? 1 : -1;
+ }
+
+ fetch_token (token, regexp, syntax);
+
+ if (BE (elem == NULL, 0))
+ return NULL;
+ if (BE (start == 0 && end == 0, 0))
+ {
+ postorder (elem, free_tree, NULL);
+ return NULL;
+ }
+
+ /* Extract "<re>{n,m}" to "<re><re>...<re><re>{0,<m-n>}". */
+ if (BE (start > 0, 0))
+ {
+ tree = elem;
+ for (i = 2; i <= start; ++i)
+ {
+ elem = duplicate_tree (elem, dfa);
+ tree = create_tree (dfa, tree, elem, CONCAT);
+ if (BE (elem == NULL || tree == NULL, 0))
+ goto parse_dup_op_espace;
+ }
+
+ if (start == end)
+ return tree;
+
+ /* Duplicate ELEM before it is marked optional. */
+ elem = duplicate_tree (elem, dfa);
+ old_tree = tree;
+ }
+ else
+ old_tree = NULL;
+
+ if (elem->token.type == SUBEXP)
+ postorder (elem, mark_opt_subexp, (void *) (long) elem->token.opr.idx);
+
+ tree = create_tree (dfa, elem, NULL, (end == -1 ? OP_DUP_ASTERISK : OP_ALT));
+ if (BE (tree == NULL, 0))
+ goto parse_dup_op_espace;
+
+ /* This loop is actually executed only when end != -1,
+ to rewrite <re>{0,n} as (<re>(<re>...<re>?)?)?... We have
+ already created the start+1-th copy. */
+ for (i = start + 2; i <= end; ++i)
+ {
+ elem = duplicate_tree (elem, dfa);
+ tree = create_tree (dfa, tree, elem, CONCAT);
+ if (BE (elem == NULL || tree == NULL, 0))
+ goto parse_dup_op_espace;
+
+ tree = create_tree (dfa, tree, NULL, OP_ALT);
+ if (BE (tree == NULL, 0))
+ goto parse_dup_op_espace;
+ }
+
+ if (old_tree)
+ tree = create_tree (dfa, old_tree, tree, CONCAT);
+
+ return tree;
+
+ parse_dup_op_espace:
+ *err = REG_ESPACE;
+ return NULL;
+}
+
+/* Size of the names for collating symbol/equivalence_class/character_class.
+ I'm not sure, but maybe enough. */
+#define BRACKET_NAME_BUF_SIZE 32
+
+#ifndef _LIBC
+ /* Local function for parse_bracket_exp only used in case of NOT _LIBC.
+ Build the range expression which starts from START_ELEM, and ends
+ at END_ELEM. The result are written to MBCSET and SBCSET.
+ RANGE_ALLOC is the allocated size of mbcset->range_starts, and
+ mbcset->range_ends, is a pointer argument sinse we may
+ update it. */
+
+static reg_errcode_t
+internal_function
+# ifdef RE_ENABLE_I18N
+build_range_exp (bitset_t sbcset, re_charset_t *mbcset, int *range_alloc,
+ bracket_elem_t *start_elem, bracket_elem_t *end_elem)
+# else /* not RE_ENABLE_I18N */
+build_range_exp (bitset_t sbcset, bracket_elem_t *start_elem,
+ bracket_elem_t *end_elem)
+# endif /* not RE_ENABLE_I18N */
+{
+ unsigned int start_ch, end_ch;
+ /* Equivalence Classes and Character Classes can't be a range start/end. */
+ if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS
+ || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS,
+ 0))
+ return REG_ERANGE;
+
+ /* We can handle no multi character collating elements without libc
+ support. */
+ if (BE ((start_elem->type == COLL_SYM
+ && strlen ((char *) start_elem->opr.name) > 1)
+ || (end_elem->type == COLL_SYM
+ && strlen ((char *) end_elem->opr.name) > 1), 0))
+ return REG_ECOLLATE;
+
+# ifdef RE_ENABLE_I18N
+ {
+ wchar_t wc;
+ wint_t start_wc;
+ wint_t end_wc;
+ wchar_t cmp_buf[6] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'};
+
+ start_ch = ((start_elem->type == SB_CHAR) ? start_elem->opr.ch
+ : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0]
+ : 0));
+ end_ch = ((end_elem->type == SB_CHAR) ? end_elem->opr.ch
+ : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0]
+ : 0));
+#ifdef GAWK
+ /*
+ * Fedora Core 2, maybe others, have broken `btowc' that returns -1
+ * for any value > 127. Sigh. Note that `start_ch' and `end_ch' are
+ * unsigned, so we don't have sign extension problems.
+ */
+ start_wc = ((start_elem->type == SB_CHAR || start_elem->type == COLL_SYM)
+ ? start_ch : start_elem->opr.wch);
+ end_wc = ((end_elem->type == SB_CHAR || end_elem->type == COLL_SYM)
+ ? end_ch : end_elem->opr.wch);
+#else
+ start_wc = ((start_elem->type == SB_CHAR || start_elem->type == COLL_SYM)
+ ? __btowc (start_ch) : start_elem->opr.wch);
+ end_wc = ((end_elem->type == SB_CHAR || end_elem->type == COLL_SYM)
+ ? __btowc (end_ch) : end_elem->opr.wch);
+#endif
+ if (start_wc == WEOF || end_wc == WEOF)
+ return REG_ECOLLATE;
+ cmp_buf[0] = start_wc;
+ cmp_buf[4] = end_wc;
+ if (wcscoll (cmp_buf, cmp_buf + 4) > 0)
+ return REG_ERANGE;
+
+ /* Got valid collation sequence values, add them as a new entry.
+ However, for !_LIBC we have no collation elements: if the
+ character set is single byte, the single byte character set
+ that we build below suffices. parse_bracket_exp passes
+ no MBCSET if dfa->mb_cur_max == 1. */
+ if (mbcset)
+ {
+ /* Check the space of the arrays. */
+ if (BE (*range_alloc == mbcset->nranges, 0))
+ {
+ /* There is not enough space, need realloc. */
+ wchar_t *new_array_start, *new_array_end;
+ int new_nranges;
+
+ /* +1 in case of mbcset->nranges is 0. */
+ new_nranges = 2 * mbcset->nranges + 1;
+ /* Use realloc since mbcset->range_starts and mbcset->range_ends
+ are NULL if *range_alloc == 0. */
+ new_array_start = re_realloc (mbcset->range_starts, wchar_t,
+ new_nranges);
+ new_array_end = re_realloc (mbcset->range_ends, wchar_t,
+ new_nranges);
+
+ if (BE (new_array_start == NULL || new_array_end == NULL, 0))
+ return REG_ESPACE;
+
+ mbcset->range_starts = new_array_start;
+ mbcset->range_ends = new_array_end;
+ *range_alloc = new_nranges;
+ }
+
+ mbcset->range_starts[mbcset->nranges] = start_wc;
+ mbcset->range_ends[mbcset->nranges++] = end_wc;
+ }
+
+ /* Build the table for single byte characters. */
+ for (wc = 0; wc < SBC_MAX; ++wc)
+ {
+ cmp_buf[2] = wc;
+ if (wcscoll (cmp_buf, cmp_buf + 2) <= 0
+ && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0)
+ bitset_set (sbcset, wc);
+ }
+ }
+# else /* not RE_ENABLE_I18N */
+ {
+ unsigned int ch;
+ start_ch = ((start_elem->type == SB_CHAR ) ? start_elem->opr.ch
+ : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0]
+ : 0));
+ end_ch = ((end_elem->type == SB_CHAR ) ? end_elem->opr.ch
+ : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0]
+ : 0));
+ if (start_ch > end_ch)
+ return REG_ERANGE;
+ /* Build the table for single byte characters. */
+ for (ch = 0; ch < SBC_MAX; ++ch)
+ if (start_ch <= ch && ch <= end_ch)
+ bitset_set (sbcset, ch);
+ }
+# endif /* not RE_ENABLE_I18N */
+ return REG_NOERROR;
+}
+#endif /* not _LIBC */
+
+#ifndef _LIBC
+/* Helper function for parse_bracket_exp only used in case of NOT _LIBC..
+ Build the collating element which is represented by NAME.
+ The result are written to MBCSET and SBCSET.
+ COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a
+ pointer argument since we may update it. */
+
+static reg_errcode_t
+internal_function
+# ifdef RE_ENABLE_I18N
+build_collating_symbol (bitset_t sbcset, re_charset_t *mbcset,
+ int *coll_sym_alloc, const unsigned char *name)
+# else /* not RE_ENABLE_I18N */
+build_collating_symbol (bitset_t sbcset, const unsigned char *name)
+# endif /* not RE_ENABLE_I18N */
+{
+ size_t name_len = strlen ((const char *) name);
+ if (BE (name_len != 1, 0))
+ return REG_ECOLLATE;
+ else
+ {
+ bitset_set (sbcset, name[0]);
+ return REG_NOERROR;
+ }
+}
+#endif /* not _LIBC */
+
+/* This function parse bracket expression like "[abc]", "[a-c]",
+ "[[.a-a.]]" etc. */
+
+static bin_tree_t *
+parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, re_token_t *token,
+ reg_syntax_t syntax, reg_errcode_t *err)
+{
+#ifdef _LIBC
+ const unsigned char *collseqmb;
+ const char *collseqwc;
+ uint32_t nrules;
+ int32_t table_size;
+ const int32_t *symb_table;
+ const unsigned char *extra;
+
+ /* Local function for parse_bracket_exp used in _LIBC environement.
+ Seek the collating symbol entry correspondings to NAME.
+ Return the index of the symbol in the SYMB_TABLE. */
+
+ auto inline int32_t
+ __attribute ((always_inline))
+ seek_collating_symbol_entry (name, name_len)
+ const unsigned char *name;
+ size_t name_len;
+ {
+ int32_t hash = elem_hash ((const char *) name, name_len);
+ int32_t elem = hash % table_size;
+ if (symb_table[2 * elem] != 0)
+ {
+ int32_t second = hash % (table_size - 2) + 1;
+
+ do
+ {
+ /* First compare the hashing value. */
+ if (symb_table[2 * elem] == hash
+ /* Compare the length of the name. */
+ && name_len == extra[symb_table[2 * elem + 1]]
+ /* Compare the name. */
+ && memcmp (name, &extra[symb_table[2 * elem + 1] + 1],
+ name_len) == 0)
+ {
+ /* Yep, this is the entry. */
+ break;
+ }
+
+ /* Next entry. */
+ elem += second;
+ }
+ while (symb_table[2 * elem] != 0);
+ }
+ return elem;
+ }
+
+ /* Local function for parse_bracket_exp used in _LIBC environment.
+ Look up the collation sequence value of BR_ELEM.
+ Return the value if succeeded, UINT_MAX otherwise. */
+
+ auto inline unsigned int
+ __attribute ((always_inline))
+ lookup_collation_sequence_value (br_elem)
+ bracket_elem_t *br_elem;
+ {
+ if (br_elem->type == SB_CHAR)
+ {
+ /*
+ if (MB_CUR_MAX == 1)
+ */
+ if (nrules == 0)
+ return collseqmb[br_elem->opr.ch];
+ else
+ {
+ wint_t wc = __btowc (br_elem->opr.ch);
+ return __collseq_table_lookup (collseqwc, wc);
+ }
+ }
+ else if (br_elem->type == MB_CHAR)
+ {
+ if (nrules != 0)
+ return __collseq_table_lookup (collseqwc, br_elem->opr.wch);
+ }
+ else if (br_elem->type == COLL_SYM)
+ {
+ size_t sym_name_len = strlen ((char *) br_elem->opr.name);
+ if (nrules != 0)
+ {
+ int32_t elem, idx;
+ elem = seek_collating_symbol_entry (br_elem->opr.name,
+ sym_name_len);
+ if (symb_table[2 * elem] != 0)
+ {
+ /* We found the entry. */
+ idx = symb_table[2 * elem + 1];
+ /* Skip the name of collating element name. */
+ idx += 1 + extra[idx];
+ /* Skip the byte sequence of the collating element. */
+ idx += 1 + extra[idx];
+ /* Adjust for the alignment. */
+ idx = (idx + 3) & ~3;
+ /* Skip the multibyte collation sequence value. */
+ idx += sizeof (unsigned int);
+ /* Skip the wide char sequence of the collating element. */
+ idx += sizeof (unsigned int) *
+ (1 + *(unsigned int *) (extra + idx));
+ /* Return the collation sequence value. */
+ return *(unsigned int *) (extra + idx);
+ }
+ else if (symb_table[2 * elem] == 0 && sym_name_len == 1)
+ {
+ /* No valid character. Match it as a single byte
+ character. */
+ return collseqmb[br_elem->opr.name[0]];
+ }
+ }
+ else if (sym_name_len == 1)
+ return collseqmb[br_elem->opr.name[0]];
+ }
+ return UINT_MAX;
+ }
+
+ /* Local function for parse_bracket_exp used in _LIBC environement.
+ Build the range expression which starts from START_ELEM, and ends
+ at END_ELEM. The result are written to MBCSET and SBCSET.
+ RANGE_ALLOC is the allocated size of mbcset->range_starts, and
+ mbcset->range_ends, is a pointer argument sinse we may
+ update it. */
+
+ auto inline reg_errcode_t
+ __attribute ((always_inline))
+ build_range_exp (sbcset, mbcset, range_alloc, start_elem, end_elem)
+ re_charset_t *mbcset;
+ int *range_alloc;
+ bitset_t sbcset;
+ bracket_elem_t *start_elem, *end_elem;
+ {
+ unsigned int ch;
+ uint32_t start_collseq;
+ uint32_t end_collseq;
+
+ /* Equivalence Classes and Character Classes can't be a range
+ start/end. */
+ if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS
+ || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS,
+ 0))
+ return REG_ERANGE;
+
+ start_collseq = lookup_collation_sequence_value (start_elem);
+ end_collseq = lookup_collation_sequence_value (end_elem);
+ /* Check start/end collation sequence values. */
+ if (BE (start_collseq == UINT_MAX || end_collseq == UINT_MAX, 0))
+ return REG_ECOLLATE;
+ if (BE ((syntax & RE_NO_EMPTY_RANGES) && start_collseq > end_collseq, 0))
+ return REG_ERANGE;
+
+ /* Got valid collation sequence values, add them as a new entry.
+ However, if we have no collation elements, and the character set
+ is single byte, the single byte character set that we
+ build below suffices. */
+ if (nrules > 0 || dfa->mb_cur_max > 1)
+ {
+ /* Check the space of the arrays. */
+ if (BE (*range_alloc == mbcset->nranges, 0))
+ {
+ /* There is not enough space, need realloc. */
+ uint32_t *new_array_start;
+ uint32_t *new_array_end;
+ int new_nranges;
+
+ /* +1 in case of mbcset->nranges is 0. */
+ new_nranges = 2 * mbcset->nranges + 1;
+ new_array_start = re_realloc (mbcset->range_starts, uint32_t,
+ new_nranges);
+ new_array_end = re_realloc (mbcset->range_ends, uint32_t,
+ new_nranges);
+
+ if (BE (new_array_start == NULL || new_array_end == NULL, 0))
+ return REG_ESPACE;
+
+ mbcset->range_starts = new_array_start;
+ mbcset->range_ends = new_array_end;
+ *range_alloc = new_nranges;
+ }
+
+ mbcset->range_starts[mbcset->nranges] = start_collseq;
+ mbcset->range_ends[mbcset->nranges++] = end_collseq;
+ }
+
+ /* Build the table for single byte characters. */
+ for (ch = 0; ch < SBC_MAX; ch++)
+ {
+ uint32_t ch_collseq;
+ /*
+ if (MB_CUR_MAX == 1)
+ */
+ if (nrules == 0)
+ ch_collseq = collseqmb[ch];
+ else
+ ch_collseq = __collseq_table_lookup (collseqwc, __btowc (ch));
+ if (start_collseq <= ch_collseq && ch_collseq <= end_collseq)
+ bitset_set (sbcset, ch);
+ }
+ return REG_NOERROR;
+ }
+
+ /* Local function for parse_bracket_exp used in _LIBC environement.
+ Build the collating element which is represented by NAME.
+ The result are written to MBCSET and SBCSET.
+ COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a
+ pointer argument sinse we may update it. */
+
+ auto inline reg_errcode_t
+ __attribute ((always_inline))
+ build_collating_symbol (sbcset, mbcset, coll_sym_alloc, name)
+ re_charset_t *mbcset;
+ int *coll_sym_alloc;
+ bitset_t sbcset;
+ const unsigned char *name;
+ {
+ int32_t elem, idx;
+ size_t name_len = strlen ((const char *) name);
+ if (nrules != 0)
+ {
+ elem = seek_collating_symbol_entry (name, name_len);
+ if (symb_table[2 * elem] != 0)
+ {
+ /* We found the entry. */
+ idx = symb_table[2 * elem + 1];
+ /* Skip the name of collating element name. */
+ idx += 1 + extra[idx];
+ }
+ else if (symb_table[2 * elem] == 0 && name_len == 1)
+ {
+ /* No valid character, treat it as a normal
+ character. */
+ bitset_set (sbcset, name[0]);
+ return REG_NOERROR;
+ }
+ else
+ return REG_ECOLLATE;
+
+ /* Got valid collation sequence, add it as a new entry. */
+ /* Check the space of the arrays. */
+ if (BE (*coll_sym_alloc == mbcset->ncoll_syms, 0))
+ {
+ /* Not enough, realloc it. */
+ /* +1 in case of mbcset->ncoll_syms is 0. */
+ int new_coll_sym_alloc = 2 * mbcset->ncoll_syms + 1;
+ /* Use realloc since mbcset->coll_syms is NULL
+ if *alloc == 0. */
+ int32_t *new_coll_syms = re_realloc (mbcset->coll_syms, int32_t,
+ new_coll_sym_alloc);
+ if (BE (new_coll_syms == NULL, 0))
+ return REG_ESPACE;
+ mbcset->coll_syms = new_coll_syms;
+ *coll_sym_alloc = new_coll_sym_alloc;
+ }
+ mbcset->coll_syms[mbcset->ncoll_syms++] = idx;
+ return REG_NOERROR;
+ }
+ else
+ {
+ if (BE (name_len != 1, 0))
+ return REG_ECOLLATE;
+ else
+ {
+ bitset_set (sbcset, name[0]);
+ return REG_NOERROR;
+ }
+ }
+ }
+#endif
+
+ re_token_t br_token;
+ re_bitset_ptr_t sbcset;
+#ifdef RE_ENABLE_I18N
+ re_charset_t *mbcset;
+ int coll_sym_alloc = 0, range_alloc = 0, mbchar_alloc = 0;
+ int equiv_class_alloc = 0, char_class_alloc = 0;
+#endif /* not RE_ENABLE_I18N */
+ int non_match = 0;
+ bin_tree_t *work_tree;
+ int token_len;
+ int first_round = 1;
+#ifdef _LIBC
+ collseqmb = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB);
+ nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+ if (nrules)
+ {
+ /*
+ if (MB_CUR_MAX > 1)
+ */
+ collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC);
+ table_size = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_SYMB_HASH_SIZEMB);
+ symb_table = (const int32_t *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_SYMB_TABLEMB);
+ extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_SYMB_EXTRAMB);
+ }
+#endif
+ sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
+#ifdef RE_ENABLE_I18N
+ mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1);
+#endif /* RE_ENABLE_I18N */
+#ifdef RE_ENABLE_I18N
+ if (BE (sbcset == NULL || mbcset == NULL, 0))
+#else
+ if (BE (sbcset == NULL, 0))
+#endif /* RE_ENABLE_I18N */
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+
+ token_len = peek_token_bracket (token, regexp, syntax);
+ if (BE (token->type == END_OF_RE, 0))
+ {
+ *err = REG_BADPAT;
+ goto parse_bracket_exp_free_return;
+ }
+ if (token->type == OP_NON_MATCH_LIST)
+ {
+#ifdef RE_ENABLE_I18N
+ mbcset->non_match = 1;
+#endif /* not RE_ENABLE_I18N */
+ non_match = 1;
+ if (syntax & RE_HAT_LISTS_NOT_NEWLINE)
+ bitset_set (sbcset, '\n');
+ re_string_skip_bytes (regexp, token_len); /* Skip a token. */
+ token_len = peek_token_bracket (token, regexp, syntax);
+ if (BE (token->type == END_OF_RE, 0))
+ {
+ *err = REG_BADPAT;
+ goto parse_bracket_exp_free_return;
+ }
+ }
+
+ /* We treat the first ']' as a normal character. */
+ if (token->type == OP_CLOSE_BRACKET)
+ token->type = CHARACTER;
+
+ while (1)
+ {
+ bracket_elem_t start_elem, end_elem;
+ unsigned char start_name_buf[BRACKET_NAME_BUF_SIZE];
+ unsigned char end_name_buf[BRACKET_NAME_BUF_SIZE];
+ reg_errcode_t ret;
+ int token_len2 = 0, is_range_exp = 0;
+ re_token_t token2;
+
+ start_elem.opr.name = start_name_buf;
+ ret = parse_bracket_element (&start_elem, regexp, token, token_len, dfa,
+ syntax, first_round);
+ if (BE (ret != REG_NOERROR, 0))
+ {
+ *err = ret;
+ goto parse_bracket_exp_free_return;
+ }
+ first_round = 0;
+
+ /* Get information about the next token. We need it in any case. */
+ token_len = peek_token_bracket (token, regexp, syntax);
+
+ /* Do not check for ranges if we know they are not allowed. */
+ if (start_elem.type != CHAR_CLASS && start_elem.type != EQUIV_CLASS)
+ {
+ if (BE (token->type == END_OF_RE, 0))
+ {
+ *err = REG_EBRACK;
+ goto parse_bracket_exp_free_return;
+ }
+ if (token->type == OP_CHARSET_RANGE)
+ {
+ re_string_skip_bytes (regexp, token_len); /* Skip '-'. */
+ token_len2 = peek_token_bracket (&token2, regexp, syntax);
+ if (BE (token2.type == END_OF_RE, 0))
+ {
+ *err = REG_EBRACK;
+ goto parse_bracket_exp_free_return;
+ }
+ if (token2.type == OP_CLOSE_BRACKET)
+ {
+ /* We treat the last '-' as a normal character. */
+ re_string_skip_bytes (regexp, -token_len);
+ token->type = CHARACTER;
+ }
+ else
+ is_range_exp = 1;
+ }
+ }
+
+ if (is_range_exp == 1)
+ {
+ end_elem.opr.name = end_name_buf;
+ ret = parse_bracket_element (&end_elem, regexp, &token2, token_len2,
+ dfa, syntax, 1);
+ if (BE (ret != REG_NOERROR, 0))
+ {
+ *err = ret;
+ goto parse_bracket_exp_free_return;
+ }
+
+ token_len = peek_token_bracket (token, regexp, syntax);
+
+#ifdef _LIBC
+ *err = build_range_exp (sbcset, mbcset, &range_alloc,
+ &start_elem, &end_elem);
+#else
+# ifdef RE_ENABLE_I18N
+ *err = build_range_exp (sbcset,
+ dfa->mb_cur_max > 1 ? mbcset : NULL,
+ &range_alloc, &start_elem, &end_elem);
+# else
+ *err = build_range_exp (sbcset, &start_elem, &end_elem);
+# endif
+#endif /* RE_ENABLE_I18N */
+ if (BE (*err != REG_NOERROR, 0))
+ goto parse_bracket_exp_free_return;
+ }
+ else
+ {
+ switch (start_elem.type)
+ {
+ case SB_CHAR:
+ bitset_set (sbcset, start_elem.opr.ch);
+ break;
+#ifdef RE_ENABLE_I18N
+ case MB_CHAR:
+ /* Check whether the array has enough space. */
+ if (BE (mbchar_alloc == mbcset->nmbchars, 0))
+ {
+ wchar_t *new_mbchars;
+ /* Not enough, realloc it. */
+ /* +1 in case of mbcset->nmbchars is 0. */
+ mbchar_alloc = 2 * mbcset->nmbchars + 1;
+ /* Use realloc since array is NULL if *alloc == 0. */
+ new_mbchars = re_realloc (mbcset->mbchars, wchar_t,
+ mbchar_alloc);
+ if (BE (new_mbchars == NULL, 0))
+ goto parse_bracket_exp_espace;
+ mbcset->mbchars = new_mbchars;
+ }
+ mbcset->mbchars[mbcset->nmbchars++] = start_elem.opr.wch;
+ break;
+#endif /* RE_ENABLE_I18N */
+ case EQUIV_CLASS:
+ *err = build_equiv_class (sbcset,
+#ifdef RE_ENABLE_I18N
+ mbcset, &equiv_class_alloc,
+#endif /* RE_ENABLE_I18N */
+ start_elem.opr.name);
+ if (BE (*err != REG_NOERROR, 0))
+ goto parse_bracket_exp_free_return;
+ break;
+ case COLL_SYM:
+ *err = build_collating_symbol (sbcset,
+#ifdef RE_ENABLE_I18N
+ mbcset, &coll_sym_alloc,
+#endif /* RE_ENABLE_I18N */
+ start_elem.opr.name);
+ if (BE (*err != REG_NOERROR, 0))
+ goto parse_bracket_exp_free_return;
+ break;
+ case CHAR_CLASS:
+ *err = build_charclass (regexp->trans, sbcset,
+#ifdef RE_ENABLE_I18N
+ mbcset, &char_class_alloc,
+#endif /* RE_ENABLE_I18N */
+ (const char *) start_elem.opr.name, syntax);
+ if (BE (*err != REG_NOERROR, 0))
+ goto parse_bracket_exp_free_return;
+ break;
+ default:
+ assert (0);
+ break;
+ }
+ }
+ if (BE (token->type == END_OF_RE, 0))
+ {
+ *err = REG_EBRACK;
+ goto parse_bracket_exp_free_return;
+ }
+ if (token->type == OP_CLOSE_BRACKET)
+ break;
+ }
+
+ re_string_skip_bytes (regexp, token_len); /* Skip a token. */
+
+ /* If it is non-matching list. */
+ if (non_match)
+ bitset_not (sbcset);
+
+#ifdef RE_ENABLE_I18N
+ /* Ensure only single byte characters are set. */
+ if (dfa->mb_cur_max > 1)
+ bitset_mask (sbcset, dfa->sb_char);
+
+ if (mbcset->nmbchars || mbcset->ncoll_syms || mbcset->nequiv_classes
+ || mbcset->nranges || (dfa->mb_cur_max > 1 && (mbcset->nchar_classes
+ || mbcset->non_match)))
+ {
+ bin_tree_t *mbc_tree;
+ int sbc_idx;
+ /* Build a tree for complex bracket. */
+ dfa->has_mb_node = 1;
+ br_token.type = COMPLEX_BRACKET;
+ br_token.opr.mbcset = mbcset;
+ mbc_tree = create_token_tree (dfa, NULL, NULL, &br_token);
+ if (BE (mbc_tree == NULL, 0))
+ goto parse_bracket_exp_espace;
+ for (sbc_idx = 0; sbc_idx < BITSET_WORDS; ++sbc_idx)
+ if (sbcset[sbc_idx])
+ break;
+ /* If there are no bits set in sbcset, there is no point
+ of having both SIMPLE_BRACKET and COMPLEX_BRACKET. */
+ if (sbc_idx < BITSET_WORDS)
+ {
+ /* Build a tree for simple bracket. */
+ br_token.type = SIMPLE_BRACKET;
+ br_token.opr.sbcset = sbcset;
+ work_tree = create_token_tree (dfa, NULL, NULL, &br_token);
+ if (BE (work_tree == NULL, 0))
+ goto parse_bracket_exp_espace;
+
+ /* Then join them by ALT node. */
+ work_tree = create_tree (dfa, work_tree, mbc_tree, OP_ALT);
+ if (BE (work_tree == NULL, 0))
+ goto parse_bracket_exp_espace;
+ }
+ else
+ {
+ re_free (sbcset);
+ work_tree = mbc_tree;
+ }
+ }
+ else
+#endif /* not RE_ENABLE_I18N */
+ {
+#ifdef RE_ENABLE_I18N
+ free_charset (mbcset);
+#endif
+ /* Build a tree for simple bracket. */
+ br_token.type = SIMPLE_BRACKET;
+ br_token.opr.sbcset = sbcset;
+ work_tree = create_token_tree (dfa, NULL, NULL, &br_token);
+ if (BE (work_tree == NULL, 0))
+ goto parse_bracket_exp_espace;
+ }
+ return work_tree;
+
+ parse_bracket_exp_espace:
+ *err = REG_ESPACE;
+ parse_bracket_exp_free_return:
+ re_free (sbcset);
+#ifdef RE_ENABLE_I18N
+ free_charset (mbcset);
+#endif /* RE_ENABLE_I18N */
+ return NULL;
+}
+
+/* Parse an element in the bracket expression. */
+
+static reg_errcode_t
+parse_bracket_element (bracket_elem_t *elem, re_string_t *regexp,
+ re_token_t *token, int token_len, re_dfa_t *dfa,
+ reg_syntax_t syntax, int accept_hyphen)
+{
+#ifdef RE_ENABLE_I18N
+ int cur_char_size;
+ cur_char_size = re_string_char_size_at (regexp, re_string_cur_idx (regexp));
+ if (cur_char_size > 1)
+ {
+ elem->type = MB_CHAR;
+ elem->opr.wch = re_string_wchar_at (regexp, re_string_cur_idx (regexp));
+ re_string_skip_bytes (regexp, cur_char_size);
+ return REG_NOERROR;
+ }
+#endif /* RE_ENABLE_I18N */
+ re_string_skip_bytes (regexp, token_len); /* Skip a token. */
+ if (token->type == OP_OPEN_COLL_ELEM || token->type == OP_OPEN_CHAR_CLASS
+ || token->type == OP_OPEN_EQUIV_CLASS)
+ return parse_bracket_symbol (elem, regexp, token);
+ if (BE (token->type == OP_CHARSET_RANGE, 0) && !accept_hyphen)
+ {
+ /* A '-' must only appear as anything but a range indicator before
+ the closing bracket. Everything else is an error. */
+ re_token_t token2;
+ (void) peek_token_bracket (&token2, regexp, syntax);
+ if (token2.type != OP_CLOSE_BRACKET)
+ /* The actual error value is not standardized since this whole
+ case is undefined. But ERANGE makes good sense. */
+ return REG_ERANGE;
+ }
+ elem->type = SB_CHAR;
+ elem->opr.ch = token->opr.c;
+ return REG_NOERROR;
+}
+
+/* Parse a bracket symbol in the bracket expression. Bracket symbols are
+ such as [:<character_class>:], [.<collating_element>.], and
+ [=<equivalent_class>=]. */
+
+static reg_errcode_t
+parse_bracket_symbol (bracket_elem_t *elem, re_string_t *regexp,
+ re_token_t *token)
+{
+ unsigned char ch, delim = token->opr.c;
+ int i = 0;
+ if (re_string_eoi(regexp))
+ return REG_EBRACK;
+ for (;; ++i)
+ {
+ if (i >= BRACKET_NAME_BUF_SIZE)
+ return REG_EBRACK;
+ if (token->type == OP_OPEN_CHAR_CLASS)
+ ch = re_string_fetch_byte_case (regexp);
+ else
+ ch = re_string_fetch_byte (regexp);
+ if (re_string_eoi(regexp))
+ return REG_EBRACK;
+ if (ch == delim && re_string_peek_byte (regexp, 0) == ']')
+ break;
+ elem->opr.name[i] = ch;
+ }
+ re_string_skip_bytes (regexp, 1);
+ elem->opr.name[i] = '\0';
+ switch (token->type)
+ {
+ case OP_OPEN_COLL_ELEM:
+ elem->type = COLL_SYM;
+ break;
+ case OP_OPEN_EQUIV_CLASS:
+ elem->type = EQUIV_CLASS;
+ break;
+ case OP_OPEN_CHAR_CLASS:
+ elem->type = CHAR_CLASS;
+ break;
+ default:
+ break;
+ }
+ return REG_NOERROR;
+}
+
+ /* Helper function for parse_bracket_exp.
+ Build the equivalence class which is represented by NAME.
+ The result are written to MBCSET and SBCSET.
+ EQUIV_CLASS_ALLOC is the allocated size of mbcset->equiv_classes,
+ is a pointer argument sinse we may update it. */
+
+static reg_errcode_t
+#ifdef RE_ENABLE_I18N
+build_equiv_class (bitset_t sbcset, re_charset_t *mbcset,
+ int *equiv_class_alloc, const unsigned char *name)
+#else /* not RE_ENABLE_I18N */
+build_equiv_class (bitset_t sbcset, const unsigned char *name)
+#endif /* not RE_ENABLE_I18N */
+{
+#ifdef _LIBC
+ uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+ if (nrules != 0)
+ {
+ const int32_t *table, *indirect;
+ const unsigned char *weights, *extra, *cp;
+ unsigned char char_buf[2];
+ int32_t idx1, idx2;
+ unsigned int ch;
+ size_t len;
+ /* This #include defines a local function! */
+# include <locale/weight.h>
+ /* Calculate the index for equivalence class. */
+ cp = name;
+ table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+ weights = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_WEIGHTMB);
+ extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_EXTRAMB);
+ indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_INDIRECTMB);
+ idx1 = findidx (&cp);
+ if (BE (idx1 == 0 || cp < name + strlen ((const char *) name), 0))
+ /* This isn't a valid character. */
+ return REG_ECOLLATE;
+
+ /* Build single byte matcing table for this equivalence class. */
+ char_buf[1] = (unsigned char) '\0';
+ len = weights[idx1 & 0xffffff];
+ for (ch = 0; ch < SBC_MAX; ++ch)
+ {
+ char_buf[0] = ch;
+ cp = char_buf;
+ idx2 = findidx (&cp);
+/*
+ idx2 = table[ch];
+*/
+ if (idx2 == 0)
+ /* This isn't a valid character. */
+ continue;
+ /* Compare only if the length matches and the collation rule
+ index is the same. */
+ if (len == weights[idx2 & 0xffffff] && (idx1 >> 24) == (idx2 >> 24))
+ {
+ int cnt = 0;
+
+ while (cnt <= len &&
+ weights[(idx1 & 0xffffff) + 1 + cnt]
+ == weights[(idx2 & 0xffffff) + 1 + cnt])
+ ++cnt;
+
+ if (cnt > len)
+ bitset_set (sbcset, ch);
+ }
+ }
+ /* Check whether the array has enough space. */
+ if (BE (*equiv_class_alloc == mbcset->nequiv_classes, 0))
+ {
+ /* Not enough, realloc it. */
+ /* +1 in case of mbcset->nequiv_classes is 0. */
+ int new_equiv_class_alloc = 2 * mbcset->nequiv_classes + 1;
+ /* Use realloc since the array is NULL if *alloc == 0. */
+ int32_t *new_equiv_classes = re_realloc (mbcset->equiv_classes,
+ int32_t,
+ new_equiv_class_alloc);
+ if (BE (new_equiv_classes == NULL, 0))
+ return REG_ESPACE;
+ mbcset->equiv_classes = new_equiv_classes;
+ *equiv_class_alloc = new_equiv_class_alloc;
+ }
+ mbcset->equiv_classes[mbcset->nequiv_classes++] = idx1;
+ }
+ else
+#endif /* _LIBC */
+ {
+ if (BE (strlen ((const char *) name) != 1, 0))
+ return REG_ECOLLATE;
+ bitset_set (sbcset, *name);
+ }
+ return REG_NOERROR;
+}
+
+ /* Helper function for parse_bracket_exp.
+ Build the character class which is represented by NAME.
+ The result are written to MBCSET and SBCSET.
+ CHAR_CLASS_ALLOC is the allocated size of mbcset->char_classes,
+ is a pointer argument sinse we may update it. */
+
+static reg_errcode_t
+#ifdef RE_ENABLE_I18N
+build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset,
+ re_charset_t *mbcset, int *char_class_alloc,
+ const char *class_name, reg_syntax_t syntax)
+#else /* not RE_ENABLE_I18N */
+build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset,
+ const char *class_name, reg_syntax_t syntax)
+#endif /* not RE_ENABLE_I18N */
+{
+ int i;
+
+ /* In case of REG_ICASE "upper" and "lower" match the both of
+ upper and lower cases. */
+ if ((syntax & RE_ICASE)
+ && (strcmp (class_name, "upper") == 0 || strcmp (class_name, "lower") == 0))
+ class_name = "alpha";
+
+#ifdef RE_ENABLE_I18N
+ /* Check the space of the arrays. */
+ if (BE (*char_class_alloc == mbcset->nchar_classes, 0))
+ {
+ /* Not enough, realloc it. */
+ /* +1 in case of mbcset->nchar_classes is 0. */
+ int new_char_class_alloc = 2 * mbcset->nchar_classes + 1;
+ /* Use realloc since array is NULL if *alloc == 0. */
+ wctype_t *new_char_classes = re_realloc (mbcset->char_classes, wctype_t,
+ new_char_class_alloc);
+ if (BE (new_char_classes == NULL, 0))
+ return REG_ESPACE;
+ mbcset->char_classes = new_char_classes;
+ *char_class_alloc = new_char_class_alloc;
+ }
+ mbcset->char_classes[mbcset->nchar_classes++] = __wctype (class_name);
+#endif /* RE_ENABLE_I18N */
+
+#define BUILD_CHARCLASS_LOOP(ctype_func) \
+ do { \
+ if (BE (trans != NULL, 0)) \
+ { \
+ for (i = 0; i < SBC_MAX; ++i) \
+ if (ctype_func (i)) \
+ bitset_set (sbcset, trans[i]); \
+ } \
+ else \
+ { \
+ for (i = 0; i < SBC_MAX; ++i) \
+ if (ctype_func (i)) \
+ bitset_set (sbcset, i); \
+ } \
+ } while (0)
+
+ if (strcmp (class_name, "alnum") == 0)
+ BUILD_CHARCLASS_LOOP (isalnum);
+ else if (strcmp (class_name, "cntrl") == 0)
+ BUILD_CHARCLASS_LOOP (iscntrl);
+ else if (strcmp (class_name, "lower") == 0)
+ BUILD_CHARCLASS_LOOP (islower);
+ else if (strcmp (class_name, "space") == 0)
+ BUILD_CHARCLASS_LOOP (isspace);
+ else if (strcmp (class_name, "alpha") == 0)
+ BUILD_CHARCLASS_LOOP (isalpha);
+ else if (strcmp (class_name, "digit") == 0)
+ BUILD_CHARCLASS_LOOP (isdigit);
+ else if (strcmp (class_name, "print") == 0)
+ BUILD_CHARCLASS_LOOP (isprint);
+ else if (strcmp (class_name, "upper") == 0)
+ BUILD_CHARCLASS_LOOP (isupper);
+ else if (strcmp (class_name, "blank") == 0)
+#ifndef GAWK
+ BUILD_CHARCLASS_LOOP (isblank);
+#else
+ /* see comments above */
+ BUILD_CHARCLASS_LOOP (is_blank);
+#endif
+ else if (strcmp (class_name, "graph") == 0)
+ BUILD_CHARCLASS_LOOP (isgraph);
+ else if (strcmp (class_name, "punct") == 0)
+ BUILD_CHARCLASS_LOOP (ispunct);
+ else if (strcmp (class_name, "xdigit") == 0)
+ BUILD_CHARCLASS_LOOP (isxdigit);
+ else
+ return REG_ECTYPE;
+
+ return REG_NOERROR;
+}
+
+static bin_tree_t *
+build_charclass_op (re_dfa_t *dfa, RE_TRANSLATE_TYPE trans,
+ const char *class_name,
+ const char *extra, int non_match,
+ reg_errcode_t *err)
+{
+ re_bitset_ptr_t sbcset;
+#ifdef RE_ENABLE_I18N
+ re_charset_t *mbcset;
+ int alloc = 0;
+#endif /* not RE_ENABLE_I18N */
+ reg_errcode_t ret;
+ re_token_t br_token;
+ bin_tree_t *tree;
+
+ sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
+#ifdef RE_ENABLE_I18N
+ mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1);
+#endif /* RE_ENABLE_I18N */
+
+#ifdef RE_ENABLE_I18N
+ if (BE (sbcset == NULL || mbcset == NULL, 0))
+#else /* not RE_ENABLE_I18N */
+ if (BE (sbcset == NULL, 0))
+#endif /* not RE_ENABLE_I18N */
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+
+ if (non_match)
+ {
+#ifdef RE_ENABLE_I18N
+ mbcset->non_match = 1;
+#endif /* not RE_ENABLE_I18N */
+ }
+
+ /* We don't care the syntax in this case. */
+ ret = build_charclass (trans, sbcset,
+#ifdef RE_ENABLE_I18N
+ mbcset, &alloc,
+#endif /* RE_ENABLE_I18N */
+ class_name, 0);
+
+ if (BE (ret != REG_NOERROR, 0))
+ {
+ re_free (sbcset);
+#ifdef RE_ENABLE_I18N
+ free_charset (mbcset);
+#endif /* RE_ENABLE_I18N */
+ *err = ret;
+ return NULL;
+ }
+ /* \w match '_' also. */
+ for (; *extra; extra++)
+ bitset_set (sbcset, *extra);
+
+ /* If it is non-matching list. */
+ if (non_match)
+ bitset_not (sbcset);
+
+#ifdef RE_ENABLE_I18N
+ /* Ensure only single byte characters are set. */
+ if (dfa->mb_cur_max > 1)
+ bitset_mask (sbcset, dfa->sb_char);
+#endif
+
+ /* Build a tree for simple bracket. */
+ br_token.type = SIMPLE_BRACKET;
+ br_token.opr.sbcset = sbcset;
+ tree = create_token_tree (dfa, NULL, NULL, &br_token);
+ if (BE (tree == NULL, 0))
+ goto build_word_op_espace;
+
+#ifdef RE_ENABLE_I18N
+ if (dfa->mb_cur_max > 1)
+ {
+ bin_tree_t *mbc_tree;
+ /* Build a tree for complex bracket. */
+ br_token.type = COMPLEX_BRACKET;
+ br_token.opr.mbcset = mbcset;
+ dfa->has_mb_node = 1;
+ mbc_tree = create_token_tree (dfa, NULL, NULL, &br_token);
+ if (BE (mbc_tree == NULL, 0))
+ goto build_word_op_espace;
+ /* Then join them by ALT node. */
+ tree = create_tree (dfa, tree, mbc_tree, OP_ALT);
+ if (BE (mbc_tree != NULL, 1))
+ return tree;
+ }
+ else
+ {
+ free_charset (mbcset);
+ return tree;
+ }
+#else /* not RE_ENABLE_I18N */
+ return tree;
+#endif /* not RE_ENABLE_I18N */
+
+ build_word_op_espace:
+ re_free (sbcset);
+#ifdef RE_ENABLE_I18N
+ free_charset (mbcset);
+#endif /* RE_ENABLE_I18N */
+ *err = REG_ESPACE;
+ return NULL;
+}
+
+/* This is intended for the expressions like "a{1,3}".
+ Fetch a number from `input', and return the number.
+ Return -1, if the number field is empty like "{,1}".
+ Return -2, If an error is occured. */
+
+static int
+fetch_number (re_string_t *input, re_token_t *token, reg_syntax_t syntax)
+{
+ int num = -1;
+ unsigned char c;
+ while (1)
+ {
+ fetch_token (token, input, syntax);
+ c = token->opr.c;
+ if (BE (token->type == END_OF_RE, 0))
+ return -2;
+ if (token->type == OP_CLOSE_DUP_NUM || c == ',')
+ break;
+ num = ((token->type != CHARACTER || c < '0' || '9' < c || num == -2)
+ ? -2 : ((num == -1) ? c - '0' : num * 10 + c - '0'));
+ num = (num > RE_DUP_MAX) ? -2 : num;
+ }
+ return num;
+}
+
+#ifdef RE_ENABLE_I18N
+static void
+free_charset (re_charset_t *cset)
+{
+ re_free (cset->mbchars);
+# ifdef _LIBC
+ re_free (cset->coll_syms);
+ re_free (cset->equiv_classes);
+ re_free (cset->range_starts);
+ re_free (cset->range_ends);
+# endif
+ re_free (cset->char_classes);
+ re_free (cset);
+}
+#endif /* RE_ENABLE_I18N */
+
+/* Functions for binary tree operation. */
+
+/* Create a tree node. */
+
+static bin_tree_t *
+create_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right,
+ re_token_type_t type)
+{
+ re_token_t t;
+ t.type = type;
+ return create_token_tree (dfa, left, right, &t);
+}
+
+static bin_tree_t *
+create_token_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right,
+ const re_token_t *token)
+{
+ bin_tree_t *tree;
+ if (BE (dfa->str_tree_storage_idx == BIN_TREE_STORAGE_SIZE, 0))
+ {
+ bin_tree_storage_t *storage = re_malloc (bin_tree_storage_t, 1);
+
+ if (storage == NULL)
+ return NULL;
+ storage->next = dfa->str_tree_storage;
+ dfa->str_tree_storage = storage;
+ dfa->str_tree_storage_idx = 0;
+ }
+ tree = &dfa->str_tree_storage->data[dfa->str_tree_storage_idx++];
+
+ tree->parent = NULL;
+ tree->left = left;
+ tree->right = right;
+ tree->token = *token;
+ tree->token.duplicated = 0;
+ tree->token.opt_subexp = 0;
+ tree->first = NULL;
+ tree->next = NULL;
+ tree->node_idx = -1;
+
+ if (left != NULL)
+ left->parent = tree;
+ if (right != NULL)
+ right->parent = tree;
+ return tree;
+}
+
+/* Mark the tree SRC as an optional subexpression.
+ To be called from preorder or postorder. */
+
+static reg_errcode_t
+mark_opt_subexp (void *extra, bin_tree_t *node)
+{
+ int idx = (int) (long) extra;
+ if (node->token.type == SUBEXP && node->token.opr.idx == idx)
+ node->token.opt_subexp = 1;
+
+ return REG_NOERROR;
+}
+
+/* Free the allocated memory inside NODE. */
+
+static void
+free_token (re_token_t *node)
+{
+#ifdef RE_ENABLE_I18N
+ if (node->type == COMPLEX_BRACKET && node->duplicated == 0)
+ free_charset (node->opr.mbcset);
+ else
+#endif /* RE_ENABLE_I18N */
+ if (node->type == SIMPLE_BRACKET && node->duplicated == 0)
+ re_free (node->opr.sbcset);
+}
+
+/* Worker function for tree walking. Free the allocated memory inside NODE
+ and its children. */
+
+static reg_errcode_t
+free_tree (void *extra, bin_tree_t *node)
+{
+ free_token (&node->token);
+ return REG_NOERROR;
+}
+
+
+/* Duplicate the node SRC, and return new node. This is a preorder
+ visit similar to the one implemented by the generic visitor, but
+ we need more infrastructure to maintain two parallel trees --- so,
+ it's easier to duplicate. */
+
+static bin_tree_t *
+duplicate_tree (const bin_tree_t *root, re_dfa_t *dfa)
+{
+ const bin_tree_t *node;
+ bin_tree_t *dup_root;
+ bin_tree_t **p_new = &dup_root, *dup_node = root->parent;
+
+ for (node = root; ; )
+ {
+ /* Create a new tree and link it back to the current parent. */
+ *p_new = create_token_tree (dfa, NULL, NULL, &node->token);
+ if (*p_new == NULL)
+ return NULL;
+ (*p_new)->parent = dup_node;
+ (*p_new)->token.duplicated = 1;
+ dup_node = *p_new;
+
+ /* Go to the left node, or up and to the right. */
+ if (node->left)
+ {
+ node = node->left;
+ p_new = &dup_node->left;
+ }
+ else
+ {
+ const bin_tree_t *prev = NULL;
+ while (node->right == prev || node->right == NULL)
+ {
+ prev = node;
+ node = node->parent;
+ dup_node = dup_node->parent;
+ if (!node)
+ return dup_root;
+ }
+ node = node->right;
+ p_new = &dup_node->right;
+ }
+ }
+}
diff --git a/deps/regex/regex.c b/deps/regex/regex.c
new file mode 100644
index 000000000..f9a8c9bf1
--- /dev/null
+++ b/deps/regex/regex.c
@@ -0,0 +1,85 @@
+/* Extended regular expression matching and search library.
+ Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA. */
+
+#include "config.h"
+
+/* Make sure noone compiles this code with a C++ compiler. */
+#ifdef __cplusplus
+# error "This is C code, use a C compiler"
+#endif
+
+#ifdef _LIBC
+/* We have to keep the namespace clean. */
+# define regfree(preg) __regfree (preg)
+# define regexec(pr, st, nm, pm, ef) __regexec (pr, st, nm, pm, ef)
+# define regcomp(preg, pattern, cflags) __regcomp (preg, pattern, cflags)
+# define regerror(errcode, preg, errbuf, errbuf_size) \
+ __regerror(errcode, preg, errbuf, errbuf_size)
+# define re_set_registers(bu, re, nu, st, en) \
+ __re_set_registers (bu, re, nu, st, en)
+# define re_match_2(bufp, string1, size1, string2, size2, pos, regs, stop) \
+ __re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
+# define re_match(bufp, string, size, pos, regs) \
+ __re_match (bufp, string, size, pos, regs)
+# define re_search(bufp, string, size, startpos, range, regs) \
+ __re_search (bufp, string, size, startpos, range, regs)
+# define re_compile_pattern(pattern, length, bufp) \
+ __re_compile_pattern (pattern, length, bufp)
+# define re_set_syntax(syntax) __re_set_syntax (syntax)
+# define re_search_2(bufp, st1, s1, st2, s2, startpos, range, regs, stop) \
+ __re_search_2 (bufp, st1, s1, st2, s2, startpos, range, regs, stop)
+# define re_compile_fastmap(bufp) __re_compile_fastmap (bufp)
+
+# include "../locale/localeinfo.h"
+#endif
+
+#if defined (_MSC_VER)
+#include <stdio.h> /* for size_t */
+#endif
+
+/* On some systems, limits.h sets RE_DUP_MAX to a lower value than
+ GNU regex allows. Include it before <regex.h>, which correctly
+ #undefs RE_DUP_MAX and sets it to the right value. */
+#include <limits.h>
+
+#ifdef GAWK
+#undef alloca
+#define alloca alloca_is_bad_you_should_never_use_it
+#endif
+#include <regex.h>
+#include "regex_internal.h"
+
+#include "regex_internal.c"
+#ifdef GAWK
+#define bool int
+#define true (1)
+#define false (0)
+#endif
+#include "regcomp.c"
+#include "regexec.c"
+
+/* Binary backward compatibility. */
+#if _LIBC
+# include <shlib-compat.h>
+# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3)
+link_warning (re_max_failures, "the 're_max_failures' variable is obsolete and will go away.")
+int re_max_failures = 2000;
+# endif
+#endif
diff --git a/deps/regex/regex.h b/deps/regex/regex.h
new file mode 100644
index 000000000..61c968387
--- /dev/null
+++ b/deps/regex/regex.h
@@ -0,0 +1,582 @@
+#include <stdio.h>
+#include <stddef.h>
+
+/* Definitions for data structures and routines for the regular
+ expression library.
+ Copyright (C) 1985,1989-93,1995-98,2000,2001,2002,2003,2005,2006,2008
+ Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA. */
+
+#ifndef _REGEX_H
+#define _REGEX_H 1
+
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifndef _LIBC
+#define __USE_GNU 1
+#endif
+
+/* Allow the use in C++ code. */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The following two types have to be signed and unsigned integer type
+ wide enough to hold a value of a pointer. For most ANSI compilers
+ ptrdiff_t and size_t should be likely OK. Still size of these two
+ types is 2 for Microsoft C. Ugh... */
+typedef long int s_reg_t;
+typedef unsigned long int active_reg_t;
+
+/* The following bits are used to determine the regexp syntax we
+ recognize. The set/not-set meanings are chosen so that Emacs syntax
+ remains the value 0. The bits are given in alphabetical order, and
+ the definitions shifted by one from the previous bit; thus, when we
+ add or remove a bit, only one other definition need change. */
+typedef unsigned long int reg_syntax_t;
+
+#ifdef __USE_GNU
+/* If this bit is not set, then \ inside a bracket expression is literal.
+ If set, then such a \ quotes the following character. */
+# define RE_BACKSLASH_ESCAPE_IN_LISTS ((unsigned long int) 1)
+
+/* If this bit is not set, then + and ? are operators, and \+ and \? are
+ literals.
+ If set, then \+ and \? are operators and + and ? are literals. */
+# define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1)
+
+/* If this bit is set, then character classes are supported. They are:
+ [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:],
+ [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
+ If not set, then character classes are not supported. */
+# define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1)
+
+/* If this bit is set, then ^ and $ are always anchors (outside bracket
+ expressions, of course).
+ If this bit is not set, then it depends:
+ ^ is an anchor if it is at the beginning of a regular
+ expression or after an open-group or an alternation operator;
+ $ is an anchor if it is at the end of a regular expression, or
+ before a close-group or an alternation operator.
+
+ This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because
+ POSIX draft 11.2 says that * etc. in leading positions is undefined.
+ We already implemented a previous draft which made those constructs
+ invalid, though, so we haven't changed the code back. */
+# define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1)
+
+/* If this bit is set, then special characters are always special
+ regardless of where they are in the pattern.
+ If this bit is not set, then special characters are special only in
+ some contexts; otherwise they are ordinary. Specifically,
+ * + ? and intervals are only special when not after the beginning,
+ open-group, or alternation operator. */
+# define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1)
+
+/* If this bit is set, then *, +, ?, and { cannot be first in an re or
+ immediately after an alternation or begin-group operator. */
+# define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1)
+
+/* If this bit is set, then . matches newline.
+ If not set, then it doesn't. */
+# define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1)
+
+/* If this bit is set, then . doesn't match NUL.
+ If not set, then it does. */
+# define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1)
+
+/* If this bit is set, nonmatching lists [^...] do not match newline.
+ If not set, they do. */
+# define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1)
+
+/* If this bit is set, either \{...\} or {...} defines an
+ interval, depending on RE_NO_BK_BRACES.
+ If not set, \{, \}, {, and } are literals. */
+# define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1)
+
+/* If this bit is set, +, ? and | aren't recognized as operators.
+ If not set, they are. */
+# define RE_LIMITED_OPS (RE_INTERVALS << 1)
+
+/* If this bit is set, newline is an alternation operator.
+ If not set, newline is literal. */
+# define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1)
+
+/* If this bit is set, then `{...}' defines an interval, and \{ and \}
+ are literals.
+ If not set, then `\{...\}' defines an interval. */
+# define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1)
+
+/* If this bit is set, (...) defines a group, and \( and \) are literals.
+ If not set, \(...\) defines a group, and ( and ) are literals. */
+# define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1)
+
+/* If this bit is set, then \<digit> matches <digit>.
+ If not set, then \<digit> is a back-reference. */
+# define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1)
+
+/* If this bit is set, then | is an alternation operator, and \| is literal.
+ If not set, then \| is an alternation operator, and | is literal. */
+# define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1)
+
+/* If this bit is set, then an ending range point collating higher
+ than the starting range point, as in [z-a], is invalid.
+ If not set, then when ending range point collates higher than the
+ starting range point, the range is ignored. */
+# define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1)
+
+/* If this bit is set, then an unmatched ) is ordinary.
+ If not set, then an unmatched ) is invalid. */
+# define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1)
+
+/* If this bit is set, succeed as soon as we match the whole pattern,
+ without further backtracking. */
+# define RE_NO_POSIX_BACKTRACKING (RE_UNMATCHED_RIGHT_PAREN_ORD << 1)
+
+/* If this bit is set, do not process the GNU regex operators.
+ If not set, then the GNU regex operators are recognized. */
+# define RE_NO_GNU_OPS (RE_NO_POSIX_BACKTRACKING << 1)
+
+/* If this bit is set, a syntactically invalid interval is treated as
+ a string of ordinary characters. For example, the ERE 'a{1' is
+ treated as 'a\{1'. */
+# define RE_INVALID_INTERVAL_ORD (RE_NO_GNU_OPS << 1)
+
+/* If this bit is set, then ignore case when matching.
+ If not set, then case is significant. */
+# define RE_ICASE (RE_INVALID_INTERVAL_ORD << 1)
+
+/* This bit is used internally like RE_CONTEXT_INDEP_ANCHORS but only
+ for ^, because it is difficult to scan the regex backwards to find
+ whether ^ should be special. */
+# define RE_CARET_ANCHORS_HERE (RE_ICASE << 1)
+
+/* If this bit is set, then \{ cannot be first in an bre or
+ immediately after an alternation or begin-group operator. */
+# define RE_CONTEXT_INVALID_DUP (RE_CARET_ANCHORS_HERE << 1)
+
+/* If this bit is set, then no_sub will be set to 1 during
+ re_compile_pattern. */
+#define RE_NO_SUB (RE_CONTEXT_INVALID_DUP << 1)
+#endif
+
+/* This global variable defines the particular regexp syntax to use (for
+ some interfaces). When a regexp is compiled, the syntax used is
+ stored in the pattern buffer, so changing this does not affect
+ already-compiled regexps. */
+extern reg_syntax_t re_syntax_options;
+
+#ifdef __USE_GNU
+/* Define combinations of the above bits for the standard possibilities.
+ (The [[[ comments delimit what gets put into the Texinfo file, so
+ don't delete them!) */
+/* [[[begin syntaxes]]] */
+#define RE_SYNTAX_EMACS 0
+
+#define RE_SYNTAX_AWK \
+ (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \
+ | RE_NO_BK_PARENS | RE_NO_BK_REFS \
+ | RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \
+ | RE_DOT_NEWLINE | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_UNMATCHED_RIGHT_PAREN_ORD | RE_NO_GNU_OPS)
+
+#define RE_SYNTAX_GNU_AWK \
+ ((RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS \
+ | RE_INVALID_INTERVAL_ORD) \
+ & ~(RE_DOT_NOT_NULL | RE_CONTEXT_INDEP_OPS \
+ | RE_CONTEXT_INVALID_OPS ))
+
+#define RE_SYNTAX_POSIX_AWK \
+ (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS \
+ | RE_INTERVALS | RE_NO_GNU_OPS \
+ | RE_INVALID_INTERVAL_ORD)
+
+#define RE_SYNTAX_GREP \
+ (RE_BK_PLUS_QM | RE_CHAR_CLASSES \
+ | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \
+ | RE_NEWLINE_ALT)
+
+#define RE_SYNTAX_EGREP \
+ (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \
+ | RE_NEWLINE_ALT | RE_NO_BK_PARENS \
+ | RE_NO_BK_VBAR)
+
+#define RE_SYNTAX_POSIX_EGREP \
+ (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES \
+ | RE_INVALID_INTERVAL_ORD)
+
+/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */
+#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC
+
+#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC
+
+/* Syntax bits common to both basic and extended POSIX regex syntax. */
+#define _RE_SYNTAX_POSIX_COMMON \
+ (RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \
+ | RE_INTERVALS | RE_NO_EMPTY_RANGES)
+
+#define RE_SYNTAX_POSIX_BASIC \
+ (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM | RE_CONTEXT_INVALID_DUP)
+
+/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes
+ RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this
+ isn't minimal, since other operators, such as \`, aren't disabled. */
+#define RE_SYNTAX_POSIX_MINIMAL_BASIC \
+ (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS)
+
+#define RE_SYNTAX_POSIX_EXTENDED \
+ (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \
+ | RE_NO_BK_PARENS | RE_NO_BK_VBAR \
+ | RE_CONTEXT_INVALID_OPS | RE_UNMATCHED_RIGHT_PAREN_ORD)
+
+/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INDEP_OPS is
+ removed and RE_NO_BK_REFS is added. */
+#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \
+ (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \
+ | RE_NO_BK_PARENS | RE_NO_BK_REFS \
+ | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD)
+/* [[[end syntaxes]]] */
+
+/* Maximum number of duplicates an interval can allow. Some systems
+ (erroneously) define this in other header files, but we want our
+ value, so remove any previous define. */
+# ifdef RE_DUP_MAX
+# undef RE_DUP_MAX
+# endif
+/* If sizeof(int) == 2, then ((1 << 15) - 1) overflows. */
+# define RE_DUP_MAX (0x7fff)
+#endif
+
+
+/* POSIX `cflags' bits (i.e., information for `regcomp'). */
+
+/* If this bit is set, then use extended regular expression syntax.
+ If not set, then use basic regular expression syntax. */
+#define REG_EXTENDED 1
+
+/* If this bit is set, then ignore case when matching.
+ If not set, then case is significant. */
+#define REG_ICASE (REG_EXTENDED << 1)
+
+/* If this bit is set, then anchors do not match at newline
+ characters in the string.
+ If not set, then anchors do match at newlines. */
+#define REG_NEWLINE (REG_ICASE << 1)
+
+/* If this bit is set, then report only success or fail in regexec.
+ If not set, then returns differ between not matching and errors. */
+#define REG_NOSUB (REG_NEWLINE << 1)
+
+
+/* POSIX `eflags' bits (i.e., information for regexec). */
+
+/* If this bit is set, then the beginning-of-line operator doesn't match
+ the beginning of the string (presumably because it's not the
+ beginning of a line).
+ If not set, then the beginning-of-line operator does match the
+ beginning of the string. */
+#define REG_NOTBOL 1
+
+/* Like REG_NOTBOL, except for the end-of-line. */
+#define REG_NOTEOL (1 << 1)
+
+/* Use PMATCH[0] to delimit the start and end of the search in the
+ buffer. */
+#define REG_STARTEND (1 << 2)
+
+
+/* If any error codes are removed, changed, or added, update the
+ `re_error_msg' table in regex.c. */
+typedef enum
+{
+#if defined _XOPEN_SOURCE || defined __USE_XOPEN2K
+ REG_ENOSYS = -1, /* This will never happen for this implementation. */
+#endif
+
+ REG_NOERROR = 0, /* Success. */
+ REG_NOMATCH, /* Didn't find a match (for regexec). */
+
+ /* POSIX regcomp return error codes. (In the order listed in the
+ standard.) */
+ REG_BADPAT, /* Invalid pattern. */
+ REG_ECOLLATE, /* Inalid collating element. */
+ REG_ECTYPE, /* Invalid character class name. */
+ REG_EESCAPE, /* Trailing backslash. */
+ REG_ESUBREG, /* Invalid back reference. */
+ REG_EBRACK, /* Unmatched left bracket. */
+ REG_EPAREN, /* Parenthesis imbalance. */
+ REG_EBRACE, /* Unmatched \{. */
+ REG_BADBR, /* Invalid contents of \{\}. */
+ REG_ERANGE, /* Invalid range end. */
+ REG_ESPACE, /* Ran out of memory. */
+ REG_BADRPT, /* No preceding re for repetition op. */
+
+ /* Error codes we've added. */
+ REG_EEND, /* Premature end. */
+ REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */
+ REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */
+} reg_errcode_t;
+
+/* This data structure represents a compiled pattern. Before calling
+ the pattern compiler, the fields `buffer', `allocated', `fastmap',
+ `translate', and `no_sub' can be set. After the pattern has been
+ compiled, the `re_nsub' field is available. All other fields are
+ private to the regex routines. */
+
+#ifndef RE_TRANSLATE_TYPE
+# define __RE_TRANSLATE_TYPE unsigned char *
+# ifdef __USE_GNU
+# define RE_TRANSLATE_TYPE __RE_TRANSLATE_TYPE
+# endif
+#endif
+
+#ifdef __USE_GNU
+# define __REPB_PREFIX(name) name
+#else
+# define __REPB_PREFIX(name) __##name
+#endif
+
+struct re_pattern_buffer
+{
+ /* Space that holds the compiled pattern. It is declared as
+ `unsigned char *' because its elements are sometimes used as
+ array indexes. */
+ unsigned char *__REPB_PREFIX(buffer);
+
+ /* Number of bytes to which `buffer' points. */
+ unsigned long int __REPB_PREFIX(allocated);
+
+ /* Number of bytes actually used in `buffer'. */
+ unsigned long int __REPB_PREFIX(used);
+
+ /* Syntax setting with which the pattern was compiled. */
+ reg_syntax_t __REPB_PREFIX(syntax);
+
+ /* Pointer to a fastmap, if any, otherwise zero. re_search uses the
+ fastmap, if there is one, to skip over impossible starting points
+ for matches. */
+ char *__REPB_PREFIX(fastmap);
+
+ /* Either a translate table to apply to all characters before
+ comparing them, or zero for no translation. The translation is
+ applied to a pattern when it is compiled and to a string when it
+ is matched. */
+ __RE_TRANSLATE_TYPE __REPB_PREFIX(translate);
+
+ /* Number of subexpressions found by the compiler. */
+ size_t re_nsub;
+
+ /* Zero if this pattern cannot match the empty string, one else.
+ Well, in truth it's used only in `re_search_2', to see whether or
+ not we should use the fastmap, so we don't set this absolutely
+ perfectly; see `re_compile_fastmap' (the `duplicate' case). */
+ unsigned __REPB_PREFIX(can_be_null) : 1;
+
+ /* If REGS_UNALLOCATED, allocate space in the `regs' structure
+ for `max (RE_NREGS, re_nsub + 1)' groups.
+ If REGS_REALLOCATE, reallocate space if necessary.
+ If REGS_FIXED, use what's there. */
+#ifdef __USE_GNU
+# define REGS_UNALLOCATED 0
+# define REGS_REALLOCATE 1
+# define REGS_FIXED 2
+#endif
+ unsigned __REPB_PREFIX(regs_allocated) : 2;
+
+ /* Set to zero when `regex_compile' compiles a pattern; set to one
+ by `re_compile_fastmap' if it updates the fastmap. */
+ unsigned __REPB_PREFIX(fastmap_accurate) : 1;
+
+ /* If set, `re_match_2' does not return information about
+ subexpressions. */
+ unsigned __REPB_PREFIX(no_sub) : 1;
+
+ /* If set, a beginning-of-line anchor doesn't match at the beginning
+ of the string. */
+ unsigned __REPB_PREFIX(not_bol) : 1;
+
+ /* Similarly for an end-of-line anchor. */
+ unsigned __REPB_PREFIX(not_eol) : 1;
+
+ /* If true, an anchor at a newline matches. */
+ unsigned __REPB_PREFIX(newline_anchor) : 1;
+};
+
+typedef struct re_pattern_buffer regex_t;
+
+/* Type for byte offsets within the string. POSIX mandates this. */
+typedef int regoff_t;
+
+
+#ifdef __USE_GNU
+/* This is the structure we store register match data in. See
+ regex.texinfo for a full description of what registers match. */
+struct re_registers
+{
+ unsigned num_regs;
+ regoff_t *start;
+ regoff_t *end;
+};
+
+
+/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer,
+ `re_match_2' returns information about at least this many registers
+ the first time a `regs' structure is passed. */
+# ifndef RE_NREGS
+# define RE_NREGS 30
+# endif
+#endif
+
+
+/* POSIX specification for registers. Aside from the different names than
+ `re_registers', POSIX uses an array of structures, instead of a
+ structure of arrays. */
+typedef struct
+{
+ regoff_t rm_so; /* Byte offset from string's start to substring's start. */
+ regoff_t rm_eo; /* Byte offset from string's start to substring's end. */
+} regmatch_t;
+
+/* Declarations for routines. */
+
+#ifdef __USE_GNU
+/* Sets the current default syntax to SYNTAX, and return the old syntax.
+ You can also simply assign to the `re_syntax_options' variable. */
+extern reg_syntax_t re_set_syntax (reg_syntax_t __syntax);
+
+/* Compile the regular expression PATTERN, with length LENGTH
+ and syntax given by the global `re_syntax_options', into the buffer
+ BUFFER. Return NULL if successful, and an error string if not. */
+extern const char *re_compile_pattern (const char *__pattern, size_t __length,
+ struct re_pattern_buffer *__buffer);
+
+
+/* Compile a fastmap for the compiled pattern in BUFFER; used to
+ accelerate searches. Return 0 if successful and -2 if was an
+ internal error. */
+extern int re_compile_fastmap (struct re_pattern_buffer *__buffer);
+
+
+/* Search in the string STRING (with length LENGTH) for the pattern
+ compiled into BUFFER. Start searching at position START, for RANGE
+ characters. Return the starting position of the match, -1 for no
+ match, or -2 for an internal error. Also return register
+ information in REGS (if REGS and BUFFER->no_sub are nonzero). */
+extern int re_search (struct re_pattern_buffer *__buffer, const char *__cstring,
+ int __length, int __start, int __range,
+ struct re_registers *__regs);
+
+
+/* Like `re_search', but search in the concatenation of STRING1 and
+ STRING2. Also, stop searching at index START + STOP. */
+extern int re_search_2 (struct re_pattern_buffer *__buffer,
+ const char *__string1, int __length1,
+ const char *__string2, int __length2, int __start,
+ int __range, struct re_registers *__regs, int __stop);
+
+
+/* Like `re_search', but return how many characters in STRING the regexp
+ in BUFFER matched, starting at position START. */
+extern int re_match (struct re_pattern_buffer *__buffer, const char *__cstring,
+ int __length, int __start, struct re_registers *__regs);
+
+
+/* Relates to `re_match' as `re_search_2' relates to `re_search'. */
+extern int re_match_2 (struct re_pattern_buffer *__buffer,
+ const char *__string1, int __length1,
+ const char *__string2, int __length2, int __start,
+ struct re_registers *__regs, int __stop);
+
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+ ENDS. Subsequent matches using BUFFER and REGS will use this memory
+ for recording register information. STARTS and ENDS must be
+ allocated with malloc, and must each be at least `NUM_REGS * sizeof
+ (regoff_t)' bytes long.
+
+ If NUM_REGS == 0, then subsequent matches should allocate their own
+ register data.
+
+ Unless this function is called, the first search or match using
+ PATTERN_BUFFER will allocate its own register data, without
+ freeing the old data. */
+extern void re_set_registers (struct re_pattern_buffer *__buffer,
+ struct re_registers *__regs,
+ unsigned int __num_regs,
+ regoff_t *__starts, regoff_t *__ends);
+#endif /* Use GNU */
+
+#if defined _REGEX_RE_COMP || (defined _LIBC && defined __USE_BSD)
+# ifndef _CRAY
+/* 4.2 bsd compatibility. */
+extern char *re_comp (const char *);
+extern int re_exec (const char *);
+# endif
+#endif
+
+/* GCC 2.95 and later have "__restrict"; C99 compilers have
+ "restrict", and "configure" may have defined "restrict". */
+#ifndef __restrict
+# if ! (2 < __GNUC__ || (2 == __GNUC__ && 95 <= __GNUC_MINOR__))
+# if defined restrict || 199901L <= __STDC_VERSION__
+# define __restrict restrict
+# else
+# define __restrict
+# endif
+# endif
+#endif
+/* gcc 3.1 and up support the [restrict] syntax. */
+#ifndef __restrict_arr
+# if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) \
+ && !defined __GNUG__
+# define __restrict_arr __restrict
+# else
+# define __restrict_arr
+# endif
+#endif
+
+/* POSIX compatibility. */
+extern int regcomp (regex_t *__restrict __preg,
+ const char *__restrict __pattern,
+ int __cflags);
+
+extern int regexec (const regex_t *__restrict __preg,
+ const char *__restrict __cstring, size_t __nmatch,
+ regmatch_t __pmatch[__restrict_arr],
+ int __eflags);
+
+extern size_t regerror (int __errcode, const regex_t *__restrict __preg,
+ char *__restrict __errbuf, size_t __errbuf_size);
+
+extern void regfree (regex_t *__preg);
+
+
+#ifdef __cplusplus
+}
+#endif /* C++ */
+
+#endif /* regex.h */
diff --git a/deps/regex/regex_internal.c b/deps/regex/regex_internal.c
new file mode 100644
index 000000000..193854cf5
--- /dev/null
+++ b/deps/regex/regex_internal.c
@@ -0,0 +1,1744 @@
+/* Extended regular expression matching and search library.
+ Copyright (C) 2002-2006, 2010 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA. */
+
+static void re_string_construct_common (const char *str, int len,
+ re_string_t *pstr,
+ RE_TRANSLATE_TYPE trans, int icase,
+ const re_dfa_t *dfa) internal_function;
+static re_dfastate_t *create_ci_newstate (const re_dfa_t *dfa,
+ const re_node_set *nodes,
+ unsigned int hash) internal_function;
+static re_dfastate_t *create_cd_newstate (const re_dfa_t *dfa,
+ const re_node_set *nodes,
+ unsigned int context,
+ unsigned int hash) internal_function;
+
+#ifdef GAWK
+#undef MAX /* safety */
+static int
+MAX(size_t a, size_t b)
+{
+ return (a > b ? a : b);
+}
+#endif
+
+/* Functions for string operation. */
+
+/* This function allocate the buffers. It is necessary to call
+ re_string_reconstruct before using the object. */
+
+static reg_errcode_t
+internal_function
+re_string_allocate (re_string_t *pstr, const char *str, int len, int init_len,
+ RE_TRANSLATE_TYPE trans, int icase, const re_dfa_t *dfa)
+{
+ reg_errcode_t ret;
+ int init_buf_len;
+
+ /* Ensure at least one character fits into the buffers. */
+ if (init_len < dfa->mb_cur_max)
+ init_len = dfa->mb_cur_max;
+ init_buf_len = (len + 1 < init_len) ? len + 1: init_len;
+ re_string_construct_common (str, len, pstr, trans, icase, dfa);
+
+ ret = re_string_realloc_buffers (pstr, init_buf_len);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+
+ pstr->word_char = dfa->word_char;
+ pstr->word_ops_used = dfa->word_ops_used;
+ pstr->mbs = pstr->mbs_allocated ? pstr->mbs : (unsigned char *) str;
+ pstr->valid_len = (pstr->mbs_allocated || dfa->mb_cur_max > 1) ? 0 : len;
+ pstr->valid_raw_len = pstr->valid_len;
+ return REG_NOERROR;
+}
+
+/* This function allocate the buffers, and initialize them. */
+
+static reg_errcode_t
+internal_function
+re_string_construct (re_string_t *pstr, const char *str, int len,
+ RE_TRANSLATE_TYPE trans, int icase, const re_dfa_t *dfa)
+{
+ reg_errcode_t ret;
+ memset (pstr, '\0', sizeof (re_string_t));
+ re_string_construct_common (str, len, pstr, trans, icase, dfa);
+
+ if (len > 0)
+ {
+ ret = re_string_realloc_buffers (pstr, len + 1);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+ }
+ pstr->mbs = pstr->mbs_allocated ? pstr->mbs : (unsigned char *) str;
+
+ if (icase)
+ {
+#ifdef RE_ENABLE_I18N
+ if (dfa->mb_cur_max > 1)
+ {
+ while (1)
+ {
+ ret = build_wcs_upper_buffer (pstr);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+ if (pstr->valid_raw_len >= len)
+ break;
+ if (pstr->bufs_len > pstr->valid_len + dfa->mb_cur_max)
+ break;
+ ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+ }
+ }
+ else
+#endif /* RE_ENABLE_I18N */
+ build_upper_buffer (pstr);
+ }
+ else
+ {
+#ifdef RE_ENABLE_I18N
+ if (dfa->mb_cur_max > 1)
+ build_wcs_buffer (pstr);
+ else
+#endif /* RE_ENABLE_I18N */
+ {
+ if (trans != NULL)
+ re_string_translate_buffer (pstr);
+ else
+ {
+ pstr->valid_len = pstr->bufs_len;
+ pstr->valid_raw_len = pstr->bufs_len;
+ }
+ }
+ }
+
+ return REG_NOERROR;
+}
+
+/* Helper functions for re_string_allocate, and re_string_construct. */
+
+static reg_errcode_t
+internal_function
+re_string_realloc_buffers (re_string_t *pstr, int new_buf_len)
+{
+#ifdef RE_ENABLE_I18N
+ if (pstr->mb_cur_max > 1)
+ {
+ wint_t *new_wcs;
+
+ /* Avoid overflow in realloc. */
+ const size_t max_object_size = MAX (sizeof (wint_t), sizeof (int));
+ if (BE (SIZE_MAX / max_object_size < new_buf_len, 0))
+ return REG_ESPACE;
+
+ new_wcs = re_realloc (pstr->wcs, wint_t, new_buf_len);
+ if (BE (new_wcs == NULL, 0))
+ return REG_ESPACE;
+ pstr->wcs = new_wcs;
+ if (pstr->offsets != NULL)
+ {
+ int *new_offsets = re_realloc (pstr->offsets, int, new_buf_len);
+ if (BE (new_offsets == NULL, 0))
+ return REG_ESPACE;
+ pstr->offsets = new_offsets;
+ }
+ }
+#endif /* RE_ENABLE_I18N */
+ if (pstr->mbs_allocated)
+ {
+ unsigned char *new_mbs = re_realloc (pstr->mbs, unsigned char,
+ new_buf_len);
+ if (BE (new_mbs == NULL, 0))
+ return REG_ESPACE;
+ pstr->mbs = new_mbs;
+ }
+ pstr->bufs_len = new_buf_len;
+ return REG_NOERROR;
+}
+
+
+static void
+internal_function
+re_string_construct_common (const char *str, int len, re_string_t *pstr,
+ RE_TRANSLATE_TYPE trans, int icase,
+ const re_dfa_t *dfa)
+{
+ pstr->raw_mbs = (const unsigned char *) str;
+ pstr->len = len;
+ pstr->raw_len = len;
+ pstr->trans = trans;
+ pstr->icase = icase ? 1 : 0;
+ pstr->mbs_allocated = (trans != NULL || icase);
+ pstr->mb_cur_max = dfa->mb_cur_max;
+ pstr->is_utf8 = dfa->is_utf8;
+ pstr->map_notascii = dfa->map_notascii;
+ pstr->stop = pstr->len;
+ pstr->raw_stop = pstr->stop;
+}
+
+#ifdef RE_ENABLE_I18N
+
+/* Build wide character buffer PSTR->WCS.
+ If the byte sequence of the string are:
+ <mb1>(0), <mb1>(1), <mb2>(0), <mb2>(1), <sb3>
+ Then wide character buffer will be:
+ <wc1> , WEOF , <wc2> , WEOF , <wc3>
+ We use WEOF for padding, they indicate that the position isn't
+ a first byte of a multibyte character.
+
+ Note that this function assumes PSTR->VALID_LEN elements are already
+ built and starts from PSTR->VALID_LEN. */
+
+static void
+internal_function
+build_wcs_buffer (re_string_t *pstr)
+{
+#ifdef _LIBC
+ unsigned char buf[MB_LEN_MAX];
+ assert (MB_LEN_MAX >= pstr->mb_cur_max);
+#else
+ unsigned char buf[64];
+#endif
+ mbstate_t prev_st;
+ int byte_idx, end_idx, remain_len;
+ size_t mbclen;
+
+ /* Build the buffers from pstr->valid_len to either pstr->len or
+ pstr->bufs_len. */
+ end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
+ for (byte_idx = pstr->valid_len; byte_idx < end_idx;)
+ {
+ wchar_t wc;
+ const char *p;
+
+ remain_len = end_idx - byte_idx;
+ prev_st = pstr->cur_state;
+ /* Apply the translation if we need. */
+ if (BE (pstr->trans != NULL, 0))
+ {
+ int i, ch;
+
+ for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i)
+ {
+ ch = pstr->raw_mbs [pstr->raw_mbs_idx + byte_idx + i];
+ buf[i] = pstr->mbs[byte_idx + i] = pstr->trans[ch];
+ }
+ p = (const char *) buf;
+ }
+ else
+ p = (const char *) pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx;
+ mbclen = __mbrtowc (&wc, p, remain_len, &pstr->cur_state);
+ if (BE (mbclen == (size_t) -2, 0))
+ {
+ /* The buffer doesn't have enough space, finish to build. */
+ pstr->cur_state = prev_st;
+ break;
+ }
+ else if (BE (mbclen == (size_t) -1 || mbclen == 0, 0))
+ {
+ /* We treat these cases as a singlebyte character. */
+ mbclen = 1;
+ wc = (wchar_t) pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx];
+ if (BE (pstr->trans != NULL, 0))
+ wc = pstr->trans[wc];
+ pstr->cur_state = prev_st;
+ }
+
+ /* Write wide character and padding. */
+ pstr->wcs[byte_idx++] = wc;
+ /* Write paddings. */
+ for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;)
+ pstr->wcs[byte_idx++] = WEOF;
+ }
+ pstr->valid_len = byte_idx;
+ pstr->valid_raw_len = byte_idx;
+}
+
+/* Build wide character buffer PSTR->WCS like build_wcs_buffer,
+ but for REG_ICASE. */
+
+static reg_errcode_t
+internal_function
+build_wcs_upper_buffer (re_string_t *pstr)
+{
+ mbstate_t prev_st;
+ int src_idx, byte_idx, end_idx, remain_len;
+ size_t mbclen;
+#ifdef _LIBC
+ char buf[MB_LEN_MAX];
+ assert (MB_LEN_MAX >= pstr->mb_cur_max);
+#else
+ char buf[64];
+#endif
+
+ byte_idx = pstr->valid_len;
+ end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
+
+ /* The following optimization assumes that ASCII characters can be
+ mapped to wide characters with a simple cast. */
+ if (! pstr->map_notascii && pstr->trans == NULL && !pstr->offsets_needed)
+ {
+ while (byte_idx < end_idx)
+ {
+ wchar_t wc;
+
+ if (isascii (pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx])
+ && mbsinit (&pstr->cur_state))
+ {
+ /* In case of a singlebyte character. */
+ pstr->mbs[byte_idx]
+ = toupper (pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]);
+ /* The next step uses the assumption that wchar_t is encoded
+ ASCII-safe: all ASCII values can be converted like this. */
+ pstr->wcs[byte_idx] = (wchar_t) pstr->mbs[byte_idx];
+ ++byte_idx;
+ continue;
+ }
+
+ remain_len = end_idx - byte_idx;
+ prev_st = pstr->cur_state;
+ mbclen = __mbrtowc (&wc,
+ ((const char *) pstr->raw_mbs + pstr->raw_mbs_idx
+ + byte_idx), remain_len, &pstr->cur_state);
+ if (BE (mbclen + 2 > 2, 1))
+ {
+ wchar_t wcu = wc;
+ if (iswlower (wc))
+ {
+ size_t mbcdlen;
+
+ wcu = towupper (wc);
+ mbcdlen = wcrtomb (buf, wcu, &prev_st);
+ if (BE (mbclen == mbcdlen, 1))
+ memcpy (pstr->mbs + byte_idx, buf, mbclen);
+ else
+ {
+ src_idx = byte_idx;
+ goto offsets_needed;
+ }
+ }
+ else
+ memcpy (pstr->mbs + byte_idx,
+ pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx, mbclen);
+ pstr->wcs[byte_idx++] = wcu;
+ /* Write paddings. */
+ for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;)
+ pstr->wcs[byte_idx++] = WEOF;
+ }
+ else if (mbclen == (size_t) -1 || mbclen == 0)
+ {
+ /* It is an invalid character or '\0'. Just use the byte. */
+ int ch = pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx];
+ pstr->mbs[byte_idx] = ch;
+ /* And also cast it to wide char. */
+ pstr->wcs[byte_idx++] = (wchar_t) ch;
+ if (BE (mbclen == (size_t) -1, 0))
+ pstr->cur_state = prev_st;
+ }
+ else
+ {
+ /* The buffer doesn't have enough space, finish to build. */
+ pstr->cur_state = prev_st;
+ break;
+ }
+ }
+ pstr->valid_len = byte_idx;
+ pstr->valid_raw_len = byte_idx;
+ return REG_NOERROR;
+ }
+ else
+ for (src_idx = pstr->valid_raw_len; byte_idx < end_idx;)
+ {
+ wchar_t wc;
+ const char *p;
+ offsets_needed:
+ remain_len = end_idx - byte_idx;
+ prev_st = pstr->cur_state;
+ if (BE (pstr->trans != NULL, 0))
+ {
+ int i, ch;
+
+ for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i)
+ {
+ ch = pstr->raw_mbs [pstr->raw_mbs_idx + src_idx + i];
+ buf[i] = pstr->trans[ch];
+ }
+ p = (const char *) buf;
+ }
+ else
+ p = (const char *) pstr->raw_mbs + pstr->raw_mbs_idx + src_idx;
+ mbclen = __mbrtowc (&wc, p, remain_len, &pstr->cur_state);
+ if (BE (mbclen + 2 > 2, 1))
+ {
+ wchar_t wcu = wc;
+ if (iswlower (wc))
+ {
+ size_t mbcdlen;
+
+ wcu = towupper (wc);
+ mbcdlen = wcrtomb ((char *) buf, wcu, &prev_st);
+ if (BE (mbclen == mbcdlen, 1))
+ memcpy (pstr->mbs + byte_idx, buf, mbclen);
+ else if (mbcdlen != (size_t) -1)
+ {
+ size_t i;
+
+ if (byte_idx + mbcdlen > pstr->bufs_len)
+ {
+ pstr->cur_state = prev_st;
+ break;
+ }
+
+ if (pstr->offsets == NULL)
+ {
+ pstr->offsets = re_malloc (int, pstr->bufs_len);
+
+ if (pstr->offsets == NULL)
+ return REG_ESPACE;
+ }
+ if (!pstr->offsets_needed)
+ {
+ for (i = 0; i < (size_t) byte_idx; ++i)
+ pstr->offsets[i] = i;
+ pstr->offsets_needed = 1;
+ }
+
+ memcpy (pstr->mbs + byte_idx, buf, mbcdlen);
+ pstr->wcs[byte_idx] = wcu;
+ pstr->offsets[byte_idx] = src_idx;
+ for (i = 1; i < mbcdlen; ++i)
+ {
+ pstr->offsets[byte_idx + i]
+ = src_idx + (i < mbclen ? i : mbclen - 1);
+ pstr->wcs[byte_idx + i] = WEOF;
+ }
+ pstr->len += mbcdlen - mbclen;
+ if (pstr->raw_stop > src_idx)
+ pstr->stop += mbcdlen - mbclen;
+ end_idx = (pstr->bufs_len > pstr->len)
+ ? pstr->len : pstr->bufs_len;
+ byte_idx += mbcdlen;
+ src_idx += mbclen;
+ continue;
+ }
+ else
+ memcpy (pstr->mbs + byte_idx, p, mbclen);
+ }
+ else
+ memcpy (pstr->mbs + byte_idx, p, mbclen);
+
+ if (BE (pstr->offsets_needed != 0, 0))
+ {
+ size_t i;
+ for (i = 0; i < mbclen; ++i)
+ pstr->offsets[byte_idx + i] = src_idx + i;
+ }
+ src_idx += mbclen;
+
+ pstr->wcs[byte_idx++] = wcu;
+ /* Write paddings. */
+ for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;)
+ pstr->wcs[byte_idx++] = WEOF;
+ }
+ else if (mbclen == (size_t) -1 || mbclen == 0)
+ {
+ /* It is an invalid character or '\0'. Just use the byte. */
+ int ch = pstr->raw_mbs[pstr->raw_mbs_idx + src_idx];
+
+ if (BE (pstr->trans != NULL, 0))
+ ch = pstr->trans [ch];
+ pstr->mbs[byte_idx] = ch;
+
+ if (BE (pstr->offsets_needed != 0, 0))
+ pstr->offsets[byte_idx] = src_idx;
+ ++src_idx;
+
+ /* And also cast it to wide char. */
+ pstr->wcs[byte_idx++] = (wchar_t) ch;
+ if (BE (mbclen == (size_t) -1, 0))
+ pstr->cur_state = prev_st;
+ }
+ else
+ {
+ /* The buffer doesn't have enough space, finish to build. */
+ pstr->cur_state = prev_st;
+ break;
+ }
+ }
+ pstr->valid_len = byte_idx;
+ pstr->valid_raw_len = src_idx;
+ return REG_NOERROR;
+}
+
+/* Skip characters until the index becomes greater than NEW_RAW_IDX.
+ Return the index. */
+
+static int
+internal_function
+re_string_skip_chars (re_string_t *pstr, int new_raw_idx, wint_t *last_wc)
+{
+ mbstate_t prev_st;
+ int rawbuf_idx;
+ size_t mbclen;
+ wint_t wc = WEOF;
+
+ /* Skip the characters which are not necessary to check. */
+ for (rawbuf_idx = pstr->raw_mbs_idx + pstr->valid_raw_len;
+ rawbuf_idx < new_raw_idx;)
+ {
+ wchar_t wc2;
+ int remain_len = pstr->len - rawbuf_idx;
+ prev_st = pstr->cur_state;
+ mbclen = __mbrtowc (&wc2, (const char *) pstr->raw_mbs + rawbuf_idx,
+ remain_len, &pstr->cur_state);
+ if (BE (mbclen == (size_t) -2 || mbclen == (size_t) -1 || mbclen == 0, 0))
+ {
+ /* We treat these cases as a single byte character. */
+ if (mbclen == 0 || remain_len == 0)
+ wc = L'\0';
+ else
+ wc = *(unsigned char *) (pstr->raw_mbs + rawbuf_idx);
+ mbclen = 1;
+ pstr->cur_state = prev_st;
+ }
+ else
+ wc = (wint_t) wc2;
+ /* Then proceed the next character. */
+ rawbuf_idx += mbclen;
+ }
+ *last_wc = (wint_t) wc;
+ return rawbuf_idx;
+}
+#endif /* RE_ENABLE_I18N */
+
+/* Build the buffer PSTR->MBS, and apply the translation if we need.
+ This function is used in case of REG_ICASE. */
+
+static void
+internal_function
+build_upper_buffer (re_string_t *pstr)
+{
+ int char_idx, end_idx;
+ end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
+
+ for (char_idx = pstr->valid_len; char_idx < end_idx; ++char_idx)
+ {
+ int ch = pstr->raw_mbs[pstr->raw_mbs_idx + char_idx];
+ if (BE (pstr->trans != NULL, 0))
+ ch = pstr->trans[ch];
+ if (islower (ch))
+ pstr->mbs[char_idx] = toupper (ch);
+ else
+ pstr->mbs[char_idx] = ch;
+ }
+ pstr->valid_len = char_idx;
+ pstr->valid_raw_len = char_idx;
+}
+
+/* Apply TRANS to the buffer in PSTR. */
+
+static void
+internal_function
+re_string_translate_buffer (re_string_t *pstr)
+{
+ int buf_idx, end_idx;
+ end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
+
+ for (buf_idx = pstr->valid_len; buf_idx < end_idx; ++buf_idx)
+ {
+ int ch = pstr->raw_mbs[pstr->raw_mbs_idx + buf_idx];
+ pstr->mbs[buf_idx] = pstr->trans[ch];
+ }
+
+ pstr->valid_len = buf_idx;
+ pstr->valid_raw_len = buf_idx;
+}
+
+/* This function re-construct the buffers.
+ Concretely, convert to wide character in case of pstr->mb_cur_max > 1,
+ convert to upper case in case of REG_ICASE, apply translation. */
+
+static reg_errcode_t
+internal_function
+re_string_reconstruct (re_string_t *pstr, int idx, int eflags)
+{
+ int offset = idx - pstr->raw_mbs_idx;
+ if (BE (offset < 0, 0))
+ {
+ /* Reset buffer. */
+#ifdef RE_ENABLE_I18N
+ if (pstr->mb_cur_max > 1)
+ memset (&pstr->cur_state, '\0', sizeof (mbstate_t));
+#endif /* RE_ENABLE_I18N */
+ pstr->len = pstr->raw_len;
+ pstr->stop = pstr->raw_stop;
+ pstr->valid_len = 0;
+ pstr->raw_mbs_idx = 0;
+ pstr->valid_raw_len = 0;
+ pstr->offsets_needed = 0;
+ pstr->tip_context = ((eflags & REG_NOTBOL) ? CONTEXT_BEGBUF
+ : CONTEXT_NEWLINE | CONTEXT_BEGBUF);
+ if (!pstr->mbs_allocated)
+ pstr->mbs = (unsigned char *) pstr->raw_mbs;
+ offset = idx;
+ }
+
+ if (BE (offset != 0, 1))
+ {
+ /* Should the already checked characters be kept? */
+ if (BE (offset < pstr->valid_raw_len, 1))
+ {
+ /* Yes, move them to the front of the buffer. */
+#ifdef RE_ENABLE_I18N
+ if (BE (pstr->offsets_needed, 0))
+ {
+ int low = 0, high = pstr->valid_len, mid;
+ do
+ {
+ mid = (high + low) / 2;
+ if (pstr->offsets[mid] > offset)
+ high = mid;
+ else if (pstr->offsets[mid] < offset)
+ low = mid + 1;
+ else
+ break;
+ }
+ while (low < high);
+ if (pstr->offsets[mid] < offset)
+ ++mid;
+ pstr->tip_context = re_string_context_at (pstr, mid - 1,
+ eflags);
+ /* This can be quite complicated, so handle specially
+ only the common and easy case where the character with
+ different length representation of lower and upper
+ case is present at or after offset. */
+ if (pstr->valid_len > offset
+ && mid == offset && pstr->offsets[mid] == offset)
+ {
+ memmove (pstr->wcs, pstr->wcs + offset,
+ (pstr->valid_len - offset) * sizeof (wint_t));
+ memmove (pstr->mbs, pstr->mbs + offset, pstr->valid_len - offset);
+ pstr->valid_len -= offset;
+ pstr->valid_raw_len -= offset;
+ for (low = 0; low < pstr->valid_len; low++)
+ pstr->offsets[low] = pstr->offsets[low + offset] - offset;
+ }
+ else
+ {
+ /* Otherwise, just find out how long the partial multibyte
+ character at offset is and fill it with WEOF/255. */
+ pstr->len = pstr->raw_len - idx + offset;
+ pstr->stop = pstr->raw_stop - idx + offset;
+ pstr->offsets_needed = 0;
+ while (mid > 0 && pstr->offsets[mid - 1] == offset)
+ --mid;
+ while (mid < pstr->valid_len)
+ if (pstr->wcs[mid] != WEOF)
+ break;
+ else
+ ++mid;
+ if (mid == pstr->valid_len)
+ pstr->valid_len = 0;
+ else
+ {
+ pstr->valid_len = pstr->offsets[mid] - offset;
+ if (pstr->valid_len)
+ {
+ for (low = 0; low < pstr->valid_len; ++low)
+ pstr->wcs[low] = WEOF;
+ memset (pstr->mbs, 255, pstr->valid_len);
+ }
+ }
+ pstr->valid_raw_len = pstr->valid_len;
+ }
+ }
+ else
+#endif
+ {
+ pstr->tip_context = re_string_context_at (pstr, offset - 1,
+ eflags);
+#ifdef RE_ENABLE_I18N
+ if (pstr->mb_cur_max > 1)
+ memmove (pstr->wcs, pstr->wcs + offset,
+ (pstr->valid_len - offset) * sizeof (wint_t));
+#endif /* RE_ENABLE_I18N */
+ if (BE (pstr->mbs_allocated, 0))
+ memmove (pstr->mbs, pstr->mbs + offset,
+ pstr->valid_len - offset);
+ pstr->valid_len -= offset;
+ pstr->valid_raw_len -= offset;
+#if DEBUG
+ assert (pstr->valid_len > 0);
+#endif
+ }
+ }
+ else
+ {
+#ifdef RE_ENABLE_I18N
+ /* No, skip all characters until IDX. */
+ int prev_valid_len = pstr->valid_len;
+
+ if (BE (pstr->offsets_needed, 0))
+ {
+ pstr->len = pstr->raw_len - idx + offset;
+ pstr->stop = pstr->raw_stop - idx + offset;
+ pstr->offsets_needed = 0;
+ }
+#endif
+ pstr->valid_len = 0;
+#ifdef RE_ENABLE_I18N
+ if (pstr->mb_cur_max > 1)
+ {
+ int wcs_idx;
+ wint_t wc = WEOF;
+
+ if (pstr->is_utf8)
+ {
+ const unsigned char *raw, *p, *end;
+
+ /* Special case UTF-8. Multi-byte chars start with any
+ byte other than 0x80 - 0xbf. */
+ raw = pstr->raw_mbs + pstr->raw_mbs_idx;
+ end = raw + (offset - pstr->mb_cur_max);
+ if (end < pstr->raw_mbs)
+ end = pstr->raw_mbs;
+ p = raw + offset - 1;
+#ifdef _LIBC
+ /* We know the wchar_t encoding is UCS4, so for the simple
+ case, ASCII characters, skip the conversion step. */
+ if (isascii (*p) && BE (pstr->trans == NULL, 1))
+ {
+ memset (&pstr->cur_state, '\0', sizeof (mbstate_t));
+ /* pstr->valid_len = 0; */
+ wc = (wchar_t) *p;
+ }
+ else
+#endif
+ for (; p >= end; --p)
+ if ((*p & 0xc0) != 0x80)
+ {
+ mbstate_t cur_state;
+ wchar_t wc2;
+ int mlen = raw + pstr->len - p;
+ unsigned char buf[6];
+ size_t mbclen;
+
+ if (BE (pstr->trans != NULL, 0))
+ {
+ int i = mlen < 6 ? mlen : 6;
+ while (--i >= 0)
+ buf[i] = pstr->trans[p[i]];
+ }
+ /* XXX Don't use mbrtowc, we know which conversion
+ to use (UTF-8 -> UCS4). */
+ memset (&cur_state, 0, sizeof (cur_state));
+ mbclen = __mbrtowc (&wc2, (const char *) p, mlen,
+ &cur_state);
+ if (raw + offset - p <= mbclen
+ && mbclen < (size_t) -2)
+ {
+ memset (&pstr->cur_state, '\0',
+ sizeof (mbstate_t));
+ pstr->valid_len = mbclen - (raw + offset - p);
+ wc = wc2;
+ }
+ break;
+ }
+ }
+
+ if (wc == WEOF)
+ pstr->valid_len = re_string_skip_chars (pstr, idx, &wc) - idx;
+ if (wc == WEOF)
+ pstr->tip_context
+ = re_string_context_at (pstr, prev_valid_len - 1, eflags);
+ else
+ pstr->tip_context = ((BE (pstr->word_ops_used != 0, 0)
+ && IS_WIDE_WORD_CHAR (wc))
+ ? CONTEXT_WORD
+ : ((IS_WIDE_NEWLINE (wc)
+ && pstr->newline_anchor)
+ ? CONTEXT_NEWLINE : 0));
+ if (BE (pstr->valid_len, 0))
+ {
+ for (wcs_idx = 0; wcs_idx < pstr->valid_len; ++wcs_idx)
+ pstr->wcs[wcs_idx] = WEOF;
+ if (pstr->mbs_allocated)
+ memset (pstr->mbs, 255, pstr->valid_len);
+ }
+ pstr->valid_raw_len = pstr->valid_len;
+ }
+ else
+#endif /* RE_ENABLE_I18N */
+ {
+ int c = pstr->raw_mbs[pstr->raw_mbs_idx + offset - 1];
+ pstr->valid_raw_len = 0;
+ if (pstr->trans)
+ c = pstr->trans[c];
+ pstr->tip_context = (bitset_contain (pstr->word_char, c)
+ ? CONTEXT_WORD
+ : ((IS_NEWLINE (c) && pstr->newline_anchor)
+ ? CONTEXT_NEWLINE : 0));
+ }
+ }
+ if (!BE (pstr->mbs_allocated, 0))
+ pstr->mbs += offset;
+ }
+ pstr->raw_mbs_idx = idx;
+ pstr->len -= offset;
+ pstr->stop -= offset;
+
+ /* Then build the buffers. */
+#ifdef RE_ENABLE_I18N
+ if (pstr->mb_cur_max > 1)
+ {
+ if (pstr->icase)
+ {
+ reg_errcode_t ret = build_wcs_upper_buffer (pstr);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+ }
+ else
+ build_wcs_buffer (pstr);
+ }
+ else
+#endif /* RE_ENABLE_I18N */
+ if (BE (pstr->mbs_allocated, 0))
+ {
+ if (pstr->icase)
+ build_upper_buffer (pstr);
+ else if (pstr->trans != NULL)
+ re_string_translate_buffer (pstr);
+ }
+ else
+ pstr->valid_len = pstr->len;
+
+ pstr->cur_idx = 0;
+ return REG_NOERROR;
+}
+
+static unsigned char
+internal_function __attribute ((pure))
+re_string_peek_byte_case (const re_string_t *pstr, int idx)
+{
+ int ch, off;
+
+ /* Handle the common (easiest) cases first. */
+ if (BE (!pstr->mbs_allocated, 1))
+ return re_string_peek_byte (pstr, idx);
+
+#ifdef RE_ENABLE_I18N
+ if (pstr->mb_cur_max > 1
+ && ! re_string_is_single_byte_char (pstr, pstr->cur_idx + idx))
+ return re_string_peek_byte (pstr, idx);
+#endif
+
+ off = pstr->cur_idx + idx;
+#ifdef RE_ENABLE_I18N
+ if (pstr->offsets_needed)
+ off = pstr->offsets[off];
+#endif
+
+ ch = pstr->raw_mbs[pstr->raw_mbs_idx + off];
+
+#ifdef RE_ENABLE_I18N
+ /* Ensure that e.g. for tr_TR.UTF-8 BACKSLASH DOTLESS SMALL LETTER I
+ this function returns CAPITAL LETTER I instead of first byte of
+ DOTLESS SMALL LETTER I. The latter would confuse the parser,
+ since peek_byte_case doesn't advance cur_idx in any way. */
+ if (pstr->offsets_needed && !isascii (ch))
+ return re_string_peek_byte (pstr, idx);
+#endif
+
+ return ch;
+}
+
+static unsigned char
+internal_function __attribute ((pure))
+re_string_fetch_byte_case (re_string_t *pstr)
+{
+ if (BE (!pstr->mbs_allocated, 1))
+ return re_string_fetch_byte (pstr);
+
+#ifdef RE_ENABLE_I18N
+ if (pstr->offsets_needed)
+ {
+ int off, ch;
+
+ /* For tr_TR.UTF-8 [[:islower:]] there is
+ [[: CAPITAL LETTER I WITH DOT lower:]] in mbs. Skip
+ in that case the whole multi-byte character and return
+ the original letter. On the other side, with
+ [[: DOTLESS SMALL LETTER I return [[:I, as doing
+ anything else would complicate things too much. */
+
+ if (!re_string_first_byte (pstr, pstr->cur_idx))
+ return re_string_fetch_byte (pstr);
+
+ off = pstr->offsets[pstr->cur_idx];
+ ch = pstr->raw_mbs[pstr->raw_mbs_idx + off];
+
+ if (! isascii (ch))
+ return re_string_fetch_byte (pstr);
+
+ re_string_skip_bytes (pstr,
+ re_string_char_size_at (pstr, pstr->cur_idx));
+ return ch;
+ }
+#endif
+
+ return pstr->raw_mbs[pstr->raw_mbs_idx + pstr->cur_idx++];
+}
+
+static void
+internal_function
+re_string_destruct (re_string_t *pstr)
+{
+#ifdef RE_ENABLE_I18N
+ re_free (pstr->wcs);
+ re_free (pstr->offsets);
+#endif /* RE_ENABLE_I18N */
+ if (pstr->mbs_allocated)
+ re_free (pstr->mbs);
+}
+
+/* Return the context at IDX in INPUT. */
+
+static unsigned int
+internal_function
+re_string_context_at (const re_string_t *input, int idx, int eflags)
+{
+ int c;
+ if (BE (idx < 0, 0))
+ /* In this case, we use the value stored in input->tip_context,
+ since we can't know the character in input->mbs[-1] here. */
+ return input->tip_context;
+ if (BE (idx == input->len, 0))
+ return ((eflags & REG_NOTEOL) ? CONTEXT_ENDBUF
+ : CONTEXT_NEWLINE | CONTEXT_ENDBUF);
+#ifdef RE_ENABLE_I18N
+ if (input->mb_cur_max > 1)
+ {
+ wint_t wc;
+ int wc_idx = idx;
+ while(input->wcs[wc_idx] == WEOF)
+ {
+#ifdef DEBUG
+ /* It must not happen. */
+ assert (wc_idx >= 0);
+#endif
+ --wc_idx;
+ if (wc_idx < 0)
+ return input->tip_context;
+ }
+ wc = input->wcs[wc_idx];
+ if (BE (input->word_ops_used != 0, 0) && IS_WIDE_WORD_CHAR (wc))
+ return CONTEXT_WORD;
+ return (IS_WIDE_NEWLINE (wc) && input->newline_anchor
+ ? CONTEXT_NEWLINE : 0);
+ }
+ else
+#endif
+ {
+ c = re_string_byte_at (input, idx);
+ if (bitset_contain (input->word_char, c))
+ return CONTEXT_WORD;
+ return IS_NEWLINE (c) && input->newline_anchor ? CONTEXT_NEWLINE : 0;
+ }
+}
+
+/* Functions for set operation. */
+
+static reg_errcode_t
+internal_function
+re_node_set_alloc (re_node_set *set, int size)
+{
+ /*
+ * ADR: valgrind says size can be 0, which then doesn't
+ * free the block of size 0. Harumph. This seems
+ * to work ok, though.
+ */
+ if (size == 0)
+ {
+ memset(set, 0, sizeof(*set));
+ return REG_NOERROR;
+ }
+ set->alloc = size;
+ set->nelem = 0;
+ set->elems = re_malloc (int, size);
+ if (BE (set->elems == NULL, 0))
+ return REG_ESPACE;
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+re_node_set_init_1 (re_node_set *set, int elem)
+{
+ set->alloc = 1;
+ set->nelem = 1;
+ set->elems = re_malloc (int, 1);
+ if (BE (set->elems == NULL, 0))
+ {
+ set->alloc = set->nelem = 0;
+ return REG_ESPACE;
+ }
+ set->elems[0] = elem;
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+re_node_set_init_2 (re_node_set *set, int elem1, int elem2)
+{
+ set->alloc = 2;
+ set->elems = re_malloc (int, 2);
+ if (BE (set->elems == NULL, 0))
+ return REG_ESPACE;
+ if (elem1 == elem2)
+ {
+ set->nelem = 1;
+ set->elems[0] = elem1;
+ }
+ else
+ {
+ set->nelem = 2;
+ if (elem1 < elem2)
+ {
+ set->elems[0] = elem1;
+ set->elems[1] = elem2;
+ }
+ else
+ {
+ set->elems[0] = elem2;
+ set->elems[1] = elem1;
+ }
+ }
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+re_node_set_init_copy (re_node_set *dest, const re_node_set *src)
+{
+ dest->nelem = src->nelem;
+ if (src->nelem > 0)
+ {
+ dest->alloc = dest->nelem;
+ dest->elems = re_malloc (int, dest->alloc);
+ if (BE (dest->elems == NULL, 0))
+ {
+ dest->alloc = dest->nelem = 0;
+ return REG_ESPACE;
+ }
+ memcpy (dest->elems, src->elems, src->nelem * sizeof (int));
+ }
+ else
+ re_node_set_init_empty (dest);
+ return REG_NOERROR;
+}
+
+/* Calculate the intersection of the sets SRC1 and SRC2. And merge it to
+ DEST. Return value indicate the error code or REG_NOERROR if succeeded.
+ Note: We assume dest->elems is NULL, when dest->alloc is 0. */
+
+static reg_errcode_t
+internal_function
+re_node_set_add_intersect (re_node_set *dest, const re_node_set *src1,
+ const re_node_set *src2)
+{
+ int i1, i2, is, id, delta, sbase;
+ if (src1->nelem == 0 || src2->nelem == 0)
+ return REG_NOERROR;
+
+ /* We need dest->nelem + 2 * elems_in_intersection; this is a
+ conservative estimate. */
+ if (src1->nelem + src2->nelem + dest->nelem > dest->alloc)
+ {
+ int new_alloc = src1->nelem + src2->nelem + dest->alloc;
+ int *new_elems = re_realloc (dest->elems, int, new_alloc);
+ if (BE (new_elems == NULL, 0))
+ return REG_ESPACE;
+ dest->elems = new_elems;
+ dest->alloc = new_alloc;
+ }
+
+ /* Find the items in the intersection of SRC1 and SRC2, and copy
+ into the top of DEST those that are not already in DEST itself. */
+ sbase = dest->nelem + src1->nelem + src2->nelem;
+ i1 = src1->nelem - 1;
+ i2 = src2->nelem - 1;
+ id = dest->nelem - 1;
+ for (;;)
+ {
+ if (src1->elems[i1] == src2->elems[i2])
+ {
+ /* Try to find the item in DEST. Maybe we could binary search? */
+ while (id >= 0 && dest->elems[id] > src1->elems[i1])
+ --id;
+
+ if (id < 0 || dest->elems[id] != src1->elems[i1])
+ dest->elems[--sbase] = src1->elems[i1];
+
+ if (--i1 < 0 || --i2 < 0)
+ break;
+ }
+
+ /* Lower the highest of the two items. */
+ else if (src1->elems[i1] < src2->elems[i2])
+ {
+ if (--i2 < 0)
+ break;
+ }
+ else
+ {
+ if (--i1 < 0)
+ break;
+ }
+ }
+
+ id = dest->nelem - 1;
+ is = dest->nelem + src1->nelem + src2->nelem - 1;
+ delta = is - sbase + 1;
+
+ /* Now copy. When DELTA becomes zero, the remaining
+ DEST elements are already in place; this is more or
+ less the same loop that is in re_node_set_merge. */
+ dest->nelem += delta;
+ if (delta > 0 && id >= 0)
+ for (;;)
+ {
+ if (dest->elems[is] > dest->elems[id])
+ {
+ /* Copy from the top. */
+ dest->elems[id + delta--] = dest->elems[is--];
+ if (delta == 0)
+ break;
+ }
+ else
+ {
+ /* Slide from the bottom. */
+ dest->elems[id + delta] = dest->elems[id];
+ if (--id < 0)
+ break;
+ }
+ }
+
+ /* Copy remaining SRC elements. */
+ memcpy (dest->elems, dest->elems + sbase, delta * sizeof (int));
+
+ return REG_NOERROR;
+}
+
+/* Calculate the union set of the sets SRC1 and SRC2. And store it to
+ DEST. Return value indicate the error code or REG_NOERROR if succeeded. */
+
+static reg_errcode_t
+internal_function
+re_node_set_init_union (re_node_set *dest, const re_node_set *src1,
+ const re_node_set *src2)
+{
+ int i1, i2, id;
+ if (src1 != NULL && src1->nelem > 0 && src2 != NULL && src2->nelem > 0)
+ {
+ dest->alloc = src1->nelem + src2->nelem;
+ dest->elems = re_malloc (int, dest->alloc);
+ if (BE (dest->elems == NULL, 0))
+ return REG_ESPACE;
+ }
+ else
+ {
+ if (src1 != NULL && src1->nelem > 0)
+ return re_node_set_init_copy (dest, src1);
+ else if (src2 != NULL && src2->nelem > 0)
+ return re_node_set_init_copy (dest, src2);
+ else
+ re_node_set_init_empty (dest);
+ return REG_NOERROR;
+ }
+ for (i1 = i2 = id = 0 ; i1 < src1->nelem && i2 < src2->nelem ;)
+ {
+ if (src1->elems[i1] > src2->elems[i2])
+ {
+ dest->elems[id++] = src2->elems[i2++];
+ continue;
+ }
+ if (src1->elems[i1] == src2->elems[i2])
+ ++i2;
+ dest->elems[id++] = src1->elems[i1++];
+ }
+ if (i1 < src1->nelem)
+ {
+ memcpy (dest->elems + id, src1->elems + i1,
+ (src1->nelem - i1) * sizeof (int));
+ id += src1->nelem - i1;
+ }
+ else if (i2 < src2->nelem)
+ {
+ memcpy (dest->elems + id, src2->elems + i2,
+ (src2->nelem - i2) * sizeof (int));
+ id += src2->nelem - i2;
+ }
+ dest->nelem = id;
+ return REG_NOERROR;
+}
+
+/* Calculate the union set of the sets DEST and SRC. And store it to
+ DEST. Return value indicate the error code or REG_NOERROR if succeeded. */
+
+static reg_errcode_t
+internal_function
+re_node_set_merge (re_node_set *dest, const re_node_set *src)
+{
+ int is, id, sbase, delta;
+ if (src == NULL || src->nelem == 0)
+ return REG_NOERROR;
+ if (dest->alloc < 2 * src->nelem + dest->nelem)
+ {
+ int new_alloc = 2 * (src->nelem + dest->alloc);
+ int *new_buffer = re_realloc (dest->elems, int, new_alloc);
+ if (BE (new_buffer == NULL, 0))
+ return REG_ESPACE;
+ dest->elems = new_buffer;
+ dest->alloc = new_alloc;
+ }
+
+ if (BE (dest->nelem == 0, 0))
+ {
+ dest->nelem = src->nelem;
+ memcpy (dest->elems, src->elems, src->nelem * sizeof (int));
+ return REG_NOERROR;
+ }
+
+ /* Copy into the top of DEST the items of SRC that are not
+ found in DEST. Maybe we could binary search in DEST? */
+ for (sbase = dest->nelem + 2 * src->nelem,
+ is = src->nelem - 1, id = dest->nelem - 1; is >= 0 && id >= 0; )
+ {
+ if (dest->elems[id] == src->elems[is])
+ is--, id--;
+ else if (dest->elems[id] < src->elems[is])
+ dest->elems[--sbase] = src->elems[is--];
+ else /* if (dest->elems[id] > src->elems[is]) */
+ --id;
+ }
+
+ if (is >= 0)
+ {
+ /* If DEST is exhausted, the remaining items of SRC must be unique. */
+ sbase -= is + 1;
+ memcpy (dest->elems + sbase, src->elems, (is + 1) * sizeof (int));
+ }
+
+ id = dest->nelem - 1;
+ is = dest->nelem + 2 * src->nelem - 1;
+ delta = is - sbase + 1;
+ if (delta == 0)
+ return REG_NOERROR;
+
+ /* Now copy. When DELTA becomes zero, the remaining
+ DEST elements are already in place. */
+ dest->nelem += delta;
+ for (;;)
+ {
+ if (dest->elems[is] > dest->elems[id])
+ {
+ /* Copy from the top. */
+ dest->elems[id + delta--] = dest->elems[is--];
+ if (delta == 0)
+ break;
+ }
+ else
+ {
+ /* Slide from the bottom. */
+ dest->elems[id + delta] = dest->elems[id];
+ if (--id < 0)
+ {
+ /* Copy remaining SRC elements. */
+ memcpy (dest->elems, dest->elems + sbase,
+ delta * sizeof (int));
+ break;
+ }
+ }
+ }
+
+ return REG_NOERROR;
+}
+
+/* Insert the new element ELEM to the re_node_set* SET.
+ SET should not already have ELEM.
+ return -1 if an error is occured, return 1 otherwise. */
+
+static int
+internal_function
+re_node_set_insert (re_node_set *set, int elem)
+{
+ int idx;
+ /* In case the set is empty. */
+ if (set->alloc == 0)
+ {
+ if (BE (re_node_set_init_1 (set, elem) == REG_NOERROR, 1))
+ return 1;
+ else
+ return -1;
+ }
+
+ if (BE (set->nelem, 0) == 0)
+ {
+ /* We already guaranteed above that set->alloc != 0. */
+ set->elems[0] = elem;
+ ++set->nelem;
+ return 1;
+ }
+
+ /* Realloc if we need. */
+ if (set->alloc == set->nelem)
+ {
+ int *new_elems;
+ set->alloc = set->alloc * 2;
+ new_elems = re_realloc (set->elems, int, set->alloc);
+ if (BE (new_elems == NULL, 0))
+ return -1;
+ set->elems = new_elems;
+ }
+
+ /* Move the elements which follows the new element. Test the
+ first element separately to skip a check in the inner loop. */
+ if (elem < set->elems[0])
+ {
+ idx = 0;
+ for (idx = set->nelem; idx > 0; idx--)
+ set->elems[idx] = set->elems[idx - 1];
+ }
+ else
+ {
+ for (idx = set->nelem; set->elems[idx - 1] > elem; idx--)
+ set->elems[idx] = set->elems[idx - 1];
+ }
+
+ /* Insert the new element. */
+ set->elems[idx] = elem;
+ ++set->nelem;
+ return 1;
+}
+
+/* Insert the new element ELEM to the re_node_set* SET.
+ SET should not already have any element greater than or equal to ELEM.
+ Return -1 if an error is occured, return 1 otherwise. */
+
+static int
+internal_function
+re_node_set_insert_last (re_node_set *set, int elem)
+{
+ /* Realloc if we need. */
+ if (set->alloc == set->nelem)
+ {
+ int *new_elems;
+ set->alloc = (set->alloc + 1) * 2;
+ new_elems = re_realloc (set->elems, int, set->alloc);
+ if (BE (new_elems == NULL, 0))
+ return -1;
+ set->elems = new_elems;
+ }
+
+ /* Insert the new element. */
+ set->elems[set->nelem++] = elem;
+ return 1;
+}
+
+/* Compare two node sets SET1 and SET2.
+ return 1 if SET1 and SET2 are equivalent, return 0 otherwise. */
+
+static int
+internal_function __attribute ((pure))
+re_node_set_compare (const re_node_set *set1, const re_node_set *set2)
+{
+ int i;
+ if (set1 == NULL || set2 == NULL || set1->nelem != set2->nelem)
+ return 0;
+ for (i = set1->nelem ; --i >= 0 ; )
+ if (set1->elems[i] != set2->elems[i])
+ return 0;
+ return 1;
+}
+
+/* Return (idx + 1) if SET contains the element ELEM, return 0 otherwise. */
+
+static int
+internal_function __attribute ((pure))
+re_node_set_contains (const re_node_set *set, int elem)
+{
+ unsigned int idx, right, mid;
+ if (set->nelem <= 0)
+ return 0;
+
+ /* Binary search the element. */
+ idx = 0;
+ right = set->nelem - 1;
+ while (idx < right)
+ {
+ mid = (idx + right) / 2;
+ if (set->elems[mid] < elem)
+ idx = mid + 1;
+ else
+ right = mid;
+ }
+ return set->elems[idx] == elem ? idx + 1 : 0;
+}
+
+static void
+internal_function
+re_node_set_remove_at (re_node_set *set, int idx)
+{
+ if (idx < 0 || idx >= set->nelem)
+ return;
+ --set->nelem;
+ for (; idx < set->nelem; idx++)
+ set->elems[idx] = set->elems[idx + 1];
+}
+
+
+/* Add the token TOKEN to dfa->nodes, and return the index of the token.
+ Or return -1, if an error will be occured. */
+
+static int
+internal_function
+re_dfa_add_node (re_dfa_t *dfa, re_token_t token)
+{
+ if (BE (dfa->nodes_len >= dfa->nodes_alloc, 0))
+ {
+ size_t new_nodes_alloc = dfa->nodes_alloc * 2;
+ int *new_nexts, *new_indices;
+ re_node_set *new_edests, *new_eclosures;
+ re_token_t *new_nodes;
+
+ /* Avoid overflows in realloc. */
+ const size_t max_object_size = MAX (sizeof (re_token_t),
+ MAX (sizeof (re_node_set),
+ sizeof (int)));
+ if (BE (SIZE_MAX / max_object_size < new_nodes_alloc, 0))
+ return -1;
+
+ new_nodes = re_realloc (dfa->nodes, re_token_t, new_nodes_alloc);
+ if (BE (new_nodes == NULL, 0))
+ return -1;
+ dfa->nodes = new_nodes;
+ new_nexts = re_realloc (dfa->nexts, int, new_nodes_alloc);
+ new_indices = re_realloc (dfa->org_indices, int, new_nodes_alloc);
+ new_edests = re_realloc (dfa->edests, re_node_set, new_nodes_alloc);
+ new_eclosures = re_realloc (dfa->eclosures, re_node_set, new_nodes_alloc);
+ if (BE (new_nexts == NULL || new_indices == NULL
+ || new_edests == NULL || new_eclosures == NULL, 0))
+ return -1;
+ dfa->nexts = new_nexts;
+ dfa->org_indices = new_indices;
+ dfa->edests = new_edests;
+ dfa->eclosures = new_eclosures;
+ dfa->nodes_alloc = new_nodes_alloc;
+ }
+ dfa->nodes[dfa->nodes_len] = token;
+ dfa->nodes[dfa->nodes_len].constraint = 0;
+#ifdef RE_ENABLE_I18N
+ dfa->nodes[dfa->nodes_len].accept_mb =
+ (token.type == OP_PERIOD && dfa->mb_cur_max > 1) || token.type == COMPLEX_BRACKET;
+#endif
+ dfa->nexts[dfa->nodes_len] = -1;
+ re_node_set_init_empty (dfa->edests + dfa->nodes_len);
+ re_node_set_init_empty (dfa->eclosures + dfa->nodes_len);
+ return dfa->nodes_len++;
+}
+
+static inline unsigned int
+internal_function
+calc_state_hash (const re_node_set *nodes, unsigned int context)
+{
+ unsigned int hash = nodes->nelem + context;
+ int i;
+ for (i = 0 ; i < nodes->nelem ; i++)
+ hash += nodes->elems[i];
+ return hash;
+}
+
+/* Search for the state whose node_set is equivalent to NODES.
+ Return the pointer to the state, if we found it in the DFA.
+ Otherwise create the new one and return it. In case of an error
+ return NULL and set the error code in ERR.
+ Note: - We assume NULL as the invalid state, then it is possible that
+ return value is NULL and ERR is REG_NOERROR.
+ - We never return non-NULL value in case of any errors, it is for
+ optimization. */
+
+static re_dfastate_t *
+internal_function
+re_acquire_state (reg_errcode_t *err, const re_dfa_t *dfa,
+ const re_node_set *nodes)
+{
+ unsigned int hash;
+ re_dfastate_t *new_state;
+ struct re_state_table_entry *spot;
+ int i;
+ if (BE (nodes->nelem == 0, 0))
+ {
+ *err = REG_NOERROR;
+ return NULL;
+ }
+ hash = calc_state_hash (nodes, 0);
+ spot = dfa->state_table + (hash & dfa->state_hash_mask);
+
+ for (i = 0 ; i < spot->num ; i++)
+ {
+ re_dfastate_t *state = spot->array[i];
+ if (hash != state->hash)
+ continue;
+ if (re_node_set_compare (&state->nodes, nodes))
+ return state;
+ }
+
+ /* There are no appropriate state in the dfa, create the new one. */
+ new_state = create_ci_newstate (dfa, nodes, hash);
+ if (BE (new_state == NULL, 0))
+ *err = REG_ESPACE;
+
+ return new_state;
+}
+
+/* Search for the state whose node_set is equivalent to NODES and
+ whose context is equivalent to CONTEXT.
+ Return the pointer to the state, if we found it in the DFA.
+ Otherwise create the new one and return it. In case of an error
+ return NULL and set the error code in ERR.
+ Note: - We assume NULL as the invalid state, then it is possible that
+ return value is NULL and ERR is REG_NOERROR.
+ - We never return non-NULL value in case of any errors, it is for
+ optimization. */
+
+static re_dfastate_t *
+internal_function
+re_acquire_state_context (reg_errcode_t *err, const re_dfa_t *dfa,
+ const re_node_set *nodes, unsigned int context)
+{
+ unsigned int hash;
+ re_dfastate_t *new_state;
+ struct re_state_table_entry *spot;
+ int i;
+ if (nodes->nelem == 0)
+ {
+ *err = REG_NOERROR;
+ return NULL;
+ }
+ hash = calc_state_hash (nodes, context);
+ spot = dfa->state_table + (hash & dfa->state_hash_mask);
+
+ for (i = 0 ; i < spot->num ; i++)
+ {
+ re_dfastate_t *state = spot->array[i];
+ if (state->hash == hash
+ && state->context == context
+ && re_node_set_compare (state->entrance_nodes, nodes))
+ return state;
+ }
+ /* There are no appropriate state in `dfa', create the new one. */
+ new_state = create_cd_newstate (dfa, nodes, context, hash);
+ if (BE (new_state == NULL, 0))
+ *err = REG_ESPACE;
+
+ return new_state;
+}
+
+/* Finish initialization of the new state NEWSTATE, and using its hash value
+ HASH put in the appropriate bucket of DFA's state table. Return value
+ indicates the error code if failed. */
+
+static reg_errcode_t
+register_state (const re_dfa_t *dfa, re_dfastate_t *newstate,
+ unsigned int hash)
+{
+ struct re_state_table_entry *spot;
+ reg_errcode_t err;
+ int i;
+
+ newstate->hash = hash;
+ err = re_node_set_alloc (&newstate->non_eps_nodes, newstate->nodes.nelem);
+ if (BE (err != REG_NOERROR, 0))
+ return REG_ESPACE;
+ for (i = 0; i < newstate->nodes.nelem; i++)
+ {
+ int elem = newstate->nodes.elems[i];
+ if (!IS_EPSILON_NODE (dfa->nodes[elem].type))
+ if (re_node_set_insert_last (&newstate->non_eps_nodes, elem) < 0)
+ return REG_ESPACE;
+ }
+
+ spot = dfa->state_table + (hash & dfa->state_hash_mask);
+ if (BE (spot->alloc <= spot->num, 0))
+ {
+ int new_alloc = 2 * spot->num + 2;
+ re_dfastate_t **new_array = re_realloc (spot->array, re_dfastate_t *,
+ new_alloc);
+ if (BE (new_array == NULL, 0))
+ return REG_ESPACE;
+ spot->array = new_array;
+ spot->alloc = new_alloc;
+ }
+ spot->array[spot->num++] = newstate;
+ return REG_NOERROR;
+}
+
+static void
+free_state (re_dfastate_t *state)
+{
+ re_node_set_free (&state->non_eps_nodes);
+ re_node_set_free (&state->inveclosure);
+ if (state->entrance_nodes != &state->nodes)
+ {
+ re_node_set_free (state->entrance_nodes);
+ re_free (state->entrance_nodes);
+ }
+ re_node_set_free (&state->nodes);
+ re_free (state->word_trtable);
+ re_free (state->trtable);
+ re_free (state);
+}
+
+/* Create the new state which is independ of contexts.
+ Return the new state if succeeded, otherwise return NULL. */
+
+static re_dfastate_t *
+internal_function
+create_ci_newstate (const re_dfa_t *dfa, const re_node_set *nodes,
+ unsigned int hash)
+{
+ int i;
+ reg_errcode_t err;
+ re_dfastate_t *newstate;
+
+ newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1);
+ if (BE (newstate == NULL, 0))
+ return NULL;
+ err = re_node_set_init_copy (&newstate->nodes, nodes);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_free (newstate);
+ return NULL;
+ }
+
+ newstate->entrance_nodes = &newstate->nodes;
+ for (i = 0 ; i < nodes->nelem ; i++)
+ {
+ re_token_t *node = dfa->nodes + nodes->elems[i];
+ re_token_type_t type = node->type;
+ if (type == CHARACTER && !node->constraint)
+ continue;
+#ifdef RE_ENABLE_I18N
+ newstate->accept_mb |= node->accept_mb;
+#endif /* RE_ENABLE_I18N */
+
+ /* If the state has the halt node, the state is a halt state. */
+ if (type == END_OF_RE)
+ newstate->halt = 1;
+ else if (type == OP_BACK_REF)
+ newstate->has_backref = 1;
+ else if (type == ANCHOR || node->constraint)
+ newstate->has_constraint = 1;
+ }
+ err = register_state (dfa, newstate, hash);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ free_state (newstate);
+ newstate = NULL;
+ }
+ return newstate;
+}
+
+/* Create the new state which is depend on the context CONTEXT.
+ Return the new state if succeeded, otherwise return NULL. */
+
+static re_dfastate_t *
+internal_function
+create_cd_newstate (const re_dfa_t *dfa, const re_node_set *nodes,
+ unsigned int context, unsigned int hash)
+{
+ int i, nctx_nodes = 0;
+ reg_errcode_t err;
+ re_dfastate_t *newstate;
+
+ newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1);
+ if (BE (newstate == NULL, 0))
+ return NULL;
+ err = re_node_set_init_copy (&newstate->nodes, nodes);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_free (newstate);
+ return NULL;
+ }
+
+ newstate->context = context;
+ newstate->entrance_nodes = &newstate->nodes;
+
+ for (i = 0 ; i < nodes->nelem ; i++)
+ {
+ re_token_t *node = dfa->nodes + nodes->elems[i];
+ re_token_type_t type = node->type;
+ unsigned int constraint = node->constraint;
+
+ if (type == CHARACTER && !constraint)
+ continue;
+#ifdef RE_ENABLE_I18N
+ newstate->accept_mb |= node->accept_mb;
+#endif /* RE_ENABLE_I18N */
+
+ /* If the state has the halt node, the state is a halt state. */
+ if (type == END_OF_RE)
+ newstate->halt = 1;
+ else if (type == OP_BACK_REF)
+ newstate->has_backref = 1;
+
+ if (constraint)
+ {
+ if (newstate->entrance_nodes == &newstate->nodes)
+ {
+ newstate->entrance_nodes = re_malloc (re_node_set, 1);
+ if (BE (newstate->entrance_nodes == NULL, 0))
+ {
+ free_state (newstate);
+ return NULL;
+ }
+ if (re_node_set_init_copy (newstate->entrance_nodes, nodes)
+ != REG_NOERROR)
+ return NULL;
+ nctx_nodes = 0;
+ newstate->has_constraint = 1;
+ }
+
+ if (NOT_SATISFY_PREV_CONSTRAINT (constraint,context))
+ {
+ re_node_set_remove_at (&newstate->nodes, i - nctx_nodes);
+ ++nctx_nodes;
+ }
+ }
+ }
+ err = register_state (dfa, newstate, hash);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ free_state (newstate);
+ newstate = NULL;
+ }
+ return newstate;
+}
diff --git a/deps/regex/regex_internal.h b/deps/regex/regex_internal.h
new file mode 100644
index 000000000..4184d7f5a
--- /dev/null
+++ b/deps/regex/regex_internal.h
@@ -0,0 +1,810 @@
+/* Extended regular expression matching and search library.
+ Copyright (C) 2002-2005, 2007, 2008, 2010 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#ifndef _REGEX_INTERNAL_H
+#define _REGEX_INTERNAL_H 1
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined HAVE_LANGINFO_H || defined HAVE_LANGINFO_CODESET || defined _LIBC
+# include <langinfo.h>
+#endif
+#if defined HAVE_LOCALE_H || defined _LIBC
+# include <locale.h>
+#endif
+#if defined HAVE_WCHAR_H || defined _LIBC
+# include <wchar.h>
+#endif /* HAVE_WCHAR_H || _LIBC */
+#if defined HAVE_WCTYPE_H || defined _LIBC
+# include <wctype.h>
+#endif /* HAVE_WCTYPE_H || _LIBC */
+#if defined HAVE_STDBOOL_H || defined _LIBC
+# include <stdbool.h>
+#endif /* HAVE_STDBOOL_H || _LIBC */
+#if !defined(ZOS_USS)
+#if defined HAVE_STDINT_H || defined _LIBC
+# include <stdint.h>
+#endif /* HAVE_STDINT_H || _LIBC */
+#endif /* !ZOS_USS */
+#if defined _LIBC
+# include <bits/libc-lock.h>
+#else
+# define __libc_lock_define(CLASS,NAME)
+# define __libc_lock_init(NAME) do { } while (0)
+# define __libc_lock_lock(NAME) do { } while (0)
+# define __libc_lock_unlock(NAME) do { } while (0)
+#endif
+
+#ifndef GAWK
+/* In case that the system doesn't have isblank(). */
+#if !defined _LIBC && !defined HAVE_ISBLANK && !defined isblank
+# define isblank(ch) ((ch) == ' ' || (ch) == '\t')
+#endif
+#else /* GAWK */
+/*
+ * This is a freaking mess. On glibc systems you have to define
+ * a magic constant to get isblank() out of <ctype.h>, since it's
+ * a C99 function. To heck with all that and borrow a page from
+ * dfa.c's book.
+ */
+
+static int
+is_blank (int c)
+{
+ return (c == ' ' || c == '\t');
+}
+#endif /* GAWK */
+
+#ifdef _LIBC
+# ifndef _RE_DEFINE_LOCALE_FUNCTIONS
+# define _RE_DEFINE_LOCALE_FUNCTIONS 1
+# include <locale/localeinfo.h>
+# include <locale/elem-hash.h>
+# include <locale/coll-lookup.h>
+# endif
+#endif
+
+/* This is for other GNU distributions with internationalized messages. */
+#if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC
+# include <libintl.h>
+# ifdef _LIBC
+# undef gettext
+# define gettext(msgid) \
+ INTUSE(__dcgettext) (_libc_intl_domainname, msgid, LC_MESSAGES)
+# endif
+#else
+# define gettext(msgid) (msgid)
+#endif
+
+#ifndef gettext_noop
+/* This define is so xgettext can find the internationalizable
+ strings. */
+# define gettext_noop(String) String
+#endif
+
+/* For loser systems without the definition. */
+#ifndef SIZE_MAX
+# define SIZE_MAX ((size_t) -1)
+#endif
+
+#ifndef NO_MBSUPPORT
+#include "mbsupport.h" /* gawk */
+#endif
+#ifndef MB_CUR_MAX
+#define MB_CUR_MAX 1
+#endif
+
+#if (defined MBS_SUPPORT) || _LIBC
+# define RE_ENABLE_I18N
+#endif
+
+#if __GNUC__ >= 3
+# define BE(expr, val) __builtin_expect (expr, val)
+#else
+# define BE(expr, val) (expr)
+# ifdef inline
+# undef inline
+# endif
+# define inline
+#endif
+
+/* Number of single byte character. */
+#define SBC_MAX 256
+
+#define COLL_ELEM_LEN_MAX 8
+
+/* The character which represents newline. */
+#define NEWLINE_CHAR '\n'
+#define WIDE_NEWLINE_CHAR L'\n'
+
+/* Rename to standard API for using out of glibc. */
+#ifndef _LIBC
+# ifdef __wctype
+# undef __wctype
+# endif
+# define __wctype wctype
+# ifdef __iswctype
+# undef __iswctype
+# endif
+# define __iswctype iswctype
+# define __btowc btowc
+# define __mbrtowc mbrtowc
+#undef __mempcpy /* GAWK */
+# define __mempcpy mempcpy
+# define __wcrtomb wcrtomb
+# define __regfree regfree
+# define attribute_hidden
+#endif /* not _LIBC */
+
+#ifdef __GNUC__
+# define __attribute(arg) __attribute__ (arg)
+#else
+# define __attribute(arg)
+#endif
+
+extern const char __re_error_msgid[] attribute_hidden;
+extern const size_t __re_error_msgid_idx[] attribute_hidden;
+
+/* An integer used to represent a set of bits. It must be unsigned,
+ and must be at least as wide as unsigned int. */
+typedef unsigned long int bitset_word_t;
+/* All bits set in a bitset_word_t. */
+#define BITSET_WORD_MAX ULONG_MAX
+/* Number of bits in a bitset_word_t. */
+#define BITSET_WORD_BITS (sizeof (bitset_word_t) * CHAR_BIT)
+/* Number of bitset_word_t in a bit_set. */
+#define BITSET_WORDS (SBC_MAX / BITSET_WORD_BITS)
+typedef bitset_word_t bitset_t[BITSET_WORDS];
+typedef bitset_word_t *re_bitset_ptr_t;
+typedef const bitset_word_t *re_const_bitset_ptr_t;
+
+#define bitset_set(set,i) \
+ (set[i / BITSET_WORD_BITS] |= (bitset_word_t) 1 << i % BITSET_WORD_BITS)
+#define bitset_clear(set,i) \
+ (set[i / BITSET_WORD_BITS] &= ~((bitset_word_t) 1 << i % BITSET_WORD_BITS))
+#define bitset_contain(set,i) \
+ (set[i / BITSET_WORD_BITS] & ((bitset_word_t) 1 << i % BITSET_WORD_BITS))
+#define bitset_empty(set) memset (set, '\0', sizeof (bitset_t))
+#define bitset_set_all(set) memset (set, '\xff', sizeof (bitset_t))
+#define bitset_copy(dest,src) memcpy (dest, src, sizeof (bitset_t))
+
+#define PREV_WORD_CONSTRAINT 0x0001
+#define PREV_NOTWORD_CONSTRAINT 0x0002
+#define NEXT_WORD_CONSTRAINT 0x0004
+#define NEXT_NOTWORD_CONSTRAINT 0x0008
+#define PREV_NEWLINE_CONSTRAINT 0x0010
+#define NEXT_NEWLINE_CONSTRAINT 0x0020
+#define PREV_BEGBUF_CONSTRAINT 0x0040
+#define NEXT_ENDBUF_CONSTRAINT 0x0080
+#define WORD_DELIM_CONSTRAINT 0x0100
+#define NOT_WORD_DELIM_CONSTRAINT 0x0200
+
+typedef enum
+{
+ INSIDE_WORD = PREV_WORD_CONSTRAINT | NEXT_WORD_CONSTRAINT,
+ WORD_FIRST = PREV_NOTWORD_CONSTRAINT | NEXT_WORD_CONSTRAINT,
+ WORD_LAST = PREV_WORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT,
+ INSIDE_NOTWORD = PREV_NOTWORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT,
+ LINE_FIRST = PREV_NEWLINE_CONSTRAINT,
+ LINE_LAST = NEXT_NEWLINE_CONSTRAINT,
+ BUF_FIRST = PREV_BEGBUF_CONSTRAINT,
+ BUF_LAST = NEXT_ENDBUF_CONSTRAINT,
+ WORD_DELIM = WORD_DELIM_CONSTRAINT,
+ NOT_WORD_DELIM = NOT_WORD_DELIM_CONSTRAINT
+} re_context_type;
+
+typedef struct
+{
+ int alloc;
+ int nelem;
+ int *elems;
+} re_node_set;
+
+typedef enum
+{
+ NON_TYPE = 0,
+
+ /* Node type, These are used by token, node, tree. */
+ CHARACTER = 1,
+ END_OF_RE = 2,
+ SIMPLE_BRACKET = 3,
+ OP_BACK_REF = 4,
+ OP_PERIOD = 5,
+#ifdef RE_ENABLE_I18N
+ COMPLEX_BRACKET = 6,
+ OP_UTF8_PERIOD = 7,
+#endif /* RE_ENABLE_I18N */
+
+ /* We define EPSILON_BIT as a macro so that OP_OPEN_SUBEXP is used
+ when the debugger shows values of this enum type. */
+#define EPSILON_BIT 8
+ OP_OPEN_SUBEXP = EPSILON_BIT | 0,
+ OP_CLOSE_SUBEXP = EPSILON_BIT | 1,
+ OP_ALT = EPSILON_BIT | 2,
+ OP_DUP_ASTERISK = EPSILON_BIT | 3,
+ ANCHOR = EPSILON_BIT | 4,
+
+ /* Tree type, these are used only by tree. */
+ CONCAT = 16,
+ SUBEXP = 17,
+
+ /* Token type, these are used only by token. */
+ OP_DUP_PLUS = 18,
+ OP_DUP_QUESTION,
+ OP_OPEN_BRACKET,
+ OP_CLOSE_BRACKET,
+ OP_CHARSET_RANGE,
+ OP_OPEN_DUP_NUM,
+ OP_CLOSE_DUP_NUM,
+ OP_NON_MATCH_LIST,
+ OP_OPEN_COLL_ELEM,
+ OP_CLOSE_COLL_ELEM,
+ OP_OPEN_EQUIV_CLASS,
+ OP_CLOSE_EQUIV_CLASS,
+ OP_OPEN_CHAR_CLASS,
+ OP_CLOSE_CHAR_CLASS,
+ OP_WORD,
+ OP_NOTWORD,
+ OP_SPACE,
+ OP_NOTSPACE,
+ BACK_SLASH
+
+} re_token_type_t;
+
+#ifdef RE_ENABLE_I18N
+typedef struct
+{
+ /* Multibyte characters. */
+ wchar_t *mbchars;
+
+ /* Collating symbols. */
+# ifdef _LIBC
+ int32_t *coll_syms;
+# endif
+
+ /* Equivalence classes. */
+# ifdef _LIBC
+ int32_t *equiv_classes;
+# endif
+
+ /* Range expressions. */
+# ifdef _LIBC
+ uint32_t *range_starts;
+ uint32_t *range_ends;
+# else /* not _LIBC */
+ wchar_t *range_starts;
+ wchar_t *range_ends;
+# endif /* not _LIBC */
+
+ /* Character classes. */
+ wctype_t *char_classes;
+
+ /* If this character set is the non-matching list. */
+ unsigned int non_match : 1;
+
+ /* # of multibyte characters. */
+ int nmbchars;
+
+ /* # of collating symbols. */
+ int ncoll_syms;
+
+ /* # of equivalence classes. */
+ int nequiv_classes;
+
+ /* # of range expressions. */
+ int nranges;
+
+ /* # of character classes. */
+ int nchar_classes;
+} re_charset_t;
+#endif /* RE_ENABLE_I18N */
+
+typedef struct
+{
+ union
+ {
+ unsigned char c; /* for CHARACTER */
+ re_bitset_ptr_t sbcset; /* for SIMPLE_BRACKET */
+#ifdef RE_ENABLE_I18N
+ re_charset_t *mbcset; /* for COMPLEX_BRACKET */
+#endif /* RE_ENABLE_I18N */
+ int idx; /* for BACK_REF */
+ re_context_type ctx_type; /* for ANCHOR */
+ } opr;
+#if __GNUC__ >= 2
+ re_token_type_t type : 8;
+#else
+ re_token_type_t type;
+#endif
+ unsigned int constraint : 10; /* context constraint */
+ unsigned int duplicated : 1;
+ unsigned int opt_subexp : 1;
+#ifdef RE_ENABLE_I18N
+ unsigned int accept_mb : 1;
+ /* These 2 bits can be moved into the union if needed (e.g. if running out
+ of bits; move opr.c to opr.c.c and move the flags to opr.c.flags). */
+ unsigned int mb_partial : 1;
+#endif
+ unsigned int word_char : 1;
+} re_token_t;
+
+#define IS_EPSILON_NODE(type) ((type) & EPSILON_BIT)
+
+struct re_string_t
+{
+ /* Indicate the raw buffer which is the original string passed as an
+ argument of regexec(), re_search(), etc.. */
+ const unsigned char *raw_mbs;
+ /* Store the multibyte string. In case of "case insensitive mode" like
+ REG_ICASE, upper cases of the string are stored, otherwise MBS points
+ the same address that RAW_MBS points. */
+ unsigned char *mbs;
+#ifdef RE_ENABLE_I18N
+ /* Store the wide character string which is corresponding to MBS. */
+ wint_t *wcs;
+ int *offsets;
+ mbstate_t cur_state;
+#endif
+ /* Index in RAW_MBS. Each character mbs[i] corresponds to
+ raw_mbs[raw_mbs_idx + i]. */
+ int raw_mbs_idx;
+ /* The length of the valid characters in the buffers. */
+ int valid_len;
+ /* The corresponding number of bytes in raw_mbs array. */
+ int valid_raw_len;
+ /* The length of the buffers MBS and WCS. */
+ int bufs_len;
+ /* The index in MBS, which is updated by re_string_fetch_byte. */
+ int cur_idx;
+ /* length of RAW_MBS array. */
+ int raw_len;
+ /* This is RAW_LEN - RAW_MBS_IDX + VALID_LEN - VALID_RAW_LEN. */
+ int len;
+ /* End of the buffer may be shorter than its length in the cases such
+ as re_match_2, re_search_2. Then, we use STOP for end of the buffer
+ instead of LEN. */
+ int raw_stop;
+ /* This is RAW_STOP - RAW_MBS_IDX adjusted through OFFSETS. */
+ int stop;
+
+ /* The context of mbs[0]. We store the context independently, since
+ the context of mbs[0] may be different from raw_mbs[0], which is
+ the beginning of the input string. */
+ unsigned int tip_context;
+ /* The translation passed as a part of an argument of re_compile_pattern. */
+ RE_TRANSLATE_TYPE trans;
+ /* Copy of re_dfa_t's word_char. */
+ re_const_bitset_ptr_t word_char;
+ /* 1 if REG_ICASE. */
+ unsigned char icase;
+ unsigned char is_utf8;
+ unsigned char map_notascii;
+ unsigned char mbs_allocated;
+ unsigned char offsets_needed;
+ unsigned char newline_anchor;
+ unsigned char word_ops_used;
+ int mb_cur_max;
+};
+typedef struct re_string_t re_string_t;
+
+
+struct re_dfa_t;
+typedef struct re_dfa_t re_dfa_t;
+
+#ifndef _LIBC
+# ifdef __i386__
+# define internal_function __attribute ((regparm (3), stdcall))
+# else
+# define internal_function
+# endif
+#endif
+
+#ifndef NOT_IN_libc
+static reg_errcode_t re_string_realloc_buffers (re_string_t *pstr,
+ int new_buf_len)
+ internal_function;
+# ifdef RE_ENABLE_I18N
+static void build_wcs_buffer (re_string_t *pstr) internal_function;
+static reg_errcode_t build_wcs_upper_buffer (re_string_t *pstr)
+ internal_function;
+# endif /* RE_ENABLE_I18N */
+static void build_upper_buffer (re_string_t *pstr) internal_function;
+static void re_string_translate_buffer (re_string_t *pstr) internal_function;
+static unsigned int re_string_context_at (const re_string_t *input, int idx,
+ int eflags)
+ internal_function __attribute ((pure));
+#endif
+#define re_string_peek_byte(pstr, offset) \
+ ((pstr)->mbs[(pstr)->cur_idx + offset])
+#define re_string_fetch_byte(pstr) \
+ ((pstr)->mbs[(pstr)->cur_idx++])
+#define re_string_first_byte(pstr, idx) \
+ ((idx) == (pstr)->valid_len || (pstr)->wcs[idx] != WEOF)
+#define re_string_is_single_byte_char(pstr, idx) \
+ ((pstr)->wcs[idx] != WEOF && ((pstr)->valid_len == (idx) + 1 \
+ || (pstr)->wcs[(idx) + 1] != WEOF))
+#define re_string_eoi(pstr) ((pstr)->stop <= (pstr)->cur_idx)
+#define re_string_cur_idx(pstr) ((pstr)->cur_idx)
+#define re_string_get_buffer(pstr) ((pstr)->mbs)
+#define re_string_length(pstr) ((pstr)->len)
+#define re_string_byte_at(pstr,idx) ((pstr)->mbs[idx])
+#define re_string_skip_bytes(pstr,idx) ((pstr)->cur_idx += (idx))
+#define re_string_set_index(pstr,idx) ((pstr)->cur_idx = (idx))
+
+#ifndef _LIBC
+# if HAVE_ALLOCA
+# if (_MSC_VER)
+# include <malloc.h>
+# define __libc_use_alloca(n) 0
+# else
+# include <alloca.h>
+/* The OS usually guarantees only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ allocate anything larger than 4096 bytes. Also care for the possibility
+ of a few compiler-allocated temporary stack slots. */
+# define __libc_use_alloca(n) ((n) < 4032)
+# endif
+# else
+/* alloca is implemented with malloc, so just use malloc. */
+# define __libc_use_alloca(n) 0
+# endif
+#endif
+
+#define re_malloc(t,n) ((t *) malloc ((n) * sizeof (t)))
+/* SunOS 4.1.x realloc doesn't accept null pointers: pre-Standard C. Sigh. */
+#define re_realloc(p,t,n) ((p != NULL) ? (t *) realloc (p,(n)*sizeof(t)) : (t *) calloc(n,sizeof(t)))
+#define re_free(p) free (p)
+
+struct bin_tree_t
+{
+ struct bin_tree_t *parent;
+ struct bin_tree_t *left;
+ struct bin_tree_t *right;
+ struct bin_tree_t *first;
+ struct bin_tree_t *next;
+
+ re_token_t token;
+
+ /* `node_idx' is the index in dfa->nodes, if `type' == 0.
+ Otherwise `type' indicate the type of this node. */
+ int node_idx;
+};
+typedef struct bin_tree_t bin_tree_t;
+
+#define BIN_TREE_STORAGE_SIZE \
+ ((1024 - sizeof (void *)) / sizeof (bin_tree_t))
+
+struct bin_tree_storage_t
+{
+ struct bin_tree_storage_t *next;
+ bin_tree_t data[BIN_TREE_STORAGE_SIZE];
+};
+typedef struct bin_tree_storage_t bin_tree_storage_t;
+
+#define CONTEXT_WORD 1
+#define CONTEXT_NEWLINE (CONTEXT_WORD << 1)
+#define CONTEXT_BEGBUF (CONTEXT_NEWLINE << 1)
+#define CONTEXT_ENDBUF (CONTEXT_BEGBUF << 1)
+
+#define IS_WORD_CONTEXT(c) ((c) & CONTEXT_WORD)
+#define IS_NEWLINE_CONTEXT(c) ((c) & CONTEXT_NEWLINE)
+#define IS_BEGBUF_CONTEXT(c) ((c) & CONTEXT_BEGBUF)
+#define IS_ENDBUF_CONTEXT(c) ((c) & CONTEXT_ENDBUF)
+#define IS_ORDINARY_CONTEXT(c) ((c) == 0)
+
+#define IS_WORD_CHAR(ch) (isalnum (ch) || (ch) == '_')
+#define IS_NEWLINE(ch) ((ch) == NEWLINE_CHAR)
+#define IS_WIDE_WORD_CHAR(ch) (iswalnum (ch) || (ch) == L'_')
+#define IS_WIDE_NEWLINE(ch) ((ch) == WIDE_NEWLINE_CHAR)
+
+#define NOT_SATISFY_PREV_CONSTRAINT(constraint,context) \
+ ((((constraint) & PREV_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \
+ || ((constraint & PREV_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \
+ || ((constraint & PREV_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context))\
+ || ((constraint & PREV_BEGBUF_CONSTRAINT) && !IS_BEGBUF_CONTEXT (context)))
+
+#define NOT_SATISFY_NEXT_CONSTRAINT(constraint,context) \
+ ((((constraint) & NEXT_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \
+ || (((constraint) & NEXT_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \
+ || (((constraint) & NEXT_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context)) \
+ || (((constraint) & NEXT_ENDBUF_CONSTRAINT) && !IS_ENDBUF_CONTEXT (context)))
+
+struct re_dfastate_t
+{
+ unsigned int hash;
+ re_node_set nodes;
+ re_node_set non_eps_nodes;
+ re_node_set inveclosure;
+ re_node_set *entrance_nodes;
+ struct re_dfastate_t **trtable, **word_trtable;
+ unsigned int context : 4;
+ unsigned int halt : 1;
+ /* If this state can accept `multi byte'.
+ Note that we refer to multibyte characters, and multi character
+ collating elements as `multi byte'. */
+ unsigned int accept_mb : 1;
+ /* If this state has backreference node(s). */
+ unsigned int has_backref : 1;
+ unsigned int has_constraint : 1;
+};
+typedef struct re_dfastate_t re_dfastate_t;
+
+struct re_state_table_entry
+{
+ int num;
+ int alloc;
+ re_dfastate_t **array;
+};
+
+/* Array type used in re_sub_match_last_t and re_sub_match_top_t. */
+
+typedef struct
+{
+ int next_idx;
+ int alloc;
+ re_dfastate_t **array;
+} state_array_t;
+
+/* Store information about the node NODE whose type is OP_CLOSE_SUBEXP. */
+
+typedef struct
+{
+ int node;
+ int str_idx; /* The position NODE match at. */
+ state_array_t path;
+} re_sub_match_last_t;
+
+/* Store information about the node NODE whose type is OP_OPEN_SUBEXP.
+ And information about the node, whose type is OP_CLOSE_SUBEXP,
+ corresponding to NODE is stored in LASTS. */
+
+typedef struct
+{
+ int str_idx;
+ int node;
+ state_array_t *path;
+ int alasts; /* Allocation size of LASTS. */
+ int nlasts; /* The number of LASTS. */
+ re_sub_match_last_t **lasts;
+} re_sub_match_top_t;
+
+struct re_backref_cache_entry
+{
+ int node;
+ int str_idx;
+ int subexp_from;
+ int subexp_to;
+ char more;
+ char unused;
+ unsigned short int eps_reachable_subexps_map;
+};
+
+typedef struct
+{
+ /* The string object corresponding to the input string. */
+ re_string_t input;
+#if defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)
+ const re_dfa_t *const dfa;
+#else
+ const re_dfa_t *dfa;
+#endif
+ /* EFLAGS of the argument of regexec. */
+ int eflags;
+ /* Where the matching ends. */
+ int match_last;
+ int last_node;
+ /* The state log used by the matcher. */
+ re_dfastate_t **state_log;
+ int state_log_top;
+ /* Back reference cache. */
+ int nbkref_ents;
+ int abkref_ents;
+ struct re_backref_cache_entry *bkref_ents;
+ int max_mb_elem_len;
+ int nsub_tops;
+ int asub_tops;
+ re_sub_match_top_t **sub_tops;
+} re_match_context_t;
+
+typedef struct
+{
+ re_dfastate_t **sifted_states;
+ re_dfastate_t **limited_states;
+ int last_node;
+ int last_str_idx;
+ re_node_set limits;
+} re_sift_context_t;
+
+struct re_fail_stack_ent_t
+{
+ int idx;
+ int node;
+ regmatch_t *regs;
+ re_node_set eps_via_nodes;
+};
+
+struct re_fail_stack_t
+{
+ int num;
+ int alloc;
+ struct re_fail_stack_ent_t *stack;
+};
+
+struct re_dfa_t
+{
+ re_token_t *nodes;
+ size_t nodes_alloc;
+ size_t nodes_len;
+ int *nexts;
+ int *org_indices;
+ re_node_set *edests;
+ re_node_set *eclosures;
+ re_node_set *inveclosures;
+ struct re_state_table_entry *state_table;
+ re_dfastate_t *init_state;
+ re_dfastate_t *init_state_word;
+ re_dfastate_t *init_state_nl;
+ re_dfastate_t *init_state_begbuf;
+ bin_tree_t *str_tree;
+ bin_tree_storage_t *str_tree_storage;
+ re_bitset_ptr_t sb_char;
+ int str_tree_storage_idx;
+
+ /* number of subexpressions `re_nsub' is in regex_t. */
+ unsigned int state_hash_mask;
+ int init_node;
+ int nbackref; /* The number of backreference in this dfa. */
+
+ /* Bitmap expressing which backreference is used. */
+ bitset_word_t used_bkref_map;
+ bitset_word_t completed_bkref_map;
+
+ unsigned int has_plural_match : 1;
+ /* If this dfa has "multibyte node", which is a backreference or
+ a node which can accept multibyte character or multi character
+ collating element. */
+ unsigned int has_mb_node : 1;
+ unsigned int is_utf8 : 1;
+ unsigned int map_notascii : 1;
+ unsigned int word_ops_used : 1;
+ int mb_cur_max;
+ bitset_t word_char;
+ reg_syntax_t syntax;
+ int *subexp_map;
+#ifdef DEBUG
+ char* re_str;
+#endif
+#if defined _LIBC
+ __libc_lock_define (, lock)
+#endif
+};
+
+#define re_node_set_init_empty(set) memset (set, '\0', sizeof (re_node_set))
+#define re_node_set_remove(set,id) \
+ (re_node_set_remove_at (set, re_node_set_contains (set, id) - 1))
+#define re_node_set_empty(p) ((p)->nelem = 0)
+#define re_node_set_free(set) re_free ((set)->elems)
+
+
+typedef enum
+{
+ SB_CHAR,
+ MB_CHAR,
+ EQUIV_CLASS,
+ COLL_SYM,
+ CHAR_CLASS
+} bracket_elem_type;
+
+typedef struct
+{
+ bracket_elem_type type;
+ union
+ {
+ unsigned char ch;
+ unsigned char *name;
+ wchar_t wch;
+ } opr;
+} bracket_elem_t;
+
+
+/* Inline functions for bitset operation. */
+static inline void
+bitset_not (bitset_t set)
+{
+ int bitset_i;
+ for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i)
+ set[bitset_i] = ~set[bitset_i];
+}
+
+static inline void
+bitset_merge (bitset_t dest, const bitset_t src)
+{
+ int bitset_i;
+ for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i)
+ dest[bitset_i] |= src[bitset_i];
+}
+
+static inline void
+bitset_mask (bitset_t dest, const bitset_t src)
+{
+ int bitset_i;
+ for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i)
+ dest[bitset_i] &= src[bitset_i];
+}
+
+#ifdef RE_ENABLE_I18N
+/* Inline functions for re_string. */
+static inline int
+internal_function __attribute ((pure))
+re_string_char_size_at (const re_string_t *pstr, int idx)
+{
+ int byte_idx;
+ if (pstr->mb_cur_max == 1)
+ return 1;
+ for (byte_idx = 1; idx + byte_idx < pstr->valid_len; ++byte_idx)
+ if (pstr->wcs[idx + byte_idx] != WEOF)
+ break;
+ return byte_idx;
+}
+
+static inline wint_t
+internal_function __attribute ((pure))
+re_string_wchar_at (const re_string_t *pstr, int idx)
+{
+ if (pstr->mb_cur_max == 1)
+ return (wint_t) pstr->mbs[idx];
+ return (wint_t) pstr->wcs[idx];
+}
+
+# ifndef NOT_IN_libc
+static int
+internal_function __attribute ((pure))
+re_string_elem_size_at (const re_string_t *pstr, int idx)
+{
+# ifdef _LIBC
+ const unsigned char *p, *extra;
+ const int32_t *table, *indirect;
+ int32_t tmp;
+# include <locale/weight.h>
+ uint_fast32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+
+ if (nrules != 0)
+ {
+ table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+ extra = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB);
+ indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_INDIRECTMB);
+ p = pstr->mbs + idx;
+ tmp = findidx (&p);
+ return p - pstr->mbs - idx;
+ }
+ else
+# endif /* _LIBC */
+ return 1;
+}
+# endif
+#endif /* RE_ENABLE_I18N */
+
+#endif /* _REGEX_INTERNAL_H */
diff --git a/deps/regex/regexec.c b/deps/regex/regexec.c
new file mode 100644
index 000000000..5eb6f1fea
--- /dev/null
+++ b/deps/regex/regexec.c
@@ -0,0 +1,4369 @@
+/* Extended regular expression matching and search library.
+ Copyright (C) 2002-2005, 2007, 2009, 2010 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA. */
+
+static reg_errcode_t match_ctx_init (re_match_context_t *cache, int eflags,
+ int n) internal_function;
+static void match_ctx_clean (re_match_context_t *mctx) internal_function;
+static void match_ctx_free (re_match_context_t *cache) internal_function;
+static reg_errcode_t match_ctx_add_entry (re_match_context_t *cache, int node,
+ int str_idx, int from, int to)
+ internal_function;
+static int search_cur_bkref_entry (const re_match_context_t *mctx, int str_idx)
+ internal_function;
+static reg_errcode_t match_ctx_add_subtop (re_match_context_t *mctx, int node,
+ int str_idx) internal_function;
+static re_sub_match_last_t * match_ctx_add_sublast (re_sub_match_top_t *subtop,
+ int node, int str_idx)
+ internal_function;
+static void sift_ctx_init (re_sift_context_t *sctx, re_dfastate_t **sifted_sts,
+ re_dfastate_t **limited_sts, int last_node,
+ int last_str_idx)
+ internal_function;
+static reg_errcode_t re_search_internal (const regex_t *preg,
+ const char *string, int length,
+ int start, int range, int stop,
+ size_t nmatch, regmatch_t pmatch[],
+ int eflags);
+static int re_search_2_stub (struct re_pattern_buffer *bufp,
+ const char *string1, int length1,
+ const char *string2, int length2,
+ int start, int range, struct re_registers *regs,
+ int stop, int ret_len);
+static int re_search_stub (struct re_pattern_buffer *bufp,
+ const char *string, int length, int start,
+ int range, int stop, struct re_registers *regs,
+ int ret_len);
+static unsigned re_copy_regs (struct re_registers *regs, regmatch_t *pmatch,
+ unsigned int nregs, int regs_allocated);
+static reg_errcode_t prune_impossible_nodes (re_match_context_t *mctx);
+static int check_matching (re_match_context_t *mctx, int fl_longest_match,
+ int *p_match_first) internal_function;
+static int check_halt_state_context (const re_match_context_t *mctx,
+ const re_dfastate_t *state, int idx)
+ internal_function;
+static void update_regs (const re_dfa_t *dfa, regmatch_t *pmatch,
+ regmatch_t *prev_idx_match, int cur_node,
+ int cur_idx, int nmatch) internal_function;
+static reg_errcode_t push_fail_stack (struct re_fail_stack_t *fs,
+ int str_idx, int dest_node, int nregs,
+ regmatch_t *regs,
+ re_node_set *eps_via_nodes)
+ internal_function;
+static reg_errcode_t set_regs (const regex_t *preg,
+ const re_match_context_t *mctx,
+ size_t nmatch, regmatch_t *pmatch,
+ int fl_backtrack) internal_function;
+static reg_errcode_t free_fail_stack_return (struct re_fail_stack_t *fs)
+ internal_function;
+
+#ifdef RE_ENABLE_I18N
+static int sift_states_iter_mb (const re_match_context_t *mctx,
+ re_sift_context_t *sctx,
+ int node_idx, int str_idx, int max_str_idx)
+ internal_function;
+#endif /* RE_ENABLE_I18N */
+static reg_errcode_t sift_states_backward (const re_match_context_t *mctx,
+ re_sift_context_t *sctx)
+ internal_function;
+static reg_errcode_t build_sifted_states (const re_match_context_t *mctx,
+ re_sift_context_t *sctx, int str_idx,
+ re_node_set *cur_dest)
+ internal_function;
+static reg_errcode_t update_cur_sifted_state (const re_match_context_t *mctx,
+ re_sift_context_t *sctx,
+ int str_idx,
+ re_node_set *dest_nodes)
+ internal_function;
+static reg_errcode_t add_epsilon_src_nodes (const re_dfa_t *dfa,
+ re_node_set *dest_nodes,
+ const re_node_set *candidates)
+ internal_function;
+static int check_dst_limits (const re_match_context_t *mctx,
+ re_node_set *limits,
+ int dst_node, int dst_idx, int src_node,
+ int src_idx) internal_function;
+static int check_dst_limits_calc_pos_1 (const re_match_context_t *mctx,
+ int boundaries, int subexp_idx,
+ int from_node, int bkref_idx)
+ internal_function;
+static int check_dst_limits_calc_pos (const re_match_context_t *mctx,
+ int limit, int subexp_idx,
+ int node, int str_idx,
+ int bkref_idx) internal_function;
+static reg_errcode_t check_subexp_limits (const re_dfa_t *dfa,
+ re_node_set *dest_nodes,
+ const re_node_set *candidates,
+ re_node_set *limits,
+ struct re_backref_cache_entry *bkref_ents,
+ int str_idx) internal_function;
+static reg_errcode_t sift_states_bkref (const re_match_context_t *mctx,
+ re_sift_context_t *sctx,
+ int str_idx, const re_node_set *candidates)
+ internal_function;
+static reg_errcode_t merge_state_array (const re_dfa_t *dfa,
+ re_dfastate_t **dst,
+ re_dfastate_t **src, int num)
+ internal_function;
+static re_dfastate_t *find_recover_state (reg_errcode_t *err,
+ re_match_context_t *mctx) internal_function;
+static re_dfastate_t *transit_state (reg_errcode_t *err,
+ re_match_context_t *mctx,
+ re_dfastate_t *state) internal_function;
+static re_dfastate_t *merge_state_with_log (reg_errcode_t *err,
+ re_match_context_t *mctx,
+ re_dfastate_t *next_state)
+ internal_function;
+static reg_errcode_t check_subexp_matching_top (re_match_context_t *mctx,
+ re_node_set *cur_nodes,
+ int str_idx) internal_function;
+#if 0
+static re_dfastate_t *transit_state_sb (reg_errcode_t *err,
+ re_match_context_t *mctx,
+ re_dfastate_t *pstate)
+ internal_function;
+#endif
+#ifdef RE_ENABLE_I18N
+static reg_errcode_t transit_state_mb (re_match_context_t *mctx,
+ re_dfastate_t *pstate)
+ internal_function;
+#endif /* RE_ENABLE_I18N */
+static reg_errcode_t transit_state_bkref (re_match_context_t *mctx,
+ const re_node_set *nodes)
+ internal_function;
+static reg_errcode_t get_subexp (re_match_context_t *mctx,
+ int bkref_node, int bkref_str_idx)
+ internal_function;
+static reg_errcode_t get_subexp_sub (re_match_context_t *mctx,
+ const re_sub_match_top_t *sub_top,
+ re_sub_match_last_t *sub_last,
+ int bkref_node, int bkref_str)
+ internal_function;
+static int find_subexp_node (const re_dfa_t *dfa, const re_node_set *nodes,
+ int subexp_idx, int type) internal_function;
+static reg_errcode_t check_arrival (re_match_context_t *mctx,
+ state_array_t *path, int top_node,
+ int top_str, int last_node, int last_str,
+ int type) internal_function;
+static reg_errcode_t check_arrival_add_next_nodes (re_match_context_t *mctx,
+ int str_idx,
+ re_node_set *cur_nodes,
+ re_node_set *next_nodes)
+ internal_function;
+static reg_errcode_t check_arrival_expand_ecl (const re_dfa_t *dfa,
+ re_node_set *cur_nodes,
+ int ex_subexp, int type)
+ internal_function;
+static reg_errcode_t check_arrival_expand_ecl_sub (const re_dfa_t *dfa,
+ re_node_set *dst_nodes,
+ int target, int ex_subexp,
+ int type) internal_function;
+static reg_errcode_t expand_bkref_cache (re_match_context_t *mctx,
+ re_node_set *cur_nodes, int cur_str,
+ int subexp_num, int type)
+ internal_function;
+static int build_trtable (const re_dfa_t *dfa,
+ re_dfastate_t *state) internal_function;
+#ifdef RE_ENABLE_I18N
+static int check_node_accept_bytes (const re_dfa_t *dfa, int node_idx,
+ const re_string_t *input, int idx)
+ internal_function;
+# ifdef _LIBC
+static unsigned int find_collation_sequence_value (const unsigned char *mbs,
+ size_t name_len)
+ internal_function;
+# endif /* _LIBC */
+#endif /* RE_ENABLE_I18N */
+static int group_nodes_into_DFAstates (const re_dfa_t *dfa,
+ const re_dfastate_t *state,
+ re_node_set *states_node,
+ bitset_t *states_ch) internal_function;
+static int check_node_accept (const re_match_context_t *mctx,
+ const re_token_t *node, int idx)
+ internal_function;
+static reg_errcode_t extend_buffers (re_match_context_t *mctx)
+ internal_function;
+
+/* Entry point for POSIX code. */
+
+/* regexec searches for a given pattern, specified by PREG, in the
+ string STRING.
+
+ If NMATCH is zero or REG_NOSUB was set in the cflags argument to
+ `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at
+ least NMATCH elements, and we set them to the offsets of the
+ corresponding matched substrings.
+
+ EFLAGS specifies `execution flags' which affect matching: if
+ REG_NOTBOL is set, then ^ does not match at the beginning of the
+ string; if REG_NOTEOL is set, then $ does not match at the end.
+
+ We return 0 if we find a match and REG_NOMATCH if not. */
+
+int
+regexec (
+ const regex_t *__restrict preg,
+ const char *__restrict string,
+ size_t nmatch,
+ regmatch_t pmatch[],
+ int eflags)
+{
+ reg_errcode_t err;
+ int start, length;
+
+ if (eflags & ~(REG_NOTBOL | REG_NOTEOL | REG_STARTEND))
+ return REG_BADPAT;
+
+ if (eflags & REG_STARTEND)
+ {
+ start = pmatch[0].rm_so;
+ length = pmatch[0].rm_eo;
+ }
+ else
+ {
+ start = 0;
+ length = strlen (string);
+ }
+
+ __libc_lock_lock (dfa->lock);
+ if (preg->no_sub)
+ err = re_search_internal (preg, string, length, start, length - start,
+ length, 0, NULL, eflags);
+ else
+ err = re_search_internal (preg, string, length, start, length - start,
+ length, nmatch, pmatch, eflags);
+ __libc_lock_unlock (dfa->lock);
+ return err != REG_NOERROR;
+}
+
+#ifdef _LIBC
+# include <shlib-compat.h>
+versioned_symbol (libc, __regexec, regexec, GLIBC_2_3_4);
+
+# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3_4)
+__typeof__ (__regexec) __compat_regexec;
+
+int
+attribute_compat_text_section
+__compat_regexec (const regex_t *__restrict preg,
+ const char *__restrict string, size_t nmatch,
+ regmatch_t pmatch[], int eflags)
+{
+ return regexec (preg, string, nmatch, pmatch,
+ eflags & (REG_NOTBOL | REG_NOTEOL));
+}
+compat_symbol (libc, __compat_regexec, regexec, GLIBC_2_0);
+# endif
+#endif
+
+/* Entry points for GNU code. */
+
+/* re_match, re_search, re_match_2, re_search_2
+
+ The former two functions operate on STRING with length LENGTH,
+ while the later two operate on concatenation of STRING1 and STRING2
+ with lengths LENGTH1 and LENGTH2, respectively.
+
+ re_match() matches the compiled pattern in BUFP against the string,
+ starting at index START.
+
+ re_search() first tries matching at index START, then it tries to match
+ starting from index START + 1, and so on. The last start position tried
+ is START + RANGE. (Thus RANGE = 0 forces re_search to operate the same
+ way as re_match().)
+
+ The parameter STOP of re_{match,search}_2 specifies that no match exceeding
+ the first STOP characters of the concatenation of the strings should be
+ concerned.
+
+ If REGS is not NULL, and BUFP->no_sub is not set, the offsets of the match
+ and all groups is stroed in REGS. (For the "_2" variants, the offsets are
+ computed relative to the concatenation, not relative to the individual
+ strings.)
+
+ On success, re_match* functions return the length of the match, re_search*
+ return the position of the start of the match. Return value -1 means no
+ match was found and -2 indicates an internal error. */
+
+int
+re_match (struct re_pattern_buffer *bufp,
+ const char *string,
+ int length,
+ int start,
+ struct re_registers *regs)
+{
+ return re_search_stub (bufp, string, length, start, 0, length, regs, 1);
+}
+#ifdef _LIBC
+weak_alias (__re_match, re_match)
+#endif
+
+int
+re_search (struct re_pattern_buffer *bufp,
+ const char *string,
+ int length, int start, int range,
+ struct re_registers *regs)
+{
+ return re_search_stub (bufp, string, length, start, range, length, regs, 0);
+}
+#ifdef _LIBC
+weak_alias (__re_search, re_search)
+#endif
+
+int
+re_match_2 (struct re_pattern_buffer *bufp,
+ const char *string1, int length1,
+ const char *string2, int length2, int start,
+ struct re_registers *regs, int stop)
+{
+ return re_search_2_stub (bufp, string1, length1, string2, length2,
+ start, 0, regs, stop, 1);
+}
+#ifdef _LIBC
+weak_alias (__re_match_2, re_match_2)
+#endif
+
+int
+re_search_2 (struct re_pattern_buffer *bufp,
+ const char *string1, int length1,
+ const char *string2, int length2, int start,
+ int range, struct re_registers *regs, int stop)
+{
+ return re_search_2_stub (bufp, string1, length1, string2, length2,
+ start, range, regs, stop, 0);
+}
+#ifdef _LIBC
+weak_alias (__re_search_2, re_search_2)
+#endif
+
+static int
+re_search_2_stub (struct re_pattern_buffer *bufp,
+ const char *string1, int length1,
+ const char *string2, int length2, int start,
+ int range, struct re_registers *regs,
+ int stop, int ret_len)
+{
+ const char *str;
+ int rval;
+ int len = length1 + length2;
+ int free_str = 0;
+
+ if (BE (length1 < 0 || length2 < 0 || stop < 0, 0))
+ return -2;
+
+ /* Concatenate the strings. */
+ if (length2 > 0)
+ if (length1 > 0)
+ {
+ char *s = re_malloc (char, len);
+
+ if (BE (s == NULL, 0))
+ return -2;
+ memcpy (s, string1, length1);
+ memcpy (s + length1, string2, length2);
+ str = s;
+ free_str = 1;
+ }
+ else
+ str = string2;
+ else
+ str = string1;
+
+ rval = re_search_stub (bufp, str, len, start, range, stop, regs, ret_len);
+ if (free_str)
+ re_free ((char *) str);
+ return rval;
+}
+
+/* The parameters have the same meaning as those of re_search.
+ Additional parameters:
+ If RET_LEN is nonzero the length of the match is returned (re_match style);
+ otherwise the position of the match is returned. */
+
+static int
+re_search_stub (struct re_pattern_buffer *bufp,
+ const char *string, int length, int start,
+ int range, int stop,
+ struct re_registers *regs, int ret_len)
+{
+ reg_errcode_t result;
+ regmatch_t *pmatch;
+ int nregs, rval;
+ int eflags = 0;
+
+ /* Check for out-of-range. */
+ if (BE (start < 0 || start > length, 0))
+ return -1;
+ if (BE (start + range > length, 0))
+ range = length - start;
+ else if (BE (start + range < 0, 0))
+ range = -start;
+
+ __libc_lock_lock (dfa->lock);
+
+ eflags |= (bufp->not_bol) ? REG_NOTBOL : 0;
+ eflags |= (bufp->not_eol) ? REG_NOTEOL : 0;
+
+ /* Compile fastmap if we haven't yet. */
+ if (range > 0 && bufp->fastmap != NULL && !bufp->fastmap_accurate)
+ re_compile_fastmap (bufp);
+
+ if (BE (bufp->no_sub, 0))
+ regs = NULL;
+
+ /* We need at least 1 register. */
+ if (regs == NULL)
+ nregs = 1;
+ else if (BE (bufp->regs_allocated == REGS_FIXED &&
+ regs->num_regs < bufp->re_nsub + 1, 0))
+ {
+ nregs = regs->num_regs;
+ if (BE (nregs < 1, 0))
+ {
+ /* Nothing can be copied to regs. */
+ regs = NULL;
+ nregs = 1;
+ }
+ }
+ else
+ nregs = bufp->re_nsub + 1;
+ pmatch = re_malloc (regmatch_t, nregs);
+ if (BE (pmatch == NULL, 0))
+ {
+ rval = -2;
+ goto out;
+ }
+
+ result = re_search_internal (bufp, string, length, start, range, stop,
+ nregs, pmatch, eflags);
+
+ rval = 0;
+
+ /* I hope we needn't fill ther regs with -1's when no match was found. */
+ if (result != REG_NOERROR)
+ rval = -1;
+ else if (regs != NULL)
+ {
+ /* If caller wants register contents data back, copy them. */
+ bufp->regs_allocated = re_copy_regs (regs, pmatch, nregs,
+ bufp->regs_allocated);
+ if (BE (bufp->regs_allocated == REGS_UNALLOCATED, 0))
+ rval = -2;
+ }
+
+ if (BE (rval == 0, 1))
+ {
+ if (ret_len)
+ {
+ assert (pmatch[0].rm_so == start);
+ rval = pmatch[0].rm_eo - start;
+ }
+ else
+ rval = pmatch[0].rm_so;
+ }
+ re_free (pmatch);
+ out:
+ __libc_lock_unlock (dfa->lock);
+ return rval;
+}
+
+static unsigned
+re_copy_regs (struct re_registers *regs,
+ regmatch_t *pmatch,
+ unsigned int nregs, int regs_allocated)
+{
+ int rval = REGS_REALLOCATE;
+ unsigned int i;
+ unsigned int need_regs = nregs + 1;
+ /* We need one extra element beyond `num_regs' for the `-1' marker GNU code
+ uses. */
+
+ /* Have the register data arrays been allocated? */
+ if (regs_allocated == REGS_UNALLOCATED)
+ { /* No. So allocate them with malloc. */
+ regs->start = re_malloc (regoff_t, need_regs);
+ if (BE (regs->start == NULL, 0))
+ return REGS_UNALLOCATED;
+ regs->end = re_malloc (regoff_t, need_regs);
+ if (BE (regs->end == NULL, 0))
+ {
+ re_free (regs->start);
+ return REGS_UNALLOCATED;
+ }
+ regs->num_regs = need_regs;
+ }
+ else if (regs_allocated == REGS_REALLOCATE)
+ { /* Yes. If we need more elements than were already
+ allocated, reallocate them. If we need fewer, just
+ leave it alone. */
+ if (BE (need_regs > regs->num_regs, 0))
+ {
+ regoff_t *new_start = re_realloc (regs->start, regoff_t, need_regs);
+ regoff_t *new_end;
+ if (BE (new_start == NULL, 0))
+ return REGS_UNALLOCATED;
+ new_end = re_realloc (regs->end, regoff_t, need_regs);
+ if (BE (new_end == NULL, 0))
+ {
+ re_free (new_start);
+ return REGS_UNALLOCATED;
+ }
+ regs->start = new_start;
+ regs->end = new_end;
+ regs->num_regs = need_regs;
+ }
+ }
+ else
+ {
+ assert (regs_allocated == REGS_FIXED);
+ /* This function may not be called with REGS_FIXED and nregs too big. */
+ assert (regs->num_regs >= nregs);
+ rval = REGS_FIXED;
+ }
+
+ /* Copy the regs. */
+ for (i = 0; i < nregs; ++i)
+ {
+ regs->start[i] = pmatch[i].rm_so;
+ regs->end[i] = pmatch[i].rm_eo;
+ }
+ for ( ; i < regs->num_regs; ++i)
+ regs->start[i] = regs->end[i] = -1;
+
+ return rval;
+}
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+ ENDS. Subsequent matches using PATTERN_BUFFER and REGS will use
+ this memory for recording register information. STARTS and ENDS
+ must be allocated using the malloc library routine, and must each
+ be at least NUM_REGS * sizeof (regoff_t) bytes long.
+
+ If NUM_REGS == 0, then subsequent matches should allocate their own
+ register data.
+
+ Unless this function is called, the first search or match using
+ PATTERN_BUFFER will allocate its own register data, without
+ freeing the old data. */
+
+void
+re_set_registers (struct re_pattern_buffer *bufp,
+ struct re_registers *regs,
+ unsigned num_regs,
+ regoff_t *starts,
+ regoff_t *ends)
+{
+ if (num_regs)
+ {
+ bufp->regs_allocated = REGS_REALLOCATE;
+ regs->num_regs = num_regs;
+ regs->start = starts;
+ regs->end = ends;
+ }
+ else
+ {
+ bufp->regs_allocated = REGS_UNALLOCATED;
+ regs->num_regs = 0;
+ regs->start = regs->end = (regoff_t *) 0;
+ }
+}
+#ifdef _LIBC
+weak_alias (__re_set_registers, re_set_registers)
+#endif
+
+/* Entry points compatible with 4.2 BSD regex library. We don't define
+ them unless specifically requested. */
+
+#if defined _REGEX_RE_COMP || defined _LIBC
+int
+# ifdef _LIBC
+weak_function
+# endif
+re_exec (s)
+ const char *s;
+{
+ return 0 == regexec (&re_comp_buf, s, 0, NULL, 0);
+}
+#endif /* _REGEX_RE_COMP */
+
+/* Internal entry point. */
+
+/* Searches for a compiled pattern PREG in the string STRING, whose
+ length is LENGTH. NMATCH, PMATCH, and EFLAGS have the same
+ mingings with regexec. START, and RANGE have the same meanings
+ with re_search.
+ Return REG_NOERROR if we find a match, and REG_NOMATCH if not,
+ otherwise return the error code.
+ Note: We assume front end functions already check ranges.
+ (START + RANGE >= 0 && START + RANGE <= LENGTH) */
+
+static reg_errcode_t
+re_search_internal (const regex_t *preg,
+ const char *string,
+ int length, int start, int range, int stop,
+ size_t nmatch, regmatch_t pmatch[],
+ int eflags)
+{
+ reg_errcode_t err;
+ const re_dfa_t *dfa = (const re_dfa_t *) preg->buffer;
+ int left_lim, right_lim, incr;
+ int fl_longest_match, match_first, match_kind, match_last = -1;
+ unsigned int extra_nmatch;
+ int sb, ch;
+#if defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)
+ re_match_context_t mctx = { .dfa = dfa };
+#else
+ re_match_context_t mctx;
+#endif
+ char *fastmap = (preg->fastmap != NULL && preg->fastmap_accurate
+ && range && !preg->can_be_null) ? preg->fastmap : NULL;
+ RE_TRANSLATE_TYPE t = preg->translate;
+
+#if !(defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L))
+ memset (&mctx, '\0', sizeof (re_match_context_t));
+ mctx.dfa = dfa;
+#endif
+
+ extra_nmatch = (nmatch > preg->re_nsub) ? nmatch - (preg->re_nsub + 1) : 0;
+ nmatch -= extra_nmatch;
+
+ /* Check if the DFA haven't been compiled. */
+ if (BE (preg->used == 0 || dfa->init_state == NULL
+ || dfa->init_state_word == NULL || dfa->init_state_nl == NULL
+ || dfa->init_state_begbuf == NULL, 0))
+ return REG_NOMATCH;
+
+#ifdef DEBUG
+ /* We assume front-end functions already check them. */
+ assert (start + range >= 0 && start + range <= length);
+#endif
+
+ /* If initial states with non-begbuf contexts have no elements,
+ the regex must be anchored. If preg->newline_anchor is set,
+ we'll never use init_state_nl, so do not check it. */
+ if (dfa->init_state->nodes.nelem == 0
+ && dfa->init_state_word->nodes.nelem == 0
+ && (dfa->init_state_nl->nodes.nelem == 0
+ || !preg->newline_anchor))
+ {
+ if (start != 0 && start + range != 0)
+ return REG_NOMATCH;
+ start = range = 0;
+ }
+
+ /* We must check the longest matching, if nmatch > 0. */
+ fl_longest_match = (nmatch != 0 || dfa->nbackref);
+
+ err = re_string_allocate (&mctx.input, string, length, dfa->nodes_len + 1,
+ preg->translate, preg->syntax & RE_ICASE, dfa);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+ mctx.input.stop = stop;
+ mctx.input.raw_stop = stop;
+ mctx.input.newline_anchor = preg->newline_anchor;
+
+ err = match_ctx_init (&mctx, eflags, dfa->nbackref * 2);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+
+ /* We will log all the DFA states through which the dfa pass,
+ if nmatch > 1, or this dfa has "multibyte node", which is a
+ back-reference or a node which can accept multibyte character or
+ multi character collating element. */
+ if (nmatch > 1 || dfa->has_mb_node)
+ {
+ /* Avoid overflow. */
+ if (BE (SIZE_MAX / sizeof (re_dfastate_t *) <= mctx.input.bufs_len, 0))
+ {
+ err = REG_ESPACE;
+ goto free_return;
+ }
+
+ mctx.state_log = re_malloc (re_dfastate_t *, mctx.input.bufs_len + 1);
+ if (BE (mctx.state_log == NULL, 0))
+ {
+ err = REG_ESPACE;
+ goto free_return;
+ }
+ }
+ else
+ mctx.state_log = NULL;
+
+ match_first = start;
+ mctx.input.tip_context = (eflags & REG_NOTBOL) ? CONTEXT_BEGBUF
+ : CONTEXT_NEWLINE | CONTEXT_BEGBUF;
+
+ /* Check incrementally whether of not the input string match. */
+ incr = (range < 0) ? -1 : 1;
+ left_lim = (range < 0) ? start + range : start;
+ right_lim = (range < 0) ? start : start + range;
+ sb = dfa->mb_cur_max == 1;
+ match_kind =
+ (fastmap
+ ? ((sb || !(preg->syntax & RE_ICASE || t) ? 4 : 0)
+ | (range >= 0 ? 2 : 0)
+ | (t != NULL ? 1 : 0))
+ : 8);
+
+ for (;; match_first += incr)
+ {
+ err = REG_NOMATCH;
+ if (match_first < left_lim || right_lim < match_first)
+ goto free_return;
+
+ /* Advance as rapidly as possible through the string, until we
+ find a plausible place to start matching. This may be done
+ with varying efficiency, so there are various possibilities:
+ only the most common of them are specialized, in order to
+ save on code size. We use a switch statement for speed. */
+ switch (match_kind)
+ {
+ case 8:
+ /* No fastmap. */
+ break;
+
+ case 7:
+ /* Fastmap with single-byte translation, match forward. */
+ while (BE (match_first < right_lim, 1)
+ && !fastmap[t[(unsigned char) string[match_first]]])
+ ++match_first;
+ goto forward_match_found_start_or_reached_end;
+
+ case 6:
+ /* Fastmap without translation, match forward. */
+ while (BE (match_first < right_lim, 1)
+ && !fastmap[(unsigned char) string[match_first]])
+ ++match_first;
+
+ forward_match_found_start_or_reached_end:
+ if (BE (match_first == right_lim, 0))
+ {
+ ch = match_first >= length
+ ? 0 : (unsigned char) string[match_first];
+ if (!fastmap[t ? t[ch] : ch])
+ goto free_return;
+ }
+ break;
+
+ case 4:
+ case 5:
+ /* Fastmap without multi-byte translation, match backwards. */
+ while (match_first >= left_lim)
+ {
+ ch = match_first >= length
+ ? 0 : (unsigned char) string[match_first];
+ if (fastmap[t ? t[ch] : ch])
+ break;
+ --match_first;
+ }
+ if (match_first < left_lim)
+ goto free_return;
+ break;
+
+ default:
+ /* In this case, we can't determine easily the current byte,
+ since it might be a component byte of a multibyte
+ character. Then we use the constructed buffer instead. */
+ for (;;)
+ {
+ /* If MATCH_FIRST is out of the valid range, reconstruct the
+ buffers. */
+ unsigned int offset = match_first - mctx.input.raw_mbs_idx;
+ if (BE (offset >= (unsigned int) mctx.input.valid_raw_len, 0))
+ {
+ err = re_string_reconstruct (&mctx.input, match_first,
+ eflags);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+
+ offset = match_first - mctx.input.raw_mbs_idx;
+ }
+ /* If MATCH_FIRST is out of the buffer, leave it as '\0'.
+ Note that MATCH_FIRST must not be smaller than 0. */
+ ch = (match_first >= length
+ ? 0 : re_string_byte_at (&mctx.input, offset));
+ if (fastmap[ch])
+ break;
+ match_first += incr;
+ if (match_first < left_lim || match_first > right_lim)
+ {
+ err = REG_NOMATCH;
+ goto free_return;
+ }
+ }
+ break;
+ }
+
+ /* Reconstruct the buffers so that the matcher can assume that
+ the matching starts from the beginning of the buffer. */
+ err = re_string_reconstruct (&mctx.input, match_first, eflags);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+
+#ifdef RE_ENABLE_I18N
+ /* Don't consider this char as a possible match start if it part,
+ yet isn't the head, of a multibyte character. */
+ if (!sb && !re_string_first_byte (&mctx.input, 0))
+ continue;
+#endif
+
+ /* It seems to be appropriate one, then use the matcher. */
+ /* We assume that the matching starts from 0. */
+ mctx.state_log_top = mctx.nbkref_ents = mctx.max_mb_elem_len = 0;
+ match_last = check_matching (&mctx, fl_longest_match,
+ range >= 0 ? &match_first : NULL);
+ if (match_last != -1)
+ {
+ if (BE (match_last == -2, 0))
+ {
+ err = REG_ESPACE;
+ goto free_return;
+ }
+ else
+ {
+ mctx.match_last = match_last;
+ if ((!preg->no_sub && nmatch > 1) || dfa->nbackref)
+ {
+ re_dfastate_t *pstate = mctx.state_log[match_last];
+ mctx.last_node = check_halt_state_context (&mctx, pstate,
+ match_last);
+ }
+ if ((!preg->no_sub && nmatch > 1 && dfa->has_plural_match)
+ || dfa->nbackref)
+ {
+ err = prune_impossible_nodes (&mctx);
+ if (err == REG_NOERROR)
+ break;
+ if (BE (err != REG_NOMATCH, 0))
+ goto free_return;
+ match_last = -1;
+ }
+ else
+ break; /* We found a match. */
+ }
+ }
+
+ match_ctx_clean (&mctx);
+ }
+
+#ifdef DEBUG
+ assert (match_last != -1);
+ assert (err == REG_NOERROR);
+#endif
+
+ /* Set pmatch[] if we need. */
+ if (nmatch > 0)
+ {
+ unsigned int reg_idx;
+
+ /* Initialize registers. */
+ for (reg_idx = 1; reg_idx < nmatch; ++reg_idx)
+ pmatch[reg_idx].rm_so = pmatch[reg_idx].rm_eo = -1;
+
+ /* Set the points where matching start/end. */
+ pmatch[0].rm_so = 0;
+ pmatch[0].rm_eo = mctx.match_last;
+
+ if (!preg->no_sub && nmatch > 1)
+ {
+ err = set_regs (preg, &mctx, nmatch, pmatch,
+ dfa->has_plural_match && dfa->nbackref > 0);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+ }
+
+ /* At last, add the offset to the each registers, since we slided
+ the buffers so that we could assume that the matching starts
+ from 0. */
+ for (reg_idx = 0; reg_idx < nmatch; ++reg_idx)
+ if (pmatch[reg_idx].rm_so != -1)
+ {
+#ifdef RE_ENABLE_I18N
+ if (BE (mctx.input.offsets_needed != 0, 0))
+ {
+ pmatch[reg_idx].rm_so =
+ (pmatch[reg_idx].rm_so == mctx.input.valid_len
+ ? mctx.input.valid_raw_len
+ : mctx.input.offsets[pmatch[reg_idx].rm_so]);
+ pmatch[reg_idx].rm_eo =
+ (pmatch[reg_idx].rm_eo == mctx.input.valid_len
+ ? mctx.input.valid_raw_len
+ : mctx.input.offsets[pmatch[reg_idx].rm_eo]);
+ }
+#else
+ assert (mctx.input.offsets_needed == 0);
+#endif
+ pmatch[reg_idx].rm_so += match_first;
+ pmatch[reg_idx].rm_eo += match_first;
+ }
+ for (reg_idx = 0; reg_idx < extra_nmatch; ++reg_idx)
+ {
+ pmatch[nmatch + reg_idx].rm_so = -1;
+ pmatch[nmatch + reg_idx].rm_eo = -1;
+ }
+
+ if (dfa->subexp_map)
+ for (reg_idx = 0; reg_idx + 1 < nmatch; reg_idx++)
+ if (dfa->subexp_map[reg_idx] != reg_idx)
+ {
+ pmatch[reg_idx + 1].rm_so
+ = pmatch[dfa->subexp_map[reg_idx] + 1].rm_so;
+ pmatch[reg_idx + 1].rm_eo
+ = pmatch[dfa->subexp_map[reg_idx] + 1].rm_eo;
+ }
+ }
+
+ free_return:
+ re_free (mctx.state_log);
+ if (dfa->nbackref)
+ match_ctx_free (&mctx);
+ re_string_destruct (&mctx.input);
+ return err;
+}
+
+static reg_errcode_t
+prune_impossible_nodes (re_match_context_t *mctx)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ int halt_node, match_last;
+ reg_errcode_t ret;
+ re_dfastate_t **sifted_states;
+ re_dfastate_t **lim_states = NULL;
+ re_sift_context_t sctx;
+#ifdef DEBUG
+ assert (mctx->state_log != NULL);
+#endif
+ match_last = mctx->match_last;
+ halt_node = mctx->last_node;
+
+ /* Avoid overflow. */
+ if (BE (SIZE_MAX / sizeof (re_dfastate_t *) <= match_last, 0))
+ return REG_ESPACE;
+
+ sifted_states = re_malloc (re_dfastate_t *, match_last + 1);
+ if (BE (sifted_states == NULL, 0))
+ {
+ ret = REG_ESPACE;
+ goto free_return;
+ }
+ if (dfa->nbackref)
+ {
+ lim_states = re_malloc (re_dfastate_t *, match_last + 1);
+ if (BE (lim_states == NULL, 0))
+ {
+ ret = REG_ESPACE;
+ goto free_return;
+ }
+ while (1)
+ {
+ memset (lim_states, '\0',
+ sizeof (re_dfastate_t *) * (match_last + 1));
+ sift_ctx_init (&sctx, sifted_states, lim_states, halt_node,
+ match_last);
+ ret = sift_states_backward (mctx, &sctx);
+ re_node_set_free (&sctx.limits);
+ if (BE (ret != REG_NOERROR, 0))
+ goto free_return;
+ if (sifted_states[0] != NULL || lim_states[0] != NULL)
+ break;
+ do
+ {
+ --match_last;
+ if (match_last < 0)
+ {
+ ret = REG_NOMATCH;
+ goto free_return;
+ }
+ } while (mctx->state_log[match_last] == NULL
+ || !mctx->state_log[match_last]->halt);
+ halt_node = check_halt_state_context (mctx,
+ mctx->state_log[match_last],
+ match_last);
+ }
+ ret = merge_state_array (dfa, sifted_states, lim_states,
+ match_last + 1);
+ re_free (lim_states);
+ lim_states = NULL;
+ if (BE (ret != REG_NOERROR, 0))
+ goto free_return;
+ }
+ else
+ {
+ sift_ctx_init (&sctx, sifted_states, lim_states, halt_node, match_last);
+ ret = sift_states_backward (mctx, &sctx);
+ re_node_set_free (&sctx.limits);
+ if (BE (ret != REG_NOERROR, 0))
+ goto free_return;
+ if (sifted_states[0] == NULL)
+ {
+ ret = REG_NOMATCH;
+ goto free_return;
+ }
+ }
+ re_free (mctx->state_log);
+ mctx->state_log = sifted_states;
+ sifted_states = NULL;
+ mctx->last_node = halt_node;
+ mctx->match_last = match_last;
+ ret = REG_NOERROR;
+ free_return:
+ re_free (sifted_states);
+ re_free (lim_states);
+ return ret;
+}
+
+/* Acquire an initial state and return it.
+ We must select appropriate initial state depending on the context,
+ since initial states may have constraints like "\<", "^", etc.. */
+
+static inline re_dfastate_t *
+__attribute ((always_inline)) internal_function
+acquire_init_state_context (reg_errcode_t *err, const re_match_context_t *mctx,
+ int idx)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ if (dfa->init_state->has_constraint)
+ {
+ unsigned int context;
+ context = re_string_context_at (&mctx->input, idx - 1, mctx->eflags);
+ if (IS_WORD_CONTEXT (context))
+ return dfa->init_state_word;
+ else if (IS_ORDINARY_CONTEXT (context))
+ return dfa->init_state;
+ else if (IS_BEGBUF_CONTEXT (context) && IS_NEWLINE_CONTEXT (context))
+ return dfa->init_state_begbuf;
+ else if (IS_NEWLINE_CONTEXT (context))
+ return dfa->init_state_nl;
+ else if (IS_BEGBUF_CONTEXT (context))
+ {
+ /* It is relatively rare case, then calculate on demand. */
+ return re_acquire_state_context (err, dfa,
+ dfa->init_state->entrance_nodes,
+ context);
+ }
+ else
+ /* Must not happen? */
+ return dfa->init_state;
+ }
+ else
+ return dfa->init_state;
+}
+
+/* Check whether the regular expression match input string INPUT or not,
+ and return the index where the matching end, return -1 if not match,
+ or return -2 in case of an error.
+ FL_LONGEST_MATCH means we want the POSIX longest matching.
+ If P_MATCH_FIRST is not NULL, and the match fails, it is set to the
+ next place where we may want to try matching.
+ Note that the matcher assume that the maching starts from the current
+ index of the buffer. */
+
+static int
+internal_function
+check_matching (re_match_context_t *mctx, int fl_longest_match,
+ int *p_match_first)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ reg_errcode_t err;
+ int match = 0;
+ int match_last = -1;
+ int cur_str_idx = re_string_cur_idx (&mctx->input);
+ re_dfastate_t *cur_state;
+ int at_init_state = p_match_first != NULL;
+ int next_start_idx = cur_str_idx;
+
+ err = REG_NOERROR;
+ cur_state = acquire_init_state_context (&err, mctx, cur_str_idx);
+ /* An initial state must not be NULL (invalid). */
+ if (BE (cur_state == NULL, 0))
+ {
+ assert (err == REG_ESPACE);
+ return -2;
+ }
+
+ if (mctx->state_log != NULL)
+ {
+ mctx->state_log[cur_str_idx] = cur_state;
+
+ /* Check OP_OPEN_SUBEXP in the initial state in case that we use them
+ later. E.g. Processing back references. */
+ if (BE (dfa->nbackref, 0))
+ {
+ at_init_state = 0;
+ err = check_subexp_matching_top (mctx, &cur_state->nodes, 0);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+
+ if (cur_state->has_backref)
+ {
+ err = transit_state_bkref (mctx, &cur_state->nodes);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ }
+ }
+
+ /* If the RE accepts NULL string. */
+ if (BE (cur_state->halt, 0))
+ {
+ if (!cur_state->has_constraint
+ || check_halt_state_context (mctx, cur_state, cur_str_idx))
+ {
+ if (!fl_longest_match)
+ return cur_str_idx;
+ else
+ {
+ match_last = cur_str_idx;
+ match = 1;
+ }
+ }
+ }
+
+ while (!re_string_eoi (&mctx->input))
+ {
+ re_dfastate_t *old_state = cur_state;
+ int next_char_idx = re_string_cur_idx (&mctx->input) + 1;
+
+ if (BE (next_char_idx >= mctx->input.bufs_len, 0)
+ || (BE (next_char_idx >= mctx->input.valid_len, 0)
+ && mctx->input.valid_len < mctx->input.len))
+ {
+ err = extend_buffers (mctx);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ assert (err == REG_ESPACE);
+ return -2;
+ }
+ }
+
+ cur_state = transit_state (&err, mctx, cur_state);
+ if (mctx->state_log != NULL)
+ cur_state = merge_state_with_log (&err, mctx, cur_state);
+
+ if (cur_state == NULL)
+ {
+ /* Reached the invalid state or an error. Try to recover a valid
+ state using the state log, if available and if we have not
+ already found a valid (even if not the longest) match. */
+ if (BE (err != REG_NOERROR, 0))
+ return -2;
+
+ if (mctx->state_log == NULL
+ || (match && !fl_longest_match)
+ || (cur_state = find_recover_state (&err, mctx)) == NULL)
+ break;
+ }
+
+ if (BE (at_init_state, 0))
+ {
+ if (old_state == cur_state)
+ next_start_idx = next_char_idx;
+ else
+ at_init_state = 0;
+ }
+
+ if (cur_state->halt)
+ {
+ /* Reached a halt state.
+ Check the halt state can satisfy the current context. */
+ if (!cur_state->has_constraint
+ || check_halt_state_context (mctx, cur_state,
+ re_string_cur_idx (&mctx->input)))
+ {
+ /* We found an appropriate halt state. */
+ match_last = re_string_cur_idx (&mctx->input);
+ match = 1;
+
+ /* We found a match, do not modify match_first below. */
+ p_match_first = NULL;
+ if (!fl_longest_match)
+ break;
+ }
+ }
+ }
+
+ if (p_match_first)
+ *p_match_first += next_start_idx;
+
+ return match_last;
+}
+
+/* Check NODE match the current context. */
+
+static int
+internal_function
+check_halt_node_context (const re_dfa_t *dfa, int node, unsigned int context)
+{
+ re_token_type_t type = dfa->nodes[node].type;
+ unsigned int constraint = dfa->nodes[node].constraint;
+ if (type != END_OF_RE)
+ return 0;
+ if (!constraint)
+ return 1;
+ if (NOT_SATISFY_NEXT_CONSTRAINT (constraint, context))
+ return 0;
+ return 1;
+}
+
+/* Check the halt state STATE match the current context.
+ Return 0 if not match, if the node, STATE has, is a halt node and
+ match the context, return the node. */
+
+static int
+internal_function
+check_halt_state_context (const re_match_context_t *mctx,
+ const re_dfastate_t *state, int idx)
+{
+ int i;
+ unsigned int context;
+#ifdef DEBUG
+ assert (state->halt);
+#endif
+ context = re_string_context_at (&mctx->input, idx, mctx->eflags);
+ for (i = 0; i < state->nodes.nelem; ++i)
+ if (check_halt_node_context (mctx->dfa, state->nodes.elems[i], context))
+ return state->nodes.elems[i];
+ return 0;
+}
+
+/* Compute the next node to which "NFA" transit from NODE("NFA" is a NFA
+ corresponding to the DFA).
+ Return the destination node, and update EPS_VIA_NODES, return -1 in case
+ of errors. */
+
+static int
+internal_function
+proceed_next_node (const re_match_context_t *mctx, int nregs, regmatch_t *regs,
+ int *pidx, int node, re_node_set *eps_via_nodes,
+ struct re_fail_stack_t *fs)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ int i, err;
+ if (IS_EPSILON_NODE (dfa->nodes[node].type))
+ {
+ re_node_set *cur_nodes = &mctx->state_log[*pidx]->nodes;
+ re_node_set *edests = &dfa->edests[node];
+ int dest_node;
+ err = re_node_set_insert (eps_via_nodes, node);
+ if (BE (err < 0, 0))
+ return -2;
+ /* Pick up a valid destination, or return -1 if none is found. */
+ for (dest_node = -1, i = 0; i < edests->nelem; ++i)
+ {
+ int candidate = edests->elems[i];
+ if (!re_node_set_contains (cur_nodes, candidate))
+ continue;
+ if (dest_node == -1)
+ dest_node = candidate;
+
+ else
+ {
+ /* In order to avoid infinite loop like "(a*)*", return the second
+ epsilon-transition if the first was already considered. */
+ if (re_node_set_contains (eps_via_nodes, dest_node))
+ return candidate;
+
+ /* Otherwise, push the second epsilon-transition on the fail stack. */
+ else if (fs != NULL
+ && push_fail_stack (fs, *pidx, candidate, nregs, regs,
+ eps_via_nodes))
+ return -2;
+
+ /* We know we are going to exit. */
+ break;
+ }
+ }
+ return dest_node;
+ }
+ else
+ {
+ int naccepted = 0;
+ re_token_type_t type = dfa->nodes[node].type;
+
+#ifdef RE_ENABLE_I18N
+ if (dfa->nodes[node].accept_mb)
+ naccepted = check_node_accept_bytes (dfa, node, &mctx->input, *pidx);
+ else
+#endif /* RE_ENABLE_I18N */
+ if (type == OP_BACK_REF)
+ {
+ int subexp_idx = dfa->nodes[node].opr.idx + 1;
+ naccepted = regs[subexp_idx].rm_eo - regs[subexp_idx].rm_so;
+ if (fs != NULL)
+ {
+ if (regs[subexp_idx].rm_so == -1 || regs[subexp_idx].rm_eo == -1)
+ return -1;
+ else if (naccepted)
+ {
+ char *buf = (char *) re_string_get_buffer (&mctx->input);
+ if (memcmp (buf + regs[subexp_idx].rm_so, buf + *pidx,
+ naccepted) != 0)
+ return -1;
+ }
+ }
+
+ if (naccepted == 0)
+ {
+ int dest_node;
+ err = re_node_set_insert (eps_via_nodes, node);
+ if (BE (err < 0, 0))
+ return -2;
+ dest_node = dfa->edests[node].elems[0];
+ if (re_node_set_contains (&mctx->state_log[*pidx]->nodes,
+ dest_node))
+ return dest_node;
+ }
+ }
+
+ if (naccepted != 0
+ || check_node_accept (mctx, dfa->nodes + node, *pidx))
+ {
+ int dest_node = dfa->nexts[node];
+ *pidx = (naccepted == 0) ? *pidx + 1 : *pidx + naccepted;
+ if (fs && (*pidx > mctx->match_last || mctx->state_log[*pidx] == NULL
+ || !re_node_set_contains (&mctx->state_log[*pidx]->nodes,
+ dest_node)))
+ return -1;
+ re_node_set_empty (eps_via_nodes);
+ return dest_node;
+ }
+ }
+ return -1;
+}
+
+static reg_errcode_t
+internal_function
+push_fail_stack (struct re_fail_stack_t *fs, int str_idx, int dest_node,
+ int nregs, regmatch_t *regs, re_node_set *eps_via_nodes)
+{
+ reg_errcode_t err;
+ int num = fs->num++;
+ if (fs->num == fs->alloc)
+ {
+ struct re_fail_stack_ent_t *new_array;
+ new_array = realloc (fs->stack, (sizeof (struct re_fail_stack_ent_t)
+ * fs->alloc * 2));
+ if (new_array == NULL)
+ return REG_ESPACE;
+ fs->alloc *= 2;
+ fs->stack = new_array;
+ }
+ fs->stack[num].idx = str_idx;
+ fs->stack[num].node = dest_node;
+ fs->stack[num].regs = re_malloc (regmatch_t, nregs);
+ if (fs->stack[num].regs == NULL)
+ return REG_ESPACE;
+ memcpy (fs->stack[num].regs, regs, sizeof (regmatch_t) * nregs);
+ err = re_node_set_init_copy (&fs->stack[num].eps_via_nodes, eps_via_nodes);
+ return err;
+}
+
+static int
+internal_function
+pop_fail_stack (struct re_fail_stack_t *fs, int *pidx, int nregs,
+ regmatch_t *regs, re_node_set *eps_via_nodes)
+{
+ int num = --fs->num;
+ assert (num >= 0);
+ *pidx = fs->stack[num].idx;
+ memcpy (regs, fs->stack[num].regs, sizeof (regmatch_t) * nregs);
+ re_node_set_free (eps_via_nodes);
+ re_free (fs->stack[num].regs);
+ *eps_via_nodes = fs->stack[num].eps_via_nodes;
+ return fs->stack[num].node;
+}
+
+/* Set the positions where the subexpressions are starts/ends to registers
+ PMATCH.
+ Note: We assume that pmatch[0] is already set, and
+ pmatch[i].rm_so == pmatch[i].rm_eo == -1 for 0 < i < nmatch. */
+
+static reg_errcode_t
+internal_function
+set_regs (const regex_t *preg, const re_match_context_t *mctx, size_t nmatch,
+ regmatch_t *pmatch, int fl_backtrack)
+{
+ const re_dfa_t *dfa = (const re_dfa_t *) preg->buffer;
+ int idx, cur_node;
+ re_node_set eps_via_nodes;
+ struct re_fail_stack_t *fs;
+ struct re_fail_stack_t fs_body = { 0, 2, NULL };
+ regmatch_t *prev_idx_match;
+ int prev_idx_match_malloced = 0;
+
+#ifdef DEBUG
+ assert (nmatch > 1);
+ assert (mctx->state_log != NULL);
+#endif
+ if (fl_backtrack)
+ {
+ fs = &fs_body;
+ fs->stack = re_malloc (struct re_fail_stack_ent_t, fs->alloc);
+ if (fs->stack == NULL)
+ return REG_ESPACE;
+ }
+ else
+ fs = NULL;
+
+ cur_node = dfa->init_node;
+ re_node_set_init_empty (&eps_via_nodes);
+
+#ifdef HAVE_ALLOCA
+ if (__libc_use_alloca (nmatch * sizeof (regmatch_t)))
+ prev_idx_match = (regmatch_t *) alloca (nmatch * sizeof (regmatch_t));
+ else
+#endif
+ {
+ prev_idx_match = re_malloc (regmatch_t, nmatch);
+ if (prev_idx_match == NULL)
+ {
+ free_fail_stack_return (fs);
+ return REG_ESPACE;
+ }
+ prev_idx_match_malloced = 1;
+ }
+ memcpy (prev_idx_match, pmatch, sizeof (regmatch_t) * nmatch);
+
+ for (idx = pmatch[0].rm_so; idx <= pmatch[0].rm_eo ;)
+ {
+ update_regs (dfa, pmatch, prev_idx_match, cur_node, idx, nmatch);
+
+ if (idx == pmatch[0].rm_eo && cur_node == mctx->last_node)
+ {
+ unsigned int reg_idx;
+ if (fs)
+ {
+ for (reg_idx = 0; reg_idx < nmatch; ++reg_idx)
+ if (pmatch[reg_idx].rm_so > -1 && pmatch[reg_idx].rm_eo == -1)
+ break;
+ if (reg_idx == nmatch)
+ {
+ re_node_set_free (&eps_via_nodes);
+ if (prev_idx_match_malloced)
+ re_free (prev_idx_match);
+ return free_fail_stack_return (fs);
+ }
+ cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch,
+ &eps_via_nodes);
+ }
+ else
+ {
+ re_node_set_free (&eps_via_nodes);
+ if (prev_idx_match_malloced)
+ re_free (prev_idx_match);
+ return REG_NOERROR;
+ }
+ }
+
+ /* Proceed to next node. */
+ cur_node = proceed_next_node (mctx, nmatch, pmatch, &idx, cur_node,
+ &eps_via_nodes, fs);
+
+ if (BE (cur_node < 0, 0))
+ {
+ if (BE (cur_node == -2, 0))
+ {
+ re_node_set_free (&eps_via_nodes);
+ if (prev_idx_match_malloced)
+ re_free (prev_idx_match);
+ free_fail_stack_return (fs);
+ return REG_ESPACE;
+ }
+ if (fs)
+ cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch,
+ &eps_via_nodes);
+ else
+ {
+ re_node_set_free (&eps_via_nodes);
+ if (prev_idx_match_malloced)
+ re_free (prev_idx_match);
+ return REG_NOMATCH;
+ }
+ }
+ }
+ re_node_set_free (&eps_via_nodes);
+ if (prev_idx_match_malloced)
+ re_free (prev_idx_match);
+ return free_fail_stack_return (fs);
+}
+
+static reg_errcode_t
+internal_function
+free_fail_stack_return (struct re_fail_stack_t *fs)
+{
+ if (fs)
+ {
+ int fs_idx;
+ for (fs_idx = 0; fs_idx < fs->num; ++fs_idx)
+ {
+ re_node_set_free (&fs->stack[fs_idx].eps_via_nodes);
+ re_free (fs->stack[fs_idx].regs);
+ }
+ re_free (fs->stack);
+ }
+ return REG_NOERROR;
+}
+
+static void
+internal_function
+update_regs (const re_dfa_t *dfa, regmatch_t *pmatch,
+ regmatch_t *prev_idx_match, int cur_node, int cur_idx, int nmatch)
+{
+ int type = dfa->nodes[cur_node].type;
+ if (type == OP_OPEN_SUBEXP)
+ {
+ int reg_num = dfa->nodes[cur_node].opr.idx + 1;
+
+ /* We are at the first node of this sub expression. */
+ if (reg_num < nmatch)
+ {
+ pmatch[reg_num].rm_so = cur_idx;
+ pmatch[reg_num].rm_eo = -1;
+ }
+ }
+ else if (type == OP_CLOSE_SUBEXP)
+ {
+ int reg_num = dfa->nodes[cur_node].opr.idx + 1;
+ if (reg_num < nmatch)
+ {
+ /* We are at the last node of this sub expression. */
+ if (pmatch[reg_num].rm_so < cur_idx)
+ {
+ pmatch[reg_num].rm_eo = cur_idx;
+ /* This is a non-empty match or we are not inside an optional
+ subexpression. Accept this right away. */
+ memcpy (prev_idx_match, pmatch, sizeof (regmatch_t) * nmatch);
+ }
+ else
+ {
+ if (dfa->nodes[cur_node].opt_subexp
+ && prev_idx_match[reg_num].rm_so != -1)
+ /* We transited through an empty match for an optional
+ subexpression, like (a?)*, and this is not the subexp's
+ first match. Copy back the old content of the registers
+ so that matches of an inner subexpression are undone as
+ well, like in ((a?))*. */
+ memcpy (pmatch, prev_idx_match, sizeof (regmatch_t) * nmatch);
+ else
+ /* We completed a subexpression, but it may be part of
+ an optional one, so do not update PREV_IDX_MATCH. */
+ pmatch[reg_num].rm_eo = cur_idx;
+ }
+ }
+ }
+}
+
+/* This function checks the STATE_LOG from the SCTX->last_str_idx to 0
+ and sift the nodes in each states according to the following rules.
+ Updated state_log will be wrote to STATE_LOG.
+
+ Rules: We throw away the Node `a' in the STATE_LOG[STR_IDX] if...
+ 1. When STR_IDX == MATCH_LAST(the last index in the state_log):
+ If `a' isn't the LAST_NODE and `a' can't epsilon transit to
+ the LAST_NODE, we throw away the node `a'.
+ 2. When 0 <= STR_IDX < MATCH_LAST and `a' accepts
+ string `s' and transit to `b':
+ i. If 'b' isn't in the STATE_LOG[STR_IDX+strlen('s')], we throw
+ away the node `a'.
+ ii. If 'b' is in the STATE_LOG[STR_IDX+strlen('s')] but 'b' is
+ thrown away, we throw away the node `a'.
+ 3. When 0 <= STR_IDX < MATCH_LAST and 'a' epsilon transit to 'b':
+ i. If 'b' isn't in the STATE_LOG[STR_IDX], we throw away the
+ node `a'.
+ ii. If 'b' is in the STATE_LOG[STR_IDX] but 'b' is thrown away,
+ we throw away the node `a'. */
+
+#define STATE_NODE_CONTAINS(state,node) \
+ ((state) != NULL && re_node_set_contains (&(state)->nodes, node))
+
+static reg_errcode_t
+internal_function
+sift_states_backward (const re_match_context_t *mctx, re_sift_context_t *sctx)
+{
+ reg_errcode_t err;
+ int null_cnt = 0;
+ int str_idx = sctx->last_str_idx;
+ re_node_set cur_dest;
+
+#ifdef DEBUG
+ assert (mctx->state_log != NULL && mctx->state_log[str_idx] != NULL);
+#endif
+
+ /* Build sifted state_log[str_idx]. It has the nodes which can epsilon
+ transit to the last_node and the last_node itself. */
+ err = re_node_set_init_1 (&cur_dest, sctx->last_node);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ err = update_cur_sifted_state (mctx, sctx, str_idx, &cur_dest);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+
+ /* Then check each states in the state_log. */
+ while (str_idx > 0)
+ {
+ /* Update counters. */
+ null_cnt = (sctx->sifted_states[str_idx] == NULL) ? null_cnt + 1 : 0;
+ if (null_cnt > mctx->max_mb_elem_len)
+ {
+ memset (sctx->sifted_states, '\0',
+ sizeof (re_dfastate_t *) * str_idx);
+ re_node_set_free (&cur_dest);
+ return REG_NOERROR;
+ }
+ re_node_set_empty (&cur_dest);
+ --str_idx;
+
+ if (mctx->state_log[str_idx])
+ {
+ err = build_sifted_states (mctx, sctx, str_idx, &cur_dest);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+ }
+
+ /* Add all the nodes which satisfy the following conditions:
+ - It can epsilon transit to a node in CUR_DEST.
+ - It is in CUR_SRC.
+ And update state_log. */
+ err = update_cur_sifted_state (mctx, sctx, str_idx, &cur_dest);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+ }
+ err = REG_NOERROR;
+ free_return:
+ re_node_set_free (&cur_dest);
+ return err;
+}
+
+static reg_errcode_t
+internal_function
+build_sifted_states (const re_match_context_t *mctx, re_sift_context_t *sctx,
+ int str_idx, re_node_set *cur_dest)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ const re_node_set *cur_src = &mctx->state_log[str_idx]->non_eps_nodes;
+ int i;
+
+ /* Then build the next sifted state.
+ We build the next sifted state on `cur_dest', and update
+ `sifted_states[str_idx]' with `cur_dest'.
+ Note:
+ `cur_dest' is the sifted state from `state_log[str_idx + 1]'.
+ `cur_src' points the node_set of the old `state_log[str_idx]'
+ (with the epsilon nodes pre-filtered out). */
+ for (i = 0; i < cur_src->nelem; i++)
+ {
+ int prev_node = cur_src->elems[i];
+ int naccepted = 0;
+ int ret;
+
+#ifdef DEBUG
+ re_token_type_t type = dfa->nodes[prev_node].type;
+ assert (!IS_EPSILON_NODE (type));
+#endif
+#ifdef RE_ENABLE_I18N
+ /* If the node may accept `multi byte'. */
+ if (dfa->nodes[prev_node].accept_mb)
+ naccepted = sift_states_iter_mb (mctx, sctx, prev_node,
+ str_idx, sctx->last_str_idx);
+#endif /* RE_ENABLE_I18N */
+
+ /* We don't check backreferences here.
+ See update_cur_sifted_state(). */
+ if (!naccepted
+ && check_node_accept (mctx, dfa->nodes + prev_node, str_idx)
+ && STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + 1],
+ dfa->nexts[prev_node]))
+ naccepted = 1;
+
+ if (naccepted == 0)
+ continue;
+
+ if (sctx->limits.nelem)
+ {
+ int to_idx = str_idx + naccepted;
+ if (check_dst_limits (mctx, &sctx->limits,
+ dfa->nexts[prev_node], to_idx,
+ prev_node, str_idx))
+ continue;
+ }
+ ret = re_node_set_insert (cur_dest, prev_node);
+ if (BE (ret == -1, 0))
+ return REG_ESPACE;
+ }
+
+ return REG_NOERROR;
+}
+
+/* Helper functions. */
+
+static reg_errcode_t
+internal_function
+clean_state_log_if_needed (re_match_context_t *mctx, int next_state_log_idx)
+{
+ int top = mctx->state_log_top;
+
+ if (next_state_log_idx >= mctx->input.bufs_len
+ || (next_state_log_idx >= mctx->input.valid_len
+ && mctx->input.valid_len < mctx->input.len))
+ {
+ reg_errcode_t err;
+ err = extend_buffers (mctx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+
+ if (top < next_state_log_idx)
+ {
+ memset (mctx->state_log + top + 1, '\0',
+ sizeof (re_dfastate_t *) * (next_state_log_idx - top));
+ mctx->state_log_top = next_state_log_idx;
+ }
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+merge_state_array (const re_dfa_t *dfa, re_dfastate_t **dst,
+ re_dfastate_t **src, int num)
+{
+ int st_idx;
+ reg_errcode_t err;
+ for (st_idx = 0; st_idx < num; ++st_idx)
+ {
+ if (dst[st_idx] == NULL)
+ dst[st_idx] = src[st_idx];
+ else if (src[st_idx] != NULL)
+ {
+ re_node_set merged_set;
+ err = re_node_set_init_union (&merged_set, &dst[st_idx]->nodes,
+ &src[st_idx]->nodes);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ dst[st_idx] = re_acquire_state (&err, dfa, &merged_set);
+ re_node_set_free (&merged_set);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ }
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+update_cur_sifted_state (const re_match_context_t *mctx,
+ re_sift_context_t *sctx, int str_idx,
+ re_node_set *dest_nodes)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ reg_errcode_t err = REG_NOERROR;
+ const re_node_set *candidates;
+ candidates = ((mctx->state_log[str_idx] == NULL) ? NULL
+ : &mctx->state_log[str_idx]->nodes);
+
+ if (dest_nodes->nelem == 0)
+ sctx->sifted_states[str_idx] = NULL;
+ else
+ {
+ if (candidates)
+ {
+ /* At first, add the nodes which can epsilon transit to a node in
+ DEST_NODE. */
+ err = add_epsilon_src_nodes (dfa, dest_nodes, candidates);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+
+ /* Then, check the limitations in the current sift_context. */
+ if (sctx->limits.nelem)
+ {
+ err = check_subexp_limits (dfa, dest_nodes, candidates, &sctx->limits,
+ mctx->bkref_ents, str_idx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ }
+
+ sctx->sifted_states[str_idx] = re_acquire_state (&err, dfa, dest_nodes);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+
+ if (candidates && mctx->state_log[str_idx]->has_backref)
+ {
+ err = sift_states_bkref (mctx, sctx, str_idx, candidates);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+add_epsilon_src_nodes (const re_dfa_t *dfa, re_node_set *dest_nodes,
+ const re_node_set *candidates)
+{
+ reg_errcode_t err = REG_NOERROR;
+ int i;
+
+ re_dfastate_t *state = re_acquire_state (&err, dfa, dest_nodes);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+
+ if (!state->inveclosure.alloc)
+ {
+ err = re_node_set_alloc (&state->inveclosure, dest_nodes->nelem);
+ if (BE (err != REG_NOERROR, 0))
+ return REG_ESPACE;
+ for (i = 0; i < dest_nodes->nelem; i++)
+ {
+ err = re_node_set_merge (&state->inveclosure,
+ dfa->inveclosures + dest_nodes->elems[i]);
+ if (BE (err != REG_NOERROR, 0))
+ return REG_ESPACE;
+ }
+ }
+ return re_node_set_add_intersect (dest_nodes, candidates,
+ &state->inveclosure);
+}
+
+static reg_errcode_t
+internal_function
+sub_epsilon_src_nodes (const re_dfa_t *dfa, int node, re_node_set *dest_nodes,
+ const re_node_set *candidates)
+{
+ int ecl_idx;
+ reg_errcode_t err;
+ re_node_set *inv_eclosure = dfa->inveclosures + node;
+ re_node_set except_nodes;
+ re_node_set_init_empty (&except_nodes);
+ for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx)
+ {
+ int cur_node = inv_eclosure->elems[ecl_idx];
+ if (cur_node == node)
+ continue;
+ if (IS_EPSILON_NODE (dfa->nodes[cur_node].type))
+ {
+ int edst1 = dfa->edests[cur_node].elems[0];
+ int edst2 = ((dfa->edests[cur_node].nelem > 1)
+ ? dfa->edests[cur_node].elems[1] : -1);
+ if ((!re_node_set_contains (inv_eclosure, edst1)
+ && re_node_set_contains (dest_nodes, edst1))
+ || (edst2 > 0
+ && !re_node_set_contains (inv_eclosure, edst2)
+ && re_node_set_contains (dest_nodes, edst2)))
+ {
+ err = re_node_set_add_intersect (&except_nodes, candidates,
+ dfa->inveclosures + cur_node);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&except_nodes);
+ return err;
+ }
+ }
+ }
+ }
+ for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx)
+ {
+ int cur_node = inv_eclosure->elems[ecl_idx];
+ if (!re_node_set_contains (&except_nodes, cur_node))
+ {
+ int idx = re_node_set_contains (dest_nodes, cur_node) - 1;
+ re_node_set_remove_at (dest_nodes, idx);
+ }
+ }
+ re_node_set_free (&except_nodes);
+ return REG_NOERROR;
+}
+
+static int
+internal_function
+check_dst_limits (const re_match_context_t *mctx, re_node_set *limits,
+ int dst_node, int dst_idx, int src_node, int src_idx)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ int lim_idx, src_pos, dst_pos;
+
+ int dst_bkref_idx = search_cur_bkref_entry (mctx, dst_idx);
+ int src_bkref_idx = search_cur_bkref_entry (mctx, src_idx);
+ for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx)
+ {
+ int subexp_idx;
+ struct re_backref_cache_entry *ent;
+ ent = mctx->bkref_ents + limits->elems[lim_idx];
+ subexp_idx = dfa->nodes[ent->node].opr.idx;
+
+ dst_pos = check_dst_limits_calc_pos (mctx, limits->elems[lim_idx],
+ subexp_idx, dst_node, dst_idx,
+ dst_bkref_idx);
+ src_pos = check_dst_limits_calc_pos (mctx, limits->elems[lim_idx],
+ subexp_idx, src_node, src_idx,
+ src_bkref_idx);
+
+ /* In case of:
+ <src> <dst> ( <subexp> )
+ ( <subexp> ) <src> <dst>
+ ( <subexp1> <src> <subexp2> <dst> <subexp3> ) */
+ if (src_pos == dst_pos)
+ continue; /* This is unrelated limitation. */
+ else
+ return 1;
+ }
+ return 0;
+}
+
+static int
+internal_function
+check_dst_limits_calc_pos_1 (const re_match_context_t *mctx, int boundaries,
+ int subexp_idx, int from_node, int bkref_idx)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ const re_node_set *eclosures = dfa->eclosures + from_node;
+ int node_idx;
+
+ /* Else, we are on the boundary: examine the nodes on the epsilon
+ closure. */
+ for (node_idx = 0; node_idx < eclosures->nelem; ++node_idx)
+ {
+ int node = eclosures->elems[node_idx];
+ switch (dfa->nodes[node].type)
+ {
+ case OP_BACK_REF:
+ if (bkref_idx != -1)
+ {
+ struct re_backref_cache_entry *ent = mctx->bkref_ents + bkref_idx;
+ do
+ {
+ int dst, cpos;
+
+ if (ent->node != node)
+ continue;
+
+ if (subexp_idx < BITSET_WORD_BITS
+ && !(ent->eps_reachable_subexps_map
+ & ((bitset_word_t) 1 << subexp_idx)))
+ continue;
+
+ /* Recurse trying to reach the OP_OPEN_SUBEXP and
+ OP_CLOSE_SUBEXP cases below. But, if the
+ destination node is the same node as the source
+ node, don't recurse because it would cause an
+ infinite loop: a regex that exhibits this behavior
+ is ()\1*\1* */
+ dst = dfa->edests[node].elems[0];
+ if (dst == from_node)
+ {
+ if (boundaries & 1)
+ return -1;
+ else /* if (boundaries & 2) */
+ return 0;
+ }
+
+ cpos =
+ check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx,
+ dst, bkref_idx);
+ if (cpos == -1 /* && (boundaries & 1) */)
+ return -1;
+ if (cpos == 0 && (boundaries & 2))
+ return 0;
+
+ if (subexp_idx < BITSET_WORD_BITS)
+ ent->eps_reachable_subexps_map
+ &= ~((bitset_word_t) 1 << subexp_idx);
+ }
+ while (ent++->more);
+ }
+ break;
+
+ case OP_OPEN_SUBEXP:
+ if ((boundaries & 1) && subexp_idx == dfa->nodes[node].opr.idx)
+ return -1;
+ break;
+
+ case OP_CLOSE_SUBEXP:
+ if ((boundaries & 2) && subexp_idx == dfa->nodes[node].opr.idx)
+ return 0;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return (boundaries & 2) ? 1 : 0;
+}
+
+static int
+internal_function
+check_dst_limits_calc_pos (const re_match_context_t *mctx, int limit,
+ int subexp_idx, int from_node, int str_idx,
+ int bkref_idx)
+{
+ struct re_backref_cache_entry *lim = mctx->bkref_ents + limit;
+ int boundaries;
+
+ /* If we are outside the range of the subexpression, return -1 or 1. */
+ if (str_idx < lim->subexp_from)
+ return -1;
+
+ if (lim->subexp_to < str_idx)
+ return 1;
+
+ /* If we are within the subexpression, return 0. */
+ boundaries = (str_idx == lim->subexp_from);
+ boundaries |= (str_idx == lim->subexp_to) << 1;
+ if (boundaries == 0)
+ return 0;
+
+ /* Else, examine epsilon closure. */
+ return check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx,
+ from_node, bkref_idx);
+}
+
+/* Check the limitations of sub expressions LIMITS, and remove the nodes
+ which are against limitations from DEST_NODES. */
+
+static reg_errcode_t
+internal_function
+check_subexp_limits (const re_dfa_t *dfa, re_node_set *dest_nodes,
+ const re_node_set *candidates, re_node_set *limits,
+ struct re_backref_cache_entry *bkref_ents, int str_idx)
+{
+ reg_errcode_t err;
+ int node_idx, lim_idx;
+
+ for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx)
+ {
+ int subexp_idx;
+ struct re_backref_cache_entry *ent;
+ ent = bkref_ents + limits->elems[lim_idx];
+
+ if (str_idx <= ent->subexp_from || ent->str_idx < str_idx)
+ continue; /* This is unrelated limitation. */
+
+ subexp_idx = dfa->nodes[ent->node].opr.idx;
+ if (ent->subexp_to == str_idx)
+ {
+ int ops_node = -1;
+ int cls_node = -1;
+ for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx)
+ {
+ int node = dest_nodes->elems[node_idx];
+ re_token_type_t type = dfa->nodes[node].type;
+ if (type == OP_OPEN_SUBEXP
+ && subexp_idx == dfa->nodes[node].opr.idx)
+ ops_node = node;
+ else if (type == OP_CLOSE_SUBEXP
+ && subexp_idx == dfa->nodes[node].opr.idx)
+ cls_node = node;
+ }
+
+ /* Check the limitation of the open subexpression. */
+ /* Note that (ent->subexp_to = str_idx != ent->subexp_from). */
+ if (ops_node >= 0)
+ {
+ err = sub_epsilon_src_nodes (dfa, ops_node, dest_nodes,
+ candidates);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+
+ /* Check the limitation of the close subexpression. */
+ if (cls_node >= 0)
+ for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx)
+ {
+ int node = dest_nodes->elems[node_idx];
+ if (!re_node_set_contains (dfa->inveclosures + node,
+ cls_node)
+ && !re_node_set_contains (dfa->eclosures + node,
+ cls_node))
+ {
+ /* It is against this limitation.
+ Remove it form the current sifted state. */
+ err = sub_epsilon_src_nodes (dfa, node, dest_nodes,
+ candidates);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ --node_idx;
+ }
+ }
+ }
+ else /* (ent->subexp_to != str_idx) */
+ {
+ for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx)
+ {
+ int node = dest_nodes->elems[node_idx];
+ re_token_type_t type = dfa->nodes[node].type;
+ if (type == OP_CLOSE_SUBEXP || type == OP_OPEN_SUBEXP)
+ {
+ if (subexp_idx != dfa->nodes[node].opr.idx)
+ continue;
+ /* It is against this limitation.
+ Remove it form the current sifted state. */
+ err = sub_epsilon_src_nodes (dfa, node, dest_nodes,
+ candidates);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ }
+ }
+ }
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+internal_function
+sift_states_bkref (const re_match_context_t *mctx, re_sift_context_t *sctx,
+ int str_idx, const re_node_set *candidates)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ reg_errcode_t err;
+ int node_idx, node;
+ re_sift_context_t local_sctx;
+ int first_idx = search_cur_bkref_entry (mctx, str_idx);
+
+ if (first_idx == -1)
+ return REG_NOERROR;
+
+ local_sctx.sifted_states = NULL; /* Mark that it hasn't been initialized. */
+
+ for (node_idx = 0; node_idx < candidates->nelem; ++node_idx)
+ {
+ int enabled_idx;
+ re_token_type_t type;
+ struct re_backref_cache_entry *entry;
+ node = candidates->elems[node_idx];
+ type = dfa->nodes[node].type;
+ /* Avoid infinite loop for the REs like "()\1+". */
+ if (node == sctx->last_node && str_idx == sctx->last_str_idx)
+ continue;
+ if (type != OP_BACK_REF)
+ continue;
+
+ entry = mctx->bkref_ents + first_idx;
+ enabled_idx = first_idx;
+ do
+ {
+ int subexp_len;
+ int to_idx;
+ int dst_node;
+ int ret;
+ re_dfastate_t *cur_state;
+
+ if (entry->node != node)
+ continue;
+ subexp_len = entry->subexp_to - entry->subexp_from;
+ to_idx = str_idx + subexp_len;
+ dst_node = (subexp_len ? dfa->nexts[node]
+ : dfa->edests[node].elems[0]);
+
+ if (to_idx > sctx->last_str_idx
+ || sctx->sifted_states[to_idx] == NULL
+ || !STATE_NODE_CONTAINS (sctx->sifted_states[to_idx], dst_node)
+ || check_dst_limits (mctx, &sctx->limits, node,
+ str_idx, dst_node, to_idx))
+ continue;
+
+ if (local_sctx.sifted_states == NULL)
+ {
+ local_sctx = *sctx;
+ err = re_node_set_init_copy (&local_sctx.limits, &sctx->limits);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+ }
+ local_sctx.last_node = node;
+ local_sctx.last_str_idx = str_idx;
+ ret = re_node_set_insert (&local_sctx.limits, enabled_idx);
+ if (BE (ret < 0, 0))
+ {
+ err = REG_ESPACE;
+ goto free_return;
+ }
+ cur_state = local_sctx.sifted_states[str_idx];
+ err = sift_states_backward (mctx, &local_sctx);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+ if (sctx->limited_states != NULL)
+ {
+ err = merge_state_array (dfa, sctx->limited_states,
+ local_sctx.sifted_states,
+ str_idx + 1);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+ }
+ local_sctx.sifted_states[str_idx] = cur_state;
+ re_node_set_remove (&local_sctx.limits, enabled_idx);
+
+ /* mctx->bkref_ents may have changed, reload the pointer. */
+ entry = mctx->bkref_ents + enabled_idx;
+ }
+ while (enabled_idx++, entry++->more);
+ }
+ err = REG_NOERROR;
+ free_return:
+ if (local_sctx.sifted_states != NULL)
+ {
+ re_node_set_free (&local_sctx.limits);
+ }
+
+ return err;
+}
+
+
+#ifdef RE_ENABLE_I18N
+static int
+internal_function
+sift_states_iter_mb (const re_match_context_t *mctx, re_sift_context_t *sctx,
+ int node_idx, int str_idx, int max_str_idx)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ int naccepted;
+ /* Check the node can accept `multi byte'. */
+ naccepted = check_node_accept_bytes (dfa, node_idx, &mctx->input, str_idx);
+ if (naccepted > 0 && str_idx + naccepted <= max_str_idx &&
+ !STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + naccepted],
+ dfa->nexts[node_idx]))
+ /* The node can't accept the `multi byte', or the
+ destination was already thrown away, then the node
+ could't accept the current input `multi byte'. */
+ naccepted = 0;
+ /* Otherwise, it is sure that the node could accept
+ `naccepted' bytes input. */
+ return naccepted;
+}
+#endif /* RE_ENABLE_I18N */
+
+
+/* Functions for state transition. */
+
+/* Return the next state to which the current state STATE will transit by
+ accepting the current input byte, and update STATE_LOG if necessary.
+ If STATE can accept a multibyte char/collating element/back reference
+ update the destination of STATE_LOG. */
+
+static re_dfastate_t *
+internal_function
+transit_state (reg_errcode_t *err, re_match_context_t *mctx,
+ re_dfastate_t *state)
+{
+ re_dfastate_t **trtable;
+ unsigned char ch;
+
+#ifdef RE_ENABLE_I18N
+ /* If the current state can accept multibyte. */
+ if (BE (state->accept_mb, 0))
+ {
+ *err = transit_state_mb (mctx, state);
+ if (BE (*err != REG_NOERROR, 0))
+ return NULL;
+ }
+#endif /* RE_ENABLE_I18N */
+
+ /* Then decide the next state with the single byte. */
+#if 0
+ if (0)
+ /* don't use transition table */
+ return transit_state_sb (err, mctx, state);
+#endif
+
+ /* Use transition table */
+ ch = re_string_fetch_byte (&mctx->input);
+ for (;;)
+ {
+ trtable = state->trtable;
+ if (BE (trtable != NULL, 1))
+ return trtable[ch];
+
+ trtable = state->word_trtable;
+ if (BE (trtable != NULL, 1))
+ {
+ unsigned int context;
+ context
+ = re_string_context_at (&mctx->input,
+ re_string_cur_idx (&mctx->input) - 1,
+ mctx->eflags);
+ if (IS_WORD_CONTEXT (context))
+ return trtable[ch + SBC_MAX];
+ else
+ return trtable[ch];
+ }
+
+ if (!build_trtable (mctx->dfa, state))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+
+ /* Retry, we now have a transition table. */
+ }
+}
+
+/* Update the state_log if we need */
+re_dfastate_t *
+internal_function
+merge_state_with_log (reg_errcode_t *err, re_match_context_t *mctx,
+ re_dfastate_t *next_state)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ int cur_idx = re_string_cur_idx (&mctx->input);
+
+ if (cur_idx > mctx->state_log_top)
+ {
+ mctx->state_log[cur_idx] = next_state;
+ mctx->state_log_top = cur_idx;
+ }
+ else if (mctx->state_log[cur_idx] == 0)
+ {
+ mctx->state_log[cur_idx] = next_state;
+ }
+ else
+ {
+ re_dfastate_t *pstate;
+ unsigned int context;
+ re_node_set next_nodes, *log_nodes, *table_nodes = NULL;
+ /* If (state_log[cur_idx] != 0), it implies that cur_idx is
+ the destination of a multibyte char/collating element/
+ back reference. Then the next state is the union set of
+ these destinations and the results of the transition table. */
+ pstate = mctx->state_log[cur_idx];
+ log_nodes = pstate->entrance_nodes;
+ if (next_state != NULL)
+ {
+ table_nodes = next_state->entrance_nodes;
+ *err = re_node_set_init_union (&next_nodes, table_nodes,
+ log_nodes);
+ if (BE (*err != REG_NOERROR, 0))
+ return NULL;
+ }
+ else
+ next_nodes = *log_nodes;
+ /* Note: We already add the nodes of the initial state,
+ then we don't need to add them here. */
+
+ context = re_string_context_at (&mctx->input,
+ re_string_cur_idx (&mctx->input) - 1,
+ mctx->eflags);
+ next_state = mctx->state_log[cur_idx]
+ = re_acquire_state_context (err, dfa, &next_nodes, context);
+ /* We don't need to check errors here, since the return value of
+ this function is next_state and ERR is already set. */
+
+ if (table_nodes != NULL)
+ re_node_set_free (&next_nodes);
+ }
+
+ if (BE (dfa->nbackref, 0) && next_state != NULL)
+ {
+ /* Check OP_OPEN_SUBEXP in the current state in case that we use them
+ later. We must check them here, since the back references in the
+ next state might use them. */
+ *err = check_subexp_matching_top (mctx, &next_state->nodes,
+ cur_idx);
+ if (BE (*err != REG_NOERROR, 0))
+ return NULL;
+
+ /* If the next state has back references. */
+ if (next_state->has_backref)
+ {
+ *err = transit_state_bkref (mctx, &next_state->nodes);
+ if (BE (*err != REG_NOERROR, 0))
+ return NULL;
+ next_state = mctx->state_log[cur_idx];
+ }
+ }
+
+ return next_state;
+}
+
+/* Skip bytes in the input that correspond to part of a
+ multi-byte match, then look in the log for a state
+ from which to restart matching. */
+re_dfastate_t *
+internal_function
+find_recover_state (reg_errcode_t *err, re_match_context_t *mctx)
+{
+ re_dfastate_t *cur_state;
+ do
+ {
+ int max = mctx->state_log_top;
+ int cur_str_idx = re_string_cur_idx (&mctx->input);
+
+ do
+ {
+ if (++cur_str_idx > max)
+ return NULL;
+ re_string_skip_bytes (&mctx->input, 1);
+ }
+ while (mctx->state_log[cur_str_idx] == NULL);
+
+ cur_state = merge_state_with_log (err, mctx, NULL);
+ }
+ while (*err == REG_NOERROR && cur_state == NULL);
+ return cur_state;
+}
+
+/* Helper functions for transit_state. */
+
+/* From the node set CUR_NODES, pick up the nodes whose types are
+ OP_OPEN_SUBEXP and which have corresponding back references in the regular
+ expression. And register them to use them later for evaluating the
+ correspoding back references. */
+
+static reg_errcode_t
+internal_function
+check_subexp_matching_top (re_match_context_t *mctx, re_node_set *cur_nodes,
+ int str_idx)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ int node_idx;
+ reg_errcode_t err;
+
+ /* TODO: This isn't efficient.
+ Because there might be more than one nodes whose types are
+ OP_OPEN_SUBEXP and whose index is SUBEXP_IDX, we must check all
+ nodes.
+ E.g. RE: (a){2} */
+ for (node_idx = 0; node_idx < cur_nodes->nelem; ++node_idx)
+ {
+ int node = cur_nodes->elems[node_idx];
+ if (dfa->nodes[node].type == OP_OPEN_SUBEXP
+ && dfa->nodes[node].opr.idx < BITSET_WORD_BITS
+ && (dfa->used_bkref_map
+ & ((bitset_word_t) 1 << dfa->nodes[node].opr.idx)))
+ {
+ err = match_ctx_add_subtop (mctx, node, str_idx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ }
+ return REG_NOERROR;
+}
+
+#if 0
+/* Return the next state to which the current state STATE will transit by
+ accepting the current input byte. */
+
+static re_dfastate_t *
+transit_state_sb (reg_errcode_t *err, re_match_context_t *mctx,
+ re_dfastate_t *state)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ re_node_set next_nodes;
+ re_dfastate_t *next_state;
+ int node_cnt, cur_str_idx = re_string_cur_idx (&mctx->input);
+ unsigned int context;
+
+ *err = re_node_set_alloc (&next_nodes, state->nodes.nelem + 1);
+ if (BE (*err != REG_NOERROR, 0))
+ return NULL;
+ for (node_cnt = 0; node_cnt < state->nodes.nelem; ++node_cnt)
+ {
+ int cur_node = state->nodes.elems[node_cnt];
+ if (check_node_accept (mctx, dfa->nodes + cur_node, cur_str_idx))
+ {
+ *err = re_node_set_merge (&next_nodes,
+ dfa->eclosures + dfa->nexts[cur_node]);
+ if (BE (*err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&next_nodes);
+ return NULL;
+ }
+ }
+ }
+ context = re_string_context_at (&mctx->input, cur_str_idx, mctx->eflags);
+ next_state = re_acquire_state_context (err, dfa, &next_nodes, context);
+ /* We don't need to check errors here, since the return value of
+ this function is next_state and ERR is already set. */
+
+ re_node_set_free (&next_nodes);
+ re_string_skip_bytes (&mctx->input, 1);
+ return next_state;
+}
+#endif
+
+#ifdef RE_ENABLE_I18N
+static reg_errcode_t
+internal_function
+transit_state_mb (re_match_context_t *mctx, re_dfastate_t *pstate)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ reg_errcode_t err;
+ int i;
+
+ for (i = 0; i < pstate->nodes.nelem; ++i)
+ {
+ re_node_set dest_nodes, *new_nodes;
+ int cur_node_idx = pstate->nodes.elems[i];
+ int naccepted, dest_idx;
+ unsigned int context;
+ re_dfastate_t *dest_state;
+
+ if (!dfa->nodes[cur_node_idx].accept_mb)
+ continue;
+
+ if (dfa->nodes[cur_node_idx].constraint)
+ {
+ context = re_string_context_at (&mctx->input,
+ re_string_cur_idx (&mctx->input),
+ mctx->eflags);
+ if (NOT_SATISFY_NEXT_CONSTRAINT (dfa->nodes[cur_node_idx].constraint,
+ context))
+ continue;
+ }
+
+ /* How many bytes the node can accept? */
+ naccepted = check_node_accept_bytes (dfa, cur_node_idx, &mctx->input,
+ re_string_cur_idx (&mctx->input));
+ if (naccepted == 0)
+ continue;
+
+ /* The node can accepts `naccepted' bytes. */
+ dest_idx = re_string_cur_idx (&mctx->input) + naccepted;
+ mctx->max_mb_elem_len = ((mctx->max_mb_elem_len < naccepted) ? naccepted
+ : mctx->max_mb_elem_len);
+ err = clean_state_log_if_needed (mctx, dest_idx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+#ifdef DEBUG
+ assert (dfa->nexts[cur_node_idx] != -1);
+#endif
+ new_nodes = dfa->eclosures + dfa->nexts[cur_node_idx];
+
+ dest_state = mctx->state_log[dest_idx];
+ if (dest_state == NULL)
+ dest_nodes = *new_nodes;
+ else
+ {
+ err = re_node_set_init_union (&dest_nodes,
+ dest_state->entrance_nodes, new_nodes);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ context = re_string_context_at (&mctx->input, dest_idx - 1,
+ mctx->eflags);
+ mctx->state_log[dest_idx]
+ = re_acquire_state_context (&err, dfa, &dest_nodes, context);
+ if (dest_state != NULL)
+ re_node_set_free (&dest_nodes);
+ if (BE (mctx->state_log[dest_idx] == NULL && err != REG_NOERROR, 0))
+ return err;
+ }
+ return REG_NOERROR;
+}
+#endif /* RE_ENABLE_I18N */
+
+static reg_errcode_t
+internal_function
+transit_state_bkref (re_match_context_t *mctx, const re_node_set *nodes)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ reg_errcode_t err;
+ int i;
+ int cur_str_idx = re_string_cur_idx (&mctx->input);
+
+ for (i = 0; i < nodes->nelem; ++i)
+ {
+ int dest_str_idx, prev_nelem, bkc_idx;
+ int node_idx = nodes->elems[i];
+ unsigned int context;
+ const re_token_t *node = dfa->nodes + node_idx;
+ re_node_set *new_dest_nodes;
+
+ /* Check whether `node' is a backreference or not. */
+ if (node->type != OP_BACK_REF)
+ continue;
+
+ if (node->constraint)
+ {
+ context = re_string_context_at (&mctx->input, cur_str_idx,
+ mctx->eflags);
+ if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context))
+ continue;
+ }
+
+ /* `node' is a backreference.
+ Check the substring which the substring matched. */
+ bkc_idx = mctx->nbkref_ents;
+ err = get_subexp (mctx, node_idx, cur_str_idx);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+
+ /* And add the epsilon closures (which is `new_dest_nodes') of
+ the backreference to appropriate state_log. */
+#ifdef DEBUG
+ assert (dfa->nexts[node_idx] != -1);
+#endif
+ for (; bkc_idx < mctx->nbkref_ents; ++bkc_idx)
+ {
+ int subexp_len;
+ re_dfastate_t *dest_state;
+ struct re_backref_cache_entry *bkref_ent;
+ bkref_ent = mctx->bkref_ents + bkc_idx;
+ if (bkref_ent->node != node_idx || bkref_ent->str_idx != cur_str_idx)
+ continue;
+ subexp_len = bkref_ent->subexp_to - bkref_ent->subexp_from;
+ new_dest_nodes = (subexp_len == 0
+ ? dfa->eclosures + dfa->edests[node_idx].elems[0]
+ : dfa->eclosures + dfa->nexts[node_idx]);
+ dest_str_idx = (cur_str_idx + bkref_ent->subexp_to
+ - bkref_ent->subexp_from);
+ context = re_string_context_at (&mctx->input, dest_str_idx - 1,
+ mctx->eflags);
+ dest_state = mctx->state_log[dest_str_idx];
+ prev_nelem = ((mctx->state_log[cur_str_idx] == NULL) ? 0
+ : mctx->state_log[cur_str_idx]->nodes.nelem);
+ /* Add `new_dest_node' to state_log. */
+ if (dest_state == NULL)
+ {
+ mctx->state_log[dest_str_idx]
+ = re_acquire_state_context (&err, dfa, new_dest_nodes,
+ context);
+ if (BE (mctx->state_log[dest_str_idx] == NULL
+ && err != REG_NOERROR, 0))
+ goto free_return;
+ }
+ else
+ {
+ re_node_set dest_nodes;
+ err = re_node_set_init_union (&dest_nodes,
+ dest_state->entrance_nodes,
+ new_dest_nodes);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&dest_nodes);
+ goto free_return;
+ }
+ mctx->state_log[dest_str_idx]
+ = re_acquire_state_context (&err, dfa, &dest_nodes, context);
+ re_node_set_free (&dest_nodes);
+ if (BE (mctx->state_log[dest_str_idx] == NULL
+ && err != REG_NOERROR, 0))
+ goto free_return;
+ }
+ /* We need to check recursively if the backreference can epsilon
+ transit. */
+ if (subexp_len == 0
+ && mctx->state_log[cur_str_idx]->nodes.nelem > prev_nelem)
+ {
+ err = check_subexp_matching_top (mctx, new_dest_nodes,
+ cur_str_idx);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+ err = transit_state_bkref (mctx, new_dest_nodes);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+ }
+ }
+ }
+ err = REG_NOERROR;
+ free_return:
+ return err;
+}
+
+/* Enumerate all the candidates which the backreference BKREF_NODE can match
+ at BKREF_STR_IDX, and register them by match_ctx_add_entry().
+ Note that we might collect inappropriate candidates here.
+ However, the cost of checking them strictly here is too high, then we
+ delay these checking for prune_impossible_nodes(). */
+
+static reg_errcode_t
+internal_function
+get_subexp (re_match_context_t *mctx, int bkref_node, int bkref_str_idx)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ int subexp_num, sub_top_idx;
+ const char *buf = (const char *) re_string_get_buffer (&mctx->input);
+ /* Return if we have already checked BKREF_NODE at BKREF_STR_IDX. */
+ int cache_idx = search_cur_bkref_entry (mctx, bkref_str_idx);
+ if (cache_idx != -1)
+ {
+ const struct re_backref_cache_entry *entry
+ = mctx->bkref_ents + cache_idx;
+ do
+ if (entry->node == bkref_node)
+ return REG_NOERROR; /* We already checked it. */
+ while (entry++->more);
+ }
+
+ subexp_num = dfa->nodes[bkref_node].opr.idx;
+
+ /* For each sub expression */
+ for (sub_top_idx = 0; sub_top_idx < mctx->nsub_tops; ++sub_top_idx)
+ {
+ reg_errcode_t err;
+ re_sub_match_top_t *sub_top = mctx->sub_tops[sub_top_idx];
+ re_sub_match_last_t *sub_last;
+ int sub_last_idx, sl_str, bkref_str_off;
+
+ if (dfa->nodes[sub_top->node].opr.idx != subexp_num)
+ continue; /* It isn't related. */
+
+ sl_str = sub_top->str_idx;
+ bkref_str_off = bkref_str_idx;
+ /* At first, check the last node of sub expressions we already
+ evaluated. */
+ for (sub_last_idx = 0; sub_last_idx < sub_top->nlasts; ++sub_last_idx)
+ {
+ int sl_str_diff;
+ sub_last = sub_top->lasts[sub_last_idx];
+ sl_str_diff = sub_last->str_idx - sl_str;
+ /* The matched string by the sub expression match with the substring
+ at the back reference? */
+ if (sl_str_diff > 0)
+ {
+ if (BE (bkref_str_off + sl_str_diff > mctx->input.valid_len, 0))
+ {
+ /* Not enough chars for a successful match. */
+ if (bkref_str_off + sl_str_diff > mctx->input.len)
+ break;
+
+ err = clean_state_log_if_needed (mctx,
+ bkref_str_off
+ + sl_str_diff);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ buf = (const char *) re_string_get_buffer (&mctx->input);
+ }
+ if (memcmp (buf + bkref_str_off, buf + sl_str, sl_str_diff) != 0)
+ /* We don't need to search this sub expression any more. */
+ break;
+ }
+ bkref_str_off += sl_str_diff;
+ sl_str += sl_str_diff;
+ err = get_subexp_sub (mctx, sub_top, sub_last, bkref_node,
+ bkref_str_idx);
+
+ /* Reload buf, since the preceding call might have reallocated
+ the buffer. */
+ buf = (const char *) re_string_get_buffer (&mctx->input);
+
+ if (err == REG_NOMATCH)
+ continue;
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+
+ if (sub_last_idx < sub_top->nlasts)
+ continue;
+ if (sub_last_idx > 0)
+ ++sl_str;
+ /* Then, search for the other last nodes of the sub expression. */
+ for (; sl_str <= bkref_str_idx; ++sl_str)
+ {
+ int cls_node, sl_str_off;
+ const re_node_set *nodes;
+ sl_str_off = sl_str - sub_top->str_idx;
+ /* The matched string by the sub expression match with the substring
+ at the back reference? */
+ if (sl_str_off > 0)
+ {
+ if (BE (bkref_str_off >= mctx->input.valid_len, 0))
+ {
+ /* If we are at the end of the input, we cannot match. */
+ if (bkref_str_off >= mctx->input.len)
+ break;
+
+ err = extend_buffers (mctx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+
+ buf = (const char *) re_string_get_buffer (&mctx->input);
+ }
+ if (buf [bkref_str_off++] != buf[sl_str - 1])
+ break; /* We don't need to search this sub expression
+ any more. */
+ }
+ if (mctx->state_log[sl_str] == NULL)
+ continue;
+ /* Does this state have a ')' of the sub expression? */
+ nodes = &mctx->state_log[sl_str]->nodes;
+ cls_node = find_subexp_node (dfa, nodes, subexp_num,
+ OP_CLOSE_SUBEXP);
+ if (cls_node == -1)
+ continue; /* No. */
+ if (sub_top->path == NULL)
+ {
+ sub_top->path = calloc (sizeof (state_array_t),
+ sl_str - sub_top->str_idx + 1);
+ if (sub_top->path == NULL)
+ return REG_ESPACE;
+ }
+ /* Can the OP_OPEN_SUBEXP node arrive the OP_CLOSE_SUBEXP node
+ in the current context? */
+ err = check_arrival (mctx, sub_top->path, sub_top->node,
+ sub_top->str_idx, cls_node, sl_str,
+ OP_CLOSE_SUBEXP);
+ if (err == REG_NOMATCH)
+ continue;
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ sub_last = match_ctx_add_sublast (sub_top, cls_node, sl_str);
+ if (BE (sub_last == NULL, 0))
+ return REG_ESPACE;
+ err = get_subexp_sub (mctx, sub_top, sub_last, bkref_node,
+ bkref_str_idx);
+ if (err == REG_NOMATCH)
+ continue;
+ }
+ }
+ return REG_NOERROR;
+}
+
+/* Helper functions for get_subexp(). */
+
+/* Check SUB_LAST can arrive to the back reference BKREF_NODE at BKREF_STR.
+ If it can arrive, register the sub expression expressed with SUB_TOP
+ and SUB_LAST. */
+
+static reg_errcode_t
+internal_function
+get_subexp_sub (re_match_context_t *mctx, const re_sub_match_top_t *sub_top,
+ re_sub_match_last_t *sub_last, int bkref_node, int bkref_str)
+{
+ reg_errcode_t err;
+ int to_idx;
+ /* Can the subexpression arrive the back reference? */
+ err = check_arrival (mctx, &sub_last->path, sub_last->node,
+ sub_last->str_idx, bkref_node, bkref_str,
+ OP_OPEN_SUBEXP);
+ if (err != REG_NOERROR)
+ return err;
+ err = match_ctx_add_entry (mctx, bkref_node, bkref_str, sub_top->str_idx,
+ sub_last->str_idx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ to_idx = bkref_str + sub_last->str_idx - sub_top->str_idx;
+ return clean_state_log_if_needed (mctx, to_idx);
+}
+
+/* Find the first node which is '(' or ')' and whose index is SUBEXP_IDX.
+ Search '(' if FL_OPEN, or search ')' otherwise.
+ TODO: This function isn't efficient...
+ Because there might be more than one nodes whose types are
+ OP_OPEN_SUBEXP and whose index is SUBEXP_IDX, we must check all
+ nodes.
+ E.g. RE: (a){2} */
+
+static int
+internal_function
+find_subexp_node (const re_dfa_t *dfa, const re_node_set *nodes,
+ int subexp_idx, int type)
+{
+ int cls_idx;
+ for (cls_idx = 0; cls_idx < nodes->nelem; ++cls_idx)
+ {
+ int cls_node = nodes->elems[cls_idx];
+ const re_token_t *node = dfa->nodes + cls_node;
+ if (node->type == type
+ && node->opr.idx == subexp_idx)
+ return cls_node;
+ }
+ return -1;
+}
+
+/* Check whether the node TOP_NODE at TOP_STR can arrive to the node
+ LAST_NODE at LAST_STR. We record the path onto PATH since it will be
+ heavily reused.
+ Return REG_NOERROR if it can arrive, or REG_NOMATCH otherwise. */
+
+static reg_errcode_t
+internal_function
+check_arrival (re_match_context_t *mctx, state_array_t *path, int top_node,
+ int top_str, int last_node, int last_str, int type)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ reg_errcode_t err = REG_NOERROR;
+ int subexp_num, backup_cur_idx, str_idx, null_cnt;
+ re_dfastate_t *cur_state = NULL;
+ re_node_set *cur_nodes, next_nodes;
+ re_dfastate_t **backup_state_log;
+ unsigned int context;
+
+ subexp_num = dfa->nodes[top_node].opr.idx;
+ /* Extend the buffer if we need. */
+ if (BE (path->alloc < last_str + mctx->max_mb_elem_len + 1, 0))
+ {
+ re_dfastate_t **new_array;
+ int old_alloc = path->alloc;
+ path->alloc += last_str + mctx->max_mb_elem_len + 1;
+ new_array = re_realloc (path->array, re_dfastate_t *, path->alloc);
+ if (BE (new_array == NULL, 0))
+ {
+ path->alloc = old_alloc;
+ return REG_ESPACE;
+ }
+ path->array = new_array;
+ memset (new_array + old_alloc, '\0',
+ sizeof (re_dfastate_t *) * (path->alloc - old_alloc));
+ }
+
+ str_idx = path->next_idx ? path->next_idx : top_str;
+
+ /* Temporary modify MCTX. */
+ backup_state_log = mctx->state_log;
+ backup_cur_idx = mctx->input.cur_idx;
+ mctx->state_log = path->array;
+ mctx->input.cur_idx = str_idx;
+
+ /* Setup initial node set. */
+ context = re_string_context_at (&mctx->input, str_idx - 1, mctx->eflags);
+ if (str_idx == top_str)
+ {
+ err = re_node_set_init_1 (&next_nodes, top_node);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num, type);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&next_nodes);
+ return err;
+ }
+ }
+ else
+ {
+ cur_state = mctx->state_log[str_idx];
+ if (cur_state && cur_state->has_backref)
+ {
+ err = re_node_set_init_copy (&next_nodes, &cur_state->nodes);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ else
+ re_node_set_init_empty (&next_nodes);
+ }
+ if (str_idx == top_str || (cur_state && cur_state->has_backref))
+ {
+ if (next_nodes.nelem)
+ {
+ err = expand_bkref_cache (mctx, &next_nodes, str_idx,
+ subexp_num, type);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&next_nodes);
+ return err;
+ }
+ }
+ cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context);
+ if (BE (cur_state == NULL && err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&next_nodes);
+ return err;
+ }
+ mctx->state_log[str_idx] = cur_state;
+ }
+
+ for (null_cnt = 0; str_idx < last_str && null_cnt <= mctx->max_mb_elem_len;)
+ {
+ re_node_set_empty (&next_nodes);
+ if (mctx->state_log[str_idx + 1])
+ {
+ err = re_node_set_merge (&next_nodes,
+ &mctx->state_log[str_idx + 1]->nodes);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&next_nodes);
+ return err;
+ }
+ }
+ if (cur_state)
+ {
+ err = check_arrival_add_next_nodes (mctx, str_idx,
+ &cur_state->non_eps_nodes,
+ &next_nodes);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&next_nodes);
+ return err;
+ }
+ }
+ ++str_idx;
+ if (next_nodes.nelem)
+ {
+ err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num, type);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&next_nodes);
+ return err;
+ }
+ err = expand_bkref_cache (mctx, &next_nodes, str_idx,
+ subexp_num, type);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&next_nodes);
+ return err;
+ }
+ }
+ context = re_string_context_at (&mctx->input, str_idx - 1, mctx->eflags);
+ cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context);
+ if (BE (cur_state == NULL && err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&next_nodes);
+ return err;
+ }
+ mctx->state_log[str_idx] = cur_state;
+ null_cnt = cur_state == NULL ? null_cnt + 1 : 0;
+ }
+ re_node_set_free (&next_nodes);
+ cur_nodes = (mctx->state_log[last_str] == NULL ? NULL
+ : &mctx->state_log[last_str]->nodes);
+ path->next_idx = str_idx;
+
+ /* Fix MCTX. */
+ mctx->state_log = backup_state_log;
+ mctx->input.cur_idx = backup_cur_idx;
+
+ /* Then check the current node set has the node LAST_NODE. */
+ if (cur_nodes != NULL && re_node_set_contains (cur_nodes, last_node))
+ return REG_NOERROR;
+
+ return REG_NOMATCH;
+}
+
+/* Helper functions for check_arrival. */
+
+/* Calculate the destination nodes of CUR_NODES at STR_IDX, and append them
+ to NEXT_NODES.
+ TODO: This function is similar to the functions transit_state*(),
+ however this function has many additional works.
+ Can't we unify them? */
+
+static reg_errcode_t
+internal_function
+check_arrival_add_next_nodes (re_match_context_t *mctx, int str_idx,
+ re_node_set *cur_nodes, re_node_set *next_nodes)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ int result;
+ int cur_idx;
+#ifdef RE_ENABLE_I18N
+ reg_errcode_t err = REG_NOERROR;
+#endif
+ re_node_set union_set;
+ re_node_set_init_empty (&union_set);
+ for (cur_idx = 0; cur_idx < cur_nodes->nelem; ++cur_idx)
+ {
+ int naccepted = 0;
+ int cur_node = cur_nodes->elems[cur_idx];
+#ifdef DEBUG
+ re_token_type_t type = dfa->nodes[cur_node].type;
+ assert (!IS_EPSILON_NODE (type));
+#endif
+#ifdef RE_ENABLE_I18N
+ /* If the node may accept `multi byte'. */
+ if (dfa->nodes[cur_node].accept_mb)
+ {
+ naccepted = check_node_accept_bytes (dfa, cur_node, &mctx->input,
+ str_idx);
+ if (naccepted > 1)
+ {
+ re_dfastate_t *dest_state;
+ int next_node = dfa->nexts[cur_node];
+ int next_idx = str_idx + naccepted;
+ dest_state = mctx->state_log[next_idx];
+ re_node_set_empty (&union_set);
+ if (dest_state)
+ {
+ err = re_node_set_merge (&union_set, &dest_state->nodes);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&union_set);
+ return err;
+ }
+ }
+ result = re_node_set_insert (&union_set, next_node);
+ if (BE (result < 0, 0))
+ {
+ re_node_set_free (&union_set);
+ return REG_ESPACE;
+ }
+ mctx->state_log[next_idx] = re_acquire_state (&err, dfa,
+ &union_set);
+ if (BE (mctx->state_log[next_idx] == NULL
+ && err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&union_set);
+ return err;
+ }
+ }
+ }
+#endif /* RE_ENABLE_I18N */
+ if (naccepted
+ || check_node_accept (mctx, dfa->nodes + cur_node, str_idx))
+ {
+ result = re_node_set_insert (next_nodes, dfa->nexts[cur_node]);
+ if (BE (result < 0, 0))
+ {
+ re_node_set_free (&union_set);
+ return REG_ESPACE;
+ }
+ }
+ }
+ re_node_set_free (&union_set);
+ return REG_NOERROR;
+}
+
+/* For all the nodes in CUR_NODES, add the epsilon closures of them to
+ CUR_NODES, however exclude the nodes which are:
+ - inside the sub expression whose number is EX_SUBEXP, if FL_OPEN.
+ - out of the sub expression whose number is EX_SUBEXP, if !FL_OPEN.
+*/
+
+static reg_errcode_t
+internal_function
+check_arrival_expand_ecl (const re_dfa_t *dfa, re_node_set *cur_nodes,
+ int ex_subexp, int type)
+{
+ reg_errcode_t err;
+ int idx, outside_node;
+ re_node_set new_nodes;
+#ifdef DEBUG
+ assert (cur_nodes->nelem);
+#endif
+ err = re_node_set_alloc (&new_nodes, cur_nodes->nelem);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ /* Create a new node set NEW_NODES with the nodes which are epsilon
+ closures of the node in CUR_NODES. */
+
+ for (idx = 0; idx < cur_nodes->nelem; ++idx)
+ {
+ int cur_node = cur_nodes->elems[idx];
+ const re_node_set *eclosure = dfa->eclosures + cur_node;
+ outside_node = find_subexp_node (dfa, eclosure, ex_subexp, type);
+ if (outside_node == -1)
+ {
+ /* There are no problematic nodes, just merge them. */
+ err = re_node_set_merge (&new_nodes, eclosure);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&new_nodes);
+ return err;
+ }
+ }
+ else
+ {
+ /* There are problematic nodes, re-calculate incrementally. */
+ err = check_arrival_expand_ecl_sub (dfa, &new_nodes, cur_node,
+ ex_subexp, type);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&new_nodes);
+ return err;
+ }
+ }
+ }
+ re_node_set_free (cur_nodes);
+ *cur_nodes = new_nodes;
+ return REG_NOERROR;
+}
+
+/* Helper function for check_arrival_expand_ecl.
+ Check incrementally the epsilon closure of TARGET, and if it isn't
+ problematic append it to DST_NODES. */
+
+static reg_errcode_t
+internal_function
+check_arrival_expand_ecl_sub (const re_dfa_t *dfa, re_node_set *dst_nodes,
+ int target, int ex_subexp, int type)
+{
+ int cur_node;
+ for (cur_node = target; !re_node_set_contains (dst_nodes, cur_node);)
+ {
+ int err;
+
+ if (dfa->nodes[cur_node].type == type
+ && dfa->nodes[cur_node].opr.idx == ex_subexp)
+ {
+ if (type == OP_CLOSE_SUBEXP)
+ {
+ err = re_node_set_insert (dst_nodes, cur_node);
+ if (BE (err == -1, 0))
+ return REG_ESPACE;
+ }
+ break;
+ }
+ err = re_node_set_insert (dst_nodes, cur_node);
+ if (BE (err == -1, 0))
+ return REG_ESPACE;
+ if (dfa->edests[cur_node].nelem == 0)
+ break;
+ if (dfa->edests[cur_node].nelem == 2)
+ {
+ err = check_arrival_expand_ecl_sub (dfa, dst_nodes,
+ dfa->edests[cur_node].elems[1],
+ ex_subexp, type);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ cur_node = dfa->edests[cur_node].elems[0];
+ }
+ return REG_NOERROR;
+}
+
+
+/* For all the back references in the current state, calculate the
+ destination of the back references by the appropriate entry
+ in MCTX->BKREF_ENTS. */
+
+static reg_errcode_t
+internal_function
+expand_bkref_cache (re_match_context_t *mctx, re_node_set *cur_nodes,
+ int cur_str, int subexp_num, int type)
+{
+ const re_dfa_t *const dfa = mctx->dfa;
+ reg_errcode_t err;
+ int cache_idx_start = search_cur_bkref_entry (mctx, cur_str);
+ struct re_backref_cache_entry *ent;
+
+ if (cache_idx_start == -1)
+ return REG_NOERROR;
+
+ restart:
+ ent = mctx->bkref_ents + cache_idx_start;
+ do
+ {
+ int to_idx, next_node;
+
+ /* Is this entry ENT is appropriate? */
+ if (!re_node_set_contains (cur_nodes, ent->node))
+ continue; /* No. */
+
+ to_idx = cur_str + ent->subexp_to - ent->subexp_from;
+ /* Calculate the destination of the back reference, and append it
+ to MCTX->STATE_LOG. */
+ if (to_idx == cur_str)
+ {
+ /* The backreference did epsilon transit, we must re-check all the
+ node in the current state. */
+ re_node_set new_dests;
+ reg_errcode_t err2, err3;
+ next_node = dfa->edests[ent->node].elems[0];
+ if (re_node_set_contains (cur_nodes, next_node))
+ continue;
+ err = re_node_set_init_1 (&new_dests, next_node);
+ err2 = check_arrival_expand_ecl (dfa, &new_dests, subexp_num, type);
+ err3 = re_node_set_merge (cur_nodes, &new_dests);
+ re_node_set_free (&new_dests);
+ if (BE (err != REG_NOERROR || err2 != REG_NOERROR
+ || err3 != REG_NOERROR, 0))
+ {
+ err = (err != REG_NOERROR ? err
+ : (err2 != REG_NOERROR ? err2 : err3));
+ return err;
+ }
+ /* TODO: It is still inefficient... */
+ goto restart;
+ }
+ else
+ {
+ re_node_set union_set;
+ next_node = dfa->nexts[ent->node];
+ if (mctx->state_log[to_idx])
+ {
+ int ret;
+ if (re_node_set_contains (&mctx->state_log[to_idx]->nodes,
+ next_node))
+ continue;
+ err = re_node_set_init_copy (&union_set,
+ &mctx->state_log[to_idx]->nodes);
+ ret = re_node_set_insert (&union_set, next_node);
+ if (BE (err != REG_NOERROR || ret < 0, 0))
+ {
+ re_node_set_free (&union_set);
+ err = err != REG_NOERROR ? err : REG_ESPACE;
+ return err;
+ }
+ }
+ else
+ {
+ err = re_node_set_init_1 (&union_set, next_node);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ mctx->state_log[to_idx] = re_acquire_state (&err, dfa, &union_set);
+ re_node_set_free (&union_set);
+ if (BE (mctx->state_log[to_idx] == NULL
+ && err != REG_NOERROR, 0))
+ return err;
+ }
+ }
+ while (ent++->more);
+ return REG_NOERROR;
+}
+
+/* Build transition table for the state.
+ Return 1 if succeeded, otherwise return NULL. */
+
+static int
+internal_function
+build_trtable (const re_dfa_t *dfa, re_dfastate_t *state)
+{
+ reg_errcode_t err;
+ int i, j, ch, need_word_trtable = 0;
+ bitset_word_t elem, mask;
+ bool dests_node_malloced = false;
+ bool dest_states_malloced = false;
+ int ndests; /* Number of the destination states from `state'. */
+ re_dfastate_t **trtable;
+ re_dfastate_t **dest_states = NULL, **dest_states_word, **dest_states_nl;
+ re_node_set follows, *dests_node;
+ bitset_t *dests_ch;
+ bitset_t acceptable;
+
+ struct dests_alloc
+ {
+ re_node_set dests_node[SBC_MAX];
+ bitset_t dests_ch[SBC_MAX];
+ } *dests_alloc;
+
+ /* We build DFA states which corresponds to the destination nodes
+ from `state'. `dests_node[i]' represents the nodes which i-th
+ destination state contains, and `dests_ch[i]' represents the
+ characters which i-th destination state accepts. */
+#ifdef HAVE_ALLOCA
+ if (__libc_use_alloca (sizeof (struct dests_alloc)))
+ dests_alloc = (struct dests_alloc *) alloca (sizeof (struct dests_alloc));
+ else
+#endif
+ {
+ dests_alloc = re_malloc (struct dests_alloc, 1);
+ if (BE (dests_alloc == NULL, 0))
+ return 0;
+ dests_node_malloced = true;
+ }
+ dests_node = dests_alloc->dests_node;
+ dests_ch = dests_alloc->dests_ch;
+
+ /* Initialize transiton table. */
+ state->word_trtable = state->trtable = NULL;
+
+ /* At first, group all nodes belonging to `state' into several
+ destinations. */
+ ndests = group_nodes_into_DFAstates (dfa, state, dests_node, dests_ch);
+ if (BE (ndests <= 0, 0))
+ {
+ if (dests_node_malloced)
+ free (dests_alloc);
+ /* Return 0 in case of an error, 1 otherwise. */
+ if (ndests == 0)
+ {
+ state->trtable = (re_dfastate_t **)
+ calloc (sizeof (re_dfastate_t *), SBC_MAX);
+ return 1;
+ }
+ return 0;
+ }
+
+ err = re_node_set_alloc (&follows, ndests + 1);
+ if (BE (err != REG_NOERROR, 0))
+ goto out_free;
+
+ /* Avoid arithmetic overflow in size calculation. */
+ if (BE ((((SIZE_MAX - (sizeof (re_node_set) + sizeof (bitset_t)) * SBC_MAX)
+ / (3 * sizeof (re_dfastate_t *)))
+ < ndests),
+ 0))
+ goto out_free;
+
+#ifdef HAVE_ALLOCA
+ if (__libc_use_alloca ((sizeof (re_node_set) + sizeof (bitset_t)) * SBC_MAX
+ + ndests * 3 * sizeof (re_dfastate_t *)))
+ dest_states = (re_dfastate_t **)
+ alloca (ndests * 3 * sizeof (re_dfastate_t *));
+ else
+#endif
+ {
+ dest_states = (re_dfastate_t **)
+ malloc (ndests * 3 * sizeof (re_dfastate_t *));
+ if (BE (dest_states == NULL, 0))
+ {
+out_free:
+ if (dest_states_malloced)
+ free (dest_states);
+ re_node_set_free (&follows);
+ for (i = 0; i < ndests; ++i)
+ re_node_set_free (dests_node + i);
+ if (dests_node_malloced)
+ free (dests_alloc);
+ return 0;
+ }
+ dest_states_malloced = true;
+ }
+ dest_states_word = dest_states + ndests;
+ dest_states_nl = dest_states_word + ndests;
+ bitset_empty (acceptable);
+
+ /* Then build the states for all destinations. */
+ for (i = 0; i < ndests; ++i)
+ {
+ int next_node;
+ re_node_set_empty (&follows);
+ /* Merge the follows of this destination states. */
+ for (j = 0; j < dests_node[i].nelem; ++j)
+ {
+ next_node = dfa->nexts[dests_node[i].elems[j]];
+ if (next_node != -1)
+ {
+ err = re_node_set_merge (&follows, dfa->eclosures + next_node);
+ if (BE (err != REG_NOERROR, 0))
+ goto out_free;
+ }
+ }
+ dest_states[i] = re_acquire_state_context (&err, dfa, &follows, 0);
+ if (BE (dest_states[i] == NULL && err != REG_NOERROR, 0))
+ goto out_free;
+ /* If the new state has context constraint,
+ build appropriate states for these contexts. */
+ if (dest_states[i]->has_constraint)
+ {
+ dest_states_word[i] = re_acquire_state_context (&err, dfa, &follows,
+ CONTEXT_WORD);
+ if (BE (dest_states_word[i] == NULL && err != REG_NOERROR, 0))
+ goto out_free;
+
+ if (dest_states[i] != dest_states_word[i] && dfa->mb_cur_max > 1)
+ need_word_trtable = 1;
+
+ dest_states_nl[i] = re_acquire_state_context (&err, dfa, &follows,
+ CONTEXT_NEWLINE);
+ if (BE (dest_states_nl[i] == NULL && err != REG_NOERROR, 0))
+ goto out_free;
+ }
+ else
+ {
+ dest_states_word[i] = dest_states[i];
+ dest_states_nl[i] = dest_states[i];
+ }
+ bitset_merge (acceptable, dests_ch[i]);
+ }
+
+ if (!BE (need_word_trtable, 0))
+ {
+ /* We don't care about whether the following character is a word
+ character, or we are in a single-byte character set so we can
+ discern by looking at the character code: allocate a
+ 256-entry transition table. */
+ trtable = state->trtable =
+ (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), SBC_MAX);
+ if (BE (trtable == NULL, 0))
+ goto out_free;
+
+ /* For all characters ch...: */
+ for (i = 0; i < BITSET_WORDS; ++i)
+ for (ch = i * BITSET_WORD_BITS, elem = acceptable[i], mask = 1;
+ elem;
+ mask <<= 1, elem >>= 1, ++ch)
+ if (BE (elem & 1, 0))
+ {
+ /* There must be exactly one destination which accepts
+ character ch. See group_nodes_into_DFAstates. */
+ for (j = 0; (dests_ch[j][i] & mask) == 0; ++j)
+ ;
+
+ /* j-th destination accepts the word character ch. */
+ if (dfa->word_char[i] & mask)
+ trtable[ch] = dest_states_word[j];
+ else
+ trtable[ch] = dest_states[j];
+ }
+ }
+ else
+ {
+ /* We care about whether the following character is a word
+ character, and we are in a multi-byte character set: discern
+ by looking at the character code: build two 256-entry
+ transition tables, one starting at trtable[0] and one
+ starting at trtable[SBC_MAX]. */
+ trtable = state->word_trtable =
+ (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), 2 * SBC_MAX);
+ if (BE (trtable == NULL, 0))
+ goto out_free;
+
+ /* For all characters ch...: */
+ for (i = 0; i < BITSET_WORDS; ++i)
+ for (ch = i * BITSET_WORD_BITS, elem = acceptable[i], mask = 1;
+ elem;
+ mask <<= 1, elem >>= 1, ++ch)
+ if (BE (elem & 1, 0))
+ {
+ /* There must be exactly one destination which accepts
+ character ch. See group_nodes_into_DFAstates. */
+ for (j = 0; (dests_ch[j][i] & mask) == 0; ++j)
+ ;
+
+ /* j-th destination accepts the word character ch. */
+ trtable[ch] = dest_states[j];
+ trtable[ch + SBC_MAX] = dest_states_word[j];
+ }
+ }
+
+ /* new line */
+ if (bitset_contain (acceptable, NEWLINE_CHAR))
+ {
+ /* The current state accepts newline character. */
+ for (j = 0; j < ndests; ++j)
+ if (bitset_contain (dests_ch[j], NEWLINE_CHAR))
+ {
+ /* k-th destination accepts newline character. */
+ trtable[NEWLINE_CHAR] = dest_states_nl[j];
+ if (need_word_trtable)
+ trtable[NEWLINE_CHAR + SBC_MAX] = dest_states_nl[j];
+ /* There must be only one destination which accepts
+ newline. See group_nodes_into_DFAstates. */
+ break;
+ }
+ }
+
+ if (dest_states_malloced)
+ free (dest_states);
+
+ re_node_set_free (&follows);
+ for (i = 0; i < ndests; ++i)
+ re_node_set_free (dests_node + i);
+
+ if (dests_node_malloced)
+ free (dests_alloc);
+
+ return 1;
+}
+
+/* Group all nodes belonging to STATE into several destinations.
+ Then for all destinations, set the nodes belonging to the destination
+ to DESTS_NODE[i] and set the characters accepted by the destination
+ to DEST_CH[i]. This function return the number of destinations. */
+
+static int
+internal_function
+group_nodes_into_DFAstates (const re_dfa_t *dfa, const re_dfastate_t *state,
+ re_node_set *dests_node, bitset_t *dests_ch)
+{
+ reg_errcode_t err;
+ int result;
+ int i, j, k;
+ int ndests; /* Number of the destinations from `state'. */
+ bitset_t accepts; /* Characters a node can accept. */
+ const re_node_set *cur_nodes = &state->nodes;
+ bitset_empty (accepts);
+ ndests = 0;
+
+ /* For all the nodes belonging to `state', */
+ for (i = 0; i < cur_nodes->nelem; ++i)
+ {
+ re_token_t *node = &dfa->nodes[cur_nodes->elems[i]];
+ re_token_type_t type = node->type;
+ unsigned int constraint = node->constraint;
+
+ /* Enumerate all single byte character this node can accept. */
+ if (type == CHARACTER)
+ bitset_set (accepts, node->opr.c);
+ else if (type == SIMPLE_BRACKET)
+ {
+ bitset_merge (accepts, node->opr.sbcset);
+ }
+ else if (type == OP_PERIOD)
+ {
+#ifdef RE_ENABLE_I18N
+ if (dfa->mb_cur_max > 1)
+ bitset_merge (accepts, dfa->sb_char);
+ else
+#endif
+ bitset_set_all (accepts);
+ if (!(dfa->syntax & RE_DOT_NEWLINE))
+ bitset_clear (accepts, '\n');
+ if (dfa->syntax & RE_DOT_NOT_NULL)
+ bitset_clear (accepts, '\0');
+ }
+#ifdef RE_ENABLE_I18N
+ else if (type == OP_UTF8_PERIOD)
+ {
+ memset (accepts, '\xff', sizeof (bitset_t) / 2);
+ if (!(dfa->syntax & RE_DOT_NEWLINE))
+ bitset_clear (accepts, '\n');
+ if (dfa->syntax & RE_DOT_NOT_NULL)
+ bitset_clear (accepts, '\0');
+ }
+#endif
+ else
+ continue;
+
+ /* Check the `accepts' and sift the characters which are not
+ match it the context. */
+ if (constraint)
+ {
+ if (constraint & NEXT_NEWLINE_CONSTRAINT)
+ {
+ bool accepts_newline = bitset_contain (accepts, NEWLINE_CHAR);
+ bitset_empty (accepts);
+ if (accepts_newline)
+ bitset_set (accepts, NEWLINE_CHAR);
+ else
+ continue;
+ }
+ if (constraint & NEXT_ENDBUF_CONSTRAINT)
+ {
+ bitset_empty (accepts);
+ continue;
+ }
+
+ if (constraint & NEXT_WORD_CONSTRAINT)
+ {
+ bitset_word_t any_set = 0;
+ if (type == CHARACTER && !node->word_char)
+ {
+ bitset_empty (accepts);
+ continue;
+ }
+#ifdef RE_ENABLE_I18N
+ if (dfa->mb_cur_max > 1)
+ for (j = 0; j < BITSET_WORDS; ++j)
+ any_set |= (accepts[j] &= (dfa->word_char[j] | ~dfa->sb_char[j]));
+ else
+#endif
+ for (j = 0; j < BITSET_WORDS; ++j)
+ any_set |= (accepts[j] &= dfa->word_char[j]);
+ if (!any_set)
+ continue;
+ }
+ if (constraint & NEXT_NOTWORD_CONSTRAINT)
+ {
+ bitset_word_t any_set = 0;
+ if (type == CHARACTER && node->word_char)
+ {
+ bitset_empty (accepts);
+ continue;
+ }
+#ifdef RE_ENABLE_I18N
+ if (dfa->mb_cur_max > 1)
+ for (j = 0; j < BITSET_WORDS; ++j)
+ any_set |= (accepts[j] &= ~(dfa->word_char[j] & dfa->sb_char[j]));
+ else
+#endif
+ for (j = 0; j < BITSET_WORDS; ++j)
+ any_set |= (accepts[j] &= ~dfa->word_char[j]);
+ if (!any_set)
+ continue;
+ }
+ }
+
+ /* Then divide `accepts' into DFA states, or create a new
+ state. Above, we make sure that accepts is not empty. */
+ for (j = 0; j < ndests; ++j)
+ {
+ bitset_t intersec; /* Intersection sets, see below. */
+ bitset_t remains;
+ /* Flags, see below. */
+ bitset_word_t has_intersec, not_subset, not_consumed;
+
+ /* Optimization, skip if this state doesn't accept the character. */
+ if (type == CHARACTER && !bitset_contain (dests_ch[j], node->opr.c))
+ continue;
+
+ /* Enumerate the intersection set of this state and `accepts'. */
+ has_intersec = 0;
+ for (k = 0; k < BITSET_WORDS; ++k)
+ has_intersec |= intersec[k] = accepts[k] & dests_ch[j][k];
+ /* And skip if the intersection set is empty. */
+ if (!has_intersec)
+ continue;
+
+ /* Then check if this state is a subset of `accepts'. */
+ not_subset = not_consumed = 0;
+ for (k = 0; k < BITSET_WORDS; ++k)
+ {
+ not_subset |= remains[k] = ~accepts[k] & dests_ch[j][k];
+ not_consumed |= accepts[k] = accepts[k] & ~dests_ch[j][k];
+ }
+
+ /* If this state isn't a subset of `accepts', create a
+ new group state, which has the `remains'. */
+ if (not_subset)
+ {
+ bitset_copy (dests_ch[ndests], remains);
+ bitset_copy (dests_ch[j], intersec);
+ err = re_node_set_init_copy (dests_node + ndests, &dests_node[j]);
+ if (BE (err != REG_NOERROR, 0))
+ goto error_return;
+ ++ndests;
+ }
+
+ /* Put the position in the current group. */
+ result = re_node_set_insert (&dests_node[j], cur_nodes->elems[i]);
+ if (BE (result < 0, 0))
+ goto error_return;
+
+ /* If all characters are consumed, go to next node. */
+ if (!not_consumed)
+ break;
+ }
+ /* Some characters remain, create a new group. */
+ if (j == ndests)
+ {
+ bitset_copy (dests_ch[ndests], accepts);
+ err = re_node_set_init_1 (dests_node + ndests, cur_nodes->elems[i]);
+ if (BE (err != REG_NOERROR, 0))
+ goto error_return;
+ ++ndests;
+ bitset_empty (accepts);
+ }
+ }
+ return ndests;
+ error_return:
+ for (j = 0; j < ndests; ++j)
+ re_node_set_free (dests_node + j);
+ return -1;
+}
+
+#ifdef RE_ENABLE_I18N
+/* Check how many bytes the node `dfa->nodes[node_idx]' accepts.
+ Return the number of the bytes the node accepts.
+ STR_IDX is the current index of the input string.
+
+ This function handles the nodes which can accept one character, or
+ one collating element like '.', '[a-z]', opposite to the other nodes
+ can only accept one byte. */
+
+static int
+internal_function
+check_node_accept_bytes (const re_dfa_t *dfa, int node_idx,
+ const re_string_t *input, int str_idx)
+{
+ const re_token_t *node = dfa->nodes + node_idx;
+ int char_len, elem_len;
+ int i;
+ wint_t wc;
+
+ if (BE (node->type == OP_UTF8_PERIOD, 0))
+ {
+ unsigned char c = re_string_byte_at (input, str_idx), d;
+ if (BE (c < 0xc2, 1))
+ return 0;
+
+ if (str_idx + 2 > input->len)
+ return 0;
+
+ d = re_string_byte_at (input, str_idx + 1);
+ if (c < 0xe0)
+ return (d < 0x80 || d > 0xbf) ? 0 : 2;
+ else if (c < 0xf0)
+ {
+ char_len = 3;
+ if (c == 0xe0 && d < 0xa0)
+ return 0;
+ }
+ else if (c < 0xf8)
+ {
+ char_len = 4;
+ if (c == 0xf0 && d < 0x90)
+ return 0;
+ }
+ else if (c < 0xfc)
+ {
+ char_len = 5;
+ if (c == 0xf8 && d < 0x88)
+ return 0;
+ }
+ else if (c < 0xfe)
+ {
+ char_len = 6;
+ if (c == 0xfc && d < 0x84)
+ return 0;
+ }
+ else
+ return 0;
+
+ if (str_idx + char_len > input->len)
+ return 0;
+
+ for (i = 1; i < char_len; ++i)
+ {
+ d = re_string_byte_at (input, str_idx + i);
+ if (d < 0x80 || d > 0xbf)
+ return 0;
+ }
+ return char_len;
+ }
+
+ char_len = re_string_char_size_at (input, str_idx);
+ if (node->type == OP_PERIOD)
+ {
+ if (char_len <= 1)
+ return 0;
+ /* FIXME: I don't think this if is needed, as both '\n'
+ and '\0' are char_len == 1. */
+ /* '.' accepts any one character except the following two cases. */
+ if ((!(dfa->syntax & RE_DOT_NEWLINE) &&
+ re_string_byte_at (input, str_idx) == '\n') ||
+ ((dfa->syntax & RE_DOT_NOT_NULL) &&
+ re_string_byte_at (input, str_idx) == '\0'))
+ return 0;
+ return char_len;
+ }
+
+ elem_len = re_string_elem_size_at (input, str_idx);
+ wc = __btowc(*(input->mbs+str_idx));
+ if (((elem_len <= 1 && char_len <= 1) || char_len == 0) && (wc != WEOF && wc < SBC_MAX))
+ return 0;
+
+ if (node->type == COMPLEX_BRACKET)
+ {
+ const re_charset_t *cset = node->opr.mbcset;
+# ifdef _LIBC
+ const unsigned char *pin
+ = ((const unsigned char *) re_string_get_buffer (input) + str_idx);
+ int j;
+ uint32_t nrules;
+# endif /* _LIBC */
+ int match_len = 0;
+ wchar_t wc = ((cset->nranges || cset->nchar_classes || cset->nmbchars)
+ ? re_string_wchar_at (input, str_idx) : 0);
+
+ /* match with multibyte character? */
+ for (i = 0; i < cset->nmbchars; ++i)
+ if (wc == cset->mbchars[i])
+ {
+ match_len = char_len;
+ goto check_node_accept_bytes_match;
+ }
+ /* match with character_class? */
+ for (i = 0; i < cset->nchar_classes; ++i)
+ {
+ wctype_t wt = cset->char_classes[i];
+ if (__iswctype (wc, wt))
+ {
+ match_len = char_len;
+ goto check_node_accept_bytes_match;
+ }
+ }
+
+# ifdef _LIBC
+ nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+ if (nrules != 0)
+ {
+ unsigned int in_collseq = 0;
+ const int32_t *table, *indirect;
+ const unsigned char *weights, *extra;
+ const char *collseqwc;
+ /* This #include defines a local function! */
+# include <locale/weight.h>
+
+ /* match with collating_symbol? */
+ if (cset->ncoll_syms)
+ extra = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB);
+ for (i = 0; i < cset->ncoll_syms; ++i)
+ {
+ const unsigned char *coll_sym = extra + cset->coll_syms[i];
+ /* Compare the length of input collating element and
+ the length of current collating element. */
+ if (*coll_sym != elem_len)
+ continue;
+ /* Compare each bytes. */
+ for (j = 0; j < *coll_sym; j++)
+ if (pin[j] != coll_sym[1 + j])
+ break;
+ if (j == *coll_sym)
+ {
+ /* Match if every bytes is equal. */
+ match_len = j;
+ goto check_node_accept_bytes_match;
+ }
+ }
+
+ if (cset->nranges)
+ {
+ if (elem_len <= char_len)
+ {
+ collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC);
+ in_collseq = __collseq_table_lookup (collseqwc, wc);
+ }
+ else
+ in_collseq = find_collation_sequence_value (pin, elem_len);
+ }
+ /* match with range expression? */
+ for (i = 0; i < cset->nranges; ++i)
+ if (cset->range_starts[i] <= in_collseq
+ && in_collseq <= cset->range_ends[i])
+ {
+ match_len = elem_len;
+ goto check_node_accept_bytes_match;
+ }
+
+ /* match with equivalence_class? */
+ if (cset->nequiv_classes)
+ {
+ const unsigned char *cp = pin;
+ table = (const int32_t *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+ weights = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTMB);
+ extra = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB);
+ indirect = (const int32_t *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTMB);
+ int32_t idx = findidx (&cp);
+ if (idx > 0)
+ for (i = 0; i < cset->nequiv_classes; ++i)
+ {
+ int32_t equiv_class_idx = cset->equiv_classes[i];
+ size_t weight_len = weights[idx & 0xffffff];
+ if (weight_len == weights[equiv_class_idx & 0xffffff]
+ && (idx >> 24) == (equiv_class_idx >> 24))
+ {
+ int cnt = 0;
+
+ idx &= 0xffffff;
+ equiv_class_idx &= 0xffffff;
+
+ while (cnt <= weight_len
+ && (weights[equiv_class_idx + 1 + cnt]
+ == weights[idx + 1 + cnt]))
+ ++cnt;
+ if (cnt > weight_len)
+ {
+ match_len = elem_len;
+ goto check_node_accept_bytes_match;
+ }
+ }
+ }
+ }
+ }
+ else
+# endif /* _LIBC */
+ {
+ /* match with range expression? */
+#if __GNUC__ >= 2
+ wchar_t cmp_buf[] = {L'\0', L'\0', wc, L'\0', L'\0', L'\0'};
+#else
+ wchar_t cmp_buf[] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'};
+ cmp_buf[2] = wc;
+#endif
+ for (i = 0; i < cset->nranges; ++i)
+ {
+ cmp_buf[0] = cset->range_starts[i];
+ cmp_buf[4] = cset->range_ends[i];
+ if (wcscoll (cmp_buf, cmp_buf + 2) <= 0
+ && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0)
+ {
+ match_len = char_len;
+ goto check_node_accept_bytes_match;
+ }
+ }
+ }
+ check_node_accept_bytes_match:
+ if (!cset->non_match)
+ return match_len;
+ else
+ {
+ if (match_len > 0)
+ return 0;
+ else
+ return (elem_len > char_len) ? elem_len : char_len;
+ }
+ }
+ return 0;
+}
+
+# ifdef _LIBC
+static unsigned int
+internal_function
+find_collation_sequence_value (const unsigned char *mbs, size_t mbs_len)
+{
+ uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+ if (nrules == 0)
+ {
+ if (mbs_len == 1)
+ {
+ /* No valid character. Match it as a single byte character. */
+ const unsigned char *collseq = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB);
+ return collseq[mbs[0]];
+ }
+ return UINT_MAX;
+ }
+ else
+ {
+ int32_t idx;
+ const unsigned char *extra = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB);
+ int32_t extrasize = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB + 1) - extra;
+
+ for (idx = 0; idx < extrasize;)
+ {
+ int mbs_cnt, found = 0;
+ int32_t elem_mbs_len;
+ /* Skip the name of collating element name. */
+ idx = idx + extra[idx] + 1;
+ elem_mbs_len = extra[idx++];
+ if (mbs_len == elem_mbs_len)
+ {
+ for (mbs_cnt = 0; mbs_cnt < elem_mbs_len; ++mbs_cnt)
+ if (extra[idx + mbs_cnt] != mbs[mbs_cnt])
+ break;
+ if (mbs_cnt == elem_mbs_len)
+ /* Found the entry. */
+ found = 1;
+ }
+ /* Skip the byte sequence of the collating element. */
+ idx += elem_mbs_len;
+ /* Adjust for the alignment. */
+ idx = (idx + 3) & ~3;
+ /* Skip the collation sequence value. */
+ idx += sizeof (uint32_t);
+ /* Skip the wide char sequence of the collating element. */
+ idx = idx + sizeof (uint32_t) * (extra[idx] + 1);
+ /* If we found the entry, return the sequence value. */
+ if (found)
+ return *(uint32_t *) (extra + idx);
+ /* Skip the collation sequence value. */
+ idx += sizeof (uint32_t);
+ }
+ return UINT_MAX;
+ }
+}
+# endif /* _LIBC */
+#endif /* RE_ENABLE_I18N */
+
+/* Check whether the node accepts the byte which is IDX-th
+ byte of the INPUT. */
+
+static int
+internal_function
+check_node_accept (const re_match_context_t *mctx, const re_token_t *node,
+ int idx)
+{
+ unsigned char ch;
+ ch = re_string_byte_at (&mctx->input, idx);
+ switch (node->type)
+ {
+ case CHARACTER:
+ if (node->opr.c != ch)
+ return 0;
+ break;
+
+ case SIMPLE_BRACKET:
+ if (!bitset_contain (node->opr.sbcset, ch))
+ return 0;
+ break;
+
+#ifdef RE_ENABLE_I18N
+ case OP_UTF8_PERIOD:
+ if (ch >= 0x80)
+ return 0;
+ /* FALLTHROUGH */
+#endif
+ case OP_PERIOD:
+ if ((ch == '\n' && !(mctx->dfa->syntax & RE_DOT_NEWLINE))
+ || (ch == '\0' && (mctx->dfa->syntax & RE_DOT_NOT_NULL)))
+ return 0;
+ break;
+
+ default:
+ return 0;
+ }
+
+ if (node->constraint)
+ {
+ /* The node has constraints. Check whether the current context
+ satisfies the constraints. */
+ unsigned int context = re_string_context_at (&mctx->input, idx,
+ mctx->eflags);
+ if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context))
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Extend the buffers, if the buffers have run out. */
+
+static reg_errcode_t
+internal_function
+extend_buffers (re_match_context_t *mctx)
+{
+ reg_errcode_t ret;
+ re_string_t *pstr = &mctx->input;
+
+ /* Avoid overflow. */
+ if (BE (INT_MAX / 2 / sizeof (re_dfastate_t *) <= pstr->bufs_len, 0))
+ return REG_ESPACE;
+
+ /* Double the lengthes of the buffers. */
+ ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+
+ if (mctx->state_log != NULL)
+ {
+ /* And double the length of state_log. */
+ /* XXX We have no indication of the size of this buffer. If this
+ allocation fail we have no indication that the state_log array
+ does not have the right size. */
+ re_dfastate_t **new_array = re_realloc (mctx->state_log, re_dfastate_t *,
+ pstr->bufs_len + 1);
+ if (BE (new_array == NULL, 0))
+ return REG_ESPACE;
+ mctx->state_log = new_array;
+ }
+
+ /* Then reconstruct the buffers. */
+ if (pstr->icase)
+ {
+#ifdef RE_ENABLE_I18N
+ if (pstr->mb_cur_max > 1)
+ {
+ ret = build_wcs_upper_buffer (pstr);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+ }
+ else
+#endif /* RE_ENABLE_I18N */
+ build_upper_buffer (pstr);
+ }
+ else
+ {
+#ifdef RE_ENABLE_I18N
+ if (pstr->mb_cur_max > 1)
+ build_wcs_buffer (pstr);
+ else
+#endif /* RE_ENABLE_I18N */
+ {
+ if (pstr->trans != NULL)
+ re_string_translate_buffer (pstr);
+ }
+ }
+ return REG_NOERROR;
+}
+
+
+/* Functions for matching context. */
+
+/* Initialize MCTX. */
+
+static reg_errcode_t
+internal_function
+match_ctx_init (re_match_context_t *mctx, int eflags, int n)
+{
+ mctx->eflags = eflags;
+ mctx->match_last = -1;
+ if (n > 0)
+ {
+ mctx->bkref_ents = re_malloc (struct re_backref_cache_entry, n);
+ mctx->sub_tops = re_malloc (re_sub_match_top_t *, n);
+ if (BE (mctx->bkref_ents == NULL || mctx->sub_tops == NULL, 0))
+ return REG_ESPACE;
+ }
+ /* Already zero-ed by the caller.
+ else
+ mctx->bkref_ents = NULL;
+ mctx->nbkref_ents = 0;
+ mctx->nsub_tops = 0; */
+ mctx->abkref_ents = n;
+ mctx->max_mb_elem_len = 1;
+ mctx->asub_tops = n;
+ return REG_NOERROR;
+}
+
+/* Clean the entries which depend on the current input in MCTX.
+ This function must be invoked when the matcher changes the start index
+ of the input, or changes the input string. */
+
+static void
+internal_function
+match_ctx_clean (re_match_context_t *mctx)
+{
+ int st_idx;
+ for (st_idx = 0; st_idx < mctx->nsub_tops; ++st_idx)
+ {
+ int sl_idx;
+ re_sub_match_top_t *top = mctx->sub_tops[st_idx];
+ for (sl_idx = 0; sl_idx < top->nlasts; ++sl_idx)
+ {
+ re_sub_match_last_t *last = top->lasts[sl_idx];
+ re_free (last->path.array);
+ re_free (last);
+ }
+ re_free (top->lasts);
+ if (top->path)
+ {
+ re_free (top->path->array);
+ re_free (top->path);
+ }
+ free (top);
+ }
+
+ mctx->nsub_tops = 0;
+ mctx->nbkref_ents = 0;
+}
+
+/* Free all the memory associated with MCTX. */
+
+static void
+internal_function
+match_ctx_free (re_match_context_t *mctx)
+{
+ /* First, free all the memory associated with MCTX->SUB_TOPS. */
+ match_ctx_clean (mctx);
+ re_free (mctx->sub_tops);
+ re_free (mctx->bkref_ents);
+}
+
+/* Add a new backreference entry to MCTX.
+ Note that we assume that caller never call this function with duplicate
+ entry, and call with STR_IDX which isn't smaller than any existing entry.
+*/
+
+static reg_errcode_t
+internal_function
+match_ctx_add_entry (re_match_context_t *mctx, int node, int str_idx, int from,
+ int to)
+{
+ if (mctx->nbkref_ents >= mctx->abkref_ents)
+ {
+ struct re_backref_cache_entry* new_entry;
+ new_entry = re_realloc (mctx->bkref_ents, struct re_backref_cache_entry,
+ mctx->abkref_ents * 2);
+ if (BE (new_entry == NULL, 0))
+ {
+ re_free (mctx->bkref_ents);
+ return REG_ESPACE;
+ }
+ mctx->bkref_ents = new_entry;
+ memset (mctx->bkref_ents + mctx->nbkref_ents, '\0',
+ sizeof (struct re_backref_cache_entry) * mctx->abkref_ents);
+ mctx->abkref_ents *= 2;
+ }
+ if (mctx->nbkref_ents > 0
+ && mctx->bkref_ents[mctx->nbkref_ents - 1].str_idx == str_idx)
+ mctx->bkref_ents[mctx->nbkref_ents - 1].more = 1;
+
+ mctx->bkref_ents[mctx->nbkref_ents].node = node;
+ mctx->bkref_ents[mctx->nbkref_ents].str_idx = str_idx;
+ mctx->bkref_ents[mctx->nbkref_ents].subexp_from = from;
+ mctx->bkref_ents[mctx->nbkref_ents].subexp_to = to;
+
+ /* This is a cache that saves negative results of check_dst_limits_calc_pos.
+ If bit N is clear, means that this entry won't epsilon-transition to
+ an OP_OPEN_SUBEXP or OP_CLOSE_SUBEXP for the N+1-th subexpression. If
+ it is set, check_dst_limits_calc_pos_1 will recurse and try to find one
+ such node.
+
+ A backreference does not epsilon-transition unless it is empty, so set
+ to all zeros if FROM != TO. */
+ mctx->bkref_ents[mctx->nbkref_ents].eps_reachable_subexps_map
+ = (from == to ? ~0 : 0);
+
+ mctx->bkref_ents[mctx->nbkref_ents++].more = 0;
+ if (mctx->max_mb_elem_len < to - from)
+ mctx->max_mb_elem_len = to - from;
+ return REG_NOERROR;
+}
+
+/* Search for the first entry which has the same str_idx, or -1 if none is
+ found. Note that MCTX->BKREF_ENTS is already sorted by MCTX->STR_IDX. */
+
+static int
+internal_function
+search_cur_bkref_entry (const re_match_context_t *mctx, int str_idx)
+{
+ int left, right, mid, last;
+ last = right = mctx->nbkref_ents;
+ for (left = 0; left < right;)
+ {
+ mid = (left + right) / 2;
+ if (mctx->bkref_ents[mid].str_idx < str_idx)
+ left = mid + 1;
+ else
+ right = mid;
+ }
+ if (left < last && mctx->bkref_ents[left].str_idx == str_idx)
+ return left;
+ else
+ return -1;
+}
+
+/* Register the node NODE, whose type is OP_OPEN_SUBEXP, and which matches
+ at STR_IDX. */
+
+static reg_errcode_t
+internal_function
+match_ctx_add_subtop (re_match_context_t *mctx, int node, int str_idx)
+{
+#ifdef DEBUG
+ assert (mctx->sub_tops != NULL);
+ assert (mctx->asub_tops > 0);
+#endif
+ if (BE (mctx->nsub_tops == mctx->asub_tops, 0))
+ {
+ int new_asub_tops = mctx->asub_tops * 2;
+ re_sub_match_top_t **new_array = re_realloc (mctx->sub_tops,
+ re_sub_match_top_t *,
+ new_asub_tops);
+ if (BE (new_array == NULL, 0))
+ return REG_ESPACE;
+ mctx->sub_tops = new_array;
+ mctx->asub_tops = new_asub_tops;
+ }
+ mctx->sub_tops[mctx->nsub_tops] = calloc (1, sizeof (re_sub_match_top_t));
+ if (BE (mctx->sub_tops[mctx->nsub_tops] == NULL, 0))
+ return REG_ESPACE;
+ mctx->sub_tops[mctx->nsub_tops]->node = node;
+ mctx->sub_tops[mctx->nsub_tops++]->str_idx = str_idx;
+ return REG_NOERROR;
+}
+
+/* Register the node NODE, whose type is OP_CLOSE_SUBEXP, and which matches
+ at STR_IDX, whose corresponding OP_OPEN_SUBEXP is SUB_TOP. */
+
+static re_sub_match_last_t *
+internal_function
+match_ctx_add_sublast (re_sub_match_top_t *subtop, int node, int str_idx)
+{
+ re_sub_match_last_t *new_entry;
+ if (BE (subtop->nlasts == subtop->alasts, 0))
+ {
+ int new_alasts = 2 * subtop->alasts + 1;
+ re_sub_match_last_t **new_array = re_realloc (subtop->lasts,
+ re_sub_match_last_t *,
+ new_alasts);
+ if (BE (new_array == NULL, 0))
+ return NULL;
+ subtop->lasts = new_array;
+ subtop->alasts = new_alasts;
+ }
+ new_entry = calloc (1, sizeof (re_sub_match_last_t));
+ if (BE (new_entry != NULL, 1))
+ {
+ subtop->lasts[subtop->nlasts] = new_entry;
+ new_entry->node = node;
+ new_entry->str_idx = str_idx;
+ ++subtop->nlasts;
+ }
+ return new_entry;
+}
+
+static void
+internal_function
+sift_ctx_init (re_sift_context_t *sctx, re_dfastate_t **sifted_sts,
+ re_dfastate_t **limited_sts, int last_node, int last_str_idx)
+{
+ sctx->sifted_states = sifted_sts;
+ sctx->limited_states = limited_sts;
+ sctx->last_node = last_node;
+ sctx->last_str_idx = last_str_idx;
+ re_node_set_init_empty (&sctx->limits);
+}
diff --git a/docs/error-handling.md b/docs/error-handling.md
new file mode 100644
index 000000000..04c855fbc
--- /dev/null
+++ b/docs/error-handling.md
@@ -0,0 +1,111 @@
+Error reporting in libgit2
+==========================
+
+Error reporting is performed on an explicit `git_error **` argument, which appears at the end of all API calls that can return an error. Yes, this does clutter the API.
+
+When a function fails, an error is set on the error variable **and** returns one of the generic error codes.
+
+~~~c
+int git_repository_open(git_repository **repository, const char *path, git_error **error)
+{
+ // perform some opening
+ if (p_exists(path) < 0) {
+ giterr_set(error, GITERR_REPOSITORY, "The path '%s' doesn't exist", path);
+ return GIT_ENOTFOUND;
+ }
+
+ ...
+
+ if (try_to_parse(path, error) < 0)
+ return GIT_ERROR;
+
+ ...
+}
+~~~
+
+The simple error API
+--------------------
+
+- `void giterr_set(git_error **, int, const char *, ...)`: the main function used to set an error. It allocates a new error object and stores it in the passed error pointer. It has no return value. The arguments for `giterr_set` are as follows:
+
+ - `git_error **error_ptr`: the pointer where the error will be created.
+ - `int error_class`: the class for the error. This is **not** an error code: this is an speficic enum that specifies the error family. The point is to map these families 1-1 with Exception types on higher level languages (e.g. GitRepositoryException)
+ - `const char *error_str, ...`: the error string, with optional formatting arguments
+
+- `void giterr_free(git_error *)`: takes an error and frees it. This function is available in the external API.
+
+- `void giterr_clear(git_error **)`: clears an error previously set in an error pointer, setting it to NULL and calling `giterr_free` on it.
+
+- `void giterr_propagate(git_error **, git_error *)`: moves an error to a given error pointer, handling the case when the error pointer is NULL (in that case the error gets freed, because it cannot be propagated).
+
+The new error code return values
+--------------------------------
+
+We are doing this the POSIX way: one error code for each "expected failure", and a generic error code for all the critical failures.
+
+For instance: A reference lookup can have an expected failure (which is when the reference cannot be found), and a critical failure (which could be any of a long list of things that could go wrong, such as the refs packfile being corrupted, a loose ref being written with the wrong permissions, etc). We cannot have distinct error codes for every single error in the library, hence `git_reference_lookup` would return GIT_SUCCESS if the operation was successful, GIT_ENOTFOUND when the reference doesn't exist, and GIT_ERROR when an error happens -- **the error is then detailed in the `git_error` parameter**.
+
+Please be smart when returning error codes. Functions have max two "expected errors", and in most cases only one.
+
+Writing error messages
+----------------------
+
+Here are some guidelines when writing error messages:
+
+- Use proper English, and an impersonal or past tenses: *The given path does not exist*, *Failed to lookup object in ODB*
+
+- Use short, direct and objective messages. **One line, max**. libgit2 is a low level library: think that all the messages reported will be thrown as Ruby or Python exceptions. Think how long are common exception messages in those languages.
+
+- **Do not add redundant information to the error message**, specially information that can be infered from the context.
+
+ E.g. in `git_repository_open`, do not report a message like "Failed to open repository: path not found". Somebody is
+ calling that function. If it fails, he already knows that the repository failed to open!
+
+General guidelines for error reporting
+--------------------------------------
+
+- We never handle programming errors with these functions. Programming errors are `assert`ed, and when their source is internal, fixed as soon as possible. This is C, people.
+
+ Example of programming errors that would **not** be handled: passing NULL to a function that expects a valid pointer; passing a `git_tree` to a function that expects a `git_commit`. All these cases need to be identified with `assert` and fixed asap.
+
+ Example of a runtime error: failing to parse a `git_tree` because it contains invalid data. Failing to open a file because it doesn't exist on disk. These errors would be handled, and a `git_error` would be set.
+
+- The `git_error **` argument is always the last in the signature of all API calls. No exceptions.
+
+- When the programmer (or us, internally) doesn't need error handling, he can pass `NULL` to the `git_error **` param. This means that the errors won't be *reported*, but obviously they still will be handled (i.e. the failing function will interrupt and return cleanly). This is transparently handled by `giterr_set`
+
+- `git_error *` **must be initialized to `NULL` before passing its value to a function!!**
+
+ ~~~c
+ git_error *err;
+ git_error *good_error = NULL;
+
+ git_foo_func(arg1, arg2, &error); // invalid: `error` is not initialized
+ git_foo_func2(arg1, arg2, &good_error); // OK!
+ git_foo_func3(arg1, arg2, NULL); // OK! But no error reporting!
+ ~~~
+
+- Piling up errors is an error! Don't do this! Errors must always be free'd when a function returns.
+
+ ~~~c
+ git_error *error = NULL;
+
+ git_foo_func1(arg1, &error);
+ git_foo_func2(arg2, &error); // WRONG! What if func1 failed? `error` would leak!
+ ~~~
+
+- Likewise: do not rethrow errors internally!
+
+ ~~~c
+ int git_commit_create(..., git_error **error)
+ {
+ if (git_reference_exists("HEAD", error) < 0) {
+ /* HEAD does not exist; create it so we can commit... */
+ if (git_reference_create("HEAD", error) < 0) {
+ /* error could be rethrown */
+ }
+ }
+
+- Remember that errors are now allocated, and hence they need to be free'd after they've been used. Failure to do so internally (e.g. in the already seen examples of error piling) will be reported by Valgrind, so we can easily find where are we rethrowing errors.
+
+- Remember that any function that fails **will set an error object**, and that object will be freed.
diff --git a/examples/Makefile b/examples/Makefile
index efb55547b..fe99c75cb 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -1,13 +1,15 @@
.PHONY: all
CC = gcc
-CFLAGS = -g -I../include
+CFLAGS = -g -I../include -I../src
LFLAGS = -L../build -lgit2 -lz
+APPS = general showindex diff
-all: general showindex
+all: $(APPS)
% : %.c
$(CC) -o $@ $(CFLAGS) $< $(LFLAGS)
clean:
- $(RM) general showindex
+ $(RM) $(APPS)
+ $(RM) -r *.dSYM
diff --git a/examples/diff.c b/examples/diff.c
new file mode 100644
index 000000000..1b4ab549b
--- /dev/null
+++ b/examples/diff.c
@@ -0,0 +1,240 @@
+#include <stdio.h>
+#include <git2.h>
+#include <stdlib.h>
+#include <string.h>
+
+void check(int error, const char *message)
+{
+ if (error) {
+ fprintf(stderr, "%s (%d)\n", message, error);
+ exit(1);
+ }
+}
+
+int resolve_to_tree(git_repository *repo, const char *identifier, git_tree **tree)
+{
+ int err = 0;
+ size_t len = strlen(identifier);
+ git_oid oid;
+ git_object *obj = NULL;
+
+ /* try to resolve as OID */
+ if (git_oid_fromstrn(&oid, identifier, len) == 0)
+ git_object_lookup_prefix(&obj, repo, &oid, len, GIT_OBJ_ANY);
+
+ /* try to resolve as reference */
+ if (obj == NULL) {
+ git_reference *ref, *resolved;
+ if (git_reference_lookup(&ref, repo, identifier) == 0) {
+ git_reference_resolve(&resolved, ref);
+ git_reference_free(ref);
+ if (resolved) {
+ git_object_lookup(&obj, repo, git_reference_oid(resolved), GIT_OBJ_ANY);
+ git_reference_free(resolved);
+ }
+ }
+ }
+
+ if (obj == NULL)
+ return GIT_ENOTFOUND;
+
+ switch (git_object_type(obj)) {
+ case GIT_OBJ_TREE:
+ *tree = (git_tree *)obj;
+ break;
+ case GIT_OBJ_COMMIT:
+ err = git_commit_tree(tree, (git_commit *)obj);
+ git_object_free(obj);
+ break;
+ default:
+ err = GIT_ENOTFOUND;
+ }
+
+ return err;
+}
+
+char *colors[] = {
+ "\033[m", /* reset */
+ "\033[1m", /* bold */
+ "\033[31m", /* red */
+ "\033[32m", /* green */
+ "\033[36m" /* cyan */
+};
+
+int printer(
+ void *data,
+ git_diff_delta *delta,
+ git_diff_range *range,
+ char usage,
+ const char *line,
+ size_t line_len)
+{
+ int *last_color = data, color = 0;
+
+ if (*last_color >= 0) {
+ switch (usage) {
+ case GIT_DIFF_LINE_ADDITION: color = 3; break;
+ case GIT_DIFF_LINE_DELETION: color = 2; break;
+ case GIT_DIFF_LINE_ADD_EOFNL: color = 3; break;
+ case GIT_DIFF_LINE_DEL_EOFNL: color = 2; break;
+ case GIT_DIFF_LINE_FILE_HDR: color = 1; break;
+ case GIT_DIFF_LINE_HUNK_HDR: color = 4; break;
+ default: color = 0;
+ }
+ if (color != *last_color) {
+ if (*last_color == 1 || color == 1)
+ fputs(colors[0], stdout);
+ fputs(colors[color], stdout);
+ *last_color = color;
+ }
+ }
+
+ fputs(line, stdout);
+ return 0;
+}
+
+int check_uint16_param(const char *arg, const char *pattern, uint16_t *val)
+{
+ size_t len = strlen(pattern);
+ uint16_t strval;
+ char *endptr = NULL;
+ if (strncmp(arg, pattern, len))
+ return 0;
+ strval = strtoul(arg + len, &endptr, 0);
+ if (endptr == arg)
+ return 0;
+ *val = strval;
+ return 1;
+}
+
+int check_str_param(const char *arg, const char *pattern, char **val)
+{
+ size_t len = strlen(pattern);
+ if (strncmp(arg, pattern, len))
+ return 0;
+ *val = (char *)(arg + len);
+ return 1;
+}
+
+void usage(const char *message, const char *arg)
+{
+ if (message && arg)
+ fprintf(stderr, "%s: %s\n", message, arg);
+ else if (message)
+ fprintf(stderr, "%s\n", message);
+ fprintf(stderr, "usage: diff [<tree-oid> [<tree-oid>]]\n");
+ exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+ char path[GIT_PATH_MAX];
+ git_repository *repo = NULL;
+ git_tree *t1 = NULL, *t2 = NULL;
+ git_diff_options opts = {0};
+ git_diff_list *diff;
+ int i, color = -1, compact = 0, cached = 0;
+ char *a, *dir = ".", *treeish1 = NULL, *treeish2 = NULL;
+
+ /* parse arguments as copied from git-diff */
+
+ for (i = 1; i < argc; ++i) {
+ a = argv[i];
+
+ if (a[0] != '-') {
+ if (treeish1 == NULL)
+ treeish1 = a;
+ else if (treeish2 == NULL)
+ treeish2 = a;
+ else
+ usage("Only one or two tree identifiers can be provided", NULL);
+ }
+ else if (!strcmp(a, "-p") || !strcmp(a, "-u") ||
+ !strcmp(a, "--patch"))
+ compact = 0;
+ else if (!strcmp(a, "--cached"))
+ cached = 1;
+ else if (!strcmp(a, "--name-status"))
+ compact = 1;
+ else if (!strcmp(a, "--color"))
+ color = 0;
+ else if (!strcmp(a, "--no-color"))
+ color = -1;
+ else if (!strcmp(a, "-R"))
+ opts.flags |= GIT_DIFF_REVERSE;
+ else if (!strcmp(a, "-a") || !strcmp(a, "--text"))
+ opts.flags |= GIT_DIFF_FORCE_TEXT;
+ else if (!strcmp(a, "--ignore-space-at-eol"))
+ opts.flags |= GIT_DIFF_IGNORE_WHITESPACE_EOL;
+ else if (!strcmp(a, "-b") || !strcmp(a, "--ignore-space-change"))
+ opts.flags |= GIT_DIFF_IGNORE_WHITESPACE_CHANGE;
+ else if (!strcmp(a, "-w") || !strcmp(a, "--ignore-all-space"))
+ opts.flags |= GIT_DIFF_IGNORE_WHITESPACE;
+ else if (!strcmp(a, "--ignored"))
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED;
+ else if (!strcmp(a, "--untracked"))
+ opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
+ else if (!check_uint16_param(a, "-U", &opts.context_lines) &&
+ !check_uint16_param(a, "--unified=", &opts.context_lines) &&
+ !check_uint16_param(a, "--inter-hunk-context=",
+ &opts.interhunk_lines) &&
+ !check_str_param(a, "--src-prefix=", &opts.old_prefix) &&
+ !check_str_param(a, "--dst-prefix=", &opts.new_prefix))
+ usage("Unknown arg", a);
+ }
+
+ /* open repo */
+
+ check(git_repository_discover(path, sizeof(path), dir, 0, "/"),
+ "Could not discover repository");
+ check(git_repository_open(&repo, path),
+ "Could not open repository");
+
+ if (treeish1)
+ check(resolve_to_tree(repo, treeish1, &t1), "Looking up first tree");
+ if (treeish2)
+ check(resolve_to_tree(repo, treeish2, &t2), "Looking up second tree");
+
+ /* <sha1> <sha2> */
+ /* <sha1> --cached */
+ /* <sha1> */
+ /* --cached */
+ /* nothing */
+
+ if (t1 && t2)
+ check(git_diff_tree_to_tree(repo, &opts, t1, t2, &diff), "Diff");
+ else if (t1 && cached)
+ check(git_diff_index_to_tree(repo, &opts, t1, &diff), "Diff");
+ else if (t1) {
+ git_diff_list *diff2;
+ check(git_diff_index_to_tree(repo, &opts, t1, &diff), "Diff");
+ check(git_diff_workdir_to_index(repo, &opts, &diff2), "Diff");
+ check(git_diff_merge(diff, diff2), "Merge diffs");
+ git_diff_list_free(diff2);
+ }
+ else if (cached) {
+ check(resolve_to_tree(repo, "HEAD", &t1), "looking up HEAD");
+ check(git_diff_index_to_tree(repo, &opts, t1, &diff), "Diff");
+ }
+ else
+ check(git_diff_workdir_to_index(repo, &opts, &diff), "Diff");
+
+ if (color >= 0)
+ fputs(colors[0], stdout);
+
+ if (compact)
+ check(git_diff_print_compact(diff, &color, printer), "Displaying diff");
+ else
+ check(git_diff_print_patch(diff, &color, printer), "Displaying diff");
+
+ if (color >= 0)
+ fputs(colors[0], stdout);
+
+ git_diff_list_free(diff);
+ git_tree_free(t1);
+ git_tree_free(t2);
+ git_repository_free(repo);
+
+ return 0;
+}
+
diff --git a/examples/general.c b/examples/general.c
index 0a908bc48..4585a4af5 100644
--- a/examples/general.c
+++ b/examples/general.c
@@ -273,7 +273,7 @@ int main (int argc, char** argv)
// Once you have the entry object, you can access the content or subtree (or commit, in the case
// of submodules) that it points to. You can also get the mode if you want.
- git_tree_entry_2object(&objt, repo, entry); // blob
+ git_tree_entry_to_object(&objt, repo, entry); // blob
// Remember to close the looked-up object once you are done using it
git_object_free(objt);
@@ -335,7 +335,7 @@ int main (int argc, char** argv)
// We can then lookup and parse the commited pointed at by the returned OID;
// note that this operation is specially fast since the raw contents of the commit object will
// be cached in memory
- while ((git_revwalk_next(&oid, walk)) == GIT_SUCCESS) {
+ while ((git_revwalk_next(&oid, walk)) == 0) {
error = git_commit_lookup(&wcommit, repo, &oid);
cmsg = git_commit_message(wcommit);
cauth = git_commit_author(wcommit);
diff --git a/examples/network/Makefile b/examples/network/Makefile
index ed0c2099f..c21869ac9 100644
--- a/examples/network/Makefile
+++ b/examples/network/Makefile
@@ -2,7 +2,7 @@ default: all
CC = gcc
CFLAGS += -g
-CFLAGS += -I../../include -L../../ -lgit2
+CFLAGS += -I../../include -L../../ -lgit2 -lpthread
OBJECTS = \
git2.o \
diff --git a/examples/network/fetch.c b/examples/network/fetch.c
index cdd4a4662..f4a044984 100644
--- a/examples/network/fetch.c
+++ b/examples/network/fetch.c
@@ -3,95 +3,111 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <pthread.h>
-static int rename_packfile(char *packname, git_indexer *idx)
+struct dl_data {
+ git_remote *remote;
+ git_off_t *bytes;
+ git_indexer_stats *stats;
+ int ret;
+ int finished;
+};
+
+static void *download(void *ptr)
{
- char path[GIT_PATH_MAX], oid[GIT_OID_HEXSZ + 1], *slash;
- int ret;
-
- strcpy(path, packname);
- slash = strrchr(path, '/');
-
- if (!slash)
- return GIT_EINVALIDARGS;
-
- memset(oid, 0x0, sizeof(oid));
- // The name of the packfile is given by it's hash which you can get
- // with git_indexer_hash after the index has been written out to
- // disk. Rename the packfile to its "real" name in the same
- // directory as it was originally (libgit2 stores it in the folder
- // where the packs go, so a rename in place is the right thing to do here
- git_oid_fmt(oid, git_indexer_hash(idx));
- ret = sprintf(slash + 1, "pack-%s.pack", oid);
- if(ret < 0)
- return GIT_EOSERR;
-
- printf("Renaming pack to %s\n", path);
- return rename(packname, path);
+ struct dl_data *data = (struct dl_data *)ptr;
+
+ // Connect to the remote end specifying that we want to fetch
+ // information from it.
+ if (git_remote_connect(data->remote, GIT_DIR_FETCH) < 0) {
+ data->ret = -1;
+ goto exit;
+ }
+
+ // Download the packfile and index it. This function updates the
+ // amount of received data and the indexer stats which lets you
+ // inform the user about progress.
+ if (git_remote_download(data->remote, data->bytes, data->stats) < 0) {
+ data->ret = -1;
+ goto exit;
+ }
+
+ data->ret = 0;
+
+exit:
+ data->finished = 1;
+ pthread_exit(&data->ret);
+}
+
+int update_cb(const char *refname, const git_oid *a, const git_oid *b)
+{
+ const char *action;
+ char a_str[GIT_OID_HEXSZ+1], b_str[GIT_OID_HEXSZ+1];
+
+ git_oid_fmt(b_str, b);
+ b_str[GIT_OID_HEXSZ] = '\0';
+
+ if (git_oid_iszero(a)) {
+ printf("[new] %.20s %s\n", b_str, refname);
+ } else {
+ git_oid_fmt(a_str, a);
+ a_str[GIT_OID_HEXSZ] = '\0';
+ printf("[updated] %.10s..%.10s %s\n", a_str, b_str, refname);
+ }
+
+ return 0;
}
int fetch(git_repository *repo, int argc, char **argv)
{
git_remote *remote = NULL;
- git_indexer *idx = NULL;
+ git_off_t bytes = 0;
git_indexer_stats stats;
- int error;
- char *packname = NULL;
+ pthread_t worker;
+ struct dl_data data;
- // Get the remote and connect to it
+ // Figure out whether it's a named remote or a URL
printf("Fetching %s\n", argv[1]);
- error = git_remote_new(&remote, repo, argv[1], NULL);
- if (error < GIT_SUCCESS)
- return error;
-
- error = git_remote_connect(remote, GIT_DIR_FETCH);
- if (error < GIT_SUCCESS)
- return error;
-
- // Download the packfile from the server. As we don't know its hash
- // yet, it will get a temporary filename
- error = git_remote_download(&packname, remote);
- if (error < GIT_SUCCESS)
- return error;
-
- // No error and a NULL packname means no packfile was needed
- if (packname != NULL) {
- printf("The packname is %s\n", packname);
-
- // Create a new instance indexer
- error = git_indexer_new(&idx, packname);
- if (error < GIT_SUCCESS)
- return error;
-
- // This should be run in paralel, but it'd be too complicated for the example
- error = git_indexer_run(idx, &stats);
- if (error < GIT_SUCCESS)
- return error;
-
- printf("Received %d objects\n", stats.total);
-
- // Write the index file. The index will be stored with the
- // correct filename
- error = git_indexer_write(idx);
- if (error < GIT_SUCCESS)
- return error;
-
- error = rename_packfile(packname, idx);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_remote_load(&remote, repo, argv[1]) < 0) {
+ if (git_remote_new(&remote, repo, NULL, argv[1], NULL) < 0)
+ return -1;
}
+ // Set up the information for the background worker thread
+ data.remote = remote;
+ data.bytes = &bytes;
+ data.stats = &stats;
+ data.ret = 0;
+ data.finished = 0;
+ memset(&stats, 0, sizeof(stats));
+
+ pthread_create(&worker, NULL, download, &data);
+
+ // Loop while the worker thread is still running. Here we show processed
+ // and total objects in the pack and the amount of received
+ // data. Most frontends will probably want to show a percentage and
+ // the download rate.
+ do {
+ usleep(10000);
+ printf("\rReceived %d/%d objects in %d bytes", stats.processed, stats.total, bytes);
+ } while (!data.finished);
+ printf("\rReceived %d/%d objects in %d bytes\n", stats.processed, stats.total, bytes);
+
+ // Disconnect the underlying connection to prevent from idling.
+ git_remote_disconnect(remote);
+
// Update the references in the remote's namespace to point to the
// right commits. This may be needed even if there was no packfile
// to download, which can happen e.g. when the branches have been
// changed but all the neede objects are available locally.
- error = git_remote_update_tips(remote);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_remote_update_tips(remote, update_cb) < 0)
+ return -1;
- free(packname);
- git_indexer_free(idx);
git_remote_free(remote);
- return GIT_SUCCESS;
+ return 0;
+
+on_error:
+ git_remote_free(remote);
+ return -1;
}
diff --git a/examples/network/git2.c b/examples/network/git2.c
index def56e83b..7c02305c4 100644
--- a/examples/network/git2.c
+++ b/examples/network/git2.c
@@ -25,13 +25,17 @@ int run_command(git_cb fn, int argc, char **argv)
// repository and pass it to the function.
error = git_repository_open(&repo, ".git");
- if (error < GIT_SUCCESS)
+ if (error < 0)
repo = NULL;
// Run the command. If something goes wrong, print the error message to stderr
error = fn(repo, argc, argv);
- if (error < GIT_SUCCESS)
- fprintf(stderr, "Bad news:\n %s\n", git_lasterror());
+ if (error < 0) {
+ if (giterr_last() == NULL)
+ fprintf(stderr, "Error without message");
+ else
+ fprintf(stderr, "Bad news:\n %s\n", giterr_last()->message);
+ }
if(repo)
git_repository_free(repo);
diff --git a/examples/network/index-pack.c b/examples/network/index-pack.c
index 671035fb7..03f3ae37e 100644
--- a/examples/network/index-pack.c
+++ b/examples/network/index-pack.c
@@ -13,6 +13,61 @@ int index_cb(const git_indexer_stats *stats, void *data)
int index_pack(git_repository *repo, int argc, char **argv)
{
+ git_indexer_stream *idx;
+ git_indexer_stats stats = {0, 0};
+ int error, fd;
+ char hash[GIT_OID_HEXSZ + 1] = {0};
+ ssize_t read_bytes;
+ char buf[512];
+
+ if (argc < 2) {
+ fprintf(stderr, "I need a packfile\n");
+ return EXIT_FAILURE;
+ }
+
+ if (git_indexer_stream_new(&idx, ".git") < 0) {
+ puts("bad idx");
+ return -1;
+ }
+
+ if ((fd = open(argv[1], 0)) < 0) {
+ perror("open");
+ return -1;
+ }
+
+ do {
+ read_bytes = read(fd, buf, sizeof(buf));
+ if (read_bytes < 0)
+ break;
+
+ if ((error = git_indexer_stream_add(idx, buf, read_bytes, &stats)) < 0)
+ goto cleanup;
+
+ printf("\rIndexing %d of %d", stats.processed, stats.total);
+ } while (read_bytes > 0);
+
+ if (read_bytes < 0) {
+ error = -1;
+ perror("failed reading");
+ goto cleanup;
+ }
+
+ if ((error = git_indexer_stream_finalize(idx, &stats)) < 0)
+ goto cleanup;
+
+ printf("\rIndexing %d of %d\n", stats.processed, stats.total);
+
+ git_oid_fmt(hash, git_indexer_stream_hash(idx));
+ puts(hash);
+
+cleanup:
+ close(fd);
+ git_indexer_stream_free(idx);
+ return error;
+}
+
+int index_pack_old(git_repository *repo, int argc, char **argv)
+{
git_indexer *indexer;
git_indexer_stats stats;
int error;
@@ -25,13 +80,13 @@ int index_pack(git_repository *repo, int argc, char **argv)
// Create a new indexer
error = git_indexer_new(&indexer, argv[1]);
- if (error < GIT_SUCCESS)
+ if (error < 0)
return error;
// Index the packfile. This function can take a very long time and
// should be run in a worker thread.
error = git_indexer_run(indexer, &stats);
- if (error < GIT_SUCCESS)
+ if (error < 0)
return error;
// Write the information out to an index file
@@ -43,5 +98,5 @@ int index_pack(git_repository *repo, int argc, char **argv)
git_indexer_free(indexer);
- return GIT_SUCCESS;
+ return 0;
}
diff --git a/examples/network/ls-remote.c b/examples/network/ls-remote.c
index 02d432e8b..39cc64725 100644
--- a/examples/network/ls-remote.c
+++ b/examples/network/ls-remote.c
@@ -9,7 +9,7 @@ static int show_ref__cb(git_remote_head *head, void *payload)
char oid[GIT_OID_HEXSZ + 1] = {0};
git_oid_fmt(oid, &head->oid);
printf("%s\t%s\n", oid, head->name);
- return GIT_SUCCESS;
+ return 0;
}
int use_unnamed(git_repository *repo, const char *url)
@@ -19,14 +19,14 @@ int use_unnamed(git_repository *repo, const char *url)
// Create an instance of a remote from the URL. The transport to use
// is detected from the URL
- error = git_remote_new(&remote, repo, url, NULL);
- if (error < GIT_SUCCESS)
+ error = git_remote_new(&remote, repo, NULL, url, NULL);
+ if (error < 0)
goto cleanup;
// When connecting, the underlying code needs to know wether we
// want to push or fetch
error = git_remote_connect(remote, GIT_DIR_FETCH);
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
// With git_remote_ls we can retrieve the advertised heads
@@ -44,11 +44,11 @@ int use_remote(git_repository *repo, char *name)
// Find the remote by name
error = git_remote_load(&remote, repo, name);
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
error = git_remote_connect(remote, GIT_DIR_FETCH);
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
error = git_remote_ls(remote, &show_ref__cb, NULL);
diff --git a/include/git2.h b/include/git2.h
index 73b23aa63..d75387318 100644
--- a/include/git2.h
+++ b/include/git2.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -13,7 +13,6 @@
#include "git2/common.h"
#include "git2/threads.h"
#include "git2/errors.h"
-#include "git2/zlib.h"
#include "git2/types.h"
@@ -23,6 +22,7 @@
#include "git2/repository.h"
#include "git2/revwalk.h"
+#include "git2/merge.h"
#include "git2/refs.h"
#include "git2/reflog.h"
@@ -31,6 +31,7 @@
#include "git2/commit.h"
#include "git2/tag.h"
#include "git2/tree.h"
+#include "git2/diff.h"
#include "git2/index.h"
#include "git2/config.h"
@@ -40,5 +41,7 @@
#include "git2/net.h"
#include "git2/status.h"
#include "git2/indexer.h"
+#include "git2/submodule.h"
+#include "git2/notes.h"
#endif
diff --git a/include/git2/attr.h b/include/git2/attr.h
index f4c5975a6..28ca3bc1c 100644
--- a/include/git2/attr.h
+++ b/include/git2/attr.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -19,42 +19,186 @@
*/
GIT_BEGIN_DECL
-#define GIT_ATTR_TRUE git_attr__true
-#define GIT_ATTR_FALSE git_attr__false
-#define GIT_ATTR_UNSPECIFIED NULL
+/**
+ * GIT_ATTR_TRUE checks if an attribute is set on. In core git
+ * parlance, this the value for "Set" attributes.
+ *
+ * For example, if the attribute file contains:
+ *
+ * *.c foo
+ *
+ * Then for file `xyz.c` looking up attribute "foo" gives a value for
+ * which `GIT_ATTR_TRUE(value)` is true.
+ */
+#define GIT_ATTR_TRUE(attr) ((attr) == git_attr__true)
-GIT_EXTERN(const char *)git_attr__true;
-GIT_EXTERN(const char *)git_attr__false;
+/**
+ * GIT_ATTR_FALSE checks if an attribute is set off. In core git
+ * parlance, this is the value for attributes that are "Unset" (not to
+ * be confused with values that a "Unspecified").
+ *
+ * For example, if the attribute file contains:
+ *
+ * *.h -foo
+ *
+ * Then for file `zyx.h` looking up attribute "foo" gives a value for
+ * which `GIT_ATTR_FALSE(value)` is true.
+ */
+#define GIT_ATTR_FALSE(attr) ((attr) == git_attr__false)
+/**
+ * GIT_ATTR_UNSPECIFIED checks if an attribute is unspecified. This
+ * may be due to the attribute not being mentioned at all or because
+ * the attribute was explicitly set unspecified via the `!` operator.
+ *
+ * For example, if the attribute file contains:
+ *
+ * *.c foo
+ * *.h -foo
+ * onefile.c !foo
+ *
+ * Then for `onefile.c` looking up attribute "foo" yields a value with
+ * `GIT_ATTR_UNSPECIFIED(value)` of true. Also, looking up "foo" on
+ * file `onefile.rb` or looking up "bar" on any file will all give
+ * `GIT_ATTR_UNSPECIFIED(value)` of true.
+ */
+#define GIT_ATTR_UNSPECIFIED(attr) (!(attr) || (attr) == git_attr__unset)
/**
- * Lookup attribute for path returning string caller must free
+ * GIT_ATTR_HAS_VALUE checks if an attribute is set to a value (as
+ * opposied to TRUE, FALSE or UNSPECIFIED). This would be the case if
+ * for a file with something like:
+ *
+ * *.txt eol=lf
+ *
+ * Given this, looking up "eol" for `onefile.txt` will give back the
+ * string "lf" and `GIT_ATTR_SET_TO_VALUE(attr)` will return true.
+ */
+#define GIT_ATTR_HAS_VALUE(attr) \
+ ((attr) && (attr) != git_attr__unset && \
+ (attr) != git_attr__true && (attr) != git_attr__false)
+
+GIT_EXTERN(const char *) git_attr__true;
+GIT_EXTERN(const char *) git_attr__false;
+GIT_EXTERN(const char *) git_attr__unset;
+
+/**
+ * Check attribute flags: Reading values from index and working directory.
+ *
+ * When checking attributes, it is possible to check attribute files
+ * in both the working directory (if there is one) and the index (if
+ * there is one). You can explicitly choose where to check and in
+ * which order using the following flags.
+ *
+ * Core git usually checks the working directory then the index,
+ * except during a checkout when it checks the index first. It will
+ * use index only for creating archives or for a bare repo (if an
+ * index has been specified for the bare repo).
+ */
+#define GIT_ATTR_CHECK_FILE_THEN_INDEX 0
+#define GIT_ATTR_CHECK_INDEX_THEN_FILE 1
+#define GIT_ATTR_CHECK_INDEX_ONLY 2
+
+/**
+ * Check attribute flags: Using the system attributes file.
+ *
+ * Normally, attribute checks include looking in the /etc (or system
+ * equivalent) directory for a `gitattributes` file. Passing this
+ * flag will cause attribute checks to ignore that file.
+ */
+#define GIT_ATTR_CHECK_NO_SYSTEM (1 << 2)
+
+/**
+ * Look up the value of one git attribute for path.
+ *
+ * @param value_out Output of the value of the attribute. Use the GIT_ATTR_...
+ * macros to test for TRUE, FALSE, UNSPECIFIED, etc. or just
+ * use the string value for attributes set to a value. You
+ * should NOT modify or free this value.
+ * @param repo The repository containing the path.
+ * @param flags A combination of GIT_ATTR_CHECK... flags.
+ * @param path The path to check for attributes. Relative paths are
+ * interpreted relative to the repo root. The file does
+ * not have to exist, but if it does not, then it will be
+ * treated as a plain file (not a directory).
+ * @param name The name of the attribute to look up.
*/
GIT_EXTERN(int) git_attr_get(
- git_repository *repo, const char *path, const char *name,
- const char **value);
+ const char **value_out,
+ git_repository *repo,
+ uint32_t flags,
+ const char *path,
+ const char *name);
/**
- * Lookup list of attributes for path, populating array of strings
+ * Look up a list of git attributes for path.
+ *
+ * Use this if you have a known list of attributes that you want to
+ * look up in a single call. This is somewhat more efficient than
+ * calling `git_attr_get()` multiple times.
+ *
+ * For example, you might write:
+ *
+ * const char *attrs[] = { "crlf", "diff", "foo" };
+ * const char **values[3];
+ * git_attr_get_many(values, repo, 0, "my/fun/file.c", 3, attrs);
+ *
+ * Then you could loop through the 3 values to get the settings for
+ * the three attributes you asked about.
+ *
+ * @param values An array of num_attr entries that will have string
+ * pointers written into it for the values of the attributes.
+ * You should not modify or free the values that are written
+ * into this array (although of course, you should free the
+ * array itself if you allocated it).
+ * @param repo The repository containing the path.
+ * @param flags A combination of GIT_ATTR_CHECK... flags.
+ * @param path The path inside the repo to check attributes. This
+ * does not have to exist, but if it does not, then
+ * it will be treated as a plain file (i.e. not a directory).
+ * @param num_attr The number of attributes being looked up
+ * @param names An array of num_attr strings containing attribute names.
*/
GIT_EXTERN(int) git_attr_get_many(
- git_repository *repo, const char *path,
- size_t num_attr, const char **names,
- const char **values);
+ const char **values_out,
+ git_repository *repo,
+ uint32_t flags,
+ const char *path,
+ size_t num_attr,
+ const char **names);
/**
- * Perform an operation on each attribute of a path.
+ * Loop over all the git attributes for a path.
+ *
+ * @param repo The repository containing the path.
+ * @param flags A combination of GIT_ATTR_CHECK... flags.
+ * @param path The path inside the repo to check attributes. This
+ * does not have to exist, but if it does not, then
+ * it will be treated as a plain file (i.e. not a directory).
+ * @param callback The function that will be invoked on each attribute
+ * and attribute value. The name parameter will be the name
+ * of the attribute and the value will be the value it is
+ * set to, including possibly NULL if the attribute is
+ * explicitly set to UNSPECIFIED using the ! sign. This
+ * will be invoked only once per attribute name, even if
+ * there are multiple rules for a given file. The highest
+ * priority rule will be used.
+ * @param payload Passed on as extra parameter to callback function.
*/
GIT_EXTERN(int) git_attr_foreach(
- git_repository *repo, const char *path,
+ git_repository *repo,
+ uint32_t flags,
+ const char *path,
int (*callback)(const char *name, const char *value, void *payload),
void *payload);
/**
* Flush the gitattributes cache.
*
- * Call this if you have reason to believe that the attributes files
- * on disk no longer match the cached contents of memory.
+ * Call this if you have reason to believe that the attributes files on
+ * disk no longer match the cached contents of memory. This will cause
+ * the attributes files to be reloaded the next time that an attribute
+ * access function is called.
*/
GIT_EXTERN(void) git_attr_cache_flush(
git_repository *repo);
@@ -62,7 +206,7 @@ GIT_EXTERN(void) git_attr_cache_flush(
/**
* Add a macro definition.
*
- * Macros will automatically be loaded from the top level .gitattributes
+ * Macros will automatically be loaded from the top level `.gitattributes`
* file of the repository (plus the build-in "binary" macro). This
* function allows you to add others. For example, to add the default
* macro, you would call:
diff --git a/include/git2/blob.h b/include/git2/blob.h
index 8b9380d82..551770678 100644
--- a/include/git2/blob.h
+++ b/include/git2/blob.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -27,7 +27,7 @@ GIT_BEGIN_DECL
* @param blob pointer to the looked up blob
* @param repo the repo to use when locating the blob.
* @param id identity of the blob to locate.
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_INLINE(int) git_blob_lookup(git_blob **blob, git_repository *repo, const git_oid *id)
{
@@ -44,7 +44,7 @@ GIT_INLINE(int) git_blob_lookup(git_blob **blob, git_repository *repo, const git
* @param repo the repo to use when locating the blob.
* @param id identity of the blob to locate.
* @param len the length of the short identifier
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_INLINE(int) git_blob_lookup_prefix(git_blob **blob, git_repository *repo, const git_oid *id, unsigned int len)
{
@@ -99,10 +99,22 @@ GIT_EXTERN(size_t) git_blob_rawsize(git_blob *blob);
* this repository cannot be bare
* @param path file from which the blob will be created,
* relative to the repository's working dir
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *path);
+/**
+ * Read a file from the filesystem and write its content
+ * to the Object Database as a loose blob
+ *
+ * @param oid return the id of the written blob
+ * @param repo repository where the blob will be written.
+ * this repository can be bare or not
+ * @param path file from which the blob will be created
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_blob_create_fromdisk(git_oid *oid, git_repository *repo, const char *path);
+
/**
* Write an in-memory buffer to the ODB as a blob
@@ -111,7 +123,7 @@ GIT_EXTERN(int) git_blob_create_fromfile(git_oid *oid, git_repository *repo, con
* @param repo repository where to blob will be written
* @param buffer data to be written into the blob
* @param len length of the data
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *buffer, size_t len);
diff --git a/include/git2/branch.h b/include/git2/branch.h
index 5a92cf570..e2432bcfc 100644
--- a/include/git2/branch.h
+++ b/include/git2/branch.h
@@ -1,15 +1,122 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
-#ifndef INCLUDE_branch_h__
-#define INCLUDE_branch_h__
+#ifndef INCLUDE_git_branch_h__
+#define INCLUDE_git_branch_h__
-struct git_branch {
- char *remote; /* TODO: Make this a git_remote */
- char *merge;
-};
+#include "common.h"
+#include "types.h"
+/**
+ * @file git2/branch.h
+ * @brief Git branch parsing routines
+ * @defgroup git_branch Git branch management
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Create a new branch pointing at a target commit
+ *
+ * A new direct reference will be created pointing to
+ * this target commit. If `force` is true and a reference
+ * already exists with the given name, it'll be replaced.
+ *
+ * @param oid_out Pointer where to store the OID of the target commit.
+ *
+ * @param repo Repository where to store the branch.
+ *
+ * @param branch_name Name for the branch; this name is
+ * validated for consistency. It should also not conflict with
+ * an already existing branch name.
+ *
+ * @param target Object to which this branch should point. This object
+ * must belong to the given `repo` and can either be a git_commit or a
+ * git_tag. When a git_tag is being passed, it should be dereferencable
+ * to a git_commit which oid will be used as the target of the branch.
+ *
+ * @param force Overwrite existing branch.
+ *
+ * @return 0 or an error code.
+ * A proper reference is written in the refs/heads namespace
+ * pointing to the provided target commit.
+ */
+GIT_EXTERN(int) git_branch_create(
+ git_oid *oid_out,
+ git_repository *repo,
+ const char *branch_name,
+ const git_object *target,
+ int force);
+
+/**
+ * Delete an existing branch reference.
+ *
+ * @param repo Repository where lives the branch.
+ *
+ * @param branch_name Name of the branch to be deleted;
+ * this name is validated for consistency.
+ *
+ * @param branch_type Type of the considered branch. This should
+ * be valued with either GIT_BRANCH_LOCAL or GIT_BRANCH_REMOTE.
+ *
+ * @return 0 on success, GIT_ENOTFOUND if the branch
+ * doesn't exist or an error code.
+ */
+GIT_EXTERN(int) git_branch_delete(
+ git_repository *repo,
+ const char *branch_name,
+ git_branch_t branch_type);
+
+/**
+ * Fill a list with all the branches in the Repository
+ *
+ * The string array will be filled with the names of the
+ * matching branches; these values are owned by the user and
+ * should be free'd manually when no longer needed, using
+ * `git_strarray_free`.
+ *
+ * @param branch_names Pointer to a git_strarray structure
+ * where the branch names will be stored.
+ *
+ * @param repo Repository where to find the branches.
+ *
+ * @param list_flags Filtering flags for the branch
+ * listing. Valid values are GIT_BRANCH_LOCAL, GIT_BRANCH_REMOTE
+ * or a combination of the two.
+ *
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_branch_list(
+ git_strarray *branch_names,
+ git_repository *repo,
+ unsigned int list_flags);
+
+/**
+ * Move/rename an existing branch reference.
+ *
+ * @param repo Repository where lives the branch.
+ *
+ * @param old_branch_name Current name of the branch to be moved;
+ * this name is validated for consistency.
+ *
+ * @param new_branch_name Target name of the branch once the move
+ * is performed; this name is validated for consistency.
+ *
+ * @param force Overwrite existing branch.
+ *
+ * @return 0 on success, GIT_ENOTFOUND if the branch
+ * doesn't exist or an error code.
+ */
+GIT_EXTERN(int) git_branch_move(
+ git_repository *repo,
+ const char *old_branch_name,
+ const char *new_branch_name,
+ int force);
+
+/** @} */
+GIT_END_DECL
#endif
diff --git a/include/git2/commit.h b/include/git2/commit.h
index 4e91b34b9..a6d9bb0e3 100644
--- a/include/git2/commit.h
+++ b/include/git2/commit.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -28,7 +28,7 @@ GIT_BEGIN_DECL
* @param repo the repo to use when locating the commit.
* @param id identity of the commit to locate. If the object is
* an annotated tag it will be peeled back to the commit.
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_INLINE(int) git_commit_lookup(git_commit **commit, git_repository *repo, const git_oid *id)
{
@@ -46,7 +46,7 @@ GIT_INLINE(int) git_commit_lookup(git_commit **commit, git_repository *repo, con
* @param id identity of the commit to locate. If the object is
* an annotated tag it will be peeled back to the commit.
* @param len the length of the short identifier
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_INLINE(int) git_commit_lookup_prefix(git_commit **commit, git_repository *repo, const git_oid *id, unsigned len)
{
@@ -135,7 +135,7 @@ GIT_EXTERN(const git_signature *) git_commit_author(git_commit *commit);
*
* @param tree_out pointer where to store the tree object
* @param commit a previously loaded commit.
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_commit_tree(git_tree **tree_out, git_commit *commit);
@@ -163,7 +163,7 @@ GIT_EXTERN(unsigned int) git_commit_parentcount(git_commit *commit);
* @param parent Pointer where to store the parent commit
* @param commit a previously loaded commit.
* @param n the position of the parent (from 0 to `parentcount`)
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n);
@@ -182,6 +182,9 @@ GIT_EXTERN(const git_oid *) git_commit_parent_oid(git_commit *commit, unsigned i
* Create a new commit in the repository using `git_object`
* instances as parameters.
*
+ * The message will be cleaned up from excess whitespace
+ * it will be made sure that the last line ends with a '\n'.
+ *
* @param oid Pointer where to store the OID of the
* newly created commit
*
@@ -191,7 +194,8 @@ GIT_EXTERN(const git_oid *) git_commit_parent_oid(git_commit *commit, unsigned i
* will be updated to point to this commit. If the reference
* is not direct, it will be resolved to a direct reference.
* Use "HEAD" to update the HEAD of the current branch and
- * make it point to this commit
+ * make it point to this commit. If the reference doesn't
+ * exist yet, it will be created.
*
* @param author Signature representing the author and the authory
* time of this commit
@@ -217,7 +221,7 @@ GIT_EXTERN(const git_oid *) git_commit_parent_oid(git_commit *commit, unsigned i
* array may be NULL if `parent_count` is 0 (root commit). All the
* given commits must be owned by the `repo`.
*
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
* The created commit will be written to the Object Database and
* the given reference will be updated to point to it
*/
@@ -237,6 +241,9 @@ GIT_EXTERN(int) git_commit_create(
* Create a new commit in the repository using a variable
* argument list.
*
+ * The message will be cleaned up from excess whitespace
+ * it will be made sure that the last line ends with a '\n'.
+ *
* The parents for the commit are specified as a variable
* list of pointers to `const git_commit *`. Note that this
* is a convenience method which may not be safe to export
diff --git a/include/git2/common.h b/include/git2/common.h
index eee918a23..0e9379804 100644
--- a/include/git2/common.h
+++ b/include/git2/common.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -51,7 +51,7 @@
# define GIT_FORMAT_PRINTF(a,b) /* empty */
#endif
-#if (defined(_WIN32) || defined(_WIN64)) && !defined(__CYGWIN__)
+#if (defined(_WIN32)) && !defined(__CYGWIN__)
#define GIT_WIN32 1
#endif
@@ -77,7 +77,7 @@ GIT_BEGIN_DECL
#endif
/**
- * The maximum length of a git valid git path.
+ * The maximum length of a valid git path.
*/
#define GIT_PATH_MAX 4096
@@ -87,6 +87,7 @@ typedef struct {
} git_strarray;
GIT_EXTERN(void) git_strarray_free(git_strarray *array);
+GIT_EXTERN(int) git_strarray_copy(git_strarray *tgt, const git_strarray *src);
/**
* Return the version of the libgit2 library
diff --git a/include/git2/config.h b/include/git2/config.h
index ffafd7959..36946c4a5 100644
--- a/include/git2/config.h
+++ b/include/git2/config.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -29,12 +29,27 @@ struct git_config_file {
/* Open means open the file/database and parse if necessary */
int (*open)(struct git_config_file *);
int (*get)(struct git_config_file *, const char *key, const char **value);
+ int (*get_multivar)(struct git_config_file *, const char *key, const char *regexp, int (*fn)(const char *, void *), void *data);
int (*set)(struct git_config_file *, const char *key, const char *value);
+ int (*set_multivar)(git_config_file *cfg, const char *name, const char *regexp, const char *value);
int (*del)(struct git_config_file *, const char *key);
int (*foreach)(struct git_config_file *, int (*fn)(const char *, const char *, void *), void *data);
void (*free)(struct git_config_file *);
};
+typedef enum {
+ GIT_CVAR_FALSE = 0,
+ GIT_CVAR_TRUE = 1,
+ GIT_CVAR_INT32,
+ GIT_CVAR_STRING
+} git_cvar_t;
+
+typedef struct {
+ git_cvar_t cvar_type;
+ const char *str_match;
+ int map_value;
+} git_cvar_map;
+
/**
* Locate the path to the global configuration file
*
@@ -47,10 +62,10 @@ struct git_config_file {
* global configuration file.
*
* @param global_config_path Buffer of GIT_PATH_MAX length to store the path
- * @return GIT_SUCCESS if a global configuration file has been
+ * @return 0 if a global configuration file has been
* found. Its path will be stored in `buffer`.
*/
-GIT_EXTERN(int) git_config_find_global(char *global_config_path);
+GIT_EXTERN(int) git_config_find_global(char *global_config_path, size_t length);
/**
* Locate the path to the system configuration file
@@ -59,10 +74,10 @@ GIT_EXTERN(int) git_config_find_global(char *global_config_path);
* %PROGRAMFILES%\Git\etc\gitconfig.
* @param system_config_path Buffer of GIT_PATH_MAX length to store the path
- * @return GIT_SUCCESS if a system configuration file has been
+ * @return 0 if a system configuration file has been
* found. Its path will be stored in `buffer`.
*/
-GIT_EXTERN(int) git_config_find_system(char *system_config_path);
+GIT_EXTERN(int) git_config_find_system(char *system_config_path, size_t length);
/**
* Open the global configuration file
@@ -71,7 +86,7 @@ GIT_EXTERN(int) git_config_find_system(char *system_config_path);
* and opens the located file, if it exists.
*
* @param out Pointer to store the config instance
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_config_open_global(git_config **out);
@@ -95,7 +110,7 @@ GIT_EXTERN(int) git_config_file__ondisk(struct git_config_file **out, const char
* can do anything with it.
*
* @param out pointer to the new configuration
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_config_new(git_config **out);
@@ -112,7 +127,7 @@ GIT_EXTERN(int) git_config_new(git_config **out);
* @param cfg the configuration to add the file to
* @param file the configuration file (backend) to add
* @param priority the priority the backend should have
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_config_add_file(git_config *cfg, git_config_file *file, int priority);
@@ -133,7 +148,7 @@ GIT_EXTERN(int) git_config_add_file(git_config *cfg, git_config_file *file, int
* @param cfg the configuration to add the file to
* @param path path to the configuration file (backend) to add
* @param priority the priority the backend should have
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_config_add_file_ondisk(git_config *cfg, const char *path, int priority);
@@ -148,7 +163,7 @@ GIT_EXTERN(int) git_config_add_file_ondisk(git_config *cfg, const char *path, in
*
* @param cfg The configuration instance to create
* @param path Path to the on-disk file to open
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_config_open_ondisk(git_config **cfg, const char *path);
@@ -162,22 +177,22 @@ GIT_EXTERN(void) git_config_free(git_config *cfg);
/**
* Get the value of an integer config variable.
*
+ * @param out pointer to the variable where the value should be stored
* @param cfg where to look for the variable
* @param name the variable's name
- * @param out pointer to the variable where the value should be stored
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
-GIT_EXTERN(int) git_config_get_int32(git_config *cfg, const char *name, int32_t *out);
+GIT_EXTERN(int) git_config_get_int32(int32_t *out, git_config *cfg, const char *name);
/**
* Get the value of a long integer config variable.
*
+ * @param out pointer to the variable where the value should be stored
* @param cfg where to look for the variable
* @param name the variable's name
- * @param out pointer to the variable where the value should be stored
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
-GIT_EXTERN(int) git_config_get_int64(git_config *cfg, const char *name, int64_t *out);
+GIT_EXTERN(int) git_config_get_int64(int64_t *out, git_config *cfg, const char *name);
/**
* Get the value of a boolean config variable.
@@ -185,12 +200,12 @@ GIT_EXTERN(int) git_config_get_int64(git_config *cfg, const char *name, int64_t
* This function uses the usual C convention of 0 being false and
* anything else true.
*
+ * @param out pointer to the variable where the value should be stored
* @param cfg where to look for the variable
* @param name the variable's name
- * @param out pointer to the variable where the value should be stored
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
-GIT_EXTERN(int) git_config_get_bool(git_config *cfg, const char *name, int *out);
+GIT_EXTERN(int) git_config_get_bool(int *out, git_config *cfg, const char *name);
/**
* Get the value of a string config variable.
@@ -198,12 +213,26 @@ GIT_EXTERN(int) git_config_get_bool(git_config *cfg, const char *name, int *out)
* The string is owned by the variable and should not be freed by the
* user.
*
+ * @param out pointer to the variable's value
+ * @param cfg where to look for the variable
+ * @param name the variable's name
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_config_get_string(const char **out, git_config *cfg, const char *name);
+
+/**
+ * Get each value of a multivar.
+ *
+ * The callback will be called on each variable found
+ *
* @param cfg where to look for the variable
* @param name the variable's name
- * @param out pointer to the variable's value
- * @return GIT_SUCCESS or an error code
+ * @param regexp regular expression to filter which variables we're
+ * interested in. Use NULL to indicate all
+ * @param fn the function to be called on each value of the variable
+ * @param data opaque pointer to pass to the callback
*/
-GIT_EXTERN(int) git_config_get_string(git_config *cfg, const char *name, const char **out);
+GIT_EXTERN(int) git_config_get_multivar(git_config *cfg, const char *name, const char *regexp, int (*fn)(const char *, void *), void *data);
/**
* Set the value of an integer config variable.
@@ -211,7 +240,7 @@ GIT_EXTERN(int) git_config_get_string(git_config *cfg, const char *name, const c
* @param cfg where to look for the variable
* @param name the variable's name
* @param value Integer value for the variable
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_config_set_int32(git_config *cfg, const char *name, int32_t value);
@@ -221,7 +250,7 @@ GIT_EXTERN(int) git_config_set_int32(git_config *cfg, const char *name, int32_t
* @param cfg where to look for the variable
* @param name the variable's name
* @param value Long integer value for the variable
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_config_set_int64(git_config *cfg, const char *name, int64_t value);
@@ -231,7 +260,7 @@ GIT_EXTERN(int) git_config_set_int64(git_config *cfg, const char *name, int64_t
* @param cfg where to look for the variable
* @param name the variable's name
* @param value the value to store
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_config_set_bool(git_config *cfg, const char *name, int value);
@@ -244,10 +273,21 @@ GIT_EXTERN(int) git_config_set_bool(git_config *cfg, const char *name, int value
* @param cfg where to look for the variable
* @param name the variable's name
* @param value the string to store.
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_config_set_string(git_config *cfg, const char *name, const char *value);
+
+/**
+ * Set a multivar
+ *
+ * @param cfg where to look for the variable
+ * @param name the variable's name
+ * @param regexp a regular expression to indicate which values to replace
+ * @param value the new value.
+ */
+GIT_EXTERN(int) git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value);
+
/**
* Delete a config variable
*
@@ -267,13 +307,50 @@ GIT_EXTERN(int) git_config_delete(git_config *cfg, const char *name);
* @param cfg where to get the variables from
* @param callback the function to call on each variable
* @param payload the data to pass to the callback
- * @return GIT_SUCCESS or the return value of the callback which didn't return 0
+ * @return 0 or the return value of the callback which didn't return 0
*/
GIT_EXTERN(int) git_config_foreach(
git_config *cfg,
int (*callback)(const char *var_name, const char *value, void *payload),
void *payload);
+
+/**
+ * Query the value of a config variable and return it mapped to
+ * an integer constant.
+ *
+ * This is a helper method to easily map different possible values
+ * to a variable to integer constants that easily identify them.
+ *
+ * A mapping array looks as follows:
+ *
+ * git_cvar_map autocrlf_mapping[3] = {
+ * {GIT_CVAR_FALSE, NULL, GIT_AUTO_CRLF_FALSE},
+ * {GIT_CVAR_TRUE, NULL, GIT_AUTO_CRLF_TRUE},
+ * {GIT_CVAR_STRING, "input", GIT_AUTO_CRLF_INPUT},
+ * {GIT_CVAR_STRING, "default", GIT_AUTO_CRLF_DEFAULT}};
+ *
+ * On any "false" value for the variable (e.g. "false", "FALSE", "no"), the
+ * mapping will store `GIT_AUTO_CRLF_FALSE` in the `out` parameter.
+ *
+ * The same thing applies for any "true" value such as "true", "yes" or "1", storing
+ * the `GIT_AUTO_CRLF_TRUE` variable.
+ *
+ * Otherwise, if the value matches the string "input" (with case insensitive comparison),
+ * the given constant will be stored in `out`, and likewise for "default".
+ *
+ * If not a single match can be made to store in `out`, an error code will be
+ * returned.
+ *
+ * @param out place to store the result of the mapping
+ * @param cfg config file to get the variables from
+ * @param name name of the config variable to lookup
+ * @param maps array of `git_cvar_map` objects specifying the possible mappings
+ * @param map_n number of mapping objects in `maps`
+ * @return 0 on success, error code otherwise
+ */
+GIT_EXTERN(int) git_config_get_mapped(int *out, git_config *cfg, const char *name, git_cvar_map *maps, size_t map_n);
+
/** @} */
GIT_END_DECL
#endif
diff --git a/include/git2/diff.h b/include/git2/diff.h
new file mode 100644
index 000000000..bafe6268c
--- /dev/null
+++ b/include/git2/diff.h
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_diff_h__
+#define INCLUDE_git_diff_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+#include "tree.h"
+#include "refs.h"
+
+/**
+ * @file git2/diff.h
+ * @brief Git tree and file differencing routines.
+ *
+ * Calculating diffs is generally done in two phases: building a diff list
+ * then traversing the diff list. This makes is easier to share logic
+ * across the various types of diffs (tree vs tree, workdir vs index, etc.),
+ * and also allows you to insert optional diff list post-processing phases,
+ * such as rename detected, in between the steps. When you are done with a
+ * diff list object, it must be freed.
+ *
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+enum {
+ GIT_DIFF_NORMAL = 0,
+ GIT_DIFF_REVERSE = (1 << 0),
+ GIT_DIFF_FORCE_TEXT = (1 << 1),
+ GIT_DIFF_IGNORE_WHITESPACE = (1 << 2),
+ GIT_DIFF_IGNORE_WHITESPACE_CHANGE = (1 << 3),
+ GIT_DIFF_IGNORE_WHITESPACE_EOL = (1 << 4),
+ GIT_DIFF_IGNORE_SUBMODULES = (1 << 5),
+ GIT_DIFF_PATIENCE = (1 << 6),
+ GIT_DIFF_INCLUDE_IGNORED = (1 << 7),
+ GIT_DIFF_INCLUDE_UNTRACKED = (1 << 8),
+ GIT_DIFF_INCLUDE_UNMODIFIED = (1 << 9),
+ GIT_DIFF_RECURSE_UNTRACKED_DIRS = (1 << 10),
+};
+
+/**
+ * Structure describing options about how the diff should be executed.
+ *
+ * Setting all values of the structure to zero will yield the default
+ * values. Similarly, passing NULL for the options structure will
+ * give the defaults. The default values are marked below.
+ *
+ * @todo Most of the parameters here are not actually supported at this time.
+ */
+typedef struct {
+ uint32_t flags; /**< defaults to GIT_DIFF_NORMAL */
+ uint16_t context_lines; /**< defaults to 3 */
+ uint16_t interhunk_lines; /**< defaults to 3 */
+ char *old_prefix; /**< defaults to "a" */
+ char *new_prefix; /**< defaults to "b" */
+ git_strarray pathspec; /**< defaults to show all paths */
+} git_diff_options;
+
+/**
+ * The diff list object that contains all individual file deltas.
+ */
+typedef struct git_diff_list git_diff_list;
+
+enum {
+ GIT_DIFF_FILE_VALID_OID = (1 << 0),
+ GIT_DIFF_FILE_FREE_PATH = (1 << 1),
+ GIT_DIFF_FILE_BINARY = (1 << 2),
+ GIT_DIFF_FILE_NOT_BINARY = (1 << 3),
+ GIT_DIFF_FILE_FREE_DATA = (1 << 4),
+ GIT_DIFF_FILE_UNMAP_DATA = (1 << 5)
+};
+
+/**
+ * What type of change is described by a git_diff_delta?
+ */
+typedef enum {
+ GIT_DELTA_UNMODIFIED = 0,
+ GIT_DELTA_ADDED = 1,
+ GIT_DELTA_DELETED = 2,
+ GIT_DELTA_MODIFIED = 3,
+ GIT_DELTA_RENAMED = 4,
+ GIT_DELTA_COPIED = 5,
+ GIT_DELTA_IGNORED = 6,
+ GIT_DELTA_UNTRACKED = 7
+} git_delta_t;
+
+/**
+ * Description of one side of a diff.
+ */
+typedef struct {
+ git_oid oid;
+ char *path;
+ uint16_t mode;
+ git_off_t size;
+ unsigned int flags;
+} git_diff_file;
+
+/**
+ * Description of changes to one file.
+ *
+ * When iterating over a diff list object, this will generally be passed to
+ * most callback functions and you can use the contents to understand
+ * exactly what has changed.
+ *
+ * Under some circumstances, not all fields will be filled in, but the code
+ * generally tries to fill in as much as possible. One example is that the
+ * "binary" field will not actually look at file contents if you do not
+ * pass in hunk and/or line callbacks to the diff foreach iteration function.
+ * It will just use the git attributes for those files.
+ */
+typedef struct {
+ git_diff_file old_file;
+ git_diff_file new_file;
+ git_delta_t status;
+ unsigned int similarity; /**< for RENAMED and COPIED, value 0-100 */
+ int binary;
+} git_diff_delta;
+
+/**
+ * When iterating over a diff, callback that will be made per file.
+ */
+typedef int (*git_diff_file_fn)(
+ void *cb_data,
+ git_diff_delta *delta,
+ float progress);
+
+/**
+ * Structure describing a hunk of a diff.
+ */
+typedef struct {
+ int old_start;
+ int old_lines;
+ int new_start;
+ int new_lines;
+} git_diff_range;
+
+/**
+ * When iterating over a diff, callback that will be made per hunk.
+ */
+typedef int (*git_diff_hunk_fn)(
+ void *cb_data,
+ git_diff_delta *delta,
+ git_diff_range *range,
+ const char *header,
+ size_t header_len);
+
+/**
+ * Line origin constants.
+ *
+ * These values describe where a line came from and will be passed to
+ * the git_diff_data_fn when iterating over a diff. There are some
+ * special origin contants at the end that are used for the text
+ * output callbacks to demarcate lines that are actually part of
+ * the file or hunk headers.
+ */
+enum {
+ /* these values will be sent to `git_diff_data_fn` along with the line */
+ GIT_DIFF_LINE_CONTEXT = ' ',
+ GIT_DIFF_LINE_ADDITION = '+',
+ GIT_DIFF_LINE_DELETION = '-',
+ GIT_DIFF_LINE_ADD_EOFNL = '\n', /**< LF was added at end of file */
+ GIT_DIFF_LINE_DEL_EOFNL = '\0', /**< LF was removed at end of file */
+ /* these values will only be sent to a `git_diff_data_fn` when the content
+ * of a diff is being formatted (eg. through git_diff_print_patch() or
+ * git_diff_print_compact(), for instance).
+ */
+ GIT_DIFF_LINE_FILE_HDR = 'F',
+ GIT_DIFF_LINE_HUNK_HDR = 'H',
+ GIT_DIFF_LINE_BINARY = 'B'
+};
+
+/**
+ * When iterating over a diff, callback that will be made per text diff
+ * line. In this context, the provided range will be NULL.
+ *
+ * When printing a diff, callback that will be made to output each line
+ * of text. This uses some extra GIT_DIFF_LINE_... constants for output
+ * of lines of file and hunk headers.
+ */
+typedef int (*git_diff_data_fn)(
+ void *cb_data,
+ git_diff_delta *delta,
+ git_diff_range *range,
+ char line_origin, /**< GIT_DIFF_LINE_... value from above */
+ const char *content,
+ size_t content_len);
+
+/** @name Diff List Generator Functions
+ *
+ * These are the functions you would use to create (or destroy) a
+ * git_diff_list from various objects in a repository.
+ */
+/**@{*/
+
+/**
+ * Deallocate a diff list.
+ */
+GIT_EXTERN(void) git_diff_list_free(git_diff_list *diff);
+
+/**
+ * Compute a difference between two tree objects.
+ *
+ * @param repo The repository containing the trees.
+ * @param opts Structure with options to influence diff or NULL for defaults.
+ * @param old_tree A git_tree object to diff from.
+ * @param new_tree A git_tree object to diff to.
+ * @param diff A pointer to a git_diff_list pointer that will be allocated.
+ */
+GIT_EXTERN(int) git_diff_tree_to_tree(
+ git_repository *repo,
+ const git_diff_options *opts, /**< can be NULL for defaults */
+ git_tree *old_tree,
+ git_tree *new_tree,
+ git_diff_list **diff);
+
+/**
+ * Compute a difference between a tree and the index.
+ *
+ * @param repo The repository containing the tree and index.
+ * @param opts Structure with options to influence diff or NULL for defaults.
+ * @param old_tree A git_tree object to diff from.
+ * @param diff A pointer to a git_diff_list pointer that will be allocated.
+ */
+GIT_EXTERN(int) git_diff_index_to_tree(
+ git_repository *repo,
+ const git_diff_options *opts, /**< can be NULL for defaults */
+ git_tree *old_tree,
+ git_diff_list **diff);
+
+/**
+ * Compute a difference between the working directory and the index.
+ *
+ * @param repo The repository.
+ * @param opts Structure with options to influence diff or NULL for defaults.
+ * @param diff A pointer to a git_diff_list pointer that will be allocated.
+ */
+GIT_EXTERN(int) git_diff_workdir_to_index(
+ git_repository *repo,
+ const git_diff_options *opts, /**< can be NULL for defaults */
+ git_diff_list **diff);
+
+/**
+ * Compute a difference between the working directory and a tree.
+ *
+ * This returns strictly the differences between the tree and the
+ * files contained in the working directory, regardless of the state
+ * of files in the index. There is no direct equivalent in C git.
+ *
+ * This is *NOT* the same as 'git diff HEAD' or 'git diff <SHA>'. Those
+ * commands diff the tree, the index, and the workdir. To emulate those
+ * functions, call `git_diff_index_to_tree` and `git_diff_workdir_to_index`,
+ * then call `git_diff_merge` on the results.
+ *
+ * @param repo The repository containing the tree.
+ * @param opts Structure with options to influence diff or NULL for defaults.
+ * @param old_tree A git_tree object to diff from.
+ * @param diff A pointer to a git_diff_list pointer that will be allocated.
+ */
+GIT_EXTERN(int) git_diff_workdir_to_tree(
+ git_repository *repo,
+ const git_diff_options *opts, /**< can be NULL for defaults */
+ git_tree *old_tree,
+ git_diff_list **diff);
+
+/**
+ * Merge one diff list into another.
+ *
+ * This merges items from the "from" list into the "onto" list. The
+ * resulting diff list will have all items that appear in either list.
+ * If an item appears in both lists, then it will be "merged" to appear
+ * as if the old version was from the "onto" list and the new version
+ * is from the "from" list (with the exception that if the item has a
+ * pending DELETE in the middle, then it will show as deleted).
+ *
+ * @param onto Diff to merge into.
+ * @param from Diff to merge.
+ */
+GIT_EXTERN(int) git_diff_merge(
+ git_diff_list *onto,
+ const git_diff_list *from);
+
+/**@}*/
+
+
+/** @name Diff List Processor Functions
+ *
+ * These are the functions you apply to a diff list to process it
+ * or read it in some way.
+ */
+/**@{*/
+
+/**
+ * Iterate over a diff list issuing callbacks.
+ *
+ * If the hunk and/or line callbacks are not NULL, then this will calculate
+ * text diffs for all files it thinks are not binary. If those are both
+ * NULL, then this will not bother with the text diffs, so it can be
+ * efficient.
+ */
+GIT_EXTERN(int) git_diff_foreach(
+ git_diff_list *diff,
+ void *cb_data,
+ git_diff_file_fn file_cb,
+ git_diff_hunk_fn hunk_cb,
+ git_diff_data_fn line_cb);
+
+/**
+ * Iterate over a diff generating text output like "git diff --name-status".
+ */
+GIT_EXTERN(int) git_diff_print_compact(
+ git_diff_list *diff,
+ void *cb_data,
+ git_diff_data_fn print_cb);
+
+/**
+ * Iterate over a diff generating text output like "git diff".
+ *
+ * This is a super easy way to generate a patch from a diff.
+ */
+GIT_EXTERN(int) git_diff_print_patch(
+ git_diff_list *diff,
+ void *cb_data,
+ git_diff_data_fn print_cb);
+
+/**@}*/
+
+
+/*
+ * Misc
+ */
+
+/**
+ * Directly run a text diff on two blobs.
+ *
+ * Compared to a file, a blob lacks some contextual information. As such, the
+ * `git_diff_file` parameters of the callbacks will be filled accordingly to the following:
+ * `mode` will be set to 0, `path` will be set to NULL. When dealing with a NULL blob, `oid`
+ * will be set to 0.
+ *
+ * When at least one of the blobs being dealt with is binary, the `git_diff_delta` binary
+ * attribute will be set to 1 and no call to the hunk_cb nor line_cb will be made.
+ */
+GIT_EXTERN(int) git_diff_blobs(
+ git_blob *old_blob,
+ git_blob *new_blob,
+ git_diff_options *options,
+ void *cb_data,
+ git_diff_file_fn file_cb,
+ git_diff_hunk_fn hunk_cb,
+ git_diff_data_fn line_cb);
+
+GIT_END_DECL
+
+/** @} */
+
+#endif
diff --git a/include/git2/errors.h b/include/git2/errors.h
index 5ac0d5b27..fb6670004 100644
--- a/include/git2/errors.h
+++ b/include/git2/errors.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -17,127 +17,91 @@
*/
GIT_BEGIN_DECL
-typedef enum {
+#ifdef GIT_OLD_ERRORS
+enum {
GIT_SUCCESS = 0,
- GIT_ERROR = -1,
-
- /** Input was not a properly formatted Git object id. */
GIT_ENOTOID = -2,
-
- /** Input does not exist in the scope searched. */
GIT_ENOTFOUND = -3,
-
- /** Not enough space available. */
GIT_ENOMEM = -4,
-
- /** Consult the OS error information. */
GIT_EOSERR = -5,
-
- /** The specified object is of invalid type */
GIT_EOBJTYPE = -6,
-
- /** The specified repository is invalid */
GIT_ENOTAREPO = -7,
-
- /** The object type is invalid or doesn't match */
GIT_EINVALIDTYPE = -8,
-
- /** The object cannot be written because it's missing internal data */
GIT_EMISSINGOBJDATA = -9,
-
- /** The packfile for the ODB is corrupted */
GIT_EPACKCORRUPTED = -10,
-
- /** Failed to acquire or release a file lock */
GIT_EFLOCKFAIL = -11,
-
- /** The Z library failed to inflate/deflate an object's data */
GIT_EZLIB = -12,
-
- /** The queried object is currently busy */
GIT_EBUSY = -13,
-
- /** The index file is not backed up by an existing repository */
GIT_EBAREINDEX = -14,
-
- /** The name of the reference is not valid */
GIT_EINVALIDREFNAME = -15,
-
- /** The specified reference has its data corrupted */
GIT_EREFCORRUPTED = -16,
-
- /** The specified symbolic reference is too deeply nested */
GIT_ETOONESTEDSYMREF = -17,
-
- /** The pack-refs file is either corrupted or its format is not currently supported */
GIT_EPACKEDREFSCORRUPTED = -18,
-
- /** The path is invalid */
GIT_EINVALIDPATH = -19,
-
- /** The revision walker is empty; there are no more commits left to iterate */
GIT_EREVWALKOVER = -20,
-
- /** The state of the reference is not valid */
GIT_EINVALIDREFSTATE = -21,
-
- /** This feature has not been implemented yet */
GIT_ENOTIMPLEMENTED = -22,
-
- /** A reference with this name already exists */
GIT_EEXISTS = -23,
-
- /** The given integer literal is too large to be parsed */
GIT_EOVERFLOW = -24,
-
- /** The given literal is not a valid number */
GIT_ENOTNUM = -25,
-
- /** Streaming error */
GIT_ESTREAM = -26,
-
- /** invalid arguments to function */
GIT_EINVALIDARGS = -27,
-
- /** The specified object has its data corrupted */
GIT_EOBJCORRUPTED = -28,
-
- /** The given short oid is ambiguous */
- GIT_EAMBIGUOUSOIDPREFIX = -29,
-
- /** Skip and passthrough the given ODB backend */
+ GIT_EAMBIGUOUS = -29,
GIT_EPASSTHROUGH = -30,
-
- /** The path pattern and string did not match */
GIT_ENOMATCH = -31,
-
- /** The buffer is too short to satisfy the request */
GIT_ESHORTBUFFER = -32,
+};
+#endif
+
+/** Generic return codes */
+enum {
+ GIT_OK = 0,
+ GIT_ERROR = -1,
+ GIT_ENOTFOUND = -3,
+ GIT_EEXISTS = -4,
+ GIT_EAMBIGUOUS = -5,
+ GIT_EBUFS = -6,
+
+ GIT_PASSTHROUGH = -30,
+ GIT_REVWALKOVER = -31,
+};
+
+typedef struct {
+ char *message;
+ int klass;
} git_error;
-/**
- * Return a detailed error string with the latest error
- * that occurred in the library.
- * @return a string explaining the error
- */
-GIT_EXTERN(const char *) git_lasterror(void);
+typedef enum {
+ GITERR_NOMEMORY,
+ GITERR_OS,
+ GITERR_INVALID,
+ GITERR_REFERENCE,
+ GITERR_ZLIB,
+ GITERR_REPOSITORY,
+ GITERR_CONFIG,
+ GITERR_REGEX,
+ GITERR_ODB,
+ GITERR_INDEX,
+ GITERR_OBJECT,
+ GITERR_NET,
+ GITERR_TAG,
+ GITERR_TREE,
+ GITERR_INDEXER,
+} git_error_t;
/**
- * strerror() for the Git library
- *
- * Get a string description for a given error code.
- * NOTE: This method will be eventually deprecated in favor
- * of the new `git_lasterror`.
+ * Return the last `git_error` object that was generated for the
+ * current thread or NULL if no error has occurred.
*
- * @param num The error code to explain
- * @return a string explaining the error code
+ * @return A git_error object.
*/
-GIT_EXTERN(const char *) git_strerror(int num);
+GIT_EXTERN(const git_error *) giterr_last(void);
/**
- * Clear the latest library error
+ * Clear the last library error that occurred for this thread.
*/
-GIT_EXTERN(void) git_clearerror(void);
+GIT_EXTERN(void) giterr_clear(void);
/** @} */
GIT_END_DECL
diff --git a/include/git2/index.h b/include/git2/index.h
index 5018c896b..6a42c8515 100644
--- a/include/git2/index.h
+++ b/include/git2/index.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -106,7 +106,7 @@ typedef struct git_index_entry_unmerged {
*
* @param index the pointer for the new index
* @param index_path the path to the index file in disk
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_index_open(git_index **index, const char *index_path);
@@ -131,7 +131,7 @@ GIT_EXTERN(void) git_index_free(git_index *index);
* by reading from the hard disk.
*
* @param index an existing index object
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_index_read(git_index *index);
@@ -140,7 +140,7 @@ GIT_EXTERN(int) git_index_read(git_index *index);
* using an atomic file lock.
*
* @param index an existing index object
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_index_write(git_index *index);
@@ -176,7 +176,7 @@ GIT_EXTERN(void) git_index_uniq(git_index *index);
* @param index an existing index object
* @param path filename to add
* @param stage stage for the entry
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_index_add(git_index *index, const char *path, int stage);
@@ -188,7 +188,7 @@ GIT_EXTERN(int) git_index_add(git_index *index, const char *path, int stage);
*
* @param index an existing index object
* @param source_entry new entry object
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_index_add2(git_index *index, const git_index_entry *source_entry);
@@ -207,7 +207,7 @@ GIT_EXTERN(int) git_index_add2(git_index *index, const git_index_entry *source_e
* @param index an existing index object
* @param path filename to add
* @param stage stage for the entry
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_index_append(git_index *index, const char *path, int stage);
@@ -224,7 +224,7 @@ GIT_EXTERN(int) git_index_append(git_index *index, const char *path, int stage);
*
* @param index an existing index object
* @param source_entry new entry object
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_index_append2(git_index *index, const git_index_entry *source_entry);
@@ -233,7 +233,7 @@ GIT_EXTERN(int) git_index_append2(git_index *index, const git_index_entry *sourc
*
* @param index an existing index object
* @param position position of the entry to remove
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_index_remove(git_index *index, int position);
@@ -312,7 +312,7 @@ GIT_EXTERN(int) git_index_entry_stage(const git_index_entry *entry);
*
* @param index an existing index object
* @param tree tree to read
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_index_read_tree(git_index *index, git_tree *tree);
diff --git a/include/git2/indexer.h b/include/git2/indexer.h
index 1e5eb380c..14bd0e402 100644
--- a/include/git2/indexer.h
+++ b/include/git2/indexer.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -23,6 +23,51 @@ typedef struct git_indexer_stats {
typedef struct git_indexer git_indexer;
+typedef struct git_indexer_stream git_indexer_stream;
+
+/**
+ * Create a new streaming indexer instance
+ *
+ * @param out where to store the inexer instance
+ * @param path to the gitdir (metadata directory)
+ */
+GIT_EXTERN(int) git_indexer_stream_new(git_indexer_stream **out, const char *gitdir);
+
+/**
+ * Add data to the indexer
+ *
+ * @param idx the indexer
+ * @param data the data to add
+ * @param size the size of the data
+ * @param stats stat storage
+ */
+GIT_EXTERN(int) git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t size, git_indexer_stats *stats);
+
+/**
+ * Finalize the pack and index
+ *
+ * Resolve any pending deltas and write out the index file
+ *
+ * @param idx the indexer
+ */
+GIT_EXTERN(int) git_indexer_stream_finalize(git_indexer_stream *idx, git_indexer_stats *stats);
+
+/**
+ * Get the packfile's hash
+ *
+ * A packfile's name is derived from the sorted hashing of all object
+ * names. This is only correct after the index has been finalized.
+ *
+ * @param idx the indexer instance
+ */
+GIT_EXTERN(const git_oid *) git_indexer_stream_hash(git_indexer_stream *idx);
+
+/**
+ * Free the indexer and its resources
+ *
+ * @param idx the indexer to free
+ */
+GIT_EXTERN(void) git_indexer_stream_free(git_indexer_stream *idx);
/**
* Create a new indexer instance
diff --git a/include/git2/merge.h b/include/git2/merge.h
new file mode 100644
index 000000000..5a0b2e7f2
--- /dev/null
+++ b/include/git2/merge.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_merge_h__
+#define INCLUDE_git_merge_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+
+/**
+ * @file git2/merge.h
+ * @brief Git merge-base routines
+ * @defgroup git_revwalk Git merge-base routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Find a merge base between two commits
+ *
+ * @param out the OID of a merge base between 'one' and 'two'
+ * @param repo the repository where the commits exist
+ * @param one one of the commits
+ * @param two the other commit
+ */
+GIT_EXTERN(int) git_merge_base(git_oid *out, git_repository *repo, git_oid *one, git_oid *two);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/net.h b/include/git2/net.h
index 08bc81f16..c2301b6f1 100644
--- a/include/git2/net.h
+++ b/include/git2/net.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
diff --git a/include/git2/notes.h b/include/git2/notes.h
new file mode 100644
index 000000000..19073abd1
--- /dev/null
+++ b/include/git2/notes.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_note_h__
+#define INCLUDE_git_note_h__
+
+#include "oid.h"
+
+/**
+ * @file git2/notes.h
+ * @brief Git notes management routines
+ * @defgroup git_note Git notes management routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Read the note for an object
+ *
+ * The note must be freed manually by the user.
+ *
+ * @param note the note; NULL in case of error
+ * @param repo the Git repository
+ * @param notes_ref OID reference to use (optional); defaults to "refs/notes/commits"
+ * @param oid OID of the object
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_note_read(git_note **note, git_repository *repo,
+ const char *notes_ref, const git_oid *oid);
+
+/**
+ * Get the note message
+ *
+ * @param note
+ * @return the note message
+ */
+GIT_EXTERN(const char *) git_note_message(git_note *note);
+
+
+/**
+ * Get the note object OID
+ *
+ * @param note
+ * @return the note object OID
+ */
+GIT_EXTERN(const git_oid *) git_note_oid(git_note *note);
+
+
+/**
+ * Add a note for an object
+ *
+ * @param oid pointer to store the OID (optional); NULL in case of error
+ * @param repo the Git repository
+ * @param author signature of the notes commit author
+ * @param committer signature of the notes commit committer
+ * @param notes_ref OID reference to update (optional); defaults to "refs/notes/commits"
+ * @param oid The OID of the object
+ * @param oid The note to add for object oid
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_note_create(git_oid *out, git_repository *repo,
+ git_signature *author, git_signature *committer,
+ const char *notes_ref, const git_oid *oid,
+ const char *note);
+
+
+/**
+ * Remove the note for an object
+ *
+ * @param repo the Git repository
+ * @param notes_ref OID reference to use (optional); defaults to "refs/notes/commits"
+ * @param author signature of the notes commit author
+ * @param committer signature of the notes commit committer
+ * @param oid the oid which note's to be removed
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_note_remove(git_repository *repo, const char *notes_ref,
+ git_signature *author, git_signature *committer,
+ const git_oid *oid);
+
+/**
+ * Free a git_note object
+ *
+ * @param note git_note object
+ */
+GIT_EXTERN(void) git_note_free(git_note *note);
+
+/**
+ * Get the default notes reference for a repository
+ *
+ * @param out Pointer to the default notes reference
+ * @param repo The Git repository
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_note_default_ref(const char **out, git_repository *repo);
+
+/**
+ * Basic components of a note
+ *
+ * - Oid of the blob containing the message
+ * - Oid of the git object being annotated
+ */
+typedef struct {
+ git_oid blob_oid;
+ git_oid annotated_object_oid;
+} git_note_data;
+
+/**
+ * Loop over all the notes within a specified namespace
+ * and issue a callback for each one.
+ *
+ * @param repo Repository where to find the notes.
+ *
+ * @param notes_ref OID reference to read from (optional); defaults to "refs/notes/commits".
+ *
+ * @param note_cb Callback to invoke per found annotation.
+ *
+ * @param payload Extra parameter to callback function.
+ *
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_note_foreach(
+ git_repository *repo,
+ const char *notes_ref,
+ int (*note_cb)(git_note_data *note_data, void *payload),
+ void *payload
+);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/object.h b/include/git2/object.h
index 86a0a585d..9e988b7b6 100644
--- a/include/git2/object.h
+++ b/include/git2/object.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -69,7 +69,7 @@ GIT_EXTERN(int) git_object_lookup(
* @param id a short identifier for the object
* @param len the length of the short identifier
* @param type the type of the object
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_object_lookup_prefix(
git_object **object_out,
diff --git a/include/git2/odb.h b/include/git2/odb.h
index b144eca7d..1df193389 100644
--- a/include/git2/odb.h
+++ b/include/git2/odb.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -29,7 +29,7 @@ GIT_BEGIN_DECL
*
* @param out location to store the database pointer, if opened.
* Set to NULL if the open failed.
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_odb_new(git_odb **out);
@@ -47,7 +47,7 @@ GIT_EXTERN(int) git_odb_new(git_odb **out);
* @param out location to store the database pointer, if opened.
* Set to NULL if the open failed.
* @param objects_dir path of the backends' "objects" directory.
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_odb_open(git_odb **out, const char *objects_dir);
@@ -108,7 +108,7 @@ GIT_EXTERN(void) git_odb_free(git_odb *db);
* @param db database to search for the object in.
* @param id identity of the object to read.
* @return
- * - GIT_SUCCESS if the object was read;
+ * - 0 if the object was read;
* - GIT_ENOTFOUND if the object is not in the database.
*/
GIT_EXTERN(int) git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id);
@@ -135,7 +135,7 @@ GIT_EXTERN(int) git_odb_read(git_odb_object **out, git_odb *db, const git_oid *i
* @param db database to search for the object in.
* @param short_id a prefix of the id of the object to read.
* @param len the length of the prefix
- * @return GIT_SUCCESS if the object was read;
+ * @return 0 if the object was read;
* GIT_ENOTFOUND if the object is not in the database.
* GIT_EAMBIGUOUS if the prefix is ambiguous (several objects match the prefix)
*/
@@ -156,7 +156,7 @@ GIT_EXTERN(int) git_odb_read_prefix(git_odb_object **out, git_odb *db, const git
* @param db database to search for the object in.
* @param id identity of the object to read.
* @return
- * - GIT_SUCCESS if the object was read;
+ * - 0 if the object was read;
* - GIT_ENOTFOUND if the object is not in the database.
*/
GIT_EXTERN(int) git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id);
@@ -188,7 +188,7 @@ GIT_EXTERN(int) git_odb_exists(git_odb *db, const git_oid *id);
* @param data buffer with the data to storr
* @param len size of the buffer
* @param type type of the data to store
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_odb_write(git_oid *oid, git_odb *odb, const void *data, size_t len, git_otype type);
@@ -257,7 +257,7 @@ GIT_EXTERN(int) git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const
* @param data data to hash
* @param len size of the data
* @param type of the data to hash
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_odb_hash(git_oid *id, const void *data, size_t len, git_otype type);
@@ -270,7 +270,7 @@ GIT_EXTERN(int) git_odb_hash(git_oid *id, const void *data, size_t len, git_otyp
* @param out oid structure the result is written into.
* @param path file to read and determine object id for
* @param type the type of the object that will be hashed
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_odb_hashfile(git_oid *out, const char *path, git_otype type);
diff --git a/include/git2/odb_backend.h b/include/git2/odb_backend.h
index eb8830fb3..f4620f5f4 100644
--- a/include/git2/odb_backend.h
+++ b/include/git2/odb_backend.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -74,6 +74,13 @@ struct git_odb_backend {
void (* free)(struct git_odb_backend *);
};
+/** Streaming mode */
+enum {
+ GIT_STREAM_RDONLY = (1 << 1),
+ GIT_STREAM_WRONLY = (1 << 2),
+ GIT_STREAM_RW = (GIT_STREAM_RDONLY | GIT_STREAM_WRONLY),
+};
+
/** A stream to read/write from a backend */
struct git_odb_stream {
struct git_odb_backend *backend;
@@ -85,13 +92,6 @@ struct git_odb_stream {
void (*free)(struct git_odb_stream *stream);
};
-/** Streaming mode */
-typedef enum {
- GIT_STREAM_RDONLY = (1 << 1),
- GIT_STREAM_WRONLY = (1 << 2),
- GIT_STREAM_RW = (GIT_STREAM_RDONLY | GIT_STREAM_WRONLY),
-} git_odb_streammode;
-
GIT_EXTERN(int) git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir);
GIT_EXTERN(int) git_odb_backend_loose(git_odb_backend **backend_out, const char *objects_dir, int compression_level, int do_fsync);
diff --git a/include/git2/oid.h b/include/git2/oid.h
index 9cebda931..c06458d24 100644
--- a/include/git2/oid.h
+++ b/include/git2/oid.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -43,7 +43,7 @@ struct _git_oid {
* @param str input hex string; must be pointing at the start of
* the hex sequence and have at least the number of bytes
* needed for an oid encoded in hex (40 bytes).
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_oid_fromstr(git_oid *out, const char *str);
@@ -56,7 +56,7 @@ GIT_EXTERN(int) git_oid_fromstr(git_oid *out, const char *str);
* @param out oid structure the result is written into.
* @param str input hex string of at least size `length`
* @param length length of the input string
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_oid_fromstrn(git_oid *out, const char *str, size_t length);
@@ -119,7 +119,7 @@ GIT_EXTERN(char *) git_oid_allocfmt(const git_oid *oid);
* @return the out buffer pointer, assuming no input parameter
* errors, otherwise a pointer to an empty string.
*/
-GIT_EXTERN(char *) git_oid_to_string(char *out, size_t n, const git_oid *oid);
+GIT_EXTERN(char *) git_oid_tostr(char *out, size_t n, const git_oid *oid);
/**
* Copy an oid from one structure to another.
@@ -155,11 +155,16 @@ GIT_EXTERN(int) git_oid_ncmp(const git_oid *a, const git_oid *b, unsigned int le
* @param a oid structure.
* @param str input hex string of an object id.
* @return GIT_ENOTOID if str is not a valid hex string,
- * GIT_SUCCESS in case of a match, GIT_ERROR otherwise.
+ * 0 in case of a match, GIT_ERROR otherwise.
*/
GIT_EXTERN(int) git_oid_streq(const git_oid *a, const char *str);
/**
+ * Check is an oid is all zeros.
+ */
+GIT_EXTERN(int) git_oid_iszero(const git_oid *a);
+
+/**
* OID Shortener object
*/
typedef struct git_oid_shorten git_oid_shorten;
@@ -178,7 +183,7 @@ typedef struct git_oid_shorten git_oid_shorten;
* be unique.
* @return a `git_oid_shorten` instance, NULL if OOM
*/
-git_oid_shorten *git_oid_shorten_new(size_t min_length);
+GIT_EXTERN(git_oid_shorten *) git_oid_shorten_new(size_t min_length);
/**
* Add a new OID to set of shortened OIDs and calculate
@@ -204,14 +209,14 @@ git_oid_shorten *git_oid_shorten_new(size_t min_length);
* added so far to the set; or an error code (<0) if an
* error occurs.
*/
-int git_oid_shorten_add(git_oid_shorten *os, const char *text_oid);
+GIT_EXTERN(int) git_oid_shorten_add(git_oid_shorten *os, const char *text_oid);
/**
* Free an OID shortener instance
*
* @param os a `git_oid_shorten` instance
*/
-void git_oid_shorten_free(git_oid_shorten *os);
+GIT_EXTERN(void) git_oid_shorten_free(git_oid_shorten *os);
/** @} */
GIT_END_DECL
diff --git a/include/git2/reflog.h b/include/git2/reflog.h
index f1d08795e..f490e29de 100644
--- a/include/git2/reflog.h
+++ b/include/git2/reflog.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -28,7 +28,7 @@ GIT_BEGIN_DECL
*
* @param reflog pointer to reflog
* @param ref reference to read the reflog for
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_reflog_read(git_reflog **reflog, git_reference *ref);
@@ -46,7 +46,7 @@ GIT_EXTERN(int) git_reflog_read(git_reflog **reflog, git_reference *ref);
* @param oid_old the OID the reference was pointing to
* @param committer the signature of the committer
* @param msg the reflog message
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_reflog_write(git_reference *ref, const git_oid *oid_old, const git_signature *committer, const char *msg);
@@ -55,7 +55,7 @@ GIT_EXTERN(int) git_reflog_write(git_reference *ref, const git_oid *oid_old, con
*
* @param ref the reference
* @param new_name the new name of the reference
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_reflog_rename(git_reference *ref, const char *new_name);
@@ -63,7 +63,7 @@ GIT_EXTERN(int) git_reflog_rename(git_reference *ref, const char *new_name);
* Delete the reflog for the given reference
*
* @param ref the reference
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_reflog_delete(git_reference *ref);
diff --git a/include/git2/refs.h b/include/git2/refs.h
index 32671aa66..882e32769 100644
--- a/include/git2/refs.h
+++ b/include/git2/refs.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -28,11 +28,22 @@ GIT_BEGIN_DECL
* @param reference_out pointer to the looked-up reference
* @param repo the repository to look up the reference
* @param name the long name for the reference (e.g. HEAD, ref/heads/master, refs/tags/v0.1.0, ...)
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_reference_lookup(git_reference **reference_out, git_repository *repo, const char *name);
/**
+ * Lookup a reference by name and resolve immediately to OID.
+ *
+ * @param oid Pointer to oid to be filled in
+ * @param repo The repository in which to look up the reference
+ * @param name The long name for the reference
+ * @return 0 on success, -1 if name could not be resolved
+ */
+GIT_EXTERN(int) git_reference_name_to_oid(
+ git_oid *out, git_repository *repo, const char *name);
+
+/**
* Create a new symbolic reference.
*
* The reference will be created in the repository and written
@@ -48,7 +59,7 @@ GIT_EXTERN(int) git_reference_lookup(git_reference **reference_out, git_reposito
* @param name The name of the reference
* @param target The target of the reference
* @param force Overwrite existing references
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_reference_create_symbolic(git_reference **ref_out, git_repository *repo, const char *name, const char *target, int force);
@@ -68,7 +79,7 @@ GIT_EXTERN(int) git_reference_create_symbolic(git_reference **ref_out, git_repos
* @param name The name of the reference
* @param id The object id pointed to by the reference.
* @param force Overwrite existing references
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_reference_create_oid(git_reference **ref_out, git_repository *repo, const char *name, const git_oid *id, int force);
@@ -100,7 +111,7 @@ GIT_EXTERN(const char *) git_reference_target(git_reference *ref);
* @param ref The reference
* @return the type
*/
-GIT_EXTERN(git_rtype) git_reference_type(git_reference *ref);
+GIT_EXTERN(git_ref_t) git_reference_type(git_reference *ref);
/**
* Get the full name of a reference
@@ -126,7 +137,7 @@ GIT_EXTERN(const char *) git_reference_name(git_reference *ref);
*
* @param resolved_ref Pointer to the peeled reference
* @param ref The reference
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_reference_resolve(git_reference **resolved_ref, git_reference *ref);
@@ -149,7 +160,7 @@ GIT_EXTERN(git_repository *) git_reference_owner(git_reference *ref);
*
* @param ref The reference
* @param target The new target for the reference
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_reference_set_target(git_reference *ref, const char *target);
@@ -164,7 +175,7 @@ GIT_EXTERN(int) git_reference_set_target(git_reference *ref, const char *target)
*
* @param ref The reference
* @param id The new target OID for the reference
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_reference_set_oid(git_reference *ref, const git_oid *id);
@@ -191,7 +202,7 @@ GIT_EXTERN(int) git_reference_set_oid(git_reference *ref, const git_oid *id);
* @param ref The reference to rename
* @param new_name The new name for the reference
* @param force Overwrite an existing reference
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*
*/
GIT_EXTERN(int) git_reference_rename(git_reference *ref, const char *new_name, int force);
@@ -205,7 +216,7 @@ GIT_EXTERN(int) git_reference_rename(git_reference *ref, const char *new_name, i
* memory. The given reference pointer will no longer be valid.
*
* @param ref The reference to remove
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_reference_delete(git_reference *ref);
@@ -220,7 +231,7 @@ GIT_EXTERN(int) git_reference_delete(git_reference *ref);
* the loose references will be removed from disk.
*
* @param repo Repository where the loose refs will be packed
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_reference_packall(git_repository *repo);
@@ -243,9 +254,9 @@ GIT_EXTERN(int) git_reference_packall(git_repository *repo);
* @param repo Repository where to find the refs
* @param list_flags Filtering flags for the reference
* listing.
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
-GIT_EXTERN(int) git_reference_listall(git_strarray *array, git_repository *repo, unsigned int list_flags);
+GIT_EXTERN(int) git_reference_list(git_strarray *array, git_repository *repo, unsigned int list_flags);
/**
@@ -265,7 +276,7 @@ GIT_EXTERN(int) git_reference_listall(git_strarray *array, git_repository *repo,
* listing.
* @param callback Function which will be called for every listed ref
* @param payload Additional data to pass to the callback
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_reference_foreach(git_repository *repo, unsigned int list_flags, int (*callback)(const char *, void *), void *payload);
@@ -293,7 +304,7 @@ GIT_EXTERN(int) git_reference_is_packed(git_reference *ref);
* returned and the reference pointer will be invalidated.
*
* @param ref The reference to reload
- * @return GIT_SUCCESS on success, or an error code
+ * @return 0 on success, or an error code
*/
GIT_EXTERN(int) git_reference_reload(git_reference *ref);
@@ -304,6 +315,15 @@ GIT_EXTERN(int) git_reference_reload(git_reference *ref);
*/
GIT_EXTERN(void) git_reference_free(git_reference *ref);
+/**
+ * Compare two references.
+ *
+ * @param ref1 The first git_reference
+ * @param ref2 The second git_reference
+ * @return 0 if the same, else a stable but meaningless ordering.
+ */
+GIT_EXTERN(int) git_reference_cmp(git_reference *ref1, git_reference *ref2);
+
/** @} */
GIT_END_DECL
#endif
diff --git a/include/git2/refspec.h b/include/git2/refspec.h
index 0f8b13cec..c0a8eabfe 100644
--- a/include/git2/refspec.h
+++ b/include/git2/refspec.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -7,6 +7,7 @@
#ifndef INCLUDE_git_refspec_h__
#define INCLUDE_git_refspec_h__
+#include "common.h"
#include "types.h"
/**
@@ -24,7 +25,7 @@ GIT_BEGIN_DECL
* @param refspec the refspec
* @return the refspec's source specifier
*/
-const char *git_refspec_src(const git_refspec *refspec);
+GIT_EXTERN(const char *) git_refspec_src(const git_refspec *refspec);
/**
* Get the destination specifier
@@ -32,17 +33,16 @@ const char *git_refspec_src(const git_refspec *refspec);
* @param refspec the refspec
* @return the refspec's destination specifier
*/
-const char *git_refspec_dst(const git_refspec *refspec);
+GIT_EXTERN(const char *) git_refspec_dst(const git_refspec *refspec);
/**
- * Match a refspec's source descriptor with a reference name
+ * Check if a refspec's source descriptor matches a reference
*
* @param refspec the refspec
* @param refname the name of the reference to check
- * @return GIT_SUCCESS on successful match; GIT_ENOMACH on match
- * failure or an error code on other failure
+ * @return 1 if the refspec matches, 0 otherwise
*/
-int git_refspec_src_match(const git_refspec *refspec, const char *refname);
+GIT_EXTERN(int) git_refspec_src_matches(const git_refspec *refspec, const char *refname);
/**
* Transform a reference to its target following the refspec's rules
@@ -51,9 +51,9 @@ int git_refspec_src_match(const git_refspec *refspec, const char *refname);
* @param outlen the size ouf the `out` buffer
* @param spec the refspec
* @param name the name of the reference to transform
- * @return GIT_SUCCESS, GIT_ESHORTBUFFER or another error
+ * @return 0, GIT_EBUFS or another error
*/
-int git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, const char *name);
+GIT_EXTERN(int) git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, const char *name);
GIT_END_DECL
diff --git a/include/git2/remote.h b/include/git2/remote.h
index 0ae38165c..865dfef04 100644
--- a/include/git2/remote.h
+++ b/include/git2/remote.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -11,6 +11,7 @@
#include "repository.h"
#include "refspec.h"
#include "net.h"
+#include "indexer.h"
/**
* @file git2/remote.h
@@ -37,11 +38,12 @@ GIT_BEGIN_DECL
*
* @param out pointer to the new remote object
* @param repo the associtated repository
- * @param url the remote repository's URL
* @param name the remote's name
- * @return GIT_SUCCESS or an error code
+ * @param url the remote repository's URL
+ * @param fetch the fetch refspec to use for this remote
+ * @return 0 or an error code
*/
-GIT_EXTERN(int) git_remote_new(git_remote **out, git_repository *repo, const char *url, const char *name);
+GIT_EXTERN(int) git_remote_new(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch);
/**
* Get the information for a particular remote
@@ -49,11 +51,19 @@ GIT_EXTERN(int) git_remote_new(git_remote **out, git_repository *repo, const cha
* @param out pointer to the new remote object
* @param cfg the repository's configuration
* @param name the remote's name
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_remote_load(git_remote **out, git_repository *repo, const char *name);
/**
+ * Save a remote to its repository's configuration
+ *
+ * @param remote the remote to save to config
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_remote_save(const git_remote *remote);
+
+/**
* Get the remote's name
*
* @param remote the remote
@@ -70,6 +80,15 @@ GIT_EXTERN(const char *) git_remote_name(git_remote *remote);
GIT_EXTERN(const char *) git_remote_url(git_remote *remote);
/**
+ * Set the remote's fetch refspec
+ *
+ * @param remote the remote
+ * @apram spec the new fetch refspec
+ * @return 0 or an error value
+ */
+GIT_EXTERN(int) git_remote_set_fetchspec(git_remote *remote, const char *spec);
+
+/**
* Get the fetch refspec
*
* @param remote the remote
@@ -78,6 +97,15 @@ GIT_EXTERN(const char *) git_remote_url(git_remote *remote);
GIT_EXTERN(const git_refspec *) git_remote_fetchspec(git_remote *remote);
/**
+ * Set the remote's push refspec
+ *
+ * @param remote the remote
+ * @apram spec the new push refspec
+ * @return 0 or an error value
+ */
+GIT_EXTERN(int) git_remote_set_pushspec(git_remote *remote, const char *spec);
+
+/**
* Get the push refspec
*
* @param remote the remote
@@ -95,7 +123,7 @@ GIT_EXTERN(const git_refspec *) git_remote_pushspec(git_remote *remote);
*
* @param remote the remote to connect to
* @param direction whether you want to receive or send data
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_remote_connect(git_remote *remote, int direction);
@@ -107,7 +135,7 @@ GIT_EXTERN(int) git_remote_connect(git_remote *remote, int direction);
*
* @param refs where to store the refs
* @param remote the remote
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload);
@@ -122,9 +150,9 @@ GIT_EXTERN(int) git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void
*
* @param remote the remote to download from
* @param filename where to store the temproray filename
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
-GIT_EXTERN(int) git_remote_download(char **filename, git_remote *remote);
+GIT_EXTERN(int) git_remote_download(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats);
/**
* Check whether the remote is connected
@@ -149,6 +177,9 @@ GIT_EXTERN(void) git_remote_disconnect(git_remote *remote);
/**
* Free the memory associated with a remote
*
+ * This also disconnects from the remote, if the connection
+ * has not been closed yet (using git_remote_disconnect).
+ *
* @param remote the remote to free
*/
GIT_EXTERN(void) git_remote_free(git_remote *remote);
@@ -156,12 +187,10 @@ GIT_EXTERN(void) git_remote_free(git_remote *remote);
/**
* Update the tips to the new state
*
- * Make sure that you only call this once you've successfully indexed
- * or expanded the packfile.
- *
* @param remote the remote to update
+ * @param cb callback to run on each ref update. 'a' is the old value, 'b' is then new value
*/
-GIT_EXTERN(int) git_remote_update_tips(git_remote *remote);
+GIT_EXTERN(int) git_remote_update_tips(git_remote *remote, int (*cb)(const char *refname, const git_oid *a, const git_oid *b));
/**
* Return whether a string is a valid remote URL
@@ -171,6 +200,35 @@ GIT_EXTERN(int) git_remote_update_tips(git_remote *remote);
*/
GIT_EXTERN(int) git_remote_valid_url(const char *url);
+/**
+ * Return whether the passed URL is supported by this version of the library.
+ *
+ * @param url the url to check
+ * @return 1 if the url is supported, 0 otherwise
+*/
+GIT_EXTERN(int) git_remote_supported_url(const char* url);
+
+/**
+ * Get a list of the configured remotes for a repo
+ *
+ * The string array must be freed by the user.
+ *
+ * @param remotes_list a string array with the names of the remotes
+ * @param repo the repository to query
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_remote_list(git_strarray *remotes_list, git_repository *repo);
+
+/**
+ * Add a remote with the default fetch refspec to the repository's configuration
+ *
+ * @param out the resulting remote
+ * @param repo the repository in which to create the remote
+ * @param name the remote's name
+ * @param url the remote's url
+ */
+GIT_EXTERN(int) git_remote_add(git_remote **out, git_repository *repo, const char *name, const char *url);
+
/** @} */
GIT_END_DECL
#endif
diff --git a/include/git2/repository.h b/include/git2/repository.h
index 4250ae161..3949438cf 100644
--- a/include/git2/repository.h
+++ b/include/git2/repository.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -31,7 +31,7 @@ GIT_BEGIN_DECL
*
* @param repository pointer to the repo which will be opened
* @param path the path to the repository
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_repository_open(git_repository **repository, const char *path);
@@ -61,7 +61,7 @@ GIT_EXTERN(int) git_repository_open(git_repository **repository, const char *pat
* start_path no matter start_path appears in ceiling_dirs ceiling_dirs
* might be NULL (which is equivalent to an empty string)
*
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_repository_discover(
char *repository_path,
@@ -70,6 +70,20 @@ GIT_EXTERN(int) git_repository_discover(
int across_fs,
const char *ceiling_dirs);
+enum {
+ GIT_REPOSITORY_OPEN_NO_SEARCH = (1 << 0),
+ GIT_REPOSITORY_OPEN_CROSS_FS = (1 << 1),
+};
+
+/**
+ * Find and open a repository with extended controls.
+ */
+GIT_EXTERN(int) git_repository_open_ext(
+ git_repository **repo,
+ const char *start_path,
+ uint32_t flags,
+ const char *ceiling_dirs);
+
/**
* Free a previously allocated repository
*
@@ -95,7 +109,7 @@ GIT_EXTERN(void) git_repository_free(git_repository *repo);
* at the pointed path. If false, provided path will be considered as the working
* directory into which the .git directory will be created.
*
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_repository_init(git_repository **repo_out, const char *path, unsigned is_bare);
@@ -180,7 +194,7 @@ GIT_EXTERN(const char *) git_repository_workdir(git_repository *repo);
*
* @param repo A repository object
* @param workdir The path to a working directory
- * @return GIT_SUCCESS, or an error code
+ * @return 0, or an error code
*/
GIT_EXTERN(int) git_repository_set_workdir(git_repository *repo, const char *workdir);
@@ -204,7 +218,7 @@ GIT_EXTERN(int) git_repository_is_bare(git_repository *repo);
*
* @param out Pointer to store the loaded config file
* @param repo A repository object
- * @return GIT_SUCCESS, or an error code
+ * @return 0, or an error code
*/
GIT_EXTERN(int) git_repository_config(git_config **out, git_repository *repo);
@@ -235,7 +249,7 @@ GIT_EXTERN(void) git_repository_set_config(git_repository *repo, git_config *con
*
* @param out Pointer to store the loaded ODB
* @param repo A repository object
- * @return GIT_SUCCESS, or an error code
+ * @return 0, or an error code
*/
GIT_EXTERN(int) git_repository_odb(git_odb **out, git_repository *repo);
@@ -266,7 +280,7 @@ GIT_EXTERN(void) git_repository_set_odb(git_repository *repo, git_odb *odb);
*
* @param out Pointer to store the loaded index
* @param repo A repository object
- * @return GIT_SUCCESS, or an error code
+ * @return 0, or an error code
*/
GIT_EXTERN(int) git_repository_index(git_index **out, git_repository *repo);
diff --git a/include/git2/revwalk.h b/include/git2/revwalk.h
index c84c5d301..aac6fb7c2 100644
--- a/include/git2/revwalk.h
+++ b/include/git2/revwalk.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -65,7 +65,7 @@ GIT_BEGIN_DECL
*
* @param walker pointer to the new revision walker
* @param repo the repo to walk through
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_revwalk_new(git_revwalk **walker, git_repository *repo);
@@ -97,10 +97,32 @@ GIT_EXTERN(void) git_revwalk_reset(git_revwalk *walker);
*
* @param walk the walker being used for the traversal.
* @param oid the oid of the commit to start from.
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_revwalk_push(git_revwalk *walk, const git_oid *oid);
+/**
+ * Push matching references
+ *
+ * The OIDs pinted to by the references that match the given glob
+ * pattern will be pushed to the revision walker.
+ *
+ * A leading 'refs/' is implied it not present as well as a trailing
+ * '/ *' if the glob lacks '?', '*' or '['.
+ *
+ * @param walk the walker being used for the traversal
+ * @param glob the glob pattern references should match
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_revwalk_push_glob(git_revwalk *walk, const char *glob);
+
+/**
+ * Push the repository's HEAD
+ *
+ * @param walk the walker being used for the traversal
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_revwalk_push_head(git_revwalk *walk);
/**
* Mark a commit (and its ancestors) uninteresting for the output.
@@ -113,11 +135,57 @@ GIT_EXTERN(int) git_revwalk_push(git_revwalk *walk, const git_oid *oid);
*
* @param walk the walker being used for the traversal.
* @param oid the oid of commit that will be ignored during the traversal
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_revwalk_hide(git_revwalk *walk, const git_oid *oid);
/**
+ * Hide matching references.
+ *
+ * The OIDs pinted to by the references that match the given glob
+ * pattern and their ancestors will be hidden from the output on the
+ * revision walk.
+ *
+ * A leading 'refs/' is implied it not present as well as a trailing
+ * '/ *' if the glob lacks '?', '*' or '['.
+ *
+ * @param walk the walker being used for the traversal
+ * @param glob the glob pattern references should match
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_revwalk_hide_glob(git_revwalk *walk, const char *glob);
+
+/**
+ * Hide the repository's HEAD
+ *
+ * @param walk the walker being used for the traversal
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_revwalk_hide_head(git_revwalk *walk);
+
+/**
+ * Push the OID pointed to by a reference
+ *
+ * The reference must point to a commit.
+ *
+ * @param walk the walker being used for the traversal
+ * @param refname the referece to push
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_revwalk_push_ref(git_revwalk *walk, const char *refname);
+
+/**
+ * Hide the OID pointed to by a reference
+ *
+ * The reference must point to a commit.
+ *
+ * @param walk the walker being used for the traversal
+ * @param refname the referece to hide
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_revwalk_hide_ref(git_revwalk *walk, const char *refname);
+
+/**
* Get the next commit from the revision walk.
*
* The initial call to this method is *not* blocking when
@@ -132,8 +200,8 @@ GIT_EXTERN(int) git_revwalk_hide(git_revwalk *walk, const git_oid *oid);
*
* @param oid Pointer where to store the oid of the next commit
* @param walk the walker to pop the commit from.
- * @return GIT_SUCCESS if the next commit was found;
- * GIT_EREVWALKOVER if there are no commits left to iterate
+ * @return 0 if the next commit was found;
+ * GIT_REVWALKOVER if there are no commits left to iterate
*/
GIT_EXTERN(int) git_revwalk_next(git_oid *oid, git_revwalk *walk);
diff --git a/include/git2/signature.h b/include/git2/signature.h
index 228929943..cbf94269f 100644
--- a/include/git2/signature.h
+++ b/include/git2/signature.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -28,7 +28,7 @@ GIT_BEGIN_DECL
* @param email email of the person
* @param time time when the action happened
* @param offset timezone offset in minutes for the time
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_signature_new(git_signature **sig_out, const char *name, const char *email, git_time_t time, int offset);
@@ -39,7 +39,7 @@ GIT_EXTERN(int) git_signature_new(git_signature **sig_out, const char *name, con
* @param sig_out new signature, in case of error NULL
* @param name name of the person
* @param email email of the person
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_signature_now(git_signature **sig_out, const char *name, const char *email);
diff --git a/include/git2/status.h b/include/git2/status.h
index c0f38c508..6a424dfd6 100644
--- a/include/git2/status.h
+++ b/include/git2/status.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -19,32 +19,111 @@
*/
GIT_BEGIN_DECL
-#define GIT_STATUS_CURRENT 0
+enum {
+ GIT_STATUS_CURRENT = 0,
-/** Flags for index status */
-#define GIT_STATUS_INDEX_NEW (1 << 0)
-#define GIT_STATUS_INDEX_MODIFIED (1 << 1)
-#define GIT_STATUS_INDEX_DELETED (1 << 2)
+ GIT_STATUS_INDEX_NEW = (1 << 0),
+ GIT_STATUS_INDEX_MODIFIED = (1 << 1),
+ GIT_STATUS_INDEX_DELETED = (1 << 2),
-/** Flags for worktree status */
-#define GIT_STATUS_WT_NEW (1 << 3)
-#define GIT_STATUS_WT_MODIFIED (1 << 4)
-#define GIT_STATUS_WT_DELETED (1 << 5)
+ GIT_STATUS_WT_NEW = (1 << 3),
+ GIT_STATUS_WT_MODIFIED = (1 << 4),
+ GIT_STATUS_WT_DELETED = (1 << 5),
-#define GIT_STATUS_IGNORED (1 << 6)
+ GIT_STATUS_IGNORED = (1 << 6),
+};
/**
* Gather file statuses and run a callback for each one.
*
- * The callback is passed the path of the file, the status and the data pointer
- * passed to this function. If the callback returns something other than
- * GIT_SUCCESS, this function will return that value.
+ * The callback is passed the path of the file, the status and the data
+ * pointer passed to this function. If the callback returns something other
+ * than 0, this function will return that value.
*
* @param repo a repository object
* @param callback the function to call on each file
- * @return GIT_SUCCESS or the return value of the callback which did not return GIT_SUCCESS
+ * @return 0 on success or the return value of the callback that was non-zero
*/
-GIT_EXTERN(int) git_status_foreach(git_repository *repo, int (*callback)(const char *, unsigned int, void *), void *payload);
+GIT_EXTERN(int) git_status_foreach(
+ git_repository *repo,
+ int (*callback)(const char *, unsigned int, void *),
+ void *payload);
+
+/**
+ * Select the files on which to report status.
+ *
+ * - GIT_STATUS_SHOW_INDEX_AND_WORKDIR is the default. This is the
+ * rough equivalent of `git status --porcelain` where each file
+ * will receive a callback indicating its status in the index and
+ * in the workdir.
+ * - GIT_STATUS_SHOW_INDEX_ONLY will only make callbacks for index
+ * side of status. The status of the index contents relative to
+ * the HEAD will be given.
+ * - GIT_STATUS_SHOW_WORKDIR_ONLY will only make callbacks for the
+ * workdir side of status, reporting the status of workdir content
+ * relative to the index.
+ * - GIT_STATUS_SHOW_INDEX_THEN_WORKDIR behaves like index-only
+ * followed by workdir-only, causing two callbacks to be issued
+ * per file (first index then workdir). This is slightly more
+ * efficient than making separate calls. This makes it easier to
+ * emulate the output of a plain `git status`.
+ */
+typedef enum {
+ GIT_STATUS_SHOW_INDEX_AND_WORKDIR = 0,
+ GIT_STATUS_SHOW_INDEX_ONLY = 1,
+ GIT_STATUS_SHOW_WORKDIR_ONLY = 2,
+ GIT_STATUS_SHOW_INDEX_THEN_WORKDIR = 3,
+} git_status_show_t;
+
+/**
+ * Flags to control status callbacks
+ *
+ * - GIT_STATUS_OPT_INCLUDE_UNTRACKED says that callbacks should
+ * be made on untracked files. These will only be made if the
+ * workdir files are included in the status "show" option.
+ * - GIT_STATUS_OPT_INCLUDE_IGNORED says that ignored files should
+ * get callbacks. Again, these callbacks will only be made if
+ * the workdir files are included in the status "show" option.
+ * Right now, there is no option to include all files in
+ * directories that are ignored completely.
+ * - GIT_STATUS_OPT_INCLUDE_UNMODIFIED indicates that callback
+ * should be made even on unmodified files.
+ * - GIT_STATUS_OPT_EXCLUDE_SUBMODULES indicates that directories
+ * which appear to be submodules should just be skipped over.
+ * - GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS indicates that the
+ * contents of untracked directories should be included in the
+ * status. Normally if an entire directory is new, then just
+ * the top-level directory will be included (with a trailing
+ * slash on the entry name). Given this flag, the directory
+ * itself will not be included, but all the files in it will.
+ */
+
+enum {
+ GIT_STATUS_OPT_INCLUDE_UNTRACKED = (1 << 0),
+ GIT_STATUS_OPT_INCLUDE_IGNORED = (1 << 1),
+ GIT_STATUS_OPT_INCLUDE_UNMODIFIED = (1 << 2),
+ GIT_STATUS_OPT_EXCLUDE_SUBMODULED = (1 << 3),
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = (1 << 4),
+};
+
+/**
+ * Options to control how callbacks will be made by
+ * `git_status_foreach_ext()`.
+ */
+typedef struct {
+ git_status_show_t show;
+ unsigned int flags;
+ git_strarray pathspec;
+} git_status_options;
+
+/**
+ * Gather file status information and run callbacks as requested.
+ */
+GIT_EXTERN(int) git_status_foreach_ext(
+ git_repository *repo,
+ const git_status_options *opts,
+ int (*callback)(const char *, unsigned int, void *),
+ void *payload);
/**
* Get file status for a single file
@@ -54,9 +133,12 @@ GIT_EXTERN(int) git_status_foreach(git_repository *repo, int (*callback)(const c
* @param path the file to retrieve status for, rooted at the repo's workdir
* @return GIT_EINVALIDPATH when `path` points at a folder, GIT_ENOTFOUND when
* the file doesn't exist in any of HEAD, the index or the worktree,
- * GIT_SUCCESS otherwise
+ * 0 otherwise
*/
-GIT_EXTERN(int) git_status_file(unsigned int *status_flags, git_repository *repo, const char *path);
+GIT_EXTERN(int) git_status_file(
+ unsigned int *status_flags,
+ git_repository *repo,
+ const char *path);
/**
* Test if the ignore rules apply to a given file.
@@ -66,13 +148,16 @@ GIT_EXTERN(int) git_status_file(unsigned int *status_flags, git_repository *repo
* would be ignored regardless of whether the file is already in the index
* or in the repository.
*
- * @param repo a repository object
- * @param path the file to check ignores for, rooted at the repo's workdir
* @param ignored boolean returning 0 if the file is not ignored, 1 if it is
- * @return GIT_SUCCESS if the ignore rules could be processed for the file
- * (regardless of whether it exists or not), or an error < 0 if they could not.
+ * @param repo a repository object
+ * @param path the file to check ignores for, rooted at the repo's workdir.
+ * @return 0 if ignore rules could be processed for the file (regardless
+ * of whether it exists or not), or an error < 0 if they could not.
*/
-GIT_EXTERN(int) git_status_should_ignore(git_repository *repo, const char *path, int *ignored);
+GIT_EXTERN(int) git_status_should_ignore(
+ int *ignored,
+ git_repository *repo,
+ const char *path);
/** @} */
GIT_END_DECL
diff --git a/include/git2/submodule.h b/include/git2/submodule.h
new file mode 100644
index 000000000..930168275
--- /dev/null
+++ b/include/git2/submodule.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_submodule_h__
+#define INCLUDE_git_submodule_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+
+/**
+ * @file git2/submodule.h
+ * @brief Git submodule management utilities
+ * @defgroup git_submodule Git submodule management routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+typedef enum {
+ GIT_SUBMODULE_UPDATE_CHECKOUT = 0,
+ GIT_SUBMODULE_UPDATE_REBASE = 1,
+ GIT_SUBMODULE_UPDATE_MERGE = 2
+} git_submodule_update_t;
+
+typedef enum {
+ GIT_SUBMODULE_IGNORE_ALL = 0, /* never dirty */
+ GIT_SUBMODULE_IGNORE_DIRTY = 1, /* only dirty if HEAD moved */
+ GIT_SUBMODULE_IGNORE_UNTRACKED = 2, /* dirty if tracked files change */
+ GIT_SUBMODULE_IGNORE_NONE = 3 /* any change or untracked == dirty */
+} git_submodule_ignore_t;
+
+/**
+ * Description of submodule
+ *
+ * This record describes a submodule found in a repository. There
+ * should be an entry for every submodule found in the HEAD and for
+ * every submodule described in .gitmodules. The fields are as follows:
+ *
+ * - `name` is the name of the submodule from .gitmodules.
+ * - `path` is the path to the submodule from the repo working directory.
+ * It is almost always the same as `name`.
+ * - `url` is the url for the submodule.
+ * - `oid` is the HEAD SHA1 for the submodule.
+ * - `update` is a value from above - see gitmodules(5) update.
+ * - `ignore` is a value from above - see gitmodules(5) ignore.
+ * - `fetch_recurse` is 0 or 1 - see gitmodules(5) fetchRecurseSubmodules.
+ * - `refcount` is for internal use.
+ *
+ * If the submodule has been added to .gitmodules but not yet git added,
+ * then the `oid` will be zero. If the submodule has been deleted, but
+ * the delete has not been committed yet, then the `oid` will be set, but
+ * the `url` will be NULL.
+ */
+typedef struct {
+ char *name;
+ char *path;
+ char *url;
+ git_oid oid; /* sha1 of submodule HEAD ref or zero if not committed */
+ git_submodule_update_t update;
+ git_submodule_ignore_t ignore;
+ int fetch_recurse;
+ int refcount;
+} git_submodule;
+
+/**
+ * Iterate over all submodules of a repository.
+ *
+ * @param repo The repository
+ * @param callback Function to be called with the name of each submodule.
+ * Return a non-zero value to terminate the iteration.
+ * @param payload Extra data to pass to callback
+ * @return 0 on success, -1 on error, or non-zero return value of callback
+ */
+GIT_EXTERN(int) git_submodule_foreach(
+ git_repository *repo,
+ int (*callback)(const char *name, void *payload),
+ void *payload);
+
+/**
+ * Lookup submodule information by name or path.
+ *
+ * Given either the submodule name or path (they are ususally the same),
+ * this returns a structure describing the submodule. If the submodule
+ * does not exist, this will return GIT_ENOTFOUND and set the submodule
+ * pointer to NULL.
+ *
+ * @param submodule Pointer to submodule description object pointer..
+ * @param repo The repository.
+ * @param name The name of the submodule. Trailing slashes will be ignored.
+ * @return 0 on success, GIT_ENOTFOUND if submodule does not exist, -1 on error
+ */
+GIT_EXTERN(int) git_submodule_lookup(
+ git_submodule **submodule,
+ git_repository *repo,
+ const char *name);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/tag.h b/include/git2/tag.h
index be49621e9..859c28995 100644
--- a/include/git2/tag.h
+++ b/include/git2/tag.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -27,7 +27,7 @@ GIT_BEGIN_DECL
* @param tag pointer to the looked up tag
* @param repo the repo to use when locating the tag.
* @param id identity of the tag to locate.
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_INLINE(int) git_tag_lookup(git_tag **tag, git_repository *repo, const git_oid *id)
{
@@ -44,7 +44,7 @@ GIT_INLINE(int) git_tag_lookup(git_tag **tag, git_repository *repo, const git_oi
* @param repo the repo to use when locating the tag.
* @param id identity of the tag to locate.
* @param len the length of the short identifier
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_INLINE(int) git_tag_lookup_prefix(git_tag **tag, git_repository *repo, const git_oid *id, unsigned int len)
{
@@ -85,7 +85,7 @@ GIT_EXTERN(const git_oid *) git_tag_id(git_tag *tag);
*
* @param target pointer where to store the target
* @param tag a previously loaded tag.
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_tag_target(git_object **target, git_tag *tag);
@@ -137,6 +137,9 @@ GIT_EXTERN(const char *) git_tag_message(git_tag *tag);
* this tag object. If `force` is true and a reference
* already exists with the given name, it'll be replaced.
*
+ * The message will be cleaned up from excess whitespace
+ * it will be made sure that the last line ends with a '\n'.
+ *
* @param oid Pointer where to store the OID of the
* newly created tag. If the tag already exists, this parameter
* will be the oid of the existing tag, and the function will
@@ -158,7 +161,7 @@ GIT_EXTERN(const char *) git_tag_message(git_tag *tag);
*
* @param force Overwrite existing references
*
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
* A tag object is written to the ODB, and a proper reference
* is written in the /refs/tags folder, pointing to it
*/
@@ -209,7 +212,7 @@ GIT_EXTERN(int) git_tag_create_frombuffer(
*
* @param force Overwrite existing references
*
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
* A proper reference is written in the /refs/tags folder,
* pointing to the provided target object
*/
@@ -228,7 +231,7 @@ GIT_EXTERN(int) git_tag_create_lightweight(
* @param tag_name Name of the tag to be deleted;
* this name is validated for consistency.
*
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_tag_delete(
git_repository *repo,
@@ -245,7 +248,7 @@ GIT_EXTERN(int) git_tag_delete(
* @param tag_names Pointer to a git_strarray structure where
* the tag names will be stored
* @param repo Repository where to find the tags
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_tag_list(
git_strarray *tag_names,
@@ -267,13 +270,28 @@ GIT_EXTERN(int) git_tag_list(
* the tag names will be stored
* @param pattern Standard fnmatch pattern
* @param repo Repository where to find the tags
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_tag_list_match(
git_strarray *tag_names,
const char *pattern,
git_repository *repo);
+/**
+ * Recursively peel a tag until a non tag git_object
+ * is met
+ *
+ * The retrieved `tag_target` object is owned by the repository
+ * and should be closed with the `git_object_free` method.
+ *
+ * @param tag_target Pointer to the peeled git_object
+ * @param tag The tag to be processed
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_tag_peel(
+ git_object **tag_target,
+ git_tag *tag);
+
/** @} */
GIT_END_DECL
#endif
diff --git a/include/git2/threads.h b/include/git2/threads.h
index 85472a441..567a10487 100644
--- a/include/git2/threads.h
+++ b/include/git2/threads.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
diff --git a/include/git2/tree.h b/include/git2/tree.h
index 982646628..777f8ff0d 100644
--- a/include/git2/tree.h
+++ b/include/git2/tree.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -27,7 +27,7 @@ GIT_BEGIN_DECL
* @param tree pointer to the looked up tree
* @param repo the repo to use when locating the tree.
* @param id identity of the tree to locate.
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_INLINE(int) git_tree_lookup(git_tree **tree, git_repository *repo, const git_oid *id)
{
@@ -44,7 +44,7 @@ GIT_INLINE(int) git_tree_lookup(git_tree **tree, git_repository *repo, const git
* @param repo the repo to use when locating the tree.
* @param id identity of the tree to locate.
* @param len the length of the short identifier
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_INLINE(int) git_tree_lookup_prefix(git_tree **tree, git_repository *repo, const git_oid *id, unsigned int len)
{
@@ -141,9 +141,9 @@ GIT_EXTERN(git_otype) git_tree_entry_type(const git_tree_entry *entry);
* @param object pointer to the converted object
* @param repo repository where to lookup the pointed object
* @param entry a tree entry
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
-GIT_EXTERN(int) git_tree_entry_2object(git_object **object_out, git_repository *repo, const git_tree_entry *entry);
+GIT_EXTERN(int) git_tree_entry_to_object(git_object **object_out, git_repository *repo, const git_tree_entry *entry);
/**
* Write a tree to the ODB from the index file
@@ -159,7 +159,7 @@ GIT_EXTERN(int) git_tree_entry_2object(git_object **object_out, git_repository *
*
* @param oid Pointer where to store the written tree
* @param index Index to write
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_tree_create_fromindex(git_oid *oid, git_index *index);
@@ -229,7 +229,7 @@ GIT_EXTERN(const git_tree_entry *) git_treebuilder_get(git_treebuilder *bld, con
* @param filename Filename of the entry
* @param id SHA1 oid of the entry
* @param attributes Folder attributes of the entry
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_treebuilder_insert(git_tree_entry **entry_out, git_treebuilder *bld, const char *filename, const git_oid *id, unsigned int attributes);
@@ -264,7 +264,7 @@ GIT_EXTERN(void) git_treebuilder_filter(git_treebuilder *bld, int (*filter)(cons
* @param oid Pointer where to store the written OID
* @param repo Repository where to store the object
* @param bld Tree builder to write
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *bld);
@@ -278,8 +278,7 @@ GIT_EXTERN(int) git_treebuilder_write(git_oid *oid, git_repository *repo, git_tr
* @param subtree Pointer where to store the subtree
* @param root A previously loaded tree which will be the root of the relative path
* @param subtree_path Path to the contained subtree
- * @return GIT_SUCCESS on success; GIT_ENOTFOUND if the path does not lead to a
- * subtree, GIT_EINVALIDPATH or an error code
+ * @return 0 on success; GIT_ENOTFOUND if the path does not lead to a subtree
*/
GIT_EXTERN(int) git_tree_get_subtree(git_tree **subtree, git_tree *root, const char *subtree_path);
@@ -309,45 +308,11 @@ enum git_treewalk_mode {
* @param callback Function to call on each tree entry
* @param mode Traversal mode (pre or post-order)
* @param payload Opaque pointer to be passed on each callback
- * @return GIT_SUCCESS or an error code
+ * @return 0 or an error code
*/
GIT_EXTERN(int) git_tree_walk(git_tree *tree, git_treewalk_cb callback, int mode, void *payload);
/** @} */
-typedef enum {
- GIT_STATUS_ADDED = 1,
- GIT_STATUS_DELETED = 2,
- GIT_STATUS_MODIFIED = 3,
-} git_status_t;
-
-typedef struct {
- unsigned int old_attr;
- unsigned int new_attr;
- git_oid old_oid;
- git_oid new_oid;
- git_status_t status;
- const char *path;
-} git_tree_diff_data;
-
-typedef int (*git_tree_diff_cb)(const git_tree_diff_data *ptr, void *data);
-
-/**
- * Diff two trees
- *
- * Compare two trees. For each difference in the trees, the callback
- * will be called with a git_tree_diff_data filled with the relevant
- * information.
- *
- * @param old the "old" tree
- * @param newer the "newer" tree
- * @param cb callback
- * @param data data to give to the callback
- * @return GIT_SUCCESS or an error code
- */
-int git_tree_diff(git_tree *old, git_tree *newer, git_tree_diff_cb cb, void *data);
-
-int git_tree_diff_index_recursive(git_tree *tree, git_index *index, git_tree_diff_cb cb, void *data);
-
GIT_END_DECL
#endif
diff --git a/include/git2/types.h b/include/git2/types.h
index ea97ee915..cfb0acf33 100644
--- a/include/git2/types.h
+++ b/include/git2/types.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -131,6 +131,9 @@ typedef struct git_reflog_entry git_reflog_entry;
/** Representation of a reference log */
typedef struct git_reflog git_reflog;
+/** Representation of a git note */
+typedef struct git_note git_note;
+
/** Time in a signature */
typedef struct git_time {
git_time_t time; /** time in seconds from epoch */
@@ -155,8 +158,13 @@ typedef enum {
GIT_REF_PACKED = 4,
GIT_REF_HAS_PEEL = 8,
GIT_REF_LISTALL = GIT_REF_OID|GIT_REF_SYMBOLIC|GIT_REF_PACKED,
-} git_rtype;
+} git_ref_t;
+/** Basic type of any Git branch. */
+typedef enum {
+ GIT_BRANCH_LOCAL = 1,
+ GIT_BRANCH_REMOTE = 2,
+} git_branch_t;
typedef struct git_refspec git_refspec;
typedef struct git_remote git_remote;
diff --git a/include/git2/version.h b/include/git2/version.h
index d78178727..8edbe323c 100644
--- a/include/git2/version.h
+++ b/include/git2/version.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -7,9 +7,9 @@
#ifndef INCLUDE_git_version_h__
#define INCLUDE_git_version_h__
-#define LIBGIT2_VERSION "0.16.0"
+#define LIBGIT2_VERSION "0.17.0"
#define LIBGIT2_VER_MAJOR 0
-#define LIBGIT2_VER_MINOR 16
+#define LIBGIT2_VER_MINOR 17
#define LIBGIT2_VER_REVISION 0
#endif
diff --git a/include/git2/windows.h b/include/git2/windows.h
index 6a2e9e2cd..8b743f0aa 100644
--- a/include/git2/windows.h
+++ b/include/git2/windows.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
diff --git a/include/git2/zlib.h b/include/git2/zlib.h
deleted file mode 100644
index e3dd23f96..000000000
--- a/include/git2/zlib.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2009-2011 the libgit2 contributors
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_zlib_h__
-#define INCLUDE_git_zlib_h__
-
-#include <zlib.h>
-
-/**
- * @file git2/zlib.h
- * @brief Git data compression routines
- * @defgroup git_zlib Git data compression routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-#if defined(NO_DEFLATE_BOUND) || ZLIB_VERNUM < 0x1200
-/**
- * deflateBound returns an upper bound on the compressed size.
- *
- * This is a stub function used when zlib does not supply the
- * deflateBound() implementation itself.
- *
- * @param stream the stream pointer.
- * @param s total length of the source data (in bytes).
- * @return maximum length of the compressed data.
- */
-GIT_INLINE(size_t) deflateBound(z_streamp stream, size_t s)
-{
- return (s + ((s + 7) >> 3) + ((s + 63) >> 6) + 11);
-}
-#endif
-
-/** @} */
-GIT_END_DECL
-#endif
diff --git a/packaging/rpm/README b/packaging/rpm/README
new file mode 100644
index 000000000..1a6410b16
--- /dev/null
+++ b/packaging/rpm/README
@@ -0,0 +1,6 @@
+To build RPM pakcages for Fedora, follow these steps:
+ cp packaging/rpm/libgit2.spec ~/rpmbuild/SPECS
+ cd ~/rpmbuild/SOURCES
+ wget https://github.com/downloads/libgit2/libgit2/libgit2-0.16.0.tar.gz
+ cd ~/rpmbuild/SPECS
+ rpmbuild -ba libgit2.spec
diff --git a/packaging/rpm/libgit2.spec b/packaging/rpm/libgit2.spec
new file mode 100644
index 000000000..a6e82b241
--- /dev/null
+++ b/packaging/rpm/libgit2.spec
@@ -0,0 +1,106 @@
+#
+# spec file for package libgit2
+#
+# Copyright (c) 2012 Saleem Ansari <tuxdna@gmail.com>
+# Copyright (c) 2012 SUSE LINUX Products GmbH, Nuernberg, Germany.
+# Copyright (c) 2011, Sascha Peilicke <saschpe@gmx.de>
+#
+# All modifications and additions to the file contributed by third parties
+# remain the property of their copyright owners, unless otherwise agreed
+# upon. The license for this file, and modifications and additions to the
+# file, is the same license as for the pristine package itself (unless the
+# license for the pristine package is not an Open Source License, in which
+# case the license is the MIT License). An "Open Source License" is a
+# license that conforms to the Open Source Definition (Version 1.9)
+# published by the Open Source Initiative.
+
+# Please submit bugfixes or comments via http://bugs.opensuse.org/
+#
+Name: libgit2
+Version: 0.16.0
+Release: 1
+Summary: C git library
+License: GPL-2.0 with linking
+Group: Development/Libraries/C and C++
+Url: http://libgit2.github.com/
+Source0: https://github.com/downloads/libgit2/libgit2/libgit2-0.16.0.tar.gz
+BuildRequires: cmake
+BuildRequires: pkgconfig
+BuildRoot: %{_tmppath}/%{name}-%{version}-build
+%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos_version}
+BuildRequires: openssl-devel
+%else
+BuildRequires: libopenssl-devel
+%endif
+
+%description
+libgit2 is a portable, pure C implementation of the Git core methods
+provided as a re-entrant linkable library with a solid API, allowing
+you to write native speed custom Git applications in any language
+with bindings.
+
+%package -n %{name}-0
+Summary: C git library
+Group: System/Libraries
+
+%description -n %{name}-0
+libgit2 is a portable, pure C implementation of the Git core methods
+provided as a re-entrant linkable library with a solid API, allowing
+you to write native speed custom Git applications in any language
+with bindings.
+
+%package devel
+Summary: C git library
+Group: Development/Libraries/C and C++
+Requires: %{name}-0 >= %{version}
+
+%description devel
+This package contains all necessary include files and libraries needed
+to compile and develop applications that use libgit2.
+
+%prep
+%setup -q
+
+%build
+cmake . \
+ -DCMAKE_C_FLAGS:STRING="%{optflags}" \
+ -DCMAKE_INSTALL_PREFIX:PATH=%{_prefix} \
+ -DINSTALL_LIB:PATH=%{_libdir}
+make %{?_smp_mflags}
+
+%install
+%make_install
+
+%post -n %{name}-0 -p /sbin/ldconfig
+%postun -n %{name}-0 -p /sbin/ldconfig
+
+%files -n %{name}-0
+%defattr (-,root,root)
+%doc AUTHORS COPYING README.md
+%{_libdir}/%{name}.so.*
+
+%files devel
+%defattr (-,root,root)
+%doc CONVENTIONS examples
+%{_libdir}/%{name}.so
+%{_includedir}/git2*
+%{_libdir}/pkgconfig/libgit2.pc
+
+%changelog
+* Tue Mar 04 2012 tuxdna@gmail.com
+- Update to version 0.16.0
+* Tue Jan 31 2012 jengelh@medozas.de
+- Provide pkgconfig symbols
+* Thu Oct 27 2011 saschpe@suse.de
+- Change license to 'GPL-2.0 with linking', fixes bnc#726789
+* Wed Oct 26 2011 saschpe@suse.de
+- Update to version 0.15.0:
+ * Upstream doesn't provide changes
+- Removed outdated %%clean section
+* Tue Jan 18 2011 saschpe@gmx.de
+- Proper Requires for devel package
+* Tue Jan 18 2011 saschpe@gmx.de
+- Set BuildRequires to "openssl-devel" also for RHEL and CentOS
+* Tue Jan 18 2011 saschpe@gmx.de
+- Initial commit (0.0.1)
+- Added patch to fix shared library soname
diff --git a/src/attr.c b/src/attr.c
index 17571f6a8..093f64d5c 100644
--- a/src/attr.c
+++ b/src/attr.c
@@ -3,13 +3,21 @@
#include "config.h"
#include <ctype.h>
+GIT__USE_STRMAP;
+
static int collect_attr_files(
- git_repository *repo, const char *path, git_vector *files);
+ git_repository *repo,
+ uint32_t flags,
+ const char *path,
+ git_vector *files);
int git_attr_get(
- git_repository *repo, const char *pathname,
- const char *name, const char **value)
+ const char **value,
+ git_repository *repo,
+ uint32_t flags,
+ const char *pathname,
+ const char *name)
{
int error;
git_attr_path path;
@@ -21,10 +29,11 @@ int git_attr_get(
*value = NULL;
- if ((error = git_attr_path__init(
- &path, pathname, git_repository_workdir(repo))) < GIT_SUCCESS ||
- (error = collect_attr_files(repo, pathname, &files)) < GIT_SUCCESS)
- return git__rethrow(error, "Could not get attribute for %s", pathname);
+ if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0)
+ return -1;
+
+ if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0)
+ goto cleanup;
attr.name = name;
attr.name_hash = git_attr_file__name_hash(name);
@@ -33,18 +42,17 @@ int git_attr_get(
git_attr_file__foreach_matching_rule(file, &path, j, rule) {
int pos = git_vector_bsearch(&rule->assigns, &attr);
- git_clearerror(); /* okay if search failed */
-
if (pos >= 0) {
*value = ((git_attr_assignment *)git_vector_get(
&rule->assigns, pos))->value;
- goto found;
+ goto cleanup;
}
}
}
-found:
+cleanup:
git_vector_free(&files);
+ git_attr_path__free(&path);
return error;
}
@@ -56,8 +64,12 @@ typedef struct {
} attr_get_many_info;
int git_attr_get_many(
- git_repository *repo, const char *pathname,
- size_t num_attr, const char **names, const char **values)
+ const char **values,
+ git_repository *repo,
+ uint32_t flags,
+ const char *pathname,
+ size_t num_attr,
+ const char **names)
{
int error;
git_attr_path path;
@@ -70,15 +82,14 @@ int git_attr_get_many(
memset((void *)values, 0, sizeof(const char *) * num_attr);
- if ((error = git_attr_path__init(
- &path, pathname, git_repository_workdir(repo))) < GIT_SUCCESS ||
- (error = collect_attr_files(repo, pathname, &files)) < GIT_SUCCESS)
- return git__rethrow(error, "Could not get attributes for %s", pathname);
+ if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0)
+ return -1;
- if ((info = git__calloc(num_attr, sizeof(attr_get_many_info))) == NULL) {
- git__rethrow(GIT_ENOMEM, "Could not get attributes for %s", pathname);
+ if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0)
goto cleanup;
- }
+
+ info = git__calloc(num_attr, sizeof(attr_get_many_info));
+ GITERR_CHECK_ALLOC(info);
git_vector_foreach(&files, i, file) {
@@ -96,8 +107,6 @@ int git_attr_get_many(
}
pos = git_vector_bsearch(&rule->assigns, &info[k].name);
- git_clearerror(); /* okay if search failed */
-
if (pos >= 0) {
info[k].found = (git_attr_assignment *)
git_vector_get(&rule->assigns, pos);
@@ -112,6 +121,7 @@ int git_attr_get_many(
cleanup:
git_vector_free(&files);
+ git_attr_path__free(&path);
git__free(info);
return error;
@@ -119,7 +129,9 @@ cleanup:
int git_attr_foreach(
- git_repository *repo, const char *pathname,
+ git_repository *repo,
+ uint32_t flags,
+ const char *pathname,
int (*callback)(const char *name, const char *value, void *payload),
void *payload)
{
@@ -130,18 +142,16 @@ int git_attr_foreach(
git_attr_file *file;
git_attr_rule *rule;
git_attr_assignment *assign;
- git_hashtable *seen = NULL;
+ git_strmap *seen = NULL;
- if ((error = git_attr_path__init(
- &path, pathname, git_repository_workdir(repo))) < GIT_SUCCESS ||
- (error = collect_attr_files(repo, pathname, &files)) < GIT_SUCCESS)
- return git__rethrow(error, "Could not get attributes for %s", pathname);
+ if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0)
+ return -1;
- seen = git_hashtable_alloc(8, git_hash__strhash_cb, git_hash__strcmp_cb);
- if (!seen) {
- error = GIT_ENOMEM;
+ if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0)
goto cleanup;
- }
+
+ seen = git_strmap_alloc();
+ GITERR_CHECK_ALLOC(seen);
git_vector_foreach(&files, i, file) {
@@ -149,27 +159,23 @@ int git_attr_foreach(
git_vector_foreach(&rule->assigns, k, assign) {
/* skip if higher priority assignment was already seen */
- if (git_hashtable_lookup(seen, assign->name))
+ if (git_strmap_exists(seen, assign->name))
continue;
- error = git_hashtable_insert(seen, assign->name, assign);
- if (error != GIT_SUCCESS)
- goto cleanup;
+ git_strmap_insert(seen, assign->name, assign, error);
+ if (error >= 0)
+ error = callback(assign->name, assign->value, payload);
- error = callback(assign->name, assign->value, payload);
- if (error != GIT_SUCCESS)
+ if (error != 0)
goto cleanup;
}
}
}
cleanup:
- if (seen)
- git_hashtable_free(seen);
+ git_strmap_free(seen);
git_vector_free(&files);
-
- if (error != GIT_SUCCESS)
- (void)git__rethrow(error, "Could not get attributes for %s", pathname);
+ git_attr_path__free(&path);
return error;
}
@@ -182,124 +188,332 @@ int git_attr_add_macro(
{
int error;
git_attr_rule *macro = NULL;
+ git_pool *pool;
- if ((error = git_attr_cache__init(repo)) < GIT_SUCCESS)
- return error;
+ if (git_attr_cache__init(repo) < 0)
+ return -1;
macro = git__calloc(1, sizeof(git_attr_rule));
- if (!macro)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(macro);
- macro->match.pattern = git__strdup(name);
- if (!macro->match.pattern) {
- git__free(macro);
- return GIT_ENOMEM;
- }
+ pool = &git_repository_attr_cache(repo)->pool;
+
+ macro->match.pattern = git_pool_strdup(pool, name);
+ GITERR_CHECK_ALLOC(macro->match.pattern);
macro->match.length = strlen(macro->match.pattern);
macro->match.flags = GIT_ATTR_FNMATCH_MACRO;
- error = git_attr_assignment__parse(repo, &macro->assigns, &values);
+ error = git_attr_assignment__parse(repo, pool, &macro->assigns, &values);
- if (error == GIT_SUCCESS)
+ if (!error)
error = git_attr_cache__insert_macro(repo, macro);
- if (error < GIT_SUCCESS)
+ if (error < 0)
git_attr_rule__free(macro);
return error;
}
-int git_attr_cache__is_cached(git_repository *repo, const char *path)
+bool git_attr_cache__is_cached(
+ git_repository *repo, git_attr_file_source source, const char *path)
{
- const char *cache_key = path;
- if (repo && git__prefixcmp(cache_key, git_repository_workdir(repo)) == 0)
- cache_key += strlen(git_repository_workdir(repo));
- return (git_hashtable_lookup(repo->attrcache.files, cache_key) == NULL);
+ git_buf cache_key = GIT_BUF_INIT;
+ git_strmap *files = git_repository_attr_cache(repo)->files;
+ const char *workdir = git_repository_workdir(repo);
+ bool rval;
+
+ if (workdir && git__prefixcmp(path, workdir) == 0)
+ path += strlen(workdir);
+ if (git_buf_printf(&cache_key, "%d#%s", (int)source, path) < 0)
+ return false;
+
+ rval = git_strmap_exists(files, git_buf_cstr(&cache_key));
+
+ git_buf_free(&cache_key);
+
+ return rval;
+}
+
+static int load_attr_file(
+ const char **data,
+ git_attr_file_stat_sig *sig,
+ const char *filename)
+{
+ int error;
+ git_buf content = GIT_BUF_INIT;
+ struct stat st;
+
+ if (p_stat(filename, &st) < 0)
+ return GIT_ENOTFOUND;
+
+ if (sig != NULL &&
+ (git_time_t)st.st_mtime == sig->seconds &&
+ (git_off_t)st.st_size == sig->size &&
+ (unsigned int)st.st_ino == sig->ino)
+ return GIT_ENOTFOUND;
+
+ error = git_futils_readbuffer_updated(&content, filename, NULL, NULL);
+ if (error < 0)
+ return error;
+
+ if (sig != NULL) {
+ sig->seconds = (git_time_t)st.st_mtime;
+ sig->size = (git_off_t)st.st_size;
+ sig->ino = (unsigned int)st.st_ino;
+ }
+
+ *data = git_buf_detach(&content);
+
+ return 0;
+}
+
+static int load_attr_blob_from_index(
+ const char **content,
+ git_blob **blob,
+ git_repository *repo,
+ const git_oid *old_oid,
+ const char *relfile)
+{
+ int error;
+ git_index *index;
+ git_index_entry *entry;
+
+ if ((error = git_repository_index__weakptr(&index, repo)) < 0 ||
+ (error = git_index_find(index, relfile)) < 0)
+ return error;
+
+ entry = git_index_get(index, error);
+
+ if (old_oid && git_oid_cmp(old_oid, &entry->oid) == 0)
+ return GIT_ENOTFOUND;
+
+ if ((error = git_blob_lookup(blob, repo, &entry->oid)) < 0)
+ return error;
+
+ *content = git_blob_rawcontent(*blob);
+ return 0;
+}
+
+static int load_attr_from_cache(
+ git_attr_file **file,
+ git_attr_cache *cache,
+ git_attr_file_source source,
+ const char *relative_path)
+{
+ git_buf cache_key = GIT_BUF_INIT;
+ khiter_t cache_pos;
+
+ *file = NULL;
+
+ if (!cache || !cache->files)
+ return 0;
+
+ if (git_buf_printf(&cache_key, "%d#%s", (int)source, relative_path) < 0)
+ return -1;
+
+ cache_pos = git_strmap_lookup_index(cache->files, cache_key.ptr);
+
+ git_buf_free(&cache_key);
+
+ if (git_strmap_valid_index(cache->files, cache_pos))
+ *file = git_strmap_value_at(cache->files, cache_pos);
+
+ return 0;
+}
+
+int git_attr_cache__internal_file(
+ git_repository *repo,
+ const char *filename,
+ git_attr_file **file)
+{
+ int error = 0;
+ git_attr_cache *cache = git_repository_attr_cache(repo);
+ khiter_t cache_pos = git_strmap_lookup_index(cache->files, filename);
+
+ if (git_strmap_valid_index(cache->files, cache_pos)) {
+ *file = git_strmap_value_at(cache->files, cache_pos);
+ return 0;
+ }
+
+ if (git_attr_file__new(file, 0, filename, &cache->pool) < 0)
+ return -1;
+
+ git_strmap_insert(cache->files, (*file)->key + 2, *file, error);
+ if (error > 0)
+ error = 0;
+
+ return error;
}
-/* add git_attr_file to vector of files, loading if needed */
int git_attr_cache__push_file(
git_repository *repo,
- git_vector *stack,
- const char *base,
- const char *filename,
- int (*loader)(git_repository *, const char *, git_attr_file *))
+ const char *base,
+ const char *filename,
+ git_attr_file_source source,
+ git_attr_file_parser parse,
+ git_vector *stack)
{
- int error = GIT_SUCCESS;
- git_attr_cache *cache = &repo->attrcache;
+ int error = 0;
git_buf path = GIT_BUF_INIT;
+ const char *workdir = git_repository_workdir(repo);
+ const char *relfile, *content = NULL;
+ git_attr_cache *cache = git_repository_attr_cache(repo);
git_attr_file *file = NULL;
- int add_to_cache = 0;
- const char *cache_key;
+ git_blob *blob = NULL;
+ git_attr_file_stat_sig st;
- if (base != NULL) {
- if ((error = git_buf_joinpath(&path, base, filename)) < GIT_SUCCESS)
- goto cleanup;
+ assert(filename && stack);
+
+ /* join base and path as needed */
+ if (base != NULL && git_path_root(filename) < 0) {
+ if (git_buf_joinpath(&path, base, filename) < 0)
+ return -1;
filename = path.ptr;
}
- /* either get attr_file from cache or read from disk */
- cache_key = filename;
- if (repo && git__prefixcmp(cache_key, git_repository_workdir(repo)) == 0)
- cache_key += strlen(git_repository_workdir(repo));
-
- file = git_hashtable_lookup(cache->files, cache_key);
- if (file == NULL && git_path_exists(filename) == GIT_SUCCESS) {
- if ((error = git_attr_file__new(&file)) == GIT_SUCCESS) {
- if ((error = loader(repo, filename, file)) < GIT_SUCCESS) {
- git_attr_file__free(file);
- file = NULL;
- }
+ relfile = filename;
+ if (workdir && git__prefixcmp(relfile, workdir) == 0)
+ relfile += strlen(workdir);
+
+ /* check cache */
+ if (load_attr_from_cache(&file, cache, source, relfile) < 0)
+ return -1;
+
+ /* if not in cache, load data, parse, and cache */
+
+ if (source == GIT_ATTR_FILE_FROM_FILE) {
+ if (file)
+ memcpy(&st, &file->cache_data.st, sizeof(st));
+ else
+ memset(&st, 0, sizeof(st));
+
+ error = load_attr_file(&content, &st, filename);
+ } else {
+ error = load_attr_blob_from_index(&content, &blob,
+ repo, file ? &file->cache_data.oid : NULL, relfile);
+ }
+
+ if (error) {
+ /* not finding a file is not an error for this function */
+ if (error == GIT_ENOTFOUND) {
+ giterr_clear();
+ error = 0;
}
- add_to_cache = (error == GIT_SUCCESS);
+ goto finish;
}
- if (error == GIT_SUCCESS && file != NULL) {
- /* add file to vector, if we found it */
+ if (!file &&
+ (error = git_attr_file__new(&file, source, relfile, &cache->pool)) < 0)
+ goto finish;
+
+ if (parse && (error = parse(repo, content, file)) < 0)
+ goto finish;
+
+ git_strmap_insert(cache->files, file->key, file, error);
+ if (error > 0)
+ error = 0;
+
+ /* remember "cache buster" file signature */
+ if (blob)
+ git_oid_cpy(&file->cache_data.oid, git_object_id((git_object *)blob));
+ else
+ memcpy(&file->cache_data.st, &st, sizeof(st));
+
+finish:
+ /* push file onto vector if we found one*/
+ if (!error && file != NULL)
error = git_vector_insert(stack, file);
- /* add file to cache, if it is new */
- /* do this after above step b/c it is not critical */
- if (error == GIT_SUCCESS && add_to_cache && file->path != NULL)
- error = git_hashtable_insert(cache->files, file->path, file);
- }
+ if (error != 0)
+ git_attr_file__free(file);
+
+ if (blob)
+ git_blob_free(blob);
+ else
+ git__free((void *)content);
-cleanup:
git_buf_free(&path);
+
return error;
}
-#define push_attrs(R,S,B,F) \
- git_attr_cache__push_file((R),(S),(B),(F),git_attr_file__from_file)
+#define push_attr_file(R,S,B,F) \
+ git_attr_cache__push_file((R),(B),(F),GIT_ATTR_FILE_FROM_FILE,git_attr_file__parse_buffer,(S))
typedef struct {
git_repository *repo;
+ uint32_t flags;
+ const char *workdir;
+ git_index *index;
git_vector *files;
} attr_walk_up_info;
+int git_attr_cache__decide_sources(
+ uint32_t flags, bool has_wd, bool has_index, git_attr_file_source *srcs)
+{
+ int count = 0;
+
+ switch (flags & 0x03) {
+ case GIT_ATTR_CHECK_FILE_THEN_INDEX:
+ if (has_wd)
+ srcs[count++] = GIT_ATTR_FILE_FROM_FILE;
+ if (has_index)
+ srcs[count++] = GIT_ATTR_FILE_FROM_INDEX;
+ break;
+ case GIT_ATTR_CHECK_INDEX_THEN_FILE:
+ if (has_index)
+ srcs[count++] = GIT_ATTR_FILE_FROM_INDEX;
+ if (has_wd)
+ srcs[count++] = GIT_ATTR_FILE_FROM_FILE;
+ break;
+ case GIT_ATTR_CHECK_INDEX_ONLY:
+ if (has_index)
+ srcs[count++] = GIT_ATTR_FILE_FROM_INDEX;
+ break;
+ }
+
+ return count;
+}
+
static int push_one_attr(void *ref, git_buf *path)
{
+ int error = 0, n_src, i;
attr_walk_up_info *info = (attr_walk_up_info *)ref;
- return push_attrs(info->repo, info->files, path->ptr, GIT_ATTR_FILE);
+ git_attr_file_source src[2];
+
+ n_src = git_attr_cache__decide_sources(
+ info->flags, info->workdir != NULL, info->index != NULL, src);
+
+ for (i = 0; !error && i < n_src; ++i)
+ error = git_attr_cache__push_file(
+ info->repo, path->ptr, GIT_ATTR_FILE, src[i],
+ git_attr_file__parse_buffer, info->files);
+
+ return error;
}
static int collect_attr_files(
- git_repository *repo, const char *path, git_vector *files)
+ git_repository *repo,
+ uint32_t flags,
+ const char *path,
+ git_vector *files)
{
- int error = GIT_SUCCESS;
+ int error;
git_buf dir = GIT_BUF_INIT;
- git_config *cfg;
const char *workdir = git_repository_workdir(repo);
attr_walk_up_info info;
- if ((error = git_attr_cache__init(repo)) < GIT_SUCCESS)
- goto cleanup;
-
- if ((error = git_vector_init(files, 4, NULL)) < GIT_SUCCESS)
- goto cleanup;
+ if (git_attr_cache__init(repo) < 0 ||
+ git_vector_init(files, 4, NULL) < 0)
+ return -1;
- if ((error = git_path_find_dir(&dir, path, workdir)) < GIT_SUCCESS)
+ /* Resolve path in a non-bare repo */
+ if (workdir != NULL)
+ error = git_path_find_dir(&dir, path, workdir);
+ else
+ error = git_path_dirname_r(&dir, path);
+ if (error < 0)
goto cleanup;
/* in precendence order highest to lowest:
@@ -309,38 +523,40 @@ static int collect_attr_files(
* - $GIT_PREFIX/etc/gitattributes
*/
- error = push_attrs(repo, files, repo->path_repository, GIT_ATTR_FILE_INREPO);
- if (error < GIT_SUCCESS)
+ error = push_attr_file(
+ repo, files, git_repository_path(repo), GIT_ATTR_FILE_INREPO);
+ if (error < 0)
goto cleanup;
- info.repo = repo;
+ info.repo = repo;
+ info.flags = flags;
+ info.workdir = workdir;
+ if (git_repository_index__weakptr(&info.index, repo) < 0)
+ giterr_clear(); /* no error even if there is no index */
info.files = files;
+
error = git_path_walk_up(&dir, workdir, push_one_attr, &info);
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
- if ((error = git_repository_config(&cfg, repo)) == GIT_SUCCESS) {
- const char *core_attribs = NULL;
- git_config_get_string(cfg, GIT_ATTR_CONFIG, &core_attribs);
- git_clearerror(); /* don't care if attributesfile is not set */
- if (core_attribs)
- error = push_attrs(repo, files, NULL, core_attribs);
- git_config_free(cfg);
+ if (git_repository_attr_cache(repo)->cfg_attr_file != NULL) {
+ error = push_attr_file(
+ repo, files, NULL, git_repository_attr_cache(repo)->cfg_attr_file);
+ if (error < 0)
+ goto cleanup;
}
- if (error == GIT_SUCCESS) {
+ if ((flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) {
error = git_futils_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM);
- if (error == GIT_SUCCESS)
- error = push_attrs(repo, files, NULL, dir.ptr);
+ if (!error)
+ error = push_attr_file(repo, files, NULL, dir.ptr);
else if (error == GIT_ENOTFOUND)
- error = GIT_SUCCESS;
+ error = 0;
}
cleanup:
- if (error < GIT_SUCCESS) {
- git__rethrow(error, "Could not get attributes for '%s'", path);
+ if (error < 0)
git_vector_free(files);
- }
git_buf_free(&dir);
return error;
@@ -349,70 +565,108 @@ static int collect_attr_files(
int git_attr_cache__init(git_repository *repo)
{
- int error = GIT_SUCCESS;
- git_attr_cache *cache = &repo->attrcache;
+ int ret;
+ git_attr_cache *cache = git_repository_attr_cache(repo);
+ git_config *cfg;
if (cache->initialized)
- return GIT_SUCCESS;
+ return 0;
+
+ /* cache config settings for attributes and ignores */
+ if (git_repository_config__weakptr(&cfg, repo) < 0)
+ return -1;
+
+ ret = git_config_get_string(&cache->cfg_attr_file, cfg, GIT_ATTR_CONFIG);
+ if (ret < 0 && ret != GIT_ENOTFOUND)
+ return ret;
+
+ ret = git_config_get_string(&cache->cfg_excl_file, cfg, GIT_IGNORE_CONFIG);
+ if (ret < 0 && ret != GIT_ENOTFOUND)
+ return ret;
+
+ giterr_clear();
+ /* allocate hashtable for attribute and ignore file contents */
if (cache->files == NULL) {
- cache->files = git_hashtable_alloc(
- 8, git_hash__strhash_cb, git_hash__strcmp_cb);
- if (!cache->files)
- return git__throw(GIT_ENOMEM, "Could not initialize attribute cache");
+ cache->files = git_strmap_alloc();
+ GITERR_CHECK_ALLOC(cache->files);
}
+ /* allocate hashtable for attribute macros */
if (cache->macros == NULL) {
- cache->macros = git_hashtable_alloc(
- 8, git_hash__strhash_cb, git_hash__strcmp_cb);
- if (!cache->macros)
- return git__throw(GIT_ENOMEM, "Could not initialize attribute cache");
+ cache->macros = git_strmap_alloc();
+ GITERR_CHECK_ALLOC(cache->macros);
}
+ /* allocate string pool */
+ if (git_pool_init(&cache->pool, 1, 0) < 0)
+ return -1;
+
cache->initialized = 1;
/* insert default macros */
- error = git_attr_add_macro(repo, "binary", "-diff -crlf");
-
- return error;
+ return git_attr_add_macro(repo, "binary", "-diff -crlf -text");
}
void git_attr_cache_flush(
git_repository *repo)
{
+ git_attr_cache *cache;
+
if (!repo)
return;
- if (repo->attrcache.files) {
- const void *GIT_UNUSED(name);
+ cache = git_repository_attr_cache(repo);
+
+ if (cache->files != NULL) {
git_attr_file *file;
- GIT_HASHTABLE_FOREACH(repo->attrcache.files, name, file,
- git_attr_file__free(file));
+ git_strmap_foreach_value(cache->files, file, {
+ git_attr_file__free(file);
+ });
- git_hashtable_free(repo->attrcache.files);
- repo->attrcache.files = NULL;
+ git_strmap_free(cache->files);
}
- if (repo->attrcache.macros) {
- const void *GIT_UNUSED(name);
+ if (cache->macros != NULL) {
git_attr_rule *rule;
- GIT_HASHTABLE_FOREACH(repo->attrcache.macros, name, rule,
- git_attr_rule__free(rule));
+ git_strmap_foreach_value(cache->macros, rule, {
+ git_attr_rule__free(rule);
+ });
- git_hashtable_free(repo->attrcache.macros);
- repo->attrcache.macros = NULL;
+ git_strmap_free(cache->macros);
}
- repo->attrcache.initialized = 0;
+ git_pool_clear(&cache->pool);
+
+ cache->initialized = 0;
}
int git_attr_cache__insert_macro(git_repository *repo, git_attr_rule *macro)
{
+ git_strmap *macros = git_repository_attr_cache(repo)->macros;
+ int error;
+
+ /* TODO: generate warning log if (macro->assigns.length == 0) */
if (macro->assigns.length == 0)
- return git__throw(GIT_EMISSINGOBJDATA, "git attribute macro with no values");
+ return 0;
- return git_hashtable_insert(
- repo->attrcache.macros, macro->match.pattern, macro);
+ git_strmap_insert(macros, macro->match.pattern, macro, error);
+ return (error < 0) ? -1 : 0;
}
+
+git_attr_rule *git_attr_cache__lookup_macro(
+ git_repository *repo, const char *name)
+{
+ git_strmap *macros = git_repository_attr_cache(repo)->macros;
+ khiter_t pos;
+
+ pos = git_strmap_lookup_index(macros, name);
+
+ if (!git_strmap_valid_index(macros, pos))
+ return NULL;
+
+ return (git_attr_rule *)git_strmap_value_at(macros, pos);
+}
+
diff --git a/src/attr.h b/src/attr.h
index ea27259f1..a35b1160f 100644
--- a/src/attr.h
+++ b/src/attr.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -8,26 +8,49 @@
#define INCLUDE_attr_h__
#include "attr_file.h"
+#include "strmap.h"
+
+#define GIT_ATTR_CONFIG "core.attributesfile"
+#define GIT_IGNORE_CONFIG "core.excludesfile"
typedef struct {
int initialized;
- git_hashtable *files; /* hash path to git_attr_file of rules */
- git_hashtable *macros; /* hash name to vector<git_attr_assignment> */
+ git_pool pool;
+ git_strmap *files; /* hash path to git_attr_file of rules */
+ git_strmap *macros; /* hash name to vector<git_attr_assignment> */
+ const char *cfg_attr_file; /* cached value of core.attributesfile */
+ const char *cfg_excl_file; /* cached value of core.excludesfile */
} git_attr_cache;
+typedef int (*git_attr_file_parser)(
+ git_repository *, const char *, git_attr_file *);
+
extern int git_attr_cache__init(git_repository *repo);
extern int git_attr_cache__insert_macro(
git_repository *repo, git_attr_rule *macro);
+extern git_attr_rule *git_attr_cache__lookup_macro(
+ git_repository *repo, const char *name);
+
extern int git_attr_cache__push_file(
git_repository *repo,
- git_vector *stack,
- const char *base,
- const char *filename,
- int (*loader)(git_repository *, const char *, git_attr_file *));
+ const char *base,
+ const char *filename,
+ git_attr_file_source source,
+ git_attr_file_parser parse,
+ git_vector *stack);
+
+extern int git_attr_cache__internal_file(
+ git_repository *repo,
+ const char *key,
+ git_attr_file **file_ptr);
+
+/* returns true if path is in cache */
+extern bool git_attr_cache__is_cached(
+ git_repository *repo, git_attr_file_source source, const char *path);
-/* returns GIT_SUCCESS if path is in cache */
-extern int git_attr_cache__is_cached(git_repository *repo, const char *path);
+extern int git_attr_cache__decide_sources(
+ uint32_t flags, bool has_wd, bool has_index, git_attr_file_source *srcs);
#endif
diff --git a/src/attr_file.c b/src/attr_file.c
index 7911381ea..5030ad5de 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -1,62 +1,65 @@
#include "common.h"
#include "repository.h"
#include "filebuf.h"
+#include "git2/blob.h"
+#include "git2/tree.h"
#include <ctype.h>
const char *git_attr__true = "[internal]__TRUE__";
const char *git_attr__false = "[internal]__FALSE__";
+const char *git_attr__unset = "[internal]__UNSET__";
static int sort_by_hash_and_name(const void *a_raw, const void *b_raw);
static void git_attr_rule__clear(git_attr_rule *rule);
-int git_attr_file__new(git_attr_file **attrs_ptr)
+int git_attr_file__new(
+ git_attr_file **attrs_ptr,
+ git_attr_file_source from,
+ const char *path,
+ git_pool *pool)
{
- int error;
git_attr_file *attrs = NULL;
attrs = git__calloc(1, sizeof(git_attr_file));
- if (attrs == NULL)
- error = GIT_ENOMEM;
- else
- error = git_vector_init(&attrs->rules, 4, NULL);
+ GITERR_CHECK_ALLOC(attrs);
- if (error != GIT_SUCCESS) {
- git__rethrow(error, "Could not allocate attribute storage");
- git__free(attrs);
- attrs = NULL;
+ if (pool)
+ attrs->pool = pool;
+ else {
+ attrs->pool = git__calloc(1, sizeof(git_pool));
+ if (!attrs->pool || git_pool_init(attrs->pool, 1, 0) < 0)
+ goto fail;
+ attrs->pool_is_allocated = true;
}
- *attrs_ptr = attrs;
+ if (path) {
+ size_t len = strlen(path);
- return error;
-}
+ attrs->key = git_pool_malloc(attrs->pool, (uint32_t)len + 3);
+ GITERR_CHECK_ALLOC(attrs->key);
-int git_attr_file__set_path(
- git_repository *repo, const char *path, git_attr_file *file)
-{
- if (file->path != NULL) {
- git__free(file->path);
- file->path = NULL;
+ attrs->key[0] = '0' + from;
+ attrs->key[1] = '#';
+ memcpy(&attrs->key[2], path, len);
+ attrs->key[len + 2] = '\0';
}
- if (repo == NULL)
- file->path = git__strdup(path);
- else {
- const char *workdir = git_repository_workdir(repo);
+ if (git_vector_init(&attrs->rules, 4, NULL) < 0)
+ goto fail;
- if (workdir && git__prefixcmp(path, workdir) == 0)
- file->path = git__strdup(path + strlen(workdir));
- else
- file->path = git__strdup(path);
- }
+ *attrs_ptr = attrs;
+ return 0;
- return (file->path == NULL) ? GIT_ENOMEM : GIT_SUCCESS;
+fail:
+ git_attr_file__free(attrs);
+ attrs_ptr = NULL;
+ return -1;
}
-int git_attr_file__from_buffer(
+int git_attr_file__parse_buffer(
git_repository *repo, const char *buffer, git_attr_file *attrs)
{
- int error = GIT_SUCCESS;
+ int error = 0;
const char *scan = NULL;
char *context = NULL;
git_attr_rule *rule = NULL;
@@ -65,22 +68,24 @@ int git_attr_file__from_buffer(
scan = buffer;
- if (attrs->path && git__suffixcmp(attrs->path, GIT_ATTR_FILE) == 0) {
- context = git__strndup(attrs->path,
- strlen(attrs->path) - strlen(GIT_ATTR_FILE));
- if (!context) error = GIT_ENOMEM;
+ /* if subdir file path, convert context for file paths */
+ if (attrs->key && git__suffixcmp(attrs->key, "/" GIT_ATTR_FILE) == 0) {
+ context = attrs->key + 2;
+ context[strlen(context) - strlen(GIT_ATTR_FILE)] = '\0';
}
- while (error == GIT_SUCCESS && *scan) {
+ while (!error && *scan) {
/* allocate rule if needed */
if (!rule && !(rule = git__calloc(1, sizeof(git_attr_rule)))) {
- error = GIT_ENOMEM;
+ error = -1;
break;
}
/* parse the next "pattern attr attr attr" line */
- if (!(error = git_attr_fnmatch__parse(&rule->match, context, &scan)) &&
- !(error = git_attr_assignment__parse(repo, &rule->assigns, &scan)))
+ if (!(error = git_attr_fnmatch__parse(
+ &rule->match, attrs->pool, context, &scan)) &&
+ !(error = git_attr_assignment__parse(
+ repo, attrs->pool, &rule->assigns, &scan)))
{
if (rule->match.flags & GIT_ATTR_FNMATCH_MACRO)
/* should generate error/warning if this is coming from any
@@ -92,39 +97,44 @@ int git_attr_file__from_buffer(
}
/* if the rule wasn't a pattern, on to the next */
- if (error != GIT_SUCCESS) {
+ if (error < 0) {
git_attr_rule__clear(rule); /* reset rule contents */
if (error == GIT_ENOTFOUND)
- error = GIT_SUCCESS;
+ error = 0;
} else {
rule = NULL; /* vector now "owns" the rule */
}
}
git_attr_rule__free(rule);
- git__free(context);
+
+ /* restore file path used for context */
+ if (context)
+ context[strlen(context)] = '.'; /* first char of GIT_ATTR_FILE */
return error;
}
-int git_attr_file__from_file(
- git_repository *repo, const char *path, git_attr_file *file)
+int git_attr_file__new_and_load(
+ git_attr_file **attrs_ptr,
+ const char *path)
{
- int error = GIT_SUCCESS;
- git_fbuffer fbuf = GIT_FBUFFER_INIT;
+ int error;
+ git_buf content = GIT_BUF_INIT;
- assert(path && file);
+ if ((error = git_attr_file__new(attrs_ptr, 0, path, NULL)) < 0)
+ return error;
- if (file->path == NULL)
- error = git_attr_file__set_path(repo, path, file);
+ if (!(error = git_futils_readbuffer(&content, path)))
+ error = git_attr_file__parse_buffer(
+ NULL, git_buf_cstr(&content), *attrs_ptr);
- if (error == GIT_SUCCESS &&
- (error = git_futils_readbuffer(&fbuf, path)) == GIT_SUCCESS)
- error = git_attr_file__from_buffer(repo, fbuf.data, file);
+ git_buf_free(&content);
- git_futils_freebuffer(&fbuf);
- if (error != GIT_SUCCESS)
- git__rethrow(error, "Could not open attribute file '%s'", path);
+ if (error) {
+ git_attr_file__free(*attrs_ptr);
+ *attrs_ptr = NULL;
+ }
return error;
}
@@ -142,15 +152,18 @@ void git_attr_file__free(git_attr_file *file)
git_vector_free(&file->rules);
- git__free(file->path);
- file->path = NULL;
+ if (file->pool_is_allocated) {
+ git_pool_clear(file->pool);
+ git__free(file->pool);
+ }
+ file->pool = NULL;
git__free(file);
}
-unsigned long git_attr_file__name_hash(const char *name)
+uint32_t git_attr_file__name_hash(const char *name)
{
- unsigned long h = 5381;
+ uint32_t h = 5381;
int c;
assert(name);
while ((c = (int)*name++) != 0)
@@ -176,7 +189,6 @@ int git_attr_file__lookup_one(
git_attr_file__foreach_matching_rule(file, path, i, rule) {
int pos = git_vector_bsearch(&rule->assigns, &name);
- git_clearerror(); /* okay if search failed */
if (pos >= 0) {
*value = ((git_attr_assignment *)
@@ -185,37 +197,37 @@ int git_attr_file__lookup_one(
}
}
- return GIT_SUCCESS;
+ return 0;
}
-int git_attr_fnmatch__match(
+bool git_attr_fnmatch__match(
git_attr_fnmatch *match,
const git_attr_path *path)
{
- int matched = FNM_NOMATCH;
+ int fnm;
if (match->flags & GIT_ATTR_FNMATCH_DIRECTORY && !path->is_dir)
- return matched;
+ return false;
if (match->flags & GIT_ATTR_FNMATCH_FULLPATH)
- matched = p_fnmatch(match->pattern, path->path, FNM_PATHNAME);
+ fnm = p_fnmatch(match->pattern, path->path, FNM_PATHNAME);
else if (path->is_dir)
- matched = p_fnmatch(match->pattern, path->basename, FNM_LEADING_DIR);
+ fnm = p_fnmatch(match->pattern, path->basename, FNM_LEADING_DIR);
else
- matched = p_fnmatch(match->pattern, path->basename, 0);
+ fnm = p_fnmatch(match->pattern, path->basename, 0);
- return matched;
+ return (fnm == FNM_NOMATCH) ? false : true;
}
-int git_attr_rule__match(
+bool git_attr_rule__match(
git_attr_rule *rule,
const git_attr_path *path)
{
- int matched = git_attr_fnmatch__match(&rule->match, path);
+ bool matched = git_attr_fnmatch__match(&rule->match, path);
if (rule->match.flags & GIT_ATTR_FNMATCH_NEGATIVE)
- matched = (matched == GIT_SUCCESS) ? FNM_NOMATCH : GIT_SUCCESS;
+ matched = !matched;
return matched;
}
@@ -230,7 +242,6 @@ git_attr_assignment *git_attr_rule__lookup_assignment(
key.name_hash = git_attr_file__name_hash(name);
pos = git_vector_bsearch(&rule->assigns, &key);
- git_clearerror(); /* okay if search failed */
return (pos >= 0) ? git_vector_get(&rule->assigns, pos) : NULL;
}
@@ -238,25 +249,48 @@ git_attr_assignment *git_attr_rule__lookup_assignment(
int git_attr_path__init(
git_attr_path *info, const char *path, const char *base)
{
- assert(info && path);
- info->path = path;
- info->basename = strrchr(path, '/');
+ /* build full path as best we can */
+ git_buf_init(&info->full, 0);
+
+ if (base != NULL && git_path_root(path) < 0) {
+ if (git_buf_joinpath(&info->full, base, path) < 0)
+ return -1;
+ info->path = info->full.ptr + strlen(base);
+ } else {
+ if (git_buf_sets(&info->full, path) < 0)
+ return -1;
+ info->path = info->full.ptr;
+ }
+
+ /* remove trailing slashes */
+ while (info->full.size > 0) {
+ if (info->full.ptr[info->full.size - 1] != '/')
+ break;
+ info->full.size--;
+ }
+ info->full.ptr[info->full.size] = '\0';
+
+ /* skip leading slashes in path */
+ while (*info->path == '/')
+ info->path++;
+
+ /* find trailing basename component */
+ info->basename = strrchr(info->path, '/');
if (info->basename)
info->basename++;
if (!info->basename || !*info->basename)
- info->basename = path;
+ info->basename = info->path;
- if (base != NULL && git_path_root(path) < 0) {
- git_buf full_path = GIT_BUF_INIT;
- int error = git_buf_joinpath(&full_path, base, path);
- if (error == GIT_SUCCESS)
- info->is_dir = (git_path_isdir(full_path.ptr) == GIT_SUCCESS);
- git_buf_free(&full_path);
- return error;
- }
- info->is_dir = (git_path_isdir(path) == GIT_SUCCESS);
+ info->is_dir = (int)git_path_isdir(info->full.ptr);
- return GIT_SUCCESS;
+ return 0;
+}
+
+void git_attr_path__free(git_attr_path *info)
+{
+ git_buf_free(&info->full);
+ info->path = NULL;
+ info->basename = NULL;
}
@@ -293,12 +327,13 @@ int git_attr_path__init(
*/
/*
- * This will return GIT_SUCCESS if the spec was filled out,
+ * This will return 0 if the spec was filled out,
* GIT_ENOTFOUND if the fnmatch does not require matching, or
* another error code there was an actual problem.
*/
int git_attr_fnmatch__parse(
git_attr_fnmatch *spec,
+ git_pool *pool,
const char *source,
const char **base)
{
@@ -309,7 +344,7 @@ int git_attr_fnmatch__parse(
pattern = *base;
- while (isspace(*pattern)) pattern++;
+ while (git__isspace(*pattern)) pattern++;
if (!*pattern || *pattern == '#') {
*base = git__next_line(pattern);
return GIT_ENOTFOUND;
@@ -333,13 +368,19 @@ int git_attr_fnmatch__parse(
slash_count = 0;
for (scan = pattern; *scan != '\0'; ++scan) {
/* scan until (non-escaped) white space */
- if (isspace(*scan) && *(scan - 1) != '\\')
+ if (git__isspace(*scan) && *(scan - 1) != '\\')
break;
if (*scan == '/') {
spec->flags = spec->flags | GIT_ATTR_FNMATCH_FULLPATH;
slash_count++;
+ if (pattern == scan)
+ pattern++;
}
+ /* remember if we see an unescaped wildcard in pattern */
+ else if (git__iswildcard(*scan) &&
+ (scan == pattern || (*(scan - 1) != '\\')))
+ spec->flags = spec->flags | GIT_ATTR_FNMATCH_HASWILD;
}
*base = scan;
@@ -360,7 +401,8 @@ int git_attr_fnmatch__parse(
/* given an unrooted fullpath match from a file inside a repo,
* prefix the pattern with the relative directory of the source file
*/
- spec->pattern = git__malloc(sourcelen + spec->length + 1);
+ spec->pattern = git_pool_malloc(
+ pool, (uint32_t)(sourcelen + spec->length + 1));
if (spec->pattern) {
memcpy(spec->pattern, source, sourcelen);
memcpy(spec->pattern + sourcelen, pattern, spec->length);
@@ -368,12 +410,12 @@ int git_attr_fnmatch__parse(
spec->pattern[spec->length] = '\0';
}
} else {
- spec->pattern = git__strndup(pattern, spec->length);
+ spec->pattern = git_pool_strndup(pool, pattern, spec->length);
}
if (!spec->pattern) {
*base = git__next_line(pattern);
- return GIT_ENOMEM;
+ return -1;
} else {
/* strip '\' that might have be used for internal whitespace */
char *to = spec->pattern;
@@ -389,7 +431,7 @@ int git_attr_fnmatch__parse(
}
}
- return GIT_SUCCESS;
+ return 0;
}
static int sort_by_hash_and_name(const void *a_raw, const void *b_raw)
@@ -407,14 +449,11 @@ static int sort_by_hash_and_name(const void *a_raw, const void *b_raw)
static void git_attr_assignment__free(git_attr_assignment *assign)
{
- git__free(assign->name);
+ /* name and value are stored in a git_pool associated with the
+ * git_attr_file, so they do not need to be freed here
+ */
assign->name = NULL;
-
- if (assign->is_allocated) {
- git__free((void *)assign->value);
- assign->value = NULL;
- }
-
+ assign->value = NULL;
git__free(assign);
}
@@ -430,10 +469,11 @@ static int merge_assignments(void **old_raw, void *new_raw)
int git_attr_assignment__parse(
git_repository *repo,
+ git_pool *pool,
git_vector *assigns,
const char **base)
{
- int error = GIT_SUCCESS;
+ int error;
const char *scan = *base;
git_attr_assignment *assign = NULL;
@@ -441,39 +481,35 @@ int git_attr_assignment__parse(
assigns->_cmp = sort_by_hash_and_name;
- while (*scan && *scan != '\n' && error == GIT_SUCCESS) {
+ while (*scan && *scan != '\n') {
const char *name_start, *value_start;
/* skip leading blanks */
- while (isspace(*scan) && *scan != '\n') scan++;
+ while (git__isspace(*scan) && *scan != '\n') scan++;
/* allocate assign if needed */
if (!assign) {
assign = git__calloc(1, sizeof(git_attr_assignment));
- if (!assign) {
- error = GIT_ENOMEM;
- break;
- }
+ GITERR_CHECK_ALLOC(assign);
GIT_REFCOUNT_INC(assign);
}
assign->name_hash = 5381;
- assign->value = GIT_ATTR_TRUE;
- assign->is_allocated = 0;
+ assign->value = git_attr__true;
/* look for magic name prefixes */
if (*scan == '-') {
- assign->value = GIT_ATTR_FALSE;
+ assign->value = git_attr__false;
scan++;
} else if (*scan == '!') {
- assign->value = NULL; /* explicit unspecified state */
+ assign->value = git_attr__unset; /* explicit unspecified state */
scan++;
} else if (*scan == '#') /* comment rest of line */
break;
/* find the name */
name_start = scan;
- while (*scan && !isspace(*scan) && *scan != '=') {
+ while (*scan && !git__isspace(*scan) && *scan != '=') {
assign->name_hash =
((assign->name_hash << 5) + assign->name_hash) + *scan;
scan++;
@@ -482,37 +518,29 @@ int git_attr_assignment__parse(
/* must have found lone prefix (" - ") or leading = ("=foo")
* or end of buffer -- advance until whitespace and continue
*/
- while (*scan && !isspace(*scan)) scan++;
+ while (*scan && !git__isspace(*scan)) scan++;
continue;
}
/* allocate permanent storage for name */
- assign->name = git__strndup(name_start, scan - name_start);
- if (!assign->name) {
- error = GIT_ENOMEM;
- break;
- }
+ assign->name = git_pool_strndup(pool, name_start, scan - name_start);
+ GITERR_CHECK_ALLOC(assign->name);
/* if there is an equals sign, find the value */
if (*scan == '=') {
- for (value_start = ++scan; *scan && !isspace(*scan); ++scan);
+ for (value_start = ++scan; *scan && !git__isspace(*scan); ++scan);
/* if we found a value, allocate permanent storage for it */
if (scan > value_start) {
- assign->value = git__strndup(value_start, scan - value_start);
- if (!assign->value) {
- error = GIT_ENOMEM;
- break;
- } else {
- assign->is_allocated = 1;
- }
+ assign->value = git_pool_strndup(pool, value_start, scan - value_start);
+ GITERR_CHECK_ALLOC(assign->value);
}
}
/* expand macros (if given a repo with a macro cache) */
- if (repo != NULL && assign->value == GIT_ATTR_TRUE) {
+ if (repo != NULL && assign->value == git_attr__true) {
git_attr_rule *macro =
- git_hashtable_lookup(repo->attrcache.macros, assign->name);
+ git_attr_cache__lookup_macro(repo, assign->name);
if (macro != NULL) {
unsigned int i;
@@ -523,35 +551,27 @@ int git_attr_assignment__parse(
error = git_vector_insert_sorted(
assigns, massign, &merge_assignments);
-
- if (error == GIT_EEXISTS)
- error = GIT_SUCCESS;
- else if (error != GIT_SUCCESS)
- break;
+ if (error < 0 && error != GIT_EEXISTS)
+ return error;
}
}
}
/* insert allocated assign into vector */
error = git_vector_insert_sorted(assigns, assign, &merge_assignments);
- if (error == GIT_EEXISTS)
- error = GIT_SUCCESS;
- else if (error < GIT_SUCCESS)
- break;
+ if (error < 0 && error != GIT_EEXISTS)
+ return error;
/* clear assign since it is now "owned" by the vector */
assign = NULL;
}
- if (!assigns->length)
- error = git__throw(GIT_ENOTFOUND, "No attribute assignments found for rule");
-
if (assign != NULL)
git_attr_assignment__free(assign);
*base = git__next_line(scan);
- return error;
+ return (assigns->length == 0) ? GIT_ENOTFOUND : 0;
}
static void git_attr_rule__clear(git_attr_rule *rule)
@@ -568,7 +588,7 @@ static void git_attr_rule__clear(git_attr_rule *rule)
git_vector_free(&rule->assigns);
}
- git__free(rule->match.pattern);
+ /* match.pattern is stored in a git_pool, so no need to free */
rule->match.pattern = NULL;
rule->match.length = 0;
}
diff --git a/src/attr_file.h b/src/attr_file.h
index dcb66c577..3718f4bda 100644
--- a/src/attr_file.h
+++ b/src/attr_file.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -9,18 +9,19 @@
#include "git2/attr.h"
#include "vector.h"
-#include "hashtable.h"
+#include "pool.h"
+#include "buffer.h"
#define GIT_ATTR_FILE ".gitattributes"
#define GIT_ATTR_FILE_INREPO "info/attributes"
#define GIT_ATTR_FILE_SYSTEM "gitattributes"
-#define GIT_ATTR_CONFIG "core.attributesfile"
#define GIT_ATTR_FNMATCH_NEGATIVE (1U << 0)
#define GIT_ATTR_FNMATCH_DIRECTORY (1U << 1)
#define GIT_ATTR_FNMATCH_FULLPATH (1U << 2)
#define GIT_ATTR_FNMATCH_MACRO (1U << 3)
#define GIT_ATTR_FNMATCH_IGNORE (1U << 4)
+#define GIT_ATTR_FNMATCH_HASWILD (1U << 5)
typedef struct {
char *pattern;
@@ -36,42 +37,59 @@ typedef struct {
typedef struct {
git_refcount unused;
const char *name;
- unsigned long name_hash;
+ uint32_t name_hash;
} git_attr_name;
typedef struct {
git_refcount rc; /* for macros */
char *name;
- unsigned long name_hash;
+ uint32_t name_hash;
const char *value;
- int is_allocated;
} git_attr_assignment;
typedef struct {
- char *path; /* cache the path this was loaded from */
+ git_time_t seconds;
+ git_off_t size;
+ unsigned int ino;
+} git_attr_file_stat_sig;
+
+typedef struct {
+ char *key; /* cache "source#path" this was loaded from */
git_vector rules; /* vector of <rule*> or <fnmatch*> */
+ git_pool *pool;
+ bool pool_is_allocated;
+ union {
+ git_oid oid;
+ git_attr_file_stat_sig st;
+ } cache_data;
} git_attr_file;
typedef struct {
+ git_buf full;
const char *path;
const char *basename;
- int is_dir;
+ int is_dir;
} git_attr_path;
+typedef enum {
+ GIT_ATTR_FILE_FROM_FILE = 0,
+ GIT_ATTR_FILE_FROM_INDEX = 1
+} git_attr_file_source;
+
/*
* git_attr_file API
*/
-extern int git_attr_file__new(git_attr_file **attrs_ptr);
+extern int git_attr_file__new(
+ git_attr_file **attrs_ptr, git_attr_file_source src, const char *path, git_pool *pool);
+
+extern int git_attr_file__new_and_load(
+ git_attr_file **attrs_ptr, const char *path);
+
extern void git_attr_file__free(git_attr_file *file);
-extern int git_attr_file__from_buffer(
+extern int git_attr_file__parse_buffer(
git_repository *repo, const char *buf, git_attr_file *file);
-extern int git_attr_file__from_file(
- git_repository *repo, const char *path, git_attr_file *file);
-
-extern int git_attr_file__set_path(
- git_repository *repo, const char *path, git_attr_file *file);
extern int git_attr_file__lookup_one(
git_attr_file *file,
@@ -82,9 +100,9 @@ extern int git_attr_file__lookup_one(
/* loop over rules in file from bottom to top */
#define git_attr_file__foreach_matching_rule(file, path, iter, rule) \
git_vector_rforeach(&(file)->rules, (iter), (rule)) \
- if (git_attr_rule__match((rule), (path)) == GIT_SUCCESS)
+ if (git_attr_rule__match((rule), (path)))
-extern unsigned long git_attr_file__name_hash(const char *name);
+extern uint32_t git_attr_file__name_hash(const char *name);
/*
@@ -93,16 +111,17 @@ extern unsigned long git_attr_file__name_hash(const char *name);
extern int git_attr_fnmatch__parse(
git_attr_fnmatch *spec,
+ git_pool *pool,
const char *source,
const char **base);
-extern int git_attr_fnmatch__match(
+extern bool git_attr_fnmatch__match(
git_attr_fnmatch *rule,
const git_attr_path *path);
extern void git_attr_rule__free(git_attr_rule *rule);
-extern int git_attr_rule__match(
+extern bool git_attr_rule__match(
git_attr_rule *rule,
const git_attr_path *path);
@@ -112,8 +131,11 @@ extern git_attr_assignment *git_attr_rule__lookup_assignment(
extern int git_attr_path__init(
git_attr_path *info, const char *path, const char *base);
+extern void git_attr_path__free(git_attr_path *info);
+
extern int git_attr_assignment__parse(
git_repository *repo, /* needed to expand macros */
+ git_pool *pool,
git_vector *assigns,
const char **scan);
diff --git a/src/blob.c b/src/blob.c
index 7497ba7bf..e25944b91 100644
--- a/src/blob.c
+++ b/src/blob.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -11,6 +11,7 @@
#include "common.h"
#include "blob.h"
+#include "filter.h"
const void *git_blob_rawcontent(git_blob *blob)
{
@@ -24,6 +25,12 @@ size_t git_blob_rawsize(git_blob *blob)
return blob->odb_object->raw.len;
}
+int git_blob__getbuf(git_buf *buffer, git_blob *blob)
+{
+ return git_buf_set(
+ buffer, blob->odb_object->raw.data, blob->odb_object->raw.len);
+}
+
void git_blob__free(git_blob *blob)
{
git_odb_object_free(blob->odb_object);
@@ -35,7 +42,7 @@ int git_blob__parse(git_blob *blob, git_odb_object *odb_obj)
assert(blob);
git_cached_obj_incref((git_cached_obj *)odb_obj);
blob->odb_object = odb_obj;
- return GIT_SUCCESS;
+ return 0;
}
int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *buffer, size_t len)
@@ -44,99 +51,189 @@ int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *b
git_odb *odb;
git_odb_stream *stream;
- error = git_repository_odb__weakptr(&odb, repo);
- if (error < GIT_SUCCESS)
+ if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
+ (error = git_odb_open_wstream(&stream, odb, len, GIT_OBJ_BLOB)) < 0)
return error;
- if ((error = git_odb_open_wstream(&stream, odb, len, GIT_OBJ_BLOB)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to create blob");
+ if ((error = stream->write(stream, buffer, len)) == 0)
+ error = stream->finalize_write(oid, stream);
- if ((error = stream->write(stream, buffer, len)) < GIT_SUCCESS) {
- stream->free(stream);
+ stream->free(stream);
+ return error;
+}
+
+static int write_file_stream(
+ git_oid *oid, git_odb *odb, const char *path, git_off_t file_size)
+{
+ int fd, error;
+ char buffer[4096];
+ git_odb_stream *stream = NULL;
+
+ if ((error = git_odb_open_wstream(
+ &stream, odb, (size_t)file_size, GIT_OBJ_BLOB)) < 0)
return error;
+
+ if ((fd = git_futils_open_ro(path)) < 0) {
+ stream->free(stream);
+ return -1;
}
- error = stream->finalize_write(oid, stream);
- stream->free(stream);
+ while (!error && file_size > 0) {
+ ssize_t read_len = p_read(fd, buffer, sizeof(buffer));
+
+ if (read_len < 0) {
+ giterr_set(
+ GITERR_OS, "Failed to create blob. Can't read whole file");
+ error = -1;
+ }
+ else if (!(error = stream->write(stream, buffer, read_len)))
+ file_size -= read_len;
+ }
+
+ p_close(fd);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to create blob");
+ if (!error)
+ error = stream->finalize_write(oid, stream);
- return GIT_SUCCESS;
+ stream->free(stream);
+ return error;
}
-int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *path)
+static int write_file_filtered(
+ git_oid *oid,
+ git_odb *odb,
+ const char *full_path,
+ git_vector *filters)
{
- int error = GIT_SUCCESS;
- int islnk = 0;
- int fd = 0;
- git_buf full_path = GIT_BUF_INIT;
- char buffer[2048];
- git_off_t size;
- git_odb_stream *stream = NULL;
- struct stat st;
- const char *workdir;
- git_odb *odb;
-
- workdir = git_repository_workdir(repo);
- if (workdir == NULL)
- return git__throw(GIT_ENOTFOUND, "Failed to create blob. (No working directory found)");
+ int error;
+ git_buf source = GIT_BUF_INIT;
+ git_buf dest = GIT_BUF_INIT;
- error = git_buf_joinpath(&full_path, workdir, path);
- if (error < GIT_SUCCESS)
+ if ((error = git_futils_readbuffer(&source, full_path)) < 0)
return error;
- error = p_lstat(full_path.ptr, &st);
- if (error < 0) {
- error = git__throw(GIT_EOSERR, "Failed to stat blob. %s", strerror(errno));
- goto cleanup;
- }
+ error = git_filters_apply(&dest, &source, filters);
- islnk = S_ISLNK(st.st_mode);
- size = st.st_size;
+ /* Free the source as soon as possible. This can be big in memory,
+ * and we don't want to ODB write to choke */
+ git_buf_free(&source);
- error = git_repository_odb__weakptr(&odb, repo);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ /* Write the file to disk if it was properly filtered */
+ if (!error)
+ error = git_odb_write(oid, odb, dest.ptr, dest.size, GIT_OBJ_BLOB);
- if (!islnk) {
- if ((fd = p_open(full_path.ptr, O_RDONLY)) < 0) {
- error = git__throw(GIT_ENOTFOUND, "Failed to create blob. Could not open '%s'", full_path.ptr
-);
- goto cleanup;
- }
+ git_buf_free(&dest);
+ return error;
+}
+
+static int write_symlink(
+ git_oid *oid, git_odb *odb, const char *path, size_t link_size)
+{
+ char *link_data;
+ ssize_t read_len;
+ int error;
+
+ link_data = git__malloc(link_size);
+ GITERR_CHECK_ALLOC(link_data);
+
+ read_len = p_readlink(path, link_data, link_size);
+ if (read_len != (ssize_t)link_size) {
+ giterr_set(GITERR_OS, "Failed to create blob. Can't read symlink '%s'", path);
+ git__free(link_data);
+ return -1;
}
- if ((error = git_odb_open_wstream(&stream, odb, (size_t)size, GIT_OBJ_BLOB)) < GIT_SUCCESS)
- goto cleanup;
+ error = git_odb_write(oid, odb, (void *)link_data, link_size, GIT_OBJ_BLOB);
+ git__free(link_data);
+ return error;
+}
- while (size > 0) {
- ssize_t read_len;
+static int blob_create_internal(git_oid *oid, git_repository *repo, const char *path)
+{
+ int error;
+ struct stat st;
+ git_odb *odb = NULL;
+ git_off_t size;
- if (!islnk)
- read_len = p_read(fd, buffer, sizeof(buffer));
- else
- read_len = p_readlink(full_path.ptr, buffer, sizeof(buffer));
+ if ((error = git_path_lstat(path, &st)) < 0 || (error = git_repository_odb__weakptr(&odb, repo)) < 0)
+ return error;
- if (read_len < 0) {
- error = git__throw(GIT_EOSERR, "Failed to create blob. Can't read full file");
- goto cleanup;
+ size = st.st_size;
+
+ if (S_ISLNK(st.st_mode)) {
+ error = write_symlink(oid, odb, path, (size_t)size);
+ } else {
+ git_vector write_filters = GIT_VECTOR_INIT;
+ int filter_count;
+
+ /* Load the filters for writing this file to the ODB */
+ filter_count = git_filters_load(
+ &write_filters, repo, path, GIT_FILTER_TO_ODB);
+
+ if (filter_count < 0) {
+ /* Negative value means there was a critical error */
+ error = filter_count;
+ } else if (filter_count == 0) {
+ /* No filters need to be applied to the document: we can stream
+ * directly from disk */
+ error = write_file_stream(oid, odb, path, size);
+ } else {
+ /* We need to apply one or more filters */
+ error = write_file_filtered(oid, odb, path, &write_filters);
}
- stream->write(stream, buffer, read_len);
- size -= read_len;
+ git_filters_free(&write_filters);
+
+ /*
+ * TODO: eventually support streaming filtered files, for files
+ * which are bigger than a given threshold. This is not a priority
+ * because applying a filter in streaming mode changes the final
+ * size of the blob, and without knowing its final size, the blob
+ * cannot be written in stream mode to the ODB.
+ *
+ * The plan is to do streaming writes to a tempfile on disk and then
+ * opening streaming that file to the ODB, using
+ * `write_file_stream`.
+ *
+ * CAREFULLY DESIGNED APIS YO
+ */
}
- error = stream->finalize_write(oid, stream);
+ return error;
+}
-cleanup:
- if (stream)
- stream->free(stream);
- if (!islnk && fd)
- p_close(fd);
- git_buf_free(&full_path);
+int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *path)
+{
+ git_buf full_path = GIT_BUF_INIT;
+ const char *workdir;
+ int error;
+
+ workdir = git_repository_workdir(repo);
+ assert(workdir); /* error to call this on bare repo */
+
+ if (git_buf_joinpath(&full_path, workdir, path) < 0) {
+ git_buf_free(&full_path);
+ return -1;
+ }
- return error == GIT_SUCCESS ? GIT_SUCCESS :
- git__rethrow(error, "Failed to create blob");
+ error = blob_create_internal(oid, repo, git_buf_cstr(&full_path));
+
+ git_buf_free(&full_path);
+ return error;
}
+int git_blob_create_fromdisk(git_oid *oid, git_repository *repo, const char *path)
+{
+ int error;
+ git_buf full_path = GIT_BUF_INIT;
+
+ if ((error = git_path_prettify(&full_path, path, NULL)) < 0) {
+ git_buf_free(&full_path);
+ return error;
+ }
+
+ error = blob_create_internal(oid, repo, git_buf_cstr(&full_path));
+
+ git_buf_free(&full_path);
+ return error;
+}
diff --git a/src/blob.h b/src/blob.h
index 0cc9900c9..0305e9473 100644
--- a/src/blob.h
+++ b/src/blob.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -19,5 +19,6 @@ struct git_blob {
void git_blob__free(git_blob *blob);
int git_blob__parse(git_blob *blob, git_odb_object *obj);
+int git_blob__getbuf(git_buf *buffer, git_blob *blob);
#endif
diff --git a/src/branch.c b/src/branch.c
new file mode 100644
index 000000000..5d5a24038
--- /dev/null
+++ b/src/branch.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+#include "commit.h"
+#include "branch.h"
+#include "tag.h"
+
+static int retrieve_branch_reference(
+ git_reference **branch_reference_out,
+ git_repository *repo,
+ const char *branch_name,
+ int is_remote)
+{
+ git_reference *branch;
+ int error = -1;
+ char *prefix;
+ git_buf ref_name = GIT_BUF_INIT;
+
+ *branch_reference_out = NULL;
+
+ prefix = is_remote ? GIT_REFS_REMOTES_DIR : GIT_REFS_HEADS_DIR;
+
+ if (git_buf_joinpath(&ref_name, prefix, branch_name) < 0)
+ goto cleanup;
+
+ if ((error = git_reference_lookup(&branch, repo, ref_name.ptr)) < 0) {
+ giterr_set(GITERR_REFERENCE,
+ "Cannot locate %s branch '%s'.", is_remote ? "remote-tracking" : "local", branch_name);
+ goto cleanup;
+ }
+
+ *branch_reference_out = branch;
+
+cleanup:
+ git_buf_free(&ref_name);
+ return error;
+}
+
+static int create_error_invalid(const char *msg)
+{
+ giterr_set(GITERR_INVALID, "Cannot create branch - %s", msg);
+ return -1;
+}
+
+int git_branch_create(
+ git_oid *oid_out,
+ git_repository *repo,
+ const char *branch_name,
+ const git_object *target,
+ int force)
+{
+ git_otype target_type = GIT_OBJ_BAD;
+ git_object *commit = NULL;
+ git_reference *branch = NULL;
+ git_buf canonical_branch_name = GIT_BUF_INIT;
+ int error = -1;
+
+ assert(repo && branch_name && target && oid_out);
+
+ if (git_object_owner(target) != repo)
+ return create_error_invalid("The given target does not belong to this repository");
+
+ target_type = git_object_type(target);
+
+ switch (target_type)
+ {
+ case GIT_OBJ_TAG:
+ if (git_tag_peel(&commit, (git_tag *)target) < 0)
+ goto cleanup;
+
+ if (git_object_type(commit) != GIT_OBJ_COMMIT) {
+ create_error_invalid("The given target does not resolve to a commit");
+ goto cleanup;
+ }
+ break;
+
+ case GIT_OBJ_COMMIT:
+ commit = (git_object *)target;
+ break;
+
+ default:
+ return create_error_invalid("Only git_tag and git_commit objects are valid targets.");
+ }
+
+ if (git_buf_joinpath(&canonical_branch_name, GIT_REFS_HEADS_DIR, branch_name) < 0)
+ goto cleanup;
+
+ if (git_reference_create_oid(&branch, repo, git_buf_cstr(&canonical_branch_name), git_object_id(commit), force) < 0)
+ goto cleanup;
+
+ git_oid_cpy(oid_out, git_reference_oid(branch));
+ error = 0;
+
+cleanup:
+ if (target_type == GIT_OBJ_TAG)
+ git_object_free(commit);
+
+ git_reference_free(branch);
+ git_buf_free(&canonical_branch_name);
+ return error;
+}
+
+int git_branch_delete(git_repository *repo, const char *branch_name, git_branch_t branch_type)
+{
+ git_reference *branch = NULL;
+ git_reference *head = NULL;
+ int error;
+
+ assert((branch_type == GIT_BRANCH_LOCAL) || (branch_type == GIT_BRANCH_REMOTE));
+
+ if ((error = retrieve_branch_reference(&branch, repo, branch_name, branch_type == GIT_BRANCH_REMOTE)) < 0)
+ return error;
+
+ if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0) {
+ giterr_set(GITERR_REFERENCE, "Cannot locate HEAD.");
+ goto on_error;
+ }
+
+ if ((git_reference_type(head) == GIT_REF_SYMBOLIC)
+ && (strcmp(git_reference_target(head), git_reference_name(branch)) == 0)) {
+ giterr_set(GITERR_REFERENCE,
+ "Cannot delete branch '%s' as it is the current HEAD of the repository.", branch_name);
+ goto on_error;
+ }
+
+ if (git_reference_delete(branch) < 0)
+ goto on_error;
+
+ git_reference_free(head);
+ return 0;
+
+on_error:
+ git_reference_free(head);
+ git_reference_free(branch);
+ return -1;
+}
+
+typedef struct {
+ git_vector *branchlist;
+ unsigned int branch_type;
+} branch_filter_data;
+
+static int branch_list_cb(const char *branch_name, void *payload)
+{
+ branch_filter_data *filter = (branch_filter_data *)payload;
+
+ if ((filter->branch_type & GIT_BRANCH_LOCAL && git__prefixcmp(branch_name, GIT_REFS_HEADS_DIR) == 0)
+ || (filter->branch_type & GIT_BRANCH_REMOTE && git__prefixcmp(branch_name, GIT_REFS_REMOTES_DIR) == 0))
+ return git_vector_insert(filter->branchlist, git__strdup(branch_name));
+
+ return 0;
+}
+
+int git_branch_list(git_strarray *branch_names, git_repository *repo, unsigned int list_flags)
+{
+ int error;
+ branch_filter_data filter;
+ git_vector branchlist;
+
+ assert(branch_names && repo);
+
+ if (git_vector_init(&branchlist, 8, NULL) < 0)
+ return -1;
+
+ filter.branchlist = &branchlist;
+ filter.branch_type = list_flags;
+
+ error = git_reference_foreach(repo, GIT_REF_LISTALL, &branch_list_cb, (void *)&filter);
+ if (error < 0) {
+ git_vector_free(&branchlist);
+ return -1;
+ }
+
+ branch_names->strings = (char **)branchlist.contents;
+ branch_names->count = branchlist.length;
+ return 0;
+}
+
+int git_branch_move(git_repository *repo, const char *old_branch_name, const char *new_branch_name, int force)
+{
+ git_reference *reference = NULL;
+ git_buf old_reference_name = GIT_BUF_INIT, new_reference_name = GIT_BUF_INIT;
+ int error = 0;
+
+ if ((error = git_buf_joinpath(&old_reference_name, GIT_REFS_HEADS_DIR, old_branch_name)) < 0)
+ goto cleanup;
+
+ /* We need to be able to return GIT_ENOTFOUND */
+ if ((error = git_reference_lookup(&reference, repo, git_buf_cstr(&old_reference_name))) < 0)
+ goto cleanup;
+
+ if ((error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name)) < 0)
+ goto cleanup;
+
+ error = git_reference_rename(reference, git_buf_cstr(&new_reference_name), force);
+
+cleanup:
+ git_reference_free(reference);
+ git_buf_free(&old_reference_name);
+ git_buf_free(&new_reference_name);
+
+ return error;
+}
diff --git a/src/branch.h b/src/branch.h
new file mode 100644
index 000000000..d0e5abc8b
--- /dev/null
+++ b/src/branch.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_branch_h__
+#define INCLUDE_branch_h__
+
+#include "git2/branch.h"
+
+struct git_branch {
+ char *remote; /* TODO: Make this a git_remote */
+ char *merge;
+};
+
+#endif
diff --git a/src/bswap.h b/src/bswap.h
index 0914906ff..995767a14 100644
--- a/src/bswap.h
+++ b/src/bswap.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
diff --git a/src/buffer.c b/src/buffer.c
index c57e4aa1b..783a36eb8 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -7,22 +7,25 @@
#include "buffer.h"
#include "posix.h"
#include <stdarg.h>
+#include <ctype.h>
/* Used as default value for git_buf->ptr so that people can always
* assume ptr is non-NULL and zero terminated even for new git_bufs.
*/
-char git_buf_initbuf[1];
+char git_buf__initbuf[1];
+
+char git_buf__oom[1];
#define ENSURE_SIZE(b, d) \
- if ((ssize_t)(d) > buf->asize && git_buf_grow(b, (d)) < GIT_SUCCESS)\
- return GIT_ENOMEM;
+ if ((d) > buf->asize && git_buf_grow(b, (d)) < 0)\
+ return -1;
void git_buf_init(git_buf *buf, size_t initial_size)
{
buf->asize = 0;
buf->size = 0;
- buf->ptr = git_buf_initbuf;
+ buf->ptr = git_buf__initbuf;
if (initial_size)
git_buf_grow(buf, initial_size);
@@ -31,8 +34,8 @@ void git_buf_init(git_buf *buf, size_t initial_size)
int git_buf_grow(git_buf *buf, size_t target_size)
{
int error = git_buf_try_grow(buf, target_size);
- if (error != GIT_SUCCESS)
- buf->asize = -1;
+ if (error != 0)
+ buf->ptr = git_buf__oom;
return error;
}
@@ -41,17 +44,17 @@ int git_buf_try_grow(git_buf *buf, size_t target_size)
char *new_ptr;
size_t new_size;
- if (buf->asize < 0)
- return GIT_ENOMEM;
+ if (buf->ptr == git_buf__oom)
+ return -1;
- if (target_size <= (size_t)buf->asize)
- return GIT_SUCCESS;
+ if (target_size <= buf->asize)
+ return 0;
if (buf->asize == 0) {
new_size = target_size;
new_ptr = NULL;
} else {
- new_size = (size_t)buf->asize;
+ new_size = buf->asize;
new_ptr = buf->ptr;
}
@@ -64,9 +67,8 @@ int git_buf_try_grow(git_buf *buf, size_t target_size)
new_size = (new_size + 7) & ~7;
new_ptr = git__realloc(new_ptr, new_size);
- /* if realloc fails, return without modifying the git_buf */
if (!new_ptr)
- return GIT_ENOMEM;
+ return -1;
buf->asize = new_size;
buf->ptr = new_ptr;
@@ -76,14 +78,14 @@ int git_buf_try_grow(git_buf *buf, size_t target_size)
buf->size = buf->asize - 1;
buf->ptr[buf->size] = '\0';
- return GIT_SUCCESS;
+ return 0;
}
void git_buf_free(git_buf *buf)
{
if (!buf) return;
- if (buf->ptr != git_buf_initbuf)
+ if (buf->ptr != git_buf__initbuf && buf->ptr != git_buf__oom)
git__free(buf->ptr);
git_buf_init(buf, 0);
@@ -96,16 +98,6 @@ void git_buf_clear(git_buf *buf)
buf->ptr[0] = '\0';
}
-int git_buf_oom(const git_buf *buf)
-{
- return (buf->asize < 0);
-}
-
-int git_buf_lasterror(const git_buf *buf)
-{
- return (buf->asize < 0) ? GIT_ENOMEM : GIT_SUCCESS;
-}
-
int git_buf_set(git_buf *buf, const char *data, size_t len)
{
if (len == 0 || data == NULL) {
@@ -118,7 +110,7 @@ int git_buf_set(git_buf *buf, const char *data, size_t len)
buf->size = len;
buf->ptr[buf->size] = '\0';
}
- return GIT_SUCCESS;
+ return 0;
}
int git_buf_sets(git_buf *buf, const char *string)
@@ -131,7 +123,7 @@ int git_buf_putc(git_buf *buf, char c)
ENSURE_SIZE(buf, buf->size + 2);
buf->ptr[buf->size++] = c;
buf->ptr[buf->size] = '\0';
- return GIT_SUCCESS;
+ return 0;
}
int git_buf_put(git_buf *buf, const char *data, size_t len)
@@ -140,7 +132,7 @@ int git_buf_put(git_buf *buf, const char *data, size_t len)
memmove(buf->ptr + buf->size, data, len);
buf->size += len;
buf->ptr[buf->size] = '\0';
- return GIT_SUCCESS;
+ return 0;
}
int git_buf_puts(git_buf *buf, const char *string)
@@ -149,24 +141,29 @@ int git_buf_puts(git_buf *buf, const char *string)
return git_buf_put(buf, string, strlen(string));
}
-int git_buf_printf(git_buf *buf, const char *format, ...)
+int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
{
int len;
- va_list arglist;
- ENSURE_SIZE(buf, buf->size + 1);
+ ENSURE_SIZE(buf, buf->size + (strlen(format) * 2));
while (1) {
- va_start(arglist, format);
- len = p_vsnprintf(buf->ptr + buf->size, buf->asize - buf->size, format, arglist);
- va_end(arglist);
+ va_list args;
+ va_copy(args, ap);
+
+ len = p_vsnprintf(
+ buf->ptr + buf->size,
+ buf->asize - buf->size,
+ format, args
+ );
if (len < 0) {
- buf->asize = -1;
- return GIT_ENOMEM;
+ git__free(buf->ptr);
+ buf->ptr = git_buf__oom;
+ return -1;
}
- if (len + 1 <= buf->asize - buf->size) {
+ if ((size_t)len + 1 <= buf->asize - buf->size) {
buf->size += len;
break;
}
@@ -174,7 +171,19 @@ int git_buf_printf(git_buf *buf, const char *format, ...)
ENSURE_SIZE(buf, buf->size + len + 1);
}
- return GIT_SUCCESS;
+ return 0;
+}
+
+int git_buf_printf(git_buf *buf, const char *format, ...)
+{
+ int r;
+ va_list ap;
+
+ va_start(ap, format);
+ r = git_buf_vprintf(buf, format, ap);
+ va_end(ap);
+
+ return r;
}
void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf)
@@ -205,14 +214,20 @@ void git_buf_consume(git_buf *buf, const char *end)
}
}
-void git_buf_truncate(git_buf *buf, ssize_t len)
+void git_buf_truncate(git_buf *buf, size_t len)
{
- if (len >= 0 && len < buf->size) {
+ if (len < buf->size) {
buf->size = len;
buf->ptr[buf->size] = '\0';
}
}
+void git_buf_rtruncate_at_char(git_buf *buf, char separator)
+{
+ ssize_t idx = git_buf_rfind_next(buf, separator);
+ git_buf_truncate(buf, idx < 0 ? 0 : (size_t)idx);
+}
+
void git_buf_swap(git_buf *buf_a, git_buf *buf_b)
{
git_buf t = *buf_a;
@@ -224,7 +239,7 @@ char *git_buf_detach(git_buf *buf)
{
char *data = buf->ptr;
- if (buf->asize <= 0)
+ if (buf->asize == 0 || buf->ptr == git_buf__oom)
return NULL;
git_buf_init(buf, 0);
@@ -232,7 +247,7 @@ char *git_buf_detach(git_buf *buf)
return data;
}
-void git_buf_attach(git_buf *buf, char *ptr, ssize_t asize)
+void git_buf_attach(git_buf *buf, char *ptr, size_t asize)
{
git_buf_free(buf);
@@ -251,9 +266,9 @@ void git_buf_attach(git_buf *buf, char *ptr, ssize_t asize)
int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
{
va_list ap;
- int i, error = GIT_SUCCESS;
- size_t total_size = 0;
- char *out;
+ int i;
+ size_t total_size = 0, original_size = buf->size;
+ char *out, *original = buf->ptr;
if (buf->size > 0 && buf->ptr[buf->size - 1] != separator)
++total_size; /* space for initial separator */
@@ -277,9 +292,10 @@ int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
va_end(ap);
/* expand buffer if needed */
- if (total_size > 0 &&
- (error = git_buf_grow(buf, buf->size + total_size + 1)) < GIT_SUCCESS)
- return error;
+ if (total_size == 0)
+ return 0;
+ if (git_buf_grow(buf, buf->size + total_size + 1) < 0)
+ return -1;
out = buf->ptr + buf->size;
@@ -296,12 +312,23 @@ int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
if (!segment)
continue;
+ /* deal with join that references buffer's original content */
+ if (segment >= original && segment < original + original_size) {
+ size_t offset = (segment - original);
+ segment = buf->ptr + offset;
+ segment_len = original_size - offset;
+ } else {
+ segment_len = strlen(segment);
+ }
+
/* skip leading separators */
if (out > buf->ptr && out[-1] == separator)
- while (*segment == separator) segment++;
+ while (segment_len > 0 && *segment == separator) {
+ segment++;
+ segment_len--;
+ }
/* copy over next buffer */
- segment_len = strlen(segment);
if (segment_len > 0) {
memmove(out, segment, segment_len);
out += segment_len;
@@ -317,7 +344,7 @@ int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
buf->size = out - buf->ptr;
buf->ptr[buf->size] = '\0';
- return error;
+ return 0;
}
int git_buf_join(
@@ -326,8 +353,7 @@ int git_buf_join(
const char *str_a,
const char *str_b)
{
- int error = GIT_SUCCESS;
- size_t strlen_a = strlen(str_a);
+ size_t strlen_a = str_a ? strlen(str_a) : 0;
size_t strlen_b = strlen(str_b);
int need_sep = 0;
ssize_t offset_a = -1;
@@ -346,9 +372,8 @@ int git_buf_join(
if (str_a >= buf->ptr && str_a < buf->ptr + buf->size)
offset_a = str_a - buf->ptr;
- error = git_buf_grow(buf, strlen_a + strlen_b + need_sep + 1);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_buf_grow(buf, strlen_a + strlen_b + need_sep + 1) < 0)
+ return -1;
/* fix up internal pointers */
if (offset_a >= 0)
@@ -364,5 +389,73 @@ int git_buf_join(
buf->size = strlen_a + strlen_b + need_sep;
buf->ptr[buf->size] = '\0';
- return error;
+ return 0;
}
+
+void git_buf_rtrim(git_buf *buf)
+{
+ while (buf->size > 0) {
+ if (!git__isspace(buf->ptr[buf->size - 1]))
+ break;
+
+ buf->size--;
+ }
+
+ buf->ptr[buf->size] = '\0';
+}
+
+int git_buf_cmp(const git_buf *a, const git_buf *b)
+{
+ int result = memcmp(a->ptr, b->ptr, min(a->size, b->size));
+ return (result != 0) ? result :
+ (a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;
+}
+
+int git_buf_common_prefix(git_buf *buf, const git_strarray *strings)
+{
+ size_t i;
+ const char *str, *pfx;
+
+ git_buf_clear(buf);
+
+ if (!strings || !strings->count)
+ return 0;
+
+ /* initialize common prefix to first string */
+ if (git_buf_sets(buf, strings->strings[0]) < 0)
+ return -1;
+
+ /* go through the rest of the strings, truncating to shared prefix */
+ for (i = 1; i < strings->count; ++i) {
+
+ for (str = strings->strings[i], pfx = buf->ptr;
+ *str && *str == *pfx; str++, pfx++)
+ /* scanning */;
+
+ git_buf_truncate(buf, pfx - buf->ptr);
+
+ if (!buf->size)
+ break;
+ }
+
+ return 0;
+}
+
+bool git_buf_is_binary(const git_buf *buf)
+{
+ size_t i;
+ int printable = 0, nonprintable = 0;
+
+ for (i = 0; i < buf->size; i++) {
+ unsigned char c = buf->ptr[i];
+ if (c > 0x1F && c < 0x7F)
+ printable++;
+ else if (c == '\0')
+ return true;
+ else if (!git__isspace(c))
+ nonprintable++;
+ }
+
+ return ((printable >> 7) < nonprintable);
+}
+
diff --git a/src/buffer.h b/src/buffer.h
index d06358527..50c75f64e 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -8,15 +8,17 @@
#define INCLUDE_buffer_h__
#include "common.h"
+#include <stdarg.h>
typedef struct {
char *ptr;
- ssize_t asize, size;
+ size_t asize, size;
} git_buf;
-extern char git_buf_initbuf[];
+extern char git_buf__initbuf[];
+extern char git_buf__oom[];
-#define GIT_BUF_INIT { git_buf_initbuf, 0, 0 }
+#define GIT_BUF_INIT { git_buf__initbuf, 0, 0 }
/**
* Initialize a git_buf structure.
@@ -32,7 +34,7 @@ void git_buf_init(git_buf *buf, size_t initial_size);
* If the allocation fails, this will return an error and the buffer
* will be marked as invalid for future operations. The existing
* contents of the buffer will be preserved however.
- * @return GIT_SUCCESS or GIT_ENOMEM on failure
+ * @return 0 on success or -1 on failure
*/
int git_buf_grow(git_buf *buf, size_t target_size);
@@ -47,7 +49,7 @@ int git_buf_try_grow(git_buf *buf, size_t target_size);
void git_buf_free(git_buf *buf);
void git_buf_swap(git_buf *buf_a, git_buf *buf_b);
char *git_buf_detach(git_buf *buf);
-void git_buf_attach(git_buf *buf, char *ptr, ssize_t asize);
+void git_buf_attach(git_buf *buf, char *ptr, size_t asize);
/**
* Test if there have been any reallocation failures with this git_buf.
@@ -57,23 +59,21 @@ void git_buf_attach(git_buf *buf, char *ptr, ssize_t asize);
* further calls to modify the buffer will fail. Check git_buf_oom() at the
* end of your sequence and it will be true if you ran out of memory at any
* point with that buffer.
- * @return 0 if no error, 1 if allocation error.
- */
-int git_buf_oom(const git_buf *buf);
-
-/**
- * Just like git_buf_oom, except returns appropriate error code.
- * @return GIT_ENOMEM if allocation error, GIT_SUCCESS if not.
+ *
+ * @return false if no error, true if allocation error
*/
-int git_buf_lasterror(const git_buf *buf);
+GIT_INLINE(bool) git_buf_oom(const git_buf *buf)
+{
+ return (buf->ptr == git_buf__oom);
+}
/*
- * The functions below that return int values, will return GIT_ENOMEM
- * if they fail to expand the git_buf when they are called, otherwise
- * GIT_SUCCESS. Passing a git_buf that has failed an allocation will
- * automatically return GIT_ENOMEM for all further calls. As a result,
- * you can ignore the return code of these functions and call them in a
- * series then just call git_buf_lasterror at the end.
+ * Functions below that return int value error codes will return 0 on
+ * success or -1 on failure (which generally means an allocation failed).
+ * Using a git_buf where the allocation has failed with result in -1 from
+ * all further calls using that buffer. As a result, you can ignore the
+ * return code of these functions and call them in a series then just call
+ * git_buf_oom at the end.
*/
int git_buf_set(git_buf *buf, const char *data, size_t len);
int git_buf_sets(git_buf *buf, const char *string);
@@ -81,37 +81,55 @@ int git_buf_putc(git_buf *buf, char c);
int git_buf_put(git_buf *buf, const char *data, size_t len);
int git_buf_puts(git_buf *buf, const char *string);
int git_buf_printf(git_buf *buf, const char *format, ...) GIT_FORMAT_PRINTF(2, 3);
+int git_buf_vprintf(git_buf *buf, const char *format, va_list ap);
void git_buf_clear(git_buf *buf);
void git_buf_consume(git_buf *buf, const char *end);
-void git_buf_truncate(git_buf *buf, ssize_t len);
+void git_buf_truncate(git_buf *buf, size_t len);
+void git_buf_rtruncate_at_char(git_buf *path, char separator);
int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...);
int git_buf_join(git_buf *buf, char separator, const char *str_a, const char *str_b);
/**
* Join two strings as paths, inserting a slash between as needed.
- * @return error code or GIT_SUCCESS
+ * @return 0 on success, -1 on failure
*/
GIT_INLINE(int) git_buf_joinpath(git_buf *buf, const char *a, const char *b)
{
return git_buf_join(buf, '/', a, b);
}
-GIT_INLINE(const char *) git_buf_cstr(git_buf *buf)
+GIT_INLINE(const char *) git_buf_cstr(const git_buf *buf)
{
return buf->ptr;
}
+GIT_INLINE(size_t) git_buf_len(const git_buf *buf)
+{
+ return buf->size;
+}
+
void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf);
#define git_buf_PUTS(buf, str) git_buf_put(buf, str, sizeof(str) - 1)
-GIT_INLINE(int) git_buf_rfind_next(git_buf *buf, char ch)
+GIT_INLINE(ssize_t) git_buf_rfind_next(git_buf *buf, char ch)
{
- int idx = buf->size - 1;
+ ssize_t idx = (ssize_t)buf->size - 1;
while (idx >= 0 && buf->ptr[idx] == ch) idx--;
while (idx >= 0 && buf->ptr[idx] != ch) idx--;
return idx;
}
+/* Remove whitespace from the end of the buffer */
+void git_buf_rtrim(git_buf *buf);
+
+int git_buf_cmp(const git_buf *a, const git_buf *b);
+
+/* Fill buf with the common prefix of a array of strings */
+int git_buf_common_prefix(git_buf *buf, const git_strarray *strings);
+
+/* Check if buffer looks like it contains binary data */
+bool git_buf_is_binary(const git_buf *buf);
+
#endif
diff --git a/src/cache.c b/src/cache.c
index 8150ec35a..31da3c36e 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -9,21 +9,14 @@
#include "repository.h"
#include "commit.h"
#include "thread-utils.h"
+#include "util.h"
#include "cache.h"
int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr)
{
if (size < 8)
size = 8;
-
- /* round up size to closest power of 2 */
- size--;
- size |= size >> 1;
- size |= size >> 2;
- size |= size >> 4;
- size |= size >> 8;
- size |= size >> 16;
- size++;
+ size = git__size_t_powerof2(size);
cache->size_mask = size - 1;
cache->lru_count = 0;
@@ -32,11 +25,10 @@ int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_pt
git_mutex_init(&cache->lock);
cache->nodes = git__malloc(size * sizeof(git_cached_obj *));
- if (cache->nodes == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(cache->nodes);
memset(cache->nodes, 0x0, size * sizeof(git_cached_obj *));
- return GIT_SUCCESS;
+ return 0;
}
void git_cache_free(git_cache *cache)
diff --git a/src/cache.h b/src/cache.h
index 688f14559..6dc706897 100644
--- a/src/cache.h
+++ b/src/cache.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
diff --git a/src/cc-compat.h b/src/cc-compat.h
index c243f1d20..9f23dcae2 100644
--- a/src/cc-compat.h
+++ b/src/cc-compat.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -33,21 +33,7 @@
# define GIT_TYPEOF(x)
#endif
-#ifdef __cplusplus
-# define GIT_UNUSED(x)
-#else
-# ifdef __GNUC__
-# define GIT_UNUSED(x) x __attribute__ ((__unused__))
-# else
-# define GIT_UNUSED(x) x
-# endif
-#endif
-
-#if defined(_MSC_VER)
-#define GIT_UNUSED_ARG(x) ((void)(x)); /* note trailing ; */
-#else
-#define GIT_UNUSED_ARG(x)
-#endif
+#define GIT_UNUSED(x) ((void)(x))
/* Define the printf format specifer to use for size_t output */
#if defined(_MSC_VER) || defined(__MINGW32__)
@@ -64,4 +50,20 @@
# pragma warning ( disable : 4127 )
#endif
+#if defined (_MSC_VER)
+ typedef unsigned char bool;
+# define true 1
+# define false 0
+#else
+# include <stdbool.h>
+#endif
+
+#ifndef va_copy
+# ifdef __va_copy
+# define va_copy(dst, src) __va_copy(dst, src)
+# else
+# define va_copy(dst, src) ((dst) = (src))
+# endif
+#endif
+
#endif /* INCLUDE_compat_h__ */
diff --git a/src/commit.c b/src/commit.c
index 5d077d54e..2bf12f3a5 100644
--- a/src/commit.c
+++ b/src/commit.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -14,6 +14,7 @@
#include "odb.h"
#include "commit.h"
#include "signature.h"
+#include "message.h"
#include <stdarg.h>
@@ -69,24 +70,84 @@ int git_commit_create_v(
...)
{
va_list ap;
- int i, error;
+ int i, res;
const git_commit **parents;
parents = git__malloc(parent_count * sizeof(git_commit *));
+ GITERR_CHECK_ALLOC(parents);
va_start(ap, parent_count);
for (i = 0; i < parent_count; ++i)
parents[i] = va_arg(ap, const git_commit *);
va_end(ap);
- error = git_commit_create(
+ res = git_commit_create(
oid, repo, update_ref, author, committer,
message_encoding, message,
tree, parent_count, parents);
git__free((void *)parents);
+ return res;
+}
+
+/* Update the reference named `ref_name` so it points to `oid` */
+static int update_reference(git_repository *repo, git_oid *oid, const char *ref_name)
+{
+ git_reference *ref;
+ int res;
+
+ res = git_reference_lookup(&ref, repo, ref_name);
+
+ /* If we haven't found the reference at all, we assume we need to create
+ * a new reference and that's it */
+ if (res == GIT_ENOTFOUND) {
+ giterr_clear();
+ return git_reference_create_oid(NULL, repo, ref_name, oid, 1);
+ }
+
+ if (res < 0)
+ return -1;
+
+ /* If we have found a reference, but it's symbolic, we need to update
+ * the direct reference it points to */
+ if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
+ git_reference *aux;
+ const char *sym_target;
+
+ /* The target pointed at by this reference */
+ sym_target = git_reference_target(ref);
+
+ /* resolve the reference to the target it points to */
+ res = git_reference_resolve(&aux, ref);
+
+ /*
+ * if the symbolic reference pointed to an inexisting ref,
+ * this is means we're creating a new branch, for example.
+ * We need to create a new direct reference with that name
+ */
+ if (res == GIT_ENOTFOUND) {
+ giterr_clear();
+ res = git_reference_create_oid(NULL, repo, sym_target, oid, 1);
+ git_reference_free(ref);
+ return res;
+ }
+
+ /* free the original symbolic reference now; not before because
+ * we're using the `sym_target` pointer */
+ git_reference_free(ref);
+
+ if (res < 0)
+ return -1;
+
+ /* store the newly found direct reference in its place */
+ ref = aux;
+ }
- return error;
+ /* ref is made to point to `oid`: ref is either the original reference,
+ * or the target of the symbolic reference we've looked up */
+ res = git_reference_set_oid(ref, oid);
+ git_reference_free(ref);
+ return res;
}
int git_commit_create(
@@ -101,21 +162,16 @@ int git_commit_create(
int parent_count,
const git_commit *parents[])
{
- git_buf commit = GIT_BUF_INIT;
- int error, i;
+ git_buf commit = GIT_BUF_INIT, cleaned_message = GIT_BUF_INIT;
+ int i;
git_odb *odb;
- if (git_object_owner((const git_object *)tree) != repo)
- return git__throw(GIT_EINVALIDARGS, "The given tree does not belong to this repository");
+ assert(git_object_owner((const git_object *)tree) == repo);
git_oid__writebuf(&commit, "tree ", git_object_id((const git_object *)tree));
for (i = 0; i < parent_count; ++i) {
- if (git_object_owner((const git_object *)parents[i]) != repo) {
- error = git__throw(GIT_EINVALIDARGS, "The given parent does not belong to this repository");
- goto cleanup;
- }
-
+ assert(git_object_owner((const git_object *)parents[i]) == repo);
git_oid__writebuf(&commit, "parent ", git_object_id((const git_object *)parents[i]));
}
@@ -126,65 +182,34 @@ int git_commit_create(
git_buf_printf(&commit, "encoding %s\n", message_encoding);
git_buf_putc(&commit, '\n');
- git_buf_puts(&commit, message);
- if (git_buf_oom(&commit)) {
- error = git__throw(git_buf_lasterror(&commit),
- "Not enough memory to build the commit data");
- goto cleanup;
- }
+ /* Remove comments by default */
+ if (git_message_prettify(&cleaned_message, message, 1) < 0)
+ goto on_error;
- error = git_repository_odb__weakptr(&odb, repo);
- if (error < GIT_SUCCESS)
- goto cleanup;
-
- error = git_odb_write(oid, odb, commit.ptr, commit.size, GIT_OBJ_COMMIT);
- git_buf_free(&commit);
-
- if (error == GIT_SUCCESS && update_ref != NULL) {
- git_reference *head;
- git_reference *target;
-
- error = git_reference_lookup(&head, repo, update_ref);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to create commit");
-
- error = git_reference_resolve(&target, head);
- if (error < GIT_SUCCESS) {
- if (error != GIT_ENOTFOUND) {
- git_reference_free(head);
- return git__rethrow(error, "Failed to create commit");
- }
- /*
- * The target of the reference was not found. This can happen
- * just after a repository has been initialized (the master
- * branch doesn't exist yet, as it doesn't have anything to
- * point to) or after an orphan checkout, so if the target
- * branch doesn't exist yet, create it and return.
- */
- error = git_reference_create_oid(&target, repo, git_reference_target(head), oid, 1);
+ if (git_buf_puts(&commit, git_buf_cstr(&cleaned_message)) < 0)
+ goto on_error;
- git_reference_free(head);
- if (error == GIT_SUCCESS)
- git_reference_free(target);
+ git_buf_free(&cleaned_message);
- return error;
- }
+ if (git_repository_odb__weakptr(&odb, repo) < 0)
+ goto on_error;
- error = git_reference_set_oid(target, oid);
+ if (git_odb_write(oid, odb, commit.ptr, commit.size, GIT_OBJ_COMMIT) < 0)
+ goto on_error;
- git_reference_free(head);
- git_reference_free(target);
- }
+ git_buf_free(&commit);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to create commit");
+ if (update_ref != NULL)
+ return update_reference(repo, oid, update_ref);
- return GIT_SUCCESS;
+ return 0;
-cleanup:
+on_error:
git_buf_free(&commit);
- return error;
+ git_buf_free(&cleaned_message);
+ giterr_set(GITERR_OBJECT, "Failed to create commit.");
+ return -1;
}
int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len)
@@ -193,35 +218,40 @@ int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len)
const char *buffer_end = (const char *)data + len;
git_oid parent_oid;
- int error;
git_vector_init(&commit->parent_oids, 4, NULL);
- if ((error = git_oid__parse(&commit->tree_oid, &buffer, buffer_end, "tree ")) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to parse buffer");
+ if (git_oid__parse(&commit->tree_oid, &buffer, buffer_end, "tree ") < 0)
+ goto bad_buffer;
/*
* TODO: commit grafts!
*/
- while (git_oid__parse(&parent_oid, &buffer, buffer_end, "parent ") == GIT_SUCCESS) {
+ while (git_oid__parse(&parent_oid, &buffer, buffer_end, "parent ") == 0) {
git_oid *new_oid;
new_oid = git__malloc(sizeof(git_oid));
+ GITERR_CHECK_ALLOC(new_oid);
+
git_oid_cpy(new_oid, &parent_oid);
- if (git_vector_insert(&commit->parent_oids, new_oid) < GIT_SUCCESS)
- return GIT_ENOMEM;
+ if (git_vector_insert(&commit->parent_oids, new_oid) < 0)
+ return -1;
}
commit->author = git__malloc(sizeof(git_signature));
- if ((error = git_signature__parse(commit->author, &buffer, buffer_end, "author ", '\n')) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to parse commit");
+ GITERR_CHECK_ALLOC(commit->author);
+
+ if (git_signature__parse(commit->author, &buffer, buffer_end, "author ", '\n') < 0)
+ return -1;
/* Always parse the committer; we need the commit time */
commit->committer = git__malloc(sizeof(git_signature));
- if ((error = git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n')) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to parse commit");
+ GITERR_CHECK_ALLOC(commit->committer);
+
+ if (git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n') < 0)
+ return -1;
if (git__prefixcmp(buffer, "encoding ") == 0) {
const char *encoding_end;
@@ -232,8 +262,7 @@ int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len)
encoding_end++;
commit->message_encoding = git__strndup(buffer, encoding_end - buffer);
- if (!commit->message_encoding)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(commit->message_encoding);
buffer = encoding_end;
}
@@ -244,11 +273,14 @@ int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len)
if (buffer <= buffer_end) {
commit->message = git__strndup(buffer, buffer_end - buffer);
- if (!commit->message)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(commit->message);
}
- return GIT_SUCCESS;
+ return 0;
+
+bad_buffer:
+ giterr_set(GITERR_OBJECT, "Failed to parse bad commit object");
+ return -1;
}
int git_commit__parse(git_commit *commit, git_odb_object *obj)
@@ -286,8 +318,10 @@ int git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n)
assert(commit);
parent_oid = git_vector_get(&commit->parent_oids, n);
- if (parent_oid == NULL)
- return git__throw(GIT_ENOTFOUND, "Parent %u does not exist", n);
+ if (parent_oid == NULL) {
+ giterr_set(GITERR_INVALID, "Parent %u does not exist", n);
+ return GIT_ENOTFOUND;
+ }
return git_commit_lookup(parent, commit->object.repo, parent_oid);
}
diff --git a/src/commit.h b/src/commit.h
index bfc4bba19..d9f492862 100644
--- a/src/commit.h
+++ b/src/commit.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
diff --git a/src/common.h b/src/common.h
index 4f037f78c..30757de70 100644
--- a/src/common.h
+++ b/src/common.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -46,6 +46,8 @@
#include "thread-utils.h"
#include "bswap.h"
+#include <regex.h>
+
extern void git___throw(const char *, ...) GIT_FORMAT_PRINTF(1, 2);
#define git__throw(error, ...) \
(git___throw(__VA_ARGS__), error)
@@ -54,6 +56,16 @@ extern void git___rethrow(const char *, ...) GIT_FORMAT_PRINTF(1, 2);
#define git__rethrow(error, ...) \
(git___rethrow(__VA_ARGS__), error)
+
+#define GITERR_CHECK_ALLOC(ptr) if (ptr == NULL) { return -1; }
+
+void giterr_set_oom(void);
+void giterr_set(int error_class, const char *string, ...);
+void giterr_clear(void);
+void giterr_set_str(int error_class, const char *string);
+void giterr_set_regex(const regex_t *regex, int error_code);
+
+
#include "util.h"
diff --git a/src/win32/fnmatch.c b/src/compat/fnmatch.c
index 7d7c77d48..835d811bc 100644
--- a/src/win32/fnmatch.c
+++ b/src/compat/fnmatch.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
diff --git a/src/win32/fnmatch.h b/src/compat/fnmatch.h
index 1caac37fe..7faef09b3 100644
--- a/src/win32/fnmatch.h
+++ b/src/compat/fnmatch.h
@@ -1,11 +1,11 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
-#ifndef INCLUDE_fnmatch__w32_h__
-#define INCLUDE_fnmatch__w32_h__
+#ifndef INCLUDE_fnmatch__compat_h__
+#define INCLUDE_fnmatch__compat_h__
#include "common.h"
diff --git a/src/config.c b/src/config.c
index a0ae4cb5b..618202c34 100644
--- a/src/config.c
+++ b/src/config.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -7,7 +7,6 @@
#include "common.h"
#include "fileops.h"
-#include "hashtable.h"
#include "config.h"
#include "git2/config.h"
#include "vector.h"
@@ -60,84 +59,77 @@ int git_config_new(git_config **out)
git_config *cfg;
cfg = git__malloc(sizeof(git_config));
- if (cfg == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(cfg);
memset(cfg, 0x0, sizeof(git_config));
if (git_vector_init(&cfg->files, 3, config_backend_cmp) < 0) {
git__free(cfg);
- return GIT_ENOMEM;
+ return -1;
}
*out = cfg;
GIT_REFCOUNT_INC(cfg);
- return GIT_SUCCESS;
+ return 0;
}
int git_config_add_file_ondisk(git_config *cfg, const char *path, int priority)
{
git_config_file *file = NULL;
- int error;
- error = git_config_file__ondisk(&file, path);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_config_file__ondisk(&file, path) < 0)
+ return -1;
- error = git_config_add_file(cfg, file, priority);
- if (error < GIT_SUCCESS) {
+ if (git_config_add_file(cfg, file, priority) < 0) {
/*
* free manually; the file is not owned by the config
* instance yet and will not be freed on cleanup
*/
file->free(file);
- return error;
+ return -1;
}
- return GIT_SUCCESS;
+ return 0;
}
int git_config_open_ondisk(git_config **cfg, const char *path)
{
- int error;
+ if (git_config_new(cfg) < 0)
+ return -1;
- error = git_config_new(cfg);
- if (error < GIT_SUCCESS)
- return error;
-
- error = git_config_add_file_ondisk(*cfg, path, 1);
- if (error < GIT_SUCCESS)
+ if (git_config_add_file_ondisk(*cfg, path, 1) < 0) {
git_config_free(*cfg);
+ return -1;
+ }
- return error;
+ return 0;
}
int git_config_add_file(git_config *cfg, git_config_file *file, int priority)
{
file_internal *internal;
- int error;
+ int result;
assert(cfg && file);
- if ((error = file->open(file)) < GIT_SUCCESS)
- return git__throw(error, "Failed to open config file");
+ if ((result = file->open(file)) < 0)
+ return result;
internal = git__malloc(sizeof(file_internal));
- if (internal == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(internal);
internal->file = file;
internal->priority = priority;
if (git_vector_insert(&cfg->files, internal) < 0) {
git__free(internal);
- return GIT_ENOMEM;
+ return -1;
}
git_vector_sort(&cfg->files);
internal->file->cfg = cfg;
- return GIT_SUCCESS;
+ return 0;
}
/*
@@ -146,7 +138,7 @@ int git_config_add_file(git_config *cfg, git_config_file *file, int priority)
int git_config_foreach(git_config *cfg, int (*fn)(const char *, const char *, void *), void *data)
{
- int ret = GIT_SUCCESS;
+ int ret = 0;
unsigned int i;
file_internal *internal;
git_config_file *file;
@@ -165,8 +157,7 @@ int git_config_delete(git_config *cfg, const char *name)
file_internal *internal;
git_config_file *file;
- if (cfg->files.length == 0)
- return git__throw(GIT_EINVALIDARGS, "Cannot delete variable; no files open in the `git_config` instance");
+ assert(cfg->files.length);
internal = git_vector_get(&cfg->files, 0);
file = internal->file;
@@ -200,8 +191,7 @@ int git_config_set_string(git_config *cfg, const char *name, const char *value)
file_internal *internal;
git_config_file *file;
- if (cfg->files.length == 0)
- return git__throw(GIT_EINVALIDARGS, "Cannot set variable value; no files open in the `git_config` instance");
+ assert(cfg->files.length);
internal = git_vector_get(&cfg->files, 0);
file = internal->file;
@@ -209,23 +199,13 @@ int git_config_set_string(git_config *cfg, const char *name, const char *value)
return file->set(file, name, value);
}
-/***********
- * Getters
- ***********/
-
-int git_config_get_int64(git_config *cfg, const char *name, int64_t *out)
+static int parse_int64(int64_t *out, const char *value)
{
- const char *value, *num_end;
- int ret;
+ const char *num_end;
int64_t num;
- ret = git_config_get_string(cfg, name, &value);
- if (ret < GIT_SUCCESS)
- return git__rethrow(ret, "Failed to retrieve value for '%s'", name);
-
- ret = git__strtol64(&num, value, &num_end, 0);
- if (ret < GIT_SUCCESS)
- return git__rethrow(ret, "Failed to convert value for '%s'", name);
+ if (git__strtol64(&num, value, &num_end, 0) < 0)
+ return -1;
switch (*num_end) {
case 'g':
@@ -245,96 +225,217 @@ int git_config_get_int64(git_config *cfg, const char *name, int64_t *out)
/* check that that there are no more characters after the
* given modifier suffix */
if (num_end[1] != '\0')
- return git__throw(GIT_EINVALIDTYPE,
- "Failed to get value for '%s'. Invalid type suffix", name);
+ return -1;
/* fallthrough */
case '\0':
*out = num;
- return GIT_SUCCESS;
+ return 0;
default:
- return git__throw(GIT_EINVALIDTYPE,
- "Failed to get value for '%s'. Value is of invalid type", name);
+ return -1;
+ }
+}
+
+static int parse_int32(int32_t *out, const char *value)
+{
+ int64_t tmp;
+ int32_t truncate;
+
+ if (parse_int64(&tmp, value) < 0)
+ return -1;
+
+ truncate = tmp & 0xFFFFFFFF;
+ if (truncate != tmp)
+ return -1;
+
+ *out = truncate;
+ return 0;
+}
+
+/***********
+ * Getters
+ ***********/
+int git_config_lookup_map_value(
+ git_cvar_map *maps, size_t map_n, const char *value, int *out)
+{
+ size_t i;
+
+ if (!value)
+ return GIT_ENOTFOUND;
+
+ for (i = 0; i < map_n; ++i) {
+ git_cvar_map *m = maps + i;
+
+ switch (m->cvar_type) {
+ case GIT_CVAR_FALSE:
+ case GIT_CVAR_TRUE: {
+ int bool_val;
+
+ if (git__parse_bool(&bool_val, value) == 0 &&
+ bool_val == (int)m->cvar_type) {
+ *out = m->map_value;
+ return 0;
+ }
+ break;
+ }
+
+ case GIT_CVAR_INT32:
+ if (parse_int32(out, value) == 0)
+ return 0;
+ break;
+
+ case GIT_CVAR_STRING:
+ if (strcasecmp(value, m->str_match) == 0) {
+ *out = m->map_value;
+ return 0;
+ }
+ break;
+ }
}
+
+ return GIT_ENOTFOUND;
}
-int git_config_get_int32(git_config *cfg, const char *name, int32_t *out)
+int git_config_get_mapped(
+ int *out,
+ git_config *cfg,
+ const char *name,
+ git_cvar_map *maps,
+ size_t map_n)
{
- int64_t tmp_long;
- int32_t tmp_int;
+ const char *value;
int ret;
- ret = git_config_get_int64(cfg, name, &tmp_long);
- if (ret < GIT_SUCCESS)
- return git__rethrow(ret, "Failed to convert value for '%s'", name);
-
- tmp_int = tmp_long & 0xFFFFFFFF;
- if (tmp_int != tmp_long)
- return git__throw(GIT_EOVERFLOW, "Value for '%s' is too large", name);
+ ret = git_config_get_string(&value, cfg, name);
+ if (ret < 0)
+ return ret;
- *out = tmp_int;
+ if (!git_config_lookup_map_value(maps, map_n, value, out))
+ return 0;
- return ret;
+ giterr_set(GITERR_CONFIG,
+ "Failed to map the '%s' config variable with a valid value", name);
+ return -1;
}
-int git_config_get_bool(git_config *cfg, const char *name, int *out)
+int git_config_get_int64(int64_t *out, git_config *cfg, const char *name)
{
const char *value;
- int error = GIT_SUCCESS;
+ int ret;
- error = git_config_get_string(cfg, name, &value);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to get value for %s", name);
+ ret = git_config_get_string(&value, cfg, name);
+ if (ret < 0)
+ return ret;
- /* A missing value means true */
- if (value == NULL) {
- *out = 1;
- return GIT_SUCCESS;
+ if (parse_int64(out, value) < 0) {
+ giterr_set(GITERR_CONFIG, "Failed to parse '%s' as an integer", value);
+ return -1;
}
- if (!strcasecmp(value, "true") ||
- !strcasecmp(value, "yes") ||
- !strcasecmp(value, "on")) {
- *out = 1;
- return GIT_SUCCESS;
- }
- if (!strcasecmp(value, "false") ||
- !strcasecmp(value, "no") ||
- !strcasecmp(value, "off")) {
- *out = 0;
- return GIT_SUCCESS;
+ return 0;
+}
+
+int git_config_get_int32(int32_t *out, git_config *cfg, const char *name)
+{
+ const char *value;
+ int ret;
+
+ ret = git_config_get_string(&value, cfg, name);
+ if (ret < 0)
+ return ret;
+
+ if (parse_int32(out, value) < 0) {
+ giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a 32-bit integer", value);
+ return -1;
}
- /* Try to parse it as an integer */
- error = git_config_get_int32(cfg, name, out);
- if (error == GIT_SUCCESS)
+ return 0;
+}
+
+int git_config_get_bool(int *out, git_config *cfg, const char *name)
+{
+ const char *value;
+ int ret;
+
+ ret = git_config_get_string(&value, cfg, name);
+ if (ret < 0)
+ return ret;
+
+ if (git__parse_bool(out, value) == 0)
+ return 0;
+
+ if (parse_int32(out, value) == 0) {
*out = !!(*out);
+ return 0;
+ }
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to get value for %s", name);
- return error;
+ giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a boolean value", value);
+ return -1;
}
-int git_config_get_string(git_config *cfg, const char *name, const char **out)
+int git_config_get_string(const char **out, git_config *cfg, const char *name)
+{
+ file_internal *internal;
+ unsigned int i;
+
+ assert(cfg->files.length);
+
+ *out = NULL;
+
+ git_vector_foreach(&cfg->files, i, internal) {
+ git_config_file *file = internal->file;
+ int ret = file->get(file, name, out);
+ if (ret != GIT_ENOTFOUND)
+ return ret;
+ }
+
+ giterr_set(GITERR_CONFIG, "Config variable '%s' not found", name);
+ return GIT_ENOTFOUND;
+}
+
+int git_config_get_multivar(git_config *cfg, const char *name, const char *regexp,
+ int (*fn)(const char *value, void *data), void *data)
{
file_internal *internal;
git_config_file *file;
- int error = GIT_ENOTFOUND;
+ int ret = GIT_ENOTFOUND;
unsigned int i;
- if (cfg->files.length == 0)
- return git__throw(GIT_EINVALIDARGS, "Cannot get variable value; no files open in the `git_config` instance");
+ assert(cfg->files.length);
- for (i = 0; i < cfg->files.length; ++i) {
- internal = git_vector_get(&cfg->files, i);
+ /*
+ * This loop runs the "wrong" way 'round because we need to
+ * look at every value from the most general to most specific
+ */
+ for (i = cfg->files.length; i > 0; --i) {
+ internal = git_vector_get(&cfg->files, i - 1);
file = internal->file;
- if ((error = file->get(file, name, out)) == GIT_SUCCESS)
- return GIT_SUCCESS;
+ ret = file->get_multivar(file, name, regexp, fn, data);
+ if (ret < 0 && ret != GIT_ENOTFOUND)
+ return ret;
}
- return git__throw(error, "Config value '%s' not found", name);
+ return 0;
+}
+
+int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value)
+{
+ file_internal *internal;
+ git_config_file *file;
+ int ret = GIT_ENOTFOUND;
+ unsigned int i;
+
+ for (i = cfg->files.length; i > 0; --i) {
+ internal = git_vector_get(&cfg->files, i - 1);
+ file = internal->file;
+ ret = file->set_multivar(file, name, regexp, value);
+ if (ret < 0 && ret != GIT_ENOTFOUND)
+ return ret;
+ }
+
+ return 0;
}
int git_config_find_global_r(git_buf *path)
@@ -342,21 +443,26 @@ int git_config_find_global_r(git_buf *path)
return git_futils_find_global_file(path, GIT_CONFIG_FILENAME);
}
-int git_config_find_global(char *global_config_path)
+int git_config_find_global(char *global_config_path, size_t length)
{
git_buf path = GIT_BUF_INIT;
- int error = git_config_find_global_r(&path);
+ int ret = git_config_find_global_r(&path);
- if (error == GIT_SUCCESS) {
- if (path.size > GIT_PATH_MAX)
- error = git__throw(GIT_ESHORTBUFFER, "Path is too long");
- else
- git_buf_copy_cstr(global_config_path, GIT_PATH_MAX, &path);
+ if (ret < 0) {
+ git_buf_free(&path);
+ return ret;
}
- git_buf_free(&path);
+ if (path.size >= length) {
+ git_buf_free(&path);
+ giterr_set(GITERR_NOMEMORY,
+ "Path is to long to fit on the given buffer");
+ return -1;
+ }
- return error;
+ git_buf_copy_cstr(global_config_path, length, &path);
+ git_buf_free(&path);
+ return 0;
}
int git_config_find_system_r(git_buf *path)
@@ -364,31 +470,39 @@ int git_config_find_system_r(git_buf *path)
return git_futils_find_system_file(path, GIT_CONFIG_FILENAME_SYSTEM);
}
-int git_config_find_system(char *system_config_path)
+int git_config_find_system(char *system_config_path, size_t length)
{
git_buf path = GIT_BUF_INIT;
- int error = git_config_find_system_r(&path);
+ int ret = git_config_find_system_r(&path);
- if (error == GIT_SUCCESS) {
- if (path.size > GIT_PATH_MAX)
- error = git__throw(GIT_ESHORTBUFFER, "Path is too long");
- else
- git_buf_copy_cstr(system_config_path, GIT_PATH_MAX, &path);
+ if (ret < 0) {
+ git_buf_free(&path);
+ return ret;
}
- git_buf_free(&path);
+ if (path.size >= length) {
+ git_buf_free(&path);
+ giterr_set(GITERR_NOMEMORY,
+ "Path is to long to fit on the given buffer");
+ return -1;
+ }
- return error;
+ git_buf_copy_cstr(system_config_path, length, &path);
+ git_buf_free(&path);
+ return 0;
}
int git_config_open_global(git_config **out)
{
int error;
- char global_path[GIT_PATH_MAX];
+ git_buf path = GIT_BUF_INIT;
- if ((error = git_config_find_global(global_path)) < GIT_SUCCESS)
+ if ((error = git_config_find_global_r(&path)) < 0)
return error;
- return git_config_open_ondisk(out, global_path);
+ error = git_config_open_ondisk(out, git_buf_cstr(&path));
+ git_buf_free(&path);
+
+ return error;
}
diff --git a/src/config.h b/src/config.h
index 6345b0a5d..82e98ce51 100644
--- a/src/config.h
+++ b/src/config.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -25,4 +25,9 @@ struct git_config {
extern int git_config_find_global_r(git_buf *global_config_path);
extern int git_config_find_system_r(git_buf *system_config_path);
+extern int git_config_parse_bool(int *out, const char *bool_string);
+
+extern int git_config_lookup_map_value(
+ git_cvar_map *maps, size_t map_n, const char *value, int *out);
+
#endif
diff --git a/src/config_cache.c b/src/config_cache.c
new file mode 100644
index 000000000..ca9602e56
--- /dev/null
+++ b/src/config_cache.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+#include "fileops.h"
+#include "config.h"
+#include "git2/config.h"
+#include "vector.h"
+#include "filter.h"
+#include "repository.h"
+
+struct map_data {
+ const char *cvar_name;
+ git_cvar_map *maps;
+ size_t map_count;
+ int default_value;
+};
+
+/*
+ * core.eol
+ * Sets the line ending type to use in the working directory for
+ * files that have the text property set. Alternatives are lf, crlf
+ * and native, which uses the platform’s native line ending. The default
+ * value is native. See gitattributes(5) for more information on
+ * end-of-line conversion.
+ */
+static git_cvar_map _cvar_map_eol[] = {
+ {GIT_CVAR_FALSE, NULL, GIT_EOL_UNSET},
+ {GIT_CVAR_STRING, "lf", GIT_EOL_LF},
+ {GIT_CVAR_STRING, "crlf", GIT_EOL_CRLF},
+ {GIT_CVAR_STRING, "native", GIT_EOL_NATIVE}
+};
+
+/*
+ * core.autocrlf
+ * Setting this variable to "true" is almost the same as setting
+ * the text attribute to "auto" on all files except that text files are
+ * not guaranteed to be normalized: files that contain CRLF in the
+ * repository will not be touched. Use this setting if you want to have
+ * CRLF line endings in your working directory even though the repository
+ * does not have normalized line endings. This variable can be set to input,
+ * in which case no output conversion is performed.
+ */
+static git_cvar_map _cvar_map_autocrlf[] = {
+ {GIT_CVAR_FALSE, NULL, GIT_AUTO_CRLF_FALSE},
+ {GIT_CVAR_TRUE, NULL, GIT_AUTO_CRLF_TRUE},
+ {GIT_CVAR_STRING, "input", GIT_AUTO_CRLF_INPUT}
+};
+
+static struct map_data _cvar_maps[] = {
+ {"core.autocrlf", _cvar_map_autocrlf, ARRAY_SIZE(_cvar_map_autocrlf), GIT_AUTO_CRLF_DEFAULT},
+ {"core.eol", _cvar_map_eol, ARRAY_SIZE(_cvar_map_eol), GIT_EOL_DEFAULT}
+};
+
+int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar)
+{
+ *out = repo->cvar_cache[(int)cvar];
+
+ if (*out == GIT_CVAR_NOT_CACHED) {
+ struct map_data *data = &_cvar_maps[(int)cvar];
+ git_config *config;
+ int error;
+
+ error = git_repository_config__weakptr(&config, repo);
+ if (error < 0)
+ return error;
+
+ error = git_config_get_mapped(out,
+ config, data->cvar_name, data->maps, data->map_count);
+
+ if (error == GIT_ENOTFOUND)
+ *out = data->default_value;
+
+ else if (error < 0)
+ return error;
+
+ repo->cvar_cache[(int)cvar] = *out;
+ }
+
+ return 0;
+}
+
+void git_repository__cvar_cache_clear(git_repository *repo)
+{
+ int i;
+
+ for (i = 0; i < GIT_CVAR_CACHE_MAX; ++i)
+ repo->cvar_cache[i] = GIT_CVAR_NOT_CACHED;
+}
+
diff --git a/src/config_file.c b/src/config_file.c
index 481c593f4..cbc48bcd9 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -12,14 +12,17 @@
#include "buffer.h"
#include "git2/config.h"
#include "git2/types.h"
-
+#include "strmap.h"
#include <ctype.h>
+#include <sys/types.h>
+#include <regex.h>
+
+GIT__USE_STRMAP;
typedef struct cvar_t {
struct cvar_t *next;
- char *section;
- char *name;
+ char *key; /* TODO: we might be able to get rid of this */
char *value;
} cvar_t;
@@ -69,10 +72,10 @@ typedef struct {
typedef struct {
git_config_file parent;
- cvar_t_list var_list;
+ git_strmap *values;
struct {
- git_fbuffer buffer;
+ git_buf buffer;
char *read_ptr;
int line_number;
int eof;
@@ -83,344 +86,379 @@ typedef struct {
static int config_parse(diskfile_backend *cfg_file);
static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_value);
-static int config_write(diskfile_backend *cfg, cvar_t *var);
+static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value);
+
+static void set_parse_error(diskfile_backend *backend, int col, const char *error_str)
+{
+ giterr_set(GITERR_CONFIG, "Failed to parse config file: %s (in %s:%d, column %d)",
+ error_str, backend->file_path, backend->reader.line_number, col);
+}
static void cvar_free(cvar_t *var)
{
if (var == NULL)
return;
- git__free(var->section);
- git__free(var->name);
+ git__free(var->key);
git__free(var->value);
git__free(var);
}
-static void cvar_list_free(cvar_t_list *list)
+/* Take something the user gave us and make it nice for our hash function */
+static int normalize_name(const char *in, char **out)
{
- cvar_t *cur;
+ char *name, *fdot, *ldot;
+
+ assert(in && out);
+
+ name = git__strdup(in);
+ GITERR_CHECK_ALLOC(name);
+
+ fdot = strchr(name, '.');
+ ldot = strrchr(name, '.');
- while (!CVAR_LIST_EMPTY(list)) {
- cur = CVAR_LIST_HEAD(list);
- CVAR_LIST_REMOVE_HEAD(list);
- cvar_free(cur);
+ if (fdot == NULL || ldot == NULL) {
+ git__free(name);
+ giterr_set(GITERR_CONFIG,
+ "Invalid variable name: '%s'", in);
+ return -1;
}
+
+ /* Downcase up to the first dot and after the last one */
+ git__strntolower(name, fdot - name);
+ git__strtolower(ldot);
+
+ *out = name;
+ return 0;
}
-/*
- * Compare according to the git rules. Section contains the section as
- * it's stored internally. query is the full name as would be given to
- * 'git config'.
- */
-static int cvar_match_section(const char *section, const char *query)
+static void free_vars(git_strmap *values)
{
- const char *sdot, *qdot, *qsub;
- size_t section_len;
+ cvar_t *var = NULL;
- sdot = strchr(section, '.');
+ if (values == NULL)
+ return;
- /* If the section doesn't have any dots, it's easy */
- if (sdot == NULL)
- return !strncasecmp(section, query, strlen(section));
+ git_strmap_foreach_value(values, var,
+ while (var != NULL) {
+ cvar_t *next = CVAR_LIST_NEXT(var);
+ cvar_free(var);
+ var = next;
+ });
- /*
- * If it does have dots, compare the sections
- * case-insensitively. The comparison includes the dots.
- */
- section_len = sdot - section + 1;
- if (strncasecmp(section, query, sdot - section))
- return 0;
+ git_strmap_free(values);
+}
+
+static int config_open(git_config_file *cfg)
+{
+ int res;
+ diskfile_backend *b = (diskfile_backend *)cfg;
+
+ b->values = git_strmap_alloc();
+ GITERR_CHECK_ALLOC(b->values);
+
+ git_buf_init(&b->reader.buffer, 0);
+ res = git_futils_readbuffer(&b->reader.buffer, b->file_path);
- qsub = query + section_len;
- qdot = strchr(qsub, '.');
- /* Make sure the subsections are the same length */
- if (strlen(sdot + 1) != (size_t) (qdot - qsub))
+ /* It's fine if the file doesn't exist */
+ if (res == GIT_ENOTFOUND)
return 0;
- /* The subsection is case-sensitive */
- return !strncmp(sdot + 1, qsub, strlen(sdot + 1));
+ if (res < 0 || config_parse(b) < 0) {
+ free_vars(b->values);
+ b->values = NULL;
+ git_buf_free(&b->reader.buffer);
+ return -1;
+ }
+
+ git_buf_free(&b->reader.buffer);
+ return 0;
}
-static int cvar_match_name(const cvar_t *var, const char *str)
+static void backend_free(git_config_file *_backend)
{
- const char *name_start;
+ diskfile_backend *backend = (diskfile_backend *)_backend;
- if (!cvar_match_section(var->section, str)) {
- return 0;
- }
- /* Early exit if the lengths are different */
- name_start = strrchr(str, '.') + 1;
- if (strlen(var->name) != strlen(name_start))
- return 0;
+ if (backend == NULL)
+ return;
- return !strcasecmp(var->name, name_start);
+ git__free(backend->file_path);
+ free_vars(backend->values);
+ git__free(backend);
}
-static cvar_t *cvar_list_find(cvar_t_list *list, const char *name)
+static int file_foreach(git_config_file *backend, int (*fn)(const char *, const char *, void *), void *data)
{
- cvar_t *iter;
+ diskfile_backend *b = (diskfile_backend *)backend;
+ cvar_t *var;
+ const char *key;
- CVAR_LIST_FOREACH (list, iter) {
- if (cvar_match_name(iter, name))
- return iter;
- }
+ if (!b->values)
+ return 0;
+
+ git_strmap_foreach(b->values, key, var,
+ do {
+ if (fn(key, var->value, data) < 0)
+ break;
- return NULL;
+ var = CVAR_LIST_NEXT(var);
+ } while (var != NULL);
+ );
+
+ return 0;
}
-static int cvar_normalize_name(cvar_t *var, char **output)
+static int config_set(git_config_file *cfg, const char *name, const char *value)
{
- char *section_sp = strchr(var->section, ' ');
- char *quote, *name;
- size_t len;
- int ret;
+ cvar_t *var = NULL, *old_var;
+ diskfile_backend *b = (diskfile_backend *)cfg;
+ char *key;
+ khiter_t pos;
+ int rval;
+
+ if (normalize_name(name, &key) < 0)
+ return -1;
/*
- * The final string is going to be at most one char longer than
- * the input
+ * Try to find it in the existing values and update it if it
+ * only has one value.
*/
- len = strlen(var->section) + strlen(var->name) + 1;
- name = git__malloc(len + 1);
- if (name == NULL)
- return GIT_ENOMEM;
-
- /* If there aren't any spaces in the section, it's easy */
- if (section_sp == NULL) {
- ret = p_snprintf(name, len + 1, "%s.%s", var->section, var->name);
- if (ret < 0) {
- git__free(name);
- return git__throw(GIT_EOSERR, "Failed to normalize name. OS err: %s", strerror(errno));
+ pos = git_strmap_lookup_index(b->values, key);
+ if (git_strmap_valid_index(b->values, pos)) {
+ cvar_t *existing = git_strmap_value_at(b->values, pos);
+ char *tmp = NULL;
+
+ git__free(key);
+ if (existing->next != NULL) {
+ giterr_set(GITERR_CONFIG, "Multivar incompatible with simple set");
+ return -1;
+ }
+
+ if (value) {
+ tmp = git__strdup(value);
+ GITERR_CHECK_ALLOC(tmp);
}
- *output = name;
- return GIT_SUCCESS;
+ git__free(existing->value);
+ existing->value = tmp;
+
+ return config_write(b, existing->key, NULL, value);
}
- /*
- * If there are spaces, we replace the space by a dot, move
- * section name so it overwrites the first quotation mark and
- * replace the last quotation mark by a dot. We then append the
- * variable name.
- */
- strcpy(name, var->section);
- section_sp = strchr(name, ' ');
- *section_sp = '.';
- /* Remove first quote */
- quote = strchr(name, '"');
- memmove(quote, quote+1, strlen(quote+1));
- /* Remove second quote */
- quote = strchr(name, '"');
- *quote = '.';
- strcpy(quote+1, var->name);
-
- *output = name;
- return GIT_SUCCESS;
-}
+ var = git__malloc(sizeof(cvar_t));
+ GITERR_CHECK_ALLOC(var);
-static char *interiorize_section(const char *orig)
-{
- char *dot, *last_dot, *section, *ret;
- size_t len;
+ memset(var, 0x0, sizeof(cvar_t));
- dot = strchr(orig, '.');
- last_dot = strrchr(orig, '.');
- len = last_dot - orig;
+ var->key = key;
+ var->value = NULL;
- /* No subsection, this is easy */
- if (last_dot == dot)
- return git__strndup(orig, dot - orig);
+ if (value) {
+ var->value = git__strdup(value);
+ GITERR_CHECK_ALLOC(var->value);
+ }
- section = git__strndup(orig, len);
- if (section == NULL)
- return NULL;
+ if (config_write(b, key, NULL, value) < 0) {
+ cvar_free(var);
+ return -1;
+ }
- ret = section;
- len = dot - orig;
- git__strntolower(section, len);
- return ret;
+ git_strmap_insert2(b->values, key, var, old_var, rval);
+ if (rval < 0)
+ return -1;
+ if (old_var != NULL)
+ cvar_free(old_var);
+
+ return 0;
}
-static int config_open(git_config_file *cfg)
+/*
+ * Internal function that actually gets the value in string form
+ */
+static int config_get(git_config_file *cfg, const char *name, const char **out)
{
- int error;
diskfile_backend *b = (diskfile_backend *)cfg;
+ char *key;
+ khiter_t pos;
- error = git_futils_readbuffer(&b->reader.buffer, b->file_path);
-
- /* It's fine if the file doesn't exist */
- if (error == GIT_ENOTFOUND)
- return GIT_SUCCESS;
-
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (normalize_name(name, &key) < 0)
+ return -1;
- error = config_parse(b);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ pos = git_strmap_lookup_index(b->values, key);
+ git__free(key);
- git_futils_freebuffer(&b->reader.buffer);
+ /* no error message; the config system will write one */
+ if (!git_strmap_valid_index(b->values, pos))
+ return GIT_ENOTFOUND;
- return GIT_SUCCESS;
+ *out = ((cvar_t *)git_strmap_value_at(b->values, pos))->value;
- cleanup:
- cvar_list_free(&b->var_list);
- git_futils_freebuffer(&b->reader.buffer);
-
- return git__rethrow(error, "Failed to open config");
+ return 0;
}
-static void backend_free(git_config_file *_backend)
+static int config_get_multivar(
+ git_config_file *cfg,
+ const char *name,
+ const char *regex_str,
+ int (*fn)(const char *, void *),
+ void *data)
{
- diskfile_backend *backend = (diskfile_backend *)_backend;
+ cvar_t *var;
+ diskfile_backend *b = (diskfile_backend *)cfg;
+ char *key;
+ khiter_t pos;
- if (backend == NULL)
- return;
+ if (normalize_name(name, &key) < 0)
+ return -1;
- git__free(backend->file_path);
- cvar_list_free(&backend->var_list);
+ pos = git_strmap_lookup_index(b->values, key);
+ git__free(key);
- git__free(backend);
-}
+ if (!git_strmap_valid_index(b->values, pos))
+ return GIT_ENOTFOUND;
-static int file_foreach(git_config_file *backend, int (*fn)(const char *, const char *, void *), void *data)
-{
- int ret = GIT_SUCCESS;
- cvar_t *var;
- diskfile_backend *b = (diskfile_backend *)backend;
+ var = git_strmap_value_at(b->values, pos);
- CVAR_LIST_FOREACH(&b->var_list, var) {
- char *normalized = NULL;
+ if (regex_str != NULL) {
+ regex_t regex;
+ int result;
- ret = cvar_normalize_name(var, &normalized);
- if (ret < GIT_SUCCESS)
- return ret;
+ /* regex matching; build the regex */
+ result = regcomp(&regex, regex_str, REG_EXTENDED);
+ if (result < 0) {
+ giterr_set_regex(&regex, result);
+ return -1;
+ }
- ret = fn(normalized, var->value, data);
- git__free(normalized);
- if (ret)
- break;
+ /* and throw the callback only on the variables that
+ * match the regex */
+ do {
+ if (regexec(&regex, var->value, 0, NULL, 0) == 0) {
+ /* early termination by the user is not an error;
+ * just break and return successfully */
+ if (fn(var->value, data) < 0)
+ break;
+ }
+
+ var = var->next;
+ } while (var != NULL);
+ regfree(&regex);
+ } else {
+ /* no regex; go through all the variables */
+ do {
+ /* early termination by the user is not an error;
+ * just break and return successfully */
+ if (fn(var->value, data) < 0)
+ break;
+
+ var = var->next;
+ } while (var != NULL);
}
- return ret;
+ return 0;
}
-static int config_set(git_config_file *cfg, const char *name, const char *value)
+static int config_set_multivar(
+ git_config_file *cfg, const char *name, const char *regexp, const char *value)
{
- cvar_t *var = NULL;
- cvar_t *existing = NULL;
- int error = GIT_SUCCESS;
- const char *last_dot;
+ int replaced = 0;
+ cvar_t *var, *newvar;
diskfile_backend *b = (diskfile_backend *)cfg;
+ char *key;
+ regex_t preg;
+ int result;
+ khiter_t pos;
- /*
- * If it already exists, we just need to update its value.
- */
- existing = cvar_list_find(&b->var_list, name);
- if (existing != NULL) {
- char *tmp = value ? git__strdup(value) : NULL;
- if (tmp == NULL && value != NULL)
- return GIT_ENOMEM;
+ assert(regexp);
- git__free(existing->value);
- existing->value = tmp;
+ if (normalize_name(name, &key) < 0)
+ return -1;
- return config_write(b, existing);
+ pos = git_strmap_lookup_index(b->values, key);
+ if (!git_strmap_valid_index(b->values, pos)) {
+ git__free(key);
+ return GIT_ENOTFOUND;
}
- /*
- * Otherwise, create it and stick it at the end of the queue. If
- * value is NULL, we return an error, because you can't delete a
- * variable that doesn't exist.
- */
+ var = git_strmap_value_at(b->values, pos);
- if (value == NULL)
- return git__throw(GIT_ENOTFOUND, "Can't delete non-exitent variable");
-
- last_dot = strrchr(name, '.');
- if (last_dot == NULL) {
- return git__throw(GIT_EINVALIDTYPE, "Variables without section aren't allowed");
+ result = regcomp(&preg, regexp, REG_EXTENDED);
+ if (result < 0) {
+ git__free(key);
+ giterr_set_regex(&preg, result);
+ return -1;
}
- var = git__malloc(sizeof(cvar_t));
- if (var == NULL)
- return GIT_ENOMEM;
+ for (;;) {
+ if (regexec(&preg, var->value, 0, NULL, 0) == 0) {
+ char *tmp = git__strdup(value);
+ GITERR_CHECK_ALLOC(tmp);
- memset(var, 0x0, sizeof(cvar_t));
+ git__free(var->value);
+ var->value = tmp;
+ replaced = 1;
+ }
- var->section = interiorize_section(name);
- if (var->section == NULL) {
- error = GIT_ENOMEM;
- goto out;
- }
+ if (var->next == NULL)
+ break;
- var->name = git__strdup(last_dot + 1);
- if (var->name == NULL) {
- error = GIT_ENOMEM;
- goto out;
+ var = var->next;
}
- var->value = value ? git__strdup(value) : NULL;
- if (var->value == NULL && value != NULL) {
- error = GIT_ENOMEM;
- goto out;
+ /* If we've reached the end of the variables and we haven't found it yet, we need to append it */
+ if (!replaced) {
+ newvar = git__malloc(sizeof(cvar_t));
+ GITERR_CHECK_ALLOC(newvar);
+
+ memset(newvar, 0x0, sizeof(cvar_t));
+
+ newvar->key = git__strdup(var->key);
+ GITERR_CHECK_ALLOC(newvar->key);
+
+ newvar->value = git__strdup(value);
+ GITERR_CHECK_ALLOC(newvar->value);
+
+ var->next = newvar;
}
- CVAR_LIST_APPEND(&b->var_list, var);
- error = config_write(b, var);
+ result = config_write(b, key, &preg, value);
- out:
- if (error < GIT_SUCCESS)
- cvar_free(var);
+ git__free(key);
+ regfree(&preg);
- return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to set config value");
+ return result;
}
-/*
- * Internal function that actually gets the value in string form
- */
-static int config_get(git_config_file *cfg, const char *name, const char **out)
+static int config_delete(git_config_file *cfg, const char *name)
{
cvar_t *var;
- int error = GIT_SUCCESS;
diskfile_backend *b = (diskfile_backend *)cfg;
+ char *key;
+ int result;
+ khiter_t pos;
- var = cvar_list_find(&b->var_list, name);
-
- if (var == NULL)
- return git__throw(GIT_ENOTFOUND, "Variable '%s' not found", name);
+ if (normalize_name(name, &key) < 0)
+ return -1;
- *out = var->value;
+ pos = git_strmap_lookup_index(b->values, key);
+ git__free(key);
- return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to get config value for %s", name);
-}
+ if (!git_strmap_valid_index(b->values, pos))
+ return GIT_ENOTFOUND;
-static int config_delete(git_config_file *cfg, const char *name)
-{
- int error;
- cvar_t *iter, *prev = NULL;
- diskfile_backend *b = (diskfile_backend *)cfg;
+ var = git_strmap_value_at(b->values, pos);
- CVAR_LIST_FOREACH (&b->var_list, iter) {
- /* This is a bit hacky because we use a singly-linked list */
- if (cvar_match_name(iter, name)) {
- if (CVAR_LIST_HEAD(&b->var_list) == iter)
- CVAR_LIST_HEAD(&b->var_list) = CVAR_LIST_NEXT(iter);
- else
- CVAR_LIST_REMOVE_AFTER(prev);
-
- git__free(iter->value);
- iter->value = NULL;
- error = config_write(b, iter);
- cvar_free(iter);
- return error == GIT_SUCCESS ?
- GIT_SUCCESS :
- git__rethrow(error, "Failed to update config file");
- }
- /* Store it for the next round */
- prev = iter;
+ if (var->next != NULL) {
+ giterr_set(GITERR_CONFIG, "Cannot delete multivar with a single delete");
+ return -1;
}
- return git__throw(GIT_ENOTFOUND, "Variable '%s' not found", name);
+ git_strmap_delete_at(b->values, pos);
+
+ result = config_write(b, var->key, NULL, NULL);
+
+ cvar_free(var);
+ return result;
}
int git_config_file__ondisk(git_config_file **out, const char *path)
@@ -428,27 +466,25 @@ int git_config_file__ondisk(git_config_file **out, const char *path)
diskfile_backend *backend;
backend = git__malloc(sizeof(diskfile_backend));
- if (backend == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(backend);
memset(backend, 0x0, sizeof(diskfile_backend));
backend->file_path = git__strdup(path);
- if (backend->file_path == NULL) {
- git__free(backend);
- return GIT_ENOMEM;
- }
+ GITERR_CHECK_ALLOC(backend->file_path);
backend->parent.open = config_open;
backend->parent.get = config_get;
+ backend->parent.get_multivar = config_get_multivar;
backend->parent.set = config_set;
+ backend->parent.set_multivar = config_set_multivar;
backend->parent.del = config_delete;
backend->parent.foreach = file_foreach;
backend->parent.free = backend_free;
*out = (git_config_file *)backend;
- return GIT_SUCCESS;
+ return 0;
}
static int cfg_getchar_raw(diskfile_backend *cfg)
@@ -489,7 +525,7 @@ static int cfg_getchar(diskfile_backend *cfg_file, int flags)
assert(cfg_file->reader.read_ptr);
do c = cfg_getchar_raw(cfg_file);
- while (skip_whitespace && isspace(c) &&
+ while (skip_whitespace && git__isspace(c) &&
!cfg_file->reader.eof);
if (skip_comments && (c == '#' || c == ';')) {
@@ -527,7 +563,7 @@ static int cfg_peek(diskfile_backend *cfg, int flags)
/*
* Read and consume a line, returning it in newly-allocated memory.
*/
-static char *cfg_readline(diskfile_backend *cfg)
+static char *cfg_readline(diskfile_backend *cfg, bool skip_whitespace)
{
char *line = NULL;
char *line_src, *line_end;
@@ -535,9 +571,11 @@ static char *cfg_readline(diskfile_backend *cfg)
line_src = cfg->reader.read_ptr;
- /* Skip empty empty lines */
- while (isspace(*line_src))
- ++line_src;
+ if (skip_whitespace) {
+ /* Skip empty empty lines */
+ while (git__isspace(*line_src))
+ ++line_src;
+ }
line_end = strchr(line_src, '\n');
@@ -554,7 +592,7 @@ static char *cfg_readline(diskfile_backend *cfg)
memcpy(line, line_src, line_len);
do line[line_len] = '\0';
- while (line_len-- > 0 && isspace(line[line_len]));
+ while (line_len-- > 0 && git__isspace(line[line_len]));
if (*line_end == '\n')
line_end++;
@@ -597,12 +635,11 @@ GIT_INLINE(int) config_keychar(int c)
return isalnum(c) || c == '-';
}
-static int parse_section_header_ext(const char *line, const char *base_name, char **section_name)
+static int parse_section_header_ext(diskfile_backend *cfg, const char *line, const char *base_name, char **section_name)
{
int c, rpos;
char *first_quote, *last_quote;
git_buf buf = GIT_BUF_INIT;
- int error = GIT_SUCCESS;
int quote_marks;
/*
* base_name is what came before the space. We should be at the
@@ -613,8 +650,10 @@ static int parse_section_header_ext(const char *line, const char *base_name, cha
first_quote = strchr(line, '"');
last_quote = strrchr(line, '"');
- if (last_quote - first_quote == 0)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse ext header. There is no final quotation mark");
+ if (last_quote - first_quote == 0) {
+ set_parse_error(cfg, 0, "Missing closing quotation mark in section header");
+ return -1;
+ }
git_buf_grow(&buf, strlen(base_name) + last_quote - first_quote + 2);
git_buf_printf(&buf, "%s.", base_name);
@@ -631,26 +670,30 @@ static int parse_section_header_ext(const char *line, const char *base_name, cha
*/
do {
if (quote_marks == 2) {
- error = git__throw(GIT_EOBJCORRUPTED, "Falied to parse ext header. Text after closing quote");
- goto out;
-
+ set_parse_error(cfg, rpos, "Unexpected text after closing quotes");
+ git_buf_free(&buf);
+ return -1;
}
switch (c) {
case '"':
++quote_marks;
continue;
+
case '\\':
c = line[rpos++];
+
switch (c) {
case '"':
case '\\':
break;
+
default:
- error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse ext header. Unsupported escape char \\%c", c);
- goto out;
+ set_parse_error(cfg, rpos, "Unsupported escape sequence");
+ git_buf_free(&buf);
+ return -1;
}
- break;
+
default:
break;
}
@@ -658,61 +701,53 @@ static int parse_section_header_ext(const char *line, const char *base_name, cha
git_buf_putc(&buf, c);
} while ((c = line[rpos++]) != ']');
- *section_name = git__strdup(git_buf_cstr(&buf));
- out:
- git_buf_free(&buf);
-
- return error;
+ *section_name = git_buf_detach(&buf);
+ return 0;
}
static int parse_section_header(diskfile_backend *cfg, char **section_out)
{
char *name, *name_end;
int name_length, c, pos;
- int error = GIT_SUCCESS;
+ int result;
char *line;
- line = cfg_readline(cfg);
+ line = cfg_readline(cfg, true);
if (line == NULL)
- return GIT_ENOMEM;
+ return -1;
/* find the end of the variable's name */
name_end = strchr(line, ']');
if (name_end == NULL) {
git__free(line);
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse header. Can't find header name end");
+ set_parse_error(cfg, 0, "Missing ']' in section header");
+ return -1;
}
name = (char *)git__malloc((size_t)(name_end - line) + 1);
- if (name == NULL) {
- git__free(line);
- return GIT_ENOMEM;
- }
+ GITERR_CHECK_ALLOC(name);
name_length = 0;
pos = 0;
/* Make sure we were given a section header */
c = line[pos++];
- if (c != '[') {
- error = git__throw(GIT_ERROR, "Failed to parse header. Didn't get section header. This is a bug");
- goto error;
- }
+ assert(c == '[');
c = line[pos++];
do {
- if (isspace(c)){
+ if (git__isspace(c)){
name[name_length] = '\0';
- error = parse_section_header_ext(line, name, section_out);
+ result = parse_section_header_ext(cfg, line, name, section_out);
git__free(line);
git__free(name);
- return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse header");
+ return result;
}
if (!config_keychar(c) && c != '.') {
- error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse header. Wrong format on header");
- goto error;
+ set_parse_error(cfg, pos, "Unexpected character in header");
+ goto fail_parse;
}
name[name_length++] = (char) tolower(c);
@@ -720,28 +755,29 @@ static int parse_section_header(diskfile_backend *cfg, char **section_out)
} while ((c = line[pos++]) != ']');
if (line[pos - 1] != ']') {
- error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse header. Config file ended unexpectedly");
- goto error;
+ set_parse_error(cfg, pos, "Unexpected end of file");
+ goto fail_parse;
}
- name[name_length] = 0;
git__free(line);
- git__strtolower(name);
+
+ name[name_length] = 0;
*section_out = name;
- return GIT_SUCCESS;
-error:
+ return 0;
+
+fail_parse:
git__free(line);
git__free(name);
- return error;
+ return -1;
}
static int skip_bom(diskfile_backend *cfg)
{
- static const char *utf8_bom = "\xef\xbb\xbf";
+ static const char utf8_bom[] = "\xef\xbb\xbf";
- if (cfg->reader.buffer.len < sizeof(utf8_bom))
- return GIT_SUCCESS;
+ if (cfg->reader.buffer.size < sizeof(utf8_bom))
+ return 0;
if (memcmp(cfg->reader.read_ptr, utf8_bom, sizeof(utf8_bom)) == 0)
cfg->reader.read_ptr += sizeof(utf8_bom);
@@ -750,7 +786,7 @@ static int skip_bom(diskfile_backend *cfg)
shit with the BoM
*/
- return GIT_SUCCESS;
+ return 0;
}
/*
@@ -792,9 +828,9 @@ static int skip_bom(diskfile_backend *cfg)
boolean_false = "no" | "0" | "false" | "off"
*/
-static void strip_comments(char *line)
+static int strip_comments(char *line, int in_quotes)
{
- int quote_count = 0;
+ int quote_count = in_quotes;
char *ptr;
for (ptr = line; *ptr; ++ptr) {
@@ -807,30 +843,37 @@ static void strip_comments(char *line)
}
}
- if (isspace(ptr[-1])) {
- /* TODO skip whitespace */
+ /* skip any space at the end */
+ if (git__isspace(ptr[-1])) {
+ ptr--;
}
+ ptr[0] = '\0';
+
+ return quote_count;
}
static int config_parse(diskfile_backend *cfg_file)
{
- int error = GIT_SUCCESS, c;
+ int c;
char *current_section = NULL;
char *var_name;
char *var_value;
- cvar_t *var;
+ cvar_t *var, *existing;
+ git_buf buf = GIT_BUF_INIT;
+ int result = 0;
+ khiter_t pos;
/* Initialize the reading position */
- cfg_file->reader.read_ptr = cfg_file->reader.buffer.data;
+ cfg_file->reader.read_ptr = cfg_file->reader.buffer.ptr;
cfg_file->reader.eof = 0;
/* If the file is empty, there's nothing for us to do */
if (*cfg_file->reader.read_ptr == '\0')
- return GIT_SUCCESS;
+ return 0;
skip_bom(cfg_file);
- while (error == GIT_SUCCESS && !cfg_file->reader.eof) {
+ while (result == 0 && !cfg_file->reader.eof) {
c = cfg_peek(cfg_file, SKIP_WHITESPACE);
@@ -842,7 +885,7 @@ static int config_parse(diskfile_backend *cfg_file)
case '[': /* section header, new section begins */
git__free(current_section);
current_section = NULL;
- error = parse_section_header(cfg_file, &current_section);
+ result = parse_section_header(cfg_file, &current_section);
break;
case ';':
@@ -851,100 +894,119 @@ static int config_parse(diskfile_backend *cfg_file)
break;
default: /* assume variable declaration */
- error = parse_variable(cfg_file, &var_name, &var_value);
-
- if (error < GIT_SUCCESS)
+ result = parse_variable(cfg_file, &var_name, &var_value);
+ if (result < 0)
break;
var = git__malloc(sizeof(cvar_t));
- if (var == NULL) {
- error = GIT_ENOMEM;
- break;
- }
+ GITERR_CHECK_ALLOC(var);
memset(var, 0x0, sizeof(cvar_t));
- var->section = git__strdup(current_section);
- if (var->section == NULL) {
- error = GIT_ENOMEM;
- git__free(var);
- break;
- }
+ git__strtolower(var_name);
+ git_buf_printf(&buf, "%s.%s", current_section, var_name);
+ git__free(var_name);
- var->name = var_name;
+ if (git_buf_oom(&buf))
+ return -1;
+
+ var->key = git_buf_detach(&buf);
var->value = var_value;
- git__strtolower(var->name);
- CVAR_LIST_APPEND(&cfg_file->var_list, var);
+ /* Add or append the new config option */
+ pos = git_strmap_lookup_index(cfg_file->values, var->key);
+ if (!git_strmap_valid_index(cfg_file->values, pos)) {
+ git_strmap_insert(cfg_file->values, var->key, var, result);
+ if (result < 0)
+ break;
+ result = 0;
+ } else {
+ existing = git_strmap_value_at(cfg_file->values, pos);
+ while (existing->next != NULL) {
+ existing = existing->next;
+ }
+ existing->next = var;
+ }
break;
}
}
git__free(current_section);
-
- return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse config");
+ return result;
}
-static int write_section(git_filebuf *file, cvar_t *var)
+static int write_section(git_filebuf *file, const char *key)
{
- int error;
+ int result;
+ const char *dot;
+ git_buf buf = GIT_BUF_INIT;
- error = git_filebuf_printf(file, "[%s]\n", var->section);
- if (error < GIT_SUCCESS)
- return error;
+ /* All of this just for [section "subsection"] */
+ dot = strchr(key, '.');
+ git_buf_putc(&buf, '[');
+ if (dot == NULL) {
+ git_buf_puts(&buf, key);
+ } else {
+ git_buf_put(&buf, key, dot - key);
+ /* TODO: escape */
+ git_buf_printf(&buf, " \"%s\"", dot + 1);
+ }
+ git_buf_puts(&buf, "]\n");
+
+ if (git_buf_oom(&buf))
+ return -1;
+
+ result = git_filebuf_write(file, git_buf_cstr(&buf), buf.size);
+ git_buf_free(&buf);
- error = git_filebuf_printf(file, " %s = %s\n", var->name, var->value);
- return error;
+ return result;
}
/*
* This is pretty much the parsing, except we write out anything we don't have
*/
-static int config_write(diskfile_backend *cfg, cvar_t *var)
+static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char* value)
{
- int error = GIT_SUCCESS, c;
- int section_matches = 0, last_section_matched = 0;
- char *current_section = NULL;
- char *var_name, *var_value, *data_start;
+ int result, c;
+ int section_matches = 0, last_section_matched = 0, preg_replaced = 0, write_trailer = 0;
+ const char *pre_end = NULL, *post_start = NULL, *data_start;
+ char *current_section = NULL, *section, *name, *ldot;
git_filebuf file = GIT_FILEBUF_INIT;
- const char *pre_end = NULL, *post_start = NULL;
/* We need to read in our own config file */
- error = git_futils_readbuffer(&cfg->reader.buffer, cfg->file_path);
- if (error < GIT_SUCCESS && error != GIT_ENOTFOUND) {
- return git__rethrow(error, "Failed to read existing config file %s", cfg->file_path);
- }
+ result = git_futils_readbuffer(&cfg->reader.buffer, cfg->file_path);
/* Initialise the reading position */
- if (error == GIT_ENOTFOUND) {
- error = GIT_SUCCESS;
+ if (result == GIT_ENOTFOUND) {
cfg->reader.read_ptr = NULL;
cfg->reader.eof = 1;
data_start = NULL;
- cfg->reader.buffer.len = 0;
- cfg->reader.buffer.data = NULL;
- } else {
- cfg->reader.read_ptr = cfg->reader.buffer.data;
+ git_buf_clear(&cfg->reader.buffer);
+ } else if (result == 0) {
+ cfg->reader.read_ptr = cfg->reader.buffer.ptr;
cfg->reader.eof = 0;
data_start = cfg->reader.read_ptr;
+ } else {
+ return -1; /* OS error when reading the file */
}
/* Lock the file */
- error = git_filebuf_open(&file, cfg->file_path, 0);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to lock config file");
+ if (git_filebuf_open(&file, cfg->file_path, 0) < 0)
+ return -1;
skip_bom(cfg);
+ ldot = strrchr(key, '.');
+ name = ldot + 1;
+ section = git__strndup(key, ldot - key);
- while (error == GIT_SUCCESS && !cfg->reader.eof) {
+ while (!cfg->reader.eof) {
c = cfg_peek(cfg, SKIP_WHITESPACE);
- switch (c) {
- case '\0': /* We've arrived at the end of the file */
+ if (c == '\0') { /* We've arrived at the end of the file */
break;
- case '[': /* section header, new section begins */
+ } else if (c == '[') { /* section header, new section begins */
/*
* We set both positions to the current one in case we
* need to add a variable to the end of a section. In that
@@ -953,23 +1015,22 @@ static int config_write(diskfile_backend *cfg, cvar_t *var)
* default case will take care of updating them.
*/
pre_end = post_start = cfg->reader.read_ptr;
- if (current_section)
- git__free(current_section);
- error = parse_section_header(cfg, &current_section);
- if (error < GIT_SUCCESS)
- break;
+
+ git__free(current_section);
+ current_section = NULL;
+ if (parse_section_header(cfg, &current_section) < 0)
+ goto rewrite_fail;
/* Keep track of when it stops matching */
last_section_matched = section_matches;
- section_matches = !strcmp(current_section, var->section);
- break;
+ section_matches = !strcmp(current_section, section);
+ }
- case ';':
- case '#':
+ else if (c == ';' || c == '#') {
cfg_consume_line(cfg);
- break;
+ }
- default:
+ else {
/*
* If the section doesn't match, but the last section did,
* it means we need to add a variable (so skip the line
@@ -983,58 +1044,54 @@ static int config_write(diskfile_backend *cfg, cvar_t *var)
if (!section_matches) {
if (!last_section_matched) {
cfg_consume_line(cfg);
- break;
+ continue;
}
} else {
- int cmp = -1;
+ int has_matched = 0;
+ char *var_name, *var_value;
pre_end = cfg->reader.read_ptr;
- if ((error = parse_variable(cfg, &var_name, &var_value)) == GIT_SUCCESS)
- cmp = strcasecmp(var->name, var_name);
+ if (parse_variable(cfg, &var_name, &var_value) < 0)
+ goto rewrite_fail;
+
+ /* First try to match the name of the variable */
+ if (strcasecmp(name, var_name) == 0)
+ has_matched = 1;
+
+ /* If the name matches, and we have a regex to match the
+ * value, try to match it */
+ if (has_matched && preg != NULL)
+ has_matched = (regexec(preg, var_value, 0, NULL, 0) == 0);
git__free(var_name);
git__free(var_value);
- if (cmp != 0)
- break;
+ /* if there is no match, keep going */
+ if (!has_matched)
+ continue;
post_start = cfg->reader.read_ptr;
}
- /*
- * We've found the variable we wanted to change, so
- * write anything up to it
- */
- error = git_filebuf_write(&file, data_start, pre_end - data_start);
- if (error < GIT_SUCCESS) {
- git__rethrow(error, "Failed to write the first part of the file");
- break;
- }
+ /* We've found the variable we wanted to change, so
+ * write anything up to it */
+ git_filebuf_write(&file, data_start, pre_end - data_start);
+ preg_replaced = 1;
- /*
- * Then replace the variable. If the value is NULL, it
- * means we want to delete it, so pretend everything went
- * fine
- */
- if (var->value == NULL)
- error = GIT_SUCCESS;
- else
- error = git_filebuf_printf(&file, "\t%s = %s\n", var->name, var->value);
- if (error < GIT_SUCCESS) {
- git__rethrow(error, "Failed to overwrite the variable");
- break;
+ /* Then replace the variable. If the value is NULL, it
+ * means we want to delete it, so don't write anything. */
+ if (value != NULL) {
+ git_filebuf_printf(&file, "\t%s = %s\n", name, value);
}
- /* And then the write out rest of the file */
- error = git_filebuf_write(&file, post_start,
- cfg->reader.buffer.len - (post_start - data_start));
-
- if (error < GIT_SUCCESS) {
- git__rethrow(error, "Failed to write the rest of the file");
- break;
+ /* multiline variable? we need to keep reading lines to match */
+ if (preg != NULL) {
+ data_start = post_start;
+ continue;
}
- goto cleanup;
+ write_trailer = 1;
+ break; /* break from the loop */
}
}
@@ -1047,127 +1104,166 @@ static int config_write(diskfile_backend *cfg, cvar_t *var)
* 2) we didn't find a section for us so we need to create it
* ourselves.
*
- * Either way we need to write out the whole file.
+ * 3) we're setting a multivar with a regex, which means we
+ * continue to search for matching values
+ *
+ * In the last case, if we've already replaced a value, we
+ * want to write the rest of the file. Otherwise we need to write
+ * out the whole file and then the new variable.
*/
+ if (write_trailer) {
+ /* Write out rest of the file */
+ git_filebuf_write(&file, post_start, cfg->reader.buffer.size - (post_start - data_start));
+ } else {
+ if (preg_replaced) {
+ git_filebuf_printf(&file, "\n%s", data_start);
+ } else {
+ git_filebuf_write(&file, cfg->reader.buffer.ptr, cfg->reader.buffer.size);
+
+ /* And now if we just need to add a variable */
+ if (!section_matches && write_section(&file, section) < 0)
+ goto rewrite_fail;
+
+ /* Sanity check: if we are here, and value is NULL, that means that somebody
+ * touched the config file after our intial read. We should probably assert()
+ * this, but instead we'll handle it gracefully with an error. */
+ if (value == NULL) {
+ giterr_set(GITERR_CONFIG,
+ "Race condition when writing a config file (a cvar has been removed)");
+ goto rewrite_fail;
+ }
- error = git_filebuf_write(&file, cfg->reader.buffer.data, cfg->reader.buffer.len);
- if (error < GIT_SUCCESS) {
- git__rethrow(error, "Failed to write original config content");
- goto cleanup;
+ git_filebuf_printf(&file, "\t%s = %s\n", name, value);
+ }
}
- /* And now if we just need to add a variable */
- if (section_matches) {
- error = git_filebuf_printf(&file, "\t%s = %s\n", var->name, var->value);
- goto cleanup;
- }
+ git__free(section);
+ git__free(current_section);
- /* Or maybe we need to write out a whole section */
- error = write_section(&file, var);
- if (error < GIT_SUCCESS)
- git__rethrow(error, "Failed to write new section");
+ result = git_filebuf_commit(&file, GIT_CONFIG_FILE_MODE);
+ git_buf_free(&cfg->reader.buffer);
+ return result;
- cleanup:
+rewrite_fail:
+ git__free(section);
git__free(current_section);
- if (error < GIT_SUCCESS)
- git_filebuf_cleanup(&file);
- else
- error = git_filebuf_commit(&file, GIT_CONFIG_FILE_MODE);
-
- git_futils_freebuffer(&cfg->reader.buffer);
- return error;
+ git_filebuf_cleanup(&file);
+ git_buf_free(&cfg->reader.buffer);
+ return -1;
}
-static int is_multiline_var(const char *str)
+/* '\"' -> '"' etc */
+static char *fixup_line(const char *ptr, int quote_count)
{
- char *end = strrchr(str, '\0') - 1;
+ char *str = git__malloc(strlen(ptr) + 1);
+ char *out = str, *esc;
+ const char *escapes = "ntb\"\\";
+ const char *escaped = "\n\t\b\"\\";
- while (isspace(*end))
- --end;
+ if (str == NULL)
+ return NULL;
- return *end == '\\';
+ while (*ptr != '\0') {
+ if (*ptr == '"') {
+ quote_count++;
+ } else if (*ptr != '\\') {
+ *out++ = *ptr;
+ } else {
+ /* backslash, check the next char */
+ ptr++;
+ /* if we're at the end, it's a multiline, so keep the backslash */
+ if (*ptr == '\0') {
+ *out++ = '\\';
+ goto out;
+ }
+ if ((esc = strchr(escapes, *ptr)) != NULL) {
+ *out++ = escaped[esc - escapes];
+ } else {
+ git__free(str);
+ giterr_set(GITERR_CONFIG, "Invalid escape at %s", ptr);
+ return NULL;
+ }
+ }
+ ptr++;
+ }
+
+out:
+ *out = '\0';
+
+ return str;
}
-static int parse_multiline_variable(diskfile_backend *cfg, const char *first, char **out)
+static int is_multiline_var(const char *str)
{
- char *line = NULL, *end;
- int error = GIT_SUCCESS, ret;
- size_t len;
- char *buf;
+ const char *end = str + strlen(str);
+ return (end > str) && (end[-1] == '\\');
+}
+
+static int parse_multiline_variable(diskfile_backend *cfg, git_buf *value, int in_quotes)
+{
+ char *line = NULL, *proc_line = NULL;
+ int quote_count;
/* Check that the next line exists */
- line = cfg_readline(cfg);
+ line = cfg_readline(cfg, false);
if (line == NULL)
- return GIT_ENOMEM;
+ return -1;
/* We've reached the end of the file, there is input missing */
if (line[0] == '\0') {
- error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse multiline var. File ended unexpectedly");
- goto out;
+ set_parse_error(cfg, 0, "Unexpected end of file while parsing multine var");
+ git__free(line);
+ return -1;
}
- strip_comments(line);
+ quote_count = strip_comments(line, !!in_quotes);
/* If it was just a comment, pretend it didn't exist */
if (line[0] == '\0') {
- error = parse_multiline_variable(cfg, first, out);
- goto out;
+ git__free(line);
+ return parse_multiline_variable(cfg, value, quote_count);
+ /* TODO: unbounded recursion. This **could** be exploitable */
}
- /* Find the continuation character '\' and strip the whitespace */
- end = strrchr(first, '\\');
- while (isspace(end[-1]))
- --end;
-
- *end = '\0'; /* Terminate the string here */
-
- len = strlen(first) + strlen(line) + 2;
- buf = git__malloc(len);
- if (buf == NULL) {
- error = GIT_ENOMEM;
- goto out;
- }
+ /* Drop the continuation character '\': to closely follow the UNIX
+ * standard, this character **has** to be last one in the buf, with
+ * no whitespace after it */
+ assert(is_multiline_var(value->ptr));
+ git_buf_truncate(value, git_buf_len(value) - 1);
- ret = p_snprintf(buf, len, "%s %s", first, line);
- if (ret < 0) {
- error = git__throw(GIT_EOSERR, "Failed to parse multiline var. Failed to put together two lines. OS err: %s", strerror(errno));
- git__free(buf);
- goto out;
+ proc_line = fixup_line(line, in_quotes);
+ if (proc_line == NULL) {
+ git__free(line);
+ return -1;
}
+ /* add this line to the multiline var */
+ git_buf_puts(value, proc_line);
+ git__free(line);
+ git__free(proc_line);
/*
- * If we need to continue reading the next line, pretend
- * everything we've read up to now was in one line and call
- * ourselves.
+ * If we need to continue reading the next line, let's just
+ * keep putting stuff in the buffer
*/
- if (is_multiline_var(buf)) {
- char *final_val;
- error = parse_multiline_variable(cfg, buf, &final_val);
- git__free(buf);
- buf = final_val;
- }
-
- *out = buf;
+ if (is_multiline_var(value->ptr))
+ return parse_multiline_variable(cfg, value, quote_count);
- out:
- git__free(line);
- return error;
+ return 0;
}
static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_value)
{
- char *tmp;
- int error = GIT_SUCCESS;
const char *var_end = NULL;
const char *value_start = NULL;
char *line;
+ int quote_count;
- line = cfg_readline(cfg);
+ line = cfg_readline(cfg, true);
if (line == NULL)
- return GIT_ENOMEM;
+ return -1;
- strip_comments(line);
+ quote_count = strip_comments(line, 0);
var_end = strchr(line, '=');
@@ -1176,57 +1272,47 @@ static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_val
else
value_start = var_end + 1;
- if (isspace(var_end[-1])) {
+ if (git__isspace(var_end[-1])) {
do var_end--;
- while (isspace(var_end[0]));
+ while (git__isspace(var_end[0]));
}
- tmp = git__strndup(line, var_end - line + 1);
- if (tmp == NULL) {
- error = GIT_ENOMEM;
- goto out;
- }
+ *var_name = git__strndup(line, var_end - line + 1);
+ GITERR_CHECK_ALLOC(*var_name);
- *var_name = tmp;
+ /* If there is no value, boolean true is assumed */
+ *var_value = NULL;
/*
* Now, let's try to parse the value
*/
if (value_start != NULL) {
-
- while (isspace(value_start[0]))
+ while (git__isspace(value_start[0]))
value_start++;
- if (value_start[0] == '\0') {
- *var_value = NULL;
- goto out;
- }
-
if (is_multiline_var(value_start)) {
- error = parse_multiline_variable(cfg, value_start, var_value);
- if (error != GIT_SUCCESS)
- {
- *var_value = NULL;
+ git_buf multi_value = GIT_BUF_INIT;
+ char *proc_line = fixup_line(value_start, 0);
+ GITERR_CHECK_ALLOC(proc_line);
+ git_buf_puts(&multi_value, proc_line);
+ git__free(proc_line);
+ if (parse_multiline_variable(cfg, &multi_value, quote_count) < 0 || git_buf_oom(&multi_value)) {
git__free(*var_name);
+ git__free(line);
+ git_buf_free(&multi_value);
+ return -1;
}
- goto out;
- }
- tmp = git__strdup(value_start);
- if (tmp == NULL) {
- git__free(*var_name);
- *var_value = NULL;
- error = GIT_ENOMEM;
- goto out;
+ *var_value = git_buf_detach(&multi_value);
+
+ }
+ else if (value_start[0] != '\0') {
+ *var_value = fixup_line(value_start, 0);
+ GITERR_CHECK_ALLOC(*var_value);
}
- *var_value = tmp;
- } else {
- /* If there is no value, boolean true is assumed */
- *var_value = NULL;
}
- out:
git__free(line);
- return error;
+ return 0;
}
diff --git a/src/config_file.h b/src/config_file.h
new file mode 100644
index 000000000..0080b5713
--- /dev/null
+++ b/src/config_file.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_config_file_h__
+#define INCLUDE_config_file_h__
+
+#include "git2/config.h"
+
+GIT_INLINE(int) git_config_file_open(git_config_file *cfg)
+{
+ return cfg->open(cfg);
+}
+
+GIT_INLINE(void) git_config_file_free(git_config_file *cfg)
+{
+ cfg->free(cfg);
+}
+
+GIT_INLINE(int) git_config_file_foreach(
+ git_config_file *cfg,
+ int (*fn)(const char *key, const char *value, void *data),
+ void *data)
+{
+ return cfg->foreach(cfg, fn, data);
+}
+
+#endif
+
diff --git a/src/crlf.c b/src/crlf.c
new file mode 100644
index 000000000..303a46d3b
--- /dev/null
+++ b/src/crlf.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+#include "fileops.h"
+#include "hash.h"
+#include "filter.h"
+#include "repository.h"
+
+#include "git2/attr.h"
+
+struct crlf_attrs {
+ int crlf_action;
+ int eol;
+};
+
+struct crlf_filter {
+ git_filter f;
+ struct crlf_attrs attrs;
+};
+
+static int check_crlf(const char *value)
+{
+ if (GIT_ATTR_TRUE(value))
+ return GIT_CRLF_TEXT;
+
+ if (GIT_ATTR_FALSE(value))
+ return GIT_CRLF_BINARY;
+
+ if (GIT_ATTR_UNSPECIFIED(value))
+ return GIT_CRLF_GUESS;
+
+ if (strcmp(value, "input") == 0)
+ return GIT_CRLF_INPUT;
+
+ if (strcmp(value, "auto") == 0)
+ return GIT_CRLF_AUTO;
+
+ return GIT_CRLF_GUESS;
+}
+
+static int check_eol(const char *value)
+{
+ if (GIT_ATTR_UNSPECIFIED(value))
+ return GIT_EOL_UNSET;
+
+ if (strcmp(value, "lf") == 0)
+ return GIT_EOL_LF;
+
+ if (strcmp(value, "crlf") == 0)
+ return GIT_EOL_CRLF;
+
+ return GIT_EOL_UNSET;
+}
+
+static int crlf_input_action(struct crlf_attrs *ca)
+{
+ if (ca->crlf_action == GIT_CRLF_BINARY)
+ return GIT_CRLF_BINARY;
+
+ if (ca->eol == GIT_EOL_LF)
+ return GIT_CRLF_INPUT;
+
+ if (ca->eol == GIT_EOL_CRLF)
+ return GIT_CRLF_CRLF;
+
+ return ca->crlf_action;
+}
+
+static int crlf_load_attributes(struct crlf_attrs *ca, git_repository *repo, const char *path)
+{
+#define NUM_CONV_ATTRS 3
+
+ static const char *attr_names[NUM_CONV_ATTRS] = {
+ "crlf", "eol", "text",
+ };
+
+ const char *attr_vals[NUM_CONV_ATTRS];
+ int error;
+
+ error = git_attr_get_many(attr_vals,
+ repo, 0, path, NUM_CONV_ATTRS, attr_names);
+
+ if (error == GIT_ENOTFOUND) {
+ ca->crlf_action = GIT_CRLF_GUESS;
+ ca->eol = GIT_EOL_UNSET;
+ return 0;
+ }
+
+ if (error == 0) {
+ ca->crlf_action = check_crlf(attr_vals[2]); /* text */
+ if (ca->crlf_action == GIT_CRLF_GUESS)
+ ca->crlf_action = check_crlf(attr_vals[0]); /* clrf */
+
+ ca->eol = check_eol(attr_vals[1]); /* eol */
+ return 0;
+ }
+
+ return -1;
+}
+
+static int drop_crlf(git_buf *dest, const git_buf *source)
+{
+ const char *scan = source->ptr, *next;
+ const char *scan_end = git_buf_cstr(source) + git_buf_len(source);
+
+ /* Main scan loop. Find the next carriage return and copy the
+ * whole chunk up to that point to the destination buffer.
+ */
+ while ((next = memchr(scan, '\r', scan_end - scan)) != NULL) {
+ /* copy input up to \r */
+ if (next > scan)
+ git_buf_put(dest, scan, next - scan);
+
+ /* Do not drop \r unless it is followed by \n */
+ if (*(next + 1) != '\n')
+ git_buf_putc(dest, '\r');
+
+ scan = next + 1;
+ }
+
+ /* If there was no \r, then tell the library to skip this filter */
+ if (scan == source->ptr)
+ return -1;
+
+ /* Copy remaining input into dest */
+ git_buf_put(dest, scan, scan_end - scan);
+ return 0;
+}
+
+static int crlf_apply_to_odb(git_filter *self, git_buf *dest, const git_buf *source)
+{
+ struct crlf_filter *filter = (struct crlf_filter *)self;
+
+ assert(self && dest && source);
+
+ /* Empty file? Nothing to do */
+ if (git_buf_len(source) == 0)
+ return 0;
+
+ /* Heuristics to see if we can skip the conversion.
+ * Straight from Core Git.
+ */
+ if (filter->attrs.crlf_action == GIT_CRLF_AUTO ||
+ filter->attrs.crlf_action == GIT_CRLF_GUESS) {
+
+ git_text_stats stats;
+ git_text_gather_stats(&stats, source);
+
+ /*
+ * We're currently not going to even try to convert stuff
+ * that has bare CR characters. Does anybody do that crazy
+ * stuff?
+ */
+ if (stats.cr != stats.crlf)
+ return -1;
+
+ /*
+ * And add some heuristics for binary vs text, of course...
+ */
+ if (git_text_is_binary(&stats))
+ return -1;
+
+#if 0
+ if (crlf_action == CRLF_GUESS) {
+ /*
+ * If the file in the index has any CR in it, do not convert.
+ * This is the new safer autocrlf handling.
+ */
+ if (has_cr_in_index(path))
+ return 0;
+ }
+#endif
+
+ if (!stats.cr)
+ return -1;
+ }
+
+ /* Actually drop the carriage returns */
+ return drop_crlf(dest, source);
+}
+
+int git_filter_add__crlf_to_odb(git_vector *filters, git_repository *repo, const char *path)
+{
+ struct crlf_attrs ca;
+ struct crlf_filter *filter;
+ int error;
+
+ /* Load gitattributes for the path */
+ if ((error = crlf_load_attributes(&ca, repo, path)) < 0)
+ return error;
+
+ /*
+ * Use the core Git logic to see if we should perform CRLF for this file
+ * based on its attributes & the value of `core.auto_crlf`
+ */
+ ca.crlf_action = crlf_input_action(&ca);
+
+ if (ca.crlf_action == GIT_CRLF_BINARY)
+ return 0;
+
+ if (ca.crlf_action == GIT_CRLF_GUESS) {
+ int auto_crlf;
+
+ if ((error = git_repository__cvar(
+ &auto_crlf, repo, GIT_CVAR_AUTO_CRLF)) < 0)
+ return error;
+
+ if (auto_crlf == GIT_AUTO_CRLF_FALSE)
+ return 0;
+ }
+
+ /* If we're good, we create a new filter object and push it
+ * into the filters array */
+ filter = git__malloc(sizeof(struct crlf_filter));
+ GITERR_CHECK_ALLOC(filter);
+
+ filter->f.apply = &crlf_apply_to_odb;
+ filter->f.do_free = NULL;
+ memcpy(&filter->attrs, &ca, sizeof(struct crlf_attrs));
+
+ return git_vector_insert(filters, filter);
+}
+
diff --git a/src/delta-apply.c b/src/delta-apply.c
index 3e40bf8cf..815ca8f16 100644
--- a/src/delta-apply.c
+++ b/src/delta-apply.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -51,14 +51,19 @@ int git__delta_apply(
* if not we would underflow while accessing data from the
* base object, resulting in data corruption or segfault.
*/
- if ((hdr_sz(&base_sz, &delta, delta_end) < 0) || (base_sz != base_len))
- return git__throw(GIT_ERROR, "Failed to apply delta. Base size does not match given data");
+ if ((hdr_sz(&base_sz, &delta, delta_end) < 0) || (base_sz != base_len)) {
+ giterr_set(GITERR_INVALID, "Failed to apply delta. Base size does not match given data");
+ return -1;
+ }
+
+ if (hdr_sz(&res_sz, &delta, delta_end) < 0) {
+ giterr_set(GITERR_INVALID, "Failed to apply delta. Base size does not match given data");
+ return -1;
+ }
- if (hdr_sz(&res_sz, &delta, delta_end) < 0)
- return git__throw(GIT_ERROR, "Failed to apply delta. Base size does not match given data");
+ res_dp = git__malloc(res_sz + 1);
+ GITERR_CHECK_ALLOC(res_dp);
- if ((res_dp = git__malloc(res_sz + 1)) == NULL)
- return GIT_ENOMEM;
res_dp[res_sz] = '\0';
out->data = res_dp;
out->len = res_sz;
@@ -106,10 +111,11 @@ int git__delta_apply(
if (delta != delta_end || res_sz)
goto fail;
- return GIT_SUCCESS;
+ return 0;
fail:
git__free(out->data);
out->data = NULL;
- return git__throw(GIT_ERROR, "Failed to apply delta");
+ giterr_set(GITERR_INVALID, "Failed to apply delta");
+ return -1;
}
diff --git a/src/delta-apply.h b/src/delta-apply.h
index 42ded3e0b..66fa76d43 100644
--- a/src/delta-apply.h
+++ b/src/delta-apply.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -20,7 +20,7 @@
* @param delta the delta to execute copy/insert instructions from.
* @param delta_len total number of bytes in the delta.
* @return
- * - GIT_SUCCESS on a successful delta unpack.
+ * - 0 on a successful delta unpack.
* - GIT_ERROR if the delta is corrupt or doesn't match the base.
*/
extern int git__delta_apply(
diff --git a/src/diff.c b/src/diff.c
new file mode 100644
index 000000000..0b2f8fb50
--- /dev/null
+++ b/src/diff.c
@@ -0,0 +1,783 @@
+/*
+ * Copyright (C) 2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#include "common.h"
+#include "git2/diff.h"
+#include "diff.h"
+#include "fileops.h"
+#include "config.h"
+#include "attr_file.h"
+
+static char *diff_prefix_from_pathspec(const git_strarray *pathspec)
+{
+ git_buf prefix = GIT_BUF_INIT;
+ const char *scan;
+
+ if (git_buf_common_prefix(&prefix, pathspec) < 0)
+ return NULL;
+
+ /* diff prefix will only be leading non-wildcards */
+ for (scan = prefix.ptr; *scan && !git__iswildcard(*scan); ++scan);
+ git_buf_truncate(&prefix, scan - prefix.ptr);
+
+ if (prefix.size > 0)
+ return git_buf_detach(&prefix);
+
+ git_buf_free(&prefix);
+ return NULL;
+}
+
+static bool diff_pathspec_is_interesting(const git_strarray *pathspec)
+{
+ const char *str;
+
+ if (pathspec == NULL || pathspec->count == 0)
+ return false;
+ if (pathspec->count > 1)
+ return true;
+
+ str = pathspec->strings[0];
+ if (!str || !str[0] || (!str[1] && (str[0] == '*' || str[0] == '.')))
+ return false;
+ return true;
+}
+
+static bool diff_path_matches_pathspec(git_diff_list *diff, const char *path)
+{
+ unsigned int i;
+ git_attr_fnmatch *match;
+
+ if (!diff->pathspec.length)
+ return true;
+
+ git_vector_foreach(&diff->pathspec, i, match) {
+ int result = p_fnmatch(match->pattern, path, 0);
+
+ /* if we didn't match, look for exact dirname prefix match */
+ if (result == FNM_NOMATCH &&
+ (match->flags & GIT_ATTR_FNMATCH_HASWILD) == 0 &&
+ strncmp(path, match->pattern, match->length) == 0 &&
+ path[match->length] == '/')
+ result = 0;
+
+ if (result == 0)
+ return (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) ? false : true;
+ }
+
+ return false;
+}
+
+static git_diff_delta *diff_delta__alloc(
+ git_diff_list *diff,
+ git_delta_t status,
+ const char *path)
+{
+ git_diff_delta *delta = git__calloc(1, sizeof(git_diff_delta));
+ if (!delta)
+ return NULL;
+
+ delta->old_file.path = git_pool_strdup(&diff->pool, path);
+ if (delta->old_file.path == NULL) {
+ git__free(delta);
+ return NULL;
+ }
+
+ delta->new_file.path = delta->old_file.path;
+
+ if (diff->opts.flags & GIT_DIFF_REVERSE) {
+ switch (status) {
+ case GIT_DELTA_ADDED: status = GIT_DELTA_DELETED; break;
+ case GIT_DELTA_DELETED: status = GIT_DELTA_ADDED; break;
+ default: break; /* leave other status values alone */
+ }
+ }
+ delta->status = status;
+
+ return delta;
+}
+
+static git_diff_delta *diff_delta__dup(
+ const git_diff_delta *d, git_pool *pool)
+{
+ git_diff_delta *delta = git__malloc(sizeof(git_diff_delta));
+ if (!delta)
+ return NULL;
+
+ memcpy(delta, d, sizeof(git_diff_delta));
+
+ delta->old_file.path = git_pool_strdup(pool, d->old_file.path);
+ if (delta->old_file.path == NULL)
+ goto fail;
+
+ if (d->new_file.path != d->old_file.path) {
+ delta->new_file.path = git_pool_strdup(pool, d->new_file.path);
+ if (delta->new_file.path == NULL)
+ goto fail;
+ } else {
+ delta->new_file.path = delta->old_file.path;
+ }
+
+ return delta;
+
+fail:
+ git__free(delta);
+ return NULL;
+}
+
+static git_diff_delta *diff_delta__merge_like_cgit(
+ const git_diff_delta *a, const git_diff_delta *b, git_pool *pool)
+{
+ git_diff_delta *dup = diff_delta__dup(a, pool);
+ if (!dup)
+ return NULL;
+
+ if (git_oid_cmp(&dup->new_file.oid, &b->new_file.oid) == 0)
+ return dup;
+
+ git_oid_cpy(&dup->new_file.oid, &b->new_file.oid);
+
+ dup->new_file.mode = b->new_file.mode;
+ dup->new_file.size = b->new_file.size;
+ dup->new_file.flags = b->new_file.flags;
+
+ /* Emulate C git for merging two diffs (a la 'git diff <sha>').
+ *
+ * When C git does a diff between the work dir and a tree, it actually
+ * diffs with the index but uses the workdir contents. This emulates
+ * those choices so we can emulate the type of diff.
+ */
+ if (git_oid_cmp(&dup->old_file.oid, &dup->new_file.oid) == 0) {
+ if (dup->status == GIT_DELTA_DELETED)
+ /* preserve pending delete info */;
+ else if (b->status == GIT_DELTA_UNTRACKED ||
+ b->status == GIT_DELTA_IGNORED)
+ dup->status = b->status;
+ else
+ dup->status = GIT_DELTA_UNMODIFIED;
+ }
+ else if (dup->status == GIT_DELTA_UNMODIFIED ||
+ b->status == GIT_DELTA_DELETED)
+ dup->status = b->status;
+
+ return dup;
+}
+
+static int diff_delta__from_one(
+ git_diff_list *diff,
+ git_delta_t status,
+ const git_index_entry *entry)
+{
+ git_diff_delta *delta;
+
+ if (status == GIT_DELTA_IGNORED &&
+ (diff->opts.flags & GIT_DIFF_INCLUDE_IGNORED) == 0)
+ return 0;
+
+ if (status == GIT_DELTA_UNTRACKED &&
+ (diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
+ return 0;
+
+ if (!diff_path_matches_pathspec(diff, entry->path))
+ return 0;
+
+ delta = diff_delta__alloc(diff, status, entry->path);
+ GITERR_CHECK_ALLOC(delta);
+
+ /* This fn is just for single-sided diffs */
+ assert(status != GIT_DELTA_MODIFIED);
+
+ if (delta->status == GIT_DELTA_DELETED) {
+ delta->old_file.mode = entry->mode;
+ delta->old_file.size = entry->file_size;
+ git_oid_cpy(&delta->old_file.oid, &entry->oid);
+ } else /* ADDED, IGNORED, UNTRACKED */ {
+ delta->new_file.mode = entry->mode;
+ delta->new_file.size = entry->file_size;
+ git_oid_cpy(&delta->new_file.oid, &entry->oid);
+ }
+
+ delta->old_file.flags |= GIT_DIFF_FILE_VALID_OID;
+ delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID;
+
+ if (git_vector_insert(&diff->deltas, delta) < 0) {
+ git__free(delta);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int diff_delta__from_two(
+ git_diff_list *diff,
+ git_delta_t status,
+ const git_index_entry *old_entry,
+ const git_index_entry *new_entry,
+ git_oid *new_oid)
+{
+ git_diff_delta *delta;
+
+ if (status == GIT_DELTA_UNMODIFIED &&
+ (diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
+ return 0;
+
+ if ((diff->opts.flags & GIT_DIFF_REVERSE) != 0) {
+ const git_index_entry *temp = old_entry;
+ old_entry = new_entry;
+ new_entry = temp;
+ }
+
+ delta = diff_delta__alloc(diff, status, old_entry->path);
+ GITERR_CHECK_ALLOC(delta);
+
+ delta->old_file.mode = old_entry->mode;
+ git_oid_cpy(&delta->old_file.oid, &old_entry->oid);
+ delta->old_file.flags |= GIT_DIFF_FILE_VALID_OID;
+
+ delta->new_file.mode = new_entry->mode;
+ git_oid_cpy(&delta->new_file.oid, new_oid ? new_oid : &new_entry->oid);
+ if (new_oid || !git_oid_iszero(&new_entry->oid))
+ delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID;
+
+ if (git_vector_insert(&diff->deltas, delta) < 0) {
+ git__free(delta);
+ return -1;
+ }
+
+ return 0;
+}
+
+static char *diff_strdup_prefix(git_pool *pool, const char *prefix)
+{
+ size_t len = strlen(prefix);
+
+ /* append '/' at end if needed */
+ if (len > 0 && prefix[len - 1] != '/')
+ return git_pool_strcat(pool, prefix, "/");
+ else
+ return git_pool_strndup(pool, prefix, len + 1);
+}
+
+static int diff_delta__cmp(const void *a, const void *b)
+{
+ const git_diff_delta *da = a, *db = b;
+ int val = strcmp(da->old_file.path, db->old_file.path);
+ return val ? val : ((int)da->status - (int)db->status);
+}
+
+static int config_bool(git_config *cfg, const char *name, int defvalue)
+{
+ int val = defvalue;
+
+ if (git_config_get_bool(&val, cfg, name) < 0)
+ giterr_clear();
+
+ return val;
+}
+
+static git_diff_list *git_diff_list_alloc(
+ git_repository *repo, const git_diff_options *opts)
+{
+ git_config *cfg;
+ size_t i;
+ git_diff_list *diff = git__calloc(1, sizeof(git_diff_list));
+ if (diff == NULL)
+ return NULL;
+
+ diff->repo = repo;
+
+ if (git_vector_init(&diff->deltas, 0, diff_delta__cmp) < 0 ||
+ git_pool_init(&diff->pool, 1, 0) < 0)
+ goto fail;
+
+ /* load config values that affect diff behavior */
+ if (git_repository_config__weakptr(&cfg, repo) < 0)
+ goto fail;
+ if (config_bool(cfg, "core.symlinks", 1))
+ diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_HAS_SYMLINKS;
+ if (config_bool(cfg, "core.ignorestat", 0))
+ diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_ASSUME_UNCHANGED;
+ if (config_bool(cfg, "core.filemode", 1))
+ diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_EXEC_BIT;
+ if (config_bool(cfg, "core.trustctime", 1))
+ diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_CTIME;
+ /* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */
+
+ if (opts == NULL)
+ return diff;
+
+ memcpy(&diff->opts, opts, sizeof(git_diff_options));
+ memset(&diff->opts.pathspec, 0, sizeof(diff->opts.pathspec));
+
+ diff->opts.old_prefix = diff_strdup_prefix(&diff->pool,
+ opts->old_prefix ? opts->old_prefix : DIFF_OLD_PREFIX_DEFAULT);
+ diff->opts.new_prefix = diff_strdup_prefix(&diff->pool,
+ opts->new_prefix ? opts->new_prefix : DIFF_NEW_PREFIX_DEFAULT);
+
+ if (!diff->opts.old_prefix || !diff->opts.new_prefix)
+ goto fail;
+
+ if (diff->opts.flags & GIT_DIFF_REVERSE) {
+ char *swap = diff->opts.old_prefix;
+ diff->opts.old_prefix = diff->opts.new_prefix;
+ diff->opts.new_prefix = swap;
+ }
+
+ /* only copy pathspec if it is "interesting" so we can test
+ * diff->pathspec.length > 0 to know if it is worth calling
+ * fnmatch as we iterate.
+ */
+ if (!diff_pathspec_is_interesting(&opts->pathspec))
+ return diff;
+
+ if (git_vector_init(
+ &diff->pathspec, (unsigned int)opts->pathspec.count, NULL) < 0)
+ goto fail;
+
+ for (i = 0; i < opts->pathspec.count; ++i) {
+ int ret;
+ const char *pattern = opts->pathspec.strings[i];
+ git_attr_fnmatch *match = git__calloc(1, sizeof(git_attr_fnmatch));
+ if (!match)
+ goto fail;
+ ret = git_attr_fnmatch__parse(match, &diff->pool, NULL, &pattern);
+ if (ret == GIT_ENOTFOUND) {
+ git__free(match);
+ continue;
+ } else if (ret < 0)
+ goto fail;
+
+ if (git_vector_insert(&diff->pathspec, match) < 0)
+ goto fail;
+ }
+
+ return diff;
+
+fail:
+ git_diff_list_free(diff);
+ return NULL;
+}
+
+void git_diff_list_free(git_diff_list *diff)
+{
+ git_diff_delta *delta;
+ git_attr_fnmatch *match;
+ unsigned int i;
+
+ if (!diff)
+ return;
+
+ git_vector_foreach(&diff->deltas, i, delta) {
+ git__free(delta);
+ diff->deltas.contents[i] = NULL;
+ }
+ git_vector_free(&diff->deltas);
+
+ git_vector_foreach(&diff->pathspec, i, match) {
+ git__free(match);
+ diff->pathspec.contents[i] = NULL;
+ }
+ git_vector_free(&diff->pathspec);
+
+ git_pool_clear(&diff->pool);
+ git__free(diff);
+}
+
+static int oid_for_workdir_item(
+ git_repository *repo,
+ const git_index_entry *item,
+ git_oid *oid)
+{
+ int result;
+ git_buf full_path = GIT_BUF_INIT;
+
+ if (git_buf_joinpath(&full_path, git_repository_workdir(repo), item->path) < 0)
+ return -1;
+
+ /* calculate OID for file if possible*/
+ if (S_ISLNK(item->mode))
+ result = git_odb__hashlink(oid, full_path.ptr);
+ else if (!git__is_sizet(item->file_size)) {
+ giterr_set(GITERR_OS, "File size overflow for 32-bit systems");
+ result = -1;
+ } else {
+ int fd = git_futils_open_ro(full_path.ptr);
+ if (fd < 0)
+ result = fd;
+ else {
+ result = git_odb__hashfd(
+ oid, fd, (size_t)item->file_size, GIT_OBJ_BLOB);
+ p_close(fd);
+ }
+ }
+
+ git_buf_free(&full_path);
+
+ return result;
+}
+
+#define EXEC_BIT_MASK 0000111
+
+static int maybe_modified(
+ git_iterator *old_iter,
+ const git_index_entry *oitem,
+ git_iterator *new_iter,
+ const git_index_entry *nitem,
+ git_diff_list *diff)
+{
+ git_oid noid, *use_noid = NULL;
+ git_delta_t status = GIT_DELTA_MODIFIED;
+ unsigned int omode = oitem->mode;
+ unsigned int nmode = nitem->mode;
+
+ GIT_UNUSED(old_iter);
+
+ if (!diff_path_matches_pathspec(diff, oitem->path))
+ return 0;
+
+ /* on platforms with no symlinks, promote plain files to symlinks */
+ if (S_ISLNK(omode) && S_ISREG(nmode) &&
+ !(diff->diffcaps & GIT_DIFFCAPS_HAS_SYMLINKS))
+ nmode = GIT_MODE_TYPE(omode) | (nmode & GIT_MODE_PERMS_MASK);
+
+ /* on platforms with no execmode, clear exec bit from comparisons */
+ if (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_EXEC_BIT)) {
+ omode = omode & ~EXEC_BIT_MASK;
+ nmode = nmode & ~EXEC_BIT_MASK;
+ }
+
+ /* support "assume unchanged" (badly, b/c we still stat everything) */
+ if ((diff->diffcaps & GIT_DIFFCAPS_ASSUME_UNCHANGED) != 0)
+ status = (oitem->flags_extended & GIT_IDXENTRY_INTENT_TO_ADD) ?
+ GIT_DELTA_MODIFIED : GIT_DELTA_UNMODIFIED;
+
+ /* support "skip worktree" index bit */
+ else if ((oitem->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE) != 0)
+ status = GIT_DELTA_UNMODIFIED;
+
+ /* if basic type of file changed, then split into delete and add */
+ else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) {
+ if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 ||
+ diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem) < 0)
+ return -1;
+ return 0;
+ }
+
+ /* if oids and modes match, then file is unmodified */
+ else if (git_oid_cmp(&oitem->oid, &nitem->oid) == 0 &&
+ omode == nmode)
+ status = GIT_DELTA_UNMODIFIED;
+
+ /* if we have a workdir item with an unknown oid, check deeper */
+ else if (git_oid_iszero(&nitem->oid) && new_iter->type == GIT_ITERATOR_WORKDIR) {
+ /* TODO: add check against index file st_mtime to avoid racy-git */
+
+ /* if they files look exactly alike, then we'll assume the same */
+ if (oitem->file_size == nitem->file_size &&
+ (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) ||
+ (oitem->ctime.seconds == nitem->ctime.seconds)) &&
+ oitem->mtime.seconds == nitem->mtime.seconds &&
+ (!(diff->diffcaps & GIT_DIFFCAPS_USE_DEV) ||
+ (oitem->dev == nitem->dev)) &&
+ oitem->ino == nitem->ino &&
+ oitem->uid == nitem->uid &&
+ oitem->gid == nitem->gid)
+ status = GIT_DELTA_UNMODIFIED;
+
+ else if (S_ISGITLINK(nmode)) {
+ git_submodule *sub;
+
+ if ((diff->opts.flags & GIT_DIFF_IGNORE_SUBMODULES) != 0)
+ status = GIT_DELTA_UNMODIFIED;
+ else if (git_submodule_lookup(&sub, diff->repo, nitem->path) < 0)
+ return -1;
+ else if (sub->ignore == GIT_SUBMODULE_IGNORE_ALL)
+ status = GIT_DELTA_UNMODIFIED;
+ else {
+ /* TODO: support other GIT_SUBMODULE_IGNORE values */
+ status = GIT_DELTA_UNMODIFIED;
+ }
+ }
+
+ /* TODO: check git attributes so we will not have to read the file
+ * in if it is marked binary.
+ */
+
+ else if (oid_for_workdir_item(diff->repo, nitem, &noid) < 0)
+ return -1;
+
+ else if (git_oid_cmp(&oitem->oid, &noid) == 0 &&
+ omode == nmode)
+ status = GIT_DELTA_UNMODIFIED;
+
+ /* store calculated oid so we don't have to recalc later */
+ use_noid = &noid;
+ }
+
+ return diff_delta__from_two(diff, status, oitem, nitem, use_noid);
+}
+
+static int diff_from_iterators(
+ git_repository *repo,
+ const git_diff_options *opts, /**< can be NULL for defaults */
+ git_iterator *old_iter,
+ git_iterator *new_iter,
+ git_diff_list **diff_ptr)
+{
+ const git_index_entry *oitem, *nitem;
+ git_buf ignore_prefix = GIT_BUF_INIT;
+ git_diff_list *diff = git_diff_list_alloc(repo, opts);
+ if (!diff)
+ goto fail;
+
+ diff->old_src = old_iter->type;
+ diff->new_src = new_iter->type;
+
+ if (git_iterator_current(old_iter, &oitem) < 0 ||
+ git_iterator_current(new_iter, &nitem) < 0)
+ goto fail;
+
+ /* run iterators building diffs */
+ while (oitem || nitem) {
+
+ /* create DELETED records for old items not matched in new */
+ if (oitem && (!nitem || strcmp(oitem->path, nitem->path) < 0)) {
+ if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 ||
+ git_iterator_advance(old_iter, &oitem) < 0)
+ goto fail;
+ }
+
+ /* create ADDED, TRACKED, or IGNORED records for new items not
+ * matched in old (and/or descend into directories as needed)
+ */
+ else if (nitem && (!oitem || strcmp(oitem->path, nitem->path) > 0)) {
+ git_delta_t delta_type = GIT_DELTA_UNTRACKED;
+
+ /* check if contained in ignored parent directory */
+ if (git_buf_len(&ignore_prefix) &&
+ git__prefixcmp(nitem->path, git_buf_cstr(&ignore_prefix)) == 0)
+ delta_type = GIT_DELTA_IGNORED;
+
+ if (S_ISDIR(nitem->mode)) {
+ /* recurse into directory only if there are tracked items in
+ * it or if the user requested the contents of untracked
+ * directories and it is not under an ignored directory.
+ */
+ if ((oitem && git__prefixcmp(oitem->path, nitem->path) == 0) ||
+ (delta_type == GIT_DELTA_UNTRACKED &&
+ (diff->opts.flags & GIT_DIFF_RECURSE_UNTRACKED_DIRS) != 0))
+ {
+ /* if this directory is ignored, remember it as the
+ * "ignore_prefix" for processing contained items
+ */
+ if (delta_type == GIT_DELTA_UNTRACKED &&
+ git_iterator_current_is_ignored(new_iter))
+ git_buf_sets(&ignore_prefix, nitem->path);
+
+ if (git_iterator_advance_into_directory(new_iter, &nitem) < 0)
+ goto fail;
+
+ continue;
+ }
+ }
+
+ /* In core git, the next two "else if" clauses are effectively
+ * reversed -- i.e. when an untracked file contained in an
+ * ignored directory is individually ignored, it shows up as an
+ * ignored file in the diff list, even though other untracked
+ * files in the same directory are skipped completely.
+ *
+ * To me, this is odd. If the directory is ignored and the file
+ * is untracked, we should skip it consistently, regardless of
+ * whether it happens to match a pattern in the ignore file.
+ *
+ * To match the core git behavior, just reverse the following
+ * two "else if" cases so that individual file ignores are
+ * checked before container directory exclusions are used to
+ * skip the file.
+ */
+ else if (delta_type == GIT_DELTA_IGNORED) {
+ if (git_iterator_advance(new_iter, &nitem) < 0)
+ goto fail;
+ continue; /* ignored parent directory, so skip completely */
+ }
+
+ else if (git_iterator_current_is_ignored(new_iter))
+ delta_type = GIT_DELTA_IGNORED;
+
+ else if (new_iter->type != GIT_ITERATOR_WORKDIR)
+ delta_type = GIT_DELTA_ADDED;
+
+ if (diff_delta__from_one(diff, delta_type, nitem) < 0 ||
+ git_iterator_advance(new_iter, &nitem) < 0)
+ goto fail;
+ }
+
+ /* otherwise item paths match, so create MODIFIED record
+ * (or ADDED and DELETED pair if type changed)
+ */
+ else {
+ assert(oitem && nitem && strcmp(oitem->path, nitem->path) == 0);
+
+ if (maybe_modified(old_iter, oitem, new_iter, nitem, diff) < 0 ||
+ git_iterator_advance(old_iter, &oitem) < 0 ||
+ git_iterator_advance(new_iter, &nitem) < 0)
+ goto fail;
+ }
+ }
+
+ git_iterator_free(old_iter);
+ git_iterator_free(new_iter);
+ git_buf_free(&ignore_prefix);
+
+ *diff_ptr = diff;
+ return 0;
+
+fail:
+ git_iterator_free(old_iter);
+ git_iterator_free(new_iter);
+ git_buf_free(&ignore_prefix);
+
+ git_diff_list_free(diff);
+ *diff_ptr = NULL;
+ return -1;
+}
+
+
+int git_diff_tree_to_tree(
+ git_repository *repo,
+ const git_diff_options *opts, /**< can be NULL for defaults */
+ git_tree *old_tree,
+ git_tree *new_tree,
+ git_diff_list **diff)
+{
+ git_iterator *a = NULL, *b = NULL;
+ char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
+
+ assert(repo && old_tree && new_tree && diff);
+
+ if (git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix) < 0 ||
+ git_iterator_for_tree_range(&b, repo, new_tree, prefix, prefix) < 0)
+ return -1;
+
+ git__free(prefix);
+
+ return diff_from_iterators(repo, opts, a, b, diff);
+}
+
+int git_diff_index_to_tree(
+ git_repository *repo,
+ const git_diff_options *opts,
+ git_tree *old_tree,
+ git_diff_list **diff)
+{
+ git_iterator *a = NULL, *b = NULL;
+ char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
+
+ assert(repo && diff);
+
+ if (git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix) < 0 ||
+ git_iterator_for_index_range(&b, repo, prefix, prefix) < 0)
+ return -1;
+
+ git__free(prefix);
+
+ return diff_from_iterators(repo, opts, a, b, diff);
+}
+
+int git_diff_workdir_to_index(
+ git_repository *repo,
+ const git_diff_options *opts,
+ git_diff_list **diff)
+{
+ git_iterator *a = NULL, *b = NULL;
+ char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
+
+ assert(repo && diff);
+
+ if (git_iterator_for_index_range(&a, repo, prefix, prefix) < 0 ||
+ git_iterator_for_workdir_range(&b, repo, prefix, prefix) < 0)
+ return -1;
+
+ git__free(prefix);
+
+ return diff_from_iterators(repo, opts, a, b, diff);
+}
+
+
+int git_diff_workdir_to_tree(
+ git_repository *repo,
+ const git_diff_options *opts,
+ git_tree *old_tree,
+ git_diff_list **diff)
+{
+ git_iterator *a = NULL, *b = NULL;
+ char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
+
+ assert(repo && old_tree && diff);
+
+ if (git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix) < 0 ||
+ git_iterator_for_workdir_range(&b, repo, prefix, prefix) < 0)
+ return -1;
+
+ git__free(prefix);
+
+ return diff_from_iterators(repo, opts, a, b, diff);
+}
+
+int git_diff_merge(
+ git_diff_list *onto,
+ const git_diff_list *from)
+{
+ int error = 0;
+ git_pool onto_pool;
+ git_vector onto_new;
+ git_diff_delta *delta;
+ unsigned int i, j;
+
+ assert(onto && from);
+
+ if (!from->deltas.length)
+ return 0;
+
+ if (git_vector_init(&onto_new, onto->deltas.length, diff_delta__cmp) < 0 ||
+ git_pool_init(&onto_pool, 1, 0) < 0)
+ return -1;
+
+ for (i = 0, j = 0; i < onto->deltas.length || j < from->deltas.length; ) {
+ git_diff_delta *o = GIT_VECTOR_GET(&onto->deltas, i);
+ const git_diff_delta *f = GIT_VECTOR_GET(&from->deltas, j);
+ int cmp = !f ? -1 : !o ? 1 : strcmp(o->old_file.path, f->old_file.path);
+
+ if (cmp < 0) {
+ delta = diff_delta__dup(o, &onto_pool);
+ i++;
+ } else if (cmp > 0) {
+ delta = diff_delta__dup(f, &onto_pool);
+ j++;
+ } else {
+ delta = diff_delta__merge_like_cgit(o, f, &onto_pool);
+ i++;
+ j++;
+ }
+
+ if ((error = !delta ? -1 : git_vector_insert(&onto_new, delta)) < 0)
+ break;
+ }
+
+ if (!error) {
+ git_vector_swap(&onto->deltas, &onto_new);
+ git_pool_swap(&onto->pool, &onto_pool);
+ onto->new_src = from->new_src;
+ }
+
+ git_vector_foreach(&onto_new, i, delta)
+ git__free(delta);
+ git_vector_free(&onto_new);
+ git_pool_clear(&onto_pool);
+
+ return error;
+}
+
diff --git a/src/diff.h b/src/diff.h
new file mode 100644
index 000000000..ac2457956
--- /dev/null
+++ b/src/diff.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_diff_h__
+#define INCLUDE_diff_h__
+
+#include <stdio.h>
+#include "vector.h"
+#include "buffer.h"
+#include "iterator.h"
+#include "repository.h"
+#include "pool.h"
+
+#define DIFF_OLD_PREFIX_DEFAULT "a/"
+#define DIFF_NEW_PREFIX_DEFAULT "b/"
+
+enum {
+ GIT_DIFFCAPS_HAS_SYMLINKS = (1 << 0), /* symlinks on platform? */
+ GIT_DIFFCAPS_ASSUME_UNCHANGED = (1 << 1), /* use stat? */
+ GIT_DIFFCAPS_TRUST_EXEC_BIT = (1 << 2), /* use st_mode exec bit? */
+ GIT_DIFFCAPS_TRUST_CTIME = (1 << 3), /* use st_ctime? */
+ GIT_DIFFCAPS_USE_DEV = (1 << 4), /* use st_dev? */
+};
+
+struct git_diff_list {
+ git_repository *repo;
+ git_diff_options opts;
+ git_vector pathspec;
+ git_vector deltas; /* vector of git_diff_file_delta */
+ git_pool pool;
+ git_iterator_type_t old_src;
+ git_iterator_type_t new_src;
+ uint32_t diffcaps;
+};
+
+#endif
+
diff --git a/src/diff_output.c b/src/diff_output.c
new file mode 100644
index 000000000..ba7ef8245
--- /dev/null
+++ b/src/diff_output.c
@@ -0,0 +1,786 @@
+/*
+ * Copyright (C) 2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#include "common.h"
+#include "git2/diff.h"
+#include "git2/attr.h"
+#include "git2/blob.h"
+#include "xdiff/xdiff.h"
+#include <ctype.h>
+#include "diff.h"
+#include "map.h"
+#include "fileops.h"
+#include "filter.h"
+
+typedef struct {
+ git_diff_list *diff;
+ void *cb_data;
+ git_diff_hunk_fn hunk_cb;
+ git_diff_data_fn line_cb;
+ unsigned int index;
+ git_diff_delta *delta;
+ git_diff_range range;
+} diff_output_info;
+
+static int read_next_int(const char **str, int *value)
+{
+ const char *scan = *str;
+ int v = 0, digits = 0;
+ /* find next digit */
+ for (scan = *str; *scan && !isdigit(*scan); scan++);
+ /* parse next number */
+ for (; isdigit(*scan); scan++, digits++)
+ v = (v * 10) + (*scan - '0');
+ *str = scan;
+ *value = v;
+ return (digits > 0) ? 0 : -1;
+}
+
+static int diff_output_cb(void *priv, mmbuffer_t *bufs, int len)
+{
+ diff_output_info *info = priv;
+
+ if (len == 1 && info->hunk_cb) {
+ git_diff_range range = { -1, 0, -1, 0 };
+ const char *scan = bufs[0].ptr;
+
+ /* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */
+ if (*scan != '@')
+ return -1;
+
+ if (read_next_int(&scan, &range.old_start) < 0)
+ return -1;
+ if (*scan == ',' && read_next_int(&scan, &range.old_lines) < 0)
+ return -1;
+
+ if (read_next_int(&scan, &range.new_start) < 0)
+ return -1;
+ if (*scan == ',' && read_next_int(&scan, &range.new_lines) < 0)
+ return -1;
+
+ if (range.old_start < 0 || range.new_start < 0)
+ return -1;
+
+ memcpy(&info->range, &range, sizeof(git_diff_range));
+
+ return info->hunk_cb(
+ info->cb_data, info->delta, &range, bufs[0].ptr, bufs[0].size);
+ }
+
+ if ((len == 2 || len == 3) && info->line_cb) {
+ int origin;
+
+ /* expect " "/"-"/"+", then data, then maybe newline */
+ origin =
+ (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_ADDITION :
+ (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_DELETION :
+ GIT_DIFF_LINE_CONTEXT;
+
+ if (info->line_cb(
+ info->cb_data, info->delta, &info->range, origin, bufs[1].ptr, bufs[1].size) < 0)
+ return -1;
+
+ /* deal with adding and removing newline at EOF */
+ if (len == 3) {
+ if (origin == GIT_DIFF_LINE_ADDITION)
+ origin = GIT_DIFF_LINE_ADD_EOFNL;
+ else
+ origin = GIT_DIFF_LINE_DEL_EOFNL;
+
+ return info->line_cb(
+ info->cb_data, info->delta, &info->range, origin, bufs[2].ptr, bufs[2].size);
+ }
+ }
+
+ return 0;
+}
+
+#define BINARY_DIFF_FLAGS (GIT_DIFF_FILE_BINARY|GIT_DIFF_FILE_NOT_BINARY)
+
+static int update_file_is_binary_by_attr(git_repository *repo, git_diff_file *file)
+{
+ const char *value;
+ if (git_attr_get(&value, repo, 0, file->path, "diff") < 0)
+ return -1;
+
+ if (GIT_ATTR_FALSE(value))
+ file->flags |= GIT_DIFF_FILE_BINARY;
+ else if (GIT_ATTR_TRUE(value))
+ file->flags |= GIT_DIFF_FILE_NOT_BINARY;
+ /* otherwise leave file->flags alone */
+
+ return 0;
+}
+
+static void update_delta_is_binary(git_diff_delta *delta)
+{
+ if ((delta->old_file.flags & GIT_DIFF_FILE_BINARY) != 0 ||
+ (delta->new_file.flags & GIT_DIFF_FILE_BINARY) != 0)
+ delta->binary = 1;
+ else if ((delta->old_file.flags & GIT_DIFF_FILE_NOT_BINARY) != 0 ||
+ (delta->new_file.flags & GIT_DIFF_FILE_NOT_BINARY) != 0)
+ delta->binary = 0;
+ /* otherwise leave delta->binary value untouched */
+}
+
+static int file_is_binary_by_attr(
+ git_diff_list *diff,
+ git_diff_delta *delta)
+{
+ int error = 0, mirror_new;
+
+ delta->binary = -1;
+
+ /* make sure files are conceivably mmap-able */
+ if ((git_off_t)((size_t)delta->old_file.size) != delta->old_file.size ||
+ (git_off_t)((size_t)delta->new_file.size) != delta->new_file.size)
+ {
+ delta->old_file.flags |= GIT_DIFF_FILE_BINARY;
+ delta->new_file.flags |= GIT_DIFF_FILE_BINARY;
+ delta->binary = 1;
+ return 0;
+ }
+
+ /* check if user is forcing us to text diff these files */
+ if (diff->opts.flags & GIT_DIFF_FORCE_TEXT) {
+ delta->old_file.flags |= GIT_DIFF_FILE_NOT_BINARY;
+ delta->new_file.flags |= GIT_DIFF_FILE_NOT_BINARY;
+ delta->binary = 0;
+ return 0;
+ }
+
+ /* check diff attribute +, -, or 0 */
+ if (update_file_is_binary_by_attr(diff->repo, &delta->old_file) < 0)
+ return -1;
+
+ mirror_new = (delta->new_file.path == delta->old_file.path ||
+ strcmp(delta->new_file.path, delta->old_file.path) == 0);
+ if (mirror_new)
+ delta->new_file.flags &= (delta->old_file.flags & BINARY_DIFF_FLAGS);
+ else
+ error = update_file_is_binary_by_attr(diff->repo, &delta->new_file);
+
+ update_delta_is_binary(delta);
+
+ return error;
+}
+
+static int file_is_binary_by_content(
+ git_diff_delta *delta,
+ git_map *old_data,
+ git_map *new_data)
+{
+ git_buf search;
+
+ if ((delta->old_file.flags & BINARY_DIFF_FLAGS) == 0) {
+ search.ptr = old_data->data;
+ search.size = min(old_data->len, 4000);
+
+ if (git_buf_is_binary(&search))
+ delta->old_file.flags |= GIT_DIFF_FILE_BINARY;
+ else
+ delta->old_file.flags |= GIT_DIFF_FILE_NOT_BINARY;
+ }
+
+ if ((delta->new_file.flags & BINARY_DIFF_FLAGS) == 0) {
+ search.ptr = new_data->data;
+ search.size = min(new_data->len, 4000);
+
+ if (git_buf_is_binary(&search))
+ delta->new_file.flags |= GIT_DIFF_FILE_BINARY;
+ else
+ delta->new_file.flags |= GIT_DIFF_FILE_NOT_BINARY;
+ }
+
+ update_delta_is_binary(delta);
+
+ /* TODO: if value != NULL, implement diff drivers */
+
+ return 0;
+}
+
+static void setup_xdiff_options(
+ git_diff_options *opts, xdemitconf_t *cfg, xpparam_t *param)
+{
+ memset(cfg, 0, sizeof(xdemitconf_t));
+ memset(param, 0, sizeof(xpparam_t));
+
+ cfg->ctxlen =
+ (!opts || !opts->context_lines) ? 3 : opts->context_lines;
+ cfg->interhunkctxlen =
+ (!opts || !opts->interhunk_lines) ? 3 : opts->interhunk_lines;
+
+ if (!opts)
+ return;
+
+ if (opts->flags & GIT_DIFF_IGNORE_WHITESPACE)
+ param->flags |= XDF_WHITESPACE_FLAGS;
+ if (opts->flags & GIT_DIFF_IGNORE_WHITESPACE_CHANGE)
+ param->flags |= XDF_IGNORE_WHITESPACE_CHANGE;
+ if (opts->flags & GIT_DIFF_IGNORE_WHITESPACE_EOL)
+ param->flags |= XDF_IGNORE_WHITESPACE_AT_EOL;
+}
+
+static int get_blob_content(
+ git_repository *repo,
+ const git_oid *oid,
+ git_map *map,
+ git_blob **blob)
+{
+ if (git_oid_iszero(oid))
+ return 0;
+
+ if (git_blob_lookup(blob, repo, oid) < 0)
+ return -1;
+
+ map->data = (void *)git_blob_rawcontent(*blob);
+ map->len = git_blob_rawsize(*blob);
+ return 0;
+}
+
+static int get_workdir_content(
+ git_repository *repo,
+ git_diff_file *file,
+ git_map *map)
+{
+ int error = 0;
+ git_buf path = GIT_BUF_INIT;
+
+ if (git_buf_joinpath(&path, git_repository_workdir(repo), file->path) < 0)
+ return -1;
+
+ if (S_ISLNK(file->mode)) {
+ ssize_t read_len;
+
+ file->flags |= GIT_DIFF_FILE_FREE_DATA;
+ file->flags |= GIT_DIFF_FILE_BINARY;
+
+ map->data = git__malloc((size_t)file->size + 1);
+ GITERR_CHECK_ALLOC(map->data);
+
+ read_len = p_readlink(path.ptr, map->data, (size_t)file->size + 1);
+ if (read_len != (ssize_t)file->size) {
+ giterr_set(GITERR_OS, "Failed to read symlink '%s'", file->path);
+ error = -1;
+ } else
+ map->len = read_len;
+ }
+ else {
+ error = git_futils_mmap_ro_file(map, path.ptr);
+ file->flags |= GIT_DIFF_FILE_UNMAP_DATA;
+ }
+ git_buf_free(&path);
+ return error;
+}
+
+static void release_content(git_diff_file *file, git_map *map, git_blob *blob)
+{
+ if (blob != NULL)
+ git_blob_free(blob);
+
+ if (file->flags & GIT_DIFF_FILE_FREE_DATA) {
+ git__free(map->data);
+ map->data = NULL;
+ file->flags &= ~GIT_DIFF_FILE_FREE_DATA;
+ }
+ else if (file->flags & GIT_DIFF_FILE_UNMAP_DATA) {
+ git_futils_mmap_free(map);
+ map->data = NULL;
+ file->flags &= ~GIT_DIFF_FILE_UNMAP_DATA;
+ }
+}
+
+static void fill_map_from_mmfile(git_map *dst, mmfile_t *src) {
+ assert(dst && src);
+
+ dst->data = src->ptr;
+ dst->len = src->size;
+#ifdef GIT_WIN32
+ dst->fmh = NULL;
+#endif
+}
+
+int git_diff_foreach(
+ git_diff_list *diff,
+ void *data,
+ git_diff_file_fn file_cb,
+ git_diff_hunk_fn hunk_cb,
+ git_diff_data_fn line_cb)
+{
+ int error = 0;
+ diff_output_info info;
+ git_diff_delta *delta;
+ xpparam_t xdiff_params;
+ xdemitconf_t xdiff_config;
+ xdemitcb_t xdiff_callback;
+
+ info.diff = diff;
+ info.cb_data = data;
+ info.hunk_cb = hunk_cb;
+ info.line_cb = line_cb;
+
+ setup_xdiff_options(&diff->opts, &xdiff_config, &xdiff_params);
+ memset(&xdiff_callback, 0, sizeof(xdiff_callback));
+ xdiff_callback.outf = diff_output_cb;
+ xdiff_callback.priv = &info;
+
+ git_vector_foreach(&diff->deltas, info.index, delta) {
+ git_blob *old_blob = NULL, *new_blob = NULL;
+ git_map old_data, new_data;
+ mmfile_t old_xdiff_data, new_xdiff_data;
+
+ if (delta->status == GIT_DELTA_UNMODIFIED &&
+ (diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
+ continue;
+
+ if (delta->status == GIT_DELTA_IGNORED &&
+ (diff->opts.flags & GIT_DIFF_INCLUDE_IGNORED) == 0)
+ continue;
+
+ if (delta->status == GIT_DELTA_UNTRACKED &&
+ (diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
+ continue;
+
+ if ((error = file_is_binary_by_attr(diff, delta)) < 0)
+ goto cleanup;
+
+ old_data.data = "";
+ old_data.len = 0;
+ new_data.data = "";
+ new_data.len = 0;
+
+ /* TODO: Partial blob reading to defer loading whole blob.
+ * I.e. I want a blob with just the first 4kb loaded, then
+ * later on I will read the rest of the blob if needed.
+ */
+
+ /* map files */
+ if (delta->binary != 1 &&
+ (hunk_cb || line_cb) &&
+ (delta->status == GIT_DELTA_DELETED ||
+ delta->status == GIT_DELTA_MODIFIED))
+ {
+ if (diff->old_src == GIT_ITERATOR_WORKDIR)
+ error = get_workdir_content(diff->repo, &delta->old_file, &old_data);
+ else
+ error = get_blob_content(
+ diff->repo, &delta->old_file.oid, &old_data, &old_blob);
+
+ if (error < 0)
+ goto cleanup;
+ }
+
+ if (delta->binary != 1 &&
+ (hunk_cb || line_cb || git_oid_iszero(&delta->new_file.oid)) &&
+ (delta->status == GIT_DELTA_ADDED ||
+ delta->status == GIT_DELTA_MODIFIED))
+ {
+ if (diff->new_src == GIT_ITERATOR_WORKDIR)
+ error = get_workdir_content(diff->repo, &delta->new_file, &new_data);
+ else
+ error = get_blob_content(
+ diff->repo, &delta->new_file.oid, &new_data, &new_blob);
+
+ if (error < 0)
+ goto cleanup;
+
+ if ((delta->new_file.flags | GIT_DIFF_FILE_VALID_OID) == 0) {
+ error = git_odb_hash(
+ &delta->new_file.oid, new_data.data, new_data.len, GIT_OBJ_BLOB);
+
+ if (error < 0)
+ goto cleanup;
+
+ /* since we did not have the definitive oid, we may have
+ * incorrect status and need to skip this item.
+ */
+ if (git_oid_cmp(&delta->old_file.oid, &delta->new_file.oid) == 0) {
+ delta->status = GIT_DELTA_UNMODIFIED;
+ if ((diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
+ goto cleanup;
+ }
+ }
+ }
+
+ /* if we have not already decided whether file is binary,
+ * check the first 4K for nul bytes to decide...
+ */
+ if (delta->binary == -1) {
+ error = file_is_binary_by_content(
+ delta, &old_data, &new_data);
+ if (error < 0)
+ goto cleanup;
+ }
+
+ /* TODO: if ignore_whitespace is set, then we *must* do text
+ * diffs to tell if a file has really been changed.
+ */
+
+ if (file_cb != NULL) {
+ error = file_cb(data, delta, (float)info.index / diff->deltas.length);
+ if (error < 0)
+ goto cleanup;
+ }
+
+ /* don't do hunk and line diffs if file is binary */
+ if (delta->binary == 1)
+ goto cleanup;
+
+ /* nothing to do if we did not get data */
+ if (!old_data.len && !new_data.len)
+ goto cleanup;
+
+ assert(hunk_cb || line_cb);
+
+ info.delta = delta;
+ old_xdiff_data.ptr = old_data.data;
+ old_xdiff_data.size = old_data.len;
+ new_xdiff_data.ptr = new_data.data;
+ new_xdiff_data.size = new_data.len;
+
+ xdl_diff(&old_xdiff_data, &new_xdiff_data,
+ &xdiff_params, &xdiff_config, &xdiff_callback);
+
+cleanup:
+ release_content(&delta->old_file, &old_data, old_blob);
+ release_content(&delta->new_file, &new_data, new_blob);
+
+ if (error < 0)
+ break;
+ }
+
+ return error;
+}
+
+
+typedef struct {
+ git_diff_list *diff;
+ git_diff_data_fn print_cb;
+ void *cb_data;
+ git_buf *buf;
+} diff_print_info;
+
+static char pick_suffix(int mode)
+{
+ if (S_ISDIR(mode))
+ return '/';
+ else if (mode & 0100)
+ /* in git, modes are very regular, so we must have 0100755 mode */
+ return '*';
+ else
+ return ' ';
+}
+
+static int print_compact(void *data, git_diff_delta *delta, float progress)
+{
+ diff_print_info *pi = data;
+ char code, old_suffix, new_suffix;
+
+ GIT_UNUSED(progress);
+
+ switch (delta->status) {
+ case GIT_DELTA_ADDED: code = 'A'; break;
+ case GIT_DELTA_DELETED: code = 'D'; break;
+ case GIT_DELTA_MODIFIED: code = 'M'; break;
+ case GIT_DELTA_RENAMED: code = 'R'; break;
+ case GIT_DELTA_COPIED: code = 'C'; break;
+ case GIT_DELTA_IGNORED: code = 'I'; break;
+ case GIT_DELTA_UNTRACKED: code = '?'; break;
+ default: code = 0;
+ }
+
+ if (!code)
+ return 0;
+
+ old_suffix = pick_suffix(delta->old_file.mode);
+ new_suffix = pick_suffix(delta->new_file.mode);
+
+ git_buf_clear(pi->buf);
+
+ if (delta->old_file.path != delta->new_file.path &&
+ strcmp(delta->old_file.path,delta->new_file.path) != 0)
+ git_buf_printf(pi->buf, "%c\t%s%c -> %s%c\n", code,
+ delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
+ else if (delta->old_file.mode != delta->new_file.mode &&
+ delta->old_file.mode != 0 && delta->new_file.mode != 0)
+ git_buf_printf(pi->buf, "%c\t%s%c (%o -> %o)\n", code,
+ delta->old_file.path, new_suffix, delta->old_file.mode, delta->new_file.mode);
+ else if (old_suffix != ' ')
+ git_buf_printf(pi->buf, "%c\t%s%c\n", code, delta->old_file.path, old_suffix);
+ else
+ git_buf_printf(pi->buf, "%c\t%s\n", code, delta->old_file.path);
+
+ if (git_buf_oom(pi->buf))
+ return -1;
+
+ return pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_FILE_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf));
+}
+
+int git_diff_print_compact(
+ git_diff_list *diff,
+ void *cb_data,
+ git_diff_data_fn print_cb)
+{
+ int error;
+ git_buf buf = GIT_BUF_INIT;
+ diff_print_info pi;
+
+ pi.diff = diff;
+ pi.print_cb = print_cb;
+ pi.cb_data = cb_data;
+ pi.buf = &buf;
+
+ error = git_diff_foreach(diff, &pi, print_compact, NULL, NULL);
+
+ git_buf_free(&buf);
+
+ return error;
+}
+
+
+static int print_oid_range(diff_print_info *pi, git_diff_delta *delta)
+{
+ char start_oid[8], end_oid[8];
+
+ /* TODO: Determine a good actual OID range to print */
+ git_oid_tostr(start_oid, sizeof(start_oid), &delta->old_file.oid);
+ git_oid_tostr(end_oid, sizeof(end_oid), &delta->new_file.oid);
+
+ /* TODO: Match git diff more closely */
+ if (delta->old_file.mode == delta->new_file.mode) {
+ git_buf_printf(pi->buf, "index %s..%s %o\n",
+ start_oid, end_oid, delta->old_file.mode);
+ } else {
+ if (delta->old_file.mode == 0) {
+ git_buf_printf(pi->buf, "new file mode %o\n", delta->new_file.mode);
+ } else if (delta->new_file.mode == 0) {
+ git_buf_printf(pi->buf, "deleted file mode %o\n", delta->old_file.mode);
+ } else {
+ git_buf_printf(pi->buf, "old mode %o\n", delta->old_file.mode);
+ git_buf_printf(pi->buf, "new mode %o\n", delta->new_file.mode);
+ }
+ git_buf_printf(pi->buf, "index %s..%s\n", start_oid, end_oid);
+ }
+
+ if (git_buf_oom(pi->buf))
+ return -1;
+
+ return 0;
+}
+
+static int print_patch_file(void *data, git_diff_delta *delta, float progress)
+{
+ diff_print_info *pi = data;
+ const char *oldpfx = pi->diff->opts.old_prefix;
+ const char *oldpath = delta->old_file.path;
+ const char *newpfx = pi->diff->opts.new_prefix;
+ const char *newpath = delta->new_file.path;
+ int result;
+
+ GIT_UNUSED(progress);
+
+ if (!oldpfx)
+ oldpfx = DIFF_OLD_PREFIX_DEFAULT;
+
+ if (!newpfx)
+ newpfx = DIFF_NEW_PREFIX_DEFAULT;
+
+ git_buf_clear(pi->buf);
+ git_buf_printf(pi->buf, "diff --git %s%s %s%s\n", oldpfx, delta->old_file.path, newpfx, delta->new_file.path);
+
+ if (print_oid_range(pi, delta) < 0)
+ return -1;
+
+ if (git_oid_iszero(&delta->old_file.oid)) {
+ oldpfx = "";
+ oldpath = "/dev/null";
+ }
+ if (git_oid_iszero(&delta->new_file.oid)) {
+ newpfx = "";
+ newpath = "/dev/null";
+ }
+
+ if (delta->binary != 1) {
+ git_buf_printf(pi->buf, "--- %s%s\n", oldpfx, oldpath);
+ git_buf_printf(pi->buf, "+++ %s%s\n", newpfx, newpath);
+ }
+
+ if (git_buf_oom(pi->buf))
+ return -1;
+
+ result = pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_FILE_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf));
+ if (result < 0)
+ return result;
+
+ if (delta->binary != 1)
+ return 0;
+
+ git_buf_clear(pi->buf);
+ git_buf_printf(
+ pi->buf, "Binary files %s%s and %s%s differ\n",
+ oldpfx, oldpath, newpfx, newpath);
+ if (git_buf_oom(pi->buf))
+ return -1;
+
+ return pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_BINARY, git_buf_cstr(pi->buf), git_buf_len(pi->buf));
+}
+
+static int print_patch_hunk(
+ void *data,
+ git_diff_delta *d,
+ git_diff_range *r,
+ const char *header,
+ size_t header_len)
+{
+ diff_print_info *pi = data;
+
+ git_buf_clear(pi->buf);
+ if (git_buf_printf(pi->buf, "%.*s", (int)header_len, header) < 0)
+ return -1;
+
+ return pi->print_cb(pi->cb_data, d, r, GIT_DIFF_LINE_HUNK_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf));
+}
+
+static int print_patch_line(
+ void *data,
+ git_diff_delta *delta,
+ git_diff_range *range,
+ char line_origin, /* GIT_DIFF_LINE value from above */
+ const char *content,
+ size_t content_len)
+{
+ diff_print_info *pi = data;
+
+ git_buf_clear(pi->buf);
+
+ if (line_origin == GIT_DIFF_LINE_ADDITION ||
+ line_origin == GIT_DIFF_LINE_DELETION ||
+ line_origin == GIT_DIFF_LINE_CONTEXT)
+ git_buf_printf(pi->buf, "%c%.*s", line_origin, (int)content_len, content);
+ else if (content_len > 0)
+ git_buf_printf(pi->buf, "%.*s", (int)content_len, content);
+
+ if (git_buf_oom(pi->buf))
+ return -1;
+
+ return pi->print_cb(pi->cb_data, delta, range, line_origin, git_buf_cstr(pi->buf), git_buf_len(pi->buf));
+}
+
+int git_diff_print_patch(
+ git_diff_list *diff,
+ void *cb_data,
+ git_diff_data_fn print_cb)
+{
+ int error;
+ git_buf buf = GIT_BUF_INIT;
+ diff_print_info pi;
+
+ pi.diff = diff;
+ pi.print_cb = print_cb;
+ pi.cb_data = cb_data;
+ pi.buf = &buf;
+
+ error = git_diff_foreach(
+ diff, &pi, print_patch_file, print_patch_hunk, print_patch_line);
+
+ git_buf_free(&buf);
+
+ return error;
+}
+
+int git_diff_blobs(
+ git_blob *old_blob,
+ git_blob *new_blob,
+ git_diff_options *options,
+ void *cb_data,
+ git_diff_file_fn file_cb,
+ git_diff_hunk_fn hunk_cb,
+ git_diff_data_fn line_cb)
+{
+ diff_output_info info;
+ git_diff_delta delta;
+ mmfile_t old_data, new_data;
+ git_map old_map, new_map;
+ xpparam_t xdiff_params;
+ xdemitconf_t xdiff_config;
+ xdemitcb_t xdiff_callback;
+ git_blob *new, *old;
+
+ memset(&delta, 0, sizeof(delta));
+
+ new = new_blob;
+ old = old_blob;
+
+ if (options && (options->flags & GIT_DIFF_REVERSE)) {
+ git_blob *swap = old;
+ old = new;
+ new = swap;
+ }
+
+ if (old) {
+ old_data.ptr = (char *)git_blob_rawcontent(old);
+ old_data.size = git_blob_rawsize(old);
+ git_oid_cpy(&delta.old_file.oid, git_object_id((const git_object *)old));
+ } else {
+ old_data.ptr = "";
+ old_data.size = 0;
+ }
+
+ if (new) {
+ new_data.ptr = (char *)git_blob_rawcontent(new);
+ new_data.size = git_blob_rawsize(new);
+ git_oid_cpy(&delta.new_file.oid, git_object_id((const git_object *)new));
+ } else {
+ new_data.ptr = "";
+ new_data.size = 0;
+ }
+
+ /* populate a "fake" delta record */
+ delta.status = new ?
+ (old ? GIT_DELTA_MODIFIED : GIT_DELTA_ADDED) :
+ (old ? GIT_DELTA_DELETED : GIT_DELTA_UNTRACKED);
+
+ if (git_oid_cmp(&delta.new_file.oid, &delta.old_file.oid) == 0)
+ delta.status = GIT_DELTA_UNMODIFIED;
+
+ delta.old_file.size = old_data.size;
+ delta.new_file.size = new_data.size;
+
+ fill_map_from_mmfile(&old_map, &old_data);
+ fill_map_from_mmfile(&new_map, &new_data);
+
+ if (file_is_binary_by_content(&delta, &old_map, &new_map) < 0)
+ return -1;
+
+ if (file_cb != NULL) {
+ int error = file_cb(cb_data, &delta, 1);
+ if (error < 0)
+ return error;
+ }
+
+ /* don't do hunk and line diffs if the two blobs are identical */
+ if (delta.status == GIT_DELTA_UNMODIFIED)
+ return 0;
+
+ /* don't do hunk and line diffs if file is binary */
+ if (delta.binary == 1)
+ return 0;
+
+ info.diff = NULL;
+ info.delta = &delta;
+ info.cb_data = cb_data;
+ info.hunk_cb = hunk_cb;
+ info.line_cb = line_cb;
+
+ setup_xdiff_options(options, &xdiff_config, &xdiff_params);
+ memset(&xdiff_callback, 0, sizeof(xdiff_callback));
+ xdiff_callback.outf = diff_output_cb;
+ xdiff_callback.priv = &info;
+
+ xdl_diff(&old_data, &new_data, &xdiff_params, &xdiff_config, &xdiff_callback);
+
+ return 0;
+}
diff --git a/src/errors.c b/src/errors.c
index 81770e786..d43d7d9b5 100644
--- a/src/errors.c
+++ b/src/errors.c
@@ -1,104 +1,119 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include "common.h"
#include "global.h"
+#include "posix.h"
+#include "buffer.h"
#include <stdarg.h>
-static struct {
- int num;
- const char *str;
-} error_codes[] = {
- {GIT_ERROR, "Unspecified error"},
- {GIT_ENOTOID, "Input was not a properly formatted Git object id."},
- {GIT_ENOTFOUND, "Object does not exist in the scope searched."},
- {GIT_ENOMEM, "Not enough space available."},
- {GIT_EOSERR, "Consult the OS error information."},
- {GIT_EOBJTYPE, "The specified object is of invalid type"},
- {GIT_EOBJCORRUPTED, "The specified object has its data corrupted"},
- {GIT_ENOTAREPO, "The specified repository is invalid"},
- {GIT_EINVALIDTYPE, "The object or config variable type is invalid or doesn't match"},
- {GIT_EMISSINGOBJDATA, "The object cannot be written that because it's missing internal data"},
- {GIT_EPACKCORRUPTED, "The packfile for the ODB is corrupted"},
- {GIT_EFLOCKFAIL, "Failed to adquire or release a file lock"},
- {GIT_EZLIB, "The Z library failed to inflate/deflate an object's data"},
- {GIT_EBUSY, "The queried object is currently busy"},
- {GIT_EINVALIDPATH, "The path is invalid"},
- {GIT_EBAREINDEX, "The index file is not backed up by an existing repository"},
- {GIT_EINVALIDREFNAME, "The name of the reference is not valid"},
- {GIT_EREFCORRUPTED, "The specified reference has its data corrupted"},
- {GIT_ETOONESTEDSYMREF, "The specified symbolic reference is too deeply nested"},
- {GIT_EPACKEDREFSCORRUPTED, "The pack-refs file is either corrupted of its format is not currently supported"},
- {GIT_EINVALIDPATH, "The path is invalid" },
- {GIT_EREVWALKOVER, "The revision walker is empty; there are no more commits left to iterate"},
- {GIT_EINVALIDREFSTATE, "The state of the reference is not valid"},
- {GIT_ENOTIMPLEMENTED, "This feature has not been implemented yet"},
- {GIT_EEXISTS, "A reference with this name already exists"},
- {GIT_EOVERFLOW, "The given integer literal is too large to be parsed"},
- {GIT_ENOTNUM, "The given literal is not a valid number"},
- {GIT_EAMBIGUOUSOIDPREFIX, "The given oid prefix is ambiguous"},
+/********************************************
+ * New error handling
+ ********************************************/
+
+static git_error g_git_oom_error = {
+ "Out of memory",
+ GITERR_NOMEMORY
};
-const char *git_strerror(int num)
+static void set_error(int error_class, char *string)
{
- size_t i;
+ git_error *error = &GIT_GLOBAL->error_t;
- if (num == GIT_EOSERR)
- return strerror(errno);
- for (i = 0; i < ARRAY_SIZE(error_codes); i++)
- if (num == error_codes[i].num)
- return error_codes[i].str;
+ git__free(error->message);
- return "Unknown error";
-}
+ error->message = string;
+ error->klass = error_class;
-#define ERROR_MAX_LEN 1024
+ GIT_GLOBAL->last_error = error;
+}
-void git___rethrow(const char *msg, ...)
+void giterr_set_oom(void)
{
- char new_error[ERROR_MAX_LEN];
- char *last_error;
- char *old_error = NULL;
-
- va_list va;
-
- last_error = GIT_GLOBAL->error.last;
-
- va_start(va, msg);
- vsnprintf(new_error, ERROR_MAX_LEN, msg, va);
- va_end(va);
-
- old_error = git__strdup(last_error);
-
- snprintf(last_error, ERROR_MAX_LEN, "%s \n - %s", new_error, old_error);
+ GIT_GLOBAL->last_error = &g_git_oom_error;
+}
- git__free(old_error);
+void giterr_set(int error_class, const char *string, ...)
+{
+ git_buf buf = GIT_BUF_INIT;
+ va_list arglist;
+
+ int unix_error_code = 0;
+
+#ifdef GIT_WIN32
+ DWORD win32_error_code = 0;
+#endif
+
+ if (error_class == GITERR_OS) {
+ unix_error_code = errno;
+ errno = 0;
+
+#ifdef GIT_WIN32
+ win32_error_code = GetLastError();
+ SetLastError(0);
+#endif
+ }
+
+ va_start(arglist, string);
+ git_buf_vprintf(&buf, string, arglist);
+ va_end(arglist);
+
+ /* automatically suffix strerror(errno) for GITERR_OS errors */
+ if (error_class == GITERR_OS) {
+
+ if (unix_error_code != 0) {
+ git_buf_PUTS(&buf, ": ");
+ git_buf_puts(&buf, strerror(unix_error_code));
+ }
+
+#ifdef GIT_WIN32
+ else if (win32_error_code != 0) {
+ LPVOID lpMsgBuf = NULL;
+
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, win32_error_code, 0, (LPTSTR) &lpMsgBuf, 0, NULL);
+
+ if (lpMsgBuf) {
+ git_buf_PUTS(&buf, ": ");
+ git_buf_puts(&buf, lpMsgBuf);
+ LocalFree(lpMsgBuf);
+ }
+ }
+#endif
+ }
+
+ if (!git_buf_oom(&buf))
+ set_error(error_class, git_buf_detach(&buf));
}
-void git___throw(const char *msg, ...)
+void giterr_set_str(int error_class, const char *string)
{
- va_list va;
+ char *message = git__strdup(string);
- va_start(va, msg);
- vsnprintf(GIT_GLOBAL->error.last, ERROR_MAX_LEN, msg, va);
- va_end(va);
+ if (message)
+ set_error(error_class, message);
}
-const char *git_lasterror(void)
+void giterr_set_regex(const regex_t *regex, int error_code)
{
- char *last_error = GIT_GLOBAL->error.last;
-
- if (!last_error[0])
- return NULL;
+ char error_buf[1024];
+ regerror(error_code, regex, error_buf, sizeof(error_buf));
+ giterr_set_str(GITERR_REGEX, error_buf);
+}
- return last_error;
+void giterr_clear(void)
+{
+ GIT_GLOBAL->last_error = NULL;
}
-void git_clearerror(void)
+const git_error *giterr_last(void)
{
- char *last_error = GIT_GLOBAL->error.last;
- last_error[0] = '\0';
+ return GIT_GLOBAL->last_error;
}
+
diff --git a/src/fetch.c b/src/fetch.c
index f9e15b232..c92cf4ef5 100644
--- a/src/fetch.c
+++ b/src/fetch.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -9,6 +9,7 @@
#include "git2/oid.h"
#include "git2/refs.h"
#include "git2/revwalk.h"
+#include "git2/indexer.h"
#include "common.h"
#include "transport.h"
@@ -28,19 +29,17 @@ struct filter_payload {
static int filter_ref__cb(git_remote_head *head, void *payload)
{
struct filter_payload *p = payload;
- int error;
if (!p->found_head && strcmp(head->name, GIT_HEAD_FILE) == 0) {
p->found_head = 1;
} else {
/* If it doesn't match the refpec, we don't want it */
- error = git_refspec_src_match(p->spec, head->name);
+ if (!git_refspec_src_matches(p->spec, head->name))
+ return 0;
- if (error == GIT_ENOMATCH)
- return GIT_SUCCESS;
-
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Error matching remote ref name");
+ /* Don't even try to ask for the annotation target */
+ if (!git__suffixcmp(head->name, "^{}"))
+ return 0;
}
/* If we have the object, mark it so we don't ask for it */
@@ -54,7 +53,6 @@ static int filter_ref__cb(git_remote_head *head, void *payload)
static int filter_wants(git_remote *remote)
{
- int error;
struct filter_payload p;
git_vector_clear(&remote->refs);
@@ -69,9 +67,8 @@ static int filter_wants(git_remote *remote)
p.found_head = 0;
p.remote = remote;
- error = git_repository_odb__weakptr(&p.odb, remote->repo);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_repository_odb__weakptr(&p.odb, remote->repo) < 0)
+ return -1;
return remote->transport->ls(remote->transport, &filter_ref__cb, &p);
}
@@ -83,19 +80,16 @@ static int filter_wants(git_remote *remote)
*/
int git_fetch_negotiate(git_remote *remote)
{
- int error;
git_transport *t = remote->transport;
- error = filter_wants(remote);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to filter the reference list for wants");
+ if (filter_wants(remote) < 0) {
+ giterr_set(GITERR_NET, "Failed to filter the reference list for wants");
+ return -1;
+ }
/* Don't try to negotiate when we don't want anything */
- if (remote->refs.length == 0)
- return GIT_SUCCESS;
-
- if (!remote->need_pack)
- return GIT_SUCCESS;
+ if (remote->refs.length == 0 || !remote->need_pack)
+ return 0;
/*
* Now we have everything set up so we can start tell the server
@@ -104,75 +98,103 @@ int git_fetch_negotiate(git_remote *remote)
return t->negotiate_fetch(t, remote->repo, &remote->refs);
}
-int git_fetch_download_pack(char **out, git_remote *remote)
+int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats)
{
- if(!remote->need_pack) {
- *out = NULL;
- return GIT_SUCCESS;
- }
+ if(!remote->need_pack)
+ return 0;
- return remote->transport->download_pack(out, remote->transport, remote->repo);
+ return remote->transport->download_pack(remote->transport, remote->repo, bytes, stats);
}
/* Receiving data from a socket and storing it is pretty much the same for git and HTTP */
int git_fetch__download_pack(
- char **out,
const char *buffered,
size_t buffered_size,
GIT_SOCKET fd,
- git_repository *repo)
+ git_repository *repo,
+ git_off_t *bytes,
+ git_indexer_stats *stats)
{
- git_filebuf file = GIT_FILEBUF_INIT;
- int error;
+ int recvd;
char buff[1024];
- git_buf path = GIT_BUF_INIT;
- static const char suff[] = "/objects/pack/pack-received";
gitno_buffer buf;
+ git_indexer_stream *idx;
gitno_buffer_setup(&buf, buff, sizeof(buff), fd);
if (memcmp(buffered, "PACK", strlen("PACK"))) {
- return git__throw(GIT_ERROR, "The pack doesn't start with the signature");
+ giterr_set(GITERR_NET, "The pack doesn't start with the signature");
+ return -1;
}
- error = git_buf_joinpath(&path, repo->path_repository, suff);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (git_indexer_stream_new(&idx, git_repository_path(repo)) < 0)
+ return -1;
- error = git_filebuf_open(&file, path.ptr, GIT_FILEBUF_TEMPORARY);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ memset(stats, 0, sizeof(git_indexer_stats));
+ if (git_indexer_stream_add(idx, buffered, buffered_size, stats) < 0)
+ goto on_error;
- /* Part of the packfile has been received, don't loose it */
- error = git_filebuf_write(&file, buffered, buffered_size);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ *bytes = buffered_size;
- while (1) {
- error = git_filebuf_write(&file, buf.data, buf.offset);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ do {
+ if (git_indexer_stream_add(idx, buf.data, buf.offset, stats) < 0)
+ goto on_error;
gitno_consume_n(&buf, buf.offset);
- error = gitno_recv(&buf);
- if (error < GIT_SUCCESS)
- goto cleanup;
- if (error == 0) /* Orderly shutdown */
- break;
- }
+ if ((recvd = gitno_recv(&buf)) < 0)
+ goto on_error;
+
+ *bytes += recvd;
+ } while(recvd > 0);
+
+ if (git_indexer_stream_finalize(idx, stats))
+ goto on_error;
+
+ git_indexer_stream_free(idx);
+ return 0;
+
+on_error:
+ git_indexer_stream_free(idx);
+ return -1;
+}
+
+int git_fetch_setup_walk(git_revwalk **out, git_repository *repo)
+{
+ git_revwalk *walk;
+ git_strarray refs;
+ unsigned int i;
+ git_reference *ref;
+
+ if (git_reference_list(&refs, repo, GIT_REF_LISTALL) < 0)
+ return -1;
+
+ if (git_revwalk_new(&walk, repo) < 0)
+ return -1;
+
+ git_revwalk_sorting(walk, GIT_SORT_TIME);
+
+ for (i = 0; i < refs.count; ++i) {
+ /* No tags */
+ if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR))
+ continue;
+
+ if (git_reference_lookup(&ref, repo, refs.strings[i]) < 0)
+ goto on_error;
+
+ if (git_reference_type(ref) == GIT_REF_SYMBOLIC)
+ continue;
+ if (git_revwalk_push(walk, git_reference_oid(ref)) < 0)
+ goto on_error;
- *out = git__strdup(file.path_lock);
- if (*out == NULL) {
- error = GIT_ENOMEM;
- goto cleanup;
+ git_reference_free(ref);
}
- /* A bit dodgy, but we need to keep the pack at the temporary path */
- error = git_filebuf_commit_at(&file, file.path_lock, GIT_PACK_FILE_MODE);
-cleanup:
- if (error < GIT_SUCCESS)
- git_filebuf_cleanup(&file);
- git_buf_free(&path);
+ git_strarray_free(&refs);
+ *out = walk;
+ return 0;
- return error;
+on_error:
+ git_reference_free(ref);
+ git_strarray_free(&refs);
+ return -1;
}
diff --git a/src/fetch.h b/src/fetch.h
index a45d936a9..b3192a563 100644
--- a/src/fetch.h
+++ b/src/fetch.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -10,9 +10,10 @@
#include "netops.h"
int git_fetch_negotiate(git_remote *remote);
-int git_fetch_download_pack(char **out, git_remote *remote);
+int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats);
-int git_fetch__download_pack(char **out, const char *buffered, size_t buffered_size,
- GIT_SOCKET fd, git_repository *repo);
+int git_fetch__download_pack(const char *buffered, size_t buffered_size, GIT_SOCKET fd,
+ git_repository *repo, git_off_t *bytes, git_indexer_stats *stats);
+int git_fetch_setup_walk(git_revwalk **out, git_repository *repo);
#endif
diff --git a/src/filebuf.c b/src/filebuf.c
index 447d8a089..6538aea66 100644
--- a/src/filebuf.c
+++ b/src/filebuf.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -14,13 +14,46 @@
static const size_t WRITE_BUFFER_SIZE = (4096 * 2);
+enum buferr_t {
+ BUFERR_OK = 0,
+ BUFERR_WRITE,
+ BUFERR_ZLIB,
+ BUFERR_MEM
+};
+
+#define ENSURE_BUF_OK(buf) if ((buf)->last_error != BUFERR_OK) { return -1; }
+
+static int verify_last_error(git_filebuf *file)
+{
+ switch (file->last_error) {
+ case BUFERR_WRITE:
+ giterr_set(GITERR_OS, "Failed to write out file");
+ return -1;
+
+ case BUFERR_MEM:
+ giterr_set_oom();
+ return -1;
+
+ case BUFERR_ZLIB:
+ giterr_set(GITERR_ZLIB,
+ "Buffer error when writing out ZLib data");
+ return -1;
+
+ default:
+ return 0;
+ }
+}
+
static int lock_file(git_filebuf *file, int flags)
{
- if (git_path_exists(file->path_lock) == 0) {
+ if (git_path_exists(file->path_lock) == true) {
if (flags & GIT_FILEBUF_FORCE)
p_unlink(file->path_lock);
- else
- return git__throw(GIT_EOSERR, "Failed to lock file");
+ else {
+ giterr_set(GITERR_OS,
+ "Failed to lock file '%s' for writing", file->path_lock);
+ return -1;
+ }
}
/* create path to the file buffer is required */
@@ -32,16 +65,22 @@ static int lock_file(git_filebuf *file, int flags)
}
if (file->fd < 0)
- return git__throw(GIT_EOSERR, "Failed to create lock");
+ return -1;
+
+ file->fd_is_open = true;
- if ((flags & GIT_FILEBUF_APPEND) && git_path_exists(file->path_original) == 0) {
+ if ((flags & GIT_FILEBUF_APPEND) && git_path_exists(file->path_original) == true) {
git_file source;
char buffer[2048];
size_t read_bytes;
source = p_open(file->path_original, O_RDONLY);
- if (source < 0)
- return git__throw(GIT_EOSERR, "Failed to lock file. Could not open %s", file->path_original);
+ if (source < 0) {
+ giterr_set(GITERR_OS,
+ "Failed to open file '%s' for reading",
+ file->path_original);
+ return -1;
+ }
while ((read_bytes = p_read(source, buffer, 2048)) > 0) {
p_write(file->fd, buffer, read_bytes);
@@ -52,15 +91,15 @@ static int lock_file(git_filebuf *file, int flags)
p_close(source);
}
- return GIT_SUCCESS;
+ return 0;
}
void git_filebuf_cleanup(git_filebuf *file)
{
- if (file->fd >= 0)
+ if (file->fd_is_open && file->fd >= 0)
p_close(file->fd);
- if (file->fd >= 0 && file->path_lock && git_path_exists(file->path_lock) == GIT_SUCCESS)
+ if (file->fd_is_open && file->path_lock && git_path_exists(file->path_lock))
p_unlink(file->path_lock);
if (file->digest)
@@ -93,20 +132,21 @@ GIT_INLINE(int) flush_buffer(git_filebuf *file)
static int write_normal(git_filebuf *file, void *source, size_t len)
{
- int result = 0;
-
if (len > 0) {
- result = p_write(file->fd, (void *)source, len);
+ if (p_write(file->fd, (void *)source, len) < 0) {
+ file->last_error = BUFERR_WRITE;
+ return -1;
+ }
+
if (file->digest)
git_hash_update(file->digest, source, len);
}
- return result;
+ return 0;
}
static int write_deflate(git_filebuf *file, void *source, size_t len)
{
- int result = Z_OK;
z_stream *zs = &file->zs;
if (len > 0 || file->flush_mode == Z_FINISH) {
@@ -119,14 +159,17 @@ static int write_deflate(git_filebuf *file, void *source, size_t len)
zs->next_out = file->z_buf;
zs->avail_out = (uInt)file->buf_size;
- result = deflate(zs, file->flush_mode);
- if (result == Z_STREAM_ERROR)
- return git__throw(GIT_ERROR, "Failed to deflate input");
+ if (deflate(zs, file->flush_mode) == Z_STREAM_ERROR) {
+ file->last_error = BUFERR_ZLIB;
+ return -1;
+ }
have = file->buf_size - (size_t)zs->avail_out;
- if (p_write(file->fd, file->z_buf, have) < GIT_SUCCESS)
- return git__throw(GIT_EOSERR, "Failed to write to file");
+ if (p_write(file->fd, file->z_buf, have) < 0) {
+ file->last_error = BUFERR_WRITE;
+ return -1;
+ }
} while (zs->avail_out == 0);
@@ -136,38 +179,39 @@ static int write_deflate(git_filebuf *file, void *source, size_t len)
git_hash_update(file->digest, source, len);
}
- return GIT_SUCCESS;
+ return 0;
}
int git_filebuf_open(git_filebuf *file, const char *path, int flags)
{
- int error, compression;
+ int compression;
size_t path_len;
- assert(file && path);
-
- if (file->buffer)
- return git__throw(GIT_EINVALIDARGS, "Tried to reopen an open filebuf");
+ /* opening an already open buffer is a programming error;
+ * assert that this never happens instead of returning
+ * an error code */
+ assert(file && path && file->buffer == NULL);
memset(file, 0x0, sizeof(git_filebuf));
+ if (flags & GIT_FILEBUF_DO_NOT_BUFFER)
+ file->do_not_buffer = true;
+
file->buf_size = WRITE_BUFFER_SIZE;
file->buf_pos = 0;
file->fd = -1;
+ file->last_error = BUFERR_OK;
/* Allocate the main cache buffer */
- file->buffer = git__malloc(file->buf_size);
- if (file->buffer == NULL){
- error = GIT_ENOMEM;
- goto cleanup;
+ if (!file->do_not_buffer) {
+ file->buffer = git__malloc(file->buf_size);
+ GITERR_CHECK_ALLOC(file->buffer);
}
/* If we are hashing on-write, allocate a new hash context */
if (flags & GIT_FILEBUF_HASH_CONTENTS) {
- if ((file->digest = git_hash_new_ctx()) == NULL) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
+ file->digest = git_hash_new_ctx();
+ GITERR_CHECK_ALLOC(file->digest);
}
compression = flags >> GIT_FILEBUF_DEFLATE_SHIFT;
@@ -176,16 +220,13 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags)
if (compression != 0) {
/* Initialize the ZLib stream */
if (deflateInit(&file->zs, compression) != Z_OK) {
- error = git__throw(GIT_EZLIB, "Failed to initialize zlib");
+ giterr_set(GITERR_ZLIB, "Failed to initialize zlib");
goto cleanup;
}
/* Allocate the Zlib cache buffer */
file->z_buf = git__malloc(file->buf_size);
- if (file->z_buf == NULL){
- error = GIT_ENOMEM;
- goto cleanup;
- }
+ GITERR_CHECK_ALLOC(file->z_buf);
/* Never flush */
file->flush_mode = Z_NO_FLUSH;
@@ -200,104 +241,101 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags)
/* Open the file as temporary for locking */
file->fd = git_futils_mktmp(&tmp_path, path);
+
if (file->fd < 0) {
git_buf_free(&tmp_path);
- error = GIT_EOSERR;
goto cleanup;
}
+ file->fd_is_open = true;
/* No original path */
file->path_original = NULL;
file->path_lock = git_buf_detach(&tmp_path);
-
- if (file->path_lock == NULL) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
+ GITERR_CHECK_ALLOC(file->path_lock);
} else {
path_len = strlen(path);
/* Save the original path of the file */
file->path_original = git__strdup(path);
- if (file->path_original == NULL) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
+ GITERR_CHECK_ALLOC(file->path_original);
/* create the locking path by appending ".lock" to the original */
file->path_lock = git__malloc(path_len + GIT_FILELOCK_EXTLENGTH);
- if (file->path_lock == NULL) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
+ GITERR_CHECK_ALLOC(file->path_lock);
memcpy(file->path_lock, file->path_original, path_len);
memcpy(file->path_lock + path_len, GIT_FILELOCK_EXTENSION, GIT_FILELOCK_EXTLENGTH);
/* open the file for locking */
- if ((error = lock_file(file, flags)) < GIT_SUCCESS)
+ if (lock_file(file, flags) < 0)
goto cleanup;
}
- return GIT_SUCCESS;
+ return 0;
cleanup:
git_filebuf_cleanup(file);
- return git__rethrow(error, "Failed to open file buffer for '%s'", path);
+ return -1;
}
int git_filebuf_hash(git_oid *oid, git_filebuf *file)
{
- int error;
-
assert(oid && file && file->digest);
- if ((error = flush_buffer(file)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to get hash for file");
+ flush_buffer(file);
+
+ if (verify_last_error(file) < 0)
+ return -1;
git_hash_final(oid, file->digest);
git_hash_free_ctx(file->digest);
file->digest = NULL;
- return GIT_SUCCESS;
+ return 0;
}
int git_filebuf_commit_at(git_filebuf *file, const char *path, mode_t mode)
{
git__free(file->path_original);
file->path_original = git__strdup(path);
- if (file->path_original == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(file->path_original);
return git_filebuf_commit(file, mode);
}
int git_filebuf_commit(git_filebuf *file, mode_t mode)
{
- int error;
-
/* temporary files cannot be committed */
assert(file && file->path_original);
file->flush_mode = Z_FINISH;
- if ((error = flush_buffer(file)) < GIT_SUCCESS)
- goto cleanup;
+ flush_buffer(file);
+
+ if (verify_last_error(file) < 0)
+ goto on_error;
p_close(file->fd);
file->fd = -1;
+ file->fd_is_open = false;
if (p_chmod(file->path_lock, mode)) {
- error = git__throw(GIT_EOSERR, "Failed to chmod locked file before committing");
- goto cleanup;
+ giterr_set(GITERR_OS, "Failed to set attributes for file at '%s'", file->path_lock);
+ goto on_error;
}
- error = p_rename(file->path_lock, file->path_original);
+ p_unlink(file->path_original);
-cleanup:
+ if (p_rename(file->path_lock, file->path_original) < 0) {
+ giterr_set(GITERR_OS, "Failed to rename lockfile to '%s'", file->path_original);
+ goto on_error;
+ }
+
+ git_filebuf_cleanup(file);
+ return 0;
+
+on_error:
git_filebuf_cleanup(file);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to commit locked file from buffer");
- return GIT_SUCCESS;
+ return -1;
}
GIT_INLINE(void) add_to_cache(git_filebuf *file, const void *buf, size_t len)
@@ -308,22 +346,25 @@ GIT_INLINE(void) add_to_cache(git_filebuf *file, const void *buf, size_t len)
int git_filebuf_write(git_filebuf *file, const void *buff, size_t len)
{
- int error;
const unsigned char *buf = buff;
+ ENSURE_BUF_OK(file);
+
+ if (file->do_not_buffer)
+ return file->write(file, (void *)buff, len);
+
for (;;) {
size_t space_left = file->buf_size - file->buf_pos;
/* cache if it's small */
if (space_left > len) {
add_to_cache(file, buf, len);
- return GIT_SUCCESS;
+ return 0;
}
add_to_cache(file, buf, space_left);
-
- if ((error = flush_buffer(file)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to write to buffer");
+ if (flush_buffer(file) < 0)
+ return -1;
len -= space_left;
buf += space_left;
@@ -332,32 +373,37 @@ int git_filebuf_write(git_filebuf *file, const void *buff, size_t len)
int git_filebuf_reserve(git_filebuf *file, void **buffer, size_t len)
{
- int error;
size_t space_left = file->buf_size - file->buf_pos;
*buffer = NULL;
- if (len > file->buf_size)
- return GIT_ENOMEM;
+ ENSURE_BUF_OK(file);
+
+ if (len > file->buf_size) {
+ file->last_error = BUFERR_MEM;
+ return -1;
+ }
if (space_left <= len) {
- if ((error = flush_buffer(file)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to reserve buffer");
+ if (flush_buffer(file) < 0)
+ return -1;
}
*buffer = (file->buffer + file->buf_pos);
file->buf_pos += len;
- return GIT_SUCCESS;
+ return 0;
}
int git_filebuf_printf(git_filebuf *file, const char *format, ...)
{
va_list arglist;
size_t space_left;
- int len, error;
+ int len, res;
char *tmp_buffer;
+ ENSURE_BUF_OK(file);
+
space_left = file->buf_size - file->buf_pos;
do {
@@ -365,24 +411,28 @@ int git_filebuf_printf(git_filebuf *file, const char *format, ...)
len = p_vsnprintf((char *)file->buffer + file->buf_pos, space_left, format, arglist);
va_end(arglist);
- if (len < 0)
- return git__throw(GIT_EOSERR, "Failed to format string");
+ if (len < 0) {
+ file->last_error = BUFERR_MEM;
+ return -1;
+ }
if ((size_t)len + 1 <= space_left) {
file->buf_pos += len;
- return GIT_SUCCESS;
+ return 0;
}
- if ((error = flush_buffer(file)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to output to buffer");
+ if (flush_buffer(file) < 0)
+ return -1;
space_left = file->buf_size - file->buf_pos;
} while ((size_t)len + 1 <= space_left);
tmp_buffer = git__malloc(len + 1);
- if (!tmp_buffer)
- return GIT_ENOMEM;
+ if (!tmp_buffer) {
+ file->last_error = BUFERR_MEM;
+ return -1;
+ }
va_start(arglist, format);
len = p_vsnprintf(tmp_buffer, len + 1, format, arglist);
@@ -390,12 +440,13 @@ int git_filebuf_printf(git_filebuf *file, const char *format, ...)
if (len < 0) {
git__free(tmp_buffer);
- return git__throw(GIT_EOSERR, "Failed to format string");
+ file->last_error = BUFERR_MEM;
+ return -1;
}
- error = git_filebuf_write(file, tmp_buffer, len);
+ res = git_filebuf_write(file, tmp_buffer, len);
git__free(tmp_buffer);
- return error;
+ return res;
}
diff --git a/src/filebuf.h b/src/filebuf.h
index 6c283bc5c..72563b57a 100644
--- a/src/filebuf.h
+++ b/src/filebuf.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -9,7 +9,7 @@
#include "fileops.h"
#include "hash.h"
-#include "git2/zlib.h"
+#include <zlib.h>
#ifdef GIT_THREADS
# define GIT_FILEBUF_THREADS
@@ -19,7 +19,8 @@
#define GIT_FILEBUF_APPEND (1 << 2)
#define GIT_FILEBUF_FORCE (1 << 3)
#define GIT_FILEBUF_TEMPORARY (1 << 4)
-#define GIT_FILEBUF_DEFLATE_SHIFT (5)
+#define GIT_FILEBUF_DO_NOT_BUFFER (1 << 5)
+#define GIT_FILEBUF_DEFLATE_SHIFT (6)
#define GIT_FILELOCK_EXTENSION ".lock\0"
#define GIT_FILELOCK_EXTLENGTH 6
@@ -40,25 +41,37 @@ struct git_filebuf {
size_t buf_size, buf_pos;
git_file fd;
+ bool fd_is_open;
+ bool do_not_buffer;
+ int last_error;
};
typedef struct git_filebuf git_filebuf;
#define GIT_FILEBUF_INIT {0}
-/* The git_filebuf object lifecycle is:
+/*
+ * The git_filebuf object lifecycle is:
* - Allocate git_filebuf, preferably using GIT_FILEBUF_INIT.
+ *
* - Call git_filebuf_open() to initialize the filebuf for use.
+ *
* - Make as many calls to git_filebuf_write(), git_filebuf_printf(),
- * git_filebuf_reserve() as you like.
+ * git_filebuf_reserve() as you like. The error codes for these
+ * functions don't need to be checked. They are stored internally
+ * by the file buffer.
+ *
* - While you are writing, you may call git_filebuf_hash() to get
- * the hash of all you have written so far.
+ * the hash of all you have written so far. This function will
+ * fail if any of the previous writes to the buffer failed.
+ *
* - To close the git_filebuf, you may call git_filebuf_commit() or
* git_filebuf_commit_at() to save the file, or
* git_filebuf_cleanup() to abandon the file. All of these will
- * clear the git_filebuf object.
+ * free the git_filebuf object. Likewise, all of these will fail
+ * if any of the previous writes to the buffer failed, and set
+ * an error code accordingly.
*/
-
int git_filebuf_write(git_filebuf *lock, const void *buff, size_t len);
int git_filebuf_reserve(git_filebuf *file, void **buff, size_t len);
int git_filebuf_printf(git_filebuf *file, const char *format, ...) GIT_FORMAT_PRINTF(2, 3);
diff --git a/src/fileops.c b/src/fileops.c
index e2a6adf0b..ee9d4212d 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -10,25 +10,19 @@
int git_futils_mkpath2file(const char *file_path, const mode_t mode)
{
- int error;
+ int result = 0;
git_buf target_folder = GIT_BUF_INIT;
- error = git_path_dirname_r(&target_folder, file_path);
- if (error < GIT_SUCCESS) {
- git_buf_free(&target_folder);
- return git__throw(GIT_EINVALIDPATH, "Failed to recursively build `%s` tree structure. Unable to parse parent folder name", file_path);
- } else {
- /* reset error */
- error = GIT_SUCCESS;
- }
+ if (git_path_dirname_r(&target_folder, file_path) < 0)
+ return -1;
/* Does the containing folder exist? */
- if (git_path_isdir(target_folder.ptr) != GIT_SUCCESS)
+ if (git_path_isdir(target_folder.ptr) == false)
/* Let's create the tree structure */
- error = git_futils_mkdir_r(target_folder.ptr, NULL, mode);
+ result = git_futils_mkdir_r(target_folder.ptr, NULL, mode);
git_buf_free(&target_folder);
- return error;
+ return result;
}
int git_futils_mktmp(git_buf *path_out, const char *filename)
@@ -39,138 +33,205 @@ int git_futils_mktmp(git_buf *path_out, const char *filename)
git_buf_puts(path_out, "_git2_XXXXXX");
if (git_buf_oom(path_out))
- return git__rethrow(git_buf_lasterror(path_out),
- "Failed to create temporary file for %s", filename);
+ return -1;
- if ((fd = p_mkstemp(path_out->ptr)) < 0)
- return git__throw(GIT_EOSERR, "Failed to create temporary file %s", path_out->ptr);
+ if ((fd = p_mkstemp(path_out->ptr)) < 0) {
+ giterr_set(GITERR_OS,
+ "Failed to create temporary file '%s'", path_out->ptr);
+ return -1;
+ }
return fd;
}
int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode_t mode)
{
- if (git_futils_mkpath2file(path, dirmode) < GIT_SUCCESS)
- return git__throw(GIT_EOSERR, "Failed to create file %s", path);
+ int fd;
+
+ if (git_futils_mkpath2file(path, dirmode) < 0)
+ return -1;
- return p_creat(path, mode);
+ fd = p_creat(path, mode);
+ if (fd < 0) {
+ giterr_set(GITERR_OS, "Failed to create file '%s'", path);
+ return -1;
+ }
+
+ return fd;
}
int git_futils_creat_locked(const char *path, const mode_t mode)
{
- int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode);
- return fd >= 0 ? fd : git__throw(GIT_EOSERR, "Failed to create locked file. Could not open %s", path);
+ int fd;
+
+#ifdef GIT_WIN32
+ wchar_t* buf;
+
+ buf = gitwin_to_utf16(path);
+ fd = _wopen(buf, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode);
+ git__free(buf);
+#else
+ fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode);
+#endif
+
+ if (fd < 0) {
+ giterr_set(GITERR_OS, "Failed to create locked file '%s'", path);
+ return -1;
+ }
+
+ return fd;
}
int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, const mode_t mode)
{
- if (git_futils_mkpath2file(path, dirmode) < GIT_SUCCESS)
- return git__throw(GIT_EOSERR, "Failed to create locked file %s", path);
+ if (git_futils_mkpath2file(path, dirmode) < 0)
+ return -1;
return git_futils_creat_locked(path, mode);
}
+int git_futils_open_ro(const char *path)
+{
+ int fd = p_open(path, O_RDONLY);
+ if (fd < 0) {
+ if (errno == ENOENT)
+ fd = GIT_ENOTFOUND;
+ giterr_set(GITERR_OS, "Failed to open '%s'", path);
+ }
+ return fd;
+}
+
git_off_t git_futils_filesize(git_file fd)
{
struct stat sb;
- if (p_fstat(fd, &sb))
- return GIT_ERROR;
+
+ if (p_fstat(fd, &sb)) {
+ giterr_set(GITERR_OS, "Failed to stat file descriptor");
+ return -1;
+ }
return sb.st_size;
}
-int git_futils_readbuffer_updated(git_fbuffer *obj, const char *path, time_t *mtime, int *updated)
+mode_t git_futils_canonical_mode(mode_t raw_mode)
+{
+ if (S_ISREG(raw_mode))
+ return S_IFREG | GIT_CANONICAL_PERMS(raw_mode);
+ else if (S_ISLNK(raw_mode))
+ return S_IFLNK;
+ else if (S_ISGITLINK(raw_mode))
+ return S_IFGITLINK;
+ else if (S_ISDIR(raw_mode))
+ return S_IFDIR;
+ else
+ return 0;
+}
+
+int git_futils_readbuffer_updated(git_buf *buf, const char *path, time_t *mtime, int *updated)
{
git_file fd;
size_t len;
struct stat st;
- unsigned char *buff;
- assert(obj && path && *path);
+ assert(buf && path && *path);
if (updated != NULL)
*updated = 0;
- if (p_stat(path, &st) < 0)
- return git__throw(GIT_ENOTFOUND, "Failed to stat file %s", path);
+ if ((fd = git_futils_open_ro(path)) < 0)
+ return fd;
- if (S_ISDIR(st.st_mode))
- return git__throw(GIT_ERROR, "Can't read a dir into a buffer");
+ if (p_fstat(fd, &st) < 0 || S_ISDIR(st.st_mode) || !git__is_sizet(st.st_size+1)) {
+ p_close(fd);
+ giterr_set(GITERR_OS, "Invalid regular file stat for '%s'", path);
+ return -1;
+ }
/*
* If we were given a time, we only want to read the file if it
* has been modified.
*/
- if (mtime != NULL && *mtime >= st.st_mtime)
- return GIT_SUCCESS;
+ if (mtime != NULL && *mtime >= st.st_mtime) {
+ p_close(fd);
+ return 0;
+ }
if (mtime != NULL)
*mtime = st.st_mtime;
- if (!git__is_sizet(st.st_size+1))
- return git__throw(GIT_ERROR, "Failed to read file `%s`. An error occured while calculating its size", path);
len = (size_t) st.st_size;
- if ((fd = p_open(path, O_RDONLY)) < 0)
- return git__throw(GIT_EOSERR, "Failed to open %s for reading", path);
+ git_buf_clear(buf);
- if ((buff = git__malloc(len + 1)) == NULL) {
+ if (git_buf_grow(buf, len + 1) < 0) {
p_close(fd);
- return GIT_ENOMEM;
+ return -1;
}
- if (p_read(fd, buff, len) < 0) {
- p_close(fd);
- git__free(buff);
- return git__throw(GIT_ERROR, "Failed to read file `%s`", path);
+ buf->ptr[len] = '\0';
+
+ while (len > 0) {
+ ssize_t read_size = p_read(fd, buf->ptr, len);
+
+ if (read_size < 0) {
+ p_close(fd);
+ giterr_set(GITERR_OS, "Failed to read descriptor for '%s'", path);
+ return -1;
+ }
+
+ len -= read_size;
+ buf->size += read_size;
}
- buff[len] = '\0';
p_close(fd);
- if (mtime != NULL)
- *mtime = st.st_mtime;
if (updated != NULL)
*updated = 1;
- obj->data = buff;
- obj->len = len;
-
- return GIT_SUCCESS;
+ return 0;
}
-int git_futils_readbuffer(git_fbuffer *obj, const char *path)
+int git_futils_readbuffer(git_buf *buf, const char *path)
{
- return git_futils_readbuffer_updated(obj, path, NULL, NULL);
+ return git_futils_readbuffer_updated(buf, path, NULL, NULL);
}
-void git_futils_fbuffer_rtrim(git_fbuffer *obj)
+int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode)
{
- unsigned char *buff = obj->data;
- while (obj->len > 0 && isspace(buff[obj->len - 1]))
- obj->len--;
- buff[obj->len] = '\0';
+ if (git_futils_mkpath2file(to, dirmode) < 0)
+ return -1;
+
+ if (p_rename(from, to) < 0) {
+ giterr_set(GITERR_OS, "Failed to rename '%s' to '%s'", from, to);
+ return -1;
+ }
+
+ return 0;
}
-void git_futils_freebuffer(git_fbuffer *obj)
+int git_futils_mmap_ro(git_map *out, git_file fd, git_off_t begin, size_t len)
{
- assert(obj);
- git__free(obj->data);
- obj->data = NULL;
+ return p_mmap(out, len, GIT_PROT_READ, GIT_MAP_SHARED, fd, begin);
}
-
-int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode)
+int git_futils_mmap_ro_file(git_map *out, const char *path)
{
- if (git_futils_mkpath2file(to, dirmode) < GIT_SUCCESS)
- return GIT_EOSERR; /* The callee already takes care of setting the correct error message. */
+ git_file fd = git_futils_open_ro(path);
+ git_off_t len;
+ int result;
- return p_rename(from, to); /* The callee already takes care of setting the correct error message. */
-}
+ if (fd < 0)
+ return fd;
-int git_futils_mmap_ro(git_map *out, git_file fd, git_off_t begin, size_t len)
-{
- return p_mmap(out, len, GIT_PROT_READ, GIT_MAP_SHARED, fd, begin);
+ len = git_futils_filesize(fd);
+ if (!git__is_sizet(len)) {
+ giterr_set(GITERR_OS, "File `%s` too large to mmap", path);
+ return -1;
+ }
+
+ result = git_futils_mmap_ro(out, fd, 0, (size_t)len);
+ p_close(fd);
+ return result;
}
void git_futils_mmap_free(git_map *out)
@@ -180,20 +241,21 @@ void git_futils_mmap_free(git_map *out)
int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
{
- int error, root_path_offset;
+ int root_path_offset;
git_buf make_path = GIT_BUF_INIT;
size_t start;
char *pp, *sp;
+ bool failed = false;
if (base != NULL) {
start = strlen(base);
- error = git_buf_joinpath(&make_path, base, path);
+ if (git_buf_joinpath(&make_path, base, path) < 0)
+ return -1;
} else {
start = 0;
- error = git_buf_puts(&make_path, path);
+ if (git_buf_puts(&make_path, path) < 0)
+ return -1;
}
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to create `%s` tree structure", path);
pp = make_path.ptr + start;
@@ -201,14 +263,13 @@ int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
if (root_path_offset > 0)
pp += root_path_offset; /* On Windows, will skip the drive name (eg. C: or D:) */
- while (error == GIT_SUCCESS && (sp = strchr(pp, '/')) != NULL) {
- if (sp != pp && git_path_isdir(make_path.ptr) < GIT_SUCCESS) {
+ while (!failed && (sp = strchr(pp, '/')) != NULL) {
+ if (sp != pp && git_path_isdir(make_path.ptr) == false) {
*sp = 0;
- error = p_mkdir(make_path.ptr, mode);
/* Do not choke while trying to recreate an existing directory */
- if (errno == EEXIST)
- error = GIT_SUCCESS;
+ if (p_mkdir(make_path.ptr, mode) < 0 && errno != EEXIST)
+ failed = true;
*sp = '/';
}
@@ -216,53 +277,76 @@ int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
pp = sp + 1;
}
- if (*pp != '\0' && error == GIT_SUCCESS) {
- error = p_mkdir(make_path.ptr, mode);
- if (errno == EEXIST)
- error = GIT_SUCCESS;
+ if (*pp != '\0' && !failed) {
+ if (p_mkdir(make_path.ptr, mode) < 0 && errno != EEXIST)
+ failed = true;
}
git_buf_free(&make_path);
- if (error < GIT_SUCCESS)
- return git__throw(error, "Failed to recursively create `%s` tree structure", path);
+ if (failed) {
+ giterr_set(GITERR_OS,
+ "Failed to create directory structure at '%s'", path);
+ return -1;
+ }
- return GIT_SUCCESS;
+ return 0;
}
static int _rmdir_recurs_foreach(void *opaque, git_buf *path)
{
- int error = GIT_SUCCESS;
- int force = *(int *)opaque;
+ git_directory_removal_type removal_type = *(git_directory_removal_type *)opaque;
+
+ assert(removal_type == GIT_DIRREMOVAL_EMPTY_HIERARCHY
+ || removal_type == GIT_DIRREMOVAL_FILES_AND_DIRS
+ || removal_type == GIT_DIRREMOVAL_ONLY_EMPTY_DIRS);
+
+ if (git_path_isdir(path->ptr) == true) {
+ if (git_path_direach(path, _rmdir_recurs_foreach, opaque) < 0)
+ return -1;
+
+ if (p_rmdir(path->ptr) < 0) {
+ if (removal_type == GIT_DIRREMOVAL_ONLY_EMPTY_DIRS && (errno == ENOTEMPTY || errno == EEXIST))
+ return 0;
+
+ giterr_set(GITERR_OS, "Could not remove directory '%s'", path->ptr);
+ return -1;
+ }
+
+ return 0;
+ }
- if (git_path_isdir(path->ptr) == GIT_SUCCESS) {
- error = git_path_direach(path, _rmdir_recurs_foreach, opaque);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to remove directory `%s`", path->ptr);
- return p_rmdir(path->ptr);
+ if (removal_type == GIT_DIRREMOVAL_FILES_AND_DIRS) {
+ if (p_unlink(path->ptr) < 0) {
+ giterr_set(GITERR_OS, "Could not remove directory. File '%s' cannot be removed", path->ptr);
+ return -1;
+ }
- } else if (force) {
- return p_unlink(path->ptr);
+ return 0;
}
- return git__rethrow(error, "Failed to remove directory. `%s` is not empty", path->ptr);
+ if (removal_type == GIT_DIRREMOVAL_EMPTY_HIERARCHY) {
+ giterr_set(GITERR_OS, "Could not remove directory. File '%s' still present", path->ptr);
+ return -1;
+ }
+
+ return 0;
}
-int git_futils_rmdir_r(const char *path, int force)
+int git_futils_rmdir_r(const char *path, git_directory_removal_type removal_type)
{
int error;
git_buf p = GIT_BUF_INIT;
error = git_buf_sets(&p, path);
- if (error == GIT_SUCCESS)
- error = _rmdir_recurs_foreach(&force, &p);
+ if (!error)
+ error = _rmdir_recurs_foreach(&removal_type, &p);
git_buf_free(&p);
return error;
}
int git_futils_find_global_file(git_buf *path, const char *filename)
{
- int error;
const char *home = getenv("HOME");
#ifdef GIT_WIN32
@@ -270,19 +354,21 @@ int git_futils_find_global_file(git_buf *path, const char *filename)
home = getenv("USERPROFILE");
#endif
- if (home == NULL)
- return git__throw(GIT_EOSERR, "Failed to open global %s file. "
- "Cannot locate the user's home directory.", filename);
+ if (home == NULL) {
+ giterr_set(GITERR_OS, "Global file lookup failed. "
+ "Cannot locate the user's home directory");
+ return -1;
+ }
- if ((error = git_buf_joinpath(path, home, filename)) < GIT_SUCCESS)
- return error;
+ if (git_buf_joinpath(path, home, filename) < 0)
+ return -1;
- if (git_path_exists(path->ptr) < GIT_SUCCESS) {
+ if (git_path_exists(path->ptr) == false) {
git_buf_clear(path);
return GIT_ENOTFOUND;
}
- return GIT_SUCCESS;
+ return 0;
}
#ifdef GIT_WIN32
@@ -299,9 +385,8 @@ static const win32_path *win32_system_root(void)
const wchar_t *root_tmpl = L"%PROGRAMFILES%\\Git\\etc\\";
s_root.len = ExpandEnvironmentStringsW(root_tmpl, NULL, 0);
-
if (s_root.len <= 0) {
- git__throw(GIT_EOSERR, "Failed to expand environment strings");
+ giterr_set(GITERR_OS, "Failed to expand environment strings");
return NULL;
}
@@ -310,7 +395,7 @@ static const win32_path *win32_system_root(void)
return NULL;
if (ExpandEnvironmentStringsW(root_tmpl, s_root.path, s_root.len) != s_root.len) {
- git__throw(GIT_EOSERR, "Failed to expand environment strings");
+ giterr_set(GITERR_OS, "Failed to expand environment strings");
git__free(s_root.path);
s_root.path = NULL;
return NULL;
@@ -322,7 +407,7 @@ static const win32_path *win32_system_root(void)
static int win32_find_system_file(git_buf *path, const char *filename)
{
- int error = GIT_SUCCESS;
+ int error = 0;
const win32_path *root = win32_system_root();
size_t len;
wchar_t *file_utf16 = NULL, *scan;
@@ -333,8 +418,7 @@ static int win32_find_system_file(git_buf *path, const char *filename)
/* allocate space for wchar_t path to file */
file_utf16 = git__calloc(root->len + len + 2, sizeof(wchar_t));
- if (!file_utf16)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(file_utf16);
/* append root + '\\' + filename as wchar_t */
memcpy(file_utf16, root->path, root->len * sizeof(wchar_t));
@@ -344,7 +428,7 @@ static int win32_find_system_file(git_buf *path, const char *filename)
if (gitwin_append_utf16(file_utf16 + root->len - 1, filename, len + 1) !=
(int)len + 1) {
- error = git__throw(GIT_EOSERR, "Failed to build file path");
+ error = -1;
goto cleanup;
}
@@ -360,9 +444,8 @@ static int win32_find_system_file(git_buf *path, const char *filename)
/* convert to utf8 */
if ((file_utf8 = gitwin_from_utf16(file_utf16)) == NULL)
- error = GIT_ENOMEM;
-
- if (file_utf8) {
+ error = -1;
+ else {
git_path_mkposix(file_utf8);
git_buf_attach(path, file_utf8, 0);
}
@@ -376,11 +459,11 @@ cleanup:
int git_futils_find_system_file(git_buf *path, const char *filename)
{
- if (git_buf_joinpath(path, "/etc", filename) < GIT_SUCCESS)
- return git_buf_lasterror(path);
+ if (git_buf_joinpath(path, "/etc", filename) < 0)
+ return -1;
- if (git_path_exists(path->ptr) == GIT_SUCCESS)
- return GIT_SUCCESS;
+ if (git_path_exists(path->ptr) == true)
+ return 0;
git_buf_clear(path);
diff --git a/src/fileops.h b/src/fileops.h
index 1ded0d3b1..be619d620 100644
--- a/src/fileops.h
+++ b/src/fileops.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -17,17 +17,8 @@
*
* Read whole files into an in-memory buffer for processing
*/
-#define GIT_FBUFFER_INIT {NULL, 0}
-
-typedef struct { /* file io buffer */
- void *data; /* data bytes */
- size_t len; /* data length */
-} git_fbuffer;
-
-extern int git_futils_readbuffer(git_fbuffer *obj, const char *path);
-extern int git_futils_readbuffer_updated(git_fbuffer *obj, const char *path, time_t *mtime, int *updated);
-extern void git_futils_freebuffer(git_fbuffer *obj);
-extern void git_futils_fbuffer_rtrim(git_fbuffer *obj);
+extern int git_futils_readbuffer(git_buf *obj, const char *path);
+extern int git_futils_readbuffer_updated(git_buf *obj, const char *path, time_t *mtime, int *updated);
/**
* File utils
@@ -35,7 +26,7 @@ extern void git_futils_fbuffer_rtrim(git_fbuffer *obj);
* These are custom filesystem-related helper methods. They are
* rather high level, and wrap the underlying POSIX methods
*
- * All these methods return GIT_SUCCESS on success,
+ * All these methods return 0 on success,
* or an error code on failure and an error message is set.
*/
@@ -67,10 +58,25 @@ extern int git_futils_mkdir_r(const char *path, const char *base, const mode_t m
*/
extern int git_futils_mkpath2file(const char *path, const mode_t mode);
+typedef enum {
+ GIT_DIRREMOVAL_EMPTY_HIERARCHY = 0,
+ GIT_DIRREMOVAL_FILES_AND_DIRS = 1,
+ GIT_DIRREMOVAL_ONLY_EMPTY_DIRS = 2,
+} git_directory_removal_type;
+
/**
* Remove path and any files and directories beneath it.
+ *
+ * @param path Path to to top level directory to process.
+ *
+ * @param removal_type GIT_DIRREMOVAL_EMPTY_HIERARCHY to remove a hierarchy
+ * of empty directories (will fail if any file is found), GIT_DIRREMOVAL_FILES_AND_DIRS
+ * to remove a hierarchy of files and folders, GIT_DIRREMOVAL_ONLY_EMPTY_DIRS to only remove
+ * empty directories (no failure on file encounter).
+ *
+ * @return 0 on success; -1 on error.
*/
-extern int git_futils_rmdir_r(const char *path, int force);
+extern int git_futils_rmdir_r(const char *path, git_directory_removal_type removal_type);
/**
* Create and open a temporary file with a `_git2_` suffix.
@@ -85,12 +91,26 @@ extern int git_futils_mktmp(git_buf *path_out, const char *filename);
*/
extern int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode);
+/**
+ * Open a file readonly and set error if needed.
+ */
+extern int git_futils_open_ro(const char *path);
/**
* Get the filesize in bytes of a file
*/
extern git_off_t git_futils_filesize(git_file fd);
+#define GIT_MODE_PERMS_MASK 0777
+#define GIT_CANONICAL_PERMS(MODE) (((MODE) & 0100) ? 0755 : 0644)
+#define GIT_MODE_TYPE(MODE) ((MODE) & ~GIT_MODE_PERMS_MASK)
+
+/**
+ * Convert a mode_t from the OS to a legal git mode_t value.
+ */
+extern mode_t git_futils_canonical_mode(mode_t raw_mode);
+
+
/**
* Read-only map all or part of a file into memory.
* When possible this function should favor a virtual memory
@@ -103,8 +123,8 @@ extern git_off_t git_futils_filesize(git_file fd);
* @param begin first byte to map, this should be page aligned.
* @param end number of bytes to map.
* @return
- * - GIT_SUCCESS on success;
- * - GIT_EOSERR on an unspecified OS related error.
+ * - 0 on success;
+ * - -1 on error.
*/
extern int git_futils_mmap_ro(
git_map *out,
@@ -113,6 +133,20 @@ extern int git_futils_mmap_ro(
size_t len);
/**
+ * Read-only map an entire file.
+ *
+ * @param out buffer to populate with the mapping information.
+ * @param path path to file to be opened.
+ * @return
+ * - 0 on success;
+ * - GIT_ENOTFOUND if not found;
+ * - -1 on an unspecified OS related error.
+ */
+extern int git_futils_mmap_ro_file(
+ git_map *out,
+ const char *path);
+
+/**
* Release the memory associated with a previous memory mapping.
* @param map the mapping description previously configured.
*/
@@ -124,9 +158,9 @@ extern void git_futils_mmap_free(git_map *map);
* @param pathbuf buffer to write the full path into
* @param filename name of file to find in the home directory
* @return
- * - GIT_SUCCESS if found;
+ * - 0 if found;
* - GIT_ENOTFOUND if not found;
- * - GIT_EOSERR on an unspecified OS related error.
+ * - -1 on an unspecified OS related error.
*/
extern int git_futils_find_global_file(git_buf *path, const char *filename);
@@ -136,9 +170,9 @@ extern int git_futils_find_global_file(git_buf *path, const char *filename);
* @param pathbuf buffer to write the full path into
* @param filename name of file to find in the home directory
* @return
- * - GIT_SUCCESS if found;
+ * - 0 if found;
* - GIT_ENOTFOUND if not found;
- * - GIT_EOSERR on an unspecified OS related error.
+ * - -1 on an unspecified OS related error.
*/
extern int git_futils_find_system_file(git_buf *path, const char *filename);
diff --git a/src/filter.c b/src/filter.c
new file mode 100644
index 000000000..8fa3eb684
--- /dev/null
+++ b/src/filter.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+#include "fileops.h"
+#include "hash.h"
+#include "filter.h"
+#include "repository.h"
+#include "git2/config.h"
+
+/* Tweaked from Core Git. I wonder what we could use this for... */
+void git_text_gather_stats(git_text_stats *stats, const git_buf *text)
+{
+ size_t i;
+
+ memset(stats, 0, sizeof(*stats));
+
+ for (i = 0; i < git_buf_len(text); i++) {
+ unsigned char c = text->ptr[i];
+
+ if (c == '\r') {
+ stats->cr++;
+
+ if (i + 1 < git_buf_len(text) && text->ptr[i + 1] == '\n')
+ stats->crlf++;
+ }
+
+ else if (c == '\n')
+ stats->lf++;
+
+ else if (c == 0x85)
+ /* Unicode CR+LF */
+ stats->crlf++;
+
+ else if (c == 127)
+ /* DEL */
+ stats->nonprintable++;
+
+ else if (c <= 0x1F || (c >= 0x80 && c <= 0x9F)) {
+ switch (c) {
+ /* BS, HT, ESC and FF */
+ case '\b': case '\t': case '\033': case '\014':
+ stats->printable++;
+ break;
+ case 0:
+ stats->nul++;
+ /* fall through */
+ default:
+ stats->nonprintable++;
+ }
+ }
+
+ else
+ stats->printable++;
+ }
+
+ /* If file ends with EOF then don't count this EOF as non-printable. */
+ if (git_buf_len(text) >= 1 && text->ptr[text->size - 1] == '\032')
+ stats->nonprintable--;
+}
+
+/*
+ * Fresh from Core Git
+ */
+int git_text_is_binary(git_text_stats *stats)
+{
+ if (stats->nul)
+ return 1;
+
+ if ((stats->printable >> 7) < stats->nonprintable)
+ return 1;
+ /*
+ * Other heuristics? Average line length might be relevant,
+ * as might LF vs CR vs CRLF counts..
+ *
+ * NOTE! It might be normal to have a low ratio of CRLF to LF
+ * (somebody starts with a LF-only file and edits it with an editor
+ * that adds CRLF only to lines that are added..). But do we
+ * want to support CR-only? Probably not.
+ */
+ return 0;
+}
+
+int git_filters_load(git_vector *filters, git_repository *repo, const char *path, int mode)
+{
+ int error;
+
+ if (mode == GIT_FILTER_TO_ODB) {
+ /* Load the CRLF cleanup filter when writing to the ODB */
+ error = git_filter_add__crlf_to_odb(filters, repo, path);
+ if (error < 0)
+ return error;
+ } else {
+ giterr_set(GITERR_INVALID, "Worktree filters are not implemented yet");
+ return -1;
+ }
+
+ return (int)filters->length;
+}
+
+void git_filters_free(git_vector *filters)
+{
+ size_t i;
+ git_filter *filter;
+
+ git_vector_foreach(filters, i, filter) {
+ if (filter->do_free != NULL)
+ filter->do_free(filter);
+ else
+ git__free(filter);
+ }
+
+ git_vector_free(filters);
+}
+
+int git_filters_apply(git_buf *dest, git_buf *source, git_vector *filters)
+{
+ unsigned int i, src;
+ git_buf *dbuffer[2];
+
+ dbuffer[0] = source;
+ dbuffer[1] = dest;
+
+ src = 0;
+
+ if (git_buf_len(source) == 0) {
+ git_buf_clear(dest);
+ return 0;
+ }
+
+ /* Pre-grow the destination buffer to more or less the size
+ * we expect it to have */
+ if (git_buf_grow(dest, git_buf_len(source)) < 0)
+ return -1;
+
+ for (i = 0; i < filters->length; ++i) {
+ git_filter *filter = git_vector_get(filters, i);
+ unsigned int dst = 1 - src;
+
+ git_buf_clear(dbuffer[dst]);
+
+ /* Apply the filter from dbuffer[src] to the other buffer;
+ * if the filtering is canceled by the user mid-filter,
+ * we skip to the next filter without changing the source
+ * of the double buffering (so that the text goes through
+ * cleanly).
+ */
+ if (filter->apply(filter, dbuffer[dst], dbuffer[src]) == 0)
+ src = dst;
+
+ if (git_buf_oom(dbuffer[dst]))
+ return -1;
+ }
+
+ /* Ensure that the output ends up in dbuffer[1] (i.e. the dest) */
+ if (src != 1)
+ git_buf_swap(dest, source);
+
+ return 0;
+}
+
diff --git a/src/filter.h b/src/filter.h
new file mode 100644
index 000000000..66e370aef
--- /dev/null
+++ b/src/filter.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_filter_h__
+#define INCLUDE_filter_h__
+
+#include "common.h"
+#include "buffer.h"
+#include "git2/odb.h"
+#include "git2/repository.h"
+
+typedef struct git_filter {
+ int (*apply)(struct git_filter *self, git_buf *dest, const git_buf *source);
+ void (*do_free)(struct git_filter *self);
+} git_filter;
+
+typedef enum {
+ GIT_FILTER_TO_WORKTREE,
+ GIT_FILTER_TO_ODB
+} git_filter_mode;
+
+typedef enum {
+ GIT_CRLF_GUESS = -1,
+ GIT_CRLF_BINARY = 0,
+ GIT_CRLF_TEXT,
+ GIT_CRLF_INPUT,
+ GIT_CRLF_CRLF,
+ GIT_CRLF_AUTO,
+} git_crlf_t;
+
+typedef struct {
+ /* NUL, CR, LF and CRLF counts */
+ unsigned int nul, cr, lf, crlf;
+
+ /* These are just approximations! */
+ unsigned int printable, nonprintable;
+} git_text_stats;
+
+/*
+ * FILTER API
+ */
+
+/*
+ * For any given path in the working directory, fill the `filters`
+ * array with the relevant filters that need to be applied.
+ *
+ * Mode is either `GIT_FILTER_TO_WORKTREE` if you need to load the
+ * filters that will be used when checking out a file to the working
+ * directory, or `GIT_FILTER_TO_ODB` for the filters used when writing
+ * a file to the ODB.
+ *
+ * @param filters Vector where to store all the loaded filters
+ * @param repo Repository object that contains `path`
+ * @param path Relative path of the file to be filtered
+ * @param mode Filtering direction (WT->ODB or ODB->WT)
+ * @return the number of filters loaded for the file (0 if the file
+ * doesn't need filtering), or a negative error code
+ */
+extern int git_filters_load(git_vector *filters, git_repository *repo, const char *path, int mode);
+
+/*
+ * Apply one or more filters to a file.
+ *
+ * The file must have been loaded as a `git_buf` object. Both the `source`
+ * and `dest` buffers are owned by the caller and must be freed once
+ * they are no longer needed.
+ *
+ * NOTE: Because of the double-buffering schema, the `source` buffer that contains
+ * the original file may be tampered once the filtering is complete. Regardless,
+ * the `dest` buffer will always contain the final result of the filtering
+ *
+ * @param dest Buffer to store the result of the filtering
+ * @param source Buffer containing the document to filter
+ * @param filters A non-empty vector of filters as supplied by `git_filters_load`
+ * @return 0 on success, an error code otherwise
+ */
+extern int git_filters_apply(git_buf *dest, git_buf *source, git_vector *filters);
+
+/*
+ * Free the `filters` array generated by `git_filters_load`.
+ *
+ * Note that this frees both the array and its contents. The array will
+ * be clean/reusable after this call.
+ *
+ * @param filters A filters array as supplied by `git_filters_load`
+ */
+extern void git_filters_free(git_vector *filters);
+
+/*
+ * Available filters
+ */
+
+/* Strip CRLF, from Worktree to ODB */
+extern int git_filter_add__crlf_to_odb(git_vector *filters, git_repository *repo, const char *path);
+
+
+/*
+ * PLAINTEXT API
+ */
+
+/*
+ * Gather stats for a piece of text
+ *
+ * Fill the `stats` structure with information on the number of
+ * unreadable characters, carriage returns, etc, so it can be
+ * used in heuristics.
+ */
+extern void git_text_gather_stats(git_text_stats *stats, const git_buf *text);
+
+/*
+ * Process `git_text_stats` data generated by `git_text_stat` to see
+ * if it qualifies as a binary file
+ */
+extern int git_text_is_binary(git_text_stats *stats);
+
+#endif
diff --git a/src/global.c b/src/global.c
index 8ef286ef0..368c6c664 100644
--- a/src/global.c
+++ b/src/global.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -62,7 +62,7 @@ git_global_st *git__global_state(void)
if ((ptr = TlsGetValue(_tls_index)) != NULL)
return ptr;
- ptr = malloc(sizeof(git_global_st));
+ ptr = git__malloc(sizeof(git_global_st));
if (!ptr)
return NULL;
@@ -78,7 +78,7 @@ static int _tls_init = 0;
static void cb__free_status(void *st)
{
- free(st);
+ git__free(st);
}
void git_threads_init(void)
@@ -103,7 +103,7 @@ git_global_st *git__global_state(void)
if ((ptr = pthread_getspecific(_tls_key)) != NULL)
return ptr;
- ptr = malloc(sizeof(git_global_st));
+ ptr = git__malloc(sizeof(git_global_st));
if (!ptr)
return NULL;
diff --git a/src/global.h b/src/global.h
index 641f47cbc..2b525ce07 100644
--- a/src/global.h
+++ b/src/global.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -14,6 +14,9 @@ typedef struct {
char last[1024];
} error;
+ git_error *last_error;
+ git_error error_t;
+
git_mwindow_ctl mem_ctl;
} git_global_st;
diff --git a/src/hash.c b/src/hash.c
index 56063cc0b..460756913 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
diff --git a/src/hash.h b/src/hash.h
index fe1ba5d46..33d7b20cd 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
diff --git a/src/hashtable.c b/src/hashtable.c
deleted file mode 100644
index 89c44ba9e..000000000
--- a/src/hashtable.c
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Copyright (C) 2009-2011 the libgit2 contributors
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "repository.h"
-#include "commit.h"
-
-#define MAX_LOOPS 5
-static const double max_load_factor = 0.65;
-
-static int resize_to(git_hashtable *self, size_t new_size);
-static int set_size(git_hashtable *self, size_t new_size);
-static git_hashtable_node *node_with_hash(git_hashtable *self, const void *key, int hash_id);
-static void node_swap_with(git_hashtable_node *self, git_hashtable_node *other);
-static int node_insert(git_hashtable *self, git_hashtable_node *new_node);
-static int insert_nodes(git_hashtable *self, git_hashtable_node *old_nodes, size_t old_size);
-
-static int resize_to(git_hashtable *self, size_t new_size)
-{
- git_hashtable_node *old_nodes = self->nodes;
- size_t old_size = self->size;
-
- self->is_resizing = 1;
-
- do {
- self->size = new_size;
- self->size_mask = new_size - 1;
- self->key_count = 0;
- self->nodes = git__calloc(1, sizeof(git_hashtable_node) * self->size);
-
- if (self->nodes == NULL)
- return GIT_ENOMEM;
-
- if (insert_nodes(self, old_nodes, old_size) == 0)
- self->is_resizing = 0;
- else {
- new_size *= 2;
- git__free(self->nodes);
- }
- } while(self->is_resizing);
-
- git__free(old_nodes);
- return GIT_SUCCESS;
-}
-
-static int set_size(git_hashtable *self, size_t new_size)
-{
- self->nodes = git__realloc(self->nodes, new_size * sizeof(git_hashtable_node));
- if (self->nodes == NULL)
- return GIT_ENOMEM;
-
- if (new_size > self->size) {
- memset(&self->nodes[self->size], 0x0, (new_size - self->size) * sizeof(git_hashtable_node));
- }
-
- self->size = new_size;
- self->size_mask = new_size - 1;
- return GIT_SUCCESS;
-}
-
-static git_hashtable_node *node_with_hash(git_hashtable *self, const void *key, int hash_id)
-{
- size_t pos = self->hash(key, hash_id) & self->size_mask;
- return git_hashtable_node_at(self->nodes, pos);
-}
-
-static void node_swap_with(git_hashtable_node *self, git_hashtable_node *other)
-{
- git_hashtable_node tmp = *self;
- *self = *other;
- *other = tmp;
-}
-
-static int node_insert(git_hashtable *self, git_hashtable_node *new_node)
-{
- int iteration, hash_id;
-
- for (iteration = 0; iteration < MAX_LOOPS; iteration++) {
- for (hash_id = 0; hash_id < GIT_HASHTABLE_HASHES; ++hash_id) {
- git_hashtable_node *node;
- node = node_with_hash(self, new_node->key, hash_id);
- node_swap_with(new_node, node);
- if(new_node->key == 0x0){
- self->key_count++;
- return GIT_SUCCESS;
- }
- }
- }
-
- if (self->is_resizing)
- return git__throw(GIT_EBUSY, "Failed to insert node. Hashtable is currently resizing");
-
- resize_to(self, self->size * 2);
- git_hashtable_insert(self, new_node->key, new_node->value);
- return GIT_SUCCESS;
-}
-
-static int insert_nodes(git_hashtable *self, git_hashtable_node *old_nodes, size_t old_size)
-{
- size_t i;
-
- for (i = 0; i < old_size; ++i) {
- git_hashtable_node *node = git_hashtable_node_at(old_nodes, i);
- if (node->key && git_hashtable_insert(self, node->key, node->value) < GIT_SUCCESS)
- return GIT_ENOMEM;
- }
-
- return GIT_SUCCESS;
-}
-
-git_hashtable *git_hashtable_alloc(size_t min_size,
- git_hash_ptr hash,
- git_hash_keyeq_ptr key_eq)
-{
- git_hashtable *table;
-
- assert(hash && key_eq);
-
- if ((table = git__malloc(sizeof(git_hashtable))) == NULL)
- return NULL;
-
- memset(table, 0x0, sizeof(git_hashtable));
-
- if (min_size < 8)
- min_size = 8;
-
- /* round up size to closest power of 2 */
- min_size--;
- min_size |= min_size >> 1;
- min_size |= min_size >> 2;
- min_size |= min_size >> 4;
- min_size |= min_size >> 8;
- min_size |= min_size >> 16;
-
- table->hash = hash;
- table->key_equal = key_eq;
-
- set_size(table, min_size + 1);
-
- return table;
-}
-
-void git_hashtable_clear(git_hashtable *self)
-{
- assert(self);
-
- memset(self->nodes, 0x0, sizeof(git_hashtable_node) * self->size);
- self->key_count = 0;
-}
-
-void git_hashtable_free(git_hashtable *self)
-{
- assert(self);
-
- git__free(self->nodes);
- git__free(self);
-}
-
-
-int git_hashtable_insert2(git_hashtable *self, const void *key, void *value, void **old_value)
-{
- int hash_id;
- git_hashtable_node *node;
-
- assert(self && self->nodes);
-
- *old_value = NULL;
-
- for (hash_id = 0; hash_id < GIT_HASHTABLE_HASHES; ++hash_id) {
- node = node_with_hash(self, key, hash_id);
-
- if (!node->key) {
- node->key = key;
- node->value = value;
- self->key_count++;
- return GIT_SUCCESS;
- }
-
- if (key == node->key || self->key_equal(key, node->key) == 0) {
- *old_value = node->value;
- node->key = key;
- node->value = value;
- return GIT_SUCCESS;
- }
- }
-
- /* no space in table; must do cuckoo dance */
- {
- git_hashtable_node x;
- x.key = key;
- x.value = value;
- return node_insert(self, &x);
- }
-}
-
-void *git_hashtable_lookup(git_hashtable *self, const void *key)
-{
- int hash_id;
- git_hashtable_node *node;
-
- assert(self && self->nodes);
-
- for (hash_id = 0; hash_id < GIT_HASHTABLE_HASHES; ++hash_id) {
- node = node_with_hash(self, key, hash_id);
- if (node->key && self->key_equal(key, node->key) == 0)
- return node->value;
- }
-
- return NULL;
-}
-
-int git_hashtable_remove2(git_hashtable *self, const void *key, void **old_value)
-{
- int hash_id;
- git_hashtable_node *node;
-
- assert(self && self->nodes);
-
- for (hash_id = 0; hash_id < GIT_HASHTABLE_HASHES; ++hash_id) {
- node = node_with_hash(self, key, hash_id);
- if (node->key && self->key_equal(key, node->key) == 0) {
- *old_value = node->value;
- node->key = NULL;
- node->value = NULL;
- self->key_count--;
- return GIT_SUCCESS;
- }
- }
-
- return git__throw(GIT_ENOTFOUND, "Entry not found in hash table");
-}
-
-int git_hashtable_merge(git_hashtable *self, git_hashtable *other)
-{
- if (resize_to(self, (self->size + other->size) * 2) < GIT_SUCCESS)
- return GIT_ENOMEM;
-
- return insert_nodes(self, other->nodes, other->key_count);
-}
-
-
-/**
- * Standard string
- */
-uint32_t git_hash__strhash_cb(const void *key, int hash_id)
-{
- static uint32_t hash_seeds[GIT_HASHTABLE_HASHES] = {
- 2147483647,
- 0x5d20bb23,
- 0x7daaab3c
- };
-
- return git__hash(key, strlen((const char *)key), hash_seeds[hash_id]);
-}
diff --git a/src/hashtable.h b/src/hashtable.h
deleted file mode 100644
index cd458eb17..000000000
--- a/src/hashtable.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2009-2011 the libgit2 contributors
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_hashtable_h__
-#define INCLUDE_hashtable_h__
-
-#include "git2/common.h"
-#include "git2/oid.h"
-#include "git2/odb.h"
-#include "common.h"
-
-#define GIT_HASHTABLE_HASHES 3
-
-typedef uint32_t (*git_hash_ptr)(const void *, int hash_id);
-typedef int (*git_hash_keyeq_ptr)(const void *key_a, const void *key_b);
-
-struct git_hashtable_node {
- const void *key;
- void *value;
-};
-
-struct git_hashtable {
- struct git_hashtable_node *nodes;
-
- size_t size_mask;
- size_t size;
- size_t key_count;
-
- int is_resizing;
-
- git_hash_ptr hash;
- git_hash_keyeq_ptr key_equal;
-};
-
-typedef struct git_hashtable_node git_hashtable_node;
-typedef struct git_hashtable git_hashtable;
-
-git_hashtable *git_hashtable_alloc(size_t min_size,
- git_hash_ptr hash,
- git_hash_keyeq_ptr key_eq);
-void *git_hashtable_lookup(git_hashtable *h, const void *key);
-int git_hashtable_remove2(git_hashtable *table, const void *key, void **old_value);
-
-GIT_INLINE(int) git_hashtable_remove(git_hashtable *table, const void *key)
-{
- void *_unused;
- return git_hashtable_remove2(table, key, &_unused);
-}
-
-
-void git_hashtable_free(git_hashtable *h);
-void git_hashtable_clear(git_hashtable *h);
-int git_hashtable_merge(git_hashtable *self, git_hashtable *other);
-
-int git_hashtable_insert2(git_hashtable *h, const void *key, void *value, void **old_value);
-
-GIT_INLINE(int) git_hashtable_insert(git_hashtable *h, const void *key, void *value)
-{
- void *_unused;
- return git_hashtable_insert2(h, key, value, &_unused);
-}
-
-#define git_hashtable_node_at(nodes, pos) ((git_hashtable_node *)(&nodes[pos]))
-
-#define GIT_HASHTABLE_FOREACH(self, pkey, pvalue, code) {\
- git_hashtable *_self = (self);\
- git_hashtable_node *_nodes = _self->nodes;\
- unsigned int _i, _size = _self->size;\
- for (_i = 0; _i < _size; _i ++) {\
- git_hashtable_node *_node = git_hashtable_node_at(_nodes, _i);\
- if (_node->key)\
- {\
- pkey = _node->key;\
- pvalue = _node->value;\
- code;\
- }\
- }\
-}
-
-#define GIT_HASHTABLE_FOREACH_DELETE() {\
- _node->key = NULL; _node->value = NULL; _self->key_count--;\
-}
-
-/*
- * If you want a hashtable with standard string keys, you can
- * just pass git_hash__strcmp_cb and git_hash__strhash_cb to
- * git_hashtable_alloc.
- */
-#define git_hash__strcmp_cb git__strcmp_cb
-extern uint32_t git_hash__strhash_cb(const void *key, int hash_id);
-
-#endif
diff --git a/src/ignore.c b/src/ignore.c
index 9690eba08..fc6194bb5 100644
--- a/src/ignore.c
+++ b/src/ignore.c
@@ -1,162 +1,203 @@
#include "ignore.h"
#include "path.h"
-#include "git2/config.h"
#define GIT_IGNORE_INTERNAL "[internal]exclude"
#define GIT_IGNORE_FILE_INREPO "info/exclude"
#define GIT_IGNORE_FILE ".gitignore"
-#define GIT_IGNORE_CONFIG "core.excludesfile"
-static int load_ignore_file(
- git_repository *repo, const char *path, git_attr_file *ignores)
+static int parse_ignore_file(
+ git_repository *repo, const char *buffer, git_attr_file *ignores)
{
- int error = GIT_SUCCESS;
- git_fbuffer fbuf = GIT_FBUFFER_INIT;
+ int error = 0;
git_attr_fnmatch *match = NULL;
const char *scan = NULL;
char *context = NULL;
- if (ignores->path == NULL)
- error = git_attr_file__set_path(repo, path, ignores);
+ GIT_UNUSED(repo);
- if (git__suffixcmp(ignores->path, GIT_IGNORE_FILE) == 0) {
- context = git__strndup(ignores->path,
- strlen(ignores->path) - strlen(GIT_IGNORE_FILE));
- if (!context) error = GIT_ENOMEM;
+ if (ignores->key && git__suffixcmp(ignores->key, "/" GIT_IGNORE_FILE) == 0) {
+ context = ignores->key + 2;
+ context[strlen(context) - strlen(GIT_IGNORE_FILE)] = '\0';
}
- if (error == GIT_SUCCESS)
- error = git_futils_readbuffer(&fbuf, path);
+ scan = buffer;
- scan = fbuf.data;
-
- while (error == GIT_SUCCESS && *scan) {
- if (!match && !(match = git__calloc(1, sizeof(git_attr_fnmatch)))) {
- error = GIT_ENOMEM;
- break;
+ while (!error && *scan) {
+ if (!match) {
+ match = git__calloc(1, sizeof(*match));
+ GITERR_CHECK_ALLOC(match);
}
- if (!(error = git_attr_fnmatch__parse(match, context, &scan))) {
+ if (!(error = git_attr_fnmatch__parse(
+ match, ignores->pool, context, &scan)))
+ {
match->flags = match->flags | GIT_ATTR_FNMATCH_IGNORE;
scan = git__next_line(scan);
error = git_vector_insert(&ignores->rules, match);
}
- if (error != GIT_SUCCESS) {
+ if (error != 0) {
git__free(match->pattern);
match->pattern = NULL;
if (error == GIT_ENOTFOUND)
- error = GIT_SUCCESS;
+ error = 0;
} else {
match = NULL; /* vector now "owns" the match */
}
}
- git_futils_freebuffer(&fbuf);
git__free(match);
- git__free(context);
-
- if (error != GIT_SUCCESS)
- git__rethrow(error, "Could not open ignore file '%s'", path);
+ /* restore file path used for context */
+ if (context)
+ context[strlen(context)] = '.'; /* first char of GIT_IGNORE_FILE */
return error;
}
-#define push_ignore(R,S,B,F) \
- git_attr_cache__push_file((R),(S),(B),(F),load_ignore_file)
+#define push_ignore_file(R,S,B,F) \
+ git_attr_cache__push_file((R),(B),(F),GIT_ATTR_FILE_FROM_FILE,parse_ignore_file,(S))
static int push_one_ignore(void *ref, git_buf *path)
{
git_ignores *ign = (git_ignores *)ref;
- return push_ignore(ign->repo, &ign->stack, path->ptr, GIT_IGNORE_FILE);
+ return push_ignore_file(ign->repo, &ign->ign_path, path->ptr, GIT_IGNORE_FILE);
}
-int git_ignore__for_path(git_repository *repo, const char *path, git_ignores *ignores)
+int git_ignore__for_path(
+ git_repository *repo,
+ const char *path,
+ git_ignores *ignores)
{
- int error = GIT_SUCCESS;
- git_buf dir = GIT_BUF_INIT;
- git_config *cfg;
+ int error = 0;
const char *workdir = git_repository_workdir(repo);
assert(ignores);
- if ((error = git_attr_cache__init(repo)) < GIT_SUCCESS)
- goto cleanup;
+ ignores->repo = repo;
+ git_buf_init(&ignores->dir, 0);
+ ignores->ign_internal = NULL;
- if ((error = git_path_find_dir(&dir, path, workdir)) < GIT_SUCCESS)
+ if ((error = git_vector_init(&ignores->ign_path, 8, NULL)) < 0 ||
+ (error = git_vector_init(&ignores->ign_global, 2, NULL)) < 0 ||
+ (error = git_attr_cache__init(repo)) < 0)
goto cleanup;
- ignores->repo = repo;
- ignores->dir = NULL;
- git_vector_init(&ignores->stack, 2, NULL);
+ /* given a unrooted path in a non-bare repo, resolve it */
+ if (workdir && git_path_root(path) < 0)
+ error = git_path_find_dir(&ignores->dir, path, workdir);
+ else
+ error = git_buf_sets(&ignores->dir, path);
+ if (error < 0)
+ goto cleanup;
- /* insert internals */
- if ((error = push_ignore(repo, &ignores->stack, NULL, GIT_IGNORE_INTERNAL)) < GIT_SUCCESS)
+ /* set up internals */
+ error = git_attr_cache__internal_file(
+ repo, GIT_IGNORE_INTERNAL, &ignores->ign_internal);
+ if (error < 0)
goto cleanup;
/* load .gitignore up the path */
- if ((error = git_path_walk_up(&dir, workdir, push_one_ignore, ignores)) < GIT_SUCCESS)
- goto cleanup;
+ if (workdir != NULL) {
+ error = git_path_walk_up(
+ &ignores->dir, workdir, push_one_ignore, ignores);
+ if (error < 0)
+ goto cleanup;
+ }
/* load .git/info/exclude */
- if ((error = push_ignore(repo, &ignores->stack, repo->path_repository, GIT_IGNORE_FILE_INREPO)) < GIT_SUCCESS)
+ error = push_ignore_file(repo, &ignores->ign_global,
+ git_repository_path(repo), GIT_IGNORE_FILE_INREPO);
+ if (error < 0)
goto cleanup;
/* load core.excludesfile */
- if ((error = git_repository_config(&cfg, repo)) == GIT_SUCCESS) {
- const char *core_ignore;
- error = git_config_get_string(cfg, GIT_IGNORE_CONFIG, &core_ignore);
- if (error == GIT_SUCCESS && core_ignore != NULL)
- error = push_ignore(repo, &ignores->stack, NULL, core_ignore);
- else {
- error = GIT_SUCCESS;
- git_clearerror(); /* don't care if attributesfile is not set */
- }
- git_config_free(cfg);
- }
+ if (git_repository_attr_cache(repo)->cfg_excl_file != NULL)
+ error = push_ignore_file(repo, &ignores->ign_global, NULL,
+ git_repository_attr_cache(repo)->cfg_excl_file);
cleanup:
- if (error < GIT_SUCCESS)
- git__rethrow(error, "Could not get ignore files for '%s'", path);
- else
- ignores->dir = git_buf_detach(&dir);
-
- git_buf_free(&dir);
+ if (error < 0)
+ git_ignore__free(ignores);
return error;
}
+int git_ignore__push_dir(git_ignores *ign, const char *dir)
+{
+ if (git_buf_joinpath(&ign->dir, ign->dir.ptr, dir) < 0)
+ return -1;
+ else
+ return push_ignore_file(
+ ign->repo, &ign->ign_path, ign->dir.ptr, GIT_IGNORE_FILE);
+}
+
+int git_ignore__pop_dir(git_ignores *ign)
+{
+ if (ign->ign_path.length > 0) {
+ git_attr_file *file = git_vector_last(&ign->ign_path);
+ if (git__suffixcmp(ign->dir.ptr, file->key + 2) == 0)
+ git_vector_pop(&ign->ign_path);
+ git_buf_rtruncate_at_char(&ign->dir, '/');
+ }
+ return 0;
+}
+
void git_ignore__free(git_ignores *ignores)
{
- git__free(ignores->dir);
- ignores->dir = NULL;
- git_vector_free(&ignores->stack);
+ /* don't need to free ignores->ign_internal since it is in cache */
+ git_vector_free(&ignores->ign_path);
+ git_vector_free(&ignores->ign_global);
+ git_buf_free(&ignores->dir);
+}
+
+static bool ignore_lookup_in_rules(
+ git_vector *rules, git_attr_path *path, int *ignored)
+{
+ unsigned int j;
+ git_attr_fnmatch *match;
+
+ git_vector_rforeach(rules, j, match) {
+ if (git_attr_fnmatch__match(match, path)) {
+ *ignored = ((match->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0);
+ return true;
+ }
+ }
+
+ return false;
}
-int git_ignore__lookup(git_ignores *ignores, const char *pathname, int *ignored)
+int git_ignore__lookup(
+ git_ignores *ignores, const char *pathname, int *ignored)
{
- int error;
- unsigned int i, j;
+ unsigned int i;
git_attr_file *file;
git_attr_path path;
- git_attr_fnmatch *match;
- if ((error = git_attr_path__init(
- &path, pathname, git_repository_workdir(ignores->repo))) < GIT_SUCCESS)
- return git__rethrow(error, "Could not get attribute for '%s'", pathname);
+ if (git_attr_path__init(
+ &path, pathname, git_repository_workdir(ignores->repo)) < 0)
+ return -1;
- *ignored = 0;
+ /* first process builtins - success means path was found */
+ if (ignore_lookup_in_rules(
+ &ignores->ign_internal->rules, &path, ignored))
+ goto cleanup;
- git_vector_foreach(&ignores->stack, i, file) {
- git_vector_rforeach(&file->rules, j, match) {
- if (git_attr_fnmatch__match(match, &path) == GIT_SUCCESS) {
- *ignored = ((match->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0);
- goto found;
- }
- }
+ /* next process files in the path */
+ git_vector_foreach(&ignores->ign_path, i, file) {
+ if (ignore_lookup_in_rules(&file->rules, &path, ignored))
+ goto cleanup;
}
-found:
- return error;
+ /* last process global ignores */
+ git_vector_foreach(&ignores->ign_global, i, file) {
+ if (ignore_lookup_in_rules(&file->rules, &path, ignored))
+ goto cleanup;
+ }
+
+ *ignored = 0;
+
+cleanup:
+ git_attr_path__free(&path);
+ return 0;
}
diff --git a/src/ignore.h b/src/ignore.h
index 9f87ae56e..809d2edbd 100644
--- a/src/ignore.h
+++ b/src/ignore.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -10,14 +10,29 @@
#include "repository.h"
#include "vector.h"
+/* The git_ignores structure maintains three sets of ignores:
+ * - internal ignores
+ * - per directory ignores
+ * - global ignores (at lower priority than the others)
+ * As you traverse from one directory to another, you can push and pop
+ * directories onto git_ignores list efficiently.
+ */
typedef struct {
git_repository *repo;
- char *dir;
- git_vector stack;
+ git_buf dir;
+ git_attr_file *ign_internal;
+ git_vector ign_path;
+ git_vector ign_global;
} git_ignores;
-extern int git_ignore__for_path(git_repository *repo, const char *path, git_ignores *stack);
-extern void git_ignore__free(git_ignores *stack);
-extern int git_ignore__lookup(git_ignores *stack, const char *path, int *ignored);
+extern int git_ignore__for_path(git_repository *repo, const char *path, git_ignores *ign);
+
+extern int git_ignore__push_dir(git_ignores *ign, const char *dir);
+
+extern int git_ignore__pop_dir(git_ignores *ign);
+
+extern void git_ignore__free(git_ignores *ign);
+
+extern int git_ignore__lookup(git_ignores *ign, const char *path, int *ignored);
#endif
diff --git a/src/index.c b/src/index.c
index 66e7a81da..f1ae9a710 100644
--- a/src/index.c
+++ b/src/index.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -135,22 +135,17 @@ int git_index_open(git_index **index_out, const char *index_path)
assert(index_out && index_path);
- index = git__malloc(sizeof(git_index));
- if (index == NULL)
- return GIT_ENOMEM;
-
- memset(index, 0x0, sizeof(git_index));
+ index = git__calloc(1, sizeof(git_index));
+ GITERR_CHECK_ALLOC(index);
index->index_file_path = git__strdup(index_path);
- if (index->index_file_path == NULL) {
- git__free(index);
- return GIT_ENOMEM;
- }
+ GITERR_CHECK_ALLOC(index->index_file_path);
- git_vector_init(&index->entries, 32, index_cmp);
+ if (git_vector_init(&index->entries, 32, index_cmp) < 0)
+ return -1;
/* Check if index file is stored on disk already */
- if (git_path_exists(index->index_file_path) == 0)
+ if (git_path_exists(index->index_file_path) == true)
index->on_disk = 1;
*index_out = index;
@@ -215,36 +210,35 @@ void git_index_clear(git_index *index)
int git_index_read(git_index *index)
{
- int error = GIT_SUCCESS, updated;
- git_fbuffer buffer = GIT_FBUFFER_INIT;
+ int error, updated;
+ git_buf buffer = GIT_BUF_INIT;
time_t mtime;
assert(index->index_file_path);
- if (!index->on_disk || git_path_exists(index->index_file_path) < 0) {
+ if (!index->on_disk || git_path_exists(index->index_file_path) == false) {
git_index_clear(index);
index->on_disk = 0;
- return GIT_SUCCESS;
+ return 0;
}
/* We don't want to update the mtime if we fail to parse the index */
mtime = index->last_modified;
- error = git_futils_readbuffer_updated(&buffer, index->index_file_path, &mtime, &updated);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to read index");
+ error = git_futils_readbuffer_updated(
+ &buffer, index->index_file_path, &mtime, &updated);
+ if (error < 0)
+ return error;
if (updated) {
git_index_clear(index);
- error = parse_index(index, buffer.data, buffer.len);
+ error = parse_index(index, buffer.ptr, buffer.size);
- if (error == GIT_SUCCESS)
+ if (!error)
index->last_modified = mtime;
- git_futils_freebuffer(&buffer);
+ git_buf_free(&buffer);
}
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to parse index");
return error;
}
@@ -256,23 +250,24 @@ int git_index_write(git_index *index)
git_vector_sort(&index->entries);
- if ((error = git_filebuf_open(&file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to write index");
+ if ((error = git_filebuf_open(
+ &file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS)) < 0)
+ return error;
- if ((error = write_index(index, &file)) < GIT_SUCCESS) {
+ if ((error = write_index(index, &file)) < 0) {
git_filebuf_cleanup(&file);
- return git__rethrow(error, "Failed to write index");
+ return error;
}
- if ((error = git_filebuf_commit(&file, GIT_INDEX_FILE_MODE)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to write index");
+ if ((error = git_filebuf_commit(&file, GIT_INDEX_FILE_MODE)) < 0)
+ return error;
if (p_stat(index->index_file_path, &indexst) == 0) {
index->last_modified = indexst.st_mtime;
index->on_disk = 1;
}
- return GIT_SUCCESS;
+ return 0;
}
unsigned int git_index_entrycount(git_index *index)
@@ -293,6 +288,20 @@ git_index_entry *git_index_get(git_index *index, unsigned int n)
return git_vector_get(&index->entries, n);
}
+void git_index__init_entry_from_stat(struct stat *st, git_index_entry *entry)
+{
+ entry->ctime.seconds = (git_time_t)st->st_ctime;
+ entry->mtime.seconds = (git_time_t)st->st_mtime;
+ /* entry->mtime.nanoseconds = st->st_mtimensec; */
+ /* entry->ctime.nanoseconds = st->st_ctimensec; */
+ entry->dev = st->st_rdev;
+ entry->ino = st->st_ino;
+ entry->mode = index_create_mode(st->st_mode);
+ entry->uid = st->st_uid;
+ entry->gid = st->st_gid;
+ entry->file_size = st->st_size;
+}
+
static int index_entry_init(git_index_entry **entry_out, git_index *index, const char *rel_path, int stage)
{
git_index_entry *entry = NULL;
@@ -302,25 +311,20 @@ static int index_entry_init(git_index_entry **entry_out, git_index *index, const
git_buf full_path = GIT_BUF_INIT;
int error;
- if (INDEX_OWNER(index) == NULL)
- return git__throw(GIT_EBAREINDEX,
- "Failed to initialize entry. Repository is bare");
+ assert(stage >= 0 && stage <= 3);
- if (stage < 0 || stage > 3)
- return git__throw(GIT_ERROR,
- "Failed to initialize entry. Invalid stage %i", stage);
-
- workdir = git_repository_workdir(INDEX_OWNER(index));
- if (workdir == NULL)
- return git__throw(GIT_EBAREINDEX,
- "Failed to initialize entry. Cannot resolved workdir");
+ if (INDEX_OWNER(index) == NULL ||
+ (workdir = git_repository_workdir(INDEX_OWNER(index))) == NULL)
+ {
+ giterr_set(GITERR_INDEX,
+ "Could not initialize index entry. Repository is bare");
+ return -1;
+ }
- error = git_buf_joinpath(&full_path, workdir, rel_path);
- if (error < GIT_SUCCESS)
+ if ((error = git_buf_joinpath(&full_path, workdir, rel_path)) < 0)
return error;
- if (p_lstat(full_path.ptr, &st) < 0) {
- error = git__throw(GIT_ENOTFOUND, "Failed to initialize entry. '%s' cannot be opened. %s", full_path.ptr, strerror(errno));
+ if ((error = git_path_lstat(full_path.ptr, &st)) < 0) {
git_buf_free(&full_path);
return error;
}
@@ -332,34 +336,21 @@ static int index_entry_init(git_index_entry **entry_out, git_index *index, const
*/
/* write the blob to disk and get the oid */
- if ((error = git_blob_create_fromfile(&oid, INDEX_OWNER(index), rel_path)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to initialize index entry");
+ if ((error = git_blob_create_fromfile(&oid, INDEX_OWNER(index), rel_path)) < 0)
+ return error;
entry = git__calloc(1, sizeof(git_index_entry));
- if (!entry)
- return GIT_ENOMEM;
-
- entry->ctime.seconds = (git_time_t)st.st_ctime;
- entry->mtime.seconds = (git_time_t)st.st_mtime;
- /* entry.mtime.nanoseconds = st.st_mtimensec; */
- /* entry.ctime.nanoseconds = st.st_ctimensec; */
- entry->dev= st.st_rdev;
- entry->ino = st.st_ino;
- entry->mode = index_create_mode(st.st_mode);
- entry->uid = st.st_uid;
- entry->gid = st.st_gid;
- entry->file_size = st.st_size;
- entry->oid = oid;
+ GITERR_CHECK_ALLOC(entry);
+ git_index__init_entry_from_stat(&st, entry);
+
+ entry->oid = oid;
entry->flags |= (stage << GIT_IDXENTRY_STAGESHIFT);
entry->path = git__strdup(rel_path);
- if (entry->path == NULL) {
- git__free(entry);
- return GIT_ENOMEM;
- }
+ GITERR_CHECK_ALLOC(entry->path);
*entry_out = entry;
- return GIT_SUCCESS;
+ return 0;
}
static git_index_entry *index_entry_dup(const git_index_entry *source_entry)
@@ -394,10 +385,7 @@ static int index_insert(git_index *index, git_index_entry *entry, int replace)
int position;
git_index_entry **entry_array;
- assert(index && entry);
-
- if (entry->path == NULL)
- return git__throw(GIT_EMISSINGOBJDATA, "Failed to insert into index. Entry has no path");
+ assert(index && entry && entry->path != NULL);
/* make sure that the path length flag is correct */
path_length = strlen(entry->path);
@@ -413,12 +401,8 @@ static int index_insert(git_index *index, git_index_entry *entry, int replace)
* replacing is not requested: just insert entry at the end;
* the index is no longer sorted
*/
- if (!replace) {
- if (git_vector_insert(&index->entries, entry) < GIT_SUCCESS)
- return GIT_ENOMEM;
-
- return GIT_SUCCESS;
- }
+ if (!replace)
+ return git_vector_insert(&index->entries, entry);
/* look if an entry with this path already exists */
position = git_index_find(index, entry->path);
@@ -427,12 +411,8 @@ static int index_insert(git_index *index, git_index_entry *entry, int replace)
* if no entry exists add the entry at the end;
* the index is no longer sorted
*/
- if (position == GIT_ENOTFOUND) {
- if (git_vector_insert(&index->entries, entry) < GIT_SUCCESS)
- return GIT_ENOMEM;
-
- return GIT_SUCCESS;
- }
+ if (position == GIT_ENOTFOUND)
+ return git_vector_insert(&index->entries, entry);
/* exists, replace it */
entry_array = (git_index_entry **) index->entries.contents;
@@ -440,7 +420,7 @@ static int index_insert(git_index *index, git_index_entry *entry, int replace)
git__free(entry_array[position]);
entry_array[position] = entry;
- return GIT_SUCCESS;
+ return 0;
}
static int index_add(git_index *index, const char *path, int stage, int replace)
@@ -448,20 +428,15 @@ static int index_add(git_index *index, const char *path, int stage, int replace)
git_index_entry *entry = NULL;
int ret;
- ret = index_entry_init(&entry, index, path, stage);
- if (ret)
- goto err;
-
- ret = index_insert(index, entry, replace);
- if (ret)
- goto err;
+ if ((ret = index_entry_init(&entry, index, path, stage)) < 0 ||
+ (ret = index_insert(index, entry, replace)) < 0)
+ {
+ index_entry_free(entry);
+ return ret;
+ }
git_tree_cache_invalidate_path(index->tree, entry->path);
-
- return ret;
-err:
- index_entry_free(entry);
- return git__rethrow(ret, "Failed to append to index");
+ return 0;
}
int git_index_add(git_index *index, const char *path, int stage)
@@ -474,28 +449,23 @@ int git_index_append(git_index *index, const char *path, int stage)
return index_add(index, path, stage, 0);
}
-static int index_add2(git_index *index, const git_index_entry *source_entry,
- int replace)
+static int index_add2(
+ git_index *index, const git_index_entry *source_entry, int replace)
{
git_index_entry *entry = NULL;
int ret;
entry = index_entry_dup(source_entry);
- if (entry == NULL) {
- ret = GIT_ENOMEM;
- goto err;
- }
+ if (entry == NULL)
+ return -1;
- ret = index_insert(index, entry, replace);
- if (ret)
- goto err;
+ if ((ret = index_insert(index, entry, replace)) < 0) {
+ index_entry_free(entry);
+ return ret;
+ }
git_tree_cache_invalidate_path(index->tree, entry->path);
-
- return ret;
-err:
- index_entry_free(entry);
- return git__rethrow(ret, "Failed to append to index");
+ return 0;
}
int git_index_add2(git_index *index, const git_index_entry *source_entry)
@@ -514,13 +484,14 @@ int git_index_remove(git_index *index, int position)
git_index_entry *entry;
git_vector_sort(&index->entries);
+
entry = git_vector_get(&index->entries, position);
if (entry != NULL)
git_tree_cache_invalidate_path(index->tree, entry->path);
error = git_vector_remove(&index->entries, (unsigned int)position);
- if (error == GIT_SUCCESS)
+ if (!error)
index_entry_free(entry);
return error;
@@ -531,12 +502,22 @@ int git_index_find(git_index *index, const char *path)
return git_vector_bsearch2(&index->entries, index_srch, path);
}
+unsigned int git_index__prefix_position(git_index *index, const char *path)
+{
+ unsigned int pos;
+
+ git_vector_bsearch3(&pos, &index->entries, index_srch, path);
+
+ return pos;
+}
+
void git_index_uniq(git_index *index)
{
git_vector_uniq(&index->entries);
}
-const git_index_entry_unmerged *git_index_get_unmerged_bypath(git_index *index, const char *path)
+const git_index_entry_unmerged *git_index_get_unmerged_bypath(
+ git_index *index, const char *path)
{
int pos;
assert(index && path);
@@ -544,75 +525,87 @@ const git_index_entry_unmerged *git_index_get_unmerged_bypath(git_index *index,
if (!index->unmerged.length)
return NULL;
- if ((pos = git_vector_bsearch2(&index->unmerged, unmerged_srch, path)) < GIT_SUCCESS)
+ if ((pos = git_vector_bsearch2(&index->unmerged, unmerged_srch, path)) < 0)
return NULL;
return git_vector_get(&index->unmerged, pos);
}
-const git_index_entry_unmerged *git_index_get_unmerged_byindex(git_index *index, unsigned int n)
+const git_index_entry_unmerged *git_index_get_unmerged_byindex(
+ git_index *index, unsigned int n)
{
assert(index);
return git_vector_get(&index->unmerged, n);
}
+static int index_error_invalid(const char *message)
+{
+ giterr_set(GITERR_INDEX, "Invalid data in index - %s", message);
+ return -1;
+}
+
static int read_unmerged(git_index *index, const char *buffer, size_t size)
{
const char *endptr;
size_t len;
int i;
- git_vector_init(&index->unmerged, 16, unmerged_cmp);
+ if (git_vector_init(&index->unmerged, 16, unmerged_cmp) < 0)
+ return -1;
while (size) {
git_index_entry_unmerged *lost;
len = strlen(buffer) + 1;
if (size <= len)
- return git__throw(GIT_ERROR, "Failed to read unmerged entries");
+ return index_error_invalid("reading unmerged entries");
- if ((lost = git__malloc(sizeof(git_index_entry_unmerged))) == NULL)
- return GIT_ENOMEM;
+ lost = git__malloc(sizeof(git_index_entry_unmerged));
+ GITERR_CHECK_ALLOC(lost);
- if (git_vector_insert(&index->unmerged, lost) < GIT_SUCCESS)
- return git__throw(GIT_ERROR, "Failed to read unmerged entries");
+ if (git_vector_insert(&index->unmerged, lost) < 0)
+ return -1;
+ /* read NUL-terminated pathname for entry */
lost->path = git__strdup(buffer);
- if (!lost->path)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(lost->path);
size -= len;
buffer += len;
+ /* read 3 ASCII octal numbers for stage entries */
for (i = 0; i < 3; i++) {
int tmp;
- if (git__strtol32(&tmp, buffer, &endptr, 8) < GIT_SUCCESS ||
- !endptr || endptr == buffer || *endptr || (unsigned)tmp > UINT_MAX)
- return GIT_ERROR;
+ if (git__strtol32(&tmp, buffer, &endptr, 8) < 0 ||
+ !endptr || endptr == buffer || *endptr ||
+ (unsigned)tmp > UINT_MAX)
+ return index_error_invalid("reading unmerged entry stage");
lost->mode[i] = tmp;
len = (endptr + 1) - buffer;
if (size <= len)
- return git__throw(GIT_ERROR, "Failed to read unmerged entries");
+ return index_error_invalid("reading unmerged entry stage");
size -= len;
buffer += len;
}
+ /* read up to 3 OIDs for stage entries */
for (i = 0; i < 3; i++) {
if (!lost->mode[i])
continue;
if (size < 20)
- return git__throw(GIT_ERROR, "Failed to read unmerged entries");
+ return index_error_invalid("reading unmerged entry oid");
+
git_oid_fromraw(&lost->oid[i], (const unsigned char *) buffer);
size -= 20;
buffer += 20;
}
}
- return GIT_SUCCESS;
+ return 0;
}
static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffer_size)
@@ -658,7 +651,7 @@ static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffe
path_end = memchr(path_ptr, '\0', buffer_size);
if (path_end == NULL)
- return 0;
+ return 0;
path_length = path_end - path_ptr;
}
@@ -683,15 +676,15 @@ static int read_header(struct index_header *dest, const void *buffer)
dest->signature = ntohl(source->signature);
if (dest->signature != INDEX_HEADER_SIG)
- return GIT_EOBJCORRUPTED;
+ return index_error_invalid("incorrect header signature");
dest->version = ntohl(source->version);
if (dest->version != INDEX_VERSION_NUMBER_EXT &&
dest->version != INDEX_VERSION_NUMBER)
- return GIT_EOBJCORRUPTED;
+ return index_error_invalid("incorrect header version");
dest->entry_count = ntohl(source->entry_count);
- return GIT_SUCCESS;
+ return 0;
}
static size_t read_extension(git_index *index, const char *buffer, size_t buffer_size)
@@ -714,10 +707,10 @@ static size_t read_extension(git_index *index, const char *buffer, size_t buffer
if (dest.signature[0] >= 'A' && dest.signature[0] <= 'Z') {
/* tree cache */
if (memcmp(dest.signature, INDEX_EXT_TREECACHE_SIG, 4) == 0) {
- if (git_tree_cache_read(&index->tree, buffer + 8, dest.extension_size) < GIT_SUCCESS)
+ if (git_tree_cache_read(&index->tree, buffer + 8, dest.extension_size) < 0)
return 0;
} else if (memcmp(dest.signature, INDEX_EXT_UNMERGED_SIG, 4) == 0) {
- if (read_unmerged(index, buffer + 8, dest.extension_size) < GIT_SUCCESS)
+ if (read_unmerged(index, buffer + 8, dest.extension_size) < 0)
return 0;
}
/* else, unsupported extension. We cannot parse this, but we can skip
@@ -739,21 +732,21 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
#define seek_forward(_increase) { \
if (_increase >= buffer_size) \
- return git__throw(GIT_EOBJCORRUPTED, "Failed to seek forward. Buffer size exceeded"); \
+ return index_error_invalid("ran out of data while parsing"); \
buffer += _increase; \
buffer_size -= _increase;\
}
if (buffer_size < INDEX_HEADER_SIZE + INDEX_FOOTER_SIZE)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse index. Buffer too small");
+ return index_error_invalid("insufficient buffer space");
/* Precalculate the SHA1 of the files's contents -- we'll match it to
* the provided SHA1 in the footer */
git_hash_buf(&checksum_calculated, buffer, buffer_size - INDEX_FOOTER_SIZE);
/* Parse header */
- if (read_header(&header, buffer) < GIT_SUCCESS)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse index. Header is corrupted");
+ if (read_header(&header, buffer) < 0)
+ return -1;
seek_forward(INDEX_HEADER_SIZE);
@@ -765,23 +758,22 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
git_index_entry *entry;
entry = git__malloc(sizeof(git_index_entry));
- if (entry == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(entry);
entry_size = read_entry(entry, buffer, buffer_size);
/* 0 bytes read means an object corruption */
if (entry_size == 0)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse index. Entry size is zero");
+ return index_error_invalid("invalid entry");
- if (git_vector_insert(&index->entries, entry) < GIT_SUCCESS)
- return GIT_ENOMEM;
+ if (git_vector_insert(&index->entries, entry) < 0)
+ return -1;
seek_forward(entry_size);
}
if (i != header.entry_count)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse index. Header entries changed while parsing");
+ return index_error_invalid("header entries changed while parsing");
/* There's still space for some extensions! */
while (buffer_size > INDEX_FOOTER_SIZE) {
@@ -791,43 +783,43 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
/* see if we have read any bytes from the extension */
if (extension_size == 0)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse index. Extension size is zero");
+ return index_error_invalid("extension size is zero");
seek_forward(extension_size);
}
if (buffer_size != INDEX_FOOTER_SIZE)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse index. Buffer size does not match index footer size");
+ return index_error_invalid("buffer size does not match index footer size");
/* 160-bit SHA-1 over the content of the index file before this checksum. */
git_oid_fromraw(&checksum_expected, (const unsigned char *)buffer);
if (git_oid_cmp(&checksum_calculated, &checksum_expected) != 0)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse index. Calculated checksum does not match expected checksum");
+ return index_error_invalid("calculated checksum does not match expected");
#undef seek_forward
/* force sorting in the vector: the entries are
* assured to be sorted on the index */
index->entries.sorted = 1;
- return GIT_SUCCESS;
+ return 0;
}
static int is_index_extended(git_index *index)
{
unsigned int i, extended;
+ git_index_entry *entry;
extended = 0;
- for (i = 0; i < index->entries.length; ++i) {
- git_index_entry *entry;
- entry = git_vector_get(&index->entries, i);
+ git_vector_foreach(&index->entries, i, entry) {
entry->flags &= ~GIT_IDXENTRY_EXTENDED;
if (entry->flags_extended & GIT_IDXENTRY_EXTENDED_FLAGS) {
extended++;
entry->flags |= GIT_IDXENTRY_EXTENDED;
}
}
+
return extended;
}
@@ -845,8 +837,8 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry)
else
disk_size = short_entry_size(path_len);
- if (git_filebuf_reserve(file, &mem, disk_size) < GIT_SUCCESS)
- return GIT_ENOMEM;
+ if (git_filebuf_reserve(file, &mem, disk_size) < 0)
+ return -1;
ondisk = (struct entry_short *)mem;
@@ -888,7 +880,7 @@ static int write_disk_entry(git_filebuf *file, git_index_entry *entry)
memcpy(path, entry->path, path_len);
- return GIT_SUCCESS;
+ return 0;
}
static int write_entries(git_index *index, git_filebuf *file)
@@ -898,16 +890,15 @@ static int write_entries(git_index *index, git_filebuf *file)
for (i = 0; i < index->entries.length; ++i) {
git_index_entry *entry;
entry = git_vector_get(&index->entries, i);
- if (write_disk_entry(file, entry) < GIT_SUCCESS)
- return GIT_ENOMEM;
+ if (write_disk_entry(file, entry) < 0)
+ return -1;
}
- return GIT_SUCCESS;
+ return 0;
}
static int write_index(git_index *index, git_filebuf *file)
{
- int error = GIT_SUCCESS;
git_oid hash_final;
struct index_header header;
@@ -922,11 +913,11 @@ static int write_index(git_index *index, git_filebuf *file)
header.version = htonl(is_extended ? INDEX_VERSION_NUMBER_EXT : INDEX_VERSION_NUMBER);
header.entry_count = htonl(index->entries.length);
- git_filebuf_write(file, &header, sizeof(struct index_header));
+ if (git_filebuf_write(file, &header, sizeof(struct index_header)) < 0)
+ return -1;
- error = write_entries(index, file);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to write index");
+ if (write_entries(index, file) < 0)
+ return -1;
/* TODO: write extensions (tree cache) */
@@ -934,9 +925,7 @@ static int write_index(git_index *index, git_filebuf *file)
git_filebuf_hash(&hash_final, file);
/* write it at the end of the file */
- git_filebuf_write(file, hash_final.id, GIT_OID_RAWSZ);
-
- return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write index");
+ return git_filebuf_write(file, hash_final.id, GIT_OID_RAWSZ);
}
int git_index_entry_stage(const git_index_entry *entry)
@@ -946,36 +935,30 @@ int git_index_entry_stage(const git_index_entry *entry)
static int read_tree_cb(const char *root, git_tree_entry *tentry, void *data)
{
- int ret = GIT_SUCCESS;
git_index *index = data;
git_index_entry *entry = NULL;
git_buf path = GIT_BUF_INIT;
- if (entry_is_tree(tentry))
- goto exit;
+ if (git_tree_entry__is_tree(tentry))
+ return 0;
- ret = git_buf_joinpath(&path, root, tentry->filename);
- if (ret < GIT_SUCCESS)
- goto exit;
+ if (git_buf_joinpath(&path, root, tentry->filename) < 0)
+ return -1;
entry = git__calloc(1, sizeof(git_index_entry));
- if (!entry) {
- ret = GIT_ENOMEM;
- goto exit;
- }
+ GITERR_CHECK_ALLOC(entry);
entry->mode = tentry->attr;
entry->oid = tentry->oid;
entry->path = git_buf_detach(&path);
-
- ret = index_insert(index, entry, 0);
-
-exit:
git_buf_free(&path);
- if (ret < GIT_SUCCESS)
+ if (index_insert(index, entry, 0) < 0) {
index_entry_free(entry);
- return ret;
+ return -1;
+ }
+
+ return 0;
}
int git_index_read_tree(git_index *index, git_tree *tree)
diff --git a/src/index.h b/src/index.h
index 9464afb6c..8515f4fcb 100644
--- a/src/index.h
+++ b/src/index.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -31,4 +31,8 @@ struct git_index {
git_vector unmerged;
};
+extern void git_index__init_entry_from_stat(struct stat *st, git_index_entry *entry);
+
+extern unsigned int git_index__prefix_position(git_index *index, const char *path);
+
#endif
diff --git a/src/indexer.c b/src/indexer.c
index 1b2cd61e0..6f735e651 100644
--- a/src/indexer.c
+++ b/src/indexer.c
@@ -1,13 +1,14 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include <zlib.h>
+
#include "git2/indexer.h"
#include "git2/object.h"
-#include "git2/zlib.h"
#include "git2/oid.h"
#include "common.h"
@@ -29,8 +30,6 @@ struct entry {
struct git_indexer {
struct git_pack_file *pack;
- struct stat st;
- struct git_pack_header hdr;
size_t nr_objects;
git_vector objects;
git_filebuf file;
@@ -38,27 +37,89 @@ struct git_indexer {
git_oid hash;
};
+struct git_indexer_stream {
+ unsigned int parsed_header :1,
+ opened_pack;
+ struct git_pack_file *pack;
+ git_filebuf pack_file;
+ git_filebuf index_file;
+ git_off_t off;
+ size_t nr_objects;
+ git_vector objects;
+ git_vector deltas;
+ unsigned int fanout[256];
+ git_oid hash;
+};
+
+struct delta_info {
+ git_off_t delta_off;
+};
+
const git_oid *git_indexer_hash(git_indexer *idx)
{
return &idx->hash;
}
-static int parse_header(git_indexer *idx)
+const git_oid *git_indexer_stream_hash(git_indexer_stream *idx)
+{
+ return &idx->hash;
+}
+
+static int open_pack(struct git_pack_file **out, const char *filename)
+{
+ size_t namelen;
+ struct git_pack_file *pack;
+ struct stat st;
+ int fd;
+
+ namelen = strlen(filename);
+ pack = git__calloc(1, sizeof(struct git_pack_file) + namelen + 1);
+ GITERR_CHECK_ALLOC(pack);
+
+ memcpy(pack->pack_name, filename, namelen + 1);
+
+ if (p_stat(filename, &st) < 0) {
+ giterr_set(GITERR_OS, "Failed to stat packfile.");
+ goto cleanup;
+ }
+
+ if ((fd = p_open(pack->pack_name, O_RDONLY)) < 0) {
+ giterr_set(GITERR_OS, "Failed to open packfile.");
+ goto cleanup;
+ }
+
+ pack->mwf.fd = fd;
+ pack->mwf.size = (git_off_t)st.st_size;
+
+ *out = pack;
+ return 0;
+
+cleanup:
+ git__free(pack);
+ return -1;
+}
+
+static int parse_header(struct git_pack_header *hdr, struct git_pack_file *pack)
{
int error;
/* Verify we recognize this pack file format. */
- if ((error = p_read(idx->pack->mwf.fd, &idx->hdr, sizeof(idx->hdr))) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to read in pack header");
-
- if (idx->hdr.hdr_signature != ntohl(PACK_SIGNATURE))
- return git__throw(GIT_EOBJCORRUPTED, "Wrong pack signature");
+ if ((error = p_read(pack->mwf.fd, hdr, sizeof(*hdr))) < 0) {
+ giterr_set(GITERR_OS, "Failed to read in pack header");
+ return error;
+ }
- if (!pack_version_ok(idx->hdr.hdr_version))
- return git__throw(GIT_EOBJCORRUPTED, "Wrong pack version");
+ if (hdr->hdr_signature != ntohl(PACK_SIGNATURE)) {
+ giterr_set(GITERR_INDEXER, "Wrong pack signature");
+ return -1;
+ }
+ if (!pack_version_ok(hdr->hdr_version)) {
+ giterr_set(GITERR_INDEXER, "Wrong pack version");
+ return -1;
+ }
- return GIT_SUCCESS;
+ return 0;
}
static int objects_cmp(const void *a, const void *b)
@@ -77,78 +138,496 @@ static int cache_cmp(const void *a, const void *b)
return git_oid_cmp(&ea->sha1, &eb->sha1);
}
+int git_indexer_stream_new(git_indexer_stream **out, const char *prefix)
+{
+ git_indexer_stream *idx;
+ git_buf path = GIT_BUF_INIT;
+ static const char suff[] = "/objects/pack/pack-received";
+ int error;
-int git_indexer_new(git_indexer **out, const char *packname)
+ idx = git__calloc(1, sizeof(git_indexer_stream));
+ GITERR_CHECK_ALLOC(idx);
+
+ error = git_buf_joinpath(&path, prefix, suff);
+ if (error < 0)
+ goto cleanup;
+
+ error = git_filebuf_open(&idx->pack_file, path.ptr,
+ GIT_FILEBUF_TEMPORARY | GIT_FILEBUF_DO_NOT_BUFFER);
+ git_buf_free(&path);
+ if (error < 0)
+ goto cleanup;
+
+ *out = idx;
+ return 0;
+
+cleanup:
+ git_buf_free(&path);
+ git_filebuf_cleanup(&idx->pack_file);
+ git__free(idx);
+ return -1;
+}
+
+/* Try to store the delta so we can try to resolve it later */
+static int store_delta(git_indexer_stream *idx)
{
- git_indexer *idx;
- size_t namelen;
- int ret, error;
+ git_otype type;
+ git_mwindow *w = NULL;
+ git_mwindow_file *mwf = &idx->pack->mwf;
+ git_off_t entry_start = idx->off;
+ struct delta_info *delta;
+ size_t entry_size;
+ git_rawobj obj;
+ int error;
- assert(out && packname);
+ /*
+ * ref-delta objects can refer to object that we haven't
+ * found yet, so give it another opportunity
+ */
+ if (git_packfile_unpack_header(&entry_size, &type, mwf, &w, &idx->off) < 0)
+ return -1;
- if (git_path_root(packname) < 0)
- return git__throw(GIT_EINVALIDPATH, "Path is not absolute");
+ git_mwindow_close(&w);
- idx = git__malloc(sizeof(git_indexer));
- if (idx == NULL)
- return GIT_ENOMEM;
+ /* If it's not a delta, mark it as failure, we can't do anything with it */
+ if (type != GIT_OBJ_REF_DELTA && type != GIT_OBJ_OFS_DELTA)
+ return -1;
- memset(idx, 0x0, sizeof(*idx));
+ if (type == GIT_OBJ_REF_DELTA) {
+ idx->off += GIT_OID_RAWSZ;
+ } else {
+ git_off_t base_off;
- namelen = strlen(packname);
- idx->pack = git__malloc(sizeof(struct git_pack_file) + namelen + 1);
- if (idx->pack == NULL) {
- error = GIT_ENOMEM;
- goto cleanup;
+ base_off = get_delta_base(idx->pack, &w, &idx->off, type, entry_start);
+ git_mwindow_close(&w);
+ if (base_off < 0)
+ return (int)base_off;
}
- memset(idx->pack, 0x0, sizeof(struct git_pack_file));
- memcpy(idx->pack->pack_name, packname, namelen + 1);
+ error = packfile_unpack_compressed(&obj, idx->pack, &w, &idx->off, entry_size, type);
+ if (error == GIT_EBUFS) {
+ idx->off = entry_start;
+ return GIT_EBUFS;
+ } else if (error < 0){
+ return -1;
+ }
- ret = p_stat(packname, &idx->st);
- if (ret < 0) {
- if (errno == ENOENT)
- error = git__throw(GIT_ENOTFOUND, "Failed to stat packfile. File not found");
+ delta = git__calloc(1, sizeof(struct delta_info));
+ GITERR_CHECK_ALLOC(delta);
+ delta->delta_off = entry_start;
+
+ git__free(obj.data);
+
+ if (git_vector_insert(&idx->deltas, delta) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int hash_and_save(git_indexer_stream *idx, git_rawobj *obj, git_off_t entry_start)
+{
+ int i;
+ git_oid oid;
+ void *packed;
+ size_t entry_size;
+ unsigned int left;
+ struct entry *entry;
+ git_mwindow *w = NULL;
+ git_mwindow_file *mwf = &idx->pack->mwf;
+ struct git_pack_entry *pentry;
+
+ entry = git__calloc(1, sizeof(*entry));
+ GITERR_CHECK_ALLOC(entry);
+
+ if (entry_start > UINT31_MAX) {
+ entry->offset = UINT32_MAX;
+ entry->offset_long = entry_start;
+ } else {
+ entry->offset = (uint32_t)entry_start;
+ }
+
+ /* FIXME: Parse the object instead of hashing it */
+ if (git_odb__hashobj(&oid, obj) < 0) {
+ giterr_set(GITERR_INDEXER, "Failed to hash object");
+ return -1;
+ }
+
+ pentry = git__malloc(sizeof(struct git_pack_entry));
+ GITERR_CHECK_ALLOC(pentry);
+
+ git_oid_cpy(&pentry->sha1, &oid);
+ pentry->offset = entry_start;
+ if (git_vector_insert(&idx->pack->cache, pentry) < 0)
+ goto on_error;
+
+ git_oid_cpy(&entry->oid, &oid);
+ entry->crc = crc32(0L, Z_NULL, 0);
+
+ entry_size = (size_t)(idx->off - entry_start);
+ packed = git_mwindow_open(mwf, &w, entry_start, entry_size, &left);
+ if (packed == NULL)
+ goto on_error;
+
+ entry->crc = htonl(crc32(entry->crc, packed, (uInt)entry_size));
+ git_mwindow_close(&w);
+
+ /* Add the object to the list */
+ if (git_vector_insert(&idx->objects, entry) < 0)
+ goto on_error;
+
+ for (i = oid.id[0]; i < 256; ++i) {
+ idx->fanout[i]++;
+ }
+
+ return 0;
+
+on_error:
+ git__free(entry);
+ git__free(pentry);
+ git__free(obj->data);
+ return -1;
+}
+
+int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t size, git_indexer_stats *stats)
+{
+ int error;
+ struct git_pack_header hdr;
+ size_t processed = stats->processed;
+ git_mwindow_file *mwf = &idx->pack->mwf;
+
+ assert(idx && data && stats);
+
+ if (git_filebuf_write(&idx->pack_file, data, size) < 0)
+ return -1;
+
+ /* Make sure we set the new size of the pack */
+ if (idx->opened_pack) {
+ idx->pack->mwf.size += size;
+ //printf("\nadding %zu for %zu\n", size, idx->pack->mwf.size);
+ } else {
+ if (open_pack(&idx->pack, idx->pack_file.path_lock) < 0)
+ return -1;
+ idx->opened_pack = 1;
+ mwf = &idx->pack->mwf;
+ if (git_mwindow_file_register(&idx->pack->mwf) < 0)
+ return -1;
+
+ return 0;
+ }
+
+ if (!idx->parsed_header) {
+ if ((unsigned)idx->pack->mwf.size < sizeof(hdr))
+ return 0;
+
+ if (parse_header(&hdr, idx->pack) < 0)
+ return -1;
+
+ idx->parsed_header = 1;
+ idx->nr_objects = ntohl(hdr.hdr_entries);
+ idx->off = sizeof(struct git_pack_header);
+
+ /* for now, limit to 2^32 objects */
+ assert(idx->nr_objects == (size_t)((unsigned int)idx->nr_objects));
+
+ if (git_vector_init(&idx->pack->cache, (unsigned int)idx->nr_objects, cache_cmp) < 0)
+ return -1;
+
+ idx->pack->has_cache = 1;
+ if (git_vector_init(&idx->objects, (unsigned int)idx->nr_objects, objects_cmp) < 0)
+ return -1;
+
+ if (git_vector_init(&idx->deltas, (unsigned int)(idx->nr_objects / 2), NULL) < 0)
+ return -1;
+
+ stats->total = (unsigned int)idx->nr_objects;
+ stats->processed = 0;
+ }
+
+ /* Now that we have data in the pack, let's try to parse it */
+
+ /* As the file grows any windows we try to use will be out of date */
+ git_mwindow_free_all(mwf);
+ while (processed < idx->nr_objects) {
+ git_rawobj obj;
+ git_off_t entry_start = idx->off;
+
+ if (idx->pack->mwf.size <= idx->off + 20)
+ return 0;
+
+ error = git_packfile_unpack(&obj, idx->pack, &idx->off);
+ if (error == GIT_EBUFS) {
+ idx->off = entry_start;
+ return 0;
+ }
+
+ if (error < 0) {
+ idx->off = entry_start;
+ error = store_delta(idx);
+ if (error == GIT_EBUFS)
+ return 0;
+ if (error < 0)
+ return error;
+
+ continue;
+ }
+
+ if (hash_and_save(idx, &obj, entry_start) < 0)
+ goto on_error;
+
+ git__free(obj.data);
+
+ stats->processed = (unsigned int)++processed;
+ }
+
+ return 0;
+
+on_error:
+ git_mwindow_free_all(mwf);
+ return -1;
+}
+
+static int index_path_stream(git_buf *path, git_indexer_stream *idx, const char *suffix)
+{
+ const char prefix[] = "pack-";
+ size_t slash = (size_t)path->size;
+
+ /* search backwards for '/' */
+ while (slash > 0 && path->ptr[slash - 1] != '/')
+ slash--;
+
+ if (git_buf_grow(path, slash + 1 + strlen(prefix) +
+ GIT_OID_HEXSZ + strlen(suffix) + 1) < 0)
+ return -1;
+
+ git_buf_truncate(path, slash);
+ git_buf_puts(path, prefix);
+ git_oid_fmt(path->ptr + git_buf_len(path), &idx->hash);
+ path->size += GIT_OID_HEXSZ;
+ git_buf_puts(path, suffix);
+
+ return git_buf_oom(path) ? -1 : 0;
+}
+
+static int resolve_deltas(git_indexer_stream *idx, git_indexer_stats *stats)
+{
+ unsigned int i;
+ struct delta_info *delta;
+
+ git_vector_foreach(&idx->deltas, i, delta) {
+ git_rawobj obj;
+
+ idx->off = delta->delta_off;
+ if (git_packfile_unpack(&obj, idx->pack, &idx->off) < 0)
+ return -1;
+
+ if (hash_and_save(idx, &obj, delta->delta_off) < 0)
+ return -1;
+
+ git__free(obj.data);
+ stats->processed++;
+ }
+
+ return 0;
+}
+
+int git_indexer_stream_finalize(git_indexer_stream *idx, git_indexer_stats *stats)
+{
+ git_mwindow *w = NULL;
+ unsigned int i, long_offsets = 0, left;
+ struct git_pack_idx_header hdr;
+ git_buf filename = GIT_BUF_INIT;
+ struct entry *entry;
+ void *packfile_hash;
+ git_oid file_hash;
+ SHA_CTX ctx;
+
+ /* Test for this before resolve_deltas(), as it plays with idx->off */
+ if (idx->off < idx->pack->mwf.size - GIT_OID_RAWSZ) {
+ giterr_set(GITERR_INDEXER, "Indexing error: junk at the end of the pack");
+ return -1;
+ }
+
+ if (idx->deltas.length > 0)
+ if (resolve_deltas(idx, stats) < 0)
+ return -1;
+
+ if (stats->processed != stats->total) {
+ giterr_set(GITERR_INDEXER, "Indexing error: early EOF");
+ return -1;
+ }
+
+ git_vector_sort(&idx->objects);
+
+ git_buf_sets(&filename, idx->pack->pack_name);
+ git_buf_truncate(&filename, filename.size - strlen("pack"));
+ git_buf_puts(&filename, "idx");
+ if (git_buf_oom(&filename))
+ return -1;
+
+ if (git_filebuf_open(&idx->index_file, filename.ptr, GIT_FILEBUF_HASH_CONTENTS) < 0)
+ goto on_error;
+
+ /* Write out the header */
+ hdr.idx_signature = htonl(PACK_IDX_SIGNATURE);
+ hdr.idx_version = htonl(2);
+ git_filebuf_write(&idx->index_file, &hdr, sizeof(hdr));
+
+ /* Write out the fanout table */
+ for (i = 0; i < 256; ++i) {
+ uint32_t n = htonl(idx->fanout[i]);
+ git_filebuf_write(&idx->index_file, &n, sizeof(n));
+ }
+
+ /* Write out the object names (SHA-1 hashes) */
+ SHA1_Init(&ctx);
+ git_vector_foreach(&idx->objects, i, entry) {
+ git_filebuf_write(&idx->index_file, &entry->oid, sizeof(git_oid));
+ SHA1_Update(&ctx, &entry->oid, GIT_OID_RAWSZ);
+ }
+ SHA1_Final(idx->hash.id, &ctx);
+
+ /* Write out the CRC32 values */
+ git_vector_foreach(&idx->objects, i, entry) {
+ git_filebuf_write(&idx->index_file, &entry->crc, sizeof(uint32_t));
+ }
+
+ /* Write out the offsets */
+ git_vector_foreach(&idx->objects, i, entry) {
+ uint32_t n;
+
+ if (entry->offset == UINT32_MAX)
+ n = htonl(0x80000000 | long_offsets++);
else
- error = git__throw(GIT_EOSERR, "Failed to stat packfile.");
+ n = htonl(entry->offset);
- goto cleanup;
+ git_filebuf_write(&idx->index_file, &n, sizeof(uint32_t));
}
- ret = p_open(idx->pack->pack_name, O_RDONLY);
- if (ret < 0) {
- error = git__throw(GIT_EOSERR, "Failed to open packfile");
- goto cleanup;
+ /* Write out the long offsets */
+ git_vector_foreach(&idx->objects, i, entry) {
+ uint32_t split[2];
+
+ if (entry->offset != UINT32_MAX)
+ continue;
+
+ split[0] = htonl(entry->offset_long >> 32);
+ split[1] = htonl(entry->offset_long & 0xffffffff);
+
+ git_filebuf_write(&idx->index_file, &split, sizeof(uint32_t) * 2);
+ }
+
+ /* Write out the packfile trailer */
+ packfile_hash = git_mwindow_open(&idx->pack->mwf, &w, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ, &left);
+ if (packfile_hash == NULL) {
+ git_mwindow_close(&w);
+ goto on_error;
}
- idx->pack->mwf.fd = ret;
- idx->pack->mwf.size = (git_off_t)idx->st.st_size;
+ memcpy(&file_hash, packfile_hash, GIT_OID_RAWSZ);
+ git_mwindow_close(&w);
- error = parse_header(idx);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to parse packfile header");
- goto cleanup;
+ git_filebuf_write(&idx->index_file, &file_hash, sizeof(git_oid));
+
+ /* Write out the packfile trailer to the idx file as well */
+ if (git_filebuf_hash(&file_hash, &idx->index_file) < 0)
+ goto on_error;
+
+ git_filebuf_write(&idx->index_file, &file_hash, sizeof(git_oid));
+
+ /* Figure out what the final name should be */
+ if (index_path_stream(&filename, idx, ".idx") < 0)
+ goto on_error;
+
+ /* Commit file */
+ if (git_filebuf_commit_at(&idx->index_file, filename.ptr, GIT_PACK_FILE_MODE) < 0)
+ goto on_error;
+
+ git_mwindow_free_all(&idx->pack->mwf);
+ p_close(idx->pack->mwf.fd);
+
+ if (index_path_stream(&filename, idx, ".pack") < 0)
+ goto on_error;
+ /* And don't forget to rename the packfile to its new place. */
+ if (git_filebuf_commit_at(&idx->pack_file, filename.ptr, GIT_PACK_FILE_MODE) < 0)
+ return -1;
+
+ git_buf_free(&filename);
+ return 0;
+
+on_error:
+ git_mwindow_free_all(&idx->pack->mwf);
+ p_close(idx->pack->mwf.fd);
+ git_filebuf_cleanup(&idx->index_file);
+ git_buf_free(&filename);
+ return -1;
+}
+
+void git_indexer_stream_free(git_indexer_stream *idx)
+{
+ unsigned int i;
+ struct entry *e;
+ struct git_pack_entry *pe;
+ struct delta_info *delta;
+
+ if (idx == NULL)
+ return;
+
+ git_vector_foreach(&idx->objects, i, e)
+ git__free(e);
+ git_vector_free(&idx->objects);
+ git_vector_foreach(&idx->pack->cache, i, pe)
+ git__free(pe);
+ git_vector_free(&idx->pack->cache);
+ git_vector_foreach(&idx->deltas, i, delta)
+ git__free(delta);
+ git_vector_free(&idx->deltas);
+ git__free(idx->pack);
+ git__free(idx);
+}
+
+int git_indexer_new(git_indexer **out, const char *packname)
+{
+ git_indexer *idx;
+ struct git_pack_header hdr;
+ int error;
+
+ assert(out && packname);
+
+ if (git_path_root(packname) < 0) {
+ giterr_set(GITERR_INDEXER, "Path is not absolute");
+ return -1;
}
- idx->nr_objects = ntohl(idx->hdr.hdr_entries);
+ idx = git__calloc(1, sizeof(git_indexer));
+ GITERR_CHECK_ALLOC(idx);
- error = git_vector_init(&idx->pack->cache, idx->nr_objects, cache_cmp);
- if (error < GIT_SUCCESS)
+ open_pack(&idx->pack, packname);
+
+ if ((error = parse_header(&hdr, idx->pack)) < 0)
+ goto cleanup;
+
+ idx->nr_objects = ntohl(hdr.hdr_entries);
+
+ /* for now, limit to 2^32 objects */
+ assert(idx->nr_objects == (size_t)((unsigned int)idx->nr_objects));
+
+ error = git_vector_init(&idx->pack->cache, (unsigned int)idx->nr_objects, cache_cmp);
+ if (error < 0)
goto cleanup;
idx->pack->has_cache = 1;
- error = git_vector_init(&idx->objects, idx->nr_objects, objects_cmp);
- if (error < GIT_SUCCESS)
+ error = git_vector_init(&idx->objects, (unsigned int)idx->nr_objects, objects_cmp);
+ if (error < 0)
goto cleanup;
*out = idx;
- return GIT_SUCCESS;
+ return 0;
cleanup:
git_indexer_free(idx);
- return error;
+ return -1;
}
static int index_path(git_buf *path, git_indexer *idx)
@@ -161,16 +640,16 @@ static int index_path(git_buf *path, git_indexer *idx)
slash--;
if (git_buf_grow(path, slash + 1 + strlen(prefix) +
- GIT_OID_HEXSZ + strlen(suffix) + 1) < GIT_SUCCESS)
- return GIT_ENOMEM;
+ GIT_OID_HEXSZ + strlen(suffix) + 1) < 0)
+ return -1;
git_buf_truncate(path, slash);
git_buf_puts(path, prefix);
- git_oid_fmt(path->ptr + path->size, &idx->hash);
+ git_oid_fmt(path->ptr + git_buf_len(path), &idx->hash);
path->size += GIT_OID_HEXSZ;
git_buf_puts(path, suffix);
- return git_buf_lasterror(path);
+ return git_buf_oom(path) ? -1 : 0;
}
int git_indexer_write(git_indexer *idx)
@@ -190,26 +669,25 @@ int git_indexer_write(git_indexer *idx)
git_buf_sets(&filename, idx->pack->pack_name);
git_buf_truncate(&filename, filename.size - strlen("pack"));
git_buf_puts(&filename, "idx");
-
- if ((error = git_buf_lasterror(&filename)) < GIT_SUCCESS)
- goto cleanup;
+ if (git_buf_oom(&filename))
+ return -1;
error = git_filebuf_open(&idx->file, filename.ptr, GIT_FILEBUF_HASH_CONTENTS);
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
/* Write out the header */
hdr.idx_signature = htonl(PACK_IDX_SIGNATURE);
hdr.idx_version = htonl(2);
error = git_filebuf_write(&idx->file, &hdr, sizeof(hdr));
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
/* Write out the fanout table */
for (i = 0; i < 256; ++i) {
uint32_t n = htonl(idx->fanout[i]);
error = git_filebuf_write(&idx->file, &n, sizeof(n));
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
}
@@ -218,7 +696,7 @@ int git_indexer_write(git_indexer *idx)
git_vector_foreach(&idx->objects, i, entry) {
error = git_filebuf_write(&idx->file, &entry->oid, sizeof(git_oid));
SHA1_Update(&ctx, &entry->oid, GIT_OID_RAWSZ);
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
}
SHA1_Final(idx->hash.id, &ctx);
@@ -226,7 +704,7 @@ int git_indexer_write(git_indexer *idx)
/* Write out the CRC32 values */
git_vector_foreach(&idx->objects, i, entry) {
error = git_filebuf_write(&idx->file, &entry->crc, sizeof(uint32_t));
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
}
@@ -240,7 +718,7 @@ int git_indexer_write(git_indexer *idx)
n = htonl(entry->offset);
error = git_filebuf_write(&idx->file, &n, sizeof(uint32_t));
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
}
@@ -255,16 +733,16 @@ int git_indexer_write(git_indexer *idx)
split[1] = htonl(entry->offset_long & 0xffffffff);
error = git_filebuf_write(&idx->file, &split, sizeof(uint32_t) * 2);
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
}
/* Write out the packfile trailer */
- packfile_hash = git_mwindow_open(&idx->pack->mwf, &w, idx->st.st_size - GIT_OID_RAWSZ, GIT_OID_RAWSZ, &left);
+ packfile_hash = git_mwindow_open(&idx->pack->mwf, &w, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ, &left);
git_mwindow_close(&w);
if (packfile_hash == NULL) {
- error = git__rethrow(GIT_ENOMEM, "Failed to open window to packfile hash");
+ error = -1;
goto cleanup;
}
@@ -273,19 +751,21 @@ int git_indexer_write(git_indexer *idx)
git_mwindow_close(&w);
error = git_filebuf_write(&idx->file, &file_hash, sizeof(git_oid));
+ if (error < 0)
+ goto cleanup;
/* Write out the index sha */
error = git_filebuf_hash(&file_hash, &idx->file);
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
error = git_filebuf_write(&idx->file, &file_hash, sizeof(git_oid));
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
/* Figure out what the final name should be */
error = index_path(&filename, idx);
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
/* Commit file */
@@ -293,7 +773,7 @@ int git_indexer_write(git_indexer *idx)
cleanup:
git_mwindow_free_all(&idx->pack->mwf);
- if (error < GIT_SUCCESS)
+ if (error < 0)
git_filebuf_cleanup(&idx->file);
git_buf_free(&filename);
@@ -303,7 +783,7 @@ cleanup:
int git_indexer_run(git_indexer *idx, git_indexer_stats *stats)
{
git_mwindow_file *mwf;
- off_t off = sizeof(struct git_pack_header);
+ git_off_t off = sizeof(struct git_pack_header);
int error;
struct entry *entry;
unsigned int left, processed;
@@ -312,10 +792,10 @@ int git_indexer_run(git_indexer *idx, git_indexer_stats *stats)
mwf = &idx->pack->mwf;
error = git_mwindow_file_register(mwf);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to register mwindow file");
+ if (error < 0)
+ return error;
- stats->total = idx->nr_objects;
+ stats->total = (unsigned int)idx->nr_objects;
stats->processed = processed = 0;
while (processed < idx->nr_objects) {
@@ -324,62 +804,62 @@ int git_indexer_run(git_indexer *idx, git_indexer_stats *stats)
struct git_pack_entry *pentry;
git_mwindow *w = NULL;
int i;
- off_t entry_start = off;
+ git_off_t entry_start = off;
void *packed;
size_t entry_size;
+ char fmt[GIT_OID_HEXSZ] = {0};
- entry = git__malloc(sizeof(struct entry));
- memset(entry, 0x0, sizeof(struct entry));
+ entry = git__calloc(1, sizeof(*entry));
+ GITERR_CHECK_ALLOC(entry);
if (off > UINT31_MAX) {
entry->offset = UINT32_MAX;
entry->offset_long = off;
} else {
- entry->offset = off;
+ entry->offset = (uint32_t)off;
}
error = git_packfile_unpack(&obj, idx->pack, &off);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to unpack object");
+ if (error < 0)
goto cleanup;
- }
/* FIXME: Parse the object instead of hashing it */
- error = git_odb__hash_obj(&oid, &obj);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to hash object");
+ error = git_odb__hashobj(&oid, &obj);
+ if (error < 0) {
+ giterr_set(GITERR_INDEXER, "Failed to hash object");
goto cleanup;
}
pentry = git__malloc(sizeof(struct git_pack_entry));
if (pentry == NULL) {
- error = GIT_ENOMEM;
+ error = -1;
goto cleanup;
}
+
git_oid_cpy(&pentry->sha1, &oid);
pentry->offset = entry_start;
+ git_oid_fmt(fmt, &oid);
+ printf("adding %s to cache\n", fmt);
error = git_vector_insert(&idx->pack->cache, pentry);
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
git_oid_cpy(&entry->oid, &oid);
entry->crc = crc32(0L, Z_NULL, 0);
- entry_size = off - entry_start;
+ entry_size = (size_t)(off - entry_start);
packed = git_mwindow_open(mwf, &w, entry_start, entry_size, &left);
if (packed == NULL) {
- error = git__rethrow(error, "Failed to open window to read packed data");
+ error = -1;
goto cleanup;
}
- entry->crc = htonl(crc32(entry->crc, packed, entry_size));
+ entry->crc = htonl(crc32(entry->crc, packed, (uInt)entry_size));
git_mwindow_close(&w);
/* Add the object to the list */
error = git_vector_insert(&idx->objects, entry);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to add entry to list");
+ if (error < 0)
goto cleanup;
- }
for (i = oid.id[0]; i < 256; ++i) {
idx->fanout[i]++;
diff --git a/src/iterator.c b/src/iterator.c
new file mode 100644
index 000000000..819b0e22a
--- /dev/null
+++ b/src/iterator.c
@@ -0,0 +1,748 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "iterator.h"
+#include "tree.h"
+#include "ignore.h"
+#include "buffer.h"
+#include "git2/submodule.h"
+
+#define ITERATOR_BASE_INIT(P,NAME_LC,NAME_UC) do { \
+ (P) = git__calloc(1, sizeof(NAME_LC ## _iterator)); \
+ GITERR_CHECK_ALLOC(P); \
+ (P)->base.type = GIT_ITERATOR_ ## NAME_UC; \
+ (P)->base.start = start ? git__strdup(start) : NULL; \
+ (P)->base.end = end ? git__strdup(end) : NULL; \
+ (P)->base.current = NAME_LC ## _iterator__current; \
+ (P)->base.at_end = NAME_LC ## _iterator__at_end; \
+ (P)->base.advance = NAME_LC ## _iterator__advance; \
+ (P)->base.seek = NAME_LC ## _iterator__seek; \
+ (P)->base.reset = NAME_LC ## _iterator__reset; \
+ (P)->base.free = NAME_LC ## _iterator__free; \
+ if ((start && !(P)->base.start) || (end && !(P)->base.end)) \
+ return -1; \
+ } while (0)
+
+
+static int empty_iterator__no_item(
+ git_iterator *iter, const git_index_entry **entry)
+{
+ GIT_UNUSED(iter);
+ *entry = NULL;
+ return 0;
+}
+
+static int empty_iterator__at_end(git_iterator *iter)
+{
+ GIT_UNUSED(iter);
+ return 1;
+}
+
+static int empty_iterator__noop(git_iterator *iter)
+{
+ GIT_UNUSED(iter);
+ return 0;
+}
+
+static int empty_iterator__seek(git_iterator *iter, const char *prefix)
+{
+ GIT_UNUSED(iter);
+ GIT_UNUSED(prefix);
+ return -1;
+}
+
+static void empty_iterator__free(git_iterator *iter)
+{
+ GIT_UNUSED(iter);
+}
+
+int git_iterator_for_nothing(git_iterator **iter)
+{
+ git_iterator *i = git__calloc(1, sizeof(git_iterator));
+ GITERR_CHECK_ALLOC(i);
+
+ i->type = GIT_ITERATOR_EMPTY;
+ i->current = empty_iterator__no_item;
+ i->at_end = empty_iterator__at_end;
+ i->advance = empty_iterator__no_item;
+ i->seek = empty_iterator__seek;
+ i->reset = empty_iterator__noop;
+ i->free = empty_iterator__free;
+
+ *iter = i;
+
+ return 0;
+}
+
+
+typedef struct tree_iterator_frame tree_iterator_frame;
+struct tree_iterator_frame {
+ tree_iterator_frame *next;
+ git_tree *tree;
+ char *start;
+ unsigned int index;
+};
+
+typedef struct {
+ git_iterator base;
+ git_repository *repo;
+ tree_iterator_frame *stack;
+ git_index_entry entry;
+ git_buf path;
+ bool path_has_filename;
+} tree_iterator;
+
+static const git_tree_entry *tree_iterator__tree_entry(tree_iterator *ti)
+{
+ return (ti->stack == NULL) ? NULL :
+ git_tree_entry_byindex(ti->stack->tree, ti->stack->index);
+}
+
+static char *tree_iterator__current_filename(
+ tree_iterator *ti, const git_tree_entry *te)
+{
+ if (!ti->path_has_filename) {
+ if (git_buf_joinpath(&ti->path, ti->path.ptr, te->filename) < 0)
+ return NULL;
+ ti->path_has_filename = true;
+ }
+
+ return ti->path.ptr;
+}
+
+static void tree_iterator__pop_frame(tree_iterator *ti)
+{
+ tree_iterator_frame *tf = ti->stack;
+ ti->stack = tf->next;
+ if (ti->stack != NULL) /* don't free the initial tree */
+ git_tree_free(tf->tree);
+ git__free(tf);
+}
+
+static int tree_iterator__to_end(tree_iterator *ti)
+{
+ while (ti->stack && ti->stack->next)
+ tree_iterator__pop_frame(ti);
+
+ if (ti->stack)
+ ti->stack->index = git_tree_entrycount(ti->stack->tree);
+
+ return 0;
+}
+
+static int tree_iterator__current(
+ git_iterator *self, const git_index_entry **entry)
+{
+ tree_iterator *ti = (tree_iterator *)self;
+ const git_tree_entry *te = tree_iterator__tree_entry(ti);
+
+ if (entry)
+ *entry = NULL;
+
+ if (te == NULL)
+ return 0;
+
+ ti->entry.mode = te->attr;
+ git_oid_cpy(&ti->entry.oid, &te->oid);
+
+ ti->entry.path = tree_iterator__current_filename(ti, te);
+ if (ti->entry.path == NULL)
+ return -1;
+
+ if (ti->base.end && git__prefixcmp(ti->entry.path, ti->base.end) > 0)
+ return tree_iterator__to_end(ti);
+
+ if (entry)
+ *entry = &ti->entry;
+
+ return 0;
+}
+
+static int tree_iterator__at_end(git_iterator *self)
+{
+ return (tree_iterator__tree_entry((tree_iterator *)self) == NULL);
+}
+
+static tree_iterator_frame *tree_iterator__alloc_frame(
+ git_tree *tree, char *start)
+{
+ tree_iterator_frame *tf = git__calloc(1, sizeof(tree_iterator_frame));
+ if (!tf)
+ return NULL;
+
+ tf->tree = tree;
+
+ if (start && *start) {
+ tf->start = start;
+ tf->index = git_tree__prefix_position(tree, start);
+ }
+
+ return tf;
+}
+
+static int tree_iterator__expand_tree(tree_iterator *ti)
+{
+ int error;
+ git_tree *subtree;
+ const git_tree_entry *te = tree_iterator__tree_entry(ti);
+ tree_iterator_frame *tf;
+ char *relpath;
+
+ while (te != NULL && git_tree_entry__is_tree(te)) {
+ if (git_buf_joinpath(&ti->path, ti->path.ptr, te->filename) < 0)
+ return -1;
+
+ /* check that we have not passed the range end */
+ if (ti->base.end != NULL &&
+ git__prefixcmp(ti->path.ptr, ti->base.end) > 0)
+ return tree_iterator__to_end(ti);
+
+ if ((error = git_tree_lookup(&subtree, ti->repo, &te->oid)) < 0)
+ return error;
+
+ relpath = NULL;
+
+ /* apply range start to new frame if relevant */
+ if (ti->stack->start &&
+ git__prefixcmp(ti->stack->start, te->filename) == 0)
+ {
+ size_t namelen = strlen(te->filename);
+ if (ti->stack->start[namelen] == '/')
+ relpath = ti->stack->start + namelen + 1;
+ }
+
+ if ((tf = tree_iterator__alloc_frame(subtree, relpath)) == NULL)
+ return -1;
+
+ tf->next = ti->stack;
+ ti->stack = tf;
+
+ te = tree_iterator__tree_entry(ti);
+ }
+
+ return 0;
+}
+
+static int tree_iterator__advance(
+ git_iterator *self, const git_index_entry **entry)
+{
+ int error = 0;
+ tree_iterator *ti = (tree_iterator *)self;
+ const git_tree_entry *te = NULL;
+
+ if (entry != NULL)
+ *entry = NULL;
+
+ if (ti->path_has_filename) {
+ git_buf_rtruncate_at_char(&ti->path, '/');
+ ti->path_has_filename = false;
+ }
+
+ while (ti->stack != NULL) {
+ te = git_tree_entry_byindex(ti->stack->tree, ++ti->stack->index);
+ if (te != NULL)
+ break;
+
+ tree_iterator__pop_frame(ti);
+
+ git_buf_rtruncate_at_char(&ti->path, '/');
+ }
+
+ if (te && git_tree_entry__is_tree(te))
+ error = tree_iterator__expand_tree(ti);
+
+ if (!error)
+ error = tree_iterator__current(self, entry);
+
+ return error;
+}
+
+static int tree_iterator__seek(git_iterator *self, const char *prefix)
+{
+ GIT_UNUSED(self);
+ GIT_UNUSED(prefix);
+ /* pop stack until matches prefix */
+ /* seek item in current frame matching prefix */
+ /* push stack which matches prefix */
+ return -1;
+}
+
+static void tree_iterator__free(git_iterator *self)
+{
+ tree_iterator *ti = (tree_iterator *)self;
+ while (ti->stack != NULL)
+ tree_iterator__pop_frame(ti);
+ git_buf_free(&ti->path);
+}
+
+static int tree_iterator__reset(git_iterator *self)
+{
+ tree_iterator *ti = (tree_iterator *)self;
+
+ while (ti->stack && ti->stack->next)
+ tree_iterator__pop_frame(ti);
+
+ if (ti->stack)
+ ti->stack->index =
+ git_tree__prefix_position(ti->stack->tree, ti->base.start);
+
+ git_buf_clear(&ti->path);
+
+ return tree_iterator__expand_tree(ti);
+}
+
+int git_iterator_for_tree_range(
+ git_iterator **iter,
+ git_repository *repo,
+ git_tree *tree,
+ const char *start,
+ const char *end)
+{
+ int error;
+ tree_iterator *ti;
+
+ if (tree == NULL)
+ return git_iterator_for_nothing(iter);
+
+ ITERATOR_BASE_INIT(ti, tree, TREE);
+
+ ti->repo = repo;
+ ti->stack = tree_iterator__alloc_frame(tree, ti->base.start);
+
+ if ((error = tree_iterator__expand_tree(ti)) < 0)
+ git_iterator_free((git_iterator *)ti);
+ else
+ *iter = (git_iterator *)ti;
+
+ return error;
+}
+
+
+typedef struct {
+ git_iterator base;
+ git_index *index;
+ unsigned int current;
+} index_iterator;
+
+static int index_iterator__current(
+ git_iterator *self, const git_index_entry **entry)
+{
+ index_iterator *ii = (index_iterator *)self;
+ git_index_entry *ie = git_index_get(ii->index, ii->current);
+
+ if (ie != NULL &&
+ ii->base.end != NULL &&
+ git__prefixcmp(ie->path, ii->base.end) > 0)
+ {
+ ii->current = git_index_entrycount(ii->index);
+ ie = NULL;
+ }
+
+ if (entry)
+ *entry = ie;
+
+ return 0;
+}
+
+static int index_iterator__at_end(git_iterator *self)
+{
+ index_iterator *ii = (index_iterator *)self;
+ return (ii->current >= git_index_entrycount(ii->index));
+}
+
+static int index_iterator__advance(
+ git_iterator *self, const git_index_entry **entry)
+{
+ index_iterator *ii = (index_iterator *)self;
+
+ if (ii->current < git_index_entrycount(ii->index))
+ ii->current++;
+
+ return index_iterator__current(self, entry);
+}
+
+static int index_iterator__seek(git_iterator *self, const char *prefix)
+{
+ GIT_UNUSED(self);
+ GIT_UNUSED(prefix);
+ /* find last item before prefix */
+ return -1;
+}
+
+static int index_iterator__reset(git_iterator *self)
+{
+ index_iterator *ii = (index_iterator *)self;
+ ii->current = 0;
+ return 0;
+}
+
+static void index_iterator__free(git_iterator *self)
+{
+ index_iterator *ii = (index_iterator *)self;
+ git_index_free(ii->index);
+ ii->index = NULL;
+}
+
+int git_iterator_for_index_range(
+ git_iterator **iter,
+ git_repository *repo,
+ const char *start,
+ const char *end)
+{
+ int error;
+ index_iterator *ii;
+
+ ITERATOR_BASE_INIT(ii, index, INDEX);
+
+ if ((error = git_repository_index(&ii->index, repo)) < 0)
+ git__free(ii);
+ else {
+ ii->current = start ? git_index__prefix_position(ii->index, start) : 0;
+ *iter = (git_iterator *)ii;
+ }
+
+ return error;
+}
+
+
+typedef struct workdir_iterator_frame workdir_iterator_frame;
+struct workdir_iterator_frame {
+ workdir_iterator_frame *next;
+ git_vector entries;
+ unsigned int index;
+ char *start;
+};
+
+typedef struct {
+ git_iterator base;
+ git_repository *repo;
+ size_t root_len;
+ workdir_iterator_frame *stack;
+ git_ignores ignores;
+ git_index_entry entry;
+ git_buf path;
+ int is_ignored;
+} workdir_iterator;
+
+static workdir_iterator_frame *workdir_iterator__alloc_frame(void)
+{
+ workdir_iterator_frame *wf = git__calloc(1, sizeof(workdir_iterator_frame));
+ if (wf == NULL)
+ return NULL;
+ if (git_vector_init(&wf->entries, 0, git_path_with_stat_cmp) != 0) {
+ git__free(wf);
+ return NULL;
+ }
+ return wf;
+}
+
+static void workdir_iterator__free_frame(workdir_iterator_frame *wf)
+{
+ unsigned int i;
+ git_path_with_stat *path;
+
+ git_vector_foreach(&wf->entries, i, path)
+ git__free(path);
+ git_vector_free(&wf->entries);
+ git__free(wf);
+}
+
+static int workdir_iterator__update_entry(workdir_iterator *wi);
+
+static int workdir_iterator__entry_cmp(const void *prefix, const void *item)
+{
+ const git_path_with_stat *ps = item;
+ return git__prefixcmp((const char *)prefix, ps->path);
+}
+
+static int workdir_iterator__expand_dir(workdir_iterator *wi)
+{
+ int error;
+ workdir_iterator_frame *wf = workdir_iterator__alloc_frame();
+ GITERR_CHECK_ALLOC(wf);
+
+ error = git_path_dirload_with_stat(wi->path.ptr, wi->root_len, &wf->entries);
+ if (error < 0 || wf->entries.length == 0) {
+ workdir_iterator__free_frame(wf);
+ return GIT_ENOTFOUND;
+ }
+
+ git_vector_sort(&wf->entries);
+
+ if (!wi->stack)
+ wf->start = wi->base.start;
+ else if (wi->stack->start &&
+ git__prefixcmp(wi->stack->start, wi->path.ptr + wi->root_len) == 0)
+ wf->start = wi->stack->start;
+
+ if (wf->start)
+ git_vector_bsearch3(
+ &wf->index, &wf->entries, workdir_iterator__entry_cmp, wf->start);
+
+ wf->next = wi->stack;
+ wi->stack = wf;
+
+ /* only push new ignores if this is not top level directory */
+ if (wi->stack->next != NULL) {
+ ssize_t slash_pos = git_buf_rfind_next(&wi->path, '/');
+ (void)git_ignore__push_dir(&wi->ignores, &wi->path.ptr[slash_pos + 1]);
+ }
+
+ return workdir_iterator__update_entry(wi);
+}
+
+static int workdir_iterator__current(
+ git_iterator *self, const git_index_entry **entry)
+{
+ workdir_iterator *wi = (workdir_iterator *)self;
+ *entry = (wi->entry.path == NULL) ? NULL : &wi->entry;
+ return 0;
+}
+
+static int workdir_iterator__at_end(git_iterator *self)
+{
+ return (((workdir_iterator *)self)->entry.path == NULL);
+}
+
+static int workdir_iterator__advance(
+ git_iterator *self, const git_index_entry **entry)
+{
+ int error;
+ workdir_iterator *wi = (workdir_iterator *)self;
+ workdir_iterator_frame *wf;
+ git_path_with_stat *next;
+
+ if (entry != NULL)
+ *entry = NULL;
+
+ if (wi->entry.path == NULL)
+ return 0;
+
+ while ((wf = wi->stack) != NULL) {
+ next = git_vector_get(&wf->entries, ++wf->index);
+ if (next != NULL) {
+ if (strcmp(next->path, DOT_GIT "/") == 0)
+ continue;
+ /* else found a good entry */
+ break;
+ }
+
+ /* pop workdir directory stack */
+ wi->stack = wf->next;
+ workdir_iterator__free_frame(wf);
+ git_ignore__pop_dir(&wi->ignores);
+
+ if (wi->stack == NULL) {
+ memset(&wi->entry, 0, sizeof(wi->entry));
+ return 0;
+ }
+ }
+
+ error = workdir_iterator__update_entry(wi);
+
+ if (!error && entry != NULL)
+ error = workdir_iterator__current(self, entry);
+
+ return error;
+}
+
+static int workdir_iterator__seek(git_iterator *self, const char *prefix)
+{
+ GIT_UNUSED(self);
+ GIT_UNUSED(prefix);
+ /* pop stack until matching prefix */
+ /* find prefix item in current frame */
+ /* push subdirectories as deep as possible while matching */
+ return 0;
+}
+
+static int workdir_iterator__reset(git_iterator *self)
+{
+ workdir_iterator *wi = (workdir_iterator *)self;
+ while (wi->stack != NULL && wi->stack->next != NULL) {
+ workdir_iterator_frame *wf = wi->stack;
+ wi->stack = wf->next;
+ workdir_iterator__free_frame(wf);
+ git_ignore__pop_dir(&wi->ignores);
+ }
+ if (wi->stack)
+ wi->stack->index = 0;
+ return 0;
+}
+
+static void workdir_iterator__free(git_iterator *self)
+{
+ workdir_iterator *wi = (workdir_iterator *)self;
+
+ while (wi->stack != NULL) {
+ workdir_iterator_frame *wf = wi->stack;
+ wi->stack = wf->next;
+ workdir_iterator__free_frame(wf);
+ }
+
+ git_ignore__free(&wi->ignores);
+ git_buf_free(&wi->path);
+}
+
+static int workdir_iterator__update_entry(workdir_iterator *wi)
+{
+ git_path_with_stat *ps = git_vector_get(&wi->stack->entries, wi->stack->index);
+
+ git_buf_truncate(&wi->path, wi->root_len);
+ memset(&wi->entry, 0, sizeof(wi->entry));
+
+ if (!ps)
+ return 0;
+
+ if (git_buf_put(&wi->path, ps->path, ps->path_len) < 0)
+ return -1;
+
+ if (wi->base.end &&
+ git__prefixcmp(wi->path.ptr + wi->root_len, wi->base.end) > 0)
+ return 0;
+
+ wi->entry.path = ps->path;
+
+ /* skip over .git directory */
+ if (strcmp(ps->path, DOT_GIT "/") == 0)
+ return workdir_iterator__advance((git_iterator *)wi, NULL);
+
+ /* if there is an error processing the entry, treat as ignored */
+ wi->is_ignored = 1;
+
+ git_index__init_entry_from_stat(&ps->st, &wi->entry);
+
+ /* need different mode here to keep directories during iteration */
+ wi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);
+
+ /* if this is a file type we don't handle, treat as ignored */
+ if (wi->entry.mode == 0)
+ return 0;
+
+ /* okay, we are far enough along to look up real ignore rule */
+ if (git_ignore__lookup(&wi->ignores, wi->entry.path, &wi->is_ignored) < 0)
+ return 0; /* if error, ignore it and ignore file */
+
+ /* detect submodules */
+ if (S_ISDIR(wi->entry.mode)) {
+ bool is_submodule = git_path_contains(&wi->path, DOT_GIT);
+
+ /* if there is no .git, still check submodules data */
+ if (!is_submodule) {
+ int res = git_submodule_lookup(NULL, wi->repo, wi->entry.path);
+ is_submodule = (res == 0);
+ if (res == GIT_ENOTFOUND)
+ giterr_clear();
+ }
+
+ /* if submodule, mark as GITLINK and remove trailing slash */
+ if (is_submodule) {
+ size_t len = strlen(wi->entry.path);
+ assert(wi->entry.path[len - 1] == '/');
+ wi->entry.path[len - 1] = '\0';
+ wi->entry.mode = S_IFGITLINK;
+ }
+ }
+
+ return 0;
+}
+
+int git_iterator_for_workdir_range(
+ git_iterator **iter,
+ git_repository *repo,
+ const char *start,
+ const char *end)
+{
+ int error;
+ workdir_iterator *wi;
+
+ assert(iter && repo);
+
+ if (git_repository_is_bare(repo)) {
+ giterr_set(GITERR_INVALID,
+ "Cannot scan working directory for bare repo");
+ return -1;
+ }
+
+ ITERATOR_BASE_INIT(wi, workdir, WORKDIR);
+
+ wi->repo = repo;
+
+ if (git_buf_sets(&wi->path, git_repository_workdir(repo)) < 0 ||
+ git_path_to_dir(&wi->path) < 0 ||
+ git_ignore__for_path(repo, "", &wi->ignores) < 0)
+ {
+ git__free(wi);
+ return -1;
+ }
+
+ wi->root_len = wi->path.size;
+
+ if ((error = workdir_iterator__expand_dir(wi)) < 0) {
+ if (error == GIT_ENOTFOUND)
+ error = 0;
+ else {
+ git_iterator_free((git_iterator *)wi);
+ wi = NULL;
+ }
+ }
+
+ *iter = (git_iterator *)wi;
+
+ return error;
+}
+
+
+int git_iterator_current_tree_entry(
+ git_iterator *iter, const git_tree_entry **tree_entry)
+{
+ *tree_entry = (iter->type != GIT_ITERATOR_TREE) ? NULL :
+ tree_iterator__tree_entry((tree_iterator *)iter);
+ return 0;
+}
+
+int git_iterator_current_is_ignored(git_iterator *iter)
+{
+ return (iter->type != GIT_ITERATOR_WORKDIR) ? 0 :
+ ((workdir_iterator *)iter)->is_ignored;
+}
+
+int git_iterator_advance_into_directory(
+ git_iterator *iter, const git_index_entry **entry)
+{
+ workdir_iterator *wi = (workdir_iterator *)iter;
+
+ if (iter->type == GIT_ITERATOR_WORKDIR &&
+ wi->entry.path &&
+ S_ISDIR(wi->entry.mode) &&
+ !S_ISGITLINK(wi->entry.mode))
+ {
+ if (workdir_iterator__expand_dir(wi) < 0)
+ /* if error loading or if empty, skip the directory. */
+ return workdir_iterator__advance(iter, entry);
+ }
+
+ return entry ? git_iterator_current(iter, entry) : 0;
+}
+
+int git_iterator_cmp(
+ git_iterator *iter, const char *path_prefix)
+{
+ const git_index_entry *entry;
+
+ /* a "done" iterator is after every prefix */
+ if (git_iterator_current(iter, &entry) < 0 ||
+ entry == NULL)
+ return 1;
+
+ /* a NULL prefix is after any valid iterator */
+ if (!path_prefix)
+ return -1;
+
+ return git__prefixcmp(entry->path, path_prefix);
+}
+
diff --git a/src/iterator.h b/src/iterator.h
new file mode 100644
index 000000000..b916a9080
--- /dev/null
+++ b/src/iterator.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_iterator_h__
+#define INCLUDE_iterator_h__
+
+#include "common.h"
+#include "git2/index.h"
+
+typedef struct git_iterator git_iterator;
+
+typedef enum {
+ GIT_ITERATOR_EMPTY = 0,
+ GIT_ITERATOR_TREE = 1,
+ GIT_ITERATOR_INDEX = 2,
+ GIT_ITERATOR_WORKDIR = 3
+} git_iterator_type_t;
+
+struct git_iterator {
+ git_iterator_type_t type;
+ char *start;
+ char *end;
+ int (*current)(git_iterator *, const git_index_entry **);
+ int (*at_end)(git_iterator *);
+ int (*advance)(git_iterator *, const git_index_entry **);
+ int (*seek)(git_iterator *, const char *prefix);
+ int (*reset)(git_iterator *);
+ void (*free)(git_iterator *);
+};
+
+extern int git_iterator_for_nothing(git_iterator **iter);
+
+extern int git_iterator_for_tree_range(
+ git_iterator **iter, git_repository *repo, git_tree *tree,
+ const char *start, const char *end);
+
+GIT_INLINE(int) git_iterator_for_tree(
+ git_iterator **iter, git_repository *repo, git_tree *tree)
+{
+ return git_iterator_for_tree_range(iter, repo, tree, NULL, NULL);
+}
+
+extern int git_iterator_for_index_range(
+ git_iterator **iter, git_repository *repo,
+ const char *start, const char *end);
+
+GIT_INLINE(int) git_iterator_for_index(
+ git_iterator **iter, git_repository *repo)
+{
+ return git_iterator_for_index_range(iter, repo, NULL, NULL);
+}
+
+extern int git_iterator_for_workdir_range(
+ git_iterator **iter, git_repository *repo,
+ const char *start, const char *end);
+
+GIT_INLINE(int) git_iterator_for_workdir(
+ git_iterator **iter, git_repository *repo)
+{
+ return git_iterator_for_workdir_range(iter, repo, NULL, NULL);
+}
+
+
+/* Entry is not guaranteed to be fully populated. For a tree iterator,
+ * we will only populate the mode, oid and path, for example. For a workdir
+ * iterator, we will not populate the oid.
+ *
+ * You do not need to free the entry. It is still "owned" by the iterator.
+ * Once you call `git_iterator_advance`, then content of the old entry is
+ * no longer guaranteed to be valid.
+ */
+GIT_INLINE(int) git_iterator_current(
+ git_iterator *iter, const git_index_entry **entry)
+{
+ return iter->current(iter, entry);
+}
+
+GIT_INLINE(int) git_iterator_at_end(git_iterator *iter)
+{
+ return iter->at_end(iter);
+}
+
+GIT_INLINE(int) git_iterator_advance(
+ git_iterator *iter, const git_index_entry **entry)
+{
+ return iter->advance(iter, entry);
+}
+
+GIT_INLINE(int) git_iterator_seek(
+ git_iterator *iter, const char *prefix)
+{
+ return iter->seek(iter, prefix);
+}
+
+GIT_INLINE(int) git_iterator_reset(git_iterator *iter)
+{
+ return iter->reset(iter);
+}
+
+GIT_INLINE(void) git_iterator_free(git_iterator *iter)
+{
+ if (iter == NULL)
+ return;
+
+ iter->free(iter);
+
+ git__free(iter->start);
+ git__free(iter->end);
+
+ memset(iter, 0, sizeof(*iter));
+
+ git__free(iter);
+}
+
+GIT_INLINE(git_iterator_type_t) git_iterator_type(git_iterator *iter)
+{
+ return iter->type;
+}
+
+extern int git_iterator_current_tree_entry(
+ git_iterator *iter, const git_tree_entry **tree_entry);
+
+extern int git_iterator_current_is_ignored(git_iterator *iter);
+
+/**
+ * Iterate into a workdir directory.
+ *
+ * Workdir iterators do not automatically descend into directories (so that
+ * when comparing two iterator entries you can detect a newly created
+ * directory in the workdir). As a result, you may get S_ISDIR items from
+ * a workdir iterator. If you wish to iterate over the contents of the
+ * directories you encounter, then call this function when you encounter
+ * a directory.
+ *
+ * If there are no files in the directory, this will end up acting like a
+ * regular advance and will skip past the directory, so you should be
+ * prepared for that case.
+ *
+ * On non-workdir iterators or if not pointing at a directory, this is a
+ * no-op and will not advance the iterator.
+ */
+extern int git_iterator_advance_into_directory(
+ git_iterator *iter, const git_index_entry **entry);
+
+extern int git_iterator_cmp(
+ git_iterator *iter, const char *path_prefix);
+
+#endif
diff --git a/src/khash.h b/src/khash.h
new file mode 100644
index 000000000..bd67fe1f7
--- /dev/null
+++ b/src/khash.h
@@ -0,0 +1,608 @@
+/* The MIT License
+
+ Copyright (c) 2008, 2009, 2011 by Attractive Chaos <attractor@live.co.uk>
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+*/
+
+/*
+ An example:
+
+#include "khash.h"
+KHASH_MAP_INIT_INT(32, char)
+int main() {
+ int ret, is_missing;
+ khiter_t k;
+ khash_t(32) *h = kh_init(32);
+ k = kh_put(32, h, 5, &ret);
+ kh_value(h, k) = 10;
+ k = kh_get(32, h, 10);
+ is_missing = (k == kh_end(h));
+ k = kh_get(32, h, 5);
+ kh_del(32, h, k);
+ for (k = kh_begin(h); k != kh_end(h); ++k)
+ if (kh_exist(h, k)) kh_value(h, k) = 1;
+ kh_destroy(32, h);
+ return 0;
+}
+*/
+
+/*
+ 2011-12-29 (0.2.7):
+
+ * Minor code clean up; no actual effect.
+
+ 2011-09-16 (0.2.6):
+
+ * The capacity is a power of 2. This seems to dramatically improve the
+ speed for simple keys. Thank Zilong Tan for the suggestion. Reference:
+
+ - http://code.google.com/p/ulib/
+ - http://nothings.org/computer/judy/
+
+ * Allow to optionally use linear probing which usually has better
+ performance for random input. Double hashing is still the default as it
+ is more robust to certain non-random input.
+
+ * Added Wang's integer hash function (not used by default). This hash
+ function is more robust to certain non-random input.
+
+ 2011-02-14 (0.2.5):
+
+ * Allow to declare global functions.
+
+ 2009-09-26 (0.2.4):
+
+ * Improve portability
+
+ 2008-09-19 (0.2.3):
+
+ * Corrected the example
+ * Improved interfaces
+
+ 2008-09-11 (0.2.2):
+
+ * Improved speed a little in kh_put()
+
+ 2008-09-10 (0.2.1):
+
+ * Added kh_clear()
+ * Fixed a compiling error
+
+ 2008-09-02 (0.2.0):
+
+ * Changed to token concatenation which increases flexibility.
+
+ 2008-08-31 (0.1.2):
+
+ * Fixed a bug in kh_get(), which has not been tested previously.
+
+ 2008-08-31 (0.1.1):
+
+ * Added destructor
+*/
+
+
+#ifndef __AC_KHASH_H
+#define __AC_KHASH_H
+
+/*!
+ @header
+
+ Generic hash table library.
+ */
+
+#define AC_VERSION_KHASH_H "0.2.6"
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+/* compipler specific configuration */
+
+#if UINT_MAX == 0xffffffffu
+typedef unsigned int khint32_t;
+#elif ULONG_MAX == 0xffffffffu
+typedef unsigned long khint32_t;
+#endif
+
+#if ULONG_MAX == ULLONG_MAX
+typedef unsigned long khint64_t;
+#else
+typedef unsigned long long khint64_t;
+#endif
+
+#ifdef _MSC_VER
+#define inline __inline
+#endif
+
+typedef khint32_t khint_t;
+typedef khint_t khiter_t;
+
+#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2)
+#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1)
+#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3)
+#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1)))
+#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1)))
+#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1)))
+#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1))
+
+#ifdef KHASH_LINEAR
+#define __ac_inc(k, m) 1
+#else
+#define __ac_inc(k, m) (((k)>>3 ^ (k)<<3) | 1) & (m)
+#endif
+
+#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4)
+
+#ifndef kroundup32
+#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
+#endif
+
+#ifndef kcalloc
+#define kcalloc(N,Z) calloc(N,Z)
+#endif
+#ifndef kmalloc
+#define kmalloc(Z) malloc(Z)
+#endif
+#ifndef krealloc
+#define krealloc(P,Z) realloc(P,Z)
+#endif
+#ifndef kfree
+#define kfree(P) free(P)
+#endif
+
+static const double __ac_HASH_UPPER = 0.77;
+
+#define __KHASH_TYPE(name, khkey_t, khval_t) \
+ typedef struct { \
+ khint_t n_buckets, size, n_occupied, upper_bound; \
+ khint32_t *flags; \
+ khkey_t *keys; \
+ khval_t *vals; \
+ } kh_##name##_t;
+
+#define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \
+ extern kh_##name##_t *kh_init_##name(void); \
+ extern void kh_destroy_##name(kh_##name##_t *h); \
+ extern void kh_clear_##name(kh_##name##_t *h); \
+ extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \
+ extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \
+ extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \
+ extern void kh_del_##name(kh_##name##_t *h, khint_t x);
+
+#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
+ SCOPE kh_##name##_t *kh_init_##name(void) { \
+ return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t)); \
+ } \
+ SCOPE void kh_destroy_##name(kh_##name##_t *h) \
+ { \
+ if (h) { \
+ kfree((void *)h->keys); kfree(h->flags); \
+ kfree((void *)h->vals); \
+ kfree(h); \
+ } \
+ } \
+ SCOPE void kh_clear_##name(kh_##name##_t *h) \
+ { \
+ if (h && h->flags) { \
+ memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \
+ h->size = h->n_occupied = 0; \
+ } \
+ } \
+ SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \
+ { \
+ if (h->n_buckets) { \
+ khint_t inc, k, i, last, mask; \
+ mask = h->n_buckets - 1; \
+ k = __hash_func(key); i = k & mask; \
+ inc = __ac_inc(k, mask); last = i; /* inc==1 for linear probing */ \
+ while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
+ i = (i + inc) & mask; \
+ if (i == last) return h->n_buckets; \
+ } \
+ return __ac_iseither(h->flags, i)? h->n_buckets : i; \
+ } else return 0; \
+ } \
+ SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \
+ { /* This function uses 0.25*n_bucktes bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \
+ khint32_t *new_flags = 0; \
+ khint_t j = 1; \
+ { \
+ kroundup32(new_n_buckets); \
+ if (new_n_buckets < 4) new_n_buckets = 4; \
+ if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \
+ else { /* hash table size to be changed (shrink or expand); rehash */ \
+ new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
+ if (!new_flags) return -1; \
+ memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
+ if (h->n_buckets < new_n_buckets) { /* expand */ \
+ khkey_t *new_keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
+ if (!new_keys) return -1; \
+ h->keys = new_keys; \
+ if (kh_is_map) { \
+ khval_t *new_vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
+ if (!new_vals) return -1; \
+ h->vals = new_vals; \
+ } \
+ } /* otherwise shrink */ \
+ } \
+ } \
+ if (j) { /* rehashing is needed */ \
+ for (j = 0; j != h->n_buckets; ++j) { \
+ if (__ac_iseither(h->flags, j) == 0) { \
+ khkey_t key = h->keys[j]; \
+ khval_t val; \
+ khint_t new_mask; \
+ new_mask = new_n_buckets - 1; \
+ if (kh_is_map) val = h->vals[j]; \
+ __ac_set_isdel_true(h->flags, j); \
+ while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \
+ khint_t inc, k, i; \
+ k = __hash_func(key); \
+ i = k & new_mask; \
+ inc = __ac_inc(k, new_mask); \
+ while (!__ac_isempty(new_flags, i)) i = (i + inc) & new_mask; \
+ __ac_set_isempty_false(new_flags, i); \
+ if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \
+ { khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \
+ if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \
+ __ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \
+ } else { /* write the element and jump out of the loop */ \
+ h->keys[i] = key; \
+ if (kh_is_map) h->vals[i] = val; \
+ break; \
+ } \
+ } \
+ } \
+ } \
+ if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \
+ h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
+ if (kh_is_map) h->vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
+ } \
+ kfree(h->flags); /* free the working space */ \
+ h->flags = new_flags; \
+ h->n_buckets = new_n_buckets; \
+ h->n_occupied = h->size; \
+ h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \
+ } \
+ return 0; \
+ } \
+ SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \
+ { \
+ khint_t x; \
+ if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \
+ if (h->n_buckets > (h->size<<1)) { \
+ if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \
+ *ret = -1; return h->n_buckets; \
+ } \
+ } else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \
+ *ret = -1; return h->n_buckets; \
+ } \
+ } /* TODO: to implement automatically shrinking; resize() already support shrinking */ \
+ { \
+ khint_t inc, k, i, site, last, mask = h->n_buckets - 1; \
+ x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \
+ if (__ac_isempty(h->flags, i)) x = i; /* for speed up */ \
+ else { \
+ inc = __ac_inc(k, mask); last = i; \
+ while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
+ if (__ac_isdel(h->flags, i)) site = i; \
+ i = (i + inc) & mask; \
+ if (i == last) { x = site; break; } \
+ } \
+ if (x == h->n_buckets) { \
+ if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \
+ else x = i; \
+ } \
+ } \
+ } \
+ if (__ac_isempty(h->flags, x)) { /* not present at all */ \
+ h->keys[x] = key; \
+ __ac_set_isboth_false(h->flags, x); \
+ ++h->size; ++h->n_occupied; \
+ *ret = 1; \
+ } else if (__ac_isdel(h->flags, x)) { /* deleted */ \
+ h->keys[x] = key; \
+ __ac_set_isboth_false(h->flags, x); \
+ ++h->size; \
+ *ret = 2; \
+ } else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \
+ return x; \
+ } \
+ SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \
+ { \
+ if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \
+ __ac_set_isdel_true(h->flags, x); \
+ --h->size; \
+ } \
+ }
+
+#define KHASH_DECLARE(name, khkey_t, khval_t) \
+ __KHASH_TYPE(name, khkey_t, khval_t) \
+ __KHASH_PROTOTYPES(name, khkey_t, khval_t)
+
+#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
+ __KHASH_TYPE(name, khkey_t, khval_t) \
+ __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
+
+#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
+ KHASH_INIT2(name, static inline, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
+
+/* --- BEGIN OF HASH FUNCTIONS --- */
+
+/*! @function
+ @abstract Integer hash function
+ @param key The integer [khint32_t]
+ @return The hash value [khint_t]
+ */
+#define kh_int_hash_func(key) (khint32_t)(key)
+/*! @function
+ @abstract Integer comparison function
+ */
+#define kh_int_hash_equal(a, b) ((a) == (b))
+/*! @function
+ @abstract 64-bit integer hash function
+ @param key The integer [khint64_t]
+ @return The hash value [khint_t]
+ */
+#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11)
+/*! @function
+ @abstract 64-bit integer comparison function
+ */
+#define kh_int64_hash_equal(a, b) ((a) == (b))
+/*! @function
+ @abstract const char* hash function
+ @param s Pointer to a null terminated string
+ @return The hash value
+ */
+static inline khint_t __ac_X31_hash_string(const char *s)
+{
+ khint_t h = (khint_t)*s;
+ if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s;
+ return h;
+}
+/*! @function
+ @abstract Another interface to const char* hash function
+ @param key Pointer to a null terminated string [const char*]
+ @return The hash value [khint_t]
+ */
+#define kh_str_hash_func(key) __ac_X31_hash_string(key)
+/*! @function
+ @abstract Const char* comparison function
+ */
+#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0)
+
+static inline khint_t __ac_Wang_hash(khint_t key)
+{
+ key += ~(key << 15);
+ key ^= (key >> 10);
+ key += (key << 3);
+ key ^= (key >> 6);
+ key += ~(key << 11);
+ key ^= (key >> 16);
+ return key;
+}
+#define kh_int_hash_func2(k) __ac_Wang_hash((khint_t)key)
+
+/* --- END OF HASH FUNCTIONS --- */
+
+/* Other convenient macros... */
+
+/*!
+ @abstract Type of the hash table.
+ @param name Name of the hash table [symbol]
+ */
+#define khash_t(name) kh_##name##_t
+
+/*! @function
+ @abstract Initiate a hash table.
+ @param name Name of the hash table [symbol]
+ @return Pointer to the hash table [khash_t(name)*]
+ */
+#define kh_init(name) kh_init_##name()
+
+/*! @function
+ @abstract Destroy a hash table.
+ @param name Name of the hash table [symbol]
+ @param h Pointer to the hash table [khash_t(name)*]
+ */
+#define kh_destroy(name, h) kh_destroy_##name(h)
+
+/*! @function
+ @abstract Reset a hash table without deallocating memory.
+ @param name Name of the hash table [symbol]
+ @param h Pointer to the hash table [khash_t(name)*]
+ */
+#define kh_clear(name, h) kh_clear_##name(h)
+
+/*! @function
+ @abstract Resize a hash table.
+ @param name Name of the hash table [symbol]
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param s New size [khint_t]
+ */
+#define kh_resize(name, h, s) kh_resize_##name(h, s)
+
+/*! @function
+ @abstract Insert a key to the hash table.
+ @param name Name of the hash table [symbol]
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param k Key [type of keys]
+ @param r Extra return code: 0 if the key is present in the hash table;
+ 1 if the bucket is empty (never used); 2 if the element in
+ the bucket has been deleted [int*]
+ @return Iterator to the inserted element [khint_t]
+ */
+#define kh_put(name, h, k, r) kh_put_##name(h, k, r)
+
+/*! @function
+ @abstract Retrieve a key from the hash table.
+ @param name Name of the hash table [symbol]
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param k Key [type of keys]
+ @return Iterator to the found element, or kh_end(h) is the element is absent [khint_t]
+ */
+#define kh_get(name, h, k) kh_get_##name(h, k)
+
+/*! @function
+ @abstract Remove a key from the hash table.
+ @param name Name of the hash table [symbol]
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param k Iterator to the element to be deleted [khint_t]
+ */
+#define kh_del(name, h, k) kh_del_##name(h, k)
+
+/*! @function
+ @abstract Test whether a bucket contains data.
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param x Iterator to the bucket [khint_t]
+ @return 1 if containing data; 0 otherwise [int]
+ */
+#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x)))
+
+/*! @function
+ @abstract Get key given an iterator
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param x Iterator to the bucket [khint_t]
+ @return Key [type of keys]
+ */
+#define kh_key(h, x) ((h)->keys[x])
+
+/*! @function
+ @abstract Get value given an iterator
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param x Iterator to the bucket [khint_t]
+ @return Value [type of values]
+ @discussion For hash sets, calling this results in segfault.
+ */
+#define kh_val(h, x) ((h)->vals[x])
+
+/*! @function
+ @abstract Alias of kh_val()
+ */
+#define kh_value(h, x) ((h)->vals[x])
+
+/*! @function
+ @abstract Get the start iterator
+ @param h Pointer to the hash table [khash_t(name)*]
+ @return The start iterator [khint_t]
+ */
+#define kh_begin(h) (khint_t)(0)
+
+/*! @function
+ @abstract Get the end iterator
+ @param h Pointer to the hash table [khash_t(name)*]
+ @return The end iterator [khint_t]
+ */
+#define kh_end(h) ((h)->n_buckets)
+
+/*! @function
+ @abstract Get the number of elements in the hash table
+ @param h Pointer to the hash table [khash_t(name)*]
+ @return Number of elements in the hash table [khint_t]
+ */
+#define kh_size(h) ((h)->size)
+
+/*! @function
+ @abstract Get the number of buckets in the hash table
+ @param h Pointer to the hash table [khash_t(name)*]
+ @return Number of buckets in the hash table [khint_t]
+ */
+#define kh_n_buckets(h) ((h)->n_buckets)
+
+/*! @function
+ @abstract Iterate over the entries in the hash table
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param kvar Variable to which key will be assigned
+ @param vvar Variable to which value will be assigned
+ @param code Block of code to execute
+ */
+#define kh_foreach(h, kvar, vvar, code) { khint_t __i; \
+ for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
+ if (!kh_exist(h,__i)) continue; \
+ (kvar) = kh_key(h,__i); \
+ (vvar) = kh_val(h,__i); \
+ code; \
+ } }
+
+/*! @function
+ @abstract Iterate over the values in the hash table
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param vvar Variable to which value will be assigned
+ @param code Block of code to execute
+ */
+#define kh_foreach_value(h, vvar, code) { khint_t __i; \
+ for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
+ if (!kh_exist(h,__i)) continue; \
+ (vvar) = kh_val(h,__i); \
+ code; \
+ } }
+
+/* More conenient interfaces */
+
+/*! @function
+ @abstract Instantiate a hash set containing integer keys
+ @param name Name of the hash table [symbol]
+ */
+#define KHASH_SET_INIT_INT(name) \
+ KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal)
+
+/*! @function
+ @abstract Instantiate a hash map containing integer keys
+ @param name Name of the hash table [symbol]
+ @param khval_t Type of values [type]
+ */
+#define KHASH_MAP_INIT_INT(name, khval_t) \
+ KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal)
+
+/*! @function
+ @abstract Instantiate a hash map containing 64-bit integer keys
+ @param name Name of the hash table [symbol]
+ */
+#define KHASH_SET_INIT_INT64(name) \
+ KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal)
+
+/*! @function
+ @abstract Instantiate a hash map containing 64-bit integer keys
+ @param name Name of the hash table [symbol]
+ @param khval_t Type of values [type]
+ */
+#define KHASH_MAP_INIT_INT64(name, khval_t) \
+ KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal)
+
+typedef const char *kh_cstr_t;
+/*! @function
+ @abstract Instantiate a hash map containing const char* keys
+ @param name Name of the hash table [symbol]
+ */
+#define KHASH_SET_INIT_STR(name) \
+ KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal)
+
+/*! @function
+ @abstract Instantiate a hash map containing const char* keys
+ @param name Name of the hash table [symbol]
+ @param khval_t Type of values [type]
+ */
+#define KHASH_MAP_INIT_STR(name, khval_t) \
+ KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
+
+#endif /* __AC_KHASH_H */
diff --git a/src/map.h b/src/map.h
index 6969de5b3..96d879547 100644
--- a/src/map.h
+++ b/src/map.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -31,6 +31,11 @@ typedef struct { /* memory mapped buffer */
#endif
} git_map;
+#define GIT_MMAP_VALIDATE(out, len, prot, flags) do { \
+ assert(out != NULL && len > 0); \
+ assert((prot & GIT_PROT_WRITE) || (prot & GIT_PROT_READ)); \
+ assert((flags & GIT_MAP_FIXED) == 0); } while (0)
+
extern int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset);
extern int p_munmap(git_map *map);
diff --git a/src/message.c b/src/message.c
new file mode 100644
index 000000000..aa0220fd0
--- /dev/null
+++ b/src/message.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "message.h"
+#include <ctype.h>
+
+static size_t line_length_without_trailing_spaces(const char *line, size_t len)
+{
+ while (len) {
+ unsigned char c = line[len - 1];
+ if (!git__isspace(c))
+ break;
+ len--;
+ }
+
+ return len;
+}
+
+/* Greatly inspired from git.git "stripspace" */
+/* see https://github.com/git/git/blob/497215d8811ac7b8955693ceaad0899ecd894ed2/builtin/stripspace.c#L4-67 */
+int git_message_prettify(git_buf *message_out, const char *message, int strip_comments)
+{
+ const size_t message_len = strlen(message);
+
+ int consecutive_empty_lines = 0;
+ size_t i, line_length, rtrimmed_line_length;
+ char *next_newline;
+
+ for (i = 0; i < strlen(message); i += line_length) {
+ next_newline = memchr(message + i, '\n', message_len - i);
+
+ if (next_newline != NULL) {
+ line_length = next_newline - (message + i) + 1;
+ } else {
+ line_length = message_len - i;
+ }
+
+ if (strip_comments && line_length && message[i] == '#')
+ continue;
+
+ rtrimmed_line_length = line_length_without_trailing_spaces(message + i, line_length);
+
+ if (!rtrimmed_line_length) {
+ consecutive_empty_lines++;
+ continue;
+ }
+
+ if (consecutive_empty_lines > 0 && message_out->size > 0)
+ git_buf_putc(message_out, '\n');
+
+ consecutive_empty_lines = 0;
+ git_buf_put(message_out, message + i, rtrimmed_line_length);
+ git_buf_putc(message_out, '\n');
+ }
+
+ return git_buf_oom(message_out) ? -1 : 0;
+}
diff --git a/src/message.h b/src/message.h
new file mode 100644
index 000000000..ddfa13e18
--- /dev/null
+++ b/src/message.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_message_h__
+#define INCLUDE_message_h__
+
+#include "buffer.h"
+
+int git_message_prettify(git_buf *message_out, const char *message, int strip_comments);
+
+#endif /* INCLUDE_message_h__ */
diff --git a/src/mwindow.c b/src/mwindow.c
index 8dc4573b4..b59c4d2f7 100644
--- a/src/mwindow.c
+++ b/src/mwindow.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -89,6 +89,7 @@ void git_mwindow_scan_lru(
{
git_mwindow *w, *w_l;
+ puts("LRU");
for (w_l = NULL, w = mwf->windows; w; w = w->next) {
if (!w->inuse_cnt) {
/*
@@ -115,7 +116,7 @@ static int git_mwindow_close_lru(git_mwindow_file *mwf)
unsigned int i;
git_mwindow *lru_w = NULL, *lru_l = NULL, **list = &mwf->windows;
- /* FIMXE: Does this give us any advantage? */
+ /* FIXME: Does this give us any advantage? */
if(mwf->windows)
git_mwindow_scan_lru(mwf, &lru_w, &lru_l);
@@ -127,22 +128,23 @@ static int git_mwindow_close_lru(git_mwindow_file *mwf)
list = &cur->windows;
}
- if (lru_w) {
- ctl->mapped -= lru_w->window_map.len;
- git_futils_mmap_free(&lru_w->window_map);
+ if (!lru_w) {
+ giterr_set(GITERR_OS, "Failed to close memory window. Couldn't find LRU");
+ return -1;
+ }
- if (lru_l)
- lru_l->next = lru_w->next;
- else
- *list = lru_w->next;
+ ctl->mapped -= lru_w->window_map.len;
+ git_futils_mmap_free(&lru_w->window_map);
- git__free(lru_w);
- ctl->open_windows--;
+ if (lru_l)
+ lru_l->next = lru_w->next;
+ else
+ *list = lru_w->next;
- return GIT_SUCCESS;
- }
+ git__free(lru_w);
+ ctl->open_windows--;
- return git__throw(GIT_ERROR, "Failed to close memory window. Couln't find LRU");
+ return 0;
}
static git_mwindow *new_window(
@@ -158,7 +160,7 @@ static git_mwindow *new_window(
w = git__malloc(sizeof(*w));
if (w == NULL)
- return w;
+ return NULL;
memset(w, 0x0, sizeof(*w));
w->offset = (offset / walign) * walign;
@@ -170,7 +172,7 @@ static git_mwindow *new_window(
ctl->mapped += (size_t)len;
while (_mw_options.mapped_limit < ctl->mapped &&
- git_mwindow_close_lru(mwf) == GIT_SUCCESS) /* nop */;
+ git_mwindow_close_lru(mwf) == 0) /* nop */;
/*
* We treat _mw_options.mapped_limit as a soft limit. If we can't find a
@@ -178,8 +180,10 @@ static git_mwindow *new_window(
* window.
*/
- if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < GIT_SUCCESS)
- goto cleanup;
+ if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < 0) {
+ git__free(w);
+ return NULL;
+ }
ctl->mmap_calls++;
ctl->open_windows++;
@@ -191,10 +195,6 @@ static git_mwindow *new_window(
ctl->peak_open_windows = ctl->open_windows;
return w;
-
-cleanup:
- git__free(w);
- return NULL;
}
/*
@@ -205,19 +205,20 @@ unsigned char *git_mwindow_open(
git_mwindow_file *mwf,
git_mwindow **cursor,
git_off_t offset,
- int extra,
+ size_t extra,
unsigned int *left)
{
git_mwindow_ctl *ctl = &GIT_GLOBAL->mem_ctl;
git_mwindow *w = *cursor;
- if (!w || !git_mwindow_contains(w, offset + extra)) {
+ if (!w || !(git_mwindow_contains(w, offset) && git_mwindow_contains(w, offset + extra))) {
if (w) {
w->inuse_cnt--;
}
for (w = mwf->windows; w; w = w->next) {
- if (git_mwindow_contains(w, offset + extra))
+ if (git_mwindow_contains(w, offset) &&
+ git_mwindow_contains(w, offset + extra))
break;
}
@@ -242,22 +243,21 @@ unsigned char *git_mwindow_open(
}
offset -= w->offset;
- assert(git__is_sizet(offset));
if (left)
*left = (unsigned int)(w->window_map.len - offset);
+ fflush(stdout);
return (unsigned char *) w->window_map.data + offset;
}
int git_mwindow_file_register(git_mwindow_file *mwf)
{
git_mwindow_ctl *ctl = &GIT_GLOBAL->mem_ctl;
- int error;
if (ctl->windowfiles.length == 0 &&
- (error = git_vector_init(&ctl->windowfiles, 8, NULL)) < GIT_SUCCESS)
- return error;
+ git_vector_init(&ctl->windowfiles, 8, NULL) < 0)
+ return -1;
return git_vector_insert(&ctl->windowfiles, mwf);
}
diff --git a/src/mwindow.h b/src/mwindow.h
index 11c3aa840..058027251 100644
--- a/src/mwindow.h
+++ b/src/mwindow.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -15,8 +15,8 @@ typedef struct git_mwindow {
struct git_mwindow *next;
git_map window_map;
git_off_t offset;
- unsigned int last_used;
- unsigned int inuse_cnt;
+ size_t last_used;
+ size_t inuse_cnt;
} git_mwindow;
typedef struct git_mwindow_file {
@@ -37,7 +37,7 @@ typedef struct git_mwindow_ctl {
int git_mwindow_contains(git_mwindow *win, git_off_t offset);
void git_mwindow_free_all(git_mwindow_file *mwf);
-unsigned char *git_mwindow_open(git_mwindow_file *mwf, git_mwindow **cursor, git_off_t offset, int extra, unsigned int *left);
+unsigned char *git_mwindow_open(git_mwindow_file *mwf, git_mwindow **cursor, git_off_t offset, size_t extra, unsigned int *left);
void git_mwindow_scan_lru(git_mwindow_file *mwf, git_mwindow **lru_w, git_mwindow **lru_l);
int git_mwindow_file_register(git_mwindow_file *mwf);
void git_mwindow_close(git_mwindow **w_cursor);
diff --git a/src/netops.c b/src/netops.c
index 0f85eaecd..4d461a049 100644
--- a/src/netops.c
+++ b/src/netops.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -11,7 +11,6 @@
# include <sys/time.h>
# include <netdb.h>
#else
-# define _WIN32_WINNT 0x0501
# include <winsock2.h>
# include <Ws2tcpip.h>
# ifdef _MSC_VER
@@ -25,8 +24,28 @@
#include "common.h"
#include "netops.h"
#include "posix.h"
+#include "buffer.h"
-void gitno_buffer_setup(gitno_buffer *buf, char *data, unsigned int len, int fd)
+#ifdef GIT_WIN32
+static void net_set_error(const char *str)
+{
+ int size, error = WSAGetLastError();
+ LPSTR err_str = NULL;
+
+ size = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ 0, error, 0, (LPSTR)&err_str, 0, 0);
+
+ giterr_set(GITERR_NET, "%s: %s", str, err_str);
+ LocalFree(err_str);
+}
+#else
+static void net_set_error(const char *str)
+{
+ giterr_set(GITERR_NET, "%s: %s", str, strerror(errno));
+}
+#endif
+
+void gitno_buffer_setup(gitno_buffer *buf, char *data, unsigned int len, GIT_SOCKET fd)
{
memset(buf, 0x0, sizeof(gitno_buffer));
memset(data, 0x0, len);
@@ -40,14 +59,13 @@ int gitno_recv(gitno_buffer *buf)
{
int ret;
- ret = recv(buf->fd, buf->data + buf->offset, buf->len - buf->offset, 0);
- if (ret < 0)
- return git__throw(GIT_EOSERR, "Failed to receive data: %s", strerror(errno));
- if (ret == 0) /* Orderly shutdown, so exit */
- return GIT_SUCCESS;
+ ret = p_recv(buf->fd, buf->data + buf->offset, buf->len - buf->offset, 0);
+ if (ret < 0) {
+ net_set_error("Error receiving socket data");
+ return -1;
+ }
buf->offset += ret;
-
return ret;
}
@@ -74,52 +92,46 @@ void gitno_consume_n(gitno_buffer *buf, size_t cons)
buf->offset -= cons;
}
-int gitno_connect(const char *host, const char *port)
+int gitno_connect(GIT_SOCKET *sock, const char *host, const char *port)
{
- struct addrinfo *info, *p;
+ struct addrinfo *info = NULL, *p;
struct addrinfo hints;
- int ret, error = GIT_SUCCESS;
- GIT_SOCKET s;
+ int ret;
+ GIT_SOCKET s = INVALID_SOCKET;
memset(&hints, 0x0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
- ret = getaddrinfo(host, port, &hints, &info);
- if (ret != 0) {
- error = GIT_EOSERR;
- info = NULL;
- goto cleanup;
+ if ((ret = getaddrinfo(host, port, &hints, &info)) < 0) {
+ giterr_set(GITERR_NET, "Failed to resolve address for %s: %s", host, gai_strerror(ret));
+ return -1;
}
for (p = info; p != NULL; p = p->ai_next) {
s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
-#ifdef GIT_WIN32
if (s == INVALID_SOCKET) {
-#else
- if (s < 0) {
-#endif
- error = GIT_EOSERR;
- goto cleanup;
+ net_set_error("error creating socket");
+ break;
}
- ret = connect(s, p->ai_addr, p->ai_addrlen);
- /* If we can't connect, try the next one */
- if (ret < 0) {
- continue;
- }
+ if (connect(s, p->ai_addr, (socklen_t)p->ai_addrlen) == 0)
+ break;
- /* Return the socket */
- error = s;
- goto cleanup;
+ /* If we can't connect, try the next one */
+ gitno_close(s);
+ s = INVALID_SOCKET;
}
/* Oops, we couldn't connect to any address */
- error = git__throw(GIT_EOSERR, "Failed to connect: %s", strerror(errno));
+ if (s == INVALID_SOCKET && p == NULL) {
+ giterr_set(GITERR_OS, "Failed to connect to %s", host);
+ return -1;
+ }
-cleanup:
freeaddrinfo(info);
- return error;
+ *sock = s;
+ return 0;
}
int gitno_send(GIT_SOCKET s, const char *msg, size_t len, int flags)
@@ -130,14 +142,16 @@ int gitno_send(GIT_SOCKET s, const char *msg, size_t len, int flags)
while (off < len) {
errno = 0;
- ret = send(s, msg + off, len - off, flags);
- if (ret < 0)
- return git__throw(GIT_EOSERR, "Error sending data: %s", strerror(errno));
+ ret = p_send(s, msg + off, len - off, flags);
+ if (ret < 0) {
+ net_set_error("Error sending data");
+ return -1;
+ }
off += ret;
}
- return off;
+ return (int)off;
}
@@ -165,35 +179,31 @@ int gitno_select_in(gitno_buffer *buf, long int sec, long int usec)
FD_SET(buf->fd, &fds);
/* The select(2) interface is silly */
- return select(buf->fd + 1, &fds, NULL, NULL, &tv);
+ return select((int)buf->fd + 1, &fds, NULL, NULL, &tv);
}
int gitno_extract_host_and_port(char **host, char **port, const char *url, const char *default_port)
{
char *colon, *slash, *delim;
- int error = GIT_SUCCESS;
colon = strchr(url, ':');
slash = strchr(url, '/');
- if (slash == NULL)
- return git__throw(GIT_EOBJCORRUPTED, "Malformed URL: missing /");
+ if (slash == NULL) {
+ giterr_set(GITERR_NET, "Malformed URL: missing /");
+ return -1;
+ }
if (colon == NULL) {
*port = git__strdup(default_port);
} else {
*port = git__strndup(colon + 1, slash - colon - 1);
}
- if (*port == NULL)
- return GIT_ENOMEM;;
-
+ GITERR_CHECK_ALLOC(*port);
delim = colon == NULL ? slash : colon;
*host = git__strndup(url, delim - url);
- if (*host == NULL) {
- git__free(*port);
- error = GIT_ENOMEM;
- }
+ GITERR_CHECK_ALLOC(*host);
- return error;
+ return 0;
}
diff --git a/src/netops.h b/src/netops.h
index f9a812747..9d13f3891 100644
--- a/src/netops.h
+++ b/src/netops.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -7,11 +7,7 @@
#ifndef INCLUDE_netops_h__
#define INCLUDE_netops_h__
-#ifndef GIT_WIN32
-typedef int GIT_SOCKET;
-#else
-typedef SOCKET GIT_SOCKET;
-#endif
+#include "posix.h"
typedef struct gitno_buffer {
char *data;
@@ -20,12 +16,12 @@ typedef struct gitno_buffer {
GIT_SOCKET fd;
} gitno_buffer;
-void gitno_buffer_setup(gitno_buffer *buf, char *data, unsigned int len, int fd);
+void gitno_buffer_setup(gitno_buffer *buf, char *data, unsigned int len, GIT_SOCKET fd);
int gitno_recv(gitno_buffer *buf);
void gitno_consume(gitno_buffer *buf, const char *ptr);
void gitno_consume_n(gitno_buffer *buf, size_t cons);
-int gitno_connect(const char *host, const char *port);
+int gitno_connect(GIT_SOCKET *s, const char *host, const char *port);
int gitno_send(GIT_SOCKET s, const char *msg, size_t len, int flags);
int gitno_close(GIT_SOCKET s);
int gitno_send_chunk_size(int s, size_t len);
diff --git a/src/notes.c b/src/notes.c
new file mode 100644
index 000000000..84ad94087
--- /dev/null
+++ b/src/notes.c
@@ -0,0 +1,548 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "notes.h"
+
+#include "git2.h"
+#include "refs.h"
+#include "config.h"
+#include "iterator.h"
+
+static int find_subtree(git_tree **subtree, const git_oid *root,
+ git_repository *repo, const char *target, int *fanout)
+{
+ int error;
+ unsigned int i;
+ git_tree *tree;
+ const git_tree_entry *entry;
+
+ *subtree = NULL;
+
+ error = git_tree_lookup(&tree, repo, root);
+ if (error < 0)
+ return error;
+
+ for (i=0; i<git_tree_entrycount(tree); i++) {
+ entry = git_tree_entry_byindex(tree, i);
+
+ if (!git__ishex(git_tree_entry_name(entry)))
+ continue;
+
+ /*
+ * A notes tree follows a strict byte-based progressive fanout
+ * (i.e. using 2/38, 2/2/36, etc. fanouts, not e.g. 4/36 fanout)
+ */
+
+ if (S_ISDIR(git_tree_entry_attributes(entry))
+ && strlen(git_tree_entry_name(entry)) == 2
+ && !strncmp(git_tree_entry_name(entry), target + *fanout, 2)) {
+
+ /* found matching subtree - unpack and resume lookup */
+
+ git_oid subtree_sha;
+ git_oid_cpy(&subtree_sha, git_tree_entry_id(entry));
+ git_tree_free(tree);
+
+ *fanout += 2;
+
+ return find_subtree(subtree, &subtree_sha, repo,
+ target, fanout);
+ }
+ }
+
+ *subtree = tree;
+ return 0;
+}
+
+static int find_blob(git_oid *blob, git_tree *tree, const char *target)
+{
+ unsigned int i;
+ const git_tree_entry *entry;
+
+ for (i=0; i<git_tree_entrycount(tree); i++) {
+ entry = git_tree_entry_byindex(tree, i);
+
+ if (!strcmp(git_tree_entry_name(entry), target)) {
+ /* found matching note object - return */
+
+ git_oid_cpy(blob, git_tree_entry_id(entry));
+ return 0;
+ }
+ }
+ return GIT_ENOTFOUND;
+}
+
+static int note_write(git_oid *out, git_repository *repo,
+ git_signature *author, git_signature *committer,
+ const char *notes_ref, const char *note,
+ const git_oid *tree_sha, const char *target,
+ int nparents, git_commit **parents)
+{
+ int error, fanout = 0;
+ git_oid oid;
+ git_tree *tree = NULL;
+ git_tree_entry *entry;
+ git_treebuilder *tb;
+
+ /* check for existing notes tree */
+
+ if (tree_sha) {
+ error = find_subtree(&tree, tree_sha, repo, target, &fanout);
+ if (error < 0)
+ return error;
+
+ error = find_blob(&oid, tree, target + fanout);
+ if (error != GIT_ENOTFOUND) {
+ git_tree_free(tree);
+ if (!error) {
+ giterr_set(GITERR_REPOSITORY, "Note for '%s' exists already", target);
+ error = GIT_EEXISTS;
+ }
+ return error;
+ }
+ }
+
+ /* no matching tree entry - add note object to target tree */
+
+ error = git_treebuilder_create(&tb, tree);
+ git_tree_free(tree);
+
+ if (error < 0)
+ return error;
+
+ if (!tree_sha)
+ /* no notes tree yet - create fanout */
+ fanout += 2;
+
+ /* create note object */
+ error = git_blob_create_frombuffer(&oid, repo, note, strlen(note));
+ if (error < 0) {
+ git_treebuilder_free(tb);
+ return error;
+ }
+
+ error = git_treebuilder_insert(&entry, tb, target + fanout, &oid, 0100644);
+ if (error < 0) {
+ /* libgit2 doesn't support object removal (gc) yet */
+ /* we leave an orphaned blob object behind - TODO */
+
+ git_treebuilder_free(tb);
+ return error;
+ }
+
+ if (out)
+ git_oid_cpy(out, git_tree_entry_id(entry));
+
+ error = git_treebuilder_write(&oid, repo, tb);
+ git_treebuilder_free(tb);
+
+ if (error < 0)
+ return 0;
+
+ if (!tree_sha) {
+ /* create fanout subtree */
+
+ char subtree[3];
+ strncpy(subtree, target, 2);
+ subtree[2] = '\0';
+
+ error = git_treebuilder_create(&tb, NULL);
+ if (error < 0)
+ return error;
+
+ error = git_treebuilder_insert(NULL, tb, subtree, &oid, 0040000);
+ if (error < 0) {
+ git_treebuilder_free(tb);
+ return error;
+ }
+
+ error = git_treebuilder_write(&oid, repo, tb);
+
+ git_treebuilder_free(tb);
+
+ if (error < 0)
+ return error;
+ }
+
+ /* create new notes commit */
+
+ error = git_tree_lookup(&tree, repo, &oid);
+ if (error < 0)
+ return error;
+
+ error = git_commit_create(&oid, repo, notes_ref, author, committer,
+ NULL, GIT_NOTES_DEFAULT_MSG_ADD,
+ tree, nparents, (const git_commit **) parents);
+
+ git_tree_free(tree);
+
+ return error;
+}
+
+static int note_lookup(git_note **out, git_repository *repo,
+ const git_oid *tree_sha, const char *target)
+{
+ int error, fanout = 0;
+ git_oid oid;
+ git_blob *blob;
+ git_tree *tree;
+ git_note *note;
+
+ error = find_subtree(&tree, tree_sha, repo, target, &fanout);
+ if (error < 0)
+ return error;
+
+ error = find_blob(&oid, tree, target + fanout);
+
+ git_tree_free(tree);
+ if (error < 0)
+ return error;
+
+ error = git_blob_lookup(&blob, repo, &oid);
+ if (error < 0)
+ return error;
+
+ note = git__malloc(sizeof(git_note));
+ GITERR_CHECK_ALLOC(note);
+
+ git_oid_cpy(&note->oid, &oid);
+ note->message = git__strdup(git_blob_rawcontent(blob));
+ GITERR_CHECK_ALLOC(note->message);
+
+ *out = note;
+
+ git_blob_free(blob);
+ return error;
+}
+
+static int note_remove(git_repository *repo,
+ git_signature *author, git_signature *committer,
+ const char *notes_ref, const git_oid *tree_sha,
+ const char *target, int nparents, git_commit **parents)
+{
+ int error, fanout = 0;
+ git_oid oid;
+ git_tree *tree;
+ git_treebuilder *tb;
+
+ error = find_subtree(&tree, tree_sha, repo, target, &fanout);
+ if (error < 0)
+ return error;
+
+ error = find_blob(&oid, tree, target + fanout);
+ if (!error)
+ error = git_treebuilder_create(&tb, tree);
+
+ git_tree_free(tree);
+ if (error < 0)
+ return error;
+
+ error = git_treebuilder_remove(tb, target + fanout);
+ if (!error)
+ error = git_treebuilder_write(&oid, repo, tb);
+
+ git_treebuilder_free(tb);
+ if (error < 0)
+ return error;
+
+ /* create new notes commit */
+
+ error = git_tree_lookup(&tree, repo, &oid);
+ if (error < 0)
+ return error;
+
+ error = git_commit_create(&oid, repo, notes_ref, author, committer,
+ NULL, GIT_NOTES_DEFAULT_MSG_RM,
+ tree, nparents, (const git_commit **) parents);
+
+ git_tree_free(tree);
+
+ return error;
+}
+
+static int note_get_default_ref(const char **out, git_repository *repo)
+{
+ int ret;
+ git_config *cfg;
+
+ *out = NULL;
+
+ if (git_repository_config__weakptr(&cfg, repo) < 0)
+ return -1;
+
+ ret = git_config_get_string(out, cfg, "core.notesRef");
+ if (ret == GIT_ENOTFOUND) {
+ *out = GIT_NOTES_DEFAULT_REF;
+ return 0;
+ }
+
+ return ret;
+}
+
+static int normalize_namespace(const char **notes_ref, git_repository *repo)
+{
+ if (*notes_ref)
+ return 0;
+
+ return note_get_default_ref(notes_ref, repo);
+}
+
+static int retrieve_note_tree_oid(git_oid *tree_oid_out, git_repository *repo, const char *notes_ref)
+{
+ int error = -1;
+ git_commit *commit = NULL;
+ git_oid oid;
+
+ if ((error = git_reference_name_to_oid(&oid, repo, notes_ref)) < 0)
+ goto cleanup;
+
+ if (git_commit_lookup(&commit, repo, &oid) < 0)
+ goto cleanup;
+
+ git_oid_cpy(tree_oid_out, git_commit_tree_oid(commit));
+
+ error = 0;
+
+cleanup:
+ git_commit_free(commit);
+ return error;
+}
+
+int git_note_read(git_note **out, git_repository *repo,
+ const char *notes_ref, const git_oid *oid)
+{
+ int error;
+ char *target;
+ git_oid sha;
+
+ *out = NULL;
+
+ if (normalize_namespace(&notes_ref, repo) < 0)
+ return -1;
+
+ if ((error = retrieve_note_tree_oid(&sha, repo, notes_ref)) < 0)
+ return error;
+
+ target = git_oid_allocfmt(oid);
+ GITERR_CHECK_ALLOC(target);
+
+ error = note_lookup(out, repo, &sha, target);
+
+ git__free(target);
+ return error;
+}
+
+int git_note_create(
+ git_oid *out, git_repository *repo,
+ git_signature *author, git_signature *committer,
+ const char *notes_ref, const git_oid *oid,
+ const char *note)
+{
+ int error, nparents = 0;
+ char *target;
+ git_oid sha;
+ git_commit *commit = NULL;
+ git_reference *ref;
+
+ if (normalize_namespace(&notes_ref, repo) < 0)
+ return -1;
+
+ error = git_reference_lookup(&ref, repo, notes_ref);
+ if (error < 0 && error != GIT_ENOTFOUND)
+ return error;
+
+ if (!error) {
+ assert(git_reference_type(ref) == GIT_REF_OID);
+
+ /* lookup existing notes tree oid */
+
+ git_oid_cpy(&sha, git_reference_oid(ref));
+ git_reference_free(ref);
+
+ error = git_commit_lookup(&commit, repo, &sha);
+ if (error < 0)
+ return error;
+
+ git_oid_cpy(&sha, git_commit_tree_oid(commit));
+ nparents++;
+ }
+
+ target = git_oid_allocfmt(oid);
+ GITERR_CHECK_ALLOC(target);
+
+ error = note_write(out, repo, author, committer, notes_ref,
+ note, nparents ? &sha : NULL, target,
+ nparents, &commit);
+
+ git__free(target);
+ git_commit_free(commit);
+ return error;
+}
+
+int git_note_remove(git_repository *repo, const char *notes_ref,
+ git_signature *author, git_signature *committer,
+ const git_oid *oid)
+{
+ int error;
+ char *target;
+ git_oid sha;
+ git_commit *commit;
+ git_reference *ref;
+
+ if (normalize_namespace(&notes_ref, repo) < 0)
+ return -1;
+
+ error = git_reference_lookup(&ref, repo, notes_ref);
+ if (error < 0)
+ return error;
+
+ assert(git_reference_type(ref) == GIT_REF_OID);
+
+ git_oid_cpy(&sha, git_reference_oid(ref));
+ git_reference_free(ref);
+
+ error = git_commit_lookup(&commit, repo, &sha);
+ if (error < 0)
+ return error;
+
+ git_oid_cpy(&sha, git_commit_tree_oid(commit));
+
+ target = git_oid_allocfmt(oid);
+ GITERR_CHECK_ALLOC(target);
+
+ error = note_remove(repo, author, committer, notes_ref,
+ &sha, target, 1, &commit);
+
+ git__free(target);
+ git_commit_free(commit);
+ return error;
+}
+
+int git_note_default_ref(const char **out, git_repository *repo)
+{
+ assert(repo);
+ return note_get_default_ref(out, repo);
+}
+
+const char * git_note_message(git_note *note)
+{
+ assert(note);
+ return note->message;
+}
+
+const git_oid * git_note_oid(git_note *note)
+{
+ assert(note);
+ return &note->oid;
+}
+
+void git_note_free(git_note *note)
+{
+ if (note == NULL)
+ return;
+
+ git__free(note->message);
+ git__free(note);
+}
+
+static int process_entry_path(
+ const char* entry_path,
+ const git_oid *note_oid,
+ int (*note_cb)(git_note_data *note_data, void *payload),
+ void *payload)
+{
+ int i = 0, j = 0, error = -1, len;
+ git_buf buf = GIT_BUF_INIT;
+ git_note_data note_data;
+
+ if (git_buf_puts(&buf, entry_path) < 0)
+ goto cleanup;
+
+ len = git_buf_len(&buf);
+
+ while (i < len) {
+ if (buf.ptr[i] == '/') {
+ i++;
+ continue;
+ }
+
+ if (git__fromhex(buf.ptr[i]) < 0) {
+ /* This is not a note entry */
+ error = 0;
+ goto cleanup;
+ }
+
+ if (i != j)
+ buf.ptr[j] = buf.ptr[i];
+
+ i++;
+ j++;
+ }
+
+ buf.ptr[j] = '\0';
+ buf.size = j;
+
+ if (j != GIT_OID_HEXSZ) {
+ /* This is not a note entry */
+ error = 0;
+ goto cleanup;
+ }
+
+ if (git_oid_fromstr(&note_data.annotated_object_oid, buf.ptr) < 0)
+ return -1;
+
+ git_oid_cpy(&note_data.blob_oid, note_oid);
+
+ error = note_cb(&note_data, payload);
+
+cleanup:
+ git_buf_free(&buf);
+ return error;
+}
+
+int git_note_foreach(
+ git_repository *repo,
+ const char *notes_ref,
+ int (*note_cb)(git_note_data *note_data, void *payload),
+ void *payload)
+{
+ int error = -1;
+ git_oid tree_oid;
+ git_iterator *iter = NULL;
+ git_tree *tree = NULL;
+ const git_index_entry *item;
+
+ if (normalize_namespace(&notes_ref, repo) < 0)
+ return -1;
+
+ if ((error = retrieve_note_tree_oid(&tree_oid, repo, notes_ref)) < 0)
+ goto cleanup;
+
+ if (git_tree_lookup(&tree, repo, &tree_oid) < 0)
+ goto cleanup;
+
+ if (git_iterator_for_tree(&iter, repo, tree) < 0)
+ goto cleanup;
+
+ if (git_iterator_current(iter, &item) < 0)
+ goto cleanup;
+
+ while (item) {
+ if (process_entry_path(item->path, &item->oid, note_cb, payload) < 0)
+ goto cleanup;
+
+ if (git_iterator_advance(iter, &item) < 0)
+ goto cleanup;
+ }
+
+ error = 0;
+
+cleanup:
+ git_iterator_free(iter);
+ git_tree_free(tree);
+ return error;
+}
diff --git a/src/notes.h b/src/notes.h
new file mode 100644
index 000000000..219db1ab0
--- /dev/null
+++ b/src/notes.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2009-2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_note_h__
+#define INCLUDE_note_h__
+
+#include "common.h"
+
+#include "git2/oid.h"
+
+#define GIT_NOTES_DEFAULT_REF "refs/notes/commits"
+
+#define GIT_NOTES_DEFAULT_MSG_ADD \
+ "Notes added by 'git_note_create' from libgit2"
+
+#define GIT_NOTES_DEFAULT_MSG_RM \
+ "Notes removed by 'git_note_remove' from libgit2"
+
+struct git_note {
+ git_oid oid;
+
+ char *message;
+};
+
+#endif /* INCLUDE_notes_h__ */
diff --git a/src/object.c b/src/object.c
index 95c7cf9d2..d3673eda0 100644
--- a/src/object.c
+++ b/src/object.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -62,19 +62,19 @@ static int create_object(git_object **object_out, git_otype type)
case GIT_OBJ_BLOB:
case GIT_OBJ_TREE:
object = git__malloc(git_object__size(type));
- if (object == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(object);
memset(object, 0x0, git_object__size(type));
break;
default:
- return git__throw(GIT_EINVALIDTYPE, "The given type is invalid");
+ giterr_set(GITERR_INVALID, "The given type is invalid");
+ return -1;
}
object->type = type;
*object_out = object;
- return GIT_SUCCESS;
+ return 0;
}
int git_object_lookup_prefix(
@@ -87,16 +87,15 @@ int git_object_lookup_prefix(
git_object *object = NULL;
git_odb *odb = NULL;
git_odb_object *odb_obj;
- int error = GIT_SUCCESS;
+ int error = 0;
assert(repo && object_out && id);
if (len < GIT_OID_MINPREFIXLEN)
- return git__throw(GIT_EAMBIGUOUSOIDPREFIX,
- "Failed to lookup object. Prefix length is lower than %d.", GIT_OID_MINPREFIXLEN);
+ return GIT_EAMBIGUOUS;
error = git_repository_odb__weakptr(&odb, repo);
- if (error < GIT_SUCCESS)
+ if (error < 0)
return error;
if (len > GIT_OID_HEXSZ)
@@ -110,13 +109,12 @@ int git_object_lookup_prefix(
if (object != NULL) {
if (type != GIT_OBJ_ANY && type != object->type) {
git_object_free(object);
- return git__throw(GIT_EINVALIDTYPE,
- "Failed to lookup object. "
- "The given type does not match the type on the ODB");
+ giterr_set(GITERR_ODB, "The given type does not match the type in ODB");
+ return GIT_ENOTFOUND;
}
*object_out = object;
- return GIT_SUCCESS;
+ return 0;
}
/* Object was not found in the cache, let's explore the backends.
@@ -147,18 +145,19 @@ int git_object_lookup_prefix(
error = git_odb_read_prefix(&odb_obj, odb, &short_oid, len);
}
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to lookup object");
+ if (error < 0)
+ return error;
if (type != GIT_OBJ_ANY && type != odb_obj->raw.type) {
git_odb_object_free(odb_obj);
- return git__throw(GIT_EINVALIDTYPE, "Failed to lookup object. The given type does not match the type on the ODB");
+ giterr_set(GITERR_ODB, "The given type does not match the type on the ODB");
+ return GIT_ENOTFOUND;
}
type = odb_obj->raw.type;
- if ((error = create_object(&object, type)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to lookup object");
+ if (create_object(&object, type) < 0)
+ return -1;
/* Initialize parent object */
git_oid_cpy(&object->cached.oid, &odb_obj->cached.oid);
@@ -187,13 +186,13 @@ int git_object_lookup_prefix(
git_odb_object_free(odb_obj);
- if (error < GIT_SUCCESS) {
+ if (error < 0) {
git_object__free(object);
- return git__rethrow(error, "Failed to lookup object");
+ return -1;
}
*object_out = git_cache_try_store(&repo->objects, object);
- return GIT_SUCCESS;
+ return 0;
}
int git_object_lookup(git_object **object_out, git_repository *repo, const git_oid *id, git_otype type) {
@@ -293,3 +292,42 @@ size_t git_object__size(git_otype type)
return git_objects_table[type].size;
}
+int git_object__resolve_to_type(git_object **obj, git_otype type)
+{
+ int error = 0;
+ git_object *scan, *next;
+
+ if (type == GIT_OBJ_ANY)
+ return 0;
+
+ scan = *obj;
+
+ while (!error && scan && git_object_type(scan) != type) {
+
+ switch (git_object_type(scan)) {
+ case GIT_OBJ_COMMIT:
+ {
+ git_tree *tree = NULL;
+ error = git_commit_tree(&tree, (git_commit *)scan);
+ next = (git_object *)tree;
+ break;
+ }
+
+ case GIT_OBJ_TAG:
+ error = git_tag_target(&next, (git_tag *)scan);
+ break;
+
+ default:
+ giterr_set(GITERR_REFERENCE, "Object does not resolve to type");
+ error = -1;
+ next = NULL;
+ break;
+ }
+
+ git_object_free(scan);
+ scan = next;
+ }
+
+ *obj = scan;
+ return error;
+}
diff --git a/src/odb.c b/src/odb.c
index 8905c2237..a6a18f831 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -1,12 +1,12 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include "common.h"
-#include "git2/zlib.h"
+#include <zlib.h>
#include "git2/object.h"
#include "fileops.h"
#include "hash.h"
@@ -32,14 +32,11 @@ static int format_object_header(char *hdr, size_t n, size_t obj_len, git_otype o
{
const char *type_str = git_object_type2string(obj_type);
int len = p_snprintf(hdr, n, "%s %"PRIuZ, type_str, obj_len);
-
- if (len < 0 || ((size_t) len) >= n)
- return git__throw(GIT_ERROR, "Cannot format object header. Length is out of bounds");
-
+ assert(len > 0 && len <= (int)n);
return len+1;
}
-int git_odb__hash_obj(git_oid *id, git_rawobj *obj)
+int git_odb__hashobj(git_oid *id, git_rawobj *obj)
{
git_buf_vec vec[2];
char header[64];
@@ -48,13 +45,11 @@ int git_odb__hash_obj(git_oid *id, git_rawobj *obj)
assert(id && obj);
if (!git_object_typeisloose(obj->type))
- return git__throw(GIT_ERROR, "Failed to hash object. Wrong object type");
-
+ return -1;
if (!obj->data && obj->len != 0)
- return git__throw(GIT_ERROR, "Failed to hash object. No data given");
+ return -1;
- if ((hdrlen = format_object_header(header, sizeof(header), obj->len, obj->type)) < 0)
- return git__rethrow(hdrlen, "Failed to hash object");
+ hdrlen = format_object_header(header, sizeof(header), obj->len, obj->type);
vec[0].data = header;
vec[0].len = hdrlen;
@@ -63,7 +58,7 @@ int git_odb__hash_obj(git_oid *id, git_rawobj *obj)
git_hash_vec(id, vec, 2);
- return GIT_SUCCESS;
+ return 0;
}
@@ -113,50 +108,95 @@ void git_odb_object_free(git_odb_object *object)
git_cached_obj_decref((git_cached_obj *)object, &free_odb_object);
}
-int git_odb_hashfile(git_oid *out, const char *path, git_otype type)
+int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type)
{
- int fd, hdr_len;
+ int hdr_len;
char hdr[64], buffer[2048];
- git_off_t size;
git_hash_ctx *ctx;
- if ((fd = p_open(path, O_RDONLY)) < 0)
- return git__throw(GIT_ENOTFOUND, "Could not open '%s'", path);
-
- if ((size = git_futils_filesize(fd)) < 0 || !git__is_sizet(size)) {
- p_close(fd);
- return git__throw(GIT_EOSERR, "'%s' appears to be corrupted", path);
- }
-
- hdr_len = format_object_header(hdr, sizeof(hdr), (size_t)size, type);
- if (hdr_len < 0)
- return git__throw(GIT_ERROR, "Failed to format blob header. Length is out of bounds");
+ hdr_len = format_object_header(hdr, sizeof(hdr), size, type);
ctx = git_hash_new_ctx();
git_hash_update(ctx, hdr, hdr_len);
while (size > 0) {
- ssize_t read_len;
-
- read_len = read(fd, buffer, sizeof(buffer));
+ ssize_t read_len = read(fd, buffer, sizeof(buffer));
if (read_len < 0) {
- p_close(fd);
git_hash_free_ctx(ctx);
- return git__throw(GIT_EOSERR, "Can't read full file '%s'", path);
+ giterr_set(GITERR_OS, "Error reading file");
+ return -1;
}
git_hash_update(ctx, buffer, read_len);
size -= read_len;
}
- p_close(fd);
-
git_hash_final(out, ctx);
git_hash_free_ctx(ctx);
- return GIT_SUCCESS;
+ return 0;
+}
+
+int git_odb__hashlink(git_oid *out, const char *path)
+{
+ struct stat st;
+ git_off_t size;
+ int result;
+
+ if (git_path_lstat(path, &st) < 0)
+ return -1;
+
+ size = st.st_size;
+
+ if (!git__is_sizet(size)) {
+ giterr_set(GITERR_OS, "File size overflow for 32-bit systems");
+ return -1;
+ }
+
+ if (S_ISLNK(st.st_mode)) {
+ char *link_data;
+ ssize_t read_len;
+
+ link_data = git__malloc((size_t)size);
+ GITERR_CHECK_ALLOC(link_data);
+
+ read_len = p_readlink(path, link_data, (size_t)(size + 1));
+ if (read_len != (ssize_t)size) {
+ giterr_set(GITERR_OS, "Failed to read symlink data for '%s'", path);
+ return -1;
+ }
+
+ result = git_odb_hash(out, link_data, (size_t)size, GIT_OBJ_BLOB);
+ git__free(link_data);
+ } else {
+ int fd = git_futils_open_ro(path);
+ if (fd < 0)
+ return -1;
+ result = git_odb__hashfd(out, fd, (size_t)size, GIT_OBJ_BLOB);
+ p_close(fd);
+ }
+
+ return result;
+}
+
+int git_odb_hashfile(git_oid *out, const char *path, git_otype type)
+{
+ git_off_t size;
+ int result, fd = git_futils_open_ro(path);
+ if (fd < 0)
+ return fd;
+
+ if ((size = git_futils_filesize(fd)) < 0 || !git__is_sizet(size)) {
+ giterr_set(GITERR_OS, "File size overflow for 32-bit systems");
+ p_close(fd);
+ return -1;
+ }
+
+ result = git_odb__hashfd(out, fd, (size_t)size, type);
+ p_close(fd);
+ return result;
}
int git_odb_hash(git_oid *id, const void *data, size_t len, git_otype type)
@@ -169,7 +209,7 @@ int git_odb_hash(git_oid *id, const void *data, size_t len, git_otype type)
raw.len = len;
raw.type = type;
- return git_odb__hash_obj(id, &raw);
+ return git_odb__hashobj(id, &raw);
}
/**
@@ -194,11 +234,11 @@ static int fake_wstream__write(git_odb_stream *_stream, const char *data, size_t
fake_wstream *stream = (fake_wstream *)_stream;
if (stream->written + len > stream->size)
- return GIT_ENOMEM;
+ return -1;
memcpy(stream->buffer + stream->written, data, len);
stream->written += len;
- return GIT_SUCCESS;
+ return 0;
}
static void fake_wstream__free(git_odb_stream *_stream)
@@ -214,15 +254,14 @@ static int init_fake_wstream(git_odb_stream **stream_p, git_odb_backend *backend
fake_wstream *stream;
stream = git__calloc(1, sizeof(fake_wstream));
- if (stream == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(stream);
stream->size = size;
stream->type = type;
stream->buffer = git__malloc(size);
if (stream->buffer == NULL) {
git__free(stream);
- return GIT_ENOMEM;
+ return -1;
}
stream->stream.backend = backend;
@@ -233,7 +272,7 @@ static int init_fake_wstream(git_odb_stream **stream_p, git_odb_backend *backend
stream->stream.mode = GIT_STREAM_WRONLY;
*stream_p = (git_odb_stream *)stream;
- return GIT_SUCCESS;
+ return 0;
}
/***********************************************************
@@ -257,26 +296,19 @@ static int backend_sort_cmp(const void *a, const void *b)
int git_odb_new(git_odb **out)
{
- int error;
-
git_odb *db = git__calloc(1, sizeof(*db));
- if (!db)
- return GIT_ENOMEM;
-
- error = git_cache_init(&db->cache, GIT_DEFAULT_CACHE_SIZE, &free_odb_object);
- if (error < GIT_SUCCESS) {
- git__free(db);
- return git__rethrow(error, "Failed to create object database");
- }
+ GITERR_CHECK_ALLOC(db);
- if ((error = git_vector_init(&db->backends, 4, backend_sort_cmp)) < GIT_SUCCESS) {
+ if (git_cache_init(&db->cache, GIT_DEFAULT_CACHE_SIZE, &free_odb_object) < 0 ||
+ git_vector_init(&db->backends, 4, backend_sort_cmp) < 0)
+ {
git__free(db);
- return git__rethrow(error, "Failed to create object database");
+ return -1;
}
*out = db;
GIT_REFCOUNT_INC(db);
- return GIT_SUCCESS;
+ return 0;
}
static int add_backend_internal(git_odb *odb, git_odb_backend *backend, int priority, int is_alternate)
@@ -285,12 +317,11 @@ static int add_backend_internal(git_odb *odb, git_odb_backend *backend, int prio
assert(odb && backend);
- if (backend->odb != NULL && backend->odb != odb)
- return git__throw(GIT_EBUSY, "The backend is already owned by another ODB");
+ /* Check if the backend is already owned by another ODB */
+ assert(!backend->odb || backend->odb == odb);
internal = git__malloc(sizeof(backend_internal));
- if (internal == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(internal);
internal->backend = backend;
internal->priority = priority;
@@ -298,12 +329,12 @@ static int add_backend_internal(git_odb *odb, git_odb_backend *backend, int prio
if (git_vector_insert(&odb->backends, internal) < 0) {
git__free(internal);
- return GIT_ENOMEM;
+ return -1;
}
git_vector_sort(&odb->backends);
internal->backend->odb = odb;
- return GIT_SUCCESS;
+ return 0;
}
int git_odb_add_backend(git_odb *odb, git_odb_backend *backend, int priority)
@@ -319,53 +350,42 @@ int git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, int priority)
static int add_default_backends(git_odb *db, const char *objects_dir, int as_alternates)
{
git_odb_backend *loose, *packed;
- int error;
/* add the loose object backend */
- error = git_odb_backend_loose(&loose, objects_dir, -1, 0);
- if (error < GIT_SUCCESS)
- return error;
-
- error = add_backend_internal(db, loose, GIT_LOOSE_PRIORITY, as_alternates);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to add backend");
+ if (git_odb_backend_loose(&loose, objects_dir, -1, 0) < 0 ||
+ add_backend_internal(db, loose, GIT_LOOSE_PRIORITY, as_alternates) < 0)
+ return -1;
/* add the packed file backend */
- error = git_odb_backend_pack(&packed, objects_dir);
- if (error < GIT_SUCCESS)
- return error;
-
- error = add_backend_internal(db, packed, GIT_PACKED_PRIORITY, as_alternates);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to add backend");
+ if (git_odb_backend_pack(&packed, objects_dir) < 0 ||
+ add_backend_internal(db, packed, GIT_PACKED_PRIORITY, as_alternates) < 0)
+ return -1;
- return GIT_SUCCESS;
+ return 0;
}
static int load_alternates(git_odb *odb, const char *objects_dir)
{
git_buf alternates_path = GIT_BUF_INIT;
+ git_buf alternates_buf = GIT_BUF_INIT;
char *buffer;
- git_fbuffer alternates_buf = GIT_FBUFFER_INIT;
const char *alternate;
- int error;
+ int result = 0;
- error = git_buf_joinpath(&alternates_path, objects_dir, GIT_ALTERNATES_FILE);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_buf_joinpath(&alternates_path, objects_dir, GIT_ALTERNATES_FILE) < 0)
+ return -1;
- if (git_path_exists(alternates_path.ptr) < GIT_SUCCESS) {
+ if (git_path_exists(alternates_path.ptr) == false) {
git_buf_free(&alternates_path);
- return GIT_SUCCESS;
+ return 0;
}
- if (git_futils_readbuffer(&alternates_buf, alternates_path.ptr) < GIT_SUCCESS) {
+ if (git_futils_readbuffer(&alternates_buf, alternates_path.ptr) < 0) {
git_buf_free(&alternates_path);
- return git__throw(GIT_EOSERR, "Failed to add backend. Can't read alternates");
+ return -1;
}
- buffer = (char *)alternates_buf.data;
- error = GIT_SUCCESS;
+ buffer = (char *)alternates_buf.ptr;
/* add each alternate as a new backend; one alternate per line */
while ((alternate = git__strtok(&buffer, "\r\n")) != NULL) {
@@ -374,47 +394,41 @@ static int load_alternates(git_odb *odb, const char *objects_dir)
/* relative path: build based on the current `objects` folder */
if (*alternate == '.') {
- error = git_buf_joinpath(&alternates_path, objects_dir, alternate);
- if (error < GIT_SUCCESS)
+ if ((result = git_buf_joinpath(&alternates_path, objects_dir, alternate)) < 0)
break;
alternate = git_buf_cstr(&alternates_path);
}
- if ((error = add_default_backends(odb, alternate, 1)) < GIT_SUCCESS)
+ if ((result = add_default_backends(odb, alternate, 1)) < 0)
break;
}
git_buf_free(&alternates_path);
- git_futils_freebuffer(&alternates_buf);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to load alternates");
- return error;
+ git_buf_free(&alternates_buf);
+
+ return result;
}
int git_odb_open(git_odb **out, const char *objects_dir)
{
git_odb *db;
- int error;
assert(out && objects_dir);
*out = NULL;
- if ((error = git_odb_new(&db)) < 0)
- return git__rethrow(error, "Failed to open ODB");
+ if (git_odb_new(&db) < 0)
+ return -1;
- if ((error = add_default_backends(db, objects_dir, 0)) < GIT_SUCCESS)
- goto cleanup;
-
- if ((error = load_alternates(db, objects_dir)) < GIT_SUCCESS)
- goto cleanup;
+ if (add_default_backends(db, objects_dir, 0) < 0 ||
+ load_alternates(db, objects_dir) < 0)
+ {
+ git_odb_free(db);
+ return -1;
+ }
*out = db;
- return GIT_SUCCESS;
-
-cleanup:
- git_odb_free(db);
- return error; /* error already set - pass through */
+ return 0;
}
static void odb_free(git_odb *db)
@@ -448,13 +462,13 @@ int git_odb_exists(git_odb *db, const git_oid *id)
{
git_odb_object *object;
unsigned int i;
- int found = 0;
+ bool found = false;
assert(db && id);
if ((object = git_cache_get(&db->cache, id)) != NULL) {
git_odb_object_free(object);
- return 1;
+ return (int)true;
}
for (i = 0; i < db->backends.length && !found; ++i) {
@@ -465,7 +479,7 @@ int git_odb_exists(git_odb *db, const git_oid *id)
found = b->exists(b, id);
}
- return found;
+ return (int)found;
}
int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id)
@@ -480,7 +494,7 @@ int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git
*len_p = object->raw.len;
*type_p = object->raw.type;
git_odb_object_free(object);
- return GIT_SUCCESS;
+ return 0;
}
for (i = 0; i < db->backends.length && error < 0; ++i) {
@@ -491,23 +505,20 @@ int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git
error = b->read_header(len_p, type_p, b, id);
}
- if (error == GIT_EPASSTHROUGH)
- return GIT_SUCCESS;
+ if (!error || error == GIT_PASSTHROUGH)
+ return 0;
/*
* no backend could read only the header.
* try reading the whole object and freeing the contents
*/
- if (error < 0) {
- if ((error = git_odb_read(&object, db, id)) < GIT_SUCCESS)
- return error; /* error already set - pass through */
-
- *len_p = object->raw.len;
- *type_p = object->raw.type;
- git_odb_object_free(object);
- }
+ if ((error = git_odb_read(&object, db, id)) < 0)
+ return error; /* error already set - pass along */
- return GIT_SUCCESS;
+ *len_p = object->raw.len;
+ *type_p = object->raw.type;
+ git_odb_object_free(object);
+ return 0;
}
int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
@@ -520,7 +531,7 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
*out = git_cache_get(&db->cache, id);
if (*out != NULL)
- return GIT_SUCCESS;
+ return 0;
for (i = 0; i < db->backends.length && error < 0; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
@@ -530,26 +541,30 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
error = b->read(&raw.data, &raw.len, &raw.type, b, id);
}
- if (error == GIT_EPASSTHROUGH || error == GIT_SUCCESS) {
- *out = git_cache_try_store(&db->cache, new_odb_object(id, &raw));
- return GIT_SUCCESS;
- }
+ /* TODO: If no backends are configured, this returns GIT_ENOTFOUND but
+ * will never have called giterr_set().
+ */
- return git__rethrow(error, "Failed to read object");
+ if (error && error != GIT_PASSTHROUGH)
+ return error;
+
+ *out = git_cache_try_store(&db->cache, new_odb_object(id, &raw));
+ return 0;
}
-int git_odb_read_prefix(git_odb_object **out, git_odb *db, const git_oid *short_id, unsigned int len)
+int git_odb_read_prefix(
+ git_odb_object **out, git_odb *db, const git_oid *short_id, unsigned int len)
{
unsigned int i;
int error = GIT_ENOTFOUND;
- git_oid full_oid;
+ git_oid found_full_oid = {{0}};
git_rawobj raw;
- int found = 0;
+ bool found = false;
assert(out && db);
if (len < GIT_OID_MINPREFIXLEN)
- return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Failed to lookup object. Prefix length is lower than %d.", GIT_OID_MINPREFIXLEN);
+ return git_odb__error_ambiguous("prefix length too short");
if (len > GIT_OID_HEXSZ)
len = GIT_OID_HEXSZ;
@@ -557,42 +572,38 @@ int git_odb_read_prefix(git_odb_object **out, git_odb *db, const git_oid *short_
if (len == GIT_OID_HEXSZ) {
*out = git_cache_get(&db->cache, short_id);
if (*out != NULL)
- return GIT_SUCCESS;
+ return 0;
}
- for (i = 0; i < db->backends.length && found < 2; ++i) {
+ for (i = 0; i < db->backends.length; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
if (b->read != NULL) {
+ git_oid full_oid;
error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, short_id, len);
- switch (error) {
- case GIT_SUCCESS:
- found++;
- break;
- case GIT_ENOTFOUND:
- case GIT_EPASSTHROUGH:
- break;
- case GIT_EAMBIGUOUSOIDPREFIX:
- return git__rethrow(error, "Failed to read object. Ambiguous sha1 prefix");
- default:
- return git__rethrow(error, "Failed to read object");
- }
+ if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH)
+ continue;
+
+ if (error)
+ return error;
+
+ if (found && git_oid_cmp(&full_oid, &found_full_oid))
+ return git_odb__error_ambiguous("multiple matches for prefix");
+ found_full_oid = full_oid;
+ found = true;
}
}
- if (found == 1) {
- *out = git_cache_try_store(&db->cache, new_odb_object(&full_oid, &raw));
- } else if (found > 1) {
- return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Failed to read object. Ambiguous sha1 prefix");
- } else {
- return git__throw(GIT_ENOTFOUND, "Failed to read object. Object not found");
- }
+ if (!found)
+ return git_odb__error_notfound("no match for prefix", short_id);
- return GIT_SUCCESS;
+ *out = git_cache_try_store(&db->cache, new_odb_object(&found_full_oid, &raw));
+ return 0;
}
-int git_odb_write(git_oid *oid, git_odb *db, const void *data, size_t len, git_otype type)
+int git_odb_write(
+ git_oid *oid, git_odb *db, const void *data, size_t len, git_otype type)
{
unsigned int i;
int error = GIT_ERROR;
@@ -612,24 +623,25 @@ int git_odb_write(git_oid *oid, git_odb *db, const void *data, size_t len, git_o
error = b->write(oid, b, data, len, type);
}
- if (error == GIT_EPASSTHROUGH || error == GIT_SUCCESS)
- return GIT_SUCCESS;
+ if (!error || error == GIT_PASSTHROUGH)
+ return 0;
/* if no backends were able to write the object directly, we try a streaming
* write to the backends; just write the whole object into the stream in one
* push */
- if ((error = git_odb_open_wstream(&stream, db, len, type)) == GIT_SUCCESS) {
- stream->write(stream, data, len);
- error = stream->finalize_write(oid, stream);
- stream->free(stream);
- return GIT_SUCCESS;
- }
+ if ((error = git_odb_open_wstream(&stream, db, len, type)) != 0)
+ return error;
+
+ stream->write(stream, data, len);
+ error = stream->finalize_write(oid, stream);
+ stream->free(stream);
- return git__rethrow(error, "Failed to write object");
+ return error;
}
-int git_odb_open_wstream(git_odb_stream **stream, git_odb *db, size_t size, git_otype type)
+int git_odb_open_wstream(
+ git_odb_stream **stream, git_odb *db, size_t size, git_otype type)
{
unsigned int i;
int error = GIT_ERROR;
@@ -650,10 +662,10 @@ int git_odb_open_wstream(git_odb_stream **stream, git_odb *db, size_t size, git_
error = init_fake_wstream(stream, b, size, type);
}
- if (error == GIT_EPASSTHROUGH || error == GIT_SUCCESS)
- return GIT_SUCCESS;
+ if (error == GIT_PASSTHROUGH)
+ error = 0;
- return git__rethrow(error, "Failed to open write stream");
+ return error;
}
int git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oid)
@@ -671,9 +683,27 @@ int git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oi
error = b->readstream(stream, b, oid);
}
- if (error == GIT_EPASSTHROUGH || error == GIT_SUCCESS)
- return GIT_SUCCESS;
+ if (error == GIT_PASSTHROUGH)
+ error = 0;
+
+ return error;
+}
+
+int git_odb__error_notfound(const char *message, const git_oid *oid)
+{
+ if (oid != NULL) {
+ char oid_str[GIT_OID_HEXSZ + 1];
+ git_oid_tostr(oid_str, sizeof(oid_str), oid);
+ giterr_set(GITERR_ODB, "Object not found - %s (%s)", message, oid_str);
+ } else
+ giterr_set(GITERR_ODB, "Object not found - %s", message);
- return git__rethrow(error, "Failed to open read stream");
+ return GIT_ENOTFOUND;
+}
+
+int git_odb__error_ambiguous(const char *message)
+{
+ giterr_set(GITERR_ODB, "Ambiguous SHA1 prefix - %s", message);
+ return GIT_EAMBIGUOUS;
}
diff --git a/src/odb.h b/src/odb.h
index b81533001..263e4c30b 100644
--- a/src/odb.h
+++ b/src/odb.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -13,6 +13,7 @@
#include "vector.h"
#include "cache.h"
+#include "posix.h"
#define GIT_OBJECTS_DIR "objects/"
#define GIT_OBJECT_DIR_MODE 0777
@@ -38,6 +39,42 @@ struct git_odb {
git_cache cache;
};
-int git_odb__hash_obj(git_oid *id, git_rawobj *obj);
+/*
+ * Hash a git_rawobj internally.
+ * The `git_rawobj` is supposed to be previously initialized
+ */
+int git_odb__hashobj(git_oid *id, git_rawobj *obj);
+
+/*
+ * Hash an open file descriptor.
+ * This is a performance call when the contents of a fd need to be hashed,
+ * but the fd is already open and we have the size of the contents.
+ *
+ * Saves us some `stat` calls.
+ *
+ * The fd is never closed, not even on error. It must be opened and closed
+ * by the caller
+ */
+int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type);
+
+/*
+ * Hash a `path`, assuming it could be a POSIX symlink: if the path is a symlink,
+ * then the raw contents of the symlink will be hashed. Otherwise, this will
+ * fallback to `git_odb__hashfd`.
+ *
+ * The hash type for this call is always `GIT_OBJ_BLOB` because symlinks may only
+ * point to blobs.
+ */
+int git_odb__hashlink(git_oid *out, const char *path);
+
+/*
+ * Generate a GIT_ENOTFOUND error for the ODB.
+ */
+int git_odb__error_notfound(const char *message, const git_oid *oid);
+
+/*
+ * Generate a GIT_EAMBIGUOUS error for the ODB.
+ */
+int git_odb__error_ambiguous(const char *message);
#endif
diff --git a/src/odb_loose.c b/src/odb_loose.c
index d958fce9f..989b03ab2 100644
--- a/src/odb_loose.c
+++ b/src/odb_loose.c
@@ -1,12 +1,12 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include "common.h"
-#include "git2/zlib.h"
+#include <zlib.h>
#include "git2/object.h"
#include "git2/oid.h"
#include "fileops.h"
@@ -61,27 +61,27 @@ static int object_file_name(git_buf *name, const char *dir, const git_oid *id)
git_buf_sets(name, dir);
/* expand length for 40 hex sha1 chars + 2 * '/' + '\0' */
- if (git_buf_grow(name, name->size + GIT_OID_HEXSZ + 3) < GIT_SUCCESS)
- return GIT_ENOMEM;
+ if (git_buf_grow(name, git_buf_len(name) + GIT_OID_HEXSZ + 3) < 0)
+ return -1;
git_path_to_dir(name);
/* loose object filename: aa/aaa... (41 bytes) */
- git_oid_pathfmt(name->ptr + name->size, id);
+ git_oid_pathfmt(name->ptr + git_buf_len(name), id);
name->size += GIT_OID_HEXSZ + 1;
name->ptr[name->size] = '\0';
- return GIT_SUCCESS;
+ return 0;
}
-static size_t get_binary_object_header(obj_hdr *hdr, git_fbuffer *obj)
+static size_t get_binary_object_header(obj_hdr *hdr, git_buf *obj)
{
unsigned char c;
- unsigned char *data = obj->data;
+ unsigned char *data = (unsigned char *)obj->ptr;
size_t shift, size, used = 0;
- if (obj->len == 0)
+ if (git_buf_len(obj) == 0)
return 0;
c = data[used++];
@@ -90,7 +90,7 @@ static size_t get_binary_object_header(obj_hdr *hdr, git_fbuffer *obj)
size = c & 15;
shift = 4;
while (c & 0x80) {
- if (obj->len <= used)
+ if (git_buf_len(obj) <= used)
return 0;
if (sizeof(size_t) * 8 <= shift)
return 0;
@@ -177,12 +177,12 @@ static void set_stream_output(z_stream *s, void *out, size_t len)
}
-static int start_inflate(z_stream *s, git_fbuffer *obj, void *out, size_t len)
+static int start_inflate(z_stream *s, git_buf *obj, void *out, size_t len)
{
int status;
init_stream(s, out, len);
- set_stream_input(s, obj->data, obj->len);
+ set_stream_input(s, obj->ptr, git_buf_len(obj));
if ((status = inflateInit(s)) < Z_OK)
return status;
@@ -199,10 +199,12 @@ static int finish_inflate(z_stream *s)
inflateEnd(s);
- if ((status != Z_STREAM_END) || (s->avail_in != 0))
- return git__throw(GIT_ERROR, "Failed to finish inflation. Stream aborted prematurely");
+ if ((status != Z_STREAM_END) || (s->avail_in != 0)) {
+ giterr_set(GITERR_ZLIB, "Failed to finish ZLib inflation. Stream aborted prematurely");
+ return -1;
+ }
- return GIT_SUCCESS;
+ return 0;
}
static int is_zlib_compressed_data(unsigned char *data)
@@ -226,21 +228,24 @@ static int inflate_buffer(void *in, size_t inlen, void *out, size_t outlen)
zs.next_in = in;
zs.avail_in = (uInt)inlen;
- if (inflateInit(&zs) < Z_OK)
- return git__throw(GIT_ERROR, "Failed to inflate buffer");
+ if (inflateInit(&zs) < Z_OK) {
+ giterr_set(GITERR_ZLIB, "Failed to inflate buffer");
+ return -1;
+ }
while (status == Z_OK)
status = inflate(&zs, Z_FINISH);
inflateEnd(&zs);
- if ((status != Z_STREAM_END) /*|| (zs.avail_in != 0) */)
- return git__throw(GIT_ERROR, "Failed to inflate buffer. Stream aborted prematurely");
-
- if (zs.total_out != outlen)
- return git__throw(GIT_ERROR, "Failed to inflate buffer. Stream aborted prematurely");
+ if (status != Z_STREAM_END /* || zs.avail_in != 0 */ ||
+ zs.total_out != outlen)
+ {
+ giterr_set(GITERR_ZLIB, "Failed to inflate buffer. Stream aborted prematurely");
+ return -1;
+ }
- return GIT_SUCCESS;
+ return 0;
}
static void *inflate_tail(z_stream *s, void *hb, size_t used, obj_hdr *hdr)
@@ -287,7 +292,7 @@ static void *inflate_tail(z_stream *s, void *hb, size_t used, obj_hdr *hdr)
* of loose object data into packs. This format is no longer used, but
* we must still read it.
*/
-static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_fbuffer *obj)
+static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_buf *obj)
{
unsigned char *in, *buf;
obj_hdr hdr;
@@ -297,24 +302,23 @@ static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_fbuffer *obj)
* read the object header, which is an (uncompressed)
* binary encoding of the object type and size.
*/
- if ((used = get_binary_object_header(&hdr, obj)) == 0)
- return git__throw(GIT_ERROR, "Failed to inflate loose object. Object has no header");
-
- if (!git_object_typeisloose(hdr.type))
- return git__throw(GIT_ERROR, "Failed to inflate loose object. Wrong object type");
+ if ((used = get_binary_object_header(&hdr, obj)) == 0 ||
+ !git_object_typeisloose(hdr.type)) {
+ giterr_set(GITERR_ODB, "Failed to inflate loose object.");
+ return -1;
+ }
/*
* allocate a buffer and inflate the data into it
*/
buf = git__malloc(hdr.size + 1);
- if (!buf)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(buf);
- in = ((unsigned char *)obj->data) + used;
- len = obj->len - used;
- if (inflate_buffer(in, len, buf, hdr.size)) {
+ in = ((unsigned char *)obj->ptr) + used;
+ len = obj->size - used;
+ if (inflate_buffer(in, len, buf, hdr.size) < 0) {
git__free(buf);
- return git__throw(GIT_ERROR, "Failed to inflate loose object. Could not inflate buffer");
+ return -1;
}
buf[hdr.size] = '\0';
@@ -322,10 +326,10 @@ static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_fbuffer *obj)
out->len = hdr.size;
out->type = hdr.type;
- return GIT_SUCCESS;
+ return 0;
}
-static int inflate_disk_obj(git_rawobj *out, git_fbuffer *obj)
+static int inflate_disk_obj(git_rawobj *out, git_buf *obj)
{
unsigned char head[64], *buf;
z_stream zs;
@@ -335,35 +339,34 @@ static int inflate_disk_obj(git_rawobj *out, git_fbuffer *obj)
/*
* check for a pack-like loose object
*/
- if (!is_zlib_compressed_data(obj->data))
+ if (!is_zlib_compressed_data((unsigned char *)obj->ptr))
return inflate_packlike_loose_disk_obj(out, obj);
/*
* inflate the initial part of the io buffer in order
* to parse the object header (type and size).
*/
- if (start_inflate(&zs, obj, head, sizeof(head)) < Z_OK)
- return git__throw(GIT_ERROR, "Failed to inflate disk object. Could not inflate buffer");
-
- if ((used = get_object_header(&hdr, head)) == 0)
- return git__throw(GIT_ERROR, "Failed to inflate disk object. Object has no header");
-
- if (!git_object_typeisloose(hdr.type))
- return git__throw(GIT_ERROR, "Failed to inflate disk object. Wrong object type");
+ if (start_inflate(&zs, obj, head, sizeof(head)) < Z_OK ||
+ (used = get_object_header(&hdr, head)) == 0 ||
+ !git_object_typeisloose(hdr.type))
+ {
+ giterr_set(GITERR_ODB, "Failed to inflate disk object.");
+ return -1;
+ }
/*
* allocate a buffer and inflate the object data into it
* (including the initial sequence in the head buffer).
*/
if ((buf = inflate_tail(&zs, head, used, &hdr)) == NULL)
- return GIT_ENOMEM;
+ return -1;
buf[hdr.size] = '\0';
out->data = buf;
out->len = hdr.size;
out->type = hdr.type;
- return GIT_SUCCESS;
+ return 0;
}
@@ -383,29 +386,28 @@ static int inflate_disk_obj(git_rawobj *out, git_fbuffer *obj)
static int read_loose(git_rawobj *out, git_buf *loc)
{
int error;
- git_fbuffer obj = GIT_FBUFFER_INIT;
+ git_buf obj = GIT_BUF_INIT;
assert(out && loc);
- if ((error = git_buf_lasterror(loc)) < GIT_SUCCESS)
- return error;
+ if (git_buf_oom(loc))
+ return -1;
out->data = NULL;
out->len = 0;
out->type = GIT_OBJ_BAD;
- if (git_futils_readbuffer(&obj, loc->ptr) < 0)
- return git__throw(GIT_ENOTFOUND, "Failed to read loose object. File not found");
+ if (!(error = git_futils_readbuffer(&obj, loc->ptr)))
+ error = inflate_disk_obj(out, &obj);
- error = inflate_disk_obj(out, &obj);
- git_futils_freebuffer(&obj);
+ git_buf_free(&obj);
- return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to read loose object");
+ return error;
}
static int read_header_loose(git_rawobj *out, git_buf *loc)
{
- int error = GIT_SUCCESS, z_return = Z_ERRNO, read_bytes;
+ int error = 0, z_return = Z_ERRNO, read_bytes;
git_file fd;
z_stream zs;
obj_hdr header_obj;
@@ -413,49 +415,41 @@ static int read_header_loose(git_rawobj *out, git_buf *loc)
assert(out && loc);
- if ((error = git_buf_lasterror(loc)) < GIT_SUCCESS)
- return error;
+ if (git_buf_oom(loc))
+ return -1;
out->data = NULL;
- if ((fd = p_open(loc->ptr, O_RDONLY)) < 0)
- return git__throw(GIT_ENOTFOUND, "Failed to read loose object header. File not found");
+ if ((fd = git_futils_open_ro(loc->ptr)) < 0)
+ return fd;
init_stream(&zs, inflated_buffer, sizeof(inflated_buffer));
- if (inflateInit(&zs) < Z_OK) {
- error = GIT_EZLIB;
- goto cleanup;
- }
+ z_return = inflateInit(&zs);
- do {
- if ((read_bytes = read(fd, raw_buffer, sizeof(raw_buffer))) > 0) {
+ while (z_return == Z_OK) {
+ if ((read_bytes = p_read(fd, raw_buffer, sizeof(raw_buffer))) > 0) {
set_stream_input(&zs, raw_buffer, read_bytes);
z_return = inflate(&zs, 0);
- } else {
+ } else
z_return = Z_STREAM_END;
- break;
- }
- } while (z_return == Z_OK);
+ }
if ((z_return != Z_STREAM_END && z_return != Z_BUF_ERROR)
|| get_object_header(&header_obj, inflated_buffer) == 0
- || git_object_typeisloose(header_obj.type) == 0) {
- error = GIT_EOBJCORRUPTED;
- goto cleanup;
+ || git_object_typeisloose(header_obj.type) == 0)
+ {
+ giterr_set(GITERR_ZLIB, "Failed to read loose object header");
+ error = -1;
+ } else {
+ out->len = header_obj.size;
+ out->type = header_obj.type;
}
- out->len = header_obj.size;
- out->type = header_obj.type;
-
-cleanup:
finish_inflate(&zs);
p_close(fd);
- if (error < GIT_SUCCESS)
- return git__throw(error, "Failed to read loose object header. Header is corrupted");
-
- return GIT_SUCCESS;
+ return error;
}
static int locate_object(
@@ -465,8 +459,8 @@ static int locate_object(
{
int error = object_file_name(object_location, backend->objects_dir, oid);
- if (error == GIT_SUCCESS)
- error = git_path_exists(git_buf_cstr(object_location));
+ if (!error && !git_path_exists(object_location->ptr))
+ return GIT_ENOTFOUND;
return error;
}
@@ -475,12 +469,12 @@ static int locate_object(
static int fn_locate_object_short_oid(void *state, git_buf *pathbuf) {
loose_locate_object_state *sstate = (loose_locate_object_state *)state;
- if (pathbuf->size - sstate->dir_len != GIT_OID_HEXSZ - 2) {
+ if (git_buf_len(pathbuf) - sstate->dir_len != GIT_OID_HEXSZ - 2) {
/* Entry cannot be an object. Continue to next entry */
- return GIT_SUCCESS;
+ return 0;
}
- if (!git_path_exists(pathbuf->ptr) && git_path_isdir(pathbuf->ptr)) {
+ if (git_path_isdir(pathbuf->ptr) == false) {
/* We are already in the directory matching the 2 first hex characters,
* compare the first ncmp characters of the oids */
if (!memcmp(sstate->short_oid + 2,
@@ -495,10 +489,11 @@ static int fn_locate_object_short_oid(void *state, git_buf *pathbuf) {
sstate->found++;
}
}
+
if (sstate->found > 1)
- return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Ambiguous sha1 prefix within loose objects");
+ return git_odb__error_ambiguous("multiple matches in loose objects");
- return GIT_SUCCESS;
+ return 0;
}
/* Locate an object matching a given short oid */
@@ -515,60 +510,56 @@ static int locate_object_short_oid(
int error;
/* prealloc memory for OBJ_DIR/xx/ */
- if ((error = git_buf_grow(object_location, dir_len + 5)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to locate object from short oid");
+ if (git_buf_grow(object_location, dir_len + 5) < 0)
+ return -1;
git_buf_sets(object_location, objects_dir);
git_path_to_dir(object_location);
/* save adjusted position at end of dir so it can be restored later */
- dir_len = object_location->size;
+ dir_len = git_buf_len(object_location);
/* Convert raw oid to hex formatted oid */
git_oid_fmt((char *)state.short_oid, short_oid);
/* Explore OBJ_DIR/xx/ where xx is the beginning of hex formatted short oid */
- error = git_buf_printf(object_location, "%.2s/", state.short_oid);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to locate object from short oid");
+ if (git_buf_printf(object_location, "%.2s/", state.short_oid) < 0)
+ return -1;
/* Check that directory exists */
- if (git_path_exists(object_location->ptr) ||
- git_path_isdir(object_location->ptr))
- return git__throw(GIT_ENOTFOUND, "Failed to locate object from short oid. Object not found");
+ if (git_path_isdir(object_location->ptr) == false)
+ return git_odb__error_notfound("no matching loose object for prefix", short_oid);
- state.dir_len = object_location->size;
+ state.dir_len = git_buf_len(object_location);
state.short_oid_len = len;
state.found = 0;
/* Explore directory to find a unique object matching short_oid */
- error = git_path_direach(object_location, fn_locate_object_short_oid, &state);
+ error = git_path_direach(
+ object_location, fn_locate_object_short_oid, &state);
if (error)
- return git__rethrow(error, "Failed to locate object from short oid");
+ return error;
- if (!state.found) {
- return git__throw(GIT_ENOTFOUND, "Failed to locate object from short oid. Object not found");
- }
+ if (!state.found)
+ return git_odb__error_notfound("no matching loose object for prefix", short_oid);
/* Convert obtained hex formatted oid to raw */
error = git_oid_fromstr(res_oid, (char *)state.res_oid);
- if (error) {
- return git__rethrow(error, "Failed to locate object from short oid");
- }
+ if (error)
+ return error;
/* Update the location according to the oid obtained */
git_buf_truncate(object_location, dir_len);
- error = git_buf_grow(object_location, dir_len + GIT_OID_HEXSZ + 2);
- if (error)
- return git__rethrow(error, "Failed to locate object from short oid");
+ if (git_buf_grow(object_location, dir_len + GIT_OID_HEXSZ + 2) < 0)
+ return -1;
git_oid_pathfmt(object_location->ptr + dir_len, res_oid);
object_location->size += GIT_OID_HEXSZ + 1;
object_location->ptr[object_location->size] = '\0';
- return GIT_SUCCESS;
+ return 0;
}
@@ -591,7 +582,7 @@ static int loose_backend__read_header(size_t *len_p, git_otype *type_p, git_odb_
{
git_buf object_path = GIT_BUF_INIT;
git_rawobj raw;
- int error = GIT_SUCCESS;
+ int error;
assert(backend && oid);
@@ -599,8 +590,8 @@ static int loose_backend__read_header(size_t *len_p, git_otype *type_p, git_odb_
raw.type = GIT_OBJ_BAD;
if (locate_object(&object_path, (loose_backend *)backend, oid) < 0)
- error = git__throw(GIT_ENOTFOUND, "Failed to read loose backend header. Object not found");
- else if ((error = read_header_loose(&raw, &object_path)) == GIT_SUCCESS) {
+ error = git_odb__error_notfound("no matching loose object", oid);
+ else if ((error = read_header_loose(&raw, &object_path)) == 0) {
*len_p = raw.len;
*type_p = raw.type;
}
@@ -614,20 +605,17 @@ static int loose_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p
{
git_buf object_path = GIT_BUF_INIT;
git_rawobj raw;
- int error = GIT_SUCCESS;
+ int error = 0;
assert(backend && oid);
if (locate_object(&object_path, (loose_backend *)backend, oid) < 0)
- error = git__throw(GIT_ENOTFOUND, "Failed to read loose backend. Object not found");
- else if ((error = read_loose(&raw, &object_path)) == GIT_SUCCESS) {
+ error = git_odb__error_notfound("no matching loose object", oid);
+ else if ((error = read_loose(&raw, &object_path)) == 0) {
*buffer_p = raw.data;
*len_p = raw.len;
*type_p = raw.type;
}
- else {
- git__rethrow(error, "Failed to read loose backend");
- }
git_buf_free(&object_path);
@@ -643,16 +631,15 @@ static int loose_backend__read_prefix(
const git_oid *short_oid,
unsigned int len)
{
- int error = GIT_SUCCESS;
+ int error = 0;
if (len < GIT_OID_MINPREFIXLEN)
- return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Failed to read loose "
- "backend. Prefix length is lower than %d.", GIT_OID_MINPREFIXLEN);
+ error = git_odb__error_ambiguous("prefix length too short");
- if (len >= GIT_OID_HEXSZ) {
+ else if (len >= GIT_OID_HEXSZ) {
/* We can fall back to regular read method */
error = loose_backend__read(buffer_p, len_p, type_p, backend, short_oid);
- if (error == GIT_SUCCESS)
+ if (!error)
git_oid_cpy(out_oid, short_oid);
} else {
git_buf object_path = GIT_BUF_INIT;
@@ -661,11 +648,9 @@ static int loose_backend__read_prefix(
assert(backend && short_oid);
if ((error = locate_object_short_oid(&object_path, out_oid,
- (loose_backend *)backend, short_oid, len)) < 0)
- git__rethrow(error, "Failed to read loose backend");
- else if ((error = read_loose(&raw, &object_path)) < GIT_SUCCESS)
- git__rethrow(error, "Failed to read loose backend");
- else {
+ (loose_backend *)backend, short_oid, len)) == 0 &&
+ (error = read_loose(&raw, &object_path)) == 0)
+ {
*buffer_p = raw.data;
*len_p = raw.len;
*type_p = raw.type;
@@ -688,47 +673,33 @@ static int loose_backend__exists(git_odb_backend *backend, const git_oid *oid)
git_buf_free(&object_path);
- return (error == GIT_SUCCESS);
+ return !error;
}
static int loose_backend__stream_fwrite(git_oid *oid, git_odb_stream *_stream)
{
loose_writestream *stream = (loose_writestream *)_stream;
loose_backend *backend = (loose_backend *)_stream->backend;
-
- int error;
git_buf final_path = GIT_BUF_INIT;
+ int error = 0;
- if ((error = git_filebuf_hash(oid, &stream->fbuf)) < GIT_SUCCESS)
- goto cleanup;
-
- if ((error = object_file_name(&final_path, backend->objects_dir, oid)) < GIT_SUCCESS)
- goto cleanup;
-
- if ((error = git_buf_lasterror(&final_path)) < GIT_SUCCESS)
- goto cleanup;
-
- if ((error = git_futils_mkpath2file(final_path.ptr, GIT_OBJECT_DIR_MODE)) < GIT_SUCCESS)
- goto cleanup;
-
+ if (git_filebuf_hash(oid, &stream->fbuf) < 0 ||
+ object_file_name(&final_path, backend->objects_dir, oid) < 0 ||
+ git_futils_mkpath2file(final_path.ptr, GIT_OBJECT_DIR_MODE) < 0)
+ error = -1;
/*
* Don't try to add an existing object to the repository. This
* is what git does and allows us to sidestep the fact that
* we're not allowed to overwrite a read-only file on Windows.
*/
- if (git_path_exists(final_path.ptr) == GIT_SUCCESS) {
+ else if (git_path_exists(final_path.ptr) == true)
git_filebuf_cleanup(&stream->fbuf);
- goto cleanup;
- }
+ else
+ error = git_filebuf_commit_at(
+ &stream->fbuf, final_path.ptr, GIT_OBJECT_FILE_MODE);
- error = git_filebuf_commit_at(&stream->fbuf, final_path.ptr, GIT_OBJECT_FILE_MODE);
-
-cleanup:
git_buf_free(&final_path);
- if (error < GIT_SUCCESS)
- git__rethrow(error, "Failed to write loose backend");
-
return error;
}
@@ -752,22 +723,18 @@ static int format_object_header(char *hdr, size_t n, size_t obj_len, git_otype o
int len = snprintf(hdr, n, "%s %"PRIuZ, type_str, obj_len);
assert(len > 0); /* otherwise snprintf() is broken */
- assert(((size_t) len) < n); /* otherwise the caller is broken! */
+ assert(((size_t)len) < n); /* otherwise the caller is broken! */
- if (len < 0 || ((size_t) len) >= n)
- return git__throw(GIT_ERROR, "Failed to format object header. Length is out of bounds");
return len+1;
}
static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_backend, size_t length, git_otype type)
{
loose_backend *backend;
- loose_writestream *stream;
-
+ loose_writestream *stream = NULL;
char hdr[64];
git_buf tmp_path = GIT_BUF_INIT;
int hdrlen;
- int error;
assert(_backend);
@@ -775,12 +742,9 @@ static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_
*stream_out = NULL;
hdrlen = format_object_header(hdr, sizeof(hdr), length, type);
- if (hdrlen < GIT_SUCCESS)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to create loose backend stream. Object is corrupted");
stream = git__calloc(1, sizeof(loose_writestream));
- if (stream == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(stream);
stream->stream.backend = _backend;
stream->stream.read = NULL; /* read only */
@@ -789,36 +753,26 @@ static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_
stream->stream.free = &loose_backend__stream_free;
stream->stream.mode = GIT_STREAM_WRONLY;
- error = git_buf_joinpath(&tmp_path, backend->objects_dir, "tmp_object");
- if (error < GIT_SUCCESS)
- goto cleanup;
-
- error = git_filebuf_open(&stream->fbuf, tmp_path.ptr,
- GIT_FILEBUF_HASH_CONTENTS |
- GIT_FILEBUF_TEMPORARY |
- (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT));
- if (error < GIT_SUCCESS)
- goto cleanup;
-
- error = stream->stream.write((git_odb_stream *)stream, hdr, hdrlen);
- if (error < GIT_SUCCESS)
- goto cleanup;
-
+ if (git_buf_joinpath(&tmp_path, backend->objects_dir, "tmp_object") < 0 ||
+ git_filebuf_open(&stream->fbuf, tmp_path.ptr,
+ GIT_FILEBUF_HASH_CONTENTS |
+ GIT_FILEBUF_TEMPORARY |
+ (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT)) < 0 ||
+ stream->stream.write((git_odb_stream *)stream, hdr, hdrlen) < 0)
+ {
+ git_filebuf_cleanup(&stream->fbuf);
+ git__free(stream);
+ stream = NULL;
+ }
git_buf_free(&tmp_path);
-
*stream_out = (git_odb_stream *)stream;
- return GIT_SUCCESS;
-cleanup:
- git_buf_free(&tmp_path);
- git_filebuf_cleanup(&stream->fbuf);
- git__free(stream);
- return git__rethrow(error, "Failed to create loose backend stream");
+ return !stream ? -1 : 0;
}
static int loose_backend__write(git_oid *oid, git_odb_backend *_backend, const void *data, size_t len, git_otype type)
{
- int error, header_len;
+ int error = 0, header_len;
git_buf final_path = GIT_BUF_INIT;
char header[64];
git_filebuf fbuf = GIT_FILEBUF_INIT;
@@ -827,39 +781,29 @@ static int loose_backend__write(git_oid *oid, git_odb_backend *_backend, const v
backend = (loose_backend *)_backend;
/* prepare the header for the file */
- {
- header_len = format_object_header(header, sizeof(header), len, type);
- if (header_len < GIT_SUCCESS)
- return GIT_EOBJCORRUPTED;
- }
-
- error = git_buf_joinpath(&final_path, backend->objects_dir, "tmp_object");
- if (error < GIT_SUCCESS)
- goto cleanup;
+ header_len = format_object_header(header, sizeof(header), len, type);
- error = git_filebuf_open(&fbuf, final_path.ptr,
- GIT_FILEBUF_HASH_CONTENTS |
- GIT_FILEBUF_TEMPORARY |
- (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT));
- if (error < GIT_SUCCESS)
+ if (git_buf_joinpath(&final_path, backend->objects_dir, "tmp_object") < 0 ||
+ git_filebuf_open(&fbuf, final_path.ptr,
+ GIT_FILEBUF_HASH_CONTENTS |
+ GIT_FILEBUF_TEMPORARY |
+ (backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT)) < 0)
+ {
+ error = -1;
goto cleanup;
+ }
git_filebuf_write(&fbuf, header, header_len);
git_filebuf_write(&fbuf, data, len);
git_filebuf_hash(oid, &fbuf);
- error = object_file_name(&final_path, backend->objects_dir, oid);
- if (error < GIT_SUCCESS)
- goto cleanup;
-
- error = git_futils_mkpath2file(final_path.ptr, GIT_OBJECT_DIR_MODE);
- if (error < GIT_SUCCESS)
- goto cleanup;
-
- error = git_filebuf_commit_at(&fbuf, final_path.ptr, GIT_OBJECT_FILE_MODE);
+ if (object_file_name(&final_path, backend->objects_dir, oid) < 0 ||
+ git_futils_mkpath2file(final_path.ptr, GIT_OBJECT_DIR_MODE) < 0 ||
+ git_filebuf_commit_at(&fbuf, final_path.ptr, GIT_OBJECT_FILE_MODE) < 0)
+ error = -1;
cleanup:
- if (error < GIT_SUCCESS)
+ if (error < 0)
git_filebuf_cleanup(&fbuf);
git_buf_free(&final_path);
return error;
@@ -884,14 +828,10 @@ int git_odb_backend_loose(
loose_backend *backend;
backend = git__calloc(1, sizeof(loose_backend));
- if (backend == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(backend);
backend->objects_dir = git__strdup(objects_dir);
- if (backend->objects_dir == NULL) {
- git__free(backend);
- return GIT_ENOMEM;
- }
+ GITERR_CHECK_ALLOC(backend->objects_dir);
if (compression_level < 0)
compression_level = Z_BEST_SPEED;
@@ -908,5 +848,5 @@ int git_odb_backend_loose(
backend->parent.free = &loose_backend__free;
*backend_out = (git_odb_backend *)backend;
- return GIT_SUCCESS;
+ return 0;
}
diff --git a/src/odb_pack.c b/src/odb_pack.c
index 81168bfa6..458f288d9 100644
--- a/src/odb_pack.c
+++ b/src/odb_pack.c
@@ -1,12 +1,12 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include "common.h"
-#include "git2/zlib.h"
+#include <zlib.h>
#include "git2/repository.h"
#include "git2/oid.h"
#include "fileops.h"
@@ -137,19 +137,19 @@ static int packfile_load__cb(void *_data, git_buf *path);
static int packfile_refresh_all(struct pack_backend *backend);
static int pack_entry_find(struct git_pack_entry *e,
- struct pack_backend *backend, const git_oid *oid);
+ struct pack_backend *backend, const git_oid *oid);
/* Can find the offset of an object given
* a prefix of an identifier.
- * Throws GIT_EAMBIGUOUSOIDPREFIX if short oid
- * is ambiguous.
+ * Sets GIT_EAMBIGUOUS if short oid is ambiguous.
* This method assumes that len is between
* GIT_OID_MINPREFIXLEN and GIT_OID_HEXSZ.
*/
-static int pack_entry_find_prefix(struct git_pack_entry *e,
- struct pack_backend *backend,
- const git_oid *short_oid,
- unsigned int len);
+static int pack_entry_find_prefix(
+ struct git_pack_entry *e,
+ struct pack_backend *backend,
+ const git_oid *short_oid,
+ unsigned int len);
@@ -159,9 +159,9 @@ static int pack_entry_find_prefix(struct git_pack_entry *e,
*
***********************************************************/
-GIT_INLINE(void) pack_window_free_all(struct pack_backend *GIT_UNUSED(backend), struct git_pack_file *p)
+GIT_INLINE(void) pack_window_free_all(struct pack_backend *backend, struct git_pack_file *p)
{
- GIT_UNUSED_ARG(backend);
+ GIT_UNUSED(backend);
git_mwindow_free_all(&p->mwf);
}
@@ -212,30 +212,25 @@ static int packfile_load__cb(void *_data, git_buf *path)
struct pack_backend *backend = (struct pack_backend *)_data;
struct git_pack_file *pack;
int error;
- size_t i;
+ unsigned int i;
if (git__suffixcmp(path->ptr, ".idx") != 0)
- return GIT_SUCCESS; /* not an index */
+ return 0; /* not an index */
for (i = 0; i < backend->packs.length; ++i) {
struct git_pack_file *p = git_vector_get(&backend->packs, i);
- if (memcmp(p->pack_name, path->ptr, path->size - strlen(".idx")) == 0)
- return GIT_SUCCESS;
+ if (memcmp(p->pack_name, git_buf_cstr(path), git_buf_len(path) - strlen(".idx")) == 0)
+ return 0;
}
error = git_packfile_check(&pack, path->ptr);
- if (error == GIT_ENOTFOUND) {
+ if (error == GIT_ENOTFOUND)
/* ignore missing .pack file as git does */
- return GIT_SUCCESS;
- } else if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to load packfile");
-
- if (git_vector_insert(&backend->packs, pack) < GIT_SUCCESS) {
- git__free(pack);
- return GIT_ENOMEM;
- }
+ return 0;
+ else if (error < 0)
+ return error;
- return GIT_SUCCESS;
+ return git_vector_insert(&backend->packs, pack);
}
static int packfile_refresh_all(struct pack_backend *backend)
@@ -244,10 +239,10 @@ static int packfile_refresh_all(struct pack_backend *backend)
struct stat st;
if (backend->pack_folder == NULL)
- return GIT_SUCCESS;
+ return 0;
if (p_stat(backend->pack_folder, &st) < 0 || !S_ISDIR(st.st_mode))
- return git__throw(GIT_ENOTFOUND, "Failed to refresh packfiles. Backend not found");
+ return git_odb__error_notfound("failed to refresh packfiles", NULL);
if (st.st_mtime != backend->pack_folder_mtime) {
git_buf path = GIT_BUF_INIT;
@@ -257,27 +252,28 @@ static int packfile_refresh_all(struct pack_backend *backend)
error = git_path_direach(&path, packfile_load__cb, (void *)backend);
git_buf_free(&path);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to refresh packfiles");
+
+ if (error < 0)
+ return error;
git_vector_sort(&backend->packs);
backend->pack_folder_mtime = st.st_mtime;
}
- return GIT_SUCCESS;
+ return 0;
}
static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backend, const git_oid *oid)
{
int error;
- size_t i;
+ unsigned int i;
- if ((error = packfile_refresh_all(backend)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to find pack entry");
+ if ((error = packfile_refresh_all(backend)) < 0)
+ return error;
if (backend->last_found &&
- git_pack_entry_find(e, backend->last_found, oid, GIT_OID_HEXSZ) == GIT_SUCCESS)
- return GIT_SUCCESS;
+ git_pack_entry_find(e, backend->last_found, oid, GIT_OID_HEXSZ) == 0)
+ return 0;
for (i = 0; i < backend->packs.length; ++i) {
struct git_pack_file *p;
@@ -286,13 +282,13 @@ static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backen
if (p == backend->last_found)
continue;
- if (git_pack_entry_find(e, p, oid, GIT_OID_HEXSZ) == GIT_SUCCESS) {
+ if (git_pack_entry_find(e, p, oid, GIT_OID_HEXSZ) == 0) {
backend->last_found = p;
- return GIT_SUCCESS;
+ return 0;
}
}
- return git__throw(GIT_ENOTFOUND, "Failed to find pack entry");
+ return git_odb__error_notfound("failed to find pack entry", oid);
}
static int pack_entry_find_prefix(
@@ -302,19 +298,18 @@ static int pack_entry_find_prefix(
unsigned int len)
{
int error;
- size_t i;
+ unsigned int i;
unsigned found = 0;
- if ((error = packfile_refresh_all(backend)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to find pack entry");
+ if ((error = packfile_refresh_all(backend)) < 0)
+ return error;
if (backend->last_found) {
error = git_pack_entry_find(e, backend->last_found, short_oid, len);
- if (error == GIT_EAMBIGUOUSOIDPREFIX) {
- return git__rethrow(error, "Failed to find pack entry. Ambiguous sha1 prefix");
- } else if (error == GIT_SUCCESS) {
+ if (error == GIT_EAMBIGUOUS)
+ return error;
+ if (!error)
found = 1;
- }
}
for (i = 0; i < backend->packs.length; ++i) {
@@ -325,24 +320,21 @@ static int pack_entry_find_prefix(
continue;
error = git_pack_entry_find(e, p, short_oid, len);
- if (error == GIT_EAMBIGUOUSOIDPREFIX) {
- return git__rethrow(error, "Failed to find pack entry. Ambiguous sha1 prefix");
- } else if (error == GIT_SUCCESS) {
- found++;
- if (found > 1)
+ if (error == GIT_EAMBIGUOUS)
+ return error;
+ if (!error) {
+ if (++found > 1)
break;
backend->last_found = p;
}
}
- if (!found) {
- return git__rethrow(GIT_ENOTFOUND, "Failed to find pack entry");
- } else if (found > 1) {
- return git__rethrow(GIT_EAMBIGUOUSOIDPREFIX, "Failed to find pack entry. Ambiguous sha1 prefix");
- } else {
- return GIT_SUCCESS;
- }
-
+ if (!found)
+ return git_odb__error_notfound("no matching pack entry for prefix", short_oid);
+ else if (found > 1)
+ return git_odb__error_ambiguous("found multiple pack entries");
+ else
+ return 0;
}
@@ -374,17 +366,15 @@ static int pack_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p,
git_rawobj raw;
int error;
- if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to read pack backend");
-
- if ((error = git_packfile_unpack(&raw, e.p, &e.offset)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to read pack backend");
+ if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0 ||
+ (error = git_packfile_unpack(&raw, e.p, &e.offset)) < 0)
+ return error;
*buffer_p = raw.data;
*len_p = raw.len;
*type_p = raw.type;
- return GIT_SUCCESS;
+ return 0;
}
static int pack_backend__read_prefix(
@@ -396,46 +386,44 @@ static int pack_backend__read_prefix(
const git_oid *short_oid,
unsigned int len)
{
+ int error = 0;
+
if (len < GIT_OID_MINPREFIXLEN)
- return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Failed to read pack backend. Prefix length is lower than %d.", GIT_OID_MINPREFIXLEN);
+ error = git_odb__error_ambiguous("prefix length too short");
- if (len >= GIT_OID_HEXSZ) {
+ else if (len >= GIT_OID_HEXSZ) {
/* We can fall back to regular read method */
- int error = pack_backend__read(buffer_p, len_p, type_p, backend, short_oid);
- if (error == GIT_SUCCESS)
+ error = pack_backend__read(buffer_p, len_p, type_p, backend, short_oid);
+ if (!error)
git_oid_cpy(out_oid, short_oid);
-
- return error;
} else {
struct git_pack_entry e;
git_rawobj raw;
- int error;
-
- if ((error = pack_entry_find_prefix(&e, (struct pack_backend *)backend, short_oid, len)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to read pack backend");
- if ((error = git_packfile_unpack(&raw, e.p, &e.offset)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to read pack backend");
-
- *buffer_p = raw.data;
- *len_p = raw.len;
- *type_p = raw.type;
- git_oid_cpy(out_oid, &e.sha1);
+ if ((error = pack_entry_find_prefix(
+ &e, (struct pack_backend *)backend, short_oid, len)) == 0 &&
+ (error = git_packfile_unpack(&raw, e.p, &e.offset)) == 0)
+ {
+ *buffer_p = raw.data;
+ *len_p = raw.len;
+ *type_p = raw.type;
+ git_oid_cpy(out_oid, &e.sha1);
+ }
}
- return GIT_SUCCESS;
+ return error;
}
static int pack_backend__exists(git_odb_backend *backend, const git_oid *oid)
{
struct git_pack_entry e;
- return pack_entry_find(&e, (struct pack_backend *)backend, oid) == GIT_SUCCESS;
+ return pack_entry_find(&e, (struct pack_backend *)backend, oid) == 0;
}
static void pack_backend__free(git_odb_backend *_backend)
{
struct pack_backend *backend;
- size_t i;
+ unsigned int i;
assert(_backend);
@@ -455,21 +443,18 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)
{
struct pack_backend *backend = NULL;
git_buf path = GIT_BUF_INIT;
- int error = GIT_SUCCESS;
backend = git__calloc(1, sizeof(struct pack_backend));
- if (backend == NULL)
- return GIT_ENOMEM;
-
- error = git_vector_init(&backend->packs, 8, packfile_sort__cb);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ GITERR_CHECK_ALLOC(backend);
- error = git_buf_joinpath(&path, objects_dir, "pack");
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (git_vector_init(&backend->packs, 8, packfile_sort__cb) < 0 ||
+ git_buf_joinpath(&path, objects_dir, "pack") < 0)
+ {
+ git__free(backend);
+ return -1;
+ }
- if (git_path_isdir(git_buf_cstr(&path)) == GIT_SUCCESS) {
+ if (git_path_isdir(git_buf_cstr(&path)) == true) {
backend->pack_folder = git_buf_detach(&path);
backend->pack_folder_mtime = 0;
}
@@ -482,10 +467,7 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)
*backend_out = (git_odb_backend *)backend;
-cleanup:
- if (error < GIT_SUCCESS)
- git__free(backend);
git_buf_free(&path);
- return error;
+ return 0;
}
diff --git a/src/oid.c b/src/oid.c
index 61bf6da8a..87756010b 100644
--- a/src/oid.c
+++ b/src/oid.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -13,13 +13,19 @@
static char to_hex[] = "0123456789abcdef";
+static int oid_error_invalid(const char *msg)
+{
+ giterr_set(GITERR_INVALID, "Unable to parse OID - %s", msg);
+ return -1;
+}
+
int git_oid_fromstrn(git_oid *out, const char *str, size_t length)
{
size_t p;
int v;
if (length < 4)
- return git__throw(GIT_ENOTOID, "Failed to generate sha1. Given string is too short");
+ return oid_error_invalid("input too short");
if (length > GIT_OID_HEXSZ)
length = GIT_OID_HEXSZ;
@@ -29,7 +35,7 @@ int git_oid_fromstrn(git_oid *out, const char *str, size_t length)
| git__fromhex(str[p + 1]);
if (v < 0)
- return git__throw(GIT_ENOTOID, "Failed to generate sha1. Given string is not a valid sha1 hash");
+ return oid_error_invalid("contains invalid characters");
out->id[p / 2] = (unsigned char)v;
}
@@ -37,7 +43,7 @@ int git_oid_fromstrn(git_oid *out, const char *str, size_t length)
if (length % 2) {
v = (git__fromhex(str[p + 0]) << 4);
if (v < 0)
- return git__throw(GIT_ENOTOID, "Failed to generate sha1. Given string is not a valid sha1 hash");
+ return oid_error_invalid("contains invalid characters");
out->id[p / 2] = (unsigned char)v;
p += 2;
@@ -45,7 +51,7 @@ int git_oid_fromstrn(git_oid *out, const char *str, size_t length)
memset(out->id + p / 2, 0, (GIT_OID_HEXSZ - p) / 2);
- return GIT_SUCCESS;
+ return 0;
}
int git_oid_fromstr(git_oid *out, const char *str)
@@ -88,7 +94,7 @@ char *git_oid_allocfmt(const git_oid *oid)
return str;
}
-char *git_oid_to_string(char *out, size_t n, const git_oid *oid)
+char *git_oid_tostr(char *out, size_t n, const git_oid *oid)
{
char str[GIT_OID_HEXSZ];
@@ -109,8 +115,9 @@ char *git_oid_to_string(char *out, size_t n, const git_oid *oid)
return out;
}
-int git_oid__parse(git_oid *oid, const char **buffer_out,
- const char *buffer_end, const char *header)
+int git_oid__parse(
+ git_oid *oid, const char **buffer_out,
+ const char *buffer_end, const char *header)
{
const size_t sha_len = GIT_OID_HEXSZ;
const size_t header_len = strlen(header);
@@ -118,20 +125,20 @@ int git_oid__parse(git_oid *oid, const char **buffer_out,
const char *buffer = *buffer_out;
if (buffer + (header_len + sha_len + 1) > buffer_end)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse OID. Buffer too small");
+ return -1;
if (memcmp(buffer, header, header_len) != 0)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse OID. Buffer and header do not match");
+ return -1;
if (buffer[header_len + sha_len] != '\n')
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse OID. Buffer not terminated correctly");
+ return -1;
- if (git_oid_fromstr(oid, buffer + header_len) < GIT_SUCCESS)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse OID. Failed to generate sha1");
+ if (git_oid_fromstr(oid, buffer + header_len) < 0)
+ return -1;
*buffer_out = buffer + (header_len + sha_len + 1);
- return GIT_SUCCESS;
+ return 0;
}
void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid)
@@ -182,12 +189,21 @@ int git_oid_ncmp(const git_oid *oid_a, const git_oid *oid_b, unsigned int len)
int git_oid_streq(const git_oid *a, const char *str)
{
git_oid id;
- int error;
- if ((error = git_oid_fromstr(&id, str)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to convert '%s' to oid.", str);
+ if (git_oid_fromstr(&id, str) < 0)
+ return -1;
- return git_oid_cmp(a, &id) == 0 ? GIT_SUCCESS : GIT_ERROR;
+ return git_oid_cmp(a, &id) == 0 ? 0 : -1;
+}
+
+int git_oid_iszero(const git_oid *oid_a)
+{
+ const unsigned char *a = oid_a->id;
+ unsigned int i;
+ for (i = 0; i < GIT_OID_RAWSZ; ++i, ++a)
+ if (*a != 0)
+ return 0;
+ return 1;
}
typedef short node_index;
@@ -206,15 +222,14 @@ struct git_oid_shorten {
static int resize_trie(git_oid_shorten *self, size_t new_size)
{
self->nodes = git__realloc(self->nodes, new_size * sizeof(trie_node));
- if (self->nodes == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(self->nodes);
if (new_size > self->size) {
memset(&self->nodes[self->size], 0x0, (new_size - self->size) * sizeof(trie_node));
}
self->size = new_size;
- return GIT_SUCCESS;
+ return 0;
}
static trie_node *push_leaf(git_oid_shorten *os, node_index idx, int push_at, const char *oid)
@@ -223,7 +238,7 @@ static trie_node *push_leaf(git_oid_shorten *os, node_index idx, int push_at, co
node_index idx_leaf;
if (os->node_count >= os->size) {
- if (resize_trie(os, os->size * 2) < GIT_SUCCESS)
+ if (resize_trie(os, os->size * 2) < 0)
return NULL;
}
@@ -245,19 +260,19 @@ git_oid_shorten *git_oid_shorten_new(size_t min_length)
{
git_oid_shorten *os;
- os = git__malloc(sizeof(git_oid_shorten));
+ assert((size_t)((int)min_length) == min_length);
+
+ os = git__calloc(1, sizeof(git_oid_shorten));
if (os == NULL)
return NULL;
- memset(os, 0x0, sizeof(git_oid_shorten));
-
- if (resize_trie(os, 16) < GIT_SUCCESS) {
+ if (resize_trie(os, 16) < 0) {
git__free(os);
return NULL;
}
os->node_count = 1;
- os->min_length = min_length;
+ os->min_length = (int)min_length;
return os;
}
@@ -315,24 +330,27 @@ void git_oid_shorten_free(git_oid_shorten *os)
*/
int git_oid_shorten_add(git_oid_shorten *os, const char *text_oid)
{
- int i, is_leaf;
+ int i;
+ bool is_leaf;
node_index idx;
if (os->full)
- return GIT_ENOMEM;
+ return -1;
if (text_oid == NULL)
return os->min_length;
idx = 0;
- is_leaf = 0;
+ is_leaf = false;
for (i = 0; i < GIT_OID_HEXSZ; ++i) {
int c = git__fromhex(text_oid[i]);
trie_node *node;
- if (c == -1)
- return git__throw(GIT_ENOTOID, "Failed to shorten OID. Invalid hex value");
+ if (c == -1) {
+ giterr_set(GITERR_INVALID, "Unable to shorten OID - invalid hex value");
+ return -1;
+ }
node = &os->nodes[idx];
@@ -343,22 +361,21 @@ int git_oid_shorten_add(git_oid_shorten *os, const char *text_oid)
node->tail = NULL;
node = push_leaf(os, idx, git__fromhex(tail[0]), &tail[1]);
- if (node == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(node);
}
if (node->children[c] == 0) {
if (push_leaf(os, idx, c, &text_oid[i + 1]) == NULL)
- return GIT_ENOMEM;
+ return -1;
break;
}
idx = node->children[c];
- is_leaf = 0;
+ is_leaf = false;
if (idx < 0) {
node->children[c] = idx = -idx;
- is_leaf = 1;
+ is_leaf = true;
}
}
diff --git a/src/oidmap.h b/src/oidmap.h
new file mode 100644
index 000000000..858268c92
--- /dev/null
+++ b/src/oidmap.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_oidmap_h__
+#define INCLUDE_oidmap_h__
+
+#include "common.h"
+#include "git2/oid.h"
+
+#define kmalloc git__malloc
+#define kcalloc git__calloc
+#define krealloc git__realloc
+#define kfree git__free
+#include "khash.h"
+
+__KHASH_TYPE(oid, const git_oid *, void *);
+typedef khash_t(oid) git_oidmap;
+
+GIT_INLINE(khint_t) hash_git_oid(const git_oid *oid)
+{
+ int i;
+ khint_t h = 0;
+ for (i = 0; i < 20; ++i)
+ h = (h << 5) - h + oid->id[i];
+ return h;
+}
+
+GIT_INLINE(int) hash_git_oid_equal(const git_oid *a, const git_oid *b)
+{
+ return (memcmp(a->id, b->id, sizeof(a->id)) == 0);
+}
+
+#define GIT__USE_OIDMAP \
+ __KHASH_IMPL(oid, static inline, const git_oid *, void *, 1, hash_git_oid, hash_git_oid_equal)
+
+#define git_oidmap_alloc() kh_init(oid)
+#define git_oidmap_free(h) kh_destroy(oid,h), h = NULL
+
+#endif
diff --git a/src/pack.c b/src/pack.c
index cf64983ca..0db1069de 100644
--- a/src/pack.c
+++ b/src/pack.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -14,15 +14,15 @@
#include "fileops.h"
#include "git2/oid.h"
-#include "git2/zlib.h"
+#include <zlib.h>
static int packfile_open(struct git_pack_file *p);
-static off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_t n);
+static git_off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_t n);
int packfile_unpack_compressed(
git_rawobj *obj,
struct git_pack_file *p,
git_mwindow **w_curs,
- off_t *curpos,
+ git_off_t *curpos,
size_t size,
git_otype type);
@@ -34,12 +34,18 @@ int packfile_unpack_compressed(
* GIT_OID_MINPREFIXLEN and GIT_OID_HEXSZ.
*/
static int pack_entry_find_offset(
- off_t *offset_out,
+ git_off_t *offset_out,
git_oid *found_oid,
struct git_pack_file *p,
const git_oid *short_oid,
unsigned int len);
+static int packfile_error(const char *message)
+{
+ giterr_set(GITERR_ODB, "Invalid pack file - %s", message);
+ return -1;
+}
+
/***********************************************************
*
* PACK INDEX METHODS
@@ -58,40 +64,31 @@ static int pack_index_check(const char *path, struct git_pack_file *p)
{
struct git_pack_idx_header *hdr;
uint32_t version, nr, i, *index;
-
void *idx_map;
size_t idx_size;
-
struct stat st;
-
- /* TODO: properly open the file without access time */
- git_file fd = p_open(path, O_RDONLY /*| O_NOATIME */);
-
int error;
-
+ /* TODO: properly open the file without access time using O_NOATIME */
+ git_file fd = git_futils_open_ro(path);
if (fd < 0)
- return git__throw(GIT_EOSERR, "Failed to check index. File missing or corrupted");
-
- if (p_fstat(fd, &st) < GIT_SUCCESS) {
- p_close(fd);
- return git__throw(GIT_EOSERR, "Failed to check index. File appears to be corrupted");
- }
-
- if (!git__is_sizet(st.st_size))
- return GIT_ENOMEM;
+ return fd;
- idx_size = (size_t)st.st_size;
-
- if (idx_size < 4 * 256 + 20 + 20) {
+ if (p_fstat(fd, &st) < 0 ||
+ !S_ISREG(st.st_mode) ||
+ !git__is_sizet(st.st_size) ||
+ (idx_size = (size_t)st.st_size) < 4 * 256 + 20 + 20)
+ {
p_close(fd);
- return git__throw(GIT_EOBJCORRUPTED, "Failed to check index. Object is corrupted");
+ giterr_set(GITERR_OS, "Failed to check pack index.");
+ return -1;
}
error = git_futils_mmap_ro(&p->index_map, fd, 0, idx_size);
+
p_close(fd);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to check index");
+ if (error < 0)
+ return error;
hdr = idx_map = p->index_map.data;
@@ -100,7 +97,7 @@ static int pack_index_check(const char *path, struct git_pack_file *p)
if (version < 2 || version > 2) {
git_futils_mmap_free(&p->index_map);
- return git__throw(GIT_EOBJCORRUPTED, "Failed to check index. Unsupported index version");
+ return packfile_error("unsupported index version");
}
} else
@@ -116,7 +113,7 @@ static int pack_index_check(const char *path, struct git_pack_file *p)
uint32_t n = ntohl(index[i]);
if (n < nr) {
git_futils_mmap_free(&p->index_map);
- return git__throw(GIT_EOBJCORRUPTED, "Failed to check index. Index is non-monotonic");
+ return packfile_error("index is non-monotonic");
}
nr = n;
}
@@ -131,7 +128,7 @@ static int pack_index_check(const char *path, struct git_pack_file *p)
*/
if (idx_size != 4*256 + nr * 24 + 20 + 20) {
git_futils_mmap_free(&p->index_map);
- return git__throw(GIT_EOBJCORRUPTED, "Failed to check index. Object is corrupted");
+ return packfile_error("index is corrupted");
}
} else if (version == 2) {
/*
@@ -155,39 +152,46 @@ static int pack_index_check(const char *path, struct git_pack_file *p)
if (idx_size < min_size || idx_size > max_size) {
git_futils_mmap_free(&p->index_map);
- return git__throw(GIT_EOBJCORRUPTED, "Failed to check index. Wrong index size");
+ return packfile_error("wrong index size");
}
}
p->index_version = version;
p->num_objects = nr;
- return GIT_SUCCESS;
+ return 0;
}
static int pack_index_open(struct git_pack_file *p)
{
char *idx_name;
int error;
+ size_t name_len, offset;
if (p->index_map.data)
- return GIT_SUCCESS;
+ return 0;
idx_name = git__strdup(p->pack_name);
- strcpy(idx_name + strlen(idx_name) - strlen(".pack"), ".idx");
+ GITERR_CHECK_ALLOC(idx_name);
+
+ name_len = strlen(idx_name);
+ offset = name_len - strlen(".pack");
+ assert(offset < name_len); /* make sure no underflow */
+
+ strncpy(idx_name + offset, ".idx", name_len - offset);
error = pack_index_check(idx_name, p);
git__free(idx_name);
- return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to open index");
+ return error;
}
static unsigned char *pack_window_open(
struct git_pack_file *p,
git_mwindow **w_cursor,
- off_t offset,
+ git_off_t offset,
unsigned int *left)
{
- if (p->mwf.fd == -1 && packfile_open(p) < GIT_SUCCESS)
+ if (p->mwf.fd == -1 && packfile_open(p) < 0)
return NULL;
/* Since packfiles end in a hash of their content and it's
@@ -201,7 +205,8 @@ static unsigned char *pack_window_open(
return git_mwindow_open(&p->mwf, w_cursor, offset, 20, left);
}
-static unsigned long packfile_unpack_header1(
+static int packfile_unpack_header1(
+ unsigned long *usedp,
size_t *sizep,
git_otype *type,
const unsigned char *buf,
@@ -216,8 +221,13 @@ static unsigned long packfile_unpack_header1(
size = c & 15;
shift = 4;
while (c & 0x80) {
- if (len <= used || bitsizeof(long) <= shift)
- return 0;
+ if (len <= used)
+ return GIT_EBUFS;
+
+ if (bitsizeof(long) <= shift) {
+ *usedp = 0;
+ return -1;
+ }
c = buf[used++];
size += (c & 0x7f) << shift;
@@ -225,7 +235,8 @@ static unsigned long packfile_unpack_header1(
}
*sizep = (size_t)size;
- return used;
+ *usedp = used;
+ return 0;
}
int git_packfile_unpack_header(
@@ -233,11 +244,12 @@ int git_packfile_unpack_header(
git_otype *type_p,
git_mwindow_file *mwf,
git_mwindow **w_curs,
- off_t *curpos)
+ git_off_t *curpos)
{
unsigned char *base;
unsigned int left;
unsigned long used;
+ int ret;
/* pack_window_open() assures us we have [base, base + 20) available
* as a range that we can look at at. (Its actually the hash
@@ -248,37 +260,39 @@ int git_packfile_unpack_header(
// base = pack_window_open(p, w_curs, *curpos, &left);
base = git_mwindow_open(mwf, w_curs, *curpos, 20, &left);
if (base == NULL)
- return GIT_ENOMEM;
-
- used = packfile_unpack_header1(size_p, type_p, base, left);
+ return GIT_EBUFS;
- if (used == 0)
- return git__throw(GIT_EOBJCORRUPTED, "Header length is zero");
+ ret = packfile_unpack_header1(&used, size_p, type_p, base, left);
+ git_mwindow_close(w_curs);
+ if (ret == GIT_EBUFS)
+ return ret;
+ else if (ret < 0)
+ return packfile_error("header length is zero");
*curpos += used;
- return GIT_SUCCESS;
+ return 0;
}
static int packfile_unpack_delta(
git_rawobj *obj,
struct git_pack_file *p,
git_mwindow **w_curs,
- off_t *curpos,
+ git_off_t *curpos,
size_t delta_size,
git_otype delta_type,
- off_t obj_offset)
+ git_off_t obj_offset)
{
- off_t base_offset;
+ git_off_t base_offset;
git_rawobj base, delta;
int error;
base_offset = get_delta_base(p, w_curs, curpos, delta_type, obj_offset);
+ git_mwindow_close(w_curs);
if (base_offset == 0)
- return git__throw(GIT_EOBJCORRUPTED, "Delta offset is zero");
- if (base_offset < 0)
- return git__rethrow(base_offset, "Failed to get delta base");
+ return packfile_error("delta offset is zero");
+ if (base_offset < 0) /* must actually be an error code */
+ return (int)base_offset;
- git_mwindow_close(w_curs);
error = git_packfile_unpack(&base, p, &base_offset);
/*
@@ -287,35 +301,35 @@ static int packfile_unpack_delta(
*
* We'll need to do this in order to support thin packs.
*/
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Corrupted delta");
+ if (error < 0)
+ return error;
error = packfile_unpack_compressed(&delta, p, w_curs, curpos, delta_size, delta_type);
- if (error < GIT_SUCCESS) {
+ git_mwindow_close(w_curs);
+ if (error < 0) {
git__free(base.data);
- return git__rethrow(error, "Corrupted delta");
+ return error;
}
obj->type = base.type;
- error = git__delta_apply(obj,
- base.data, base.len,
- delta.data, delta.len);
+ error = git__delta_apply(obj, base.data, base.len, delta.data, delta.len);
git__free(base.data);
git__free(delta.data);
/* TODO: we might want to cache this shit. eventually */
//add_delta_base_cache(p, base_offset, base, base_size, *type);
+
return error; /* error set by git__delta_apply */
}
int git_packfile_unpack(
- git_rawobj *obj,
- struct git_pack_file *p,
- off_t *obj_offset)
+ git_rawobj *obj,
+ struct git_pack_file *p,
+ git_off_t *obj_offset)
{
git_mwindow *w_curs = NULL;
- off_t curpos = *obj_offset;
+ git_off_t curpos = *obj_offset;
int error;
size_t size = 0;
@@ -330,8 +344,10 @@ int git_packfile_unpack(
obj->type = GIT_OBJ_BAD;
error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to unpack packfile");
+ git_mwindow_close(&w_curs);
+
+ if (error < 0)
+ return error;
switch (type) {
case GIT_OBJ_OFS_DELTA:
@@ -351,52 +367,70 @@ int git_packfile_unpack(
break;
default:
- error = GIT_EOBJCORRUPTED;
+ error = packfile_error("invalid packfile type in header");;
break;
}
- git_mwindow_close(&w_curs);
+ *obj_offset = curpos;
+ return error;
+}
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to unpack object");
+static void *use_git_alloc(void *opaq, unsigned int count, unsigned int size)
+{
+ GIT_UNUSED(opaq);
+ return git__calloc(count, size);
+}
- *obj_offset = curpos;
- return GIT_SUCCESS;
+static void use_git_free(void *opaq, void *ptr)
+{
+ GIT_UNUSED(opaq);
+ git__free(ptr);
}
int packfile_unpack_compressed(
- git_rawobj *obj,
- struct git_pack_file *p,
- git_mwindow **w_curs,
- off_t *curpos,
- size_t size,
- git_otype type)
+ git_rawobj *obj,
+ struct git_pack_file *p,
+ git_mwindow **w_curs,
+ git_off_t *curpos,
+ size_t size,
+ git_otype type)
{
int st;
z_stream stream;
unsigned char *buffer, *in;
- buffer = git__malloc(size + 1);
- memset(buffer, 0x0, size + 1);
+ buffer = git__calloc(1, size + 1);
+ GITERR_CHECK_ALLOC(buffer);
memset(&stream, 0, sizeof(stream));
stream.next_out = buffer;
stream.avail_out = (uInt)size + 1;
+ stream.zalloc = use_git_alloc;
+ stream.zfree = use_git_free;
st = inflateInit(&stream);
if (st != Z_OK) {
git__free(buffer);
- return git__throw(GIT_EZLIB, "Error in zlib");
+ giterr_set(GITERR_ZLIB, "Failed to inflate packfile");
+
+ return -1;
}
do {
in = pack_window_open(p, w_curs, *curpos, &stream.avail_in);
stream.next_in = in;
st = inflate(&stream, Z_FINISH);
+ git_mwindow_close(w_curs);
if (!stream.avail_out)
break; /* the payload is larger than it should be */
+ if (st == Z_BUF_ERROR && in == NULL) {
+ inflateEnd(&stream);
+ git__free(buffer);
+ return GIT_EBUFS;
+ }
+
*curpos += stream.next_in - in;
} while (st == Z_OK || st == Z_BUF_ERROR);
@@ -404,30 +438,36 @@ int packfile_unpack_compressed(
if ((st != Z_STREAM_END) || stream.total_out != size) {
git__free(buffer);
- return git__throw(GIT_EZLIB, "Error in zlib");
+ giterr_set(GITERR_ZLIB, "Failed to inflate packfile");
+ return -1;
}
obj->type = type;
obj->len = size;
obj->data = buffer;
- return GIT_SUCCESS;
+ return 0;
}
/*
* curpos is where the data starts, delta_obj_offset is the where the
* header starts
*/
-off_t get_delta_base(
- struct git_pack_file *p,
- git_mwindow **w_curs,
- off_t *curpos,
- git_otype type,
- off_t delta_obj_offset)
+git_off_t get_delta_base(
+ struct git_pack_file *p,
+ git_mwindow **w_curs,
+ git_off_t *curpos,
+ git_otype type,
+ git_off_t delta_obj_offset)
{
- unsigned char *base_info = pack_window_open(p, w_curs, *curpos, NULL);
- off_t base_offset;
+ unsigned int left = 0;
+ unsigned char *base_info;
+ git_off_t base_offset;
git_oid unused;
+ base_info = pack_window_open(p, w_curs, *curpos, &left);
+ /* Assumption: the only reason this would fail is because the file is too small */
+ if (base_info == NULL)
+ return GIT_EBUFS;
/* pack_window_open() assured us we have [base_info, base_info + 20)
* as a range that we can look at without walking off the
* end of the mapped window. Its actually the hash size
@@ -439,6 +479,8 @@ off_t get_delta_base(
unsigned char c = base_info[used++];
base_offset = c & 127;
while (c & 128) {
+ if (left <= used)
+ return GIT_EBUFS;
base_offset += 1;
if (!base_offset || MSB(base_offset, 7))
return 0; /* overflow */
@@ -463,8 +505,8 @@ off_t get_delta_base(
}
}
/* The base entry _must_ be in the same pack */
- if (pack_entry_find_offset(&base_offset, &unused, p, (git_oid *)base_info, GIT_OID_HEXSZ) < GIT_SUCCESS)
- return git__rethrow(GIT_EPACKCORRUPTED, "Base entry delta is not in the same pack");
+ if (pack_entry_find_offset(&base_offset, &unused, p, (git_oid *)base_info, GIT_OID_HEXSZ) < 0)
+ return packfile_error("base entry delta is not in the same pack");
*curpos += 20;
} else
return 0;
@@ -478,11 +520,11 @@ off_t get_delta_base(
*
***********************************************************/
-static struct git_pack_file *packfile_alloc(int extra)
+static struct git_pack_file *packfile_alloc(size_t extra)
{
- struct git_pack_file *p = git__malloc(sizeof(*p) + extra);
- memset(p, 0, sizeof(*p));
- p->mwf.fd = -1;
+ struct git_pack_file *p = git__calloc(1, sizeof(*p) + extra);
+ if (p != NULL)
+ p->mwf.fd = -1;
return p;
}
@@ -510,24 +552,25 @@ static int packfile_open(struct git_pack_file *p)
git_oid sha1;
unsigned char *idx_sha1;
- if (!p->index_map.data && pack_index_open(p) < GIT_SUCCESS)
- return git__throw(GIT_ENOTFOUND, "Failed to open packfile. File not found");
+ assert(p->index_map.data);
+
+ if (!p->index_map.data && pack_index_open(p) < 0)
+ return git_odb__error_notfound("failed to open packfile", NULL);
/* TODO: open with noatime */
- p->mwf.fd = p_open(p->pack_name, O_RDONLY);
- if (p->mwf.fd < 0 || p_fstat(p->mwf.fd, &st) < GIT_SUCCESS)
- return git__throw(GIT_EOSERR, "Failed to open packfile. File appears to be corrupted");
+ p->mwf.fd = git_futils_open_ro(p->pack_name);
+ if (p->mwf.fd < 0)
+ return p->mwf.fd;
- if (git_mwindow_file_register(&p->mwf) < GIT_SUCCESS) {
- p_close(p->mwf.fd);
- return git__throw(GIT_ERROR, "Failed to register packfile windows");
- }
+ if (p_fstat(p->mwf.fd, &st) < 0 ||
+ git_mwindow_file_register(&p->mwf) < 0)
+ goto cleanup;
/* If we created the struct before we had the pack we lack size. */
if (!p->mwf.size) {
if (!S_ISREG(st.st_mode))
goto cleanup;
- p->mwf.size = (off_t)st.st_size;
+ p->mwf.size = (git_off_t)st.st_size;
} else if (p->mwf.size != st.st_size)
goto cleanup;
@@ -537,44 +580,35 @@ static int packfile_open(struct git_pack_file *p)
*/
fd_flag = fcntl(p->mwf.fd, F_GETFD, 0);
if (fd_flag < 0)
- return error("cannot determine file descriptor flags");
+ goto cleanup;
fd_flag |= FD_CLOEXEC;
if (fcntl(p->pack_fd, F_SETFD, fd_flag) == -1)
- return GIT_EOSERR;
+ goto cleanup;
#endif
/* Verify we recognize this pack file format. */
- if (p_read(p->mwf.fd, &hdr, sizeof(hdr)) < GIT_SUCCESS)
- goto cleanup;
-
- if (hdr.hdr_signature != htonl(PACK_SIGNATURE))
- goto cleanup;
-
- if (!pack_version_ok(hdr.hdr_version))
+ if (p_read(p->mwf.fd, &hdr, sizeof(hdr)) < 0 ||
+ hdr.hdr_signature != htonl(PACK_SIGNATURE) ||
+ !pack_version_ok(hdr.hdr_version))
goto cleanup;
/* Verify the pack matches its index. */
- if (p->num_objects != ntohl(hdr.hdr_entries))
- goto cleanup;
-
- if (p_lseek(p->mwf.fd, p->mwf.size - GIT_OID_RAWSZ, SEEK_SET) == -1)
- goto cleanup;
-
- if (p_read(p->mwf.fd, sha1.id, GIT_OID_RAWSZ) < GIT_SUCCESS)
+ if (p->num_objects != ntohl(hdr.hdr_entries) ||
+ p_lseek(p->mwf.fd, p->mwf.size - GIT_OID_RAWSZ, SEEK_SET) == -1 ||
+ p_read(p->mwf.fd, sha1.id, GIT_OID_RAWSZ) < 0)
goto cleanup;
idx_sha1 = ((unsigned char *)p->index_map.data) + p->index_map.len - 40;
- if (git_oid_cmp(&sha1, (git_oid *)idx_sha1) != 0)
- goto cleanup;
-
- return GIT_SUCCESS;
+ if (git_oid_cmp(&sha1, (git_oid *)idx_sha1) == 0)
+ return 0;
cleanup:
+ giterr_set(GITERR_OS, "Invalid packfile '%s'", p->pack_name);
p_close(p->mwf.fd);
p->mwf.fd = -1;
- return git__throw(GIT_EPACKCORRUPTED, "Failed to open packfile. Pack is corrupted");
+ return -1;
}
int git_packfile_check(struct git_pack_file **pack_out, const char *path)
@@ -586,6 +620,7 @@ int git_packfile_check(struct git_pack_file **pack_out, const char *path)
*pack_out = NULL;
path_len = strlen(path);
p = packfile_alloc(path_len + 2);
+ GITERR_CHECK_ALLOC(p);
/*
* Make sure a corresponding .pack file exists and that
@@ -594,19 +629,19 @@ int git_packfile_check(struct git_pack_file **pack_out, const char *path)
path_len -= strlen(".idx");
if (path_len < 1) {
git__free(p);
- return git__throw(GIT_ENOTFOUND, "Failed to check packfile. Wrong path name");
+ return git_odb__error_notfound("invalid packfile path", NULL);
}
memcpy(p->pack_name, path, path_len);
strcpy(p->pack_name + path_len, ".keep");
- if (git_path_exists(p->pack_name) == GIT_SUCCESS)
+ if (git_path_exists(p->pack_name) == true)
p->pack_keep = 1;
strcpy(p->pack_name + path_len, ".pack");
- if (p_stat(p->pack_name, &st) < GIT_SUCCESS || !S_ISREG(st.st_mode)) {
+ if (p_stat(p->pack_name, &st) < 0 || !S_ISREG(st.st_mode)) {
git__free(p);
- return git__throw(GIT_ENOTFOUND, "Failed to check packfile. File not found");
+ return git_odb__error_notfound("packfile not found", NULL);
}
/* ok, it looks sane as far as we can check without
@@ -618,11 +653,12 @@ int git_packfile_check(struct git_pack_file **pack_out, const char *path)
/* see if we can parse the sha1 oid in the packfile name */
if (path_len < 40 ||
- git_oid_fromstr(&p->sha1, path + path_len - GIT_OID_HEXSZ) < GIT_SUCCESS)
+ git_oid_fromstr(&p->sha1, path + path_len - GIT_OID_HEXSZ) < 0)
memset(&p->sha1, 0x0, GIT_OID_RAWSZ);
*pack_out = p;
- return GIT_SUCCESS;
+
+ return 0;
}
/***********************************************************
@@ -631,7 +667,7 @@ int git_packfile_check(struct git_pack_file **pack_out, const char *path)
*
***********************************************************/
-static off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_t n)
+static git_off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_t n)
{
const unsigned char *index = p->index_map.data;
index += 4 * 256;
@@ -650,11 +686,11 @@ static off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_t n)
}
static int pack_entry_find_offset(
- off_t *offset_out,
- git_oid *found_oid,
- struct git_pack_file *p,
- const git_oid *short_oid,
- unsigned int len)
+ git_off_t *offset_out,
+ git_oid *found_oid,
+ struct git_pack_file *p,
+ const git_oid *short_oid,
+ unsigned int len)
{
const uint32_t *level1_ofs = p->index_map.data;
const unsigned char *index = p->index_map.data;
@@ -667,8 +703,8 @@ static int pack_entry_find_offset(
if (index == NULL) {
int error;
- if ((error = pack_index_open(p)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to find offset for pack entry");
+ if ((error = pack_index_open(p)) < 0)
+ return error;
assert(p->index_map.data);
@@ -711,9 +747,8 @@ static int pack_entry_find_offset(
if (pos < (int)p->num_objects) {
current = index + pos * stride;
- if (!git_oid_ncmp(short_oid, (const git_oid *)current, len)) {
+ if (!git_oid_ncmp(short_oid, (const git_oid *)current, len))
found = 1;
- }
}
}
@@ -726,22 +761,22 @@ static int pack_entry_find_offset(
}
}
- if (!found) {
- return git__throw(GIT_ENOTFOUND, "Failed to find offset for pack entry. Entry not found");
- } else if (found > 1) {
- return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Failed to find offset for pack entry. Ambiguous sha1 prefix within pack");
- } else {
- *offset_out = nth_packed_object_offset(p, pos);
- git_oid_fromraw(found_oid, current);
+ if (!found)
+ return git_odb__error_notfound("failed to find offset for pack entry", short_oid);
+ if (found > 1)
+ return git_odb__error_ambiguous("found multiple offsets for pack entry");
+ *offset_out = nth_packed_object_offset(p, pos);
+ git_oid_fromraw(found_oid, current);
#ifdef INDEX_DEBUG_LOOKUP
+ {
unsigned char hex_sha1[GIT_OID_HEXSZ + 1];
git_oid_fmt(hex_sha1, found_oid);
hex_sha1[GIT_OID_HEXSZ] = '\0';
printf("found lo=%d %s\n", lo, hex_sha1);
-#endif
- return GIT_SUCCESS;
}
+#endif
+ return 0;
}
int git_pack_entry_find(
@@ -750,7 +785,7 @@ int git_pack_entry_find(
const git_oid *short_oid,
unsigned int len)
{
- off_t offset;
+ git_off_t offset;
git_oid found_oid;
int error;
@@ -760,22 +795,22 @@ int git_pack_entry_find(
unsigned i;
for (i = 0; i < p->num_bad_objects; i++)
if (git_oid_cmp(short_oid, &p->bad_object_sha1[i]) == 0)
- return git__throw(GIT_ERROR, "Failed to find pack entry. Bad object found");
+ return packfile_error("bad object found in packfile");
}
error = pack_entry_find_offset(&offset, &found_oid, p, short_oid, len);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to find pack entry. Couldn't find offset");
+ if (error < 0)
+ return error;
/* we found a unique entry in the index;
* make sure the packfile backing the index
* still exists on disk */
- if (p->mwf.fd == -1 && packfile_open(p) < GIT_SUCCESS)
- return git__throw(GIT_EOSERR, "Failed to find pack entry. Packfile doesn't exist on disk");
+ if (p->mwf.fd == -1 && (error = packfile_open(p)) < 0)
+ return error;
e->offset = offset;
e->p = p;
git_oid_cpy(&e->sha1, &found_oid);
- return GIT_SUCCESS;
+ return 0;
}
diff --git a/src/pack.h b/src/pack.h
index aecf580e9..cd7a4d2e1 100644
--- a/src/pack.h
+++ b/src/pack.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -70,7 +70,7 @@ struct git_pack_file {
};
struct git_pack_entry {
- off_t offset;
+ git_off_t offset;
git_oid sha1;
struct git_pack_file *p;
};
@@ -80,13 +80,20 @@ int git_packfile_unpack_header(
git_otype *type_p,
git_mwindow_file *mwf,
git_mwindow **w_curs,
- off_t *curpos);
-
-int git_packfile_unpack(git_rawobj *obj, struct git_pack_file *p, off_t *obj_offset);
-
-off_t get_delta_base(struct git_pack_file *p, git_mwindow **w_curs,
- off_t *curpos, git_otype type,
- off_t delta_obj_offset);
+ git_off_t *curpos);
+
+int git_packfile_unpack(git_rawobj *obj, struct git_pack_file *p, git_off_t *obj_offset);
+int packfile_unpack_compressed(
+ git_rawobj *obj,
+ struct git_pack_file *p,
+ git_mwindow **w_curs,
+ git_off_t *curpos,
+ size_t size,
+ git_otype type);
+
+git_off_t get_delta_base(struct git_pack_file *p, git_mwindow **w_curs,
+ git_off_t *curpos, git_otype type,
+ git_off_t delta_obj_offset);
void packfile_free(struct git_pack_file *p);
int git_packfile_check(struct git_pack_file **pack_out, const char *path);
diff --git a/src/path.c b/src/path.c
index 5319ca6a5..84edf6d89 100644
--- a/src/path.c
+++ b/src/path.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -9,6 +9,7 @@
#include "posix.h"
#ifdef GIT_WIN32
#include "win32/dir.h"
+#include "win32/posix.h"
#else
#include <dirent.h>
#endif
@@ -49,16 +50,14 @@ int git_path_basename_r(git_buf *buffer, const char *path)
while (startp > path && *(startp - 1) != '/')
startp--;
- len = endp - startp +1;
+ /* Cast is safe because max path < max int */
+ len = (int)(endp - startp + 1);
Exit:
result = len;
- if (buffer != NULL) {
- if (git_buf_set(buffer, startp, len) < GIT_SUCCESS)
- return git__rethrow(git_buf_lasterror(buffer),
- "Could not get basename of '%s'", path);
- }
+ if (buffer != NULL && git_buf_set(buffer, startp, len) < 0)
+ return -1;
return result;
}
@@ -99,7 +98,8 @@ int git_path_dirname_r(git_buf *buffer, const char *path)
endp--;
} while (endp > path && *endp == '/');
- len = endp - path +1;
+ /* Cast is safe because max path < max int */
+ len = (int)(endp - path + 1);
#ifdef GIT_WIN32
/* Mimic unix behavior where '/.git' returns '/': 'C:/.git' will return
@@ -114,11 +114,8 @@ int git_path_dirname_r(git_buf *buffer, const char *path)
Exit:
result = len;
- if (buffer != NULL) {
- if (git_buf_set(buffer, path, len) < GIT_SUCCESS)
- return git__rethrow(git_buf_lasterror(buffer),
- "Could not get dirname of '%s'", path);
- }
+ if (buffer != NULL && git_buf_set(buffer, path, len) < 0)
+ return -1;
return result;
}
@@ -152,7 +149,7 @@ char *git_path_basename(const char *path)
const char *git_path_topdir(const char *path)
{
size_t len;
- int i;
+ ssize_t i;
assert(path);
len = strlen(path);
@@ -160,7 +157,7 @@ const char *git_path_topdir(const char *path)
if (!len || path[len - 1] != '/')
return NULL;
- for (i = len - 2; i >= 0; --i)
+ for (i = (ssize_t)len - 2; i >= 0; --i)
if (path[i] == '/')
break;
@@ -175,54 +172,65 @@ int git_path_root(const char *path)
/* Does the root of the path look like a windows drive ? */
if (isalpha(path[0]) && (path[1] == ':'))
offset += 2;
+
+ /* Are we dealing with a windows network path? */
+ else if ((path[0] == '/' && path[1] == '/') ||
+ (path[0] == '\\' && path[1] == '\\'))
+ {
+ offset += 2;
+
+ /* Skip the computer name segment */
+ while (path[offset] && path[offset] != '/' && path[offset] != '\\')
+ offset++;
+ }
#endif
- if (*(path + offset) == '/')
+ if (path[offset] == '/' || path[offset] == '\\')
return offset;
- return -1; /* Not a real error. Rather a signal than the path is not rooted */
+ return -1; /* Not a real error - signals that path is not rooted */
}
int git_path_prettify(git_buf *path_out, const char *path, const char *base)
{
- int error = GIT_SUCCESS;
char buf[GIT_PATH_MAX];
- git_buf_clear(path_out);
+ assert(path && path_out);
/* construct path if needed */
if (base != NULL && git_path_root(path) < 0) {
- if ((error = git_buf_joinpath(path_out, base, path)) < GIT_SUCCESS)
- return error;
+ if (git_buf_joinpath(path_out, base, path) < 0)
+ return -1;
path = path_out->ptr;
}
- if (path == NULL || p_realpath(path, buf) == NULL)
- error = GIT_EOSERR;
- else
- error = git_buf_sets(path_out, buf);
+ if (p_realpath(path, buf) == NULL) {
+ /* giterr_set resets the errno when dealing with a GITERR_OS kind of error */
+ int error = (errno == ENOENT || errno == ENOTDIR) ? GIT_ENOTFOUND : -1;
+ giterr_set(GITERR_OS, "Failed to resolve path '%s'", path);
- return error;
+ git_buf_clear(path_out);
+
+ return error;
+ }
+
+ return git_buf_sets(path_out, buf);
}
int git_path_prettify_dir(git_buf *path_out, const char *path, const char *base)
{
int error = git_path_prettify(path_out, path, base);
-
- if (error == GIT_SUCCESS)
- error = git_path_to_dir(path_out);
-
- return error;
+ return (error < 0) ? error : git_path_to_dir(path_out);
}
int git_path_to_dir(git_buf *path)
{
if (path->asize > 0 &&
- path->size > 0 &&
- path->ptr[path->size - 1] != '/')
+ git_buf_len(path) > 0 &&
+ path->ptr[git_buf_len(path) - 1] != '/')
git_buf_putc(path, '/');
- return git_buf_lasterror(path);
+ return git_buf_oom(path) ? -1 : 0;
}
void git_path_string_to_dir(char* path, size_t size)
@@ -237,10 +245,10 @@ void git_path_string_to_dir(char* path, size_t size)
int git__percent_decode(git_buf *decoded_out, const char *input)
{
- int len, hi, lo, i, error = GIT_SUCCESS;
+ int len, hi, lo, i;
assert(decoded_out && input);
- len = strlen(input);
+ len = (int)strlen(input);
git_buf_clear(decoded_out);
for(i = 0; i < len; i++)
@@ -263,39 +271,40 @@ int git__percent_decode(git_buf *decoded_out, const char *input)
i += 2;
append:
- error = git_buf_putc(decoded_out, c);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to percent decode '%s'.", input);
+ if (git_buf_putc(decoded_out, c) < 0)
+ return -1;
}
- return error;
+ return 0;
+}
+
+static int error_invalid_local_file_uri(const char *uri)
+{
+ giterr_set(GITERR_CONFIG, "'%s' is not a valid local file URI", uri);
+ return -1;
}
int git_path_fromurl(git_buf *local_path_out, const char *file_url)
{
- int error = GIT_SUCCESS, offset = 0, len;
+ int offset = 0, len;
assert(local_path_out && file_url);
if (git__prefixcmp(file_url, "file://") != 0)
- return git__throw(GIT_EINVALIDPATH,
- "Parsing of '%s' failed. A file Uri is expected (ie. with 'file://' scheme).",
- file_url);
+ return error_invalid_local_file_uri(file_url);
offset += 7;
- len = strlen(file_url);
+ len = (int)strlen(file_url);
if (offset < len && file_url[offset] == '/')
offset++;
else if (offset < len && git__prefixcmp(file_url + offset, "localhost/") == 0)
offset += 10;
else
- return git__throw(GIT_EINVALIDPATH,
- "Parsing of '%s' failed. A local file Uri is expected.", file_url);
+ return error_invalid_local_file_uri(file_url);
if (offset >= len || file_url[offset] == '/')
- return git__throw(GIT_EINVALIDPATH,
- "Parsing of '%s' failed. Invalid file Uri format.", file_url);
+ return error_invalid_local_file_uri(file_url);
#ifndef _MSC_VER
offset--; /* A *nix absolute path starts with a forward slash */
@@ -303,11 +312,7 @@ int git_path_fromurl(git_buf *local_path_out, const char *file_url)
git_buf_clear(local_path_out);
- error = git__percent_decode(local_path_out, file_url + offset);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Parsing of '%s' failed.", file_url);
-
- return error;
+ return git__percent_decode(local_path_out, file_url + offset);
}
int git_path_walk_up(
@@ -316,7 +321,7 @@ int git_path_walk_up(
int (*cb)(void *data, git_buf *),
void *data)
{
- int error = GIT_SUCCESS;
+ int error = 0;
git_buf iter;
ssize_t stop = 0, scan;
char oldc = '\0';
@@ -324,19 +329,19 @@ int git_path_walk_up(
assert(path && cb);
if (ceiling != NULL) {
- if (git__prefixcmp(path->ptr, ceiling) == GIT_SUCCESS)
+ if (git__prefixcmp(path->ptr, ceiling) == 0)
stop = (ssize_t)strlen(ceiling);
else
- stop = path->size;
+ stop = git_buf_len(path);
}
- scan = path->size;
+ scan = git_buf_len(path);
iter.ptr = path->ptr;
- iter.size = path->size;
+ iter.size = git_buf_len(path);
iter.asize = path->asize;
while (scan >= stop) {
- if ((error = cb(data, &iter)) < GIT_SUCCESS)
+ if ((error = cb(data, &iter)) < 0)
break;
iter.ptr[scan] = oldc;
scan = git_buf_rfind_next(&iter, '/');
@@ -354,124 +359,123 @@ int git_path_walk_up(
return error;
}
-int git_path_exists(const char *path)
+bool git_path_exists(const char *path)
{
assert(path);
- return p_access(path, F_OK);
+ return p_access(path, F_OK) == 0;
}
-int git_path_isdir(const char *path)
+bool git_path_isdir(const char *path)
{
-#ifdef GIT_WIN32
- DWORD attr = GetFileAttributes(path);
- if (attr == INVALID_FILE_ATTRIBUTES)
- return GIT_ERROR;
-
- return (attr & FILE_ATTRIBUTE_DIRECTORY) ? GIT_SUCCESS : GIT_ERROR;
-
-#else
struct stat st;
- if (p_stat(path, &st) < GIT_SUCCESS)
- return GIT_ERROR;
+ if (p_stat(path, &st) < 0)
+ return false;
- return S_ISDIR(st.st_mode) ? GIT_SUCCESS : GIT_ERROR;
-#endif
+ return S_ISDIR(st.st_mode) != 0;
}
-int git_path_isfile(const char *path)
+bool git_path_isfile(const char *path)
{
struct stat st;
- int stat_error;
assert(path);
- stat_error = p_stat(path, &st);
+ if (p_stat(path, &st) < 0)
+ return false;
- if (stat_error < GIT_SUCCESS)
- return -1;
+ return S_ISREG(st.st_mode) != 0;
+}
- if (!S_ISREG(st.st_mode))
- return -1;
+int git_path_lstat(const char *path, struct stat *st)
+{
+ int err = 0;
- return 0;
+ if (p_lstat(path, st) < 0) {
+ err = (errno == ENOENT) ? GIT_ENOTFOUND : -1;
+ giterr_set(GITERR_OS, "Failed to stat file '%s'", path);
+ }
+
+ return err;
}
-static int _check_dir_contents(
+static bool _check_dir_contents(
git_buf *dir,
const char *sub,
- int append_on_success,
- int (*predicate)(const char *))
+ bool (*predicate)(const char *))
{
- int error = GIT_SUCCESS;
- size_t dir_size = dir->size;
+ bool result;
+ size_t dir_size = git_buf_len(dir);
size_t sub_size = strlen(sub);
/* leave base valid even if we could not make space for subdir */
- if ((error = git_buf_try_grow(dir, dir_size + sub_size + 2)) < GIT_SUCCESS)
- return error;
+ if (git_buf_try_grow(dir, dir_size + sub_size + 2) < 0)
+ return false;
/* save excursion */
git_buf_joinpath(dir, dir->ptr, sub);
- error = (*predicate)(dir->ptr);
+ result = predicate(dir->ptr);
- /* restore excursion */
- if (!append_on_success || error != GIT_SUCCESS)
- git_buf_truncate(dir, dir_size);
+ /* restore path */
+ git_buf_truncate(dir, dir_size);
+ return result;
+}
- return error;
+bool git_path_contains(git_buf *dir, const char *item)
+{
+ return _check_dir_contents(dir, item, &git_path_exists);
}
-int git_path_contains_dir(git_buf *base, const char *subdir, int append_if_exists)
+bool git_path_contains_dir(git_buf *base, const char *subdir)
{
- return _check_dir_contents(base, subdir, append_if_exists, &git_path_isdir);
+ return _check_dir_contents(base, subdir, &git_path_isdir);
}
-int git_path_contains_file(git_buf *base, const char *file, int append_if_exists)
+bool git_path_contains_file(git_buf *base, const char *file)
{
- return _check_dir_contents(base, file, append_if_exists, &git_path_isfile);
+ return _check_dir_contents(base, file, &git_path_isfile);
}
int git_path_find_dir(git_buf *dir, const char *path, const char *base)
{
- int error = GIT_SUCCESS;
+ int error;
if (base != NULL && git_path_root(path) < 0)
error = git_buf_joinpath(dir, base, path);
else
error = git_buf_sets(dir, path);
- if (error == GIT_SUCCESS) {
+ if (!error) {
char buf[GIT_PATH_MAX];
if (p_realpath(dir->ptr, buf) != NULL)
error = git_buf_sets(dir, buf);
}
/* call dirname if this is not a directory */
- if (error == GIT_SUCCESS && git_path_isdir(dir->ptr) != GIT_SUCCESS)
- if (git_path_dirname_r(dir, dir->ptr) < GIT_SUCCESS)
- error = git_buf_lasterror(dir);
+ if (!error && git_path_isdir(dir->ptr) == false)
+ error = git_path_dirname_r(dir, dir->ptr);
- if (error == GIT_SUCCESS)
+ if (!error)
error = git_path_to_dir(dir);
return error;
}
-int git_path_cmp(const char *name1, int len1, int isdir1,
- const char *name2, int len2, int isdir2)
+int git_path_cmp(
+ const char *name1, size_t len1, int isdir1,
+ const char *name2, size_t len2, int isdir2)
{
- int len = len1 < len2 ? len1 : len2;
+ size_t len = len1 < len2 ? len1 : len2;
int cmp;
cmp = memcmp(name1, name2, len);
if (cmp)
return cmp;
if (len1 < len2)
- return ((!isdir1 && !isdir2) ? -1 :
- (isdir1 ? '/' - name2[len1] : name2[len1] - '/'));
+ return (!isdir1 && !isdir2) ? -1 :
+ (isdir1 ? '/' - name2[len1] : name2[len1] - '/');
if (len1 > len2)
- return ((!isdir1 && !isdir2) ? 1 :
- (isdir2 ? name1[len2] - '/' : '/' - name1[len2]));
+ return (!isdir1 && !isdir2) ? 1 :
+ (isdir2 ? name1[len2] - '/' : '/' - name1[len2]);
return 0;
}
@@ -490,35 +494,163 @@ int git_path_direach(
{
ssize_t wd_len;
DIR *dir;
- struct dirent *de;
+ struct dirent *de, *de_buf;
+
+ if (git_path_to_dir(path) < 0)
+ return -1;
+
+ wd_len = git_buf_len(path);
- if (git_path_to_dir(path) < GIT_SUCCESS)
- return git_buf_lasterror(path);
+ if ((dir = opendir(path->ptr)) == NULL) {
+ giterr_set(GITERR_OS, "Failed to open directory '%s'", path->ptr);
+ return -1;
+ }
- wd_len = path->size;
- dir = opendir(path->ptr);
- if (!dir)
- return git__throw(GIT_EOSERR, "Failed to process `%s` tree structure. An error occured while opening the directory", path->ptr);
+#ifdef __sun
+ de_buf = git__malloc(sizeof(struct dirent) + FILENAME_MAX + 1);
+#else
+ de_buf = git__malloc(sizeof(struct dirent));
+#endif
- while ((de = readdir(dir)) != NULL) {
+ while (p_readdir_r(dir, de_buf, &de) == 0 && de != NULL) {
int result;
if (is_dot_or_dotdot(de->d_name))
continue;
- if (git_buf_puts(path, de->d_name) < GIT_SUCCESS)
- return git_buf_lasterror(path);
+ if (git_buf_puts(path, de->d_name) < 0) {
+ closedir(dir);
+ git__free(de_buf);
+ return -1;
+ }
result = fn(arg, path);
git_buf_truncate(path, wd_len); /* restore path */
- if (result != GIT_SUCCESS) {
+ if (result < 0) {
closedir(dir);
- return result; /* The callee is reponsible for setting the correct error message */
+ git__free(de_buf);
+ return -1;
}
}
closedir(dir);
- return GIT_SUCCESS;
+ git__free(de_buf);
+ return 0;
+}
+
+int git_path_dirload(
+ const char *path,
+ size_t prefix_len,
+ size_t alloc_extra,
+ git_vector *contents)
+{
+ int error, need_slash;
+ DIR *dir;
+ struct dirent *de, *de_buf;
+ size_t path_len;
+
+ assert(path != NULL && contents != NULL);
+ path_len = strlen(path);
+ assert(path_len > 0 && path_len >= prefix_len);
+
+ if ((dir = opendir(path)) == NULL) {
+ giterr_set(GITERR_OS, "Failed to open directory '%s'", path);
+ return -1;
+ }
+
+#ifdef __sun
+ de_buf = git__malloc(sizeof(struct dirent) + FILENAME_MAX + 1);
+#else
+ de_buf = git__malloc(sizeof(struct dirent));
+#endif
+
+ path += prefix_len;
+ path_len -= prefix_len;
+ need_slash = (path_len > 0 && path[path_len-1] != '/') ? 1 : 0;
+
+ while ((error = p_readdir_r(dir, de_buf, &de)) == 0 && de != NULL) {
+ char *entry_path;
+ size_t entry_len;
+
+ if (is_dot_or_dotdot(de->d_name))
+ continue;
+
+ entry_len = strlen(de->d_name);
+
+ entry_path = git__malloc(
+ path_len + need_slash + entry_len + 1 + alloc_extra);
+ GITERR_CHECK_ALLOC(entry_path);
+
+ if (path_len)
+ memcpy(entry_path, path, path_len);
+ if (need_slash)
+ entry_path[path_len] = '/';
+ memcpy(&entry_path[path_len + need_slash], de->d_name, entry_len);
+ entry_path[path_len + need_slash + entry_len] = '\0';
+
+ if (git_vector_insert(contents, entry_path) < 0) {
+ closedir(dir);
+ git__free(de_buf);
+ return -1;
+ }
+ }
+
+ closedir(dir);
+ git__free(de_buf);
+
+ if (error != 0)
+ giterr_set(GITERR_OS, "Failed to process directory entry in '%s'", path);
+
+ return error;
+}
+
+int git_path_with_stat_cmp(const void *a, const void *b)
+{
+ const git_path_with_stat *psa = a, *psb = b;
+ return git__strcmp_cb(psa->path, psb->path);
+}
+
+int git_path_dirload_with_stat(
+ const char *path,
+ size_t prefix_len,
+ git_vector *contents)
+{
+ int error;
+ unsigned int i;
+ git_path_with_stat *ps;
+ git_buf full = GIT_BUF_INIT;
+
+ if (git_buf_set(&full, path, prefix_len) < 0)
+ return -1;
+
+ error = git_path_dirload(
+ path, prefix_len, sizeof(git_path_with_stat) + 1, contents);
+ if (error < 0) {
+ git_buf_free(&full);
+ return error;
+ }
+
+ git_vector_foreach(contents, i, ps) {
+ size_t path_len = strlen((char *)ps);
+
+ memmove(ps->path, ps, path_len + 1);
+ ps->path_len = path_len;
+
+ if ((error = git_buf_joinpath(&full, full.ptr, ps->path)) < 0 ||
+ (error = git_path_lstat(full.ptr, &ps->st)) < 0)
+ break;
+
+ git_buf_truncate(&full, prefix_len);
+
+ if (S_ISDIR(ps->st.st_mode)) {
+ ps->path[path_len] = '/';
+ ps->path[path_len + 1] = '\0';
+ }
+ }
+
+ git_buf_free(&full);
+
+ return error;
}
diff --git a/src/path.h b/src/path.h
index ee3607ce9..fd76805e5 100644
--- a/src/path.h
+++ b/src/path.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -9,6 +9,7 @@
#include "common.h"
#include "buffer.h"
+#include "vector.h"
/**
* Path manipulation utils
@@ -112,21 +113,35 @@ extern int git_path_fromurl(git_buf *local_path_out, const char *file_url);
/**
* Check if a file exists and can be accessed.
- * @return GIT_SUCCESS if file exists, < 0 otherwise.
+ * @return true or false
*/
-extern int git_path_exists(const char *path);
+extern bool git_path_exists(const char *path);
/**
* Check if the given path points to a directory.
- * @return GIT_SUCCESS if it is a directory, < 0 otherwise.
+ * @return true or false
*/
-extern int git_path_isdir(const char *path);
+extern bool git_path_isdir(const char *path);
/**
* Check if the given path points to a regular file.
- * @return GIT_SUCCESS if it is a regular file, < 0 otherwise.
+ * @return true or false
*/
-extern int git_path_isfile(const char *path);
+extern bool git_path_isfile(const char *path);
+
+/**
+ * Stat a file and/or link and set error if needed.
+ */
+extern int git_path_lstat(const char *path, struct stat *st);
+
+/**
+ * Check if the parent directory contains the item.
+ *
+ * @param dir Directory to check.
+ * @param item Item that might be in the directory.
+ * @return 0 if item exists in directory, <0 otherwise.
+ */
+extern bool git_path_contains(git_buf *dir, const char *item);
/**
* Check if the given path contains the given subdirectory.
@@ -134,9 +149,9 @@ extern int git_path_isfile(const char *path);
* @param parent Directory path that might contain subdir
* @param subdir Subdirectory name to look for in parent
* @param append_if_exists If true, then subdir will be appended to the parent path if it does exist
- * @return GIT_SUCCESS if subdirectory exists, < 0 otherwise.
+ * @return true if subdirectory exists, false otherwise.
*/
-extern int git_path_contains_dir(git_buf *parent, const char *subdir, int append_if_exists);
+extern bool git_path_contains_dir(git_buf *parent, const char *subdir);
/**
* Check if the given path contains the given file.
@@ -144,9 +159,9 @@ extern int git_path_contains_dir(git_buf *parent, const char *subdir, int append
* @param dir Directory path that might contain file
* @param file File name to look for in parent
* @param append_if_exists If true, then file will be appended to the path if it does exist
- * @return GIT_SUCCESS if file exists, < 0 otherwise.
+ * @return true if file exists, false otherwise.
*/
-extern int git_path_contains_file(git_buf *dir, const char *file, int append_if_exists);
+extern bool git_path_contains_file(git_buf *dir, const char *file);
/**
* Clean up path, prepending base if it is not already rooted.
@@ -189,14 +204,14 @@ extern int git_path_direach(
* Sort function to order two paths.
*/
extern int git_path_cmp(
- const char *name1, int len1, int isdir1,
- const char *name2, int len2, int isdir2);
+ const char *name1, size_t len1, int isdir1,
+ const char *name2, size_t len2, int isdir2);
/**
* Invoke callback up path directory by directory until the ceiling is
* reached (inclusive of a final call at the root_path).
*
- * Returning anything other than GIT_SUCCESS from the callback function
+ * Returning anything other than 0 from the callback function
* will stop the iteration and propogate the error to the caller.
*
* @param pathbuf Buffer the function reads the directory from and
@@ -216,4 +231,48 @@ extern int git_path_walk_up(
int (*fn)(void *state, git_buf *),
void *state);
+/**
+ * Load all directory entries (except '.' and '..') into a vector.
+ *
+ * For cases where `git_path_direach()` is not appropriate, this
+ * allows you to load the filenames in a directory into a vector
+ * of strings. That vector can then be sorted, iterated, or whatever.
+ * Remember to free alloc of the allocated strings when you are done.
+ *
+ * @param path The directory to read from.
+ * @param prefix_len When inserting entries, the trailing part of path
+ * will be prefixed after this length. I.e. given path "/a/b" and
+ * prefix_len 3, the entries will look like "b/e1", "b/e2", etc.
+ * @param alloc_extra Extra bytes to add to each string allocation in
+ * case you want to append anything funny.
+ * @param contents Vector to fill with directory entry names.
+ */
+extern int git_path_dirload(
+ const char *path,
+ size_t prefix_len,
+ size_t alloc_extra,
+ git_vector *contents);
+
+
+typedef struct {
+ struct stat st;
+ size_t path_len;
+ char path[GIT_FLEX_ARRAY];
+} git_path_with_stat;
+
+extern int git_path_with_stat_cmp(const void *a, const void *b);
+
+/**
+ * Load all directory entries along with stat info into a vector.
+ *
+ * This is just like git_path_dirload except that each entry in the
+ * vector is a git_path_with_stat structure that contains both the
+ * path and the stat info, plus directories will have a / suffixed
+ * to their path name.
+ */
+extern int git_path_dirload_with_stat(
+ const char *path,
+ size_t prefix_len,
+ git_vector *contents);
+
#endif
diff --git a/src/pkt.c b/src/pkt.c
index 324265089..95430ddfc 100644
--- a/src/pkt.c
+++ b/src/pkt.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -31,30 +31,28 @@ static int flush_pkt(git_pkt **out)
git_pkt *pkt;
pkt = git__malloc(sizeof(git_pkt));
- if (pkt == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_FLUSH;
*out = pkt;
- return GIT_SUCCESS;
+ return 0;
}
/* the rest of the line will be useful for multi_ack */
-static int ack_pkt(git_pkt **out, const char *GIT_UNUSED(line), size_t GIT_UNUSED(len))
+static int ack_pkt(git_pkt **out, const char *line, size_t len)
{
git_pkt *pkt;
- GIT_UNUSED_ARG(line);
- GIT_UNUSED_ARG(len);
+ GIT_UNUSED(line);
+ GIT_UNUSED(len);
pkt = git__malloc(sizeof(git_pkt));
- if (pkt == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_ACK;
*out = pkt;
- return GIT_SUCCESS;
+ return 0;
}
static int nak_pkt(git_pkt **out)
@@ -62,13 +60,12 @@ static int nak_pkt(git_pkt **out)
git_pkt *pkt;
pkt = git__malloc(sizeof(git_pkt));
- if (pkt == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_NAK;
*out = pkt;
- return GIT_SUCCESS;
+ return 0;
}
static int pack_pkt(git_pkt **out)
@@ -76,13 +73,12 @@ static int pack_pkt(git_pkt **out)
git_pkt *pkt;
pkt = git__malloc(sizeof(git_pkt));
- if (pkt == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_PACK;
*out = pkt;
- return GIT_SUCCESS;
+ return 0;
}
static int comment_pkt(git_pkt **out, const char *line, size_t len)
@@ -90,8 +86,7 @@ static int comment_pkt(git_pkt **out, const char *line, size_t len)
git_pkt_comment *pkt;
pkt = git__malloc(sizeof(git_pkt_comment) + len + 1);
- if (pkt == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(pkt);
pkt->type = GIT_PKT_COMMENT;
memcpy(pkt->comment, line, len);
@@ -99,7 +94,26 @@ static int comment_pkt(git_pkt **out, const char *line, size_t len)
*out = (git_pkt *) pkt;
- return GIT_SUCCESS;
+ return 0;
+}
+
+static int err_pkt(git_pkt **out, const char *line, size_t len)
+{
+ git_pkt_err *pkt;
+
+ /* Remove "ERR " from the line */
+ line += 4;
+ len -= 4;
+ pkt = git__malloc(sizeof(git_pkt_err) + len + 1);
+ GITERR_CHECK_ALLOC(pkt);
+
+ pkt->type = GIT_PKT_ERR;
+ memcpy(pkt->error, line, len);
+ pkt->error[len] = '\0';
+
+ *out = (git_pkt *) pkt;
+
+ return 0;
}
/*
@@ -107,25 +121,22 @@ static int comment_pkt(git_pkt **out, const char *line, size_t len)
*/
static int ref_pkt(git_pkt **out, const char *line, size_t len)
{
- git_pkt_ref *pkt;
int error;
+ git_pkt_ref *pkt;
pkt = git__malloc(sizeof(git_pkt_ref));
- if (pkt == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(pkt);
memset(pkt, 0x0, sizeof(git_pkt_ref));
pkt->type = GIT_PKT_REF;
- error = git_oid_fromstr(&pkt->head.oid, line);
- if (error < GIT_SUCCESS) {
- error = git__throw(error, "Failed to parse reference ID");
- goto out;
- }
+ if ((error = git_oid_fromstr(&pkt->head.oid, line)) < 0)
+ goto error_out;
/* Check for a bit of consistency */
if (line[GIT_OID_HEXSZ] != ' ') {
- error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse ref. No SP");
- goto out;
+ giterr_set(GITERR_NET, "Error parsing pkt-line");
+ error = -1;
+ goto error_out;
}
/* Jump from the name */
@@ -136,10 +147,8 @@ static int ref_pkt(git_pkt **out, const char *line, size_t len)
--len;
pkt->head.name = git__malloc(len + 1);
- if (pkt->head.name == NULL) {
- error = GIT_ENOMEM;
- goto out;
- }
+ GITERR_CHECK_ALLOC(pkt->head.name);
+
memcpy(pkt->head.name, line, len);
pkt->head.name[len] = '\0';
@@ -147,36 +156,35 @@ static int ref_pkt(git_pkt **out, const char *line, size_t len)
pkt->capabilities = strchr(pkt->head.name, '\0') + 1;
}
-out:
- if (error < GIT_SUCCESS)
- git__free(pkt);
- else
- *out = (git_pkt *)pkt;
+ *out = (git_pkt *)pkt;
+ return 0;
+error_out:
+ git__free(pkt);
return error;
}
-static ssize_t parse_len(const char *line)
+static int32_t parse_len(const char *line)
{
char num[PKT_LEN_SIZE + 1];
int i, error;
- int len;
+ int32_t len;
const char *num_end;
memcpy(num, line, PKT_LEN_SIZE);
num[PKT_LEN_SIZE] = '\0';
for (i = 0; i < PKT_LEN_SIZE; ++i) {
- if (!isxdigit(num[i]))
- return GIT_ENOTNUM;
+ if (!isxdigit(num[i])) {
+ giterr_set(GITERR_NET, "Found invalid hex digit in length");
+ return -1;
+ }
}
- error = git__strtol32(&len, num, &num_end, 16);
- if (error < GIT_SUCCESS) {
+ if ((error = git__strtol32(&len, num, &num_end, 16)) < 0)
return error;
- }
- return (unsigned int) len;
+ return len;
}
/*
@@ -192,37 +200,37 @@ static ssize_t parse_len(const char *line)
* in ASCII hexadecimal (including itself)
*/
-int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t bufflen)
+int git_pkt_parse_line(
+ git_pkt **head, const char *line, const char **out, size_t bufflen)
{
- int error = GIT_SUCCESS;
- size_t len;
+ int ret;
+ int32_t len;
/* Not even enough for the length */
if (bufflen > 0 && bufflen < PKT_LEN_SIZE)
- return GIT_ESHORTBUFFER;
+ return GIT_EBUFS;
- error = parse_len(line);
- if (error < GIT_SUCCESS) {
+ len = parse_len(line);
+ if (len < 0) {
/*
* If we fail to parse the length, it might be because the
* server is trying to send us the packfile already.
*/
if (bufflen >= 4 && !git__prefixcmp(line, "PACK")) {
+ giterr_clear();
*out = line;
return pack_pkt(head);
}
- return git__throw(error, "Failed to parse pkt length");
+ return (int)len;
}
- len = error;
-
/*
* If we were given a buffer length, then make sure there is
* enough in the buffer to satisfy this line
*/
- if (bufflen > 0 && bufflen < len)
- return GIT_ESHORTBUFFER;
+ if (bufflen > 0 && bufflen < (size_t)len)
+ return GIT_EBUFS;
line += PKT_LEN_SIZE;
/*
@@ -231,7 +239,7 @@ int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_
*/
if (len == PKT_LEN_SIZE) {
*out = line;
- return GIT_SUCCESS;
+ return 0;
}
if (len == 0) { /* Flush pkt */
@@ -243,22 +251,24 @@ int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_
/* Assming the minimal size is actually 4 */
if (!git__prefixcmp(line, "ACK"))
- error = ack_pkt(head, line, len);
+ ret = ack_pkt(head, line, len);
else if (!git__prefixcmp(line, "NAK"))
- error = nak_pkt(head);
+ ret = nak_pkt(head);
+ else if (!git__prefixcmp(line, "ERR "))
+ ret = err_pkt(head, line, len);
else if (*line == '#')
- error = comment_pkt(head, line, len);
+ ret = comment_pkt(head, line, len);
else
- error = ref_pkt(head, line, len);
+ ret = ref_pkt(head, line, len);
*out = line + len;
- return error;
+ return ret;
}
void git_pkt_free(git_pkt *pkt)
{
- if(pkt->type == GIT_PKT_REF) {
+ if (pkt->type == GIT_PKT_REF) {
git_pkt_ref *p = (git_pkt_ref *) pkt;
git__free(p->head.name);
}
@@ -271,7 +281,7 @@ int git_pkt_buffer_flush(git_buf *buf)
return git_buf_put(buf, pkt_flush_str, strlen(pkt_flush_str));
}
-int git_pkt_send_flush(int s)
+int git_pkt_send_flush(GIT_SOCKET s)
{
return gitno_send(s, pkt_flush_str, strlen(pkt_flush_str), 0);
@@ -281,33 +291,20 @@ static int buffer_want_with_caps(git_remote_head *head, git_transport_caps *caps
{
char capstr[20];
char oid[GIT_OID_HEXSZ +1] = {0};
- int len;
+ unsigned int len;
if (caps->ofs_delta)
- strcpy(capstr, GIT_CAP_OFS_DELTA);
+ strncpy(capstr, GIT_CAP_OFS_DELTA, sizeof(capstr));
- len = strlen("XXXXwant ") + GIT_OID_HEXSZ + 1 /* NUL */ + strlen(capstr) + 1 /* LF */;
- git_buf_grow(buf, buf->size + len);
+ len = (unsigned int)
+ (strlen("XXXXwant ") + GIT_OID_HEXSZ + 1 /* NUL */ +
+ strlen(capstr) + 1 /* LF */);
+ git_buf_grow(buf, git_buf_len(buf) + len);
git_oid_fmt(oid, &head->oid);
return git_buf_printf(buf, "%04xwant %s%c%s\n", len, oid, 0, capstr);
}
-static int send_want_with_caps(git_remote_head *head, git_transport_caps *caps, GIT_SOCKET fd)
-{
- git_buf buf = GIT_BUF_INIT;
- int error;
-
- error = buffer_want_with_caps(head, caps, &buf);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to buffer want with caps");
-
- error = gitno_send(fd, buf.ptr, buf.size, 0);
- git_buf_free(&buf);
-
- return error;
-}
-
/*
* All "want" packets have the same length and format, so what we do
* is overwrite the OID each time.
@@ -316,7 +313,6 @@ static int send_want_with_caps(git_remote_head *head, git_transport_caps *caps,
int git_pkt_buffer_wants(const git_vector *refs, git_transport_caps *caps, git_buf *buf)
{
unsigned int i = 0;
- int error;
git_remote_head *head;
if (caps->common) {
@@ -326,9 +322,8 @@ int git_pkt_buffer_wants(const git_vector *refs, git_transport_caps *caps, git_b
break;
}
- error = buffer_want_with_caps(refs->contents[i], caps, buf);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to buffer want with caps");
+ if (buffer_want_with_caps(refs->contents[i], caps, buf) < 0)
+ return -1;
i++;
}
@@ -344,54 +339,13 @@ int git_pkt_buffer_wants(const git_vector *refs, git_transport_caps *caps, git_b
git_buf_put(buf, pkt_want_prefix, strlen(pkt_want_prefix));
git_buf_put(buf, oid, GIT_OID_HEXSZ);
git_buf_putc(buf, '\n');
+ if (git_buf_oom(buf))
+ return -1;
}
return git_pkt_buffer_flush(buf);
}
-int git_pkt_send_wants(const git_vector *refs, git_transport_caps *caps, int fd)
-{
- unsigned int i = 0;
- int error = GIT_SUCCESS;
- char buf[sizeof(pkt_want_prefix) + GIT_OID_HEXSZ + 1];
- git_remote_head *head;
-
- memcpy(buf, pkt_want_prefix, strlen(pkt_want_prefix));
- buf[sizeof(buf) - 2] = '\n';
- buf[sizeof(buf) - 1] = '\0';
-
- /* If there are common caps, find the first one */
- if (caps->common) {
- for (; i < refs->length; ++i) {
- head = refs->contents[i];
- if (head->local)
- continue;
- else
- break;
- }
-
- error = send_want_with_caps(refs->contents[i], caps, fd);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to send want pkt with caps");
- /* Increase it here so it's correct whether we run this or not */
- i++;
- }
-
- /* Continue from where we left off */
- for (; i < refs->length; ++i) {
- head = refs->contents[i];
- if (head->local)
- continue;
-
- git_oid_fmt(buf + strlen(pkt_want_prefix), &head->oid);
- error = gitno_send(fd, buf, strlen(buf), 0);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to send want pkt");
- }
-
- return git_pkt_send_flush(fd);
-}
-
int git_pkt_buffer_have(git_oid *oid, git_buf *buf)
{
char oidhex[GIT_OID_HEXSZ + 1];
@@ -401,21 +355,7 @@ int git_pkt_buffer_have(git_oid *oid, git_buf *buf)
return git_buf_printf(buf, "%s%s\n", pkt_have_prefix, oidhex);
}
-int git_pkt_send_have(git_oid *oid, int fd)
-{
- char buf[] = "0032have 0000000000000000000000000000000000000000\n";
-
- git_oid_fmt(buf + strlen(pkt_have_prefix), oid);
- return gitno_send(fd, buf, strlen(buf), 0);
-}
-
-
int git_pkt_buffer_done(git_buf *buf)
{
return git_buf_puts(buf, pkt_done_str);
}
-
-int git_pkt_send_done(int fd)
-{
- return gitno_send(fd, pkt_done_str, strlen(pkt_done_str), 0);
-}
diff --git a/src/pkt.h b/src/pkt.h
index 7ce9c6cd9..75442c833 100644
--- a/src/pkt.h
+++ b/src/pkt.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -11,6 +11,7 @@
#include "common.h"
#include "transport.h"
#include "buffer.h"
+#include "posix.h"
#include "git2/net.h"
enum git_pkt_type {
@@ -22,6 +23,7 @@ enum git_pkt_type {
GIT_PKT_NAK,
GIT_PKT_PACK,
GIT_PKT_COMMENT,
+ GIT_PKT_ERR,
};
/* Used for multi-ack */
@@ -63,15 +65,17 @@ typedef struct {
char comment[GIT_FLEX_ARRAY];
} git_pkt_comment;
+typedef struct {
+ enum git_pkt_type type;
+ char error[GIT_FLEX_ARRAY];
+} git_pkt_err;
+
int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t len);
int git_pkt_buffer_flush(git_buf *buf);
-int git_pkt_send_flush(int s);
+int git_pkt_send_flush(GIT_SOCKET s);
int git_pkt_buffer_done(git_buf *buf);
-int git_pkt_send_done(int s);
int git_pkt_buffer_wants(const git_vector *refs, git_transport_caps *caps, git_buf *buf);
-int git_pkt_send_wants(const git_vector *refs, git_transport_caps *caps, int fd);
int git_pkt_buffer_have(git_oid *oid, git_buf *buf);
-int git_pkt_send_have(git_oid *oid, int fd);
void git_pkt_free(git_pkt *pkt);
#endif
diff --git a/src/pool.c b/src/pool.c
new file mode 100644
index 000000000..641292d06
--- /dev/null
+++ b/src/pool.c
@@ -0,0 +1,294 @@
+#include "pool.h"
+#ifndef GIT_WIN32
+#include <unistd.h>
+#endif
+
+struct git_pool_page {
+ git_pool_page *next;
+ uint32_t size;
+ uint32_t avail;
+ char data[GIT_FLEX_ARRAY];
+};
+
+#define GIT_POOL_MIN_USABLE 4
+#define GIT_POOL_MIN_PAGESZ 2 * sizeof(void*)
+
+static void *pool_alloc_page(git_pool *pool, uint32_t size);
+static void pool_insert_page(git_pool *pool, git_pool_page *page);
+
+int git_pool_init(
+ git_pool *pool, uint32_t item_size, uint32_t items_per_page)
+{
+ assert(pool);
+
+ if (!item_size)
+ item_size = 1;
+ /* round up item_size for decent object alignment */
+ if (item_size > 4)
+ item_size = (item_size + 7) & ~7;
+ else if (item_size == 3)
+ item_size = 4;
+
+ if (!items_per_page)
+ items_per_page = git_pool__suggest_items_per_page(item_size);
+ if (item_size * items_per_page < GIT_POOL_MIN_PAGESZ)
+ items_per_page = (GIT_POOL_MIN_PAGESZ + item_size - 1) / item_size;
+
+ memset(pool, 0, sizeof(git_pool));
+ pool->item_size = item_size;
+ pool->page_size = item_size * items_per_page;
+
+ return 0;
+}
+
+void git_pool_clear(git_pool *pool)
+{
+ git_pool_page *scan, *next;
+
+ for (scan = pool->open; scan != NULL; scan = next) {
+ next = scan->next;
+ git__free(scan);
+ }
+ pool->open = NULL;
+
+ for (scan = pool->full; scan != NULL; scan = next) {
+ next = scan->next;
+ git__free(scan);
+ }
+ pool->full = NULL;
+
+ pool->free_list = NULL;
+
+ pool->items = 0;
+
+ pool->has_string_alloc = 0;
+ pool->has_multi_item_alloc = 0;
+ pool->has_large_page_alloc = 0;
+}
+
+void git_pool_swap(git_pool *a, git_pool *b)
+{
+ git_pool temp;
+
+ if (a == b)
+ return;
+
+ memcpy(&temp, a, sizeof(temp));
+ memcpy(a, b, sizeof(temp));
+ memcpy(b, &temp, sizeof(temp));
+}
+
+static void pool_insert_page(git_pool *pool, git_pool_page *page)
+{
+ git_pool_page *scan;
+
+ /* If there are no open pages or this page has the most open space,
+ * insert it at the beginning of the list. This is the common case.
+ */
+ if (pool->open == NULL || pool->open->avail < page->avail) {
+ page->next = pool->open;
+ pool->open = page;
+ return;
+ }
+
+ /* Otherwise insert into sorted position. */
+ for (scan = pool->open;
+ scan->next && scan->next->avail > page->avail;
+ scan = scan->next);
+ page->next = scan->next;
+ scan->next = page;
+}
+
+static void *pool_alloc_page(git_pool *pool, uint32_t size)
+{
+ git_pool_page *page;
+ uint32_t alloc_size;
+
+ if (size <= pool->page_size)
+ alloc_size = pool->page_size;
+ else {
+ alloc_size = size;
+ pool->has_large_page_alloc = 1;
+ }
+
+ page = git__calloc(1, alloc_size + sizeof(git_pool_page));
+ if (!page)
+ return NULL;
+
+ page->size = alloc_size;
+ page->avail = alloc_size - size;
+
+ if (page->avail > 0)
+ pool_insert_page(pool, page);
+ else {
+ page->next = pool->full;
+ pool->full = page;
+ }
+
+ pool->items++;
+
+ return page->data;
+}
+
+GIT_INLINE(void) pool_remove_page(
+ git_pool *pool, git_pool_page *page, git_pool_page *prev)
+{
+ if (prev == NULL)
+ pool->open = page->next;
+ else
+ prev->next = page->next;
+}
+
+void *git_pool_malloc(git_pool *pool, uint32_t items)
+{
+ git_pool_page *scan = pool->open, *prev;
+ uint32_t size = items * pool->item_size;
+ void *ptr = NULL;
+
+ pool->has_string_alloc = 0;
+ if (items > 1)
+ pool->has_multi_item_alloc = 1;
+ else if (pool->free_list != NULL) {
+ ptr = pool->free_list;
+ pool->free_list = *((void **)pool->free_list);
+ return ptr;
+ }
+
+ /* just add a block if there is no open one to accomodate this */
+ if (size >= pool->page_size || !scan || scan->avail < size)
+ return pool_alloc_page(pool, size);
+
+ pool->items++;
+
+ /* find smallest block in free list with space */
+ for (scan = pool->open, prev = NULL;
+ scan->next && scan->next->avail >= size;
+ prev = scan, scan = scan->next);
+
+ /* allocate space from the block */
+ ptr = &scan->data[scan->size - scan->avail];
+ scan->avail -= size;
+
+ /* move to full list if there is almost no space left */
+ if (scan->avail < pool->item_size || scan->avail < GIT_POOL_MIN_USABLE) {
+ pool_remove_page(pool, scan, prev);
+ scan->next = pool->full;
+ pool->full = scan;
+ }
+ /* reorder list if block is now smaller than the one after it */
+ else if (scan->next != NULL && scan->next->avail > scan->avail) {
+ pool_remove_page(pool, scan, prev);
+ pool_insert_page(pool, scan);
+ }
+
+ return ptr;
+}
+
+char *git_pool_strndup(git_pool *pool, const char *str, size_t n)
+{
+ void *ptr = NULL;
+
+ assert(pool && str && pool->item_size == sizeof(char));
+
+ if ((ptr = git_pool_malloc(pool, (uint32_t)(n + 1))) != NULL) {
+ memcpy(ptr, str, n);
+ *(((char *)ptr) + n) = '\0';
+ }
+ pool->has_string_alloc = 1;
+
+ return ptr;
+}
+
+char *git_pool_strdup(git_pool *pool, const char *str)
+{
+ assert(pool && str && pool->item_size == sizeof(char));
+
+ return git_pool_strndup(pool, str, strlen(str));
+}
+
+char *git_pool_strcat(git_pool *pool, const char *a, const char *b)
+{
+ void *ptr;
+ size_t len_a, len_b;
+
+ assert(pool && a && b && pool->item_size == sizeof(char));
+
+ len_a = a ? strlen(a) : 0;
+ len_b = b ? strlen(b) : 0;
+
+ if ((ptr = git_pool_malloc(pool, (uint32_t)(len_a + len_b + 1))) != NULL) {
+ if (len_a)
+ memcpy(ptr, a, len_a);
+ if (len_b)
+ memcpy(((char *)ptr) + len_a, b, len_b);
+ *(((char *)ptr) + len_a + len_b) = '\0';
+ }
+ pool->has_string_alloc = 1;
+
+ return ptr;
+}
+
+void git_pool_free(git_pool *pool, void *ptr)
+{
+ assert(pool && ptr && pool->item_size >= sizeof(void*));
+
+ *((void **)ptr) = pool->free_list;
+ pool->free_list = ptr;
+}
+
+uint32_t git_pool__open_pages(git_pool *pool)
+{
+ uint32_t ct = 0;
+ git_pool_page *scan;
+ for (scan = pool->open; scan != NULL; scan = scan->next) ct++;
+ return ct;
+}
+
+uint32_t git_pool__full_pages(git_pool *pool)
+{
+ uint32_t ct = 0;
+ git_pool_page *scan;
+ for (scan = pool->full; scan != NULL; scan = scan->next) ct++;
+ return ct;
+}
+
+bool git_pool__ptr_in_pool(git_pool *pool, void *ptr)
+{
+ git_pool_page *scan;
+ for (scan = pool->open; scan != NULL; scan = scan->next)
+ if ((void *)scan->data <= ptr &&
+ (void *)(((char *)scan->data) + scan->size) > ptr)
+ return true;
+ for (scan = pool->full; scan != NULL; scan = scan->next)
+ if ((void *)scan->data <= ptr &&
+ (void *)(((char *)scan->data) + scan->size) > ptr)
+ return true;
+ return false;
+}
+
+uint32_t git_pool__system_page_size(void)
+{
+ static uint32_t size = 0;
+
+ if (!size) {
+#ifdef GIT_WIN32
+ SYSTEM_INFO info;
+ GetSystemInfo(&info);
+ size = (uint32_t)info.dwPageSize;
+#else
+ size = (uint32_t)sysconf(_SC_PAGE_SIZE);
+#endif
+
+ size -= 2 * sizeof(void *); /* allow space for malloc overhead */
+ }
+
+ return size;
+}
+
+uint32_t git_pool__suggest_items_per_page(uint32_t item_size)
+{
+ uint32_t page_bytes =
+ git_pool__system_page_size() - sizeof(git_pool_page);
+ return page_bytes / item_size;
+}
+
diff --git a/src/pool.h b/src/pool.h
new file mode 100644
index 000000000..54a2861ed
--- /dev/null
+++ b/src/pool.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_pool_h__
+#define INCLUDE_pool_h__
+
+#include "common.h"
+
+typedef struct git_pool_page git_pool_page;
+
+/**
+ * Chunked allocator.
+ *
+ * A `git_pool` can be used when you want to cheaply allocate
+ * multiple items of the same type and are willing to free them
+ * all together with a single call. The two most common cases
+ * are a set of fixed size items (such as lots of OIDs) or a
+ * bunch of strings.
+ *
+ * Internally, a `git_pool` allocates pages of memory and then
+ * deals out blocks from the trailing unused portion of each page.
+ * The pages guarantee that the number of actual allocations done
+ * will be much smaller than the number of items needed.
+ *
+ * For examples of how to set up a `git_pool` see `git_pool_init`.
+ */
+typedef struct {
+ git_pool_page *open; /* pages with space left */
+ git_pool_page *full; /* pages with no space left */
+ void *free_list; /* optional: list of freed blocks */
+ uint32_t item_size; /* size of single alloc unit in bytes */
+ uint32_t page_size; /* size of page in bytes */
+ uint32_t items;
+ unsigned has_string_alloc : 1; /* was the strdup function used */
+ unsigned has_multi_item_alloc : 1; /* was items ever > 1 in malloc */
+ unsigned has_large_page_alloc : 1; /* are any pages > page_size */
+} git_pool;
+
+#define GIT_POOL_INIT_STRINGPOOL { 0, 0, 0, 1, 4000, 0, 0, 0, 0 }
+
+/**
+ * Initialize a pool.
+ *
+ * To allocation strings, use like this:
+ *
+ * git_pool_init(&string_pool, 1, 0);
+ * my_string = git_pool_strdup(&string_pool, your_string);
+ *
+ * To allocate items of fixed size, use like this:
+ *
+ * git_pool_init(&pool, sizeof(item), 0);
+ * my_item = git_pool_malloc(&pool, 1);
+ *
+ * Of course, you can use this in other ways, but those are the
+ * two most common patterns.
+ */
+extern int git_pool_init(
+ git_pool *pool, uint32_t item_size, uint32_t items_per_page);
+
+/**
+ * Free all items in pool
+ */
+extern void git_pool_clear(git_pool *pool);
+
+/**
+ * Swap two pools with one another
+ */
+extern void git_pool_swap(git_pool *a, git_pool *b);
+
+/**
+ * Allocate space for one or more items from a pool.
+ */
+extern void *git_pool_malloc(git_pool *pool, uint32_t items);
+
+/**
+ * Allocate space and duplicate string data into it.
+ *
+ * This is allowed only for pools with item_size == sizeof(char)
+ */
+extern char *git_pool_strndup(git_pool *pool, const char *str, size_t n);
+
+/**
+ * Allocate space and duplicate a string into it.
+ *
+ * This is allowed only for pools with item_size == sizeof(char)
+ */
+extern char *git_pool_strdup(git_pool *pool, const char *str);
+
+/**
+ * Allocate space for the concatenation of two strings.
+ *
+ * This is allowed only for pools with item_size == sizeof(char)
+ */
+extern char *git_pool_strcat(git_pool *pool, const char *a, const char *b);
+
+/**
+ * Push a block back onto the free list for the pool.
+ *
+ * This is allowed only if the item_size is >= sizeof(void*).
+ *
+ * In some cases, it is helpful to "release" an allocated block
+ * for reuse. Pools don't support a general purpose free, but
+ * they will keep a simple free blocks linked list provided the
+ * native block size is large enough to hold a void pointer
+ */
+extern void git_pool_free(git_pool *pool, void *ptr);
+
+/*
+ * Misc utilities
+ */
+
+extern uint32_t git_pool__open_pages(git_pool *pool);
+
+extern uint32_t git_pool__full_pages(git_pool *pool);
+
+extern bool git_pool__ptr_in_pool(git_pool *pool, void *ptr);
+
+extern uint32_t git_pool__system_page_size(void);
+
+extern uint32_t git_pool__suggest_items_per_page(uint32_t item_size);
+
+#endif
diff --git a/src/posix.c b/src/posix.c
index 916aad726..a9a6af984 100644
--- a/src/posix.c
+++ b/src/posix.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -12,9 +12,20 @@
#ifndef GIT_WIN32
-int p_open(const char *path, int flags)
+int p_open(const char *path, int flags, ...)
{
- return open(path, flags | O_BINARY);
+ mode_t mode = 0;
+
+ if (flags & O_CREAT)
+ {
+ va_list arg_list;
+
+ va_start(arg_list, flags);
+ mode = (mode_t)va_arg(arg_list, int);
+ va_end(arg_list);
+ }
+
+ return open(path, flags | O_BINARY, mode);
}
int p_creat(const char *path, mode_t mode)
@@ -31,27 +42,25 @@ int p_getcwd(char *buffer_out, size_t size)
cwd_buffer = getcwd(buffer_out, size);
if (cwd_buffer == NULL)
- return git__throw(GIT_EOSERR, "Failed to retrieve current working directory");
+ return -1;
git_path_mkposix(buffer_out);
+ git_path_string_to_dir(buffer_out, size); /* append trailing slash */
- git_path_string_to_dir(buffer_out, size); //Ensure the path ends with a trailing slash
-
- return GIT_SUCCESS;
+ return 0;
}
int p_rename(const char *from, const char *to)
{
if (!link(from, to)) {
p_unlink(from);
- return GIT_SUCCESS;
+ return 0;
}
if (!rename(from, to))
- return GIT_SUCCESS;
-
- return GIT_ERROR;
+ return 0;
+ return -1;
}
#endif
@@ -60,11 +69,17 @@ int p_read(git_file fd, void *buf, size_t cnt)
{
char *b = buf;
while (cnt) {
- ssize_t r = read(fd, b, cnt);
+ ssize_t r;
+#ifdef GIT_WIN32
+ assert((size_t)((unsigned int)cnt) == cnt);
+ r = read(fd, b, (unsigned int)cnt);
+#else
+ r = read(fd, b, cnt);
+#endif
if (r < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
- return GIT_EOSERR;
+ return -1;
}
if (!r)
break;
@@ -78,18 +93,24 @@ int p_write(git_file fd, const void *buf, size_t cnt)
{
const char *b = buf;
while (cnt) {
- ssize_t r = write(fd, b, cnt);
+ ssize_t r;
+#ifdef GIT_WIN32
+ assert((size_t)((unsigned int)cnt) == cnt);
+ r = write(fd, b, (unsigned int)cnt);
+#else
+ r = write(fd, b, cnt);
+#endif
if (r < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
- return GIT_EOSERR;
+ return -1;
}
if (!r) {
errno = EPIPE;
- return GIT_EOSERR;
+ return -1;
}
cnt -= r;
b += r;
}
- return GIT_SUCCESS;
+ return 0;
}
diff --git a/src/posix.h b/src/posix.h
index c12b41364..d020d94ac 100644
--- a/src/posix.h
+++ b/src/posix.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -42,7 +42,7 @@ extern int p_write(git_file fd, const void *buf, size_t cnt);
#define p_close(fd) close(fd)
#define p_umask(m) umask(m)
-extern int p_open(const char *path, int flags);
+extern int p_open(const char *path, int flags, ...);
extern int p_creat(const char *path, mode_t mode);
extern int p_getcwd(char *buffer_out, size_t size);
extern int p_rename(const char *from, const char *to);
@@ -54,6 +54,14 @@ extern int p_rename(const char *from, const char *to);
#define p_rmdir(p) rmdir(p)
#define p_chmod(p,m) chmod(p, m)
#define p_access(p,m) access(p,m)
+#define p_recv(s,b,l,f) recv(s,b,l,f)
+#define p_send(s,b,l,f) send(s,b,l,f)
+typedef int GIT_SOCKET;
+#define INVALID_SOCKET -1
+
+#else
+
+typedef SOCKET GIT_SOCKET;
#endif
@@ -66,4 +74,6 @@ extern int p_rename(const char *from, const char *to);
# include "unix/posix.h"
#endif
+#define p_readdir_r(d,e,r) readdir_r(d,e,r)
+
#endif
diff --git a/src/ppc/sha1.c b/src/ppc/sha1.c
index a34bf2557..803b81d0a 100644
--- a/src/ppc/sha1.c
+++ b/src/ppc/sha1.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
diff --git a/src/ppc/sha1.h b/src/ppc/sha1.h
index 7448381ab..aca4e5dda 100644
--- a/src/ppc/sha1.h
+++ b/src/ppc/sha1.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
diff --git a/src/pqueue.c b/src/pqueue.c
index 80713fbba..cb59c13ec 100644
--- a/src/pqueue.c
+++ b/src/pqueue.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -17,14 +17,14 @@ int git_pqueue_init(git_pqueue *q, size_t n, git_pqueue_cmp cmppri)
assert(q);
/* Need to allocate n+1 elements since element 0 isn't used. */
- if ((q->d = git__malloc((n + 1) * sizeof(void *))) == NULL)
- return GIT_ENOMEM;
+ q->d = git__malloc((n + 1) * sizeof(void *));
+ GITERR_CHECK_ALLOC(q->d);
q->size = 1;
q->avail = q->step = (n + 1); /* see comment above about n+1 */
q->cmppri = cmppri;
- return GIT_SUCCESS;
+ return 0;
}
@@ -102,8 +102,8 @@ int git_pqueue_insert(git_pqueue *q, void *d)
/* allocate more memory if necessary */
if (q->size >= q->avail) {
newsize = q->size + q->step;
- if ((tmp = git__realloc(q->d, sizeof(void *) * newsize)) == NULL)
- return GIT_ENOMEM;
+ tmp = git__realloc(q->d, sizeof(void *) * newsize);
+ GITERR_CHECK_ALLOC(tmp);
q->d = tmp;
q->avail = newsize;
@@ -114,7 +114,7 @@ int git_pqueue_insert(git_pqueue *q, void *d)
q->d[i] = d;
bubble_up(q, i);
- return GIT_SUCCESS;
+ return 0;
}
diff --git a/src/pqueue.h b/src/pqueue.h
index 6826055e5..a3e1edd1d 100644
--- a/src/pqueue.h
+++ b/src/pqueue.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
diff --git a/src/protocol.c b/src/protocol.c
index 1f39f105b..6b3861796 100644
--- a/src/protocol.c
+++ b/src/protocol.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -17,10 +17,12 @@ int git_protocol_store_refs(git_protocol *p, const char *data, size_t len)
const char *line_end, *ptr;
if (len == 0) { /* EOF */
- if (buf->size != 0)
- return p->error = git__throw(GIT_ERROR, "EOF and unprocessed data");
- else
+ if (git_buf_len(buf) != 0) {
+ giterr_set(GITERR_NET, "Unexpected EOF");
+ return p->error = -1;
+ } else {
return 0;
+ }
}
git_buf_put(buf, data, len);
@@ -28,23 +30,29 @@ int git_protocol_store_refs(git_protocol *p, const char *data, size_t len)
while (1) {
git_pkt *pkt;
- if (buf->size == 0)
+ if (git_buf_len(buf) == 0)
return 0;
- error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->size);
- if (error == GIT_ESHORTBUFFER)
+ error = git_pkt_parse_line(&pkt, ptr, &line_end, git_buf_len(buf));
+ if (error == GIT_EBUFS)
return 0; /* Ask for more */
- if (error < GIT_SUCCESS)
- return p->error = git__rethrow(error, "Failed to parse pkt-line");
+ if (error < 0)
+ return p->error = -1;
git_buf_consume(buf, line_end);
- error = git_vector_insert(refs, pkt);
- if (error < GIT_SUCCESS)
- return p->error = git__rethrow(error, "Failed to add pkt to list");
+
+ if (pkt->type == GIT_PKT_ERR) {
+ giterr_set(GITERR_NET, "Remote error: %s", ((git_pkt_err *)pkt)->error);
+ git__free(pkt);
+ return -1;
+ }
+
+ if (git_vector_insert(refs, pkt) < 0)
+ return p->error = -1;
if (pkt->type == GIT_PKT_FLUSH)
p->flush = 1;
}
- return error;
+ return 0;
}
diff --git a/src/protocol.h b/src/protocol.h
index e3315738a..a6c3e0735 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
diff --git a/src/reflog.c b/src/reflog.c
index 970e7c2de..3ea073e65 100644
--- a/src/reflog.c
+++ b/src/reflog.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -16,23 +16,21 @@ static int reflog_init(git_reflog **reflog, git_reference *ref)
*reflog = NULL;
- log = git__malloc(sizeof(git_reflog));
- if (log == NULL)
- return GIT_ENOMEM;
-
- memset(log, 0x0, sizeof(git_reflog));
+ log = git__calloc(1, sizeof(git_reflog));
+ GITERR_CHECK_ALLOC(log);
log->ref_name = git__strdup(ref->name);
+ GITERR_CHECK_ALLOC(log->ref_name);
if (git_vector_init(&log->entries, 0, NULL) < 0) {
git__free(log->ref_name);
git__free(log);
- return GIT_ENOMEM;
+ return -1;
}
*reflog = log;
- return GIT_SUCCESS;
+ return 0;
}
static int reflog_write(const char *log_path, const char *oid_old,
@@ -42,9 +40,22 @@ static int reflog_write(const char *log_path, const char *oid_old,
int error;
git_buf log = GIT_BUF_INIT;
git_filebuf fbuf = GIT_FILEBUF_INIT;
+ bool trailing_newline = false;
assert(log_path && oid_old && oid_new && committer);
+ if (msg) {
+ const char *newline = strchr(msg, '\n');
+ if (newline) {
+ if (*(newline + 1) == '\0')
+ trailing_newline = true;
+ else {
+ giterr_set(GITERR_INVALID, "Reflog message cannot contain newline");
+ return -1;
+ }
+ }
+ }
+
git_buf_puts(&log, oid_old);
git_buf_putc(&log, ' ');
@@ -54,68 +65,58 @@ static int reflog_write(const char *log_path, const char *oid_old,
git_buf_truncate(&log, log.size - 1); /* drop LF */
if (msg) {
- if (strchr(msg, '\n')) {
- git_buf_free(&log);
- return git__throw(GIT_ERROR, "Reflog message cannot contain newline");
- }
-
git_buf_putc(&log, '\t');
git_buf_puts(&log, msg);
}
- git_buf_putc(&log, '\n');
+ if (!trailing_newline)
+ git_buf_putc(&log, '\n');
- if ((error = git_buf_lasterror(&log)) < GIT_SUCCESS) {
+ if (git_buf_oom(&log)) {
git_buf_free(&log);
- return git__rethrow(error, "Failed to write reflog. Memory allocation failure");
+ return -1;
}
- if ((error = git_filebuf_open(&fbuf, log_path, GIT_FILEBUF_APPEND)) < GIT_SUCCESS) {
- git_buf_free(&log);
- return git__rethrow(error, "Failed to write reflog. Cannot open reflog `%s`", log_path);
+ error = git_filebuf_open(&fbuf, log_path, GIT_FILEBUF_APPEND);
+ if (!error) {
+ if ((error = git_filebuf_write(&fbuf, log.ptr, log.size)) < 0)
+ git_filebuf_cleanup(&fbuf);
+ else
+ error = git_filebuf_commit(&fbuf, GIT_REFLOG_FILE_MODE);
}
- git_filebuf_write(&fbuf, log.ptr, log.size);
- error = git_filebuf_commit(&fbuf, GIT_REFLOG_FILE_MODE);
-
git_buf_free(&log);
- return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write reflog");
+ return error;
}
static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size)
{
- int error = GIT_SUCCESS;
const char *ptr;
git_reflog_entry *entry;
-#define seek_forward(_increase) { \
+#define seek_forward(_increase) do { \
if (_increase >= buf_size) { \
- if (entry->committer) \
- git__free(entry->committer); \
- git__free(entry); \
- return git__throw(GIT_ERROR, "Failed to seek forward. Buffer size exceeded"); \
+ giterr_set(GITERR_INVALID, "Ran out of data while parsing reflog"); \
+ goto fail; \
} \
buf += _increase; \
buf_size -= _increase; \
-}
+ } while (0)
while (buf_size > GIT_REFLOG_SIZE_MIN) {
entry = git__malloc(sizeof(git_reflog_entry));
- if (entry == NULL)
- return GIT_ENOMEM;
- entry->committer = NULL;
+ GITERR_CHECK_ALLOC(entry);
- if (git_oid_fromstrn(&entry->oid_old, buf, GIT_OID_HEXSZ) < GIT_SUCCESS) {
- git__free(entry);
- return GIT_ERROR;
- }
+ entry->committer = git__malloc(sizeof(git_signature));
+ GITERR_CHECK_ALLOC(entry->committer);
+
+ if (git_oid_fromstrn(&entry->oid_old, buf, GIT_OID_HEXSZ) < 0)
+ goto fail;
seek_forward(GIT_OID_HEXSZ + 1);
- if (git_oid_fromstrn(&entry->oid_cur, buf, GIT_OID_HEXSZ) < GIT_SUCCESS) {
- git__free(entry);
- return GIT_ERROR;
- }
+ if (git_oid_fromstrn(&entry->oid_cur, buf, GIT_OID_HEXSZ) < 0)
+ goto fail;
seek_forward(GIT_OID_HEXSZ + 1);
ptr = buf;
@@ -124,17 +125,8 @@ static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size)
while (*buf && *buf != '\t' && *buf != '\n')
seek_forward(1);
- entry->committer = git__malloc(sizeof(git_signature));
- if (entry->committer == NULL) {
- git__free(entry);
- return GIT_ENOMEM;
- }
-
- if ((error = git_signature__parse(entry->committer, &ptr, buf + 1, NULL, *buf)) < GIT_SUCCESS) {
- git__free(entry->committer);
- git__free(entry);
- return git__rethrow(error, "Failed to parse reflog. Could not parse signature");
- }
+ if (git_signature__parse(entry->committer, &ptr, buf + 1, NULL, *buf) < 0)
+ goto fail;
if (*buf == '\t') {
/* We got a message. Read everything till we reach LF. */
@@ -145,19 +137,27 @@ static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size)
seek_forward(1);
entry->msg = git__strndup(ptr, buf - ptr);
+ GITERR_CHECK_ALLOC(entry->msg);
} else
entry->msg = NULL;
while (*buf && *buf == '\n' && buf_size > 1)
seek_forward(1);
- if ((error = git_vector_insert(&log->entries, entry)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to parse reflog. Could not add new entry");
+ if (git_vector_insert(&log->entries, entry) < 0)
+ goto fail;
}
+ return 0;
+
#undef seek_forward
- return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse reflog");
+fail:
+ if (entry) {
+ git__free(entry->committer);
+ git__free(entry);
+ }
+ return -1;
}
void git_reflog_free(git_reflog *reflog)
@@ -183,33 +183,29 @@ int git_reflog_read(git_reflog **reflog, git_reference *ref)
{
int error;
git_buf log_path = GIT_BUF_INIT;
- git_fbuffer log_file = GIT_FBUFFER_INIT;
+ git_buf log_file = GIT_BUF_INIT;
git_reflog *log = NULL;
*reflog = NULL;
- if ((error = reflog_init(&log, ref)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to read reflog. Cannot init reflog");
+ if (reflog_init(&log, ref) < 0)
+ return -1;
error = git_buf_join_n(&log_path, '/', 3,
ref->owner->path_repository, GIT_REFLOG_DIR, ref->name);
- if (error < GIT_SUCCESS)
- goto cleanup;
- if ((error = git_futils_readbuffer(&log_file, log_path.ptr)) < GIT_SUCCESS) {
- git__rethrow(error, "Failed to read reflog. Cannot read file `%s`", log_path.ptr);
- goto cleanup;
- }
+ if (!error)
+ error = git_futils_readbuffer(&log_file, log_path.ptr);
- if ((error = reflog_parse(log, log_file.data, log_file.len)) < GIT_SUCCESS)
- git__rethrow(error, "Failed to read reflog");
- else
- *reflog = log;
+ if (!error)
+ error = reflog_parse(log, log_file.ptr, log_file.size);
-cleanup:
- if (error != GIT_SUCCESS && log != NULL)
+ if (!error)
+ *reflog = log;
+ else
git_reflog_free(log);
- git_futils_freebuffer(&log_file);
+
+ git_buf_free(&log_file);
git_buf_free(&log_path);
return error;
@@ -225,45 +221,42 @@ int git_reflog_write(git_reference *ref, const git_oid *oid_old,
git_reference *r;
const git_oid *oid;
- if ((error = git_reference_resolve(&r, ref)) < GIT_SUCCESS)
- return git__rethrow(error,
- "Failed to write reflog. Cannot resolve reference `%s`", ref->name);
+ if ((error = git_reference_resolve(&r, ref)) < 0)
+ return error;
oid = git_reference_oid(r);
if (oid == NULL) {
- error = git__throw(GIT_ERROR,
+ giterr_set(GITERR_REFERENCE,
"Failed to write reflog. Cannot resolve reference `%s`", r->name);
git_reference_free(r);
- return error;
+ return -1;
}
- git_oid_to_string(new, GIT_OID_HEXSZ+1, oid);
+ git_oid_tostr(new, GIT_OID_HEXSZ+1, oid);
git_reference_free(r);
error = git_buf_join_n(&log_path, '/', 3,
ref->owner->path_repository, GIT_REFLOG_DIR, ref->name);
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
- if (git_path_exists(log_path.ptr)) {
+ if (git_path_exists(log_path.ptr) == false) {
error = git_futils_mkpath2file(log_path.ptr, GIT_REFLOG_DIR_MODE);
- if (error < GIT_SUCCESS)
- git__rethrow(error,
- "Failed to write reflog. Cannot create reflog directory");
- } else if (git_path_isfile(log_path.ptr)) {
- error = git__throw(GIT_ERROR,
+ } else if (git_path_isfile(log_path.ptr) == false) {
+ giterr_set(GITERR_REFERENCE,
"Failed to write reflog. `%s` is directory", log_path.ptr);
+ error = -1;
} else if (oid_old == NULL) {
- error = git__throw(GIT_ERROR,
+ giterr_set(GITERR_REFERENCE,
"Failed to write reflog. Old OID cannot be NULL for existing reference");
+ error = -1;
}
-
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
if (oid_old)
- git_oid_to_string(old, sizeof(old), oid_old);
+ git_oid_tostr(old, sizeof(old), oid_old);
else
p_snprintf(old, sizeof(old), "%0*d", GIT_OID_HEXSZ, 0);
@@ -280,13 +273,13 @@ int git_reflog_rename(git_reference *ref, const char *new_name)
git_buf old_path = GIT_BUF_INIT;
git_buf new_path = GIT_BUF_INIT;
- if (git_buf_join_n(&old_path, '/', 3, ref->owner->path_repository,
- GIT_REFLOG_DIR, ref->name) &&
- git_buf_join_n(&new_path, '/', 3, ref->owner->path_repository,
- GIT_REFLOG_DIR, new_name))
+ if (!git_buf_join_n(&old_path, '/', 3, ref->owner->path_repository,
+ GIT_REFLOG_DIR, ref->name) &&
+ !git_buf_join_n(&new_path, '/', 3, ref->owner->path_repository,
+ GIT_REFLOG_DIR, new_name))
error = p_rename(git_buf_cstr(&old_path), git_buf_cstr(&new_path));
else
- error = GIT_ENOMEM;
+ error = -1;
git_buf_free(&old_path);
git_buf_free(&new_path);
@@ -296,13 +289,13 @@ int git_reflog_rename(git_reference *ref, const char *new_name)
int git_reflog_delete(git_reference *ref)
{
- int error = GIT_SUCCESS;
+ int error;
git_buf path = GIT_BUF_INIT;
- error = git_buf_join_n(&path, '/', 3,
- ref->owner->path_repository, GIT_REFLOG_DIR, ref->name);
+ error = git_buf_join_n(
+ &path, '/', 3, ref->owner->path_repository, GIT_REFLOG_DIR, ref->name);
- if (error == GIT_SUCCESS && git_path_exists(path.ptr) == 0)
+ if (!error && git_path_exists(path.ptr))
error = p_unlink(path.ptr);
git_buf_free(&path);
diff --git a/src/reflog.h b/src/reflog.h
index 44b063700..33cf0776c 100644
--- a/src/reflog.h
+++ b/src/reflog.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
diff --git a/src/refs.c b/src/refs.c
index 86e5f5dba..1ef3e13a4 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -15,7 +15,10 @@
#include <git2/tag.h>
#include <git2/object.h>
-#define MAX_NESTING_LEVEL 5
+GIT__USE_STRMAP;
+
+#define DEFAULT_NESTING_LEVEL 5
+#define MAX_NESTING_LEVEL 10
enum {
GIT_PACKREF_HAS_PEEL = 1,
@@ -29,18 +32,16 @@ struct packref {
char name[GIT_FLEX_ARRAY];
};
-static const int default_table_size = 32;
-
static int reference_read(
- git_fbuffer *file_content,
+ git_buf *file_content,
time_t *mtime,
const char *repo_path,
const char *ref_name,
int *updated);
/* loose refs */
-static int loose_parse_symbolic(git_reference *ref, git_fbuffer *file_content);
-static int loose_parse_oid(git_oid *ref, git_fbuffer *file_content);
+static int loose_parse_symbolic(git_reference *ref, git_buf *file_content);
+static int loose_parse_oid(git_oid *ref, git_buf *file_content);
static int loose_lookup(git_reference *ref);
static int loose_lookup_to_packfile(struct packref **ref_out,
git_repository *repo, const char *name);
@@ -61,7 +62,7 @@ static int packed_lookup(git_reference *ref);
static int packed_write(git_repository *repo);
/* internal helpers */
-static int reference_available(git_repository *repo,
+static int reference_path_available(git_repository *repo,
const char *ref, const char *old_ref);
static int reference_delete(git_reference *ref);
static int reference_lookup(git_reference *ref);
@@ -97,151 +98,151 @@ static int reference_alloc(
assert(ref_out && repo && name);
reference = git__malloc(sizeof(git_reference));
- if (reference == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(reference);
memset(reference, 0x0, sizeof(git_reference));
reference->owner = repo;
reference->name = git__strdup(name);
- if (reference->name == NULL) {
- free(reference);
- return GIT_ENOMEM;
- }
+ GITERR_CHECK_ALLOC(reference->name);
*ref_out = reference;
- return GIT_SUCCESS;
+ return 0;
}
-static int reference_read(git_fbuffer *file_content, time_t *mtime, const char *repo_path, const char *ref_name, int *updated)
+static int reference_read(
+ git_buf *file_content,
+ time_t *mtime,
+ const char *repo_path,
+ const char *ref_name,
+ int *updated)
{
git_buf path = GIT_BUF_INIT;
- int error = GIT_SUCCESS;
+ int result;
assert(file_content && repo_path && ref_name);
/* Determine the full path of the file */
- if ((error = git_buf_joinpath(&path, repo_path, ref_name)) == GIT_SUCCESS)
- error = git_futils_readbuffer_updated(file_content, path.ptr, mtime, updated);
+ if (git_buf_joinpath(&path, repo_path, ref_name) < 0)
+ return -1;
+ result = git_futils_readbuffer_updated(file_content, path.ptr, mtime, updated);
git_buf_free(&path);
-
- return error;
+ return result;
}
-static int loose_parse_symbolic(git_reference *ref, git_fbuffer *file_content)
+static int loose_parse_symbolic(git_reference *ref, git_buf *file_content)
{
- const unsigned int header_len = strlen(GIT_SYMREF);
+ const unsigned int header_len = (unsigned int)strlen(GIT_SYMREF);
const char *refname_start;
char *eol;
- refname_start = (const char *)file_content->data;
+ refname_start = (const char *)file_content->ptr;
- if (file_content->len < (header_len + 1))
- return git__throw(GIT_EOBJCORRUPTED,
- "Failed to parse loose reference. Object too short");
+ if (git_buf_len(file_content) < header_len + 1)
+ goto corrupt;
/*
* Assume we have already checked for the header
* before calling this function
*/
-
refname_start += header_len;
ref->target.symbolic = git__strdup(refname_start);
- if (ref->target.symbolic == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(ref->target.symbolic);
/* remove newline at the end of file */
eol = strchr(ref->target.symbolic, '\n');
if (eol == NULL)
- return git__throw(GIT_EOBJCORRUPTED,
- "Failed to parse loose reference. Missing EOL");
+ goto corrupt;
*eol = '\0';
if (eol[-1] == '\r')
eol[-1] = '\0';
- return GIT_SUCCESS;
+ return 0;
+
+corrupt:
+ giterr_set(GITERR_REFERENCE, "Corrupted loose reference file");
+ return -1;
}
-static int loose_parse_oid(git_oid *oid, git_fbuffer *file_content)
+static int loose_parse_oid(git_oid *oid, git_buf *file_content)
{
- int error;
char *buffer;
- buffer = (char *)file_content->data;
+ buffer = (char *)file_content->ptr;
/* File format: 40 chars (OID) + newline */
- if (file_content->len < GIT_OID_HEXSZ + 1)
- return git__throw(GIT_EOBJCORRUPTED,
- "Failed to parse loose reference. Reference too short");
+ if (git_buf_len(file_content) < GIT_OID_HEXSZ + 1)
+ goto corrupt;
- if ((error = git_oid_fromstr(oid, buffer)) < GIT_SUCCESS)
- return git__rethrow(GIT_EOBJCORRUPTED, "Failed to parse loose reference.");
+ if (git_oid_fromstr(oid, buffer) < 0)
+ goto corrupt;
buffer = buffer + GIT_OID_HEXSZ;
if (*buffer == '\r')
buffer++;
if (*buffer != '\n')
- return git__throw(GIT_EOBJCORRUPTED,
- "Failed to parse loose reference. Missing EOL");
+ goto corrupt;
+
+ return 0;
- return GIT_SUCCESS;
+corrupt:
+ giterr_set(GITERR_REFERENCE, "Corrupted loose reference file");
+ return -1;
}
-static git_rtype loose_guess_rtype(const git_buf *full_path)
+static git_ref_t loose_guess_rtype(const git_buf *full_path)
{
- git_fbuffer ref_file = GIT_FBUFFER_INIT;
- git_rtype type;
+ git_buf ref_file = GIT_BUF_INIT;
+ git_ref_t type;
type = GIT_REF_INVALID;
- if (git_futils_readbuffer(&ref_file, full_path->ptr) == GIT_SUCCESS) {
- if (git__prefixcmp((const char *)(ref_file.data), GIT_SYMREF) == 0)
+ if (git_futils_readbuffer(&ref_file, full_path->ptr) == 0) {
+ if (git__prefixcmp((const char *)(ref_file.ptr), GIT_SYMREF) == 0)
type = GIT_REF_SYMBOLIC;
else
type = GIT_REF_OID;
}
- git_futils_freebuffer(&ref_file);
+ git_buf_free(&ref_file);
return type;
}
static int loose_lookup(git_reference *ref)
{
- int error = GIT_SUCCESS, updated;
- git_fbuffer ref_file = GIT_FBUFFER_INIT;
+ int result, updated;
+ git_buf ref_file = GIT_BUF_INIT;
- if (reference_read(&ref_file, &ref->mtime,
- ref->owner->path_repository, ref->name, &updated) < GIT_SUCCESS)
- return git__throw(GIT_ENOTFOUND, "Failed to lookup loose reference");
+ result = reference_read(&ref_file, &ref->mtime,
+ ref->owner->path_repository, ref->name, &updated);
+
+ if (result < 0)
+ return result;
if (!updated)
- return GIT_SUCCESS;
+ return 0;
if (ref->flags & GIT_REF_SYMBOLIC) {
- free(ref->target.symbolic);
+ git__free(ref->target.symbolic);
ref->target.symbolic = NULL;
}
ref->flags = 0;
- if (git__prefixcmp((const char *)(ref_file.data), GIT_SYMREF) == 0) {
+ if (git__prefixcmp((const char *)(ref_file.ptr), GIT_SYMREF) == 0) {
ref->flags |= GIT_REF_SYMBOLIC;
- error = loose_parse_symbolic(ref, &ref_file);
+ result = loose_parse_symbolic(ref, &ref_file);
} else {
ref->flags |= GIT_REF_OID;
- error = loose_parse_oid(&ref->target.oid, &ref_file);
+ result = loose_parse_oid(&ref->target.oid, &ref_file);
}
- git_futils_freebuffer(&ref_file);
-
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to lookup loose reference");
-
- return GIT_SUCCESS;
+ git_buf_free(&ref_file);
+ return result;
}
static int loose_lookup_to_packfile(
@@ -249,53 +250,59 @@ static int loose_lookup_to_packfile(
git_repository *repo,
const char *name)
{
- int error = GIT_SUCCESS;
- git_fbuffer ref_file = GIT_FBUFFER_INIT;
+ git_buf ref_file = GIT_BUF_INIT;
struct packref *ref = NULL;
size_t name_len;
*ref_out = NULL;
- error = reference_read(&ref_file, NULL, repo->path_repository, name, NULL);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (reference_read(&ref_file, NULL, repo->path_repository, name, NULL) < 0)
+ return -1;
name_len = strlen(name);
ref = git__malloc(sizeof(struct packref) + name_len + 1);
+ GITERR_CHECK_ALLOC(ref);
memcpy(ref->name, name, name_len);
ref->name[name_len] = 0;
- error = loose_parse_oid(&ref->oid, &ref_file);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (loose_parse_oid(&ref->oid, &ref_file) < 0) {
+ git_buf_free(&ref_file);
+ git__free(ref);
+ return -1;
+ }
ref->flags = GIT_PACKREF_WAS_LOOSE;
*ref_out = ref;
- git_futils_freebuffer(&ref_file);
- return GIT_SUCCESS;
-
-cleanup:
- git_futils_freebuffer(&ref_file);
- free(ref);
- return git__rethrow(error, "Failed to lookup loose reference");
+ git_buf_free(&ref_file);
+ return 0;
}
static int loose_write(git_reference *ref)
{
git_filebuf file = GIT_FILEBUF_INIT;
git_buf ref_path = GIT_BUF_INIT;
- int error;
struct stat st;
- error = git_buf_joinpath(&ref_path, ref->owner->path_repository, ref->name);
- if (error < GIT_SUCCESS)
- goto unlock;
+ if (git_buf_joinpath(&ref_path, ref->owner->path_repository, ref->name) < 0)
+ return -1;
+
+ /* Remove a possibly existing empty directory hierarchy
+ * which name would collide with the reference name
+ */
+ if (git_path_isdir(git_buf_cstr(&ref_path)) &&
+ (git_futils_rmdir_r(git_buf_cstr(&ref_path), GIT_DIRREMOVAL_ONLY_EMPTY_DIRS) < 0)) {
+ git_buf_free(&ref_path);
+ return -1;
+ }
+
+ if (git_filebuf_open(&file, ref_path.ptr, GIT_FILEBUF_FORCE) < 0) {
+ git_buf_free(&ref_path);
+ return -1;
+ }
- error = git_filebuf_open(&file, ref_path.ptr, GIT_FILEBUF_FORCE);
- if (error < GIT_SUCCESS)
- goto unlock;
+ git_buf_free(&ref_path);
if (ref->flags & GIT_REF_OID) {
char oid[GIT_OID_HEXSZ + 1];
@@ -303,29 +310,18 @@ static int loose_write(git_reference *ref)
git_oid_fmt(oid, &ref->target.oid);
oid[GIT_OID_HEXSZ] = '\0';
- error = git_filebuf_printf(&file, "%s\n", oid);
- if (error < GIT_SUCCESS)
- goto unlock;
+ git_filebuf_printf(&file, "%s\n", oid);
- } else if (ref->flags & GIT_REF_SYMBOLIC) { /* GIT_REF_SYMBOLIC */
- error = git_filebuf_printf(&file, GIT_SYMREF "%s\n", ref->target.symbolic);
+ } else if (ref->flags & GIT_REF_SYMBOLIC) {
+ git_filebuf_printf(&file, GIT_SYMREF "%s\n", ref->target.symbolic);
} else {
- error = git__throw(GIT_EOBJCORRUPTED,
- "Failed to write reference. Invalid reference type");
- goto unlock;
+ assert(0); /* don't let this happen */
}
- if (p_stat(ref_path.ptr, &st) == GIT_SUCCESS)
+ if (p_stat(ref_path.ptr, &st) == 0)
ref->mtime = st.st_mtime;
- git_buf_free(&ref_path);
-
return git_filebuf_commit(&file, GIT_REFS_FILE_MODE);
-
-unlock:
- git_buf_free(&ref_path);
- git_filebuf_cleanup(&file);
- return git__rethrow(error, "Failed to write loose reference");
}
static int packed_parse_peel(
@@ -339,34 +335,32 @@ static int packed_parse_peel(
/* Ensure it's not the first entry of the file */
if (tag_ref == NULL)
- return git__throw(GIT_EPACKEDREFSCORRUPTED,
- "Failed to parse packed reference. "
- "Reference is the first entry of the file");
+ goto corrupt;
/* Ensure reference is a tag */
if (git__prefixcmp(tag_ref->name, GIT_REFS_TAGS_DIR) != 0)
- return git__throw(GIT_EPACKEDREFSCORRUPTED,
- "Failed to parse packed reference. Reference is not a tag");
+ goto corrupt;
if (buffer + GIT_OID_HEXSZ >= buffer_end)
- return git__throw(GIT_EPACKEDREFSCORRUPTED,
- "Failed to parse packed reference. Buffer too small");
+ goto corrupt;
/* Is this a valid object id? */
- if (git_oid_fromstr(&tag_ref->peel, buffer) < GIT_SUCCESS)
- return git__throw(GIT_EPACKEDREFSCORRUPTED,
- "Failed to parse packed reference. Not a valid object ID");
+ if (git_oid_fromstr(&tag_ref->peel, buffer) < 0)
+ goto corrupt;
buffer = buffer + GIT_OID_HEXSZ;
if (*buffer == '\r')
buffer++;
if (*buffer != '\n')
- return git__throw(GIT_EPACKEDREFSCORRUPTED,
- "Failed to parse packed reference. Buffer not terminated correctly");
+ goto corrupt;
*buffer_out = buffer + 1;
- return GIT_SUCCESS;
+ return 0;
+
+corrupt:
+ giterr_set(GITERR_REFERENCE, "The packed references file is corrupted");
+ return -1;
}
static int packed_parse_oid(
@@ -379,26 +373,20 @@ static int packed_parse_oid(
const char *buffer = *buffer_out;
const char *refname_begin, *refname_end;
- int error = GIT_SUCCESS;
size_t refname_len;
git_oid id;
refname_begin = (buffer + GIT_OID_HEXSZ + 1);
- if (refname_begin >= buffer_end ||
- refname_begin[-1] != ' ') {
- error = GIT_EPACKEDREFSCORRUPTED;
- goto cleanup;
- }
+ if (refname_begin >= buffer_end || refname_begin[-1] != ' ')
+ goto corrupt;
/* Is this a valid object id? */
- if ((error = git_oid_fromstr(&id, buffer)) < GIT_SUCCESS)
- goto cleanup;
+ if (git_oid_fromstr(&id, buffer) < 0)
+ goto corrupt;
refname_end = memchr(refname_begin, '\n', buffer_end - refname_begin);
- if (refname_end == NULL) {
- error = GIT_EPACKEDREFSCORRUPTED;
- goto cleanup;
- }
+ if (refname_end == NULL)
+ goto corrupt;
if (refname_end[-1] == '\r')
refname_end--;
@@ -406,6 +394,7 @@ static int packed_parse_oid(
refname_len = refname_end - refname_begin;
ref = git__malloc(sizeof(struct packref) + refname_len + 1);
+ GITERR_CHECK_ALLOC(ref);
memcpy(ref->name, refname_begin, refname_len);
ref->name[refname_len] = 0;
@@ -417,32 +406,28 @@ static int packed_parse_oid(
*ref_out = ref;
*buffer_out = refname_end + 1;
- return GIT_SUCCESS;
+ return 0;
-cleanup:
- free(ref);
- return git__rethrow(error, "Failed to parse OID of packed reference");
+corrupt:
+ git__free(ref);
+ giterr_set(GITERR_REFERENCE, "The packed references file is corrupted");
+ return -1;
}
static int packed_load(git_repository *repo)
{
- int error = GIT_SUCCESS, updated;
- git_fbuffer packfile = GIT_FBUFFER_INIT;
+ int result, updated;
+ git_buf packfile = GIT_BUF_INIT;
const char *buffer_start, *buffer_end;
git_refcache *ref_cache = &repo->references;
/* First we make sure we have allocated the hash table */
if (ref_cache->packfile == NULL) {
- ref_cache->packfile = git_hashtable_alloc(
- default_table_size, git_hash__strhash_cb, git_hash__strcmp_cb);
-
- if (ref_cache->packfile == NULL) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
+ ref_cache->packfile = git_strmap_alloc();
+ GITERR_CHECK_ALLOC(ref_cache->packfile);
}
- error = reference_read(&packfile, &ref_cache->packfile_time,
+ result = reference_read(&packfile, &ref_cache->packfile_time,
repo->path_repository, GIT_PACKEDREFS_FILE, &updated);
/*
@@ -452,62 +437,59 @@ static int packed_load(git_repository *repo)
* for us here, so just return. Anything else means we need to
* refresh the packed refs.
*/
- if (error == GIT_ENOTFOUND) {
- git_hashtable_clear(ref_cache->packfile);
- return GIT_SUCCESS;
- } else if (error < GIT_SUCCESS) {
- return git__rethrow(error, "Failed to read packed refs");
- } else if (!updated) {
- return GIT_SUCCESS;
+ if (result == GIT_ENOTFOUND) {
+ git_strmap_clear(ref_cache->packfile);
+ return 0;
}
+ if (result < 0)
+ return -1;
+
+ if (!updated)
+ return 0;
+
/*
* At this point, we want to refresh the packed refs. We already
* have the contents in our buffer.
*/
+ git_strmap_clear(ref_cache->packfile);
- git_hashtable_clear(ref_cache->packfile);
-
- buffer_start = (const char *)packfile.data;
- buffer_end = (const char *)(buffer_start) + packfile.len;
+ buffer_start = (const char *)packfile.ptr;
+ buffer_end = (const char *)(buffer_start) + packfile.size;
while (buffer_start < buffer_end && buffer_start[0] == '#') {
buffer_start = strchr(buffer_start, '\n');
- if (buffer_start == NULL) {
- error = GIT_EPACKEDREFSCORRUPTED;
- goto cleanup;
- }
+ if (buffer_start == NULL)
+ goto parse_failed;
+
buffer_start++;
}
while (buffer_start < buffer_end) {
+ int err;
struct packref *ref = NULL;
- error = packed_parse_oid(&ref, &buffer_start, buffer_end);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (packed_parse_oid(&ref, &buffer_start, buffer_end) < 0)
+ goto parse_failed;
if (buffer_start[0] == '^') {
- error = packed_parse_peel(ref, &buffer_start, buffer_end);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (packed_parse_peel(ref, &buffer_start, buffer_end) < 0)
+ goto parse_failed;
}
- error = git_hashtable_insert(ref_cache->packfile, ref->name, ref);
- if (error < GIT_SUCCESS) {
- free(ref);
- goto cleanup;
- }
+ git_strmap_insert(ref_cache->packfile, ref->name, ref, err);
+ if (err < 0)
+ goto parse_failed;
}
- git_futils_freebuffer(&packfile);
- return GIT_SUCCESS;
+ git_buf_free(&packfile);
+ return 0;
-cleanup:
- git_hashtable_free(ref_cache->packfile);
+parse_failed:
+ git_strmap_free(ref_cache->packfile);
ref_cache->packfile = NULL;
- git_futils_freebuffer(&packfile);
- return git__rethrow(error, "Failed to load packed references");
+ git_buf_free(&packfile);
+ return -1;
}
@@ -525,17 +507,17 @@ static int _dirent_loose_listall(void *_data, git_buf *full_path)
struct dirent_list_data *data = (struct dirent_list_data *)_data;
const char *file_path = full_path->ptr + data->repo_path_len;
- if (git_path_isdir(full_path->ptr) == GIT_SUCCESS)
+ if (git_path_isdir(full_path->ptr) == true)
return git_path_direach(full_path, _dirent_loose_listall, _data);
/* do not add twice a reference that exists already in the packfile */
if ((data->list_flags & GIT_REF_PACKED) != 0 &&
- git_hashtable_lookup(data->repo->references.packfile, file_path) != NULL)
- return GIT_SUCCESS;
+ git_strmap_exists(data->repo->references.packfile, file_path))
+ return 0;
if (data->list_flags != GIT_REF_LISTALL) {
if ((data->list_flags & loose_guess_rtype(full_path)) == 0)
- return GIT_SUCCESS; /* we are filtering out this reference */
+ return 0; /* we are filtering out this reference */
}
return data->callback(file_path, data->callback_payload);
@@ -547,30 +529,25 @@ static int _dirent_loose_load(void *data, git_buf *full_path)
void *old_ref = NULL;
struct packref *ref;
const char *file_path;
- int error;
+ int err;
- if (git_path_isdir(full_path->ptr) == GIT_SUCCESS)
+ if (git_path_isdir(full_path->ptr) == true)
return git_path_direach(full_path, _dirent_loose_load, repository);
file_path = full_path->ptr + strlen(repository->path_repository);
- error = loose_lookup_to_packfile(&ref, repository, file_path);
-
- if (error == GIT_SUCCESS) {
- if (git_hashtable_insert2(
- repository->references.packfile,
- ref->name, ref, &old_ref) < GIT_SUCCESS) {
- free(ref);
- return GIT_ENOMEM;
- }
+ if (loose_lookup_to_packfile(&ref, repository, file_path) < 0)
+ return -1;
- if (old_ref != NULL)
- free(old_ref);
+ git_strmap_insert2(
+ repository->references.packfile, ref->name, ref, old_ref, err);
+ if (err < 0) {
+ git__free(ref);
+ return -1;
}
- return error == GIT_SUCCESS ?
- GIT_SUCCESS :
- git__rethrow(error, "Failed to load loose references into packfile");
+ git__free(old_ref);
+ return 0;
}
/*
@@ -581,24 +558,24 @@ static int _dirent_loose_load(void *data, git_buf *full_path)
*/
static int packed_loadloose(git_repository *repository)
{
- int error = GIT_SUCCESS;
git_buf refs_path = GIT_BUF_INIT;
+ int result;
/* the packfile must have been previously loaded! */
assert(repository->references.packfile);
- if ((error = git_buf_joinpath(&refs_path,
- repository->path_repository, GIT_REFS_DIR)) < GIT_SUCCESS)
- return error;
+ if (git_buf_joinpath(&refs_path, repository->path_repository, GIT_REFS_DIR) < 0)
+ return -1;
/*
* Load all the loose files from disk into the Packfile table.
* This will overwrite any old packed entries with their
* updated loose versions
*/
- error = git_path_direach(&refs_path, _dirent_loose_load, repository);
+ result = git_path_direach(&refs_path, _dirent_loose_load, repository);
git_buf_free(&refs_path);
- return error;
+
+ return result;
}
/*
@@ -606,7 +583,6 @@ static int packed_loadloose(git_repository *repository)
*/
static int packed_write_ref(struct packref *ref, git_filebuf *file)
{
- int error;
char oid[GIT_OID_HEXSZ + 1];
git_oid_fmt(oid, &ref->oid);
@@ -627,14 +603,14 @@ static int packed_write_ref(struct packref *ref, git_filebuf *file)
git_oid_fmt(peel, &ref->peel);
peel[GIT_OID_HEXSZ] = 0;
- error = git_filebuf_printf(file, "%s %s\n^%s\n", oid, ref->name, peel);
+ if (git_filebuf_printf(file, "%s %s\n^%s\n", oid, ref->name, peel) < 0)
+ return -1;
} else {
- error = git_filebuf_printf(file, "%s %s\n", oid, ref->name);
+ if (git_filebuf_printf(file, "%s %s\n", oid, ref->name) < 0)
+ return -1;
}
- return error == GIT_SUCCESS ?
- GIT_SUCCESS :
- git__rethrow(error, "Failed to write packed reference");
+ return 0;
}
/*
@@ -648,24 +624,22 @@ static int packed_write_ref(struct packref *ref, git_filebuf *file)
static int packed_find_peel(git_repository *repo, struct packref *ref)
{
git_object *object;
- int error;
if (ref->flags & GIT_PACKREF_HAS_PEEL)
- return GIT_SUCCESS;
+ return 0;
/*
* Only applies to tags, i.e. references
* in the /refs/tags folder
*/
if (git__prefixcmp(ref->name, GIT_REFS_TAGS_DIR) != 0)
- return GIT_SUCCESS;
+ return 0;
/*
* Find the tagged object in the repository
*/
- error = git_object_lookup(&object, repo, &ref->oid, GIT_OBJ_ANY);
- if (error < GIT_SUCCESS)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to find packed reference");
+ if (git_object_lookup(&object, repo, &ref->oid, GIT_OBJ_ANY) < 0)
+ return -1;
/*
* If the tagged object is a Tag object, we need to resolve it;
@@ -689,7 +663,7 @@ static int packed_find_peel(git_repository *repo, struct packref *ref)
}
git_object_free(object);
- return GIT_SUCCESS;
+ return 0;
}
/*
@@ -707,25 +681,27 @@ static int packed_remove_loose(git_repository *repo, git_vector *packing_list)
{
unsigned int i;
git_buf full_path = GIT_BUF_INIT;
- int error = GIT_SUCCESS;
+ int failed = 0;
for (i = 0; i < packing_list->length; ++i) {
struct packref *ref = git_vector_get(packing_list, i);
- int an_error;
if ((ref->flags & GIT_PACKREF_WAS_LOOSE) == 0)
continue;
- an_error = git_buf_joinpath(&full_path, repo->path_repository, ref->name);
+ if (git_buf_joinpath(&full_path, repo->path_repository, ref->name) < 0)
+ return -1; /* critical; do not try to recover on oom */
- if (an_error == GIT_SUCCESS &&
- git_path_exists(full_path.ptr) == GIT_SUCCESS &&
- p_unlink(full_path.ptr) < GIT_SUCCESS)
- an_error = GIT_EOSERR;
+ if (git_path_exists(full_path.ptr) == true && p_unlink(full_path.ptr) < 0) {
+ if (failed)
+ continue;
- /* keep the error if we haven't seen one yet */
- if (error > an_error)
- error = an_error;
+ giterr_set(GITERR_REFERENCE,
+ "Failed to remove loose reference '%s' after packing: %s",
+ full_path.ptr, strerror(errno));
+
+ failed = 1;
+ }
/*
* if we fail to remove a single file, this is *not* good,
@@ -736,10 +712,7 @@ static int packed_remove_loose(git_repository *repo, git_vector *packing_list)
}
git_buf_free(&full_path);
-
- return error == GIT_SUCCESS ?
- GIT_SUCCESS :
- git__rethrow(error, "Failed to remove loose packed reference");
+ return failed ? -1 : 0;
}
static int packed_sort(const void *a, const void *b)
@@ -756,217 +729,271 @@ static int packed_sort(const void *a, const void *b)
static int packed_write(git_repository *repo)
{
git_filebuf pack_file = GIT_FILEBUF_INIT;
- int error;
- const char *errmsg = "Failed to write packed references file";
unsigned int i;
git_buf pack_file_path = GIT_BUF_INIT;
git_vector packing_list;
- size_t total_refs;
+ unsigned int total_refs;
assert(repo && repo->references.packfile);
- total_refs = repo->references.packfile->key_count;
- if ((error =
- git_vector_init(&packing_list, total_refs, packed_sort)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to init packed references list");
+ total_refs =
+ (unsigned int)git_strmap_num_entries(repo->references.packfile);
+
+ if (git_vector_init(&packing_list, total_refs, packed_sort) < 0)
+ return -1;
/* Load all the packfile into a vector */
{
struct packref *reference;
- const void *GIT_UNUSED(_unused);
- GIT_HASHTABLE_FOREACH(repo->references.packfile, _unused, reference,
- /* cannot fail: vector already has the right size */
+ /* cannot fail: vector already has the right size */
+ git_strmap_foreach_value(repo->references.packfile, reference, {
git_vector_insert(&packing_list, reference);
- );
+ });
}
/* sort the vector so the entries appear sorted on the packfile */
git_vector_sort(&packing_list);
/* Now we can open the file! */
- error = git_buf_joinpath(&pack_file_path, repo->path_repository, GIT_PACKEDREFS_FILE);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (git_buf_joinpath(&pack_file_path, repo->path_repository, GIT_PACKEDREFS_FILE) < 0)
+ goto cleanup_memory;
- if ((error = git_filebuf_open(&pack_file, pack_file_path.ptr, 0)) < GIT_SUCCESS) {
- errmsg = "Failed to open packed references file";
- goto cleanup;
- }
+ if (git_filebuf_open(&pack_file, pack_file_path.ptr, 0) < 0)
+ goto cleanup_packfile;
/* Packfiles have a header... apparently
* This is in fact not required, but we might as well print it
* just for kicks */
- if ((error =
- git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER)) < GIT_SUCCESS) {
- errmsg = "Failed to write packed references file header";
- goto cleanup;
- }
+ if (git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER) < 0)
+ goto cleanup_packfile;
for (i = 0; i < packing_list.length; ++i) {
struct packref *ref = (struct packref *)git_vector_get(&packing_list, i);
- if ((error = packed_find_peel(repo, ref)) < GIT_SUCCESS) {
- error = git__throw(GIT_EOBJCORRUPTED,
- "A reference cannot be peeled");
- goto cleanup;
- }
+ if (packed_find_peel(repo, ref) < 0)
+ goto cleanup_packfile;
- if ((error = packed_write_ref(ref, &pack_file)) < GIT_SUCCESS)
- goto cleanup;
+ if (packed_write_ref(ref, &pack_file) < 0)
+ goto cleanup_packfile;
}
-cleanup:
/* if we've written all the references properly, we can commit
* the packfile to make the changes effective */
- if (error == GIT_SUCCESS) {
- error = git_filebuf_commit(&pack_file, GIT_PACKEDREFS_FILE_MODE);
+ if (git_filebuf_commit(&pack_file, GIT_PACKEDREFS_FILE_MODE) < 0)
+ goto cleanup_memory;
- /* when and only when the packfile has been properly written,
- * we can go ahead and remove the loose refs */
- if (error == GIT_SUCCESS) {
- struct stat st;
+ /* when and only when the packfile has been properly written,
+ * we can go ahead and remove the loose refs */
+ if (packed_remove_loose(repo, &packing_list) < 0)
+ goto cleanup_memory;
- error = packed_remove_loose(repo, &packing_list);
-
- if (p_stat(pack_file_path.ptr, &st) == GIT_SUCCESS)
- repo->references.packfile_time = st.st_mtime;
- }
- }
- else git_filebuf_cleanup(&pack_file);
+ {
+ struct stat st;
+ if (p_stat(pack_file_path.ptr, &st) == 0)
+ repo->references.packfile_time = st.st_mtime;
+ }
git_vector_free(&packing_list);
git_buf_free(&pack_file_path);
- if (error < GIT_SUCCESS)
- git__rethrow(error, "%s", errmsg);
+ /* we're good now */
+ return 0;
+
+cleanup_packfile:
+ git_filebuf_cleanup(&pack_file);
+
+cleanup_memory:
+ git_vector_free(&packing_list);
+ git_buf_free(&pack_file_path);
- return error;
+ return -1;
}
+struct reference_available_t {
+ const char *new_ref;
+ const char *old_ref;
+ int available;
+};
+
static int _reference_available_cb(const char *ref, void *data)
{
- const char *new, *old;
- const char **refs;
+ struct reference_available_t *d;
assert(ref && data);
+ d = (struct reference_available_t *)data;
- refs = (const char **)data;
+ if (!d->old_ref || strcmp(d->old_ref, ref)) {
+ size_t reflen = strlen(ref);
+ size_t newlen = strlen(d->new_ref);
+ size_t cmplen = reflen < newlen ? reflen : newlen;
+ const char *lead = reflen < newlen ? d->new_ref : ref;
- new = (const char *)refs[0];
- old = (const char *)refs[1];
-
- if (!old || strcmp(old, ref)) {
- int reflen = strlen(ref);
- int newlen = strlen(new);
- int cmplen = reflen < newlen ? reflen : newlen;
- const char *lead = reflen < newlen ? new : ref;
-
- if (!strncmp(new, ref, cmplen) &&
- lead[cmplen] == '/')
- return GIT_EEXISTS;
+ if (!strncmp(d->new_ref, ref, cmplen) && lead[cmplen] == '/') {
+ d->available = 0;
+ return -1;
+ }
}
- return GIT_SUCCESS;
+ return 0;
}
-static int reference_available(
+static int reference_path_available(
git_repository *repo,
const char *ref,
const char* old_ref)
{
- const char *refs[2];
+ struct reference_available_t data;
- refs[0] = ref;
- refs[1] = old_ref;
+ data.new_ref = ref;
+ data.old_ref = old_ref;
+ data.available = 1;
if (git_reference_foreach(repo, GIT_REF_LISTALL,
- _reference_available_cb, (void *)refs) < 0) {
- return git__throw(GIT_EEXISTS,
- "Reference name `%s` conflicts with existing reference", ref);
+ _reference_available_cb, (void *)&data) < 0)
+ return -1;
+
+ if (!data.available) {
+ giterr_set(GITERR_REFERENCE,
+ "The path to reference '%s' collides with an existing one", ref);
+ return -1;
}
- return GIT_SUCCESS;
+ return 0;
}
static int reference_exists(int *exists, git_repository *repo, const char *ref_name)
{
- int error;
git_buf ref_path = GIT_BUF_INIT;
- error = packed_load(repo);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Cannot resolve if a reference exists");
+ if (packed_load(repo) < 0)
+ return -1;
- error = git_buf_joinpath(&ref_path, repo->path_repository, ref_name);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Cannot resolve if a reference exists");
+ if (git_buf_joinpath(&ref_path, repo->path_repository, ref_name) < 0)
+ return -1;
- if (git_path_isfile(ref_path.ptr) == GIT_SUCCESS ||
- git_hashtable_lookup(repo->references.packfile, ref_path.ptr) != NULL) {
+ if (git_path_isfile(ref_path.ptr) == true ||
+ git_strmap_exists(repo->references.packfile, ref_path.ptr))
+ {
*exists = 1;
} else {
*exists = 0;
}
git_buf_free(&ref_path);
+ return 0;
+}
- return GIT_SUCCESS;
+/*
+ * Check if a reference could be written to disk, based on:
+ *
+ * - Whether a reference with the same name already exists,
+ * and we are allowing or disallowing overwrites
+ *
+ * - Whether the name of the reference would collide with
+ * an existing path
+ */
+static int reference_can_write(
+ git_repository *repo,
+ const char *refname,
+ const char *previous_name,
+ int force)
+{
+ /* see if the reference shares a path with an existing reference;
+ * if a path is shared, we cannot create the reference, even when forcing */
+ if (reference_path_available(repo, refname, previous_name) < 0)
+ return -1;
+
+ /* check if the reference actually exists, but only if we are not forcing
+ * the rename. If we are forcing, it's OK to overwrite */
+ if (!force) {
+ int exists;
+
+ if (reference_exists(&exists, repo, refname) < 0)
+ return -1;
+
+ /* We cannot proceed if the reference already exists and we're not forcing
+ * the rename; the existing one would be overwritten */
+ if (exists) {
+ giterr_set(GITERR_REFERENCE,
+ "A reference with that name (%s) already exists", refname);
+ return GIT_EEXISTS;
+ }
+ }
+
+ /* FIXME: if the reference exists and we are forcing, do we really need to
+ * remove the reference first?
+ *
+ * Two cases:
+ *
+ * - the reference already exists and is loose: not a problem, the file
+ * gets overwritten on disk
+ *
+ * - the reference already exists and is packed: we write a new one as
+ * loose, which by all means renders the packed one useless
+ */
+
+ return 0;
}
+
static int packed_lookup(git_reference *ref)
{
- int error;
struct packref *pack_ref = NULL;
+ git_strmap *packfile_refs;
+ khiter_t pos;
- error = packed_load(ref->owner);
- if (error < GIT_SUCCESS)
- return git__rethrow(error,
- "Failed to lookup reference from packfile");
+ if (packed_load(ref->owner) < 0)
+ return -1;
- if (ref->flags & GIT_REF_PACKED &&
+ /* maybe the packfile hasn't changed at all, so we don't
+ * have to re-lookup the reference */
+ if ((ref->flags & GIT_REF_PACKED) &&
ref->mtime == ref->owner->references.packfile_time)
- return GIT_SUCCESS;
+ return 0;
if (ref->flags & GIT_REF_SYMBOLIC) {
- free(ref->target.symbolic);
+ git__free(ref->target.symbolic);
ref->target.symbolic = NULL;
}
/* Look up on the packfile */
- pack_ref = git_hashtable_lookup(ref->owner->references.packfile, ref->name);
- if (pack_ref == NULL)
- return git__throw(GIT_ENOTFOUND,
- "Failed to lookup reference from packfile");
+ packfile_refs = ref->owner->references.packfile;
+ pos = git_strmap_lookup_index(packfile_refs, ref->name);
+ if (!git_strmap_valid_index(packfile_refs, pos)) {
+ giterr_set(GITERR_REFERENCE, "Reference '%s' not found", ref->name);
+ return GIT_ENOTFOUND;
+ }
+
+ pack_ref = git_strmap_value_at(packfile_refs, pos);
ref->flags = GIT_REF_OID | GIT_REF_PACKED;
ref->mtime = ref->owner->references.packfile_time;
git_oid_cpy(&ref->target.oid, &pack_ref->oid);
- return GIT_SUCCESS;
+ return 0;
}
static int reference_lookup(git_reference *ref)
{
- int error_loose, error_packed;
+ int result;
- error_loose = loose_lookup(ref);
- if (error_loose == GIT_SUCCESS)
- return GIT_SUCCESS;
+ result = loose_lookup(ref);
+ if (result == 0)
+ return 0;
- error_packed = packed_lookup(ref);
- if (error_packed == GIT_SUCCESS)
- return GIT_SUCCESS;
+ /* only try to lookup this reference on the packfile if it
+ * wasn't found on the loose refs; not if there was a critical error */
+ if (result == GIT_ENOTFOUND) {
+ giterr_clear();
+ result = packed_lookup(ref);
+ if (result == 0)
+ return 0;
+ }
+ /* unexpected error; free the reference */
git_reference_free(ref);
-
- if (error_loose != GIT_ENOTFOUND)
- return git__rethrow(error_loose, "Failed to lookup reference");
-
- if (error_packed != GIT_ENOTFOUND)
- return git__rethrow(error_packed, "Failed to lookup reference");
-
- return git__throw(GIT_ENOTFOUND, "Reference not found");
+ return result;
}
/*
@@ -976,7 +1003,7 @@ static int reference_lookup(git_reference *ref)
*/
static int reference_delete(git_reference *ref)
{
- int error;
+ int result;
assert(ref);
@@ -984,17 +1011,28 @@ static int reference_delete(git_reference *ref)
* We need to reload the packfile, remove the reference from the
* packing list, and repack */
if (ref->flags & GIT_REF_PACKED) {
+ git_strmap *packfile_refs;
struct packref *packref;
+ khiter_t pos;
+
/* load the existing packfile */
- if ((error = packed_load(ref->owner)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to delete reference");
+ if (packed_load(ref->owner) < 0)
+ return -1;
+
+ packfile_refs = ref->owner->references.packfile;
+ pos = git_strmap_lookup_index(packfile_refs, ref->name);
+ if (!git_strmap_valid_index(packfile_refs, pos)) {
+ giterr_set(GITERR_REFERENCE,
+ "Reference %s stopped existing in the packfile", ref->name);
+ return -1;
+ }
- if (git_hashtable_remove2(ref->owner->references.packfile,
- ref->name, (void **) &packref) < GIT_SUCCESS)
- return git__throw(GIT_ENOTFOUND, "Reference not found");
+ packref = git_strmap_value_at(packfile_refs, pos);
+ git_strmap_delete_at(packfile_refs, pos);
- git__free (packref);
- error = packed_write(ref->owner);
+ git__free(packref);
+ if (packed_write(ref->owner) < 0)
+ return -1;
/* If the reference is loose, we can just remove the reference
* from the filesystem */
@@ -1002,72 +1040,120 @@ static int reference_delete(git_reference *ref)
git_reference *ref_in_pack;
git_buf full_path = GIT_BUF_INIT;
- error = git_buf_joinpath(&full_path, ref->owner->path_repository, ref->name);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (git_buf_joinpath(&full_path, ref->owner->path_repository, ref->name) < 0)
+ return -1;
- error = p_unlink(full_path.ptr);
+ result = p_unlink(full_path.ptr);
git_buf_free(&full_path); /* done with path at this point */
- if (error < GIT_SUCCESS)
- goto cleanup;
+
+ if (result < 0) {
+ giterr_set(GITERR_OS, "Failed to unlink '%s'", full_path.ptr);
+ return -1;
+ }
/* When deleting a loose reference, we have to ensure that an older
* packed version of it doesn't exist */
- if (git_reference_lookup(&ref_in_pack, ref->owner,
- ref->name) == GIT_SUCCESS) {
+ if (git_reference_lookup(&ref_in_pack, ref->owner, ref->name) == 0) {
assert((ref_in_pack->flags & GIT_REF_PACKED) != 0);
- error = git_reference_delete(ref_in_pack);
+ return git_reference_delete(ref_in_pack);
}
+
+ giterr_clear();
}
-cleanup:
- return error == GIT_SUCCESS ?
- GIT_SUCCESS :
- git__rethrow(error, "Failed to delete reference");
+ return 0;
}
int git_reference_delete(git_reference *ref)
{
- int error = reference_delete(ref);
- if (error < GIT_SUCCESS)
- return error;
-
+ int result = reference_delete(ref);
git_reference_free(ref);
- return GIT_SUCCESS;
+ return result;
}
-
int git_reference_lookup(git_reference **ref_out,
git_repository *repo, const char *name)
{
+ return git_reference_lookup_resolved(ref_out, repo, name, 0);
+}
+
+int git_reference_name_to_oid(
+ git_oid *out, git_repository *repo, const char *name)
+{
int error;
- char normalized_name[GIT_REFNAME_MAX];
- git_reference *ref = NULL;
+ git_reference *ref;
+
+ if ((error = git_reference_lookup_resolved(&ref, repo, name, -1)) < 0)
+ return error;
+
+ git_oid_cpy(out, git_reference_oid(ref));
+ git_reference_free(ref);
+ return 0;
+}
+
+int git_reference_lookup_resolved(
+ git_reference **ref_out,
+ git_repository *repo,
+ const char *name,
+ int max_nesting)
+{
+ git_reference *scan;
+ int result, nesting;
assert(ref_out && repo && name);
*ref_out = NULL;
- error = normalize_name(normalized_name, sizeof(normalized_name), name, 0);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to lookup reference");
+ if (max_nesting > MAX_NESTING_LEVEL)
+ max_nesting = MAX_NESTING_LEVEL;
+ else if (max_nesting < 0)
+ max_nesting = DEFAULT_NESTING_LEVEL;
- error = reference_alloc(&ref, repo, normalized_name);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to lookup reference");
+ scan = git__calloc(1, sizeof(git_reference));
+ GITERR_CHECK_ALLOC(scan);
- error = reference_lookup(ref);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to lookup reference");
+ scan->name = git__calloc(GIT_REFNAME_MAX + 1, sizeof(char));
+ GITERR_CHECK_ALLOC(scan->name);
- *ref_out = ref;
- return GIT_SUCCESS;
+ if ((result = normalize_name(scan->name, GIT_REFNAME_MAX, name, 0)) < 0) {
+ git_reference_free(scan);
+ return result;
+ }
+
+ scan->target.symbolic = git__strdup(scan->name);
+ GITERR_CHECK_ALLOC(scan->target.symbolic);
+
+ scan->owner = repo;
+ scan->flags = GIT_REF_SYMBOLIC;
+
+ for (nesting = max_nesting;
+ nesting >= 0 && (scan->flags & GIT_REF_SYMBOLIC) != 0;
+ nesting--)
+ {
+ if (nesting != max_nesting)
+ strncpy(scan->name, scan->target.symbolic, GIT_REFNAME_MAX);
+
+ scan->mtime = 0;
+
+ if ((result = reference_lookup(scan)) < 0)
+ return result; /* lookup git_reference_free on scan already */
+ }
+
+ if ((scan->flags & GIT_REF_OID) == 0 && max_nesting != 0) {
+ giterr_set(GITERR_REFERENCE,
+ "Cannot resolve reference (>%u levels deep)", max_nesting);
+ git_reference_free(scan);
+ return -1;
+ }
+
+ *ref_out = scan;
+ return 0;
}
/**
* Getters
*/
-git_rtype git_reference_type(git_reference *ref)
+git_ref_t git_reference_type(git_reference *ref)
{
assert(ref);
@@ -1126,43 +1212,32 @@ int git_reference_create_symbolic(
int force)
{
char normalized[GIT_REFNAME_MAX];
- int ref_exists, error = GIT_SUCCESS;
git_reference *ref = NULL;
- error = normalize_name(normalized, sizeof(normalized), name, 0);
- if (error < GIT_SUCCESS)
- goto cleanup;
-
- if ((error = reference_exists(&ref_exists, repo, normalized) < GIT_SUCCESS))
- return git__rethrow(error, "Failed to create symbolic reference");
+ if (normalize_name(normalized, sizeof(normalized), name, 0) < 0)
+ return -1;
- if (ref_exists && !force)
- return git__throw(GIT_EEXISTS,
- "Failed to create symbolic reference. Reference already exists");
+ if (reference_can_write(repo, normalized, NULL, force) < 0)
+ return -1;
- error = reference_alloc(&ref, repo, normalized);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (reference_alloc(&ref, repo, normalized) < 0)
+ return -1;
ref->flags |= GIT_REF_SYMBOLIC;
/* set the target; this will normalize the name automatically
* and write the reference on disk */
- error = git_reference_set_target(ref, target);
- if (error < GIT_SUCCESS)
- goto cleanup;
-
+ if (git_reference_set_target(ref, target) < 0) {
+ git_reference_free(ref);
+ return -1;
+ }
if (ref_out == NULL) {
git_reference_free(ref);
} else {
*ref_out = ref;
}
- return GIT_SUCCESS;
-
-cleanup:
- git_reference_free(ref);
- return git__rethrow(error, "Failed to create symbolic reference");
+ return 0;
}
int git_reference_create_oid(
@@ -1172,34 +1247,25 @@ int git_reference_create_oid(
const git_oid *id,
int force)
{
- int error = GIT_SUCCESS, ref_exists;
git_reference *ref = NULL;
char normalized[GIT_REFNAME_MAX];
- error = normalize_name(normalized, sizeof(normalized), name, 1);
- if (error < GIT_SUCCESS)
- goto cleanup;
-
- if ((error = reference_exists(&ref_exists, repo, normalized) < GIT_SUCCESS))
- return git__rethrow(error, "Failed to create OID reference");
+ if (normalize_name(normalized, sizeof(normalized), name, 1) < 0)
+ return -1;
- if (ref_exists && !force)
- return git__throw(GIT_EEXISTS,
- "Failed to create OID reference. Reference already exists");
+ if (reference_can_write(repo, normalized, NULL, force) < 0)
+ return -1;
- if ((error = reference_available(repo, name, NULL)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to create reference");
-
- error = reference_alloc(&ref, repo, name);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (reference_alloc(&ref, repo, name) < 0)
+ return -1;
ref->flags |= GIT_REF_OID;
/* set the oid; this will write the reference on disk */
- error = git_reference_set_oid(ref, id);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (git_reference_set_oid(ref, id) < 0) {
+ git_reference_free(ref);
+ return -1;
+ }
if (ref_out == NULL) {
git_reference_free(ref);
@@ -1207,13 +1273,8 @@ int git_reference_create_oid(
*ref_out = ref;
}
- return GIT_SUCCESS;
-
-cleanup:
- git_reference_free(ref);
- return git__rethrow(error, "Failed to create reference OID");
+ return 0;
}
-
/*
* Change the OID target of a reference.
*
@@ -1225,38 +1286,31 @@ cleanup:
*/
int git_reference_set_oid(git_reference *ref, const git_oid *id)
{
- int error = GIT_SUCCESS, exists;
git_odb *odb = NULL;
- if ((ref->flags & GIT_REF_OID) == 0)
- return git__throw(GIT_EINVALIDREFSTATE,
- "Failed to set OID target of reference. Not an OID reference");
+ if ((ref->flags & GIT_REF_OID) == 0) {
+ giterr_set(GITERR_REFERENCE, "Cannot set OID on symbolic reference");
+ return -1;
+ }
assert(ref->owner);
- error = git_repository_odb__weakptr(&odb, ref->owner);
- if (error < GIT_SUCCESS)
- return error;
-
- exists = git_odb_exists(odb, id);
-
- git_odb_free(odb);
+ if (git_repository_odb__weakptr(&odb, ref->owner) < 0)
+ return -1;
/* Don't let the user create references to OIDs that
* don't exist in the ODB */
- if (!exists)
- return git__throw(GIT_ENOTFOUND,
- "Failed to set OID target of reference. OID doesn't exist in ODB");
+ if (!git_odb_exists(odb, id)) {
+ giterr_set(GITERR_REFERENCE,
+ "Target OID for the reference doesn't exist on the repository");
+ return -1;
+ }
/* Update the OID value on `ref` */
git_oid_cpy(&ref->target.oid, id);
/* Write back to disk */
- error = loose_write(ref);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to set OID target of reference");
-
- return GIT_SUCCESS;
+ return loose_write(ref);
}
/*
@@ -1268,82 +1322,44 @@ int git_reference_set_oid(git_reference *ref, const git_oid *id)
*/
int git_reference_set_target(git_reference *ref, const char *target)
{
- int error;
char normalized[GIT_REFNAME_MAX];
- if ((ref->flags & GIT_REF_SYMBOLIC) == 0)
- return git__throw(GIT_EINVALIDREFSTATE,
- "Failed to set reference target. Not a symbolic reference");
+ if ((ref->flags & GIT_REF_SYMBOLIC) == 0) {
+ giterr_set(GITERR_REFERENCE,
+ "Cannot set symbolic target on a direct reference");
+ return -1;
+ }
- error = normalize_name(normalized, sizeof(normalized), target, 0);
- if (error < GIT_SUCCESS)
- return git__rethrow(error,
- "Failed to set reference target. Invalid target name");
+ if (normalize_name(normalized, sizeof(normalized), target, 0))
+ return -1;
git__free(ref->target.symbolic);
ref->target.symbolic = git__strdup(normalized);
- if (ref->target.symbolic == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(ref->target.symbolic);
return loose_write(ref);
}
int git_reference_rename(git_reference *ref, const char *new_name, int force)
{
- int error;
+ int result;
git_buf aux_path = GIT_BUF_INIT;
char normalized[GIT_REFNAME_MAX];
const char *head_target = NULL;
- git_reference *existing_ref = NULL, *head = NULL;
-
- error = normalize_name(normalized, sizeof(normalized),
- new_name, ref->flags & GIT_REF_OID);
-
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to rename reference. Invalid name");
-
- new_name = normalized;
-
- /* If we are forcing the rename, try to lookup a reference with the
- * new one. If the lookup succeeds, we need to delete that ref
- * before the renaming can proceed */
- if (force) {
- error = git_reference_lookup(&existing_ref, ref->owner, new_name);
-
- if (error == GIT_SUCCESS) {
- error = git_reference_delete(existing_ref);
- if (error < GIT_SUCCESS)
- return git__rethrow(error,
- "Failed to rename reference. "
- "The existing reference cannot be deleted");
- } else if (error != GIT_ENOTFOUND)
- goto cleanup;
-
- /* If we're not forcing the rename, check if the reference exists.
- * If it does, renaming cannot continue */
- } else {
- int exists;
+ git_reference *head = NULL;
- error = reference_exists(&exists, ref->owner, normalized);
- if (error < GIT_SUCCESS)
- goto cleanup;
-
- if (exists)
- return git__throw(GIT_EEXISTS,
- "Failed to rename reference. Reference already exists");
- }
+ if (normalize_name(normalized, sizeof(normalized),
+ new_name, ref->flags & GIT_REF_OID) < 0)
+ return -1;
- if ((error = reference_available(ref->owner, new_name, ref->name)) < GIT_SUCCESS)
- return git__rethrow(error,
- "Failed to rename reference. Reference already exists");
+ if (reference_can_write(ref->owner, normalized, ref->name, force) < 0)
+ return -1;
/* Initialize path now so we won't get an allocation failure once
- * we actually start removing things.
- */
- error = git_buf_joinpath(&aux_path, ref->owner->path_repository, new_name);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ * we actually start removing things. */
+ if (git_buf_joinpath(&aux_path, ref->owner->path_repository, new_name) < 0)
+ return -1;
/*
* Now delete the old ref and remove an possibly existing directory
@@ -1351,57 +1367,54 @@ int git_reference_rename(git_reference *ref, const char *new_name, int force)
* method deletes the ref from disk but doesn't free the pointer, so
* we can still access the ref's attributes for creating the new one
*/
- if ((error = reference_delete(ref)) < GIT_SUCCESS)
+ if (reference_delete(ref) < 0)
goto cleanup;
- if (git_path_exists(aux_path.ptr) == GIT_SUCCESS) {
- if (git_path_isdir(aux_path.ptr) == GIT_SUCCESS) {
- if ((error = git_futils_rmdir_r(aux_path.ptr, 0)) < GIT_SUCCESS)
- goto rollback;
- } else goto rollback;
- }
-
/*
* Finally we can create the new reference.
*/
if (ref->flags & GIT_REF_SYMBOLIC) {
- error = git_reference_create_symbolic(
- NULL, ref->owner, new_name, ref->target.symbolic, 0);
+ result = git_reference_create_symbolic(
+ NULL, ref->owner, new_name, ref->target.symbolic, force);
} else {
- error = git_reference_create_oid(
- NULL, ref->owner, new_name, &ref->target.oid, 0);
+ result = git_reference_create_oid(
+ NULL, ref->owner, new_name, &ref->target.oid, force);
}
- if (error < GIT_SUCCESS)
+ if (result < 0)
goto rollback;
/*
* Check if we have to update HEAD.
*/
- error = git_reference_lookup(&head, ref->owner, GIT_HEAD_FILE);
- if (error < GIT_SUCCESS)
+ if (git_reference_lookup(&head, ref->owner, GIT_HEAD_FILE) < 0) {
+ giterr_set(GITERR_REFERENCE,
+ "Failed to update HEAD after renaming reference");
goto cleanup;
+ }
head_target = git_reference_target(head);
if (head_target && !strcmp(head_target, ref->name)) {
- error = git_reference_create_symbolic(
- &head, ref->owner, "HEAD", new_name, 1);
-
- if (error < GIT_SUCCESS)
+ if (git_reference_create_symbolic(&head, ref->owner, "HEAD", new_name, 1) < 0) {
+ giterr_set(GITERR_REFERENCE,
+ "Failed to update HEAD after renaming reference");
goto cleanup;
+ }
}
/*
* Rename the reflog file.
*/
- error = git_buf_join_n(&aux_path, '/', 3, ref->owner->path_repository,
- GIT_REFLOG_DIR, ref->name);
- if (error < GIT_SUCCESS)
+ if (git_buf_join_n(&aux_path, '/', 3, ref->owner->path_repository, GIT_REFLOG_DIR, ref->name) < 0)
goto cleanup;
- if (git_path_exists(aux_path.ptr) == GIT_SUCCESS)
- error = git_reflog_rename(ref, new_name);
+ if (git_path_exists(aux_path.ptr) == true) {
+ if (git_reflog_rename(ref, new_name) < 0)
+ goto cleanup;
+ } else {
+ giterr_clear();
+ }
/*
* Change the name of the reference given by the user.
@@ -1412,92 +1425,49 @@ int git_reference_rename(git_reference *ref, const char *new_name, int force)
/* The reference is no longer packed */
ref->flags &= ~GIT_REF_PACKED;
+ git_reference_free(head);
+ git_buf_free(&aux_path);
+ return 0;
+
cleanup:
- /* We no longer need the newly created reference nor the head */
git_reference_free(head);
git_buf_free(&aux_path);
- return error == GIT_SUCCESS ?
- GIT_SUCCESS :
- git__rethrow(error, "Failed to rename reference");
+ return -1;
rollback:
/*
- * Try to create the old reference again.
+ * Try to create the old reference again, ignore failures
*/
if (ref->flags & GIT_REF_SYMBOLIC)
- error = git_reference_create_symbolic(
+ git_reference_create_symbolic(
NULL, ref->owner, ref->name, ref->target.symbolic, 0);
else
- error = git_reference_create_oid(
+ git_reference_create_oid(
NULL, ref->owner, ref->name, &ref->target.oid, 0);
/* The reference is no longer packed */
ref->flags &= ~GIT_REF_PACKED;
git_buf_free(&aux_path);
-
- return error == GIT_SUCCESS ?
- git__rethrow(GIT_ERROR, "Failed to rename reference. Did rollback") :
- git__rethrow(error, "Failed to rename reference. Failed to rollback");
+ return -1;
}
int git_reference_resolve(git_reference **ref_out, git_reference *ref)
{
- int error, i = 0;
- git_repository *repo;
-
- assert(ref);
-
- *ref_out = NULL;
- repo = ref->owner;
-
- /* If the reference is already resolved, we need to return a
- * copy. Instead of duplicating `ref`, we look it up again to
- * ensure the copy is out to date */
if (ref->flags & GIT_REF_OID)
return git_reference_lookup(ref_out, ref->owner, ref->name);
-
- /* Otherwise, keep iterating until the reference is resolved */
- for (i = 0; i < MAX_NESTING_LEVEL; ++i) {
- git_reference *new_ref;
-
- error = git_reference_lookup(&new_ref, repo, ref->target.symbolic);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to resolve reference");
-
- /* Free intermediate references, except for the original one
- * we've received */
- if (i > 0)
- git_reference_free(ref);
-
- ref = new_ref;
-
- /* When the reference we've just looked up is an OID, we've
- * successfully resolved the symbolic ref */
- if (ref->flags & GIT_REF_OID) {
- *ref_out = ref;
- return GIT_SUCCESS;
- }
- }
-
- return git__throw(GIT_ENOMEM,
- "Failed to resolve reference. Reference is too nested");
+ else
+ return git_reference_lookup_resolved(ref_out, ref->owner, ref->target.symbolic, -1);
}
int git_reference_packall(git_repository *repo)
{
- int error;
-
- /* load the existing packfile */
- if ((error = packed_load(repo)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to pack references");
+ if (packed_load(repo) < 0 || /* load the existing packfile */
+ packed_loadloose(repo) < 0 || /* add all the loose refs */
+ packed_write(repo) < 0) /* write back to disk */
+ return -1;
- /* update it in-memory with all the loose references */
- if ((error = packed_loadloose(repo)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to pack references");
-
- /* write it back to disk */
- return packed_write(repo);
+ return 0;
}
int git_reference_foreach(
@@ -1506,23 +1476,23 @@ int git_reference_foreach(
int (*callback)(const char *, void *),
void *payload)
{
- int error;
+ int result;
struct dirent_list_data data;
git_buf refs_path = GIT_BUF_INIT;
/* list all the packed references first */
if (list_flags & GIT_REF_PACKED) {
const char *ref_name;
- void *GIT_UNUSED(_unused);
+ void *ref;
+ GIT_UNUSED(ref);
- if ((error = packed_load(repo)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to list references");
+ if (packed_load(repo) < 0)
+ return -1;
- GIT_HASHTABLE_FOREACH(repo->references.packfile, ref_name, _unused,
- if ((error = callback(ref_name, payload)) < GIT_SUCCESS)
- return git__throw(error,
- "Failed to list references. User callback failed");
- );
+ git_strmap_foreach(repo->references.packfile, ref_name, ref, {
+ if (callback(ref_name, payload) < 0)
+ return 0;
+ });
}
/* now list the loose references, trying not to
@@ -1534,15 +1504,13 @@ int git_reference_foreach(
data.callback = callback;
data.callback_payload = payload;
- if ((error = git_buf_joinpath(&refs_path,
- repo->path_repository, GIT_REFS_DIR)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to alloc space for references");
-
- error = git_path_direach(&refs_path, _dirent_loose_listall, &data);
+ if (git_buf_joinpath(&refs_path, repo->path_repository, GIT_REFS_DIR) < 0)
+ return -1;
+ result = git_path_direach(&refs_path, _dirent_loose_listall, &data);
git_buf_free(&refs_path);
- return error;
+ return result;
}
static int cb__reflist_add(const char *ref, void *data)
@@ -1550,12 +1518,11 @@ static int cb__reflist_add(const char *ref, void *data)
return git_vector_insert((git_vector *)data, git__strdup(ref));
}
-int git_reference_listall(
+int git_reference_list(
git_strarray *array,
git_repository *repo,
unsigned int list_flags)
{
- int error;
git_vector ref_list;
assert(array && repo);
@@ -1563,46 +1530,37 @@ int git_reference_listall(
array->strings = NULL;
array->count = 0;
- if (git_vector_init(&ref_list, 8, NULL) < GIT_SUCCESS)
- return GIT_ENOMEM;
+ if (git_vector_init(&ref_list, 8, NULL) < 0)
+ return -1;
- error = git_reference_foreach(
- repo, list_flags, &cb__reflist_add, (void *)&ref_list);
-
- if (error < GIT_SUCCESS) {
+ if (git_reference_foreach(
+ repo, list_flags, &cb__reflist_add, (void *)&ref_list) < 0) {
git_vector_free(&ref_list);
- return error;
+ return -1;
}
array->strings = (char **)ref_list.contents;
array->count = ref_list.length;
- return GIT_SUCCESS;
+ return 0;
}
int git_reference_reload(git_reference *ref)
{
- int error = reference_lookup(ref);
-
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to reload reference");
-
- return GIT_SUCCESS;
+ return reference_lookup(ref);
}
-
void git_repository__refcache_free(git_refcache *refs)
{
assert(refs);
if (refs->packfile) {
- const void *GIT_UNUSED(_unused);
struct packref *reference;
- GIT_HASHTABLE_FOREACH(refs->packfile, _unused, reference,
- free(reference);
- );
+ git_strmap_foreach_value(refs->packfile, reference, {
+ git__free(reference);
+ });
- git_hashtable_free(refs->packfile);
+ git_strmap_free(refs->packfile);
}
}
@@ -1646,33 +1604,26 @@ static int normalize_name(
/* A refname can not be empty */
if (name_end == name)
- return git__throw(GIT_EINVALIDREFNAME,
- "Failed to normalize name. Reference name is empty");
+ goto invalid_name;
/* A refname can not end with a dot or a slash */
if (*(name_end - 1) == '.' || *(name_end - 1) == '/')
- return git__throw(GIT_EINVALIDREFNAME,
- "Failed to normalize name. Reference name ends with dot or slash");
+ goto invalid_name;
while (current < name_end && out_size) {
if (!is_valid_ref_char(*current))
- return git__throw(GIT_EINVALIDREFNAME,
- "Failed to normalize name. "
- "Reference name contains invalid characters");
+ goto invalid_name;
if (buffer_out > buffer_out_start) {
char prev = *(buffer_out - 1);
/* A refname can not start with a dot nor contain a double dot */
if (*current == '.' && ((prev == '.') || (prev == '/')))
- return git__throw(GIT_EINVALIDREFNAME,
- "Failed to normalize name. "
- "Reference name starts with a dot or contains a double dot");
+ goto invalid_name;
/* '@{' is forbidden within a refname */
if (*current == '{' && prev == '@')
- return git__throw(GIT_EINVALIDREFNAME,
- "Failed to normalize name. Reference name contains '@{'");
+ goto invalid_name;
/* Prevent multiple slashes from being added to the output */
if (*current == '/' && prev == '/') {
@@ -1689,7 +1640,7 @@ static int normalize_name(
}
if (!out_size)
- return git__throw(GIT_EINVALIDREFNAME, "Reference name is too long");
+ goto invalid_name;
/* Object id refname have to contain at least one slash, except
* for HEAD in a detached state or MERGE_HEAD if we're in the
@@ -1699,13 +1650,11 @@ static int normalize_name(
strcmp(name, GIT_HEAD_FILE) != 0 &&
strcmp(name, GIT_MERGE_HEAD_FILE) != 0 &&
strcmp(name, GIT_FETCH_HEAD_FILE) != 0)
- return git__throw(GIT_EINVALIDREFNAME,
- "Failed to normalize name. Reference name contains no slashes");
+ goto invalid_name;
/* A refname can not end with ".lock" */
if (!git__suffixcmp(name, GIT_FILELOCK_EXTENSION))
- return git__throw(GIT_EINVALIDREFNAME,
- "Failed to normalize name. Reference name ends with '.lock'");
+ goto invalid_name;
*buffer_out = '\0';
@@ -1715,11 +1664,13 @@ static int normalize_name(
*/
if (is_oid_ref && !(git__prefixcmp(buffer_out_start, GIT_REFS_DIR) ||
strcmp(buffer_out_start, GIT_HEAD_FILE)))
- return git__throw(GIT_EINVALIDREFNAME,
- "Failed to normalize name. "
- "Reference name does not start with 'refs/'");
+ goto invalid_name;
+
+ return 0;
- return GIT_SUCCESS;
+invalid_name:
+ giterr_set(GITERR_REFERENCE, "The given reference name is not valid");
+ return -1;
}
int git_reference__normalize_name(
@@ -1737,3 +1688,20 @@ int git_reference__normalize_name_oid(
{
return normalize_name(buffer_out, out_size, name, 1);
}
+
+#define GIT_REF_TYPEMASK (GIT_REF_OID | GIT_REF_SYMBOLIC)
+
+int git_reference_cmp(git_reference *ref1, git_reference *ref2)
+{
+ assert(ref1 && ref2);
+
+ /* let's put symbolic refs before OIDs */
+ if ((ref1->flags & GIT_REF_TYPEMASK) != (ref2->flags & GIT_REF_TYPEMASK))
+ return (ref1->flags & GIT_REF_SYMBOLIC) ? -1 : 1;
+
+ if (ref1->flags & GIT_REF_SYMBOLIC)
+ return strcmp(ref1->target.symbolic, ref2->target.symbolic);
+
+ return git_oid_cmp(&ref1->target.oid, &ref2->target.oid);
+}
+
diff --git a/src/refs.h b/src/refs.h
index c90f5bcc4..369e91e1c 100644
--- a/src/refs.h
+++ b/src/refs.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -10,7 +10,7 @@
#include "common.h"
#include "git2/oid.h"
#include "git2/refs.h"
-#include "hashtable.h"
+#include "strmap.h"
#define GIT_REFS_DIR "refs/"
#define GIT_REFS_HEADS_DIR GIT_REFS_DIR "heads/"
@@ -46,7 +46,7 @@ struct git_reference {
};
typedef struct {
- git_hashtable *packfile;
+ git_strmap *packfile;
time_t packfile_time;
} git_refcache;
@@ -55,4 +55,27 @@ void git_repository__refcache_free(git_refcache *refs);
int git_reference__normalize_name(char *buffer_out, size_t out_size, const char *name);
int git_reference__normalize_name_oid(char *buffer_out, size_t out_size, const char *name);
+/**
+ * Lookup a reference by name and try to resolve to an OID.
+ *
+ * You can control how many dereferences this will attempt to resolve the
+ * reference with the `max_deref` parameter, or pass -1 to use a sane
+ * default. If you pass 0 for `max_deref`, this will not attempt to resolve
+ * the reference. For any value of `max_deref` other than 0, not
+ * successfully resolving the reference will be reported as an error.
+
+ * The generated reference must be freed by the user.
+ *
+ * @param reference_out Pointer to the looked-up reference
+ * @param repo The repository to look up the reference
+ * @param name The long name for the reference (e.g. HEAD, ref/heads/master, refs/tags/v0.1.0, ...)
+ * @param max_deref Maximum number of dereferences to make of symbolic refs, 0 means simple lookup, < 0 means use default reasonable value
+ * @return 0 on success or < 0 on error; not being able to resolve the reference is an error unless 0 was passed for max_deref
+ */
+int git_reference_lookup_resolved(
+ git_reference **reference_out,
+ git_repository *repo,
+ const char *name,
+ int max_deref);
+
#endif
diff --git a/src/refspec.c b/src/refspec.c
index 48265bcde..697b1bf87 100644
--- a/src/refspec.c
+++ b/src/refspec.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -10,6 +10,7 @@
#include "common.h"
#include "refspec.h"
#include "util.h"
+#include "posix.h"
int git_refspec_parse(git_refspec *refspec, const char *str)
{
@@ -25,24 +26,21 @@ int git_refspec_parse(git_refspec *refspec, const char *str)
delim = strchr(str, ':');
if (delim == NULL) {
refspec->src = git__strdup(str);
- if (refspec->src == NULL)
- return GIT_ENOMEM;
-
- return GIT_SUCCESS;
+ GITERR_CHECK_ALLOC(refspec->src);
+ return 0;
}
refspec->src = git__strndup(str, delim - str);
- if (refspec->src == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(refspec->src);
refspec->dst = git__strdup(delim + 1);
if (refspec->dst == NULL) {
git__free(refspec->src);
refspec->src = NULL;
- return GIT_ENOMEM;
+ return -1;
}
- return GIT_SUCCESS;
+ return 0;
}
const char *git_refspec_src(const git_refspec *refspec)
@@ -55,9 +53,12 @@ const char *git_refspec_dst(const git_refspec *refspec)
return refspec == NULL ? NULL : refspec->dst;
}
-int git_refspec_src_match(const git_refspec *refspec, const char *refname)
+int git_refspec_src_matches(const git_refspec *refspec, const char *refname)
{
- return (refspec == NULL || refspec->src == NULL) ? GIT_ENOMATCH : git__fnmatch(refspec->src, refname, 0);
+ if (refspec == NULL || refspec->src == NULL)
+ return false;
+
+ return (p_fnmatch(refspec->src, refname, 0) == 0);
}
int git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, const char *name)
@@ -65,8 +66,10 @@ int git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, con
size_t baselen, namelen;
baselen = strlen(spec->dst);
- if (outlen <= baselen)
- return git__throw(GIT_EINVALIDREFNAME, "Reference name too long");
+ if (outlen <= baselen) {
+ giterr_set(GITERR_INVALID, "Reference name too long");
+ return GIT_EBUFS;
+ }
/*
* No '*' at the end means that it's mapped to one specific local
@@ -74,7 +77,7 @@ int git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, con
*/
if (spec->dst[baselen - 1] != '*') {
memcpy(out, spec->dst, baselen + 1); /* include '\0' */
- return GIT_SUCCESS;
+ return 0;
}
/* There's a '*' at the end, so remove its length */
@@ -85,29 +88,35 @@ int git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, con
namelen = strlen(name);
- if (outlen <= baselen + namelen)
- return git__throw(GIT_EINVALIDREFNAME, "Reference name too long");
+ if (outlen <= baselen + namelen) {
+ giterr_set(GITERR_INVALID, "Reference name too long");
+ return GIT_EBUFS;
+ }
memcpy(out, spec->dst, baselen);
memcpy(out + baselen, name, namelen + 1);
- return GIT_SUCCESS;
+ return 0;
}
int git_refspec_transform_r(git_buf *out, const git_refspec *spec, const char *name)
{
- if (git_buf_sets(out, spec->dst) < GIT_SUCCESS)
- return git_buf_lasterror(out);
+ if (git_buf_sets(out, spec->dst) < 0)
+ return -1;
/*
* No '*' at the end means that it's mapped to one specific local
* branch, so no actual transformation is needed.
*/
- if (out->size > 0 && out->ptr[out->size - 1] != '*')
- return GIT_SUCCESS;
+ if (git_buf_len(out) > 0 && out->ptr[git_buf_len(out) - 1] != '*')
+ return 0;
- git_buf_truncate(out, out->size - 1); /* remove trailing '*' */
+ git_buf_truncate(out, git_buf_len(out) - 1); /* remove trailing '*' */
git_buf_puts(out, name + strlen(spec->src) - 1);
- return git_buf_lasterror(out);
+ if (git_buf_oom(out))
+ return -1;
+
+ return 0;
}
+
diff --git a/src/refspec.h b/src/refspec.h
index cd9f894bf..2db504910 100644
--- a/src/refspec.h
+++ b/src/refspec.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -28,7 +28,7 @@ int git_refspec_parse(struct git_refspec *refspec, const char *str);
* @param out where to store the target name
* @param spec the refspec
* @param name the name of the reference to transform
- * @return GIT_SUCCESS or error if buffer allocation fails
+ * @return 0 or error if buffer allocation fails
*/
int git_refspec_transform_r(git_buf *out, const git_refspec *spec, const char *name);
diff --git a/src/remote.c b/src/remote.c
index cdf28789b..9740344f8 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -15,6 +15,8 @@
#include "fetch.h"
#include "refs.h"
+#include <regex.h>
+
static int refspec_parse(git_refspec *refspec, const char *str)
{
char *delim;
@@ -27,36 +29,32 @@ static int refspec_parse(git_refspec *refspec, const char *str)
}
delim = strchr(str, ':');
- if (delim == NULL)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse refspec. No ':'");
+ if (delim == NULL) {
+ giterr_set(GITERR_NET, "Invalid refspec, missing ':'");
+ return -1;
+ }
refspec->src = git__strndup(str, delim - str);
- if (refspec->src == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(refspec->src);
refspec->dst = git__strdup(delim + 1);
- if (refspec->dst == NULL) {
- git__free(refspec->src);
- refspec->src = NULL;
- return GIT_ENOMEM;
- }
+ GITERR_CHECK_ALLOC(refspec->dst);
- return GIT_SUCCESS;
+ return 0;
}
static int parse_remote_refspec(git_config *cfg, git_refspec *refspec, const char *var)
{
- const char *val;
int error;
+ const char *val;
- error = git_config_get_string(cfg, var, &val);
- if (error < GIT_SUCCESS)
+ if ((error = git_config_get_string(&val, cfg, var)) < 0)
return error;
return refspec_parse(refspec, val);
}
-int git_remote_new(git_remote **out, git_repository *repo, const char *url, const char *name)
+int git_remote_new(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch)
{
git_remote *remote;
@@ -64,129 +62,164 @@ int git_remote_new(git_remote **out, git_repository *repo, const char *url, cons
assert(out && repo && url);
remote = git__malloc(sizeof(git_remote));
- if (remote == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(remote);
memset(remote, 0x0, sizeof(git_remote));
remote->repo = repo;
- if (git_vector_init(&remote->refs, 32, NULL) < 0) {
- git_remote_free(remote);
- return GIT_ENOMEM;
- }
+ if (git_vector_init(&remote->refs, 32, NULL) < 0)
+ return -1;
remote->url = git__strdup(url);
- if (remote->url == NULL) {
- git_remote_free(remote);
- return GIT_ENOMEM;
- }
+ GITERR_CHECK_ALLOC(remote->url);
if (name != NULL) {
remote->name = git__strdup(name);
- if (remote->name == NULL) {
- git_remote_free(remote);
- return GIT_ENOMEM;
- }
+ GITERR_CHECK_ALLOC(remote->name);
+ }
+
+ if (fetch != NULL) {
+ if (refspec_parse(&remote->fetch, fetch) < 0)
+ goto on_error;
}
*out = remote;
- return GIT_SUCCESS;
+ return 0;
+
+on_error:
+ git_remote_free(remote);
+ return -1;
}
int git_remote_load(git_remote **out, git_repository *repo, const char *name)
{
git_remote *remote;
- char *buf = NULL;
+ git_buf buf = GIT_BUF_INIT;
const char *val;
- int ret, error, buf_len;
+ int error = 0;
git_config *config;
assert(out && repo && name);
- error = git_repository_config__weakptr(&config, repo);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_repository_config__weakptr(&config, repo) < 0)
+ return -1;
remote = git__malloc(sizeof(git_remote));
- if (remote == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(remote);
memset(remote, 0x0, sizeof(git_remote));
remote->name = git__strdup(name);
- if (remote->name == NULL) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
+ GITERR_CHECK_ALLOC(remote->name);
if (git_vector_init(&remote->refs, 32, NULL) < 0) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
-
- /* "fetch" is the longest var name we're interested in */
- buf_len = strlen("remote.") + strlen(".fetch") + strlen(name) + 1;
- buf = git__malloc(buf_len);
- if (buf == NULL) {
- error = GIT_ENOMEM;
+ error = -1;
goto cleanup;
}
- ret = p_snprintf(buf, buf_len, "%s.%s.%s", "remote", name, "url");
- if (ret < 0) {
- error = git__throw(GIT_EOSERR, "Failed to build config var name");
+ if (git_buf_printf(&buf, "remote.%s.url", name) < 0) {
+ error = -1;
goto cleanup;
}
- error = git_config_get_string(config, buf, &val);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Remote's url doesn't exist");
+ if ((error = git_config_get_string(&val, config, git_buf_cstr(&buf))) < 0)
goto cleanup;
- }
remote->repo = repo;
remote->url = git__strdup(val);
- if (remote->url == NULL) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
+ GITERR_CHECK_ALLOC(remote->url);
- ret = p_snprintf(buf, buf_len, "%s.%s.%s", "remote", name, "fetch");
- if (ret < 0) {
- error = git__throw(GIT_EOSERR, "Failed to build config var name");
+ git_buf_clear(&buf);
+ if (git_buf_printf(&buf, "remote.%s.fetch", name) < 0) {
+ error = -1;
goto cleanup;
}
- error = parse_remote_refspec(config, &remote->fetch, buf);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to get fetch refspec");
+ error = parse_remote_refspec(config, &remote->fetch, git_buf_cstr(&buf));
+ if (error == GIT_ENOTFOUND)
+ error = 0;
+
+ if (error < 0) {
+ error = -1;
goto cleanup;
}
- ret = p_snprintf(buf, buf_len, "%s.%s.%s", "remote", name, "push");
- if (ret < 0) {
- error = git__throw(GIT_EOSERR, "Failed to build config var name");
+ git_buf_clear(&buf);
+ if (git_buf_printf(&buf, "remote.%s.push", name) < 0) {
+ error = -1;
goto cleanup;
}
- error = parse_remote_refspec(config, &remote->push, buf);
- /* Not finding push is fine */
+ error = parse_remote_refspec(config, &remote->push, git_buf_cstr(&buf));
if (error == GIT_ENOTFOUND)
- error = GIT_SUCCESS;
+ error = 0;
- if (error < GIT_SUCCESS)
+ if (error < 0) {
+ error = -1;
goto cleanup;
+ }
*out = remote;
cleanup:
- git__free(buf);
+ git_buf_free(&buf);
- if (error < GIT_SUCCESS)
+ if (error < 0)
git_remote_free(remote);
return error;
}
+int git_remote_save(const git_remote *remote)
+{
+ git_config *config;
+ git_buf buf = GIT_BUF_INIT, value = GIT_BUF_INIT;
+
+ if (git_repository_config__weakptr(&config, remote->repo) < 0)
+ return -1;
+
+ if (git_buf_printf(&buf, "remote.%s.%s", remote->name, "url") < 0)
+ return -1;
+
+ if (git_config_set_string(config, git_buf_cstr(&buf), remote->url) < 0) {
+ git_buf_free(&buf);
+ return -1;
+ }
+
+ if (remote->fetch.src != NULL && remote->fetch.dst != NULL) {
+ git_buf_clear(&buf);
+ git_buf_clear(&value);
+ git_buf_printf(&buf, "remote.%s.fetch", remote->name);
+ git_buf_printf(&value, "%s:%s", remote->fetch.src, remote->fetch.dst);
+ if (git_buf_oom(&buf) || git_buf_oom(&value))
+ return -1;
+
+ if (git_config_set_string(config, git_buf_cstr(&buf), git_buf_cstr(&value)) < 0)
+ goto on_error;
+ }
+
+ if (remote->push.src != NULL && remote->push.dst != NULL) {
+ git_buf_clear(&buf);
+ git_buf_clear(&value);
+ git_buf_printf(&buf, "remote.%s.push", remote->name);
+ git_buf_printf(&value, "%s:%s", remote->push.src, remote->push.dst);
+ if (git_buf_oom(&buf) || git_buf_oom(&value))
+ return -1;
+
+ if (git_config_set_string(config, git_buf_cstr(&buf), git_buf_cstr(&value)) < 0)
+ goto on_error;
+ }
+
+ git_buf_free(&buf);
+ git_buf_free(&value);
+
+ return 0;
+
+on_error:
+ git_buf_free(&buf);
+ git_buf_free(&value);
+ return -1;
+}
+
const char *git_remote_name(git_remote *remote)
{
assert(remote);
@@ -199,12 +232,46 @@ const char *git_remote_url(git_remote *remote)
return remote->url;
}
+int git_remote_set_fetchspec(git_remote *remote, const char *spec)
+{
+ git_refspec refspec;
+
+ assert(remote && spec);
+
+ if (refspec_parse(&refspec, spec) < 0)
+ return -1;
+
+ git__free(remote->fetch.src);
+ git__free(remote->fetch.dst);
+ remote->fetch.src = refspec.src;
+ remote->fetch.dst = refspec.dst;
+
+ return 0;
+}
+
const git_refspec *git_remote_fetchspec(git_remote *remote)
{
assert(remote);
return &remote->fetch;
}
+int git_remote_set_pushspec(git_remote *remote, const char *spec)
+{
+ git_refspec refspec;
+
+ assert(remote && spec);
+
+ if (refspec_parse(&refspec, spec) < 0)
+ return -1;
+
+ git__free(remote->push.src);
+ git__free(remote->push.dst);
+ remote->push.src = refspec.src;
+ remote->push.dst = refspec.dst;
+
+ return 0;
+}
+
const git_refspec *git_remote_pushspec(git_remote *remote)
{
assert(remote);
@@ -213,57 +280,56 @@ const git_refspec *git_remote_pushspec(git_remote *remote)
int git_remote_connect(git_remote *remote, int direction)
{
- int error;
git_transport *t;
assert(remote);
- error = git_transport_new(&t, remote->url);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to create transport");
+ if (git_transport_new(&t, remote->url) < 0)
+ return -1;
- error = t->connect(t, direction);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to connect the transport");
- goto cleanup;
+ if (t->connect(t, direction) < 0) {
+ goto on_error;
}
remote->transport = t;
-cleanup:
- if (error < GIT_SUCCESS)
- t->free(t);
+ return 0;
- return error;
+on_error:
+ t->free(t);
+ return -1;
}
int git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload)
{
assert(remote);
- if (!remote->transport || !remote->transport->connected)
- return git__throw(GIT_ERROR, "The remote is not connected");
+ if (!remote->transport || !remote->transport->connected) {
+ giterr_set(GITERR_NET, "The remote is not connected");
+ return -1;
+ }
return remote->transport->ls(remote->transport, list_cb, payload);
}
-int git_remote_download(char **filename, git_remote *remote)
+int git_remote_download(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats)
{
int error;
- assert(filename && remote);
+ assert(remote && bytes && stats);
if ((error = git_fetch_negotiate(remote)) < 0)
- return git__rethrow(error, "Error negotiating");
+ return error;
- return git_fetch_download_pack(filename, remote);
+ return git_fetch_download_pack(remote, bytes, stats);
}
-int git_remote_update_tips(git_remote *remote)
+int git_remote_update_tips(git_remote *remote, int (*cb)(const char *refname, const git_oid *a, const git_oid *b))
{
- int error = GIT_SUCCESS;
+ int error = 0;
unsigned int i = 0;
git_buf refname = GIT_BUF_INIT;
+ git_oid old;
git_vector *refs = &remote->refs;
git_remote_head *head;
git_reference *ref;
@@ -272,36 +338,52 @@ int git_remote_update_tips(git_remote *remote)
assert(remote);
if (refs->length == 0)
- return GIT_SUCCESS;
+ return 0;
/* HEAD is only allowed to be the first in the list */
head = refs->contents[0];
if (!strcmp(head->name, GIT_HEAD_FILE)) {
- error = git_reference_create_oid(&ref, remote->repo, GIT_FETCH_HEAD_FILE, &head->oid, 1);
- i = 1;
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to update FETCH_HEAD");
+ if (git_reference_create_oid(&ref, remote->repo, GIT_FETCH_HEAD_FILE, &head->oid, 1) < 0)
+ return -1;
+ i = 1;
git_reference_free(ref);
}
for (; i < refs->length; ++i) {
head = refs->contents[i];
- error = git_refspec_transform_r(&refname, spec, head->name);
- if (error < GIT_SUCCESS)
- break;
+ if (git_refspec_transform_r(&refname, spec, head->name) < 0)
+ goto on_error;
+
+ error = git_reference_name_to_oid(&old, remote->repo, refname.ptr);
+ if (error < 0 && error != GIT_ENOTFOUND)
+ goto on_error;
+
+ if (error == GIT_ENOTFOUND)
+ memset(&old, 0, GIT_OID_RAWSZ);
+
+ if (!git_oid_cmp(&old, &head->oid))
+ continue;
- error = git_reference_create_oid(&ref, remote->repo, refname.ptr, &head->oid, 1);
- if (error < GIT_SUCCESS)
+ if (git_reference_create_oid(&ref, remote->repo, refname.ptr, &head->oid, 1) < 0)
break;
git_reference_free(ref);
+
+ if (cb != NULL) {
+ if (cb(refname.ptr, &old, &head->oid) < 0)
+ goto on_error;
+ }
}
git_buf_free(&refname);
+ return 0;
+
+on_error:
+ git_buf_free(&refname);
+ return -1;
- return error;
}
int git_remote_connected(git_remote *remote)
@@ -314,13 +396,8 @@ void git_remote_disconnect(git_remote *remote)
{
assert(remote);
- if (remote->transport != NULL) {
- if (remote->transport->connected)
+ if (remote->transport != NULL && remote->transport->connected)
remote->transport->close(remote->transport);
-
- remote->transport->free(remote->transport);
- remote->transport = NULL;
- }
}
void git_remote_free(git_remote *remote)
@@ -328,13 +405,106 @@ void git_remote_free(git_remote *remote)
if (remote == NULL)
return;
+ if (remote->transport != NULL) {
+ git_remote_disconnect(remote);
+
+ remote->transport->free(remote->transport);
+ remote->transport = NULL;
+ }
+
+ git_vector_free(&remote->refs);
+
git__free(remote->fetch.src);
git__free(remote->fetch.dst);
git__free(remote->push.src);
git__free(remote->push.dst);
git__free(remote->url);
git__free(remote->name);
- git_vector_free(&remote->refs);
- git_remote_disconnect(remote);
git__free(remote);
}
+
+struct cb_data {
+ git_vector *list;
+ regex_t *preg;
+};
+
+static int remote_list_cb(const char *name, const char *value, void *data_)
+{
+ struct cb_data *data = (struct cb_data *)data_;
+ size_t nmatch = 2;
+ regmatch_t pmatch[2];
+ GIT_UNUSED(value);
+
+ if (!regexec(data->preg, name, nmatch, pmatch, 0)) {
+ char *remote_name = git__strndup(&name[pmatch[1].rm_so], pmatch[1].rm_eo - pmatch[1].rm_so);
+ GITERR_CHECK_ALLOC(remote_name);
+
+ if (git_vector_insert(data->list, remote_name) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+int git_remote_list(git_strarray *remotes_list, git_repository *repo)
+{
+ git_config *cfg;
+ git_vector list;
+ regex_t preg;
+ struct cb_data data;
+ int error;
+
+ if (git_repository_config__weakptr(&cfg, repo) < 0)
+ return -1;
+
+ if (git_vector_init(&list, 4, NULL) < 0)
+ return -1;
+
+ if (regcomp(&preg, "^remote\\.(.*)\\.url$", REG_EXTENDED) < 0) {
+ giterr_set(GITERR_OS, "Remote catch regex failed to compile");
+ return -1;
+ }
+
+ data.list = &list;
+ data.preg = &preg;
+ error = git_config_foreach(cfg, remote_list_cb, &data);
+ regfree(&preg);
+ if (error < 0) {
+ size_t i;
+ char *elem;
+ git_vector_foreach(&list, i, elem) {
+ git__free(elem);
+ }
+
+ git_vector_free(&list);
+ return error;
+ }
+
+ remotes_list->strings = (char **)list.contents;
+ remotes_list->count = list.length;
+
+ return 0;
+}
+
+int git_remote_add(git_remote **out, git_repository *repo, const char *name, const char *url)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ if (git_buf_printf(&buf, "refs/heads/*:refs/remotes/%s/*", name) < 0)
+ return -1;
+
+ if (git_remote_new(out, repo, name, url, git_buf_cstr(&buf)) < 0)
+ goto on_error;
+
+ git_buf_free(&buf);
+
+ if (git_remote_save(*out) < 0)
+ goto on_error;
+
+ return 0;
+
+on_error:
+ git_buf_free(&buf);
+ git_remote_free(*out);
+ return -1;
+}
diff --git a/src/remote.h b/src/remote.h
index a24e14845..5a1625d05 100644
--- a/src/remote.h
+++ b/src/remote.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
diff --git a/src/repository.c b/src/repository.c
index 536522a9b..6ce3a560f 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -1,10 +1,11 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include <stdarg.h>
+#include <ctype.h>
#include "git2/object.h"
@@ -20,12 +21,11 @@
#define GIT_OBJECTS_INFO_DIR GIT_OBJECTS_DIR "info/"
#define GIT_OBJECTS_PACK_DIR GIT_OBJECTS_DIR "pack/"
-#define GIT_FILE_CONTENT_PREFIX "gitdir: "
+#define GIT_FILE_CONTENT_PREFIX "gitdir:"
#define GIT_BRANCH_MASTER "master"
-#define GIT_CONFIG_CORE_REPOSITORYFORMATVERSION "core.repositoryformatversion"
-#define GIT_REPOSITORYFORMATVERSION 0
+#define GIT_REPO_VERSION 0
static void drop_odb(git_repository *repo)
{
@@ -43,6 +43,8 @@ static void drop_config(git_repository *repo)
git_config_free(repo->_config);
repo->_config = NULL;
}
+
+ git_repository__cvar_cache_clear(repo);
}
static void drop_index(git_repository *repo)
@@ -62,6 +64,7 @@ void git_repository_free(git_repository *repo)
git_cache_free(&repo->objects);
git_repository__refcache_free(&repo->references);
git_attr_cache_flush(repo);
+ git_submodule_config_free(repo);
git__free(repo->path_repository);
git__free(repo->workdir);
@@ -78,218 +81,423 @@ void git_repository_free(git_repository *repo)
*
* Open a repository object from its path
*/
-static int quickcheck_repository_dir(git_buf *repository_path)
+static bool valid_repository_path(git_buf *repository_path)
{
/* Check OBJECTS_DIR first, since it will generate the longest path name */
- if (git_path_contains_dir(repository_path, GIT_OBJECTS_DIR, 0) < 0)
- return GIT_ERROR;
+ if (git_path_contains_dir(repository_path, GIT_OBJECTS_DIR) == false)
+ return false;
/* Ensure HEAD file exists */
- if (git_path_contains_file(repository_path, GIT_HEAD_FILE, 0) < 0)
- return GIT_ERROR;
+ if (git_path_contains_file(repository_path, GIT_HEAD_FILE) == false)
+ return false;
- if (git_path_contains_dir(repository_path, GIT_REFS_DIR, 0) < 0)
- return GIT_ERROR;
+ if (git_path_contains_dir(repository_path, GIT_REFS_DIR) == false)
+ return false;
- return GIT_SUCCESS;
+ return true;
}
-
static git_repository *repository_alloc(void)
{
- int error;
-
git_repository *repo = git__malloc(sizeof(git_repository));
if (!repo)
return NULL;
memset(repo, 0x0, sizeof(git_repository));
- error = git_cache_init(&repo->objects, GIT_DEFAULT_CACHE_SIZE, &git_object__free);
- if (error < GIT_SUCCESS) {
+ if (git_cache_init(&repo->objects, GIT_DEFAULT_CACHE_SIZE, &git_object__free) < 0) {
git__free(repo);
return NULL;
}
+ /* set all the entries in the cvar cache to `unset` */
+ git_repository__cvar_cache_clear(repo);
+
return repo;
}
static int load_config_data(git_repository *repo)
{
- int error, is_bare;
+ int is_bare;
+ git_config *config;
+
+ if (git_repository_config__weakptr(&config, repo) < 0)
+ return -1;
+
+ if (git_config_get_bool(&is_bare, config, "core.bare") < 0)
+ return -1; /* FIXME: We assume that a missing core.bare
+ variable is an error. Is this right? */
+
+ repo->is_bare = is_bare;
+ return 0;
+}
+
+static int load_workdir(git_repository *repo, git_buf *parent_path)
+{
+ int error;
git_config *config;
+ const char *worktree;
+ git_buf worktree_buf = GIT_BUF_INIT;
+
+ if (repo->is_bare)
+ return 0;
+
+ if (git_repository_config__weakptr(&config, repo) < 0)
+ return -1;
- error = git_repository_config__weakptr(&config, repo);
- if (error < GIT_SUCCESS)
+ error = git_config_get_string(&worktree, config, "core.worktree");
+ if (!error && worktree != NULL)
+ repo->workdir = git__strdup(worktree);
+ else if (error != GIT_ENOTFOUND)
return error;
+ else {
+ giterr_clear();
+
+ if (parent_path && git_path_isdir(parent_path->ptr))
+ repo->workdir = git_buf_detach(parent_path);
+ else {
+ git_path_dirname_r(&worktree_buf, repo->path_repository);
+ git_path_to_dir(&worktree_buf);
+ repo->workdir = git_buf_detach(&worktree_buf);
+ }
+ }
+
+ GITERR_CHECK_ALLOC(repo->workdir);
+
+ return 0;
+}
+
+/*
+ * This function returns furthest offset into path where a ceiling dir
+ * is found, so we can stop processing the path at that point.
+ *
+ * Note: converting this to use git_bufs instead of GIT_PATH_MAX buffers on
+ * the stack could remove directories name limits, but at the cost of doing
+ * repeated malloc/frees inside the loop below, so let's not do it now.
+ */
+static int find_ceiling_dir_offset(
+ const char *path,
+ const char *ceiling_directories)
+{
+ char buf[GIT_PATH_MAX + 1];
+ char buf2[GIT_PATH_MAX + 1];
+ const char *ceil, *sep;
+ size_t len, max_len = 0, min_len;
+
+ assert(path);
+
+ min_len = (size_t)(git_path_root(path) + 1);
+
+ if (ceiling_directories == NULL || min_len == 0)
+ return (int)min_len;
+
+ for (sep = ceil = ceiling_directories; *sep; ceil = sep + 1) {
+ for (sep = ceil; *sep && *sep != GIT_PATH_LIST_SEPARATOR; sep++);
+ len = sep - ceil;
+
+ if (len == 0 || len >= sizeof(buf) || git_path_root(ceil) == -1)
+ continue;
+
+ strncpy(buf, ceil, len);
+ buf[len] = '\0';
- error = git_config_get_bool(config, "core.bare", &is_bare);
- if (error == GIT_SUCCESS)
- repo->is_bare = is_bare;
+ if (p_realpath(buf, buf2) == NULL)
+ continue;
+
+ len = strlen(buf2);
+ if (len > 0 && buf2[len-1] == '/')
+ buf[--len] = '\0';
- /* TODO: what else can we load/cache here? */
+ if (!strncmp(path, buf2, len) &&
+ path[len] == '/' &&
+ len > max_len)
+ {
+ max_len = len;
+ }
+ }
- return GIT_SUCCESS;
+ return (int)(max_len <= min_len ? min_len : max_len);
}
-static int load_workdir(git_repository *repo)
+/*
+ * Read the contents of `file_path` and set `path_out` to the repo dir that
+ * it points to. Before calling, set `path_out` to the base directory that
+ * should be used if the contents of `file_path` are a relative path.
+ */
+static int read_gitfile(git_buf *path_out, const char *file_path)
{
- int error;
- git_buf workdir_buf = GIT_BUF_INIT;
+ int error = 0;
+ git_buf file = GIT_BUF_INIT;
+ size_t prefix_len = strlen(GIT_FILE_CONTENT_PREFIX);
- if (repo->is_bare)
- return GIT_SUCCESS;
+ assert(path_out && file_path);
- git_path_dirname_r(&workdir_buf, repo->path_repository);
- git_path_to_dir(&workdir_buf);
+ if (git_futils_readbuffer(&file, file_path) < 0)
+ return -1;
- if ((error = git_buf_lasterror(&workdir_buf)) == GIT_SUCCESS)
- repo->workdir = git_buf_detach(&workdir_buf);
+ git_buf_rtrim(&file);
- git_buf_free(&workdir_buf);
+ if (file.size <= prefix_len ||
+ memcmp(file.ptr, GIT_FILE_CONTENT_PREFIX, prefix_len) != 0)
+ {
+ giterr_set(GITERR_REPOSITORY, "The `.git` file at '%s' is malformed", file_path);
+ error = -1;
+ }
+ else if ((error = git_path_dirname_r(path_out, file_path)) >= 0) {
+ const char *gitlink = ((const char *)file.ptr) + prefix_len;
+ while (*gitlink && git__isspace(*gitlink)) gitlink++;
+ error = git_path_prettify_dir(path_out, gitlink, path_out->ptr);
+ }
+ git_buf_free(&file);
return error;
}
-int git_repository_open(git_repository **repo_out, const char *path)
+static int find_repo(
+ git_buf *repo_path,
+ git_buf *parent_path,
+ const char *start_path,
+ uint32_t flags,
+ const char *ceiling_dirs)
{
- int error = GIT_SUCCESS;
- git_buf path_buf = GIT_BUF_INIT;
- git_repository *repo = NULL;
+ int error;
+ git_buf path = GIT_BUF_INIT;
+ struct stat st;
+ dev_t initial_device = 0;
+ bool try_with_dot_git = false;
+ int ceiling_offset;
+
+ git_buf_free(repo_path);
+
+ if ((error = git_path_prettify_dir(&path, start_path, NULL)) < 0)
+ return error;
+
+ ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs);
+
+ if ((error = git_buf_joinpath(&path, path.ptr, DOT_GIT)) < 0)
+ return error;
+
+ while (!error && !git_buf_len(repo_path)) {
+ if (p_stat(path.ptr, &st) == 0) {
+ /* check that we have not crossed device boundaries */
+ if (initial_device == 0)
+ initial_device = st.st_dev;
+ else if (st.st_dev != initial_device &&
+ (flags & GIT_REPOSITORY_OPEN_CROSS_FS) == 0)
+ break;
+
+ if (S_ISDIR(st.st_mode)) {
+ if (valid_repository_path(&path)) {
+ git_path_to_dir(&path);
+ git_buf_set(repo_path, path.ptr, path.size);
+ break;
+ }
+ }
+ else if (S_ISREG(st.st_mode)) {
+ git_buf repo_link = GIT_BUF_INIT;
- error = git_path_prettify_dir(&path_buf, path, NULL);
- if (error < GIT_SUCCESS)
- goto cleanup;
-
- /**
- * Check if the path we've been given is actually the path
- * of the working dir, by testing if it contains a `.git`
- * folder inside of it.
- */
- git_path_contains_dir(&path_buf, GIT_DIR, 1); /* append on success */
- /* ignore error, since it just means `path/.git` doesn't exist */
-
- if (quickcheck_repository_dir(&path_buf) < GIT_SUCCESS) {
- error = git__throw(GIT_ENOTAREPO,
- "The given path (%s) is not a valid Git repository", git_buf_cstr(&path_buf));
- goto cleanup;
+ if (!(error = read_gitfile(&repo_link, path.ptr))) {
+ if (valid_repository_path(&repo_link))
+ git_buf_swap(repo_path, &repo_link);
+
+ git_buf_free(&repo_link);
+ break;
+ }
+ git_buf_free(&repo_link);
+ }
+ }
+
+ /* move up one directory level */
+ if (git_path_dirname_r(&path, path.ptr) < 0) {
+ error = -1;
+ break;
+ }
+
+ if (try_with_dot_git) {
+ /* if we tried original dir with and without .git AND either hit
+ * directory ceiling or NO_SEARCH was requested, then be done.
+ */
+ if (path.ptr[ceiling_offset] == '\0' ||
+ (flags & GIT_REPOSITORY_OPEN_NO_SEARCH) != 0)
+ break;
+ /* otherwise look first for .git item */
+ error = git_buf_joinpath(&path, path.ptr, DOT_GIT);
+ }
+ try_with_dot_git = !try_with_dot_git;
}
- repo = repository_alloc();
- if (repo == NULL) {
- error = GIT_ENOMEM;
- goto cleanup;
+ if (!error && parent_path != NULL) {
+ if (!git_buf_len(repo_path))
+ git_buf_clear(parent_path);
+ else {
+ git_path_dirname_r(parent_path, path.ptr);
+ git_path_to_dir(parent_path);
+ }
+ if (git_buf_oom(parent_path))
+ return -1;
}
- repo->path_repository = git_buf_detach(&path_buf);
- if (repo->path_repository == NULL) {
- error = GIT_ENOMEM;
- goto cleanup;
+ git_buf_free(&path);
+
+ if (!git_buf_len(repo_path) && !error) {
+ giterr_set(GITERR_REPOSITORY,
+ "Could not find repository from '%s'", start_path);
+ error = GIT_ENOTFOUND;
}
- error = load_config_data(repo);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ return error;
+}
- error = load_workdir(repo);
- if (error < GIT_SUCCESS)
- goto cleanup;
+int git_repository_open_ext(
+ git_repository **repo_ptr,
+ const char *start_path,
+ uint32_t flags,
+ const char *ceiling_dirs)
+{
+ int error;
+ git_buf path = GIT_BUF_INIT, parent = GIT_BUF_INIT;
+ git_repository *repo;
- *repo_out = repo;
- return GIT_SUCCESS;
+ *repo_ptr = NULL;
- cleanup:
- git_repository_free(repo);
- git_buf_free(&path_buf);
- return error;
+ if ((error = find_repo(&path, &parent, start_path, flags, ceiling_dirs)) < 0)
+ return error;
+
+ repo = repository_alloc();
+ GITERR_CHECK_ALLOC(repo);
+
+ repo->path_repository = git_buf_detach(&path);
+ GITERR_CHECK_ALLOC(repo->path_repository);
+
+ if ((error = load_config_data(repo)) < 0 ||
+ (error = load_workdir(repo, &parent)) < 0)
+ {
+ git_repository_free(repo);
+ return error;
+ }
+
+ git_buf_free(&parent);
+ *repo_ptr = repo;
+ return 0;
+}
+
+int git_repository_open(git_repository **repo_out, const char *path)
+{
+ return git_repository_open_ext(
+ repo_out, path, GIT_REPOSITORY_OPEN_NO_SEARCH, NULL);
+}
+
+int git_repository_discover(
+ char *repository_path,
+ size_t size,
+ const char *start_path,
+ int across_fs,
+ const char *ceiling_dirs)
+{
+ git_buf path = GIT_BUF_INIT;
+ uint32_t flags = across_fs ? GIT_REPOSITORY_OPEN_CROSS_FS : 0;
+ int error;
+
+ assert(start_path && repository_path && size > 0);
+
+ *repository_path = '\0';
+
+ if ((error = find_repo(&path, NULL, start_path, flags, ceiling_dirs)) < 0)
+ return error != GIT_ENOTFOUND ? -1 : error;
+
+ if (size < (size_t)(path.size + 1)) {
+ giterr_set(GITERR_REPOSITORY,
+ "The given buffer is too long to store the discovered path");
+ git_buf_free(&path);
+ return -1;
+ }
+
+ /* success: we discovered a repository */
+ git_buf_copy_cstr(repository_path, size, &path);
+ git_buf_free(&path);
+ return 0;
}
static int load_config(
- git_config **out,
- git_repository *repo,
- const char *global_config_path,
- const char *system_config_path)
+ git_config **out,
+ git_repository *repo,
+ const char *global_config_path,
+ const char *system_config_path)
{
git_buf config_path = GIT_BUF_INIT;
- int error;
git_config *cfg = NULL;
assert(repo && out);
- error = git_config_new(&cfg);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_config_new(&cfg) < 0)
+ return -1;
+
+ if (git_buf_joinpath(
+ &config_path, repo->path_repository, GIT_CONFIG_FILENAME_INREPO) < 0)
+ goto on_error;
- error = git_buf_joinpath(&config_path, repo->path_repository,
- GIT_CONFIG_FILENAME_INREPO);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (git_config_add_file_ondisk(cfg, config_path.ptr, 3) < 0)
+ goto on_error;
- error = git_config_add_file_ondisk(cfg, config_path.ptr, 3);
- git_buf_free(&config_path); /* done with config_path now */
- if (error < GIT_SUCCESS)
- goto cleanup;
+ git_buf_free(&config_path);
if (global_config_path != NULL) {
- error = git_config_add_file_ondisk(cfg, global_config_path, 2);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (git_config_add_file_ondisk(cfg, global_config_path, 2) < 0)
+ goto on_error;
}
if (system_config_path != NULL) {
- error = git_config_add_file_ondisk(cfg, system_config_path, 1);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (git_config_add_file_ondisk(cfg, system_config_path, 1) < 0)
+ goto on_error;
}
*out = cfg;
- return GIT_SUCCESS;
+ return 0;
-cleanup:
+on_error:
+ git_buf_free(&config_path);
git_config_free(cfg);
*out = NULL;
- return error;
+ return -1;
}
int git_repository_config__weakptr(git_config **out, git_repository *repo)
{
if (repo->_config == NULL) {
- int error;
git_buf global_buf = GIT_BUF_INIT, system_buf = GIT_BUF_INIT;
+ int res;
const char *global_config_path = NULL;
const char *system_config_path = NULL;
- if (git_config_find_global_r(&global_buf) == GIT_SUCCESS)
+ if (git_config_find_global_r(&global_buf) == 0)
global_config_path = global_buf.ptr;
- if (git_config_find_system_r(&system_buf) == GIT_SUCCESS)
+ if (git_config_find_system_r(&system_buf) == 0)
system_config_path = system_buf.ptr;
- error = load_config(&repo->_config, repo, global_config_path, system_config_path);
+ res = load_config(&repo->_config, repo, global_config_path, system_config_path);
git_buf_free(&global_buf);
git_buf_free(&system_buf);
- if (error < GIT_SUCCESS)
- return error;
+ if (res < 0)
+ return -1;
GIT_REFCOUNT_OWN(repo->_config, repo);
}
*out = repo->_config;
- return GIT_SUCCESS;
+ return 0;
}
int git_repository_config(git_config **out, git_repository *repo)
{
- int error = git_repository_config__weakptr(out, repo);
-
- if (error == GIT_SUCCESS) {
- GIT_REFCOUNT_INC(*out);
- }
+ if (git_repository_config__weakptr(out, repo) < 0)
+ return -1;
- return error;
+ GIT_REFCOUNT_INC(*out);
+ return 0;
}
void git_repository_set_config(git_repository *repo, git_config *config)
@@ -307,34 +515,32 @@ int git_repository_odb__weakptr(git_odb **out, git_repository *repo)
assert(repo && out);
if (repo->_odb == NULL) {
- int error;
git_buf odb_path = GIT_BUF_INIT;
+ int res;
- error = git_buf_joinpath(&odb_path, repo->path_repository, GIT_OBJECTS_DIR);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_buf_joinpath(&odb_path, repo->path_repository, GIT_OBJECTS_DIR) < 0)
+ return -1;
- error = git_odb_open(&repo->_odb, odb_path.ptr);
+ res = git_odb_open(&repo->_odb, odb_path.ptr);
git_buf_free(&odb_path); /* done with path */
- if (error < GIT_SUCCESS)
- return error;
+
+ if (res < 0)
+ return -1;
GIT_REFCOUNT_OWN(repo->_odb, repo);
}
*out = repo->_odb;
- return GIT_SUCCESS;
+ return 0;
}
int git_repository_odb(git_odb **out, git_repository *repo)
{
- int error = git_repository_odb__weakptr(out, repo);
+ if (git_repository_odb__weakptr(out, repo) < 0)
+ return -1;
- if (error == GIT_SUCCESS) {
- GIT_REFCOUNT_INC(*out);
- }
-
- return error;
+ GIT_REFCOUNT_INC(*out);
+ return 0;
}
void git_repository_set_odb(git_repository *repo, git_odb *odb)
@@ -345,6 +551,7 @@ void git_repository_set_odb(git_repository *repo, git_odb *odb)
repo->_odb = odb;
GIT_REFCOUNT_OWN(repo->_odb, repo);
+ GIT_REFCOUNT_INC(odb);
}
int git_repository_index__weakptr(git_index **out, git_repository *repo)
@@ -352,34 +559,32 @@ int git_repository_index__weakptr(git_index **out, git_repository *repo)
assert(out && repo);
if (repo->_index == NULL) {
- int error;
+ int res;
git_buf index_path = GIT_BUF_INIT;
- error = git_buf_joinpath(&index_path, repo->path_repository, GIT_INDEX_FILE);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_buf_joinpath(&index_path, repo->path_repository, GIT_INDEX_FILE) < 0)
+ return -1;
- error = git_index_open(&repo->_index, index_path.ptr);
+ res = git_index_open(&repo->_index, index_path.ptr);
git_buf_free(&index_path); /* done with path */
- if (error < GIT_SUCCESS)
- return error;
+
+ if (res < 0)
+ return -1;
GIT_REFCOUNT_OWN(repo->_index, repo);
}
*out = repo->_index;
- return GIT_SUCCESS;
+ return 0;
}
int git_repository_index(git_index **out, git_repository *repo)
{
- int error = git_repository_index__weakptr(out, repo);
-
- if (error == GIT_SUCCESS) {
- GIT_REFCOUNT_INC(*out);
- }
+ if (git_repository_index__weakptr(out, repo) < 0)
+ return -1;
- return error;
+ GIT_REFCOUNT_INC(*out);
+ return 0;
}
void git_repository_set_index(git_repository *repo, git_index *index)
@@ -390,476 +595,269 @@ void git_repository_set_index(git_repository *repo, git_index *index)
repo->_index = index;
GIT_REFCOUNT_OWN(repo->_index, repo);
-}
-
-
-static int retrieve_device(dev_t *device_out, const char *path)
-{
- struct stat path_info;
-
- assert(device_out);
-
- if (p_lstat(path, &path_info))
- return git__throw(GIT_EOSERR, "Failed to get file informations: %s", path);
-
- *device_out = path_info.st_dev;
-
- return GIT_SUCCESS;
-}
-
-/*
- * This function returns furthest offset into path where a ceiling dir
- * is found, so we can stop processing the path at that point.
- *
- * Note: converting this to use git_bufs instead of GIT_PATH_MAX buffers on
- * the stack could remove directories name limits, but at the cost of doing
- * repeated malloc/frees inside the loop below, so let's not do it now.
- */
-static int retrieve_ceiling_directories_offset(
- const char *path,
- const char *ceiling_directories)
-{
- char buf[GIT_PATH_MAX + 1];
- char buf2[GIT_PATH_MAX + 1];
- const char *ceil, *sep;
- int len, max_len = -1;
- int min_len;
-
- assert(path);
-
- min_len = git_path_root(path) + 1;
-
- if (ceiling_directories == NULL || min_len == 0)
- return min_len;
-
- for (sep = ceil = ceiling_directories; *sep; ceil = sep + 1) {
- for (sep = ceil; *sep && *sep != GIT_PATH_LIST_SEPARATOR; sep++);
- len = sep - ceil;
-
- if (len == 0 || len >= (int)sizeof(buf) || git_path_root(ceil) == -1)
- continue;
-
- strncpy(buf, ceil, len);
- buf[len] = '\0';
-
- if (p_realpath(buf, buf2) == NULL)
- continue;
-
- len = strlen(buf2);
- if (len > 0 && buf2[len-1] == '/')
- buf[--len] = '\0';
-
- if (!strncmp(path, buf2, len) &&
- path[len] == '/' &&
- len > max_len)
- {
- max_len = len;
- }
- }
-
- return max_len <= min_len ? min_len : max_len;
-}
-
-/*
- * Read the contents of `file_path` and set `path_out` to the repo dir that
- * it points to. Before calling, set `path_out` to the base directory that
- * should be used if the contents of `file_path` are a relative path.
- */
-static int read_gitfile(git_buf *path_out, const char *file_path, const char *base_path)
-{
- git_fbuffer file;
- int error;
-
- assert(path_out && file_path);
-
- error = git_futils_readbuffer(&file, file_path);
- if (error < GIT_SUCCESS)
- return error;
-
- if (git__prefixcmp((char *)file.data, GIT_FILE_CONTENT_PREFIX)) {
- git_futils_freebuffer(&file);
- return git__throw(GIT_ENOTFOUND, "Invalid gitfile format `%s`", file_path);
- }
-
- git_futils_fbuffer_rtrim(&file);
-
- if (strlen(GIT_FILE_CONTENT_PREFIX) == file.len) {
- git_futils_freebuffer(&file);
- return git__throw(GIT_ENOTFOUND, "No path in git file `%s`", file_path);
- }
-
- error = git_path_prettify_dir(path_out,
- ((char *)file.data) + strlen(GIT_FILE_CONTENT_PREFIX), base_path);
-
- git_futils_freebuffer(&file);
-
- if (error == GIT_SUCCESS && git_path_exists(path_out->ptr) == 0)
- return GIT_SUCCESS;
-
- return git__throw(GIT_EOBJCORRUPTED, "The `.git` file points to a nonexistent path");
-}
-
-int git_repository_discover(
- char *repository_path,
- size_t size,
- const char *start_path,
- int across_fs,
- const char *ceiling_dirs)
-{
- int error, ceiling_offset;
- git_buf bare_path = GIT_BUF_INIT;
- git_buf normal_path = GIT_BUF_INIT;
- git_buf *found_path = NULL;
- dev_t current_device = 0;
-
- assert(start_path && repository_path);
-
- *repository_path = '\0';
-
- error = git_path_prettify_dir(&bare_path, start_path, NULL);
- if (error < GIT_SUCCESS)
- goto cleanup;
-
- if (!across_fs) {
- error = retrieve_device(&current_device, bare_path.ptr);
- if (error < GIT_SUCCESS)
- goto cleanup;
- }
-
- ceiling_offset = retrieve_ceiling_directories_offset(bare_path.ptr, ceiling_dirs);
-
- while(1) {
- error = git_buf_joinpath(&normal_path, bare_path.ptr, DOT_GIT);
- if (error < GIT_SUCCESS)
- break;
-
- /**
- * If the `.git` file is regular instead of
- * a directory, it should contain the path of the actual git repository
- */
- if (git_path_isfile(normal_path.ptr) == GIT_SUCCESS) {
- git_buf gitfile_path = GIT_BUF_INIT;
-
- error = read_gitfile(&gitfile_path, normal_path.ptr, bare_path.ptr);
- if (error < GIT_SUCCESS)
- git__rethrow(error, "Unable to read git file `%s`", normal_path.ptr);
- else if ((error = quickcheck_repository_dir(&gitfile_path)) < GIT_SUCCESS)
- git__throw(GIT_ENOTFOUND,
- "The `.git` file found at '%s' points "
- "to a nonexistent git folder", normal_path.ptr);
- else {
- git_buf_swap(&normal_path, &gitfile_path);
- found_path = &normal_path;
- }
-
- git_buf_free(&gitfile_path);
- break;
- }
-
- /**
- * If the `.git` file is a folder, we check inside of it
- */
- if (git_path_isdir(normal_path.ptr) == GIT_SUCCESS) {
- error = quickcheck_repository_dir(&normal_path);
- if (error == GIT_SUCCESS) {
- found_path = &normal_path;
- break;
- }
- }
-
- /**
- * Otherwise, the repository may be bare, let's check
- * the root anyway
- */
- error = quickcheck_repository_dir(&bare_path);
- if (error == GIT_SUCCESS) {
- found_path = &bare_path;
- break;
- }
-
- /**
- * If we didn't find it, walk up the tree
- */
- error = git_path_dirname_r(&normal_path, bare_path.ptr);
- if (error < GIT_SUCCESS) {
- git__rethrow(GIT_EOSERR, "Failed to dirname '%s'", bare_path.ptr);
- break;
- }
-
- git_buf_swap(&bare_path, &normal_path);
-
- if (!across_fs) {
- dev_t new_device;
- error = retrieve_device(&new_device, bare_path.ptr);
-
- if (error < GIT_SUCCESS || current_device != new_device) {
- error = git__throw(GIT_ENOTAREPO,
- "Not a git repository (or any parent up to mount parent %s)\n"
- "Stopping at filesystem boundary.", normal_path.ptr);
- break;
- }
- current_device = new_device;
- }
-
- /* nothing has been found, lets try the parent directory
- * but stop if we hit one of the ceiling directories
- */
- if (bare_path.ptr[ceiling_offset] == '\0') {
- error = git__throw(GIT_ENOTAREPO,
- "Not a git repository (or any of the parent directories): %s", start_path);
- break;
- }
- }
-
- assert(found_path || error != GIT_SUCCESS);
-
- if (found_path) {
- if ((error = git_path_to_dir(found_path)) < GIT_SUCCESS)
- git__rethrow(error, "Could not convert git repository to directory");
- else if (size < (size_t)(found_path->size + 1))
- error = git__throw(GIT_ESHORTBUFFER,
- "The repository buffer is not long enough to "
- "handle the repository path `%s`", found_path->ptr);
- else
- git_buf_copy_cstr(repository_path, size, found_path);
- }
-
-cleanup:
- git_buf_free(&bare_path);
- git_buf_free(&normal_path);
- return error;
+ GIT_REFCOUNT_INC(index);
}
static int check_repositoryformatversion(git_repository *repo)
{
git_config *config;
- int version, error = GIT_SUCCESS;
+ int version;
- if ((error = git_repository_config(&config, repo)) < GIT_SUCCESS)
- return git__throw(error, "Failed to open config file.");
+ if (git_repository_config__weakptr(&config, repo) < 0)
+ return -1;
- error = git_config_get_int32(config, GIT_CONFIG_CORE_REPOSITORYFORMATVERSION, &version);
+ if (git_config_get_int32(&version, config, "core.repositoryformatversion") < 0)
+ return -1;
- if (GIT_REPOSITORYFORMATVERSION < version)
- error = git__throw(GIT_ERROR, "Unsupported git repository version (Expected version <= %d, found %d).", GIT_REPOSITORYFORMATVERSION, version);
-
- git_config_free(config);
+ if (GIT_REPO_VERSION < version) {
+ giterr_set(GITERR_REPOSITORY,
+ "Unsupported repository version %d. Only versions up to %d are supported.",
+ version, GIT_REPO_VERSION);
+ return -1;
+ }
- return error;
+ return 0;
}
static int repo_init_reinit(git_repository **repo_out, const char *repository_path, int is_bare)
{
- int error;
git_repository *repo = NULL;
- if ((error = git_repository_open(&repo, repository_path)) < GIT_SUCCESS)
- goto error;
+ GIT_UNUSED(is_bare);
+
+ if (git_repository_open(&repo, repository_path) < 0)
+ return -1;
- if ((error = check_repositoryformatversion(repo)) < GIT_SUCCESS)
- goto error;
+ if (check_repositoryformatversion(repo) < 0) {
+ git_repository_free(repo);
+ return -1;
+ }
/* TODO: reinitialize the templates */
*repo_out = repo;
-
- return GIT_SUCCESS;
-
-error:
- git_repository_free(repo);
-
- return git__rethrow(error,
- "Failed to reinitialize the %srepository at '%s'. ",
- is_bare ? "bare " : "", repository_path);
+ return 0;
}
static int repo_init_createhead(const char *git_dir)
{
- int error;
git_buf ref_path = GIT_BUF_INIT;
git_filebuf ref = GIT_FILEBUF_INIT;
- if (!(error = git_buf_joinpath(&ref_path, git_dir, GIT_HEAD_FILE)) &&
- !(error = git_filebuf_open(&ref, ref_path.ptr, 0)) &&
- !(error = git_filebuf_printf(&ref, "ref: refs/heads/master\n")))
- error = git_filebuf_commit(&ref, GIT_REFS_FILE_MODE);
+ if (git_buf_joinpath(&ref_path, git_dir, GIT_HEAD_FILE) < 0 ||
+ git_filebuf_open(&ref, ref_path.ptr, 0) < 0 ||
+ git_filebuf_printf(&ref, "ref: refs/heads/master\n") < 0 ||
+ git_filebuf_commit(&ref, GIT_REFS_FILE_MODE) < 0)
+ return -1;
git_buf_free(&ref_path);
- return error;
+ return 0;
}
static int repo_init_config(const char *git_dir, int is_bare)
{
git_buf cfg_path = GIT_BUF_INIT;
git_config *config = NULL;
- int error = GIT_SUCCESS;
#define SET_REPO_CONFIG(type, name, val) {\
- error = git_config_set_##type(config, name, val);\
- if (error < GIT_SUCCESS)\
- goto cleanup;\
+ if (git_config_set_##type(config, name, val) < 0) { \
+ git_buf_free(&cfg_path); \
+ git_config_free(config); \
+ return -1; } \
}
- error = git_buf_joinpath(&cfg_path, git_dir, GIT_CONFIG_FILENAME_INREPO);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (git_buf_joinpath(&cfg_path, git_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
+ return -1;
- error = git_config_open_ondisk(&config, cfg_path.ptr);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (git_config_open_ondisk(&config, cfg_path.ptr) < 0) {
+ git_buf_free(&cfg_path);
+ return -1;
+ }
SET_REPO_CONFIG(bool, "core.bare", is_bare);
- SET_REPO_CONFIG(int32, GIT_CONFIG_CORE_REPOSITORYFORMATVERSION, GIT_REPOSITORYFORMATVERSION);
+ SET_REPO_CONFIG(int32, "core.repositoryformatversion", GIT_REPO_VERSION);
/* TODO: what other defaults? */
-cleanup:
git_buf_free(&cfg_path);
git_config_free(config);
+ return 0;
+}
+
+#define GIT_HOOKS_DIR "hooks/"
+#define GIT_HOOKS_DIR_MODE 0755
+
+#define GIT_HOOKS_README_FILE GIT_HOOKS_DIR "README.sample"
+#define GIT_HOOKS_README_MODE 0755
+#define GIT_HOOKS_README_CONTENT \
+"#!/bin/sh\n"\
+"#\n"\
+"# Place appropriately named executable hook scripts into this directory\n"\
+"# to intercept various actions that git takes. See `git help hooks` for\n"\
+"# more information.\n"
+
+#define GIT_INFO_DIR "info/"
+#define GIT_INFO_DIR_MODE 0755
+
+#define GIT_INFO_EXCLUDE_FILE GIT_INFO_DIR "exclude"
+#define GIT_INFO_EXCLUDE_MODE 0644
+#define GIT_INFO_EXCLUDE_CONTENT \
+"# File patterns to ignore; see `git help ignore` for more information.\n"\
+"# Lines that start with '#' are comments.\n"
+
+#define GIT_DESC_FILE "description"
+#define GIT_DESC_MODE 0644
+#define GIT_DESC_CONTENT "Unnamed repository; edit this file 'description' to name the repository.\n"
+
+static int repo_write_template(
+ const char *git_dir, const char *file, mode_t mode, const char *content)
+{
+ git_buf path = GIT_BUF_INIT;
+ int fd, error = 0;
+
+ if (git_buf_joinpath(&path, git_dir, file) < 0)
+ return -1;
+
+ fd = p_open(git_buf_cstr(&path), O_WRONLY | O_CREAT | O_EXCL, mode);
+
+ if (fd >= 0) {
+ error = p_write(fd, content, strlen(content));
+
+ p_close(fd);
+ }
+ else if (errno != EEXIST)
+ error = fd;
+
+ git_buf_free(&path);
+
+ if (error)
+ giterr_set(GITERR_OS,
+ "Failed to initialize repository with template '%s'", file);
+
return error;
}
static int repo_init_structure(const char *git_dir, int is_bare)
{
- int error, i;
+ int i;
struct { const char *dir; mode_t mode; } dirs[] = {
{ GIT_OBJECTS_INFO_DIR, GIT_OBJECT_DIR_MODE }, /* '/objects/info/' */
{ GIT_OBJECTS_PACK_DIR, GIT_OBJECT_DIR_MODE }, /* '/objects/pack/' */
{ GIT_REFS_HEADS_DIR, GIT_REFS_DIR_MODE }, /* '/refs/heads/' */
{ GIT_REFS_TAGS_DIR, GIT_REFS_DIR_MODE }, /* '/refs/tags/' */
+ { GIT_HOOKS_DIR, GIT_HOOKS_DIR_MODE }, /* '/hooks/' */
+ { GIT_INFO_DIR, GIT_INFO_DIR_MODE }, /* '/info/' */
{ NULL, 0 }
};
+ struct { const char *file; mode_t mode; const char *content; } tmpl[] = {
+ { GIT_DESC_FILE, GIT_DESC_MODE, GIT_DESC_CONTENT },
+ { GIT_HOOKS_README_FILE, GIT_HOOKS_README_MODE, GIT_HOOKS_README_CONTENT },
+ { GIT_INFO_EXCLUDE_FILE, GIT_INFO_EXCLUDE_MODE, GIT_INFO_EXCLUDE_CONTENT },
+ { NULL, 0, NULL }
+ };
/* Make the base directory */
- error = git_futils_mkdir_r(git_dir, NULL, is_bare ?
- GIT_BARE_DIR_MODE : GIT_DIR_MODE);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to initialize repository structure. Could not mkdir");
+ if (git_futils_mkdir_r(git_dir, NULL, is_bare ? GIT_BARE_DIR_MODE : GIT_DIR_MODE) < 0)
+ return -1;
/* Hides the ".git" directory */
if (!is_bare) {
#ifdef GIT_WIN32
- error = p_hide_directory__w32(git_dir);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to initialize repository structure");
+ if (p_hide_directory__w32(git_dir) < 0) {
+ giterr_set(GITERR_REPOSITORY,
+ "Failed to mark Git repository folder as hidden");
+ return -1;
+ }
#endif
}
/* Make subdirectories as needed */
for (i = 0; dirs[i].dir != NULL; ++i) {
- error = git_futils_mkdir_r(dirs[i].dir, git_dir, dirs[i].mode);
- if (error < GIT_SUCCESS)
- return git__rethrow(error,
- "Failed to create repository folder `%s`", dirs[i].dir);
+ if (git_futils_mkdir_r(dirs[i].dir, git_dir, dirs[i].mode) < 0)
+ return -1;
}
- /* TODO: what's left? templates? */
+ /* Make template files as needed */
+ for (i = 0; tmpl[i].file != NULL; ++i) {
+ if (repo_write_template(
+ git_dir, tmpl[i].file, tmpl[i].mode, tmpl[i].content) < 0)
+ return -1;
+ }
- return error;
+ return 0;
}
int git_repository_init(git_repository **repo_out, const char *path, unsigned is_bare)
{
- int error = GIT_SUCCESS;
- git_repository *repo = NULL;
git_buf repository_path = GIT_BUF_INIT;
assert(repo_out && path);
- error = git_buf_joinpath(&repository_path, path, is_bare ? "" : GIT_DIR);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_buf_joinpath(&repository_path, path, is_bare ? "" : GIT_DIR) < 0)
+ return -1;
- if (git_path_isdir(repository_path.ptr) == GIT_SUCCESS) {
- if (quickcheck_repository_dir(&repository_path) == GIT_SUCCESS) {
- error = repo_init_reinit(repo_out, repository_path.ptr, is_bare);
+ if (git_path_isdir(repository_path.ptr) == true) {
+ if (valid_repository_path(&repository_path) == true) {
+ int res = repo_init_reinit(repo_out, repository_path.ptr, is_bare);
git_buf_free(&repository_path);
- return error;
+ return res;
}
}
- if (!(error = repo_init_structure(repository_path.ptr, is_bare)) &&
- !(error = repo_init_config(repository_path.ptr, is_bare)) &&
- !(error = repo_init_createhead(repository_path.ptr)))
- error = git_repository_open(repo_out, repository_path.ptr);
- else
- git_repository_free(repo);
+ if (repo_init_structure(repository_path.ptr, is_bare) < 0 ||
+ repo_init_config(repository_path.ptr, is_bare) < 0 ||
+ repo_init_createhead(repository_path.ptr) < 0 ||
+ git_repository_open(repo_out, repository_path.ptr) < 0) {
+ git_buf_free(&repository_path);
+ return -1;
+ }
git_buf_free(&repository_path);
-
- if (error != GIT_SUCCESS)
- git__rethrow(error, "Failed to (re)init the repository `%s`", path);
-
- return error;
+ return 0;
}
int git_repository_head_detached(git_repository *repo)
{
git_reference *ref;
- int error;
- size_t _size;
- git_otype type;
git_odb *odb = NULL;
+ int exists;
- error = git_repository_odb__weakptr(&odb, repo);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_repository_odb__weakptr(&odb, repo) < 0)
+ return -1;
- error = git_reference_lookup(&ref, repo, GIT_HEAD_FILE);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_reference_lookup(&ref, repo, GIT_HEAD_FILE) < 0)
+ return -1;
if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
git_reference_free(ref);
return 0;
}
- error = git_odb_read_header(&_size, &type, odb, git_reference_oid(ref));
+ exists = git_odb_exists(odb, git_reference_oid(ref));
git_reference_free(ref);
-
- if (error < GIT_SUCCESS)
- return error;
-
- if (type != GIT_OBJ_COMMIT)
- return git__throw(GIT_EOBJCORRUPTED, "HEAD is not a commit");
-
- return 1;
+ return exists;
}
int git_repository_head(git_reference **head_out, git_repository *repo)
{
- git_reference *ref, *resolved_ref;
- int error;
-
- *head_out = NULL;
-
- error = git_reference_lookup(&ref, repo, GIT_HEAD_FILE);
- if (error < GIT_SUCCESS)
- return git__rethrow(GIT_ENOTAREPO, "Failed to locate the HEAD");
-
- error = git_reference_resolve(&resolved_ref, ref);
- if (error < GIT_SUCCESS) {
- git_reference_free(ref);
- return git__rethrow(error, "Failed to resolve the HEAD");
- }
-
- git_reference_free(ref);
-
- *head_out = resolved_ref;
- return GIT_SUCCESS;
+ return git_reference_lookup_resolved(head_out, repo, GIT_HEAD_FILE, -1);
}
int git_repository_head_orphan(git_repository *repo)
{
- git_reference *ref;
+ git_reference *ref = NULL;
int error;
error = git_repository_head(&ref, repo);
+ git_reference_free(ref);
- if (error == GIT_SUCCESS)
- git_reference_free(ref);
+ if (error == GIT_ENOTFOUND)
+ return 1;
+
+ if (error < 0)
+ return -1;
- return error == GIT_ENOTFOUND ? 1 : error;
+ return 0;
}
int git_repository_is_empty(git_repository *repo)
@@ -867,9 +865,8 @@ int git_repository_is_empty(git_repository *repo)
git_reference *head = NULL, *branch = NULL;
int error;
- error = git_reference_lookup(&head, repo, "HEAD");
- if (error < GIT_SUCCESS)
- return git__throw(error, "Corrupted repository. HEAD does not exist");
+ if (git_reference_lookup(&head, repo, "HEAD") < 0)
+ return -1;
if (git_reference_type(head) != GIT_REF_SYMBOLIC) {
git_reference_free(head);
@@ -886,7 +883,13 @@ int git_repository_is_empty(git_repository *repo)
git_reference_free(head);
git_reference_free(branch);
- return error == GIT_ENOTFOUND ? 1 : error;
+ if (error == GIT_ENOTFOUND)
+ return 1;
+
+ if (error < 0)
+ return -1;
+
+ return 0;
}
const char *git_repository_path(git_repository *repo)
@@ -907,16 +910,18 @@ const char *git_repository_workdir(git_repository *repo)
int git_repository_set_workdir(git_repository *repo, const char *workdir)
{
+ git_buf path = GIT_BUF_INIT;
+
assert(repo && workdir);
- free(repo->workdir);
+ if (git_path_prettify_dir(&path, workdir, NULL) < 0)
+ return -1;
- repo->workdir = git__strdup(workdir);
- if (repo->workdir == NULL)
- return GIT_ENOMEM;
+ git__free(repo->workdir);
+ repo->workdir = git_buf_detach(&path);
repo->is_bare = 0;
- return GIT_SUCCESS;
+ return 0;
}
int git_repository_is_bare(git_repository *repo)
@@ -924,3 +929,23 @@ int git_repository_is_bare(git_repository *repo)
assert(repo);
return repo->is_bare;
}
+
+int git_repository_head_tree(git_tree **tree, git_repository *repo)
+{
+ git_oid head_oid;
+ git_object *obj = NULL;
+
+ if (git_reference_name_to_oid(&head_oid, repo, GIT_HEAD_FILE) < 0) {
+ /* cannot resolve HEAD - probably brand new repo */
+ giterr_clear();
+ *tree = NULL;
+ return 0;
+ }
+
+ if (git_object_lookup(&obj, repo, &head_oid, GIT_OBJ_ANY) < 0 ||
+ git_object__resolve_to_type(&obj, GIT_OBJ_TREE) < 0)
+ return -1;
+
+ *tree = (git_tree *)obj;
+ return 0;
+}
diff --git a/src/repository.h b/src/repository.h
index 5274fc1d0..91c69a655 100644
--- a/src/repository.h
+++ b/src/repository.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -13,19 +13,62 @@
#include "git2/repository.h"
#include "git2/object.h"
-#include "hashtable.h"
#include "index.h"
#include "cache.h"
#include "refs.h"
#include "buffer.h"
#include "odb.h"
#include "attr.h"
+#include "strmap.h"
#define DOT_GIT ".git"
#define GIT_DIR DOT_GIT "/"
#define GIT_DIR_MODE 0755
#define GIT_BARE_DIR_MODE 0777
+/** Cvar cache identifiers */
+typedef enum {
+ GIT_CVAR_AUTO_CRLF = 0, /* core.autocrlf */
+ GIT_CVAR_EOL, /* core.eol */
+ GIT_CVAR_CACHE_MAX
+} git_cvar_cached;
+
+/**
+ * CVAR value enumerations
+ *
+ * These are the values that are actually stored in the cvar cache, instead
+ * of their string equivalents. These values are internal and symbolic;
+ * make sure that none of them is set to `-1`, since that is the unique
+ * identifier for "not cached"
+ */
+typedef enum {
+ /* The value hasn't been loaded from the cache yet */
+ GIT_CVAR_NOT_CACHED = -1,
+
+ /* core.safecrlf: false, 'fail', 'warn' */
+ GIT_SAFE_CRLF_FALSE = 0,
+ GIT_SAFE_CRLF_FAIL = 1,
+ GIT_SAFE_CRLF_WARN = 2,
+
+ /* core.autocrlf: false, true, 'input; */
+ GIT_AUTO_CRLF_FALSE = 0,
+ GIT_AUTO_CRLF_TRUE = 1,
+ GIT_AUTO_CRLF_INPUT = 2,
+ GIT_AUTO_CRLF_DEFAULT = GIT_AUTO_CRLF_FALSE,
+
+ /* core.eol: unset, 'crlf', 'lf', 'native' */
+ GIT_EOL_UNSET = 0,
+ GIT_EOL_CRLF = 1,
+ GIT_EOL_LF = 2,
+#ifdef GIT_WIN32
+ GIT_EOL_NATIVE = GIT_EOL_CRLF,
+#else
+ GIT_EOL_NATIVE = GIT_EOL_LF,
+#endif
+ GIT_EOL_DEFAULT = GIT_EOL_NATIVE
+} git_cvar_value;
+
+/** Base git object for inheritance */
struct git_object {
git_cached_obj cached;
git_repository *repo;
@@ -40,23 +83,56 @@ struct git_repository {
git_cache objects;
git_refcache references;
git_attr_cache attrcache;
+ git_strmap *submodules;
char *path_repository;
char *workdir;
unsigned is_bare:1;
unsigned int lru_counter;
+
+ git_cvar_value cvar_cache[GIT_CVAR_CACHE_MAX];
};
/* fully free the object; internal method, do not
* export */
void git_object__free(void *object);
+int git_object__resolve_to_type(git_object **obj, git_otype type);
+
int git_oid__parse(git_oid *oid, const char **buffer_out, const char *buffer_end, const char *header);
void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid);
+GIT_INLINE(git_attr_cache *) git_repository_attr_cache(git_repository *repo)
+{
+ return &repo->attrcache;
+}
+
+int git_repository_head_tree(git_tree **tree, git_repository *repo);
+
+/*
+ * Weak pointers to repository internals.
+ *
+ * The returned pointers do not need to be freed. Do not keep
+ * permanent references to these (i.e. between API calls), since they may
+ * become invalidated if the user replaces a repository internal.
+ */
int git_repository_config__weakptr(git_config **out, git_repository *repo);
int git_repository_odb__weakptr(git_odb **out, git_repository *repo);
int git_repository_index__weakptr(git_index **out, git_repository *repo);
+/*
+ * CVAR cache
+ *
+ * Efficient access to the most used config variables of a repository.
+ * The cache is cleared everytime the config backend is replaced.
+ */
+int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar);
+void git_repository__cvar_cache_clear(git_repository *repo);
+
+/*
+ * Submodule cache
+ */
+extern void git_submodule_config_free(git_repository *repo);
+
#endif
diff --git a/src/revwalk.c b/src/revwalk.c
index d632a19b8..e64d93f20 100644
--- a/src/revwalk.c
+++ b/src/revwalk.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -8,10 +8,21 @@
#include "common.h"
#include "commit.h"
#include "odb.h"
-#include "hashtable.h"
#include "pqueue.h"
+#include "pool.h"
+#include "oidmap.h"
#include "git2/revwalk.h"
+#include "git2/merge.h"
+
+#include <regex.h>
+
+GIT__USE_OIDMAP;
+
+#define PARENT1 (1 << 0)
+#define PARENT2 (1 << 1)
+#define RESULT (1 << 2)
+#define STALE (1 << 3)
typedef struct commit_object {
git_oid oid;
@@ -19,7 +30,8 @@ typedef struct commit_object {
unsigned int seen:1,
uninteresting:1,
topo_delay:1,
- parsed:1;
+ parsed:1,
+ flags : 4;
unsigned short in_degree;
unsigned short out_degree;
@@ -36,7 +48,8 @@ struct git_revwalk {
git_repository *repo;
git_odb *odb;
- git_hashtable *commits;
+ git_oidmap *commits;
+ git_pool commit_pool;
commit_list *iterator_topo;
commit_list *iterator_rand;
@@ -46,22 +59,47 @@ struct git_revwalk {
int (*get_next)(commit_object **, git_revwalk *);
int (*enqueue)(git_revwalk *, commit_object *);
- git_vector memory_alloc;
- size_t chunk_size;
-
unsigned walking:1;
unsigned int sorting;
+
+ /* merge base calculation */
+ commit_object *one;
+ git_vector twos;
};
+static int commit_time_cmp(void *a, void *b)
+{
+ commit_object *commit_a = (commit_object *)a;
+ commit_object *commit_b = (commit_object *)b;
+
+ return (commit_a->time < commit_b->time);
+}
+
static commit_list *commit_list_insert(commit_object *item, commit_list **list_p)
{
commit_list *new_list = git__malloc(sizeof(commit_list));
- new_list->item = item;
- new_list->next = *list_p;
+ if (new_list != NULL) {
+ new_list->item = item;
+ new_list->next = *list_p;
+ }
*list_p = new_list;
return new_list;
}
+static commit_list *commit_list_insert_by_date(commit_object *item, commit_list **list_p)
+{
+ commit_list **pp = list_p;
+ commit_list *p;
+
+ while ((p = *pp) != NULL) {
+ if (commit_time_cmp(p->item, item) < 0)
+ break;
+
+ pp = &p->next;
+ }
+
+ return commit_list_insert(item, pp);
+}
static void commit_list_free(commit_list **list_p)
{
commit_list *list = *list_p;
@@ -87,68 +125,36 @@ static commit_object *commit_list_pop(commit_list **stack)
return item;
}
-static int commit_time_cmp(void *a, void *b)
-{
- commit_object *commit_a = (commit_object *)a;
- commit_object *commit_b = (commit_object *)b;
-
- return (commit_a->time < commit_b->time);
-}
-
-static uint32_t object_table_hash(const void *key, int hash_id)
-{
- uint32_t r;
- const git_oid *id = key;
-
- memcpy(&r, id->id + (hash_id * sizeof(uint32_t)), sizeof(r));
- return r;
-}
-
-#define COMMITS_PER_CHUNK 128
-#define CHUNK_STEP 64
-#define PARENTS_PER_COMMIT ((CHUNK_STEP - sizeof(commit_object)) / sizeof(commit_object *))
-
-static int alloc_chunk(git_revwalk *walk)
-{
- void *chunk;
-
- chunk = git__calloc(COMMITS_PER_CHUNK, CHUNK_STEP);
- if (chunk == NULL)
- return GIT_ENOMEM;
-
- walk->chunk_size = 0;
- return git_vector_insert(&walk->memory_alloc, chunk);
-}
+#define PARENTS_PER_COMMIT 2
+#define COMMIT_ALLOC \
+ (sizeof(commit_object) + PARENTS_PER_COMMIT * sizeof(commit_object *))
static commit_object *alloc_commit(git_revwalk *walk)
{
- unsigned char *chunk;
-
- if (walk->chunk_size == COMMITS_PER_CHUNK)
- alloc_chunk(walk);
-
- chunk = git_vector_get(&walk->memory_alloc, walk->memory_alloc.length - 1);
- chunk += (walk->chunk_size * CHUNK_STEP);
- walk->chunk_size++;
-
- return (commit_object *)chunk;
+ return (commit_object *)git_pool_malloc(&walk->commit_pool, COMMIT_ALLOC);
}
-static commit_object **alloc_parents(commit_object *commit, size_t n_parents)
+static commit_object **alloc_parents(
+ git_revwalk *walk, commit_object *commit, size_t n_parents)
{
if (n_parents <= PARENTS_PER_COMMIT)
- return (commit_object **)((unsigned char *)commit + sizeof(commit_object));
+ return (commit_object **)((char *)commit + sizeof(commit_object));
- return git__malloc(n_parents * sizeof(commit_object *));
+ return (commit_object **)git_pool_malloc(
+ &walk->commit_pool, (uint32_t)(n_parents * sizeof(commit_object *)));
}
static commit_object *commit_lookup(git_revwalk *walk, const git_oid *oid)
{
commit_object *commit;
+ khiter_t pos;
+ int ret;
- if ((commit = git_hashtable_lookup(walk->commits, oid)) != NULL)
- return commit;
+ /* lookup and reserve space if not already present */
+ pos = kh_get(oid, walk->commits, oid);
+ if (pos != kh_end(walk->commits))
+ return kh_value(walk->commits, pos);
commit = alloc_commit(walk);
if (commit == NULL)
@@ -156,17 +162,16 @@ static commit_object *commit_lookup(git_revwalk *walk, const git_oid *oid)
git_oid_cpy(&commit->oid, oid);
- if (git_hashtable_insert(walk->commits, &commit->oid, commit) < GIT_SUCCESS) {
- git__free(commit);
- return NULL;
- }
+ pos = kh_put(oid, walk->commits, &commit->oid, &ret);
+ assert(ret != 0);
+ kh_value(walk->commits, pos) = commit;
return commit;
}
static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawobj *raw)
{
- const int parent_len = strlen("parent ") + GIT_OID_HEXSZ + 1;
+ const size_t parent_len = strlen("parent ") + GIT_OID_HEXSZ + 1;
unsigned char *buffer = raw->data;
unsigned char *buffer_end = buffer + raw->len;
@@ -183,39 +188,44 @@ static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawo
buffer += parent_len;
}
- commit->parents = alloc_parents(commit, parents);
- if (commit->parents == NULL)
- return GIT_ENOMEM;
+ commit->parents = alloc_parents(walk, commit, parents);
+ GITERR_CHECK_ALLOC(commit->parents);
buffer = parents_start;
for (i = 0; i < parents; ++i) {
git_oid oid;
- if (git_oid_fromstr(&oid, (char *)buffer + strlen("parent ")) < GIT_SUCCESS)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse commit. Parent object is corrupted");
+ if (git_oid_fromstr(&oid, (char *)buffer + strlen("parent ")) < 0)
+ return -1;
commit->parents[i] = commit_lookup(walk, &oid);
if (commit->parents[i] == NULL)
- return GIT_ENOMEM;
+ return -1;
buffer += parent_len;
}
commit->out_degree = (unsigned short)parents;
- if ((buffer = memchr(buffer, '\n', buffer_end - buffer)) == NULL)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse commit. Object is corrupted");
+ if ((buffer = memchr(buffer, '\n', buffer_end - buffer)) == NULL) {
+ giterr_set(GITERR_ODB, "Failed to parse commit. Object is corrupted");
+ return -1;
+ }
buffer = memchr(buffer, '>', buffer_end - buffer);
- if (buffer == NULL)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse commit. Can't find author");
+ if (buffer == NULL) {
+ giterr_set(GITERR_ODB, "Failed to parse commit. Can't find author");
+ return -1;
+ }
- if (git__strtol32(&commit_time, (char *)buffer + 2, NULL, 10) < GIT_SUCCESS)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse commit. Can't parse commit time");
+ if (git__strtol32(&commit_time, (char *)buffer + 2, NULL, 10) < 0) {
+ giterr_set(GITERR_ODB, "Failed to parse commit. Can't parse commit time");
+ return -1;
+ }
commit->time = (time_t)commit_time;
commit->parsed = 1;
- return GIT_SUCCESS;
+ return 0;
}
static int commit_parse(git_revwalk *walk, commit_object *commit)
@@ -224,19 +234,159 @@ static int commit_parse(git_revwalk *walk, commit_object *commit)
int error;
if (commit->parsed)
- return GIT_SUCCESS;
+ return 0;
- if ((error = git_odb_read(&obj, walk->odb, &commit->oid)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to parse commit. Can't read object");
+ if ((error = git_odb_read(&obj, walk->odb, &commit->oid)) < 0)
+ return error;
if (obj->raw.type != GIT_OBJ_COMMIT) {
git_odb_object_free(obj);
- return git__throw(GIT_EOBJTYPE, "Failed to parse commit. Object is no commit object");
+ giterr_set(GITERR_INVALID, "Failed to parse commit. Object is no commit object");
+ return -1;
}
error = commit_quick_parse(walk, commit, &obj->raw);
git_odb_object_free(obj);
- return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse commit");
+ return error;
+}
+
+static int interesting(git_pqueue *list)
+{
+ unsigned int i;
+ for (i = 1; i < git_pqueue_size(list); i++) {
+ commit_object *commit = list->d[i];
+ if ((commit->flags & STALE) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+static int merge_bases_many(commit_list **out, git_revwalk *walk, commit_object *one, git_vector *twos)
+{
+ int error;
+ unsigned int i;
+ commit_object *two;
+ commit_list *result = NULL, *tmp = NULL;
+ git_pqueue list;
+
+ /* if the commit is repeated, we have a our merge base already */
+ git_vector_foreach(twos, i, two) {
+ if (one == two)
+ return commit_list_insert(one, out) ? 0 : -1;
+ }
+
+ if (git_pqueue_init(&list, twos->length * 2, commit_time_cmp) < 0)
+ return -1;
+
+ if (commit_parse(walk, one) < 0)
+ return -1;
+
+ one->flags |= PARENT1;
+ if (git_pqueue_insert(&list, one) < 0)
+ return -1;
+
+ git_vector_foreach(twos, i, two) {
+ commit_parse(walk, two);
+ two->flags |= PARENT2;
+ if (git_pqueue_insert(&list, two) < 0)
+ return -1;
+ }
+
+ /* as long as there are non-STALE commits */
+ while (interesting(&list)) {
+ commit_object *commit;
+ int flags;
+
+ commit = git_pqueue_pop(&list);
+
+ flags = commit->flags & (PARENT1 | PARENT2 | STALE);
+ if (flags == (PARENT1 | PARENT2)) {
+ if (!(commit->flags & RESULT)) {
+ commit->flags |= RESULT;
+ if (commit_list_insert(commit, &result) == NULL)
+ return -1;
+ }
+ /* we mark the parents of a merge stale */
+ flags |= STALE;
+ }
+
+ for (i = 0; i < commit->out_degree; i++) {
+ commit_object *p = commit->parents[i];
+ if ((p->flags & flags) == flags)
+ continue;
+
+ if ((error = commit_parse(walk, p)) < 0)
+ return error;
+
+ p->flags |= flags;
+ if (git_pqueue_insert(&list, p) < 0)
+ return -1;
+ }
+ }
+
+ git_pqueue_free(&list);
+
+ /* filter out any stale commits in the results */
+ tmp = result;
+ result = NULL;
+
+ while (tmp) {
+ struct commit_list *next = tmp->next;
+ if (!(tmp->item->flags & STALE))
+ if (commit_list_insert_by_date(tmp->item, &result) == NULL)
+ return -1;
+
+ git__free(tmp);
+ tmp = next;
+ }
+
+ *out = result;
+ return 0;
+}
+
+int git_merge_base(git_oid *out, git_repository *repo, git_oid *one, git_oid *two)
+{
+ git_revwalk *walk;
+ git_vector list;
+ commit_list *result = NULL;
+ commit_object *commit;
+ void *contents[1];
+
+ if (git_revwalk_new(&walk, repo) < 0)
+ return -1;
+
+ commit = commit_lookup(walk, two);
+ if (commit == NULL)
+ goto on_error;
+
+ /* This is just one value, so we can do it on the stack */
+ memset(&list, 0x0, sizeof(git_vector));
+ contents[0] = commit;
+ list.length = 1;
+ list.contents = contents;
+
+ commit = commit_lookup(walk, one);
+ if (commit == NULL)
+ goto on_error;
+
+ if (merge_bases_many(&result, walk, commit, &list) < 0)
+ goto on_error;
+
+ if (!result) {
+ git_revwalk_free(walk);
+ return GIT_ENOTFOUND;
+ }
+
+ git_oid_cpy(out, &result->item->oid);
+ commit_list_free(&result);
+ git_revwalk_free(walk);
+
+ return 0;
+
+on_error:
+ git_revwalk_free(walk);
+ return -1;
}
static void mark_uninteresting(commit_object *commit)
@@ -246,6 +396,10 @@ static void mark_uninteresting(commit_object *commit)
commit->uninteresting = 1;
+ /* This means we've reached a merge base, so there's no need to walk any more */
+ if ((commit->flags & (RESULT | STALE)) == RESULT)
+ return;
+
for (i = 0; i < commit->out_degree; ++i)
if (!commit->parents[i]->uninteresting)
mark_uninteresting(commit->parents[i]);
@@ -259,12 +413,12 @@ static int process_commit(git_revwalk *walk, commit_object *commit, int hide)
mark_uninteresting(commit);
if (commit->seen)
- return GIT_SUCCESS;
+ return 0;
commit->seen = 1;
- if ((error = commit_parse(walk, commit)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to process commit");
+ if ((error = commit_parse(walk, commit)) < 0)
+ return error;
return walk->enqueue(walk, commit);
}
@@ -272,13 +426,12 @@ static int process_commit(git_revwalk *walk, commit_object *commit, int hide)
static int process_commit_parents(git_revwalk *walk, commit_object *commit)
{
unsigned short i;
- int error = GIT_SUCCESS;
+ int error = 0;
- for (i = 0; i < commit->out_degree && error == GIT_SUCCESS; ++i) {
+ for (i = 0; i < commit->out_degree && !error; ++i)
error = process_commit(walk, commit->parents[i], commit->uninteresting);
- }
- return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to process commit parents");
+ return error;
}
static int push_commit(git_revwalk *walk, const git_oid *oid, int uninteresting)
@@ -287,9 +440,17 @@ static int push_commit(git_revwalk *walk, const git_oid *oid, int uninteresting)
commit = commit_lookup(walk, oid);
if (commit == NULL)
- return git__throw(GIT_ENOTFOUND, "Failed to push commit. Object not found");
+ return -1; /* error already reported by failed lookup */
- return process_commit(walk, commit, uninteresting);
+ commit->uninteresting = uninteresting;
+ if (walk->one == NULL && !uninteresting) {
+ walk->one = commit;
+ } else {
+ if (git_vector_insert(&walk->twos, commit) < 0)
+ return -1;
+ }
+
+ return 0;
}
int git_revwalk_push(git_revwalk *walk, const git_oid *oid)
@@ -298,12 +459,122 @@ int git_revwalk_push(git_revwalk *walk, const git_oid *oid)
return push_commit(walk, oid, 0);
}
+
int git_revwalk_hide(git_revwalk *walk, const git_oid *oid)
{
assert(walk && oid);
return push_commit(walk, oid, 1);
}
+static int push_ref(git_revwalk *walk, const char *refname, int hide)
+{
+ git_oid oid;
+
+ if (git_reference_name_to_oid(&oid, walk->repo, refname) < 0)
+ return -1;
+
+ return push_commit(walk, &oid, hide);
+}
+
+struct push_cb_data {
+ git_revwalk *walk;
+ const char *glob;
+ int hide;
+};
+
+static int push_glob_cb(const char *refname, void *data_)
+{
+ struct push_cb_data *data = (struct push_cb_data *)data_;
+
+ if (!p_fnmatch(data->glob, refname, 0))
+ return push_ref(data->walk, refname, data->hide);
+
+ return 0;
+}
+
+static int push_glob(git_revwalk *walk, const char *glob, int hide)
+{
+ git_buf buf = GIT_BUF_INIT;
+ struct push_cb_data data;
+ regex_t preg;
+
+ assert(walk && glob);
+
+ /* refs/ is implied if not given in the glob */
+ if (strncmp(glob, GIT_REFS_DIR, strlen(GIT_REFS_DIR))) {
+ git_buf_printf(&buf, GIT_REFS_DIR "%s", glob);
+ } else {
+ git_buf_puts(&buf, glob);
+ }
+
+ /* If no '?', '*' or '[' exist, we append '/ *' to the glob */
+ memset(&preg, 0x0, sizeof(regex_t));
+ if (regcomp(&preg, "[?*[]", REG_EXTENDED)) {
+ giterr_set(GITERR_OS, "Regex failed to compile");
+ git_buf_free(&buf);
+ return -1;
+ }
+
+ if (regexec(&preg, glob, 0, NULL, 0))
+ git_buf_puts(&buf, "/*");
+
+ if (git_buf_oom(&buf))
+ goto on_error;
+
+ data.walk = walk;
+ data.glob = git_buf_cstr(&buf);
+ data.hide = hide;
+
+ if (git_reference_foreach(
+ walk->repo, GIT_REF_LISTALL, push_glob_cb, &data) < 0)
+ goto on_error;
+
+ regfree(&preg);
+ git_buf_free(&buf);
+ return 0;
+
+on_error:
+ regfree(&preg);
+ git_buf_free(&buf);
+ return -1;
+}
+
+int git_revwalk_push_glob(git_revwalk *walk, const char *glob)
+{
+ assert(walk && glob);
+ return push_glob(walk, glob, 0);
+}
+
+int git_revwalk_hide_glob(git_revwalk *walk, const char *glob)
+{
+ assert(walk && glob);
+ return push_glob(walk, glob, 1);
+}
+
+int git_revwalk_push_head(git_revwalk *walk)
+{
+ assert(walk);
+ return push_ref(walk, GIT_HEAD_FILE, 0);
+}
+
+int git_revwalk_hide_head(git_revwalk *walk)
+{
+ assert(walk);
+ return push_ref(walk, GIT_HEAD_FILE, 1);
+}
+
+int git_revwalk_push_ref(git_revwalk *walk, const char *refname)
+{
+ assert(walk && refname);
+ return push_ref(walk, refname, 0);
+}
+
+int git_revwalk_hide_ref(git_revwalk *walk, const char *refname)
+{
+ assert(walk && refname);
+ return push_ref(walk, refname, 1);
+}
+
static int revwalk_enqueue_timesort(git_revwalk *walk, commit_object *commit)
{
return git_pqueue_insert(&walk->iterator_time, commit);
@@ -311,7 +582,7 @@ static int revwalk_enqueue_timesort(git_revwalk *walk, commit_object *commit)
static int revwalk_enqueue_unsorted(git_revwalk *walk, commit_object *commit)
{
- return commit_list_insert(commit, &walk->iterator_rand) ? GIT_SUCCESS : GIT_ENOMEM;
+ return commit_list_insert(commit, &walk->iterator_rand) ? 0 : -1;
}
static int revwalk_next_timesort(commit_object **object_out, git_revwalk *walk)
@@ -320,16 +591,16 @@ static int revwalk_next_timesort(commit_object **object_out, git_revwalk *walk)
commit_object *next;
while ((next = git_pqueue_pop(&walk->iterator_time)) != NULL) {
- if ((error = process_commit_parents(walk, next)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to load next revision");
+ if ((error = process_commit_parents(walk, next)) < 0)
+ return error;
if (!next->uninteresting) {
*object_out = next;
- return GIT_SUCCESS;
+ return 0;
}
}
- return git__throw(GIT_EREVWALKOVER, "Failed to load next revision");
+ return GIT_REVWALKOVER;
}
static int revwalk_next_unsorted(commit_object **object_out, git_revwalk *walk)
@@ -338,16 +609,16 @@ static int revwalk_next_unsorted(commit_object **object_out, git_revwalk *walk)
commit_object *next;
while ((next = commit_list_pop(&walk->iterator_rand)) != NULL) {
- if ((error = process_commit_parents(walk, next)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to load next revision");
+ if ((error = process_commit_parents(walk, next)) < 0)
+ return error;
if (!next->uninteresting) {
*object_out = next;
- return GIT_SUCCESS;
+ return 0;
}
}
- return git__throw(GIT_EREVWALKOVER, "Failed to load next revision");
+ return GIT_REVWALKOVER;
}
static int revwalk_next_toposort(commit_object **object_out, git_revwalk *walk)
@@ -358,7 +629,7 @@ static int revwalk_next_toposort(commit_object **object_out, git_revwalk *walk)
for (;;) {
next = commit_list_pop(&walk->iterator_topo);
if (next == NULL)
- return git__throw(GIT_EREVWALKOVER, "Failed to load next revision");
+ return GIT_REVWALKOVER;
if (next->in_degree > 0) {
next->topo_delay = 1;
@@ -370,58 +641,83 @@ static int revwalk_next_toposort(commit_object **object_out, git_revwalk *walk)
if (--parent->in_degree == 0 && parent->topo_delay) {
parent->topo_delay = 0;
- commit_list_insert(parent, &walk->iterator_topo);
+ if (commit_list_insert(parent, &walk->iterator_topo) == NULL)
+ return -1;
}
}
*object_out = next;
- return GIT_SUCCESS;
+ return 0;
}
}
static int revwalk_next_reverse(commit_object **object_out, git_revwalk *walk)
{
*object_out = commit_list_pop(&walk->iterator_reverse);
- return *object_out ? GIT_SUCCESS : GIT_EREVWALKOVER;
+ return *object_out ? 0 : GIT_REVWALKOVER;
}
static int prepare_walk(git_revwalk *walk)
{
int error;
- commit_object *next;
+ unsigned int i;
+ commit_object *next, *two;
+ commit_list *bases = NULL;
+
+ /*
+ * If walk->one is NULL, there were no positive references,
+ * so we know that the walk is already over.
+ */
+ if (walk->one == NULL)
+ return GIT_REVWALKOVER;
+
+ /* first figure out what the merge bases are */
+ if (merge_bases_many(&bases, walk, walk->one, &walk->twos) < 0)
+ return -1;
+
+ commit_list_free(&bases);
+ if (process_commit(walk, walk->one, walk->one->uninteresting) < 0)
+ return -1;
+
+ git_vector_foreach(&walk->twos, i, two) {
+ if (process_commit(walk, two, two->uninteresting) < 0)
+ return -1;
+ }
if (walk->sorting & GIT_SORT_TOPOLOGICAL) {
unsigned short i;
- while ((error = walk->get_next(&next, walk)) == GIT_SUCCESS) {
+ while ((error = walk->get_next(&next, walk)) == 0) {
for (i = 0; i < next->out_degree; ++i) {
commit_object *parent = next->parents[i];
parent->in_degree++;
}
- commit_list_insert(next, &walk->iterator_topo);
+ if (commit_list_insert(next, &walk->iterator_topo) == NULL)
+ return -1;
}
- if (error != GIT_EREVWALKOVER)
- return git__rethrow(error, "Failed to prepare revision walk");
+ if (error != GIT_REVWALKOVER)
+ return error;
walk->get_next = &revwalk_next_toposort;
}
if (walk->sorting & GIT_SORT_REVERSE) {
- while ((error = walk->get_next(&next, walk)) == GIT_SUCCESS)
- commit_list_insert(next, &walk->iterator_reverse);
+ while ((error = walk->get_next(&next, walk)) == 0)
+ if (commit_list_insert(next, &walk->iterator_reverse) == NULL)
+ return -1;
- if (error != GIT_EREVWALKOVER)
- return git__rethrow(error, "Failed to prepare revision walk");
+ if (error != GIT_REVWALKOVER)
+ return error;
walk->get_next = &revwalk_next_reverse;
}
walk->walking = 1;
- return GIT_SUCCESS;
+ return 0;
}
@@ -430,70 +726,48 @@ static int prepare_walk(git_revwalk *walk)
int git_revwalk_new(git_revwalk **revwalk_out, git_repository *repo)
{
- int error;
git_revwalk *walk;
walk = git__malloc(sizeof(git_revwalk));
- if (walk == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(walk);
memset(walk, 0x0, sizeof(git_revwalk));
- walk->commits = git_hashtable_alloc(64,
- object_table_hash,
- (git_hash_keyeq_ptr)git_oid_cmp);
+ walk->commits = git_oidmap_alloc();
+ GITERR_CHECK_ALLOC(walk->commits);
- if (walk->commits == NULL) {
- git__free(walk);
- return GIT_ENOMEM;
- }
-
- git_pqueue_init(&walk->iterator_time, 8, commit_time_cmp);
- git_vector_init(&walk->memory_alloc, 8, NULL);
- alloc_chunk(walk);
+ if (git_pqueue_init(&walk->iterator_time, 8, commit_time_cmp) < 0 ||
+ git_vector_init(&walk->twos, 4, NULL) < 0 ||
+ git_pool_init(&walk->commit_pool, 1,
+ git_pool__suggest_items_per_page(COMMIT_ALLOC) * COMMIT_ALLOC) < 0)
+ return -1;
walk->get_next = &revwalk_next_unsorted;
walk->enqueue = &revwalk_enqueue_unsorted;
walk->repo = repo;
- error = git_repository_odb(&walk->odb, repo);
- if (error < GIT_SUCCESS) {
+ if (git_repository_odb(&walk->odb, repo) < 0) {
git_revwalk_free(walk);
- return error;
+ return -1;
}
*revwalk_out = walk;
- return GIT_SUCCESS;
+ return 0;
}
void git_revwalk_free(git_revwalk *walk)
{
- unsigned int i;
- const void *GIT_UNUSED(_unused);
- commit_object *commit;
-
if (walk == NULL)
return;
git_revwalk_reset(walk);
git_odb_free(walk->odb);
- /* if the parent has more than PARENTS_PER_COMMIT parents,
- * we had to allocate a separate array for those parents.
- * make sure it's being free'd */
- GIT_HASHTABLE_FOREACH(walk->commits, _unused, commit, {
- if (commit->out_degree > PARENTS_PER_COMMIT)
- git__free(commit->parents);
- });
-
- git_hashtable_free(walk->commits);
+ git_oidmap_free(walk->commits);
+ git_pool_clear(&walk->commit_pool);
git_pqueue_free(&walk->iterator_time);
-
- for (i = 0; i < walk->memory_alloc.length; ++i)
- git__free(git_vector_get(&walk->memory_alloc, i));
-
- git_vector_free(&walk->memory_alloc);
+ git_vector_free(&walk->twos);
git__free(walk);
}
@@ -529,41 +803,43 @@ int git_revwalk_next(git_oid *oid, git_revwalk *walk)
assert(walk && oid);
if (!walk->walking) {
- if ((error = prepare_walk(walk)) < GIT_SUCCESS)
- return git__rethrow(error, "Failed to load next revision");
+ if ((error = prepare_walk(walk)) < 0)
+ return error;
}
error = walk->get_next(&next, walk);
- if (error == GIT_EREVWALKOVER) {
+ if (error == GIT_REVWALKOVER) {
git_revwalk_reset(walk);
- return GIT_EREVWALKOVER;
+ return GIT_REVWALKOVER;
}
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to load next revision");
+ if (!error)
+ git_oid_cpy(oid, &next->oid);
- git_oid_cpy(oid, &next->oid);
- return GIT_SUCCESS;
+ return error;
}
void git_revwalk_reset(git_revwalk *walk)
{
- const void *GIT_UNUSED(_unused);
commit_object *commit;
assert(walk);
- GIT_HASHTABLE_FOREACH(walk->commits, _unused, commit,
+ kh_foreach_value(walk->commits, commit, {
commit->seen = 0;
commit->in_degree = 0;
commit->topo_delay = 0;
- );
+ commit->uninteresting = 0;
+ });
git_pqueue_clear(&walk->iterator_time);
commit_list_free(&walk->iterator_topo);
commit_list_free(&walk->iterator_rand);
commit_list_free(&walk->iterator_reverse);
walk->walking = 0;
+
+ walk->one = NULL;
+ git_vector_clear(&walk->twos);
}
diff --git a/src/sha1.c b/src/sha1.c
index 4043fb168..8aaedeb8f 100644
--- a/src/sha1.c
+++ b/src/sha1.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -232,7 +232,7 @@ void git__blk_SHA1_Init(blk_SHA_CTX *ctx)
ctx->H[4] = 0xc3d2e1f0;
}
-void git__blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, unsigned long len)
+void git__blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, size_t len)
{
unsigned int lenW = ctx->size & 63;
@@ -242,7 +242,7 @@ void git__blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, unsigned long len)
if (lenW) {
unsigned int left = 64 - lenW;
if (len < left)
- left = len;
+ left = (unsigned int)len;
memcpy(lenW + (char *)ctx->W, data, left);
lenW = (lenW + left) & 63;
len -= left;
diff --git a/src/sha1.h b/src/sha1.h
index f1e905e76..93a244d76 100644
--- a/src/sha1.h
+++ b/src/sha1.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -12,7 +12,7 @@ typedef struct {
} blk_SHA_CTX;
void git__blk_SHA1_Init(blk_SHA_CTX *ctx);
-void git__blk_SHA1_Update(blk_SHA_CTX *ctx, const void *dataIn, unsigned long len);
+void git__blk_SHA1_Update(blk_SHA_CTX *ctx, const void *dataIn, size_t len);
void git__blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx);
#define SHA_CTX blk_SHA_CTX
diff --git a/src/sha1_lookup.c b/src/sha1_lookup.c
index 3051568a3..096da1739 100644
--- a/src/sha1_lookup.c
+++ b/src/sha1_lookup.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -158,7 +158,8 @@ int sha1_entry_pos(const void *table,
#endif
if (!(lo <= mi && mi < hi)) {
- return git__throw(GIT_ERROR, "Assertion failure. Binary search invariant is false");
+ giterr_set(GITERR_INVALID, "Assertion failure. Binary search invariant is false");
+ return -1;
}
mi_key = base + elem_size * mi + key_offset;
diff --git a/src/sha1_lookup.h b/src/sha1_lookup.h
index 52282e672..cd40a9d57 100644
--- a/src/sha1_lookup.h
+++ b/src/sha1_lookup.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
diff --git a/src/signature.c b/src/signature.c
index fb2bb3cce..7d329c4c9 100644
--- a/src/signature.c
+++ b/src/signature.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -38,31 +38,38 @@ static const char *skip_trailing_spaces(const char *buffer_start, const char *bu
return buffer_end;
}
+static int signature_error(const char *msg)
+{
+ giterr_set(GITERR_INVALID, "Failed to parse signature - %s", msg);
+ return -1;
+}
+
static int process_trimming(const char *input, char **storage, const char *input_end, int fail_when_empty)
{
const char *left, *right;
- int trimmed_input_length;
+ size_t trimmed_input_length;
+
+ assert(storage);
left = skip_leading_spaces(input, input_end);
right = skip_trailing_spaces(input, input_end - 1);
if (right < left) {
if (fail_when_empty)
- return git__throw(GIT_EINVALIDARGS, "Failed to trim. Input is either empty or only contains spaces");
- else
- right = left - 1;
+ return signature_error("input is either empty of contains only spaces");
+
+ right = left - 1;
}
trimmed_input_length = right - left + 1;
*storage = git__malloc(trimmed_input_length + 1);
- if (*storage == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(*storage);
memcpy(*storage, left, trimmed_input_length);
(*storage)[trimmed_input_length] = 0;
- return GIT_SUCCESS;
+ return 0;
}
int git_signature_new(git_signature **sig_out, const char *name, const char *email, git_time_t time, int offset)
@@ -74,23 +81,14 @@ int git_signature_new(git_signature **sig_out, const char *name, const char *ema
*sig_out = NULL;
- if ((p = git__malloc(sizeof(git_signature))) == NULL) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
-
- memset(p, 0x0, sizeof(git_signature));
-
- error = process_trimming(name, &p->name, name + strlen(name), 1);
- if (error < GIT_SUCCESS) {
- git__rethrow(GIT_EINVALIDARGS, "Failed to create signature. 'name' argument is invalid");
- goto cleanup;
- }
+ p = git__calloc(1, sizeof(git_signature));
+ GITERR_CHECK_ALLOC(p);
- error = process_trimming(email, &p->email, email + strlen(email), 1);
- if (error < GIT_SUCCESS) {
- git__rethrow(GIT_EINVALIDARGS, "Failed to create signature. 'email' argument is invalid");
- goto cleanup;
+ if ((error = process_trimming(name, &p->name, name + strlen(name), 1)) < 0 ||
+ (error = process_trimming(email, &p->email, email + strlen(email), 1)) < 0)
+ {
+ git_signature_free(p);
+ return error;
}
p->when.time = time;
@@ -98,24 +96,19 @@ int git_signature_new(git_signature **sig_out, const char *name, const char *ema
*sig_out = p;
- return error;
-
-cleanup:
- git_signature_free(p);
- return error;
+ return 0;
}
git_signature *git_signature_dup(const git_signature *sig)
{
git_signature *new;
- if (git_signature_new(&new, sig->name, sig->email, sig->when.time, sig->when.offset) < GIT_SUCCESS)
+ if (git_signature_new(&new, sig->name, sig->email, sig->when.time, sig->when.offset) < 0)
return NULL;
return new;
}
int git_signature_now(git_signature **sig_out, const char *name, const char *email)
{
- int error;
time_t now;
time_t offset;
struct tm *utc_tm, *local_tm;
@@ -148,12 +141,18 @@ int git_signature_now(git_signature **sig_out, const char *name, const char *ema
if (local_tm->tm_isdst)
offset += 60;
- if ((error = git_signature_new(&sig, name, email, now, (int)offset)) < GIT_SUCCESS)
- return error;
+ if (git_signature_new(&sig, name, email, now, (int)offset) < 0)
+ return -1;
*sig_out = sig;
- return error;
+ return 0;
+}
+
+static int timezone_error(const char *msg)
+{
+ giterr_set(GITERR_INVALID, "Failed to parse TZ offset - %s", msg);
+ return -1;
}
static int parse_timezone_offset(const char *buffer, int *offset_out)
@@ -168,32 +167,32 @@ static int parse_timezone_offset(const char *buffer, int *offset_out)
if (*offset_start == '\n') {
*offset_out = 0;
- return GIT_SUCCESS;
+ return 0;
}
if (offset_start[0] != '-' && offset_start[0] != '+')
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. It doesn't start with '+' or '-'");
+ return timezone_error("does not start with '+' or '-'");
if (offset_start[1] < '0' || offset_start[1] > '9')
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset.");
+ return timezone_error("expected initial digit");
- if (git__strtol32(&dec_offset, offset_start + 1, &offset_end, 10) < GIT_SUCCESS)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. It isn't a number");
+ if (git__strtol32(&dec_offset, offset_start + 1, &offset_end, 10) < 0)
+ return timezone_error("not a valid number");
if (offset_end - offset_start != 5)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Invalid length");
+ return timezone_error("invalid length");
if (dec_offset > 1400)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Value too large");
+ return timezone_error("value too large");
hours = dec_offset / 100;
mins = dec_offset % 100;
if (hours > 14) // see http://www.worldtimezone.com/faq.html
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Hour value too large");
+ return timezone_error("hour value too large");
if (mins > 59)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse TZ offset. Minute value too large");
+ return timezone_error("minutes value too large");
offset = (hours * 60) + mins;
@@ -202,22 +201,22 @@ static int parse_timezone_offset(const char *buffer, int *offset_out)
*offset_out = offset;
- return GIT_SUCCESS;
+ return 0;
}
static int process_next_token(const char **buffer_out, char **storage,
const char *token_end, const char *right_boundary)
{
int error = process_trimming(*buffer_out, storage, token_end, 0);
- if (error < GIT_SUCCESS)
+ if (error < 0)
return error;
*buffer_out = token_end + 1;
if (*buffer_out > right_boundary)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Signature too short");
+ return signature_error("signature is too short");
- return GIT_SUCCESS;
+ return 0;
}
static const char *scan_for_previous_token(const char *buffer, const char *left_boundary)
@@ -241,17 +240,17 @@ static int parse_time(git_time_t *time_out, const char *buffer)
int time;
int error;
- if (*buffer == '+' || *buffer == '-')
- return git__throw(GIT_ERROR, "Failed while parsing time. '%s' rather look like a timezone offset.", buffer);
+ if (*buffer == '+' || *buffer == '-') {
+ giterr_set(GITERR_INVALID, "Failed while parsing time. '%s' actually looks like a timezone offset.", buffer);
+ return -1;
+ }
error = git__strtol32(&time, buffer, &buffer, 10);
- if (error < GIT_SUCCESS)
- return error;
+ if (!error)
+ *time_out = (git_time_t)time;
- *time_out = (git_time_t)time;
-
- return GIT_SUCCESS;
+ return error;
}
int git_signature__parse(git_signature *sig, const char **buffer_out,
@@ -259,40 +258,40 @@ int git_signature__parse(git_signature *sig, const char **buffer_out,
{
const char *buffer = *buffer_out;
const char *line_end, *name_end, *email_end, *tz_start, *time_start;
- int error = GIT_SUCCESS;
+ int error = 0;
memset(sig, 0x0, sizeof(git_signature));
if ((line_end = memchr(buffer, ender, buffer_end - buffer)) == NULL)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. No newline given");
+ return signature_error("no newline given");
if (header) {
const size_t header_len = strlen(header);
if (memcmp(buffer, header, header_len) != 0)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Expected prefix '%s' doesn't match actual", header);
+ return signature_error("expected prefix doesn't match actual");
buffer += header_len;
}
if (buffer > line_end)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Signature too short");
+ return signature_error("signature too short");
if ((name_end = strchr(buffer, '<')) == NULL)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Cannot find '<' in signature");
+ return signature_error("character '<' not allowed in signature");
if ((email_end = strchr(name_end, '>')) == NULL)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Cannot find '>' in signature");
+ return signature_error("character '>' not allowed in signature");
if (email_end < name_end)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse signature. Malformed e-mail");
+ return signature_error("malformed e-mail");
error = process_next_token(&buffer, &sig->name, name_end, line_end);
- if (error < GIT_SUCCESS)
+ if (error < 0)
return error;
error = process_next_token(&buffer, &sig->email, email_end, line_end);
- if (error < GIT_SUCCESS)
+ if (error < 0)
return error;
tz_start = scan_for_previous_token(line_end - 1, buffer);
@@ -301,19 +300,19 @@ int git_signature__parse(git_signature *sig, const char **buffer_out,
goto clean_exit; /* No timezone nor date */
time_start = scan_for_previous_token(tz_start - 1, buffer);
- if (time_start == NULL || parse_time(&sig->when.time, time_start) < GIT_SUCCESS) {
+ if (time_start == NULL || parse_time(&sig->when.time, time_start) < 0) {
/* The tz_start might point at the time */
parse_time(&sig->when.time, tz_start);
goto clean_exit;
}
- if (parse_timezone_offset(tz_start, &sig->when.offset) < GIT_SUCCESS) {
+ if (parse_timezone_offset(tz_start, &sig->when.offset) < 0) {
sig->when.time = 0; /* Bogus timezone, we reset the time */
}
clean_exit:
*buffer_out = line_end + 1;
- return GIT_SUCCESS;
+ return 0;
}
void git_signature__writebuf(git_buf *buf, const char *header, const git_signature *sig)
diff --git a/src/signature.h b/src/signature.h
index a66060661..97b3a055e 100644
--- a/src/signature.h
+++ b/src/signature.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
diff --git a/src/status.c b/src/status.c
index 62ef35685..e9ad3cfe4 100644
--- a/src/status.c
+++ b/src/status.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -15,780 +15,228 @@
#include "repository.h"
#include "ignore.h"
-struct status_entry {
- git_index_time mtime;
+#include "git2/diff.h"
+#include "diff.h"
- git_oid head_oid;
- git_oid index_oid;
- git_oid wt_oid;
-
- unsigned int status_flags;
-
- char path[GIT_FLEX_ARRAY]; /* more */
-};
-
-static struct status_entry *status_entry_new(git_vector *entries, const char *path)
-{
- struct status_entry *e = git__calloc(sizeof(*e) + strlen(path) + 1, 1);
- if (e == NULL)
- return NULL;
-
- if (entries != NULL)
- git_vector_insert(entries, e);
-
- strcpy(e->path, path);
-
- return e;
-}
-
-GIT_INLINE(void) status_entry_update_from_tree_entry(struct status_entry *e, const git_tree_entry *tree_entry)
-{
- assert(e && tree_entry);
-
- git_oid_cpy(&e->head_oid, &tree_entry->oid);
-}
-
-GIT_INLINE(void) status_entry_update_from_index_entry(struct status_entry *e, const git_index_entry *index_entry)
-{
- assert(e && index_entry);
-
- git_oid_cpy(&e->index_oid, &index_entry->oid);
- e->mtime = index_entry->mtime;
-}
-
-static void status_entry_update_from_index(struct status_entry *e, git_index *index)
-{
- int idx;
- git_index_entry *index_entry;
-
- assert(e && index);
-
- idx = git_index_find(index, e->path);
- if (idx < 0)
- return;
-
- index_entry = git_index_get(index, idx);
-
- status_entry_update_from_index_entry(e, index_entry);
-}
-
-static int status_entry_update_from_workdir(struct status_entry *e, const char* full_path)
-{
- struct stat filest;
-
- if (p_stat(full_path, &filest) < GIT_SUCCESS)
- return git__throw(GIT_EOSERR, "Failed to determine status of file '%s'. Can't read file", full_path);
-
- if (e->mtime.seconds == (git_time_t)filest.st_mtime)
- git_oid_cpy(&e->wt_oid, &e->index_oid);
- else
- git_odb_hashfile(&e->wt_oid, full_path, GIT_OBJ_BLOB);
-
- return GIT_SUCCESS;
-}
-
-static int status_entry_update_flags(struct status_entry *e)
-{
- git_oid zero;
- int head_zero, index_zero, wt_zero;
-
- memset(&zero, 0x0, sizeof(git_oid));
-
- head_zero = git_oid_cmp(&zero, &e->head_oid);
- index_zero = git_oid_cmp(&zero, &e->index_oid);
- wt_zero = git_oid_cmp(&zero, &e->wt_oid);
-
- if (head_zero == 0 && index_zero == 0 && wt_zero == 0)
- return GIT_ENOTFOUND;
-
- if (head_zero == 0 && index_zero != 0)
- e->status_flags |= GIT_STATUS_INDEX_NEW;
- else if (index_zero == 0 && head_zero != 0)
- e->status_flags |= GIT_STATUS_INDEX_DELETED;
- else if (git_oid_cmp(&e->head_oid, &e->index_oid) != 0)
- e->status_flags |= GIT_STATUS_INDEX_MODIFIED;
-
- if (index_zero == 0 && wt_zero != 0)
- e->status_flags |= GIT_STATUS_WT_NEW;
- else if (wt_zero == 0 && index_zero != 0)
- e->status_flags |= GIT_STATUS_WT_DELETED;
- else if (git_oid_cmp(&e->index_oid, &e->wt_oid) != 0)
- e->status_flags |= GIT_STATUS_WT_MODIFIED;
-
- return GIT_SUCCESS;
-}
-
-static int status_entry_is_ignorable(struct status_entry *e)
-{
- /* don't ignore files that exist in head or index already */
- return (e->status_flags == GIT_STATUS_WT_NEW);
-}
-
-static int status_entry_update_ignore(struct status_entry *e, git_ignores *ignores, const char *path)
-{
- int error, ignored;
-
- if ((error = git_ignore__lookup(ignores, path, &ignored)) == GIT_SUCCESS &&
- ignored)
- e->status_flags =
- (e->status_flags & ~GIT_STATUS_WT_NEW) | GIT_STATUS_IGNORED;
-
- return error;
-}
-
-struct status_st {
- git_repository *repo;
- git_vector *vector;
- git_index *index;
- git_tree *tree;
- git_ignores *ignores;
-
- int workdir_path_len;
- git_buf head_tree_relative_path;
- int head_tree_relative_path_len;
- unsigned int tree_position;
- unsigned int index_position;
- int is_dir:1;
-};
-
-static int retrieve_head_tree(git_tree **tree_out, git_repository *repo)
+static unsigned int index_delta2status(git_delta_t index_status)
{
- git_reference *resolved_head_ref;
- git_commit *head_commit = NULL;
- git_tree *tree;
- int error = GIT_SUCCESS;
-
- *tree_out = NULL;
-
- error = git_repository_head(&resolved_head_ref, repo);
- /*
- * We assume that a situation where HEAD exists but can not be resolved is valid.
- * A new repository fits this description for instance.
- */
- if (error == GIT_ENOTFOUND)
- return GIT_SUCCESS;
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "HEAD can't be resolved");
-
- if ((error = git_commit_lookup(&head_commit, repo, git_reference_oid(resolved_head_ref))) < GIT_SUCCESS)
- return git__rethrow(error, "The tip of HEAD can't be retrieved");
-
- git_reference_free(resolved_head_ref);
-
- if ((error = git_commit_tree(&tree, head_commit)) < GIT_SUCCESS) {
- error = git__rethrow(error, "The tree of HEAD can't be retrieved");
- goto exit;
- }
-
- *tree_out = tree;
-
-exit:
- git_commit_free(head_commit);
- return error;
-}
+ unsigned int st = GIT_STATUS_CURRENT;
-enum path_type {
- GIT_STATUS_PATH_NULL,
- GIT_STATUS_PATH_IGNORE,
- GIT_STATUS_PATH_FILE,
- GIT_STATUS_PATH_FOLDER,
-};
-
-static int dirent_cb(void *state, git_buf *full_path);
-static int alphasorted_futils_direach(
- git_buf *path, int (*fn)(void *, git_buf *), void *arg);
-
-static int process_folder(
- struct status_st *st,
- const git_tree_entry *tree_entry,
- git_buf *full_path,
- enum path_type path_type)
-{
- git_object *subtree = NULL;
- git_tree *pushed_tree = NULL;
- int error, pushed_tree_position = 0;
- git_otype tree_entry_type = GIT_OBJ_BAD;
-
- if (tree_entry != NULL) {
- tree_entry_type = git_tree_entry_type(tree_entry);
-
- switch (tree_entry_type) {
- case GIT_OBJ_TREE:
- error = git_tree_entry_2object(&subtree, ((git_object *)(st->tree))->repo, tree_entry);
- pushed_tree = st->tree;
- pushed_tree_position = st->tree_position;
- st->tree = (git_tree *)subtree;
- st->tree_position = 0;
- st->head_tree_relative_path_len += 1 + tree_entry->filename_len; /* path + '/' + name */
- break;
-
- case GIT_OBJ_BLOB:
- /* No op */
- break;
-
- case GIT_OBJ_COMMIT:
- /* TODO: proper submodule support */
- break;
-
- default:
- return git__throw(GIT_EINVALIDTYPE, "Unexpected tree entry type");
- }
+ switch (index_status) {
+ case GIT_DELTA_ADDED:
+ case GIT_DELTA_COPIED:
+ case GIT_DELTA_RENAMED:
+ st = GIT_STATUS_INDEX_NEW;
+ break;
+ case GIT_DELTA_DELETED:
+ st = GIT_STATUS_INDEX_DELETED;
+ break;
+ case GIT_DELTA_MODIFIED:
+ st = GIT_STATUS_INDEX_MODIFIED;
+ break;
+ default:
+ break;
}
-
- if (full_path != NULL && path_type == GIT_STATUS_PATH_FOLDER) {
- git_ignores ignores, *old_ignores;
-
- if ((error = git_ignore__for_path(st->repo,
- full_path->ptr + st->workdir_path_len, &ignores)) == GIT_SUCCESS)
- {
- old_ignores = st->ignores;
- st->ignores = &ignores;
-
- error = alphasorted_futils_direach(full_path, dirent_cb, st);
-
- git_ignore__free(st->ignores);
- st->ignores = old_ignores;
- }
- } else {
- error = dirent_cb(st, NULL);
- }
-
- if (tree_entry_type == GIT_OBJ_TREE) {
- git_object_free(subtree);
- st->head_tree_relative_path_len -= 1 + tree_entry->filename_len;
- st->tree = pushed_tree;
- st->tree_position = pushed_tree_position;
- st->tree_position++;
- }
-
- return error;
+ return st;
}
-static int store_if_changed(struct status_st *st, struct status_entry *e)
+static unsigned int workdir_delta2status(git_delta_t workdir_status)
{
- int error;
- if ((error = status_entry_update_flags(e)) < GIT_SUCCESS)
- return git__throw(error, "Failed to process the file '%s'. It doesn't exist in the workdir, in the HEAD nor in the index", e->path);
-
- if (status_entry_is_ignorable(e) &&
- (error = status_entry_update_ignore(e, st->ignores, e->path)) < GIT_SUCCESS)
- return error;
+ unsigned int st = GIT_STATUS_CURRENT;
- if (e->status_flags == GIT_STATUS_CURRENT) {
- git__free(e);
- return GIT_SUCCESS;
+ switch (workdir_status) {
+ case GIT_DELTA_ADDED:
+ case GIT_DELTA_COPIED:
+ case GIT_DELTA_RENAMED:
+ case GIT_DELTA_UNTRACKED:
+ st = GIT_STATUS_WT_NEW;
+ break;
+ case GIT_DELTA_DELETED:
+ st = GIT_STATUS_WT_DELETED;
+ break;
+ case GIT_DELTA_MODIFIED:
+ st = GIT_STATUS_WT_MODIFIED;
+ break;
+ case GIT_DELTA_IGNORED:
+ st = GIT_STATUS_IGNORED;
+ break;
+ default:
+ break;
}
- return git_vector_insert(st->vector, e);
+ return st;
}
-static int determine_status(
- struct status_st *st,
- int in_head, int in_index, int in_workdir,
- const git_tree_entry *tree_entry,
- const git_index_entry *index_entry,
- git_buf *full_path,
- const char *status_path,
- enum path_type path_type)
-{
- struct status_entry *e;
- int error = GIT_SUCCESS;
- git_otype tree_entry_type = GIT_OBJ_BAD;
-
- if (tree_entry != NULL)
- tree_entry_type = git_tree_entry_type(tree_entry);
-
- /* If we're dealing with a directory in the workdir, let's recursively tackle it first */
- if (path_type == GIT_STATUS_PATH_FOLDER)
- return process_folder(st, tree_entry, full_path, path_type);
-
- /* Are we dealing with a file somewhere? */
- if (in_workdir || in_index || (in_head && tree_entry_type == GIT_OBJ_BLOB)) {
- e = status_entry_new(NULL, status_path);
+int git_status_foreach_ext(
+ git_repository *repo,
+ const git_status_options *opts,
+ int (*cb)(const char *, unsigned int, void *),
+ void *cbdata)
+{
+ int err = 0, cmp;
+ git_diff_options diffopt;
+ git_diff_list *idx2head = NULL, *wd2idx = NULL;
+ git_tree *head = NULL;
+ git_status_show_t show =
+ opts ? opts->show : GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+ git_diff_delta *i2h, *w2i;
+ unsigned int i, j, i_max, j_max;
+
+ assert(show <= GIT_STATUS_SHOW_INDEX_THEN_WORKDIR);
+
+ if ((err = git_repository_head_tree(&head, repo)) < 0)
+ return err;
+
+ memset(&diffopt, 0, sizeof(diffopt));
+ memcpy(&diffopt.pathspec, &opts->pathspec, sizeof(diffopt.pathspec));
+
+ if ((opts->flags & GIT_STATUS_OPT_INCLUDE_UNTRACKED) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNTRACKED;
+ if ((opts->flags & GIT_STATUS_OPT_INCLUDE_IGNORED) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_IGNORED;
+ if ((opts->flags & GIT_STATUS_OPT_INCLUDE_UNMODIFIED) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNMODIFIED;
+ if ((opts->flags & GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_RECURSE_UNTRACKED_DIRS;
+ /* TODO: support EXCLUDE_SUBMODULES flag */
+
+ if (show != GIT_STATUS_SHOW_WORKDIR_ONLY &&
+ (err = git_diff_index_to_tree(repo, &diffopt, head, &idx2head)) < 0)
+ goto cleanup;
- if (in_head && tree_entry_type == GIT_OBJ_BLOB) {
- status_entry_update_from_tree_entry(e, tree_entry);
- st->tree_position++;
- }
+ if (show != GIT_STATUS_SHOW_INDEX_ONLY &&
+ (err = git_diff_workdir_to_index(repo, &diffopt, &wd2idx)) < 0)
+ goto cleanup;
- if (in_index) {
- status_entry_update_from_index_entry(e, index_entry);
- st->index_position++;
+ if (show == GIT_STATUS_SHOW_INDEX_THEN_WORKDIR) {
+ for (i = 0; !err && i < idx2head->deltas.length; i++) {
+ i2h = GIT_VECTOR_GET(&idx2head->deltas, i);
+ err = cb(i2h->old_file.path, index_delta2status(i2h->status), cbdata);
}
-
- if (in_workdir)
- if ((error = status_entry_update_from_workdir(
- e, full_path->ptr)) < GIT_SUCCESS)
- return error; /* The callee has already set the error message */
-
- return store_if_changed(st, e);
+ git_diff_list_free(idx2head);
+ idx2head = NULL;
}
- /* Are we dealing with a subtree? */
- if (tree_entry_type == GIT_OBJ_TREE) {
- assert(in_head && !in_index && !in_workdir);
- return process_folder(st, tree_entry, full_path, path_type);
- }
-
- /* We're dealing with something else -- most likely a submodule;
- * skip it for now */
- if (in_head)
- st->tree_position++;
- if (in_index)
- st->index_position++;
- return GIT_SUCCESS;
-}
-
-static int path_type_from(git_buf *full_path, int is_dir)
-{
- if (full_path == NULL)
- return GIT_STATUS_PATH_NULL;
-
- if (!is_dir)
- return GIT_STATUS_PATH_FILE;
-
- if (!git__suffixcmp(full_path->ptr, "/" DOT_GIT "/"))
- return GIT_STATUS_PATH_IGNORE;
-
- return GIT_STATUS_PATH_FOLDER;
-}
-
-static const char *status_path(const char *first, const char *second, const char *third)
-{
- /* At least one of them can not be NULL */
- assert(first != NULL || second != NULL || third != NULL);
-
- /* TODO: Fixme. Ensure that when non null, they're all equal */
- if (first != NULL)
- return first;
+ i_max = idx2head ? idx2head->deltas.length : 0;
+ j_max = wd2idx ? wd2idx->deltas.length : 0;
- if (second != NULL)
- return second;
+ for (i = 0, j = 0; !err && (i < i_max || j < j_max); ) {
+ i2h = idx2head ? GIT_VECTOR_GET(&idx2head->deltas,i) : NULL;
+ w2i = wd2idx ? GIT_VECTOR_GET(&wd2idx->deltas,j) : NULL;
- return third;
-}
-
-static int compare(const char *left, const char *right)
-{
- if (left == NULL && right == NULL)
- return 0;
+ cmp = !w2i ? -1 : !i2h ? 1 : strcmp(i2h->old_file.path, w2i->old_file.path);
- if (left == NULL)
- return 1;
-
- if (right == NULL)
- return -1;
-
- return strcmp(left, right);
-}
-
-/* Greatly inspired from JGit IndexTreeWalker */
-/* https://github.com/spearce/jgit/blob/ed47e29c777accfa78c6f50685a5df2b8f5b8ff5/org.spearce.jgit/src/org/spearce/jgit/lib/IndexTreeWalker.java#L88 */
-
-static int dirent_cb(void *state, git_buf *a)
-{
- const git_tree_entry *m;
- const git_index_entry *entry;
- enum path_type path_type;
- int cmpma, cmpmi, cmpai, error;
- const char *pm, *pa, *pi;
- const char *m_name, *i_name, *a_name;
- struct status_st *st = (struct status_st *)state;
-
- path_type = path_type_from(a, st->is_dir);
-
- if (path_type == GIT_STATUS_PATH_IGNORE)
- return GIT_SUCCESS; /* Let's skip the ".git" directory */
-
- a_name = (path_type != GIT_STATUS_PATH_NULL) ? a->ptr + st->workdir_path_len : NULL;
-
- while (1) {
- if (st->tree == NULL)
- m = NULL;
- else
- m = git_tree_entry_byindex(st->tree, st->tree_position);
-
- entry = git_index_get(st->index, st->index_position);
-
- if ((m == NULL) && (a == NULL) && (entry == NULL))
- return GIT_SUCCESS;
-
- if (m != NULL) {
- git_buf_truncate(&st->head_tree_relative_path,
- st->head_tree_relative_path_len);
- git_buf_joinpath(&st->head_tree_relative_path,
- st->head_tree_relative_path.ptr, m->filename);
- /* When the tree entry is a folder, append a forward slash to its name */
- if (git_tree_entry_type(m) == GIT_OBJ_TREE)
- git_path_to_dir(&st->head_tree_relative_path);
-
- error = git_buf_lasterror(&st->head_tree_relative_path);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "An error occured while "
- "determining the status of '%s'", a->ptr);
-
- m_name = st->head_tree_relative_path.ptr;
- } else
- m_name = NULL;
-
- i_name = (entry != NULL) ? entry->path : NULL;
-
- cmpma = compare(m_name, a_name);
- cmpmi = compare(m_name, i_name);
- cmpai = compare(a_name, i_name);
-
- pm = ((cmpma <= 0) && (cmpmi <= 0)) ? m_name : NULL;
- pa = ((cmpma >= 0) && (cmpai <= 0)) ? a_name : NULL;
- pi = ((cmpmi >= 0) && (cmpai >= 0)) ? i_name : NULL;
-
- if ((error = determine_status(st, pm != NULL, pi != NULL, pa != NULL,
- m, entry, a, status_path(pm, pi, pa), path_type)) < GIT_SUCCESS)
- return git__rethrow(error, "An error occured while determining the status of '%s'", a->ptr);
-
- if ((pa != NULL) || (path_type == GIT_STATUS_PATH_FOLDER))
- return GIT_SUCCESS;
+ if (cmp < 0) {
+ err = cb(i2h->old_file.path, index_delta2status(i2h->status), cbdata);
+ i++;
+ } else if (cmp > 0) {
+ err = cb(w2i->old_file.path, workdir_delta2status(w2i->status), cbdata);
+ j++;
+ } else {
+ err = cb(i2h->old_file.path, index_delta2status(i2h->status) |
+ workdir_delta2status(w2i->status), cbdata);
+ i++; j++;
+ }
}
-}
-
-static int status_cmp(const void *a, const void *b)
-{
- const struct status_entry *entry_a = (const struct status_entry *)(a);
- const struct status_entry *entry_b = (const struct status_entry *)(b);
- return strcmp(entry_a->path, entry_b->path);
+cleanup:
+ git_tree_free(head);
+ git_diff_list_free(idx2head);
+ git_diff_list_free(wd2idx);
+ return err;
}
-#define DEFAULT_SIZE 16
-
int git_status_foreach(
git_repository *repo,
int (*callback)(const char *, unsigned int, void *),
void *payload)
{
- git_vector entries;
- git_ignores ignores;
- git_index *index = NULL;
- git_buf temp_path = GIT_BUF_INIT;
- struct status_st dirent_st = {0};
- int error = GIT_SUCCESS;
- unsigned int i;
- git_tree *tree;
- struct status_entry *e;
- const char *workdir;
-
- if ((workdir = git_repository_workdir(repo)) == NULL)
- return git__throw(GIT_ERROR,
- "Cannot retrieve status on a bare repository");
-
- if ((error = git_repository_index__weakptr(&index, repo)) < GIT_SUCCESS) {
- return git__rethrow(error,
- "Failed to determine statuses. Index can't be opened");
- }
-
- if ((error = retrieve_head_tree(&tree, repo)) < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to determine statuses");
- goto exit;
- }
-
- git_vector_init(&entries, DEFAULT_SIZE, status_cmp);
-
- dirent_st.repo = repo;
- dirent_st.vector = &entries;
- dirent_st.index = index;
- dirent_st.tree = tree;
- dirent_st.ignores = &ignores;
- dirent_st.workdir_path_len = strlen(workdir);
- git_buf_init(&dirent_st.head_tree_relative_path, 0);
- dirent_st.head_tree_relative_path_len = 0;
- dirent_st.tree_position = 0;
- dirent_st.index_position = 0;
- dirent_st.is_dir = 1;
-
- if (git_path_isdir(workdir)) {
- error = git__throw(GIT_EINVALIDPATH,
- "Failed to determine status of file '%s'. "
- "The given path doesn't lead to a folder", workdir);
- goto exit;
- }
+ git_status_options opts;
- git_buf_sets(&temp_path, workdir);
+ memset(&opts, 0, sizeof(opts));
+ opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+ opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED |
+ GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
- error = git_ignore__for_path(repo, "", dirent_st.ignores);
- if (error < GIT_SUCCESS)
- goto exit;
-
- error = alphasorted_futils_direach(
- &temp_path, dirent_cb, &dirent_st);
-
- if (error < GIT_SUCCESS)
- error = git__rethrow(error,
- "Failed to determine statuses. "
- "An error occured while processing the working directory");
-
- if ((error == GIT_SUCCESS) &&
- ((error = dirent_cb(&dirent_st, NULL)) < GIT_SUCCESS))
- error = git__rethrow(error,
- "Failed to determine statuses. "
- "An error occured while post-processing the HEAD tree and the index");
-
- for (i = 0; i < entries.length; ++i) {
- e = (struct status_entry *)git_vector_get(&entries, i);
-
- if (error == GIT_SUCCESS) {
- error = callback(e->path, e->status_flags, payload);
- if (error < GIT_SUCCESS)
- error = git__rethrow(error,
- "Failed to determine statuses. User callback failed");
- }
-
- git__free(e);
- }
-
-exit:
- git_buf_free(&dirent_st.head_tree_relative_path);
- git_buf_free(&temp_path);
- git_vector_free(&entries);
- git_ignore__free(&ignores);
- git_tree_free(tree);
- return error;
+ return git_status_foreach_ext(repo, &opts, callback, payload);
}
-static int recurse_tree_entry(git_tree *tree, struct status_entry *e, const char *path)
-{
- char *dir_sep;
- const git_tree_entry *tree_entry;
- git_tree *subtree;
- int error = GIT_SUCCESS;
-
- dir_sep = strchr(path, '/');
- if (!dir_sep) {
- tree_entry = git_tree_entry_byname(tree, path);
- if (tree_entry == NULL)
- return GIT_SUCCESS; /* The leaf doesn't exist in the tree*/
-
- status_entry_update_from_tree_entry(e, tree_entry);
- return GIT_SUCCESS;
- }
-
- /* Retrieve subtree name */
- *dir_sep = '\0';
-
- tree_entry = git_tree_entry_byname(tree, path);
- if (tree_entry == NULL)
- return GIT_SUCCESS; /* The subtree doesn't exist in the tree*/
-
- *dir_sep = '/';
-
- /* Retreive subtree */
- if ((error = git_tree_lookup(&subtree, tree->object.repo, &tree_entry->oid)) < GIT_SUCCESS)
- return git__throw(GIT_EOBJCORRUPTED, "Can't find tree object '%s'", tree_entry->filename);
-
- error = recurse_tree_entry(subtree, e, dir_sep+1);
- git_tree_free(subtree);
- return error;
-}
+struct status_file_info {
+ unsigned int count;
+ unsigned int status;
+ char *expected;
+};
-int git_status_file(unsigned int *status_flags, git_repository *repo, const char *path)
+static int get_one_status(const char *path, unsigned int status, void *data)
{
- struct status_entry *e;
- git_index *index = NULL;
- git_buf temp_path = GIT_BUF_INIT;
- int error = GIT_SUCCESS;
- git_tree *tree = NULL;
- const char *workdir;
-
- assert(status_flags && repo && path);
-
- if ((workdir = git_repository_workdir(repo)) == NULL)
- return git__throw(GIT_ERROR,
- "Cannot retrieve status on a bare repository");
+ struct status_file_info *sfi = data;
- if ((error = git_buf_joinpath(&temp_path, workdir, path)) < GIT_SUCCESS)
- return git__rethrow(error,
- "Failed to determine status of file '%s'", path);
-
- if (git_path_isdir(temp_path.ptr) == GIT_SUCCESS) {
- git_buf_free(&temp_path);
- return git__throw(GIT_EINVALIDPATH,
- "Failed to determine status of file '%s'. "
- "Given path leads to a folder, not a file", path);
- }
-
- e = status_entry_new(NULL, path);
- if (e == NULL) {
- git_buf_free(&temp_path);
- return GIT_ENOMEM;
- }
-
- /* Find file in Workdir */
- if (git_path_exists(temp_path.ptr) == GIT_SUCCESS) {
- if ((error = status_entry_update_from_workdir(e, temp_path.ptr)) < GIT_SUCCESS)
- goto cleanup; /* The callee has already set the error message */
- }
+ sfi->count++;
+ sfi->status = status;
- /* Find file in Index */
- if ((error = git_repository_index__weakptr(&index, repo)) < GIT_SUCCESS) {
- git__rethrow(error,
- "Failed to determine status of file '%s'."
- "Index can't be opened", path);
- goto cleanup;
- }
-
- status_entry_update_from_index(e, index);
-
- if ((error = retrieve_head_tree(&tree, repo)) < GIT_SUCCESS) {
- git__rethrow(error,
- "Failed to determine status of file '%s'", path);
- goto cleanup;
- }
-
- /* If the repository is not empty, try and locate the file in HEAD */
- if (tree != NULL) {
- if ((error = git_buf_sets(&temp_path, path)) < GIT_SUCCESS) {
- git__rethrow(error,
- "Failed to determine status of file '%s'", path);
- goto cleanup;
- }
-
- error = recurse_tree_entry(tree, e, temp_path.ptr);
- if (error < GIT_SUCCESS) {
- git__rethrow(error,
- "Failed to determine status of file '%s'. "
- "An error occured while processing the tree", path);
- goto cleanup;
- }
- }
-
- /* Determine status */
- if ((error = status_entry_update_flags(e)) < GIT_SUCCESS) {
- git__throw(error, "Nonexistent file");
- goto cleanup;
- }
-
- if (status_entry_is_ignorable(e)) {
- git_ignores ignores;
-
- if ((error = git_ignore__for_path(repo, path, &ignores)) == GIT_SUCCESS)
- error = status_entry_update_ignore(e, &ignores, path);
-
- git_ignore__free(&ignores);
-
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (sfi->count > 1 || strcmp(sfi->expected, path) != 0) {
+ giterr_set(GITERR_INVALID,
+ "Ambiguous path '%s' given to git_status_file", sfi->expected);
+ return -1;
}
- *status_flags = e->status_flags;
-
-cleanup:
- git_buf_free(&temp_path);
- git_tree_free(tree);
- git__free(e);
- return error;
+ return 0;
}
-/*
- * git_path_direach is not supposed to return entries in an ordered manner.
- * alphasorted_futils_direach wraps git_path_direach and invokes the callback
- * function by passing it alphabeticcally sorted paths parameters.
- *
- */
-
-static char *alphasorted_dirent_info_new(const git_buf *path)
+int git_status_file(
+ unsigned int *status_flags,
+ git_repository *repo,
+ const char *path)
{
- char *di = git__malloc(path->size + 2);
- if (!di)
- return di;
-
- git_buf_copy_cstr(di, path->size + 1, path);
-
- if (git_path_isdir(path->ptr) == GIT_SUCCESS) {
- /*
- * Append a forward slash to the name to force folders
- * to be ordered in a similar way than in a tree
- *
- * The file "subdir" should appear before the file "subdir.txt"
- * The folder "subdir" should appear after the file "subdir.txt"
- */
- di[path->size] = '/';
- di[path->size + 1] = '\0';
- }
+ int error;
+ git_status_options opts;
+ struct status_file_info sfi;
- return di;
-}
+ assert(status_flags && repo && path);
-static int alphasorted_dirent_cb(void *state, git_buf *full_path)
-{
- char *entry;
- git_vector *entry_names;
+ memset(&sfi, 0, sizeof(sfi));
+ if ((sfi.expected = git__strdup(path)) == NULL)
+ return -1;
- entry_names = (git_vector *)state;
- entry = alphasorted_dirent_info_new(full_path);
+ memset(&opts, 0, sizeof(opts));
+ opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+ opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED |
+ GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS |
+ GIT_STATUS_OPT_INCLUDE_UNMODIFIED;
+ opts.pathspec.count = 1;
+ opts.pathspec.strings = &sfi.expected;
- if (entry == NULL)
- return GIT_ENOMEM;
+ error = git_status_foreach_ext(repo, &opts, get_one_status, &sfi);
- if (git_vector_insert(entry_names, entry) < GIT_SUCCESS) {
- git__free(entry);
- return GIT_ENOMEM;
+ if (!error && !sfi.count) {
+ giterr_set(GITERR_INVALID,
+ "Attempt to get status of nonexistent file '%s'", path);
+ error = GIT_ENOTFOUND;
}
- return GIT_SUCCESS;
-}
-
-static int alphasorted_futils_direach(
- git_buf *path,
- int (*fn)(void *, git_buf *),
- void *arg)
-{
- char *entry;
- git_vector entry_names;
- unsigned int idx;
- int error = GIT_SUCCESS;
- git_buf entry_path = GIT_BUF_INIT;
-
- if (git_vector_init(&entry_names, 16, git__strcmp_cb) < GIT_SUCCESS)
- return GIT_ENOMEM;
-
- error = git_path_direach(path, alphasorted_dirent_cb, &entry_names);
-
- git_vector_sort(&entry_names);
-
- for (idx = 0; idx < entry_names.length; ++idx) {
- entry = (char *)git_vector_get(&entry_names, idx);
-
- /* We have to walk the entire vector even if there was an error,
- * in order to free up memory, but we stop making callbacks after
- * an error.
- */
- if (error == GIT_SUCCESS)
- error = git_buf_sets(&entry_path, entry);
-
- if (error == GIT_SUCCESS) {
- ((struct status_st *)arg)->is_dir =
- (entry[entry_path.size - 1] == '/');
- error = fn(arg, &entry_path);
- }
+ *status_flags = sfi.status;
- git__free(entry);
- }
+ git__free(sfi.expected);
- git_buf_free(&entry_path);
- git_vector_free(&entry_names);
return error;
}
-
-int git_status_should_ignore(git_repository *repo, const char *path, int *ignored)
+int git_status_should_ignore(
+ int *ignored,
+ git_repository *repo,
+ const char *path)
{
int error;
git_ignores ignores;
- if ((error = git_ignore__for_path(repo, path, &ignores)) == GIT_SUCCESS)
- error = git_ignore__lookup(&ignores, path, ignored);
+ if (git_ignore__for_path(repo, path, &ignores) < 0)
+ return -1;
+ error = git_ignore__lookup(&ignores, path, ignored);
git_ignore__free(&ignores);
-
return error;
}
diff --git a/src/strmap.h b/src/strmap.h
new file mode 100644
index 000000000..da5ca0dba
--- /dev/null
+++ b/src/strmap.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_strmap_h__
+#define INCLUDE_strmap_h__
+
+#include "common.h"
+
+#define kmalloc git__malloc
+#define kcalloc git__calloc
+#define krealloc git__realloc
+#define kfree git__free
+#include "khash.h"
+
+__KHASH_TYPE(str, const char *, void *);
+typedef khash_t(str) git_strmap;
+
+#define GIT__USE_STRMAP \
+ __KHASH_IMPL(str, static inline, const char *, void *, 1, kh_str_hash_func, kh_str_hash_equal)
+
+#define git_strmap_alloc() kh_init(str)
+#define git_strmap_free(h) kh_destroy(str, h), h = NULL
+#define git_strmap_clear(h) kh_clear(str, h)
+
+#define git_strmap_num_entries(h) kh_size(h)
+
+#define git_strmap_lookup_index(h, k) kh_get(str, h, k)
+#define git_strmap_valid_index(h, idx) (idx != kh_end(h))
+
+#define git_strmap_exists(h, k) (kh_get(str, h, k) != kh_end(h))
+
+#define git_strmap_value_at(h, idx) kh_val(h, idx)
+#define git_strmap_set_value_at(h, idx, v) kh_val(h, idx) = v
+#define git_strmap_delete_at(h, idx) kh_del(str, h, idx)
+
+#define git_strmap_insert(h, key, val, rval) do { \
+ khiter_t __pos = kh_put(str, h, key, &rval); \
+ if (rval >= 0) { \
+ if (rval == 0) kh_key(h, __pos) = key; \
+ kh_val(h, __pos) = val; \
+ } } while (0)
+
+#define git_strmap_insert2(h, key, val, oldv, rval) do { \
+ khiter_t __pos = kh_put(str, h, key, &rval); \
+ if (rval >= 0) { \
+ if (rval == 0) { \
+ oldv = kh_val(h, __pos); \
+ kh_key(h, __pos) = key; \
+ } else { oldv = NULL; } \
+ kh_val(h, __pos) = val; \
+ } } while (0)
+
+#define git_strmap_delete(h, key) do { \
+ khiter_t __pos = git_strmap_lookup_index(h, key); \
+ if (git_strmap_valid_index(h, __pos)) \
+ git_strmap_delete_at(h, __pos); } while (0)
+
+#define git_strmap_foreach kh_foreach
+#define git_strmap_foreach_value kh_foreach_value
+
+#endif
diff --git a/src/submodule.c b/src/submodule.c
new file mode 100644
index 000000000..3c07e657d
--- /dev/null
+++ b/src/submodule.c
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+#include "git2/config.h"
+#include "git2/types.h"
+#include "git2/repository.h"
+#include "git2/index.h"
+#include "git2/submodule.h"
+#include "buffer.h"
+#include "vector.h"
+#include "posix.h"
+#include "config_file.h"
+#include "config.h"
+#include "repository.h"
+
+static git_cvar_map _sm_update_map[] = {
+ {GIT_CVAR_STRING, "checkout", GIT_SUBMODULE_UPDATE_CHECKOUT},
+ {GIT_CVAR_STRING, "rebase", GIT_SUBMODULE_UPDATE_REBASE},
+ {GIT_CVAR_STRING, "merge", GIT_SUBMODULE_UPDATE_MERGE}
+};
+
+static git_cvar_map _sm_ignore_map[] = {
+ {GIT_CVAR_STRING, "all", GIT_SUBMODULE_IGNORE_ALL},
+ {GIT_CVAR_STRING, "dirty", GIT_SUBMODULE_IGNORE_DIRTY},
+ {GIT_CVAR_STRING, "untracked", GIT_SUBMODULE_IGNORE_UNTRACKED},
+ {GIT_CVAR_STRING, "none", GIT_SUBMODULE_IGNORE_NONE}
+};
+
+static inline khint_t str_hash_no_trailing_slash(const char *s)
+{
+ khint_t h;
+
+ for (h = 0; *s; ++s)
+ if (s[1] || *s != '/')
+ h = (h << 5) - h + *s;
+
+ return h;
+}
+
+static inline int str_equal_no_trailing_slash(const char *a, const char *b)
+{
+ size_t alen = a ? strlen(a) : 0;
+ size_t blen = b ? strlen(b) : 0;
+
+ if (alen && a[alen] == '/')
+ alen--;
+ if (blen && b[blen] == '/')
+ blen--;
+
+ return (alen == blen && strncmp(a, b, alen) == 0);
+}
+
+__KHASH_IMPL(str, static inline, const char *, void *, 1, str_hash_no_trailing_slash, str_equal_no_trailing_slash);
+
+static git_submodule *submodule_alloc(const char *name)
+{
+ git_submodule *sm = git__calloc(1, sizeof(git_submodule));
+ if (sm == NULL)
+ return sm;
+
+ sm->path = sm->name = git__strdup(name);
+ if (!sm->name) {
+ git__free(sm);
+ return NULL;
+ }
+
+ return sm;
+}
+
+static void submodule_release(git_submodule *sm, int decr)
+{
+ if (!sm)
+ return;
+
+ sm->refcount -= decr;
+
+ if (sm->refcount == 0) {
+ if (sm->name != sm->path)
+ git__free(sm->path);
+ git__free(sm->name);
+ git__free(sm->url);
+ git__free(sm);
+ }
+}
+
+static int submodule_from_entry(
+ git_strmap *smcfg, git_index_entry *entry)
+{
+ git_submodule *sm;
+ void *old_sm;
+ khiter_t pos;
+ int error;
+
+ pos = git_strmap_lookup_index(smcfg, entry->path);
+
+ if (git_strmap_valid_index(smcfg, pos))
+ sm = git_strmap_value_at(smcfg, pos);
+ else
+ sm = submodule_alloc(entry->path);
+
+ git_oid_cpy(&sm->oid, &entry->oid);
+
+ if (strcmp(sm->path, entry->path) != 0) {
+ if (sm->path != sm->name) {
+ git__free(sm->path);
+ sm->path = sm->name;
+ }
+ sm->path = git__strdup(entry->path);
+ if (!sm->path)
+ goto fail;
+ }
+
+ git_strmap_insert2(smcfg, sm->path, sm, old_sm, error);
+ if (error < 0)
+ goto fail;
+ sm->refcount++;
+
+ if (old_sm && ((git_submodule *)old_sm) != sm) {
+ /* TODO: log warning about multiple entrys for same submodule path */
+ submodule_release(old_sm, 1);
+ }
+
+ return 0;
+
+fail:
+ submodule_release(sm, 0);
+ return -1;
+}
+
+static int submodule_from_config(
+ const char *key, const char *value, void *data)
+{
+ git_strmap *smcfg = data;
+ const char *namestart;
+ const char *property;
+ git_buf name = GIT_BUF_INIT;
+ git_submodule *sm;
+ void *old_sm = NULL;
+ bool is_path;
+ khiter_t pos;
+ int error;
+
+ if (git__prefixcmp(key, "submodule.") != 0)
+ return 0;
+
+ namestart = key + strlen("submodule.");
+ property = strrchr(namestart, '.');
+ if (property == NULL)
+ return 0;
+ property++;
+ is_path = (strcmp(property, "path") == 0);
+
+ if (git_buf_set(&name, namestart, property - namestart - 1) < 0)
+ return -1;
+
+ pos = git_strmap_lookup_index(smcfg, name.ptr);
+ if (!git_strmap_valid_index(smcfg, pos) && is_path)
+ pos = git_strmap_lookup_index(smcfg, value);
+ if (!git_strmap_valid_index(smcfg, pos))
+ sm = submodule_alloc(name.ptr);
+ else
+ sm = git_strmap_value_at(smcfg, pos);
+ if (!sm)
+ goto fail;
+
+ if (strcmp(sm->name, name.ptr) != 0) {
+ assert(sm->path == sm->name);
+ sm->name = git_buf_detach(&name);
+
+ git_strmap_insert2(smcfg, sm->name, sm, old_sm, error);
+ if (error < 0)
+ goto fail;
+ sm->refcount++;
+ }
+ else if (is_path && strcmp(sm->path, value) != 0) {
+ assert(sm->path == sm->name);
+ sm->path = git__strdup(value);
+ if (sm->path == NULL)
+ goto fail;
+
+ git_strmap_insert2(smcfg, sm->path, sm, old_sm, error);
+ if (error < 0)
+ goto fail;
+ sm->refcount++;
+ }
+ git_buf_free(&name);
+
+ if (old_sm && ((git_submodule *)old_sm) != sm) {
+ /* TODO: log warning about multiple submodules with same path */
+ submodule_release(old_sm, 1);
+ }
+
+ if (is_path)
+ return 0;
+
+ /* copy other properties into submodule entry */
+ if (strcmp(property, "url") == 0) {
+ if (sm->url) {
+ git__free(sm->url);
+ sm->url = NULL;
+ }
+ if ((sm->url = git__strdup(value)) == NULL)
+ goto fail;
+ }
+ else if (strcmp(property, "update") == 0) {
+ int val;
+ if (git_config_lookup_map_value(
+ _sm_update_map, ARRAY_SIZE(_sm_update_map), value, &val) < 0) {
+ giterr_set(GITERR_INVALID,
+ "Invalid value for submodule update property: '%s'", value);
+ goto fail;
+ }
+ sm->update = (git_submodule_update_t)val;
+ }
+ else if (strcmp(property, "fetchRecurseSubmodules") == 0) {
+ if (git__parse_bool(&sm->fetch_recurse, value) < 0) {
+ giterr_set(GITERR_INVALID,
+ "Invalid value for submodule 'fetchRecurseSubmodules' property: '%s'", value);
+ goto fail;
+ }
+ }
+ else if (strcmp(property, "ignore") == 0) {
+ int val;
+ if (git_config_lookup_map_value(
+ _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), value, &val) < 0) {
+ giterr_set(GITERR_INVALID,
+ "Invalid value for submodule ignore property: '%s'", value);
+ goto fail;
+ }
+ sm->ignore = (git_submodule_ignore_t)val;
+ }
+ /* ignore other unknown submodule properties */
+
+ return 0;
+
+fail:
+ submodule_release(sm, 0);
+ git_buf_free(&name);
+ return -1;
+}
+
+static int load_submodule_config(git_repository *repo)
+{
+ int error;
+ git_index *index;
+ unsigned int i, max_i;
+ git_oid gitmodules_oid;
+ git_strmap *smcfg;
+ struct git_config_file *mods = NULL;
+
+ if (repo->submodules)
+ return 0;
+
+ /* submodule data is kept in a hashtable with each submodule stored
+ * under both its name and its path. These are usually the same, but
+ * that is not guaranteed.
+ */
+ smcfg = git_strmap_alloc();
+ GITERR_CHECK_ALLOC(smcfg);
+
+ /* scan index for gitmodules (and .gitmodules entry) */
+ if ((error = git_repository_index__weakptr(&index, repo)) < 0)
+ goto cleanup;
+ memset(&gitmodules_oid, 0, sizeof(gitmodules_oid));
+ max_i = git_index_entrycount(index);
+
+ for (i = 0; i < max_i; i++) {
+ git_index_entry *entry = git_index_get(index, i);
+ if (S_ISGITLINK(entry->mode)) {
+ if ((error = submodule_from_entry(smcfg, entry)) < 0)
+ goto cleanup;
+ }
+ else if (strcmp(entry->path, ".gitmodules") == 0)
+ git_oid_cpy(&gitmodules_oid, &entry->oid);
+ }
+
+ /* load .gitmodules from workdir if it exists */
+ if (git_repository_workdir(repo) != NULL) {
+ /* look in workdir for .gitmodules */
+ git_buf path = GIT_BUF_INIT;
+ if (!git_buf_joinpath(
+ &path, git_repository_workdir(repo), ".gitmodules") &&
+ git_path_isfile(path.ptr))
+ {
+ if (!(error = git_config_file__ondisk(&mods, path.ptr)))
+ error = git_config_file_open(mods);
+ }
+ git_buf_free(&path);
+ }
+
+ /* load .gitmodules from object cache if not in workdir */
+ if (!error && mods == NULL && !git_oid_iszero(&gitmodules_oid)) {
+ /* TODO: is it worth loading gitmodules from object cache? */
+ }
+
+ /* process .gitmodules info */
+ if (!error && mods != NULL)
+ error = git_config_file_foreach(mods, submodule_from_config, smcfg);
+
+ /* store submodule config in repo */
+ if (!error)
+ repo->submodules = smcfg;
+
+cleanup:
+ if (mods != NULL)
+ git_config_file_free(mods);
+ if (error)
+ git_strmap_free(smcfg);
+ return error;
+}
+
+void git_submodule_config_free(git_repository *repo)
+{
+ git_strmap *smcfg = repo->submodules;
+ git_submodule *sm;
+
+ repo->submodules = NULL;
+
+ if (smcfg == NULL)
+ return;
+
+ git_strmap_foreach_value(smcfg, sm, {
+ submodule_release(sm,1);
+ });
+ git_strmap_free(smcfg);
+}
+
+static int submodule_cmp(const void *a, const void *b)
+{
+ return strcmp(((git_submodule *)a)->name, ((git_submodule *)b)->name);
+}
+
+int git_submodule_foreach(
+ git_repository *repo,
+ int (*callback)(const char *name, void *payload),
+ void *payload)
+{
+ int error;
+ git_submodule *sm;
+ git_vector seen = GIT_VECTOR_INIT;
+ seen._cmp = submodule_cmp;
+
+ if ((error = load_submodule_config(repo)) < 0)
+ return error;
+
+ git_strmap_foreach_value(repo->submodules, sm, {
+ /* usually the following will not come into play */
+ if (sm->refcount > 1) {
+ if (git_vector_bsearch(&seen, sm) != GIT_ENOTFOUND)
+ continue;
+ if ((error = git_vector_insert(&seen, sm)) < 0)
+ break;
+ }
+
+ if ((error = callback(sm->name, payload)) < 0)
+ break;
+ });
+
+ git_vector_free(&seen);
+
+ return error;
+}
+
+int git_submodule_lookup(
+ git_submodule **sm_ptr, /* NULL allowed if user only wants to test */
+ git_repository *repo,
+ const char *name) /* trailing slash is allowed */
+{
+ khiter_t pos;
+
+ if (load_submodule_config(repo) < 0)
+ return -1;
+
+ pos = git_strmap_lookup_index(repo->submodules, name);
+ if (!git_strmap_valid_index(repo->submodules, pos))
+ return GIT_ENOTFOUND;
+
+ if (sm_ptr)
+ *sm_ptr = git_strmap_value_at(repo->submodules, pos);
+
+ return 0;
+}
diff --git a/src/tag.c b/src/tag.c
index 31f96b0ea..63424f530 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -9,6 +9,7 @@
#include "commit.h"
#include "tag.h"
#include "signature.h"
+#include "message.h"
#include "git2/object.h"
#include "git2/repository.h"
#include "git2/signature.h"
@@ -61,24 +62,32 @@ const char *git_tag_message(git_tag *t)
return t->message;
}
-static int parse_tag_buffer(git_tag *tag, const char *buffer, const char *buffer_end)
+static int tag_error(const char *str)
+{
+ giterr_set(GITERR_TAG, "Failed to parse tag. %s", str);
+ return -1;
+}
+
+int git_tag__parse_buffer(git_tag *tag, const char *buffer, size_t length)
{
static const char *tag_types[] = {
NULL, "commit\n", "tree\n", "blob\n", "tag\n"
};
- unsigned int i, text_len;
+ unsigned int i;
+ size_t text_len;
char *search;
- int error;
- if ((error = git_oid__parse(&tag->target, &buffer, buffer_end, "object ")) < 0)
- return git__rethrow(error, "Failed to parse tag. Object field invalid");
+ const char *buffer_end = buffer + length;
+
+ if (git_oid__parse(&tag->target, &buffer, buffer_end, "object ") < 0)
+ return tag_error("Object field invalid");
if (buffer + 5 >= buffer_end)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. Object too short");
+ return tag_error("Object too short");
if (memcmp(buffer, "type ", 5) != 0)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. Type field not found");
+ return tag_error("Type field not found");
buffer += 5;
tag->type = GIT_OBJ_BAD;
@@ -87,7 +96,7 @@ static int parse_tag_buffer(git_tag *tag, const char *buffer, const char *buffer
size_t type_length = strlen(tag_types[i]);
if (buffer + type_length >= buffer_end)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. Object too short");
+ return tag_error("Object too short");
if (memcmp(buffer, tag_types[i], type_length) == 0) {
tag->type = i;
@@ -97,25 +106,24 @@ static int parse_tag_buffer(git_tag *tag, const char *buffer, const char *buffer
}
if (tag->type == GIT_OBJ_BAD)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. Invalid object type");
+ return tag_error("Invalid object type");
if (buffer + 4 >= buffer_end)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. Object too short");
+ return tag_error("Object too short");
if (memcmp(buffer, "tag ", 4) != 0)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. Tag field not found");
+ return tag_error("Tag field not found");
buffer += 4;
search = memchr(buffer, '\n', buffer_end - buffer);
if (search == NULL)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. Object too short");
+ return tag_error("Object too short");
text_len = search - buffer;
tag->tag_name = git__malloc(text_len + 1);
- if (tag->tag_name == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(tag->tag_name);
memcpy(tag->tag_name, buffer, text_len);
tag->tag_name[text_len] = '\0';
@@ -125,27 +133,24 @@ static int parse_tag_buffer(git_tag *tag, const char *buffer, const char *buffer
tag->tagger = NULL;
if (*buffer != '\n') {
tag->tagger = git__malloc(sizeof(git_signature));
- if (tag->tagger == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(tag->tagger);
- if ((error = git_signature__parse(tag->tagger, &buffer, buffer_end, "tagger ", '\n') != 0)) {
- return git__rethrow(error, "Failed to parse tag");
- }
+ if (git_signature__parse(tag->tagger, &buffer, buffer_end, "tagger ", '\n') < 0)
+ return -1;
}
if( *buffer != '\n' )
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tag. No new line before message");
+ return tag_error("No new line before message");
text_len = buffer_end - ++buffer;
tag->message = git__malloc(text_len + 1);
- if (tag->message == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(tag->message);
memcpy(tag->message, buffer, text_len);
tag->message[text_len] = '\0';
- return GIT_SUCCESS;
+ return 0;
}
static int retrieve_tag_reference(
@@ -159,17 +164,28 @@ static int retrieve_tag_reference(
*tag_reference_out = NULL;
- error = git_buf_joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to retrieve tag reference");
+ if (git_buf_joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name) < 0)
+ return -1;
error = git_reference_lookup(&tag_ref, repo, ref_name_out->ptr);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to retrieve tag reference");
+ if (error < 0)
+ return error; /* Be it not foundo or corrupted */
*tag_reference_out = tag_ref;
- return GIT_SUCCESS;
+ return 0;
+}
+
+static int retrieve_tag_reference_oid(
+ git_oid *oid,
+ git_buf *ref_name_out,
+ git_repository *repo,
+ const char *tag_name)
+{
+ if (git_buf_joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name) < 0)
+ return -1;
+
+ return git_reference_name_to_oid(oid, repo, ref_name_out->ptr);
}
static int write_tag_annotation(
@@ -180,8 +196,7 @@ static int write_tag_annotation(
const git_signature *tagger,
const char *message)
{
- int error = GIT_SUCCESS;
- git_buf tag = GIT_BUF_INIT;
+ git_buf tag = GIT_BUF_INIT, cleaned_message = GIT_BUF_INIT;
git_odb *odb;
git_oid__writebuf(&tag, "object ", git_object_id(target));
@@ -189,27 +204,30 @@ static int write_tag_annotation(
git_buf_printf(&tag, "tag %s\n", tag_name);
git_signature__writebuf(&tag, "tagger ", tagger);
git_buf_putc(&tag, '\n');
- git_buf_puts(&tag, message);
- error = git_buf_lasterror(&tag);
- if (error < GIT_SUCCESS) {
- git_buf_free(&tag);
- return git__rethrow(error, "Not enough memory to build the tag data");
- }
+ /* Remove comments by default */
+ if (git_message_prettify(&cleaned_message, message, 1) < 0)
+ goto on_error;
- error = git_repository_odb__weakptr(&odb, repo);
- if (error < GIT_SUCCESS) {
- git_buf_free(&tag);
- return error;
- }
+ if (git_buf_puts(&tag, git_buf_cstr(&cleaned_message)) < 0)
+ goto on_error;
- error = git_odb_write(oid, odb, tag.ptr, tag.size, GIT_OBJ_TAG);
- git_buf_free(&tag);
+ git_buf_free(&cleaned_message);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to create tag annotation");
+ if (git_repository_odb__weakptr(&odb, repo) < 0)
+ goto on_error;
- return error;
+ if (git_odb_write(oid, odb, tag.ptr, tag.size, GIT_OBJ_TAG) < 0)
+ goto on_error;
+
+ git_buf_free(&tag);
+ return 0;
+
+on_error:
+ git_buf_free(&tag);
+ git_buf_free(&cleaned_message);
+ giterr_set(GITERR_OBJECT, "Failed to create tag annotation.");
+ return -1;
}
static int git_tag_create__internal(
@@ -225,51 +243,38 @@ static int git_tag_create__internal(
git_reference *new_ref = NULL;
git_buf ref_name = GIT_BUF_INIT;
- int error, should_update_ref = 0;
- const char *errmsg = "Failed to create tag";
+ int error;
assert(repo && tag_name && target);
assert(!create_tag_annotation || (tagger && message));
- if (git_object_owner(target) != repo)
- return git__throw(GIT_EINVALIDARGS,
- "The given target does not belong to this repository");
+ if (git_object_owner(target) != repo) {
+ giterr_set(GITERR_INVALID, "The given target does not belong to this repository");
+ return -1;
+ }
- error = retrieve_tag_reference(&new_ref, &ref_name, repo, tag_name);
- if (error < GIT_SUCCESS && error != GIT_ENOTFOUND)
- goto cleanup;
+ error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag_name);
+ if (error < 0 && error != GIT_ENOTFOUND)
+ return -1;
/** Ensure the tag name doesn't conflict with an already existing
* reference unless overwriting has explictly been requested **/
- if (new_ref != NULL) {
- if (!allow_ref_overwrite) {
- git_oid_cpy(oid, git_reference_oid(new_ref));
- error = GIT_EEXISTS;
- errmsg = "Tag already exists";
- goto cleanup;
- } else {
- should_update_ref = 1;
- }
+ if (error == 0 && !allow_ref_overwrite) {
+ git_buf_free(&ref_name);
+ giterr_set(GITERR_TAG, "Tag already exists");
+ return GIT_EEXISTS;
}
if (create_tag_annotation) {
- if ((error = write_tag_annotation(oid, repo, tag_name, target, tagger, message)) < GIT_SUCCESS)
- goto cleanup;
+ if (write_tag_annotation(oid, repo, tag_name, target, tagger, message) < 0)
+ return -1;
} else
git_oid_cpy(oid, git_object_id(target));
- if (!should_update_ref)
- error = git_reference_create_oid(&new_ref, repo, ref_name.ptr, oid, 0);
- else
- error = git_reference_set_oid(new_ref, oid);
+ error = git_reference_create_oid(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite);
-cleanup:
git_reference_free(new_ref);
git_buf_free(&ref_name);
-
- if (error < GIT_SUCCESS)
- git__rethrow(error, "%s", errmsg);
-
return error;
}
@@ -298,8 +303,7 @@ int git_tag_create_lightweight(
int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *buffer, int allow_ref_overwrite)
{
git_tag tag;
- int error, should_update_ref = 0;
- const char *errmsg = "Failed to create tag";
+ int error;
git_odb *odb;
git_odb_stream *stream;
git_odb_object *target_obj;
@@ -311,71 +315,66 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu
memset(&tag, 0, sizeof(tag));
- error = git_repository_odb__weakptr(&odb, repo);
- if (error < GIT_SUCCESS)
- return error;
+ if (git_repository_odb__weakptr(&odb, repo) < 0)
+ return -1;
/* validate the buffer */
- if ((error = parse_tag_buffer(&tag, buffer, buffer + strlen(buffer))) < GIT_SUCCESS)
- goto cleanup;
+ if (git_tag__parse_buffer(&tag, buffer, strlen(buffer)) < 0)
+ return -1;
/* validate the target */
- if ((error = git_odb_read(&target_obj, odb, &tag.target)) < GIT_SUCCESS)
- goto cleanup;
+ if (git_odb_read(&target_obj, odb, &tag.target) < 0)
+ goto on_error;
if (tag.type != target_obj->raw.type) {
- error = GIT_EINVALIDTYPE;
- errmsg = "The type for the given target is invalid";
- goto cleanup;
+ giterr_set(GITERR_TAG, "The type for the given target is invalid");
+ goto on_error;
}
- git_odb_object_free(target_obj);
+ error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag.tag_name);
+ if (error < 0 && error != GIT_ENOTFOUND)
+ goto on_error;
- error = retrieve_tag_reference(&new_ref, &ref_name, repo, tag.tag_name);
- if (error < GIT_SUCCESS && error != GIT_ENOTFOUND)
- goto cleanup;
+ /* We don't need these objects after this */
+ git_signature_free(tag.tagger);
+ git__free(tag.tag_name);
+ git__free(tag.message);
+ git_odb_object_free(target_obj);
/** Ensure the tag name doesn't conflict with an already existing
* reference unless overwriting has explictly been requested **/
- if (new_ref != NULL) {
- if (!allow_ref_overwrite) {
- git_oid_cpy(oid, git_reference_oid(new_ref));
- error = GIT_EEXISTS;
- errmsg = "Tag already exists";
- goto cleanup;
- } else {
- should_update_ref = 1;
- }
+ if (error == 0 && !allow_ref_overwrite) {
+ giterr_set(GITERR_TAG, "Tag already exists");
+ return GIT_EEXISTS;
}
/* write the buffer */
- if ((error = git_odb_open_wstream(&stream, odb, strlen(buffer), GIT_OBJ_TAG)) < GIT_SUCCESS)
- goto cleanup;
+ if (git_odb_open_wstream(&stream, odb, strlen(buffer), GIT_OBJ_TAG) < 0)
+ return -1;
stream->write(stream, buffer, strlen(buffer));
error = stream->finalize_write(oid, stream);
stream->free(stream);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (error < 0) {
+ git_buf_free(&ref_name);
+ return -1;
+ }
- if (!should_update_ref)
- error = git_reference_create_oid(&new_ref, repo, ref_name.ptr, oid, 0);
- else
- error = git_reference_set_oid(new_ref, oid);
+ error = git_reference_create_oid(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite);
-cleanup:
git_reference_free(new_ref);
- git_signature_free(tag.tagger);
- git__free(tag.tag_name);
- git__free(tag.message);
git_buf_free(&ref_name);
- if (error < GIT_SUCCESS)
- git__rethrow(error, "%s", errmsg);
-
return error;
+
+on_error:
+ git_signature_free(tag.tagger);
+ git__free(tag.tag_name);
+ git__free(tag.message);
+ git_odb_object_free(target_obj);
+ return -1;
}
int git_tag_delete(git_repository *repo, const char *tag_name)
@@ -388,8 +387,8 @@ int git_tag_delete(git_repository *repo, const char *tag_name)
git_buf_free(&ref_name);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to delete tag");
+ if (error < 0)
+ return -1;
return git_reference_delete(tag_ref);
}
@@ -397,7 +396,7 @@ int git_tag_delete(git_repository *repo, const char *tag_name)
int git_tag__parse(git_tag *tag, git_odb_object *obj)
{
assert(tag);
- return parse_tag_buffer(tag, obj->raw.data, (char *)obj->raw.data + obj->raw.len);
+ return git_tag__parse_buffer(tag, obj->raw.data, obj->raw.len);
}
typedef struct {
@@ -412,13 +411,13 @@ static int tag_list_cb(const char *tag_name, void *payload)
tag_filter_data *filter;
if (git__prefixcmp(tag_name, GIT_REFS_TAGS_DIR) != 0)
- return GIT_SUCCESS;
+ return 0;
filter = (tag_filter_data *)payload;
- if (!*filter->pattern || p_fnmatch(filter->pattern, tag_name + GIT_REFS_TAGS_DIR_LEN, 0) == GIT_SUCCESS)
+ if (!*filter->pattern || p_fnmatch(filter->pattern, tag_name + GIT_REFS_TAGS_DIR_LEN, 0) == 0)
return git_vector_insert(filter->taglist, git__strdup(tag_name));
- return GIT_SUCCESS;
+ return 0;
}
int git_tag_list_match(git_strarray *tag_names, const char *pattern, git_repository *repo)
@@ -429,24 +428,44 @@ int git_tag_list_match(git_strarray *tag_names, const char *pattern, git_reposit
assert(tag_names && repo && pattern);
- if (git_vector_init(&taglist, 8, NULL) < GIT_SUCCESS)
- return GIT_ENOMEM;
+ if (git_vector_init(&taglist, 8, NULL) < 0)
+ return -1;
filter.taglist = &taglist;
filter.pattern = pattern;
error = git_reference_foreach(repo, GIT_REF_OID|GIT_REF_PACKED, &tag_list_cb, (void *)&filter);
- if (error < GIT_SUCCESS) {
+ if (error < 0) {
git_vector_free(&taglist);
- return git__rethrow(error, "Failed to list tags");
+ return -1;
}
tag_names->strings = (char **)taglist.contents;
tag_names->count = taglist.length;
- return GIT_SUCCESS;
+ return 0;
}
int git_tag_list(git_strarray *tag_names, git_repository *repo)
{
return git_tag_list_match(tag_names, "", repo);
}
+
+int git_tag_peel(git_object **tag_target, git_tag *tag)
+{
+ int error;
+ git_object *target;
+
+ assert(tag_target && tag);
+
+ if (git_tag_target(&target, tag) < 0)
+ return -1;
+
+ if (git_object_type(target) == GIT_OBJ_TAG) {
+ error = git_tag_peel(tag_target, (git_tag *)target);
+ git_object_free(target);
+ return error;
+ }
+
+ *tag_target = target;
+ return 0;
+}
diff --git a/src/tag.h b/src/tag.h
index a537b1ee8..47f425509 100644
--- a/src/tag.h
+++ b/src/tag.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -24,5 +24,6 @@ struct git_tag {
void git_tag__free(git_tag *tag);
int git_tag__parse(git_tag *tag, git_odb_object *obj);
+int git_tag__parse_buffer(git_tag *tag, const char *data, size_t len);
#endif
diff --git a/src/thread-utils.c b/src/thread-utils.c
index f582f4311..0ca01ef82 100644
--- a/src/thread-utils.c
+++ b/src/thread-utils.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
diff --git a/src/thread-utils.h b/src/thread-utils.h
index 913941978..a309e93d1 100644
--- a/src/thread-utils.h
+++ b/src/thread-utils.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
diff --git a/src/transport.c b/src/transport.c
index 00b79dc6d..5b2cd7ea4 100644
--- a/src/transport.c
+++ b/src/transport.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -9,6 +9,7 @@
#include "git2/remote.h"
#include "git2/net.h"
#include "transport.h"
+#include "path.h"
static struct {
char *prefix;
@@ -29,13 +30,20 @@ static git_transport_cb transport_find_fn(const char *url)
{
size_t i = 0;
- /* TODO: Parse "example.com:project.git" as an SSH URL */
-
+ // First, check to see if it's an obvious URL, which a URL scheme
for (i = 0; i < GIT_TRANSPORT_COUNT; ++i) {
if (!strncasecmp(url, transports[i].prefix, strlen(transports[i].prefix)))
return transports[i].fn;
}
+ /* still here? Check to see if the path points to a file on the local file system */
+ if ((git_path_exists(url) == 0) && git_path_isdir(url))
+ return &git_transport_local;
+
+ /* It could be a SSH remote path. Check to see if there's a : */
+ if (strrchr(url, ':'))
+ return &git_transport_dummy; /* SSH is an unsupported transport mechanism in this version of libgit2 */
+
return NULL;
}
@@ -43,10 +51,11 @@ static git_transport_cb transport_find_fn(const char *url)
* Public API *
**************/
-int git_transport_dummy(git_transport **GIT_UNUSED(transport))
+int git_transport_dummy(git_transport **transport)
{
- GIT_UNUSED_ARG(transport);
- return git__throw(GIT_ENOTIMPLEMENTED, "This protocol isn't implemented. Sorry");
+ GIT_UNUSED(transport);
+ giterr_set(GITERR_NET, "This transport isn't implemented. Sorry");
+ return -1;
}
int git_transport_new(git_transport **out, const char *url)
@@ -57,24 +66,21 @@ int git_transport_new(git_transport **out, const char *url)
fn = transport_find_fn(url);
- /*
- * If we haven't found the transport, we assume we mean a
- * local file.
- */
- if (fn == NULL)
- fn = &git_transport_local;
+ if (fn == NULL) {
+ giterr_set(GITERR_NET, "Unsupported URL protocol");
+ return -1;
+ }
error = fn(&transport);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to create new transport");
+ if (error < 0)
+ return error;
transport->url = git__strdup(url);
- if (transport->url == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(transport->url);
*out = transport;
- return GIT_SUCCESS;
+ return 0;
}
/* from remote.h */
@@ -83,3 +89,9 @@ int git_remote_valid_url(const char *url)
return transport_find_fn(url) != NULL;
}
+int git_remote_supported_url(const char* url)
+{
+ git_transport_cb transport_fn = transport_find_fn(url);
+
+ return ((transport_fn != NULL) && (transport_fn != &git_transport_dummy));
+}
diff --git a/src/transport.h b/src/transport.h
index 2ed8ad32a..125df2745 100644
--- a/src/transport.h
+++ b/src/transport.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -8,6 +8,7 @@
#define INCLUDE_transport_h__
#include "git2/net.h"
+#include "git2/indexer.h"
#include "vector.h"
#define GIT_CAP_OFS_DELTA "ofs-delta"
@@ -66,22 +67,14 @@ struct git_transport {
*/
int (*push)(struct git_transport *transport);
/**
- * Send a 'done' message
- */
- int (*send_done)(struct git_transport *transport);
- /**
* Negotiate the minimal amount of objects that need to be
* retrieved
*/
int (*negotiate_fetch)(struct git_transport *transport, git_repository *repo, const git_vector *wants);
/**
- * Send a flush
- */
- int (*send_flush)(struct git_transport *transport);
- /**
* Download the packfile
*/
- int (*download_pack)(char **out, struct git_transport *transport, git_repository *repo);
+ int (*download_pack)(struct git_transport *transport, git_repository *repo, git_off_t *bytes, git_indexer_stats *stats);
/**
* Fetch the changes
*/
@@ -102,6 +95,11 @@ int git_transport_local(struct git_transport **transport);
int git_transport_git(struct git_transport **transport);
int git_transport_http(struct git_transport **transport);
int git_transport_dummy(struct git_transport **transport);
+
+/**
+ Returns true if the passed URL is valid (a URL with a Git supported scheme,
+ or pointing to an existing path)
+*/
int git_transport_valid_url(const char *url);
typedef struct git_transport git_transport;
diff --git a/src/transports/git.c b/src/transports/git.c
index ece4d40a8..5baa810f0 100644
--- a/src/transports/git.c
+++ b/src/transports/git.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -46,11 +46,13 @@ static int gen_proto(git_buf *request, const char *cmd, const char *url)
char *delim, *repo;
char default_command[] = "git-upload-pack";
char host[] = "host=";
- int len;
+ size_t len;
delim = strchr(url, '/');
- if (delim == NULL)
- return git__throw(GIT_EOBJCORRUPTED, "Failed to create proto-request: malformed URL");
+ if (delim == NULL) {
+ giterr_set(GITERR_NET, "Malformed URL");
+ return -1;
+ }
repo = delim;
@@ -64,11 +66,15 @@ static int gen_proto(git_buf *request, const char *cmd, const char *url)
len = 4 + strlen(cmd) + 1 + strlen(repo) + 1 + strlen(host) + (delim - url) + 1;
git_buf_grow(request, len);
- git_buf_printf(request, "%04x%s %s%c%s", len, cmd, repo, 0, host);
+ git_buf_printf(request, "%04x%s %s%c%s",
+ (unsigned int)(len & 0x0FFFF), cmd, repo, 0, host);
git_buf_put(request, url, delim - url);
git_buf_putc(request, '\0');
- return git_buf_lasterror(request);
+ if (git_buf_oom(request))
+ return -1;
+
+ return 0;
}
static int send_request(GIT_SOCKET s, const char *cmd, const char *url)
@@ -77,7 +83,7 @@ static int send_request(GIT_SOCKET s, const char *cmd, const char *url)
git_buf request = GIT_BUF_INIT;
error = gen_proto(&request, cmd, url);
- if (error < GIT_SUCCESS)
+ if (error < 0)
goto cleanup;
error = gitno_send(s, request.ptr, request.size, 0);
@@ -94,32 +100,36 @@ cleanup:
*/
static int do_connect(transport_git *t, const char *url)
{
- GIT_SOCKET s;
char *host, *port;
const char prefix[] = "git://";
- int error, connected = 0;
+ int error;
+
+ t->socket = INVALID_SOCKET;
if (!git__prefixcmp(url, prefix))
url += strlen(prefix);
- error = gitno_extract_host_and_port(&host, &port, url, GIT_DEFAULT_PORT);
- if (error < GIT_SUCCESS)
- return error;
+ if (gitno_extract_host_and_port(&host, &port, url, GIT_DEFAULT_PORT) < 0)
+ return -1;
- s = gitno_connect(host, port);
- connected = 1;
- error = send_request(s, NULL, url);
- t->socket = s;
+ if ((error = gitno_connect(&t->socket, host, port)) == 0) {
+ error = send_request(t->socket, NULL, url);
+ }
git__free(host);
git__free(port);
- if (error < GIT_SUCCESS && s > 0)
- close(s);
- if (!connected)
- error = git__throw(GIT_EOSERR, "Failed to connect to any of the addresses");
+ if (error < 0 && t->socket != INVALID_SOCKET) {
+ gitno_close(t->socket);
+ t->socket = INVALID_SOCKET;
+ }
- return error;
+ if (t->socket == INVALID_SOCKET) {
+ giterr_set(GITERR_NET, "Failed to connect to the host");
+ return -1;
+ }
+
+ return 0;
}
/*
@@ -128,33 +138,30 @@ static int do_connect(transport_git *t, const char *url)
static int store_refs(transport_git *t)
{
gitno_buffer *buf = &t->buf;
- int error = GIT_SUCCESS;
+ int ret = 0;
while (1) {
- error = gitno_recv(buf);
- if (error < GIT_SUCCESS)
- return git__rethrow(GIT_EOSERR, "Failed to receive data");
- if (error == GIT_SUCCESS) /* Orderly shutdown, so exit */
- return GIT_SUCCESS;
-
- error = git_protocol_store_refs(&t->proto, buf->data, buf->offset);
- if (error == GIT_ESHORTBUFFER) {
+ if ((ret = gitno_recv(buf)) < 0)
+ return -1;
+ if (ret == 0) /* Orderly shutdown, so exit */
+ return 0;
+
+ ret = git_protocol_store_refs(&t->proto, buf->data, buf->offset);
+ if (ret == GIT_EBUFS) {
gitno_consume_n(buf, buf->len);
continue;
}
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to store refs");
+ if (ret < 0)
+ return ret;
gitno_consume_n(buf, buf->offset);
if (t->proto.flush) { /* No more refs */
t->proto.flush = 0;
- return GIT_SUCCESS;
+ return 0;
}
}
-
- return error;
}
static int detect_caps(transport_git *t)
@@ -167,7 +174,7 @@ static int detect_caps(transport_git *t)
pkt = git_vector_get(refs, 0);
/* No refs or capabilites, odd but not a problem */
if (pkt == NULL || pkt->capabilities == NULL)
- return GIT_SUCCESS;
+ return 0;
ptr = pkt->capabilities;
while (ptr != NULL && *ptr != '\0') {
@@ -184,7 +191,7 @@ static int detect_caps(transport_git *t)
ptr = strchr(ptr, ' ');
}
- return GIT_SUCCESS;
+ return 0;
}
/*
@@ -194,36 +201,33 @@ static int detect_caps(transport_git *t)
static int git_connect(git_transport *transport, int direction)
{
transport_git *t = (transport_git *) transport;
- int error = GIT_SUCCESS;
- if (direction == GIT_DIR_PUSH)
- return git__throw(GIT_EINVALIDARGS, "Pushing is not supported with the git protocol");
+ if (direction == GIT_DIR_PUSH) {
+ giterr_set(GITERR_NET, "Pushing over git:// is not supported");
+ return -1;
+ }
t->parent.direction = direction;
- error = git_vector_init(&t->refs, 16, NULL);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (git_vector_init(&t->refs, 16, NULL) < 0)
+ return -1;
/* Connect and ask for the refs */
- error = do_connect(t, transport->url);
- if (error < GIT_SUCCESS)
- return error;
+ if (do_connect(t, transport->url) < 0)
+ goto cleanup;
gitno_buffer_setup(&t->buf, t->buff, sizeof(t->buff), t->socket);
t->parent.connected = 1;
- error = store_refs(t);
- if (error < GIT_SUCCESS)
- return error;
+ if (store_refs(t) < 0)
+ goto cleanup;
- error = detect_caps(t);
+ if (detect_caps(t) < 0)
+ goto cleanup;
+ return 0;
cleanup:
- if (error < GIT_SUCCESS) {
- git_vector_free(&t->refs);
- }
-
- return error;
+ git_vector_free(&t->refs);
+ return -1;
}
static int git_ls(git_transport *transport, git_headlist_cb list_cb, void *opaque)
@@ -241,149 +245,129 @@ static int git_ls(git_transport *transport, git_headlist_cb list_cb, void *opaqu
pkt = (git_pkt_ref *)p;
- if (list_cb(&pkt->head, opaque) < 0)
- return git__throw(GIT_ERROR,
- "The user callback returned an error code");
+ if (list_cb(&pkt->head, opaque) < 0) {
+ giterr_set(GITERR_NET, "User callback returned error");
+ return -1;
+ }
}
- return GIT_SUCCESS;
+ return 0;
+}
+
+/* Wait until we get an ack from the */
+static int recv_pkt(gitno_buffer *buf)
+{
+ const char *ptr = buf->data, *line_end;
+ git_pkt *pkt;
+ int pkt_type, error;
+
+ do {
+ /* Wait for max. 1 second */
+ if ((error = gitno_select_in(buf, 1, 0)) < 0) {
+ return -1;
+ } else if (error == 0) {
+ /*
+ * Some servers don't respond immediately, so if this
+ * happens, we keep sending information until it
+ * answers. Pretend we received a NAK to convince higher
+ * layers to do so.
+ */
+ return GIT_PKT_NAK;
+ }
+
+ if ((error = gitno_recv(buf)) < 0)
+ return -1;
+
+ error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset);
+ if (error == GIT_EBUFS)
+ continue;
+ if (error < 0)
+ return -1;
+ } while (error);
+
+ gitno_consume(buf, line_end);
+ pkt_type = pkt->type;
+ git__free(pkt);
+
+ return pkt_type;
}
static int git_negotiate_fetch(git_transport *transport, git_repository *repo, const git_vector *wants)
{
transport_git *t = (transport_git *) transport;
git_revwalk *walk;
- git_reference *ref;
- git_strarray refs;
git_oid oid;
int error;
unsigned int i;
+ git_buf data = GIT_BUF_INIT;
gitno_buffer *buf = &t->buf;
- error = git_pkt_send_wants(wants, &t->caps, t->socket);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to send wants list");
+ if (git_pkt_buffer_wants(wants, &t->caps, &data) < 0)
+ return -1;
- error = git_reference_listall(&refs, repo, GIT_REF_LISTALL);
- if (error < GIT_ERROR)
- return git__rethrow(error, "Failed to list all references");
+ if (git_fetch_setup_walk(&walk, repo) < 0)
+ goto on_error;
- error = git_revwalk_new(&walk, repo);
- if (error < GIT_ERROR) {
- error = git__rethrow(error, "Failed to list all references");
- goto cleanup;
- }
- git_revwalk_sorting(walk, GIT_SORT_TIME);
-
- for (i = 0; i < refs.count; ++i) {
- /* No tags */
- if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR))
- continue;
-
- error = git_reference_lookup(&ref, repo, refs.strings[i]);
- if (error < GIT_ERROR) {
- error = git__rethrow(error, "Failed to lookup %s", refs.strings[i]);
- goto cleanup;
- }
-
- if (git_reference_type(ref) == GIT_REF_SYMBOLIC)
- continue;
-
- error = git_revwalk_push(walk, git_reference_oid(ref));
- if (error < GIT_ERROR) {
- error = git__rethrow(error, "Failed to push %s", refs.strings[i]);
- goto cleanup;
- }
- }
- git_strarray_free(&refs);
+ if (gitno_send(t->socket, data.ptr, data.size, 0) < 0)
+ goto on_error;
+ git_buf_clear(&data);
/*
* We don't support any kind of ACK extensions, so the negotiation
* boils down to sending what we have and listening for an ACK
* every once in a while.
*/
i = 0;
- while ((error = git_revwalk_next(&oid, walk)) == GIT_SUCCESS) {
- error = git_pkt_send_have(&oid, t->socket);
+ while ((error = git_revwalk_next(&oid, walk)) == 0) {
+ git_pkt_buffer_have(&oid, &data);
i++;
if (i % 20 == 0) {
- const char *ptr = buf->data, *line_end;
- git_pkt *pkt;
- git_pkt_send_flush(t->socket);
- while (1) {
- /* Wait for max. 1 second */
- error = gitno_select_in(buf, 1, 0);
- if (error < GIT_SUCCESS) {
- error = git__throw(GIT_EOSERR, "Error in select");
- } else if (error == 0) {
- /*
- * Some servers don't respond immediately, so if this
- * happens, we keep sending information until it
- * answers.
- */
- break;
- }
-
- error = gitno_recv(buf);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Error receiving data");
- goto cleanup;
- }
- error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset);
- if (error == GIT_ESHORTBUFFER)
- continue;
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to get answer");
- goto cleanup;
- }
-
- gitno_consume(buf, line_end);
-
- if (pkt->type == GIT_PKT_ACK) {
- git__free(pkt);
- error = GIT_SUCCESS;
- goto done;
- } else if (pkt->type == GIT_PKT_NAK) {
- git__free(pkt);
- break;
- } else {
- error = git__throw(GIT_ERROR, "Got unexpected pkt type");
- goto cleanup;
- }
- }
- }
- }
- if (error == GIT_EREVWALKOVER)
- error = GIT_SUCCESS;
+ int pkt_type;
-done:
- git_pkt_send_flush(t->socket);
- git_pkt_send_done(t->socket);
+ git_pkt_buffer_flush(&data);
+ if (git_buf_oom(&data))
+ goto on_error;
-cleanup:
- git_revwalk_free(walk);
+ if (gitno_send(t->socket, data.ptr, data.size, 0) < 0)
+ goto on_error;
- return error;
-}
+ pkt_type = recv_pkt(buf);
-static int git_send_flush(git_transport *transport)
-{
- transport_git *t = (transport_git *) transport;
+ if (pkt_type == GIT_PKT_ACK) {
+ break;
+ } else if (pkt_type == GIT_PKT_NAK) {
+ continue;
+ } else {
+ giterr_set(GITERR_NET, "Unexpected pkt type");
+ goto on_error;
+ }
- return git_pkt_send_flush(t->socket);
-}
+ }
+ }
+ if (error < 0 && error != GIT_REVWALKOVER)
+ goto on_error;
-static int git_send_done(git_transport *transport)
-{
- transport_git *t = (transport_git *) transport;
+ /* Tell the other end that we're done negotiating */
+ git_buf_clear(&data);
+ git_pkt_buffer_flush(&data);
+ git_pkt_buffer_done(&data);
+ if (gitno_send(t->socket, data.ptr, data.size, 0) < 0)
+ goto on_error;
+
+ git_buf_free(&data);
+ git_revwalk_free(walk);
+ return 0;
- return git_pkt_send_done(t->socket);
+on_error:
+ git_buf_free(&data);
+ git_revwalk_free(walk);
+ return -1;
}
-static int git_download_pack(char **out, git_transport *transport, git_repository *repo)
+static int git_download_pack(git_transport *transport, git_repository *repo, git_off_t *bytes, git_indexer_stats *stats)
{
transport_git *t = (transport_git *) transport;
- int error = GIT_SUCCESS;
+ int error = 0, read_bytes;
gitno_buffer *buf = &t->buf;
git_pkt *pkt;
const char *line_end, *ptr;
@@ -391,7 +375,7 @@ static int git_download_pack(char **out, git_transport *transport, git_repositor
/*
* For now, we ignore everything and wait for the pack
*/
- while (1) {
+ do {
ptr = buf->data;
/* Whilst we're searching for the pack */
while (1) {
@@ -400,15 +384,15 @@ static int git_download_pack(char **out, git_transport *transport, git_repositor
}
error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset);
- if (error == GIT_ESHORTBUFFER)
+ if (error == GIT_EBUFS)
break;
- if (error < GIT_SUCCESS)
+ if (error < 0)
return error;
if (pkt->type == GIT_PKT_PACK) {
git__free(pkt);
- return git_fetch__download_pack(out, buf->data, buf->offset, t->socket, repo);
+ return git_fetch__download_pack(buf->data, buf->offset, t->socket, repo, bytes, stats);
}
/* For now we don't care about anything */
@@ -416,34 +400,28 @@ static int git_download_pack(char **out, git_transport *transport, git_repositor
gitno_consume(buf, line_end);
}
- error = gitno_recv(buf);
- if (error < GIT_SUCCESS)
- return git__rethrow(GIT_EOSERR, "Failed to receive data");
- if (error == 0) { /* Orderly shutdown */
- return GIT_SUCCESS;
- }
+ read_bytes = gitno_recv(buf);
+ } while (read_bytes);
- }
+ return read_bytes;
}
-
static int git_close(git_transport *transport)
{
transport_git *t = (transport_git*) transport;
- int error;
/* Can't do anything if there's an error, so don't bother checking */
git_pkt_send_flush(t->socket);
- error = gitno_close(t->socket);
-
- if (error < 0)
- error = git__throw(GIT_EOSERR, "Failed to close socket");
+ if (gitno_close(t->socket) < 0) {
+ giterr_set(GITERR_NET, "Failed to close socket");
+ return -1;
+ }
#ifdef GIT_WIN32
WSACleanup();
#endif
- return error;
+ return 0;
}
static void git_free(git_transport *transport)
@@ -472,16 +450,13 @@ int git_transport_git(git_transport **out)
#endif
t = git__malloc(sizeof(transport_git));
- if (t == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(t);
memset(t, 0x0, sizeof(transport_git));
t->parent.connect = git_connect;
t->parent.ls = git_ls;
t->parent.negotiate_fetch = git_negotiate_fetch;
- t->parent.send_flush = git_send_flush;
- t->parent.send_done = git_send_done;
t->parent.download_pack = git_download_pack;
t->parent.close = git_close;
t->parent.free = git_free;
@@ -494,9 +469,10 @@ int git_transport_git(git_transport **out)
ret = WSAStartup(MAKEWORD(2,2), &t->wsd);
if (ret != 0) {
git_free(*out);
- return git__throw(GIT_EOSERR, "Winsock init failed");
+ giterr_set(GITERR_NET, "Winsock init failed");
+ return -1;
}
#endif
- return GIT_SUCCESS;
+ return 0;
}
diff --git a/src/transports/http.c b/src/transports/http.c
index 38446bdef..2a8ebbb09 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -32,7 +32,7 @@ typedef struct {
git_protocol proto;
git_vector refs;
git_vector common;
- int socket;
+ GIT_SOCKET socket;
git_buf buf;
git_remote_head **heads;
int error;
@@ -77,24 +77,26 @@ static int gen_request(git_buf *buf, const char *url, const char *host, const ch
}
git_buf_puts(buf, "\r\n");
- return git_buf_lasterror(buf);
+ if (git_buf_oom(buf))
+ return -1;
+
+ return 0;
}
static int do_connect(transport_http *t, const char *host, const char *port)
{
- GIT_SOCKET s = -1;
+ GIT_SOCKET s;
if (t->parent.connected && http_should_keep_alive(&t->parser))
- return GIT_SUCCESS;
+ return 0;
+
+ if (gitno_connect(&s, host, port) < 0)
+ return -1;
- s = gitno_connect(host, port);
- if (s < GIT_SUCCESS) {
- return git__rethrow(s, "Failed to connect to host");
- }
t->socket = s;
t->parent.connected = 1;
- return GIT_SUCCESS;
+ return 0;
}
/*
@@ -116,8 +118,7 @@ static int on_header_field(http_parser *parser, const char *str, size_t len)
t->ct_finished = 1;
t->ct_found = 0;
t->content_type = git__strdup(git_buf_cstr(buf));
- if (t->content_type == NULL)
- return t->error = GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(t->content_type);
git_buf_clear(buf);
}
@@ -164,19 +165,25 @@ static int on_headers_complete(http_parser *parser)
transport_http *t = (transport_http *) parser->data;
git_buf *buf = &t->buf;
+ /* The content-type is text/plain for 404, so don't validate */
+ if (parser->status_code == 404) {
+ git_buf_clear(buf);
+ return 0;
+ }
+
if (t->content_type == NULL) {
t->content_type = git__strdup(git_buf_cstr(buf));
if (t->content_type == NULL)
- return t->error = GIT_ENOMEM;
+ return t->error = -1;
}
git_buf_clear(buf);
git_buf_printf(buf, "application/x-git-%s-advertisement", t->service);
if (git_buf_oom(buf))
- return GIT_ENOMEM;
+ return t->error = -1;
if (strcmp(t->content_type, git_buf_cstr(buf)))
- return t->error = git__throw(GIT_EOBJCORRUPTED, "Content-Type '%s' is wrong", t->content_type);
+ return t->error = -1;
git_buf_clear(buf);
return 0;
@@ -186,6 +193,10 @@ static int on_body_store_refs(http_parser *parser, const char *str, size_t len)
{
transport_http *t = (transport_http *) parser->data;
+ if (parser->status_code == 404) {
+ return git_buf_put(&t->buf, str, len);
+ }
+
return git_protocol_store_refs(&t->proto, str, len);
}
@@ -194,16 +205,22 @@ static int on_message_complete(http_parser *parser)
transport_http *t = (transport_http *) parser->data;
t->transfer_finished = 1;
+
+ if (parser->status_code == 404) {
+ giterr_set(GITERR_NET, "Remote error: %s", git_buf_cstr(&t->buf));
+ t->error = -1;
+ }
+
return 0;
}
static int store_refs(transport_http *t)
{
- int error = GIT_SUCCESS;
http_parser_settings settings;
char buffer[1024];
gitno_buffer buf;
git_pkt *pkt;
+ int ret;
http_parser_init(&t->parser, HTTP_RESPONSE);
t->parser.data = t;
@@ -219,83 +236,76 @@ static int store_refs(transport_http *t)
while(1) {
size_t parsed;
- error = gitno_recv(&buf);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Error receiving data from network");
+ if ((ret = gitno_recv(&buf)) < 0)
+ return -1;
parsed = http_parser_execute(&t->parser, &settings, buf.data, buf.offset);
/* Both should happen at the same time */
- if (parsed != buf.offset || t->error < GIT_SUCCESS)
- return git__rethrow(t->error, "Error parsing HTTP data");
+ if (parsed != buf.offset || t->error < 0)
+ return t->error;
gitno_consume_n(&buf, parsed);
- if (error == 0 || t->transfer_finished)
- return GIT_SUCCESS;
+ if (ret == 0 || t->transfer_finished)
+ return 0;
}
pkt = git_vector_get(&t->refs, 0);
- if (pkt == NULL || pkt->type != GIT_PKT_COMMENT)
- return t->error = git__throw(GIT_EOBJCORRUPTED, "Not a valid smart HTTP response");
- else
+ if (pkt == NULL || pkt->type != GIT_PKT_COMMENT) {
+ giterr_set(GITERR_NET, "Invalid HTTP response");
+ return t->error = -1;
+ } else {
git_vector_remove(&t->refs, 0);
+ }
- return error;
+ return 0;
}
static int http_connect(git_transport *transport, int direction)
{
transport_http *t = (transport_http *) transport;
- int error;
+ int ret;
git_buf request = GIT_BUF_INIT;
const char *service = "upload-pack";
const char *url = t->parent.url, *prefix = "http://";
- if (direction == GIT_DIR_PUSH)
- return git__throw(GIT_EINVALIDARGS, "Pushing over HTTP is not supported");
+ if (direction == GIT_DIR_PUSH) {
+ giterr_set(GITERR_NET, "Pushing over HTTP is not implemented");
+ return -1;
+ }
t->parent.direction = direction;
- error = git_vector_init(&t->refs, 16, NULL);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to init refs vector");
+ if (git_vector_init(&t->refs, 16, NULL) < 0)
+ return -1;
if (!git__prefixcmp(url, prefix))
url += strlen(prefix);
- error = gitno_extract_host_and_port(&t->host, &t->port, url, "80");
- if (error < GIT_SUCCESS)
+ if ((ret = gitno_extract_host_and_port(&t->host, &t->port, url, "80")) < 0)
goto cleanup;
t->service = git__strdup(service);
- if (t->service == NULL) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
+ GITERR_CHECK_ALLOC(t->service);
- error = do_connect(t, t->host, t->port);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to connect to host");
+ if ((ret = do_connect(t, t->host, t->port)) < 0)
goto cleanup;
- }
/* Generate and send the HTTP request */
- error = gen_request(&request, url, t->host, "GET", service, 0, 1);
- if (error < GIT_SUCCESS) {
- error = git__throw(error, "Failed to generate request");
+ if ((ret = gen_request(&request, url, t->host, "GET", service, 0, 1)) < 0) {
+ giterr_set(GITERR_NET, "Failed to generate request");
goto cleanup;
}
- error = gitno_send(t->socket, request.ptr, request.size, 0);
- if (error < GIT_SUCCESS)
- error = git__rethrow(error, "Failed to send the HTTP request");
+ if ((ret = gitno_send(t->socket, request.ptr, request.size, 0)) < 0)
+ goto cleanup;
- error = store_refs(t);
+ ret = store_refs(t);
cleanup:
git_buf_free(&request);
git_buf_clear(&t->buf);
- return error;
+ return ret;
}
static int http_ls(git_transport *transport, git_headlist_cb list_cb, void *opaque)
@@ -309,12 +319,13 @@ static int http_ls(git_transport *transport, git_headlist_cb list_cb, void *opaq
if (p->type != GIT_PKT_REF)
continue;
- if (list_cb(&p->head, opaque) < 0)
- return git__throw(GIT_ERROR,
- "The user callback returned an error code");
+ if (list_cb(&p->head, opaque) < 0) {
+ giterr_set(GITERR_NET, "The user callback returned error");
+ return -1;
+ }
}
- return GIT_SUCCESS;
+ return 0;
}
static int on_body_parse_response(http_parser *parser, const char *str, size_t len)
@@ -326,10 +337,12 @@ static int on_body_parse_response(http_parser *parser, const char *str, size_t l
const char *line_end, *ptr;
if (len == 0) { /* EOF */
- if (buf->size != 0)
- return t->error = git__throw(GIT_ERROR, "EOF and unprocessed data");
- else
+ if (git_buf_len(buf) != 0) {
+ giterr_set(GITERR_NET, "Unexpected EOF");
+ return t->error = -1;
+ } else {
return 0;
+ }
}
git_buf_put(buf, str, len);
@@ -337,15 +350,15 @@ static int on_body_parse_response(http_parser *parser, const char *str, size_t l
while (1) {
git_pkt *pkt;
- if (buf->size == 0)
+ if (git_buf_len(buf) == 0)
return 0;
- error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->size);
- if (error == GIT_ESHORTBUFFER) {
+ error = git_pkt_parse_line(&pkt, ptr, &line_end, git_buf_len(buf));
+ if (error == GIT_EBUFS) {
return 0; /* Ask for more */
}
- if (error < GIT_SUCCESS)
- return t->error = git__rethrow(error, "Failed to parse pkt-line");
+ if (error < 0)
+ return t->error = -1;
git_buf_consume(buf, line_end);
@@ -365,9 +378,8 @@ static int on_body_parse_response(http_parser *parser, const char *str, size_t l
continue;
}
- error = git_vector_insert(common, pkt);
- if (error < GIT_SUCCESS)
- return t->error = git__rethrow(error, "Failed to add pkt to list");
+ if (git_vector_insert(common, pkt) < 0)
+ return -1;
}
return error;
@@ -376,7 +388,7 @@ static int on_body_parse_response(http_parser *parser, const char *str, size_t l
static int parse_response(transport_http *t)
{
- int error = GIT_SUCCESS;
+ int ret = 0;
http_parser_settings settings;
char buffer[1024];
gitno_buffer buf;
@@ -396,74 +408,28 @@ static int parse_response(transport_http *t)
while(1) {
size_t parsed;
- error = gitno_recv(&buf);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Error receiving data from network");
+ if ((ret = gitno_recv(&buf)) < 0)
+ return -1;
parsed = http_parser_execute(&t->parser, &settings, buf.data, buf.offset);
/* Both should happen at the same time */
- if (parsed != buf.offset || t->error < GIT_SUCCESS)
- return git__rethrow(t->error, "Error parsing HTTP data");
+ if (parsed != buf.offset || t->error < 0)
+ return t->error;
gitno_consume_n(&buf, parsed);
- if (error == 0 || t->transfer_finished || t->pack_ready) {
- return GIT_SUCCESS;
- }
- }
-
- return error;
-}
-
-static int setup_walk(git_revwalk **out, git_repository *repo)
-{
- git_revwalk *walk;
- git_strarray refs;
- unsigned int i;
- git_reference *ref;
- int error;
-
- error = git_reference_listall(&refs, repo, GIT_REF_LISTALL);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to list references");
-
- error = git_revwalk_new(&walk, repo);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to setup walk");
-
- git_revwalk_sorting(walk, GIT_SORT_TIME);
-
- for (i = 0; i < refs.count; ++i) {
- /* No tags */
- if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR))
- continue;
-
- error = git_reference_lookup(&ref, repo, refs.strings[i]);
- if (error < GIT_ERROR) {
- error = git__rethrow(error, "Failed to lookup %s", refs.strings[i]);
- goto cleanup;
- }
-
- if (git_reference_type(ref) == GIT_REF_SYMBOLIC)
- continue;
- error = git_revwalk_push(walk, git_reference_oid(ref));
- if (error < GIT_ERROR) {
- error = git__rethrow(error, "Failed to push %s", refs.strings[i]);
- goto cleanup;
+ if (ret == 0 || t->transfer_finished || t->pack_ready) {
+ return 0;
}
}
- *out = walk;
-cleanup:
- git_strarray_free(&refs);
-
- return error;
+ return ret;
}
static int http_negotiate_fetch(git_transport *transport, git_repository *repo, const git_vector *wants)
{
transport_http *t = (transport_http *) transport;
- int error;
+ int ret;
unsigned int i;
char buff[128];
gitno_buffer buf;
@@ -479,82 +445,55 @@ static int http_negotiate_fetch(git_transport *transport, git_repository *repo,
if (!git__prefixcmp(url, prefix))
url += strlen(prefix);
- error = git_vector_init(common, 16, NULL);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to init common vector");
+ if (git_vector_init(common, 16, NULL) < 0)
+ return -1;
- error = setup_walk(&walk, repo);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to setup walk");
- goto cleanup;
- }
+ if (git_fetch_setup_walk(&walk, repo) < 0)
+ return -1;
do {
- error = do_connect(t, t->host, t->port);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to connect to host");
+ if ((ret = do_connect(t, t->host, t->port)) < 0)
goto cleanup;
- }
- error = git_pkt_buffer_wants(wants, &t->caps, &data);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to send wants");
+ if ((ret = git_pkt_buffer_wants(wants, &t->caps, &data)) < 0)
goto cleanup;
- }
/* We need to send these on each connection */
git_vector_foreach (common, i, pkt) {
- error = git_pkt_buffer_have(&pkt->oid, &data);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to buffer common have");
+ if ((ret = git_pkt_buffer_have(&pkt->oid, &data)) < 0)
goto cleanup;
- }
}
i = 0;
- while ((i < 20) && ((error = git_revwalk_next(&oid, walk)) == GIT_SUCCESS)) {
- error = git_pkt_buffer_have(&oid, &data);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to buffer have");
+ while ((i < 20) && ((ret = git_revwalk_next(&oid, walk)) == 0)) {
+ if ((ret = git_pkt_buffer_have(&oid, &data)) < 0)
goto cleanup;
- }
+
i++;
}
git_pkt_buffer_done(&data);
- error = gen_request(&request, url, t->host, "POST", "upload-pack", data.size, 0);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to generate request");
+ if ((ret = gen_request(&request, url, t->host, "POST", "upload-pack", data.size, 0)) < 0)
goto cleanup;
- }
- error = gitno_send(t->socket, request.ptr, request.size, 0);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to send request");
+ if ((ret = gitno_send(t->socket, request.ptr, request.size, 0)) < 0)
goto cleanup;
- }
- error = gitno_send(t->socket, data.ptr, data.size, 0);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to send data");
+ if ((ret = gitno_send(t->socket, data.ptr, data.size, 0)) < 0)
goto cleanup;
- }
git_buf_clear(&request);
git_buf_clear(&data);
- if (error < GIT_SUCCESS || i >= 256)
+ if (ret < 0 || i >= 256)
break;
- error = parse_response(t);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Error parsing the response");
+ if ((ret = parse_response(t)) < 0)
goto cleanup;
- }
if (t->pack_ready) {
- error = GIT_SUCCESS;
+ ret = 0;
goto cleanup;
}
@@ -564,11 +503,12 @@ cleanup:
git_buf_free(&request);
git_buf_free(&data);
git_revwalk_free(walk);
- return error;
+ return ret;
}
typedef struct {
- git_filebuf *file;
+ git_indexer_stream *idx;
+ git_indexer_stats *stats;
transport_http *transport;
} download_pack_cbdata;
@@ -584,10 +524,10 @@ static int on_body_download_pack(http_parser *parser, const char *str, size_t le
{
download_pack_cbdata *data = (download_pack_cbdata *) parser->data;
transport_http *t = data->transport;
- git_filebuf *file = data->file;
-
+ git_indexer_stream *idx = data->idx;
+ git_indexer_stats *stats = data->stats;
- return t->error = git_filebuf_write(file, str, len);
+ return t->error = git_indexer_stream_add(idx, str, len, stats);
}
/*
@@ -596,96 +536,81 @@ static int on_body_download_pack(http_parser *parser, const char *str, size_t le
* the simple downloader. Furthermore, we're using keep-alive
* connections, so the simple downloader would just hang.
*/
-static int http_download_pack(char **out, git_transport *transport, git_repository *repo)
+static int http_download_pack(git_transport *transport, git_repository *repo, git_off_t *bytes, git_indexer_stats *stats)
{
transport_http *t = (transport_http *) transport;
git_buf *oldbuf = &t->buf;
- int error = GIT_SUCCESS;
+ int recvd;
http_parser_settings settings;
char buffer[1024];
gitno_buffer buf;
+ git_indexer_stream *idx = NULL;
download_pack_cbdata data;
- git_filebuf file = GIT_FILEBUF_INIT;
- git_buf path = GIT_BUF_INIT;
- char suff[] = "/objects/pack/pack-received\0";
+
+ gitno_buffer_setup(&buf, buffer, sizeof(buffer), t->socket);
+
+ if (memcmp(oldbuf->ptr, "PACK", strlen("PACK"))) {
+ giterr_set(GITERR_NET, "The pack doesn't start with a pack signature");
+ return -1;
+ }
+
+ if (git_indexer_stream_new(&idx, git_repository_path(repo)) < 0)
+ return -1;
+
/*
* This is part of the previous response, so we don't want to
* re-init the parser, just set these two callbacks.
*/
- data.file = &file;
+ memset(stats, 0, sizeof(git_indexer_stats));
+ data.stats = stats;
+ data.idx = idx;
data.transport = t;
t->parser.data = &data;
t->transfer_finished = 0;
memset(&settings, 0x0, sizeof(settings));
settings.on_message_complete = on_message_complete_download_pack;
settings.on_body = on_body_download_pack;
+ *bytes = git_buf_len(oldbuf);
- gitno_buffer_setup(&buf, buffer, sizeof(buffer), t->socket);
+ if (git_indexer_stream_add(idx, git_buf_cstr(oldbuf), git_buf_len(oldbuf), stats) < 0)
+ goto on_error;
- if (memcmp(oldbuf->ptr, "PACK", strlen("PACK"))) {
- return git__throw(GIT_ERROR, "The pack doesn't start with the signature");
- }
-
- error = git_buf_joinpath(&path, repo->path_repository, suff);
- if (error < GIT_SUCCESS)
- goto cleanup;
-
- error = git_filebuf_open(&file, path.ptr, GIT_FILEBUF_TEMPORARY);
- if (error < GIT_SUCCESS)
- goto cleanup;
-
- /* Part of the packfile has been received, don't loose it */
- error = git_filebuf_write(&file, oldbuf->ptr, oldbuf->size);
- if (error < GIT_SUCCESS)
- goto cleanup;
-
- while(1) {
+ do {
size_t parsed;
- error = gitno_recv(&buf);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Error receiving data from network");
+ if ((recvd = gitno_recv(&buf)) < 0)
+ goto on_error;
parsed = http_parser_execute(&t->parser, &settings, buf.data, buf.offset);
- /* Both should happen at the same time */
- if (parsed != buf.offset || t->error < GIT_SUCCESS)
- return git__rethrow(t->error, "Error parsing HTTP data");
+ if (parsed != buf.offset || t->error < 0)
+ goto on_error;
+ *bytes += recvd;
gitno_consume_n(&buf, parsed);
+ } while (recvd > 0 && !t->transfer_finished);
- if (error == 0 || t->transfer_finished) {
- break;
- }
- }
-
- *out = git__strdup(file.path_lock);
- if (*out == NULL) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
-
- /* A bit dodgy, but we need to keep the pack at the temporary path */
- error = git_filebuf_commit_at(&file, file.path_lock, GIT_PACK_FILE_MODE);
+ if (git_indexer_stream_finalize(idx, stats) < 0)
+ goto on_error;
-cleanup:
- if (error < GIT_SUCCESS)
- git_filebuf_cleanup(&file);
- git_buf_free(&path);
+ git_indexer_stream_free(idx);
+ return 0;
- return error;
+on_error:
+ git_indexer_stream_free(idx);
+ return -1;
}
static int http_close(git_transport *transport)
{
transport_http *t = (transport_http *) transport;
- int error;
- error = gitno_close(t->socket);
- if (error < 0)
- return git__throw(GIT_EOSERR, "Failed to close the socket: %s", strerror(errno));
+ if (gitno_close(t->socket) < 0) {
+ giterr_set(GITERR_OS, "Failed to close the socket: %s", strerror(errno));
+ return -1;
+ }
- return GIT_SUCCESS;
+ return 0;
}
@@ -729,8 +654,7 @@ int git_transport_http(git_transport **out)
transport_http *t;
t = git__malloc(sizeof(transport_http));
- if (t == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(t);
memset(t, 0x0, sizeof(transport_http));
@@ -748,10 +672,11 @@ int git_transport_http(git_transport **out)
* before any socket calls can be performed */
if (WSAStartup(MAKEWORD(2,2), &t->wsd) != 0) {
http_free((git_transport *) t);
- return git__throw(GIT_EOSERR, "Winsock init failed");
+ giterr_set(GITERR_OS, "Winsock init failed");
+ return -1;
}
#endif
*out = (git_transport *) t;
- return GIT_SUCCESS;
+ return 0;
}
diff --git a/src/transports/local.c b/src/transports/local.c
index a2135e73e..000993e69 100644
--- a/src/transports/local.c
+++ b/src/transports/local.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -26,110 +26,94 @@ static int add_ref(transport_local *t, const char *name)
{
const char peeled[] = "^{}";
git_remote_head *head;
- git_reference *ref, *resolved_ref;
- git_object *obj = NULL;
- int error = GIT_SUCCESS, peel_len, ret;
+ git_object *obj = NULL, *target = NULL;
+ git_buf buf = GIT_BUF_INIT;
head = git__malloc(sizeof(git_remote_head));
- if (head == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(head);
head->name = git__strdup(name);
- if (head->name == NULL) {
- error = GIT_ENOMEM;
- goto out;
- }
-
- error = git_reference_lookup(&ref, t->repo, name);
- if (error < GIT_SUCCESS)
- goto out;
-
- error = git_reference_resolve(&resolved_ref, ref);
- if (error < GIT_SUCCESS)
- goto out;
+ GITERR_CHECK_ALLOC(head->name);
- git_oid_cpy(&head->oid, git_reference_oid(resolved_ref));
-
- error = git_vector_insert(&t->refs, head);
- if (error < GIT_SUCCESS)
- goto out;
+ if (git_reference_name_to_oid(&head->oid, t->repo, name) < 0 ||
+ git_vector_insert(&t->refs, head) < 0)
+ {
+ git__free(head->name);
+ git__free(head);
+ return -1;
+ }
/* If it's not a tag, we don't need to try to peel it */
if (git__prefixcmp(name, GIT_REFS_TAGS_DIR))
- goto out;
+ return 0;
- error = git_object_lookup(&obj, t->repo, &head->oid, GIT_OBJ_ANY);
- if (error < GIT_SUCCESS) {
- git__rethrow(error, "Failed to lookup object");
- }
+ if (git_object_lookup(&obj, t->repo, &head->oid, GIT_OBJ_ANY) < 0)
+ return -1;
head = NULL;
/* If it's not an annotated tag, just get out */
- if (git_object_type(obj) != GIT_OBJ_TAG)
- goto out;
+ if (git_object_type(obj) != GIT_OBJ_TAG) {
+ git_object_free(obj);
+ return 0;
+ }
/* And if it's a tag, peel it, and add it to the list */
head = git__malloc(sizeof(git_remote_head));
- peel_len = strlen(name) + strlen(peeled);
- head->name = git__malloc(peel_len + 1);
- ret = p_snprintf(head->name, peel_len + 1, "%s%s", name, peeled);
+ GITERR_CHECK_ALLOC(head);
+ if (git_buf_join(&buf, 0, name, peeled) < 0)
+ return -1;
- assert(ret < peel_len + 1);
- (void)ret;
+ head->name = git_buf_detach(&buf);
- git_oid_cpy(&head->oid, git_tag_target_oid((git_tag *) obj));
+ if (git_tag_peel(&target, (git_tag *) obj) < 0)
+ goto on_error;
- error = git_vector_insert(&t->refs, head);
- if (error < GIT_SUCCESS)
- goto out;
+ git_oid_cpy(&head->oid, git_object_id(target));
+ git_object_free(obj);
+ git_object_free(target);
- out:
- git_reference_free(ref);
- git_reference_free(resolved_ref);
+ if (git_vector_insert(&t->refs, head) < 0)
+ return -1;
- git_object_free(obj);
- if (head && error < GIT_SUCCESS) {
- git__free(head->name);
- git__free(head);
- }
+ return 0;
- return error;
+on_error:
+ git_object_free(obj);
+ git_object_free(target);
+ return -1;
}
static int store_refs(transport_local *t)
{
- int error;
unsigned int i;
git_strarray ref_names = {0};
assert(t);
- error = git_vector_init(&t->refs, ref_names.count, NULL);
- if (error < GIT_SUCCESS)
- return error;
-
- error = git_reference_listall(&ref_names, t->repo, GIT_REF_LISTALL);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to list remote heads");
+ if (git_reference_list(&ref_names, t->repo, GIT_REF_LISTALL) < 0 ||
+ git_vector_init(&t->refs, (unsigned int)ref_names.count, NULL) < 0)
+ goto on_error;
/* Sort the references first */
git__tsort((void **)ref_names.strings, ref_names.count, &git__strcmp_cb);
/* Add HEAD */
- error = add_ref(t, GIT_HEAD_FILE);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (add_ref(t, GIT_HEAD_FILE) < 0)
+ goto on_error;
for (i = 0; i < ref_names.count; ++i) {
- error = add_ref(t, ref_names.strings[i]);
- if (error < GIT_SUCCESS)
- goto cleanup;
+ if (add_ref(t, ref_names.strings[i]) < 0)
+ goto on_error;
}
-cleanup:
git_strarray_free(&ref_names);
- return error;
+ return 0;
+
+on_error:
+ git_vector_free(&t->refs);
+ git_strarray_free(&ref_names);
+ return -1;
}
static int local_ls(git_transport *transport, git_headlist_cb list_cb, void *payload)
@@ -143,18 +127,17 @@ static int local_ls(git_transport *transport, git_headlist_cb list_cb, void *pay
git_vector_foreach(refs, i, h) {
if (list_cb(h, payload) < 0)
- return git__throw(GIT_ERROR,
- "The user callback returned an error code");
+ return -1;
}
- return GIT_SUCCESS;
+ return 0;
}
/*
* Try to open the url as a git directory. The direction doesn't
* matter in this case because we're calulating the heads ourselves.
*/
-static int local_connect(git_transport *transport, int GIT_UNUSED(direction))
+static int local_connect(git_transport *transport, int direction)
{
git_repository *repo;
int error;
@@ -162,46 +145,55 @@ static int local_connect(git_transport *transport, int GIT_UNUSED(direction))
const char *path;
git_buf buf = GIT_BUF_INIT;
- GIT_UNUSED_ARG(direction);
+ GIT_UNUSED(direction);
/* The repo layer doesn't want the prefix */
if (!git__prefixcmp(transport->url, "file://")) {
- error = git_path_fromurl(&buf, transport->url);
- if (error < GIT_SUCCESS) {
+ if (git_path_fromurl(&buf, transport->url) < 0) {
git_buf_free(&buf);
- return git__rethrow(error, "Failed to parse remote path");
+ return -1;
}
path = git_buf_cstr(&buf);
- } else /* We assume transport->url is already a path */
+ } else { /* We assume transport->url is already a path */
path = transport->url;
+ }
error = git_repository_open(&repo, path);
git_buf_free(&buf);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to open remote");
+ if (error < 0)
+ return -1;
t->repo = repo;
- error = store_refs(t);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to retrieve references");
+ if (store_refs(t) < 0)
+ return -1;
t->parent.connected = 1;
- return GIT_SUCCESS;
+ return 0;
+}
+
+static int local_negotiate_fetch(git_transport *transport, git_repository *repo, const git_vector *wants)
+{
+ GIT_UNUSED(transport);
+ GIT_UNUSED(repo);
+ GIT_UNUSED(wants);
+
+ giterr_set(GITERR_NET, "Fetch via local transport isn't implemented. Sorry");
+ return -1;
}
-static int local_close(git_transport *GIT_UNUSED(transport))
+static int local_close(git_transport *transport)
{
transport_local *t = (transport_local *)transport;
git_repository_free(t->repo);
t->repo = NULL;
- return GIT_SUCCESS;
+ return 0;
}
static void local_free(git_transport *transport)
@@ -232,17 +224,17 @@ int git_transport_local(git_transport **out)
transport_local *t;
t = git__malloc(sizeof(transport_local));
- if (t == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(t);
memset(t, 0x0, sizeof(transport_local));
t->parent.connect = local_connect;
t->parent.ls = local_ls;
+ t->parent.negotiate_fetch = local_negotiate_fetch;
t->parent.close = local_close;
t->parent.free = local_free;
*out = (git_transport *) t;
- return GIT_SUCCESS;
+ return 0;
}
diff --git a/src/tree-cache.c b/src/tree-cache.c
index ea8b7bfb7..ebc2c6807 100644
--- a/src/tree-cache.c
+++ b/src/tree-cache.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -82,24 +82,19 @@ static int read_tree_internal(git_tree_cache **out,
git_tree_cache *tree = NULL;
const char *name_start, *buffer;
int count;
- int error = GIT_SUCCESS;
size_t name_len;
buffer = name_start = *buffer_in;
- if ((buffer = memchr(buffer, '\0', buffer_end - buffer)) == NULL) {
- error = GIT_EOBJCORRUPTED;
- goto cleanup;
- }
+ if ((buffer = memchr(buffer, '\0', buffer_end - buffer)) == NULL)
+ goto corrupted;
- if (++buffer >= buffer_end) {
- error = GIT_EOBJCORRUPTED;
- goto cleanup;
- }
+ if (++buffer >= buffer_end)
+ goto corrupted;
name_len = strlen(name_start);
- if ((tree = git__malloc(sizeof(git_tree_cache) + name_len + 1)) == NULL)
- return GIT_ENOMEM;
+ tree = git__malloc(sizeof(git_tree_cache) + name_len + 1);
+ GITERR_CHECK_ALLOC(tree);
memset(tree, 0x0, sizeof(git_tree_cache));
tree->parent = parent;
@@ -109,39 +104,28 @@ static int read_tree_internal(git_tree_cache **out,
tree->name[name_len] = '\0';
/* Blank-terminated ASCII decimal number of entries in this tree */
- if (git__strtol32(&count, buffer, &buffer, 10) < GIT_SUCCESS || count < -1) {
- error = GIT_EOBJCORRUPTED;
- goto cleanup;
- }
+ if (git__strtol32(&count, buffer, &buffer, 10) < 0 || count < -1)
+ goto corrupted;
tree->entries = count;
- if (*buffer != ' ' || ++buffer >= buffer_end) {
- error = GIT_EOBJCORRUPTED;
- goto cleanup;
- }
+ if (*buffer != ' ' || ++buffer >= buffer_end)
+ goto corrupted;
/* Number of children of the tree, newline-terminated */
- if (git__strtol32(&count, buffer, &buffer, 10) < GIT_SUCCESS ||
- count < 0) {
- error = GIT_EOBJCORRUPTED;
- goto cleanup;
- }
+ if (git__strtol32(&count, buffer, &buffer, 10) < 0 || count < 0)
+ goto corrupted;
tree->children_count = count;
- if (*buffer != '\n' || ++buffer >= buffer_end) {
- error = GIT_EOBJCORRUPTED;
- goto cleanup;
- }
+ if (*buffer != '\n' || ++buffer > buffer_end)
+ goto corrupted;
/* The SHA1 is only there if it's not invalidated */
if (tree->entries >= 0) {
/* 160-bit SHA-1 for this tree and it's children */
- if (buffer + GIT_OID_RAWSZ > buffer_end) {
- error = GIT_EOBJCORRUPTED;
- goto cleanup;
- }
+ if (buffer + GIT_OID_RAWSZ > buffer_end)
+ goto corrupted;
git_oid_fromraw(&tree->oid, (const unsigned char *)buffer);
buffer += GIT_OID_RAWSZ;
@@ -150,40 +134,39 @@ static int read_tree_internal(git_tree_cache **out,
/* Parse children: */
if (tree->children_count > 0) {
unsigned int i;
- int err;
tree->children = git__malloc(tree->children_count * sizeof(git_tree_cache *));
- if (tree->children == NULL)
- goto cleanup;
+ GITERR_CHECK_ALLOC(tree->children);
for (i = 0; i < tree->children_count; ++i) {
- err = read_tree_internal(&tree->children[i], &buffer, buffer_end, tree);
-
- if (err < GIT_SUCCESS)
- goto cleanup;
+ if (read_tree_internal(&tree->children[i], &buffer, buffer_end, tree) < 0)
+ return -1;
}
}
*buffer_in = buffer;
*out = tree;
- return GIT_SUCCESS;
+ return 0;
- cleanup:
+ corrupted:
git_tree_cache_free(tree);
- return error;
+ giterr_set(GITERR_INDEX, "Corruped TREE extension in index");
+ return -1;
}
int git_tree_cache_read(git_tree_cache **tree, const char *buffer, size_t buffer_size)
{
const char *buffer_end = buffer + buffer_size;
- int error;
- error = read_tree_internal(tree, &buffer, buffer_end, NULL);
+ if (read_tree_internal(tree, &buffer, buffer_end, NULL) < 0)
+ return -1;
- if (buffer < buffer_end)
- return GIT_EOBJCORRUPTED;
+ if (buffer < buffer_end) {
+ giterr_set(GITERR_INDEX, "Corruped TREE extension in index (unexpected trailing data)");
+ return -1;
+ }
- return error;
+ return 0;
}
void git_tree_cache_free(git_tree_cache *tree)
diff --git a/src/tree-cache.h b/src/tree-cache.h
index 0d9329157..41fde997a 100644
--- a/src/tree-cache.h
+++ b/src/tree-cache.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
diff --git a/src/tree.c b/src/tree.c
index 73056273b..92b1b1e39 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -31,8 +31,8 @@ static int entry_sort_cmp(const void *a, const void *b)
const git_tree_entry *entry_b = (const git_tree_entry *)(b);
return git_path_cmp(
- entry_a->filename, entry_a->filename_len, entry_is_tree(entry_a),
- entry_b->filename, entry_b->filename_len, entry_is_tree(entry_b));
+ entry_a->filename, entry_a->filename_len, git_tree_entry__is_tree(entry_a),
+ entry_b->filename, entry_b->filename_len, git_tree_entry__is_tree(entry_b));
}
@@ -97,7 +97,7 @@ static int tree_key_search(git_vector *entries, const char *filename)
for (i = homing; i < (int)entries->length; ++i) {
entry = entries->contents[i];
- if (homing_search_cmp(&ksearch, entry) != 0)
+ if (homing_search_cmp(&ksearch, entry) < 0)
break;
if (strcmp(filename, entry->filename) == 0)
@@ -109,7 +109,7 @@ static int tree_key_search(git_vector *entries, const char *filename)
for (i = homing - 1; i >= 0; --i) {
entry = entries->contents[i];
- if (homing_search_cmp(&ksearch, entry) != 0)
+ if (homing_search_cmp(&ksearch, entry) > 0)
break;
if (strcmp(filename, entry->filename) == 0)
@@ -170,7 +170,10 @@ git_otype git_tree_entry_type(const git_tree_entry *entry)
return GIT_OBJ_BLOB;
}
-int git_tree_entry_2object(git_object **object_out, git_repository *repo, const git_tree_entry *entry)
+int git_tree_entry_to_object(
+ git_object **object_out,
+ git_repository *repo,
+ const git_tree_entry *entry)
{
assert(entry && object_out);
return git_object_lookup(object_out, repo, &entry->oid, GIT_OBJ_ANY);
@@ -195,47 +198,71 @@ const git_tree_entry *git_tree_entry_byindex(git_tree *tree, unsigned int idx)
return git_vector_get(&tree->entries, idx);
}
+int git_tree__prefix_position(git_tree *tree, const char *path)
+{
+ git_vector *entries = &tree->entries;
+ struct tree_key_search ksearch;
+ unsigned int at_pos;
+
+ ksearch.filename = path;
+ ksearch.filename_len = strlen(path);
+
+ /* Find tree entry with appropriate prefix */
+ git_vector_bsearch3(&at_pos, entries, &homing_search_cmp, &ksearch);
+
+ for (; at_pos < entries->length; ++at_pos) {
+ const git_tree_entry *entry = entries->contents[at_pos];
+ if (homing_search_cmp(&ksearch, entry) < 0)
+ break;
+ }
+
+ for (; at_pos > 0; --at_pos) {
+ const git_tree_entry *entry = entries->contents[at_pos - 1];
+ if (homing_search_cmp(&ksearch, entry) > 0)
+ break;
+ }
+
+ return at_pos;
+}
+
unsigned int git_tree_entrycount(git_tree *tree)
{
assert(tree);
return tree->entries.length;
}
-static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buffer_end)
+static int tree_error(const char *str)
{
- int error = GIT_SUCCESS;
+ giterr_set(GITERR_TREE, "%s", str);
+ return -1;
+}
- if (git_vector_init(&tree->entries, DEFAULT_TREE_SIZE, entry_sort_cmp) < GIT_SUCCESS)
- return GIT_ENOMEM;
+static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buffer_end)
+{
+ if (git_vector_init(&tree->entries, DEFAULT_TREE_SIZE, entry_sort_cmp) < 0)
+ return -1;
while (buffer < buffer_end) {
git_tree_entry *entry;
int tmp;
entry = git__calloc(1, sizeof(git_tree_entry));
- if (entry == NULL) {
- error = GIT_ENOMEM;
- break;
- }
+ GITERR_CHECK_ALLOC(entry);
- if (git_vector_insert(&tree->entries, entry) < GIT_SUCCESS)
- return GIT_ENOMEM;
+ if (git_vector_insert(&tree->entries, entry) < 0)
+ return -1;
- if (git__strtol32(&tmp, buffer, &buffer, 8) < GIT_SUCCESS ||
+ if (git__strtol32(&tmp, buffer, &buffer, 8) < 0 ||
!buffer || !valid_attributes(tmp))
- return git__throw(GIT_EOBJCORRUPTED, "Failed to parse tree. Can't parse attributes");
+ return tree_error("Failed to parse tree. Can't parse attributes");
entry->attr = tmp;
- if (*buffer++ != ' ') {
- error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse tree. Object it corrupted");
- break;
- }
+ if (*buffer++ != ' ')
+ return tree_error("Failed to parse tree. Object is corrupted");
- if (memchr(buffer, 0, buffer_end - buffer) == NULL) {
- error = git__throw(GIT_EOBJCORRUPTED, "Failed to parse tree. Object it corrupted");
- break;
- }
+ if (memchr(buffer, 0, buffer_end - buffer) == NULL)
+ return tree_error("Failed to parse tree. Object is corrupted");
entry->filename = git__strdup(buffer);
entry->filename_len = strlen(buffer);
@@ -249,7 +276,7 @@ static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buf
buffer += GIT_OID_RAWSZ;
}
- return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to parse buffer");
+ return 0;
}
int git_tree__parse(git_tree *tree, git_odb_object *obj)
@@ -280,10 +307,9 @@ static int append_entry(git_treebuilder *bld, const char *filename, const git_oi
{
git_tree_entry *entry;
- if ((entry = git__malloc(sizeof(git_tree_entry))) == NULL)
- return GIT_ENOMEM;
+ entry = git__calloc(1, sizeof(git_tree_entry));
+ GITERR_CHECK_ALLOC(entry);
- memset(entry, 0x0, sizeof(git_tree_entry));
entry->filename = git__strdup(filename);
entry->filename_len = strlen(entry->filename);
@@ -291,9 +317,9 @@ static int append_entry(git_treebuilder *bld, const char *filename, const git_oi
entry->attr = attributes;
if (git_vector_insert(&bld->entries, entry) < 0)
- return GIT_ENOMEM;
+ return -1;
- return GIT_SUCCESS;
+ return 0;
}
static int write_tree(
@@ -318,7 +344,7 @@ static int write_tree(
error = git_treebuilder_create(&bld, NULL);
if (bld == NULL) {
- return GIT_ENOMEM;
+ return -1;
}
/*
@@ -354,15 +380,13 @@ static int write_tree(
char *subdir, *last_comp;
subdir = git__strndup(entry->path, next_slash - entry->path);
- if (subdir == NULL) {
- error = GIT_ENOMEM;
- goto cleanup;
- }
+ GITERR_CHECK_ALLOC(subdir);
/* Write out the subtree */
written = write_tree(&sub_oid, repo, index, subdir, i);
if (written < 0) {
- error = git__rethrow(written, "Failed to write subtree %s", subdir);
+ tree_error("Failed to write subtree");
+ goto on_error;
} else {
i = written - 1; /* -1 because of the loop increment */
}
@@ -381,51 +405,49 @@ static int write_tree(
}
error = append_entry(bld, last_comp, &sub_oid, S_IFDIR);
git__free(subdir);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to insert dir");
- goto cleanup;
+ if (error < 0) {
+ tree_error("Failed to insert dir");
+ goto on_error;
}
} else {
error = append_entry(bld, filename, &entry->oid, entry->mode);
- if (error < GIT_SUCCESS) {
- error = git__rethrow(error, "Failed to insert file");
+ if (error < 0) {
+ tree_error("Failed to insert file");
+ goto on_error;
}
}
}
- error = git_treebuilder_write(oid, repo, bld);
- if (error < GIT_SUCCESS)
- error = git__rethrow(error, "Failed to write tree to db");
+ if (git_treebuilder_write(oid, repo, bld) < 0)
+ goto on_error;
- cleanup:
git_treebuilder_free(bld);
+ return i;
- if (error < GIT_SUCCESS)
- return error;
- else
- return i;
+on_error:
+ git_treebuilder_free(bld);
+ return -1;
}
int git_tree_create_fromindex(git_oid *oid, git_index *index)
{
+ int ret;
git_repository *repo;
- int error;
repo = (git_repository *)GIT_REFCOUNT_OWNER(index);
if (repo == NULL)
- return git__throw(GIT_EBAREINDEX,
- "Failed to create tree. "
- "The index file is not backed up by an existing repository");
+ return tree_error("Failed to create tree. "
+ "The index file is not backed up by an existing repository");
if (index->tree != NULL && index->tree->entries >= 0) {
git_oid_cpy(oid, &index->tree->oid);
- return GIT_SUCCESS;
+ return 0;
}
/* The tree cache didn't help us */
- error = write_tree(oid, repo, index, "", 0);
- return (error < GIT_SUCCESS) ? git__rethrow(error, "Failed to create tree") : GIT_SUCCESS;
+ ret = write_tree(oid, repo, index, "", 0);
+ return ret < 0 ? ret : 0;
}
static void sort_entries(git_treebuilder *bld)
@@ -441,30 +463,29 @@ int git_treebuilder_create(git_treebuilder **builder_p, const git_tree *source)
assert(builder_p);
bld = git__calloc(1, sizeof(git_treebuilder));
- if (bld == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(bld);
if (source != NULL)
source_entries = source->entries.length;
- if (git_vector_init(&bld->entries, source_entries, entry_sort_cmp) < GIT_SUCCESS) {
- git__free(bld);
- return GIT_ENOMEM;
- }
+ if (git_vector_init(&bld->entries, source_entries, entry_sort_cmp) < 0)
+ goto on_error;
if (source != NULL) {
for (i = 0; i < source->entries.length; ++i) {
git_tree_entry *entry_src = source->entries.contents[i];
- if (append_entry(bld, entry_src->filename, &entry_src->oid, entry_src->attr) < 0) {
- git_treebuilder_free(bld);
- return GIT_ENOMEM;
- }
+ if (append_entry(bld, entry_src->filename, &entry_src->oid, entry_src->attr) < 0)
+ goto on_error;
}
}
*builder_p = bld;
- return GIT_SUCCESS;
+ return 0;
+
+on_error:
+ git_treebuilder_free(bld);
+ return -1;
}
int git_treebuilder_insert(git_tree_entry **entry_out, git_treebuilder *bld, const char *filename, const git_oid *id, unsigned int attributes)
@@ -475,10 +496,10 @@ int git_treebuilder_insert(git_tree_entry **entry_out, git_treebuilder *bld, con
assert(bld && id && filename);
if (!valid_attributes(attributes))
- return git__throw(GIT_ERROR, "Failed to insert entry. Invalid attributes");
+ return tree_error("Failed to insert entry. Invalid attributes");
if (!valid_entry_name(filename))
- return git__throw(GIT_ERROR, "Failed to insert entry. Invalid name for a tree entry");
+ return tree_error("Failed to insert entry. Invalid name for a tree entry");
pos = tree_key_search(&bld->entries, filename);
@@ -487,10 +508,9 @@ int git_treebuilder_insert(git_tree_entry **entry_out, git_treebuilder *bld, con
if (entry->removed)
entry->removed = 0;
} else {
- if ((entry = git__malloc(sizeof(git_tree_entry))) == NULL)
- return GIT_ENOMEM;
+ entry = git__calloc(1, sizeof(git_tree_entry));
+ GITERR_CHECK_ALLOC(entry);
- memset(entry, 0x0, sizeof(git_tree_entry));
entry->filename = git__strdup(filename);
entry->filename_len = strlen(entry->filename);
}
@@ -500,13 +520,13 @@ int git_treebuilder_insert(git_tree_entry **entry_out, git_treebuilder *bld, con
if (pos == GIT_ENOTFOUND) {
if (git_vector_insert(&bld->entries, entry) < 0)
- return GIT_ENOMEM;
+ return -1;
}
if (entry_out != NULL)
*entry_out = entry;
- return GIT_SUCCESS;
+ return 0;
}
static git_tree_entry *treebuilder_get(git_treebuilder *bld, const char *filename)
@@ -537,16 +557,15 @@ int git_treebuilder_remove(git_treebuilder *bld, const char *filename)
git_tree_entry *remove_ptr = treebuilder_get(bld, filename);
if (remove_ptr == NULL || remove_ptr->removed)
- return git__throw(GIT_ENOTFOUND, "Failed to remove entry. File isn't in the tree");
+ return tree_error("Failed to remove entry. File isn't in the tree");
remove_ptr->removed = 1;
- return GIT_SUCCESS;
+ return 0;
}
int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *bld)
{
unsigned int i;
- int error;
git_buf tree = GIT_BUF_INIT;
git_odb *odb;
@@ -568,21 +587,22 @@ int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *b
git_buf_put(&tree, (char *)entry->oid.id, GIT_OID_RAWSZ);
}
- if ((error = git_buf_lasterror(&tree)) < GIT_SUCCESS) {
- git_buf_free(&tree);
- return git__rethrow(error, "Not enough memory to build the tree data");
- }
+ if (git_buf_oom(&tree))
+ goto on_error;
- error = git_repository_odb__weakptr(&odb, repo);
- if (error < GIT_SUCCESS) {
- git_buf_free(&tree);
- return error;
- }
+ if (git_repository_odb__weakptr(&odb, repo) < 0)
+ goto on_error;
+
+
+ if (git_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE) < 0)
+ goto on_error;
- error = git_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE);
git_buf_free(&tree);
+ return 0;
- return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write tree");
+on_error:
+ git_buf_free(&tree);
+ return -1;
}
void git_treebuilder_filter(git_treebuilder *bld, int (*filter)(const git_tree_entry *, void *), void *payload)
@@ -623,16 +643,18 @@ static int tree_frompath(
git_tree **parent_out,
git_tree *root,
git_buf *treeentry_path,
- int offset)
+ size_t offset)
{
char *slash_pos = NULL;
const git_tree_entry* entry;
- int error = GIT_SUCCESS;
+ int error = 0;
git_tree *subtree;
- if (!*(treeentry_path->ptr + offset))
- return git__rethrow(GIT_EINVALIDPATH,
+ if (!*(treeentry_path->ptr + offset)) {
+ giterr_set(GITERR_INVALID,
"Invalid relative path to a tree entry '%s'.", treeentry_path->ptr);
+ return -1;
+ }
slash_pos = (char *)strchr(treeentry_path->ptr + offset, '/');
@@ -643,9 +665,11 @@ static int tree_frompath(
git_object_id((const git_object *)root)
);
- if (slash_pos == treeentry_path->ptr + offset)
- return git__rethrow(GIT_EINVALIDPATH,
+ if (slash_pos == treeentry_path->ptr + offset) {
+ giterr_set(GITERR_INVALID,
"Invalid relative path to a tree entry '%s'.", treeentry_path->ptr);
+ return -1;
+ }
*slash_pos = '\0';
@@ -654,14 +678,15 @@ static int tree_frompath(
if (slash_pos != NULL)
*slash_pos = '/';
- if (entry == NULL)
- return git__rethrow(GIT_ENOTFOUND,
+ if (entry == NULL) {
+ giterr_set(GITERR_TREE,
"No tree entry can be found from "
"the given tree and relative path '%s'.", treeentry_path->ptr);
+ return GIT_ENOTFOUND;
+ }
- error = git_tree_lookup(&subtree, root->object.repo, &entry->oid);
- if (error < GIT_SUCCESS)
+ if (git_tree_lookup(&subtree, root->object.repo, &entry->oid) < 0)
return error;
error = tree_frompath(
@@ -685,7 +710,7 @@ int git_tree_get_subtree(
assert(subtree && root && subtree_path);
- if ((error = git_buf_sets(&buffer, subtree_path)) == GIT_SUCCESS)
+ if ((error = git_buf_sets(&buffer, subtree_path)) == 0)
error = tree_frompath(subtree, root, &buffer, 0);
git_buf_free(&buffer);
@@ -699,7 +724,7 @@ static int tree_walk_post(
git_buf *path,
void *payload)
{
- int error = GIT_SUCCESS;
+ int error = 0;
unsigned int i;
for (i = 0; i < tree->entries.length; ++i) {
@@ -708,9 +733,9 @@ static int tree_walk_post(
if (callback(path->ptr, entry, payload) < 0)
continue;
- if (entry_is_tree(entry)) {
+ if (git_tree_entry__is_tree(entry)) {
git_tree *subtree;
- size_t path_len = path->size;
+ size_t path_len = git_buf_len(path);
if ((error = git_tree_lookup(
&subtree, tree->object.repo, &entry->oid)) < 0)
@@ -719,24 +744,24 @@ static int tree_walk_post(
/* append the next entry to the path */
git_buf_puts(path, entry->filename);
git_buf_putc(path, '/');
- if ((error = git_buf_lasterror(path)) < GIT_SUCCESS)
- break;
- error = tree_walk_post(subtree, callback, path, payload);
- if (error < GIT_SUCCESS)
- break;
+ if (git_buf_oom(path))
+ return -1;
+
+ if (tree_walk_post(subtree, callback, path, payload) < 0)
+ return -1;
git_buf_truncate(path, path_len);
git_tree_free(subtree);
}
}
- return error;
+ return 0;
}
int git_tree_walk(git_tree *tree, git_treewalk_cb callback, int mode, void *payload)
{
- int error = GIT_SUCCESS;
+ int error = 0;
git_buf root_path = GIT_BUF_INIT;
switch (mode) {
@@ -745,14 +770,12 @@ int git_tree_walk(git_tree *tree, git_treewalk_cb callback, int mode, void *payl
break;
case GIT_TREEWALK_PRE:
- error = git__throw(GIT_ENOTIMPLEMENTED,
- "Preorder tree walking is still not implemented");
- break;
+ tree_error("Preorder tree walking is still not implemented");
+ return -1;
default:
- error = git__throw(GIT_EINVALIDARGS,
- "Invalid walking mode for tree walk");
- break;
+ giterr_set(GITERR_INVALID, "Invalid walking mode for tree walk");
+ return -1;
}
git_buf_free(&root_path);
@@ -760,280 +783,3 @@ int git_tree_walk(git_tree *tree, git_treewalk_cb callback, int mode, void *payl
return error;
}
-static int tree_entry_cmp(const git_tree_entry *a, const git_tree_entry *b)
-{
- int ret;
-
- ret = a->attr - b->attr;
- if (ret != 0)
- return ret;
-
- return git_oid_cmp(&a->oid, &b->oid);
-}
-
-static void mark_del(git_tree_diff_data *diff, git_tree_entry *entry)
-{
- diff->old_attr = entry->attr;
- git_oid_cpy(&diff->old_oid, &entry->oid);
- diff->path = entry->filename;
- diff->status |= GIT_STATUS_DELETED;
-}
-
-static void mark_add(git_tree_diff_data *diff, git_tree_entry *entry)
-{
- diff->new_attr = entry->attr;
- git_oid_cpy(&diff->new_oid, &entry->oid);
- diff->path = entry->filename;
- diff->status |= GIT_STATUS_ADDED;
-}
-
-static int signal_additions(git_tree *tree, int start, int end, git_tree_diff_cb cb, void *data)
-{
- git_tree_diff_data diff;
- git_tree_entry *entry;
- int i, error;
-
- if (end < 0)
- end = git_tree_entrycount(tree);
-
- for (i = start; i < end; ++i) {
- memset(&diff, 0x0, sizeof(git_tree_diff_data));
- entry = git_vector_get(&tree->entries, i);
- mark_add(&diff, entry);
-
- error = cb(&diff, data);
- if (error < GIT_SUCCESS)
- return error;
- }
-
- return GIT_SUCCESS;
-}
-
-static int signal_addition(git_tree_entry *entry, git_tree_diff_cb cb, void *data)
-{
- git_tree_diff_data diff;
-
- memset(&diff, 0x0, sizeof(git_tree_diff_data));
-
- mark_add(&diff, entry);
-
- return cb(&diff, data);
-}
-
-static int signal_deletions(git_tree *tree, int start, int end, git_tree_diff_cb cb, void *data)
-{
- git_tree_diff_data diff;
- git_tree_entry *entry;
- int i, error;
-
- if (end < 0)
- end = git_tree_entrycount(tree);
-
- for (i = start; i < end; ++i) {
- memset(&diff, 0x0, sizeof(git_tree_diff_data));
- entry = git_vector_get(&tree->entries, i);
- mark_del(&diff, entry);
-
- error = cb(&diff, data);
- if (error < GIT_SUCCESS)
- return error;
- }
-
- return GIT_SUCCESS;
-}
-
-static int signal_deletion(git_tree_entry *entry, git_tree_diff_cb cb, void *data)
-{
- git_tree_diff_data diff;
-
- memset(&diff, 0x0, sizeof(git_tree_diff_data));
-
- mark_del(&diff, entry);
-
- return cb(&diff, data);
-}
-
-static int signal_modification(git_tree_entry *a, git_tree_entry *b,
- git_tree_diff_cb cb, void *data)
-{
- git_tree_diff_data diff;
-
- memset(&diff, 0x0, sizeof(git_tree_diff_data));
-
- mark_del(&diff, a);
- mark_add(&diff, b);
-
- return cb(&diff, data);
-}
-
-int git_tree_diff(git_tree *a, git_tree *b, git_tree_diff_cb cb, void *data)
-{
- unsigned int i_a = 0, i_b = 0; /* Counters for trees a and b */
- git_tree_entry *entry_a = NULL, *entry_b = NULL;
- git_tree_diff_data diff;
- int error = GIT_SUCCESS, cmp;
-
- while (1) {
- entry_a = a == NULL ? NULL : git_vector_get(&a->entries, i_a);
- entry_b = b == NULL ? NULL : git_vector_get(&b->entries, i_b);
-
- if (!entry_a && !entry_b)
- goto exit;
-
- memset(&diff, 0x0, sizeof(git_tree_diff_data));
-
- /*
- * We've run out of tree on one side so the rest of the
- * entries on the tree with remaining entries are all
- * deletions or additions.
- */
- if (entry_a && !entry_b)
- return signal_deletions(a, i_a, -1, cb, data);
- if (!entry_a && entry_b)
- return signal_additions(b, i_b, -1, cb, data);
-
- /*
- * Both trees are sorted with git's almost-alphabetical
- * sorting, so a comparison value < 0 means the entry was
- * deleted in the right tree. > 0 means the entry was added.
- */
- cmp = entry_sort_cmp(entry_a, entry_b);
-
- if (cmp == 0) {
- i_a++;
- i_b++;
-
- /* If everything's the same, jump to next pair */
- if (!tree_entry_cmp(entry_a, entry_b))
- continue;
-
- /* If they're not both dirs or both files, it's add + del */
- if (S_ISDIR(entry_a->attr) != S_ISDIR(entry_b->attr)) {
- if ((error = signal_addition(entry_a, cb, data)) < 0)
- goto exit;
- if ((error = signal_deletion(entry_b, cb, data)) < 0)
- goto exit;
- }
-
- /* Otherwise consider it a modification */
- if ((error = signal_modification(entry_a, entry_b, cb, data)) < 0)
- goto exit;
-
- } else if (cmp < 0) {
- i_a++;
- if ((error = signal_deletion(entry_a, cb, data)) < 0)
- goto exit;
- } else if (cmp > 0) {
- i_b++;
- if ((error = signal_addition(entry_b, cb, data)) < 0)
- goto exit;
- }
- }
-
-exit:
- return error;
-}
-
-struct diff_index_cbdata {
- git_index *index;
- unsigned int i;
- git_tree_diff_cb cb;
- void *data;
-};
-
-static int cmp_tentry_ientry(git_tree_entry *tentry, git_index_entry *ientry)
-{
- int cmp;
-
- cmp = tentry->attr - ientry->mode;
- if (cmp != 0)
- return cmp;
-
- return git_oid_cmp(&tentry->oid, &ientry->oid);
-}
-
-static void make_tentry(git_tree_entry *tentry, git_index_entry *ientry)
-{
- char *last_slash;
-
- memset(tentry, 0x0, sizeof(git_tree_entry));
- tentry->attr = ientry->mode;
-
- last_slash = strrchr(ientry->path, '/');
- if (last_slash)
- last_slash++;
- else
- last_slash = ientry->path;
- tentry->filename = last_slash;
-
- git_oid_cpy(&tentry->oid, &ientry->oid);
- tentry->filename_len = strlen(tentry->filename);
-}
-
-static int diff_index_cb(const char *root, git_tree_entry *tentry, void *data)
-{
- struct diff_index_cbdata *cbdata = (struct diff_index_cbdata *) data;
- git_index_entry *ientry = git_index_get(cbdata->index, cbdata->i);
- git_tree_entry fake_entry;
- git_buf fn_buf = GIT_BUF_INIT;
- int cmp, error = GIT_SUCCESS;
-
- if (entry_is_tree(tentry))
- return GIT_SUCCESS;
-
- git_buf_puts(&fn_buf, root);
- git_buf_puts(&fn_buf, tentry->filename);
-
- if (!ientry) {
- error = signal_deletion(tentry, cbdata->cb, cbdata->data);
- git_buf_free(&fn_buf);
- goto exit;
- }
-
- /* Like with 'git diff-index', the index is the right side*/
- cmp = strcmp(git_buf_cstr(&fn_buf), ientry->path);
- git_buf_free(&fn_buf);
- if (cmp == 0) {
- cbdata->i++;
- if (!cmp_tentry_ientry(tentry, ientry))
- goto exit;
- /* modification */
- make_tentry(&fake_entry, ientry);
- if ((error = signal_modification(tentry, &fake_entry, cbdata->cb, cbdata->data)) < 0)
- goto exit;
- } else if (cmp < 0) {
- /* deletion */
- memcpy(&fake_entry, tentry, sizeof(git_tree_entry));
- if ((error = signal_deletion(tentry, cbdata->cb, cbdata->data)) < 0)
- goto exit;
- } else {
- /* addition */
- cbdata->i++;
- make_tentry(&fake_entry, ientry);
- if ((error = signal_addition(&fake_entry, cbdata->cb, cbdata->data)) < 0)
- goto exit;
- /*
- * The index has an addition. This means that we need to use
- * the next entry in the index without advancing the tree
- * walker, so call ourselves with the same tree state.
- */
- if ((error = diff_index_cb(root, tentry, data)) < 0)
- goto exit;
- }
-
- exit:
- return error;
-}
-
-int git_tree_diff_index_recursive(git_tree *tree, git_index *index, git_tree_diff_cb cb, void *data)
-{
- struct diff_index_cbdata cbdata;
- git_buf dummy_path = GIT_BUF_INIT;
-
- cbdata.index = index;
- cbdata.i = 0;
- cbdata.cb = cb;
- cbdata.data = data;
-
- return tree_walk_post(tree, diff_index_cb, &dummy_path, &cbdata);
-}
diff --git a/src/tree.h b/src/tree.h
index f993cea9f..498a90d66 100644
--- a/src/tree.h
+++ b/src/tree.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -30,12 +30,22 @@ struct git_treebuilder {
};
-GIT_INLINE(unsigned int) entry_is_tree(const struct git_tree_entry *e)
+GIT_INLINE(bool) git_tree_entry__is_tree(const struct git_tree_entry *e)
{
- return e->attr & 040000;
+ return (S_ISDIR(e->attr) && !S_ISGITLINK(e->attr));
}
void git_tree__free(git_tree *tree);
int git_tree__parse(git_tree *tree, git_odb_object *obj);
+/**
+ * Lookup the first position in the tree with a given prefix.
+ *
+ * @param tree a previously loaded tree.
+ * @param prefix the beginning of a path to find in the tree.
+ * @return index of the first item at or after the given prefix.
+ */
+int git_tree__prefix_position(git_tree *tree, const char *prefix);
+
+
#endif
diff --git a/src/tsort.c b/src/tsort.c
index df230b59d..f54c21e50 100644
--- a/src/tsort.c
+++ b/src/tsort.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -30,8 +30,10 @@ static int binsearch(void **dst, const void *x, size_t size, cmp_ptr_t cmp)
int l, c, r;
void *lx, *cx;
+ assert(size > 0);
+
l = 0;
- r = size - 1;
+ r = (int)size - 1;
c = r >> 1;
lx = dst[l];
@@ -84,7 +86,7 @@ static void bisort(void **dst, size_t start, size_t size, cmp_ptr_t cmp)
/* Else we need to find the right place, shift everything over, and squeeze in */
x = dst[i];
location = binsearch(dst, x, i, cmp);
- for (j = i - 1; j >= location; j--) {
+ for (j = (int)i - 1; j >= location; j--) {
dst[j + 1] = dst[j];
}
dst[location] = x;
@@ -104,7 +106,7 @@ struct tsort_store {
void **storage;
};
-static void reverse_elements(void **dst, int start, int end)
+static void reverse_elements(void **dst, ssize_t start, ssize_t end)
{
while (start < end) {
void *tmp = dst[start];
@@ -116,7 +118,7 @@ static void reverse_elements(void **dst, int start, int end)
}
}
-static int count_run(void **dst, ssize_t start, ssize_t size, struct tsort_store *store)
+static ssize_t count_run(void **dst, ssize_t start, ssize_t size, struct tsort_store *store)
{
ssize_t curr = start + 2;
@@ -148,7 +150,7 @@ static int count_run(void **dst, ssize_t start, ssize_t size, struct tsort_store
}
}
-static int compute_minrun(size_t n)
+static size_t compute_minrun(size_t n)
{
int r = 0;
while (n >= 64) {
@@ -158,19 +160,19 @@ static int compute_minrun(size_t n)
return n + r;
}
-static int check_invariant(struct tsort_run *stack, int stack_curr)
+static int check_invariant(struct tsort_run *stack, ssize_t stack_curr)
{
if (stack_curr < 2)
return 1;
else if (stack_curr == 2) {
- const int A = stack[stack_curr - 2].length;
- const int B = stack[stack_curr - 1].length;
+ const ssize_t A = stack[stack_curr - 2].length;
+ const ssize_t B = stack[stack_curr - 1].length;
return (A > B);
} else {
- const int A = stack[stack_curr - 3].length;
- const int B = stack[stack_curr - 2].length;
- const int C = stack[stack_curr - 1].length;
+ const ssize_t A = stack[stack_curr - 3].length;
+ const ssize_t B = stack[stack_curr - 2].length;
+ const ssize_t C = stack[stack_curr - 1].length;
return !((A <= B + C) || (B <= C));
}
}
@@ -195,7 +197,7 @@ static int resize(struct tsort_store *store, size_t new_size)
return 0;
}
-static void merge(void **dst, const struct tsort_run *stack, int stack_curr, struct tsort_store *store)
+static void merge(void **dst, const struct tsort_run *stack, ssize_t stack_curr, struct tsort_store *store)
{
const ssize_t A = stack[stack_curr - 2].length;
const ssize_t B = stack[stack_curr - 1].length;
@@ -343,7 +345,7 @@ void git__tsort(void **dst, size_t size, cmp_ptr_t cmp)
}
/* compute the minimum run length */
- minrun = compute_minrun(size);
+ minrun = (ssize_t)compute_minrun(size);
/* temporary storage for merges */
store->alloc = 0;
diff --git a/src/unix/map.c b/src/unix/map.c
index 2fb4be571..772f4e247 100644
--- a/src/unix/map.c
+++ b/src/unix/map.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -17,12 +17,7 @@ int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offs
int mprot = 0;
int mflag = 0;
- assert((out != NULL) && (len > 0));
-
- if ((out == NULL) || (len == 0)) {
- errno = EINVAL;
- return git__throw(GIT_ERROR, "Failed to mmap. No map or zero length");
- }
+ GIT_MMAP_VALIDATE(out, len, prot, flags);
out->data = NULL;
out->len = 0;
@@ -31,39 +26,28 @@ int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offs
mprot = PROT_WRITE;
else if (prot & GIT_PROT_READ)
mprot = PROT_READ;
- else {
- errno = EINVAL;
- return git__throw(GIT_ERROR, "Failed to mmap. Invalid protection parameters");
- }
if ((flags & GIT_MAP_TYPE) == GIT_MAP_SHARED)
mflag = MAP_SHARED;
else if ((flags & GIT_MAP_TYPE) == GIT_MAP_PRIVATE)
mflag = MAP_PRIVATE;
- if (flags & GIT_MAP_FIXED) {
- errno = EINVAL;
- return git__throw(GIT_ERROR, "Failed to mmap. FIXED not set");
+ out->data = mmap(NULL, len, mprot, mflag, fd, offset);
+ if (!out->data || out->data == MAP_FAILED) {
+ giterr_set(GITERR_OS, "Failed to mmap. Could not write data");
+ return -1;
}
- out->data = mmap(NULL, len, mprot, mflag, fd, offset);
- if (!out->data || out->data == MAP_FAILED)
- return git__throw(GIT_EOSERR, "Failed to mmap. Could not write data");
out->len = len;
- return GIT_SUCCESS;
+ return 0;
}
int p_munmap(git_map *map)
{
assert(map != NULL);
-
- if (!map)
- return git__throw(GIT_ERROR, "Failed to munmap. Map does not exist");
-
munmap(map->data, map->len);
-
- return GIT_SUCCESS;
+ return 0;
}
#endif
diff --git a/src/unix/posix.h b/src/unix/posix.h
index 881e651f4..48b492941 100644
--- a/src/unix/posix.h
+++ b/src/unix/posix.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -7,7 +7,14 @@
#ifndef INCLUDE_posix__w32_h__
#define INCLUDE_posix__w32_h__
-#include <fnmatch.h>
+#ifndef __sun
+# include <fnmatch.h>
+# define p_fnmatch(p, s, f) fnmatch(p, s, f)
+#else
+# include "compat/fnmatch.h"
+#endif
+
+#include <stdio.h>
#define p_lstat(p,b) lstat(p,b)
#define p_readlink(a, b, c) readlink(a, b, c)
@@ -16,7 +23,6 @@
#define p_mkdir(p,m) mkdir(p, m)
#define p_fsync(fd) fsync(fd)
#define p_realpath(p, po) realpath(p, po)
-#define p_fnmatch(p, s, f) fnmatch(p, s, f)
#define p_vsnprintf(b, c, f, a) vsnprintf(b, c, f, a)
#define p_snprintf(b, c, f, ...) snprintf(b, c, f, __VA_ARGS__)
#define p_mkstemp(p) mkstemp(p)
diff --git a/src/util.c b/src/util.c
index 1ca9d850c..ce770203a 100644
--- a/src/util.c
+++ b/src/util.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -31,19 +31,33 @@ void git_strarray_free(git_strarray *array)
git__free(array->strings);
}
-int git__fnmatch(const char *pattern, const char *name, int flags)
+int git_strarray_copy(git_strarray *tgt, const git_strarray *src)
{
- int ret;
-
- ret = p_fnmatch(pattern, name, flags);
- switch (ret) {
- case 0:
- return GIT_SUCCESS;
- case FNM_NOMATCH:
- return GIT_ENOMATCH;
- default:
- return git__throw(GIT_EOSERR, "Error trying to match path");
+ size_t i;
+
+ assert(tgt && src);
+
+ memset(tgt, 0, sizeof(*tgt));
+
+ if (!src->count)
+ return 0;
+
+ tgt->strings = git__calloc(src->count, sizeof(char *));
+ GITERR_CHECK_ALLOC(tgt->strings);
+
+ for (i = 0; i < src->count; ++i) {
+ tgt->strings[tgt->count] = git__strdup(src->strings[i]);
+
+ if (!tgt->strings[tgt->count]) {
+ git_strarray_free(tgt);
+ memset(tgt, 0, sizeof(*tgt));
+ return -1;
+ }
+
+ tgt->count++;
}
+
+ return 0;
}
int git__strtol64(int64_t *result, const char *nptr, const char **endptr, int base)
@@ -61,7 +75,7 @@ int git__strtol64(int64_t *result, const char *nptr, const char **endptr, int ba
/*
* White space
*/
- while (isspace(*p))
+ while (git__isspace(*p))
p++;
/*
@@ -111,34 +125,40 @@ int git__strtol64(int64_t *result, const char *nptr, const char **endptr, int ba
}
Return:
- if (ndig == 0)
- return git__throw(GIT_ENOTNUM, "Failed to convert string to long. Not a number");
+ if (ndig == 0) {
+ giterr_set(GITERR_INVALID, "Failed to convert string to long. Not a number");
+ return -1;
+ }
if (endptr)
*endptr = p;
- if (ovfl)
- return git__throw(GIT_EOVERFLOW, "Failed to convert string to long. Overflow error");
+ if (ovfl) {
+ giterr_set(GITERR_INVALID, "Failed to convert string to long. Overflow error");
+ return -1;
+ }
*result = neg ? -n : n;
- return GIT_SUCCESS;
+ return 0;
}
int git__strtol32(int32_t *result, const char *nptr, const char **endptr, int base)
{
- int error = GIT_SUCCESS;
+ int error;
int32_t tmp_int;
int64_t tmp_long;
- if ((error = git__strtol64(&tmp_long, nptr, endptr, base)) < GIT_SUCCESS)
+ if ((error = git__strtol64(&tmp_long, nptr, endptr, base)) < 0)
return error;
tmp_int = tmp_long & 0xFFFFFFFF;
- if (tmp_int != tmp_long)
- return git__throw(GIT_EOVERFLOW, "Failed to convert. '%s' is too large", nptr);
+ if (tmp_int != tmp_long) {
+ giterr_set(GITERR_INVALID, "Failed to convert. '%s' is too large", nptr);
+ return -1;
+ }
*result = tmp_int;
-
+
return error;
}
@@ -355,23 +375,27 @@ int git__bsearch(
int (*compare)(const void *, const void *),
size_t *position)
{
- int lim, cmp;
+ unsigned int lim;
+ int cmp = -1;
void **part, **base = array;
- for (lim = array_len; lim != 0; lim >>= 1) {
+ for (lim = (unsigned int)array_len; lim != 0; lim >>= 1) {
part = base + (lim >> 1);
cmp = (*compare)(key, *part);
if (cmp == 0) {
- *position = (part - array);
- return GIT_SUCCESS;
- } else if (cmp > 0) { /* key > p; take right partition */
+ base = part;
+ break;
+ }
+ if (cmp > 0) { /* key > p; take right partition */
base = part + 1;
lim--;
} /* else take left partition */
}
- *position = (base - array);
- return GIT_ENOTFOUND;
+ if (position)
+ *position = (base - array);
+
+ return (cmp == 0) ? 0 : -1;
}
/**
@@ -387,3 +411,27 @@ int git__strcmp_cb(const void *a, const void *b)
return strcmp(stra, strb);
}
+
+int git__parse_bool(int *out, const char *value)
+{
+ /* A missing value means true */
+ if (value == NULL) {
+ *out = 1;
+ return 0;
+ }
+
+ if (!strcasecmp(value, "true") ||
+ !strcasecmp(value, "yes") ||
+ !strcasecmp(value, "on")) {
+ *out = 1;
+ return 0;
+ }
+ if (!strcasecmp(value, "false") ||
+ !strcasecmp(value, "no") ||
+ !strcasecmp(value, "off")) {
+ *out = 0;
+ return 0;
+ }
+
+ return -1;
+}
diff --git a/src/util.h b/src/util.h
index 6c929cf0a..c6851ac7e 100644
--- a/src/util.h
+++ b/src/util.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -22,24 +22,21 @@
GIT_INLINE(void *) git__malloc(size_t len)
{
void *ptr = malloc(len);
- if (!ptr)
- git__throw(GIT_ENOMEM, "Out of memory. Failed to allocate %d bytes.", (int)len);
+ if (!ptr) giterr_set_oom();
return ptr;
}
GIT_INLINE(void *) git__calloc(size_t nelem, size_t elsize)
{
void *ptr = calloc(nelem, elsize);
- if (!ptr)
- git__throw(GIT_ENOMEM, "Out of memory. Failed to allocate %d bytes.", (int)elsize);
+ if (!ptr) giterr_set_oom();
return ptr;
}
GIT_INLINE(char *) git__strdup(const char *str)
{
char *ptr = strdup(str);
- if (!ptr)
- git__throw(GIT_ENOMEM, "Out of memory. Failed to duplicate string");
+ if (!ptr) giterr_set_oom();
return ptr;
}
@@ -54,7 +51,7 @@ GIT_INLINE(char *) git__strndup(const char *str, size_t n)
ptr = (char*)malloc(length + 1);
if (!ptr) {
- git__throw(GIT_ENOMEM, "Out of memory. Failed to duplicate string");
+ giterr_set_oom();
return NULL;
}
@@ -67,8 +64,7 @@ GIT_INLINE(char *) git__strndup(const char *str, size_t n)
GIT_INLINE(void *) git__realloc(void *ptr, size_t size)
{
void *new_ptr = realloc(ptr, size);
- if (!new_ptr)
- git__throw(GIT_ENOMEM, "Out of memory. Failed to allocate %d bytes.", (int)size);
+ if (!new_ptr) giterr_set_oom();
return new_ptr;
}
@@ -109,10 +105,13 @@ GIT_INLINE(const char *) git__next_line(const char *s)
return s;
}
-extern int git__fnmatch(const char *pattern, const char *name, int flags);
-
extern void git__tsort(void **dst, size_t size, int (*cmp)(const void *, const void *));
+/**
+ * @param position If non-NULL, this will be set to the position where the
+ * element is or would be inserted if not found.
+ * @return pos (>=0) if found or -1 if not found
+ */
extern int git__bsearch(
void **array,
size_t array_len,
@@ -169,4 +168,59 @@ GIT_INLINE(int) git__fromhex(char h)
return from_hex[(unsigned char) h];
}
+GIT_INLINE(int) git__ishex(const char *str)
+{
+ unsigned i;
+ for (i=0; i<strlen(str); i++)
+ if (git__fromhex(str[i]) < 0)
+ return 0;
+ return 1;
+}
+
+GIT_INLINE(size_t) git__size_t_bitmask(size_t v)
+{
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+
+ return v;
+}
+
+GIT_INLINE(size_t) git__size_t_powerof2(size_t v)
+{
+ return git__size_t_bitmask(v) + 1;
+}
+
+GIT_INLINE(bool) git__isupper(int c)
+{
+ return (c >= 'A' && c <= 'Z');
+}
+
+GIT_INLINE(bool) git__isalpha(int c)
+{
+ return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'));
+}
+
+GIT_INLINE(bool) git__isspace(int c)
+{
+ return (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r' || c == '\v');
+}
+
+GIT_INLINE(bool) git__iswildcard(int c)
+{
+ return (c == '*' || c == '?' || c == '[');
+}
+
+/*
+ * Parse a string value as a boolean, just like Core Git
+ * does.
+ *
+ * Valid values for true are: 'true', 'yes', 'on'
+ * Valid values for false are: 'false', 'no', 'off'
+ */
+extern int git__parse_bool(int *out, const char *value);
+
#endif /* INCLUDE_util_h__ */
diff --git a/src/vector.c b/src/vector.c
index 593d037d4..6f9aacccf 100644
--- a/src/vector.c
+++ b/src/vector.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -10,7 +10,7 @@
#include "vector.h"
static const double resize_factor = 1.75;
-static const size_t minimum_size = 8;
+static const unsigned int minimum_size = 8;
static int resize_vector(git_vector *v)
{
@@ -19,13 +19,11 @@ static int resize_vector(git_vector *v)
v->_alloc_size = minimum_size;
v->contents = git__realloc(v->contents, v->_alloc_size * sizeof(void *));
- if (v->contents == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(v->contents);
- return GIT_SUCCESS;
+ return 0;
}
-
void git_vector_free(git_vector *v)
{
assert(v);
@@ -53,30 +51,29 @@ int git_vector_init(git_vector *v, unsigned int initial_size, git_vector_cmp cmp
v->sorted = 1;
v->contents = git__malloc(v->_alloc_size * sizeof(void *));
- if (v->contents == NULL)
- return GIT_ENOMEM;
+ GITERR_CHECK_ALLOC(v->contents);
- return GIT_SUCCESS;
+ return 0;
}
int git_vector_insert(git_vector *v, void *element)
{
assert(v);
- if (v->length >= v->_alloc_size) {
- if (resize_vector(v) < 0)
- return GIT_ENOMEM;
- }
+ if (v->length >= v->_alloc_size &&
+ resize_vector(v) < 0)
+ return -1;
v->contents[v->length++] = element;
v->sorted = 0;
- return GIT_SUCCESS;
+ return 0;
}
-int git_vector_insert_sorted(git_vector *v, void *element, int (*on_dup)(void **old, void *new))
+int git_vector_insert_sorted(
+ git_vector *v, void *element, int (*on_dup)(void **old, void *new))
{
- int error = GIT_SUCCESS;
+ int result;
size_t pos;
assert(v && v->_cmp);
@@ -84,22 +81,18 @@ int git_vector_insert_sorted(git_vector *v, void *element, int (*on_dup)(void **
if (!v->sorted)
git_vector_sort(v);
- if (v->length >= v->_alloc_size) {
- if (resize_vector(v) < 0)
- return GIT_ENOMEM;
- }
+ if (v->length >= v->_alloc_size &&
+ resize_vector(v) < 0)
+ return -1;
- error = git__bsearch(v->contents, v->length, element, v->_cmp, &pos);
-
- /* If we found the element and have a duplicate handler callback,
- * invoke it. If it returns an error, then cancel insert, otherwise
+ /* If we find the element and have a duplicate handler callback,
+ * invoke it. If it returns non-zero, then cancel insert, otherwise
* proceed with normal insert.
*/
- if (error == GIT_SUCCESS && on_dup != NULL) {
- error = on_dup(&v->contents[pos], element);
- if (error != GIT_SUCCESS)
- return error;
- }
+ if (git__bsearch(v->contents, v->length, element, v->_cmp, &pos) >= 0 &&
+ on_dup != NULL &&
+ (result = on_dup(&v->contents[pos], element)) < 0)
+ return result;
/* shift elements to the right */
if (pos < v->length) {
@@ -109,8 +102,7 @@ int git_vector_insert_sorted(git_vector *v, void *element, int (*on_dup)(void **
v->contents[pos] = element;
v->length++;
-
- return GIT_SUCCESS;
+ return 0;
}
void git_vector_sort(git_vector *v)
@@ -124,26 +116,32 @@ void git_vector_sort(git_vector *v)
v->sorted = 1;
}
-int git_vector_bsearch2(git_vector *v, git_vector_cmp key_lookup, const void *key)
+int git_vector_bsearch3(
+ unsigned int *at_pos,
+ git_vector *v,
+ git_vector_cmp key_lookup,
+ const void *key)
{
+ int rval;
size_t pos;
assert(v && key && key_lookup);
/* need comparison function to sort the vector */
- if (v->_cmp == NULL)
- return git__throw(GIT_ENOTFOUND, "Can't sort vector. No comparison function set");
+ assert(v->_cmp != NULL);
git_vector_sort(v);
- if (git__bsearch(v->contents, v->length, key, key_lookup,
- &pos) == GIT_SUCCESS)
- return (int)pos;
+ rval = git__bsearch(v->contents, v->length, key, key_lookup, &pos);
+
+ if (at_pos != NULL)
+ *at_pos = (unsigned int)pos;
- return git__throw(GIT_ENOTFOUND, "Can't find element");
+ return (rval >= 0) ? (int)pos : GIT_ENOTFOUND;
}
-int git_vector_search2(git_vector *v, git_vector_cmp key_lookup, const void *key)
+int git_vector_search2(
+ git_vector *v, git_vector_cmp key_lookup, const void *key)
{
unsigned int i;
@@ -154,7 +152,7 @@ int git_vector_search2(git_vector *v, git_vector_cmp key_lookup, const void *key
return i;
}
- return git__throw(GIT_ENOTFOUND, "Can't find element");
+ return GIT_ENOTFOUND;
}
static int strict_comparison(const void *a, const void *b)
@@ -167,11 +165,6 @@ int git_vector_search(git_vector *v, const void *entry)
return git_vector_search2(v, v->_cmp ? v->_cmp : strict_comparison, entry);
}
-int git_vector_bsearch(git_vector *v, const void *key)
-{
- return git_vector_bsearch2(v, v->_cmp, key);
-}
-
int git_vector_remove(git_vector *v, unsigned int idx)
{
unsigned int i;
@@ -179,13 +172,19 @@ int git_vector_remove(git_vector *v, unsigned int idx)
assert(v);
if (idx >= v->length || v->length == 0)
- return git__throw(GIT_ENOTFOUND, "Can't remove element. Index out of bounds");
+ return GIT_ENOTFOUND;
for (i = idx; i < v->length - 1; ++i)
v->contents[i] = v->contents[i + 1];
v->length--;
- return GIT_SUCCESS;
+ return 0;
+}
+
+void git_vector_pop(git_vector *v)
+{
+ if (v->length > 0)
+ v->length--;
}
void git_vector_uniq(git_vector *v)
@@ -215,4 +214,14 @@ void git_vector_clear(git_vector *v)
v->sorted = 1;
}
+void git_vector_swap(git_vector *a, git_vector *b)
+{
+ git_vector t;
+
+ if (!a || !b || a == b)
+ return;
+ memcpy(&t, a, sizeof(t));
+ memcpy(a, b, sizeof(t));
+ memcpy(b, &t, sizeof(t));
+}
diff --git a/src/vector.h b/src/vector.h
index 9ee3c9ed5..9139db345 100644
--- a/src/vector.h
+++ b/src/vector.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -24,20 +24,44 @@ typedef struct git_vector {
int git_vector_init(git_vector *v, unsigned int initial_size, git_vector_cmp cmp);
void git_vector_free(git_vector *v);
void git_vector_clear(git_vector *v);
+void git_vector_swap(git_vector *a, git_vector *b);
+
+void git_vector_sort(git_vector *v);
int git_vector_search(git_vector *v, const void *entry);
int git_vector_search2(git_vector *v, git_vector_cmp cmp, const void *key);
-int git_vector_bsearch(git_vector *v, const void *entry);
-int git_vector_bsearch2(git_vector *v, git_vector_cmp cmp, const void *key);
+int git_vector_bsearch3(
+ unsigned int *at_pos, git_vector *v, git_vector_cmp cmp, const void *key);
-void git_vector_sort(git_vector *v);
+GIT_INLINE(int) git_vector_bsearch(git_vector *v, const void *key)
+{
+ return git_vector_bsearch3(NULL, v, v->_cmp, key);
+}
+
+GIT_INLINE(int) git_vector_bsearch2(
+ git_vector *v, git_vector_cmp cmp, const void *key)
+{
+ return git_vector_bsearch3(NULL, v, cmp, key);
+}
GIT_INLINE(void *) git_vector_get(git_vector *v, unsigned int position)
{
return (position < v->length) ? v->contents[position] : NULL;
}
+GIT_INLINE(const void *) git_vector_get_const(const git_vector *v, unsigned int position)
+{
+ return (position < v->length) ? v->contents[position] : NULL;
+}
+
+#define GIT_VECTOR_GET(V,I) ((I) < (V)->length ? (V)->contents[(I)] : NULL)
+
+GIT_INLINE(void *) git_vector_last(git_vector *v)
+{
+ return (v->length > 0) ? git_vector_get(v, v->length - 1) : NULL;
+}
+
#define git_vector_foreach(v, iter, elem) \
for ((iter) = 0; (iter) < (v)->length && ((elem) = (v)->contents[(iter)], 1); (iter)++ )
@@ -48,6 +72,7 @@ int git_vector_insert(git_vector *v, void *element);
int git_vector_insert_sorted(git_vector *v, void *element,
int (*on_dup)(void **old, void *new));
int git_vector_remove(git_vector *v, unsigned int idx);
+void git_vector_pop(git_vector *v);
void git_vector_uniq(git_vector *v);
#endif
diff --git a/src/win32/dir.c b/src/win32/dir.c
index 01aaaaad3..bc3d40fa5 100644
--- a/src/win32/dir.c
+++ b/src/win32/dir.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -27,8 +27,8 @@ static int init_filter(char *filter, size_t n, const char *dir)
git__DIR *git__opendir(const char *dir)
{
char filter[4096];
- wchar_t* filter_w;
- git__DIR *new;
+ wchar_t* filter_w = NULL;
+ git__DIR *new = NULL;
if (!dir || !init_filter(filter, sizeof(filter), dir))
return NULL;
@@ -37,46 +37,78 @@ git__DIR *git__opendir(const char *dir)
if (!new)
return NULL;
- new->dir = git__malloc(strlen(dir)+1);
- if (!new->dir) {
- git__free(new);
- return NULL;
- }
- strcpy(new->dir, dir);
+ new->dir = git__strdup(dir);
+ if (!new->dir)
+ goto fail;
filter_w = gitwin_to_utf16(filter);
+ if (!filter_w)
+ goto fail;
+
new->h = FindFirstFileW(filter_w, &new->f);
git__free(filter_w);
if (new->h == INVALID_HANDLE_VALUE) {
- git__free(new->dir);
- git__free(new);
- return NULL;
+ giterr_set(GITERR_OS, "Could not open directory '%s'", dir);
+ goto fail;
}
- new->first = 1;
+ new->first = 1;
return new;
+
+fail:
+ git__free(new->dir);
+ git__free(new);
+ return NULL;
}
-struct git__dirent *git__readdir(git__DIR *d)
+int git__readdir_ext(
+ git__DIR *d,
+ struct git__dirent *entry,
+ struct git__dirent **result,
+ int *is_dir)
{
- if (!d || d->h == INVALID_HANDLE_VALUE)
- return NULL;
+ if (!d || !entry || !result || d->h == INVALID_HANDLE_VALUE)
+ return -1;
+
+ *result = NULL;
if (d->first)
d->first = 0;
- else {
- if (!FindNextFileW(d->h, &d->f))
- return NULL;
+ else if (!FindNextFileW(d->h, &d->f)) {
+ if (GetLastError() == ERROR_NO_MORE_FILES)
+ return 0;
+ giterr_set(GITERR_OS, "Could not read from directory '%s'", d->dir);
+ return -1;
}
- if (wcslen(d->f.cFileName) >= sizeof(d->entry.d_name))
- return NULL;
+ if (wcslen(d->f.cFileName) >= sizeof(entry->d_name))
+ return -1;
+
+ entry->d_ino = 0;
+
+ if (WideCharToMultiByte(
+ gitwin_get_codepage(), 0, d->f.cFileName, -1,
+ entry->d_name, GIT_PATH_MAX, NULL, NULL) == 0)
+ {
+ giterr_set(GITERR_OS, "Could not convert filename to UTF-8");
+ return -1;
+ }
- d->entry.d_ino = 0;
- WideCharToMultiByte(gitwin_get_codepage(), 0, d->f.cFileName, -1, d->entry.d_name, GIT_PATH_MAX, NULL, NULL);
+ *result = entry;
- return &d->entry;
+ if (is_dir != NULL)
+ *is_dir = ((d->f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0);
+
+ return 0;
+}
+
+struct git__dirent *git__readdir(git__DIR *d)
+{
+ struct git__dirent *result;
+ if (git__readdir_ext(d, &d->entry, &result, NULL) < 0)
+ return NULL;
+ return result;
}
void git__rewinddir(git__DIR *d)
@@ -84,32 +116,40 @@ void git__rewinddir(git__DIR *d)
char filter[4096];
wchar_t* filter_w;
- if (d) {
- if (d->h != INVALID_HANDLE_VALUE)
- FindClose(d->h);
+ if (!d)
+ return;
+
+ if (d->h != INVALID_HANDLE_VALUE) {
+ FindClose(d->h);
d->h = INVALID_HANDLE_VALUE;
d->first = 0;
+ }
- if (init_filter(filter, sizeof(filter), d->dir)) {
- filter_w = gitwin_to_utf16(filter);
- d->h = FindFirstFileW(filter_w, &d->f);
- git__free(filter_w);
+ if (!init_filter(filter, sizeof(filter), d->dir) ||
+ (filter_w = gitwin_to_utf16(filter)) == NULL)
+ return;
- if (d->h != INVALID_HANDLE_VALUE)
- d->first = 1;
- }
- }
+ d->h = FindFirstFileW(filter_w, &d->f);
+ git__free(filter_w);
+
+ if (d->h == INVALID_HANDLE_VALUE)
+ giterr_set(GITERR_OS, "Could not open directory '%s'", d->dir);
+ else
+ d->first = 1;
}
int git__closedir(git__DIR *d)
{
- if (d) {
- if (d->h != INVALID_HANDLE_VALUE)
- FindClose(d->h);
- if (d->dir)
- git__free(d->dir);
- git__free(d);
+ if (!d)
+ return 0;
+
+ if (d->h != INVALID_HANDLE_VALUE) {
+ FindClose(d->h);
+ d->h = INVALID_HANDLE_VALUE;
}
+ git__free(d->dir);
+ d->dir = NULL;
+ git__free(d);
return 0;
}
diff --git a/src/win32/dir.h b/src/win32/dir.h
index b16a3cfeb..c816d79bb 100644
--- a/src/win32/dir.h
+++ b/src/win32/dir.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -24,6 +24,8 @@ typedef struct {
extern git__DIR *git__opendir(const char *);
extern struct git__dirent *git__readdir(git__DIR *);
+extern int git__readdir_ext(
+ git__DIR *, struct git__dirent *, struct git__dirent **, int *);
extern void git__rewinddir(git__DIR *);
extern int git__closedir(git__DIR *);
@@ -32,6 +34,7 @@ extern int git__closedir(git__DIR *);
# define DIR git__DIR
# define opendir git__opendir
# define readdir git__readdir
+# define readdir_r(d,e,r) git__readdir_ext((d),(e),(r),NULL)
# define rewinddir git__rewinddir
# define closedir git__closedir
# endif
diff --git a/src/win32/git2.rc b/src/win32/git2.rc
index 16a7b1f1b..3a65c0a0f 100644
--- a/src/win32/git2.rc
+++ b/src/win32/git2.rc
@@ -28,7 +28,7 @@ BEGIN
VALUE "FileDescription", "libgit2 - the Git linkable library\0"
VALUE "FileVersion", LIBGIT2_VERSION "\0"
VALUE "InternalName", LIBGIT2_FILENAME "\0"
- VALUE "LegalCopyright", "Copyright (C) 2009-2011 the libgit2 contributors\0"
+ VALUE "LegalCopyright", "Copyright (C) 2009-2012 the libgit2 contributors\0"
VALUE "OriginalFilename", LIBGIT2_FILENAME "\0"
VALUE "ProductName", "libgit2\0"
VALUE "ProductVersion", LIBGIT2_VERSION "\0"
diff --git a/src/win32/map.c b/src/win32/map.c
index 3e6b3d878..f730120cc 100644
--- a/src/win32/map.c
+++ b/src/win32/map.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -33,12 +33,7 @@ int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offs
git_off_t page_start;
git_off_t page_offset;
- assert((out != NULL) && (len > 0));
-
- if ((out == NULL) || (len == 0)) {
- errno = EINVAL;
- return git__throw(GIT_ERROR, "Failed to mmap. No map or zero length");
- }
+ GIT_MMAP_VALIDATE(out, len, prot, flags);
out->data = NULL;
out->len = 0;
@@ -46,86 +41,75 @@ int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offs
if (fh == INVALID_HANDLE_VALUE) {
errno = EBADF;
- return git__throw(GIT_ERROR, "Failed to mmap. Invalid handle value");
+ giterr_set(GITERR_OS, "Failed to mmap. Invalid handle value");
+ return -1;
}
if (prot & GIT_PROT_WRITE)
fmap_prot |= PAGE_READWRITE;
else if (prot & GIT_PROT_READ)
fmap_prot |= PAGE_READONLY;
- else {
- errno = EINVAL;
- return git__throw(GIT_ERROR, "Failed to mmap. Invalid protection parameters");
- }
if (prot & GIT_PROT_WRITE)
view_prot |= FILE_MAP_WRITE;
if (prot & GIT_PROT_READ)
view_prot |= FILE_MAP_READ;
- if (flags & GIT_MAP_FIXED) {
- errno = EINVAL;
- return git__throw(GIT_ERROR, "Failed to mmap. FIXED not set");
- }
-
page_start = (offset / page_size) * page_size;
page_offset = offset - page_start;
if (page_offset != 0) { /* offset must be multiple of page size */
errno = EINVAL;
- return git__throw(GIT_ERROR, "Failed to mmap. Offset must be multiple of page size");
+ giterr_set(GITERR_OS, "Failed to mmap. Offset must be multiple of page size");
+ return -1;
}
out->fmh = CreateFileMapping(fh, NULL, fmap_prot, 0, 0, NULL);
if (!out->fmh || out->fmh == INVALID_HANDLE_VALUE) {
- /* errno = ? */
+ giterr_set(GITERR_OS, "Failed to mmap. Invalid handle value");
out->fmh = NULL;
- return git__throw(GIT_ERROR, "Failed to mmap. Invalid handle value");
+ return -1;
}
assert(sizeof(git_off_t) == 8);
+
off_low = (DWORD)(page_start);
off_hi = (DWORD)(page_start >> 32);
out->data = MapViewOfFile(out->fmh, view_prot, off_hi, off_low, len);
if (!out->data) {
- /* errno = ? */
+ giterr_set(GITERR_OS, "Failed to mmap. No data written");
CloseHandle(out->fmh);
out->fmh = NULL;
- return git__throw(GIT_ERROR, "Failed to mmap. No data written");
+ return -1;
}
out->len = len;
- return GIT_SUCCESS;
+ return 0;
}
int p_munmap(git_map *map)
{
- assert(map != NULL);
+ int error = 0;
- if (!map)
- return git__throw(GIT_ERROR, "Failed to munmap. Map does not exist");
+ assert(map != NULL);
if (map->data) {
if (!UnmapViewOfFile(map->data)) {
- /* errno = ? */
- CloseHandle(map->fmh);
- map->data = NULL;
- map->fmh = NULL;
- return git__throw(GIT_ERROR, "Failed to munmap. Could not unmap view of file");
+ giterr_set(GITERR_OS, "Failed to munmap. Could not unmap view of file");
+ error = -1;
}
map->data = NULL;
}
if (map->fmh) {
if (!CloseHandle(map->fmh)) {
- /* errno = ? */
- map->fmh = NULL;
- return git__throw(GIT_ERROR, "Failed to munmap. Could not close handle");
+ giterr_set(GITERR_OS, "Failed to munmap. Could not close handle");
+ error = -1;
}
map->fmh = NULL;
}
- return GIT_SUCCESS;
+ return error;
}
diff --git a/src/win32/mingw-compat.h b/src/win32/mingw-compat.h
index 7207d882f..6200dc094 100644
--- a/src/win32/mingw-compat.h
+++ b/src/win32/mingw-compat.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
diff --git a/src/win32/msvc-compat.h b/src/win32/msvc-compat.h
index 167e2694f..3ef09c85b 100644
--- a/src/win32/msvc-compat.h
+++ b/src/win32/msvc-compat.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
diff --git a/src/win32/posix.h b/src/win32/posix.h
index ae6323679..baa4a3b4e 100644
--- a/src/win32/posix.h
+++ b/src/win32/posix.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -8,23 +8,23 @@
#define INCLUDE_posix__w32_h__
#include "common.h"
-#include "fnmatch.h"
+#include "compat/fnmatch.h"
#include "utf-conv.h"
-GIT_INLINE(int) p_link(const char *GIT_UNUSED(old), const char *GIT_UNUSED(new))
+GIT_INLINE(int) p_link(const char *old, const char *new)
{
- GIT_UNUSED_ARG(old)
- GIT_UNUSED_ARG(new)
+ GIT_UNUSED(old);
+ GIT_UNUSED(new);
errno = ENOSYS;
return -1;
}
-GIT_INLINE(int) p_mkdir(const char *path, mode_t GIT_UNUSED(mode))
+GIT_INLINE(int) p_mkdir(const char *path, mode_t mode)
{
wchar_t* buf = gitwin_to_utf16(path);
int ret = _wmkdir(buf);
- GIT_UNUSED_ARG(mode)
+ GIT_UNUSED(mode);
git__free(buf);
return ret;
@@ -45,9 +45,11 @@ extern int p_chmod(const char* path, mode_t mode);
extern int p_rmdir(const char* path);
extern int p_access(const char* path, mode_t mode);
extern int p_fsync(int fd);
-extern int p_open(const char *path, int flags);
+extern int p_open(const char *path, int flags, ...);
extern int p_creat(const char *path, mode_t mode);
extern int p_getcwd(char *buffer_out, size_t size);
extern int p_rename(const char *from, const char *to);
+extern int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags);
+extern int p_send(GIT_SOCKET socket, const void *buffer, size_t length, int flags);
#endif
diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c
index 3786f0162..10de70da8 100644
--- a/src/win32/posix_w32.c
+++ b/src/win32/posix_w32.c
@@ -1,10 +1,10 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
-#include "posix.h"
+#include "../posix.h"
#include "path.h"
#include "utf-conv.h"
#include <errno.h>
@@ -17,10 +17,11 @@ int p_unlink(const char *path)
int ret = 0;
wchar_t* buf;
- buf = gitwin_to_utf16(path);
- _wchmod(buf, 0666);
- ret = _wunlink(buf);
- git__free(buf);
+ if ((buf = gitwin_to_utf16(path)) != NULL) {
+ _wchmod(buf, 0666);
+ ret = _wunlink(buf);
+ git__free(buf);
+ }
return ret;
}
@@ -60,6 +61,8 @@ static int do_lstat(const char *file_name, struct stat *buf)
{
WIN32_FILE_ATTRIBUTE_DATA fdata;
wchar_t* fbuf = gitwin_to_utf16(file_name);
+ if (!fbuf)
+ return -1;
if (GetFileAttributesExW(fbuf, GetFileExInfoStandard, &fdata)) {
int fMode = S_IREAD;
@@ -87,54 +90,43 @@ static int do_lstat(const char *file_name, struct stat *buf)
buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
git__free(fbuf);
- return GIT_SUCCESS;
+ return 0;
}
git__free(fbuf);
-
- switch (GetLastError()) {
- case ERROR_ACCESS_DENIED:
- case ERROR_SHARING_VIOLATION:
- case ERROR_LOCK_VIOLATION:
- case ERROR_SHARING_BUFFER_EXCEEDED:
- return GIT_EOSERR;
-
- case ERROR_BUFFER_OVERFLOW:
- case ERROR_NOT_ENOUGH_MEMORY:
- return GIT_ENOMEM;
-
- default:
- return GIT_EINVALIDPATH;
- }
+ return -1;
}
int p_lstat(const char *file_name, struct stat *buf)
{
- int namelen, error;
- char alt_name[GIT_PATH_MAX];
+ int error;
+ size_t namelen;
+ char *alt_name;
- if ((error = do_lstat(file_name, buf)) == GIT_SUCCESS)
- return GIT_SUCCESS;
+ if (do_lstat(file_name, buf) == 0)
+ return 0;
/* if file_name ended in a '/', Windows returned ENOENT;
* try again without trailing slashes
*/
- if (error != GIT_EINVALIDPATH)
- return git__throw(GIT_EOSERR, "Failed to lstat file");
-
namelen = strlen(file_name);
if (namelen && file_name[namelen-1] != '/')
- return git__throw(GIT_EOSERR, "Failed to lstat file");
+ return -1;
while (namelen && file_name[namelen-1] == '/')
--namelen;
- if (!namelen || namelen >= GIT_PATH_MAX)
- return git__throw(GIT_ENOMEM, "Failed to lstat file");
+ if (!namelen)
+ return -1;
+
+ alt_name = git__strndup(file_name, namelen);
+ if (!alt_name)
+ return -1;
+
+ error = do_lstat(alt_name, buf);
- memcpy(alt_name, file_name, namelen);
- alt_name[namelen] = 0;
- return do_lstat(alt_name, buf);
+ git__free(alt_name);
+ return error;
}
int p_readlink(const char *link, char *target, size_t target_len)
@@ -145,6 +137,9 @@ int p_readlink(const char *link, char *target, size_t target_len)
DWORD dwRet;
wchar_t* link_w;
wchar_t* target_w;
+ int error = 0;
+
+ assert(link && target && target_len > 0);
/*
* Try to load the pointer to pGetFinalPath dynamically, because
@@ -156,12 +151,15 @@ int p_readlink(const char *link, char *target, size_t target_len)
if (library != NULL)
pGetFinalPath = (fpath_func)GetProcAddress(library, "GetFinalPathNameByHandleW");
- if (pGetFinalPath == NULL)
- return git__throw(GIT_EOSERR,
+ if (pGetFinalPath == NULL) {
+ giterr_set(GITERR_OS,
"'GetFinalPathNameByHandleW' is not available in this platform");
+ return -1;
+ }
}
link_w = gitwin_to_utf16(link);
+ GITERR_CHECK_ALLOC(link_w);
hFile = CreateFileW(link_w, // file to open
GENERIC_READ, // open for reading
@@ -173,58 +171,72 @@ int p_readlink(const char *link, char *target, size_t target_len)
git__free(link_w);
- if (hFile == INVALID_HANDLE_VALUE)
- return GIT_EOSERR;
-
- if (target_len <= 0) {
- return GIT_EINVALIDARGS;
+ if (hFile == INVALID_HANDLE_VALUE) {
+ giterr_set(GITERR_OS, "Cannot open '%s' for reading", link);
+ return -1;
}
target_w = (wchar_t*)git__malloc(target_len * sizeof(wchar_t));
+ GITERR_CHECK_ALLOC(target_w);
- dwRet = pGetFinalPath(hFile, target_w, target_len, 0x0);
- if (dwRet >= target_len) {
- git__free(target_w);
- CloseHandle(hFile);
- return GIT_ENOMEM;
- }
-
- if (!WideCharToMultiByte(CP_UTF8, 0, target_w, -1, target, target_len * sizeof(char), NULL, NULL)) {
- git__free(target_w);
- return GIT_EOSERR;
- }
+ dwRet = pGetFinalPath(hFile, target_w, (DWORD)target_len, 0x0);
+ if (dwRet == 0 ||
+ dwRet >= target_len ||
+ !WideCharToMultiByte(CP_UTF8, 0, target_w, -1, target,
+ (int)(target_len * sizeof(char)), NULL, NULL))
+ error = -1;
git__free(target_w);
CloseHandle(hFile);
- if (dwRet > 4) {
- /* Skip first 4 characters if they are "\\?\" */
- if (target[0] == '\\' && target[1] == '\\' && target[2] == '?' && target[3] == '\\') {
- char tmp[GIT_PATH_MAX];
- unsigned int offset = 4;
- dwRet -= 4;
-
- /* \??\UNC\ */
- if (dwRet > 7 && target[4] == 'U' && target[5] == 'N' && target[6] == 'C') {
- offset += 2;
- dwRet -= 2;
- target[offset] = '\\';
- }
-
- memcpy(tmp, target + offset, dwRet);
- memcpy(target, tmp, dwRet);
+ if (error)
+ return error;
+
+ /* Skip first 4 characters if they are "\\?\" */
+ if (dwRet > 4 &&
+ target[0] == '\\' && target[1] == '\\' &&
+ target[2] == '?' && target[3] == '\\')
+ {
+ unsigned int offset = 4;
+ dwRet -= 4;
+
+ /* \??\UNC\ */
+ if (dwRet > 7 &&
+ target[4] == 'U' && target[5] == 'N' && target[6] == 'C')
+ {
+ offset += 2;
+ dwRet -= 2;
+ target[offset] = '\\';
}
+
+ memmove(target, target + offset, dwRet);
}
target[dwRet] = '\0';
+
return dwRet;
}
-int p_open(const char *path, int flags)
+int p_open(const char *path, int flags, ...)
{
int fd;
- wchar_t* buf = gitwin_to_utf16(path);
- fd = _wopen(buf, flags | _O_BINARY);
+ wchar_t* buf;
+ mode_t mode = 0;
+
+ buf = gitwin_to_utf16(path);
+ if (!buf)
+ return -1;
+
+ if (flags & O_CREAT)
+ {
+ va_list arg_list;
+
+ va_start(arg_list, flags);
+ mode = va_arg(arg_list, mode_t);
+ va_end(arg_list);
+ }
+
+ fd = _wopen(buf, flags | _O_BINARY, mode);
git__free(buf);
return fd;
@@ -234,24 +246,31 @@ int p_creat(const char *path, mode_t mode)
{
int fd;
wchar_t* buf = gitwin_to_utf16(path);
+ if (!buf)
+ return -1;
fd = _wopen(buf, _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY, mode);
-
git__free(buf);
return fd;
}
int p_getcwd(char *buffer_out, size_t size)
{
- wchar_t* buf = (wchar_t*)git__malloc(sizeof(wchar_t) * (int)size);
+ int ret;
+ wchar_t* buf;
+
+ if ((size_t)((int)size) != size)
+ return -1;
+
+ buf = (wchar_t*)git__malloc(sizeof(wchar_t) * (int)size);
+ GITERR_CHECK_ALLOC(buf);
+
_wgetcwd(buf, (int)size);
- if (!WideCharToMultiByte(CP_UTF8, 0, buf, -1, buffer_out, size, NULL, NULL)) {
- git__free(buf);
- return GIT_EOSERR;
- }
+ ret = WideCharToMultiByte(
+ CP_UTF8, 0, buf, -1, buffer_out, (int)size, NULL, NULL);
git__free(buf);
- return GIT_SUCCESS;
+ return !ret ? -1 : 0;
}
int p_stat(const char* path, struct stat* buf)
@@ -262,8 +281,10 @@ int p_stat(const char* path, struct stat* buf)
int p_chdir(const char* path)
{
wchar_t* buf = gitwin_to_utf16(path);
- int ret = _wchdir(buf);
-
+ int ret;
+ if (!buf)
+ return -1;
+ ret = _wchdir(buf);
git__free(buf);
return ret;
}
@@ -271,8 +292,10 @@ int p_chdir(const char* path)
int p_chmod(const char* path, mode_t mode)
{
wchar_t* buf = gitwin_to_utf16(path);
- int ret = _wchmod(buf, mode);
-
+ int ret;
+ if (!buf)
+ return -1;
+ ret = _wchmod(buf, mode);
git__free(buf);
return ret;
}
@@ -280,44 +303,47 @@ int p_chmod(const char* path, mode_t mode)
int p_rmdir(const char* path)
{
wchar_t* buf = gitwin_to_utf16(path);
- int ret = _wrmdir(buf);
-
+ int ret;
+ if (!buf)
+ return -1;
+ ret = _wrmdir(buf);
git__free(buf);
return ret;
}
int p_hide_directory__w32(const char *path)
{
- int error;
+ int res;
wchar_t* buf = gitwin_to_utf16(path);
+ if (!buf)
+ return -1;
- error = SetFileAttributesW(buf, FILE_ATTRIBUTE_HIDDEN) != 0 ?
- GIT_SUCCESS : GIT_ERROR; /* MSDN states a "non zero" value indicates a success */
-
+ res = SetFileAttributesW(buf, FILE_ATTRIBUTE_HIDDEN);
git__free(buf);
- if (error < GIT_SUCCESS)
- error = git__throw(GIT_EOSERR, "Failed to hide directory '%s'", path);
-
- return error;
+ return (res != 0) ? 0 : -1; /* MSDN states a "non zero" value indicates a success */
}
char *p_realpath(const char *orig_path, char *buffer)
{
- int ret;
+ int ret, buffer_sz = 0;
wchar_t* orig_path_w = gitwin_to_utf16(orig_path);
wchar_t* buffer_w = (wchar_t*)git__malloc(GIT_PATH_MAX * sizeof(wchar_t));
+ if (!orig_path_w || !buffer_w)
+ return NULL;
+
ret = GetFullPathNameW(orig_path_w, GIT_PATH_MAX, buffer_w, NULL);
git__free(orig_path_w);
- if (!ret || ret > GIT_PATH_MAX) {
+ /* According to MSDN, a return value equals to zero means a failure. */
+ if (ret == 0 || ret > GIT_PATH_MAX) {
buffer = NULL;
goto done;
}
if (buffer == NULL) {
- int buffer_sz = WideCharToMultiByte(CP_UTF8, 0, buffer_w, -1, NULL, 0, NULL, NULL);
+ buffer_sz = WideCharToMultiByte(CP_UTF8, 0, buffer_w, -1, NULL, 0, NULL, NULL);
if (!buffer_sz ||
!(buffer = (char *)git__malloc(buffer_sz)) ||
@@ -325,10 +351,22 @@ char *p_realpath(const char *orig_path, char *buffer)
{
git__free(buffer);
buffer = NULL;
+ goto done;
}
} else {
- if (!WideCharToMultiByte(CP_UTF8, 0, buffer_w, -1, buffer, GIT_PATH_MAX, NULL, NULL))
+ if (!WideCharToMultiByte(CP_UTF8, 0, buffer_w, -1, buffer, GIT_PATH_MAX, NULL, NULL)) {
buffer = NULL;
+ goto done;
+ }
+ }
+
+ if (!git_path_exists(buffer))
+ {
+ if (buffer_sz > 0)
+ git__free(buffer);
+
+ buffer = NULL;
+ errno = ENOENT;
}
done:
@@ -341,8 +379,12 @@ done:
int p_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr)
{
#ifdef _MSC_VER
- int len = _vsnprintf(buffer, count, format, argptr);
- return (len < 0) ? _vscprintf(format, argptr) : len;
+ int len;
+
+ if (count == 0 || (len = _vsnprintf(buffer, count, format, argptr)) < 0)
+ return _vscprintf(format, argptr);
+
+ return len;
#else /* MinGW */
return vsnprintf(buffer, count, format, argptr);
#endif
@@ -366,10 +408,10 @@ int p_mkstemp(char *tmp_path)
{
#if defined(_MSC_VER)
if (_mktemp_s(tmp_path, strlen(tmp_path) + 1) != 0)
- return GIT_EOSERR;
+ return -1;
#else
if (_mktemp(tmp_path) == NULL)
- return GIT_EOSERR;
+ return -1;
#endif
return p_creat(tmp_path, 0744);
@@ -378,15 +420,17 @@ int p_mkstemp(char *tmp_path)
int p_setenv(const char* name, const char* value, int overwrite)
{
if (overwrite != 1)
- return EINVAL;
+ return -1;
- return (SetEnvironmentVariableA(name, value) == 0 ? GIT_EOSERR : GIT_SUCCESS);
+ return (SetEnvironmentVariableA(name, value) == 0 ? -1 : 0);
}
int p_access(const char* path, mode_t mode)
{
wchar_t *buf = gitwin_to_utf16(path);
int ret;
+ if (!buf)
+ return -1;
ret = _waccess(buf, mode);
git__free(buf);
@@ -394,16 +438,35 @@ int p_access(const char* path, mode_t mode)
return ret;
}
-extern int p_rename(const char *from, const char *to)
+int p_rename(const char *from, const char *to)
{
wchar_t *wfrom = gitwin_to_utf16(from);
wchar_t *wto = gitwin_to_utf16(to);
int ret;
- ret = MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? GIT_SUCCESS : GIT_EOSERR;
+ if (!wfrom || !wto)
+ return -1;
+
+ ret = MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? 0 : -1;
git__free(wfrom);
git__free(wto);
return ret;
}
+
+int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags)
+{
+ if ((size_t)((int)length) != length)
+ return -1; /* giterr_set will be done by caller */
+
+ return recv(socket, buffer, (int)length, flags);
+}
+
+int p_send(GIT_SOCKET socket, const void *buffer, size_t length, int flags)
+{
+ if ((size_t)((int)length) != length)
+ return -1; /* giterr_set will be done by caller */
+
+ return send(socket, buffer, (int)length, flags);
+}
diff --git a/src/win32/pthread.c b/src/win32/pthread.c
index e0f6c14a8..3a186c8d9 100644
--- a/src/win32/pthread.c
+++ b/src/win32/pthread.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -7,13 +7,16 @@
#include "pthread.h"
-int pthread_create(pthread_t *GIT_RESTRICT thread,
- const pthread_attr_t *GIT_RESTRICT GIT_UNUSED(attr),
- void *(*start_routine)(void*), void *GIT_RESTRICT arg)
+int pthread_create(
+ pthread_t *GIT_RESTRICT thread,
+ const pthread_attr_t *GIT_RESTRICT attr,
+ void *(*start_routine)(void*),
+ void *GIT_RESTRICT arg)
{
- GIT_UNUSED_ARG(attr);
- *thread = (pthread_t) CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)start_routine, arg, 0, NULL);
- return *thread ? GIT_SUCCESS : git__throw(GIT_EOSERR, "Failed to create pthread");
+ GIT_UNUSED(attr);
+ *thread = (pthread_t) CreateThread(
+ NULL, 0, (LPTHREAD_START_ROUTINE)start_routine, arg, 0, NULL);
+ return *thread ? 0 : -1;
}
int pthread_join(pthread_t thread, void **value_ptr)
@@ -26,9 +29,9 @@ int pthread_join(pthread_t thread, void **value_ptr)
}
int pthread_mutex_init(pthread_mutex_t *GIT_RESTRICT mutex,
- const pthread_mutexattr_t *GIT_RESTRICT GIT_UNUSED(mutexattr))
+ const pthread_mutexattr_t *GIT_RESTRICT mutexattr)
{
- GIT_UNUSED_ARG(mutexattr);
+ GIT_UNUSED(mutexattr);
InitializeCriticalSection(mutex);
return 0;
}
diff --git a/src/win32/pthread.h b/src/win32/pthread.h
index 3ea8c7ac1..b194cbfa7 100644
--- a/src/win32/pthread.h
+++ b/src/win32/pthread.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
diff --git a/src/win32/utf-conv.c b/src/win32/utf-conv.c
index b1b838eb7..76f1e4237 100644
--- a/src/win32/utf-conv.c
+++ b/src/win32/utf-conv.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
@@ -7,6 +7,7 @@
#include "common.h"
#include "utf-conv.h"
+#include "git2/windows.h"
/*
* Default codepage value
@@ -31,25 +32,24 @@ void gitwin_set_utf8(void)
wchar_t* gitwin_to_utf16(const char* str)
{
wchar_t* ret;
- int cb;
+ size_t cb;
- if (!str) {
+ if (!str)
return NULL;
- }
cb = strlen(str) * sizeof(wchar_t);
- if (cb == 0) {
- ret = (wchar_t*)git__malloc(sizeof(wchar_t));
- ret[0] = 0;
- return ret;
- }
+ if (cb == 0)
+ return (wchar_t *)git__calloc(1, sizeof(wchar_t));
/* Add space for null terminator */
cb += sizeof(wchar_t);
- ret = (wchar_t*)git__malloc(cb);
+ ret = (wchar_t *)git__malloc(cb);
+ if (!ret)
+ return NULL;
- if (MultiByteToWideChar(_active_codepage, 0, str, -1, ret, cb) == 0) {
+ if (MultiByteToWideChar(_active_codepage, 0, str, -1, ret, (int)cb) == 0) {
+ giterr_set(GITERR_OS, "Could not convert string to UTF-16");
git__free(ret);
ret = NULL;
}
@@ -59,31 +59,33 @@ wchar_t* gitwin_to_utf16(const char* str)
int gitwin_append_utf16(wchar_t *buffer, const char *str, size_t len)
{
- return MultiByteToWideChar(_active_codepage, 0, str, -1, buffer, len);
+ int result = MultiByteToWideChar(_active_codepage, 0, str, -1, buffer, (int)len);
+ if (result == 0)
+ giterr_set(GITERR_OS, "Could not convert string to UTF-16");
+ return result;
}
char* gitwin_from_utf16(const wchar_t* str)
{
char* ret;
- int cb;
+ size_t cb;
- if (!str) {
+ if (!str)
return NULL;
- }
cb = wcslen(str) * sizeof(char);
- if (cb == 0) {
- ret = (char*)git__malloc(sizeof(char));
- ret[0] = 0;
- return ret;
- }
+ if (cb == 0)
+ return (char *)git__calloc(1, sizeof(char));
/* Add space for null terminator */
cb += sizeof(char);
ret = (char*)git__malloc(cb);
+ if (!ret)
+ return NULL;
- if (WideCharToMultiByte(_active_codepage, 0, str, -1, ret, cb, NULL, NULL) == 0) {
+ if (WideCharToMultiByte(_active_codepage, 0, str, -1, ret, (int)cb, NULL, NULL) == 0) {
+ giterr_set(GITERR_OS, "Could not convert string to UTF-8");
git__free(ret);
ret = NULL;
}
diff --git a/src/win32/utf-conv.h b/src/win32/utf-conv.h
index feae249f9..ae9f29f6c 100644
--- a/src/win32/utf-conv.h
+++ b/src/win32/utf-conv.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2011 the libgit2 contributors
+ * Copyright (C) 2009-2012 the libgit2 contributors
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
diff --git a/src/xdiff/xdiff.h b/src/xdiff/xdiff.h
new file mode 100644
index 000000000..cb8b235b5
--- /dev/null
+++ b/src/xdiff/xdiff.h
@@ -0,0 +1,135 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XDIFF_H)
+#define XDIFF_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* #ifdef __cplusplus */
+
+
+#define XDF_NEED_MINIMAL (1 << 1)
+#define XDF_IGNORE_WHITESPACE (1 << 2)
+#define XDF_IGNORE_WHITESPACE_CHANGE (1 << 3)
+#define XDF_IGNORE_WHITESPACE_AT_EOL (1 << 4)
+#define XDF_PATIENCE_DIFF (1 << 5)
+#define XDF_HISTOGRAM_DIFF (1 << 6)
+#define XDF_WHITESPACE_FLAGS (XDF_IGNORE_WHITESPACE | XDF_IGNORE_WHITESPACE_CHANGE | XDF_IGNORE_WHITESPACE_AT_EOL)
+
+#define XDL_PATCH_NORMAL '-'
+#define XDL_PATCH_REVERSE '+'
+#define XDL_PATCH_MODEMASK ((1 << 8) - 1)
+#define XDL_PATCH_IGNOREBSPACE (1 << 8)
+
+#define XDL_EMIT_FUNCNAMES (1 << 0)
+#define XDL_EMIT_COMMON (1 << 1)
+#define XDL_EMIT_FUNCCONTEXT (1 << 2)
+
+#define XDL_MMB_READONLY (1 << 0)
+
+#define XDL_MMF_ATOMIC (1 << 0)
+
+#define XDL_BDOP_INS 1
+#define XDL_BDOP_CPY 2
+#define XDL_BDOP_INSB 3
+
+/* merge simplification levels */
+#define XDL_MERGE_MINIMAL 0
+#define XDL_MERGE_EAGER 1
+#define XDL_MERGE_ZEALOUS 2
+#define XDL_MERGE_ZEALOUS_ALNUM 3
+
+/* merge favor modes */
+#define XDL_MERGE_FAVOR_OURS 1
+#define XDL_MERGE_FAVOR_THEIRS 2
+#define XDL_MERGE_FAVOR_UNION 3
+
+/* merge output styles */
+#define XDL_MERGE_DIFF3 1
+
+typedef struct s_mmfile {
+ char *ptr;
+ size_t size;
+} mmfile_t;
+
+typedef struct s_mmbuffer {
+ char *ptr;
+ size_t size;
+} mmbuffer_t;
+
+typedef struct s_xpparam {
+ unsigned long flags;
+} xpparam_t;
+
+typedef struct s_xdemitcb {
+ void *priv;
+ int (*outf)(void *, mmbuffer_t *, int);
+} xdemitcb_t;
+
+typedef long (*find_func_t)(const char *line, long line_len, char *buffer, long buffer_size, void *priv);
+
+typedef struct s_xdemitconf {
+ long ctxlen;
+ long interhunkctxlen;
+ unsigned long flags;
+ find_func_t find_func;
+ void *find_func_priv;
+ void (*emit_func)(void);
+} xdemitconf_t;
+
+typedef struct s_bdiffparam {
+ long bsize;
+} bdiffparam_t;
+
+
+#define xdl_malloc(x) malloc(x)
+#define xdl_free(ptr) free(ptr)
+#define xdl_realloc(ptr,x) realloc(ptr,x)
+
+void *xdl_mmfile_first(mmfile_t *mmf, long *size);
+long xdl_mmfile_size(mmfile_t *mmf);
+
+int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+ xdemitconf_t const *xecfg, xdemitcb_t *ecb);
+
+typedef struct s_xmparam {
+ xpparam_t xpp;
+ int marker_size;
+ int level;
+ int favor;
+ int style;
+ const char *ancestor; /* label for orig */
+ const char *file1; /* label for mf1 */
+ const char *file2; /* label for mf2 */
+} xmparam_t;
+
+#define DEFAULT_CONFLICT_MARKER_SIZE 7
+
+int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2,
+ xmparam_t const *xmp, mmbuffer_t *result);
+
+#ifdef __cplusplus
+}
+#endif /* #ifdef __cplusplus */
+
+#endif /* #if !defined(XDIFF_H) */
diff --git a/src/xdiff/xdiffi.c b/src/xdiff/xdiffi.c
new file mode 100644
index 000000000..75a392275
--- /dev/null
+++ b/src/xdiff/xdiffi.c
@@ -0,0 +1,572 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#include "xinclude.h"
+
+
+
+#define XDL_MAX_COST_MIN 256
+#define XDL_HEUR_MIN_COST 256
+#define XDL_LINE_MAX (long)((1UL << (CHAR_BIT * sizeof(long) - 1)) - 1)
+#define XDL_SNAKE_CNT 20
+#define XDL_K_HEUR 4
+
+
+
+typedef struct s_xdpsplit {
+ long i1, i2;
+ int min_lo, min_hi;
+} xdpsplit_t;
+
+
+
+
+static long xdl_split(unsigned long const *ha1, long off1, long lim1,
+ unsigned long const *ha2, long off2, long lim2,
+ long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl,
+ xdalgoenv_t *xenv);
+static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1, long chg2);
+
+
+
+
+
+/*
+ * See "An O(ND) Difference Algorithm and its Variations", by Eugene Myers.
+ * Basically considers a "box" (off1, off2, lim1, lim2) and scan from both
+ * the forward diagonal starting from (off1, off2) and the backward diagonal
+ * starting from (lim1, lim2). If the K values on the same diagonal crosses
+ * returns the furthest point of reach. We might end up having to expensive
+ * cases using this algorithm is full, so a little bit of heuristic is needed
+ * to cut the search and to return a suboptimal point.
+ */
+static long xdl_split(unsigned long const *ha1, long off1, long lim1,
+ unsigned long const *ha2, long off2, long lim2,
+ long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl,
+ xdalgoenv_t *xenv) {
+ long dmin = off1 - lim2, dmax = lim1 - off2;
+ long fmid = off1 - off2, bmid = lim1 - lim2;
+ long odd = (fmid - bmid) & 1;
+ long fmin = fmid, fmax = fmid;
+ long bmin = bmid, bmax = bmid;
+ long ec, d, i1, i2, prev1, best, dd, v, k;
+
+ /*
+ * Set initial diagonal values for both forward and backward path.
+ */
+ kvdf[fmid] = off1;
+ kvdb[bmid] = lim1;
+
+ for (ec = 1;; ec++) {
+ int got_snake = 0;
+
+ /*
+ * We need to extent the diagonal "domain" by one. If the next
+ * values exits the box boundaries we need to change it in the
+ * opposite direction because (max - min) must be a power of two.
+ * Also we initialize the external K value to -1 so that we can
+ * avoid extra conditions check inside the core loop.
+ */
+ if (fmin > dmin)
+ kvdf[--fmin - 1] = -1;
+ else
+ ++fmin;
+ if (fmax < dmax)
+ kvdf[++fmax + 1] = -1;
+ else
+ --fmax;
+
+ for (d = fmax; d >= fmin; d -= 2) {
+ if (kvdf[d - 1] >= kvdf[d + 1])
+ i1 = kvdf[d - 1] + 1;
+ else
+ i1 = kvdf[d + 1];
+ prev1 = i1;
+ i2 = i1 - d;
+ for (; i1 < lim1 && i2 < lim2 && ha1[i1] == ha2[i2]; i1++, i2++);
+ if (i1 - prev1 > xenv->snake_cnt)
+ got_snake = 1;
+ kvdf[d] = i1;
+ if (odd && bmin <= d && d <= bmax && kvdb[d] <= i1) {
+ spl->i1 = i1;
+ spl->i2 = i2;
+ spl->min_lo = spl->min_hi = 1;
+ return ec;
+ }
+ }
+
+ /*
+ * We need to extent the diagonal "domain" by one. If the next
+ * values exits the box boundaries we need to change it in the
+ * opposite direction because (max - min) must be a power of two.
+ * Also we initialize the external K value to -1 so that we can
+ * avoid extra conditions check inside the core loop.
+ */
+ if (bmin > dmin)
+ kvdb[--bmin - 1] = XDL_LINE_MAX;
+ else
+ ++bmin;
+ if (bmax < dmax)
+ kvdb[++bmax + 1] = XDL_LINE_MAX;
+ else
+ --bmax;
+
+ for (d = bmax; d >= bmin; d -= 2) {
+ if (kvdb[d - 1] < kvdb[d + 1])
+ i1 = kvdb[d - 1];
+ else
+ i1 = kvdb[d + 1] - 1;
+ prev1 = i1;
+ i2 = i1 - d;
+ for (; i1 > off1 && i2 > off2 && ha1[i1 - 1] == ha2[i2 - 1]; i1--, i2--);
+ if (prev1 - i1 > xenv->snake_cnt)
+ got_snake = 1;
+ kvdb[d] = i1;
+ if (!odd && fmin <= d && d <= fmax && i1 <= kvdf[d]) {
+ spl->i1 = i1;
+ spl->i2 = i2;
+ spl->min_lo = spl->min_hi = 1;
+ return ec;
+ }
+ }
+
+ if (need_min)
+ continue;
+
+ /*
+ * If the edit cost is above the heuristic trigger and if
+ * we got a good snake, we sample current diagonals to see
+ * if some of the, have reached an "interesting" path. Our
+ * measure is a function of the distance from the diagonal
+ * corner (i1 + i2) penalized with the distance from the
+ * mid diagonal itself. If this value is above the current
+ * edit cost times a magic factor (XDL_K_HEUR) we consider
+ * it interesting.
+ */
+ if (got_snake && ec > xenv->heur_min) {
+ for (best = 0, d = fmax; d >= fmin; d -= 2) {
+ dd = d > fmid ? d - fmid: fmid - d;
+ i1 = kvdf[d];
+ i2 = i1 - d;
+ v = (i1 - off1) + (i2 - off2) - dd;
+
+ if (v > XDL_K_HEUR * ec && v > best &&
+ off1 + xenv->snake_cnt <= i1 && i1 < lim1 &&
+ off2 + xenv->snake_cnt <= i2 && i2 < lim2) {
+ for (k = 1; ha1[i1 - k] == ha2[i2 - k]; k++)
+ if (k == xenv->snake_cnt) {
+ best = v;
+ spl->i1 = i1;
+ spl->i2 = i2;
+ break;
+ }
+ }
+ }
+ if (best > 0) {
+ spl->min_lo = 1;
+ spl->min_hi = 0;
+ return ec;
+ }
+
+ for (best = 0, d = bmax; d >= bmin; d -= 2) {
+ dd = d > bmid ? d - bmid: bmid - d;
+ i1 = kvdb[d];
+ i2 = i1 - d;
+ v = (lim1 - i1) + (lim2 - i2) - dd;
+
+ if (v > XDL_K_HEUR * ec && v > best &&
+ off1 < i1 && i1 <= lim1 - xenv->snake_cnt &&
+ off2 < i2 && i2 <= lim2 - xenv->snake_cnt) {
+ for (k = 0; ha1[i1 + k] == ha2[i2 + k]; k++)
+ if (k == xenv->snake_cnt - 1) {
+ best = v;
+ spl->i1 = i1;
+ spl->i2 = i2;
+ break;
+ }
+ }
+ }
+ if (best > 0) {
+ spl->min_lo = 0;
+ spl->min_hi = 1;
+ return ec;
+ }
+ }
+
+ /*
+ * Enough is enough. We spent too much time here and now we collect
+ * the furthest reaching path using the (i1 + i2) measure.
+ */
+ if (ec >= xenv->mxcost) {
+ long fbest, fbest1, bbest, bbest1;
+
+ fbest = fbest1 = -1;
+ for (d = fmax; d >= fmin; d -= 2) {
+ i1 = XDL_MIN(kvdf[d], lim1);
+ i2 = i1 - d;
+ if (lim2 < i2)
+ i1 = lim2 + d, i2 = lim2;
+ if (fbest < i1 + i2) {
+ fbest = i1 + i2;
+ fbest1 = i1;
+ }
+ }
+
+ bbest = bbest1 = XDL_LINE_MAX;
+ for (d = bmax; d >= bmin; d -= 2) {
+ i1 = XDL_MAX(off1, kvdb[d]);
+ i2 = i1 - d;
+ if (i2 < off2)
+ i1 = off2 + d, i2 = off2;
+ if (i1 + i2 < bbest) {
+ bbest = i1 + i2;
+ bbest1 = i1;
+ }
+ }
+
+ if ((lim1 + lim2) - bbest < fbest - (off1 + off2)) {
+ spl->i1 = fbest1;
+ spl->i2 = fbest - fbest1;
+ spl->min_lo = 1;
+ spl->min_hi = 0;
+ } else {
+ spl->i1 = bbest1;
+ spl->i2 = bbest - bbest1;
+ spl->min_lo = 0;
+ spl->min_hi = 1;
+ }
+ return ec;
+ }
+ }
+}
+
+
+/*
+ * Rule: "Divide et Impera". Recursively split the box in sub-boxes by calling
+ * the box splitting function. Note that the real job (marking changed lines)
+ * is done in the two boundary reaching checks.
+ */
+int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,
+ diffdata_t *dd2, long off2, long lim2,
+ long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv) {
+ unsigned long const *ha1 = dd1->ha, *ha2 = dd2->ha;
+
+ /*
+ * Shrink the box by walking through each diagonal snake (SW and NE).
+ */
+ for (; off1 < lim1 && off2 < lim2 && ha1[off1] == ha2[off2]; off1++, off2++);
+ for (; off1 < lim1 && off2 < lim2 && ha1[lim1 - 1] == ha2[lim2 - 1]; lim1--, lim2--);
+
+ /*
+ * If one dimension is empty, then all records on the other one must
+ * be obviously changed.
+ */
+ if (off1 == lim1) {
+ char *rchg2 = dd2->rchg;
+ long *rindex2 = dd2->rindex;
+
+ for (; off2 < lim2; off2++)
+ rchg2[rindex2[off2]] = 1;
+ } else if (off2 == lim2) {
+ char *rchg1 = dd1->rchg;
+ long *rindex1 = dd1->rindex;
+
+ for (; off1 < lim1; off1++)
+ rchg1[rindex1[off1]] = 1;
+ } else {
+ xdpsplit_t spl;
+ spl.i1 = spl.i2 = 0;
+
+ /*
+ * Divide ...
+ */
+ if (xdl_split(ha1, off1, lim1, ha2, off2, lim2, kvdf, kvdb,
+ need_min, &spl, xenv) < 0) {
+
+ return -1;
+ }
+
+ /*
+ * ... et Impera.
+ */
+ if (xdl_recs_cmp(dd1, off1, spl.i1, dd2, off2, spl.i2,
+ kvdf, kvdb, spl.min_lo, xenv) < 0 ||
+ xdl_recs_cmp(dd1, spl.i1, lim1, dd2, spl.i2, lim2,
+ kvdf, kvdb, spl.min_hi, xenv) < 0) {
+
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+ xdfenv_t *xe) {
+ long ndiags;
+ long *kvd, *kvdf, *kvdb;
+ xdalgoenv_t xenv;
+ diffdata_t dd1, dd2;
+
+ if (xpp->flags & XDF_PATIENCE_DIFF)
+ return xdl_do_patience_diff(mf1, mf2, xpp, xe);
+
+ if (xpp->flags & XDF_HISTOGRAM_DIFF)
+ return xdl_do_histogram_diff(mf1, mf2, xpp, xe);
+
+ if (xdl_prepare_env(mf1, mf2, xpp, xe) < 0) {
+
+ return -1;
+ }
+
+ /*
+ * Allocate and setup K vectors to be used by the differential algorithm.
+ * One is to store the forward path and one to store the backward path.
+ */
+ ndiags = xe->xdf1.nreff + xe->xdf2.nreff + 3;
+ if (!(kvd = (long *) xdl_malloc((2 * ndiags + 2) * sizeof(long)))) {
+
+ xdl_free_env(xe);
+ return -1;
+ }
+ kvdf = kvd;
+ kvdb = kvdf + ndiags;
+ kvdf += xe->xdf2.nreff + 1;
+ kvdb += xe->xdf2.nreff + 1;
+
+ xenv.mxcost = xdl_bogosqrt(ndiags);
+ if (xenv.mxcost < XDL_MAX_COST_MIN)
+ xenv.mxcost = XDL_MAX_COST_MIN;
+ xenv.snake_cnt = XDL_SNAKE_CNT;
+ xenv.heur_min = XDL_HEUR_MIN_COST;
+
+ dd1.nrec = xe->xdf1.nreff;
+ dd1.ha = xe->xdf1.ha;
+ dd1.rchg = xe->xdf1.rchg;
+ dd1.rindex = xe->xdf1.rindex;
+ dd2.nrec = xe->xdf2.nreff;
+ dd2.ha = xe->xdf2.ha;
+ dd2.rchg = xe->xdf2.rchg;
+ dd2.rindex = xe->xdf2.rindex;
+
+ if (xdl_recs_cmp(&dd1, 0, dd1.nrec, &dd2, 0, dd2.nrec,
+ kvdf, kvdb, (xpp->flags & XDF_NEED_MINIMAL) != 0, &xenv) < 0) {
+
+ xdl_free(kvd);
+ xdl_free_env(xe);
+ return -1;
+ }
+
+ xdl_free(kvd);
+
+ return 0;
+}
+
+
+static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1, long chg2) {
+ xdchange_t *xch;
+
+ if (!(xch = (xdchange_t *) xdl_malloc(sizeof(xdchange_t))))
+ return NULL;
+
+ xch->next = xscr;
+ xch->i1 = i1;
+ xch->i2 = i2;
+ xch->chg1 = chg1;
+ xch->chg2 = chg2;
+
+ return xch;
+}
+
+
+int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {
+ long ix, ixo, ixs, ixref, grpsiz, nrec = xdf->nrec;
+ char *rchg = xdf->rchg, *rchgo = xdfo->rchg;
+ xrecord_t **recs = xdf->recs;
+
+ /*
+ * This is the same of what GNU diff does. Move back and forward
+ * change groups for a consistent and pretty diff output. This also
+ * helps in finding joinable change groups and reduce the diff size.
+ */
+ for (ix = ixo = 0;;) {
+ /*
+ * Find the first changed line in the to-be-compacted file.
+ * We need to keep track of both indexes, so if we find a
+ * changed lines group on the other file, while scanning the
+ * to-be-compacted file, we need to skip it properly. Note
+ * that loops that are testing for changed lines on rchg* do
+ * not need index bounding since the array is prepared with
+ * a zero at position -1 and N.
+ */
+ for (; ix < nrec && !rchg[ix]; ix++)
+ while (rchgo[ixo++]);
+ if (ix == nrec)
+ break;
+
+ /*
+ * Record the start of a changed-group in the to-be-compacted file
+ * and find the end of it, on both to-be-compacted and other file
+ * indexes (ix and ixo).
+ */
+ ixs = ix;
+ for (ix++; rchg[ix]; ix++);
+ for (; rchgo[ixo]; ixo++);
+
+ do {
+ grpsiz = ix - ixs;
+
+ /*
+ * If the line before the current change group, is equal to
+ * the last line of the current change group, shift backward
+ * the group.
+ */
+ while (ixs > 0 && recs[ixs - 1]->ha == recs[ix - 1]->ha &&
+ xdl_recmatch(recs[ixs - 1]->ptr, recs[ixs - 1]->size, recs[ix - 1]->ptr, recs[ix - 1]->size, flags)) {
+ rchg[--ixs] = 1;
+ rchg[--ix] = 0;
+
+ /*
+ * This change might have joined two change groups,
+ * so we try to take this scenario in account by moving
+ * the start index accordingly (and so the other-file
+ * end-of-group index).
+ */
+ for (; rchg[ixs - 1]; ixs--);
+ while (rchgo[--ixo]);
+ }
+
+ /*
+ * Record the end-of-group position in case we are matched
+ * with a group of changes in the other file (that is, the
+ * change record before the end-of-group index in the other
+ * file is set).
+ */
+ ixref = rchgo[ixo - 1] ? ix: nrec;
+
+ /*
+ * If the first line of the current change group, is equal to
+ * the line next of the current change group, shift forward
+ * the group.
+ */
+ while (ix < nrec && recs[ixs]->ha == recs[ix]->ha &&
+ xdl_recmatch(recs[ixs]->ptr, recs[ixs]->size, recs[ix]->ptr, recs[ix]->size, flags)) {
+ rchg[ixs++] = 0;
+ rchg[ix++] = 1;
+
+ /*
+ * This change might have joined two change groups,
+ * so we try to take this scenario in account by moving
+ * the start index accordingly (and so the other-file
+ * end-of-group index). Keep tracking the reference
+ * index in case we are shifting together with a
+ * corresponding group of changes in the other file.
+ */
+ for (; rchg[ix]; ix++);
+ while (rchgo[++ixo])
+ ixref = ix;
+ }
+ } while (grpsiz != ix - ixs);
+
+ /*
+ * Try to move back the possibly merged group of changes, to match
+ * the recorded postion in the other file.
+ */
+ while (ixref < ix) {
+ rchg[--ixs] = 1;
+ rchg[--ix] = 0;
+ while (rchgo[--ixo]);
+ }
+ }
+
+ return 0;
+}
+
+
+int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) {
+ xdchange_t *cscr = NULL, *xch;
+ char *rchg1 = xe->xdf1.rchg, *rchg2 = xe->xdf2.rchg;
+ long i1, i2, l1, l2;
+
+ /*
+ * Trivial. Collects "groups" of changes and creates an edit script.
+ */
+ for (i1 = xe->xdf1.nrec, i2 = xe->xdf2.nrec; i1 >= 0 || i2 >= 0; i1--, i2--)
+ if (rchg1[i1 - 1] || rchg2[i2 - 1]) {
+ for (l1 = i1; rchg1[i1 - 1]; i1--);
+ for (l2 = i2; rchg2[i2 - 1]; i2--);
+
+ if (!(xch = xdl_add_change(cscr, i1, i2, l1 - i1, l2 - i2))) {
+ xdl_free_script(cscr);
+ return -1;
+ }
+ cscr = xch;
+ }
+
+ *xscr = cscr;
+
+ return 0;
+}
+
+
+void xdl_free_script(xdchange_t *xscr) {
+ xdchange_t *xch;
+
+ while ((xch = xscr) != NULL) {
+ xscr = xscr->next;
+ xdl_free(xch);
+ }
+}
+
+
+int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+ xdemitconf_t const *xecfg, xdemitcb_t *ecb) {
+ xdchange_t *xscr;
+ xdfenv_t xe;
+ emit_func_t ef = xecfg->emit_func ?
+ (emit_func_t)xecfg->emit_func : xdl_emit_diff;
+
+ if (xdl_do_diff(mf1, mf2, xpp, &xe) < 0) {
+
+ return -1;
+ }
+ if (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 ||
+ xdl_change_compact(&xe.xdf2, &xe.xdf1, xpp->flags) < 0 ||
+ xdl_build_script(&xe, &xscr) < 0) {
+
+ xdl_free_env(&xe);
+ return -1;
+ }
+ if (xscr) {
+ if (ef(&xe, xscr, ecb, xecfg) < 0) {
+
+ xdl_free_script(xscr);
+ xdl_free_env(&xe);
+ return -1;
+ }
+ xdl_free_script(xscr);
+ }
+ xdl_free_env(&xe);
+
+ return 0;
+}
diff --git a/src/xdiff/xdiffi.h b/src/xdiff/xdiffi.h
new file mode 100644
index 000000000..7a92ea9c4
--- /dev/null
+++ b/src/xdiff/xdiffi.h
@@ -0,0 +1,63 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XDIFFI_H)
+#define XDIFFI_H
+
+
+typedef struct s_diffdata {
+ long nrec;
+ unsigned long const *ha;
+ long *rindex;
+ char *rchg;
+} diffdata_t;
+
+typedef struct s_xdalgoenv {
+ long mxcost;
+ long snake_cnt;
+ long heur_min;
+} xdalgoenv_t;
+
+typedef struct s_xdchange {
+ struct s_xdchange *next;
+ long i1, i2;
+ long chg1, chg2;
+} xdchange_t;
+
+
+
+int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,
+ diffdata_t *dd2, long off2, long lim2,
+ long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv);
+int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+ xdfenv_t *xe);
+int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags);
+int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr);
+void xdl_free_script(xdchange_t *xscr);
+int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+ xdemitconf_t const *xecfg);
+int xdl_do_patience_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+ xdfenv_t *env);
+int xdl_do_histogram_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+ xdfenv_t *env);
+
+#endif /* #if !defined(XDIFFI_H) */
diff --git a/src/xdiff/xemit.c b/src/xdiff/xemit.c
new file mode 100644
index 000000000..e3e63d902
--- /dev/null
+++ b/src/xdiff/xemit.c
@@ -0,0 +1,253 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#include "xinclude.h"
+
+
+
+
+static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec);
+static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb);
+
+
+
+
+static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec) {
+
+ *rec = xdf->recs[ri]->ptr;
+
+ return xdf->recs[ri]->size;
+}
+
+
+static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) {
+ long size, psize = (long)strlen(pre);
+ char const *rec;
+
+ size = xdl_get_rec(xdf, ri, &rec);
+ if (xdl_emit_diffrec(rec, size, pre, psize, ecb) < 0) {
+
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Starting at the passed change atom, find the latest change atom to be included
+ * inside the differential hunk according to the specified configuration.
+ */
+xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg) {
+ xdchange_t *xch, *xchp;
+ long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;
+
+ for (xchp = xscr, xch = xscr->next; xch; xchp = xch, xch = xch->next)
+ if (xch->i1 - (xchp->i1 + xchp->chg1) > max_common)
+ break;
+
+ return xchp;
+}
+
+
+static long def_ff(const char *rec, long len, char *buf, long sz, void *priv)
+{
+ (void)priv;
+
+ if (len > 0 &&
+ (isalpha((unsigned char)*rec) || /* identifier? */
+ *rec == '_' || /* also identifier? */
+ *rec == '$')) { /* identifiers from VMS and other esoterico */
+ if (len > sz)
+ len = sz;
+ while (0 < len && isspace((unsigned char)rec[len - 1]))
+ len--;
+ memcpy(buf, rec, len);
+ return len;
+ }
+ return -1;
+}
+
+static int xdl_emit_common(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+ xdemitconf_t const *xecfg) {
+ xdfile_t *xdf = &xe->xdf2;
+ const char *rchg = xdf->rchg;
+ long ix;
+
+ (void)xscr;
+ (void)xecfg;
+
+ for (ix = 0; ix < xdf->nrec; ix++) {
+ if (rchg[ix])
+ continue;
+ if (xdl_emit_record(xdf, ix, "", ecb))
+ return -1;
+ }
+ return 0;
+}
+
+struct func_line {
+ long len;
+ char buf[80];
+};
+
+static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,
+ struct func_line *func_line, long start, long limit)
+{
+ find_func_t ff = xecfg->find_func ? xecfg->find_func : def_ff;
+ long l, size, step = (start > limit) ? -1 : 1;
+ char *buf, dummy[1];
+
+ buf = func_line ? func_line->buf : dummy;
+ size = func_line ? sizeof(func_line->buf) : sizeof(dummy);
+
+ for (l = start; l != limit && 0 <= l && l < xe->xdf1.nrec; l += step) {
+ const char *rec;
+ long reclen = xdl_get_rec(&xe->xdf1, l, &rec);
+ long len = ff(rec, reclen, buf, size, xecfg->find_func_priv);
+ if (len >= 0) {
+ if (func_line)
+ func_line->len = len;
+ return l;
+ }
+ }
+ return -1;
+}
+
+int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+ xdemitconf_t const *xecfg) {
+ long s1, s2, e1, e2, lctx;
+ xdchange_t *xch, *xche;
+ long funclineprev = -1;
+ struct func_line func_line = { 0 };
+
+ if (xecfg->flags & XDL_EMIT_COMMON)
+ return xdl_emit_common(xe, xscr, ecb, xecfg);
+
+ for (xch = xscr; xch; xch = xche->next) {
+ xche = xdl_get_hunk(xch, xecfg);
+
+ s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);
+ s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);
+
+ if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) {
+ long fs1 = get_func_line(xe, xecfg, NULL, xch->i1, -1);
+ if (fs1 < 0)
+ fs1 = 0;
+ if (fs1 < s1) {
+ s2 -= s1 - fs1;
+ s1 = fs1;
+ }
+ }
+
+ again:
+ lctx = xecfg->ctxlen;
+ lctx = XDL_MIN(lctx, xe->xdf1.nrec - (xche->i1 + xche->chg1));
+ lctx = XDL_MIN(lctx, xe->xdf2.nrec - (xche->i2 + xche->chg2));
+
+ e1 = xche->i1 + xche->chg1 + lctx;
+ e2 = xche->i2 + xche->chg2 + lctx;
+
+ if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) {
+ long fe1 = get_func_line(xe, xecfg, NULL,
+ xche->i1 + xche->chg1,
+ xe->xdf1.nrec);
+ if (fe1 < 0)
+ fe1 = xe->xdf1.nrec;
+ if (fe1 > e1) {
+ e2 += fe1 - e1;
+ e1 = fe1;
+ }
+
+ /*
+ * Overlap with next change? Then include it
+ * in the current hunk and start over to find
+ * its new end.
+ */
+ if (xche->next) {
+ long l = xche->next->i1;
+ if (l <= e1 ||
+ get_func_line(xe, xecfg, NULL, l, e1) < 0) {
+ xche = xche->next;
+ goto again;
+ }
+ }
+ }
+
+ /*
+ * Emit current hunk header.
+ */
+
+ if (xecfg->flags & XDL_EMIT_FUNCNAMES) {
+ get_func_line(xe, xecfg, &func_line,
+ s1 - 1, funclineprev);
+ funclineprev = s1 - 1;
+ }
+ if (xdl_emit_hunk_hdr(s1 + 1, e1 - s1, s2 + 1, e2 - s2,
+ func_line.buf, func_line.len, ecb) < 0)
+ return -1;
+
+ /*
+ * Emit pre-context.
+ */
+ for (; s2 < xch->i2; s2++)
+ if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0)
+ return -1;
+
+ for (s1 = xch->i1, s2 = xch->i2;; xch = xch->next) {
+ /*
+ * Merge previous with current change atom.
+ */
+ for (; s1 < xch->i1 && s2 < xch->i2; s1++, s2++)
+ if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0)
+ return -1;
+
+ /*
+ * Removes lines from the first file.
+ */
+ for (s1 = xch->i1; s1 < xch->i1 + xch->chg1; s1++)
+ if (xdl_emit_record(&xe->xdf1, s1, "-", ecb) < 0)
+ return -1;
+
+ /*
+ * Adds lines from the second file.
+ */
+ for (s2 = xch->i2; s2 < xch->i2 + xch->chg2; s2++)
+ if (xdl_emit_record(&xe->xdf2, s2, "+", ecb) < 0)
+ return -1;
+
+ if (xch == xche)
+ break;
+ s1 = xch->i1 + xch->chg1;
+ s2 = xch->i2 + xch->chg2;
+ }
+
+ /*
+ * Emit post-context.
+ */
+ for (s2 = xche->i2 + xche->chg2; s2 < e2; s2++)
+ if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0)
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/xdiff/xemit.h b/src/xdiff/xemit.h
new file mode 100644
index 000000000..c2e2e8302
--- /dev/null
+++ b/src/xdiff/xemit.h
@@ -0,0 +1,36 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XEMIT_H)
+#define XEMIT_H
+
+
+typedef int (*emit_func_t)(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+ xdemitconf_t const *xecfg);
+
+xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg);
+int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+ xdemitconf_t const *xecfg);
+
+
+
+#endif /* #if !defined(XEMIT_H) */
diff --git a/src/xdiff/xhistogram.c b/src/xdiff/xhistogram.c
new file mode 100644
index 000000000..5d101754d
--- /dev/null
+++ b/src/xdiff/xhistogram.c
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in JGit's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "xinclude.h"
+#include "xtypes.h"
+#include "xdiff.h"
+
+#define MAX_PTR UINT_MAX
+#define MAX_CNT UINT_MAX
+
+#define LINE_END(n) (line##n + count##n - 1)
+#define LINE_END_PTR(n) (*line##n + *count##n - 1)
+
+struct histindex {
+ struct record {
+ unsigned int ptr, cnt;
+ struct record *next;
+ } **records, /* an ocurrence */
+ **line_map; /* map of line to record chain */
+ chastore_t rcha;
+ unsigned int *next_ptrs;
+ unsigned int table_bits,
+ records_size,
+ line_map_size;
+
+ unsigned int max_chain_length,
+ key_shift,
+ ptr_shift;
+
+ unsigned int cnt,
+ has_common;
+
+ xdfenv_t *env;
+ xpparam_t const *xpp;
+};
+
+struct region {
+ unsigned int begin1, end1;
+ unsigned int begin2, end2;
+};
+
+#define LINE_MAP(i, a) (i->line_map[(a) - i->ptr_shift])
+
+#define NEXT_PTR(index, ptr) \
+ (index->next_ptrs[(ptr) - index->ptr_shift])
+
+#define CNT(index, ptr) \
+ ((LINE_MAP(index, ptr))->cnt)
+
+#define REC(env, s, l) \
+ (env->xdf##s.recs[l - 1])
+
+static int cmp_recs(xpparam_t const *xpp,
+ xrecord_t *r1, xrecord_t *r2)
+{
+ return r1->ha == r2->ha &&
+ xdl_recmatch(r1->ptr, r1->size, r2->ptr, r2->size,
+ xpp->flags);
+}
+
+#define CMP_ENV(xpp, env, s1, l1, s2, l2) \
+ (cmp_recs(xpp, REC(env, s1, l1), REC(env, s2, l2)))
+
+#define CMP(i, s1, l1, s2, l2) \
+ (cmp_recs(i->xpp, REC(i->env, s1, l1), REC(i->env, s2, l2)))
+
+#define TABLE_HASH(index, side, line) \
+ XDL_HASHLONG((REC(index->env, side, line))->ha, index->table_bits)
+
+static int scanA(struct histindex *index, unsigned int line1, unsigned int count1)
+{
+ unsigned int ptr;
+ unsigned int tbl_idx;
+ unsigned int chain_len;
+ struct record **rec_chain, *rec;
+
+ for (ptr = LINE_END(1); line1 <= ptr; ptr--) {
+ tbl_idx = TABLE_HASH(index, 1, ptr);
+ rec_chain = index->records + tbl_idx;
+ rec = *rec_chain;
+
+ chain_len = 0;
+ while (rec) {
+ if (CMP(index, 1, rec->ptr, 1, ptr)) {
+ /*
+ * ptr is identical to another element. Insert
+ * it onto the front of the existing element
+ * chain.
+ */
+ NEXT_PTR(index, ptr) = rec->ptr;
+ rec->ptr = ptr;
+ /* cap rec->cnt at MAX_CNT */
+ rec->cnt = XDL_MIN(MAX_CNT, rec->cnt + 1);
+ LINE_MAP(index, ptr) = rec;
+ goto continue_scan;
+ }
+
+ rec = rec->next;
+ chain_len++;
+ }
+
+ if (chain_len == index->max_chain_length)
+ return -1;
+
+ /*
+ * This is the first time we have ever seen this particular
+ * element in the sequence. Construct a new chain for it.
+ */
+ if (!(rec = xdl_cha_alloc(&index->rcha)))
+ return -1;
+ rec->ptr = ptr;
+ rec->cnt = 1;
+ rec->next = *rec_chain;
+ *rec_chain = rec;
+ LINE_MAP(index, ptr) = rec;
+
+continue_scan:
+ ; /* no op */
+ }
+
+ return 0;
+}
+
+static int try_lcs(
+ struct histindex *index, struct region *lcs, unsigned int b_ptr,
+ unsigned int line1, unsigned int count1,
+ unsigned int line2, unsigned int count2)
+{
+ unsigned int b_next = b_ptr + 1;
+ struct record *rec = index->records[TABLE_HASH(index, 2, b_ptr)];
+ unsigned int as, ae, bs, be, np, rc;
+ int should_break;
+
+ for (; rec; rec = rec->next) {
+ if (rec->cnt > index->cnt) {
+ if (!index->has_common)
+ index->has_common = CMP(index, 1, rec->ptr, 2, b_ptr);
+ continue;
+ }
+
+ as = rec->ptr;
+ if (!CMP(index, 1, as, 2, b_ptr))
+ continue;
+
+ index->has_common = 1;
+ for (;;) {
+ should_break = 0;
+ np = NEXT_PTR(index, as);
+ bs = b_ptr;
+ ae = as;
+ be = bs;
+ rc = rec->cnt;
+
+ while (line1 < as && line2 < bs
+ && CMP(index, 1, as - 1, 2, bs - 1)) {
+ as--;
+ bs--;
+ if (1 < rc)
+ rc = XDL_MIN(rc, CNT(index, as));
+ }
+ while (ae < LINE_END(1) && be < LINE_END(2)
+ && CMP(index, 1, ae + 1, 2, be + 1)) {
+ ae++;
+ be++;
+ if (1 < rc)
+ rc = XDL_MIN(rc, CNT(index, ae));
+ }
+
+ if (b_next <= be)
+ b_next = be + 1;
+ if (lcs->end1 - lcs->begin1 < ae - as || rc < index->cnt) {
+ lcs->begin1 = as;
+ lcs->begin2 = bs;
+ lcs->end1 = ae;
+ lcs->end2 = be;
+ index->cnt = rc;
+ }
+
+ if (np == 0)
+ break;
+
+ while (np <= ae) {
+ np = NEXT_PTR(index, np);
+ if (np == 0) {
+ should_break = 1;
+ break;
+ }
+ }
+
+ if (should_break)
+ break;
+
+ as = np;
+ }
+ }
+ return b_next;
+}
+
+static int find_lcs(
+ struct histindex *index, struct region *lcs,
+ unsigned int line1, unsigned int count1,
+ unsigned int line2, unsigned int count2)
+{
+ unsigned int b_ptr;
+
+ if (scanA(index, line1, count1))
+ return -1;
+
+ index->cnt = index->max_chain_length + 1;
+
+ for (b_ptr = line2; b_ptr <= LINE_END(2); )
+ b_ptr = try_lcs(index, lcs, b_ptr, line1, count1, line2, count2);
+
+ return index->has_common && index->max_chain_length < index->cnt;
+}
+
+static int fall_back_to_classic_diff(struct histindex *index,
+ int line1, int count1, int line2, int count2)
+{
+ xpparam_t xpp;
+ xpp.flags = index->xpp->flags & ~XDF_HISTOGRAM_DIFF;
+
+ return xdl_fall_back_diff(index->env, &xpp,
+ line1, count1, line2, count2);
+}
+
+static int histogram_diff(
+ xpparam_t const *xpp, xdfenv_t *env,
+ unsigned int line1, unsigned int count1,
+ unsigned int line2, unsigned int count2)
+{
+ struct histindex index;
+ struct region lcs;
+ unsigned int sz;
+ int result = -1;
+
+ if (count1 <= 0 && count2 <= 0)
+ return 0;
+
+ if (LINE_END(1) >= MAX_PTR)
+ return -1;
+
+ if (!count1) {
+ while(count2--)
+ env->xdf2.rchg[line2++ - 1] = 1;
+ return 0;
+ } else if (!count2) {
+ while(count1--)
+ env->xdf1.rchg[line1++ - 1] = 1;
+ return 0;
+ }
+
+ memset(&index, 0, sizeof(index));
+
+ index.env = env;
+ index.xpp = xpp;
+
+ index.records = NULL;
+ index.line_map = NULL;
+ /* in case of early xdl_cha_free() */
+ index.rcha.head = NULL;
+
+ index.table_bits = xdl_hashbits(count1);
+ sz = index.records_size = 1 << index.table_bits;
+ sz *= sizeof(struct record *);
+ if (!(index.records = (struct record **) xdl_malloc(sz)))
+ goto cleanup;
+ memset(index.records, 0, sz);
+
+ sz = index.line_map_size = count1;
+ sz *= sizeof(struct record *);
+ if (!(index.line_map = (struct record **) xdl_malloc(sz)))
+ goto cleanup;
+ memset(index.line_map, 0, sz);
+
+ sz = index.line_map_size;
+ sz *= sizeof(unsigned int);
+ if (!(index.next_ptrs = (unsigned int *) xdl_malloc(sz)))
+ goto cleanup;
+ memset(index.next_ptrs, 0, sz);
+
+ /* lines / 4 + 1 comes from xprepare.c:xdl_prepare_ctx() */
+ if (xdl_cha_init(&index.rcha, sizeof(struct record), count1 / 4 + 1) < 0)
+ goto cleanup;
+
+ index.ptr_shift = line1;
+ index.max_chain_length = 64;
+
+ memset(&lcs, 0, sizeof(lcs));
+ if (find_lcs(&index, &lcs, line1, count1, line2, count2))
+ result = fall_back_to_classic_diff(&index, line1, count1, line2, count2);
+ else {
+ if (lcs.begin1 == 0 && lcs.begin2 == 0) {
+ while (count1--)
+ env->xdf1.rchg[line1++ - 1] = 1;
+ while (count2--)
+ env->xdf2.rchg[line2++ - 1] = 1;
+ result = 0;
+ } else {
+ result = histogram_diff(xpp, env,
+ line1, lcs.begin1 - line1,
+ line2, lcs.begin2 - line2);
+ if (result)
+ goto cleanup;
+ result = histogram_diff(xpp, env,
+ lcs.end1 + 1, LINE_END(1) - lcs.end1,
+ lcs.end2 + 1, LINE_END(2) - lcs.end2);
+ if (result)
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ xdl_free(index.records);
+ xdl_free(index.line_map);
+ xdl_free(index.next_ptrs);
+ xdl_cha_free(&index.rcha);
+
+ return result;
+}
+
+int xdl_do_histogram_diff(mmfile_t *file1, mmfile_t *file2,
+ xpparam_t const *xpp, xdfenv_t *env)
+{
+ if (xdl_prepare_env(file1, file2, xpp, env) < 0)
+ return -1;
+
+ return histogram_diff(xpp, env,
+ env->xdf1.dstart + 1, env->xdf1.dend - env->xdf1.dstart + 1,
+ env->xdf2.dstart + 1, env->xdf2.dend - env->xdf2.dstart + 1);
+}
diff --git a/src/xdiff/xinclude.h b/src/xdiff/xinclude.h
new file mode 100644
index 000000000..4a1cde909
--- /dev/null
+++ b/src/xdiff/xinclude.h
@@ -0,0 +1,46 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XINCLUDE_H)
+#define XINCLUDE_H
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#ifdef _WIN32
+#else
+#include <unistd.h>
+#endif
+
+#include "xmacros.h"
+#include "xdiff.h"
+#include "xtypes.h"
+#include "xutils.h"
+#include "xprepare.h"
+#include "xdiffi.h"
+#include "xemit.h"
+
+
+#endif /* #if !defined(XINCLUDE_H) */
diff --git a/src/xdiff/xmacros.h b/src/xdiff/xmacros.h
new file mode 100644
index 000000000..165a895a9
--- /dev/null
+++ b/src/xdiff/xmacros.h
@@ -0,0 +1,54 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XMACROS_H)
+#define XMACROS_H
+
+
+
+
+#define XDL_MIN(a, b) ((a) < (b) ? (a): (b))
+#define XDL_MAX(a, b) ((a) > (b) ? (a): (b))
+#define XDL_ABS(v) ((v) >= 0 ? (v): -(v))
+#define XDL_ISDIGIT(c) ((c) >= '0' && (c) <= '9')
+#define XDL_ISSPACE(c) (isspace((unsigned char)(c)))
+#define XDL_ADDBITS(v,b) ((v) + ((v) >> (b)))
+#define XDL_MASKBITS(b) ((1UL << (b)) - 1)
+#define XDL_HASHLONG(v,b) (XDL_ADDBITS((unsigned long)(v), b) & XDL_MASKBITS(b))
+#define XDL_PTRFREE(p) do { if (p) { xdl_free(p); (p) = NULL; } } while (0)
+#define XDL_LE32_PUT(p, v) \
+do { \
+ unsigned char *__p = (unsigned char *) (p); \
+ *__p++ = (unsigned char) (v); \
+ *__p++ = (unsigned char) ((v) >> 8); \
+ *__p++ = (unsigned char) ((v) >> 16); \
+ *__p = (unsigned char) ((v) >> 24); \
+} while (0)
+#define XDL_LE32_GET(p, v) \
+do { \
+ unsigned char const *__p = (unsigned char const *) (p); \
+ (v) = (unsigned long) __p[0] | ((unsigned long) __p[1]) << 8 | \
+ ((unsigned long) __p[2]) << 16 | ((unsigned long) __p[3]) << 24; \
+} while (0)
+
+
+#endif /* #if !defined(XMACROS_H) */
diff --git a/src/xdiff/xmerge.c b/src/xdiff/xmerge.c
new file mode 100644
index 000000000..84e424672
--- /dev/null
+++ b/src/xdiff/xmerge.c
@@ -0,0 +1,619 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003-2006 Davide Libenzi, Johannes E. Schindelin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#include "xinclude.h"
+
+typedef struct s_xdmerge {
+ struct s_xdmerge *next;
+ /*
+ * 0 = conflict,
+ * 1 = no conflict, take first,
+ * 2 = no conflict, take second.
+ * 3 = no conflict, take both.
+ */
+ int mode;
+ /*
+ * These point at the respective postimages. E.g. <i1,chg1> is
+ * how side #1 wants to change the common ancestor; if there is no
+ * overlap, lines before i1 in the postimage of side #1 appear
+ * in the merge result as a region touched by neither side.
+ */
+ long i1, i2;
+ long chg1, chg2;
+ /*
+ * These point at the preimage; of course there is just one
+ * preimage, that is from the shared common ancestor.
+ */
+ long i0;
+ long chg0;
+} xdmerge_t;
+
+static int xdl_append_merge(xdmerge_t **merge, int mode,
+ long i0, long chg0,
+ long i1, long chg1,
+ long i2, long chg2)
+{
+ xdmerge_t *m = *merge;
+ if (m && (i1 <= m->i1 + m->chg1 || i2 <= m->i2 + m->chg2)) {
+ if (mode != m->mode)
+ m->mode = 0;
+ m->chg0 = i0 + chg0 - m->i0;
+ m->chg1 = i1 + chg1 - m->i1;
+ m->chg2 = i2 + chg2 - m->i2;
+ } else {
+ m = xdl_malloc(sizeof(xdmerge_t));
+ if (!m)
+ return -1;
+ m->next = NULL;
+ m->mode = mode;
+ m->i0 = i0;
+ m->chg0 = chg0;
+ m->i1 = i1;
+ m->chg1 = chg1;
+ m->i2 = i2;
+ m->chg2 = chg2;
+ if (*merge)
+ (*merge)->next = m;
+ *merge = m;
+ }
+ return 0;
+}
+
+static int xdl_cleanup_merge(xdmerge_t *c)
+{
+ int count = 0;
+ xdmerge_t *next_c;
+
+ /* were there conflicts? */
+ for (; c; c = next_c) {
+ if (c->mode == 0)
+ count++;
+ next_c = c->next;
+ free(c);
+ }
+ return count;
+}
+
+static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,
+ int line_count, long flags)
+{
+ int i;
+ xrecord_t **rec1 = xe1->xdf2.recs + i1;
+ xrecord_t **rec2 = xe2->xdf2.recs + i2;
+
+ for (i = 0; i < line_count; i++) {
+ int result = xdl_recmatch(rec1[i]->ptr, rec1[i]->size,
+ rec2[i]->ptr, rec2[i]->size, flags);
+ if (!result)
+ return -1;
+ }
+ return 0;
+}
+
+static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int add_nl, char *dest)
+{
+ xrecord_t **recs;
+ int size = 0;
+
+ recs = (use_orig ? xe->xdf1.recs : xe->xdf2.recs) + i;
+
+ if (count < 1)
+ return 0;
+
+ for (i = 0; i < count; size += recs[i++]->size)
+ if (dest)
+ memcpy(dest + size, recs[i]->ptr, recs[i]->size);
+ if (add_nl) {
+ i = recs[count - 1]->size;
+ if (i == 0 || recs[count - 1]->ptr[i - 1] != '\n') {
+ if (dest)
+ dest[size] = '\n';
+ size++;
+ }
+ }
+ return size;
+}
+
+static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)
+{
+ return xdl_recs_copy_0(0, xe, i, count, add_nl, dest);
+}
+
+static int xdl_orig_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)
+{
+ return xdl_recs_copy_0(1, xe, i, count, add_nl, dest);
+}
+
+static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
+ xdfenv_t *xe2, const char *name2,
+ const char *name3,
+ int size, int i, int style,
+ xdmerge_t *m, char *dest, int marker_size)
+{
+ int marker1_size = (name1 ? (int)strlen(name1) + 1 : 0);
+ int marker2_size = (name2 ? (int)strlen(name2) + 1 : 0);
+ int marker3_size = (name3 ? (int)strlen(name3) + 1 : 0);
+
+ if (marker_size <= 0)
+ marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
+
+ /* Before conflicting part */
+ size += xdl_recs_copy(xe1, i, m->i1 - i, 0,
+ dest ? dest + size : NULL);
+
+ if (!dest) {
+ size += marker_size + 1 + marker1_size;
+ } else {
+ memset(dest + size, '<', marker_size);
+ size += marker_size;
+ if (marker1_size) {
+ dest[size] = ' ';
+ memcpy(dest + size + 1, name1, marker1_size - 1);
+ size += marker1_size;
+ }
+ dest[size++] = '\n';
+ }
+
+ /* Postimage from side #1 */
+ size += xdl_recs_copy(xe1, m->i1, m->chg1, 1,
+ dest ? dest + size : NULL);
+
+ if (style == XDL_MERGE_DIFF3) {
+ /* Shared preimage */
+ if (!dest) {
+ size += marker_size + 1 + marker3_size;
+ } else {
+ memset(dest + size, '|', marker_size);
+ size += marker_size;
+ if (marker3_size) {
+ dest[size] = ' ';
+ memcpy(dest + size + 1, name3, marker3_size - 1);
+ size += marker3_size;
+ }
+ dest[size++] = '\n';
+ }
+ size += xdl_orig_copy(xe1, m->i0, m->chg0, 1,
+ dest ? dest + size : NULL);
+ }
+
+ if (!dest) {
+ size += marker_size + 1;
+ } else {
+ memset(dest + size, '=', marker_size);
+ size += marker_size;
+ dest[size++] = '\n';
+ }
+
+ /* Postimage from side #2 */
+ size += xdl_recs_copy(xe2, m->i2, m->chg2, 1,
+ dest ? dest + size : NULL);
+ if (!dest) {
+ size += marker_size + 1 + marker2_size;
+ } else {
+ memset(dest + size, '>', marker_size);
+ size += marker_size;
+ if (marker2_size) {
+ dest[size] = ' ';
+ memcpy(dest + size + 1, name2, marker2_size - 1);
+ size += marker2_size;
+ }
+ dest[size++] = '\n';
+ }
+ return size;
+}
+
+static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
+ xdfenv_t *xe2, const char *name2,
+ const char *ancestor_name,
+ int favor,
+ xdmerge_t *m, char *dest, int style,
+ int marker_size)
+{
+ int size, i;
+
+ for (size = i = 0; m; m = m->next) {
+ if (favor && !m->mode)
+ m->mode = favor;
+
+ if (m->mode == 0)
+ size = fill_conflict_hunk(xe1, name1, xe2, name2,
+ ancestor_name,
+ size, i, style, m, dest,
+ marker_size);
+ else if (m->mode & 3) {
+ /* Before conflicting part */
+ size += xdl_recs_copy(xe1, i, m->i1 - i, 0,
+ dest ? dest + size : NULL);
+ /* Postimage from side #1 */
+ if (m->mode & 1)
+ size += xdl_recs_copy(xe1, m->i1, m->chg1, 1,
+ dest ? dest + size : NULL);
+ /* Postimage from side #2 */
+ if (m->mode & 2)
+ size += xdl_recs_copy(xe2, m->i2, m->chg2, 1,
+ dest ? dest + size : NULL);
+ } else
+ continue;
+ i = m->i1 + m->chg1;
+ }
+ size += xdl_recs_copy(xe1, i, xe1->xdf2.nrec - i, 0,
+ dest ? dest + size : NULL);
+ return size;
+}
+
+/*
+ * Sometimes, changes are not quite identical, but differ in only a few
+ * lines. Try hard to show only these few lines as conflicting.
+ */
+static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,
+ xpparam_t const *xpp)
+{
+ for (; m; m = m->next) {
+ mmfile_t t1, t2;
+ xdfenv_t xe;
+ xdchange_t *xscr, *x;
+ int i1 = m->i1, i2 = m->i2;
+
+ /* let's handle just the conflicts */
+ if (m->mode)
+ continue;
+
+ /* no sense refining a conflict when one side is empty */
+ if (m->chg1 == 0 || m->chg2 == 0)
+ continue;
+
+ /*
+ * This probably does not work outside git, since
+ * we have a very simple mmfile structure.
+ */
+ t1.ptr = (char *)xe1->xdf2.recs[m->i1]->ptr;
+ t1.size = xe1->xdf2.recs[m->i1 + m->chg1 - 1]->ptr
+ + xe1->xdf2.recs[m->i1 + m->chg1 - 1]->size - t1.ptr;
+ t2.ptr = (char *)xe2->xdf2.recs[m->i2]->ptr;
+ t2.size = xe2->xdf2.recs[m->i2 + m->chg2 - 1]->ptr
+ + xe2->xdf2.recs[m->i2 + m->chg2 - 1]->size - t2.ptr;
+ if (xdl_do_diff(&t1, &t2, xpp, &xe) < 0)
+ return -1;
+ if (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 ||
+ xdl_change_compact(&xe.xdf2, &xe.xdf1, xpp->flags) < 0 ||
+ xdl_build_script(&xe, &xscr) < 0) {
+ xdl_free_env(&xe);
+ return -1;
+ }
+ if (!xscr) {
+ /* If this happens, the changes are identical. */
+ xdl_free_env(&xe);
+ m->mode = 4;
+ continue;
+ }
+ x = xscr;
+ m->i1 = xscr->i1 + i1;
+ m->chg1 = xscr->chg1;
+ m->i2 = xscr->i2 + i2;
+ m->chg2 = xscr->chg2;
+ while (xscr->next) {
+ xdmerge_t *m2 = xdl_malloc(sizeof(xdmerge_t));
+ if (!m2) {
+ xdl_free_env(&xe);
+ xdl_free_script(x);
+ return -1;
+ }
+ xscr = xscr->next;
+ m2->next = m->next;
+ m->next = m2;
+ m = m2;
+ m->mode = 0;
+ m->i1 = xscr->i1 + i1;
+ m->chg1 = xscr->chg1;
+ m->i2 = xscr->i2 + i2;
+ m->chg2 = xscr->chg2;
+ }
+ xdl_free_env(&xe);
+ xdl_free_script(x);
+ }
+ return 0;
+}
+
+static int line_contains_alnum(const char *ptr, long size)
+{
+ while (size--)
+ if (isalnum((unsigned char)*(ptr++)))
+ return 1;
+ return 0;
+}
+
+static int lines_contain_alnum(xdfenv_t *xe, int i, int chg)
+{
+ for (; chg; chg--, i++)
+ if (line_contains_alnum(xe->xdf2.recs[i]->ptr,
+ xe->xdf2.recs[i]->size))
+ return 1;
+ return 0;
+}
+
+/*
+ * This function merges m and m->next, marking everything between those hunks
+ * as conflicting, too.
+ */
+static void xdl_merge_two_conflicts(xdmerge_t *m)
+{
+ xdmerge_t *next_m = m->next;
+ m->chg1 = next_m->i1 + next_m->chg1 - m->i1;
+ m->chg2 = next_m->i2 + next_m->chg2 - m->i2;
+ m->next = next_m->next;
+ free(next_m);
+}
+
+/*
+ * If there are less than 3 non-conflicting lines between conflicts,
+ * it appears simpler -- because it takes up less (or as many) lines --
+ * if the lines are moved into the conflicts.
+ */
+static int xdl_simplify_non_conflicts(xdfenv_t *xe1, xdmerge_t *m,
+ int simplify_if_no_alnum)
+{
+ int result = 0;
+
+ if (!m)
+ return result;
+ for (;;) {
+ xdmerge_t *next_m = m->next;
+ int begin, end;
+
+ if (!next_m)
+ return result;
+
+ begin = m->i1 + m->chg1;
+ end = next_m->i1;
+
+ if (m->mode != 0 || next_m->mode != 0 ||
+ (end - begin > 3 &&
+ (!simplify_if_no_alnum ||
+ lines_contain_alnum(xe1, begin, end - begin)))) {
+ m = next_m;
+ } else {
+ result++;
+ xdl_merge_two_conflicts(m);
+ }
+ }
+}
+
+/*
+ * level == 0: mark all overlapping changes as conflict
+ * level == 1: mark overlapping changes as conflict only if not identical
+ * level == 2: analyze non-identical changes for minimal conflict set
+ * level == 3: analyze non-identical changes for minimal conflict set, but
+ * treat hunks not containing any letter or number as conflicting
+ *
+ * returns < 0 on error, == 0 for no conflicts, else number of conflicts
+ */
+static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,
+ xdfenv_t *xe2, xdchange_t *xscr2,
+ xmparam_t const *xmp, mmbuffer_t *result)
+{
+ xdmerge_t *changes, *c;
+ xpparam_t const *xpp = &xmp->xpp;
+ const char *const ancestor_name = xmp->ancestor;
+ const char *const name1 = xmp->file1;
+ const char *const name2 = xmp->file2;
+ int i0, i1, i2, chg0, chg1, chg2;
+ int level = xmp->level;
+ int style = xmp->style;
+ int favor = xmp->favor;
+
+ if (style == XDL_MERGE_DIFF3) {
+ /*
+ * "diff3 -m" output does not make sense for anything
+ * more aggressive than XDL_MERGE_EAGER.
+ */
+ if (XDL_MERGE_EAGER < level)
+ level = XDL_MERGE_EAGER;
+ }
+
+ c = changes = NULL;
+
+ while (xscr1 && xscr2) {
+ if (!changes)
+ changes = c;
+ if (xscr1->i1 + xscr1->chg1 < xscr2->i1) {
+ i0 = xscr1->i1;
+ i1 = xscr1->i2;
+ i2 = xscr2->i2 - xscr2->i1 + xscr1->i1;
+ chg0 = xscr1->chg1;
+ chg1 = xscr1->chg2;
+ chg2 = xscr1->chg1;
+ if (xdl_append_merge(&c, 1,
+ i0, chg0, i1, chg1, i2, chg2)) {
+ xdl_cleanup_merge(changes);
+ return -1;
+ }
+ xscr1 = xscr1->next;
+ continue;
+ }
+ if (xscr2->i1 + xscr2->chg1 < xscr1->i1) {
+ i0 = xscr2->i1;
+ i1 = xscr1->i2 - xscr1->i1 + xscr2->i1;
+ i2 = xscr2->i2;
+ chg0 = xscr2->chg1;
+ chg1 = xscr2->chg1;
+ chg2 = xscr2->chg2;
+ if (xdl_append_merge(&c, 2,
+ i0, chg0, i1, chg1, i2, chg2)) {
+ xdl_cleanup_merge(changes);
+ return -1;
+ }
+ xscr2 = xscr2->next;
+ continue;
+ }
+ if (level == XDL_MERGE_MINIMAL || xscr1->i1 != xscr2->i1 ||
+ xscr1->chg1 != xscr2->chg1 ||
+ xscr1->chg2 != xscr2->chg2 ||
+ xdl_merge_cmp_lines(xe1, xscr1->i2,
+ xe2, xscr2->i2,
+ xscr1->chg2, xpp->flags)) {
+ /* conflict */
+ int off = xscr1->i1 - xscr2->i1;
+ int ffo = off + xscr1->chg1 - xscr2->chg1;
+
+ i0 = xscr1->i1;
+ i1 = xscr1->i2;
+ i2 = xscr2->i2;
+ if (off > 0) {
+ i0 -= off;
+ i1 -= off;
+ }
+ else
+ i2 += off;
+ chg0 = xscr1->i1 + xscr1->chg1 - i0;
+ chg1 = xscr1->i2 + xscr1->chg2 - i1;
+ chg2 = xscr2->i2 + xscr2->chg2 - i2;
+ if (ffo < 0) {
+ chg0 -= ffo;
+ chg1 -= ffo;
+ } else
+ chg2 += ffo;
+ if (xdl_append_merge(&c, 0,
+ i0, chg0, i1, chg1, i2, chg2)) {
+ xdl_cleanup_merge(changes);
+ return -1;
+ }
+ }
+
+ i1 = xscr1->i1 + xscr1->chg1;
+ i2 = xscr2->i1 + xscr2->chg1;
+
+ if (i1 >= i2)
+ xscr2 = xscr2->next;
+ if (i2 >= i1)
+ xscr1 = xscr1->next;
+ }
+ while (xscr1) {
+ if (!changes)
+ changes = c;
+ i0 = xscr1->i1;
+ i1 = xscr1->i2;
+ i2 = xscr1->i1 + xe2->xdf2.nrec - xe2->xdf1.nrec;
+ chg0 = xscr1->chg1;
+ chg1 = xscr1->chg2;
+ chg2 = xscr1->chg1;
+ if (xdl_append_merge(&c, 1,
+ i0, chg0, i1, chg1, i2, chg2)) {
+ xdl_cleanup_merge(changes);
+ return -1;
+ }
+ xscr1 = xscr1->next;
+ }
+ while (xscr2) {
+ if (!changes)
+ changes = c;
+ i0 = xscr2->i1;
+ i1 = xscr2->i1 + xe1->xdf2.nrec - xe1->xdf1.nrec;
+ i2 = xscr2->i2;
+ chg0 = xscr2->chg1;
+ chg1 = xscr2->chg1;
+ chg2 = xscr2->chg2;
+ if (xdl_append_merge(&c, 2,
+ i0, chg0, i1, chg1, i2, chg2)) {
+ xdl_cleanup_merge(changes);
+ return -1;
+ }
+ xscr2 = xscr2->next;
+ }
+ if (!changes)
+ changes = c;
+ /* refine conflicts */
+ if (XDL_MERGE_ZEALOUS <= level &&
+ (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 ||
+ xdl_simplify_non_conflicts(xe1, changes,
+ XDL_MERGE_ZEALOUS < level) < 0)) {
+ xdl_cleanup_merge(changes);
+ return -1;
+ }
+ /* output */
+ if (result) {
+ int marker_size = xmp->marker_size;
+ int size = xdl_fill_merge_buffer(xe1, name1, xe2, name2,
+ ancestor_name,
+ favor, changes, NULL, style,
+ marker_size);
+ result->ptr = xdl_malloc(size);
+ if (!result->ptr) {
+ xdl_cleanup_merge(changes);
+ return -1;
+ }
+ result->size = size;
+ xdl_fill_merge_buffer(xe1, name1, xe2, name2,
+ ancestor_name, favor, changes,
+ result->ptr, style, marker_size);
+ }
+ return xdl_cleanup_merge(changes);
+}
+
+int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2,
+ xmparam_t const *xmp, mmbuffer_t *result)
+{
+ xdchange_t *xscr1, *xscr2;
+ xdfenv_t xe1, xe2;
+ int status;
+ xpparam_t const *xpp = &xmp->xpp;
+
+ result->ptr = NULL;
+ result->size = 0;
+
+ if (xdl_do_diff(orig, mf1, xpp, &xe1) < 0 ||
+ xdl_do_diff(orig, mf2, xpp, &xe2) < 0) {
+ return -1;
+ }
+ if (xdl_change_compact(&xe1.xdf1, &xe1.xdf2, xpp->flags) < 0 ||
+ xdl_change_compact(&xe1.xdf2, &xe1.xdf1, xpp->flags) < 0 ||
+ xdl_build_script(&xe1, &xscr1) < 0) {
+ xdl_free_env(&xe1);
+ return -1;
+ }
+ if (xdl_change_compact(&xe2.xdf1, &xe2.xdf2, xpp->flags) < 0 ||
+ xdl_change_compact(&xe2.xdf2, &xe2.xdf1, xpp->flags) < 0 ||
+ xdl_build_script(&xe2, &xscr2) < 0) {
+ xdl_free_env(&xe2);
+ return -1;
+ }
+ status = 0;
+ if (!xscr1) {
+ result->ptr = xdl_malloc(mf2->size);
+ memcpy(result->ptr, mf2->ptr, mf2->size);
+ result->size = mf2->size;
+ } else if (!xscr2) {
+ result->ptr = xdl_malloc(mf1->size);
+ memcpy(result->ptr, mf1->ptr, mf1->size);
+ result->size = mf1->size;
+ } else {
+ status = xdl_do_merge(&xe1, xscr1,
+ &xe2, xscr2,
+ xmp, result);
+ }
+ xdl_free_script(xscr1);
+ xdl_free_script(xscr2);
+
+ xdl_free_env(&xe1);
+ xdl_free_env(&xe2);
+
+ return status;
+}
diff --git a/src/xdiff/xpatience.c b/src/xdiff/xpatience.c
new file mode 100644
index 000000000..fdd7d0263
--- /dev/null
+++ b/src/xdiff/xpatience.c
@@ -0,0 +1,358 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003-2009 Davide Libenzi, Johannes E. Schindelin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+#include "xinclude.h"
+#include "xtypes.h"
+#include "xdiff.h"
+
+/*
+ * The basic idea of patience diff is to find lines that are unique in
+ * both files. These are intuitively the ones that we want to see as
+ * common lines.
+ *
+ * The maximal ordered sequence of such line pairs (where ordered means
+ * that the order in the sequence agrees with the order of the lines in
+ * both files) naturally defines an initial set of common lines.
+ *
+ * Now, the algorithm tries to extend the set of common lines by growing
+ * the line ranges where the files have identical lines.
+ *
+ * Between those common lines, the patience diff algorithm is applied
+ * recursively, until no unique line pairs can be found; these line ranges
+ * are handled by the well-known Myers algorithm.
+ */
+
+#define NON_UNIQUE ULONG_MAX
+
+/*
+ * This is a hash mapping from line hash to line numbers in the first and
+ * second file.
+ */
+struct hashmap {
+ int nr, alloc;
+ struct entry {
+ unsigned long hash;
+ /*
+ * 0 = unused entry, 1 = first line, 2 = second, etc.
+ * line2 is NON_UNIQUE if the line is not unique
+ * in either the first or the second file.
+ */
+ unsigned long line1, line2;
+ /*
+ * "next" & "previous" are used for the longest common
+ * sequence;
+ * initially, "next" reflects only the order in file1.
+ */
+ struct entry *next, *previous;
+ } *entries, *first, *last;
+ /* were common records found? */
+ unsigned long has_matches;
+ mmfile_t *file1, *file2;
+ xdfenv_t *env;
+ xpparam_t const *xpp;
+};
+
+/* The argument "pass" is 1 for the first file, 2 for the second. */
+static void insert_record(int line, struct hashmap *map, int pass)
+{
+ xrecord_t **records = pass == 1 ?
+ map->env->xdf1.recs : map->env->xdf2.recs;
+ xrecord_t *record = records[line - 1], *other;
+ /*
+ * After xdl_prepare_env() (or more precisely, due to
+ * xdl_classify_record()), the "ha" member of the records (AKA lines)
+ * is _not_ the hash anymore, but a linearized version of it. In
+ * other words, the "ha" member is guaranteed to start with 0 and
+ * the second record's ha can only be 0 or 1, etc.
+ *
+ * So we multiply ha by 2 in the hope that the hashing was
+ * "unique enough".
+ */
+ int index = (int)((record->ha << 1) % map->alloc);
+
+ while (map->entries[index].line1) {
+ other = map->env->xdf1.recs[map->entries[index].line1 - 1];
+ if (map->entries[index].hash != record->ha ||
+ !xdl_recmatch(record->ptr, record->size,
+ other->ptr, other->size,
+ map->xpp->flags)) {
+ if (++index >= map->alloc)
+ index = 0;
+ continue;
+ }
+ if (pass == 2)
+ map->has_matches = 1;
+ if (pass == 1 || map->entries[index].line2)
+ map->entries[index].line2 = NON_UNIQUE;
+ else
+ map->entries[index].line2 = line;
+ return;
+ }
+ if (pass == 2)
+ return;
+ map->entries[index].line1 = line;
+ map->entries[index].hash = record->ha;
+ if (!map->first)
+ map->first = map->entries + index;
+ if (map->last) {
+ map->last->next = map->entries + index;
+ map->entries[index].previous = map->last;
+ }
+ map->last = map->entries + index;
+ map->nr++;
+}
+
+/*
+ * This function has to be called for each recursion into the inter-hunk
+ * parts, as previously non-unique lines can become unique when being
+ * restricted to a smaller part of the files.
+ *
+ * It is assumed that env has been prepared using xdl_prepare().
+ */
+static int fill_hashmap(mmfile_t *file1, mmfile_t *file2,
+ xpparam_t const *xpp, xdfenv_t *env,
+ struct hashmap *result,
+ int line1, int count1, int line2, int count2)
+{
+ result->file1 = file1;
+ result->file2 = file2;
+ result->xpp = xpp;
+ result->env = env;
+
+ /* We know exactly how large we want the hash map */
+ result->alloc = count1 * 2;
+ result->entries = (struct entry *)
+ xdl_malloc(result->alloc * sizeof(struct entry));
+ if (!result->entries)
+ return -1;
+ memset(result->entries, 0, result->alloc * sizeof(struct entry));
+
+ /* First, fill with entries from the first file */
+ while (count1--)
+ insert_record(line1++, result, 1);
+
+ /* Then search for matches in the second file */
+ while (count2--)
+ insert_record(line2++, result, 2);
+
+ return 0;
+}
+
+/*
+ * Find the longest sequence with a smaller last element (meaning a smaller
+ * line2, as we construct the sequence with entries ordered by line1).
+ */
+static int binary_search(struct entry **sequence, int longest,
+ struct entry *entry)
+{
+ int left = -1, right = longest;
+
+ while (left + 1 < right) {
+ int middle = (left + right) / 2;
+ /* by construction, no two entries can be equal */
+ if (sequence[middle]->line2 > entry->line2)
+ right = middle;
+ else
+ left = middle;
+ }
+ /* return the index in "sequence", _not_ the sequence length */
+ return left;
+}
+
+/*
+ * The idea is to start with the list of common unique lines sorted by
+ * the order in file1. For each of these pairs, the longest (partial)
+ * sequence whose last element's line2 is smaller is determined.
+ *
+ * For efficiency, the sequences are kept in a list containing exactly one
+ * item per sequence length: the sequence with the smallest last
+ * element (in terms of line2).
+ */
+static struct entry *find_longest_common_sequence(struct hashmap *map)
+{
+ struct entry **sequence = xdl_malloc(map->nr * sizeof(struct entry *));
+ int longest = 0, i;
+ struct entry *entry;
+
+ for (entry = map->first; entry; entry = entry->next) {
+ if (!entry->line2 || entry->line2 == NON_UNIQUE)
+ continue;
+ i = binary_search(sequence, longest, entry);
+ entry->previous = i < 0 ? NULL : sequence[i];
+ sequence[++i] = entry;
+ if (i == longest)
+ longest++;
+ }
+
+ /* No common unique lines were found */
+ if (!longest) {
+ xdl_free(sequence);
+ return NULL;
+ }
+
+ /* Iterate starting at the last element, adjusting the "next" members */
+ entry = sequence[longest - 1];
+ entry->next = NULL;
+ while (entry->previous) {
+ entry->previous->next = entry;
+ entry = entry->previous;
+ }
+ xdl_free(sequence);
+ return entry;
+}
+
+static int match(struct hashmap *map, int line1, int line2)
+{
+ xrecord_t *record1 = map->env->xdf1.recs[line1 - 1];
+ xrecord_t *record2 = map->env->xdf2.recs[line2 - 1];
+ return xdl_recmatch(record1->ptr, record1->size,
+ record2->ptr, record2->size, map->xpp->flags);
+}
+
+static int patience_diff(mmfile_t *file1, mmfile_t *file2,
+ xpparam_t const *xpp, xdfenv_t *env,
+ int line1, int count1, int line2, int count2);
+
+static int walk_common_sequence(struct hashmap *map, struct entry *first,
+ int line1, int count1, int line2, int count2)
+{
+ int end1 = line1 + count1, end2 = line2 + count2;
+ int next1, next2;
+
+ for (;;) {
+ /* Try to grow the line ranges of common lines */
+ if (first) {
+ next1 = first->line1;
+ next2 = first->line2;
+ while (next1 > line1 && next2 > line2 &&
+ match(map, next1 - 1, next2 - 1)) {
+ next1--;
+ next2--;
+ }
+ } else {
+ next1 = end1;
+ next2 = end2;
+ }
+ while (line1 < next1 && line2 < next2 &&
+ match(map, line1, line2)) {
+ line1++;
+ line2++;
+ }
+
+ /* Recurse */
+ if (next1 > line1 || next2 > line2) {
+ struct hashmap submap;
+
+ memset(&submap, 0, sizeof(submap));
+ if (patience_diff(map->file1, map->file2,
+ map->xpp, map->env,
+ line1, next1 - line1,
+ line2, next2 - line2))
+ return -1;
+ }
+
+ if (!first)
+ return 0;
+
+ while (first->next &&
+ first->next->line1 == first->line1 + 1 &&
+ first->next->line2 == first->line2 + 1)
+ first = first->next;
+
+ line1 = first->line1 + 1;
+ line2 = first->line2 + 1;
+
+ first = first->next;
+ }
+}
+
+static int fall_back_to_classic_diff(struct hashmap *map,
+ int line1, int count1, int line2, int count2)
+{
+ xpparam_t xpp;
+ xpp.flags = map->xpp->flags & ~XDF_PATIENCE_DIFF;
+
+ return xdl_fall_back_diff(map->env, &xpp,
+ line1, count1, line2, count2);
+}
+
+/*
+ * Recursively find the longest common sequence of unique lines,
+ * and if none was found, ask xdl_do_diff() to do the job.
+ *
+ * This function assumes that env was prepared with xdl_prepare_env().
+ */
+static int patience_diff(mmfile_t *file1, mmfile_t *file2,
+ xpparam_t const *xpp, xdfenv_t *env,
+ int line1, int count1, int line2, int count2)
+{
+ struct hashmap map;
+ struct entry *first;
+ int result = 0;
+
+ /* trivial case: one side is empty */
+ if (!count1) {
+ while(count2--)
+ env->xdf2.rchg[line2++ - 1] = 1;
+ return 0;
+ } else if (!count2) {
+ while(count1--)
+ env->xdf1.rchg[line1++ - 1] = 1;
+ return 0;
+ }
+
+ memset(&map, 0, sizeof(map));
+ if (fill_hashmap(file1, file2, xpp, env, &map,
+ line1, count1, line2, count2))
+ return -1;
+
+ /* are there any matching lines at all? */
+ if (!map.has_matches) {
+ while(count1--)
+ env->xdf1.rchg[line1++ - 1] = 1;
+ while(count2--)
+ env->xdf2.rchg[line2++ - 1] = 1;
+ xdl_free(map.entries);
+ return 0;
+ }
+
+ first = find_longest_common_sequence(&map);
+ if (first)
+ result = walk_common_sequence(&map, first,
+ line1, count1, line2, count2);
+ else
+ result = fall_back_to_classic_diff(&map,
+ line1, count1, line2, count2);
+
+ xdl_free(map.entries);
+ return result;
+}
+
+int xdl_do_patience_diff(mmfile_t *file1, mmfile_t *file2,
+ xpparam_t const *xpp, xdfenv_t *env)
+{
+ if (xdl_prepare_env(file1, file2, xpp, env) < 0)
+ return -1;
+
+ /* environment is cleaned up in xdl_diff() */
+ return patience_diff(file1, file2, xpp, env,
+ 1, env->xdf1.nrec, 1, env->xdf2.nrec);
+}
diff --git a/src/xdiff/xprepare.c b/src/xdiff/xprepare.c
new file mode 100644
index 000000000..e419f4f72
--- /dev/null
+++ b/src/xdiff/xprepare.c
@@ -0,0 +1,483 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#include "xinclude.h"
+
+
+#define XDL_KPDIS_RUN 4
+#define XDL_MAX_EQLIMIT 1024
+#define XDL_SIMSCAN_WINDOW 100
+#define XDL_GUESS_NLINES1 256
+#define XDL_GUESS_NLINES2 20
+
+
+typedef struct s_xdlclass {
+ struct s_xdlclass *next;
+ unsigned long ha;
+ char const *line;
+ long size;
+ long idx;
+ long len1, len2;
+} xdlclass_t;
+
+typedef struct s_xdlclassifier {
+ unsigned int hbits;
+ long hsize;
+ xdlclass_t **rchash;
+ chastore_t ncha;
+ xdlclass_t **rcrecs;
+ long alloc;
+ long count;
+ long flags;
+} xdlclassifier_t;
+
+
+
+
+static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags);
+static void xdl_free_classifier(xdlclassifier_t *cf);
+static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,
+ unsigned int hbits, xrecord_t *rec);
+static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,
+ xdlclassifier_t *cf, xdfile_t *xdf);
+static void xdl_free_ctx(xdfile_t *xdf);
+static int xdl_clean_mmatch(char const *dis, long i, long s, long e);
+static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);
+static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2);
+static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);
+
+
+
+
+static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags) {
+ cf->flags = flags;
+
+ cf->hbits = xdl_hashbits((unsigned int) size);
+ cf->hsize = 1 << cf->hbits;
+
+ if (xdl_cha_init(&cf->ncha, sizeof(xdlclass_t), size / 4 + 1) < 0) {
+
+ return -1;
+ }
+ if (!(cf->rchash = (xdlclass_t **) xdl_malloc(cf->hsize * sizeof(xdlclass_t *)))) {
+
+ xdl_cha_free(&cf->ncha);
+ return -1;
+ }
+ memset(cf->rchash, 0, cf->hsize * sizeof(xdlclass_t *));
+
+ cf->alloc = size;
+ if (!(cf->rcrecs = (xdlclass_t **) xdl_malloc(cf->alloc * sizeof(xdlclass_t *)))) {
+
+ xdl_free(cf->rchash);
+ xdl_cha_free(&cf->ncha);
+ return -1;
+ }
+
+ cf->count = 0;
+
+ return 0;
+}
+
+
+static void xdl_free_classifier(xdlclassifier_t *cf) {
+
+ xdl_free(cf->rcrecs);
+ xdl_free(cf->rchash);
+ xdl_cha_free(&cf->ncha);
+}
+
+
+static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,
+ unsigned int hbits, xrecord_t *rec) {
+ long hi;
+ char const *line;
+ xdlclass_t *rcrec;
+ xdlclass_t **rcrecs;
+
+ line = rec->ptr;
+ hi = (long) XDL_HASHLONG(rec->ha, cf->hbits);
+ for (rcrec = cf->rchash[hi]; rcrec; rcrec = rcrec->next)
+ if (rcrec->ha == rec->ha &&
+ xdl_recmatch(rcrec->line, rcrec->size,
+ rec->ptr, rec->size, cf->flags))
+ break;
+
+ if (!rcrec) {
+ if (!(rcrec = xdl_cha_alloc(&cf->ncha))) {
+
+ return -1;
+ }
+ rcrec->idx = cf->count++;
+ if (cf->count > cf->alloc) {
+ cf->alloc *= 2;
+ if (!(rcrecs = (xdlclass_t **) xdl_realloc(cf->rcrecs, cf->alloc * sizeof(xdlclass_t *)))) {
+
+ return -1;
+ }
+ cf->rcrecs = rcrecs;
+ }
+ cf->rcrecs[rcrec->idx] = rcrec;
+ rcrec->line = line;
+ rcrec->size = rec->size;
+ rcrec->ha = rec->ha;
+ rcrec->len1 = rcrec->len2 = 0;
+ rcrec->next = cf->rchash[hi];
+ cf->rchash[hi] = rcrec;
+ }
+
+ (pass == 1) ? rcrec->len1++ : rcrec->len2++;
+
+ rec->ha = (unsigned long) rcrec->idx;
+
+ hi = (long) XDL_HASHLONG(rec->ha, hbits);
+ rec->next = rhash[hi];
+ rhash[hi] = rec;
+
+ return 0;
+}
+
+
+static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,
+ xdlclassifier_t *cf, xdfile_t *xdf) {
+ unsigned int hbits;
+ long nrec, hsize, bsize;
+ unsigned long hav;
+ char const *blk, *cur, *top, *prev;
+ xrecord_t *crec;
+ xrecord_t **recs, **rrecs;
+ xrecord_t **rhash;
+ unsigned long *ha;
+ char *rchg;
+ long *rindex;
+
+ ha = NULL;
+ rindex = NULL;
+ rchg = NULL;
+ rhash = NULL;
+ recs = NULL;
+
+ if (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)
+ goto abort;
+ if (!(recs = (xrecord_t **) xdl_malloc(narec * sizeof(xrecord_t *))))
+ goto abort;
+
+ if (xpp->flags & XDF_HISTOGRAM_DIFF)
+ hbits = hsize = 0;
+ else {
+ hbits = xdl_hashbits((unsigned int) narec);
+ hsize = 1 << hbits;
+ if (!(rhash = (xrecord_t **) xdl_malloc(hsize * sizeof(xrecord_t *))))
+ goto abort;
+ memset(rhash, 0, hsize * sizeof(xrecord_t *));
+ }
+
+ nrec = 0;
+ if ((cur = blk = xdl_mmfile_first(mf, &bsize)) != NULL) {
+ for (top = blk + bsize; cur < top; ) {
+ prev = cur;
+ hav = xdl_hash_record(&cur, top, xpp->flags);
+ if (nrec >= narec) {
+ narec *= 2;
+ if (!(rrecs = (xrecord_t **) xdl_realloc(recs, narec * sizeof(xrecord_t *))))
+ goto abort;
+ recs = rrecs;
+ }
+ if (!(crec = xdl_cha_alloc(&xdf->rcha)))
+ goto abort;
+ crec->ptr = prev;
+ crec->size = (long) (cur - prev);
+ crec->ha = hav;
+ recs[nrec++] = crec;
+
+ if (!(xpp->flags & XDF_HISTOGRAM_DIFF) &&
+ xdl_classify_record(pass, cf, rhash, hbits, crec) < 0)
+ goto abort;
+ }
+ }
+
+ if (!(rchg = (char *) xdl_malloc((nrec + 2) * sizeof(char))))
+ goto abort;
+ memset(rchg, 0, (nrec + 2) * sizeof(char));
+
+ if (!(rindex = (long *) xdl_malloc((nrec + 1) * sizeof(long))))
+ goto abort;
+ if (!(ha = (unsigned long *) xdl_malloc((nrec + 1) * sizeof(unsigned long))))
+ goto abort;
+
+ xdf->nrec = nrec;
+ xdf->recs = recs;
+ xdf->hbits = hbits;
+ xdf->rhash = rhash;
+ xdf->rchg = rchg + 1;
+ xdf->rindex = rindex;
+ xdf->nreff = 0;
+ xdf->ha = ha;
+ xdf->dstart = 0;
+ xdf->dend = nrec - 1;
+
+ return 0;
+
+abort:
+ xdl_free(ha);
+ xdl_free(rindex);
+ xdl_free(rchg);
+ xdl_free(rhash);
+ xdl_free(recs);
+ xdl_cha_free(&xdf->rcha);
+ return -1;
+}
+
+
+static void xdl_free_ctx(xdfile_t *xdf) {
+
+ xdl_free(xdf->rhash);
+ xdl_free(xdf->rindex);
+ xdl_free(xdf->rchg - 1);
+ xdl_free(xdf->ha);
+ xdl_free(xdf->recs);
+ xdl_cha_free(&xdf->rcha);
+}
+
+
+int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+ xdfenv_t *xe) {
+ long enl1, enl2, sample;
+ xdlclassifier_t cf;
+
+ memset(&cf, 0, sizeof(cf));
+
+ /*
+ * For histogram diff, we can afford a smaller sample size and
+ * thus a poorer estimate of the number of lines, as the hash
+ * table (rhash) won't be filled up/grown. The number of lines
+ * (nrecs) will be updated correctly anyway by
+ * xdl_prepare_ctx().
+ */
+ sample = xpp->flags & XDF_HISTOGRAM_DIFF ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1;
+
+ enl1 = xdl_guess_lines(mf1, sample) + 1;
+ enl2 = xdl_guess_lines(mf2, sample) + 1;
+
+ if (!(xpp->flags & XDF_HISTOGRAM_DIFF) &&
+ xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0) {
+
+ return -1;
+ }
+
+ if (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {
+
+ xdl_free_classifier(&cf);
+ return -1;
+ }
+ if (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {
+
+ xdl_free_ctx(&xe->xdf1);
+ xdl_free_classifier(&cf);
+ return -1;
+ }
+
+ if (!(xpp->flags & XDF_PATIENCE_DIFF) &&
+ !(xpp->flags & XDF_HISTOGRAM_DIFF) &&
+ xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {
+
+ xdl_free_ctx(&xe->xdf2);
+ xdl_free_ctx(&xe->xdf1);
+ return -1;
+ }
+
+ if (!(xpp->flags & XDF_HISTOGRAM_DIFF))
+ xdl_free_classifier(&cf);
+
+ return 0;
+}
+
+
+void xdl_free_env(xdfenv_t *xe) {
+
+ xdl_free_ctx(&xe->xdf2);
+ xdl_free_ctx(&xe->xdf1);
+}
+
+
+static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {
+ long r, rdis0, rpdis0, rdis1, rpdis1;
+
+ /*
+ * Limits the window the is examined during the similar-lines
+ * scan. The loops below stops when dis[i - r] == 1 (line that
+ * has no match), but there are corner cases where the loop
+ * proceed all the way to the extremities by causing huge
+ * performance penalties in case of big files.
+ */
+ if (i - s > XDL_SIMSCAN_WINDOW)
+ s = i - XDL_SIMSCAN_WINDOW;
+ if (e - i > XDL_SIMSCAN_WINDOW)
+ e = i + XDL_SIMSCAN_WINDOW;
+
+ /*
+ * Scans the lines before 'i' to find a run of lines that either
+ * have no match (dis[j] == 0) or have multiple matches (dis[j] > 1).
+ * Note that we always call this function with dis[i] > 1, so the
+ * current line (i) is already a multimatch line.
+ */
+ for (r = 1, rdis0 = 0, rpdis0 = 1; (i - r) >= s; r++) {
+ if (!dis[i - r])
+ rdis0++;
+ else if (dis[i - r] == 2)
+ rpdis0++;
+ else
+ break;
+ }
+ /*
+ * If the run before the line 'i' found only multimatch lines, we
+ * return 0 and hence we don't make the current line (i) discarded.
+ * We want to discard multimatch lines only when they appear in the
+ * middle of runs with nomatch lines (dis[j] == 0).
+ */
+ if (rdis0 == 0)
+ return 0;
+ for (r = 1, rdis1 = 0, rpdis1 = 1; (i + r) <= e; r++) {
+ if (!dis[i + r])
+ rdis1++;
+ else if (dis[i + r] == 2)
+ rpdis1++;
+ else
+ break;
+ }
+ /*
+ * If the run after the line 'i' found only multimatch lines, we
+ * return 0 and hence we don't make the current line (i) discarded.
+ */
+ if (rdis1 == 0)
+ return 0;
+ rdis1 += rdis0;
+ rpdis1 += rpdis0;
+
+ return rpdis1 * XDL_KPDIS_RUN < (rpdis1 + rdis1);
+}
+
+
+/*
+ * Try to reduce the problem complexity, discard records that have no
+ * matches on the other file. Also, lines that have multiple matches
+ * might be potentially discarded if they happear in a run of discardable.
+ */
+static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {
+ long i, nm, nreff, mlim;
+ xrecord_t **recs;
+ xdlclass_t *rcrec;
+ char *dis, *dis1, *dis2;
+
+ if (!(dis = (char *) xdl_malloc(xdf1->nrec + xdf2->nrec + 2))) {
+
+ return -1;
+ }
+ memset(dis, 0, xdf1->nrec + xdf2->nrec + 2);
+ dis1 = dis;
+ dis2 = dis1 + xdf1->nrec + 1;
+
+ if ((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT)
+ mlim = XDL_MAX_EQLIMIT;
+ for (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {
+ rcrec = cf->rcrecs[(*recs)->ha];
+ nm = rcrec ? rcrec->len2 : 0;
+ dis1[i] = (nm == 0) ? 0: (nm >= mlim) ? 2: 1;
+ }
+
+ if ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)
+ mlim = XDL_MAX_EQLIMIT;
+ for (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {
+ rcrec = cf->rcrecs[(*recs)->ha];
+ nm = rcrec ? rcrec->len1 : 0;
+ dis2[i] = (nm == 0) ? 0: (nm >= mlim) ? 2: 1;
+ }
+
+ for (nreff = 0, i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart];
+ i <= xdf1->dend; i++, recs++) {
+ if (dis1[i] == 1 ||
+ (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {
+ xdf1->rindex[nreff] = i;
+ xdf1->ha[nreff] = (*recs)->ha;
+ nreff++;
+ } else
+ xdf1->rchg[i] = 1;
+ }
+ xdf1->nreff = nreff;
+
+ for (nreff = 0, i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart];
+ i <= xdf2->dend; i++, recs++) {
+ if (dis2[i] == 1 ||
+ (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {
+ xdf2->rindex[nreff] = i;
+ xdf2->ha[nreff] = (*recs)->ha;
+ nreff++;
+ } else
+ xdf2->rchg[i] = 1;
+ }
+ xdf2->nreff = nreff;
+
+ xdl_free(dis);
+
+ return 0;
+}
+
+
+/*
+ * Early trim initial and terminal matching records.
+ */
+static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {
+ long i, lim;
+ xrecord_t **recs1, **recs2;
+
+ recs1 = xdf1->recs;
+ recs2 = xdf2->recs;
+ for (i = 0, lim = XDL_MIN(xdf1->nrec, xdf2->nrec); i < lim;
+ i++, recs1++, recs2++)
+ if ((*recs1)->ha != (*recs2)->ha)
+ break;
+
+ xdf1->dstart = xdf2->dstart = i;
+
+ recs1 = xdf1->recs + xdf1->nrec - 1;
+ recs2 = xdf2->recs + xdf2->nrec - 1;
+ for (lim -= i, i = 0; i < lim; i++, recs1--, recs2--)
+ if ((*recs1)->ha != (*recs2)->ha)
+ break;
+
+ xdf1->dend = xdf1->nrec - i - 1;
+ xdf2->dend = xdf2->nrec - i - 1;
+
+ return 0;
+}
+
+
+static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {
+
+ if (xdl_trim_ends(xdf1, xdf2) < 0 ||
+ xdl_cleanup_records(cf, xdf1, xdf2) < 0) {
+
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/xdiff/xprepare.h b/src/xdiff/xprepare.h
new file mode 100644
index 000000000..8fb06a537
--- /dev/null
+++ b/src/xdiff/xprepare.h
@@ -0,0 +1,34 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XPREPARE_H)
+#define XPREPARE_H
+
+
+
+int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+ xdfenv_t *xe);
+void xdl_free_env(xdfenv_t *xe);
+
+
+
+#endif /* #if !defined(XPREPARE_H) */
diff --git a/src/xdiff/xtypes.h b/src/xdiff/xtypes.h
new file mode 100644
index 000000000..2511aef8d
--- /dev/null
+++ b/src/xdiff/xtypes.h
@@ -0,0 +1,67 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XTYPES_H)
+#define XTYPES_H
+
+
+
+typedef struct s_chanode {
+ struct s_chanode *next;
+ long icurr;
+} chanode_t;
+
+typedef struct s_chastore {
+ chanode_t *head, *tail;
+ long isize, nsize;
+ chanode_t *ancur;
+ chanode_t *sncur;
+ long scurr;
+} chastore_t;
+
+typedef struct s_xrecord {
+ struct s_xrecord *next;
+ char const *ptr;
+ long size;
+ unsigned long ha;
+} xrecord_t;
+
+typedef struct s_xdfile {
+ chastore_t rcha;
+ long nrec;
+ unsigned int hbits;
+ xrecord_t **rhash;
+ long dstart, dend;
+ xrecord_t **recs;
+ char *rchg;
+ long *rindex;
+ long nreff;
+ unsigned long *ha;
+} xdfile_t;
+
+typedef struct s_xdfenv {
+ xdfile_t xdf1, xdf2;
+} xdfenv_t;
+
+
+
+#endif /* #if !defined(XTYPES_H) */
diff --git a/src/xdiff/xutils.c b/src/xdiff/xutils.c
new file mode 100644
index 000000000..bb7bdee49
--- /dev/null
+++ b/src/xdiff/xutils.c
@@ -0,0 +1,419 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#include "xinclude.h"
+
+
+
+
+long xdl_bogosqrt(long n) {
+ long i;
+
+ /*
+ * Classical integer square root approximation using shifts.
+ */
+ for (i = 1; n > 0; n >>= 2)
+ i <<= 1;
+
+ return i;
+}
+
+
+int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize,
+ xdemitcb_t *ecb) {
+ int i = 2;
+ mmbuffer_t mb[3];
+
+ mb[0].ptr = (char *) pre;
+ mb[0].size = psize;
+ mb[1].ptr = (char *) rec;
+ mb[1].size = size;
+ if (size > 0 && rec[size - 1] != '\n') {
+ mb[2].ptr = (char *) "\n\\ No newline at end of file\n";
+ mb[2].size = strlen(mb[2].ptr);
+ i++;
+ }
+ if (ecb->outf(ecb->priv, mb, i) < 0) {
+
+ return -1;
+ }
+
+ return 0;
+}
+
+void *xdl_mmfile_first(mmfile_t *mmf, long *size)
+{
+ *size = (long)mmf->size;
+ return mmf->ptr;
+}
+
+
+long xdl_mmfile_size(mmfile_t *mmf)
+{
+ return (long)mmf->size;
+}
+
+
+int xdl_cha_init(chastore_t *cha, long isize, long icount) {
+
+ cha->head = cha->tail = NULL;
+ cha->isize = isize;
+ cha->nsize = icount * isize;
+ cha->ancur = cha->sncur = NULL;
+ cha->scurr = 0;
+
+ return 0;
+}
+
+
+void xdl_cha_free(chastore_t *cha) {
+ chanode_t *cur, *tmp;
+
+ for (cur = cha->head; (tmp = cur) != NULL;) {
+ cur = cur->next;
+ xdl_free(tmp);
+ }
+}
+
+
+void *xdl_cha_alloc(chastore_t *cha) {
+ chanode_t *ancur;
+ void *data;
+
+ if (!(ancur = cha->ancur) || ancur->icurr == cha->nsize) {
+ if (!(ancur = (chanode_t *) xdl_malloc(sizeof(chanode_t) + cha->nsize))) {
+
+ return NULL;
+ }
+ ancur->icurr = 0;
+ ancur->next = NULL;
+ if (cha->tail)
+ cha->tail->next = ancur;
+ if (!cha->head)
+ cha->head = ancur;
+ cha->tail = ancur;
+ cha->ancur = ancur;
+ }
+
+ data = (char *) ancur + sizeof(chanode_t) + ancur->icurr;
+ ancur->icurr += cha->isize;
+
+ return data;
+}
+
+
+void *xdl_cha_first(chastore_t *cha) {
+ chanode_t *sncur;
+
+ if (!(cha->sncur = sncur = cha->head))
+ return NULL;
+
+ cha->scurr = 0;
+
+ return (char *) sncur + sizeof(chanode_t) + cha->scurr;
+}
+
+
+void *xdl_cha_next(chastore_t *cha) {
+ chanode_t *sncur;
+
+ if (!(sncur = cha->sncur))
+ return NULL;
+ cha->scurr += cha->isize;
+ if (cha->scurr == sncur->icurr) {
+ if (!(sncur = cha->sncur = sncur->next))
+ return NULL;
+ cha->scurr = 0;
+ }
+
+ return (char *) sncur + sizeof(chanode_t) + cha->scurr;
+}
+
+
+long xdl_guess_lines(mmfile_t *mf, long sample) {
+ long nl = 0, size, tsize = 0;
+ char const *data, *cur, *top;
+
+ if ((cur = data = xdl_mmfile_first(mf, &size)) != NULL) {
+ for (top = data + size; nl < sample && cur < top; ) {
+ nl++;
+ if (!(cur = memchr(cur, '\n', top - cur)))
+ cur = top;
+ else
+ cur++;
+ }
+ tsize += (long) (cur - data);
+ }
+
+ if (nl && tsize)
+ nl = xdl_mmfile_size(mf) / (tsize / nl);
+
+ return nl + 1;
+}
+
+int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
+{
+ int i1, i2;
+
+ if (s1 == s2 && !memcmp(l1, l2, s1))
+ return 1;
+ if (!(flags & XDF_WHITESPACE_FLAGS))
+ return 0;
+
+ i1 = 0;
+ i2 = 0;
+
+ /*
+ * -w matches everything that matches with -b, and -b in turn
+ * matches everything that matches with --ignore-space-at-eol.
+ *
+ * Each flavor of ignoring needs different logic to skip whitespaces
+ * while we have both sides to compare.
+ */
+ if (flags & XDF_IGNORE_WHITESPACE) {
+ goto skip_ws;
+ while (i1 < s1 && i2 < s2) {
+ if (l1[i1++] != l2[i2++])
+ return 0;
+ skip_ws:
+ while (i1 < s1 && XDL_ISSPACE(l1[i1]))
+ i1++;
+ while (i2 < s2 && XDL_ISSPACE(l2[i2]))
+ i2++;
+ }
+ } else if (flags & XDF_IGNORE_WHITESPACE_CHANGE) {
+ while (i1 < s1 && i2 < s2) {
+ if (XDL_ISSPACE(l1[i1]) && XDL_ISSPACE(l2[i2])) {
+ /* Skip matching spaces and try again */
+ while (i1 < s1 && XDL_ISSPACE(l1[i1]))
+ i1++;
+ while (i2 < s2 && XDL_ISSPACE(l2[i2]))
+ i2++;
+ continue;
+ }
+ if (l1[i1++] != l2[i2++])
+ return 0;
+ }
+ } else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL) {
+ while (i1 < s1 && i2 < s2 && l1[i1++] == l2[i2++])
+ ; /* keep going */
+ }
+
+ /*
+ * After running out of one side, the remaining side must have
+ * nothing but whitespace for the lines to match. Note that
+ * ignore-whitespace-at-eol case may break out of the loop
+ * while there still are characters remaining on both lines.
+ */
+ if (i1 < s1) {
+ while (i1 < s1 && XDL_ISSPACE(l1[i1]))
+ i1++;
+ if (s1 != i1)
+ return 0;
+ }
+ if (i2 < s2) {
+ while (i2 < s2 && XDL_ISSPACE(l2[i2]))
+ i2++;
+ return (s2 == i2);
+ }
+ return 1;
+}
+
+static unsigned long xdl_hash_record_with_whitespace(char const **data,
+ char const *top, long flags) {
+ unsigned long ha = 5381;
+ char const *ptr = *data;
+
+ for (; ptr < top && *ptr != '\n'; ptr++) {
+ if (XDL_ISSPACE(*ptr)) {
+ const char *ptr2 = ptr;
+ int at_eol;
+ while (ptr + 1 < top && XDL_ISSPACE(ptr[1])
+ && ptr[1] != '\n')
+ ptr++;
+ at_eol = (top <= ptr + 1 || ptr[1] == '\n');
+ if (flags & XDF_IGNORE_WHITESPACE)
+ ; /* already handled */
+ else if (flags & XDF_IGNORE_WHITESPACE_CHANGE
+ && !at_eol) {
+ ha += (ha << 5);
+ ha ^= (unsigned long) ' ';
+ }
+ else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL
+ && !at_eol) {
+ while (ptr2 != ptr + 1) {
+ ha += (ha << 5);
+ ha ^= (unsigned long) *ptr2;
+ ptr2++;
+ }
+ }
+ continue;
+ }
+ ha += (ha << 5);
+ ha ^= (unsigned long) *ptr;
+ }
+ *data = ptr < top ? ptr + 1: ptr;
+
+ return ha;
+}
+
+
+unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
+ unsigned long ha = 5381;
+ char const *ptr = *data;
+
+ if (flags & XDF_WHITESPACE_FLAGS)
+ return xdl_hash_record_with_whitespace(data, top, flags);
+
+ for (; ptr < top && *ptr != '\n'; ptr++) {
+ ha += (ha << 5);
+ ha ^= (unsigned long) *ptr;
+ }
+ *data = ptr < top ? ptr + 1: ptr;
+
+ return ha;
+}
+
+
+unsigned int xdl_hashbits(unsigned int size) {
+ unsigned int val = 1, bits = 0;
+
+ for (; val < size && bits < CHAR_BIT * sizeof(unsigned int); val <<= 1, bits++);
+ return bits ? bits: 1;
+}
+
+
+int xdl_num_out(char *out, long val) {
+ char *ptr, *str = out;
+ char buf[32];
+
+ ptr = buf + sizeof(buf) - 1;
+ *ptr = '\0';
+ if (val < 0) {
+ *--ptr = '-';
+ val = -val;
+ }
+ for (; val && ptr > buf; val /= 10)
+ *--ptr = "0123456789"[val % 10];
+ if (*ptr)
+ for (; *ptr; ptr++, str++)
+ *str = *ptr;
+ else
+ *str++ = '0';
+ *str = '\0';
+
+ return (int)(str - out);
+}
+
+
+long xdl_atol(char const *str, char const **next) {
+ long val, base;
+ char const *top;
+
+ for (top = str; XDL_ISDIGIT(*top); top++);
+ if (next)
+ *next = top;
+ for (val = 0, base = 1, top--; top >= str; top--, base *= 10)
+ val += base * (long)(*top - '0');
+ return val;
+}
+
+
+int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
+ const char *func, long funclen, xdemitcb_t *ecb) {
+ int nb = 0;
+ mmbuffer_t mb;
+ char buf[128];
+
+ memcpy(buf, "@@ -", 4);
+ nb += 4;
+
+ nb += xdl_num_out(buf + nb, c1 ? s1: s1 - 1);
+
+ if (c1 != 1) {
+ memcpy(buf + nb, ",", 1);
+ nb += 1;
+
+ nb += xdl_num_out(buf + nb, c1);
+ }
+
+ memcpy(buf + nb, " +", 2);
+ nb += 2;
+
+ nb += xdl_num_out(buf + nb, c2 ? s2: s2 - 1);
+
+ if (c2 != 1) {
+ memcpy(buf + nb, ",", 1);
+ nb += 1;
+
+ nb += xdl_num_out(buf + nb, c2);
+ }
+
+ memcpy(buf + nb, " @@", 3);
+ nb += 3;
+ if (func && funclen) {
+ buf[nb++] = ' ';
+ if (funclen > (long)sizeof(buf) - nb - 1)
+ funclen = (long)sizeof(buf) - nb - 1;
+ memcpy(buf + nb, func, funclen);
+ nb += funclen;
+ }
+ buf[nb++] = '\n';
+
+ mb.ptr = buf;
+ mb.size = nb;
+ if (ecb->outf(ecb->priv, &mb, 1) < 0)
+ return -1;
+
+ return 0;
+}
+
+int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,
+ int line1, int count1, int line2, int count2)
+{
+ /*
+ * This probably does not work outside Git, since
+ * we have a very simple mmfile structure.
+ *
+ * Note: ideally, we would reuse the prepared environment, but
+ * the libxdiff interface does not (yet) allow for diffing only
+ * ranges of lines instead of the whole files.
+ */
+ mmfile_t subfile1, subfile2;
+ xdfenv_t env;
+
+ subfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1]->ptr;
+ subfile1.size = diff_env->xdf1.recs[line1 + count1 - 2]->ptr +
+ diff_env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr;
+ subfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1]->ptr;
+ subfile2.size = diff_env->xdf2.recs[line2 + count2 - 2]->ptr +
+ diff_env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr;
+ if (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0)
+ return -1;
+
+ memcpy(diff_env->xdf1.rchg + line1 - 1, env.xdf1.rchg, count1);
+ memcpy(diff_env->xdf2.rchg + line2 - 1, env.xdf2.rchg, count2);
+
+ xdl_free_env(&env);
+
+ return 0;
+}
diff --git a/src/xdiff/xutils.h b/src/xdiff/xutils.h
new file mode 100644
index 000000000..714719a89
--- /dev/null
+++ b/src/xdiff/xutils.h
@@ -0,0 +1,49 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XUTILS_H)
+#define XUTILS_H
+
+
+
+long xdl_bogosqrt(long n);
+int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize,
+ xdemitcb_t *ecb);
+int xdl_cha_init(chastore_t *cha, long isize, long icount);
+void xdl_cha_free(chastore_t *cha);
+void *xdl_cha_alloc(chastore_t *cha);
+void *xdl_cha_first(chastore_t *cha);
+void *xdl_cha_next(chastore_t *cha);
+long xdl_guess_lines(mmfile_t *mf, long sample);
+int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags);
+unsigned long xdl_hash_record(char const **data, char const *top, long flags);
+unsigned int xdl_hashbits(unsigned int size);
+int xdl_num_out(char *out, long val);
+long xdl_atol(char const *str, char const **next);
+int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
+ const char *func, long funclen, xdemitcb_t *ecb);
+int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,
+ int line1, int count1, int line2, int count2);
+
+
+
+#endif /* #if !defined(XUTILS_H) */
diff --git a/tests-clar/attr/attr_expect.h b/tests-clar/attr/attr_expect.h
new file mode 100644
index 000000000..df1e1044b
--- /dev/null
+++ b/tests-clar/attr/attr_expect.h
@@ -0,0 +1,42 @@
+#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 *value)
+{
+ switch (expected) {
+ case EXPECT_TRUE:
+ cl_assert(GIT_ATTR_TRUE(value));
+ break;
+
+ case EXPECT_FALSE:
+ cl_assert(GIT_ATTR_FALSE(value));
+ break;
+
+ case EXPECT_UNDEFINED:
+ cl_assert(GIT_ATTR_UNSPECIFIED(value));
+ break;
+
+ case EXPECT_STRING:
+ cl_assert_equal_s(expected_str, value);
+ break;
+ }
+}
+
+#endif
diff --git a/tests-clar/attr/file.c b/tests-clar/attr/file.c
index af50cd38e..d19708838 100644
--- a/tests-clar/attr/file.c
+++ b/tests-clar/attr/file.c
@@ -1,5 +1,6 @@
#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)))
@@ -10,23 +11,22 @@ void test_attr_file__simple_read(void)
git_attr_assignment *assign;
git_attr_rule *rule;
- cl_git_pass(git_attr_file__new(&file));
- cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr0"), file));
- cl_assert_strequal(cl_fixture("attr/attr0"), file->path);
+ 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_strequal("*", rule->match.pattern);
+ cl_assert_equal_s("*", rule->match.pattern);
cl_assert(rule->match.length == 1);
- cl_assert(rule->match.flags == 0);
+ 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_strequal("binary", assign->name);
- cl_assert(assign->value == GIT_ATTR_TRUE);
- cl_assert(!assign->is_allocated);
+ cl_assert_equal_s("binary", assign->name);
+ cl_assert(GIT_ATTR_TRUE(assign->value));
git_attr_file__free(file);
}
@@ -37,9 +37,9 @@ void test_attr_file__match_variants(void)
git_attr_rule *rule;
git_attr_assignment *assign;
- cl_git_pass(git_attr_file__new(&file));
- cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr1"), file));
- cl_assert_strequal(cl_fixture("attr/attr1"), file->path);
+ 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
@@ -47,51 +47,52 @@ void test_attr_file__match_variants(void)
*/
rule = get_rule(0);
cl_assert(rule);
- cl_assert_strequal("pat0", rule->match.pattern);
+ cl_assert_equal_s("pat0", rule->match.pattern);
cl_assert(rule->match.length == strlen("pat0"));
cl_assert(rule->match.flags == 0);
cl_assert(rule->assigns.length == 1);
assign = get_assign(rule,0);
- cl_assert_strequal("attr0", assign->name);
+ cl_assert_equal_s("attr0", assign->name);
cl_assert(assign->name_hash == git_attr_file__name_hash(assign->name));
- cl_assert(assign->value == GIT_ATTR_TRUE);
- cl_assert(!assign->is_allocated);
+ cl_assert(GIT_ATTR_TRUE(assign->value));
rule = get_rule(1);
- cl_assert_strequal("pat1", rule->match.pattern);
+ cl_assert_equal_s("pat1", rule->match.pattern);
cl_assert(rule->match.length == strlen("pat1"));
cl_assert(rule->match.flags == GIT_ATTR_FNMATCH_NEGATIVE);
rule = get_rule(2);
- cl_assert_strequal("pat2", rule->match.pattern);
+ cl_assert_equal_s("pat2", rule->match.pattern);
cl_assert(rule->match.length == strlen("pat2"));
cl_assert(rule->match.flags == GIT_ATTR_FNMATCH_DIRECTORY);
rule = get_rule(3);
- cl_assert_strequal("pat3dir/pat3file", rule->match.pattern);
+ cl_assert_equal_s("pat3dir/pat3file", rule->match.pattern);
cl_assert(rule->match.flags == GIT_ATTR_FNMATCH_FULLPATH);
rule = get_rule(4);
- cl_assert_strequal("pat4.*", rule->match.pattern);
- cl_assert(rule->match.flags == 0);
+ cl_assert_equal_s("pat4.*", rule->match.pattern);
+ cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_HASWILD) != 0);
rule = get_rule(5);
- cl_assert_strequal("*.pat5", rule->match.pattern);
+ cl_assert_equal_s("*.pat5", rule->match.pattern);
+ cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_HASWILD) != 0);
rule = get_rule(7);
- cl_assert_strequal("pat7[a-e]??[xyz]", rule->match.pattern);
+ 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_strequal("attr7", assign->name);
- cl_assert(assign->value == GIT_ATTR_TRUE);
+ cl_assert_equal_s("attr7", assign->name);
+ cl_assert(GIT_ATTR_TRUE(assign->value));
rule = get_rule(8);
- cl_assert_strequal("pat8 with spaces", rule->match.pattern);
+ cl_assert_equal_s("pat8 with spaces", rule->match.pattern);
cl_assert(rule->match.length == strlen("pat8 with spaces"));
cl_assert(rule->match.flags == 0);
rule = get_rule(9);
- cl_assert_strequal("pat9", rule->match.pattern);
+ cl_assert_equal_s("pat9", rule->match.pattern);
git_attr_file__free(file);
}
@@ -102,21 +103,18 @@ static void check_one_assign(
int assign_idx,
const char *pattern,
const char *name,
- const char *value,
- int is_allocated)
+ 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_strequal(pattern, rule->match.pattern);
+ cl_assert_equal_s(pattern, rule->match.pattern);
cl_assert(rule->assigns.length == 1);
- cl_assert_strequal(name, assign->name);
+ cl_assert_equal_s(name, assign->name);
cl_assert(assign->name_hash == git_attr_file__name_hash(assign->name));
- cl_assert(assign->is_allocated == is_allocated);
- if (is_allocated)
- cl_assert_strequal(value, assign->value);
- else
- cl_assert(assign->value == value);
+
+ attr_check_expected(expected, expected_str, assign->value);
}
void test_attr_file__assign_variants(void)
@@ -125,62 +123,62 @@ void test_attr_file__assign_variants(void)
git_attr_rule *rule;
git_attr_assignment *assign;
- cl_git_pass(git_attr_file__new(&file));
- cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr2"), file));
- cl_assert_strequal(cl_fixture("attr/attr2"), file->path);
+ 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", GIT_ATTR_TRUE, 0);
- check_one_assign(file, 1, 0, "pat1", "neg", GIT_ATTR_FALSE, 0);
- check_one_assign(file, 2, 0, "*", "notundef", GIT_ATTR_TRUE, 0);
- check_one_assign(file, 3, 0, "pat2", "notundef", NULL, 0);
- check_one_assign(file, 4, 0, "pat3", "assigned", "test-value", 1);
- check_one_assign(file, 5, 0, "pat4", "rule-with-more-chars", "value-with-more-chars", 1);
- check_one_assign(file, 6, 0, "pat5", "empty", GIT_ATTR_TRUE, 0);
- check_one_assign(file, 7, 0, "pat6", "negempty", GIT_ATTR_FALSE, 0);
+ 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_strequal("pat7", rule->match.pattern);
+ 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_strequal("multiple", assign->name);
- cl_assert(assign->value == GIT_ATTR_TRUE);
+ 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_strequal("single", assign->name);
- cl_assert(assign->value == GIT_ATTR_FALSE);
+ 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_strequal("values", assign->name);
- cl_assert_strequal("1", assign->value);
+ 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_strequal("also", assign->name);
- cl_assert_strequal("a-really-long-value/*", assign->value);
+ 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_strequal("happy", assign->name);
- cl_assert_strequal("yes!", assign->value);
+ 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_strequal("pat8", rule->match.pattern);
+ 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_strequal("again", assign->name);
- cl_assert(assign->value == GIT_ATTR_TRUE);
+ 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_strequal("another", assign->name);
- cl_assert_strequal("12321", assign->value);
+ cl_assert_equal_s("another", assign->name);
+ cl_assert_equal_s("12321", assign->value);
- check_one_assign(file, 10, 0, "pat9", "at-eof", GIT_ATTR_FALSE, 0);
+ check_one_assign(file, 10, 0, "pat9", "at-eof", EXPECT_FALSE, NULL);
git_attr_file__free(file);
}
@@ -191,39 +189,38 @@ void test_attr_file__check_attr_examples(void)
git_attr_rule *rule;
git_attr_assignment *assign;
- cl_git_pass(git_attr_file__new(&file));
- cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr3"), file));
- cl_assert_strequal(cl_fixture("attr/attr3"), file->path);
+ 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_strequal("*.java", rule->match.pattern);
+ cl_assert_equal_s("*.java", rule->match.pattern);
cl_assert(rule->assigns.length == 3);
assign = git_attr_rule__lookup_assignment(rule, "diff");
- cl_assert_strequal("diff", assign->name);
- cl_assert_strequal("java", assign->value);
+ cl_assert_equal_s("diff", assign->name);
+ cl_assert_equal_s("java", assign->value);
assign = git_attr_rule__lookup_assignment(rule, "crlf");
- cl_assert_strequal("crlf", assign->name);
- cl_assert(GIT_ATTR_FALSE == assign->value);
+ cl_assert_equal_s("crlf", assign->name);
+ cl_assert(GIT_ATTR_FALSE(assign->value));
assign = git_attr_rule__lookup_assignment(rule, "myAttr");
- cl_assert_strequal("myAttr", assign->name);
- cl_assert(GIT_ATTR_TRUE == assign->value);
+ 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_strequal("NoMyAttr.java", rule->match.pattern);
+ cl_assert_equal_s("NoMyAttr.java", rule->match.pattern);
cl_assert(rule->assigns.length == 1);
assign = get_assign(rule, 0);
- cl_assert_strequal("myAttr", assign->name);
- cl_assert(assign->value == NULL);
+ cl_assert_equal_s("myAttr", assign->name);
+ cl_assert(GIT_ATTR_UNSPECIFIED(assign->value));
rule = get_rule(2);
- cl_assert_strequal("README", rule->match.pattern);
+ cl_assert_equal_s("README", rule->match.pattern);
cl_assert(rule->assigns.length == 1);
assign = get_assign(rule, 0);
- cl_assert_strequal("caveat", assign->name);
- cl_assert_strequal("unspecified", assign->value);
+ cl_assert_equal_s("caveat", assign->name);
+ cl_assert_equal_s("unspecified", assign->value);
git_attr_file__free(file);
}
diff --git a/tests-clar/attr/flags.c b/tests-clar/attr/flags.c
new file mode 100644
index 000000000..80c6e1171
--- /dev/null
+++ b/tests-clar/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-clar/attr/lookup.c b/tests-clar/attr/lookup.c
index 9462bbe7f..b2a6aac64 100644
--- a/tests-clar/attr/lookup.c
+++ b/tests-clar/attr/lookup.c
@@ -1,61 +1,52 @@
#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(&file));
- cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr0"), file));
- cl_assert_strequal(cl_fixture("attr/attr0"), file->path);
+ 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_strequal("test", path.path);
- cl_assert_strequal("test", path.basename);
+ 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(value == GIT_ATTR_TRUE);
+ 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);
}
-typedef struct {
- const char *path;
- const char *attr;
- const char *expected;
- int use_strcmp;
- int force_dir;
-} test_case;
-
-static void run_test_cases(git_attr_file *file, test_case *cases)
+static void run_test_cases(git_attr_file *file, struct attr_expected *cases, int force_dir)
{
git_attr_path path;
const char *value = NULL;
- test_case *c;
+ 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 (c->force_dir)
+ if (force_dir)
path.is_dir = 1;
error = git_attr_file__lookup_one(file,&path,c->attr,&value);
- if (error != GIT_SUCCESS)
- fprintf(stderr, "failure with %s %s %s\n", c->path, c->attr, c->expected);
cl_git_pass(error);
- if (c->use_strcmp)
- cl_assert_strequal(c->expected, value);
- else
- cl_assert(c->expected == value);
+ attr_check_expected(c->expected, c->expected_str, value);
+
+ git_attr_path__free(&path);
}
}
@@ -63,140 +54,146 @@ void test_attr_lookup__match_variants(void)
{
git_attr_file *file;
git_attr_path path;
- test_case cases[] = {
+
+ 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", GIT_ATTR_TRUE, 0, 0 },
- { "/testing/for/pat0", "attr0", GIT_ATTR_TRUE, 0, 0 },
- { "relative/to/pat0", "attr0", GIT_ATTR_TRUE, 0, 0 },
- { "this-contains-pat0-inside", "attr0", NULL, 0, 0 },
- { "this-aint-right", "attr0", NULL, 0, 0 },
- { "/this/pat0/dont/match", "attr0", NULL, 0, 0 },
+ { "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", GIT_ATTR_TRUE, 0, 0 },
- { "pat1", "attr1", NULL, 0, 0 },
- { "/testing/for/pat1", "attr1", NULL, 0, 0 },
- { "/testing/for/pat0", "attr1", GIT_ATTR_TRUE, 0, 0 },
- { "/testing/for/pat1/inside", "attr1", GIT_ATTR_TRUE, 0, 0 },
- { "misc", "attr1", GIT_ATTR_TRUE, 0, 0 },
+ { "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", NULL, 0, 0 },
- { "pat2", "attr2", GIT_ATTR_TRUE, 0, 1 },
- { "/testing/for/pat2", "attr2", NULL, 0, 0 },
- { "/testing/for/pat2", "attr2", GIT_ATTR_TRUE, 0, 1 },
- { "/not/pat2/yousee", "attr2", NULL, 0, 0 },
- { "/not/pat2/yousee", "attr2", NULL, 0, 1 },
+ { "pat2", "attr2", EXPECT_UNDEFINED, NULL },
+ { "/testing/for/pat2", "attr2", EXPECT_UNDEFINED, NULL },
+ { "/not/pat2/yousee", "attr2", EXPECT_UNDEFINED, NULL },
/* path match */
- { "pat3file", "attr3", NULL, 0, 0 },
- { "/pat3dir/pat3file", "attr3", NULL, 0, 0 },
- { "pat3dir/pat3file", "attr3", GIT_ATTR_TRUE, 0, 0 },
+ { "pat3file", "attr3", EXPECT_UNDEFINED, NULL },
+ { "/pat3dir/pat3file", "attr3", EXPECT_TRUE, NULL },
+ { "pat3dir/pat3file", "attr3", EXPECT_TRUE, NULL },
/* pattern* match */
- { "pat4.txt", "attr4", GIT_ATTR_TRUE, 0, 0 },
- { "/fun/fun/fun/pat4.c", "attr4", GIT_ATTR_TRUE, 0, 0 },
- { "pat4.", "attr4", GIT_ATTR_TRUE, 0, 0 },
- { "pat4", "attr4", NULL, 0, 0 },
- { "/fun/fun/fun/pat4.dir", "attr4", GIT_ATTR_TRUE, 0, 1 },
+ { "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", GIT_ATTR_TRUE, 0, 0 },
- { "foo.pat5", "attr5", GIT_ATTR_TRUE, 0, 1 },
- { "/this/is/ok.pat5", "attr5", GIT_ATTR_TRUE, 0, 0 },
- { "/this/is/bad.pat5/yousee.txt", "attr5", NULL, 0, 0 },
- { "foo.pat5", "attr100", NULL, 0, 0 },
+ { "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", NULL, 0, 0 },
- { "pat6/pat6/foobar.pat6", "attr6", GIT_ATTR_TRUE, 0, 0 },
- { "pat6/pat6/.pat6", "attr6", GIT_ATTR_TRUE, 0, 0 },
- { "pat6/pat6/extra/foobar.pat6", "attr6", NULL, 0, 0 },
- { "/prefix/pat6/pat6/foobar.pat6", "attr6", NULL, 0, 0 },
- { "/pat6/pat6/foobar.pat6", "attr6", NULL, 0, 0 },
+ { "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", GIT_ATTR_TRUE, 0, 0 },
- { "pat7e__x", "attr7", GIT_ATTR_TRUE, 0, 0 },
- { "pat7b/1y", "attr7", NULL, 0, 0 }, /* ? does not match / */
- { "pat7e_x", "attr7", NULL, 0, 0 },
- { "pat7aaaa", "attr7", NULL, 0, 0 },
- { "pat7zzzz", "attr7", NULL, 0, 0 },
- { "/this/can/be/anything/pat7a12z", "attr7", GIT_ATTR_TRUE, 0, 0 },
- { "but/it/still/must/match/pat7aaaa", "attr7", NULL, 0, 0 },
- { "pat7aaay.fail", "attr7", NULL, 0, 0 },
+ { "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", GIT_ATTR_TRUE, 0, 0 },
- { "/gotta love/pat8 with spaces", "attr8", GIT_ATTR_TRUE, 0, 0 },
- { "failing pat8 with spaces", "attr8", NULL, 0, 0 },
- { "spaces", "attr8", NULL, 0, 0 },
+ { "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", GIT_ATTR_TRUE, 0, 0 },
- { "/eof/pat9", "attr9", GIT_ATTR_TRUE, 0, 0 },
- { "pat", "attr9", NULL, 0, 0 },
- { "at9", "attr9", NULL, 0, 0 },
- { "pat9.fail", "attr9", NULL, 0, 0 },
+ { "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, NULL, 0, 0 }
+ { NULL, NULL, 0, NULL }
};
- cl_git_pass(git_attr_file__new(&file));
- cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr1"), file));
- cl_assert_strequal(cl_fixture("attr/attr1"), file->path);
+ 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_strequal("pat0", path.basename);
+ cl_assert_equal_s("pat0", path.basename);
- run_test_cases(file, cases);
+ 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;
- test_case cases[] = {
+
+ struct attr_expected cases[] = {
/* pat0 -> simple assign */
- { "pat0", "simple", GIT_ATTR_TRUE, 0, 0 },
- { "/testing/pat0", "simple", GIT_ATTR_TRUE, 0, 0 },
- { "pat0", "fail", NULL, 0, 0 },
- { "/testing/pat0", "fail", NULL, 0, 0 },
+ { "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", GIT_ATTR_FALSE, 0, 0 },
- { "/testing/pat1", "neg", GIT_ATTR_FALSE, 0, 0 },
- { "pat1", "fail", NULL, 0, 0 },
- { "/testing/pat1", "fail", NULL, 0, 0 },
+ { "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", GIT_ATTR_TRUE, 0, 0 },
- { "pat2", "notundef", NULL, 0, 0 },
- { "/lead/in/pat1", "notundef", GIT_ATTR_TRUE, 0, 0 },
- { "/lead/in/pat2", "notundef", NULL, 0, 0 },
+ { "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", "test-value", 1, 0 },
- { "pat3", "notassigned", NULL, 0, 0 },
+ { "pat3", "assigned", EXPECT_STRING, "test-value" },
+ { "pat3", "notassigned", EXPECT_UNDEFINED, NULL },
/* assign value */
- { "pat4", "rule-with-more-chars", "value-with-more-chars", 1, 0 },
- { "pat4", "notassigned-rule-with-more-chars", NULL, 0, 0 },
+ { "pat4", "rule-with-more-chars", EXPECT_STRING, "value-with-more-chars" },
+ { "pat4", "notassigned-rule-with-more-chars", EXPECT_UNDEFINED, NULL },
/* empty assignments */
- { "pat5", "empty", GIT_ATTR_TRUE, 0, 0 },
- { "pat6", "negempty", GIT_ATTR_FALSE, 0, 0 },
+ { "pat5", "empty", EXPECT_TRUE, NULL },
+ { "pat6", "negempty", EXPECT_FALSE, NULL },
/* multiple assignment */
- { "pat7", "multiple", GIT_ATTR_TRUE, 0, 0 },
- { "pat7", "single", GIT_ATTR_FALSE, 0, 0 },
- { "pat7", "values", "1", 1, 0 },
- { "pat7", "also", "a-really-long-value/*", 1, 0 },
- { "pat7", "happy", "yes!", 1, 0 },
- { "pat8", "again", GIT_ATTR_TRUE, 0, 0 },
- { "pat8", "another", "12321", 1, 0 },
+ { "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", NULL, 0, 0 },
- { "patbad0", "notundef", GIT_ATTR_TRUE, 0, 0 },
- { "patbad1", "simple", NULL, 0, 0 },
+ { "patbad0", "simple", EXPECT_UNDEFINED, NULL },
+ { "patbad0", "notundef", EXPECT_TRUE, NULL },
+ { "patbad1", "simple", EXPECT_UNDEFINED, NULL },
/* eof assignment */
- { "pat9", "at-eof", GIT_ATTR_FALSE, 0, 0 },
+ { "pat9", "at-eof", EXPECT_FALSE, NULL },
/* sentinel at end */
- { NULL, NULL, NULL, 0, 0 }
+ { NULL, NULL, 0, NULL }
};
- cl_git_pass(git_attr_file__new(&file));
- cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr2"), file));
+ 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);
+ run_test_cases(file, cases, 0);
git_attr_file__free(file);
}
@@ -204,34 +201,34 @@ void test_attr_lookup__assign_variants(void)
void test_attr_lookup__check_attr_examples(void)
{
git_attr_file *file;
- test_case cases[] = {
- { "foo.java", "diff", "java", 1, 0 },
- { "foo.java", "crlf", GIT_ATTR_FALSE, 0, 0 },
- { "foo.java", "myAttr", GIT_ATTR_TRUE, 0, 0 },
- { "foo.java", "other", NULL, 0, 0 },
- { "/prefix/dir/foo.java", "diff", "java", 1, 0 },
- { "/prefix/dir/foo.java", "crlf", GIT_ATTR_FALSE, 0, 0 },
- { "/prefix/dir/foo.java", "myAttr", GIT_ATTR_TRUE, 0, 0 },
- { "/prefix/dir/foo.java", "other", NULL, 0, 0 },
- { "NoMyAttr.java", "crlf", GIT_ATTR_FALSE, 0, 0 },
- { "NoMyAttr.java", "myAttr", NULL, 0, 0 },
- { "NoMyAttr.java", "other", NULL, 0, 0 },
- { "/prefix/dir/NoMyAttr.java", "crlf", GIT_ATTR_FALSE, 0, 0 },
- { "/prefix/dir/NoMyAttr.java", "myAttr", NULL, 0, 0 },
- { "/prefix/dir/NoMyAttr.java", "other", NULL, 0, 0 },
- { "README", "caveat", "unspecified", 1, 0 },
- { "/specific/path/README", "caveat", "unspecified", 1, 0 },
- { "README", "missing", NULL, 0, 0 },
- { "/specific/path/README", "missing", NULL, 0, 0 },
+
+ 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, NULL, 0, 0 }
+ { NULL, NULL, 0, NULL }
};
- cl_git_pass(git_attr_file__new(&file));
- cl_git_pass(git_attr_file__from_file(NULL, cl_fixture("attr/attr3"), file));
+ 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);
+ run_test_cases(file, cases, 0);
git_attr_file__free(file);
}
@@ -239,24 +236,27 @@ void test_attr_lookup__check_attr_examples(void)
void test_attr_lookup__from_buffer(void)
{
git_attr_file *file;
- test_case cases[] = {
- { "abc", "foo", GIT_ATTR_TRUE, 0, 0 },
- { "abc", "bar", GIT_ATTR_TRUE, 0, 0 },
- { "abc", "baz", GIT_ATTR_TRUE, 0, 0 },
- { "aaa", "foo", GIT_ATTR_TRUE, 0, 0 },
- { "aaa", "bar", NULL, 0, 0 },
- { "aaa", "baz", GIT_ATTR_TRUE, 0, 0 },
- { "qqq", "foo", NULL, 0, 0 },
- { "qqq", "bar", NULL, 0, 0 },
- { "qqq", "baz", GIT_ATTR_TRUE, 0, 0 },
- { NULL, NULL, NULL, 0, 0 }
+
+ struct attr_expected cases[] = {
+ { "abc", "foo", EXPECT_TRUE, NULL },
+ { "abc", "bar", EXPECT_TRUE, NULL },
+ { "abc", "baz", EXPECT_TRUE, NULL },
+ { "aaa", "foo", EXPECT_TRUE, NULL },
+ { "aaa", "bar", EXPECT_UNDEFINED, NULL },
+ { "aaa", "baz", EXPECT_TRUE, NULL },
+ { "qqq", "foo", EXPECT_UNDEFINED, NULL },
+ { "qqq", "bar", EXPECT_UNDEFINED, NULL },
+ { "qqq", "baz", EXPECT_TRUE, NULL },
+ { NULL, NULL, 0, NULL }
};
- cl_git_pass(git_attr_file__new(&file));
- cl_git_pass(git_attr_file__from_buffer(NULL, "a* foo\nabc bar\n* baz", file));
+ cl_git_pass(git_attr_file__new(&file, 0, NULL, NULL));
+
+ cl_git_pass(git_attr_file__parse_buffer(NULL, "a* foo\nabc bar\n* baz", file));
+
cl_assert(file->rules.length == 3);
- run_test_cases(file, cases);
+ run_test_cases(file, cases, 0);
git_attr_file__free(file);
}
diff --git a/tests-clar/attr/repo.c b/tests-clar/attr/repo.c
index 7a716042a..a88dfb3f9 100644
--- a/tests-clar/attr/repo.c
+++ b/tests-clar/attr/repo.c
@@ -3,6 +3,8 @@
#include "git2/attr.h"
#include "attr.h"
+#include "attr_expect.h"
+
static git_repository *g_repo = NULL;
void test_attr_repo__initialize(void)
@@ -12,88 +14,63 @@ void test_attr_repo__initialize(void)
* Also rename gitattributes to .gitattributes, because it contains
* macro definitions which are only allowed in the root.
*/
- cl_fixture_sandbox("attr");
- cl_git_pass(p_rename("attr/.gitted", "attr/.git"));
- cl_git_pass(p_rename("attr/gitattributes", "attr/.gitattributes"));
- cl_git_pass(git_repository_open(&g_repo, "attr/.git"));
+ g_repo = cl_git_sandbox_init("attr");
}
void test_attr_repo__cleanup(void)
{
- git_repository_free(g_repo);
+ cl_git_sandbox_cleanup();
g_repo = NULL;
- cl_fixture_cleanup("attr");
}
void test_attr_repo__get_one(void)
{
- const char *value;
- struct {
- const char *file;
- const char *attr;
- const char *expected;
- } test_cases[] = {
- { "root_test1", "repoattr", GIT_ATTR_TRUE },
- { "root_test1", "rootattr", GIT_ATTR_TRUE },
- { "root_test1", "missingattr", NULL },
- { "root_test1", "subattr", NULL },
- { "root_test1", "negattr", NULL },
- { "root_test2", "repoattr", GIT_ATTR_TRUE },
- { "root_test2", "rootattr", GIT_ATTR_FALSE },
- { "root_test2", "missingattr", NULL },
- { "root_test2", "multiattr", GIT_ATTR_FALSE },
- { "root_test3", "repoattr", GIT_ATTR_TRUE },
- { "root_test3", "rootattr", NULL },
- { "root_test3", "multiattr", "3" },
- { "root_test3", "multi2", NULL },
- { "sub/subdir_test1", "repoattr", GIT_ATTR_TRUE },
- { "sub/subdir_test1", "rootattr", GIT_ATTR_TRUE },
- { "sub/subdir_test1", "missingattr", NULL },
- { "sub/subdir_test1", "subattr", "yes" },
- { "sub/subdir_test1", "negattr", GIT_ATTR_FALSE },
- { "sub/subdir_test1", "another", NULL },
- { "sub/subdir_test2.txt", "repoattr", GIT_ATTR_TRUE },
- { "sub/subdir_test2.txt", "rootattr", GIT_ATTR_TRUE },
- { "sub/subdir_test2.txt", "missingattr", NULL },
- { "sub/subdir_test2.txt", "subattr", "yes" },
- { "sub/subdir_test2.txt", "negattr", GIT_ATTR_FALSE },
- { "sub/subdir_test2.txt", "another", "zero" },
- { "sub/subdir_test2.txt", "reposub", GIT_ATTR_TRUE },
- { "sub/sub/subdir.txt", "another", "one" },
- { "sub/sub/subdir.txt", "reposubsub", GIT_ATTR_TRUE },
- { "sub/sub/subdir.txt", "reposub", NULL },
- { "does-not-exist", "foo", "yes" },
- { "sub/deep/file", "deepdeep", GIT_ATTR_TRUE },
- { NULL, NULL, NULL }
+ 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->file != NULL; scan++) {
- git_buf b = GIT_BUF_INIT;
-
- git_buf_printf(&b, "%s:%s == expect %s",
- scan->file, scan->attr, scan->expected);
-
- cl_must_pass_(
- git_attr_get(g_repo, scan->file, scan->attr, &value) == GIT_SUCCESS,
- b.ptr);
-
- git_buf_printf(&b, ", got %s", value);
-
- if (scan->expected == NULL ||
- scan->expected == GIT_ATTR_TRUE ||
- scan->expected == GIT_ATTR_FALSE)
- {
- cl_assert_(scan->expected == value, b.ptr);
- } else {
- cl_assert_strequal(scan->expected, value);
- }
-
- git_buf_free(&b);
+ 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, value);
}
- cl_git_pass(git_attr_cache__is_cached(g_repo, ".git/info/attributes"));
- cl_git_pass(git_attr_cache__is_cached(g_repo, ".gitattributes"));
- cl_git_pass(git_attr_cache__is_cached(g_repo, "sub/.gitattributes"));
+ 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)
@@ -101,40 +78,39 @@ 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(g_repo, "root_test1", 4, names, values));
+ cl_git_pass(git_attr_get_many(values, g_repo, 0, "root_test1", 4, names));
- cl_assert(values[0] == GIT_ATTR_TRUE);
- cl_assert(values[1] == GIT_ATTR_TRUE);
- cl_assert(values[2] == NULL);
- cl_assert(values[3] == NULL);
+ 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(g_repo, "root_test2", 4, names, values));
+ cl_git_pass(git_attr_get_many(values, g_repo, 0, "root_test2", 4, names));
- cl_assert(values[0] == GIT_ATTR_TRUE);
- cl_assert(values[1] == GIT_ATTR_FALSE);
- cl_assert(values[2] == NULL);
- cl_assert(values[3] == NULL);
+ 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(g_repo, "sub/subdir_test1", 4, names, values));
-
- cl_assert(values[0] == GIT_ATTR_TRUE);
- cl_assert(values[1] == GIT_ATTR_TRUE);
- cl_assert(values[2] == NULL);
- cl_assert_strequal("yes", values[3]);
+ cl_git_pass(git_attr_get_many(values, g_repo, 0, "sub/subdir_test1", 4, names));
+ cl_assert(GIT_ATTR_TRUE(values[0]));
+ cl_assert(GIT_ATTR_TRUE(values[1]));
+ cl_assert(GIT_ATTR_UNSPECIFIED(values[2]));
+ cl_assert_equal_s("yes", values[3]);
}
static int count_attrs(
- const char *GIT_UNUSED(name),
- const char *GIT_UNUSED(value),
+ const char *name,
+ const char *value,
void *payload)
{
- GIT_UNUSED_ARG(name);
- GIT_UNUSED_ARG(value);
+ GIT_UNUSED(name);
+ GIT_UNUSED(value);
*((int *)payload) += 1;
- return GIT_SUCCESS;
+ return 0;
}
void test_attr_repo__foreach(void)
@@ -142,16 +118,17 @@ void test_attr_repo__foreach(void)
int count;
count = 0;
- cl_git_pass(git_attr_foreach(g_repo, "root_test1", &count_attrs, &count));
+ 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, "sub/subdir_test1",
+ 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, "sub/subdir_test2.txt",
+ 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 */
}
@@ -160,20 +137,20 @@ void test_attr_repo__manpage_example(void)
{
const char *value;
- cl_git_pass(git_attr_get(g_repo, "sub/abc", "foo", &value));
- cl_assert(value == GIT_ATTR_TRUE);
+ 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(g_repo, "sub/abc", "bar", &value));
- cl_assert(value == NULL);
+ 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(g_repo, "sub/abc", "baz", &value));
- cl_assert(value == GIT_ATTR_FALSE);
+ 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(g_repo, "sub/abc", "merge", &value));
- cl_assert_strequal("filfre", 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(g_repo, "sub/abc", "frotz", &value));
- cl_assert(value == NULL);
+ 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)
@@ -183,27 +160,27 @@ void test_attr_repo__macros(void)
const char *names3[3] = { "macro2", "multi2", "multi3" };
const char *values[5];
- cl_git_pass(git_attr_get_many(g_repo, "binfile", 5, names, values));
+ cl_git_pass(git_attr_get_many(values, g_repo, 0, "binfile", 5, names));
- cl_assert(values[0] == GIT_ATTR_TRUE);
- cl_assert(values[1] == GIT_ATTR_TRUE);
- cl_assert(values[2] == GIT_ATTR_FALSE);
- cl_assert(values[3] == GIT_ATTR_FALSE);
- cl_assert(values[4] == NULL);
+ 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(g_repo, "macro_test", 5, names2, values));
+ cl_git_pass(git_attr_get_many(values, g_repo, 0, "macro_test", 5, names2));
- cl_assert(values[0] == GIT_ATTR_TRUE);
- cl_assert(values[1] == GIT_ATTR_TRUE);
- cl_assert(values[2] == GIT_ATTR_FALSE);
- cl_assert(values[3] == NULL);
- cl_assert_strequal("77", values[4]);
+ 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(g_repo, "macro_test", 3, names3, values));
+ cl_git_pass(git_attr_get_many(values, g_repo, 0, "macro_test", 3, names3));
- cl_assert(values[0] == GIT_ATTR_TRUE);
- cl_assert(values[1] == GIT_ATTR_FALSE);
- cl_assert_strequal("answer", values[2]);
+ 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)
@@ -212,12 +189,12 @@ void test_attr_repo__bad_macros(void)
"firstmacro", "secondmacro", "thirdmacro" };
const char *values[6];
- cl_git_pass(git_attr_get_many(g_repo, "macro_bad", 6, names, values));
+ 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(values[0] == NULL);
- cl_assert(values[1] == GIT_ATTR_TRUE);
- cl_assert(values[2] == GIT_ATTR_FALSE);
+ 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
@@ -241,7 +218,56 @@ void test_attr_repo__bad_macros(void)
* so summary results should be:
* -firstmacro secondmacro="hahaha" thirdmacro
*/
- cl_assert(values[3] == GIT_ATTR_FALSE);
- cl_assert_strequal("hahaha", values[4]);
- cl_assert(values[5] == GIT_ATTR_TRUE);
+ 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)
+{
+ int index_pos;
+ git_index_entry *entry;
+
+ add_to_workdir(filename, CONTENT);
+ cl_git_pass(git_index_add(index, filename, 0));
+
+ index_pos = git_index_find(index, filename);
+ cl_assert(index_pos >= 0);
+
+ entry = git_index_get(index, index_pos);
+ cl_assert_equal_i(0, git_oid_streq(&entry->oid, expected_sha));
+}
+
+void test_attr_repo__staging_properly_normalizes_line_endings_according_to_gitattributes_directives(void)
+{
+ git_index* index;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ add_to_workdir(".gitattributes", GITATTR);
+
+ assert_proper_normalization(index, "text.txt", "22c74203bace3c2e950278c7ab08da0fca9f4e9b");
+ assert_proper_normalization(index, "huh.dunno", "22c74203bace3c2e950278c7ab08da0fca9f4e9b");
+ assert_proper_normalization(index, "binary.data", "66eeff1fcbacf589e6d70aa70edd3fce5be2b37c");
+
+ git_index_free(index);
}
diff --git a/tests-clar/buf/basic.c b/tests-clar/buf/basic.c
index b025c9915..d558757a9 100644
--- a/tests-clar/buf/basic.c
+++ b/tests-clar/buf/basic.c
@@ -8,7 +8,7 @@ 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(strcmp(git_buf_cstr(&buf1), test_string) == 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);
@@ -20,10 +20,10 @@ 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(strcmp(git_buf_cstr(&buf2), "shoop da 23 ") == 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(strcmp(git_buf_cstr(&buf2), "shoop da 23 woop 42") == 0);
+ cl_assert_equal_s(git_buf_cstr(&buf2), "shoop da 23 woop 42");
git_buf_free(&buf2);
}
diff --git a/tests-clar/clar b/tests-clar/clar
index a718a00ec..873dc3b0c 100755
--- a/tests-clar/clar
+++ b/tests-clar/clar
@@ -87,7 +87,7 @@ class ClarTestBuilder:
if not self.suite_data:
raise RuntimeError(
- 'No tests found under "%s"' % folder_name)
+ 'No tests found under "%s"' % path)
def render(self):
main_file = os.path.join(self.path, 'clar_main.c')
@@ -107,10 +107,11 @@ class ClarTestBuilder:
def _render_cb(self, cb):
return '{"%s", &%s}' % (cb['short_name'], cb['symbol'])
- def _render_suite(self, suite):
+ def _render_suite(self, suite, index):
template = Template(
r"""
{
+ ${suite_index},
"${clean_name}",
${initialize},
${cleanup},
@@ -124,6 +125,7 @@ r"""
if suite[cb] else "{NULL, NULL}")
return template.substitute(
+ suite_index = index,
clean_name = suite['name'].replace("_", "::"),
initialize = callbacks['initialize'],
cleanup = callbacks['cleanup'],
@@ -178,8 +180,8 @@ static const struct clar_func _clar_cb_${suite_name}[] = {
suite_names = sorted(self.suite_names)
suite_data = [
- self._render_suite(self.suite_data[s])
- for s in suite_names
+ self._render_suite(self.suite_data[s], i)
+ for i, s in enumerate(suite_names)
]
callbacks = [
@@ -214,7 +216,7 @@ static const struct clar_func _clar_cb_${suite_name}[] = {
content = bytearray(content, 'utf_8')
content = base64.b64decode(content)
content = zlib.decompress(content)
- return str(content)
+ return str(content, 'utf-8')
else:
content = base64.b64decode(content)
return zlib.decompress(content)
@@ -297,13 +299,13 @@ static const struct clar_func _clar_cb_${suite_name}[] = {
CLAR_FILES = {
-"clar.c" : r"""eJyNGdtu2zb0Wf4Kzt0aOVEcJ32L1wBFtw7BtgxoU3RAEwi0RMdcJdETqVzW+d93eHgRdXG6vsQ6d5472Re8yoomZ+RHKiWr1XxzMXnhYZKpv8ptD6bygq8GMC76oJpXd11YSdVmwEhrpJqcHJKa/d3wmuVkLWoiaZWvxCMIIYcnIcuTPFFPWyZ7kgAsFcUDAHidszVJP11evTqbvIg81QOvcvFgWFuotb0FyA0rCrrlPXAOxmVWQwQKeMVI+vuby6v07VuSplnOsiJAaXPiLZw5gZ8zkna/W7ryCwi2iFLkDEhbUECXbTyQpMFHS0GzjEnZFTWEhRbWebON4Q+a5z/0Ifi6Qh+mv19e/fLp1VmaAjDa1vSupCQTZckqFUMmJGSK7np1NtWSA9FVtn2KlUjIuhZlQpRIJf8HTLKoVCLSgh1Vev3+49XbN9c/h8I+pX/8ShZnAeRDevnhp8v38eOMxPEjeUlSgLwDyIx895osQubyi2LlNnUuKFiFDh4AgYVVOV9PIp1e+uxgaJMpEzjy4frNdXq9nLxghWSdZIHMe6Bc5wWBJNY/tzyPz2aYty1dU3FId5NSveQZqOxpRLPaZJ9mBa3nm+lkoul4Ru4Fh6KRaV3GmaikglShNTlMpWjqjM2WfbpMQGRGKBMSAnMGabr0SkLUZM0fVVOzVLuvI2lFZU+MI61oyYw4PKI+Q8rqGkr96yQKGRToXU7AcYron2nVlCtWL7tEsuGK9WBrXjDLWIB7xxlRZVrKOw1358xqvlVcVGBeNLTvsGKPYNGu9YWl6RlOM8XvWWrtH8FYo42J+GE0SHdcoWjhQYELMtFUao9xXsIIrqDAjL81M4Y/PixEBlqygtGq2c5ihB5CZAy+i4YAPxWC5podRkG6atZE1bTcCu1hZ7YHpKyiq4IB+Q5aFBjSi/e6qbK+13ReLL1xW2g/aNLMObzlRo/tYR9o4RVXnBbQWsaw9ng+TAMCzEL0KkhIu2HQdkGlv4OGZTi2MOtUejjPdMmHtRZgtT1xN6AJafPAAgYpjmUjeyUciJWbRsFIq74tWgNM8iNgv0gkQnlQQM6kfYm3X4yotDlxv7LxQMaaoLoNYE2hgvPnROKJ4nEvPcdHV6Lu2gIdICHz+XzWD6ZdPPYEs6ks3iWppdDmh+wOrWX/fM80lhbFimZfiLgHz3HoOlrB91+NSzVJ6jE75HvTKHHHKlZTBUuR9hbJqaJk9YSqAnYnWzN22vWwfNL2t/x8S15DPRH4ZwUZ+K7T60wBBHwmgYA1ZDLA3XKUzdnX5+zCbV29FTUzp9WVqNuy7IVigsx1U2GvjZ8v4mQ/uu0RzxC5Rjn5arqdqSGpT4GHm3cbOQjSvMLapvuqIRt2SZBwim1+TWKzasd90hl5rdcZ3fSQrLX4+AJapV52rj7+9tsM0FEPp1UDWFvhvyPIj+fMWThzDE1nFIS6RtBjLG56zJxYCx/YHsKN3dZI39COjjQULwkllAmh1RNBXcfgOdfOScnURuSYLmM2EqNxOYp0xnoiG8lON/MOxS7mPRE0XoDFw7wgFz5v4Lx6tk1GEpptoUtZDtNAXNJxkyt753/ilpRJZMAuOf128LCB3kpig3Wux7zSjECPGDgYionCs9uBcHSUENfzo2hdMxZbnmCD6uHw01lkRbc5aH3jbG23FR+DUTdB3YdzYNjjzFBA5z3XGUALEh5f9IY9HwTf6LPUdtj4QjfIIG3Dda9VYjeVkeSwhaevvTHHLwj4j6FxdvUgR0fcBK2jyB5G//nMb+dWUdTtki8tOiEvreCg/XmY63YYpx1epclC32v0fUnUtObFE8m5NB1jX1uWcG0vxuLzjbY8CN8+Z/1/Rw9d5AgmPQehVf/TOTt/Kxucv5H0rrui0PoOD4PJtI6nHzXFOflBks8Ci0be3lQ31TQhmnLZEv5hsOeAA/DJiUcQcqz+/PNG3aj3TUVEBTFRGzs0zUJFAI1cIY8c4TG+6zOxR9hWj0/3NKotrSVLwViJayL8yBJ7Vn3Y+7ZtddL61KS1Jg8y2fuo0U8KQKYlQJ4uHY5m5moWRXYnxbmmx4lj+ry41S3t4PgAB2EQBpS1uDWj0AgyGgzfKWoBkTp5VK1E4WWSI3IGkXefCTldzLzi1lyt9mZxQP79V1sGp1s8a4J84CrbgOVoinUAXJnJgTw4xyEO0mPThmZa4MXr4eZl2KJuhzIb7vRDGM4fcpIL2DMrAWvLI5dqjlkGWOzLURBm+NB9OWgapqu97OyLwHlriFc1o1/wSDlb06ZQ53uPrSWbZtLuyiaPsOz2Z1D/9qRHK3zMxnbKpIsMbz6AmU5x6LolJFjTZxgyE4cRd77DGwlczN17ZFtn4CNYzee2YEJX7oIlEA33qvU5YRU4DRW2tWS8gMfXUoh+aULCdixFgyExOK8prW+Gkt92TO3dJvdtNns9bKmDBwzrcT8knegW2t6ltCk1U01dkaEg7EFt80nNS3VsOgz02ZzrWkqGb0FJ+xaU7HkE6sGDRcYyy41oijzFdMCk3LeB+exyBukQmDOFW5nOWpHFpwlekMQ6HsibzbpLuBt7/e3bj8OO+sEmNdzaPc4se6GEkT3M4yyLHaSD4brsUNhrvScMn08cnZvaw1He0ugwAol92bPA4HEPcPYhyuJ8ZJ3p5qnPOCcIb+iX4RZrxoF+Du+utmMLib6ZjKS/ubDg1S5MIX+T+27fNcx295FuhC0bWhIoMWc7J7R39SE15RIaFq2g4WcM7Z6bBtVp9tjrC1HdjV06E+L6mC08UJLCNctf9exbXf8JMTHvJIdiS/9uwv2tfwlrX9+ev4cZQVj/9sGgFHlT4PuILk7/ny8l5dVgkOAEutVm6AcO217audPptrvJf1q+/6U=""",
-"clar_print_default.c" : r"""eJyFU01P4zAQPSe/YqgU1a5Cuadi98ap4rLaE6DIxA5YSu3InnQPK/479jgFB9FycuZ53vObj5QeBeoOjlZL6Abh2tFpg602Gln4AFQe285OBmuIsZ80qhPQWeMRulfhYJMujDgoz8v/ZcGiJP+k78qCpHu22lshlYRKJjXfQOUfzaqG+CJfvJCrZgp/UDhUMpAC+laWZ6rwrxNK+8/8XEkElHPWJeBcBQnKmB9YRt6Vn0YfTfJYkCunRuuwpVzPLlqnHPJtpsOp0x7d1GFKowTY0EF2T09CaCyHO6GHyamG+hokeO6q8k1TeWCV5/AQgko+wcM1hiOml0VBqte/qNAsjr2I4cpYkMp3To+o7YLS6yFnDNqE8U2HZ+W+6MzowhecFmHOS009+BfK0j2w+SJ7HK5u4f7vfs+D/DmdLJ0vp3N5f6yJTlm+5sl62Me0M1klCehD35X8uj+RsFsixMlWuuqC38SG37C+W0MD6+36B380Ifb9f0gmbjZgrB1hc7Pc3uTokrR4Dru6kA6DqGG73ZLwUbSDDlfCvYw7Cn38KVmMa0gzK479XJ5HGWZBeE0UnjjKSDaHb+U7mrWGAw==""",
-"clar_print_tap.c" : r"""eJyNVMFu2zAMPVtfwbgIYBu2gWK3BmuxnYthh+02wFBtORXmSIYkZxiG/vso2m6lJF12skk9ko+PlJh13MkWjlp20A7cNKORyjVSSZfhDzhhXdPqSbkSvG0n6cTqaLWyDtpnbqCYDxQ/CJuzPyzJfMr8LXy3ugLgiW/FEYU+S799+gpHYazUCm4//FBpvmMvjL1D2T5PrtO/1HXa3iGM0WZ2/A/d2BcE7xhLZA/ZJkqYvPZwAyO3VnTAhwG2HRHLbI7NlAFJbCwRgxVRYM/lgIEYxA9a7U+jg4IlxiVxtjXNbV1vu/Nq78tIaUlDNR3WEVtnptbNMAJAQZ9AOkR7Lda6AFVVzSMLfDhzy/cC7mBr35qo7udeDnYfw63A8Uv3+460OMtGowE4y0b+GOqbhwtQ74+RPYp+Cen9MXKQakV2IdL7G5TjSZh8XY/lqBO2NXJ0fqM3H+HL98fHcFkAAsApgeAoj5Wu6/ra5dCKVie8sLQP/hrOF2I2ifXsmNePJryW2lq/hNVCDIkvK/oAqdIO9M8UxUjx48/ChK8mlmMJ0SdyRozaLDtnsysd0Fizy29ORPMGiqJAkv5DCga4f5fgT0gnKoE7WXqBqcCRN4PEI272445MzIQB3i5hWd9+oWHxNZrwtUk/o0iAvxug/T2eAqiET5HPOYXqssV8YX8BFTvXlQ==""",
-"clar_sandbox.c" : r"""eJyNVV1P20AQfLZ/xRIkYpNATItaVSkPlaBVVEoiEgQSRJaxz+SEfY7uLmkD4r931+fEHwRahBST3Zudmb0xSgeahxDOAgl+mATSnwd6dnvsffk07du2MmUutM2VvwwSHvk6nedNTpgJpc3RffrCtZ9tazz5NvEnoDSetngMDkE4VO7CntIu7JyA59qWJZleSAHeum9n7A/Gp4NLPHCotJ9mEXObfcWzE4QhU6pAvfaHP104Idi+/VLjHHNR5ZszvV/EMZNdUPyJ+RoSJh4M9V0ei4jF4F8PLj5+sK0Cx6gsupdoUJgthIYTOO43egw+E0s0SqrbKfagIVZr8muEulpdoKf848x8Xo3PLkeXw++D87OWDdYLSgSrmMRJb5xJcDjieH3g8LUc34dOh7s5fGM2Nj8wjQ/OhgifojGWMRm/JFPplOZiwWhKXnm9Xmo1I1CmFOF85ay9w1J37RxBV5ZkWS82/tpWbx8GMegZo24uM5EytC3KmBJt9DNYQSBWesbFQxe0XIHOYKEY9HA+7PfsN0i1qN4qeDVpmWKNWYUYktpliWIG+gfTE5bORwTqnF4PL09dc6wLBq5x+XaZiHhsdE1mXIFaKc3SjaCEPzIUUNNC4sOFlLlwLlmoMyy+I+7wTWWH78la/3lwVA3AMuMR5JFeCBWI6D7749B3eUyJQCXv3pQC1L7z2qVqvBoYiWoiwhmqQJZIs2JIrHyZVsCaKUQ/eRL5BQWjdMOjcnup4OuAJ3lyWjkeWXOT/7QobZvIrl8a9YCXHEy8s7hKy8UAVd885JZtIRhOQ7/xoS6iqf4ZcPUikyku7YnldGnRo+F4cAOY1N+BjEAlgZoxlS+5EmXrVZRJRBni5j54sY+7fB+W1ShBu9feRG2ziAYGKTuAoym9cbHfDKrXO50SjO7R+tqVXdAhpt1yOducxTHYtMUyYpQ+Ykzmvvrndhr/GMx6DAJdu+px77PnbT1QCTieosE1nujpxdX5+atDhYFlquoXOEf4/wjB3t62O7/9/hGKyVWV6FYvavT+AhbcW38=""",
+"clar.c" : r"""eJytGWtv20byM/krNs4lpmxasZTD4c5OXAS55mC0dYA8kAKJQazIlbUORcpcMrHb6r93ZvbB5UN2D2g+xOa8dmZ2nuvHskjzJhPsBVdKVPV0dRY+djAl6uv1pgers1wuBjBZ9kGVLK66sDWvVwNGXhFV+OyAVeKmkZXI2LKsmOJFtihvQQg7eOaz3Kln9d1GqJ4kAKuakwEAXmZiyZJP5xfP5+HjwFF9l0VWftesLdTo3gLUSuQ538geOAPlUnNCAAfIQrDkl1fnF8nr1yxJ0kykuYdCdaIN2BzDrxOWdL9buvVXEGwQ6zITQNqCPLp05YAs8T5aCp6mQqmuqCHM17DKmk0EP0g994FGyGVBPkx+Ob/436fn8yQBYLCp+NWas7Rcr0VRRxAJMdsjdz2f76FkT3SRbu6iuozZsirXMavLRMnfQCWDShQhDdhSJR/efbx4/erDj76wT8nbn9jx3IO8T87f//f8XXQ7YVF0y56yBCBvADJhj16y444mxQZCsU7ETbRolrH6LV6u65jHC7RZ45agi8G58x0ViBK5EqMS7a9LJCoyuQwDjE10HFjZpLW+dfb+w6sPyYfT8LGR1Anb71xiUDHIAPx1I7NoPqGgb+maQkKu6HjsRZ53nSN69fXpqUM6t2m0l+a8mq72whDpZMq+lRLSUSXVOkrLQtUQhLxiB4kqmyoVk9M+XVrCnY9QxswHZgIS4NQd4qPCpbytm0okGLAdSQuuemIsacHXQosjE9GGRFQVFJHfw8BnqOHc0xC8WjP8NSma9UJUp10i1cha9GBLmQvDmIOnxxnpyGStrhBu7UwruallWYB6wVC/g0Lcgkbb1heGpqc4T2v5TSRG/xGMUVqrSB/6BGXNLWueO5DngrRsitpCKrEpq9qQJWWR3+1Q28keweVcGRHITIERHeRlCuenueBFs5lEBD2AO9P4Lhqu/i4veYbs0H4SyExWV3y9KdH31iAHSETBF7kA8i2URVCkFwnLpkj7/sSIOXXKbaDkkUoTexUtN/kS2fFQ6B7i9nRU1OBEWcha8hxK2xjWmOpkDQgoVsnDICHpXhbqCMXiDRRMzaHT/mCaYtXwM9LDoj5R99pj1kaLAQwSgZJL9RLdE6tWTQ0ttXhYNAJ0ihBgt0giInmQZlalXUG4W0xZoDpRP//JIK2NVwM0YMkhz7P7RJJF0biXfJspUu4TxBeQaR1BUDhiNp1OJ/3bNZPQjtttCoO3EWwoUBef3aJR9o/fBGJ5ni94+pWV38CVEooVHvCP37WPkSRxmC3xvWrq8koUouI1TGnoPpbxmrPFHR3lsVvZyNip8sPcStrf1edL9hKSjcE/I0jDt50SqTPC49MRBaw+kwZuT0fZrH59zi7cJNrrshLaWkxNrOaqdxUhMXfKp/Z3aCsHHDEbr5f0I7atIAw0+KVWdOoq7fcVxCaLNBZmnIuPP/88wcIUICPQE+boTIsJgmFCHR7GzOZMECwrISLD4/WpHo4+rUZGNFxp4CvndG0rP9P6QakKR9zUFNTJovuLX7wb3dbWe4hss7FXoGuPqp263TYZao+VRjc0XJMNO42+S5C6ZJFekaI+6YS9xDGU7gfJWo2PzqDduAsEdNDD4dEARi3ct+fzUXWOrTrmIvx26p81gh5jsR14YsUa+EB3H671Nkq6RnB4iFBa7tZQTRgv7hiddQSes22QrUW9KjPKqjEdXTiNIa2yjsj3xHCimdjc6GYrOpmG807mmB6Ct6Tvg+o8fvbtJGA0DCx25gKPLnuyOyf83jIsk7rRUDTflzXgBSI8OusNENI65tGDnuk2OyNOdzjzYdubyxlN6kWAP5e2OplhacRNJoZx848kfUHNfOHbYqYfdngodV51DjK244/P8nJqDgq6BeepQcfsqTPEVRIHs4WD0m5LrwnsGLezWkDmVbyS+R3LpNLJN9oI8DZlcZWPXecDFW5w27uc9dcdPXSRJQh7DiKt/qJztm59HNjfKH7VnZJ4dUXG6K002vuIFCfsiWKfS+o76vJL8aXYixlSnraEbzX2BHA+mLEj9euvX+ov9bumYBi9rF4JbT3TIx2DUEIrGdANmG8YQ+a3yKgzgfyiQArsxyuoJrzQiWt4xS2Mz0ezHem74ZUSCaiuaG6FX9LYWI6mf2vngE6Qz3SQI7kX185jDb6xABlKgKiltoAZbFGfjy+xGO8f7VNp8VxPHMeXupOo77JOVx7b7NIcBBs121f7J/QVmIkbDkQjJhCUdZk7LnbI5hAG9jNms2MaDjoaYdcz2iK9PiawJeia6AN9xtFMf5EzrnXGX4MzBmMdeuaaDNRngV4pdNxWkc4Aea1z3++/7ZlGgSBYVIJ/NR/b0P6H0pHwBT4gBbs8aggjbdQEGc5eDtW2Z49M/Xvv/TB9krGshNm7KGFGuZWqnlIeAFYf5oWeVbTXMp52PACMRk1nJU4P5rZv9k/a9jXsAWZMdbwhPXrxJq9PdoYYKbX18gJs1WlBNWV3QvS3U5BEkTc2osddpL9ZAmZvLwy8YcXbeiZ0mzrcR27iDW18rC7tezOVEaqU4F/YdKYm//1b2HrDIinujkY7oePP2lbaFgbtAzK+O6N0K7g/2betOxh0xtE8kZMHw0NedkzobfHh+GSz2/OG2nteMjfhJgMruoW2KyuqUom6qQo2FESltq2xif4LRaQLKTSXTGJriIcvdXH7UhfveKLrwb0FyDCrVdnkWUJhQsG6a3NzUWcVwivQNvnbHEZzmUazmPbQchkN5E16YWF7fX96dzNA5/jBBjac+h1OL4m+hJH9zeEMi5keBhPFaYfCPKc4Qv/ZytLZUWUY/S0NXiOQmHdXA/SeXgFnHgMNzt2sVV0/xGrneNfr+2W4/eIApf8M0l2Jx6Yw3GxGwl8vPNSo/BBym+CjXWucaRgjVYq6AJQqOETbdsJ4b3ViFZdK6KnlNhWk91QXrk7/oE6Ql8XV2NIaM1vfTOIxP/EScdOAlSrqPZfPermk5v9vSu5KOQSuOc4uMJKomU0F9scfTM3bSvmDRSoYOE7cgKAw1+ZmEHDLD8nz5qxFs/z8z+P//OsSvdP7UxFDRMz29p+ofRq04Cd0ZiPYrRe2MB3HFK+xthJvKmYkpmPYQ/6VpsDJmXGL/Pv8iQ8RM7REznsumM3/PeoBgIMDYEQBpicZGA9qIfffY3yiRO3eIcxjfP9vBLF+6zwoN/ym8Tei/gtB+6R+/yOBFkTNxTz6rcusyemNE93m/qK75rIYTC809lyiGvhIaXpXO+x0mvw2/BMhekzB""",
+"clar_print_default.c" : r"""eJyFU8Fu2zAMPdtfwQUwIgVuenew9tZTsMuwU1sYqiW3AhzJkOhswNB/n0Q5rRws6Ukmxff4RD6XHgXqDo5WS+gG4drRaYOtNhpZ+ABUHtvOTgZriLGfNKpTorPGI3RvwsEmXRhxUJ6Xf8uCRUr+Cd+VBVH3bLW3QioJlUxsvoHKP5lVDbEjX3TIWTOGnygcKhlAIftelhde4d8mlPa3+folMaGcsy4lLr0gpTLkRy4D78pPoU8maSxIlVOjddhSrWdXpVMN6TbT4TRpj27qMJVRAWzoILmnlhAGy+FB6GFyqqG5Bgqeq6p801QeWOU5PIagks/weIPhiOVlURDrzR09NIvjLGK4Mhak8p3TI2q7gPR6yBGDNmF90+FFuTOeObvQBScjzHVpqAf/SlW6BzZfZM3h23f48Wu/54H+Ek9Wzpfbue4fa6JSlts8SQ9+TJ7JXpISfZi7kuf+iYDdMkOYzNJVF/QmNNzD+mENDay36y/00YbY///D3ObaSPWHVN1uwFg7wuZ2aWeqOLN4kn2tv3gJhl70D9uqYbvdUrOjaAcdroR7HXcU+vjnshjXkBZbHPt5Bh5lWBjla4LwhFFGsjl8L/8BsUiTTQ==""",
+"clar_print_tap.c" : r"""eJyNVE1vnDAQPcOvmGWFBAiQot6yaqr2HFU9tLdKyAGzscLayDbbVlX+e8cDJPbuJtsTzPObmTcfdmwss6KFoxIdtAPTzaiFtI2Qwmb4A5Yb27RqkrYEZ5tJWL4CrZLGQvvINBTzgWQHbvL4bxxlLmT+6r5bIY94gq08ktBnyffP3+DItRFKws2HnzLJd/FzHL8h2TxOtlO/5HXZDuBaKz0D/yM3xDznXRxHoodsEwSMXmrYwsiM4R2wYYC0I2GZybGY0hOJhUV8MDxw7JkY0BGd2EHJ/am3l7BEvyiMtoa5qeu0O8/2dhspLPVQTod1xMbqqbUzjQhQ0MdrHbJdL9a8AFVVzSPzMJy5YXsOt5Ca1yKqu7mWg9mHdMNx/ML+uaVenEWj0QCcRSM8pLri4QLV4SGzx6ZfYjo8ZA5CrszOZzq8wXY8cJ2v67Ecddy0WozWbfTmI3z9cX/vLwuARzgV4B3lYafrur52OZSk1fEvLO2Du4bzhZhNUj0D8/rRhNdUqXFLWC3CUPiyop8gkcqCekqwGQl+3Jkf8MXEdHFE8kmc5qPSy86Z7EoFNNbs8pvj33IhO/470L2FoihQNWTbtMudQY313X3X92WwB5QcyMC9Ld0QKOeRNYPAI6b3445MjIQOzi5hWfF+UWbwxZrwRUq+YCMBfzdAO348JVAKFyKfY3LZZYv5HP8D5Mbj9w==""",
+"clar_sandbox.c" : r"""eJydVWtP4kAU/dz+iism0gpKfWQ3G9YPm+gasioEMJgomdR2KhPplMwM7KLxv++dTqEP0DVrTKjcO+eec+6cKpWvWADBxBdAgqkvyMxXk/tT79uXcdu2pSkzrmwmycKfspCoeJY2OUHCpTJH9/UXrv1qW4PhjyEZglR42mIROBrC0eUm7Enlws4ZeK5tWYKqueDgrfp2BqQzOO/08cChVCROQupW+7Jnxw8CKmWGOiLdXy6cadi2/VbiHDFe5JsyfZxHERVNkOyFEgVTyp8M9V0W8ZBGQEadm5Nj28pwjMqse4EGBcmcKziD03alx+BTvkCjhLwfYw8aYtWG1z3UVWuCfko/Lszn7eCi3+t3f3auLmo2WG8oEaxsEtN6o0SAwxDHawOD7/n4NjQazE3hK7Ox+YkqfHDWRNgYjbGMyfilNlWfUozPqZ6SVjbXq1vNCJQpeDBbOivvsNRcOaehC0uyrDcbf22rtQ+dCNSE6m4mEh5TtC1MqOR19NNfgs+XasL4UxOUWIJKYC4ptHA+7Lfsd0jVdL2W8arSMsUSswIxJLVLp5Ia6EuqhjSe9TSocz7q9s9dc6wJBq5y+XYpD1lkdA0nTIJcSkXjtaApe6YooKRFiw/mQqTCmaCBSrD4gbjDd5UdfiRr9efBUTEAi4SFkEZ6zqXPw8fkj6O/S2OqCRTy7o11gOoPXj1XjVcDI1FMRDBBFcgSaRYMiSQRcQGsmkL0k01DklEwStc8CrdXF4jy2TRNTi3F09bcpT81nbZ1ZFcvjXLAcw4m3klUpOVigIpvHu2WbSEYTkO/8aEsoqr+FXD1PBExLu2FpnT1onvdQecOMKm/fRGCnPpyQmW65EKUrY0oaxF5iKv7YNk+HtJ9WFalBPVWfR219SIqGFrZARyN9RsX+82gcr3RyMH0PVpdu7wLGpppM1/ONmdxDDZllgF6xjgNHUKuOzeXo5NjQtyMXPyMkZmVjqLMm9urq4296P74Wd+34la9r5638S9EH8BkF0enKytPJfKf92ML7v8QWb1i8NQn5a5XmOe6HKEU4fMhhr29banbngCNYpJdJLrVixK9v7GvgW8=""",
"clar_fixtures.c" : r"""eJyFUV1LwzAUfW5+xZU9rLUVJ4ggZQ9DFAUfZEwQSglZmrBAl5Qkk6n43236tWbKfMvNOfecc+81llhBgSppLNAN0XCOuNjbnWa4InYTjpE1MSzxuD1Vki2L0BcKTKfn0EYgu57d3uRpjYhPhi1opSwumUwRCvo3zMFYXT9C5xA5stWSVh9hI5FAa+wUFG//osgJCA5tmQ1SF3CVw9kcppfTCAWBj8ZxDg3UN4/zZ7MaHBrHSBw7vpcJ4mGS5Ijtai9qnannNqk1q7myXU+KvhGaCF4wDnfPiyV+eHpbvS7v8cti9YjGq6Yl7lzCkxfo1L0j/lJOwOtrUrwrUcDBBRsii7Xan3bjBlNVL2WUzuMkgGlJdLuIP21oyYjcVf/a6G3ozXTQPRqmsZkwWQiOfgAVGffP""",
"clar_fs.c" : r"""eJylVdtu20YQfSa/YkAD8TKWY8dJX6L0wXDEVqgsBhINN7UFhiGX1qIkl9hd+dLG/57ZCynJUWEkfZE0s7NnZufMGe2xsqAlpJfj6ZsT399DgzUUojhKo8npb3Mg+ud8PBlNE/hq/NP4LJ5G49n5aTKOp71zNJvFs4vx06DzPz6MZ6HvS5UplkO+zAS89EtWUd7KtM3UkuS8kcqdGE/o/+t71tYm/ArTi8lk6HuS/UNTBRVtbtRyAGzo+x4rgaQ2zMaFvucJqlaicdd8z15AHKkE/rbxIQI6+DqrKp4TF3YAJ2GH/AxwTeu8fTBRA0jtl0Xp0K+sucAsx9suzPPauX2v5AIIMxYweO9AhnBwwELAbvTFXLGFrmf/aF+X4/Uu2L++3scEjwjmitRnQ/+x7/0tZ0XXecIaBTUv6AC22i/5SuRPnQWVynAy/z3CSYg/zpPZxVkCJQLp4m2YvYqVbJHrEHU7bJgG+y7IZNBQf1HBz2nNxQN5oeEHoDnnJdlOHYa2aa18dRetmlxziI8ZOl8bCV5ruk3u3ptw9OlUnaeMquxGorOfd/OcKs2kpEKlBFuMibHUuKUCm8gbW1aoOTge4HFwyZqC30l4EgdlhmYR+J4tVVBK1q0wpnv0U4JkKmqygxTDQEdfFKcfRpNRMsKx6zgzM7oLL+c4oz9A80aSs/jjp40U6bpmA46t0vgVzZpVS7TLApg3lOwe55A6ivMqE04hwcsgtCB7tJK0KxdH0pdLWlUpXylii3IVZuLm9mphsPXg6gsrqeXECtwH+Kl7jF96sLj4m6z1i773cGw1VLYCb5dEqoIKodnzgvmDVLQGtLl4B5/t7c+Q40ZwFL66bgLNmUfvmSKHr0Onsg5eT4LFp/c0vyWm1uPFwBTdBd9lTGGwvjCAF7b+Ad4b9mq9HP05TubJaXIxJ/b8f3DZU2lNU9Ivi+G2VNcL1dopLh3dt17IuC0LpHVDwuvA9TLtT21LrHm1EXlo9ly/s/4rwC5C1z00g6MvrDnK22DovCYoOJz1jpPFpsaN6412udkJndTNwdtF/zdiFF6vpMJxlNKIfD12hjQj7MiwD4qD7jkovbfcSEvtlVlTfOH3uxX+rKg3NL3B0dvFrh6I+rselNtN6F68oxk/+2araVBLuv3SZ6RvZL5q3BVi9r52bTgeUfZNwUr/G9kaoSs=""",
-"clar.h" : r"""eJy9Vctu2zAQPEdfwVo9WIIQp9c0DWAENmLACIrUQXojaHIVEZVJlaQaAUX/vSQlP/Rw3PTgk6nlDmd2d0iHPBUMUoTx3XL6iFezbyt8j3EQ2iAX0IsHIRc0LxmgG21YzteX2W0Q/JKcIZoThTHRGpQZBxdcGESlYNxwKZLgwq61jWREoTjlOSR1Sm5ZOruglFSdGANNFS+asxxQZ7LMGSZrqUz0eacBazCY5kBEWYx9bBw3n1H9HUcJqheyID9LsOAtNtUtqDs25Knrj+/CfPF99fQ4w1+nq/vgUJ2D8sqUCsbtMn0MC7JpsTRhTQRby+o9kK26NyAh2J6nQTCJ4wDFaOrnYduGNoQqqdErNxmCqsg55Qb5XqMNaE1ewOZPdpO3rJtSG1zYieKxBagEuSlE7UH7nQjdfkFXiXXLfLGcYexWy8WDX43mpaBeACV5jlJiZ8+u0QiF+zMT9CnqEbvM08Q3R3lnVQHUAENpS4CRXsMJBTXJafoPx+u2/Mr21RFzjYQ0yKgShni3s7rLgP74jzlRhzvToK6iPvOZJzUk4QyDuopOXCoh//E6NZKGbtjD03I5fBU6oMOe90BN6TtE2811+nHTnapjb7c9Q9+CPVF7r3Rhb9biU7qIwUrmUlFnInuafQ8nr0QJLl666r2AAZ8cc8cK7EtbX4bL0fBj0TC959TnGoJYqdyPcSRQAS2dq65HA57zOjZgMsnspiMhLlf7+j7+hsqAEvhw50+w/TP4C4S1nfY="""
+"clar.h" : r"""eJy9VU1P4zAQPZNfYZo9JJUFlCMLSAi1AqlCKyjavVmO4xBrEyfYztLVav874yRtmq922QOX1pnxzHvOe+O4IpIhjxAht8ubR7KaP63IHSGOC0EheS/uuEKypAg5utQmTERwEl87zq9MhIglVBFCtebKeM6RkAaxTIbCiExi5wjWGiIxVWgaiYTjaksCKJ0sVypTnVjINVMir3vZQh1nRRISGmTK+F8HOBD+WtCEaG+3Dx5/gKa9ADQe6ys8WzBUNNRl04ZobghLOJVF7pUxb1o/+tXz1MeoWmQ5fS14Q4FEulVq27oisvKVIi3uf6yeH+fk283qztnlYEvF2hSKe20VyhiRNG2h1GFNZRhk64+UbNjtKXE5WCJynNPp1EFTdFO+UlAVpZSpTKM3YWLE13kimDCotAJKudb0hcP+060xATUttCE5iEI8KFAYWZP4bR+WGR9dX6EzDGZe3C/nhNjV8v6hXE0WhWQlAUaTBEUUrBleoAlym54YzfwesN15GPhyFHe+zjkzPERRi4DJSg4HGNROPAh/PH5uwFfwXi2w0EhmBhlV8CHcjVa3MWc//0MnZus+Sagzv4/8yUoNUfgEoc78A0Mls38cp5rS0IQ9PC+Xw6PQKdp9572i+ujbirabq+3jpjt0jsZuDULfgj1SjVe6ZXvPUm7pVgyeZJEpZk0E3eA+PH2jSgr50mVfEhjwyZg7Vhxu2moYTibDl0WN9JGu36sSFBbK/hkLwtecFdZVF5MBz61+53A42nFe93SdL7OeYX3eprTNQdLHHqTxluGW4OTJlLxSoVNqWFwOg57BL8yRXZ6PXJjbT/cMi2Fg4UESgMUgsCsaELEfJPCCGQ7GQI6PIe1j+zcMFDRAwX6g3MtnOD/fmSQPIj66ukIehHcksiqm3MRZCPpZWtRKVYn05Q9fG64k2c38dTbf63eIKlZw"""
}
if __name__ == '__main__':
main()
diff --git a/tests-clar/clar_helpers.c b/tests-clar/clar_helpers.c
index eea8bc87d..d180285b8 100644
--- a/tests-clar/clar_helpers.c
+++ b/tests-clar/clar_helpers.c
@@ -27,3 +27,74 @@ void cl_git_mkfile(const char *filename, const char *content)
cl_must_pass(p_close(fd));
}
+
+void cl_git_write2file(const char *filename, const char *new_content, int flags)
+{
+ int fd = p_open(filename, flags, 0644);
+ cl_assert(fd >= 0);
+ if (!new_content)
+ new_content = "\n";
+ cl_must_pass(p_write(fd, new_content, strlen(new_content)));
+ cl_must_pass(p_close(fd));
+}
+
+void cl_git_append2file(const char *filename, const char *new_content)
+{
+ cl_git_write2file(filename, new_content, O_WRONLY | O_CREAT | O_APPEND);
+}
+
+void cl_git_rewritefile(const char *filename, const char *new_content)
+{
+ cl_git_write2file(filename, new_content, O_WRONLY | O_CREAT | O_TRUNC);
+}
+
+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(p_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(p_rename("gitattributes", ".gitattributes"));
+
+ /* As with `gitattributes`, we may need `gitignore` just for testing. */
+ if (p_access("gitignore", F_OK) == 0)
+ cl_git_pass(p_rename("gitignore", ".gitignore"));
+
+ cl_git_pass(p_chdir(".."));
+
+ /* Now open the sandbox repository and make it available for tests */
+ cl_git_pass(git_repository_open(&_cl_repo, sandbox));
+
+ return _cl_repo;
+}
+
+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;
+ }
+}
diff --git a/tests-clar/clar_libgit2.h b/tests-clar/clar_libgit2.h
index 73ef66844..d250494f5 100644
--- a/tests-clar/clar_libgit2.h
+++ b/tests-clar/clar_libgit2.h
@@ -13,9 +13,9 @@
* return error codes!
*/
#define cl_git_pass(expr) do { \
- git_clearerror(); \
- if ((expr) != GIT_SUCCESS) \
- clar__assert(0, __FILE__, __LINE__, "Function call failed: " #expr, git_lasterror(), 1); \
+ giterr_clear(); \
+ if ((expr) != 0) \
+ clar__assert(0, __FILE__, __LINE__, "Function call failed: " #expr, giterr_last() ? giterr_last()->message : NULL, 1); \
} while(0)
/**
@@ -25,23 +25,6 @@
*/
#define cl_git_fail(expr) cl_must_fail(expr)
-/**
- * Wrapper for string comparison that knows about nulls.
- */
-#define cl_assert_strequal(a,b) \
- cl_assert_strequal_internal(a,b,__FILE__,__LINE__,"string mismatch: " #a " != " #b)
-
-GIT_INLINE(void) cl_assert_strequal_internal(
- const char *a, const char *b, const char *file, int line, const char *err)
-{
- int match = (a == NULL || b == NULL) ? (a == b) : (strcmp(a, b) == 0);
- if (!match) {
- char buf[4096];
- snprintf(buf, 4096, "'%s' != '%s'", a, b);
- clar__assert(0, file, line, buf, err, 1);
- }
-}
-
/*
* Some utility macros for building long strings
*/
@@ -53,5 +36,13 @@ GIT_INLINE(void) cl_assert_strequal_internal(
/* Write the contents of a buffer to disk */
void cl_git_mkfile(const char *filename, const char *content);
+void cl_git_append2file(const char *filename, const char *new_content);
+void cl_git_rewritefile(const char *filename, const char *new_content);
+void cl_git_write2file(const char *filename, const char *new_content, int mode);
+
+/* Git sandbox setup helpers */
+
+git_repository *cl_git_sandbox_init(const char *sandbox);
+void cl_git_sandbox_cleanup(void);
#endif
diff --git a/tests-clar/commit/commit.c b/tests-clar/commit/commit.c
new file mode 100644
index 000000000..1205e5285
--- /dev/null
+++ b/tests-clar/commit/commit.c
@@ -0,0 +1,44 @@
+#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);
+ 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_oid(ref)));
+
+ git_tree_free(tree);
+ git_commit_free(commit);
+ git_signature_free(s);
+ git_reference_free(ref);
+}
diff --git a/tests-clar/commit/parse.c b/tests-clar/commit/parse.c
new file mode 100644
index 000000000..bbb502cb5
--- /dev/null
+++ b/tests-clar/commit/parse.c
@@ -0,0 +1,350 @@
+#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", 0, 0},
+ {"committer Vicent Marti <tanoku@gmail.com> 123456 +0163 \n", "committer ", "Vicent Marti", "tanoku@gmail.com", 0, 0},
+ {"author Vicent Marti <tanoku@gmail.com> notime \n", "author ", "Vicent Marti", "tanoku@gmail.com", 0, 0},
+ {"author Vicent Marti <tanoku@gmail.com> 123456 notimezone \n", "author ", "Vicent Marti", "tanoku@gmail.com", 0, 0},
+ {"author Vicent Marti <tanoku@gmail.com> notime +0100\n", "author ", "Vicent Marti", "tanoku@gmail.com", 0, 0},
+ {"author Vicent Marti <tanoku@gmail.com>\n", "author ", "Vicent Marti", "tanoku@gmail.com", 0, 0},
+ {"author A U Thor <author@example.com>, C O. Miter <comiter@example.com> 1234567890 -0700\n", "author ", "A U Thor", "author@example.com", 1234567890, -420},
+ {"author A U Thor <author@example.com> and others 1234567890 -0700\n", "author ", "A U Thor", "author@example.com", 1234567890, -420},
+ {"author A U Thor <author@example.com> and others 1234567890\n", "author ", "A U Thor", "author@example.com", 1234567890, 0},
+ {"author A U Thor> <author@example.com> and others 1234567890\n", "author ", "A U Thor>", "author@example.com", 1234567890, 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 = {NULL, NULL, {0, 0}};
+ cl_git_pass(git_signature__parse(&person, &str, str + len, passcase->header, '\n'));
+ cl_assert(strcmp(passcase->name, person.name) == 0);
+ cl_assert(strcmp(passcase->email, person.email) == 0);
+ cl_assert(passcase->time == person.when.time);
+ cl_assert(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 = {NULL, NULL, {0, 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",
+};
+
+void test_commit_parse__entire_commit(void)
+{
+ const int broken_commit_count = sizeof(failing_commit_cases) / sizeof(*failing_commit_cases);
+ const int working_commit_count = sizeof(passing_commit_cases) / sizeof(*passing_commit_cases);
+ int i;
+
+ for (i = 0; i < broken_commit_count; ++i) {
+ git_commit *commit;
+ commit = (git_commit*)git__malloc(sizeof(git_commit));
+ memset(commit, 0x0, sizeof(git_commit));
+ commit->object.repo = g_repo;
+
+ cl_git_fail(git_commit__parse_buffer(
+ commit,
+ failing_commit_cases[i],
+ strlen(failing_commit_cases[i]))
+ );
+
+ git_commit__free(commit);
+ }
+
+ for (i = 0; i < working_commit_count; ++i) {
+ git_commit *commit;
+
+ commit = (git_commit*)git__malloc(sizeof(git_commit));
+ memset(commit, 0x0, sizeof(git_commit));
+ commit->object.repo = g_repo;
+
+ cl_git_pass(git_commit__parse_buffer(
+ commit,
+ passing_commit_cases[i],
+ strlen(passing_commit_cases[i]))
+ );
+
+ git_commit__free(commit);
+
+ commit = (git_commit*)git__malloc(sizeof(git_commit));
+ memset(commit, 0x0, sizeof(git_commit));
+ commit->object.repo = g_repo;
+
+ cl_git_pass(git_commit__parse_buffer(
+ commit,
+ passing_commit_cases[i],
+ strlen(passing_commit_cases[i]))
+ );
+
+ 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(strcmp(author->name, "Scott Chacon") == 0);
+ cl_assert(strcmp(author->email, "schacon@gmail.com") == 0);
+ cl_assert(strcmp(committer->name, "Scott Chacon") == 0);
+ cl_assert(strcmp(committer->email, "schacon@gmail.com") == 0);
+ cl_assert(message != NULL);
+ cl_assert(strchr(message, '\n') != NULL);
+ cl_assert(commit_time > 0);
+ cl_assert(parents <= 2);
+ for (p = 0;p < parents;p++) {
+ if (old_parent != NULL)
+ git_commit_free(old_parent);
+
+ old_parent = parent;
+ cl_git_pass(git_commit_parent(&parent, commit, p));
+ cl_assert(parent != NULL);
+ cl_assert(git_commit_author(parent) != NULL); // is it really a commit?
+ }
+ git_commit_free(old_parent);
+ git_commit_free(parent);
+
+ cl_git_fail(git_commit_parent(&parent, commit, parents));
+ git_commit_free(commit);
+ }
+}
+
diff --git a/tests-clar/commit/signature.c b/tests-clar/commit/signature.c
new file mode 100644
index 000000000..290b11fa3
--- /dev/null
+++ b/tests-clar/commit/signature.c
@@ -0,0 +1,65 @@
+#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;
+}
+
+
+void test_commit_signature__create_trim(void)
+{
+ // creating a signature trims leading and trailing spaces
+ git_signature *sign;
+ cl_git_pass(git_signature_new(&sign, " nulltoken ", " emeric.fermas@gmail.com ", 1234567890, 60));
+ cl_assert(strcmp(sign->name, "nulltoken") == 0);
+ cl_assert(strcmp(sign->email, "emeric.fermas@gmail.com") == 0);
+ git_signature_free((git_signature *)sign);
+}
+
+
+void test_commit_signature__create_empties(void)
+{
+ // can not create a signature with empty name or email
+ cl_git_pass(try_build_signature("nulltoken", "emeric.fermas@gmail.com", 1234567890, 60));
+
+ cl_git_fail(try_build_signature("", "emeric.fermas@gmail.com", 1234567890, 60));
+ cl_git_fail(try_build_signature(" ", "emeric.fermas@gmail.com", 1234567890, 60));
+ cl_git_fail(try_build_signature("nulltoken", "", 1234567890, 60));
+ cl_git_fail(try_build_signature("nulltoken", " ", 1234567890, 60));
+}
+
+void test_commit_signature__create_one_char(void)
+{
+ // creating a one character signature
+ git_signature *sign;
+ cl_git_pass(git_signature_new(&sign, "x", "foo@bar.baz", 1234567890, 60));
+ cl_assert(strcmp(sign->name, "x") == 0);
+ cl_assert(strcmp(sign->email, "foo@bar.baz") == 0);
+ git_signature_free((git_signature *)sign);
+}
+
+void test_commit_signature__create_two_char(void)
+{
+ // creating a two character signature
+ git_signature *sign;
+ cl_git_pass(git_signature_new(&sign, "xx", "x@y.z", 1234567890, 60));
+ cl_assert(strcmp(sign->name, "xx") == 0);
+ cl_assert(strcmp(sign->email, "x@y.z") == 0);
+ git_signature_free((git_signature *)sign);
+}
+
+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-clar/commit/write.c b/tests-clar/commit/write.c
new file mode 100644
index 000000000..9c4d077a6
--- /dev/null
+++ b/tests-clar/commit/write.c
@@ -0,0 +1,140 @@
+#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);
+ git_reference_free(branch);
+
+ git_commit_free(commit);
+
+ git__free(head_old);
+
+ 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(strcmp(author1->name, committer_name) == 0);
+ cl_assert(strcmp(author1->email, committer_email) == 0);
+ cl_assert(author1->when.time == 987654321);
+ cl_assert(author1->when.offset == 90);
+
+ committer1 = git_commit_committer(commit);
+ cl_assert(committer1 != NULL);
+ cl_assert(strcmp(committer1->name, committer_name) == 0);
+ cl_assert(strcmp(committer1->email, committer_email) == 0);
+ cl_assert(committer1->when.time == 123456789);
+ cl_assert(committer1->when.offset == 60);
+
+ cl_assert(strcmp(git_commit_message(commit), commit_message) == 0);
+}
+
+// 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_target(head));
+ cl_assert(head_old != NULL);
+
+ cl_git_pass(git_reference_set_target(head, branch_name));
+
+ 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_oid(branch);
+ cl_git_pass(git_oid_cmp(branch_oid, &commit_id));
+ cl_assert(!strcmp(git_commit_message(commit), root_commit_message));
+}
diff --git a/tests-clar/config/add.c b/tests-clar/config/add.c
index b58029951..9854fbb39 100644
--- a/tests-clar/config/add.c
+++ b/tests-clar/config/add.c
@@ -17,7 +17,7 @@ void test_config_add__to_existing_section(void)
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(cfg, "empty.tmp", &i));
+ cl_git_pass(git_config_get_int32(&i, cfg, "empty.tmp"));
cl_assert(i == 5);
cl_git_pass(git_config_delete(cfg, "empty.tmp"));
git_config_free(cfg);
@@ -30,7 +30,7 @@ void test_config_add__to_new_section(void)
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(cfg, "section.tmp", &i));
+ cl_git_pass(git_config_get_int32(&i, cfg, "section.tmp"));
cl_assert(i == 5);
cl_git_pass(git_config_delete(cfg, "section.tmp"));
git_config_free(cfg);
diff --git a/tests-clar/config/multivar.c b/tests-clar/config/multivar.c
new file mode 100644
index 000000000..3b40cd09a
--- /dev/null
+++ b/tests-clar/config/multivar.c
@@ -0,0 +1,151 @@
+#include "clar_libgit2.h"
+
+static const char *_name = "remote.fancy.url";
+
+void test_config_multivar__initialize(void)
+{
+ cl_fixture_sandbox("config");
+}
+
+void test_config_multivar__cleanup(void)
+{
+ cl_fixture_cleanup("config");
+}
+
+static int mv_read_cb(const char *name, const char *value, void *data)
+{
+ int *n = (int *) data;
+
+ GIT_UNUSED(value);
+
+ if (!strcmp(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 char *val, void *data)
+{
+ int *n = (int *) data;
+
+ GIT_UNUSED(val);
+
+ (*n)++;
+
+ return 0;
+}
+
+void test_config_multivar__get(void)
+{
+ git_config *cfg;
+ int n;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar(cfg, _name, NULL, cb, &n));
+ cl_assert(n == 2);
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar(cfg, _name, "example", cb, &n));
+ cl_assert(n == 1);
+
+ git_config_free(cfg);
+}
+
+void test_config_multivar__add(void)
+{
+ git_config *cfg;
+ int n;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+ cl_git_pass(git_config_set_multivar(cfg, _name, "nonexistant", "git://git.otherplace.org/libgit2"));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar(cfg, _name, NULL, cb, &n));
+ cl_assert(n == 3);
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar(cfg, _name, "otherplace", cb, &n));
+ cl_assert(n == 1);
+
+ git_config_free(cfg);
+
+ /* We know it works in memory, let's see if the file is written correctly */
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar(cfg, _name, NULL, cb, &n));
+ cl_assert(n == 3);
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar(cfg, _name, "otherplace", cb, &n));
+ cl_assert(n == 1);
+
+ git_config_free(cfg);
+}
+
+void test_config_multivar__replace(void)
+{
+ git_config *cfg;
+ int n;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar(cfg, _name, NULL, cb, &n));
+ cl_assert(n == 2);
+
+ cl_git_pass(git_config_set_multivar(cfg, _name, "github", "git://git.otherplace.org/libgit2"));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar(cfg, _name, NULL, cb, &n));
+ cl_assert(n == 2);
+
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar(cfg, _name, NULL, cb, &n));
+ cl_assert(n == 2);
+
+ git_config_free(cfg);
+}
+
+void test_config_multivar__replace_multiple(void)
+{
+ git_config *cfg;
+ int n;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+ cl_git_pass(git_config_set_multivar(cfg, _name, "git://", "git://git.otherplace.org/libgit2"));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar(cfg, _name, "otherplace", cb, &n));
+ cl_assert(n == 2);
+
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar(cfg, _name, "otherplace", cb, &n));
+ cl_assert(n == 2);
+
+ git_config_free(cfg);
+}
diff --git a/tests-clar/config/new.c b/tests-clar/config/new.c
index 96aed2bb3..fae3ce941 100644
--- a/tests-clar/config/new.c
+++ b/tests-clar/config/new.c
@@ -25,10 +25,10 @@ void test_config_new__write_new_config(void)
cl_git_pass(git_config_new(&config));
cl_git_pass(git_config_add_file(config, file, 0));
- cl_git_pass(git_config_get_string(config, "color.ui", &out));
- cl_assert(strcmp(out, "auto") == 0);
- cl_git_pass(git_config_get_string(config, "core.editor", &out));
- cl_assert(strcmp(out, "ed") == 0);
+ 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);
diff --git a/tests-clar/config/read.c b/tests-clar/config/read.c
index 26e6f4248..f33bdd89e 100644
--- a/tests-clar/config/read.c
+++ b/tests-clar/config/read.c
@@ -7,13 +7,13 @@ void test_config_read__simple_read(void)
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config0")));
- cl_git_pass(git_config_get_int32(cfg, "core.repositoryformatversion", &i));
+ cl_git_pass(git_config_get_int32(&i, cfg, "core.repositoryformatversion"));
cl_assert(i == 0);
- cl_git_pass(git_config_get_bool(cfg, "core.filemode", &i));
+ cl_git_pass(git_config_get_bool(&i, cfg, "core.filemode"));
cl_assert(i == 1);
- cl_git_pass(git_config_get_bool(cfg, "core.bare", &i));
+ cl_git_pass(git_config_get_bool(&i, cfg, "core.bare"));
cl_assert(i == 0);
- cl_git_pass(git_config_get_bool(cfg, "core.logallrefupdates", &i));
+ cl_git_pass(git_config_get_bool(&i, cfg, "core.logallrefupdates"));
cl_assert(i == 1);
git_config_free(cfg);
@@ -27,18 +27,18 @@ void test_config_read__case_sensitive(void)
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config1")));
- cl_git_pass(git_config_get_string(cfg, "this.that.other", &str));
- cl_assert(!strcmp(str, "true"));
- cl_git_pass(git_config_get_string(cfg, "this.That.other", &str));
- cl_assert(!strcmp(str, "yes"));
+ 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(cfg, "this.that.other", &i));
+ cl_git_pass(git_config_get_bool(&i, cfg, "this.that.other"));
cl_assert(i == 1);
- cl_git_pass(git_config_get_bool(cfg, "this.That.other", &i));
+ 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(cfg, "this.thaT.other", &i));
+ cl_must_fail(git_config_get_bool(&i, cfg, "this.thaT.other"));
git_config_free(cfg);
}
@@ -54,8 +54,8 @@ void test_config_read__multiline_value(void)
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config2")));
- cl_git_pass(git_config_get_string(cfg, "this.That.and", &str));
- cl_assert(!strcmp(str, "one one one two two three three"));
+ 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);
}
@@ -70,11 +70,11 @@ void test_config_read__subsection_header(void)
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config3")));
- cl_git_pass(git_config_get_string(cfg, "section.subsection.var", &str));
- cl_assert(!strcmp(str, "hello"));
+ 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(cfg, "section.subSectIon.var", &str));
+ cl_must_fail(git_config_get_string(&str, cfg, "section.subSectIon.var"));
git_config_free(cfg);
}
@@ -87,10 +87,10 @@ void test_config_read__lone_variable(void)
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config4")));
- cl_git_pass(git_config_get_string(cfg, "some.section.variable", &str));
+ cl_git_pass(git_config_get_string(&str, cfg, "some.section.variable"));
cl_assert(str == NULL);
- cl_git_pass(git_config_get_bool(cfg, "some.section.variable", &i));
+ cl_git_pass(git_config_get_bool(&i, cfg, "some.section.variable"));
cl_assert(i == 1);
git_config_free(cfg);
@@ -103,25 +103,25 @@ void test_config_read__number_suffixes(void)
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config5")));
- cl_git_pass(git_config_get_int64(cfg, "number.simple", &i));
+ cl_git_pass(git_config_get_int64(&i, cfg, "number.simple"));
cl_assert(i == 1);
- cl_git_pass(git_config_get_int64(cfg, "number.k", &i));
+ cl_git_pass(git_config_get_int64(&i, cfg, "number.k"));
cl_assert(i == 1 * 1024);
- cl_git_pass(git_config_get_int64(cfg, "number.kk", &i));
+ cl_git_pass(git_config_get_int64(&i, cfg, "number.kk"));
cl_assert(i == 1 * 1024);
- cl_git_pass(git_config_get_int64(cfg, "number.m", &i));
+ cl_git_pass(git_config_get_int64(&i, cfg, "number.m"));
cl_assert(i == 1 * 1024 * 1024);
- cl_git_pass(git_config_get_int64(cfg, "number.mm", &i));
+ cl_git_pass(git_config_get_int64(&i, cfg, "number.mm"));
cl_assert(i == 1 * 1024 * 1024);
- cl_git_pass(git_config_get_int64(cfg, "number.g", &i));
+ 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(cfg, "number.gg", &i));
+ cl_git_pass(git_config_get_int64(&i, cfg, "number.gg"));
cl_assert(i == 1 * 1024 * 1024 * 1024);
git_config_free(cfg);
@@ -134,10 +134,10 @@ void test_config_read__blank_lines(void)
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config6")));
- cl_git_pass(git_config_get_bool(cfg, "valid.subsection.something", &i));
+ cl_git_pass(git_config_get_bool(&i, cfg, "valid.subsection.something"));
cl_assert(i == 1);
- cl_git_pass(git_config_get_bool(cfg, "something.else.something", &i));
+ cl_git_pass(git_config_get_bool(&i, cfg, "something.else.something"));
cl_assert(i == 0);
git_config_free(cfg);
@@ -170,11 +170,23 @@ void test_config_read__prefixes(void)
const char *str;
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config9")));
- cl_git_pass(git_config_get_string(cfg, "remote.ab.url", &str));
- cl_assert(strcmp(str, "http://example.com/git/ab") == 0);
+ 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(cfg, "remote.abba.url", &str));
- cl_assert(strcmp(str, "http://example.com/git/abba") == 0);
+ 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(strcmp(str, "\"C:/Program Files/Nonsense/bah.exe\" \"--some option\"") == 0);
git_config_free(cfg);
}
diff --git a/tests-clar/config/stress.c b/tests-clar/config/stress.c
index e3b1114f0..3de1f7692 100644
--- a/tests-clar/config/stress.c
+++ b/tests-clar/config/stress.c
@@ -27,13 +27,35 @@ void test_config_stress__dont_break_on_invalid_input(void)
struct git_config_file *file;
git_config *config;
- cl_git_pass(git_path_exists("git-test-config"));
+ cl_assert(git_path_exists("git-test-config"));
cl_git_pass(git_config_file__ondisk(&file, "git-test-config"));
cl_git_pass(git_config_new(&config));
cl_git_pass(git_config_add_file(config, file, 0));
- cl_git_pass(git_config_get_string(config, "color.ui", &color));
- cl_git_pass(git_config_get_string(config, "core.editor", &editor));
+ 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)
+{
+ struct git_config_file *file;
+ git_config *config;
+ const char *str;
+
+ cl_git_pass(git_config_file__ondisk(&file, cl_fixture("config/config12")));
+ cl_git_pass(git_config_new(&config));
+ cl_git_pass(git_config_add_file(config, file, 0));
+
+ cl_git_pass(git_config_get_string(&str, config, "some.section.other"));
+ cl_assert(!strcmp(str, "hello! \" ; ; ; "));
+
+ cl_git_pass(git_config_get_string(&str, config, "some.section.multi"));
+ cl_assert(!strcmp(str, "hi, this is a ; multiline comment # with ;\n special chars and other stuff !@#"));
+
+ cl_git_pass(git_config_get_string(&str, config, "some.section.back"));
+ cl_assert(!strcmp(str, "this is \ba phrase"));
git_config_free(config);
}
diff --git a/tests-clar/config/write.c b/tests-clar/config/write.c
index d22c6f2cf..f8774473e 100644
--- a/tests-clar/config/write.c
+++ b/tests-clar/config/write.c
@@ -22,7 +22,7 @@ void test_config_write__replace_value(void)
git_config_free(cfg);
cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
- cl_git_pass(git_config_get_int32(cfg, "core.dummy", &i));
+ cl_git_pass(git_config_get_int32(&i, cfg, "core.dummy"));
cl_assert(i == 5);
git_config_free(cfg);
@@ -35,12 +35,12 @@ void test_config_write__replace_value(void)
git_config_free(cfg);
cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
- cl_git_pass(git_config_get_int64(cfg, "core.verylong", &l));
+ 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(cfg, "core.verylong", &i));
+ cl_must_fail(git_config_get_int32(&i, cfg, "core.verylong"));
git_config_free(cfg);
cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
@@ -62,11 +62,26 @@ void test_config_write__delete_value(void)
git_config_free(cfg);
cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
- cl_assert(git_config_get_int32(cfg, "core.dummy", &i) == GIT_ENOTFOUND);
+ 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);
}
+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_git_pass(strcmp(str, "works"));
+ git_config_free(cfg);
+}
+
void test_config_write__delete_inexistent(void)
{
git_config *cfg;
diff --git a/tests-clar/core/buffer.c b/tests-clar/core/buffer.c
index 740cd8578..6a718f459 100644
--- a/tests-clar/core/buffer.c
+++ b/tests-clar/core/buffer.c
@@ -19,11 +19,11 @@ void test_core_buffer__0(void)
git_buf_puts(&buf, test_string);
cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_strequal(test_string, git_buf_cstr(&buf));
+ 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_strequal(test_string_x2, git_buf_cstr(&buf));
+ cl_assert_equal_s(test_string_x2, git_buf_cstr(&buf));
git_buf_free(&buf);
}
@@ -35,11 +35,11 @@ void test_core_buffer__1(void)
git_buf_printf(&buf, "%s %s %d ", "shoop", "da", 23);
cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_strequal("shoop da 23 ", git_buf_cstr(&buf));
+ 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_strequal("shoop da 23 woop 42", git_buf_cstr(&buf));
+ cl_assert_equal_s("shoop da 23 woop 42", git_buf_cstr(&buf));
git_buf_free(&buf);
}
@@ -59,7 +59,7 @@ void test_core_buffer__2(void)
cl_assert(buf.asize == 0);
/* empty buffer should be empty string */
- cl_assert_strequal("", git_buf_cstr(&buf));
+ 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 */
@@ -71,38 +71,38 @@ void test_core_buffer__2(void)
/* add letter */
git_buf_putc(&buf, '+');
cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_strequal("+", git_buf_cstr(&buf));
+ cl_assert_equal_s("+", git_buf_cstr(&buf));
/* add letter again */
git_buf_putc(&buf, '+');
cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_strequal("++", git_buf_cstr(&buf));
+ 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_strequal("++++++++++++++++++", git_buf_cstr(&buf));
+ 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_strequal("xo", git_buf_cstr(&buf));
+ 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_strequal("xoxo", git_buf_cstr(&buf));
+ 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_strequal("xoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxo",
+ cl_assert_equal_s("xoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxo",
git_buf_cstr(&buf));
git_buf_free(&buf);
@@ -110,21 +110,21 @@ void test_core_buffer__2(void)
/* set to string */
git_buf_sets(&buf, test_string);
cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_strequal(test_string, git_buf_cstr(&buf));
+ 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_strequal(test_string_x2, git_buf_cstr(&buf));
+ 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_strequal(test_string, git_buf_cstr(&buf));
+ cl_assert_equal_s(test_string, git_buf_cstr(&buf));
/* test clear */
git_buf_clear(&buf);
- cl_assert_strequal("", git_buf_cstr(&buf));
+ cl_assert_equal_s("", git_buf_cstr(&buf));
git_buf_free(&buf);
@@ -133,27 +133,27 @@ void test_core_buffer__2(void)
cl_assert(git_buf_oom(&buf) == 0);
git_buf_copy_cstr(data, sizeof(data), &buf);
- cl_assert_strequal(REP4("0123456789"), data);
+ cl_assert_equal_s(REP4("0123456789"), data);
git_buf_copy_cstr(data, 11, &buf);
- cl_assert_strequal("0123456789", data);
+ cl_assert_equal_s("0123456789", data);
git_buf_copy_cstr(data, 3, &buf);
- cl_assert_strequal("01", data);
+ cl_assert_equal_s("01", data);
git_buf_copy_cstr(data, 1, &buf);
- cl_assert_strequal("", data);
+ cl_assert_equal_s("", data);
git_buf_copy_cstr(data, sizeof(data), &buf);
- cl_assert_strequal(REP4("0123456789"), data);
+ 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_strequal(REP4(REP16("x")) REP16("x") REP16("x")
+ 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_strequal("", data);
+ cl_assert_equal_s("", data);
}
/* let's do some tests with larger buffers to push our limits */
@@ -164,17 +164,17 @@ void test_core_buffer__3(void)
/* set to string */
git_buf_set(&buf, test_4096, 4096);
cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_strequal(test_4096, git_buf_cstr(&buf));
+ 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_strequal(test_8192, git_buf_cstr(&buf));
+ 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_strequal(test_4096, git_buf_cstr(&buf));
+ cl_assert_equal_s(test_4096, git_buf_cstr(&buf));
git_buf_free(&buf);
}
@@ -192,22 +192,22 @@ void test_core_buffer__4(void)
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_strequal("12341234123412341234", git_buf_cstr(&buf));
+ cl_assert_equal_s("12341234123412341234", git_buf_cstr(&buf));
git_buf_consume(&buf, NULL);
- cl_assert_strequal("12341234123412341234", git_buf_cstr(&buf));
+ cl_assert_equal_s("12341234123412341234", git_buf_cstr(&buf));
git_buf_consume(&buf, "invalid pointer");
- cl_assert_strequal("12341234123412341234", git_buf_cstr(&buf));
+ cl_assert_equal_s("12341234123412341234", git_buf_cstr(&buf));
git_buf_consume(&buf, buf.ptr);
- cl_assert_strequal("12341234123412341234", git_buf_cstr(&buf));
+ cl_assert_equal_s("12341234123412341234", git_buf_cstr(&buf));
git_buf_consume(&buf, buf.ptr + 1);
- cl_assert_strequal("2341234123412341234", git_buf_cstr(&buf));
+ cl_assert_equal_s("2341234123412341234", git_buf_cstr(&buf));
git_buf_consume(&buf, buf.ptr + buf.size);
- cl_assert_strequal("", git_buf_cstr(&buf));
+ cl_assert_equal_s("", git_buf_cstr(&buf));
git_buf_free(&buf);
}
@@ -218,8 +218,8 @@ check_buf_append(
const char* data_a,
const char* data_b,
const char* expected_data,
- ssize_t expected_size,
- ssize_t expected_asize)
+ size_t expected_size,
+ size_t expected_asize)
{
git_buf tgt = GIT_BUF_INIT;
@@ -227,7 +227,7 @@ check_buf_append(
cl_assert(git_buf_oom(&tgt) == 0);
git_buf_puts(&tgt, data_b);
cl_assert(git_buf_oom(&tgt) == 0);
- cl_assert_strequal(expected_data, git_buf_cstr(&tgt));
+ 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);
@@ -250,27 +250,27 @@ check_buf_append_abc(
git_buf_sets(&buf, buf_a);
cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_strequal(buf_a, git_buf_cstr(&buf));
+ 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_strequal(expected_ab, git_buf_cstr(&buf));
+ 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_strequal(expected_abc, git_buf_cstr(&buf));
+ 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_strequal(expected_abca, git_buf_cstr(&buf));
+ 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_strequal(expected_abcab, git_buf_cstr(&buf));
+ 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_strequal(expected_abcabc, git_buf_cstr(&buf));
+ cl_assert_equal_s(expected_abcabc, git_buf_cstr(&buf));
git_buf_free(&buf);
}
@@ -330,13 +330,13 @@ void test_core_buffer__6(void)
git_buf_sets(&b, "bar");
cl_assert(git_buf_oom(&b) == 0);
- cl_assert_strequal("foo", git_buf_cstr(&a));
- cl_assert_strequal("bar", git_buf_cstr(&b));
+ 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_strequal("bar", git_buf_cstr(&a));
- cl_assert_strequal("foo", git_buf_cstr(&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);
@@ -352,36 +352,36 @@ void test_core_buffer__7(void)
git_buf_sets(&a, "foo");
cl_assert(git_buf_oom(&a) == 0);
- cl_assert_strequal("foo", git_buf_cstr(&a));
+ cl_assert_equal_s("foo", git_buf_cstr(&a));
b = git_buf_detach(&a);
- cl_assert_strequal("foo", b);
- cl_assert_strequal("", a.ptr);
+ cl_assert_equal_s("foo", b);
+ cl_assert_equal_s("", a.ptr);
git__free(b);
b = git_buf_detach(&a);
- cl_assert_strequal(NULL, b);
- cl_assert_strequal("", a.ptr);
+ 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_strequal(fun, a.ptr);
- cl_assert(a.size == (ssize_t)strlen(fun));
- cl_assert(a.asize == (ssize_t)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);
b = git__strdup(fun);
git_buf_attach(&a, b, strlen(fun) + 1);
- cl_assert_strequal(fun, a.ptr);
- cl_assert(a.size == (ssize_t)strlen(fun));
- cl_assert(a.asize == (ssize_t)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);
}
@@ -398,7 +398,7 @@ check_joinbuf_2(
git_buf_join(&buf, sep, a, b);
cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_strequal(expected, git_buf_cstr(&buf));
+ cl_assert_equal_s(expected, git_buf_cstr(&buf));
git_buf_free(&buf);
}
@@ -416,7 +416,7 @@ check_joinbuf_n_2(
git_buf_join_n(&buf, sep, 1, b);
cl_assert(git_buf_oom(&buf) == 0);
- cl_assert_strequal(expected, git_buf_cstr(&buf));
+ cl_assert_equal_s(expected, git_buf_cstr(&buf));
git_buf_free(&buf);
}
@@ -433,7 +433,7 @@ check_joinbuf_n_4(
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_strequal(expected, git_buf_cstr(&buf));
+ cl_assert_equal_s(expected, git_buf_cstr(&buf));
git_buf_free(&buf);
}
@@ -444,15 +444,15 @@ void test_core_buffer__8(void)
git_buf_join_n(&a, '/', 1, "foo");
cl_assert(git_buf_oom(&a) == 0);
- cl_assert_strequal("foo", git_buf_cstr(&a));
+ 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_strequal("foo/bar", git_buf_cstr(&a));
+ 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_strequal("foo/bar/baz", git_buf_cstr(&a));
+ cl_assert_equal_s("foo/bar/baz", git_buf_cstr(&a));
git_buf_free(&a);
@@ -536,7 +536,7 @@ void test_core_buffer__9(void)
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_strequal(*expect, buf.ptr);
+ cl_assert_equal_s(*expect, buf.ptr);
expect++;
}
}
@@ -544,3 +544,70 @@ void test_core_buffer__9(void)
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_common_prefix(&a, &t));
+ cl_assert_equal_s(a.ptr, "");
+
+ t.strings = t2;
+ t.count = 3;
+ cl_git_pass(git_buf_common_prefix(&a, &t));
+ cl_assert_equal_s(a.ptr, "some");
+
+ t.strings = t3;
+ t.count = 3;
+ cl_git_pass(git_buf_common_prefix(&a, &t));
+ cl_assert_equal_s(a.ptr, "");
+
+ t.strings = t4;
+ t.count = 3;
+ cl_git_pass(git_buf_common_prefix(&a, &t));
+ cl_assert_equal_s(a.ptr, "happ");
+
+ t.strings = t5;
+ t.count = 3;
+ cl_git_pass(git_buf_common_prefix(&a, &t));
+ cl_assert_equal_s(a.ptr, "happ");
+
+ t.strings = t6;
+ t.count = 3;
+ cl_git_pass(git_buf_common_prefix(&a, &t));
+ cl_assert_equal_s(a.ptr, "");
+
+ t.strings = t7;
+ t.count = 3;
+ cl_git_pass(git_buf_common_prefix(&a, &t));
+ cl_assert_equal_s(a.ptr, "");
+
+ git_buf_free(&a);
+}
diff --git a/tests-clar/core/dirent.c b/tests-clar/core/dirent.c
index edd04471e..5a7859d1b 100644
--- a/tests-clar/core/dirent.c
+++ b/tests-clar/core/dirent.c
@@ -88,10 +88,10 @@ static int one_entry(void *state, git_buf *path)
return GIT_ERROR;
}
-static int dont_call_me(void *GIT_UNUSED(state), git_buf *GIT_UNUSED(path))
+static int dont_call_me(void *state, git_buf *path)
{
- GIT_UNUSED_ARG(state)
- GIT_UNUSED_ARG(path)
+ GIT_UNUSED(state);
+ GIT_UNUSED(path);
return GIT_ERROR;
}
@@ -222,3 +222,14 @@ void test_core_dirent__traverse_weird_filenames(void)
check_counts(&odd);
}
+
+/* test filename length limits */
+void test_core_dirent__length_limits(void)
+{
+ char *big_filename = (char *)git__malloc(FILENAME_MAX + 1);
+ memset(big_filename, 'a', FILENAME_MAX + 1);
+ big_filename[FILENAME_MAX] = 0;
+
+ cl_must_fail(p_creat(big_filename, 0666));
+ git__free(big_filename);
+}
diff --git a/tests-clar/core/errors.c b/tests-clar/core/errors.c
new file mode 100644
index 000000000..0be3e7aca
--- /dev/null
+++ b/tests-clar/core/errors.c
@@ -0,0 +1,60 @@
+#include "clar_libgit2.h"
+#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));
+ 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-clar/core/filebuf.c b/tests-clar/core/filebuf.c
index 29d6bca74..eab8a26eb 100644
--- a/tests-clar/core/filebuf.c
+++ b/tests-clar/core/filebuf.c
@@ -14,7 +14,7 @@ void test_core_filebuf__0(void)
cl_must_pass(p_close(fd));
cl_git_fail(git_filebuf_open(&file, test, 0));
- cl_git_pass(git_path_exists(testlock));
+ cl_assert(git_path_exists(testlock));
cl_must_pass(p_unlink(testlock));
}
@@ -56,20 +56,6 @@ void test_core_filebuf__2(void)
cl_must_pass(p_unlink(test));
}
-
-/* make sure git_filebuf_open won't reopen an open buffer */
-void test_core_filebuf__3(void)
-{
- git_filebuf file = GIT_FILEBUF_INIT;
- char test[] = "test";
-
- cl_git_pass(git_filebuf_open(&file, test, 0));
- cl_git_fail(git_filebuf_open(&file, test, 0));
-
- git_filebuf_cleanup(&file);
-}
-
-
/* make sure git_filebuf_cleanup clears the buffer */
void test_core_filebuf__4(void)
{
diff --git a/tests-clar/core/oid.c b/tests-clar/core/oid.c
index 60361c42c..c89713955 100644
--- a/tests-clar/core/oid.c
+++ b/tests-clar/core/oid.c
@@ -10,9 +10,9 @@ void test_core_oid__initialize(void)
void test_core_oid__streq(void)
{
- cl_assert(git_oid_streq(&id, str_oid) == GIT_SUCCESS);
- cl_assert(git_oid_streq(&id, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef") == GIT_ERROR);
+ cl_assert(git_oid_streq(&id, str_oid) == 0);
+ cl_assert(git_oid_streq(&id, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef") == -1);
- cl_assert(git_oid_streq(&id, "deadbeef") == GIT_ENOTOID);
- cl_assert(git_oid_streq(&id, "I'm not an oid.... :)") == GIT_ENOTOID);
+ cl_assert(git_oid_streq(&id, "deadbeef") == -1);
+ cl_assert(git_oid_streq(&id, "I'm not an oid.... :)") == -1);
}
diff --git a/tests-clar/core/path.c b/tests-clar/core/path.c
index 3ff5d7daf..d826612ac 100644
--- a/tests-clar/core/path.c
+++ b/tests-clar/core/path.c
@@ -8,11 +8,11 @@ check_dirname(const char *A, const char *B)
char *dir2;
cl_assert(git_path_dirname_r(&dir, A) >= 0);
- cl_assert_strequal(B, dir.ptr);
+ cl_assert_equal_s(B, dir.ptr);
git_buf_free(&dir);
cl_assert((dir2 = git_path_dirname(A)) != NULL);
- cl_assert_strequal(B, dir2);
+ cl_assert_equal_s(B, dir2);
git__free(dir2);
}
@@ -23,11 +23,11 @@ check_basename(const char *A, const char *B)
char *base2;
cl_assert(git_path_basename_r(&base, A) >= 0);
- cl_assert_strequal(B, base.ptr);
+ cl_assert_equal_s(B, base.ptr);
git_buf_free(&base);
cl_assert((base2 = git_path_basename(A)) != NULL);
- cl_assert_strequal(B, base2);
+ cl_assert_equal_s(B, base2);
git__free(base2);
}
@@ -37,7 +37,7 @@ check_topdir(const char *A, const char *B)
const char *dir;
cl_assert((dir = git_path_topdir(A)) != NULL);
- cl_assert_strequal(B, dir);
+ cl_assert_equal_s(B, dir);
}
static void
@@ -46,7 +46,7 @@ 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_strequal(expected_path, joined_path.ptr);
+ cl_assert_equal_s(expected_path, joined_path.ptr);
git_buf_free(&joined_path);
}
@@ -63,7 +63,7 @@ check_joinpath_n(
cl_git_pass(git_buf_join_n(&joined_path, '/', 4,
path_a, path_b, path_c, path_d));
- cl_assert_strequal(expected_path, joined_path.ptr);
+ cl_assert_equal_s(expected_path, joined_path.ptr);
git_buf_free(&joined_path);
}
@@ -189,7 +189,7 @@ check_path_to_dir(
git_buf_sets(&tgt, path);
cl_git_pass(git_path_to_dir(&tgt));
- cl_assert_strequal(expected, tgt.ptr);
+ cl_assert_equal_s(expected, tgt.ptr);
git_buf_free(&tgt);
}
@@ -197,16 +197,18 @@ check_path_to_dir(
static void
check_string_to_dir(
const char* path,
- int maxlen,
+ size_t maxlen,
const char* expected)
{
- int len = strlen(path);
+ 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_strequal(expected, buf);
+ cl_assert_equal_s(expected, buf);
git__free(buf);
}
@@ -243,32 +245,32 @@ void test_core_path__07_path_to_dir(void)
void test_core_path__08_self_join(void)
{
git_buf path = GIT_BUF_INIT;
- ssize_t asize = 0;
+ size_t asize = 0;
asize = path.asize;
cl_git_pass(git_buf_sets(&path, "/foo"));
- cl_assert_strequal(path.ptr, "/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_strequal(path.ptr, "/foo/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_strequal(path.ptr, "/foo/this is a new string/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_strequal(path.ptr, "/bar/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_strequal(path.ptr, "/baz/somethinglongenoughtorealloc");
+ cl_assert_equal_s(path.ptr, "/baz/somethinglongenoughtorealloc");
cl_assert(asize < path.asize);
git_buf_free(&path);
@@ -279,7 +281,7 @@ static void check_percent_decoding(const char *expected_result, const char *inpu
git_buf buf = GIT_BUF_INIT;
cl_git_pass(git__percent_decode(&buf, input));
- cl_assert_strequal(expected_result, git_buf_cstr(&buf));
+ cl_assert_equal_s(expected_result, git_buf_cstr(&buf));
git_buf_free(&buf);
}
@@ -306,7 +308,7 @@ static void check_fromurl(const char *expected_result, const char *input, int sh
if (!should_fail) {
cl_git_pass(git_path_fromurl(&buf, input));
- cl_assert_strequal(expected_result, git_buf_cstr(&buf));
+ cl_assert_equal_s(expected_result, git_buf_cstr(&buf));
} else
cl_git_fail(git_path_fromurl(&buf, input));
@@ -346,9 +348,9 @@ 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_strequal(info->expect[info->expect_idx], path->ptr);
+ cl_assert_equal_s(info->expect[info->expect_idx], path->ptr);
info->expect_idx++;
- return GIT_SUCCESS;
+ return 0;
}
void test_core_path__11_walkup(void)
@@ -380,7 +382,7 @@ void test_core_path__11_walkup(void)
git_path_walk_up(&p, root[j], check_one_walkup_step, &info)
);
- cl_assert_strequal(p.ptr, expect[i]);
+ cl_assert_equal_s(p.ptr, expect[i]);
/* skip to next run of expectations */
while (expect[i] != NULL) i++;
@@ -388,3 +390,31 @@ void test_core_path__11_walkup(void)
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);
+}
diff --git a/tests-clar/core/pool.c b/tests-clar/core/pool.c
new file mode 100644
index 000000000..5ed97366f
--- /dev/null
+++ b/tests-clar/core/pool.c
@@ -0,0 +1,85 @@
+#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);
+}
diff --git a/tests-clar/core/rmdir.c b/tests-clar/core/rmdir.c
index 66b647587..530f1f908 100644
--- a/tests-clar/core/rmdir.c
+++ b/tests-clar/core/rmdir.c
@@ -30,25 +30,39 @@ void test_core_rmdir__initialize(void)
/* 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, 0));
+ cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, GIT_DIRREMOVAL_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;
- int fd;
cl_git_pass(git_buf_joinpath(&file, empty_tmp_dir, "/two/file.txt"));
- fd = p_creat(file.ptr, 0666);
- cl_assert(fd >= 0);
+ cl_git_mkfile(git_buf_cstr(&file), "dummy");
- cl_must_pass(p_close(fd));
- cl_git_fail(git_futils_rmdir_r(empty_tmp_dir, 0));
+ cl_git_fail(git_futils_rmdir_r(empty_tmp_dir, GIT_DIRREMOVAL_EMPTY_HIERARCHY));
cl_must_pass(p_unlink(file.ptr));
- cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, 0));
+ cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, GIT_DIRREMOVAL_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, GIT_DIRREMOVAL_ONLY_EMPTY_DIRS));
+ cl_assert(git_path_exists(git_buf_cstr(&file)) == true);
+
+ cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, GIT_DIRREMOVAL_FILES_AND_DIRS));
+ cl_assert(git_path_exists(empty_tmp_dir) == false);
git_buf_free(&file);
}
diff --git a/tests-clar/core/strmap.c b/tests-clar/core/strmap.c
new file mode 100644
index 000000000..f34a4f89f
--- /dev/null
+++ b/tests-clar/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-clar/diff/blob.c b/tests-clar/diff/blob.c
new file mode 100644
index 000000000..6d7ad41d6
--- /dev/null
+++ b/tests-clar/diff/blob.c
@@ -0,0 +1,254 @@
+#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;
+
+void test_diff_blob__initialize(void)
+{
+ git_oid oid;
+
+ g_repo = cl_git_sandbox_init("attr");
+
+ memset(&opts, 0, sizeof(opts));
+ opts.context_lines = 1;
+ opts.interhunk_lines = 1;
+
+ memset(&expected, 0, sizeof(expected));
+
+ /* tests/resources/attr/root_test4.txt */
+ cl_git_pass(git_oid_fromstrn(&oid, "fe773770c5a6", 12));
+ cl_git_pass(git_blob_lookup_prefix(&d, g_repo, &oid, 6));
+
+ /* 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);
+ git_blob_free(alien);
+
+ 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 */
+
+ cl_git_pass(git_diff_blobs(
+ a, b, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ cl_assert(expected.files == 1);
+ cl_assert(expected.file_mods == 1);
+ cl_assert(expected.at_least_one_of_them_is_binary == false);
+
+ cl_assert(expected.hunks == 1);
+ cl_assert(expected.lines == 6);
+ cl_assert(expected.line_ctxt == 1);
+ cl_assert(expected.line_adds == 5);
+ cl_assert(expected.line_dels == 0);
+
+ memset(&expected, 0, sizeof(expected));
+ cl_git_pass(git_diff_blobs(
+ b, c, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ cl_assert(expected.files == 1);
+ cl_assert(expected.file_mods == 1);
+ cl_assert(expected.at_least_one_of_them_is_binary == false);
+
+ cl_assert(expected.hunks == 1);
+ cl_assert(expected.lines == 15);
+ cl_assert(expected.line_ctxt == 3);
+ cl_assert(expected.line_adds == 9);
+ cl_assert(expected.line_dels == 3);
+
+ memset(&expected, 0, sizeof(expected));
+ cl_git_pass(git_diff_blobs(
+ a, c, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ cl_assert(expected.files == 1);
+ cl_assert(expected.file_mods == 1);
+ cl_assert(expected.at_least_one_of_them_is_binary == false);
+
+ cl_assert(expected.hunks == 1);
+ cl_assert(expected.lines == 13);
+ cl_assert(expected.line_ctxt == 0);
+ cl_assert(expected.line_adds == 12);
+ cl_assert(expected.line_dels == 1);
+
+ opts.context_lines = 1;
+
+ memset(&expected, 0, sizeof(expected));
+ cl_git_pass(git_diff_blobs(
+ c, d, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ cl_assert(expected.files == 1);
+ cl_assert(expected.file_mods == 1);
+ cl_assert(expected.at_least_one_of_them_is_binary == false);
+
+ cl_assert(expected.hunks == 2);
+ cl_assert(expected.lines == 14);
+ cl_assert(expected.line_ctxt == 4);
+ cl_assert(expected.line_adds == 6);
+ cl_assert(expected.line_dels == 4);
+
+ 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, e, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ cl_assert(expected.files == 1);
+ cl_assert(expected.file_dels == 1);
+ cl_assert(expected.at_least_one_of_them_is_binary == false);
+
+ cl_assert(expected.hunks == 1);
+ cl_assert(expected.hunk_old_lines == 14);
+ cl_assert(expected.lines == 14);
+ cl_assert(expected.line_dels == 14);
+
+ opts.flags |= GIT_DIFF_REVERSE;
+ memset(&expected, 0, sizeof(expected));
+
+ cl_git_pass(git_diff_blobs(
+ d, e, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ cl_assert(expected.files == 1);
+ cl_assert(expected.file_adds == 1);
+ cl_assert(expected.at_least_one_of_them_is_binary == false);
+
+ cl_assert(expected.hunks == 1);
+ cl_assert(expected.hunk_new_lines == 14);
+ cl_assert(expected.lines == 14);
+ cl_assert(expected.line_adds == 14);
+
+ opts.flags ^= GIT_DIFF_REVERSE;
+ memset(&expected, 0, sizeof(expected));
+
+ cl_git_pass(git_diff_blobs(
+ alien, NULL, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ cl_assert(expected.at_least_one_of_them_is_binary == true);
+
+ cl_assert(expected.files == 1);
+ cl_assert(expected.file_dels == 1);
+ cl_assert(expected.hunks == 0);
+ cl_assert(expected.lines == 0);
+
+ memset(&expected, 0, sizeof(expected));
+
+ cl_git_pass(git_diff_blobs(
+ NULL, alien, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ cl_assert(expected.at_least_one_of_them_is_binary == true);
+
+ cl_assert(expected.files == 1);
+ cl_assert(expected.file_adds == 1);
+ cl_assert(expected.hunks == 0);
+ cl_assert(expected.lines == 0);
+}
+
+static void assert_identical_blobs_comparison(diff_expects expected)
+{
+ cl_assert(expected.files == 1);
+ cl_assert(expected.file_unmodified == 1);
+ cl_assert(expected.hunks == 0);
+ cl_assert(expected.lines == 0);
+}
+
+void test_diff_blob__can_compare_identical_blobs(void)
+{
+ cl_git_pass(git_diff_blobs(
+ d, d, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ cl_assert(expected.at_least_one_of_them_is_binary == false);
+ assert_identical_blobs_comparison(expected);
+
+ memset(&expected, 0, sizeof(expected));
+ cl_git_pass(git_diff_blobs(
+ NULL, NULL, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ cl_assert(expected.at_least_one_of_them_is_binary == false);
+ assert_identical_blobs_comparison(expected);
+
+ memset(&expected, 0, sizeof(expected));
+ cl_git_pass(git_diff_blobs(
+ alien, alien, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ cl_assert(expected.at_least_one_of_them_is_binary == true);
+ assert_identical_blobs_comparison(expected);
+}
+
+static void assert_binary_blobs_comparison(diff_expects expected)
+{
+ cl_assert(expected.at_least_one_of_them_is_binary == true);
+
+ cl_assert(expected.files == 1);
+ cl_assert(expected.file_mods == 1);
+ cl_assert(expected.hunks == 0);
+ cl_assert(expected.lines == 0);
+}
+
+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, heart, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ assert_binary_blobs_comparison(expected);
+
+ memset(&expected, 0, sizeof(expected));
+
+ cl_git_pass(git_diff_blobs(
+ heart, alien, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ 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, d, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ assert_binary_blobs_comparison(expected);
+
+ memset(&expected, 0, sizeof(expected));
+
+ cl_git_pass(git_diff_blobs(
+ d, alien, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ assert_binary_blobs_comparison(expected);
+}
diff --git a/tests-clar/diff/diff_helpers.c b/tests-clar/diff/diff_helpers.c
new file mode 100644
index 000000000..8587be9b1
--- /dev/null
+++ b/tests-clar/diff/diff_helpers.c
@@ -0,0 +1,99 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+
+git_tree *resolve_commit_oid_to_tree(
+ git_repository *repo,
+ const char *partial_oid)
+{
+ unsigned int len = (unsigned int)strlen(partial_oid);
+ git_oid oid;
+ git_object *obj = NULL;
+ git_tree *tree = NULL;
+
+ if (git_oid_fromstrn(&oid, partial_oid, len) == 0)
+ git_object_lookup_prefix(&obj, repo, &oid, len, GIT_OBJ_ANY);
+ cl_assert(obj);
+ if (git_object_type(obj) == GIT_OBJ_TREE)
+ return (git_tree *)obj;
+ cl_assert(git_object_type(obj) == GIT_OBJ_COMMIT);
+ cl_git_pass(git_commit_tree(&tree, (git_commit *)obj));
+ git_object_free(obj);
+ return tree;
+}
+
+int diff_file_fn(
+ void *cb_data,
+ git_diff_delta *delta,
+ float progress)
+{
+ diff_expects *e = cb_data;
+
+ GIT_UNUSED(progress);
+
+ e-> at_least_one_of_them_is_binary = delta->binary;
+
+ e->files++;
+ switch (delta->status) {
+ case GIT_DELTA_ADDED: e->file_adds++; break;
+ case GIT_DELTA_DELETED: e->file_dels++; break;
+ case GIT_DELTA_MODIFIED: e->file_mods++; break;
+ case GIT_DELTA_IGNORED: e->file_ignored++; break;
+ case GIT_DELTA_UNTRACKED: e->file_untracked++; break;
+ case GIT_DELTA_UNMODIFIED: e->file_unmodified++; break;
+ default: break;
+ }
+ return 0;
+}
+
+int diff_hunk_fn(
+ void *cb_data,
+ git_diff_delta *delta,
+ git_diff_range *range,
+ const char *header,
+ size_t header_len)
+{
+ diff_expects *e = cb_data;
+
+ GIT_UNUSED(delta);
+ GIT_UNUSED(header);
+ GIT_UNUSED(header_len);
+
+ e->hunks++;
+ e->hunk_old_lines += range->old_lines;
+ e->hunk_new_lines += range->new_lines;
+ return 0;
+}
+
+int diff_line_fn(
+ void *cb_data,
+ git_diff_delta *delta,
+ git_diff_range *range,
+ char line_origin,
+ const char *content,
+ size_t content_len)
+{
+ diff_expects *e = cb_data;
+
+ GIT_UNUSED(delta);
+ GIT_UNUSED(range);
+ GIT_UNUSED(content);
+ GIT_UNUSED(content_len);
+
+ e->lines++;
+ switch (line_origin) {
+ case GIT_DIFF_LINE_CONTEXT:
+ e->line_ctxt++;
+ break;
+ case GIT_DIFF_LINE_ADDITION:
+ case GIT_DIFF_LINE_ADD_EOFNL:
+ e->line_adds++;
+ break;
+ case GIT_DIFF_LINE_DELETION:
+ case GIT_DIFF_LINE_DEL_EOFNL:
+ e->line_dels++;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
diff --git a/tests-clar/diff/diff_helpers.h b/tests-clar/diff/diff_helpers.h
new file mode 100644
index 000000000..0aaa6c111
--- /dev/null
+++ b/tests-clar/diff/diff_helpers.h
@@ -0,0 +1,47 @@
+#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 file_adds;
+ int file_dels;
+ int file_mods;
+ int file_ignored;
+ int file_untracked;
+ int file_unmodified;
+
+ int hunks;
+ int hunk_new_lines;
+ int hunk_old_lines;
+
+ int lines;
+ int line_ctxt;
+ int line_adds;
+ int line_dels;
+
+ bool at_least_one_of_them_is_binary;
+} diff_expects;
+
+extern int diff_file_fn(
+ void *cb_data,
+ git_diff_delta *delta,
+ float progress);
+
+extern int diff_hunk_fn(
+ void *cb_data,
+ git_diff_delta *delta,
+ git_diff_range *range,
+ const char *header,
+ size_t header_len);
+
+extern int diff_line_fn(
+ void *cb_data,
+ git_diff_delta *delta,
+ git_diff_range *range,
+ char line_origin,
+ const char *content,
+ size_t content_len);
+
diff --git a/tests-clar/diff/index.c b/tests-clar/diff/index.c
new file mode 100644
index 000000000..171815df5
--- /dev/null
+++ b/tests-clar/diff/index.c
@@ -0,0 +1,92 @@
+#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 = {0};
+ git_diff_list *diff = NULL;
+ diff_expects exp;
+
+ cl_assert(a);
+ cl_assert(b);
+
+ opts.context_lines = 1;
+ opts.interhunk_lines = 1;
+
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_index_to_tree(g_repo, &opts, a, &diff));
+
+ cl_git_pass(git_diff_foreach(
+ diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ /* 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(exp.files == 8);
+ cl_assert(exp.file_adds == 3);
+ cl_assert(exp.file_dels == 2);
+ cl_assert(exp.file_mods == 3);
+
+ cl_assert(exp.hunks == 8);
+
+ cl_assert(exp.lines == 11);
+ cl_assert(exp.line_ctxt == 3);
+ cl_assert(exp.line_adds == 6);
+ cl_assert(exp.line_dels == 2);
+
+ git_diff_list_free(diff);
+ diff = NULL;
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_index_to_tree(g_repo, &opts, b, &diff));
+
+ cl_git_pass(git_diff_foreach(
+ diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ /* 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(exp.files == 12);
+ cl_assert(exp.file_adds == 7);
+ cl_assert(exp.file_dels == 2);
+ cl_assert(exp.file_mods == 3);
+
+ cl_assert(exp.hunks == 12);
+
+ cl_assert(exp.lines == 16);
+ cl_assert(exp.line_ctxt == 3);
+ cl_assert(exp.line_adds == 11);
+ cl_assert(exp.line_dels == 2);
+
+ git_diff_list_free(diff);
+ diff = NULL;
+
+ git_tree_free(a);
+ git_tree_free(b);
+}
diff --git a/tests-clar/diff/iterator.c b/tests-clar/diff/iterator.c
new file mode 100644
index 000000000..be29bea66
--- /dev/null
+++ b/tests-clar/diff/iterator.c
@@ -0,0 +1,570 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+#include "iterator.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 count = 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_range(&i, repo, t, start, end));
+ cl_git_pass(git_iterator_current(i, &entry));
+
+ while (entry != NULL) {
+ if (expected_values != NULL)
+ cl_assert_equal_s(expected_values[count], entry->path);
+
+ count++;
+
+ cl_git_pass(git_iterator_advance(i, &entry));
+ }
+
+ git_iterator_free(i);
+
+ cl_assert(expected_count == count);
+
+ 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);
+}
+
+/* -- 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_iterator *i;
+ const git_index_entry *entry;
+ int count = 0;
+ git_repository *repo = cl_git_sandbox_init(sandbox);
+
+ cl_git_pass(git_iterator_for_index_range(&i, repo, start, end));
+ cl_git_pass(git_iterator_current(i, &entry));
+
+ while (entry != NULL) {
+ 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_git_pass(git_iterator_advance(i, &entry));
+ }
+
+ git_iterator_free(i);
+
+ cl_assert_equal_i(expected_count, count);
+}
+
+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",
+ "fe773770c5a6cc7185580c9204b1ff18a33ff3fc",
+ "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",
+ "fe773770c5a6cc7185580c9204b1ff18a33ff3fc",
+};
+
+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 count = 0, count_all = 0;
+ git_repository *repo = cl_git_sandbox_init(sandbox);
+
+ cl_git_pass(git_iterator_for_workdir_range(&i, repo, start, end));
+ cl_git_pass(git_iterator_current(i, &entry));
+
+ while (entry != NULL) {
+ int ignored = git_iterator_current_is_ignored(i);
+
+ if (S_ISDIR(entry->mode)) {
+ cl_git_pass(git_iterator_advance_into_directory(i, &entry));
+ 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++;
+
+ cl_git_pass(git_iterator_advance(i, &entry));
+ }
+
+ git_iterator_free(i);
+
+ cl_assert(count == expected_count);
+ cl_assert(count_all == expected_count + expected_ignores);
+}
+
+void test_diff_iterator__workdir_0(void)
+{
+ workdir_iterator_test("attr", NULL, NULL, 25, 2, 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",
+ NULL
+};
+
+void test_diff_iterator__workdir_1(void)
+{
+ workdir_iterator_test(
+ "status", NULL, NULL, 12, 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",
+ NULL
+};
+
+void test_diff_iterator__workdir_1_ranged_4(void)
+{
+ workdir_iterator_test(
+ "status", "subdir/", NULL, 3, 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", "z_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);
+}
diff --git a/tests-clar/diff/patch.c b/tests-clar/diff/patch.c
new file mode 100644
index 000000000..05e748667
--- /dev/null
+++ b/tests-clar/diff/patch.c
@@ -0,0 +1,99 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+
+static git_repository *g_repo = NULL;
+
+void test_diff_patch__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("status");
+}
+
+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(
+ void *cb_data,
+ git_diff_delta *delta,
+ git_diff_range *range,
+ char line_origin,
+ const char *formatted_output,
+ size_t output_len)
+{
+ GIT_UNUSED(cb_data);
+ GIT_UNUSED(output_len);
+
+ switch (line_origin) {
+ case GIT_DIFF_LINE_FILE_HDR:
+ cl_assert_equal_s(EXPECTED_HEADER, formatted_output);
+ cl_assert(range == NULL);
+ goto check_delta;
+
+ case GIT_DIFF_LINE_HUNK_HDR:
+ cl_assert_equal_s(EXPECTED_HUNK, formatted_output);
+ /* Fall through */
+
+ case GIT_DIFF_LINE_CONTEXT:
+ case GIT_DIFF_LINE_DELETION:
+ goto check_range;
+
+ default:
+ /* unexpected code path */
+ return -1;
+ }
+
+check_range:
+ cl_assert(range != NULL);
+ cl_assert_equal_i(1, range->old_start);
+ cl_assert_equal_i(2, range->old_lines);
+ cl_assert_equal_i(0, range->new_start);
+ cl_assert_equal_i(0, range->new_lines);
+
+check_delta:
+ cl_assert_equal_s("subdir.txt", delta->old_file.path);
+ cl_assert_equal_s("subdir.txt", delta->new_file.path);
+ cl_assert_equal_i(GIT_DELTA_DELETED, delta->status);
+
+ return 0;
+}
+
+void test_diff_patch__can_properly_display_the_removal_of_a_file(void)
+{
+ /*
+ * $ git diff 26a125e..735b6a2
+ * diff --git a/subdir.txt b/subdir.txt
+ * deleted file mode 100644
+ * index e8ee89e..0000000
+ * --- a/subdir.txt
+ * +++ /dev/null
+ * @@ -1,2 +0,0 @@
+ * -Is it a bird?
+ * -Is it a plane?
+ */
+
+ const char *one_sha = "26a125e";
+ const char *another_sha = "735b6a2";
+ git_tree *one, *another;
+ git_diff_list *diff;
+
+ 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(g_repo, NULL, one, another, &diff));
+
+ cl_git_pass(git_diff_print_patch(diff, NULL, check_removal_cb));
+
+ git_diff_list_free(diff);
+
+ git_tree_free(another);
+ git_tree_free(one);
+}
diff --git a/tests-clar/diff/tree.c b/tests-clar/diff/tree.c
new file mode 100644
index 000000000..b932fa10e
--- /dev/null
+++ b/tests-clar/diff/tree.c
@@ -0,0 +1,210 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+
+static git_repository *g_repo = NULL;
+
+void test_diff_tree__initialize(void)
+{
+}
+
+void test_diff_tree__cleanup(void)
+{
+ 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 *a, *b, *c;
+ git_diff_options opts = {0};
+ git_diff_list *diff = NULL;
+ diff_expects exp;
+
+ 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;
+
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_tree_to_tree(g_repo, &opts, a, b, &diff));
+
+ cl_git_pass(git_diff_foreach(
+ diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ cl_assert(exp.files == 5);
+ cl_assert(exp.file_adds == 2);
+ cl_assert(exp.file_dels == 1);
+ cl_assert(exp.file_mods == 2);
+
+ cl_assert(exp.hunks == 5);
+
+ cl_assert(exp.lines == 7 + 24 + 1 + 6 + 6);
+ cl_assert(exp.line_ctxt == 1);
+ cl_assert(exp.line_adds == 24 + 1 + 5 + 5);
+ cl_assert(exp.line_dels == 7 + 1);
+
+ git_diff_list_free(diff);
+ diff = NULL;
+
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_tree_to_tree(g_repo, &opts, c, b, &diff));
+
+ cl_git_pass(git_diff_foreach(
+ diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ cl_assert(exp.files == 2);
+ cl_assert(exp.file_adds == 0);
+ cl_assert(exp.file_dels == 0);
+ cl_assert(exp.file_mods == 2);
+
+ cl_assert(exp.hunks == 2);
+
+ cl_assert(exp.lines == 8 + 15);
+ cl_assert(exp.line_ctxt == 1);
+ cl_assert(exp.line_adds == 1);
+ cl_assert(exp.line_dels == 7 + 14);
+
+ git_diff_list_free(diff);
+
+ git_tree_free(a);
+ git_tree_free(b);
+ git_tree_free(c);
+}
+
+void test_diff_tree__options(void)
+{
+ /* grabbed a couple of commit oids from the history of the attr repo */
+ const char *a_commit = "6bab5c79cd5140d0";
+ const char *b_commit = "605812ab7fe421fdd";
+ const char *c_commit = "f5b0af1fb4f5";
+ const char *d_commit = "a97cc019851";
+ git_tree *a, *b, *c, *d;
+ git_diff_options opts = {0};
+ git_diff_list *diff = NULL;
+ 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 */
+ { GIT_DIFF_NORMAL, 1, 1, NULL, NULL, {0} },
+ { GIT_DIFF_NORMAL, 3, 1, NULL, NULL, {0} },
+ { GIT_DIFF_REVERSE, 2, 1, NULL, NULL, {0} },
+ { GIT_DIFF_FORCE_TEXT, 2, 1, NULL, NULL, {0} },
+ /* c vs d tests */
+ { GIT_DIFF_NORMAL, 3, 1, NULL, NULL, {0} },
+ { GIT_DIFF_IGNORE_WHITESPACE, 3, 1, NULL, NULL, {0} },
+ { GIT_DIFF_IGNORE_WHITESPACE_CHANGE, 3, 1, NULL, NULL, {0} },
+ { GIT_DIFF_IGNORE_WHITESPACE_EOL, 3, 1, NULL, NULL, {0} },
+ { GIT_DIFF_IGNORE_WHITESPACE | GIT_DIFF_REVERSE, 1, 1, NULL, NULL, {0} },
+ };
+ /* to generate these values:
+ * - cd to tests/resources/attr,
+ * - mv .gitted .git
+ * - git diff [options] 6bab5c79cd5140d0 605812ab7fe421fdd
+ * - mv .git .gitted
+ */
+ diff_expects test_expects[] = {
+ /* a vs b tests */
+ { 5, 3, 0, 2, 0, 0, 0, 4, 0, 0, 51, 2, 46, 3 },
+ { 5, 3, 0, 2, 0, 0, 0, 4, 0, 0, 53, 4, 46, 3 },
+ { 5, 0, 3, 2, 0, 0, 0, 4, 0, 0, 52, 3, 3, 46 },
+ { 5, 3, 0, 2, 0, 0, 0, 5, 0, 0, 54, 3, 48, 3 },
+ /* c vs d tests */
+ { 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 22, 9, 10, 3 },
+ { 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 19, 12, 7, 0 },
+ { 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 20, 11, 8, 1 },
+ { 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 20, 11, 8, 1 },
+ { 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 18, 11, 0, 7 },
+ { 0 },
+ };
+ diff_expects *expected;
+ int i;
+
+ 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(g_repo, &opts, a, b, &diff));
+ else
+ cl_git_pass(git_diff_tree_to_tree(g_repo, &opts, c, d, &diff));
+
+ cl_git_pass(git_diff_foreach(
+ diff, &actual, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ expected = &test_expects[i];
+ cl_assert_equal_i(actual.files, expected->files);
+ cl_assert_equal_i(actual.file_adds, expected->file_adds);
+ cl_assert_equal_i(actual.file_dels, expected->file_dels);
+ cl_assert_equal_i(actual.file_mods, expected->file_mods);
+ cl_assert_equal_i(actual.hunks, expected->hunks);
+ cl_assert_equal_i(actual.lines, expected->lines);
+ cl_assert_equal_i(actual.line_ctxt, expected->line_ctxt);
+ cl_assert_equal_i(actual.line_adds, expected->line_adds);
+ cl_assert_equal_i(actual.line_dels, expected->line_dels);
+
+ git_diff_list_free(diff);
+ diff = NULL;
+ }
+
+ git_tree_free(a);
+ git_tree_free(b);
+ git_tree_free(c);
+ git_tree_free(d);
+}
+
+void test_diff_tree__bare(void)
+{
+ const char *a_commit = "8496071c1b46c85";
+ const char *b_commit = "be3563ae3f79";
+ git_tree *a, *b;
+ git_diff_options opts = {0};
+ git_diff_list *diff = NULL;
+ diff_expects exp;
+
+ 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;
+
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_tree_to_tree(g_repo, &opts, a, b, &diff));
+
+ cl_git_pass(git_diff_foreach(
+ diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ cl_assert(exp.files == 3);
+ cl_assert(exp.file_adds == 2);
+ cl_assert(exp.file_dels == 0);
+ cl_assert(exp.file_mods == 1);
+
+ cl_assert(exp.hunks == 3);
+
+ cl_assert(exp.lines == 4);
+ cl_assert(exp.line_ctxt == 0);
+ cl_assert(exp.line_adds == 3);
+ cl_assert(exp.line_dels == 1);
+
+ git_diff_list_free(diff);
+ git_tree_free(a);
+ git_tree_free(b);
+}
diff --git a/tests-clar/diff/workdir.c b/tests-clar/diff/workdir.c
new file mode 100644
index 000000000..1ea1af86a
--- /dev/null
+++ b/tests-clar/diff/workdir.c
@@ -0,0 +1,301 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+
+static git_repository *g_repo = NULL;
+
+void test_diff_workdir__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("status");
+}
+
+void test_diff_workdir__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_diff_workdir__to_index(void)
+{
+ git_diff_options opts = {0};
+ git_diff_list *diff = NULL;
+ diff_expects exp;
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff));
+
+ cl_git_pass(git_diff_foreach(
+ diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ /* 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(12, exp.files);
+ cl_assert_equal_i(0, exp.file_adds);
+ cl_assert_equal_i(4, exp.file_dels);
+ cl_assert_equal_i(4, exp.file_mods);
+ cl_assert_equal_i(1, exp.file_ignored);
+ cl_assert_equal_i(3, exp.file_untracked);
+
+ cl_assert_equal_i(8, exp.hunks);
+
+ cl_assert_equal_i(14, exp.lines);
+ cl_assert_equal_i(5, exp.line_ctxt);
+ cl_assert_equal_i(4, exp.line_adds);
+ cl_assert_equal_i(5, exp.line_dels);
+
+ git_diff_list_free(diff);
+}
+
+void test_diff_workdir__to_tree(void)
+{
+ /* grabbed a couple of commit oids from the history of the attr repo */
+ const char *a_commit = "26a125ee1bf"; /* the current HEAD */
+ const char *b_commit = "0017bd4ab1ec3"; /* the start */
+ git_tree *a = 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 = {0};
+ git_diff_list *diff = NULL;
+ git_diff_list *diff2 = NULL;
+ diff_expects exp;
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+
+ memset(&exp, 0, sizeof(exp));
+
+ /* You can't really generate the equivalent of git_diff_workdir_to_tree()
+ * 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_workdir_to_tree(g_repo, &opts, a, &diff));
+
+ cl_git_pass(git_diff_foreach(
+ diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ cl_assert(exp.files == 13);
+ cl_assert(exp.file_adds == 0);
+ cl_assert(exp.file_dels == 4);
+ cl_assert(exp.file_mods == 4);
+ cl_assert(exp.file_ignored == 1);
+ cl_assert(exp.file_untracked == 4);
+
+ /* Since there is no git diff equivalent, let's just assume that the
+ * text diffs produced by git_diff_foreach are accurate here. We will
+ * do more apples-to-apples test comparison below.
+ */
+
+ git_diff_list_free(diff);
+ diff = NULL;
+ memset(&exp, 0, sizeof(exp));
+
+ /* This is a compatible emulation of "git diff <sha>" which looks like
+ * a workdir to tree diff (even though it is not really). This is what
+ * you would get from "git diff --name-status 26a125ee1bf"
+ */
+ cl_git_pass(git_diff_index_to_tree(g_repo, &opts, a, &diff));
+ cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff2));
+ cl_git_pass(git_diff_merge(diff, diff2));
+ git_diff_list_free(diff2);
+
+ cl_git_pass(git_diff_foreach(
+ diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ cl_assert(exp.files == 14);
+ cl_assert(exp.file_adds == 2);
+ cl_assert(exp.file_dels == 5);
+ cl_assert(exp.file_mods == 4);
+ cl_assert(exp.file_ignored == 1);
+ cl_assert(exp.file_untracked == 2);
+
+ cl_assert(exp.hunks == 11);
+
+ cl_assert(exp.lines == 17);
+ cl_assert(exp.line_ctxt == 4);
+ cl_assert(exp.line_adds == 8);
+ cl_assert(exp.line_dels == 5);
+
+ git_diff_list_free(diff);
+ diff = NULL;
+ memset(&exp, 0, sizeof(exp));
+
+ /* Again, emulating "git diff <sha>" for testing purposes using
+ * "git diff --name-status 0017bd4ab1ec3" instead.
+ */
+ cl_git_pass(git_diff_index_to_tree(g_repo, &opts, b, &diff));
+ cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff2));
+ cl_git_pass(git_diff_merge(diff, diff2));
+ git_diff_list_free(diff2);
+
+ cl_git_pass(git_diff_foreach(
+ diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ cl_assert(exp.files == 15);
+ cl_assert(exp.file_adds == 5);
+ cl_assert(exp.file_dels == 4);
+ cl_assert(exp.file_mods == 3);
+ cl_assert(exp.file_ignored == 1);
+ cl_assert(exp.file_untracked == 2);
+
+ cl_assert(exp.hunks == 12);
+
+ cl_assert(exp.lines == 19);
+ cl_assert(exp.line_ctxt == 3);
+ cl_assert(exp.line_adds == 12);
+ cl_assert(exp.line_dels == 4);
+
+ git_diff_list_free(diff);
+
+ git_tree_free(a);
+ git_tree_free(b);
+}
+
+void test_diff_workdir__to_index_with_pathspec(void)
+{
+ git_diff_options opts = {0};
+ git_diff_list *diff = NULL;
+ diff_expects exp;
+ char *pathspec = NULL;
+
+ 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;
+
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff));
+ cl_git_pass(git_diff_foreach(diff, &exp, diff_file_fn, NULL, NULL));
+
+ cl_assert_equal_i(12, exp.files);
+ cl_assert_equal_i(0, exp.file_adds);
+ cl_assert_equal_i(4, exp.file_dels);
+ cl_assert_equal_i(4, exp.file_mods);
+ cl_assert_equal_i(1, exp.file_ignored);
+ cl_assert_equal_i(3, exp.file_untracked);
+
+ git_diff_list_free(diff);
+
+ memset(&exp, 0, sizeof(exp));
+ pathspec = "modified_file";
+
+ cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff));
+ cl_git_pass(git_diff_foreach(diff, &exp, diff_file_fn, NULL, NULL));
+
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(0, exp.file_adds);
+ cl_assert_equal_i(0, exp.file_dels);
+ cl_assert_equal_i(1, exp.file_mods);
+ cl_assert_equal_i(0, exp.file_ignored);
+ cl_assert_equal_i(0, exp.file_untracked);
+
+ git_diff_list_free(diff);
+
+ memset(&exp, 0, sizeof(exp));
+ pathspec = "subdir";
+
+ cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff));
+ cl_git_pass(git_diff_foreach(diff, &exp, diff_file_fn, NULL, NULL));
+
+ cl_assert_equal_i(3, exp.files);
+ cl_assert_equal_i(0, exp.file_adds);
+ cl_assert_equal_i(1, exp.file_dels);
+ cl_assert_equal_i(1, exp.file_mods);
+ cl_assert_equal_i(0, exp.file_ignored);
+ cl_assert_equal_i(1, exp.file_untracked);
+
+ git_diff_list_free(diff);
+
+ memset(&exp, 0, sizeof(exp));
+ pathspec = "*_deleted";
+
+ cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff));
+ cl_git_pass(git_diff_foreach(diff, &exp, diff_file_fn, NULL, NULL));
+
+ cl_assert_equal_i(2, exp.files);
+ cl_assert_equal_i(0, exp.file_adds);
+ cl_assert_equal_i(2, exp.file_dels);
+ cl_assert_equal_i(0, exp.file_mods);
+ cl_assert_equal_i(0, exp.file_ignored);
+ cl_assert_equal_i(0, exp.file_untracked);
+
+ git_diff_list_free(diff);
+}
+
+/* PREPARATION OF TEST DATA
+ *
+ * Since there is no command line equivalent of git_diff_workdir_to_tree,
+ * 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
+ */
diff --git a/tests-clar/index/tests.c b/tests-clar/index/tests.c
new file mode 100644
index 000000000..3436f8d1e
--- /dev/null
+++ b/tests-clar/index/tests.c
@@ -0,0 +1,246 @@
+#include "clar_libgit2.h"
+#include "index.h"
+
+static const int index_entry_count = 109;
+static const int 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 {
+ int 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);
+ 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__cleanup(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) == (unsigned int)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) == (unsigned int)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) {
+ int idx = git_index_find(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) {
+ int idx = git_index_find(index, test_entries[i].path);
+ cl_assert(idx == GIT_ENOTFOUND);
+ }
+
+ 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);
+}
+
+void test_index_tests__add(void)
+{
+ git_index *index;
+ git_filebuf file = GIT_FILEBUF_INIT;
+ git_repository *repo;
+ git_index_entry *entry;
+ git_oid id1;
+
+ /* Intialize a new repository */
+ cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
+
+ /* Ensure we're the only guy in the room */
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(git_index_entrycount(index) == 0);
+
+ /* Create a new file in the working directory */
+ cl_git_pass(git_futils_mkpath2file("myrepo/test.txt", 0777));
+ cl_git_pass(git_filebuf_open(&file, "myrepo/test.txt", 0));
+ cl_git_pass(git_filebuf_write(&file, "hey there\n", 10));
+ cl_git_pass(git_filebuf_commit(&file, 0666));
+
+ /* Store the expected hash of the file/blob
+ * This has been generated by executing the following
+ * $ echo "hey there" | git hash-object --stdin
+ */
+ cl_git_pass(git_oid_fromstr(&id1, "a8233120f6ad708f843d861ce2b7228ec4e3dec6"));
+
+ /* Add the new file to the index */
+ cl_git_pass(git_index_add(index, "test.txt", 0));
+
+ /* Wow... it worked! */
+ cl_assert(git_index_entrycount(index) == 1);
+ entry = git_index_get(index, 0);
+
+ /* And the built-in hashing mechanism worked as expected */
+ cl_assert(git_oid_cmp(&id1, &entry->oid) == 0);
+
+ git_index_free(index);
+ git_repository_free(repo);
+}
+
diff --git a/tests-clar/network/createremotethenload.c b/tests-clar/network/createremotethenload.c
index 68ba9291e..45931d376 100644
--- a/tests-clar/network/createremotethenload.c
+++ b/tests-clar/network/createremotethenload.c
@@ -28,6 +28,6 @@ void test_network_createremotethenload__cleanup(void)
void test_network_createremotethenload__parsing(void)
{
- cl_assert(!strcmp(git_remote_name(_remote), "origin"));
- cl_assert(!strcmp(git_remote_url(_remote), url));
+ cl_assert_equal_s(git_remote_name(_remote), "origin");
+ cl_assert_equal_s(git_remote_url(_remote), url);
}
diff --git a/tests-clar/network/remotelocal.c b/tests-clar/network/remotelocal.c
index 81af77756..98abbbeb9 100644
--- a/tests-clar/network/remotelocal.c
+++ b/tests-clar/network/remotelocal.c
@@ -68,14 +68,24 @@ static int count_ref__cb(git_remote_head *head, void *payload)
(void)head;
(*count)++;
- return GIT_SUCCESS;
+ return 0;
+}
+
+static int ensure_peeled__cb(git_remote_head *head, void *payload)
+{
+ GIT_UNUSED(payload);
+
+ if(strcmp(head->name, "refs/tags/test^{}") != 0)
+ return 0;
+
+ return git_oid_streq(&head->oid, "e90810b8df3e80c413d903f631643c716887138d");
}
static void connect_to_local_repository(const char *local_repository)
{
build_local_file_url(&file_path_buf, local_repository);
- cl_git_pass(git_remote_new(&remote, repo, git_buf_cstr(&file_path_buf), NULL));
+ cl_git_pass(git_remote_new(&remote, repo, NULL, git_buf_cstr(&file_path_buf), NULL));
cl_git_pass(git_remote_connect(remote, GIT_DIR_FETCH));
}
@@ -88,7 +98,7 @@ void test_network_remotelocal__retrieve_advertised_references(void)
cl_git_pass(git_remote_ls(remote, &count_ref__cb, &how_many_refs));
- cl_assert(how_many_refs == 12); /* 1 HEAD + 9 refs + 2 peeled tags */
+ cl_assert(how_many_refs == 14); /* 1 HEAD + 6 heads + 1 lightweight tag + 3 annotated tags + 3 peeled target */
}
void test_network_remotelocal__retrieve_advertised_references_from_spaced_repository(void)
@@ -102,7 +112,17 @@ void test_network_remotelocal__retrieve_advertised_references_from_spaced_reposi
cl_git_pass(git_remote_ls(remote, &count_ref__cb, &how_many_refs));
- cl_assert(how_many_refs == 12); /* 1 HEAD */
+ cl_assert(how_many_refs == 14); /* 1 HEAD + 6 heads + 1 lightweight tag + 3 annotated tags + 3 peeled target */
+
+ git_remote_free(remote); /* Disconnect from the "spaced repo" before the cleanup */
+ remote = NULL;
cl_fixture_cleanup("spaced testrepo.git");
}
+
+void test_network_remotelocal__nested_tags_are_completely_peeled(void)
+{
+ connect_to_local_repository(cl_fixture("testrepo.git"));
+
+ cl_git_pass(git_remote_ls(remote, &ensure_peeled__cb, NULL));
+}
diff --git a/tests-clar/network/remotes.c b/tests-clar/network/remotes.c
index f3a45d6ad..0649c86dd 100644
--- a/tests-clar/network/remotes.c
+++ b/tests-clar/network/remotes.c
@@ -1,6 +1,7 @@
#include "clar_libgit2.h"
#include "buffer.h"
#include "refspec.h"
+#include "transport.h"
static git_remote *_remote;
static git_repository *_repo;
@@ -26,20 +27,82 @@ void test_network_remotes__cleanup(void)
void test_network_remotes__parsing(void)
{
- cl_assert(!strcmp(git_remote_name(_remote), "test"));
- cl_assert(!strcmp(git_remote_url(_remote), "git://github.com/libgit2/libgit2"));
+ cl_assert_equal_s(git_remote_name(_remote), "test");
+ cl_assert_equal_s(git_remote_url(_remote), "git://github.com/libgit2/libgit2");
+}
+
+void test_network_remotes__parsing_ssh_remote(void)
+{
+ cl_assert( git_remote_valid_url("git@github.com:libgit2/libgit2.git") );
+}
+
+void test_network_remotes__parsing_local_path_fails_if_path_not_found(void)
+{
+ cl_assert( !git_remote_valid_url("/home/git/repos/libgit2.git") );
+}
+
+void test_network_remotes__supported_transport_methods_are_supported(void)
+{
+ cl_assert( git_remote_supported_url("git://github.com/libgit2/libgit2") );
+}
+
+void test_network_remotes__unsupported_transport_methods_are_unsupported(void)
+{
+ cl_assert( !git_remote_supported_url("git@github.com:libgit2/libgit2.git") );
}
void test_network_remotes__refspec_parsing(void)
{
- cl_assert(!strcmp(git_refspec_src(_refspec), "refs/heads/*"));
- cl_assert(!strcmp(git_refspec_dst(_refspec), "refs/remotes/test/*"));
+ cl_assert_equal_s(git_refspec_src(_refspec), "refs/heads/*");
+ cl_assert_equal_s(git_refspec_dst(_refspec), "refs/remotes/test/*");
+}
+
+void test_network_remotes__set_fetchspec(void)
+{
+ cl_git_pass(git_remote_set_fetchspec(_remote, "refs/*:refs/*"));
+ _refspec = git_remote_fetchspec(_remote);
+ cl_assert_equal_s(git_refspec_src(_refspec), "refs/*");
+ cl_assert_equal_s(git_refspec_dst(_refspec), "refs/*");
+}
+
+void test_network_remotes__set_pushspec(void)
+{
+ cl_git_pass(git_remote_set_pushspec(_remote, "refs/*:refs/*"));
+ _refspec = git_remote_pushspec(_remote);
+ cl_assert_equal_s(git_refspec_src(_refspec), "refs/*");
+ cl_assert_equal_s(git_refspec_dst(_refspec), "refs/*");
+}
+
+void test_network_remotes__save(void)
+{
+ git_remote_free(_remote);
+
+ /* Set up the remote and save it to config */
+ cl_git_pass(git_remote_new(&_remote, _repo, "upstream", "git://github.com/libgit2/libgit2", NULL));
+ cl_git_pass(git_remote_set_fetchspec(_remote, "refs/heads/*:refs/remotes/upstream/*"));
+ cl_git_pass(git_remote_set_pushspec(_remote, "refs/heads/*:refs/heads/*"));
+ 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"));
+
+ _refspec = git_remote_fetchspec(_remote);
+ cl_assert(_refspec != NULL);
+ cl_assert_equal_s(git_refspec_src(_refspec), "refs/heads/*");
+ cl_assert_equal_s(git_refspec_dst(_refspec), "refs/remotes/upstream/*");
+
+ _refspec = git_remote_pushspec(_remote);
+ cl_assert(_refspec != NULL);
+ cl_assert_equal_s(git_refspec_src(_refspec), "refs/heads/*");
+ cl_assert_equal_s(git_refspec_dst(_refspec), "refs/heads/*");
}
void test_network_remotes__fnmatch(void)
{
- cl_git_pass(git_refspec_src_match(_refspec, "refs/heads/master"));
- cl_git_pass(git_refspec_src_match(_refspec, "refs/heads/multi/level/branch"));
+ 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_remotes__transform(void)
@@ -48,7 +111,7 @@ void test_network_remotes__transform(void)
memset(ref, 0x0, sizeof(ref));
cl_git_pass(git_refspec_transform(ref, sizeof(ref), _refspec, "refs/heads/master"));
- cl_assert(!strcmp(ref, "refs/remotes/test/master"));
+ cl_assert_equal_s(ref, "refs/remotes/test/master");
}
void test_network_remotes__transform_r(void)
@@ -56,6 +119,54 @@ void test_network_remotes__transform_r(void)
git_buf buf = GIT_BUF_INIT;
cl_git_pass(git_refspec_transform_r(&buf, _refspec, "refs/heads/master"));
- cl_assert(!strcmp(git_buf_cstr(&buf), "refs/remotes/test/master"));
+ cl_assert_equal_s(git_buf_cstr(&buf), "refs/remotes/test/master");
git_buf_free(&buf);
}
+
+void test_network_remotes__missing_refspecs(void)
+{
+ git_config *cfg;
+
+ git_remote_free(_remote);
+
+ 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_remotes__list(void)
+{
+ git_strarray list;
+ git_config *cfg;
+
+ cl_git_pass(git_remote_list(&list, _repo));
+ cl_assert(list.count == 1);
+ git_strarray_free(&list);
+
+ cl_git_pass(git_repository_config(&cfg, _repo));
+ cl_git_pass(git_config_set_string(cfg, "remote.specless.url", "http://example.com"));
+ cl_git_pass(git_remote_list(&list, _repo));
+ cl_assert(list.count == 2);
+ git_strarray_free(&list);
+
+ git_config_free(cfg);
+}
+
+void test_network_remotes__loading_a_missing_remote_returns_ENOTFOUND(void)
+{
+ cl_assert_equal_i(GIT_ENOTFOUND, git_remote_load(&_remote, _repo, "just-left-few-minutes-ago"));
+}
+
+void test_network_remotes__add(void)
+{
+ git_remote_free(_remote);
+ cl_git_pass(git_remote_add(&_remote, _repo, "addtest", "http://github.com/libgit2/libgit2"));
+ git_remote_free(_remote);
+
+ cl_git_pass(git_remote_load(&_remote, _repo, "addtest"));
+ _refspec = git_remote_fetchspec(_remote);
+ cl_assert(!strcmp(git_refspec_src(_refspec), "refs/heads/*"));
+ cl_assert(!strcmp(git_refspec_dst(_refspec), "refs/remotes/addtest/*"));
+}
diff --git a/tests-clar/notes/notes.c b/tests-clar/notes/notes.c
new file mode 100644
index 000000000..5185f25ea
--- /dev/null
+++ b/tests-clar/notes/notes.c
@@ -0,0 +1,133 @@
+#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);
+ cl_git_sandbox_cleanup();
+}
+
+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));
+}
+
+void test_notes_notes__1(void)
+{
+ git_oid oid, note_oid;
+ static git_note *note;
+ static git_blob *blob;
+
+ cl_git_pass(git_oid_fromstr(&oid, "8496071c1b46c854b31185ea97743be6a8774479"));
+
+ cl_git_pass(git_note_create(&note_oid, _repo, _sig, _sig, "refs/notes/some/namespace", &oid, "hello world\n"));
+ cl_git_pass(git_note_create(&note_oid, _repo, _sig, _sig, NULL, &oid, "hello world\n"));
+
+ cl_git_pass(git_note_read(&note, _repo, NULL, &oid));
+
+ cl_assert_equal_s(git_note_message(note), "hello world\n");
+ 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), git_blob_rawcontent(blob));
+
+ cl_git_fail(git_note_create(&note_oid, _repo, _sig, _sig, NULL, &oid, "hello world\n"));
+ cl_git_fail(git_note_create(&note_oid, _repo, _sig, _sig, "refs/notes/some/namespace", &oid, "hello world\n"));
+
+ cl_git_pass(git_note_remove(_repo, NULL, _sig, _sig, &oid));
+ cl_git_pass(git_note_remove(_repo, "refs/notes/some/namespace", _sig, _sig, &oid));
+
+ cl_git_fail(git_note_remove(_repo, NULL, _sig, _sig, &note_oid));
+ cl_git_fail(git_note_remove(_repo, "refs/notes/some/namespace", _sig, _sig, &oid));
+
+ git_note_free(note);
+ git_blob_free(blob);
+}
+
+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(git_note_data *note_data, 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, &note_data->blob_oid) == 0);
+
+ cl_git_pass(git_oid_fromstr(&expected_target_oid, list_expectations[*count].annotated_object_sha));
+ cl_assert(git_oid_cmp(&expected_target_oid, &note_data->annotated_object_oid) == 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);
+}
+
+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);
+}
diff --git a/tests-clar/notes/notesref.c b/tests-clar/notes/notesref.c
new file mode 100644
index 000000000..79ad0afee
--- /dev/null
+++ b/tests-clar/notes/notesref.c
@@ -0,0 +1,57 @@
+#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);
+ git_signature_free(_sig);
+ git_config_free(_cfg);
+
+ git_repository_free(_repo);
+ 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"));
+
+ cl_git_pass(git_note_read(&_note, _repo, NULL, &oid));
+ cl_assert(!strcmp(git_note_message(_note), "test123test\n"));
+ 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(!strcmp(git_note_message(_note), "test123test\n"));
+ cl_assert(!git_oid_cmp(git_note_oid(_note), &note_oid));
+
+ cl_git_pass(git_note_default_ref(&default_ref, _repo));
+ cl_assert(!strcmp(default_ref, "refs/notes/mydefaultnotesref"));
+
+ cl_git_pass(git_config_delete(_cfg, "core.notesRef"));
+
+ cl_git_pass(git_note_default_ref(&default_ref, _repo));
+ cl_assert(!strcmp(default_ref, GIT_NOTES_DEFAULT_REF));
+}
diff --git a/tests-clar/object/blob/filter.c b/tests-clar/object/blob/filter.c
new file mode 100644
index 000000000..0b87b2b46
--- /dev/null
+++ b/tests-clar/object/blob/filter.c
@@ -0,0 +1,125 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "blob.h"
+#include "filter.h"
+
+static git_repository *g_repo = NULL;
+#define NUM_TEST_OBJECTS 6
+static git_oid g_oids[NUM_TEST_OBJECTS];
+static const char *g_raw[NUM_TEST_OBJECTS] = {
+ "",
+ "foo\nbar\n",
+ "foo\rbar\r",
+ "foo\r\nbar\r\n",
+ "foo\nbar\rboth\r\nreversed\n\ragain\nproblems\r",
+ "123\n\000\001\002\003\004abc\255\254\253\r\n"
+};
+static int g_len[NUM_TEST_OBJECTS] = { -1, -1, -1, -1, -1, 17 };
+static git_text_stats g_stats[NUM_TEST_OBJECTS] = {
+ { 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 2, 0, 6, 0 },
+ { 0, 2, 0, 0, 6, 0 },
+ { 0, 2, 2, 2, 6, 0 },
+ { 0, 4, 4, 1, 31, 0 },
+ { 1, 1, 2, 1, 9, 5 }
+};
+static git_buf g_crlf_filtered[NUM_TEST_OBJECTS] = {
+ { "", 0, 0 },
+ { "foo\nbar\n", 0, 8 },
+ { "foo\rbar\r", 0, 8 },
+ { "foo\nbar\n", 0, 8 },
+ { "foo\nbar\rboth\nreversed\n\ragain\nproblems\r", 0, 38 },
+ { "123\n\000\001\002\003\004abc\255\254\253\n", 0, 16 }
+};
+
+void test_object_blob_filter__initialize(void)
+{
+ int i;
+
+ cl_fixture_sandbox("empty_standard_repo");
+ cl_git_pass(p_rename(
+ "empty_standard_repo/.gitted", "empty_standard_repo/.git"));
+ cl_git_pass(git_repository_open(&g_repo, "empty_standard_repo"));
+
+ for (i = 0; i < NUM_TEST_OBJECTS; i++) {
+ size_t len = (g_len[i] < 0) ? strlen(g_raw[i]) : (size_t)g_len[i];
+ g_len[i] = (int)len;
+
+ cl_git_pass(
+ git_blob_create_frombuffer(&g_oids[i], g_repo, g_raw[i], len)
+ );
+ }
+}
+
+void test_object_blob_filter__cleanup(void)
+{
+ git_repository_free(g_repo);
+ g_repo = NULL;
+ cl_fixture_cleanup("empty_standard_repo");
+}
+
+void test_object_blob_filter__unfiltered(void)
+{
+ int i;
+ git_blob *blob;
+
+ for (i = 0; i < NUM_TEST_OBJECTS; i++) {
+ cl_git_pass(git_blob_lookup(&blob, g_repo, &g_oids[i]));
+ cl_assert((size_t)g_len[i] == git_blob_rawsize(blob));
+ cl_assert(memcmp(git_blob_rawcontent(blob), g_raw[i], g_len[i]) == 0);
+ git_blob_free(blob);
+ }
+}
+
+void test_object_blob_filter__stats(void)
+{
+ int i;
+ git_blob *blob;
+ git_buf buf = GIT_BUF_INIT;
+ git_text_stats stats;
+
+ for (i = 0; i < NUM_TEST_OBJECTS; i++) {
+ cl_git_pass(git_blob_lookup(&blob, g_repo, &g_oids[i]));
+ cl_git_pass(git_blob__getbuf(&buf, blob));
+ git_text_gather_stats(&stats, &buf);
+ cl_assert(memcmp(&g_stats[i], &stats, sizeof(stats)) == 0);
+ git_blob_free(blob);
+ }
+
+ git_buf_free(&buf);
+}
+
+void test_object_blob_filter__to_odb(void)
+{
+ git_vector filters = GIT_VECTOR_INIT;
+ git_config *cfg;
+ int i;
+ git_blob *blob;
+ git_buf orig = GIT_BUF_INIT, out = GIT_BUF_INIT;
+
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_assert(cfg);
+
+ git_attr_cache_flush(g_repo);
+ cl_git_append2file("empty_standard_repo/.gitattributes", "*.txt text\n");
+
+ cl_assert(git_filters_load(
+ &filters, g_repo, "filename.txt", GIT_FILTER_TO_ODB) > 0);
+ cl_assert(filters.length == 1);
+
+ for (i = 0; i < NUM_TEST_OBJECTS; i++) {
+ cl_git_pass(git_blob_lookup(&blob, g_repo, &g_oids[i]));
+ cl_git_pass(git_blob__getbuf(&orig, blob));
+
+ cl_git_pass(git_filters_apply(&out, &orig, &filters));
+ cl_assert(git_buf_cmp(&out, &g_crlf_filtered[i]) == 0);
+
+ git_blob_free(blob);
+ }
+
+ git_filters_free(&filters);
+ git_buf_free(&orig);
+ git_buf_free(&out);
+ git_config_free(cfg);
+}
+
diff --git a/tests-clar/object/blob/write.c b/tests-clar/object/blob/write.c
new file mode 100644
index 000000000..722c7b956
--- /dev/null
+++ b/tests-clar/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_fromfile);
+}
+
+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, GIT_DIRREMOVAL_FILES_AND_DIRS));
+}
+
+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, GIT_DIRREMOVAL_FILES_AND_DIRS));
+}
diff --git a/tests-clar/object/commit/commitstagedfile.c b/tests-clar/object/commit/commitstagedfile.c
index de69b4496..a852458f4 100644
--- a/tests-clar/object/commit/commitstagedfile.c
+++ b/tests-clar/object/commit/commitstagedfile.c
@@ -83,8 +83,16 @@ void test_object_commit_commitstagedfile__generate_predictable_object_ids(void)
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
}
/*
@@ -106,7 +114,7 @@ void test_object_commit_commitstagedfile__generate_predictable_object_ids(void)
signature,
signature,
NULL,
- "Initial commit\n", // Note: the trailing linefeed is mandatory to replicate git behavior
+ "Initial commit",
tree,
0));
diff --git a/tests-clar/object/lookup.c b/tests-clar/object/lookup.c
new file mode 100644
index 000000000..7cbcc6140
--- /dev/null
+++ b/tests-clar/object/lookup.c
@@ -0,0 +1,63 @@
+#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);
+}
+
+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-clar/object/message.c b/tests-clar/object/message.c
new file mode 100644
index 000000000..cbdc80a64
--- /dev/null
+++ b/tests-clar/object/message.c
@@ -0,0 +1,171 @@
+#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);
+}
diff --git a/tests-clar/object/raw/compare.c b/tests-clar/object/raw/compare.c
index 22f958098..1c9ce4b81 100644
--- a/tests-clar/object/raw/compare.c
+++ b/tests-clar/object/raw/compare.c
@@ -87,7 +87,7 @@ void test_object_raw_compare__compare_fmt_oids(void)
/* Format produced the right result */
out[GIT_OID_HEXSZ] = '\0';
- cl_assert(strcmp(exp, out) == 0);
+ cl_assert_equal_s(exp, out);
}
void test_object_raw_compare__compare_allocfmt_oids(void)
@@ -100,7 +100,7 @@ void test_object_raw_compare__compare_allocfmt_oids(void)
out = git_oid_allocfmt(&in);
cl_assert(out);
- cl_assert(strcmp(exp, out) == 0);
+ cl_assert_equal_s(exp, out);
git__free(out);
}
@@ -120,5 +120,5 @@ void test_object_raw_compare__compare_pathfmt_oids(void)
/* Format produced the right result */
out[GIT_OID_HEXSZ + 1] = '\0';
- cl_assert(strcmp(exp2, out) == 0);
+ cl_assert_equal_s(exp2, out);
}
diff --git a/tests-clar/object/raw/convert.c b/tests-clar/object/raw/convert.c
index b9715a5da..7f310ddf0 100644
--- a/tests-clar/object/raw/convert.c
+++ b/tests-clar/object/raw/convert.c
@@ -14,24 +14,24 @@ void test_object_raw_convert__succeed_on_oid_to_string_conversion(void)
cl_git_pass(git_oid_fromstr(&in, exp));
/* NULL buffer pointer, returns static empty string */
- str = git_oid_to_string(NULL, sizeof(out), &in);
+ 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_to_string(out, 0, &in);
+ str = git_oid_tostr(out, 0, &in);
cl_assert(str && *str == '\0' && str != out);
/* NULL oid pointer, returns static empty string */
- str = git_oid_to_string(out, sizeof(out), NULL);
+ 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_to_string(out, 1, &in);
+ 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_to_string(out, i+1, &in);
+ str = git_oid_tostr(out, i+1, &in);
/* returns out containing c-string */
cl_assert(str && str == out);
/* must be '\0' terminated */
@@ -43,9 +43,9 @@ void test_object_raw_convert__succeed_on_oid_to_string_conversion(void)
}
/* returns out as hex formatted c-string */
- str = git_oid_to_string(out, sizeof(out), &in);
+ str = git_oid_tostr(out, sizeof(out), &in);
cl_assert(str && str == out && *(str+GIT_OID_HEXSZ) == '\0');
- cl_assert(strcmp(exp, out) == 0);
+ cl_assert_equal_s(exp, out);
}
void test_object_raw_convert__succeed_on_oid_to_string_conversion_big(void)
@@ -64,9 +64,9 @@ void test_object_raw_convert__succeed_on_oid_to_string_conversion_big(void)
big[GIT_OID_HEXSZ+3] = 'Z'; /* ditto */
/* returns big as hex formatted c-string */
- str = git_oid_to_string(big, sizeof(big), &in);
+ str = git_oid_tostr(big, sizeof(big), &in);
cl_assert(str && str == big && *(str+GIT_OID_HEXSZ) == '\0');
- cl_assert(strcmp(exp, big) == 0);
+ cl_assert_equal_s(exp, big);
/* check tail material is untouched */
cl_assert(str && str == big && *(str+GIT_OID_HEXSZ+1) == 'X');
diff --git a/tests-clar/object/raw/hash.c b/tests-clar/object/raw/hash.c
index 2375851bb..4b8b1b74c 100644
--- a/tests-clar/object/raw/hash.c
+++ b/tests-clar/object/raw/hash.c
@@ -6,9 +6,13 @@
#include "data.h"
-static int hash_object(git_oid *oid, git_rawobj *obj)
+static void hash_object_pass(git_oid *oid, git_rawobj *obj)
{
- return git_odb_hash(oid, obj->data, obj->len, obj->type);
+ 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";
@@ -74,28 +78,28 @@ void test_object_raw_hash__hash_junk_data(void)
/* invalid types: */
junk_obj.data = some_data;
- cl_git_fail(hash_object(&id, &junk_obj));
+ hash_object_fail(&id, &junk_obj);
junk_obj.type = GIT_OBJ__EXT1;
- cl_git_fail(hash_object(&id, &junk_obj));
+ hash_object_fail(&id, &junk_obj);
junk_obj.type = GIT_OBJ__EXT2;
- cl_git_fail(hash_object(&id, &junk_obj));
+ hash_object_fail(&id, &junk_obj);
junk_obj.type = GIT_OBJ_OFS_DELTA;
- cl_git_fail(hash_object(&id, &junk_obj));
+ hash_object_fail(&id, &junk_obj);
junk_obj.type = GIT_OBJ_REF_DELTA;
- cl_git_fail(hash_object(&id, &junk_obj));
+ 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;
- cl_git_pass(hash_object(&id, &junk_obj));
+ hash_object_pass(&id, &junk_obj);
cl_assert(git_oid_cmp(&id, &id_zero) == 0);
junk_obj.len = 1;
- cl_git_fail(hash_object(&id, &junk_obj));
+ hash_object_fail(&id, &junk_obj);
}
void test_object_raw_hash__hash_commit_object(void)
@@ -103,7 +107,7 @@ void test_object_raw_hash__hash_commit_object(void)
git_oid id1, id2;
cl_git_pass(git_oid_fromstr(&id1, commit_id));
- cl_git_pass(hash_object(&id2, &commit_obj));
+ hash_object_pass(&id2, &commit_obj);
cl_assert(git_oid_cmp(&id1, &id2) == 0);
}
@@ -112,7 +116,7 @@ void test_object_raw_hash__hash_tree_object(void)
git_oid id1, id2;
cl_git_pass(git_oid_fromstr(&id1, tree_id));
- cl_git_pass(hash_object(&id2, &tree_obj));
+ hash_object_pass(&id2, &tree_obj);
cl_assert(git_oid_cmp(&id1, &id2) == 0);
}
@@ -121,7 +125,7 @@ void test_object_raw_hash__hash_tag_object(void)
git_oid id1, id2;
cl_git_pass(git_oid_fromstr(&id1, tag_id));
- cl_git_pass(hash_object(&id2, &tag_obj));
+ hash_object_pass(&id2, &tag_obj);
cl_assert(git_oid_cmp(&id1, &id2) == 0);
}
@@ -130,7 +134,7 @@ void test_object_raw_hash__hash_zero_length_object(void)
git_oid id1, id2;
cl_git_pass(git_oid_fromstr(&id1, zero_id));
- cl_git_pass(hash_object(&id2, &zero_obj));
+ hash_object_pass(&id2, &zero_obj);
cl_assert(git_oid_cmp(&id1, &id2) == 0);
}
@@ -139,7 +143,7 @@ void test_object_raw_hash__hash_one_byte_object(void)
git_oid id1, id2;
cl_git_pass(git_oid_fromstr(&id1, one_id));
- cl_git_pass(hash_object(&id2, &one_obj));
+ hash_object_pass(&id2, &one_obj);
cl_assert(git_oid_cmp(&id1, &id2) == 0);
}
@@ -148,7 +152,7 @@ void test_object_raw_hash__hash_two_byte_object(void)
git_oid id1, id2;
cl_git_pass(git_oid_fromstr(&id1, two_id));
- cl_git_pass(hash_object(&id2, &two_obj));
+ hash_object_pass(&id2, &two_obj);
cl_assert(git_oid_cmp(&id1, &id2) == 0);
}
@@ -157,6 +161,6 @@ void test_object_raw_hash__hash_multi_byte_object(void)
git_oid id1, id2;
cl_git_pass(git_oid_fromstr(&id1, some_id));
- cl_git_pass(hash_object(&id2, &some_obj));
+ hash_object_pass(&id2, &some_obj);
cl_assert(git_oid_cmp(&id1, &id2) == 0);
}
diff --git a/tests-clar/object/raw/type2string.c b/tests-clar/object/raw/type2string.c
index 24ed1c44f..a3585487f 100644
--- a/tests-clar/object/raw/type2string.c
+++ b/tests-clar/object/raw/type2string.c
@@ -6,19 +6,19 @@
void test_object_raw_type2string__convert_type_to_string(void)
{
- cl_assert(!strcmp(git_object_type2string(GIT_OBJ_BAD), ""));
- cl_assert(!strcmp(git_object_type2string(GIT_OBJ__EXT1), ""));
- cl_assert(!strcmp(git_object_type2string(GIT_OBJ_COMMIT), "commit"));
- cl_assert(!strcmp(git_object_type2string(GIT_OBJ_TREE), "tree"));
- cl_assert(!strcmp(git_object_type2string(GIT_OBJ_BLOB), "blob"));
- cl_assert(!strcmp(git_object_type2string(GIT_OBJ_TAG), "tag"));
- cl_assert(!strcmp(git_object_type2string(GIT_OBJ__EXT2), ""));
- cl_assert(!strcmp(git_object_type2string(GIT_OBJ_OFS_DELTA), "OFS_DELTA"));
- cl_assert(!strcmp(git_object_type2string(GIT_OBJ_REF_DELTA), "REF_DELTA"));
-
- cl_assert(!strcmp(git_object_type2string(-2), ""));
- cl_assert(!strcmp(git_object_type2string(8), ""));
- cl_assert(!strcmp(git_object_type2string(1234), ""));
+ 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)
diff --git a/tests-clar/object/raw/write.c b/tests-clar/object/raw/write.c
new file mode 100644
index 000000000..1b28d0df7
--- /dev/null
+++ b/tests-clar/object/raw/write.c
@@ -0,0 +1,455 @@
+
+#include "clar_libgit2.h"
+#include "fileops.h"
+#include "odb.h"
+
+typedef struct object_data {
+ char *id; /* object id (sha1) */
+ char *dir; /* object store (fan-out) directory name */
+ char *file; /* object store filename */
+} object_data;
+
+static const char *odb_dir = "test-objects";
+
+void test_body(object_data *d, git_rawobj *o);
+
+
+
+// Helpers
+static void remove_object_files(object_data *d)
+{
+ cl_git_pass(p_unlink(d->file));
+ cl_git_pass(p_rmdir(d->dir));
+ cl_assert(errno != ENOTEMPTY);
+ cl_git_pass(p_rmdir(odb_dir) < 0);
+}
+
+static void streaming_write(git_oid *oid, git_odb *odb, git_rawobj *raw)
+{
+ git_odb_stream *stream;
+ int error;
+
+ cl_git_pass(git_odb_open_wstream(&stream, odb, raw->len, raw->type));
+ stream->write(stream, raw->data, raw->len);
+ error = stream->finalize_write(oid, stream);
+ stream->free(stream);
+ cl_git_pass(error);
+}
+
+static void check_object_files(object_data *d)
+{
+ cl_assert(git_path_exists(d->dir));
+ cl_assert(git_path_exists(d->file));
+}
+
+static void cmp_objects(git_rawobj *o1, git_rawobj *o2)
+{
+ cl_assert(o1->type == o2->type);
+ cl_assert(o1->len == o2->len);
+ if (o1->len > 0)
+ cl_assert(memcmp(o1->data, o2->data, o1->len) == 0);
+}
+
+static void make_odb_dir(void)
+{
+ cl_git_pass(p_mkdir(odb_dir, GIT_OBJECT_DIR_MODE));
+}
+
+
+// Standard test form
+void test_body(object_data *d, git_rawobj *o)
+{
+ git_odb *db;
+ git_oid id1, id2;
+ git_odb_object *obj;
+
+ 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));
+ cmp_objects(&obj->raw, o);
+
+ git_odb_object_free(obj);
+ git_odb_free(db);
+ remove_object_files(d);
+}
+
+
+void test_object_raw_write__loose_object(void)
+{
+ object_data commit = {
+ "3d7f8a6af076c8c3f20071a8935cdbe8228594d1",
+ "test-objects/3d",
+ "test-objects/3d/7f8a6af076c8c3f20071a8935cdbe8228594d1",
+ };
+
+ unsigned char commit_data[] = {
+ 0x74, 0x72, 0x65, 0x65, 0x20, 0x64, 0x66, 0x66,
+ 0x32, 0x64, 0x61, 0x39, 0x30, 0x62, 0x32, 0x35,
+ 0x34, 0x65, 0x31, 0x62, 0x65, 0x62, 0x38, 0x38,
+ 0x39, 0x64, 0x31, 0x66, 0x31, 0x66, 0x31, 0x32,
+ 0x38, 0x38, 0x62, 0x65, 0x31, 0x38, 0x30, 0x33,
+ 0x37, 0x38, 0x32, 0x64, 0x66, 0x0a, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x20, 0x41, 0x20, 0x55,
+ 0x20, 0x54, 0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x40, 0x65, 0x78,
+ 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x3e, 0x20, 0x31, 0x32, 0x32, 0x37, 0x38,
+ 0x31, 0x34, 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30,
+ 0x30, 0x30, 0x30, 0x0a, 0x63, 0x6f, 0x6d, 0x6d,
+ 0x69, 0x74, 0x74, 0x65, 0x72, 0x20, 0x43, 0x20,
+ 0x4f, 0x20, 0x4d, 0x69, 0x74, 0x74, 0x65, 0x72,
+ 0x20, 0x3c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+ 0x74, 0x65, 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d,
+ 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e,
+ 0x20, 0x31, 0x32, 0x32, 0x37, 0x38, 0x31, 0x34,
+ 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30, 0x30, 0x30,
+ 0x30, 0x0a, 0x0a, 0x41, 0x20, 0x6f, 0x6e, 0x65,
+ 0x2d, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x63, 0x6f,
+ 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x73, 0x75, 0x6d,
+ 0x6d, 0x61, 0x72, 0x79, 0x0a, 0x0a, 0x54, 0x68,
+ 0x65, 0x20, 0x62, 0x6f, 0x64, 0x79, 0x20, 0x6f,
+ 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f,
+ 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x6d, 0x65, 0x73,
+ 0x73, 0x61, 0x67, 0x65, 0x2c, 0x20, 0x63, 0x6f,
+ 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67,
+ 0x20, 0x66, 0x75, 0x72, 0x74, 0x68, 0x65, 0x72,
+ 0x20, 0x65, 0x78, 0x70, 0x6c, 0x61, 0x6e, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x6f, 0x66, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x70, 0x75, 0x72, 0x70,
+ 0x6f, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67,
+ 0x65, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x72, 0x6f,
+ 0x64, 0x75, 0x63, 0x65, 0x64, 0x20, 0x62, 0x79,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d,
+ 0x6d, 0x69, 0x74, 0x2e, 0x0a, 0x0a, 0x53, 0x69,
+ 0x67, 0x6e, 0x65, 0x64, 0x2d, 0x6f, 0x66, 0x2d,
+ 0x62, 0x79, 0x3a, 0x20, 0x41, 0x20, 0x55, 0x20,
+ 0x54, 0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x40, 0x65, 0x78, 0x61,
+ 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x3e, 0x0a,
+ };
+
+ git_rawobj commit_obj = {
+ commit_data,
+ sizeof(commit_data),
+ GIT_OBJ_COMMIT
+ };
+
+ test_body(&commit, &commit_obj);
+}
+
+void test_object_raw_write__loose_tree(void)
+{
+ static object_data tree = {
+ "dff2da90b254e1beb889d1f1f1288be1803782df",
+ "test-objects/df",
+ "test-objects/df/f2da90b254e1beb889d1f1f1288be1803782df",
+ };
+
+ static unsigned char tree_data[] = {
+ 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x6f,
+ 0x6e, 0x65, 0x00, 0x8b, 0x13, 0x78, 0x91, 0x79,
+ 0x1f, 0xe9, 0x69, 0x27, 0xad, 0x78, 0xe6, 0x4b,
+ 0x0a, 0xad, 0x7b, 0xde, 0xd0, 0x8b, 0xdc, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x73, 0x6f,
+ 0x6d, 0x65, 0x00, 0xfd, 0x84, 0x30, 0xbc, 0x86,
+ 0x4c, 0xfc, 0xd5, 0xf1, 0x0e, 0x55, 0x90, 0xf8,
+ 0xa4, 0x47, 0xe0, 0x1b, 0x94, 0x2b, 0xfe, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x77,
+ 0x6f, 0x00, 0x78, 0x98, 0x19, 0x22, 0x61, 0x3b,
+ 0x2a, 0xfb, 0x60, 0x25, 0x04, 0x2f, 0xf6, 0xbd,
+ 0x87, 0x8a, 0xc1, 0x99, 0x4e, 0x85, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x7a, 0x65, 0x72,
+ 0x6f, 0x00, 0xe6, 0x9d, 0xe2, 0x9b, 0xb2, 0xd1,
+ 0xd6, 0x43, 0x4b, 0x8b, 0x29, 0xae, 0x77, 0x5a,
+ 0xd8, 0xc2, 0xe4, 0x8c, 0x53, 0x91,
+ };
+
+ static git_rawobj tree_obj = {
+ tree_data,
+ sizeof(tree_data),
+ GIT_OBJ_TREE
+ };
+
+ test_body(&tree, &tree_obj);
+}
+
+void test_object_raw_write__loose_tag(void)
+{
+ static object_data tag = {
+ "09d373e1dfdc16b129ceec6dd649739911541e05",
+ "test-objects/09",
+ "test-objects/09/d373e1dfdc16b129ceec6dd649739911541e05",
+ };
+
+ static unsigned char tag_data[] = {
+ 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x33,
+ 0x64, 0x37, 0x66, 0x38, 0x61, 0x36, 0x61, 0x66,
+ 0x30, 0x37, 0x36, 0x63, 0x38, 0x63, 0x33, 0x66,
+ 0x32, 0x30, 0x30, 0x37, 0x31, 0x61, 0x38, 0x39,
+ 0x33, 0x35, 0x63, 0x64, 0x62, 0x65, 0x38, 0x32,
+ 0x32, 0x38, 0x35, 0x39, 0x34, 0x64, 0x31, 0x0a,
+ 0x74, 0x79, 0x70, 0x65, 0x20, 0x63, 0x6f, 0x6d,
+ 0x6d, 0x69, 0x74, 0x0a, 0x74, 0x61, 0x67, 0x20,
+ 0x76, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x0a, 0x74,
+ 0x61, 0x67, 0x67, 0x65, 0x72, 0x20, 0x43, 0x20,
+ 0x4f, 0x20, 0x4d, 0x69, 0x74, 0x74, 0x65, 0x72,
+ 0x20, 0x3c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+ 0x74, 0x65, 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d,
+ 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e,
+ 0x20, 0x31, 0x32, 0x32, 0x37, 0x38, 0x31, 0x34,
+ 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30, 0x30, 0x30,
+ 0x30, 0x0a, 0x0a, 0x54, 0x68, 0x69, 0x73, 0x20,
+ 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74,
+ 0x61, 0x67, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63,
+ 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x72, 0x65,
+ 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20, 0x76, 0x30,
+ 0x2e, 0x30, 0x2e, 0x31, 0x0a,
+ };
+
+ static git_rawobj tag_obj = {
+ tag_data,
+ sizeof(tag_data),
+ GIT_OBJ_TAG
+ };
+
+
+ test_body(&tag, &tag_obj);
+}
+
+void test_object_raw_write__zero_length(void)
+{
+ static object_data zero = {
+ "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
+ "test-objects/e6",
+ "test-objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391",
+ };
+
+ static unsigned char zero_data[] = {
+ 0x00 /* dummy data */
+ };
+
+ static git_rawobj zero_obj = {
+ zero_data,
+ 0,
+ GIT_OBJ_BLOB
+ };
+
+ test_body(&zero, &zero_obj);
+}
+
+void test_object_raw_write__one_byte(void)
+{
+ static object_data one = {
+ "8b137891791fe96927ad78e64b0aad7bded08bdc",
+ "test-objects/8b",
+ "test-objects/8b/137891791fe96927ad78e64b0aad7bded08bdc",
+ };
+
+ static unsigned char one_data[] = {
+ 0x0a,
+ };
+
+ static git_rawobj one_obj = {
+ one_data,
+ sizeof(one_data),
+ GIT_OBJ_BLOB
+ };
+
+ test_body(&one, &one_obj);
+}
+
+void test_object_raw_write__two_byte(void)
+{
+ static object_data two = {
+ "78981922613b2afb6025042ff6bd878ac1994e85",
+ "test-objects/78",
+ "test-objects/78/981922613b2afb6025042ff6bd878ac1994e85",
+ };
+
+ static unsigned char two_data[] = {
+ 0x61, 0x0a,
+ };
+
+ static git_rawobj two_obj = {
+ two_data,
+ sizeof(two_data),
+ GIT_OBJ_BLOB
+ };
+
+ test_body(&two, &two_obj);
+}
+
+void test_object_raw_write__several_bytes(void)
+{
+ static object_data some = {
+ "fd8430bc864cfcd5f10e5590f8a447e01b942bfe",
+ "test-objects/fd",
+ "test-objects/fd/8430bc864cfcd5f10e5590f8a447e01b942bfe",
+ };
+
+ static unsigned char some_data[] = {
+ 0x2f, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x54, 0x68,
+ 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20,
+ 0x69, 0x73, 0x20, 0x66, 0x72, 0x65, 0x65, 0x20,
+ 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
+ 0x3b, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x63, 0x61,
+ 0x6e, 0x20, 0x72, 0x65, 0x64, 0x69, 0x73, 0x74,
+ 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x69,
+ 0x74, 0x20, 0x61, 0x6e, 0x64, 0x2f, 0x6f, 0x72,
+ 0x20, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0a,
+ 0x20, 0x2a, 0x20, 0x69, 0x74, 0x20, 0x75, 0x6e,
+ 0x64, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x74, 0x65, 0x72, 0x6d, 0x73, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x47, 0x4e, 0x55,
+ 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
+ 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x2c,
+ 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+ 0x20, 0x32, 0x2c, 0x0a, 0x20, 0x2a, 0x20, 0x61,
+ 0x73, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73,
+ 0x68, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x46, 0x72, 0x65, 0x65, 0x20,
+ 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
+ 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x2a, 0x0a,
+ 0x20, 0x2a, 0x20, 0x49, 0x6e, 0x20, 0x61, 0x64,
+ 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74,
+ 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x65,
+ 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e,
+ 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x47, 0x4e, 0x55, 0x20, 0x47, 0x65, 0x6e,
+ 0x65, 0x72, 0x61, 0x6c, 0x20, 0x50, 0x75, 0x62,
+ 0x6c, 0x69, 0x63, 0x20, 0x4c, 0x69, 0x63, 0x65,
+ 0x6e, 0x73, 0x65, 0x2c, 0x0a, 0x20, 0x2a, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x61, 0x75, 0x74, 0x68,
+ 0x6f, 0x72, 0x73, 0x20, 0x67, 0x69, 0x76, 0x65,
+ 0x20, 0x79, 0x6f, 0x75, 0x20, 0x75, 0x6e, 0x6c,
+ 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x20, 0x70,
+ 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f,
+ 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x6c, 0x69, 0x6e,
+ 0x6b, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f,
+ 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x64, 0x0a, 0x20,
+ 0x2a, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
+ 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69,
+ 0x73, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x69,
+ 0x6e, 0x74, 0x6f, 0x20, 0x63, 0x6f, 0x6d, 0x62,
+ 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6f, 0x74,
+ 0x68, 0x65, 0x72, 0x20, 0x70, 0x72, 0x6f, 0x67,
+ 0x72, 0x61, 0x6d, 0x73, 0x2c, 0x0a, 0x20, 0x2a,
+ 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x6f, 0x20,
+ 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75,
+ 0x74, 0x65, 0x20, 0x74, 0x68, 0x6f, 0x73, 0x65,
+ 0x20, 0x63, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x77, 0x69,
+ 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x61, 0x6e,
+ 0x79, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x2a,
+ 0x20, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x20,
+ 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c,
+ 0x65, 0x2e, 0x20, 0x20, 0x28, 0x54, 0x68, 0x65,
+ 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
+ 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x0a,
+ 0x20, 0x2a, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72,
+ 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20,
+ 0x64, 0x6f, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x79,
+ 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x74, 0x68, 0x65,
+ 0x72, 0x20, 0x72, 0x65, 0x73, 0x70, 0x65, 0x63,
+ 0x74, 0x73, 0x3b, 0x20, 0x66, 0x6f, 0x72, 0x20,
+ 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c,
+ 0x20, 0x74, 0x68, 0x65, 0x79, 0x20, 0x63, 0x6f,
+ 0x76, 0x65, 0x72, 0x0a, 0x20, 0x2a, 0x20, 0x6d,
+ 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x2c,
+ 0x20, 0x61, 0x6e, 0x64, 0x20, 0x64, 0x69, 0x73,
+ 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x6e,
+ 0x6f, 0x74, 0x20, 0x6c, 0x69, 0x6e, 0x6b, 0x65,
+ 0x64, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x0a, 0x20,
+ 0x2a, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x62,
+ 0x69, 0x6e, 0x65, 0x64, 0x20, 0x65, 0x78, 0x65,
+ 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e,
+ 0x29, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20,
+ 0x54, 0x68, 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c,
+ 0x65, 0x20, 0x69, 0x73, 0x20, 0x64, 0x69, 0x73,
+ 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x64,
+ 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x68, 0x6f, 0x70, 0x65, 0x20, 0x74, 0x68, 0x61,
+ 0x74, 0x20, 0x69, 0x74, 0x20, 0x77, 0x69, 0x6c,
+ 0x6c, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65,
+ 0x66, 0x75, 0x6c, 0x2c, 0x20, 0x62, 0x75, 0x74,
+ 0x0a, 0x20, 0x2a, 0x20, 0x57, 0x49, 0x54, 0x48,
+ 0x4f, 0x55, 0x54, 0x20, 0x41, 0x4e, 0x59, 0x20,
+ 0x57, 0x41, 0x52, 0x52, 0x41, 0x4e, 0x54, 0x59,
+ 0x3b, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75,
+ 0x74, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x69,
+ 0x65, 0x64, 0x20, 0x77, 0x61, 0x72, 0x72, 0x61,
+ 0x6e, 0x74, 0x79, 0x20, 0x6f, 0x66, 0x0a, 0x20,
+ 0x2a, 0x20, 0x4d, 0x45, 0x52, 0x43, 0x48, 0x41,
+ 0x4e, 0x54, 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54,
+ 0x59, 0x20, 0x6f, 0x72, 0x20, 0x46, 0x49, 0x54,
+ 0x4e, 0x45, 0x53, 0x53, 0x20, 0x46, 0x4f, 0x52,
+ 0x20, 0x41, 0x20, 0x50, 0x41, 0x52, 0x54, 0x49,
+ 0x43, 0x55, 0x4c, 0x41, 0x52, 0x20, 0x50, 0x55,
+ 0x52, 0x50, 0x4f, 0x53, 0x45, 0x2e, 0x20, 0x20,
+ 0x53, 0x65, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x47, 0x4e, 0x55, 0x0a, 0x20, 0x2a, 0x20, 0x47,
+ 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x20, 0x50,
+ 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x4c, 0x69,
+ 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x66, 0x6f,
+ 0x72, 0x20, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x64,
+ 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x0a,
+ 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x59, 0x6f,
+ 0x75, 0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64,
+ 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x72, 0x65,
+ 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x20, 0x61,
+ 0x20, 0x63, 0x6f, 0x70, 0x79, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x47, 0x4e, 0x55,
+ 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
+ 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x0a,
+ 0x20, 0x2a, 0x20, 0x61, 0x6c, 0x6f, 0x6e, 0x67,
+ 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68,
+ 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72,
+ 0x61, 0x6d, 0x3b, 0x20, 0x73, 0x65, 0x65, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x65,
+ 0x20, 0x43, 0x4f, 0x50, 0x59, 0x49, 0x4e, 0x47,
+ 0x2e, 0x20, 0x20, 0x49, 0x66, 0x20, 0x6e, 0x6f,
+ 0x74, 0x2c, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65,
+ 0x20, 0x74, 0x6f, 0x0a, 0x20, 0x2a, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x46, 0x72, 0x65, 0x65, 0x20,
+ 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
+ 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x35, 0x31, 0x20,
+ 0x46, 0x72, 0x61, 0x6e, 0x6b, 0x6c, 0x69, 0x6e,
+ 0x20, 0x53, 0x74, 0x72, 0x65, 0x65, 0x74, 0x2c,
+ 0x20, 0x46, 0x69, 0x66, 0x74, 0x68, 0x20, 0x46,
+ 0x6c, 0x6f, 0x6f, 0x72, 0x2c, 0x0a, 0x20, 0x2a,
+ 0x20, 0x42, 0x6f, 0x73, 0x74, 0x6f, 0x6e, 0x2c,
+ 0x20, 0x4d, 0x41, 0x20, 0x30, 0x32, 0x31, 0x31,
+ 0x30, 0x2d, 0x31, 0x33, 0x30, 0x31, 0x2c, 0x20,
+ 0x55, 0x53, 0x41, 0x2e, 0x0a, 0x20, 0x2a, 0x2f,
+ 0x0a,
+ };
+
+ static git_rawobj some_obj = {
+ some_data,
+ sizeof(some_data),
+ GIT_OBJ_BLOB
+ };
+
+ test_body(&some, &some_obj);
+}
diff --git a/tests-clar/object/tag/peel.c b/tests-clar/object/tag/peel.c
new file mode 100644
index 000000000..97c5a7dd3
--- /dev/null
+++ b/tests-clar/object/tag/peel.c
@@ -0,0 +1,56 @@
+#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);
+ git_object_free(target);
+ git_repository_free(repo);
+
+ 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-clar/object/tag/read.c b/tests-clar/object/tag/read.c
new file mode 100644
index 000000000..6a0ad8a23
--- /dev/null
+++ b/tests-clar/object/tag/read.c
@@ -0,0 +1,130 @@
+#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 git_repository *g_repo;
+
+
+// Helpers
+static void ensure_tag_pattern_match(git_repository *repo,
+ const char *pattern,
+ const size_t expected_matches)
+{
+ git_strarray tag_list;
+ int error = 0;
+
+ if ((error = git_tag_list_match(&tag_list, pattern, repo)) < 0)
+ goto exit;
+
+ if (tag_list.count != expected_matches)
+ error = GIT_ERROR;
+
+exit:
+ git_strarray_free(&tag_list);
+ cl_git_pass(error);
+}
+
+
+// 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_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__list(void)
+{
+ // list all tag names from the repository
+ git_strarray tag_list;
+
+ cl_git_pass(git_tag_list(&tag_list, g_repo));
+
+ cl_assert(tag_list.count == 3);
+
+ git_strarray_free(&tag_list);
+}
+
+void test_object_tag_read__list_pattern(void)
+{
+ // list all tag names from the repository matching a specified pattern
+ ensure_tag_pattern_match(g_repo, "", 3);
+ ensure_tag_pattern_match(g_repo, "*", 3);
+ ensure_tag_pattern_match(g_repo, "t*", 1);
+ ensure_tag_pattern_match(g_repo, "*b", 2);
+ ensure_tag_pattern_match(g_repo, "e", 0);
+ ensure_tag_pattern_match(g_repo, "e90810b", 1);
+ ensure_tag_pattern_match(g_repo, "e90810[ab]", 1);
+}
+
+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);
+}
diff --git a/tests-clar/object/tag/write.c b/tests-clar/object/tag/write.c
new file mode 100644
index 000000000..cb196b64e
--- /dev/null
+++ b/tests-clar/object/tag/write.c
@@ -0,0 +1,192 @@
+#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_oid(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_oid(ref_tag), &tag_id) == 0);
+ cl_git_pass(git_reference_delete(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_git_fail(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_oid(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_oid(ref_tag), &tag_id) == 0);
+ cl_assert(git_oid_cmp(git_reference_oid(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_oid(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_git_fail(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);
+}
diff --git a/tests-clar/object/tree/diff.c b/tests-clar/object/tree/diff.c
deleted file mode 100644
index d481b6f2b..000000000
--- a/tests-clar/object/tree/diff.c
+++ /dev/null
@@ -1,168 +0,0 @@
-#include "clar_libgit2.h"
-#include "tree.h"
-#include "repository.h"
-
-static git_repository *repo;
-static git_index *theindex;
-static git_tree *atree, *btree;
-static git_oid aoid, boid;
-
-static void diff_cmp(const git_tree_diff_data *a, const git_tree_diff_data *b)
-{
- cl_assert(a->old_attr - b->old_attr == 0);
-
- cl_assert(a->new_attr - b->new_attr == 0);
-
- cl_assert(git_oid_cmp(&a->old_oid, &b->old_oid) == 0);
- cl_assert(git_oid_cmp(&a->new_oid, &b->new_oid) == 0);
-
- cl_assert(a->status - b->status == 0);
-
- cl_assert(strcmp(a->path, b->path) == 0);
-}
-
-static int diff_cb(const git_tree_diff_data *diff, void *data)
-{
- diff_cmp(diff, data);
- return GIT_SUCCESS;
-}
-
-static void test_diff(git_tree *a, git_tree *b, git_tree_diff_cb cb, void *data)
-{
- cl_must_pass(git_tree_diff(a, b, cb, data));
-
- cl_git_pass(git_index_read_tree(theindex, b));
- cl_git_pass(git_tree_diff_index_recursive(a, theindex, cb, data));
-}
-
-void test_object_tree_diff__initialize(void)
-{
- cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
- cl_git_pass(git_repository_index(&theindex, repo));
-}
-
-void test_object_tree_diff__cleanup(void)
-{
- git_tree_free(atree);
- git_tree_free(btree);
- git_index_free(theindex);
- git_repository_free(repo);
-}
-
-void test_object_tree_diff__addition(void)
-{
- char *astr = "181037049a54a1eb5fab404658a3a250b44335d7";
- char *bstr = "f60079018b664e4e79329a7ef9559c8d9e0378d1";
- git_tree_diff_data expect;
-
- memset(&expect, 0x0, sizeof(git_tree_diff_data));
- expect.old_attr = 0;
- expect.new_attr = 0100644;
- git_oid_fromstr(&expect.new_oid, "fa49b077972391ad58037050f2a75f74e3671e92");
- expect.status = GIT_STATUS_ADDED;
- expect.path = "new.txt";
-
- cl_must_pass(git_oid_fromstr(&aoid, astr));
- cl_must_pass(git_oid_fromstr(&boid, bstr));
-
- cl_must_pass(git_tree_lookup(&atree, repo, &aoid));
- cl_must_pass(git_tree_lookup(&btree, repo, &boid));
-
- test_diff(atree, btree, diff_cb, &expect);
-}
-
-void test_object_tree_diff__deletion(void)
-{
- char *astr = "f60079018b664e4e79329a7ef9559c8d9e0378d1";
- char *bstr = "181037049a54a1eb5fab404658a3a250b44335d7";
- git_tree_diff_data expect;
-
- memset(&expect, 0x0, sizeof(git_tree_diff_data));
- expect.old_attr = 0100644;
- expect.new_attr = 0;
- git_oid_fromstr(&expect.old_oid, "fa49b077972391ad58037050f2a75f74e3671e92");
- expect.status = GIT_STATUS_DELETED;
- expect.path = "new.txt";
- cl_must_pass(git_oid_fromstr(&aoid, astr));
- cl_must_pass(git_oid_fromstr(&boid, bstr));
-
- cl_must_pass(git_tree_lookup(&atree, repo, &aoid));
- cl_must_pass(git_tree_lookup(&btree, repo, &boid));
-
- test_diff(atree, btree, diff_cb, &expect);
-}
-
-void test_object_tree_diff__modification(void)
-{
- char *astr = "1810dff58d8a660512d4832e740f692884338ccd";
- char *bstr = "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162";
- git_tree_diff_data expect;
-
- expect.old_attr = 0100644;
- expect.new_attr = 0100644;
- git_oid_fromstr(&expect.old_oid, "45b983be36b73c0788dc9cbcb76cbb80fc7bb057");
- git_oid_fromstr(&expect.new_oid, "3697d64be941a53d4ae8f6a271e4e3fa56b022cc");
- expect.status = GIT_STATUS_MODIFIED;
- expect.path = "branch_file.txt";
-
- cl_must_pass(git_oid_fromstr(&aoid, astr));
- cl_must_pass(git_oid_fromstr(&boid, bstr));
-
- cl_must_pass(git_tree_lookup(&atree, repo, &aoid));
- cl_must_pass(git_tree_lookup(&btree, repo, &boid));
-
- test_diff(atree, btree, diff_cb, &expect);
-}
-
-struct diff_more_data {
- git_tree_diff_data expect[3];
- int expect_idx;
-};
-
-static int diff_more_cb(const git_tree_diff_data *diff, void *data)
-{
- struct diff_more_data *more_data = data;
- diff_cmp(diff, &more_data->expect[more_data->expect_idx]);
-
- more_data->expect_idx = (more_data->expect_idx + 1) % ARRAY_SIZE(more_data->expect);
-
- return GIT_SUCCESS;
-}
-
-void test_object_tree_diff__more(void)
-{
- char *astr = "814889a078c031f61ed08ab5fa863aea9314344d";
- char *bstr = "75057dd4114e74cca1d750d0aee1647c903cb60a";
- struct diff_more_data more_data;
- git_tree_diff_data *expect = more_data.expect;
-
- memset(&more_data, 0x0, sizeof(struct diff_more_data));
- /* M README */
- expect[0].old_attr = 0100644;
- expect[0].new_attr = 0100644;
- git_oid_fromstr(&expect[0].old_oid, "a8233120f6ad708f843d861ce2b7228ec4e3dec6");
- git_oid_fromstr(&expect[0].new_oid, "1385f264afb75a56a5bec74243be9b367ba4ca08");
- expect[0].status = GIT_STATUS_MODIFIED;
- expect[0].path = "README";
- /* A branch_file.txt */
- expect[1].old_attr = 0;
- expect[1].new_attr = 0100644;
- git_oid_fromstr(&expect[1].new_oid, "45b983be36b73c0788dc9cbcb76cbb80fc7bb057");
- expect[1].status = GIT_STATUS_ADDED;
- expect[1].path = "branch_file.txt";
- /* M new.txt */
- expect[2].old_attr = 0100644;
- expect[2].new_attr = 0100644;
- git_oid_fromstr(&expect[2].old_oid, "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd");
- git_oid_fromstr(&expect[2].new_oid, "fa49b077972391ad58037050f2a75f74e3671e92");
- expect[2].status = GIT_STATUS_MODIFIED;
- expect[2].path = "new.txt";
-
- cl_must_pass(git_oid_fromstr(&aoid, astr));
- cl_must_pass(git_oid_fromstr(&boid, bstr));
-
- cl_must_pass(git_tree_lookup(&atree, repo, &aoid));
- cl_must_pass(git_tree_lookup(&btree, repo, &boid));
-
- test_diff(atree, btree, diff_more_cb, &more_data);
-}
diff --git a/tests-clar/object/tree/frompath.c b/tests-clar/object/tree/frompath.c
index 3857d42a8..06c69ac08 100644
--- a/tests-clar/object/tree/frompath.c
+++ b/tests-clar/object/tree/frompath.c
@@ -24,38 +24,44 @@ void test_object_tree_frompath__cleanup(void)
cl_fixture_cleanup("testrepo.git");
}
-static void assert_tree_from_path(git_tree *root, const char *path, git_error expected_result, const char *expected_raw_oid)
+static void assert_tree_from_path(git_tree *root, const char *path, int expected_result, const char *expected_raw_oid)
{
git_tree *containing_tree = NULL;
cl_assert(git_tree_get_subtree(&containing_tree, root, path) == expected_result);
- if (containing_tree == NULL && expected_result != GIT_SUCCESS)
+ if (containing_tree == NULL && expected_result != 0)
return;
- cl_assert(containing_tree != NULL && expected_result == GIT_SUCCESS);
+ cl_assert(containing_tree != NULL && expected_result == 0);
- cl_assert(git_oid_streq(git_object_id((const git_object *)containing_tree), expected_raw_oid) == GIT_SUCCESS);
+ cl_git_pass(git_oid_streq(git_object_id((const git_object *)containing_tree), expected_raw_oid));
git_tree_free(containing_tree);
}
+static void assert_tree_from_path_klass(git_tree *root, const char *path, int expected_result, const char *expected_raw_oid)
+{
+ assert_tree_from_path(root, path, GIT_ERROR, expected_raw_oid);
+ cl_assert(giterr_last()->klass == expected_result);
+}
+
void test_object_tree_frompath__retrieve_tree_from_path_to_treeentry(void)
{
/* Will return self if given a one path segment... */
- assert_tree_from_path(tree, "README", GIT_SUCCESS, tree_with_subtrees_oid);
+ assert_tree_from_path(tree, "README", 0, tree_with_subtrees_oid);
/* ...even one that lead to a non existent tree entry. */
- assert_tree_from_path(tree, "i-do-not-exist.txt", GIT_SUCCESS, tree_with_subtrees_oid);
+ assert_tree_from_path(tree, "i-do-not-exist.txt", 0, tree_with_subtrees_oid);
/* Will return fgh tree oid given this following path... */
- assert_tree_from_path(tree, "ab/de/fgh/1.txt", GIT_SUCCESS, "3259a6bd5b57fb9c1281bb7ed3167b50f224cb54");
+ assert_tree_from_path(tree, "ab/de/fgh/1.txt", 0, "3259a6bd5b57fb9c1281bb7ed3167b50f224cb54");
/* ... and ab tree oid given this one. */
- assert_tree_from_path(tree, "ab/de", GIT_SUCCESS, "f1425cef211cc08caa31e7b545ffb232acb098c3");
+ assert_tree_from_path(tree, "ab/de", 0, "f1425cef211cc08caa31e7b545ffb232acb098c3");
/* Will succeed if given a valid path which leads to a tree entry which doesn't exist */
- assert_tree_from_path(tree, "ab/de/fgh/i-do-not-exist.txt", GIT_SUCCESS, "3259a6bd5b57fb9c1281bb7ed3167b50f224cb54");
+ assert_tree_from_path(tree, "ab/de/fgh/i-do-not-exist.txt", 0, "3259a6bd5b57fb9c1281bb7ed3167b50f224cb54");
}
void test_object_tree_frompath__fail_when_processing_an_unknown_tree_segment(void)
@@ -66,10 +72,10 @@ void test_object_tree_frompath__fail_when_processing_an_unknown_tree_segment(voi
void test_object_tree_frompath__fail_when_processing_an_invalid_path(void)
{
- assert_tree_from_path(tree, "/", GIT_EINVALIDPATH, NULL);
- assert_tree_from_path(tree, "/ab", GIT_EINVALIDPATH, NULL);
- assert_tree_from_path(tree, "/ab/de", GIT_EINVALIDPATH, NULL);
- assert_tree_from_path(tree, "ab/", GIT_EINVALIDPATH, NULL);
- assert_tree_from_path(tree, "ab//de", GIT_EINVALIDPATH, NULL);
- assert_tree_from_path(tree, "ab/de/", GIT_EINVALIDPATH, NULL);
+ assert_tree_from_path_klass(tree, "/", GITERR_INVALID, NULL);
+ assert_tree_from_path_klass(tree, "/ab", GITERR_INVALID, NULL);
+ assert_tree_from_path_klass(tree, "/ab/de", GITERR_INVALID, NULL);
+ assert_tree_from_path_klass(tree, "ab/", GITERR_INVALID, NULL);
+ assert_tree_from_path_klass(tree, "ab//de", GITERR_INVALID, NULL);
+ assert_tree_from_path_klass(tree, "ab/de/", GITERR_INVALID, NULL);
}
diff --git a/tests-clar/object/tree/read.c b/tests-clar/object/tree/read.c
new file mode 100644
index 000000000..59a809bf1
--- /dev/null
+++ b/tests-clar/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-clar/object/tree/write.c b/tests-clar/object/tree/write.c
new file mode 100644
index 000000000..3911f6f0e
--- /dev/null
+++ b/tests-clar/object/tree/write.c
@@ -0,0 +1,84 @@
+#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, 0100644));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, "/", &bid, 0100644));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, "folder/new.txt", &bid, 0100644));
+
+ cl_git_pass(git_treebuilder_insert(NULL,builder,"new.txt",&bid,0100644));
+ 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,0100644));
+ 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,040000));
+ 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);
+}
diff --git a/tests-clar/odb/mixed.c b/tests-clar/odb/mixed.c
new file mode 100644
index 000000000..0bd23e157
--- /dev/null
+++ b/tests-clar/odb/mixed.c
@@ -0,0 +1,24 @@
+#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);
+}
+
+void test_odb_mixed__dup_oid(void) {
+ const char hex[] = "ce013625030ba8dba906f756967f9e9ca394464a";
+ git_oid oid;
+ git_odb_object *obj;
+ cl_git_pass(git_oid_fromstr(&oid, hex));
+ cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, GIT_OID_HEXSZ));
+ git_odb_object_free(obj);
+}
+
diff --git a/tests-clar/refs/branches/create.c b/tests-clar/refs/branches/create.c
new file mode 100644
index 000000000..ad7e1fd2c
--- /dev/null
+++ b/tests-clar/refs/branches/create.c
@@ -0,0 +1,113 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+#include "branch.h"
+
+static git_repository *repo;
+static git_oid branch_target_oid;
+static git_object *target;
+
+void test_refs_branches_create__initialize(void)
+{
+ cl_fixture_sandbox("testrepo.git");
+ cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+}
+
+void test_refs_branches_create__cleanup(void)
+{
+ git_object_free(target);
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("testrepo.git");
+}
+
+static 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));
+}
+
+static void retrieve_known_commit(git_object **object, git_repository *repo)
+{
+ retrieve_target_from_oid(object, 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_target_oid, repo, NEW_BRANCH_NAME, target, 0));
+ cl_git_pass(git_oid_cmp(&branch_target_oid, git_object_id(target)));
+}
+
+void test_refs_branches_create__creating_a_local_branch_triggers_the_creation_of_a_new_direct_reference(void)
+{
+ git_reference *branch;
+
+ retrieve_known_commit(&target, repo);
+
+ cl_git_fail(git_reference_lookup(&branch, repo, GIT_REFS_HEADS_DIR NEW_BRANCH_NAME));
+
+ cl_git_pass(git_branch_create(&branch_target_oid, repo, NEW_BRANCH_NAME, target, 0));
+
+ cl_git_pass(git_reference_lookup(&branch, repo, GIT_REFS_HEADS_DIR NEW_BRANCH_NAME));
+ cl_assert(git_reference_type(branch) == GIT_REF_OID);
+
+ git_reference_free(branch);
+}
+
+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_git_fail(git_branch_create(&branch_target_oid, 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_target_oid, repo, "br2", target, 1));
+ cl_git_pass(git_oid_cmp(&branch_target_oid, git_object_id(target)));
+}
+
+void test_refs_branches_create__can_not_create_a_branch_pointing_at_an_object_unknown_from_the_repository(void)
+{
+ git_repository *repo2;
+
+ /* Open another instance of the same repository */
+ cl_git_pass(git_repository_open(&repo2, cl_fixture("testrepo.git")));
+
+ /* Retrieve a commit object from this different repository */
+ retrieve_known_commit(&target, repo2);
+
+ cl_git_fail(git_branch_create(&branch_target_oid, repo, NEW_BRANCH_NAME, target, 0));
+
+ git_repository_free(repo2);
+}
+
+void test_refs_branches_create__creating_a_branch_targeting_a_tag_dereferences_it_to_its_commit(void)
+{
+ /* b25fa35 is a tag, pointing to another tag which points to a commit */
+ retrieve_target_from_oid(&target, repo, "b25fa35b38051e4ae45d4222e795f9df2e43f1d1");
+
+ cl_git_pass(git_branch_create(&branch_target_oid, repo, NEW_BRANCH_NAME, target, 0));
+ cl_git_pass(git_oid_streq(&branch_target_oid, "e90810b8df3e80c413d903f631643c716887138d"));
+}
+
+void test_refs_branches_create__can_not_create_a_branch_pointing_to_a_non_commit_object(void)
+{
+ /* 53fc32d is the tree of commit e90810b */
+ retrieve_target_from_oid(&target, repo, "53fc32d17276939fc79ed05badaef2db09990016");
+
+ cl_git_fail(git_branch_create(&branch_target_oid, repo, NEW_BRANCH_NAME, target, 0));
+ git_object_free(target);
+
+ /* 521d87c is an annotated tag pointing to a blob */
+ retrieve_target_from_oid(&target, repo, "521d87c1ec3aef9824daf6d96cc0ae3710766d91");
+
+ cl_git_fail(git_branch_create(&branch_target_oid, repo, NEW_BRANCH_NAME, target, 0));
+}
diff --git a/tests-clar/refs/branches/delete.c b/tests-clar/refs/branches/delete.c
new file mode 100644
index 000000000..03d3c56d7
--- /dev/null
+++ b/tests-clar/refs/branches/delete.c
@@ -0,0 +1,91 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+#include "branch.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_oid(&fake_remote, repo, "refs/remotes/nulltoken/master", &id, 0));
+}
+
+void test_refs_branches_delete__cleanup(void)
+{
+ git_reference_free(fake_remote);
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("testrepo.git");
+}
+
+void test_refs_branches_delete__can_not_delete_a_non_existing_branch(void)
+{
+ cl_git_fail(git_branch_delete(repo, "i-am-not-a-local-branch", GIT_BRANCH_LOCAL));
+ cl_git_fail(git_branch_delete(repo, "neither/a-remote-one", GIT_BRANCH_REMOTE));
+}
+
+void test_refs_branches_delete__can_not_delete_a_branch_pointed_at_by_HEAD(void)
+{
+ git_reference *head;
+
+ /* Ensure HEAD targets the local master branch */
+ cl_git_pass(git_reference_lookup(&head, repo, GIT_HEAD_FILE));
+ cl_assert(strcmp("refs/heads/master", git_reference_target(head)) == 0);
+ git_reference_free(head);
+
+ cl_git_fail(git_branch_delete(repo, "master", GIT_BRANCH_LOCAL));
+}
+
+void test_refs_branches_delete__can_not_delete_a_branch_if_HEAD_is_missing(void)
+{
+ git_reference *head;
+
+ cl_git_pass(git_reference_lookup(&head, repo, GIT_HEAD_FILE));
+ git_reference_delete(head);
+
+ cl_git_fail(git_branch_delete(repo, "br2", GIT_BRANCH_LOCAL));
+}
+
+void test_refs_branches_delete__can_delete_a_branch_pointed_at_by_detached_HEAD(void)
+{
+ git_reference *master, *head;
+
+ /* Detach HEAD and make it target the commit that "master" points to */
+ cl_git_pass(git_reference_lookup(&master, repo, "refs/heads/master"));
+ cl_git_pass(git_reference_create_oid(&head, repo, "HEAD", git_reference_oid(master), 1));
+ git_reference_free(head);
+ git_reference_free(master);
+
+ cl_git_pass(git_branch_delete(repo, "master", GIT_BRANCH_LOCAL));
+}
+
+void test_refs_branches_delete__can_delete_a_local_branch(void)
+{
+ cl_git_pass(git_branch_delete(repo, "br2", GIT_BRANCH_LOCAL));
+}
+
+void test_refs_branches_delete__can_delete_a_remote_branch(void)
+{
+ cl_git_pass(git_branch_delete(repo, "nulltoken/master", GIT_BRANCH_REMOTE));
+}
+
+static void assert_non_exisitng_branch_removal(const char *branch_name, git_branch_t branch_type)
+{
+ int error;
+ error = git_branch_delete(repo, branch_name, branch_type);
+
+ cl_git_fail(error);
+ cl_assert_equal_i(GIT_ENOTFOUND, error);
+}
+
+void test_refs_branches_delete__deleting_a_non_existing_branch_returns_ENOTFOUND(void)
+{
+ assert_non_exisitng_branch_removal("i-do-not-locally-exist", GIT_BRANCH_LOCAL);
+ assert_non_exisitng_branch_removal("neither/remotely", GIT_BRANCH_REMOTE);
+}
diff --git a/tests-clar/refs/branches/listall.c b/tests-clar/refs/branches/listall.c
new file mode 100644
index 000000000..0a5634fb4
--- /dev/null
+++ b/tests-clar/refs/branches/listall.c
@@ -0,0 +1,78 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+#include "branch.h"
+
+static git_repository *repo;
+static git_strarray branch_list;
+static git_reference *fake_remote;
+
+void test_refs_branches_listall__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_oid(&fake_remote, repo, "refs/remotes/nulltoken/master", &id, 0));
+}
+
+void test_refs_branches_listall__cleanup(void)
+{
+ git_strarray_free(&branch_list);
+ git_reference_free(fake_remote);
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("testrepo.git");
+}
+
+static void assert_retrieval(unsigned int flags, unsigned int expected_count)
+{
+ cl_git_pass(git_branch_list(&branch_list, repo, flags));
+
+ cl_assert_equal_i(expected_count, branch_list.count);
+}
+
+void test_refs_branches_listall__retrieve_all_branches(void)
+{
+ assert_retrieval(GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE, 6 + 1);
+}
+
+void test_refs_branches_listall__retrieve_remote_branches(void)
+{
+ assert_retrieval(GIT_BRANCH_REMOTE, 1);
+}
+
+void test_refs_branches_listall__retrieve_local_branches(void)
+{
+ assert_retrieval(GIT_BRANCH_LOCAL, 6);
+}
+
+static void assert_branch_list_contains(git_strarray *branches, const char* expected_branch_name)
+{
+ unsigned int i;
+
+ for (i = 0; i < branches->count; i++) {
+ if (strcmp(expected_branch_name, branches->strings[i]) == 0)
+ return;
+ }
+
+ cl_fail("expected branch not found in list.");
+}
+
+/*
+ * $ git branch -r
+ * nulltoken/HEAD -> nulltoken/master
+ * nulltoken/master
+ */
+void test_refs_branches_listall__retrieve_remote_symbolic_HEAD_when_present(void)
+{
+ git_reference_free(fake_remote);
+ cl_git_pass(git_reference_create_symbolic(&fake_remote, repo, "refs/remotes/nulltoken/HEAD", "refs/remotes/nulltoken/master", 0));
+
+ cl_git_pass(git_branch_list(&branch_list, repo, GIT_BRANCH_REMOTE));
+
+ cl_assert_equal_i(2, branch_list.count);
+ assert_branch_list_contains(&branch_list, "refs/remotes/nulltoken/HEAD");
+ assert_branch_list_contains(&branch_list, "refs/remotes/nulltoken/master");
+}
diff --git a/tests-clar/refs/branches/move.c b/tests-clar/refs/branches/move.c
new file mode 100644
index 000000000..242e5cd01
--- /dev/null
+++ b/tests-clar/refs/branches/move.c
@@ -0,0 +1,72 @@
+#include "clar_libgit2.h"
+#include "branch.h"
+
+static git_repository *repo;
+
+void test_refs_branches_move__initialize(void)
+{
+ cl_fixture_sandbox("testrepo.git");
+ cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+}
+
+void test_refs_branches_move__cleanup(void)
+{
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("testrepo.git");
+}
+
+#define NEW_BRANCH_NAME "new-branch-on-the-block"
+
+void test_refs_branches_move__can_move_a_local_branch(void)
+{
+ cl_git_pass(git_branch_move(repo, "br2", NEW_BRANCH_NAME, 0));
+}
+
+void test_refs_branches_move__can_move_a_local_branch_to_a_different_namespace(void)
+{
+ /* Downward */
+ cl_git_pass(git_branch_move(repo, "br2", "somewhere/" NEW_BRANCH_NAME, 0));
+
+ /* Upward */
+ cl_git_pass(git_branch_move(repo, "somewhere/" NEW_BRANCH_NAME, "br2", 0));
+}
+
+void test_refs_branches_move__can_move_a_local_branch_to_a_partially_colliding_namespace(void)
+{
+ /* Downward */
+ cl_git_pass(git_branch_move(repo, "br2", "br2/" NEW_BRANCH_NAME, 0));
+
+ /* Upward */
+ cl_git_pass(git_branch_move(repo, "br2/" NEW_BRANCH_NAME, "br2", 0));
+}
+
+void test_refs_branches_move__can_not_move_a_branch_if_its_destination_name_collide_with_an_existing_one(void)
+{
+ cl_git_fail(git_branch_move(repo, "br2", "master", 0));
+}
+
+void test_refs_branches_move__can_not_move_a_non_existing_branch(void)
+{
+ cl_git_fail(git_branch_move(repo, "i-am-no-branch", NEW_BRANCH_NAME, 0));
+}
+
+void test_refs_branches_move__can_force_move_over_an_existing_branch(void)
+{
+ cl_git_pass(git_branch_move(repo, "br2", "master", 1));
+}
+
+void test_refs_branches_move__can_not_move_a_branch_through_its_canonical_name(void)
+{
+ cl_git_fail(git_branch_move(repo, "refs/heads/br2", NEW_BRANCH_NAME, 1));
+}
+
+void test_refs_branches_move__moving_a_non_exisiting_branch_returns_ENOTFOUND(void)
+{
+ int error;
+
+ error = git_branch_move(repo, "where/am/I", NEW_BRANCH_NAME, 0);
+ cl_git_fail(error);
+
+ cl_assert_equal_i(GIT_ENOTFOUND, error);
+}
diff --git a/tests-clar/refs/crashes.c b/tests-clar/refs/crashes.c
index 26ce98a68..e1b289ace 100644
--- a/tests-clar/refs/crashes.c
+++ b/tests-clar/refs/crashes.c
@@ -11,7 +11,7 @@ void test_refs_crashes__double_free(void)
cl_git_pass(git_reference_lookup(&ref2, repo, REFNAME));
cl_git_pass(git_reference_delete(ref));
/* reference is gone from disk, so reloading it will fail */
- cl_must_fail(git_reference_reload(ref2));
+ cl_git_fail(git_reference_reload(ref2));
git_repository_free(repo);
}
diff --git a/tests-clar/refs/create.c b/tests-clar/refs/create.c
new file mode 100644
index 000000000..dde4c5745
--- /dev/null
+++ b/tests-clar/refs/create.c
@@ -0,0 +1,149 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "git2/reflog.h"
+#include "reflog.h"
+
+static const char *current_master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750";
+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;
+ git_buf ref_path = GIT_BUF_INIT;
+
+ const char *new_head_tracker = "another-head-tracker";
+
+ git_oid_fromstr(&id, current_master_tip);
+
+ /* Retrieve the physical path to the symbolic ref for further cleaning */
+ cl_git_pass(git_buf_joinpath(&ref_path, g_repo->path_repository, new_head_tracker));
+ git_buf_free(&ref_path);
+
+ /* Create and write the new symbolic reference */
+ cl_git_pass(git_reference_create_symbolic(&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(git_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_oid(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_oid(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;
+ git_buf ref_path = GIT_BUF_INIT;
+
+ const char *new_head_tracker = "deep/rooted/tracker";
+
+ git_oid_fromstr(&id, current_master_tip);
+
+ cl_git_pass(git_buf_joinpath(&ref_path, g_repo->path_repository, new_head_tracker));
+ cl_git_pass(git_reference_create_symbolic(&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_oid(resolved_ref)) == 0);
+
+ git_reference_free(new_reference);
+ git_reference_free(looked_up_ref);
+ git_reference_free(resolved_ref);
+ git_buf_free(&ref_path);
+}
+
+void test_refs_create__oid(void)
+{
+ // create a new OID reference
+ git_reference *new_reference, *looked_up_ref;
+ git_repository *repo2;
+ git_oid id;
+ git_buf ref_path = GIT_BUF_INIT;
+
+ const char *new_head = "refs/heads/new-head";
+
+ git_oid_fromstr(&id, current_master_tip);
+
+ /* Retrieve the physical path to the symbolic ref for further cleaning */
+ cl_git_pass(git_buf_joinpath(&ref_path, g_repo->path_repository, new_head));
+
+ /* Create and write the new object id reference */
+ cl_git_pass(git_reference_create_oid(&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(git_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_oid(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_oid(looked_up_ref)) == 0);
+
+ git_repository_free(repo2);
+
+ git_reference_free(new_reference);
+ git_reference_free(looked_up_ref);
+ git_buf_free(&ref_path);
+}
+
+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_oid(&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));
+}
diff --git a/tests-clar/refs/delete.c b/tests-clar/refs/delete.c
new file mode 100644
index 000000000..912f41456
--- /dev/null
+++ b/tests-clar/refs/delete.c
@@ -0,0 +1,85 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "git2/reflog.h"
+#include "reflog.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, g_repo->path_repository, 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(git_reference_is_packed(looked_up_ref) == 0);
+
+ /* Now that the reference is deleted... */
+ cl_git_pass(git_reference_delete(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_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_oid(&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(git_reference_is_packed(ref) == 0);
+
+ /* Pack all existing references */
+ cl_git_pass(git_reference_packall(g_repo));
+
+ /* Reload the reference from disk */
+ cl_git_pass(git_reference_reload(ref));
+
+ /* Ensure it's a packed reference */
+ cl_assert(git_reference_is_packed(ref) == 1);
+
+ /* This should pass */
+ cl_git_pass(git_reference_delete(ref));
+}
diff --git a/tests-clar/refs/list.c b/tests-clar/refs/list.c
new file mode 100644
index 000000000..2a7b157ca
--- /dev/null
+++ b/tests-clar/refs/list.c
@@ -0,0 +1,53 @@
+#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, GIT_REF_LISTALL));
+
+ /*{
+ unsigned short i;
+ for (i = 0; i < ref_list.count; ++i)
+ printf("# %s\n", ref_list.strings[i]);
+ }*/
+
+ /* We have exactly 9 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(ref_list.count == 9);
+
+ git_strarray_free(&ref_list);
+}
+
+void test_refs_list__symbolic_only(void)
+{
+ // try to list only the symbolic references
+ git_strarray ref_list;
+
+ cl_git_pass(git_reference_list(&ref_list, g_repo, GIT_REF_SYMBOLIC));
+ cl_assert(ref_list.count == 0); /* no symrefs in the test repo */
+
+ git_strarray_free(&ref_list);
+}
diff --git a/tests-clar/refs/listall.c b/tests-clar/refs/listall.c
index 4aa7051c8..7f1de74cc 100644
--- a/tests-clar/refs/listall.c
+++ b/tests-clar/refs/listall.c
@@ -6,10 +6,10 @@ static git_strarray ref_list;
static void ensure_no_refname_starts_with_a_forward_slash(const char *path)
{
- int i;
+ size_t i;
cl_git_pass(git_repository_open(&repo, path));
- cl_git_pass(git_reference_listall(&ref_list, repo, GIT_REF_LISTALL));
+ cl_git_pass(git_reference_list(&ref_list, repo, GIT_REF_LISTALL));
cl_assert(ref_list.count > 0);
diff --git a/tests-clar/refs/lookup.c b/tests-clar/refs/lookup.c
new file mode 100644
index 000000000..ab563ac2b
--- /dev/null
+++ b/tests-clar/refs/lookup.c
@@ -0,0 +1,42 @@
+#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__oid(void)
+{
+ git_oid tag, expected;
+
+ cl_git_pass(git_reference_name_to_oid(&tag, g_repo, "refs/tags/point_to_blob"));
+ cl_git_pass(git_oid_fromstr(&expected, "1385f264afb75a56a5bec74243be9b367ba4ca08"));
+ cl_assert(git_oid_cmp(&tag, &expected) == 0);
+}
diff --git a/tests-clar/refs/normalize.c b/tests-clar/refs/normalize.c
new file mode 100644
index 000000000..135d0a9b6
--- /dev/null
+++ b/tests-clar/refs/normalize.c
@@ -0,0 +1,200 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "git2/reflog.h"
+#include "reflog.h"
+
+
+// Helpers
+static void ensure_refname_normalized(int is_oid_ref,
+ const char *input_refname,
+ const char *expected_refname)
+{
+ char buffer_out[GIT_REFNAME_MAX];
+
+ if (is_oid_ref)
+ cl_git_pass(git_reference__normalize_name_oid(buffer_out, sizeof(buffer_out), input_refname));
+ else
+ cl_git_pass(git_reference__normalize_name(buffer_out, sizeof(buffer_out), input_refname));
+
+ if (expected_refname)
+ cl_assert(0 == strcmp(buffer_out, expected_refname));
+}
+
+static void ensure_refname_invalid(int is_oid_ref, const char *input_refname)
+{
+ char buffer_out[GIT_REFNAME_MAX];
+
+ if (is_oid_ref)
+ cl_git_fail(git_reference__normalize_name_oid(buffer_out, sizeof(buffer_out), input_refname));
+ else
+ cl_git_fail(git_reference__normalize_name(buffer_out, sizeof(buffer_out), input_refname));
+}
+
+#define OID_REF 1
+#define SYM_REF 0
+
+
+
+void test_refs_normalize__direct(void)
+{
+ // normalize a direct (OID) reference name
+ ensure_refname_invalid(OID_REF, "a");
+ ensure_refname_invalid(OID_REF, "");
+ ensure_refname_invalid(OID_REF, "refs/heads/a/");
+ ensure_refname_invalid(OID_REF, "refs/heads/a.");
+ ensure_refname_invalid(OID_REF, "refs/heads/a.lock");
+ ensure_refname_normalized(OID_REF, "refs/dummy/a", NULL);
+ ensure_refname_normalized(OID_REF, "refs/stash", NULL);
+ ensure_refname_normalized(OID_REF, "refs/tags/a", "refs/tags/a");
+ ensure_refname_normalized(OID_REF, "refs/heads/a/b", "refs/heads/a/b");
+ ensure_refname_normalized(OID_REF, "refs/heads/a./b", "refs/heads/a./b");
+ ensure_refname_invalid(OID_REF, "refs/heads/foo?bar");
+ ensure_refname_invalid(OID_REF, "refs/heads\foo");
+ ensure_refname_normalized(OID_REF, "refs/heads/v@ation", "refs/heads/v@ation");
+ ensure_refname_normalized(OID_REF, "refs///heads///a", "refs/heads/a");
+ ensure_refname_invalid(OID_REF, "refs/heads/.a/b");
+ ensure_refname_invalid(OID_REF, "refs/heads/foo/../bar");
+ ensure_refname_invalid(OID_REF, "refs/heads/foo..bar");
+ ensure_refname_invalid(OID_REF, "refs/heads/./foo");
+ ensure_refname_invalid(OID_REF, "refs/heads/v@{ation");
+}
+
+void test_refs_normalize__symbolic(void)
+{
+ // normalize a symbolic reference name
+ ensure_refname_normalized(SYM_REF, "a", "a");
+ ensure_refname_normalized(SYM_REF, "a/b", "a/b");
+ ensure_refname_normalized(SYM_REF, "refs///heads///a", "refs/heads/a");
+ ensure_refname_invalid(SYM_REF, "");
+ ensure_refname_invalid(SYM_REF, "heads\foo");
+}
+
+/* Ported from JGit, BSD licence.
+ * See https://github.com/spearce/JGit/commit/e4bf8f6957bbb29362575d641d1e77a02d906739 */
+void test_refs_normalize__jgit_suite(void)
+{
+ // tests borrowed from JGit
+
+/* EmptyString */
+ ensure_refname_invalid(SYM_REF, "");
+ ensure_refname_invalid(SYM_REF, "/");
+
+/* MustHaveTwoComponents */
+ ensure_refname_invalid(OID_REF, "master");
+ ensure_refname_normalized(SYM_REF, "heads/master", "heads/master");
+
+/* ValidHead */
+
+ ensure_refname_normalized(SYM_REF, "refs/heads/master", "refs/heads/master");
+ ensure_refname_normalized(SYM_REF, "refs/heads/pu", "refs/heads/pu");
+ ensure_refname_normalized(SYM_REF, "refs/heads/z", "refs/heads/z");
+ ensure_refname_normalized(SYM_REF, "refs/heads/FoO", "refs/heads/FoO");
+
+/* ValidTag */
+ ensure_refname_normalized(SYM_REF, "refs/tags/v1.0", "refs/tags/v1.0");
+
+/* NoLockSuffix */
+ ensure_refname_invalid(SYM_REF, "refs/heads/master.lock");
+
+/* NoDirectorySuffix */
+ ensure_refname_invalid(SYM_REF, "refs/heads/master/");
+
+/* NoSpace */
+ ensure_refname_invalid(SYM_REF, "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(SYM_REF, buffer);
+ }
+ }
+
+/* NoBareDot */
+ ensure_refname_invalid(SYM_REF, "refs/heads/.");
+ ensure_refname_invalid(SYM_REF, "refs/heads/..");
+ ensure_refname_invalid(SYM_REF, "refs/heads/./master");
+ ensure_refname_invalid(SYM_REF, "refs/heads/../master");
+
+/* NoLeadingOrTrailingDot */
+ ensure_refname_invalid(SYM_REF, ".");
+ ensure_refname_invalid(SYM_REF, "refs/heads/.bar");
+ ensure_refname_invalid(SYM_REF, "refs/heads/..bar");
+ ensure_refname_invalid(SYM_REF, "refs/heads/bar.");
+
+/* ContainsDot */
+ ensure_refname_normalized(SYM_REF, "refs/heads/m.a.s.t.e.r", "refs/heads/m.a.s.t.e.r");
+ ensure_refname_invalid(SYM_REF, "refs/heads/master..pu");
+
+/* NoMagicRefCharacters */
+ ensure_refname_invalid(SYM_REF, "refs/heads/master^");
+ ensure_refname_invalid(SYM_REF, "refs/heads/^master");
+ ensure_refname_invalid(SYM_REF, "^refs/heads/master");
+
+ ensure_refname_invalid(SYM_REF, "refs/heads/master~");
+ ensure_refname_invalid(SYM_REF, "refs/heads/~master");
+ ensure_refname_invalid(SYM_REF, "~refs/heads/master");
+
+ ensure_refname_invalid(SYM_REF, "refs/heads/master:");
+ ensure_refname_invalid(SYM_REF, "refs/heads/:master");
+ ensure_refname_invalid(SYM_REF, ":refs/heads/master");
+
+/* ShellGlob */
+ ensure_refname_invalid(SYM_REF, "refs/heads/master?");
+ ensure_refname_invalid(SYM_REF, "refs/heads/?master");
+ ensure_refname_invalid(SYM_REF, "?refs/heads/master");
+
+ ensure_refname_invalid(SYM_REF, "refs/heads/master[");
+ ensure_refname_invalid(SYM_REF, "refs/heads/[master");
+ ensure_refname_invalid(SYM_REF, "[refs/heads/master");
+
+ ensure_refname_invalid(SYM_REF, "refs/heads/master*");
+ ensure_refname_invalid(SYM_REF, "refs/heads/*master");
+ ensure_refname_invalid(SYM_REF, "*refs/heads/master");
+
+/* ValidSpecialCharacters */
+ ensure_refname_normalized(SYM_REF, "refs/heads/!", "refs/heads/!");
+ ensure_refname_normalized(SYM_REF, "refs/heads/\"", "refs/heads/\"");
+ ensure_refname_normalized(SYM_REF, "refs/heads/#", "refs/heads/#");
+ ensure_refname_normalized(SYM_REF, "refs/heads/$", "refs/heads/$");
+ ensure_refname_normalized(SYM_REF, "refs/heads/%", "refs/heads/%");
+ ensure_refname_normalized(SYM_REF, "refs/heads/&", "refs/heads/&");
+ ensure_refname_normalized(SYM_REF, "refs/heads/'", "refs/heads/'");
+ ensure_refname_normalized(SYM_REF, "refs/heads/(", "refs/heads/(");
+ ensure_refname_normalized(SYM_REF, "refs/heads/)", "refs/heads/)");
+ ensure_refname_normalized(SYM_REF, "refs/heads/+", "refs/heads/+");
+ ensure_refname_normalized(SYM_REF, "refs/heads/,", "refs/heads/,");
+ ensure_refname_normalized(SYM_REF, "refs/heads/-", "refs/heads/-");
+ ensure_refname_normalized(SYM_REF, "refs/heads/;", "refs/heads/;");
+ ensure_refname_normalized(SYM_REF, "refs/heads/<", "refs/heads/<");
+ ensure_refname_normalized(SYM_REF, "refs/heads/=", "refs/heads/=");
+ ensure_refname_normalized(SYM_REF, "refs/heads/>", "refs/heads/>");
+ ensure_refname_normalized(SYM_REF, "refs/heads/@", "refs/heads/@");
+ ensure_refname_normalized(SYM_REF, "refs/heads/]", "refs/heads/]");
+ ensure_refname_normalized(SYM_REF, "refs/heads/_", "refs/heads/_");
+ ensure_refname_normalized(SYM_REF, "refs/heads/`", "refs/heads/`");
+ ensure_refname_normalized(SYM_REF, "refs/heads/{", "refs/heads/{");
+ ensure_refname_normalized(SYM_REF, "refs/heads/|", "refs/heads/|");
+ ensure_refname_normalized(SYM_REF, "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(SYM_REF, "refs/heads/\\");
+
+/* UnicodeNames */
+ /*
+ * Currently this fails.
+ * ensure_refname_normalized(SYM_REF, "refs/heads/\u00e5ngstr\u00f6m", "refs/heads/\u00e5ngstr\u00f6m");
+ */
+
+/* RefLogQueryIsValidRef */
+ ensure_refname_invalid(SYM_REF, "refs/heads/master@{1}");
+ ensure_refname_invalid(SYM_REF, "refs/heads/master@{1.hour.ago}");
+}
diff --git a/tests-clar/refs/overwrite.c b/tests-clar/refs/overwrite.c
new file mode 100644
index 000000000..410e39a84
--- /dev/null
+++ b/tests-clar/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_create_symbolic(&branch_ref, g_repo, ref_branch_name, ref_master_name, 0));
+ cl_git_pass(git_reference_create_symbolic(&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_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_create_symbolic(&ref, g_repo, ref_name, ref_master_name, 0));
+ cl_git_pass(git_reference_create_symbolic(&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_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_oid(ref));
+ git_reference_free(ref);
+
+ /* Create it */
+ cl_git_pass(git_reference_create_oid(&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_oid(ref));
+ git_reference_free(ref);
+
+ /* Ensure we can't overwrite unless we force it */
+ cl_git_fail(git_reference_create_oid(&ref, g_repo, ref_name, &id, 0));
+ cl_git_pass(git_reference_create_oid(&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_oid(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_oid(ref));
+ git_reference_free(ref);
+
+ cl_git_pass(git_reference_create_oid(&ref, g_repo, ref_name, &id, 0));
+ git_reference_free(ref);
+ cl_git_fail(git_reference_create_symbolic(&ref, g_repo, ref_name, ref_master_name, 0));
+ cl_git_pass(git_reference_create_symbolic(&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_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_oid(ref));
+ git_reference_free(ref);
+
+ /* Create the symbolic ref */
+ cl_git_pass(git_reference_create_symbolic(&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_oid(&ref, g_repo, ref_name, &id, 0));
+ cl_git_pass(git_reference_create_oid(&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_oid(ref), &id));
+
+ git_reference_free(ref);
+}
diff --git a/tests-clar/refs/pack.c b/tests-clar/refs/pack.c
new file mode 100644
index 000000000..305594c28
--- /dev/null
+++ b/tests-clar/refs/pack.c
@@ -0,0 +1,67 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "git2/reflog.h"
+#include "reflog.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();
+}
+
+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, g_repo->path_repository, 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);
+
+ cl_git_pass(git_reference_packall(g_repo));
+}
+
+void test_refs_pack__loose(void)
+{
+ // create a packfile from all the loose rn a repo
+ git_reference *reference;
+ git_buf temp_path = GIT_BUF_INIT;
+
+ /* Ensure a known loose ref can be looked up */
+ cl_git_pass(git_reference_lookup(&reference, g_repo, loose_tag_ref_name));
+ cl_assert(git_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
+ */
+ cl_git_pass(git_reference_packall(g_repo));
+
+ /* Ensure the packed-refs file exists */
+ cl_git_pass(git_buf_joinpath(&temp_path, g_repo->path_repository, 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(git_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, g_repo->path_repository, loose_tag_ref_name));
+ cl_assert(!git_path_exists(temp_path.ptr));
+
+ git_reference_free(reference);
+ git_buf_free(&temp_path);
+}
diff --git a/tests-clar/refs/read.c b/tests-clar/refs/read.c
new file mode 100644
index 000000000..d7111b232
--- /dev/null
+++ b/tests-clar/refs/read.c
@@ -0,0 +1,194 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "git2/reflog.h"
+#include "reflog.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)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_refs_read__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+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(git_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_oid(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(git_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_oid(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(git_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_oid(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_oid(comp_base_ref), git_reference_oid(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_oid(comp_base_ref), git_reference_oid(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_oid(master_ref), git_reference_oid(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(git_reference_is_packed(reference));
+ cl_assert_equal_s(reference->name, packed_head_name);
+
+ cl_git_pass(git_object_lookup(&object, g_repo, git_reference_oid(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(git_reference_is_packed(reference) == 0);
+ cl_assert_equal_s(reference->name, packed_test_head_name);
+
+ git_reference_free(reference);
+}
diff --git a/tests-clar/refs/reflog.c b/tests-clar/refs/reflog.c
new file mode 100644
index 000000000..1bc51b2b8
--- /dev/null
+++ b/tests-clar/refs/reflog.c
@@ -0,0 +1,123 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "git2/reflog.h"
+#include "reflog.h"
+
+
+static const char *new_ref = "refs/heads/test-reflog";
+static const char *current_master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750";
+static const char *commit_msg = "commit: bla bla";
+
+static git_repository *g_repo;
+
+
+// helpers
+static void assert_signature(git_signature *expected, git_signature *actual)
+{
+ cl_assert(actual);
+ cl_assert_equal_s(expected->name, actual->name);
+ cl_assert_equal_s(expected->email, actual->email);
+ cl_assert(expected->when.offset == actual->when.offset);
+ cl_assert(expected->when.time == actual->when.time);
+}
+
+
+// Fixture setup and teardown
+void test_refs_reflog__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_refs_reflog__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+
+
+void test_refs_reflog__write_then_read(void)
+{
+ // write a reflog for a given reference and ensure it can be read back
+ git_repository *repo2;
+ git_reference *ref, *lookedup_ref;
+ git_oid oid;
+ git_signature *committer;
+ git_reflog *reflog;
+ git_reflog_entry *entry;
+ char oid_str[GIT_OID_HEXSZ+1];
+
+ /* Create a new branch pointing at the HEAD */
+ git_oid_fromstr(&oid, current_master_tip);
+ cl_git_pass(git_reference_create_oid(&ref, g_repo, new_ref, &oid, 0));
+ git_reference_free(ref);
+ cl_git_pass(git_reference_lookup(&ref, g_repo, new_ref));
+
+ cl_git_pass(git_signature_now(&committer, "foo", "foo@bar"));
+
+ cl_git_pass(git_reflog_write(ref, NULL, committer, NULL));
+ cl_git_fail(git_reflog_write(ref, NULL, committer, "no ancestor NULL for an existing reflog"));
+ cl_git_fail(git_reflog_write(ref, NULL, committer, "no\nnewline"));
+ cl_git_pass(git_reflog_write(ref, &oid, committer, commit_msg));
+
+ /* Reopen a new instance of the repository */
+ cl_git_pass(git_repository_open(&repo2, "testrepo"));
+
+ /* Lookup the preivously created branch */
+ cl_git_pass(git_reference_lookup(&lookedup_ref, repo2, new_ref));
+
+ /* Read and parse the reflog for this branch */
+ cl_git_pass(git_reflog_read(&reflog, lookedup_ref));
+ cl_assert(reflog->entries.length == 2);
+
+ entry = (git_reflog_entry *)git_vector_get(&reflog->entries, 0);
+ assert_signature(committer, entry->committer);
+ git_oid_tostr(oid_str, GIT_OID_HEXSZ+1, &entry->oid_old);
+ cl_assert_equal_s("0000000000000000000000000000000000000000", oid_str);
+ git_oid_tostr(oid_str, GIT_OID_HEXSZ+1, &entry->oid_cur);
+ cl_assert_equal_s(current_master_tip, oid_str);
+ cl_assert(entry->msg == NULL);
+
+ entry = (git_reflog_entry *)git_vector_get(&reflog->entries, 1);
+ assert_signature(committer, entry->committer);
+ git_oid_tostr(oid_str, GIT_OID_HEXSZ+1, &entry->oid_old);
+ cl_assert_equal_s(current_master_tip, oid_str);
+ git_oid_tostr(oid_str, GIT_OID_HEXSZ+1, &entry->oid_cur);
+ cl_assert_equal_s(current_master_tip, oid_str);
+ cl_assert_equal_s(commit_msg, entry->msg);
+
+ git_signature_free(committer);
+ git_reflog_free(reflog);
+ git_repository_free(repo2);
+
+ git_reference_free(ref);
+ git_reference_free(lookedup_ref);
+}
+
+void test_refs_reflog__dont_write_bad(void)
+{
+ // avoid writing an obviously wrong reflog
+ 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_oid(&ref, g_repo, new_ref, &oid, 0));
+ git_reference_free(ref);
+ cl_git_pass(git_reference_lookup(&ref, g_repo, new_ref));
+
+ cl_git_pass(git_signature_now(&committer, "foo", "foo@bar"));
+
+ /* Write the reflog for the new branch */
+ cl_git_pass(git_reflog_write(ref, NULL, committer, NULL));
+
+ /* Try to update the reflog with wrong information:
+ * It's no new reference, so the ancestor OID cannot
+ * be NULL. */
+ cl_git_fail(git_reflog_write(ref, NULL, committer, NULL));
+
+ git_signature_free(committer);
+
+ git_reference_free(ref);
+}
diff --git a/tests-clar/refs/rename.c b/tests-clar/refs/rename.c
new file mode 100644
index 000000000..4b917ef6d
--- /dev/null
+++ b/tests-clar/refs/rename.c
@@ -0,0 +1,339 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "git2/reflog.h"
+#include "reflog.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, *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, g_repo->path_repository, 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(git_reference_is_packed(looked_up_ref) == 0);
+
+ /* Now that the reference is renamed... */
+ cl_git_pass(git_reference_rename(looked_up_ref, new_name, 0));
+ cl_assert_equal_s(looked_up_ref->name, new_name);
+
+ /* ...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(another_looked_up_ref->name, new_name);
+
+ /* .. the ref is still loose... */
+ cl_assert(git_reference_is_packed(another_looked_up_ref) == 0);
+ cl_assert(git_reference_is_packed(looked_up_ref) == 0);
+
+ /* ...and the ref can be found in the file system */
+ cl_git_pass(git_buf_joinpath(&temp_path, g_repo->path_repository, new_name));
+ cl_assert(git_path_exists(temp_path.ptr));
+
+ git_reference_free(looked_up_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, *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, g_repo->path_repository, 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(git_reference_is_packed(looked_up_ref) != 0);
+
+ /* Now that the reference is renamed... */
+ cl_git_pass(git_reference_rename(looked_up_ref, brand_new_name, 0));
+ cl_assert_equal_s(looked_up_ref->name, brand_new_name);
+
+ /* ...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(git_reference_is_packed(another_looked_up_ref) == 0);
+ cl_assert(git_reference_is_packed(looked_up_ref) == 0);
+
+ /* ...and the ref now happily lives in the file system */
+ cl_git_pass(git_buf_joinpath(&temp_path, g_repo->path_repository, brand_new_name));
+ cl_assert(git_path_exists(temp_path.ptr));
+
+ git_reference_free(looked_up_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;
+ 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, g_repo->path_repository, 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(git_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(git_reference_is_packed(looked_up_ref) != 0);
+
+ /* Now that the reference is renamed... */
+ cl_git_pass(git_reference_rename(looked_up_ref, brand_new_name, 0));
+
+ /* 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(git_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(looked_up_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;
+
+ /* 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(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;
+
+ /* 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_git_fail(git_reference_rename(looked_up_ref, "Hello! I'm a very invalid name.", 0));
+
+ /* Can not be renamed outside of the refs hierarchy. */
+ cl_git_fail(git_reference_rename(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;
+ 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_oid(looked_up_ref));
+
+ /* Can be force-renamed to the name of another existing reference. */
+ cl_git_pass(git_reference_rename(looked_up_ref, packed_test_head_name, 1));
+ git_reference_free(looked_up_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_oid(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;
+ 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_oid(looked_up_ref));
+
+ /* Can be force-renamed to the name of another existing reference. */
+ cl_git_pass(git_reference_rename(looked_up_ref, "refs/heads/test", 1));
+ git_reference_free(looked_up_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_oid(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_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_oid(ref));
+
+ /* Create loose references */
+ cl_git_pass(git_reference_create_oid(&ref_one, g_repo, ref_one_name, &id, 0));
+ cl_git_pass(git_reference_create_oid(&ref_two, g_repo, ref_two_name, &id, 0));
+
+ /* Pack everything */
+ cl_git_pass(git_reference_packall(g_repo));
+
+ /* Attempt to create illegal reference */
+ cl_git_fail(git_reference_create_oid(&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);
+}
+
+
+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;
+ 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_oid(ref));
+
+ /* Create loose references */
+ cl_git_pass(git_reference_create_oid(&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(looked_up_ref, ref_two_name_new, 0));
+ git_reference_free(looked_up_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;
+ 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_oid(ref));
+
+ /* Create loose references */
+ cl_git_pass(git_reference_create_oid(&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(looked_up_ref, ref_two_name, 0));
+ git_reference_free(looked_up_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);
+}
diff --git a/tests-clar/refs/unicode.c b/tests-clar/refs/unicode.c
new file mode 100644
index 000000000..889c85666
--- /dev/null
+++ b/tests-clar/refs/unicode.c
@@ -0,0 +1,42 @@
+#include "clar_libgit2.h"
+
+static git_repository *repo;
+
+void test_refs_unicode__initialize(void)
+{
+ cl_fixture_sandbox("testrepo.git");
+
+ cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+}
+
+void test_refs_unicode__cleanup(void)
+{
+ git_repository_free(repo);
+ cl_fixture_cleanup("testrepo.git");
+}
+
+void test_refs_unicode__create_and_lookup(void)
+{
+ git_reference *ref0, *ref1, *ref2;
+ git_repository *repo2;
+
+ const char *REFNAME = "refs/heads/" "\305" "ngstr" "\366" "m";
+ const char *master = "refs/heads/master";
+
+ /* Create the reference */
+ cl_git_pass(git_reference_lookup(&ref0, repo, master));
+ cl_git_pass(git_reference_create_oid(&ref1, repo, REFNAME, git_reference_oid(ref0), 0));
+ cl_assert(strcmp(REFNAME, git_reference_name(ref1)) == 0);
+
+ /* Lookup the reference in a different instance of the repository */
+ cl_git_pass(git_repository_open(&repo2, "testrepo.git"));
+ cl_git_pass(git_reference_lookup(&ref2, repo2, REFNAME));
+
+ cl_assert(git_oid_cmp(git_reference_oid(ref1), git_reference_oid(ref2)) == 0);
+ cl_assert(strcmp(REFNAME, git_reference_name(ref2)) == 0);
+
+ git_reference_free(ref0);
+ git_reference_free(ref1);
+ git_reference_free(ref2);
+ git_repository_free(repo2);
+}
diff --git a/tests-clar/repo/discover.c b/tests-clar/repo/discover.c
new file mode 100644
index 000000000..b3d639bd1
--- /dev/null
+++ b/tests-clar/repo/discover.c
@@ -0,0 +1,142 @@
+#include "clar_libgit2.h"
+
+#include "odb.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, GIT_DIRREMOVAL_FILES_AND_DIRS));
+ git_repository_free(repo);
+ git_buf_free(&ceiling_dirs_buf);
+}
+
diff --git a/tests-clar/repo/getters.c b/tests-clar/repo/getters.c
index a0d437983..966de1f16 100644
--- a/tests-clar/repo/getters.c
+++ b/tests-clar/repo/getters.c
@@ -68,3 +68,19 @@ void test_repo_getters__head_orphan(void)
git_reference_free(ref);
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, "testrepo.git"));
+
+ cl_git_pass(git_repository_odb(&odb, repo));
+ cl_assert(((git_refcount *)odb)->refcount == 2);
+
+ git_repository_free(repo);
+ cl_assert(((git_refcount *)odb)->refcount == 1);
+
+ git_odb_free(odb);
+}
diff --git a/tests-clar/repo/init.c b/tests-clar/repo/init.c
index a12a2c2fb..7f16b5b7c 100644
--- a/tests-clar/repo/init.c
+++ b/tests-clar/repo/init.c
@@ -141,3 +141,27 @@ void test_repo_init__reinit_too_recent_bare_repo(void)
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);
+}
diff --git a/tests-clar/repo/open.c b/tests-clar/repo/open.c
index c3a7dadbd..c70ec83a9 100644
--- a/tests-clar/repo/open.c
+++ b/tests-clar/repo/open.c
@@ -1,25 +1,28 @@
#include "clar_libgit2.h"
-#include "posix.h"
-
-static git_repository *repo;
+#include "fileops.h"
+#include <ctype.h>
void test_repo_open__cleanup(void)
{
- git_repository_free(repo);
+ cl_git_sandbox_cleanup();
+
+ if (git_path_isdir("alternate"))
+ git_futils_rmdir_r("alternate", GIT_DIRREMOVAL_FILES_AND_DIRS);
}
void test_repo_open__bare_empty_repo(void)
{
- cl_git_pass(git_repository_open(&repo, cl_fixture("empty_bare.git")));
+ 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);
@@ -27,20 +30,253 @@ void test_repo_open__standard_empty_repo_through_gitdir(void)
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)
{
- cl_fixture_sandbox("empty_standard_repo");
- cl_git_pass(p_rename("empty_standard_repo/.gitted", "empty_standard_repo/.git"));
-
- cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
cl_assert(git_repository_path(repo) != NULL);
cl_assert(git__suffixcmp(git_repository_path(repo), "/") == 0);
cl_assert(git_repository_workdir(repo) != NULL);
cl_assert(git__suffixcmp(git_repository_workdir(repo), "/") == 0);
+}
+
+
+void test_repo_open__open_with_discover(void)
+{
+ static const char *variants[] = {
+ "attr", "attr/", "attr/.git", "attr/.git/",
+ "attr/sub", "attr/sub/", "attr/sub/sub", "attr/sub/sub/",
+ NULL
+ };
+ git_repository *repo;
+ const char **scan;
+
+ cl_fixture_sandbox("attr");
+ cl_git_pass(p_rename("attr/.gitted", "attr/.git"));
+
+ for (scan = variants; *scan != NULL; scan++) {
+ cl_git_pass(git_repository_open_ext(&repo, *scan, 0, NULL));
+ cl_assert(git__suffixcmp(git_repository_path(repo), "attr/.git/") == 0);
+ cl_assert(git__suffixcmp(git_repository_workdir(repo), "attr/") == 0);
+ git_repository_free(repo);
+ }
+
+ cl_fixture_cleanup("attr");
+}
+
+void test_repo_open__gitlinked(void)
+{
+ /* need to have both repo dir and workdir set up correctly */
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+ git_repository *repo2;
+
+ cl_must_pass(p_mkdir("alternate", 0777));
+ cl_git_mkfile("alternate/.git", "gitdir: ../empty_standard_repo/.git");
+
+ cl_git_pass(git_repository_open(&repo2, "alternate"));
+
+ cl_assert(git_repository_path(repo2) != NULL);
+ cl_assert_(git__suffixcmp(git_repository_path(repo2), "empty_standard_repo/.git/") == 0, git_repository_path(repo2));
+ cl_assert_equal_s(git_repository_path(repo), git_repository_path(repo2));
+
+ cl_assert(git_repository_workdir(repo2) != NULL);
+ cl_assert_(git__suffixcmp(git_repository_workdir(repo2), "alternate/") == 0, git_repository_workdir(repo2));
+
+ git_repository_free(repo2);
+}
+
+void test_repo_open__from_git_new_workdir(void)
+{
+ /* The git-new-workdir script that ships with git sets up a bunch of
+ * symlinks to create a second workdir that shares the object db with
+ * another checkout. Libgit2 can open a repo that has been configured
+ * this way.
+ */
+ cl_git_sandbox_init("empty_standard_repo");
+
+#ifndef GIT_WIN32
+ git_repository *repo2;
+ git_buf link_tgt = GIT_BUF_INIT, link = GIT_BUF_INIT, body = GIT_BUF_INIT;
+ const char **scan;
+ int link_fd;
+ static const char *links[] = {
+ "config", "refs", "logs/refs", "objects", "info", "hooks",
+ "packed-refs", "remotes", "rr-cache", "svn", NULL
+ };
+ static const char *copies[] = {
+ "HEAD", NULL
+ };
+
+ cl_git_pass(p_mkdir("alternate", 0777));
+ cl_git_pass(p_mkdir("alternate/.git", 0777));
+
+ for (scan = links; *scan != NULL; scan++) {
+ git_buf_joinpath(&link_tgt, "empty_standard_repo/.git", *scan);
+ if (git_path_exists(link_tgt.ptr)) {
+ git_buf_joinpath(&link_tgt, "../../empty_standard_repo/.git", *scan);
+ git_buf_joinpath(&link, "alternate/.git", *scan);
+ if (strchr(*scan, '/'))
+ git_futils_mkpath2file(link.ptr, 0777);
+ cl_assert_(symlink(link_tgt.ptr, link.ptr) == 0, strerror(errno));
+ }
+ }
+ for (scan = copies; *scan != NULL; scan++) {
+ git_buf_joinpath(&link_tgt, "empty_standard_repo/.git", *scan);
+ if (git_path_exists(link_tgt.ptr)) {
+ git_buf_joinpath(&link, "alternate/.git", *scan);
+ cl_git_pass(git_futils_readbuffer(&body, link_tgt.ptr));
+
+ cl_assert((link_fd = git_futils_creat_withpath(link.ptr, 0777, 0666)) >= 0);
+ cl_must_pass(p_write(link_fd, body.ptr, body.size));
+ p_close(link_fd);
+ }
+ }
+
+ git_buf_free(&link_tgt);
+ git_buf_free(&link);
+ git_buf_free(&body);
+
+
+ cl_git_pass(git_repository_open(&repo2, "alternate"));
+
+ cl_assert(git_repository_path(repo2) != NULL);
+ cl_assert_(git__suffixcmp(git_repository_path(repo2), "alternate/.git/") == 0, git_repository_path(repo2));
+
+ cl_assert(git_repository_workdir(repo2) != NULL);
+ cl_assert_(git__suffixcmp(git_repository_workdir(repo2), "alternate/") == 0, git_repository_workdir(repo2));
- cl_fixture_cleanup("empty_standard_repo");
+ git_repository_free(repo2);
+#endif
+}
+
+void test_repo_open__failures(void)
+{
+ git_repository *base, *repo;
+ git_buf ceiling = GIT_BUF_INIT;
+
+ base = cl_git_sandbox_init("attr");
+ cl_git_pass(git_buf_sets(&ceiling, git_repository_workdir(base)));
+
+ /* fail with no searching */
+ cl_git_fail(git_repository_open(&repo, "attr/sub"));
+ cl_git_fail(git_repository_open_ext(
+ &repo, "attr/sub", GIT_REPOSITORY_OPEN_NO_SEARCH, NULL));
+
+ /* fail with ceiling too low */
+ cl_git_pass(git_buf_joinpath(&ceiling, ceiling.ptr, "sub"));
+ cl_git_fail(git_repository_open_ext(&repo, "attr/sub", 0, ceiling.ptr));
+
+ /* fail with no repo */
+ cl_git_pass(p_mkdir("alternate", 0777));
+ cl_git_pass(p_mkdir("alternate/.git", 0777));
+ cl_git_fail(git_repository_open_ext(&repo, "alternate", 0, NULL));
+ cl_git_fail(git_repository_open_ext(&repo, "alternate/.git", 0, NULL));
+
+ git_buf_free(&ceiling);
+}
+
+void test_repo_open__bad_gitlinks(void)
+{
+ git_repository *repo;
+ static const char *bad_links[] = {
+ "garbage\n", "gitdir", "gitdir:\n", "gitdir: foobar",
+ "gitdir: ../invalid", "gitdir: ../invalid2",
+ "gitdir: ../attr/.git with extra stuff",
+ NULL
+ };
+ const char **scan;
+
+ cl_git_sandbox_init("attr");
+
+ cl_git_pass(p_mkdir("alternate", 0777));
+ cl_git_pass(p_mkdir("invalid", 0777));
+ cl_git_pass(git_futils_mkdir_r("invalid2/.git", NULL, 0777));
+
+ for (scan = bad_links; *scan != NULL; scan++) {
+ cl_git_rewritefile("alternate/.git", *scan);
+ cl_git_fail(git_repository_open_ext(&repo, "alternate", 0, NULL));
+ }
+
+ git_futils_rmdir_r("invalid", GIT_DIRREMOVAL_FILES_AND_DIRS);
+ git_futils_rmdir_r("invalid2", GIT_DIRREMOVAL_FILES_AND_DIRS);
+}
+
+#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"));
}
diff --git a/tests-clar/repo/setters.c b/tests-clar/repo/setters.c
new file mode 100644
index 000000000..6242d8541
--- /dev/null
+++ b/tests-clar/repo/setters.c
@@ -0,0 +1,80 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "posix.h"
+#include "util.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);
+ cl_fixture_cleanup("testrepo.git");
+ cl_must_pass(p_rmdir("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"));
+
+ 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"));
+
+ cl_assert(git__suffixcmp(git_repository_workdir(repo), "/") == 0);
+}
+
+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 == 1);
+
+ git_repository_set_index(repo, new_index);
+ cl_assert(((git_refcount *)new_index)->refcount == 2);
+
+ git_repository_free(repo);
+ cl_assert(((git_refcount *)new_index)->refcount == 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 == 1);
+
+ git_repository_set_odb(repo, new_odb);
+ cl_assert(((git_refcount *)new_odb)->refcount == 2);
+
+ git_repository_free(repo);
+ cl_assert(((git_refcount *)new_odb)->refcount == 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/resources/.gitattributes b/tests-clar/resources/.gitattributes
index 556f8c827..556f8c827 100644
--- a/tests/resources/.gitattributes
+++ b/tests-clar/resources/.gitattributes
diff --git a/tests/resources/.gitignore b/tests-clar/resources/.gitignore
index 43a19cc9d..43a19cc9d 100644
--- a/tests/resources/.gitignore
+++ b/tests-clar/resources/.gitignore
diff --git a/tests/resources/attr/.gitted/HEAD b/tests-clar/resources/attr/.gitted/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests/resources/attr/.gitted/HEAD
+++ b/tests-clar/resources/attr/.gitted/HEAD
diff --git a/tests/resources/attr/.gitted/config b/tests-clar/resources/attr/.gitted/config
index af107929f..af107929f 100644
--- a/tests/resources/attr/.gitted/config
+++ b/tests-clar/resources/attr/.gitted/config
diff --git a/tests/resources/attr/.gitted/description b/tests-clar/resources/attr/.gitted/description
index 498b267a8..498b267a8 100644
--- a/tests/resources/attr/.gitted/description
+++ b/tests-clar/resources/attr/.gitted/description
diff --git a/tests-clar/resources/attr/.gitted/index b/tests-clar/resources/attr/.gitted/index
new file mode 100644
index 000000000..1d60eab8f
--- /dev/null
+++ b/tests-clar/resources/attr/.gitted/index
Binary files differ
diff --git a/tests/resources/attr/.gitted/info/attributes b/tests-clar/resources/attr/.gitted/info/attributes
index 5fe62a37a..5fe62a37a 100644
--- a/tests/resources/attr/.gitted/info/attributes
+++ b/tests-clar/resources/attr/.gitted/info/attributes
diff --git a/tests/resources/attr/.gitted/info/exclude b/tests-clar/resources/attr/.gitted/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests/resources/attr/.gitted/info/exclude
+++ b/tests-clar/resources/attr/.gitted/info/exclude
diff --git a/tests-clar/resources/attr/.gitted/logs/HEAD b/tests-clar/resources/attr/.gitted/logs/HEAD
new file mode 100644
index 000000000..73f00f345
--- /dev/null
+++ b/tests-clar/resources/attr/.gitted/logs/HEAD
@@ -0,0 +1,8 @@
+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
diff --git a/tests-clar/resources/attr/.gitted/logs/refs/heads/master b/tests-clar/resources/attr/.gitted/logs/refs/heads/master
new file mode 100644
index 000000000..73f00f345
--- /dev/null
+++ b/tests-clar/resources/attr/.gitted/logs/refs/heads/master
@@ -0,0 +1,8 @@
+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
diff --git a/tests-clar/resources/attr/.gitted/objects/10/8bb4e7fd7b16490dc33ff7d972151e73d7166e b/tests-clar/resources/attr/.gitted/objects/10/8bb4e7fd7b16490dc33ff7d972151e73d7166e
new file mode 100644
index 000000000..edcf7520c
--- /dev/null
+++ b/tests-clar/resources/attr/.gitted/objects/10/8bb4e7fd7b16490dc33ff7d972151e73d7166e
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/21/7878ab49e1314388ea2e32dc6fdb58a1b969e0 b/tests-clar/resources/attr/.gitted/objects/21/7878ab49e1314388ea2e32dc6fdb58a1b969e0
new file mode 100644
index 000000000..b537899f2
--- /dev/null
+++ b/tests-clar/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-clar/resources/attr/.gitted/objects/24/fa9a9fc4e202313e24b648087495441dab432b b/tests-clar/resources/attr/.gitted/objects/24/fa9a9fc4e202313e24b648087495441dab432b
new file mode 100644
index 000000000..e7099bbaa
--- /dev/null
+++ b/tests-clar/resources/attr/.gitted/objects/24/fa9a9fc4e202313e24b648087495441dab432b
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/29/29de282ce999e95183aedac6451d3384559c4b b/tests-clar/resources/attr/.gitted/objects/29/29de282ce999e95183aedac6451d3384559c4b
index ad84f0854..ad84f0854 100644
--- a/tests/resources/attr/.gitted/objects/29/29de282ce999e95183aedac6451d3384559c4b
+++ b/tests-clar/resources/attr/.gitted/objects/29/29de282ce999e95183aedac6451d3384559c4b
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/2b/40c5aca159b04ea8d20ffe36cdf8b09369b14a b/tests-clar/resources/attr/.gitted/objects/2b/40c5aca159b04ea8d20ffe36cdf8b09369b14a
index 0e2368069..0e2368069 100644
--- a/tests/resources/attr/.gitted/objects/2b/40c5aca159b04ea8d20ffe36cdf8b09369b14a
+++ b/tests-clar/resources/attr/.gitted/objects/2b/40c5aca159b04ea8d20ffe36cdf8b09369b14a
diff --git a/tests/resources/attr/.gitted/objects/2c/66e14f77196ea763fb1e41612c1aa2bc2d8ed2 b/tests-clar/resources/attr/.gitted/objects/2c/66e14f77196ea763fb1e41612c1aa2bc2d8ed2
index 4b75d50eb..4b75d50eb 100644
--- a/tests/resources/attr/.gitted/objects/2c/66e14f77196ea763fb1e41612c1aa2bc2d8ed2
+++ b/tests-clar/resources/attr/.gitted/objects/2c/66e14f77196ea763fb1e41612c1aa2bc2d8ed2
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/2d/e7dfe3588f3c7e9ad59e7d50ba90e3329df9d9 b/tests-clar/resources/attr/.gitted/objects/2d/e7dfe3588f3c7e9ad59e7d50ba90e3329df9d9
index e0fd0468e..e0fd0468e 100644
--- a/tests/resources/attr/.gitted/objects/2d/e7dfe3588f3c7e9ad59e7d50ba90e3329df9d9
+++ b/tests-clar/resources/attr/.gitted/objects/2d/e7dfe3588f3c7e9ad59e7d50ba90e3329df9d9
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/37/0fe9ec224ce33e71f9e5ec2bd1142ce9937a6a b/tests-clar/resources/attr/.gitted/objects/37/0fe9ec224ce33e71f9e5ec2bd1142ce9937a6a
new file mode 100644
index 000000000..9c37c5946
--- /dev/null
+++ b/tests-clar/resources/attr/.gitted/objects/37/0fe9ec224ce33e71f9e5ec2bd1142ce9937a6a
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/3a/6df026462ebafe455af9867d27eda20a9e0974 b/tests-clar/resources/attr/.gitted/objects/3a/6df026462ebafe455af9867d27eda20a9e0974
new file mode 100644
index 000000000..c74add826
--- /dev/null
+++ b/tests-clar/resources/attr/.gitted/objects/3a/6df026462ebafe455af9867d27eda20a9e0974
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/3b/74db7ab381105dc0d28f8295a77f6a82989292 b/tests-clar/resources/attr/.gitted/objects/3b/74db7ab381105dc0d28f8295a77f6a82989292
index e5cef35fa..e5cef35fa 100644
--- a/tests/resources/attr/.gitted/objects/3b/74db7ab381105dc0d28f8295a77f6a82989292
+++ b/tests-clar/resources/attr/.gitted/objects/3b/74db7ab381105dc0d28f8295a77f6a82989292
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/3e/42ffc54a663f9401cc25843d6c0e71a33e4249 b/tests-clar/resources/attr/.gitted/objects/3e/42ffc54a663f9401cc25843d6c0e71a33e4249
index 091d79b14..091d79b14 100644
--- a/tests/resources/attr/.gitted/objects/3e/42ffc54a663f9401cc25843d6c0e71a33e4249
+++ b/tests-clar/resources/attr/.gitted/objects/3e/42ffc54a663f9401cc25843d6c0e71a33e4249
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/45/141a79a77842c59a63229403220a4e4be74e3d b/tests-clar/resources/attr/.gitted/objects/45/141a79a77842c59a63229403220a4e4be74e3d
index 5b58ef024..5b58ef024 100644
--- a/tests/resources/attr/.gitted/objects/45/141a79a77842c59a63229403220a4e4be74e3d
+++ b/tests-clar/resources/attr/.gitted/objects/45/141a79a77842c59a63229403220a4e4be74e3d
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/45/5a314fa848d52ae1f11d254da4f60858fc97f4 b/tests-clar/resources/attr/.gitted/objects/45/5a314fa848d52ae1f11d254da4f60858fc97f4
new file mode 100644
index 000000000..f90f0d79c
--- /dev/null
+++ b/tests-clar/resources/attr/.gitted/objects/45/5a314fa848d52ae1f11d254da4f60858fc97f4
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests-clar/resources/attr/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
index 7ca4ceed5..7ca4ceed5 100644
--- a/tests/resources/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
+++ b/tests-clar/resources/attr/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/4d/713dc48e6b1bd75b0d61ad078ba9ca3a56745d b/tests-clar/resources/attr/.gitted/objects/4d/713dc48e6b1bd75b0d61ad078ba9ca3a56745d
new file mode 100644
index 000000000..eb1e8d0c5
--- /dev/null
+++ b/tests-clar/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-clar/resources/attr/.gitted/objects/4e/49ba8c5b6c32ff28cd9dcb60be34df50fcc485 b/tests-clar/resources/attr/.gitted/objects/4e/49ba8c5b6c32ff28cd9dcb60be34df50fcc485
new file mode 100644
index 000000000..6fcc549b4
--- /dev/null
+++ b/tests-clar/resources/attr/.gitted/objects/4e/49ba8c5b6c32ff28cd9dcb60be34df50fcc485
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/55/6f8c827b8e4a02ad5cab77dca2bcb3e226b0b3 b/tests-clar/resources/attr/.gitted/objects/55/6f8c827b8e4a02ad5cab77dca2bcb3e226b0b3
index 4bcff1faa..4bcff1faa 100644
--- a/tests/resources/attr/.gitted/objects/55/6f8c827b8e4a02ad5cab77dca2bcb3e226b0b3
+++ b/tests-clar/resources/attr/.gitted/objects/55/6f8c827b8e4a02ad5cab77dca2bcb3e226b0b3
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/58/19a185d77b03325aaf87cafc771db36f6ddca7 b/tests-clar/resources/attr/.gitted/objects/58/19a185d77b03325aaf87cafc771db36f6ddca7
index fe34eb63a..fe34eb63a 100644
--- a/tests/resources/attr/.gitted/objects/58/19a185d77b03325aaf87cafc771db36f6ddca7
+++ b/tests-clar/resources/attr/.gitted/objects/58/19a185d77b03325aaf87cafc771db36f6ddca7
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/60/5812ab7fe421fdd325a935d35cb06a9234a7d7 b/tests-clar/resources/attr/.gitted/objects/60/5812ab7fe421fdd325a935d35cb06a9234a7d7
index b0cc51ee6..b0cc51ee6 100644
--- a/tests/resources/attr/.gitted/objects/60/5812ab7fe421fdd325a935d35cb06a9234a7d7
+++ b/tests-clar/resources/attr/.gitted/objects/60/5812ab7fe421fdd325a935d35cb06a9234a7d7
diff --git a/tests/resources/attr/.gitted/objects/6b/ab5c79cd5140d0f800917f550eb2a3dc32b0da b/tests-clar/resources/attr/.gitted/objects/6b/ab5c79cd5140d0f800917f550eb2a3dc32b0da
index f51e11ccc..f51e11ccc 100644
--- a/tests/resources/attr/.gitted/objects/6b/ab5c79cd5140d0f800917f550eb2a3dc32b0da
+++ b/tests-clar/resources/attr/.gitted/objects/6b/ab5c79cd5140d0f800917f550eb2a3dc32b0da
diff --git a/tests-clar/resources/attr/.gitted/objects/6d/968d62c89c7d9ea23a4c9a7b665d017c3d8ffd b/tests-clar/resources/attr/.gitted/objects/6d/968d62c89c7d9ea23a4c9a7b665d017c3d8ffd
new file mode 100644
index 000000000..e832241c9
--- /dev/null
+++ b/tests-clar/resources/attr/.gitted/objects/6d/968d62c89c7d9ea23a4c9a7b665d017c3d8ffd
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/71/7fc31f6b84f9d6fc3a4edbca259d7fc92beee2 b/tests-clar/resources/attr/.gitted/objects/71/7fc31f6b84f9d6fc3a4edbca259d7fc92beee2
new file mode 100644
index 000000000..a80265cac
--- /dev/null
+++ b/tests-clar/resources/attr/.gitted/objects/71/7fc31f6b84f9d6fc3a4edbca259d7fc92beee2
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/93/61f40bb97239cf55811892e14de2e344168ba1 b/tests-clar/resources/attr/.gitted/objects/93/61f40bb97239cf55811892e14de2e344168ba1
new file mode 100644
index 000000000..4b57836cd
--- /dev/null
+++ b/tests-clar/resources/attr/.gitted/objects/93/61f40bb97239cf55811892e14de2e344168ba1
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/94/da4faa0a6bfb8ee6ccf7153801a69202b31857 b/tests-clar/resources/attr/.gitted/objects/94/da4faa0a6bfb8ee6ccf7153801a69202b31857
index a9ddf5d20..a9ddf5d20 100644
--- a/tests/resources/attr/.gitted/objects/94/da4faa0a6bfb8ee6ccf7153801a69202b31857
+++ b/tests-clar/resources/attr/.gitted/objects/94/da4faa0a6bfb8ee6ccf7153801a69202b31857
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/96/089fd31ce1d3ee2afb0ba09ba063066932f027 b/tests-clar/resources/attr/.gitted/objects/96/089fd31ce1d3ee2afb0ba09ba063066932f027
new file mode 100644
index 000000000..efa62f912
--- /dev/null
+++ b/tests-clar/resources/attr/.gitted/objects/96/089fd31ce1d3ee2afb0ba09ba063066932f027
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9 b/tests-clar/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9
index 8f5acc70a..8f5acc70a 100644
--- a/tests/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9
+++ b/tests-clar/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/9e/5bdc47d6a80f2be0ea3049ad74231b94609242 b/tests-clar/resources/attr/.gitted/objects/9e/5bdc47d6a80f2be0ea3049ad74231b94609242
new file mode 100644
index 000000000..d6385ec8d
--- /dev/null
+++ b/tests-clar/resources/attr/.gitted/objects/9e/5bdc47d6a80f2be0ea3049ad74231b94609242
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9 b/tests-clar/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9
index 7663ad0ad..7663ad0ad 100644
--- a/tests/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9
+++ b/tests-clar/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320 b/tests-clar/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320
index d898ae9b8..d898ae9b8 100644
--- a/tests/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320
+++ b/tests-clar/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/a5/d76cad53f66f1312bd995909a5bab3c0820770 b/tests-clar/resources/attr/.gitted/objects/a5/d76cad53f66f1312bd995909a5bab3c0820770
index cd6a389f9..cd6a389f9 100644
--- a/tests/resources/attr/.gitted/objects/a5/d76cad53f66f1312bd995909a5bab3c0820770
+++ b/tests-clar/resources/attr/.gitted/objects/a5/d76cad53f66f1312bd995909a5bab3c0820770
diff --git a/tests-clar/resources/attr/.gitted/objects/a9/7cc019851d401a4f1d091cb91a15890a0dd1ba b/tests-clar/resources/attr/.gitted/objects/a9/7cc019851d401a4f1d091cb91a15890a0dd1ba
new file mode 100644
index 000000000..1a7ec0c55
--- /dev/null
+++ b/tests-clar/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/c0/091889c0c77142b87a1fa5123a6398a61d33e7 b/tests-clar/resources/attr/.gitted/objects/c0/091889c0c77142b87a1fa5123a6398a61d33e7
index 11dc63c79..11dc63c79 100644
--- a/tests/resources/attr/.gitted/objects/c0/091889c0c77142b87a1fa5123a6398a61d33e7
+++ b/tests-clar/resources/attr/.gitted/objects/c0/091889c0c77142b87a1fa5123a6398a61d33e7
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/c4/85abe35abd4aa6fd83b076a78bbea9e2e7e06c b/tests-clar/resources/attr/.gitted/objects/c4/85abe35abd4aa6fd83b076a78bbea9e2e7e06c
index 58569ca0e..58569ca0e 100644
--- a/tests/resources/attr/.gitted/objects/c4/85abe35abd4aa6fd83b076a78bbea9e2e7e06c
+++ b/tests-clar/resources/attr/.gitted/objects/c4/85abe35abd4aa6fd83b076a78bbea9e2e7e06c
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/c7/aadd770d5907a8475c29e9ee21a27b88bf675d b/tests-clar/resources/attr/.gitted/objects/c7/aadd770d5907a8475c29e9ee21a27b88bf675d
index 39aedb7d9..39aedb7d9 100644
--- a/tests/resources/attr/.gitted/objects/c7/aadd770d5907a8475c29e9ee21a27b88bf675d
+++ b/tests-clar/resources/attr/.gitted/objects/c7/aadd770d5907a8475c29e9ee21a27b88bf675d
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/c9/6bbb2c2557a8325ae1559e3ba79cdcecb23076 b/tests-clar/resources/attr/.gitted/objects/c9/6bbb2c2557a8325ae1559e3ba79cdcecb23076
new file mode 100644
index 000000000..589f9ad31
--- /dev/null
+++ b/tests-clar/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-clar/resources/attr/.gitted/objects/ce/39a97a7fb1fa90bcf5e711249c1e507476ae0e b/tests-clar/resources/attr/.gitted/objects/ce/39a97a7fb1fa90bcf5e711249c1e507476ae0e
new file mode 100644
index 000000000..1005f944a
--- /dev/null
+++ b/tests-clar/resources/attr/.gitted/objects/ce/39a97a7fb1fa90bcf5e711249c1e507476ae0e
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/d5/7da33c16b14326ecb05d19bbea908f5e4c47d9 b/tests-clar/resources/attr/.gitted/objects/d5/7da33c16b14326ecb05d19bbea908f5e4c47d9
index b96d40c24..b96d40c24 100644
--- a/tests/resources/attr/.gitted/objects/d5/7da33c16b14326ecb05d19bbea908f5e4c47d9
+++ b/tests-clar/resources/attr/.gitted/objects/d5/7da33c16b14326ecb05d19bbea908f5e4c47d9
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2 b/tests-clar/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2
index 83f3b726d..83f3b726d 100644
--- a/tests/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2
+++ b/tests-clar/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/dc/cada462d3df8ac6de596fb8c896aba9344f941 b/tests-clar/resources/attr/.gitted/objects/dc/cada462d3df8ac6de596fb8c896aba9344f941
index ef62f8b9d..ef62f8b9d 100644
--- a/tests/resources/attr/.gitted/objects/dc/cada462d3df8ac6de596fb8c896aba9344f941
+++ b/tests-clar/resources/attr/.gitted/objects/dc/cada462d3df8ac6de596fb8c896aba9344f941
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/de/863bff4976c9ed7e17a4da0fd524908dc84049 b/tests-clar/resources/attr/.gitted/objects/de/863bff4976c9ed7e17a4da0fd524908dc84049
new file mode 100644
index 000000000..7d9b8551f
--- /dev/null
+++ b/tests-clar/resources/attr/.gitted/objects/de/863bff4976c9ed7e17a4da0fd524908dc84049
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/e5/63cf4758f0d646f1b14b76016aa17fa9e549a4 b/tests-clar/resources/attr/.gitted/objects/e5/63cf4758f0d646f1b14b76016aa17fa9e549a4
index 1bc1f0f0b..1bc1f0f0b 100644
--- a/tests/resources/attr/.gitted/objects/e5/63cf4758f0d646f1b14b76016aa17fa9e549a4
+++ b/tests-clar/resources/attr/.gitted/objects/e5/63cf4758f0d646f1b14b76016aa17fa9e549a4
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/ec/b97df2a174987475ac816e3847fc8e9f6c596b b/tests-clar/resources/attr/.gitted/objects/ec/b97df2a174987475ac816e3847fc8e9f6c596b
new file mode 100644
index 000000000..44d703b2e
--- /dev/null
+++ b/tests-clar/resources/attr/.gitted/objects/ec/b97df2a174987475ac816e3847fc8e9f6c596b
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/ed/f3dcee4003d71f139777898882ccd097e34c53 b/tests-clar/resources/attr/.gitted/objects/ed/f3dcee4003d71f139777898882ccd097e34c53
new file mode 100644
index 000000000..d28184670
--- /dev/null
+++ b/tests-clar/resources/attr/.gitted/objects/ed/f3dcee4003d71f139777898882ccd097e34c53
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/f2/c6d717cf4a5a3e6b02684155ab07b766982165 b/tests-clar/resources/attr/.gitted/objects/f2/c6d717cf4a5a3e6b02684155ab07b766982165
index 27a25dc86..27a25dc86 100644
--- a/tests/resources/attr/.gitted/objects/f2/c6d717cf4a5a3e6b02684155ab07b766982165
+++ b/tests-clar/resources/attr/.gitted/objects/f2/c6d717cf4a5a3e6b02684155ab07b766982165
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/f5/b0af1fb4f5c0cd7aad880711d368a07333c307 b/tests-clar/resources/attr/.gitted/objects/f5/b0af1fb4f5c0cd7aad880711d368a07333c307
new file mode 100644
index 000000000..21faeb8a2
--- /dev/null
+++ b/tests-clar/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-clar/resources/attr/.gitted/objects/fb/5067b1aef3ac1ada4b379dbcb7d17255df7d78
index 6c8ff837e..6c8ff837e 100644
--- a/tests/resources/attr/.gitted/objects/fb/5067b1aef3ac1ada4b379dbcb7d17255df7d78
+++ b/tests-clar/resources/attr/.gitted/objects/fb/5067b1aef3ac1ada4b379dbcb7d17255df7d78
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/fe/773770c5a6cc7185580c9204b1ff18a33ff3fc b/tests-clar/resources/attr/.gitted/objects/fe/773770c5a6cc7185580c9204b1ff18a33ff3fc
new file mode 100644
index 000000000..e6fcbc0b3
--- /dev/null
+++ b/tests-clar/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-clar/resources/attr/.gitted/objects/ff/69f8639ce2e6010b3f33a74160aad98b48da2b
index b736c0b2b..b736c0b2b 100644
--- a/tests/resources/attr/.gitted/objects/ff/69f8639ce2e6010b3f33a74160aad98b48da2b
+++ b/tests-clar/resources/attr/.gitted/objects/ff/69f8639ce2e6010b3f33a74160aad98b48da2b
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/refs/heads/master b/tests-clar/resources/attr/.gitted/refs/heads/master
new file mode 100644
index 000000000..8768776b3
--- /dev/null
+++ b/tests-clar/resources/attr/.gitted/refs/heads/master
@@ -0,0 +1 @@
+24fa9a9fc4e202313e24b648087495441dab432b
diff --git a/tests/resources/attr/attr0 b/tests-clar/resources/attr/attr0
index 556f8c827..556f8c827 100644
--- a/tests/resources/attr/attr0
+++ b/tests-clar/resources/attr/attr0
diff --git a/tests/resources/attr/attr1 b/tests-clar/resources/attr/attr1
index 3b74db7ab..3b74db7ab 100644
--- a/tests/resources/attr/attr1
+++ b/tests-clar/resources/attr/attr1
diff --git a/tests/resources/attr/attr2 b/tests-clar/resources/attr/attr2
index 2c66e14f7..2c66e14f7 100644
--- a/tests/resources/attr/attr2
+++ b/tests-clar/resources/attr/attr2
diff --git a/tests/resources/attr/attr3 b/tests-clar/resources/attr/attr3
index c485abe35..c485abe35 100644
--- a/tests/resources/attr/attr3
+++ b/tests-clar/resources/attr/attr3
diff --git a/tests/resources/attr/binfile b/tests-clar/resources/attr/binfile
index d800886d9..d800886d9 100644
--- a/tests/resources/attr/binfile
+++ b/tests-clar/resources/attr/binfile
diff --git a/tests/resources/attr/dir/file b/tests-clar/resources/attr/dir/file
index e69de29bb..e69de29bb 100644
--- a/tests/resources/attr/dir/file
+++ b/tests-clar/resources/attr/dir/file
diff --git a/tests/resources/attr/file b/tests-clar/resources/attr/file
index 45b983be3..45b983be3 100644
--- a/tests/resources/attr/file
+++ b/tests-clar/resources/attr/file
diff --git a/tests/resources/attr/gitattributes b/tests-clar/resources/attr/gitattributes
index c0c2a56d0..e038983ec 100644
--- a/tests/resources/attr/gitattributes
+++ b/tests-clar/resources/attr/gitattributes
@@ -23,3 +23,7 @@ macro* macro2 macro2 macro2
[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-clar/resources/attr/gitignore
index 546d48f3a..546d48f3a 100644
--- a/tests/resources/attr/gitignore
+++ b/tests-clar/resources/attr/gitignore
diff --git a/tests/resources/attr/ign b/tests-clar/resources/attr/ign
index 592fd2594..592fd2594 100644
--- a/tests/resources/attr/ign
+++ b/tests-clar/resources/attr/ign
diff --git a/tests/resources/attr/macro_bad b/tests-clar/resources/attr/macro_bad
index 5819a185d..5819a185d 100644
--- a/tests/resources/attr/macro_bad
+++ b/tests-clar/resources/attr/macro_bad
diff --git a/tests/resources/attr/macro_test b/tests-clar/resources/attr/macro_test
index ff69f8639..ff69f8639 100644
--- a/tests/resources/attr/macro_test
+++ b/tests-clar/resources/attr/macro_test
diff --git a/tests/resources/attr/root_test1 b/tests-clar/resources/attr/root_test1
index 45141a79a..45141a79a 100644
--- a/tests/resources/attr/root_test1
+++ b/tests-clar/resources/attr/root_test1
diff --git a/tests-clar/resources/attr/root_test2 b/tests-clar/resources/attr/root_test2
new file mode 100644
index 000000000..4d713dc48
--- /dev/null
+++ b/tests-clar/resources/attr/root_test2
@@ -0,0 +1,6 @@
+Hello from the root
+
+Some additional lines
+
+Down here below
+
diff --git a/tests-clar/resources/attr/root_test3 b/tests-clar/resources/attr/root_test3
new file mode 100644
index 000000000..108bb4e7f
--- /dev/null
+++ b/tests-clar/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-clar/resources/attr/root_test4.txt b/tests-clar/resources/attr/root_test4.txt
new file mode 100644
index 000000000..fe773770c
--- /dev/null
+++ b/tests-clar/resources/attr/root_test4.txt
@@ -0,0 +1,14 @@
+Here is some stuff at the start
+
+This should go in one hunk
+
+Some additional lines
+
+Down here below the other lines
+
+With even more at the end
+
+Followed by a second hunk of stuff
+
+That happens down here
+
diff --git a/tests/resources/attr/sub/.gitattributes b/tests-clar/resources/attr/sub/.gitattributes
index 329c1c5b8..329c1c5b8 100644
--- a/tests/resources/attr/sub/.gitattributes
+++ b/tests-clar/resources/attr/sub/.gitattributes
diff --git a/tests/resources/attr/sub/abc b/tests-clar/resources/attr/sub/abc
index 3e42ffc54..3e42ffc54 100644
--- a/tests/resources/attr/sub/abc
+++ b/tests-clar/resources/attr/sub/abc
diff --git a/tests/resources/attr/sub/dir/file b/tests-clar/resources/attr/sub/dir/file
index e69de29bb..e69de29bb 100644
--- a/tests/resources/attr/sub/dir/file
+++ b/tests-clar/resources/attr/sub/dir/file
diff --git a/tests/resources/attr/sub/file b/tests-clar/resources/attr/sub/file
index 45b983be3..45b983be3 100644
--- a/tests/resources/attr/sub/file
+++ b/tests-clar/resources/attr/sub/file
diff --git a/tests/resources/attr/sub/ign b/tests-clar/resources/attr/sub/ign
index 592fd2594..592fd2594 100644
--- a/tests/resources/attr/sub/ign
+++ b/tests-clar/resources/attr/sub/ign
diff --git a/tests-clar/resources/attr/sub/sub/.gitattributes b/tests-clar/resources/attr/sub/sub/.gitattributes
new file mode 100644
index 000000000..55225e4d6
--- /dev/null
+++ b/tests-clar/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-clar/resources/attr/sub/sub/dir
index e69de29bb..e69de29bb 100644
--- a/tests/resources/attr/sub/sub/dir
+++ b/tests-clar/resources/attr/sub/sub/dir
diff --git a/tests/resources/attr/sub/sub/file b/tests-clar/resources/attr/sub/sub/file
index 45b983be3..45b983be3 100644
--- a/tests/resources/attr/sub/sub/file
+++ b/tests-clar/resources/attr/sub/sub/file
diff --git a/tests/resources/attr/sub/sub/subsub.txt b/tests-clar/resources/attr/sub/sub/subsub.txt
index 9e5bdc47d..9e5bdc47d 100644
--- a/tests/resources/attr/sub/sub/subsub.txt
+++ b/tests-clar/resources/attr/sub/sub/subsub.txt
diff --git a/tests/resources/attr/sub/subdir_test1 b/tests-clar/resources/attr/sub/subdir_test1
index e563cf475..e563cf475 100644
--- a/tests/resources/attr/sub/subdir_test1
+++ b/tests-clar/resources/attr/sub/subdir_test1
diff --git a/tests/resources/attr/sub/subdir_test2.txt b/tests-clar/resources/attr/sub/subdir_test2.txt
index fb5067b1a..fb5067b1a 100644
--- a/tests/resources/attr/sub/subdir_test2.txt
+++ b/tests-clar/resources/attr/sub/subdir_test2.txt
diff --git a/tests/resources/bad_tag.git/HEAD b/tests-clar/resources/attr_index/.gitted/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests/resources/bad_tag.git/HEAD
+++ b/tests-clar/resources/attr_index/.gitted/HEAD
diff --git a/tests/resources/status/.gitted/config b/tests-clar/resources/attr_index/.gitted/config
index af107929f..af107929f 100644
--- a/tests/resources/status/.gitted/config
+++ b/tests-clar/resources/attr_index/.gitted/config
diff --git a/tests/resources/empty_bare.git/description b/tests-clar/resources/attr_index/.gitted/description
index 498b267a8..498b267a8 100644
--- a/tests/resources/empty_bare.git/description
+++ b/tests-clar/resources/attr_index/.gitted/description
diff --git a/tests-clar/resources/attr_index/.gitted/index b/tests-clar/resources/attr_index/.gitted/index
new file mode 100644
index 000000000..d87480332
--- /dev/null
+++ b/tests-clar/resources/attr_index/.gitted/index
Binary files differ
diff --git a/tests/resources/empty_bare.git/info/exclude b/tests-clar/resources/attr_index/.gitted/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests/resources/empty_bare.git/info/exclude
+++ b/tests-clar/resources/attr_index/.gitted/info/exclude
diff --git a/tests-clar/resources/attr_index/.gitted/info/refs b/tests-clar/resources/attr_index/.gitted/info/refs
new file mode 100644
index 000000000..60feca293
--- /dev/null
+++ b/tests-clar/resources/attr_index/.gitted/info/refs
@@ -0,0 +1 @@
+58f7cf825b553ef7c26e5b9f8a23599c1a9ca296 refs/heads/master
diff --git a/tests-clar/resources/attr_index/.gitted/logs/HEAD b/tests-clar/resources/attr_index/.gitted/logs/HEAD
new file mode 100644
index 000000000..ffd298c04
--- /dev/null
+++ b/tests-clar/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-clar/resources/attr_index/.gitted/logs/refs/heads/master b/tests-clar/resources/attr_index/.gitted/logs/refs/heads/master
new file mode 100644
index 000000000..ffd298c04
--- /dev/null
+++ b/tests-clar/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-clar/resources/attr_index/.gitted/objects/38/12cfef36615db1788d4e63f90028007e17a348 b/tests-clar/resources/attr_index/.gitted/objects/38/12cfef36615db1788d4e63f90028007e17a348
new file mode 100644
index 000000000..ee2991571
--- /dev/null
+++ b/tests-clar/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-clar/resources/attr_index/.gitted/objects/59/d942b8be2784bc96db9b22202c10815c9a077b b/tests-clar/resources/attr_index/.gitted/objects/59/d942b8be2784bc96db9b22202c10815c9a077b
new file mode 100644
index 000000000..ff33737db
--- /dev/null
+++ b/tests-clar/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-clar/resources/attr_index/.gitted/objects/cd/f17ea3fe625ef812f4dce7f423f4f299287505 b/tests-clar/resources/attr_index/.gitted/objects/cd/f17ea3fe625ef812f4dce7f423f4f299287505
new file mode 100644
index 000000000..2a410057e
--- /dev/null
+++ b/tests-clar/resources/attr_index/.gitted/objects/cd/f17ea3fe625ef812f4dce7f423f4f299287505
Binary files differ
diff --git a/tests-clar/resources/attr_index/.gitted/objects/f7/2502ddd01412bb20796ff812af56fd53b82b52 b/tests-clar/resources/attr_index/.gitted/objects/f7/2502ddd01412bb20796ff812af56fd53b82b52
new file mode 100644
index 000000000..048928000
--- /dev/null
+++ b/tests-clar/resources/attr_index/.gitted/objects/f7/2502ddd01412bb20796ff812af56fd53b82b52
Binary files differ
diff --git a/tests-clar/resources/attr_index/.gitted/objects/info/packs b/tests-clar/resources/attr_index/.gitted/objects/info/packs
new file mode 100644
index 000000000..559dc741c
--- /dev/null
+++ b/tests-clar/resources/attr_index/.gitted/objects/info/packs
@@ -0,0 +1,2 @@
+P pack-4e6438607204ce78827e3885594b2c0bb4f13895.pack
+
diff --git a/tests-clar/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.idx b/tests-clar/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.idx
new file mode 100644
index 000000000..fbef4aa1d
--- /dev/null
+++ b/tests-clar/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.idx
Binary files differ
diff --git a/tests-clar/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.pack b/tests-clar/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.pack
new file mode 100644
index 000000000..09c9e06d9
--- /dev/null
+++ b/tests-clar/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.pack
Binary files differ
diff --git a/tests-clar/resources/attr_index/.gitted/packed-refs b/tests-clar/resources/attr_index/.gitted/packed-refs
new file mode 100644
index 000000000..6b3e4decf
--- /dev/null
+++ b/tests-clar/resources/attr_index/.gitted/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+58f7cf825b553ef7c26e5b9f8a23599c1a9ca296 refs/heads/master
diff --git a/tests-clar/resources/attr_index/.gitted/refs/heads/master b/tests-clar/resources/attr_index/.gitted/refs/heads/master
new file mode 100644
index 000000000..9b7562931
--- /dev/null
+++ b/tests-clar/resources/attr_index/.gitted/refs/heads/master
@@ -0,0 +1 @@
+3812cfef36615db1788d4e63f90028007e17a348
diff --git a/tests-clar/resources/attr_index/README.md b/tests-clar/resources/attr_index/README.md
new file mode 100644
index 000000000..59d942b8b
--- /dev/null
+++ b/tests-clar/resources/attr_index/README.md
@@ -0,0 +1 @@
+This is contains tests for when the index and work dir differ
diff --git a/tests-clar/resources/attr_index/README.txt b/tests-clar/resources/attr_index/README.txt
new file mode 100644
index 000000000..874c12b79
--- /dev/null
+++ b/tests-clar/resources/attr_index/README.txt
@@ -0,0 +1 @@
+This contains files for testing when the index and the workdir differ
diff --git a/tests-clar/resources/attr_index/gitattributes b/tests-clar/resources/attr_index/gitattributes
new file mode 100644
index 000000000..cdf17ea3f
--- /dev/null
+++ b/tests-clar/resources/attr_index/gitattributes
@@ -0,0 +1,4 @@
+* bar
+*.txt -foo beep=10
+*.md blargh=goop -bar
+
diff --git a/tests-clar/resources/attr_index/sub/sub/.gitattributes b/tests-clar/resources/attr_index/sub/sub/.gitattributes
new file mode 100644
index 000000000..060c9a261
--- /dev/null
+++ b/tests-clar/resources/attr_index/sub/sub/.gitattributes
@@ -0,0 +1,3 @@
+*.txt another=one again
+*.md bar=1234
+
diff --git a/tests-clar/resources/attr_index/sub/sub/README.md b/tests-clar/resources/attr_index/sub/sub/README.md
new file mode 100644
index 000000000..59652e349
--- /dev/null
+++ b/tests-clar/resources/attr_index/sub/sub/README.md
@@ -0,0 +1 @@
+More testing
diff --git a/tests-clar/resources/attr_index/sub/sub/README.txt b/tests-clar/resources/attr_index/sub/sub/README.txt
new file mode 100644
index 000000000..59652e349
--- /dev/null
+++ b/tests-clar/resources/attr_index/sub/sub/README.txt
@@ -0,0 +1 @@
+More testing
diff --git a/tests/resources/empty_bare.git/HEAD b/tests-clar/resources/bad_tag.git/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests/resources/empty_bare.git/HEAD
+++ b/tests-clar/resources/bad_tag.git/HEAD
diff --git a/tests/resources/bad_tag.git/config b/tests-clar/resources/bad_tag.git/config
index 2f8958058..2f8958058 100644
--- a/tests/resources/bad_tag.git/config
+++ b/tests-clar/resources/bad_tag.git/config
diff --git a/tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.idx b/tests-clar/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.idx
index c404aa15b..c404aa15b 100644
--- a/tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.idx
+++ b/tests-clar/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-clar/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.pack
index 90eac5032..90eac5032 100644
--- a/tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.pack
+++ b/tests-clar/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.pack
Binary files differ
diff --git a/tests/resources/bad_tag.git/packed-refs b/tests-clar/resources/bad_tag.git/packed-refs
index f9fd2fd4a..f9fd2fd4a 100644
--- a/tests/resources/bad_tag.git/packed-refs
+++ b/tests-clar/resources/bad_tag.git/packed-refs
diff --git a/tests/resources/bad_tag.git/refs/dummy-marker.txt b/tests-clar/resources/bad_tag.git/refs/dummy-marker.txt
index e69de29bb..e69de29bb 100644
--- a/tests/resources/bad_tag.git/refs/dummy-marker.txt
+++ b/tests-clar/resources/bad_tag.git/refs/dummy-marker.txt
diff --git a/tests/resources/big.index b/tests-clar/resources/big.index
index 66932f14b..66932f14b 100644
--- a/tests/resources/big.index
+++ b/tests-clar/resources/big.index
Binary files differ
diff --git a/tests/resources/config/.gitconfig b/tests-clar/resources/config/.gitconfig
index fa72bddfc..fa72bddfc 100644
--- a/tests/resources/config/.gitconfig
+++ b/tests-clar/resources/config/.gitconfig
diff --git a/tests/resources/config/config0 b/tests-clar/resources/config/config0
index 85235c501..85235c501 100644
--- a/tests/resources/config/config0
+++ b/tests-clar/resources/config/config0
diff --git a/tests/resources/config/config1 b/tests-clar/resources/config/config1
index 211dc9e7d..211dc9e7d 100644
--- a/tests/resources/config/config1
+++ b/tests-clar/resources/config/config1
diff --git a/tests/resources/config/config10 b/tests-clar/resources/config/config10
index dde17911b..dde17911b 100644
--- a/tests/resources/config/config10
+++ b/tests-clar/resources/config/config10
diff --git a/tests-clar/resources/config/config11 b/tests-clar/resources/config/config11
new file mode 100644
index 000000000..880c94589
--- /dev/null
+++ b/tests-clar/resources/config/config11
@@ -0,0 +1,3 @@
+[remote "fancy"]
+ url = git://github.com/libgit2/libgit2
+ url = git://git.example.com/libgit2
diff --git a/tests-clar/resources/config/config12 b/tests-clar/resources/config/config12
new file mode 100644
index 000000000..b57a81b08
--- /dev/null
+++ b/tests-clar/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-clar/resources/config/config13 b/tests-clar/resources/config/config13
new file mode 100644
index 000000000..c1e0c5647
--- /dev/null
+++ b/tests-clar/resources/config/config13
@@ -0,0 +1,2 @@
+[core]
+ editor = \"C:/Program Files/Nonsense/bah.exe\" \"--some option\"
diff --git a/tests/resources/config/config2 b/tests-clar/resources/config/config2
index 60a389827..60a389827 100644
--- a/tests/resources/config/config2
+++ b/tests-clar/resources/config/config2
diff --git a/tests/resources/config/config3 b/tests-clar/resources/config/config3
index 44a5e50ea..44a5e50ea 100644
--- a/tests/resources/config/config3
+++ b/tests-clar/resources/config/config3
diff --git a/tests/resources/config/config4 b/tests-clar/resources/config/config4
index 741fa0ffd..741fa0ffd 100644
--- a/tests/resources/config/config4
+++ b/tests-clar/resources/config/config4
diff --git a/tests/resources/config/config5 b/tests-clar/resources/config/config5
index 8ab60ccec..8ab60ccec 100644
--- a/tests/resources/config/config5
+++ b/tests-clar/resources/config/config5
diff --git a/tests/resources/config/config6 b/tests-clar/resources/config/config6
index 0f8f90ac9..0f8f90ac9 100644
--- a/tests/resources/config/config6
+++ b/tests-clar/resources/config/config6
diff --git a/tests/resources/config/config7 b/tests-clar/resources/config/config7
index 6af6fcf25..6af6fcf25 100644
--- a/tests/resources/config/config7
+++ b/tests-clar/resources/config/config7
diff --git a/tests/resources/config/config8 b/tests-clar/resources/config/config8
index e69de29bb..e69de29bb 100644
--- a/tests/resources/config/config8
+++ b/tests-clar/resources/config/config8
diff --git a/tests/resources/config/config9 b/tests-clar/resources/config/config9
index fcaac424e..fcaac424e 100644
--- a/tests/resources/config/config9
+++ b/tests-clar/resources/config/config9
diff --git a/tests-clar/resources/duplicate.git/COMMIT_EDITMSG b/tests-clar/resources/duplicate.git/COMMIT_EDITMSG
new file mode 100644
index 000000000..01f9a2aac
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/COMMIT_EDITMSG
@@ -0,0 +1 @@
+commit
diff --git a/tests/resources/empty_standard_repo/.gitted/HEAD b/tests-clar/resources/duplicate.git/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests/resources/empty_standard_repo/.gitted/HEAD
+++ b/tests-clar/resources/duplicate.git/HEAD
diff --git a/tests-clar/resources/duplicate.git/config b/tests-clar/resources/duplicate.git/config
new file mode 100644
index 000000000..515f48362
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/config
@@ -0,0 +1,5 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
diff --git a/tests/resources/empty_standard_repo/.gitted/description b/tests-clar/resources/duplicate.git/description
index 498b267a8..498b267a8 100644
--- a/tests/resources/empty_standard_repo/.gitted/description
+++ b/tests-clar/resources/duplicate.git/description
diff --git a/tests-clar/resources/duplicate.git/hooks/applypatch-msg.sample b/tests-clar/resources/duplicate.git/hooks/applypatch-msg.sample
new file mode 100755
index 000000000..8b2a2fe84
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/hooks/applypatch-msg.sample
@@ -0,0 +1,15 @@
+#!/bin/sh
+#
+# An example hook script to check the commit log message taken by
+# applypatch from an e-mail message.
+#
+# The hook should exit with non-zero status after issuing an
+# appropriate message if it wants to stop the commit. The hook is
+# allowed to edit the commit message file.
+#
+# To enable this hook, rename this file to "applypatch-msg".
+
+. git-sh-setup
+test -x "$GIT_DIR/hooks/commit-msg" &&
+ exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"}
+:
diff --git a/tests-clar/resources/duplicate.git/hooks/commit-msg.sample b/tests-clar/resources/duplicate.git/hooks/commit-msg.sample
new file mode 100755
index 000000000..b58d1184a
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/hooks/commit-msg.sample
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+# An example hook script to check the commit log message.
+# Called by "git commit" with one argument, the name of the file
+# that has the commit message. The hook should exit with non-zero
+# status after issuing an appropriate message if it wants to stop the
+# commit. The hook is allowed to edit the commit message file.
+#
+# To enable this hook, rename this file to "commit-msg".
+
+# Uncomment the below to add a Signed-off-by line to the message.
+# Doing this in a hook is a bad idea in general, but the prepare-commit-msg
+# hook is more suited to it.
+#
+# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
+# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
+
+# This example catches duplicate Signed-off-by lines.
+
+test "" = "$(grep '^Signed-off-by: ' "$1" |
+ sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
+ echo >&2 Duplicate Signed-off-by lines.
+ exit 1
+}
diff --git a/tests-clar/resources/duplicate.git/hooks/post-update.sample b/tests-clar/resources/duplicate.git/hooks/post-update.sample
new file mode 100755
index 000000000..ec17ec193
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/hooks/post-update.sample
@@ -0,0 +1,8 @@
+#!/bin/sh
+#
+# An example hook script to prepare a packed repository for use over
+# dumb transports.
+#
+# To enable this hook, rename this file to "post-update".
+
+exec git update-server-info
diff --git a/tests-clar/resources/duplicate.git/hooks/pre-applypatch.sample b/tests-clar/resources/duplicate.git/hooks/pre-applypatch.sample
new file mode 100755
index 000000000..b1f187c2e
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/hooks/pre-applypatch.sample
@@ -0,0 +1,14 @@
+#!/bin/sh
+#
+# An example hook script to verify what is about to be committed
+# by applypatch from an e-mail message.
+#
+# The hook should exit with non-zero status after issuing an
+# appropriate message if it wants to stop the commit.
+#
+# To enable this hook, rename this file to "pre-applypatch".
+
+. git-sh-setup
+test -x "$GIT_DIR/hooks/pre-commit" &&
+ exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"}
+:
diff --git a/tests-clar/resources/duplicate.git/hooks/pre-commit.sample b/tests-clar/resources/duplicate.git/hooks/pre-commit.sample
new file mode 100755
index 000000000..18c482976
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/hooks/pre-commit.sample
@@ -0,0 +1,50 @@
+#!/bin/sh
+#
+# An example hook script to verify what is about to be committed.
+# Called by "git commit" with no arguments. The hook should
+# exit with non-zero status after issuing an appropriate message if
+# it wants to stop the commit.
+#
+# To enable this hook, rename this file to "pre-commit".
+
+if git rev-parse --verify HEAD >/dev/null 2>&1
+then
+ against=HEAD
+else
+ # Initial commit: diff against an empty tree object
+ against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
+fi
+
+# If you want to allow non-ascii filenames set this variable to true.
+allownonascii=$(git config hooks.allownonascii)
+
+# Redirect output to stderr.
+exec 1>&2
+
+# Cross platform projects tend to avoid non-ascii filenames; prevent
+# them from being added to the repository. We exploit the fact that the
+# printable range starts at the space character and ends with tilde.
+if [ "$allownonascii" != "true" ] &&
+ # Note that the use of brackets around a tr range is ok here, (it's
+ # even required, for portability to Solaris 10's /usr/bin/tr), since
+ # the square bracket bytes happen to fall in the designated range.
+ test $(git diff --cached --name-only --diff-filter=A -z $against |
+ LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
+then
+ echo "Error: Attempt to add a non-ascii file name."
+ echo
+ echo "This can cause problems if you want to work"
+ echo "with people on other platforms."
+ echo
+ echo "To be portable it is advisable to rename the file ..."
+ echo
+ echo "If you know what you are doing you can disable this"
+ echo "check using:"
+ echo
+ echo " git config hooks.allownonascii true"
+ echo
+ exit 1
+fi
+
+# If there are whitespace errors, print the offending file names and fail.
+exec git diff-index --check --cached $against --
diff --git a/tests-clar/resources/duplicate.git/hooks/pre-rebase.sample b/tests-clar/resources/duplicate.git/hooks/pre-rebase.sample
new file mode 100755
index 000000000..9773ed4cb
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/hooks/pre-rebase.sample
@@ -0,0 +1,169 @@
+#!/bin/sh
+#
+# Copyright (c) 2006, 2008 Junio C Hamano
+#
+# The "pre-rebase" hook is run just before "git rebase" starts doing
+# its job, and can prevent the command from running by exiting with
+# non-zero status.
+#
+# The hook is called with the following parameters:
+#
+# $1 -- the upstream the series was forked from.
+# $2 -- the branch being rebased (or empty when rebasing the current branch).
+#
+# This sample shows how to prevent topic branches that are already
+# merged to 'next' branch from getting rebased, because allowing it
+# would result in rebasing already published history.
+
+publish=next
+basebranch="$1"
+if test "$#" = 2
+then
+ topic="refs/heads/$2"
+else
+ topic=`git symbolic-ref HEAD` ||
+ exit 0 ;# we do not interrupt rebasing detached HEAD
+fi
+
+case "$topic" in
+refs/heads/??/*)
+ ;;
+*)
+ exit 0 ;# we do not interrupt others.
+ ;;
+esac
+
+# Now we are dealing with a topic branch being rebased
+# on top of master. Is it OK to rebase it?
+
+# Does the topic really exist?
+git show-ref -q "$topic" || {
+ echo >&2 "No such branch $topic"
+ exit 1
+}
+
+# Is topic fully merged to master?
+not_in_master=`git rev-list --pretty=oneline ^master "$topic"`
+if test -z "$not_in_master"
+then
+ echo >&2 "$topic is fully merged to master; better remove it."
+ exit 1 ;# we could allow it, but there is no point.
+fi
+
+# Is topic ever merged to next? If so you should not be rebasing it.
+only_next_1=`git rev-list ^master "^$topic" ${publish} | sort`
+only_next_2=`git rev-list ^master ${publish} | sort`
+if test "$only_next_1" = "$only_next_2"
+then
+ not_in_topic=`git rev-list "^$topic" master`
+ if test -z "$not_in_topic"
+ then
+ echo >&2 "$topic is already up-to-date with master"
+ exit 1 ;# we could allow it, but there is no point.
+ else
+ exit 0
+ fi
+else
+ not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"`
+ /usr/bin/perl -e '
+ my $topic = $ARGV[0];
+ my $msg = "* $topic has commits already merged to public branch:\n";
+ my (%not_in_next) = map {
+ /^([0-9a-f]+) /;
+ ($1 => 1);
+ } split(/\n/, $ARGV[1]);
+ for my $elem (map {
+ /^([0-9a-f]+) (.*)$/;
+ [$1 => $2];
+ } split(/\n/, $ARGV[2])) {
+ if (!exists $not_in_next{$elem->[0]}) {
+ if ($msg) {
+ print STDERR $msg;
+ undef $msg;
+ }
+ print STDERR " $elem->[1]\n";
+ }
+ }
+ ' "$topic" "$not_in_next" "$not_in_master"
+ exit 1
+fi
+
+exit 0
+
+################################################################
+
+This sample hook safeguards topic branches that have been
+published from being rewound.
+
+The workflow assumed here is:
+
+ * Once a topic branch forks from "master", "master" is never
+ merged into it again (either directly or indirectly).
+
+ * Once a topic branch is fully cooked and merged into "master",
+ it is deleted. If you need to build on top of it to correct
+ earlier mistakes, a new topic branch is created by forking at
+ the tip of the "master". This is not strictly necessary, but
+ it makes it easier to keep your history simple.
+
+ * Whenever you need to test or publish your changes to topic
+ branches, merge them into "next" branch.
+
+The script, being an example, hardcodes the publish branch name
+to be "next", but it is trivial to make it configurable via
+$GIT_DIR/config mechanism.
+
+With this workflow, you would want to know:
+
+(1) ... if a topic branch has ever been merged to "next". Young
+ topic branches can have stupid mistakes you would rather
+ clean up before publishing, and things that have not been
+ merged into other branches can be easily rebased without
+ affecting other people. But once it is published, you would
+ not want to rewind it.
+
+(2) ... if a topic branch has been fully merged to "master".
+ Then you can delete it. More importantly, you should not
+ build on top of it -- other people may already want to
+ change things related to the topic as patches against your
+ "master", so if you need further changes, it is better to
+ fork the topic (perhaps with the same name) afresh from the
+ tip of "master".
+
+Let's look at this example:
+
+ o---o---o---o---o---o---o---o---o---o "next"
+ / / / /
+ / a---a---b A / /
+ / / / /
+ / / c---c---c---c B /
+ / / / \ /
+ / / / b---b C \ /
+ / / / / \ /
+ ---o---o---o---o---o---o---o---o---o---o---o "master"
+
+
+A, B and C are topic branches.
+
+ * A has one fix since it was merged up to "next".
+
+ * B has finished. It has been fully merged up to "master" and "next",
+ and is ready to be deleted.
+
+ * C has not merged to "next" at all.
+
+We would want to allow C to be rebased, refuse A, and encourage
+B to be deleted.
+
+To compute (1):
+
+ git rev-list ^master ^topic next
+ git rev-list ^master next
+
+ if these match, topic has not merged in next at all.
+
+To compute (2):
+
+ git rev-list master..topic
+
+ if this is empty, it is fully merged to "master".
diff --git a/tests-clar/resources/duplicate.git/hooks/prepare-commit-msg.sample b/tests-clar/resources/duplicate.git/hooks/prepare-commit-msg.sample
new file mode 100755
index 000000000..f093a02ec
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/hooks/prepare-commit-msg.sample
@@ -0,0 +1,36 @@
+#!/bin/sh
+#
+# An example hook script to prepare the commit log message.
+# Called by "git commit" with the name of the file that has the
+# commit message, followed by the description of the commit
+# message's source. The hook's purpose is to edit the commit
+# message file. If the hook fails with a non-zero status,
+# the commit is aborted.
+#
+# To enable this hook, rename this file to "prepare-commit-msg".
+
+# This hook includes three examples. The first comments out the
+# "Conflicts:" part of a merge commit.
+#
+# The second includes the output of "git diff --name-status -r"
+# into the message, just before the "git status" output. It is
+# commented because it doesn't cope with --amend or with squashed
+# commits.
+#
+# The third example adds a Signed-off-by line to the message, that can
+# still be edited. This is rarely a good idea.
+
+case "$2,$3" in
+ merge,)
+ /usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;;
+
+# ,|template,)
+# /usr/bin/perl -i.bak -pe '
+# print "\n" . `git diff --cached --name-status -r`
+# if /^#/ && $first++ == 0' "$1" ;;
+
+ *) ;;
+esac
+
+# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
+# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
diff --git a/tests-clar/resources/duplicate.git/hooks/update.sample b/tests-clar/resources/duplicate.git/hooks/update.sample
new file mode 100755
index 000000000..71ab04edc
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/hooks/update.sample
@@ -0,0 +1,128 @@
+#!/bin/sh
+#
+# An example hook script to blocks unannotated tags from entering.
+# Called by "git receive-pack" with arguments: refname sha1-old sha1-new
+#
+# To enable this hook, rename this file to "update".
+#
+# Config
+# ------
+# hooks.allowunannotated
+# This boolean sets whether unannotated tags will be allowed into the
+# repository. By default they won't be.
+# hooks.allowdeletetag
+# This boolean sets whether deleting tags will be allowed in the
+# repository. By default they won't be.
+# hooks.allowmodifytag
+# This boolean sets whether a tag may be modified after creation. By default
+# it won't be.
+# hooks.allowdeletebranch
+# This boolean sets whether deleting branches will be allowed in the
+# repository. By default they won't be.
+# hooks.denycreatebranch
+# This boolean sets whether remotely creating branches will be denied
+# in the repository. By default this is allowed.
+#
+
+# --- Command line
+refname="$1"
+oldrev="$2"
+newrev="$3"
+
+# --- Safety check
+if [ -z "$GIT_DIR" ]; then
+ echo "Don't run this script from the command line." >&2
+ echo " (if you want, you could supply GIT_DIR then run" >&2
+ echo " $0 <ref> <oldrev> <newrev>)" >&2
+ exit 1
+fi
+
+if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
+ echo "Usage: $0 <ref> <oldrev> <newrev>" >&2
+ exit 1
+fi
+
+# --- Config
+allowunannotated=$(git config --bool hooks.allowunannotated)
+allowdeletebranch=$(git config --bool hooks.allowdeletebranch)
+denycreatebranch=$(git config --bool hooks.denycreatebranch)
+allowdeletetag=$(git config --bool hooks.allowdeletetag)
+allowmodifytag=$(git config --bool hooks.allowmodifytag)
+
+# check for no description
+projectdesc=$(sed -e '1q' "$GIT_DIR/description")
+case "$projectdesc" in
+"Unnamed repository"* | "")
+ echo "*** Project description file hasn't been set" >&2
+ exit 1
+ ;;
+esac
+
+# --- Check types
+# if $newrev is 0000...0000, it's a commit to delete a ref.
+zero="0000000000000000000000000000000000000000"
+if [ "$newrev" = "$zero" ]; then
+ newrev_type=delete
+else
+ newrev_type=$(git cat-file -t $newrev)
+fi
+
+case "$refname","$newrev_type" in
+ refs/tags/*,commit)
+ # un-annotated tag
+ short_refname=${refname##refs/tags/}
+ if [ "$allowunannotated" != "true" ]; then
+ echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2
+ echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
+ exit 1
+ fi
+ ;;
+ refs/tags/*,delete)
+ # delete tag
+ if [ "$allowdeletetag" != "true" ]; then
+ echo "*** Deleting a tag is not allowed in this repository" >&2
+ exit 1
+ fi
+ ;;
+ refs/tags/*,tag)
+ # annotated tag
+ if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1
+ then
+ echo "*** Tag '$refname' already exists." >&2
+ echo "*** Modifying a tag is not allowed in this repository." >&2
+ exit 1
+ fi
+ ;;
+ refs/heads/*,commit)
+ # branch
+ if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then
+ echo "*** Creating a branch is not allowed in this repository" >&2
+ exit 1
+ fi
+ ;;
+ refs/heads/*,delete)
+ # delete branch
+ if [ "$allowdeletebranch" != "true" ]; then
+ echo "*** Deleting a branch is not allowed in this repository" >&2
+ exit 1
+ fi
+ ;;
+ refs/remotes/*,commit)
+ # tracking branch
+ ;;
+ refs/remotes/*,delete)
+ # delete tracking branch
+ if [ "$allowdeletebranch" != "true" ]; then
+ echo "*** Deleting a tracking branch is not allowed in this repository" >&2
+ exit 1
+ fi
+ ;;
+ *)
+ # Anything else (is there anything else?)
+ echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
+ exit 1
+ ;;
+esac
+
+# --- Finished
+exit 0
diff --git a/tests-clar/resources/duplicate.git/index b/tests-clar/resources/duplicate.git/index
new file mode 100644
index 000000000..a61e1c5ca
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/index
Binary files differ
diff --git a/tests/resources/empty_standard_repo/.gitted/info/exclude b/tests-clar/resources/duplicate.git/info/exclude
index a5196d1be..a5196d1be 100644
--- a/tests/resources/empty_standard_repo/.gitted/info/exclude
+++ b/tests-clar/resources/duplicate.git/info/exclude
diff --git a/tests-clar/resources/duplicate.git/info/refs b/tests-clar/resources/duplicate.git/info/refs
new file mode 100644
index 000000000..3b5bb03be
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/info/refs
@@ -0,0 +1 @@
+8d2f05c97ef29a4697b37c30fe81c248ef411a23 refs/heads/master
diff --git a/tests-clar/resources/duplicate.git/logs/HEAD b/tests-clar/resources/duplicate.git/logs/HEAD
new file mode 100644
index 000000000..be9b4c6cb
--- /dev/null
+++ b/tests-clar/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-clar/resources/duplicate.git/logs/refs/heads/master b/tests-clar/resources/duplicate.git/logs/refs/heads/master
new file mode 100644
index 000000000..be9b4c6cb
--- /dev/null
+++ b/tests-clar/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-clar/resources/duplicate.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a b/tests-clar/resources/duplicate.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a
new file mode 100644
index 000000000..6802d4949
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a
Binary files differ
diff --git a/tests-clar/resources/duplicate.git/objects/info/packs b/tests-clar/resources/duplicate.git/objects/info/packs
new file mode 100644
index 000000000..3696a7d36
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/objects/info/packs
@@ -0,0 +1,2 @@
+P pack-e87994ad581c9af946de0eb890175c08cd005f38.pack
+
diff --git a/tests-clar/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.idx b/tests-clar/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.idx
new file mode 100644
index 000000000..fd8abee98
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.idx
Binary files differ
diff --git a/tests-clar/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.pack b/tests-clar/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.pack
new file mode 100644
index 000000000..9879e0869
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.pack
Binary files differ
diff --git a/tests-clar/resources/duplicate.git/packed-refs b/tests-clar/resources/duplicate.git/packed-refs
new file mode 100644
index 000000000..9f0d4e434
--- /dev/null
+++ b/tests-clar/resources/duplicate.git/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+8d2f05c97ef29a4697b37c30fe81c248ef411a23 refs/heads/master
diff --git a/tests/resources/status/.gitted/HEAD b/tests-clar/resources/empty_bare.git/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests/resources/status/.gitted/HEAD
+++ b/tests-clar/resources/empty_bare.git/HEAD
diff --git a/tests/resources/empty_bare.git/config b/tests-clar/resources/empty_bare.git/config
index 90e16477b..90e16477b 100644
--- a/tests/resources/empty_bare.git/config
+++ b/tests-clar/resources/empty_bare.git/config
diff --git a/tests/resources/status/.gitted/description b/tests-clar/resources/empty_bare.git/description
index 498b267a8..498b267a8 100644
--- a/tests/resources/status/.gitted/description
+++ b/tests-clar/resources/empty_bare.git/description
diff --git a/tests-clar/resources/empty_bare.git/info/exclude b/tests-clar/resources/empty_bare.git/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests-clar/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-clar/resources/empty_bare.git/objects/info/dummy-marker.txt
index e69de29bb..e69de29bb 100644
--- a/tests/resources/empty_bare.git/objects/info/dummy-marker.txt
+++ b/tests-clar/resources/empty_bare.git/objects/info/dummy-marker.txt
diff --git a/tests/resources/empty_bare.git/objects/pack/dummy-marker.txt b/tests-clar/resources/empty_bare.git/objects/pack/dummy-marker.txt
index e69de29bb..e69de29bb 100644
--- a/tests/resources/empty_bare.git/objects/pack/dummy-marker.txt
+++ b/tests-clar/resources/empty_bare.git/objects/pack/dummy-marker.txt
diff --git a/tests/resources/empty_bare.git/refs/heads/dummy-marker.txt b/tests-clar/resources/empty_bare.git/refs/heads/dummy-marker.txt
index e69de29bb..e69de29bb 100644
--- a/tests/resources/empty_bare.git/refs/heads/dummy-marker.txt
+++ b/tests-clar/resources/empty_bare.git/refs/heads/dummy-marker.txt
diff --git a/tests/resources/empty_bare.git/refs/tags/dummy-marker.txt b/tests-clar/resources/empty_bare.git/refs/tags/dummy-marker.txt
index e69de29bb..e69de29bb 100644
--- a/tests/resources/empty_bare.git/refs/tags/dummy-marker.txt
+++ b/tests-clar/resources/empty_bare.git/refs/tags/dummy-marker.txt
diff --git a/tests/resources/testrepo.git/HEAD b/tests-clar/resources/empty_standard_repo/.gitted/HEAD
index cb089cd89..cb089cd89 100644
--- a/tests/resources/testrepo.git/HEAD
+++ b/tests-clar/resources/empty_standard_repo/.gitted/HEAD
diff --git a/tests/resources/empty_standard_repo/.gitted/config b/tests-clar/resources/empty_standard_repo/.gitted/config
index 78387c50b..78387c50b 100644
--- a/tests/resources/empty_standard_repo/.gitted/config
+++ b/tests-clar/resources/empty_standard_repo/.gitted/config
diff --git a/tests-clar/resources/empty_standard_repo/.gitted/description b/tests-clar/resources/empty_standard_repo/.gitted/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests-clar/resources/empty_standard_repo/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests-clar/resources/empty_standard_repo/.gitted/info/exclude b/tests-clar/resources/empty_standard_repo/.gitted/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests-clar/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-clar/resources/empty_standard_repo/.gitted/objects/info/dummy-marker.txt
index e69de29bb..e69de29bb 100644
--- a/tests/resources/empty_standard_repo/.gitted/objects/info/dummy-marker.txt
+++ b/tests-clar/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-clar/resources/empty_standard_repo/.gitted/objects/pack/dummy-marker.txt
index e69de29bb..e69de29bb 100644
--- a/tests/resources/empty_standard_repo/.gitted/objects/pack/dummy-marker.txt
+++ b/tests-clar/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-clar/resources/empty_standard_repo/.gitted/refs/heads/dummy-marker.txt
index e69de29bb..e69de29bb 100644
--- a/tests/resources/empty_standard_repo/.gitted/refs/heads/dummy-marker.txt
+++ b/tests-clar/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-clar/resources/empty_standard_repo/.gitted/refs/tags/dummy-marker.txt
index e69de29bb..e69de29bb 100644
--- a/tests/resources/empty_standard_repo/.gitted/refs/tags/dummy-marker.txt
+++ b/tests-clar/resources/empty_standard_repo/.gitted/refs/tags/dummy-marker.txt
diff --git a/tests/resources/gitgit.index b/tests-clar/resources/gitgit.index
index 215da649e..215da649e 100644
--- a/tests/resources/gitgit.index
+++ b/tests-clar/resources/gitgit.index
Binary files differ
diff --git a/tests-clar/resources/issue_592/.gitted/COMMIT_EDITMSG b/tests-clar/resources/issue_592/.gitted/COMMIT_EDITMSG
new file mode 100644
index 000000000..5852f4463
--- /dev/null
+++ b/tests-clar/resources/issue_592/.gitted/COMMIT_EDITMSG
@@ -0,0 +1 @@
+Initial commit
diff --git a/tests-clar/resources/issue_592/.gitted/HEAD b/tests-clar/resources/issue_592/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests-clar/resources/issue_592/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests-clar/resources/issue_592/.gitted/config b/tests-clar/resources/issue_592/.gitted/config
new file mode 100644
index 000000000..78387c50b
--- /dev/null
+++ b/tests-clar/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-clar/resources/issue_592/.gitted/index b/tests-clar/resources/issue_592/.gitted/index
new file mode 100644
index 000000000..eaeb5d761
--- /dev/null
+++ b/tests-clar/resources/issue_592/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/issue_592/.gitted/info/exclude b/tests-clar/resources/issue_592/.gitted/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests-clar/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-clar/resources/issue_592/.gitted/logs/HEAD b/tests-clar/resources/issue_592/.gitted/logs/HEAD
new file mode 100644
index 000000000..f19fe35a6
--- /dev/null
+++ b/tests-clar/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-clar/resources/issue_592/.gitted/logs/refs/heads/master b/tests-clar/resources/issue_592/.gitted/logs/refs/heads/master
new file mode 100644
index 000000000..f19fe35a6
--- /dev/null
+++ b/tests-clar/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-clar/resources/issue_592/.gitted/objects/06/07ee9d4ccce8e4c4fa13c2c7d727e7faba4e0e b/tests-clar/resources/issue_592/.gitted/objects/06/07ee9d4ccce8e4c4fa13c2c7d727e7faba4e0e
new file mode 100644
index 000000000..05dec10f7
--- /dev/null
+++ b/tests-clar/resources/issue_592/.gitted/objects/06/07ee9d4ccce8e4c4fa13c2c7d727e7faba4e0e
Binary files differ
diff --git a/tests-clar/resources/issue_592/.gitted/objects/49/363a72a90d9424240258cd3759f23788ecf1d8 b/tests-clar/resources/issue_592/.gitted/objects/49/363a72a90d9424240258cd3759f23788ecf1d8
new file mode 100644
index 000000000..e997e1b49
--- /dev/null
+++ b/tests-clar/resources/issue_592/.gitted/objects/49/363a72a90d9424240258cd3759f23788ecf1d8
Binary files differ
diff --git a/tests-clar/resources/issue_592/.gitted/objects/4d/383e87f0371ba8fa353f3912db6862b2625e85 b/tests-clar/resources/issue_592/.gitted/objects/4d/383e87f0371ba8fa353f3912db6862b2625e85
new file mode 100644
index 000000000..c49a8be58
--- /dev/null
+++ b/tests-clar/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-clar/resources/issue_592/.gitted/objects/71/44be264b61825fbff68046fe999bdfe96a1792 b/tests-clar/resources/issue_592/.gitted/objects/71/44be264b61825fbff68046fe999bdfe96a1792
new file mode 100644
index 000000000..25d44d938
--- /dev/null
+++ b/tests-clar/resources/issue_592/.gitted/objects/71/44be264b61825fbff68046fe999bdfe96a1792
Binary files differ
diff --git a/tests-clar/resources/issue_592/.gitted/objects/be/de83ee10b5b3f00239660b00acec2d55fd0b84 b/tests-clar/resources/issue_592/.gitted/objects/be/de83ee10b5b3f00239660b00acec2d55fd0b84
new file mode 100644
index 000000000..1d6e38d37
--- /dev/null
+++ b/tests-clar/resources/issue_592/.gitted/objects/be/de83ee10b5b3f00239660b00acec2d55fd0b84
Binary files differ
diff --git a/tests-clar/resources/issue_592/.gitted/objects/e3/8fcc7a6060f5eb5b876e836b52ae4769363f21 b/tests-clar/resources/issue_592/.gitted/objects/e3/8fcc7a6060f5eb5b876e836b52ae4769363f21
new file mode 100644
index 000000000..36c5b9aab
--- /dev/null
+++ b/tests-clar/resources/issue_592/.gitted/objects/e3/8fcc7a6060f5eb5b876e836b52ae4769363f21
Binary files differ
diff --git a/tests-clar/resources/issue_592/.gitted/objects/f1/adef63cb08891a0942b76fc4b9c50c6c494bc7 b/tests-clar/resources/issue_592/.gitted/objects/f1/adef63cb08891a0942b76fc4b9c50c6c494bc7
new file mode 100644
index 000000000..c08ecd5ed
--- /dev/null
+++ b/tests-clar/resources/issue_592/.gitted/objects/f1/adef63cb08891a0942b76fc4b9c50c6c494bc7
Binary files differ
diff --git a/tests-clar/resources/issue_592/.gitted/refs/heads/master b/tests-clar/resources/issue_592/.gitted/refs/heads/master
new file mode 100644
index 000000000..1f6669628
--- /dev/null
+++ b/tests-clar/resources/issue_592/.gitted/refs/heads/master
@@ -0,0 +1 @@
+e38fcc7a6060f5eb5b876e836b52ae4769363f21
diff --git a/tests-clar/resources/issue_592/a.txt b/tests-clar/resources/issue_592/a.txt
new file mode 100644
index 000000000..f1adef63c
--- /dev/null
+++ b/tests-clar/resources/issue_592/a.txt
@@ -0,0 +1 @@
+nothing here
diff --git a/tests-clar/resources/issue_592/c/a.txt b/tests-clar/resources/issue_592/c/a.txt
new file mode 100644
index 000000000..f1adef63c
--- /dev/null
+++ b/tests-clar/resources/issue_592/c/a.txt
@@ -0,0 +1 @@
+nothing here
diff --git a/tests-clar/resources/issue_592/l.txt b/tests-clar/resources/issue_592/l.txt
new file mode 100644
index 000000000..f1adef63c
--- /dev/null
+++ b/tests-clar/resources/issue_592/l.txt
@@ -0,0 +1 @@
+nothing here
diff --git a/tests-clar/resources/issue_592/t/a.txt b/tests-clar/resources/issue_592/t/a.txt
new file mode 100644
index 000000000..f1adef63c
--- /dev/null
+++ b/tests-clar/resources/issue_592/t/a.txt
@@ -0,0 +1 @@
+nothing here
diff --git a/tests-clar/resources/issue_592/t/b.txt b/tests-clar/resources/issue_592/t/b.txt
new file mode 100644
index 000000000..f1adef63c
--- /dev/null
+++ b/tests-clar/resources/issue_592/t/b.txt
@@ -0,0 +1 @@
+nothing here
diff --git a/tests-clar/resources/issue_592b/.gitted/HEAD b/tests-clar/resources/issue_592b/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests-clar/resources/issue_592b/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests-clar/resources/issue_592b/.gitted/config b/tests-clar/resources/issue_592b/.gitted/config
new file mode 100644
index 000000000..af107929f
--- /dev/null
+++ b/tests-clar/resources/issue_592b/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests-clar/resources/issue_592b/.gitted/description b/tests-clar/resources/issue_592b/.gitted/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests-clar/resources/issue_592b/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests-clar/resources/issue_592b/.gitted/hooks/post-update.sample b/tests-clar/resources/issue_592b/.gitted/hooks/post-update.sample
new file mode 100755
index 000000000..ec17ec193
--- /dev/null
+++ b/tests-clar/resources/issue_592b/.gitted/hooks/post-update.sample
@@ -0,0 +1,8 @@
+#!/bin/sh
+#
+# An example hook script to prepare a packed repository for use over
+# dumb transports.
+#
+# To enable this hook, rename this file to "post-update".
+
+exec git update-server-info
diff --git a/tests-clar/resources/issue_592b/.gitted/index b/tests-clar/resources/issue_592b/.gitted/index
new file mode 100644
index 000000000..596438216
--- /dev/null
+++ b/tests-clar/resources/issue_592b/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/issue_592b/.gitted/info/exclude b/tests-clar/resources/issue_592b/.gitted/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests-clar/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-clar/resources/issue_592b/.gitted/logs/HEAD b/tests-clar/resources/issue_592b/.gitted/logs/HEAD
new file mode 100644
index 000000000..6f3ba90cc
--- /dev/null
+++ b/tests-clar/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-clar/resources/issue_592b/.gitted/logs/refs/heads/master b/tests-clar/resources/issue_592b/.gitted/logs/refs/heads/master
new file mode 100644
index 000000000..6f3ba90cc
--- /dev/null
+++ b/tests-clar/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-clar/resources/issue_592b/.gitted/objects/3f/bf1852f72fd268e36457b13a18cdd9a4c9ea35 b/tests-clar/resources/issue_592b/.gitted/objects/3f/bf1852f72fd268e36457b13a18cdd9a4c9ea35
new file mode 100644
index 000000000..6eaf64b46
--- /dev/null
+++ b/tests-clar/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-clar/resources/issue_592b/.gitted/objects/6f/a891d3e578c83e1c03bdb9e0fdd8e6e934157f b/tests-clar/resources/issue_592b/.gitted/objects/6f/a891d3e578c83e1c03bdb9e0fdd8e6e934157f
new file mode 100644
index 000000000..c4becfe2f
--- /dev/null
+++ b/tests-clar/resources/issue_592b/.gitted/objects/6f/a891d3e578c83e1c03bdb9e0fdd8e6e934157f
Binary files differ
diff --git a/tests-clar/resources/issue_592b/.gitted/objects/80/07d41d5794e6ce4d4d2c97e370d5a9aa6d5213 b/tests-clar/resources/issue_592b/.gitted/objects/80/07d41d5794e6ce4d4d2c97e370d5a9aa6d5213
new file mode 100644
index 000000000..aea14f2af
--- /dev/null
+++ b/tests-clar/resources/issue_592b/.gitted/objects/80/07d41d5794e6ce4d4d2c97e370d5a9aa6d5213
Binary files differ
diff --git a/tests-clar/resources/issue_592b/.gitted/objects/a6/5fb6583a7c425284142f285bc359a2d6565513 b/tests-clar/resources/issue_592b/.gitted/objects/a6/5fb6583a7c425284142f285bc359a2d6565513
new file mode 100644
index 000000000..9b7407221
--- /dev/null
+++ b/tests-clar/resources/issue_592b/.gitted/objects/a6/5fb6583a7c425284142f285bc359a2d6565513
Binary files differ
diff --git a/tests-clar/resources/issue_592b/.gitted/objects/ae/be7a55922c7097ef91ca3a7bc327a901d87c2c b/tests-clar/resources/issue_592b/.gitted/objects/ae/be7a55922c7097ef91ca3a7bc327a901d87c2c
new file mode 100644
index 000000000..1494ed822
--- /dev/null
+++ b/tests-clar/resources/issue_592b/.gitted/objects/ae/be7a55922c7097ef91ca3a7bc327a901d87c2c
Binary files differ
diff --git a/tests-clar/resources/issue_592b/.gitted/objects/b3/44b055867fcdc1f01eaa75056a43e868eb4fbc b/tests-clar/resources/issue_592b/.gitted/objects/b3/44b055867fcdc1f01eaa75056a43e868eb4fbc
new file mode 100644
index 000000000..7a6626636
--- /dev/null
+++ b/tests-clar/resources/issue_592b/.gitted/objects/b3/44b055867fcdc1f01eaa75056a43e868eb4fbc
Binary files differ
diff --git a/tests-clar/resources/issue_592b/.gitted/objects/f7/d75fbfad8b1d2e307ced287ea78aad403cdce3 b/tests-clar/resources/issue_592b/.gitted/objects/f7/d75fbfad8b1d2e307ced287ea78aad403cdce3
new file mode 100644
index 000000000..65a1fd0d0
--- /dev/null
+++ b/tests-clar/resources/issue_592b/.gitted/objects/f7/d75fbfad8b1d2e307ced287ea78aad403cdce3
Binary files differ
diff --git a/tests-clar/resources/issue_592b/.gitted/refs/heads/master b/tests-clar/resources/issue_592b/.gitted/refs/heads/master
new file mode 100644
index 000000000..c0a9ab495
--- /dev/null
+++ b/tests-clar/resources/issue_592b/.gitted/refs/heads/master
@@ -0,0 +1 @@
+3fbf1852f72fd268e36457b13a18cdd9a4c9ea35
diff --git a/tests-clar/resources/issue_592b/gitignore b/tests-clar/resources/issue_592b/gitignore
new file mode 100644
index 000000000..8007d41d5
--- /dev/null
+++ b/tests-clar/resources/issue_592b/gitignore
@@ -0,0 +1 @@
+ignored/
diff --git a/tests-clar/resources/issue_592b/ignored/contained/ignored3.txt b/tests-clar/resources/issue_592b/ignored/contained/ignored3.txt
new file mode 100644
index 000000000..b5dc7b073
--- /dev/null
+++ b/tests-clar/resources/issue_592b/ignored/contained/ignored3.txt
@@ -0,0 +1 @@
+I'm ignored
diff --git a/tests-clar/resources/issue_592b/ignored/contained/tracked3.txt b/tests-clar/resources/issue_592b/ignored/contained/tracked3.txt
new file mode 100644
index 000000000..b344b0558
--- /dev/null
+++ b/tests-clar/resources/issue_592b/ignored/contained/tracked3.txt
@@ -0,0 +1 @@
+You added me anyhow
diff --git a/tests-clar/resources/issue_592b/ignored/ignored2.txt b/tests-clar/resources/issue_592b/ignored/ignored2.txt
new file mode 100644
index 000000000..b5dc7b073
--- /dev/null
+++ b/tests-clar/resources/issue_592b/ignored/ignored2.txt
@@ -0,0 +1 @@
+I'm ignored
diff --git a/tests-clar/resources/issue_592b/ignored/tracked2.txt b/tests-clar/resources/issue_592b/ignored/tracked2.txt
new file mode 100644
index 000000000..6fa891d3e
--- /dev/null
+++ b/tests-clar/resources/issue_592b/ignored/tracked2.txt
@@ -0,0 +1 @@
+You like me
diff --git a/tests-clar/resources/issue_592b/ignored1.txt b/tests-clar/resources/issue_592b/ignored1.txt
new file mode 100644
index 000000000..b5dc7b073
--- /dev/null
+++ b/tests-clar/resources/issue_592b/ignored1.txt
@@ -0,0 +1 @@
+I'm ignored
diff --git a/tests-clar/resources/issue_592b/tracked1.txt b/tests-clar/resources/issue_592b/tracked1.txt
new file mode 100644
index 000000000..6fa891d3e
--- /dev/null
+++ b/tests-clar/resources/issue_592b/tracked1.txt
@@ -0,0 +1 @@
+You like me
diff --git a/tests/resources/status/.gitted/COMMIT_EDITMSG b/tests-clar/resources/status/.gitted/COMMIT_EDITMSG
index 1a25cd4a6..1a25cd4a6 100644
--- a/tests/resources/status/.gitted/COMMIT_EDITMSG
+++ b/tests-clar/resources/status/.gitted/COMMIT_EDITMSG
diff --git a/tests-clar/resources/status/.gitted/HEAD b/tests-clar/resources/status/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests-clar/resources/status/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/status/.gitted/ORIG_HEAD b/tests-clar/resources/status/.gitted/ORIG_HEAD
index b46871fd6..b46871fd6 100644
--- a/tests/resources/status/.gitted/ORIG_HEAD
+++ b/tests-clar/resources/status/.gitted/ORIG_HEAD
diff --git a/tests-clar/resources/status/.gitted/config b/tests-clar/resources/status/.gitted/config
new file mode 100644
index 000000000..af107929f
--- /dev/null
+++ b/tests-clar/resources/status/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests-clar/resources/status/.gitted/description b/tests-clar/resources/status/.gitted/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests-clar/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-clar/resources/status/.gitted/index
index d793791c9..9a383ec0c 100644
--- a/tests/resources/status/.gitted/index
+++ b/tests-clar/resources/status/.gitted/index
Binary files differ
diff --git a/tests/resources/status/.gitted/info/exclude b/tests-clar/resources/status/.gitted/info/exclude
index 0c4042a6a..0c4042a6a 100644
--- a/tests/resources/status/.gitted/info/exclude
+++ b/tests-clar/resources/status/.gitted/info/exclude
diff --git a/tests/resources/status/.gitted/logs/HEAD b/tests-clar/resources/status/.gitted/logs/HEAD
index 7b95b3cf1..7b95b3cf1 100644
--- a/tests/resources/status/.gitted/logs/HEAD
+++ b/tests-clar/resources/status/.gitted/logs/HEAD
diff --git a/tests/resources/status/.gitted/logs/refs/heads/master b/tests-clar/resources/status/.gitted/logs/refs/heads/master
index 7b95b3cf1..7b95b3cf1 100644
--- a/tests/resources/status/.gitted/logs/refs/heads/master
+++ b/tests-clar/resources/status/.gitted/logs/refs/heads/master
diff --git a/tests/resources/status/.gitted/objects/00/17bd4ab1ec30440b17bae1680cff124ab5f1f6 b/tests-clar/resources/status/.gitted/objects/00/17bd4ab1ec30440b17bae1680cff124ab5f1f6
index b256d95a3..b256d95a3 100644
--- a/tests/resources/status/.gitted/objects/00/17bd4ab1ec30440b17bae1680cff124ab5f1f6
+++ b/tests-clar/resources/status/.gitted/objects/00/17bd4ab1ec30440b17bae1680cff124ab5f1f6
diff --git a/tests/resources/status/.gitted/objects/06/1d42a44cacde5726057b67558821d95db96f19 b/tests-clar/resources/status/.gitted/objects/06/1d42a44cacde5726057b67558821d95db96f19
index 82e02cb0e..82e02cb0e 100644
--- a/tests/resources/status/.gitted/objects/06/1d42a44cacde5726057b67558821d95db96f19
+++ b/tests-clar/resources/status/.gitted/objects/06/1d42a44cacde5726057b67558821d95db96f19
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/18/88c805345ba265b0ee9449b8877b6064592058 b/tests-clar/resources/status/.gitted/objects/18/88c805345ba265b0ee9449b8877b6064592058
index e3cad2f02..e3cad2f02 100644
--- a/tests/resources/status/.gitted/objects/18/88c805345ba265b0ee9449b8877b6064592058
+++ b/tests-clar/resources/status/.gitted/objects/18/88c805345ba265b0ee9449b8877b6064592058
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/19/d9cc8584ac2c7dcf57d2680375e80f099dc481 b/tests-clar/resources/status/.gitted/objects/19/d9cc8584ac2c7dcf57d2680375e80f099dc481
index 2d5e711b9..2d5e711b9 100644
--- a/tests/resources/status/.gitted/objects/19/d9cc8584ac2c7dcf57d2680375e80f099dc481
+++ b/tests-clar/resources/status/.gitted/objects/19/d9cc8584ac2c7dcf57d2680375e80f099dc481
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/26/a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f b/tests-clar/resources/status/.gitted/objects/26/a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f
index f7dddc4ff..f7dddc4ff 100644
--- a/tests/resources/status/.gitted/objects/26/a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f
+++ b/tests-clar/resources/status/.gitted/objects/26/a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f
diff --git a/tests/resources/status/.gitted/objects/32/504b727382542f9f089e24fddac5e78533e96c b/tests-clar/resources/status/.gitted/objects/32/504b727382542f9f089e24fddac5e78533e96c
index 7fca67be8..7fca67be8 100644
--- a/tests/resources/status/.gitted/objects/32/504b727382542f9f089e24fddac5e78533e96c
+++ b/tests-clar/resources/status/.gitted/objects/32/504b727382542f9f089e24fddac5e78533e96c
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/37/fcb02ccc1a85d1941e7f106d52dc3702dcf0d0 b/tests-clar/resources/status/.gitted/objects/37/fcb02ccc1a85d1941e7f106d52dc3702dcf0d0
index b75481b51..b75481b51 100644
--- a/tests/resources/status/.gitted/objects/37/fcb02ccc1a85d1941e7f106d52dc3702dcf0d0
+++ b/tests-clar/resources/status/.gitted/objects/37/fcb02ccc1a85d1941e7f106d52dc3702dcf0d0
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/45/2e4244b5d083ddf0460acf1ecc74db9dcfa11a b/tests-clar/resources/status/.gitted/objects/45/2e4244b5d083ddf0460acf1ecc74db9dcfa11a
index 5b47461e9..5b47461e9 100644
--- a/tests/resources/status/.gitted/objects/45/2e4244b5d083ddf0460acf1ecc74db9dcfa11a
+++ b/tests-clar/resources/status/.gitted/objects/45/2e4244b5d083ddf0460acf1ecc74db9dcfa11a
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/52/9a16e8e762d4acb7b9636ff540a00831f9155a b/tests-clar/resources/status/.gitted/objects/52/9a16e8e762d4acb7b9636ff540a00831f9155a
index 615009ad0..615009ad0 100644
--- a/tests/resources/status/.gitted/objects/52/9a16e8e762d4acb7b9636ff540a00831f9155a
+++ b/tests-clar/resources/status/.gitted/objects/52/9a16e8e762d4acb7b9636ff540a00831f9155a
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/53/ace0d1cc1145a5f4fe4f78a186a60263190733 b/tests-clar/resources/status/.gitted/objects/53/ace0d1cc1145a5f4fe4f78a186a60263190733
index cdb7e961a..cdb7e961a 100644
--- a/tests/resources/status/.gitted/objects/53/ace0d1cc1145a5f4fe4f78a186a60263190733
+++ b/tests-clar/resources/status/.gitted/objects/53/ace0d1cc1145a5f4fe4f78a186a60263190733
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/54/52d32f1dd538eb0405e8a83cc185f79e25e80f b/tests-clar/resources/status/.gitted/objects/54/52d32f1dd538eb0405e8a83cc185f79e25e80f
index a72dff646..a72dff646 100644
--- a/tests/resources/status/.gitted/objects/54/52d32f1dd538eb0405e8a83cc185f79e25e80f
+++ b/tests-clar/resources/status/.gitted/objects/54/52d32f1dd538eb0405e8a83cc185f79e25e80f
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/55/d316c9ba708999f1918e9677d01dfcae69c6b9 b/tests-clar/resources/status/.gitted/objects/55/d316c9ba708999f1918e9677d01dfcae69c6b9
index 72807f3d0..72807f3d0 100644
--- a/tests/resources/status/.gitted/objects/55/d316c9ba708999f1918e9677d01dfcae69c6b9
+++ b/tests-clar/resources/status/.gitted/objects/55/d316c9ba708999f1918e9677d01dfcae69c6b9
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/70/bd9443ada07063e7fbf0b3ff5c13f7494d89c2 b/tests-clar/resources/status/.gitted/objects/70/bd9443ada07063e7fbf0b3ff5c13f7494d89c2
index 3665a8f7c..3665a8f7c 100644
--- a/tests/resources/status/.gitted/objects/70/bd9443ada07063e7fbf0b3ff5c13f7494d89c2
+++ b/tests-clar/resources/status/.gitted/objects/70/bd9443ada07063e7fbf0b3ff5c13f7494d89c2
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/73/5b6a258cd196a8f7c9428419b02c1dca93fd75 b/tests-clar/resources/status/.gitted/objects/73/5b6a258cd196a8f7c9428419b02c1dca93fd75
index 08e6fd246..08e6fd246 100644
--- a/tests/resources/status/.gitted/objects/73/5b6a258cd196a8f7c9428419b02c1dca93fd75
+++ b/tests-clar/resources/status/.gitted/objects/73/5b6a258cd196a8f7c9428419b02c1dca93fd75
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/75/6e27627e67bfbc048d01ece5819c6de733d7ea b/tests-clar/resources/status/.gitted/objects/75/6e27627e67bfbc048d01ece5819c6de733d7ea
index 8f3fa89e5..8f3fa89e5 100644
--- a/tests/resources/status/.gitted/objects/75/6e27627e67bfbc048d01ece5819c6de733d7ea
+++ b/tests-clar/resources/status/.gitted/objects/75/6e27627e67bfbc048d01ece5819c6de733d7ea
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/90/6ee7711f4f4928ddcb2a5f8fbc500deba0d2a8 b/tests-clar/resources/status/.gitted/objects/90/6ee7711f4f4928ddcb2a5f8fbc500deba0d2a8
index bb732b08e..bb732b08e 100644
--- a/tests/resources/status/.gitted/objects/90/6ee7711f4f4928ddcb2a5f8fbc500deba0d2a8
+++ b/tests-clar/resources/status/.gitted/objects/90/6ee7711f4f4928ddcb2a5f8fbc500deba0d2a8
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/90/b8c29d8ba39434d1c63e1b093daaa26e5bd972 b/tests-clar/resources/status/.gitted/objects/90/b8c29d8ba39434d1c63e1b093daaa26e5bd972
index 7a96618ff..7a96618ff 100644
--- a/tests/resources/status/.gitted/objects/90/b8c29d8ba39434d1c63e1b093daaa26e5bd972
+++ b/tests-clar/resources/status/.gitted/objects/90/b8c29d8ba39434d1c63e1b093daaa26e5bd972
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/9c/2e02cdffa8d73e6c189074594477a6baf87960 b/tests-clar/resources/status/.gitted/objects/9c/2e02cdffa8d73e6c189074594477a6baf87960
index 20a3c497e..20a3c497e 100644
--- a/tests/resources/status/.gitted/objects/9c/2e02cdffa8d73e6c189074594477a6baf87960
+++ b/tests-clar/resources/status/.gitted/objects/9c/2e02cdffa8d73e6c189074594477a6baf87960
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/a0/de7e0ac200c489c41c59dfa910154a70264e6e b/tests-clar/resources/status/.gitted/objects/a0/de7e0ac200c489c41c59dfa910154a70264e6e
index a1789c9a6..a1789c9a6 100644
--- a/tests/resources/status/.gitted/objects/a0/de7e0ac200c489c41c59dfa910154a70264e6e
+++ b/tests-clar/resources/status/.gitted/objects/a0/de7e0ac200c489c41c59dfa910154a70264e6e
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/a6/191982709b746d5650e93c2acf34ef74e11504 b/tests-clar/resources/status/.gitted/objects/a6/191982709b746d5650e93c2acf34ef74e11504
index cc1f377b3..cc1f377b3 100644
--- a/tests/resources/status/.gitted/objects/a6/191982709b746d5650e93c2acf34ef74e11504
+++ b/tests-clar/resources/status/.gitted/objects/a6/191982709b746d5650e93c2acf34ef74e11504
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/a6/be623522ce87a1d862128ac42672604f7b468b b/tests-clar/resources/status/.gitted/objects/a6/be623522ce87a1d862128ac42672604f7b468b
index c47298347..c47298347 100644
--- a/tests/resources/status/.gitted/objects/a6/be623522ce87a1d862128ac42672604f7b468b
+++ b/tests-clar/resources/status/.gitted/objects/a6/be623522ce87a1d862128ac42672604f7b468b
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/aa/27a641456848200fdb7f7c99ba36f8a0952877 b/tests-clar/resources/status/.gitted/objects/aa/27a641456848200fdb7f7c99ba36f8a0952877
index a4669ccbb..a4669ccbb 100644
--- a/tests/resources/status/.gitted/objects/aa/27a641456848200fdb7f7c99ba36f8a0952877
+++ b/tests-clar/resources/status/.gitted/objects/aa/27a641456848200fdb7f7c99ba36f8a0952877
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/da/bc8af9bd6e9f5bbe96a176f1a24baf3d1f8916 b/tests-clar/resources/status/.gitted/objects/da/bc8af9bd6e9f5bbe96a176f1a24baf3d1f8916
index 3e3c03c96..3e3c03c96 100644
--- a/tests/resources/status/.gitted/objects/da/bc8af9bd6e9f5bbe96a176f1a24baf3d1f8916
+++ b/tests-clar/resources/status/.gitted/objects/da/bc8af9bd6e9f5bbe96a176f1a24baf3d1f8916
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/e8/ee89e15bbe9b20137715232387b3de5b28972e b/tests-clar/resources/status/.gitted/objects/e8/ee89e15bbe9b20137715232387b3de5b28972e
index cfc2413d5..cfc2413d5 100644
--- a/tests/resources/status/.gitted/objects/e8/ee89e15bbe9b20137715232387b3de5b28972e
+++ b/tests-clar/resources/status/.gitted/objects/e8/ee89e15bbe9b20137715232387b3de5b28972e
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/e9/b9107f290627c04d097733a10055af941f6bca b/tests-clar/resources/status/.gitted/objects/e9/b9107f290627c04d097733a10055af941f6bca
index 1266d3eac..1266d3eac 100644
--- a/tests/resources/status/.gitted/objects/e9/b9107f290627c04d097733a10055af941f6bca
+++ b/tests-clar/resources/status/.gitted/objects/e9/b9107f290627c04d097733a10055af941f6bca
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/ed/062903b8f6f3dccb2fa81117ba6590944ef9bd b/tests-clar/resources/status/.gitted/objects/ed/062903b8f6f3dccb2fa81117ba6590944ef9bd
index 8fa8c1707..8fa8c1707 100644
--- a/tests/resources/status/.gitted/objects/ed/062903b8f6f3dccb2fa81117ba6590944ef9bd
+++ b/tests-clar/resources/status/.gitted/objects/ed/062903b8f6f3dccb2fa81117ba6590944ef9bd
Binary files differ
diff --git a/tests/resources/status/.gitted/refs/heads/master b/tests-clar/resources/status/.gitted/refs/heads/master
index 3e2e2a07a..3e2e2a07a 100644
--- a/tests/resources/status/.gitted/refs/heads/master
+++ b/tests-clar/resources/status/.gitted/refs/heads/master
diff --git a/tests/resources/status/current_file b/tests-clar/resources/status/current_file
index a0de7e0ac..a0de7e0ac 100644
--- a/tests/resources/status/current_file
+++ b/tests-clar/resources/status/current_file
diff --git a/tests/resources/status/ignored_file b/tests-clar/resources/status/ignored_file
index 6a79f808a..6a79f808a 100644
--- a/tests/resources/status/ignored_file
+++ b/tests-clar/resources/status/ignored_file
diff --git a/tests/resources/status/modified_file b/tests-clar/resources/status/modified_file
index 0a5396305..0a5396305 100644
--- a/tests/resources/status/modified_file
+++ b/tests-clar/resources/status/modified_file
diff --git a/tests/resources/status/new_file b/tests-clar/resources/status/new_file
index d4fa8600b..d4fa8600b 100644
--- a/tests/resources/status/new_file
+++ b/tests-clar/resources/status/new_file
diff --git a/tests/resources/status/staged_changes b/tests-clar/resources/status/staged_changes
index 55d316c9b..55d316c9b 100644
--- a/tests/resources/status/staged_changes
+++ b/tests-clar/resources/status/staged_changes
diff --git a/tests/resources/status/staged_changes_modified_file b/tests-clar/resources/status/staged_changes_modified_file
index 011c3440d..011c3440d 100644
--- a/tests/resources/status/staged_changes_modified_file
+++ b/tests-clar/resources/status/staged_changes_modified_file
diff --git a/tests/resources/status/staged_delete_modified_file b/tests-clar/resources/status/staged_delete_modified_file
index dabc8af9b..dabc8af9b 100644
--- a/tests/resources/status/staged_delete_modified_file
+++ b/tests-clar/resources/status/staged_delete_modified_file
diff --git a/tests/resources/status/staged_new_file b/tests-clar/resources/status/staged_new_file
index 529a16e8e..529a16e8e 100644
--- a/tests/resources/status/staged_new_file
+++ b/tests-clar/resources/status/staged_new_file
diff --git a/tests/resources/status/staged_new_file_modified_file b/tests-clar/resources/status/staged_new_file_modified_file
index 8b090c06d..8b090c06d 100644
--- a/tests/resources/status/staged_new_file_modified_file
+++ b/tests-clar/resources/status/staged_new_file_modified_file
diff --git a/tests/resources/status/subdir.txt b/tests-clar/resources/status/subdir.txt
index e8ee89e15..e8ee89e15 100644
--- a/tests/resources/status/subdir.txt
+++ b/tests-clar/resources/status/subdir.txt
diff --git a/tests/resources/status/subdir/current_file b/tests-clar/resources/status/subdir/current_file
index 53ace0d1c..53ace0d1c 100644
--- a/tests/resources/status/subdir/current_file
+++ b/tests-clar/resources/status/subdir/current_file
diff --git a/tests/resources/status/subdir/modified_file b/tests-clar/resources/status/subdir/modified_file
index 57274b75e..57274b75e 100644
--- a/tests/resources/status/subdir/modified_file
+++ b/tests-clar/resources/status/subdir/modified_file
diff --git a/tests/resources/status/subdir/new_file b/tests-clar/resources/status/subdir/new_file
index 80a86a693..80a86a693 100644
--- a/tests/resources/status/subdir/new_file
+++ b/tests-clar/resources/status/subdir/new_file
diff --git a/tests-clar/resources/submodules/.gitted/HEAD b/tests-clar/resources/submodules/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests-clar/resources/submodules/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests-clar/resources/submodules/.gitted/config b/tests-clar/resources/submodules/.gitted/config
new file mode 100644
index 000000000..af107929f
--- /dev/null
+++ b/tests-clar/resources/submodules/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests-clar/resources/submodules/.gitted/description b/tests-clar/resources/submodules/.gitted/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests-clar/resources/submodules/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests-clar/resources/submodules/.gitted/index b/tests-clar/resources/submodules/.gitted/index
new file mode 100644
index 000000000..97bf8ef51
--- /dev/null
+++ b/tests-clar/resources/submodules/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/submodules/.gitted/info/exclude b/tests-clar/resources/submodules/.gitted/info/exclude
new file mode 100644
index 000000000..dfc411579
--- /dev/null
+++ b/tests-clar/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-clar/resources/submodules/.gitted/info/refs b/tests-clar/resources/submodules/.gitted/info/refs
new file mode 100644
index 000000000..ba17abdde
--- /dev/null
+++ b/tests-clar/resources/submodules/.gitted/info/refs
@@ -0,0 +1 @@
+09176a980273d801a3e37cc45c84af1366501ed9 refs/heads/master
diff --git a/tests-clar/resources/submodules/.gitted/logs/HEAD b/tests-clar/resources/submodules/.gitted/logs/HEAD
new file mode 100644
index 000000000..87a7bdafc
--- /dev/null
+++ b/tests-clar/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-clar/resources/submodules/.gitted/logs/refs/heads/master b/tests-clar/resources/submodules/.gitted/logs/refs/heads/master
new file mode 100644
index 000000000..87a7bdafc
--- /dev/null
+++ b/tests-clar/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-clar/resources/submodules/.gitted/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e b/tests-clar/resources/submodules/.gitted/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e
new file mode 100644
index 000000000..2c3c2cb61
--- /dev/null
+++ b/tests-clar/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-clar/resources/submodules/.gitted/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357 b/tests-clar/resources/submodules/.gitted/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357
new file mode 100644
index 000000000..c85fb5512
--- /dev/null
+++ b/tests-clar/resources/submodules/.gitted/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357
Binary files differ
diff --git a/tests-clar/resources/submodules/.gitted/objects/97/896810b3210244a62a82458b8e0819ecfc6850 b/tests-clar/resources/submodules/.gitted/objects/97/896810b3210244a62a82458b8e0819ecfc6850
new file mode 100644
index 000000000..1c8dbdf9f
--- /dev/null
+++ b/tests-clar/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-clar/resources/submodules/.gitted/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888 b/tests-clar/resources/submodules/.gitted/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888
new file mode 100644
index 000000000..3d78bd6be
--- /dev/null
+++ b/tests-clar/resources/submodules/.gitted/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888
Binary files differ
diff --git a/tests-clar/resources/submodules/.gitted/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818 b/tests-clar/resources/submodules/.gitted/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818
new file mode 100644
index 000000000..6e0b49e86
--- /dev/null
+++ b/tests-clar/resources/submodules/.gitted/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818
Binary files differ
diff --git a/tests-clar/resources/submodules/.gitted/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae b/tests-clar/resources/submodules/.gitted/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae
new file mode 100644
index 000000000..082a58941
--- /dev/null
+++ b/tests-clar/resources/submodules/.gitted/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae
Binary files differ
diff --git a/tests-clar/resources/submodules/.gitted/objects/info/packs b/tests-clar/resources/submodules/.gitted/objects/info/packs
new file mode 100644
index 000000000..0785ef698
--- /dev/null
+++ b/tests-clar/resources/submodules/.gitted/objects/info/packs
@@ -0,0 +1,2 @@
+P pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack
+
diff --git a/tests-clar/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idx b/tests-clar/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idx
new file mode 100644
index 000000000..810fc3181
--- /dev/null
+++ b/tests-clar/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idx
Binary files differ
diff --git a/tests-clar/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack b/tests-clar/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack
new file mode 100644
index 000000000..c25c4a73f
--- /dev/null
+++ b/tests-clar/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack
Binary files differ
diff --git a/tests-clar/resources/submodules/.gitted/packed-refs b/tests-clar/resources/submodules/.gitted/packed-refs
new file mode 100644
index 000000000..a6450691e
--- /dev/null
+++ b/tests-clar/resources/submodules/.gitted/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+09176a980273d801a3e37cc45c84af1366501ed9 refs/heads/master
diff --git a/tests-clar/resources/submodules/.gitted/refs/heads/master b/tests-clar/resources/submodules/.gitted/refs/heads/master
new file mode 100644
index 000000000..32b935853
--- /dev/null
+++ b/tests-clar/resources/submodules/.gitted/refs/heads/master
@@ -0,0 +1 @@
+97896810b3210244a62a82458b8e0819ecfc6850
diff --git a/tests-clar/resources/submodules/added b/tests-clar/resources/submodules/added
new file mode 100644
index 000000000..d5f7fc3f7
--- /dev/null
+++ b/tests-clar/resources/submodules/added
@@ -0,0 +1 @@
+added
diff --git a/tests-clar/resources/submodules/gitmodules b/tests-clar/resources/submodules/gitmodules
new file mode 100644
index 000000000..1262f8bb0
--- /dev/null
+++ b/tests-clar/resources/submodules/gitmodules
@@ -0,0 +1,3 @@
+[submodule "testrepo"]
+ path = testrepo
+ url = \ No newline at end of file
diff --git a/tests-clar/resources/submodules/ignored b/tests-clar/resources/submodules/ignored
new file mode 100644
index 000000000..092bfb9bd
--- /dev/null
+++ b/tests-clar/resources/submodules/ignored
@@ -0,0 +1 @@
+yo
diff --git a/tests-clar/resources/submodules/modified b/tests-clar/resources/submodules/modified
new file mode 100644
index 000000000..452216e1d
--- /dev/null
+++ b/tests-clar/resources/submodules/modified
@@ -0,0 +1,2 @@
+changed
+
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/HEAD b/tests-clar/resources/submodules/testrepo/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests-clar/resources/submodules/testrepo/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/config b/tests-clar/resources/submodules/testrepo/.gitted/config
new file mode 100644
index 000000000..d6dcad12b
--- /dev/null
+++ b/tests-clar/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-clar/resources/submodules/testrepo/.gitted/description b/tests-clar/resources/submodules/testrepo/.gitted/description
new file mode 100644
index 000000000..498b267a8
--- /dev/null
+++ b/tests-clar/resources/submodules/testrepo/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/index b/tests-clar/resources/submodules/testrepo/.gitted/index
new file mode 100644
index 000000000..3eb8d84fe
--- /dev/null
+++ b/tests-clar/resources/submodules/testrepo/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/info/exclude b/tests-clar/resources/submodules/testrepo/.gitted/info/exclude
new file mode 100644
index 000000000..a5196d1be
--- /dev/null
+++ b/tests-clar/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-clar/resources/submodules/testrepo/.gitted/logs/HEAD b/tests-clar/resources/submodules/testrepo/.gitted/logs/HEAD
new file mode 100644
index 000000000..147643a30
--- /dev/null
+++ b/tests-clar/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-clar/resources/submodules/testrepo/.gitted/logs/refs/heads/master b/tests-clar/resources/submodules/testrepo/.gitted/logs/refs/heads/master
new file mode 100644
index 000000000..147643a30
--- /dev/null
+++ b/tests-clar/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/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/tests-clar/resources/submodules/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
index cedb2a22e..cedb2a22e 100644
--- a/tests/resources/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/tests-clar/resources/submodules/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
index 93a16f146..93a16f146 100644
--- a/tests/resources/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccd b/tests-clar/resources/submodules/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
index ba0bfb30c..ba0bfb30c 100644
--- a/tests/resources/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccd
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b b/tests-clar/resources/submodules/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
index 225c45734..225c45734 100644
--- a/tests/resources/testrepo.git/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d b/tests-clar/resources/submodules/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
index df40d99af..df40d99af 100644
--- a/tests/resources/testrepo.git/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54 b/tests-clar/resources/submodules/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
index 321eaa867..321eaa867 100644
--- a/tests/resources/testrepo.git/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc b/tests-clar/resources/submodules/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
index 9bb5b623b..9bb5b623b 100644
--- a/tests/resources/testrepo.git/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests-clar/resources/submodules/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
new file mode 100644
index 000000000..7ca4ceed5
--- /dev/null
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/tests-clar/resources/submodules/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
index 8953b6cef..8953b6cef 100644
--- a/tests/resources/testrepo.git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
diff --git a/tests/resources/testrepo.git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/tests-clar/resources/submodules/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
index c1f22c54f..c1f22c54f 100644
--- a/tests/resources/testrepo.git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
diff --git a/tests/resources/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/tests-clar/resources/submodules/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
index 2ef4faa0f..2ef4faa0f 100644
--- a/tests/resources/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af b/tests-clar/resources/submodules/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
index 716b0c64b..716b0c64b 100644
--- a/tests/resources/testrepo.git/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
diff --git a/tests/resources/testrepo.git/objects/7b/4384978d2493e851f9cca7858815fac9b10980 b/tests-clar/resources/submodules/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980
index 23c462f34..23c462f34 100644
--- a/tests/resources/testrepo.git/objects/7b/4384978d2493e851f9cca7858815fac9b10980
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/tests-clar/resources/submodules/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
index 2f9b6b6e3..2f9b6b6e3 100644
--- a/tests/resources/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/tests-clar/resources/submodules/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
index 5df58dda5..5df58dda5 100644
--- a/tests/resources/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162 b/tests-clar/resources/submodules/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
index 4cc3f4dff..4cc3f4dff 100644
--- a/tests/resources/testrepo.git/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
diff --git a/tests/resources/testrepo.git/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4 b/tests-clar/resources/submodules/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
index bf7b2bb68..bf7b2bb68 100644
--- a/tests/resources/testrepo.git/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/tests-clar/resources/submodules/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
index a79612435..a79612435 100644
--- a/tests/resources/testrepo.git/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
diff --git a/tests/resources/testrepo.git/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/tests-clar/resources/submodules/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
index f8588696b..f8588696b 100644
--- a/tests/resources/testrepo.git/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
diff --git a/tests/resources/testrepo.git/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750 b/tests-clar/resources/submodules/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
index 29c8e824d..29c8e824d 100644
--- a/tests/resources/testrepo.git/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
diff --git a/tests/resources/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/tests-clar/resources/submodules/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
index d0d7e736e..d0d7e736e 100644
--- a/tests/resources/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/tests-clar/resources/submodules/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
index 18a7f61c2..18a7f61c2 100644
--- a/tests/resources/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/ae/90f12eea699729ed24555e40b9fd669da12a12 b/tests-clar/resources/submodules/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
index d95254674..d95254674 100644
--- a/tests/resources/testrepo.git/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 b/tests-clar/resources/submodules/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
index f460f2547..f460f2547 100644
--- a/tests/resources/testrepo.git/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
diff --git a/tests/resources/testrepo.git/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593 b/tests-clar/resources/submodules/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
index f613670e2..f613670e2 100644
--- a/tests/resources/testrepo.git/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 b/tests-clar/resources/submodules/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
index 0817229bc..0817229bc 100644
--- a/tests/resources/testrepo.git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
diff --git a/tests/resources/testrepo.git/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd b/tests-clar/resources/submodules/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
index 75f541f10..75f541f10 100644
--- a/tests/resources/testrepo.git/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
diff --git a/tests/resources/testrepo.git/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f b/tests-clar/resources/submodules/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
index a67d6e647..a67d6e647 100644
--- a/tests/resources/testrepo.git/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests-clar/resources/submodules/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
index 711223894..711223894 100644
--- a/tests/resources/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0 b/tests-clar/resources/submodules/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
index b135eccda..b135eccda 100644
--- a/tests/resources/testrepo.git/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3 b/tests-clar/resources/submodules/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
index 82e2790e8..82e2790e8 100644
--- a/tests/resources/testrepo.git/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 b/tests-clar/resources/submodules/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
index 697c94c92..697c94c92 100644
--- a/tests/resources/testrepo.git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/tests-clar/resources/submodules/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
index 112998d42..112998d42 100644
--- a/tests/resources/testrepo.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/fd/093bff70906175335656e6ce6ae05783708765 b/tests-clar/resources/submodules/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
index 12bf5f3e3..12bf5f3e3 100644
--- a/tests/resources/testrepo.git/objects/fd/093bff70906175335656e6ce6ae05783708765
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx b/tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
index 5068f2818..5068f2818 100644
--- a/tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack b/tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
index a6a1f3020..a6a1f3020 100644
--- a/tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx b/tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
index 94c3c71da..94c3c71da 100644
--- a/tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack b/tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
index 74c7fe4f3..74c7fe4f3 100644
--- a/tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx b/tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
index 555cfa977..555cfa977 100644
--- a/tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack b/tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
index 4d539ed0a..4d539ed0a 100644
--- a/tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
+++ b/tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/packed-refs b/tests-clar/resources/submodules/testrepo/.gitted/packed-refs
new file mode 100644
index 000000000..9c0433e1c
--- /dev/null
+++ b/tests-clar/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/testrepo.git/refs/heads/master b/tests-clar/resources/submodules/testrepo/.gitted/refs/heads/master
index 3d8f0a402..3d8f0a402 100644
--- a/tests/resources/testrepo.git/refs/heads/master
+++ b/tests-clar/resources/submodules/testrepo/.gitted/refs/heads/master
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/refs/remotes/origin/HEAD b/tests-clar/resources/submodules/testrepo/.gitted/refs/remotes/origin/HEAD
new file mode 100644
index 000000000..6efe28fff
--- /dev/null
+++ b/tests-clar/resources/submodules/testrepo/.gitted/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests-clar/resources/submodules/testrepo/README b/tests-clar/resources/submodules/testrepo/README
new file mode 100644
index 000000000..a8233120f
--- /dev/null
+++ b/tests-clar/resources/submodules/testrepo/README
@@ -0,0 +1 @@
+hey there
diff --git a/tests-clar/resources/submodules/testrepo/branch_file.txt b/tests-clar/resources/submodules/testrepo/branch_file.txt
new file mode 100644
index 000000000..3697d64be
--- /dev/null
+++ b/tests-clar/resources/submodules/testrepo/branch_file.txt
@@ -0,0 +1,2 @@
+hi
+bye!
diff --git a/tests-clar/resources/submodules/testrepo/new.txt b/tests-clar/resources/submodules/testrepo/new.txt
new file mode 100644
index 000000000..a71586c1d
--- /dev/null
+++ b/tests-clar/resources/submodules/testrepo/new.txt
@@ -0,0 +1 @@
+my new file
diff --git a/tests-clar/resources/submodules/unmodified b/tests-clar/resources/submodules/unmodified
new file mode 100644
index 000000000..092bfb9bd
--- /dev/null
+++ b/tests-clar/resources/submodules/unmodified
@@ -0,0 +1 @@
+yo
diff --git a/tests-clar/resources/submodules/untracked b/tests-clar/resources/submodules/untracked
new file mode 100644
index 000000000..092bfb9bd
--- /dev/null
+++ b/tests-clar/resources/submodules/untracked
@@ -0,0 +1 @@
+yo
diff --git a/tests-clar/resources/testrepo.git/HEAD b/tests-clar/resources/testrepo.git/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/testrepo.git/config b/tests-clar/resources/testrepo.git/config
index 1a5aacdfa..1a5aacdfa 100644
--- a/tests/resources/testrepo.git/config
+++ b/tests-clar/resources/testrepo.git/config
diff --git a/tests/resources/testrepo.git/head-tracker b/tests-clar/resources/testrepo.git/head-tracker
index 40d876b4c..40d876b4c 100644
--- a/tests/resources/testrepo.git/head-tracker
+++ b/tests-clar/resources/testrepo.git/head-tracker
diff --git a/tests/resources/testrepo.git/index b/tests-clar/resources/testrepo.git/index
index a27fb9c96..a27fb9c96 100644
--- a/tests/resources/testrepo.git/index
+++ b/tests-clar/resources/testrepo.git/index
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/tests-clar/resources/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
new file mode 100644
index 000000000..cedb2a22e
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/tests-clar/resources/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
new file mode 100644
index 000000000..93a16f146
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccd b/tests-clar/resources/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccd
new file mode 100644
index 000000000..ba0bfb30c
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccd
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b b/tests-clar/resources/testrepo.git/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
new file mode 100644
index 000000000..225c45734
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d b/tests-clar/resources/testrepo.git/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
new file mode 100644
index 000000000..df40d99af
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54 b/tests-clar/resources/testrepo.git/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
new file mode 100644
index 000000000..321eaa867
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc b/tests-clar/resources/testrepo.git/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
new file mode 100644
index 000000000..9bb5b623b
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests-clar/resources/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
new file mode 100644
index 000000000..7ca4ceed5
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/tests-clar/resources/testrepo.git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
new file mode 100644
index 000000000..8953b6cef
--- /dev/null
+++ b/tests-clar/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-clar/resources/testrepo.git/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91 b/tests-clar/resources/testrepo.git/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91
new file mode 100644
index 000000000..351cff823
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/tests-clar/resources/testrepo.git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
new file mode 100644
index 000000000..c1f22c54f
--- /dev/null
+++ b/tests-clar/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-clar/resources/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/tests-clar/resources/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
new file mode 100644
index 000000000..2ef4faa0f
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af b/tests-clar/resources/testrepo.git/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
new file mode 100644
index 000000000..716b0c64b
--- /dev/null
+++ b/tests-clar/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-clar/resources/testrepo.git/objects/7b/4384978d2493e851f9cca7858815fac9b10980 b/tests-clar/resources/testrepo.git/objects/7b/4384978d2493e851f9cca7858815fac9b10980
new file mode 100644
index 000000000..23c462f34
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/7b/4384978d2493e851f9cca7858815fac9b10980
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/tests-clar/resources/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
new file mode 100644
index 000000000..2f9b6b6e3
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/tests-clar/resources/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479
new file mode 100644
index 000000000..5df58dda5
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162 b/tests-clar/resources/testrepo.git/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
new file mode 100644
index 000000000..4cc3f4dff
--- /dev/null
+++ b/tests-clar/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-clar/resources/testrepo.git/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4 b/tests-clar/resources/testrepo.git/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
new file mode 100644
index 000000000..bf7b2bb68
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/tests-clar/resources/testrepo.git/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
new file mode 100644
index 000000000..a79612435
--- /dev/null
+++ b/tests-clar/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-clar/resources/testrepo.git/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/tests-clar/resources/testrepo.git/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
new file mode 100644
index 000000000..f8588696b
--- /dev/null
+++ b/tests-clar/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-clar/resources/testrepo.git/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750 b/tests-clar/resources/testrepo.git/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
new file mode 100644
index 000000000..29c8e824d
--- /dev/null
+++ b/tests-clar/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-clar/resources/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/tests-clar/resources/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
new file mode 100644
index 000000000..d0d7e736e
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/tests-clar/resources/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
new file mode 100644
index 000000000..18a7f61c2
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/ae/90f12eea699729ed24555e40b9fd669da12a12 b/tests-clar/resources/testrepo.git/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
new file mode 100644
index 000000000..d95254674
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 b/tests-clar/resources/testrepo.git/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
new file mode 100644
index 000000000..f460f2547
--- /dev/null
+++ b/tests-clar/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-clar/resources/testrepo.git/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593 b/tests-clar/resources/testrepo.git/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
new file mode 100644
index 000000000..f613670e2
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 b/tests-clar/resources/testrepo.git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
new file mode 100644
index 000000000..0817229bc
--- /dev/null
+++ b/tests-clar/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-clar/resources/testrepo.git/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd b/tests-clar/resources/testrepo.git/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
new file mode 100644
index 000000000..75f541f10
--- /dev/null
+++ b/tests-clar/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-clar/resources/testrepo.git/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f b/tests-clar/resources/testrepo.git/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
new file mode 100644
index 000000000..a67d6e647
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests-clar/resources/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644
index 000000000..711223894
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0 b/tests-clar/resources/testrepo.git/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
new file mode 100644
index 000000000..b135eccda
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3 b/tests-clar/resources/testrepo.git/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
new file mode 100644
index 000000000..82e2790e8
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 b/tests-clar/resources/testrepo.git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
new file mode 100644
index 000000000..697c94c92
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/tests-clar/resources/testrepo.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92
new file mode 100644
index 000000000..112998d42
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/fd/093bff70906175335656e6ce6ae05783708765 b/tests-clar/resources/testrepo.git/objects/fd/093bff70906175335656e6ce6ae05783708765
new file mode 100644
index 000000000..12bf5f3e3
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/fd/093bff70906175335656e6ce6ae05783708765
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx b/tests-clar/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
new file mode 100644
index 000000000..5068f2818
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack b/tests-clar/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
new file mode 100644
index 000000000..a6a1f3020
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx b/tests-clar/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
new file mode 100644
index 000000000..94c3c71da
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack b/tests-clar/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
new file mode 100644
index 000000000..74c7fe4f3
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx b/tests-clar/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
new file mode 100644
index 000000000..555cfa977
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack b/tests-clar/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
new file mode 100644
index 000000000..4d539ed0a
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
Binary files differ
diff --git a/tests/resources/testrepo.git/packed-refs b/tests-clar/resources/testrepo.git/packed-refs
index 52f5e876f..52f5e876f 100644
--- a/tests/resources/testrepo.git/packed-refs
+++ b/tests-clar/resources/testrepo.git/packed-refs
diff --git a/tests/resources/testrepo.git/refs/heads/br2 b/tests-clar/resources/testrepo.git/refs/heads/br2
index aab87e5e7..aab87e5e7 100644
--- a/tests/resources/testrepo.git/refs/heads/br2
+++ b/tests-clar/resources/testrepo.git/refs/heads/br2
diff --git a/tests-clar/resources/testrepo.git/refs/heads/master b/tests-clar/resources/testrepo.git/refs/heads/master
new file mode 100644
index 000000000..3d8f0a402
--- /dev/null
+++ b/tests-clar/resources/testrepo.git/refs/heads/master
@@ -0,0 +1 @@
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750
diff --git a/tests/resources/testrepo.git/refs/heads/packed-test b/tests-clar/resources/testrepo.git/refs/heads/packed-test
index f2c14ad83..f2c14ad83 100644
--- a/tests/resources/testrepo.git/refs/heads/packed-test
+++ b/tests-clar/resources/testrepo.git/refs/heads/packed-test
diff --git a/tests/resources/testrepo.git/refs/heads/subtrees b/tests-clar/resources/testrepo.git/refs/heads/subtrees
index ad27e0b13..ad27e0b13 100644
--- a/tests/resources/testrepo.git/refs/heads/subtrees
+++ b/tests-clar/resources/testrepo.git/refs/heads/subtrees
diff --git a/tests/resources/testrepo.git/refs/heads/test b/tests-clar/resources/testrepo.git/refs/heads/test
index 399c4c73e..399c4c73e 100644
--- a/tests/resources/testrepo.git/refs/heads/test
+++ b/tests-clar/resources/testrepo.git/refs/heads/test
diff --git a/tests-clar/resources/testrepo.git/refs/tags/annotated_tag_to_blob b/tests-clar/resources/testrepo.git/refs/tags/annotated_tag_to_blob
new file mode 100644
index 000000000..6c146d6e3
--- /dev/null
+++ b/tests-clar/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-clar/resources/testrepo.git/refs/tags/e90810b
index 584495d3c..584495d3c 100644
--- a/tests/resources/testrepo.git/refs/tags/e90810b
+++ b/tests-clar/resources/testrepo.git/refs/tags/e90810b
diff --git a/tests/resources/testrepo.git/refs/tags/point_to_blob b/tests-clar/resources/testrepo.git/refs/tags/point_to_blob
index f874a3ffc..f874a3ffc 100644
--- a/tests/resources/testrepo.git/refs/tags/point_to_blob
+++ b/tests-clar/resources/testrepo.git/refs/tags/point_to_blob
diff --git a/tests/resources/testrepo.git/refs/tags/test b/tests-clar/resources/testrepo.git/refs/tags/test
index 6ee952a03..6ee952a03 100644
--- a/tests/resources/testrepo.git/refs/tags/test
+++ b/tests-clar/resources/testrepo.git/refs/tags/test
diff --git a/tests-clar/resources/testrepo/.gitted/HEAD b/tests-clar/resources/testrepo/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests-clar/resources/testrepo/.gitted/config b/tests-clar/resources/testrepo/.gitted/config
new file mode 100644
index 000000000..1a5aacdfa
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/config
@@ -0,0 +1,8 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = true
+ logallrefupdates = true
+[remote "test"]
+ url = git://github.com/libgit2/libgit2
+ fetch = +refs/heads/*:refs/remotes/test/*
diff --git a/tests-clar/resources/testrepo/.gitted/head-tracker b/tests-clar/resources/testrepo/.gitted/head-tracker
new file mode 100644
index 000000000..40d876b4c
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/head-tracker
@@ -0,0 +1 @@
+ref: HEAD
diff --git a/tests-clar/resources/testrepo/.gitted/index b/tests-clar/resources/testrepo/.gitted/index
new file mode 100644
index 000000000..a27fb9c96
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/tests-clar/resources/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
new file mode 100644
index 000000000..cedb2a22e
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/tests-clar/resources/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
new file mode 100644
index 000000000..93a16f146
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd b/tests-clar/resources/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
new file mode 100644
index 000000000..ba0bfb30c
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b b/tests-clar/resources/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
new file mode 100644
index 000000000..225c45734
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d b/tests-clar/resources/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
new file mode 100644
index 000000000..df40d99af
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54 b/tests-clar/resources/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
new file mode 100644
index 000000000..321eaa867
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc b/tests-clar/resources/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
new file mode 100644
index 000000000..9bb5b623b
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests-clar/resources/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
new file mode 100644
index 000000000..7ca4ceed5
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/tests-clar/resources/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
new file mode 100644
index 000000000..8953b6cef
--- /dev/null
+++ b/tests-clar/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-clar/resources/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/tests-clar/resources/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
new file mode 100644
index 000000000..c1f22c54f
--- /dev/null
+++ b/tests-clar/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-clar/resources/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/tests-clar/resources/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
new file mode 100644
index 000000000..2ef4faa0f
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af b/tests-clar/resources/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
new file mode 100644
index 000000000..716b0c64b
--- /dev/null
+++ b/tests-clar/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-clar/resources/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980 b/tests-clar/resources/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980
new file mode 100644
index 000000000..23c462f34
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/tests-clar/resources/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
new file mode 100644
index 000000000..2f9b6b6e3
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/tests-clar/resources/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
new file mode 100644
index 000000000..5df58dda5
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162 b/tests-clar/resources/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
new file mode 100644
index 000000000..4cc3f4dff
--- /dev/null
+++ b/tests-clar/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-clar/resources/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4 b/tests-clar/resources/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
new file mode 100644
index 000000000..bf7b2bb68
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/tests-clar/resources/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
new file mode 100644
index 000000000..a79612435
--- /dev/null
+++ b/tests-clar/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-clar/resources/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/tests-clar/resources/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
new file mode 100644
index 000000000..f8588696b
--- /dev/null
+++ b/tests-clar/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-clar/resources/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750 b/tests-clar/resources/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
new file mode 100644
index 000000000..29c8e824d
--- /dev/null
+++ b/tests-clar/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-clar/resources/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/tests-clar/resources/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
new file mode 100644
index 000000000..d0d7e736e
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/tests-clar/resources/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
new file mode 100644
index 000000000..18a7f61c2
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12 b/tests-clar/resources/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
new file mode 100644
index 000000000..d95254674
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 b/tests-clar/resources/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
new file mode 100644
index 000000000..f460f2547
--- /dev/null
+++ b/tests-clar/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-clar/resources/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593 b/tests-clar/resources/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
new file mode 100644
index 000000000..f613670e2
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 b/tests-clar/resources/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
new file mode 100644
index 000000000..0817229bc
--- /dev/null
+++ b/tests-clar/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-clar/resources/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd b/tests-clar/resources/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
new file mode 100644
index 000000000..75f541f10
--- /dev/null
+++ b/tests-clar/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-clar/resources/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f b/tests-clar/resources/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
new file mode 100644
index 000000000..a67d6e647
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests-clar/resources/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644
index 000000000..711223894
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0 b/tests-clar/resources/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
new file mode 100644
index 000000000..b135eccda
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3 b/tests-clar/resources/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
new file mode 100644
index 000000000..82e2790e8
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 b/tests-clar/resources/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
new file mode 100644
index 000000000..697c94c92
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/tests-clar/resources/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
new file mode 100644
index 000000000..112998d42
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765 b/tests-clar/resources/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
new file mode 100644
index 000000000..12bf5f3e3
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx b/tests-clar/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
new file mode 100644
index 000000000..5068f2818
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack b/tests-clar/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
new file mode 100644
index 000000000..a6a1f3020
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx b/tests-clar/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
new file mode 100644
index 000000000..94c3c71da
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack b/tests-clar/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
new file mode 100644
index 000000000..74c7fe4f3
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx b/tests-clar/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
new file mode 100644
index 000000000..555cfa977
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack b/tests-clar/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
new file mode 100644
index 000000000..4d539ed0a
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/packed-refs b/tests-clar/resources/testrepo/.gitted/packed-refs
new file mode 100644
index 000000000..52f5e876f
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/packed-refs
@@ -0,0 +1,3 @@
+# pack-refs with: peeled
+41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9 refs/heads/packed
+5b5b025afb0b4c913b4c338a42934a3863bf3644 refs/heads/packed-test
diff --git a/tests-clar/resources/testrepo/.gitted/refs/heads/br2 b/tests-clar/resources/testrepo/.gitted/refs/heads/br2
new file mode 100644
index 000000000..aab87e5e7
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/refs/heads/br2
@@ -0,0 +1 @@
+a4a7dce85cf63874e984719f4fdd239f5145052f
diff --git a/tests-clar/resources/testrepo/.gitted/refs/heads/master b/tests-clar/resources/testrepo/.gitted/refs/heads/master
new file mode 100644
index 000000000..3d8f0a402
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/refs/heads/master
@@ -0,0 +1 @@
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750
diff --git a/tests-clar/resources/testrepo/.gitted/refs/heads/packed-test b/tests-clar/resources/testrepo/.gitted/refs/heads/packed-test
new file mode 100644
index 000000000..f2c14ad83
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/refs/heads/packed-test
@@ -0,0 +1 @@
+4a202b346bb0fb0db7eff3cffeb3c70babbd2045
diff --git a/tests-clar/resources/testrepo/.gitted/refs/heads/subtrees b/tests-clar/resources/testrepo/.gitted/refs/heads/subtrees
new file mode 100644
index 000000000..ad27e0b13
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/refs/heads/subtrees
@@ -0,0 +1 @@
+763d71aadf09a7951596c9746c024e7eece7c7af
diff --git a/tests-clar/resources/testrepo/.gitted/refs/heads/test b/tests-clar/resources/testrepo/.gitted/refs/heads/test
new file mode 100644
index 000000000..399c4c73e
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/refs/heads/test
@@ -0,0 +1 @@
+e90810b8df3e80c413d903f631643c716887138d
diff --git a/tests-clar/resources/testrepo/.gitted/refs/tags/e90810b b/tests-clar/resources/testrepo/.gitted/refs/tags/e90810b
new file mode 100644
index 000000000..584495d3c
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/refs/tags/e90810b
@@ -0,0 +1 @@
+7b4384978d2493e851f9cca7858815fac9b10980
diff --git a/tests-clar/resources/testrepo/.gitted/refs/tags/point_to_blob b/tests-clar/resources/testrepo/.gitted/refs/tags/point_to_blob
new file mode 100644
index 000000000..f874a3ffc
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/refs/tags/point_to_blob
@@ -0,0 +1 @@
+1385f264afb75a56a5bec74243be9b367ba4ca08
diff --git a/tests-clar/resources/testrepo/.gitted/refs/tags/test b/tests-clar/resources/testrepo/.gitted/refs/tags/test
new file mode 100644
index 000000000..6ee952a03
--- /dev/null
+++ b/tests-clar/resources/testrepo/.gitted/refs/tags/test
@@ -0,0 +1 @@
+b25fa35b38051e4ae45d4222e795f9df2e43f1d1
diff --git a/tests/t05-revwalk.c b/tests-clar/revwalk/basic.c
index ab509ab94..a5a9b2eda 100644
--- a/tests/t05-revwalk.c
+++ b/tests-clar/revwalk/basic.c
@@ -1,29 +1,4 @@
-/*
- * This file is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2,
- * as published by the Free Software Foundation.
- *
- * In addition to the permissions in the GNU General Public License,
- * the authors give you unlimited permission to link the compiled
- * version of this file into combinations with other programs,
- * and to distribute those combinations without any restriction
- * coming from the use of this file. (The General Public License
- * restrictions do apply in other respects; for example, they cover
- * modification of the file, and distribution when not linked into
- * a combined executable.)
- *
- * This file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-#include "test_lib.h"
-#include "test_helpers.h"
+#include "clar_libgit2.h"
/*
$ git log --oneline --graph --decorate
@@ -98,7 +73,7 @@ static int test_walk(git_revwalk *walk, const git_oid *root,
i = 0;
- while (git_revwalk_next(&oid, walk) == GIT_SUCCESS) {
+ while (git_revwalk_next(&oid, walk) == 0) {
result_array[i++] = get_commit_index(&oid);
/*{
char str[41];
@@ -111,30 +86,96 @@ static int test_walk(git_revwalk *walk, const git_oid *root,
for (i = 0; i < results_count; ++i)
if (memcmp(possible_results[i],
result_array, result_bytes) == 0)
- return GIT_SUCCESS;
+ return 0;
return GIT_ERROR;
}
-BEGIN_TEST(walk0, "do a simple walk on a repo with different sorting modes")
- git_oid id;
- git_repository *repo;
- git_revwalk *walk;
+static git_repository *_repo;
+static git_revwalk *_walk;
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
- must_pass(git_revwalk_new(&walk, repo));
+void test_revwalk_basic__initialize(void)
+{
+ cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_revwalk_new(&_walk, _repo));
+}
+
+void test_revwalk_basic__cleanup(void)
+{
+ git_revwalk_free(_walk);
+ git_repository_free(_repo);
+}
+
+void test_revwalk_basic__sorting_modes(void)
+{
+ git_oid id;
git_oid_fromstr(&id, commit_head);
- must_pass(test_walk(walk, &id, GIT_SORT_TIME, commit_sorting_time, 1));
- must_pass(test_walk(walk, &id, GIT_SORT_TOPOLOGICAL, commit_sorting_topo, 2));
- must_pass(test_walk(walk, &id, GIT_SORT_TIME | GIT_SORT_REVERSE, commit_sorting_time_reverse, 1));
- must_pass(test_walk(walk, &id, GIT_SORT_TOPOLOGICAL | GIT_SORT_REVERSE, commit_sorting_topo_reverse, 2));
+ 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));
+}
- git_revwalk_free(walk);
- git_repository_free(repo);
-END_TEST
+void test_revwalk_basic__glob_heads(void)
+{
+ int i = 0;
+ git_oid oid;
+
+ cl_git_pass(git_revwalk_push_glob(_walk, "heads"));
+
+ while (git_revwalk_next(&oid, _walk) == 0) {
+ i++;
+ }
+
+ /* git log --branches --oneline | wc -l => 13 */
+ cl_assert(i == 13);
+}
+
+void test_revwalk_basic__push_head(void)
+{
+ int i = 0;
+ git_oid oid;
-BEGIN_SUITE(revwalk)
- ADD_TEST(walk0);
-END_SUITE
+ cl_git_pass(git_revwalk_push_head(_walk));
+
+ while (git_revwalk_next(&oid, _walk) == 0) {
+ i++;
+ }
+
+ /* git log HEAD --oneline | wc -l => 7 */
+ cl_assert(i == 7);
+}
+
+void test_revwalk_basic__push_head_hide_ref(void)
+{
+ int i = 0;
+ git_oid oid;
+
+ cl_git_pass(git_revwalk_push_head(_walk));
+ cl_git_pass(git_revwalk_hide_ref(_walk, "refs/heads/packed-test"));
+
+ while (git_revwalk_next(&oid, _walk) == 0) {
+ i++;
+ }
+
+ /* git log HEAD --oneline --not refs/heads/packed-test | wc -l => 4 */
+ cl_assert(i == 4);
+}
+
+void test_revwalk_basic__push_head_hide_ref_nobase(void)
+{
+ int i = 0;
+ git_oid oid;
+
+ cl_git_pass(git_revwalk_push_head(_walk));
+ cl_git_pass(git_revwalk_hide_ref(_walk, "refs/heads/packed"));
+
+ while (git_revwalk_next(&oid, _walk) == 0) {
+ i++;
+ }
+
+ /* git log HEAD --oneline --not refs/heads/packed | wc -l => 7 */
+ cl_assert(i == 7);
+}
diff --git a/tests-clar/revwalk/mergebase.c b/tests-clar/revwalk/mergebase.c
new file mode 100644
index 000000000..e807e3ad2
--- /dev/null
+++ b/tests-clar/revwalk/mergebase.c
@@ -0,0 +1,148 @@
+#include "clar_libgit2.h"
+
+static git_repository *_repo;
+
+void test_revwalk_mergebase__initialize(void)
+{
+ cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git")));
+}
+
+void test_revwalk_mergebase__cleanup(void)
+{
+ git_repository_free(_repo);
+}
+
+void test_revwalk_mergebase__single1(void)
+{
+ git_oid result, one, two, expected;
+
+ git_oid_fromstr(&one, "c47800c7266a2be04c571c04d5a6614691ea99bd ");
+ git_oid_fromstr(&two, "9fd738e8f7967c078dceed8190330fc8648ee56a");
+ git_oid_fromstr(&expected, "5b5b025afb0b4c913b4c338a42934a3863bf3644");
+
+ cl_git_pass(git_merge_base(&result, _repo, &one, &two));
+ cl_assert(git_oid_cmp(&result, &expected) == 0);
+}
+
+void test_revwalk_mergebase__single2(void)
+{
+ git_oid result, one, two, expected;
+
+ git_oid_fromstr(&one, "763d71aadf09a7951596c9746c024e7eece7c7af");
+ git_oid_fromstr(&two, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ 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__merged_branch(void)
+{
+ git_oid result, one, two, expected;
+
+ git_oid_fromstr(&one, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ git_oid_fromstr(&two, "9fd738e8f7967c078dceed8190330fc8648ee56a");
+ 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);
+}
+
+void test_revwalk_mergebase__no_common_ancestor_returns_ENOTFOUND(void)
+{
+ git_oid result, one, two, expected;
+ int error;
+
+ git_oid_fromstr(&one, "763d71aadf09a7951596c9746c024e7eece7c7af");
+ git_oid_fromstr(&two, "e90810b8df3e80c413d903f631643c716887138d");
+ git_oid_fromstr(&expected, "c47800c7266a2be04c571c04d5a6614691ea99bd");
+
+ error = git_merge_base(&result, _repo, &one, &two);
+ cl_git_fail(error);
+
+ cl_assert_equal_i(GIT_ENOTFOUND, error);
+}
+
+/*
+ * $ 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
+ */
diff --git a/tests-clar/status/ignore.c b/tests-clar/status/ignore.c
index 67aecba31..369b25bda 100644
--- a/tests-clar/status/ignore.c
+++ b/tests-clar/status/ignore.c
@@ -2,26 +2,17 @@
#include "fileops.h"
#include "git2/attr.h"
#include "attr.h"
+#include "status_helpers.h"
static git_repository *g_repo = NULL;
void test_status_ignore__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 gitignore to .gitignore.
- */
- cl_fixture_sandbox("attr");
- cl_git_pass(p_rename("attr/.gitted", "attr/.git"));
- cl_git_pass(p_rename("attr/gitignore", "attr/.gitignore"));
- cl_git_pass(git_repository_open(&g_repo, "attr/.git"));
}
void test_status_ignore__cleanup(void)
{
- git_repository_free(g_repo);
- g_repo = NULL;
- cl_fixture_cleanup("attr");
+ cl_git_sandbox_cleanup();
}
void test_status_ignore__0(void)
@@ -49,13 +40,94 @@ void test_status_ignore__0(void)
{ 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(g_repo, one_test->path, &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_git_pass(git_attr_cache__is_cached(g_repo, ".git/info/exclude"));
- cl_git_pass(git_attr_cache__is_cached(g_repo, ".gitignore"));
+ 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);
+}
+
diff --git a/tests-clar/status/status_data.h b/tests-clar/status/status_data.h
index 1a68648f4..f109717e8 100644
--- a/tests-clar/status/status_data.h
+++ b/tests-clar/status/status_data.h
@@ -1,12 +1,6 @@
+#include "status_helpers.h"
-struct status_entry_counts {
- 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;
-};
+/* entries for a plain copy of tests/resources/status */
static const char *entry_paths0[] = {
"file_deleted",
@@ -48,3 +42,153 @@ static const unsigned int entry_statuses0[] = {
static const size_t entry_count0 = 15;
+/* 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 size_t entry_count2 = 15;
+
+/* entries for a copy of tests/resources/status with some mods */
+
+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",
+};
+
+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,
+};
+
+static const size_t entry_count3 = 21;
+
+
+/* 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"
+};
+
+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,
+};
+
+static const size_t entry_count4 = 22;
diff --git a/tests-clar/status/status_helpers.c b/tests-clar/status/status_helpers.c
new file mode 100644
index 000000000..3dbf43a5b
--- /dev/null
+++ b/tests-clar/status/status_helpers.c
@@ -0,0 +1,49 @@
+#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->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;
+
+ GIT_UNUSED(p);
+
+ data->count++;
+ data->status = s;
+
+ return 0;
+}
diff --git a/tests-clar/status/status_helpers.h b/tests-clar/status/status_helpers.h
new file mode 100644
index 000000000..cffca66a5
--- /dev/null
+++ b/tests-clar/status/status_helpers.h
@@ -0,0 +1,33 @@
+#ifndef INCLUDE_cl_status_helpers_h__
+#define INCLUDE_cl_status_helpers_h__
+
+typedef struct {
+ size_t wrong_status_flags_count;
+ size_t wrong_sorted_path;
+ size_t entry_count;
+ const unsigned int* expected_statuses;
+ const char** expected_paths;
+ size_t expected_entry_count;
+} 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;
+} 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);
+
+#endif
diff --git a/tests-clar/status/submodules.c b/tests-clar/status/submodules.c
new file mode 100644
index 000000000..9423e8490
--- /dev/null
+++ b/tests-clar/status/submodules.c
@@ -0,0 +1,112 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "path.h"
+#include "posix.h"
+#include "status_helpers.h"
+
+static git_repository *g_repo = NULL;
+
+void test_status_submodules__initialize(void)
+{
+ git_buf modpath = GIT_BUF_INIT;
+
+ g_repo = cl_git_sandbox_init("submodules");
+
+ cl_fixture_sandbox("testrepo.git");
+
+ cl_git_pass(git_buf_sets(&modpath, git_repository_workdir(g_repo)));
+ cl_assert(git_path_dirname_r(&modpath, modpath.ptr) >= 0);
+ cl_git_pass(git_buf_joinpath(&modpath, modpath.ptr, "testrepo.git\n"));
+
+ p_rename("submodules/gitmodules", "submodules/.gitmodules");
+ cl_git_append2file("submodules/.gitmodules", modpath.ptr);
+ git_buf_free(&modpath);
+
+ p_rename("submodules/testrepo/.gitted", "submodules/testrepo/.git");
+}
+
+void test_status_submodules__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_status_submodules__api(void)
+{
+ git_submodule *sm;
+
+ cl_assert(git_submodule_lookup(NULL, g_repo, "nonexistent") == GIT_ENOTFOUND);
+
+ cl_assert(git_submodule_lookup(NULL, g_repo, "modified") == GIT_ENOTFOUND);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
+ cl_assert(sm != NULL);
+ cl_assert_equal_s("testrepo", sm->name);
+ cl_assert_equal_s("testrepo", sm->path);
+}
+
+void test_status_submodules__0(void)
+{
+ int counts = 0;
+
+ cl_assert(git_path_isdir("submodules/.git"));
+ cl_assert(git_path_isdir("submodules/testrepo/.git"));
+ cl_assert(git_path_isfile("submodules/.gitmodules"));
+
+ cl_git_pass(
+ git_status_foreach(g_repo, cb_status__count, &counts)
+ );
+
+ cl_assert(counts == 6);
+}
+
+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)
+{
+ volatile int *index = (int *)payload;
+
+ cl_assert_equal_s(expected_files[*index], p);
+ cl_assert(expected_status[*index] == s);
+ (*index)++;
+
+ return 0;
+}
+
+void test_status_submodules__1(void)
+{
+ int index = 0;
+
+ cl_assert(git_path_isdir("submodules/.git"));
+ cl_assert(git_path_isdir("submodules/testrepo/.git"));
+ cl_assert(git_path_isfile("submodules/.gitmodules"));
+
+ cl_git_pass(
+ git_status_foreach(g_repo, cb_status__match, &index)
+ );
+
+ cl_assert(index == 6);
+}
+
+void test_status_submodules__single_file(void)
+{
+ unsigned int status;
+ cl_git_pass( git_status_file(&status, g_repo, "testrepo") );
+ cl_assert(status == 0);
+}
diff --git a/tests-clar/status/worktree.c b/tests-clar/status/worktree.c
index d8e62a94d..6cc6259b8 100644
--- a/tests-clar/status/worktree.c
+++ b/tests-clar/status/worktree.c
@@ -2,153 +2,517 @@
#include "fileops.h"
#include "ignore.h"
#include "status_data.h"
-
+#include "posix.h"
+#include "util.h"
+#include "path.h"
/**
- * Test fixtures
+ * Initializer
+ *
+ * Not all of the tests in this file use the same fixtures, so we allow each
+ * test to load their fixture at the top of the test function.
*/
-static git_repository *_repository = NULL;
+void test_status_worktree__initialize(void)
+{
+}
+/**
+ * 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();
+}
/**
- * Auxiliary methods
+ * Tests - Status determination on a working tree
*/
-static int
-cb_status__normal( const char *path, unsigned int status_flags, void *payload)
+/* this test is equivalent to t18-status.c:statuscb0 */
+void test_status_worktree__whole_repository(void)
{
- struct status_entry_counts *counts = payload;
+ status_entry_counts counts;
+ git_repository *repo = cl_git_sandbox_init("status");
- if (counts->entry_count >= counts->expected_entry_count) {
- counts->wrong_status_flags_count++;
- goto exit;
- }
+ memset(&counts, 0x0, sizeof(status_entry_counts));
+ counts.expected_entry_count = entry_count0;
+ counts.expected_paths = entry_paths0;
+ counts.expected_statuses = entry_statuses0;
- if (strcmp(path, counts->expected_paths[counts->entry_count])) {
- counts->wrong_sorted_path++;
- goto exit;
- }
+ cl_git_pass(
+ git_status_foreach(repo, cb_status__normal, &counts)
+ );
- if (status_flags != counts->expected_statuses[counts->entry_count])
- counts->wrong_status_flags_count++;
+ 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);
+}
-exit:
- counts->entry_count++;
- return GIT_SUCCESS;
+/* 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
-cb_status__count(const char *GIT_UNUSED(p), unsigned int GIT_UNUSED(s), void *payload)
+static int remove_file_cb(void *data, git_buf *file)
{
- volatile int *count = (int *)payload;
+ const char *filename = git_buf_cstr(file);
+
+ GIT_UNUSED(data);
- GIT_UNUSED_ARG(p);
- GIT_UNUSED_ARG(s);
+ if (git__suffixcmp(filename, ".git") == 0)
+ return 0;
- *count++;
+ if (git_path_isdir(filename))
+ cl_git_pass(git_futils_rmdir_r(filename, GIT_DIRREMOVAL_FILES_AND_DIRS));
+ else
+ cl_git_pass(p_unlink(git_buf_cstr(file)));
- return GIT_SUCCESS;
+ return 0;
}
+/* this test is equivalent to t18-status.c:statuscb2 */
+void test_status_worktree__purged_worktree(void)
+{
+ status_entry_counts counts;
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_buf workdir = GIT_BUF_INIT;
+ /* first purge the contents of the worktree */
+ cl_git_pass(git_buf_sets(&workdir, git_repository_workdir(repo)));
+ cl_git_pass(git_path_direach(&workdir, remove_file_cb, NULL));
+ git_buf_free(&workdir);
-/**
- * Initializer
- *
- * This method is called once before starting each
- * test, and will load the required fixtures
- */
-void test_status_worktree__initialize(void)
-{
- /*
- * Sandbox the `status/` repository from our Fixtures.
- * This will copy the whole folder to our sandbox,
- * so now it can be accessed with `./status`
- */
- cl_fixture_sandbox("status");
+ /* 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;
- /*
- * Rename `status/.gitted` to `status/.git`
- * We do this because we cannot store a folder named `.git`
- * inside the fixtures folder in our libgit2 repo.
- */
cl_git_pass(
- p_rename("status/.gitted", "status/.git")
+ git_status_foreach(repo, cb_status__normal, &counts)
);
- /*
- * Open the sandboxed "status" repository
- */
- cl_git_pass(git_repository_open(&_repository, "status/.git"));
+ 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);
}
-/**
- * Cleanup
- *
- * This will be called once after each test finishes, even
- * if the test failed
- */
-void test_status_worktree__cleanup(void)
+/* this test is similar to t18-status.c:statuscb3 */
+void test_status_worktree__swap_subdir_and_file(void)
{
- git_repository_free(_repository);
- _repository = NULL;
+ status_entry_counts counts;
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_status_options opts;
- cl_fixture_cleanup("status");
-}
+ /* 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"));
-/**
- * Tests - Status determination on a working tree
- */
-void test_status_worktree__whole_repository(void)
-{
- struct status_entry_counts counts;
+ cl_git_mkfile("status/.HEADER", "dummy");
+ cl_git_mkfile("status/42-is-not-prime.sigh", "dummy");
+ cl_git_mkfile("status/README.md", "dummy");
- memset(&counts, 0x0, sizeof(struct status_entry_counts));
- counts.expected_entry_count = entry_count0;
- counts.expected_paths = entry_paths0;
- counts.expected_statuses = entry_statuses0;
+ /* now get status */
+ memset(&counts, 0x0, sizeof(status_entry_counts));
+ counts.expected_entry_count = entry_count3;
+ counts.expected_paths = entry_paths3;
+ counts.expected_statuses = entry_statuses3;
+
+ memset(&opts, 0, sizeof(opts));
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_INCLUDE_IGNORED;
cl_git_pass(
- git_status_foreach(_repository, cb_status__normal, &counts)
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)
);
- cl_assert(counts.entry_count == counts.expected_entry_count);
- cl_assert(counts.wrong_status_flags_count == 0);
- cl_assert(counts.wrong_sorted_path == 0);
+ 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__empty_repository(void)
+void test_status_worktree__swap_subdir_with_recurse_and_pathspec(void)
{
- int count = 0;
+ status_entry_counts counts;
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_status_options opts;
+
+ /* 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;
- git_status_foreach(_repository, cb_status__count, &count);
- cl_assert(count == 0);
+ memset(&opts, 0, sizeof(opts));
+ 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, _repository, entry_paths0[i])
+ 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(_repository, entry_paths0[i], &ignored));
+ 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(_repository, "nonexistent_file", &ignored));
+ cl_git_pass(
+ git_status_should_ignore(&ignored, repo, "nonexistent_file")
+ );
cl_assert(!ignored);
- cl_git_pass(git_status_should_ignore(_repository, "ignored_nonexistent_file", &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)
+{
+ GIT_UNUSED(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_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_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), GIT_DIRREMOVAL_FILES_AND_DIRS));
+
+ 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), GIT_DIRREMOVAL_FILES_AND_DIRS));
+ 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__cannot_retrieve_the_status_of_a_bare_repository(void)
+{
+ git_repository *repo;
+ int error;
+ unsigned int status = 0;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+ error = git_status_file(&status, repo, "dummy");
+
+ cl_git_fail(error);
+ cl_assert(error != GIT_ENOTFOUND);
+
+ git_repository_free(repo);
+}
+
+void test_status_worktree__first_commit_in_progress(void)
+{
+ git_repository *repo;
+ git_index *index;
+ status_entry_single result;
+
+ 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(index, "testfile.txt", 0));
+ 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__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"));
+
+ cl_git_pass(git_index_open(&index, "empty-index"));
+ cl_assert_equal_i(0, 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_oid(&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__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"));
+
+ 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"));
+}
diff --git a/tests-clar/threads/basic.c b/tests-clar/threads/basic.c
new file mode 100644
index 000000000..2b1c36808
--- /dev/null
+++ b/tests-clar/threads/basic.c
@@ -0,0 +1,20 @@
+#include "clar_libgit2.h"
+
+#include "cache.h"
+
+
+static git_repository *g_repo;
+
+void test_threads_basic__initialize(void) {
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_threads_basic__cleanup(void) {
+ cl_git_sandbox_cleanup();
+}
+
+
+void test_threads_basic__cache(void) {
+ // run several threads polling the cache at the same time
+ cl_assert(1 == 1);
+}
diff --git a/tests/.gitignore b/tests/.gitignore
deleted file mode 100644
index 690624bdf..000000000
--- a/tests/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-*.toc
diff --git a/tests/NAMING b/tests/NAMING
deleted file mode 100644
index c2da0163f..000000000
--- a/tests/NAMING
+++ /dev/null
@@ -1,52 +0,0 @@
-Test sources should be named:
-
- t????-function.c
-
-where ???? is a four digit code. The first two digits classify
-the test into a major category; the final two digits indicate the
-sequence of the test within that category. The function part of
-the test name should give a rough indication of what it does.
-
-Categories
-----------
-
-00__: Core library routines based only on the standard library,
- and that are essential for everything else to run. E.g.
- errno and malloc.
-
-01__: Basic hashing functions, needed to handle the content
- addressable store.
-
-02__: Basic object read access.
-
-03__: Basic object writing.
-
-04__: Parsing and loading commit data
-
-05__: Revision walking
-
-06__: Index reading, writing and searching
-
-07__: Tests for the internal hashtable code
-
-08__: Tag reading and writing
-
-09__: Reading tree objects
-
-10__: Symbolic, loose and packed references reading and writing.
-
-11__: SQLite backend
-
-12__: Repository init and opening
-
-13__: Threads, empty as of now
-
-14__: Redis backend
-
-15__: Configuration parsing
-
-16__: Remotes
-
-17__: Buffers
-
-18__: File Status
diff --git a/tests/resources/attr/.gitted/index b/tests/resources/attr/.gitted/index
deleted file mode 100644
index c52747e0b..000000000
--- a/tests/resources/attr/.gitted/index
+++ /dev/null
Binary files differ
diff --git a/tests/resources/attr/.gitted/logs/HEAD b/tests/resources/attr/.gitted/logs/HEAD
deleted file mode 100644
index f518a465a..000000000
--- a/tests/resources/attr/.gitted/logs/HEAD
+++ /dev/null
@@ -1,3 +0,0 @@
-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
diff --git a/tests/resources/attr/.gitted/logs/refs/heads/master b/tests/resources/attr/.gitted/logs/refs/heads/master
deleted file mode 100644
index f518a465a..000000000
--- a/tests/resources/attr/.gitted/logs/refs/heads/master
+++ /dev/null
@@ -1,3 +0,0 @@
-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
diff --git a/tests/resources/attr/.gitted/refs/heads/master b/tests/resources/attr/.gitted/refs/heads/master
deleted file mode 100644
index 0516af2d2..000000000
--- a/tests/resources/attr/.gitted/refs/heads/master
+++ /dev/null
@@ -1 +0,0 @@
-a5d76cad53f66f1312bd995909a5bab3c0820770
diff --git a/tests/resources/attr/root_test2 b/tests/resources/attr/root_test2
deleted file mode 100644
index 45141a79a..000000000
--- a/tests/resources/attr/root_test2
+++ /dev/null
@@ -1 +0,0 @@
-Hello from the root
diff --git a/tests/resources/attr/root_test3 b/tests/resources/attr/root_test3
deleted file mode 100644
index 45141a79a..000000000
--- a/tests/resources/attr/root_test3
+++ /dev/null
@@ -1 +0,0 @@
-Hello from the root
diff --git a/tests/resources/attr/root_test4.txt b/tests/resources/attr/root_test4.txt
deleted file mode 100644
index fb5067b1a..000000000
--- a/tests/resources/attr/root_test4.txt
+++ /dev/null
@@ -1 +0,0 @@
-Hello again
diff --git a/tests/t00-core.c b/tests/t00-core.c
deleted file mode 100644
index 58f048af6..000000000
--- a/tests/t00-core.c
+++ /dev/null
@@ -1,636 +0,0 @@
-/*
- * This file is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2,
- * as published by the Free Software Foundation.
- *
- * In addition to the permissions in the GNU General Public License,
- * the authors give you unlimited permission to link the compiled
- * version of this file into combinations with other programs,
- * and to distribute those combinations without any restriction
- * coming from the use of this file. (The General Public License
- * restrictions do apply in other respects; for example, they cover
- * modification of the file, and distribution when not linked into
- * a combined executable.)
- *
- * This file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-#include "test_lib.h"
-
-#include "vector.h"
-#include "fileops.h"
-#include "filebuf.h"
-
-BEGIN_TEST(string0, "compare prefixes")
- must_be_true(git__prefixcmp("", "") == 0);
- must_be_true(git__prefixcmp("a", "") == 0);
- must_be_true(git__prefixcmp("", "a") < 0);
- must_be_true(git__prefixcmp("a", "b") < 0);
- must_be_true(git__prefixcmp("b", "a") > 0);
- must_be_true(git__prefixcmp("ab", "a") == 0);
- must_be_true(git__prefixcmp("ab", "ac") < 0);
- must_be_true(git__prefixcmp("ab", "aa") > 0);
-END_TEST
-
-BEGIN_TEST(string1, "compare suffixes")
- must_be_true(git__suffixcmp("", "") == 0);
- must_be_true(git__suffixcmp("a", "") == 0);
- must_be_true(git__suffixcmp("", "a") < 0);
- must_be_true(git__suffixcmp("a", "b") < 0);
- must_be_true(git__suffixcmp("b", "a") > 0);
- must_be_true(git__suffixcmp("ba", "a") == 0);
- must_be_true(git__suffixcmp("zaa", "ac") < 0);
- must_be_true(git__suffixcmp("zaz", "ac") > 0);
-END_TEST
-
-
-BEGIN_TEST(vector0, "initial size of 1 would cause writing past array bounds")
- 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);
-END_TEST
-
-BEGIN_TEST(vector1, "don't read past array bounds on remove()")
- 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);
-END_TEST
-
-static int test_cmp(const void *a, const void *b)
-{
- return *(const int *)a - *(const int *)b;
-}
-
-BEGIN_TEST(vector2, "remove duplicates")
- git_vector x;
- int *ptrs[2];
-
- ptrs[0] = git__malloc(sizeof(int));
- ptrs[1] = git__malloc(sizeof(int));
-
- *ptrs[0] = 2;
- *ptrs[1] = 1;
-
- must_pass(git_vector_init(&x, 5, test_cmp));
- must_pass(git_vector_insert(&x, ptrs[0]));
- must_pass(git_vector_insert(&x, ptrs[1]));
- must_pass(git_vector_insert(&x, ptrs[1]));
- must_pass(git_vector_insert(&x, ptrs[0]));
- must_pass(git_vector_insert(&x, ptrs[1]));
- must_be_true(x.length == 5);
- git_vector_uniq(&x);
- must_be_true(x.length == 2);
- git_vector_free(&x);
-
- git__free(ptrs[0]);
- git__free(ptrs[1]);
-END_TEST
-
-
-BEGIN_TEST(path0, "get the dirname of a path")
- git_buf dir = GIT_BUF_INIT;
- char *dir2;
-
-#define DIRNAME_TEST(A, B) { \
- must_be_true(git_path_dirname_r(&dir, A) >= 0); \
- must_be_true(strcmp(B, dir.ptr) == 0); \
- must_be_true((dir2 = git_path_dirname(A)) != NULL); \
- must_be_true(strcmp(dir2, B) == 0); \
- git__free(dir2); \
-}
-
- DIRNAME_TEST(NULL, ".");
- DIRNAME_TEST("", ".");
- DIRNAME_TEST("a", ".");
- DIRNAME_TEST("/", "/");
- DIRNAME_TEST("/usr", "/");
- DIRNAME_TEST("/usr/", "/");
- DIRNAME_TEST("/usr/lib", "/usr");
- DIRNAME_TEST("/usr/lib/", "/usr");
- DIRNAME_TEST("/usr/lib//", "/usr");
- DIRNAME_TEST("usr/lib", "usr");
- DIRNAME_TEST("usr/lib/", "usr");
- DIRNAME_TEST("usr/lib//", "usr");
- DIRNAME_TEST(".git/", ".");
-
-#undef DIRNAME_TEST
-
- git_buf_free(&dir);
-END_TEST
-
-BEGIN_TEST(path1, "get the base name of a path")
- git_buf base = GIT_BUF_INIT;
- char *base2;
-
-#define BASENAME_TEST(A, B) { \
- must_be_true(git_path_basename_r(&base, A) >= 0); \
- must_be_true(strcmp(B, base.ptr) == 0); \
- must_be_true((base2 = git_path_basename(A)) != NULL); \
- must_be_true(strcmp(base2, B) == 0); \
- git__free(base2); \
-}
-
- BASENAME_TEST(NULL, ".");
- BASENAME_TEST("", ".");
- BASENAME_TEST("a", "a");
- BASENAME_TEST("/", "/");
- BASENAME_TEST("/usr", "usr");
- BASENAME_TEST("/usr/", "usr");
- BASENAME_TEST("/usr/lib", "lib");
- BASENAME_TEST("/usr/lib//", "lib");
- BASENAME_TEST("usr/lib", "lib");
-
-#undef BASENAME_TEST
-
- git_buf_free(&base);
-END_TEST
-
-BEGIN_TEST(path2, "get the latest component in a path")
- const char *dir;
-
-#define TOPDIR_TEST(A, B) { \
- must_be_true((dir = git_path_topdir(A)) != NULL); \
- must_be_true(strcmp(dir, B) == 0); \
-}
-
- TOPDIR_TEST(".git/", ".git/");
- TOPDIR_TEST("/.git/", ".git/");
- TOPDIR_TEST("usr/local/.git/", ".git/");
- TOPDIR_TEST("./.git/", ".git/");
- TOPDIR_TEST("/usr/.git/", ".git/");
- TOPDIR_TEST("/", "/");
- TOPDIR_TEST("a/", "a/");
-
- must_be_true(git_path_topdir("/usr/.git") == NULL);
- must_be_true(git_path_topdir(".") == NULL);
- must_be_true(git_path_topdir("") == NULL);
- must_be_true(git_path_topdir("a") == NULL);
-
-#undef TOPDIR_TEST
-END_TEST
-
-static int ensure_joinpath(const char *path_a, const char *path_b, const char *expected_path)
-{
- int error = GIT_SUCCESS;
- git_buf joined_path = GIT_BUF_INIT;
- if (!(error = git_buf_joinpath(&joined_path, path_a, path_b)))
- error = strcmp(joined_path.ptr, expected_path) == 0 ?
- GIT_SUCCESS : GIT_ERROR;
- git_buf_free(&joined_path);
- return error;
-}
-
-BEGIN_TEST(path5, "properly join path components")
- must_pass(ensure_joinpath("", "", ""));
- must_pass(ensure_joinpath("", "a", "a"));
- must_pass(ensure_joinpath("", "/a", "/a"));
- must_pass(ensure_joinpath("a", "", "a/"));
- must_pass(ensure_joinpath("a", "/", "a/"));
- must_pass(ensure_joinpath("a", "b", "a/b"));
- must_pass(ensure_joinpath("/", "a", "/a"));
- must_pass(ensure_joinpath("/", "", "/"));
- must_pass(ensure_joinpath("/a", "/b", "/a/b"));
- must_pass(ensure_joinpath("/a", "/b/", "/a/b/"));
- must_pass(ensure_joinpath("/a/", "b/", "/a/b/"));
- must_pass(ensure_joinpath("/a/", "/b/", "/a/b/"));
-END_TEST
-
-static int ensure_joinpath_n(const char *path_a, const char *path_b, const char *path_c, const char *path_d, const char *expected_path)
-{
- int error = GIT_SUCCESS;
- git_buf joined_path = GIT_BUF_INIT;
- if (!(error = git_buf_join_n(&joined_path, '/', 4,
- path_a, path_b, path_c, path_d)))
- error = strcmp(joined_path.ptr, expected_path) == 0 ?
- GIT_SUCCESS : GIT_ERROR;
- git_buf_free(&joined_path);
- return error;
-}
-
-BEGIN_TEST(path6, "properly join path components for more than one path")
- must_pass(ensure_joinpath_n("", "", "", "", ""));
- must_pass(ensure_joinpath_n("", "a", "", "", "a/"));
- must_pass(ensure_joinpath_n("a", "", "", "", "a/"));
- must_pass(ensure_joinpath_n("", "", "", "a", "a"));
- must_pass(ensure_joinpath_n("a", "b", "", "/c/d/", "a/b/c/d/"));
- must_pass(ensure_joinpath_n("a", "b", "", "/c/d", "a/b/c/d"));
-END_TEST
-
-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; /* buffer to store path */
-} walk_data;
-
-
-static char *top_dir = "dir-walk";
-static walk_data *state_loc;
-
-static int error(const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- fprintf(stderr, "\n");
- return -1;
-}
-
-static int setup(walk_data *d)
-{
- name_data *n;
-
- if (p_mkdir(top_dir, 0777) < 0)
- return error("can't mkdir(\"%s\")", top_dir);
-
- if (p_chdir(top_dir) < 0)
- return error("can't chdir(\"%s\")", top_dir);
-
- if (strcmp(d->sub, ".") != 0)
- if (p_mkdir(d->sub, 0777) < 0)
- return error("can't mkdir(\"%s\")", d->sub);
-
- if (git_buf_sets(&d->path, d->sub) < 0)
- return error("can't allocate space for \"%s\"", d->sub);
-
- state_loc = d;
-
- for (n = d->names; n->name; n++) {
- git_file fd = p_creat(n->name, 0666);
- if (fd < 0)
- return GIT_ERROR;
- p_close(fd);
- n->count = 0;
- }
-
- return 0;
-}
-
-static int knockdown(walk_data *d)
-{
- name_data *n;
-
- git_buf_free(&d->path);
-
- for (n = d->names; n->name; n++) {
- if (p_unlink(n->name) < 0)
- return error("can't unlink(\"%s\")", n->name);
- }
-
- if (strcmp(d->sub, ".") != 0)
- if (p_rmdir(d->sub) < 0)
- return error("can't rmdir(\"%s\")", d->sub);
-
- if (p_chdir("..") < 0)
- return error("can't chdir(\"..\")");
-
- if (p_rmdir(top_dir) < 0)
- return error("can't rmdir(\"%s\")", top_dir);
-
- return 0;
-}
-
-static int check_counts(walk_data *d)
-{
- int ret = 0;
- name_data *n;
-
- for (n = d->names; n->name; n++) {
- if (n->count != 1)
- ret = error("count (%d, %s)", n->count, n->name);
- }
- return ret;
-}
-
-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
-};
-
-BEGIN_TEST(dirent0, "make sure that the '.' folder is not traversed")
- must_pass(setup(&dot));
-
- must_pass(git_path_direach(&dot.path,
- one_entry,
- &dot));
-
- must_pass(check_counts(&dot));
-
- must_pass(knockdown(&dot));
-END_TEST
-
-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
-};
-
-BEGIN_TEST(dirent1, "traverse a subfolder")
-
- must_pass(setup(&sub));
-
- must_pass(git_path_direach(&sub.path,
- one_entry,
- &sub));
-
- must_pass(check_counts(&sub));
-
- must_pass(knockdown(&sub));
-END_TEST
-
-static walk_data sub_slash = {
- "sub/",
- sub_names,
- GIT_BUF_INIT
-};
-
-BEGIN_TEST(dirent2, "traverse a slash-terminated subfolder")
-
- must_pass(setup(&sub_slash));
-
- must_pass(git_path_direach(&sub_slash.path,
- one_entry,
- &sub_slash));
-
- must_pass(check_counts(&sub_slash));
-
- must_pass(knockdown(&sub_slash));
-END_TEST
-
-static name_data empty_names[] = {
- { 0, NULL }
-};
-static walk_data empty = {
- "empty",
- empty_names,
- GIT_BUF_INIT
-};
-
-static int dont_call_me(void *GIT_UNUSED(state), git_buf *GIT_UNUSED(path))
-{
- GIT_UNUSED_ARG(state)
- GIT_UNUSED_ARG(path)
- return GIT_ERROR;
-}
-
-BEGIN_TEST(dirent3, "make sure that empty folders are not traversed")
-
- must_pass(setup(&empty));
-
- must_pass(git_path_direach(&empty.path,
- one_entry,
- &empty));
-
- must_pass(check_counts(&empty));
-
- /* make sure callback not called */
- must_pass(git_path_direach(&empty.path,
- dont_call_me,
- &empty));
-
- must_pass(knockdown(&empty));
-END_TEST
-
-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
-};
-
-BEGIN_TEST(dirent4, "make sure that strange looking filenames ('..c') are traversed")
-
- must_pass(setup(&odd));
-
- must_pass(git_path_direach(&odd.path,
- one_entry,
- &odd));
-
- must_pass(check_counts(&odd));
-
- must_pass(knockdown(&odd));
-END_TEST
-
-BEGIN_TEST(filebuf0, "make sure git_filebuf_open doesn't delete an existing lock")
- git_filebuf file = GIT_FILEBUF_INIT;
- int fd;
- char test[] = "test", testlock[] = "test.lock";
-
- fd = p_creat(testlock, 0744);
- must_pass(fd);
- must_pass(p_close(fd));
- must_fail(git_filebuf_open(&file, test, 0));
- must_pass(git_path_exists(testlock));
- must_pass(p_unlink(testlock));
-END_TEST
-
-BEGIN_TEST(filebuf1, "make sure GIT_FILEBUF_APPEND works as expected")
- git_filebuf file = GIT_FILEBUF_INIT;
- int fd;
- char test[] = "test";
-
- fd = p_creat(test, 0666);
- must_pass(fd);
- must_pass(p_write(fd, "libgit2 rocks\n", 14));
- must_pass(p_close(fd));
-
- must_pass(git_filebuf_open(&file, test, GIT_FILEBUF_APPEND));
- must_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
- must_pass(git_filebuf_commit(&file, 0666));
-
- must_pass(p_unlink(test));
-END_TEST
-
-BEGIN_TEST(filebuf2, "make sure git_filebuf_write writes large buffer correctly")
- git_filebuf file = GIT_FILEBUF_INIT;
- char test[] = "test";
- unsigned char buf[4096 * 4]; /* 2 * WRITE_BUFFER_SIZE */
-
- memset(buf, 0xfe, sizeof(buf));
- must_pass(git_filebuf_open(&file, test, 0));
- must_pass(git_filebuf_write(&file, buf, sizeof(buf)));
- must_pass(git_filebuf_commit(&file, 0666));
-
- must_pass(p_unlink(test));
-END_TEST
-
-static char *empty_tmp_dir = "test_gitfo_rmdir_recurs_test";
-
-static int setup_empty_tmp_dir(void)
-{
- git_buf path = GIT_BUF_INIT;
-
- int error =
- p_mkdir(empty_tmp_dir, 0777) ||
- git_buf_joinpath(&path, empty_tmp_dir, "/one") ||
- p_mkdir(path.ptr, 0777) ||
- git_buf_joinpath(&path, empty_tmp_dir, "/one/two_one") ||
- p_mkdir(path.ptr, 0777) ||
- git_buf_joinpath(&path, empty_tmp_dir, "/one/two_two") ||
- p_mkdir(path.ptr, 0777) ||
- git_buf_joinpath(&path, empty_tmp_dir, "/one/two_two/three") ||
- p_mkdir(path.ptr, 0777) ||
- git_buf_joinpath(&path, empty_tmp_dir, "/two") ||
- p_mkdir(path.ptr, 0777);
-
- git_buf_free(&path);
-
- return error ? -1 : 0;
-}
-
-BEGIN_TEST(rmdir0, "make sure empty dir can be deleted recusively")
- must_pass(setup_empty_tmp_dir());
- must_pass(git_futils_rmdir_r(empty_tmp_dir, 0));
-END_TEST
-
-BEGIN_TEST(rmdir1, "make sure non-empty dir cannot be deleted recusively")
- git_buf file = GIT_BUF_INIT;
- int fd;
-
- must_pass(setup_empty_tmp_dir());
- must_pass(git_buf_joinpath(&file, empty_tmp_dir, "/two/file.txt"));
- fd = p_creat(file.ptr, 0777);
- must_pass(fd);
- must_pass(p_close(fd));
- must_fail(git_futils_rmdir_r(empty_tmp_dir, 0));
- must_pass(p_unlink(file.ptr));
- must_pass(git_futils_rmdir_r(empty_tmp_dir, 0));
- git_buf_free(&file);
-END_TEST
-
-BEGIN_TEST(strtol0, "parsing out 32 integers from a string")
- int32_t i;
-
- must_pass(git__strtol32(&i, "123", NULL, 10));
- must_be_true(i == 123);
-
- must_pass(git__strtol32(&i, " +123 ", NULL, 10));
- must_be_true(i == 123);
-
- must_pass(git__strtol32(&i, " +2147483647 ", NULL, 10));
- must_be_true(i == 2147483647);
-
- must_pass(git__strtol32(&i, " -2147483648 ", NULL, 10));
- must_be_true(i == -2147483648LL);
-
- must_fail(git__strtol32(&i, " 2147483657 ", NULL, 10));
- must_fail(git__strtol32(&i, " -2147483657 ", NULL, 10));
-END_TEST
-
-BEGIN_TEST(strtol1, "parsing out 64 integers from a string")
- int64_t i;
-
- must_pass(git__strtol64(&i, "123", NULL, 10));
- must_be_true(i == 123);
-
- must_pass(git__strtol64(&i, " +123 ", NULL, 10));
- must_be_true(i == 123);
-
- must_pass(git__strtol64(&i, " +2147483647 ", NULL, 10));
- must_be_true(i == 2147483647);
-
- must_pass(git__strtol64(&i, " -2147483648 ", NULL, 10));
- must_be_true(i == -2147483648LL);
-
- must_pass(git__strtol64(&i, " 2147483657 ", NULL, 10));
- must_be_true(i == 2147483657LL);
-
- must_pass(git__strtol64(&i, " -2147483657 ", NULL, 10));
- must_be_true(i == -2147483657LL);
-END_TEST
-
-BEGIN_SUITE(core)
- ADD_TEST(string0);
- ADD_TEST(string1);
-
- ADD_TEST(vector0);
- ADD_TEST(vector1);
- ADD_TEST(vector2);
-
- ADD_TEST(path0);
- ADD_TEST(path1);
- ADD_TEST(path2);
- ADD_TEST(path5);
- ADD_TEST(path6);
-
- ADD_TEST(dirent0);
- ADD_TEST(dirent1);
- ADD_TEST(dirent2);
- ADD_TEST(dirent3);
- ADD_TEST(dirent4);
-
- ADD_TEST(filebuf0);
- ADD_TEST(filebuf1);
- ADD_TEST(filebuf2);
-
- ADD_TEST(rmdir0);
- ADD_TEST(rmdir1);
-
- ADD_TEST(strtol0);
- ADD_TEST(strtol1);
-END_SUITE
diff --git a/tests/t01-data.h b/tests/t01-data.h
deleted file mode 100644
index 268269d69..000000000
--- a/tests/t01-data.h
+++ /dev/null
@@ -1,322 +0,0 @@
-
-/*
- * 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,
-};
-
-static unsigned char zero_data[] = {
- 0x00 /* dummy data */
-};
-
-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 IDS
- */
-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/t01-rawobj.c b/tests/t01-rawobj.c
deleted file mode 100644
index 7b9ca1ee1..000000000
--- a/tests/t01-rawobj.c
+++ /dev/null
@@ -1,627 +0,0 @@
-/*
- * This file is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2,
- * as published by the Free Software Foundation.
- *
- * In addition to the permissions in the GNU General Public License,
- * the authors give you unlimited permission to link the compiled
- * version of this file into combinations with other programs,
- * and to distribute those combinations without any restriction
- * coming from the use of this file. (The General Public License
- * restrictions do apply in other respects; for example, they cover
- * modification of the file, and distribution when not linked into
- * a combined executable.)
- *
- * This file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-#include "test_lib.h"
-
-#include "odb.h"
-#include "hash.h"
-
-#include "t01-data.h"
-
-static int hash_object(git_oid *oid, git_rawobj *obj)
-{
- return git_odb_hash(oid, obj->data, obj->len, obj->type);
-}
-
-BEGIN_TEST(oid0, "validate size of oid objects")
- git_oid out;
- must_be_true(20 == GIT_OID_RAWSZ);
- must_be_true(40 == GIT_OID_HEXSZ);
- must_be_true(sizeof(out) == GIT_OID_RAWSZ);
- must_be_true(sizeof(out.id) == GIT_OID_RAWSZ);
-END_TEST
-
-BEGIN_TEST(oid1, "fail when parsing an empty string as oid")
- git_oid out;
- must_fail(git_oid_fromstr(&out, ""));
-END_TEST
-
-BEGIN_TEST(oid2, "fail when parsing an invalid string as oid")
- git_oid out;
- must_fail(git_oid_fromstr(&out, "moo"));
-END_TEST
-
-BEGIN_TEST(oid3, "find all invalid characters when parsing an oid")
- 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);
- must_pass(git_oid_fromstr(&out, in));
- must_be_true(memcmp(out.id, exp, sizeof(out.id)) == 0);
- } else {
- must_fail(git_oid_fromstr(&out, in));
- }
- }
-END_TEST
-
-BEGIN_TEST(oid4, "fail when parsing an invalid oid string")
- git_oid out;
- must_fail(git_oid_fromstr(&out, "16a67770b7d8d72317c4b775213c23a8bd74f5ez"));
-END_TEST
-
-BEGIN_TEST(oid5, "succeed when parsing a valid oid string")
- 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,
- };
-
- must_pass(git_oid_fromstr(&out, "16a67770b7d8d72317c4b775213c23a8bd74f5e0"));
- must_pass(memcmp(out.id, exp, sizeof(out.id)));
-
- must_pass(git_oid_fromstr(&out, "16A67770B7D8D72317C4b775213C23A8BD74F5E0"));
- must_pass(memcmp(out.id, exp, sizeof(out.id)));
-END_TEST
-
-BEGIN_TEST(oid6, "build a valid oid from raw bytes")
- 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);
- must_pass(memcmp(out.id, exp, sizeof(out.id)));
-END_TEST
-
-BEGIN_TEST(oid7, "properly copy an oid to another")
- 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);
- must_pass(memcmp(a.id, exp, sizeof(a.id)));
-END_TEST
-
-BEGIN_TEST(oid8, "compare two oids (lesser than)")
- 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);
- must_be_true(git_oid_cmp(&a, &b) < 0);
-END_TEST
-
-BEGIN_TEST(oid9, "compare two oids (equal)")
- 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);
- must_be_true(git_oid_cmp(&a, &b) == 0);
-END_TEST
-
-BEGIN_TEST(oid10, "compare two oids (greater than)")
- 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);
- must_be_true(git_oid_cmp(&a, &b) > 0);
-END_TEST
-
-BEGIN_TEST(oid11, "compare formated oids")
- const char *exp = "16a0123456789abcdef4b775213c23a8bd74f5e0";
- git_oid in;
- char out[GIT_OID_HEXSZ + 1];
-
- must_pass(git_oid_fromstr(&in, exp));
-
- /* Format doesn't touch the last byte */
- out[GIT_OID_HEXSZ] = 'Z';
- git_oid_fmt(out, &in);
- must_be_true(out[GIT_OID_HEXSZ] == 'Z');
-
- /* Format produced the right result */
- out[GIT_OID_HEXSZ] = '\0';
- must_be_true(strcmp(exp, out) == 0);
-END_TEST
-
-BEGIN_TEST(oid12, "compare oids (allocate + format)")
- const char *exp = "16a0123456789abcdef4b775213c23a8bd74f5e0";
- git_oid in;
- char *out;
-
- must_pass(git_oid_fromstr(&in, exp));
-
- out = git_oid_allocfmt(&in);
- must_be_true(out);
- must_be_true(strcmp(exp, out) == 0);
- git__free(out);
-END_TEST
-
-BEGIN_TEST(oid13, "compare oids (path format)")
- const char *exp1 = "16a0123456789abcdef4b775213c23a8bd74f5e0";
- const char *exp2 = "16/a0123456789abcdef4b775213c23a8bd74f5e0";
- git_oid in;
- char out[GIT_OID_HEXSZ + 2];
-
- must_pass(git_oid_fromstr(&in, exp1));
-
- /* Format doesn't touch the last byte */
- out[GIT_OID_HEXSZ + 1] = 'Z';
- git_oid_pathfmt(out, &in);
- must_be_true(out[GIT_OID_HEXSZ + 1] == 'Z');
-
- /* Format produced the right result */
- out[GIT_OID_HEXSZ + 1] = '\0';
- must_be_true(strcmp(exp2, out) == 0);
-END_TEST
-
-BEGIN_TEST(oid14, "convert raw oid to string")
- const char *exp = "16a0123456789abcdef4b775213c23a8bd74f5e0";
- git_oid in;
- char out[GIT_OID_HEXSZ + 1];
- char *str;
- int i;
-
- must_pass(git_oid_fromstr(&in, exp));
-
- /* NULL buffer pointer, returns static empty string */
- str = git_oid_to_string(NULL, sizeof(out), &in);
- must_be_true(str && *str == '\0' && str != out);
-
- /* zero buffer size, returns static empty string */
- str = git_oid_to_string(out, 0, &in);
- must_be_true(str && *str == '\0' && str != out);
-
- /* NULL oid pointer, returns static empty string */
- str = git_oid_to_string(out, sizeof(out), NULL);
- must_be_true(str && *str == '\0' && str != out);
-
- /* n == 1, returns out as an empty string */
- str = git_oid_to_string(out, 1, &in);
- must_be_true(str && *str == '\0' && str == out);
-
- for (i = 1; i < GIT_OID_HEXSZ; i++) {
- out[i+1] = 'Z';
- str = git_oid_to_string(out, i+1, &in);
- /* returns out containing c-string */
- must_be_true(str && str == out);
- /* must be '\0' terminated */
- must_be_true(*(str+i) == '\0');
- /* must not touch bytes past end of string */
- must_be_true(*(str+(i+1)) == 'Z');
- /* i == n-1 charaters of string */
- must_pass(strncmp(exp, out, i));
- }
-
- /* returns out as hex formatted c-string */
- str = git_oid_to_string(out, sizeof(out), &in);
- must_be_true(str && str == out && *(str+GIT_OID_HEXSZ) == '\0');
- must_be_true(strcmp(exp, out) == 0);
-END_TEST
-
-BEGIN_TEST(oid15, "convert raw oid to string (big)")
- const char *exp = "16a0123456789abcdef4b775213c23a8bd74f5e0";
- git_oid in;
- char big[GIT_OID_HEXSZ + 1 + 3]; /* note + 4 => big buffer */
- char *str;
-
- must_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_to_string(big, sizeof(big), &in);
- must_be_true(str && str == big && *(str+GIT_OID_HEXSZ) == '\0');
- must_be_true(strcmp(exp, big) == 0);
-
- /* check tail material is untouched */
- must_be_true(str && str == big && *(str+GIT_OID_HEXSZ+1) == 'X');
- must_be_true(str && str == big && *(str+GIT_OID_HEXSZ+2) == 'Y');
- must_be_true(str && str == big && *(str+GIT_OID_HEXSZ+3) == 'Z');
-END_TEST
-
-
-BEGIN_TEST(oid16, "make sure the OID shortener doesn't choke on duplicate sha1s")
-
- git_oid_shorten *os;
- int min_len;
-
- os = git_oid_shorten_new(0);
- must_be_true(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");
-
- must_be_true(min_len == GIT_OID_HEXSZ + 1);
-
- git_oid_shorten_free(os);
-END_TEST
-
-BEGIN_TEST(oid17, "stress test for the git_oid_shorten object")
-
-#define MAX_OIDS 1000
-
- git_oid_shorten *os;
- char *oids[MAX_OIDS];
- char number_buffer[16];
- git_oid oid;
- size_t i, j;
-
- int min_len = 0, found_collision;
-
- os = git_oid_shorten_new(0);
- must_be_true(os != NULL);
-
- /*
- * Insert in the shortener 1000 unique SHA1 ids
- */
- for (i = 0; i < MAX_OIDS; ++i) {
- char *oid_text;
-
- sprintf(number_buffer, "%u", (unsigned int)i);
- git_hash_buf(&oid, number_buffer, strlen(number_buffer));
-
- oid_text = git__malloc(GIT_OID_HEXSZ + 1);
- git_oid_fmt(oid_text, &oid);
- oid_text[GIT_OID_HEXSZ] = 0;
-
- min_len = git_oid_shorten_add(os, oid_text);
- must_be_true(min_len >= 0);
-
- oids[i] = oid_text;
- }
-
- /*
- * 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 = 0; j < MAX_OIDS; ++j) {
- if (i != j && memcmp(oids[i], oids[j], min_len - 1) == 0)
- found_collision = 1;
- }
- }
- must_be_true(found_collision == 1);
-
- /*
- * 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 = 0; j < MAX_OIDS; ++j) {
- if (i != j && memcmp(oids[i], oids[j], min_len) == 0)
- found_collision = 1;
- }
- }
- must_be_true(found_collision == 0);
-
- /* cleanup */
- for (i = 0; i < MAX_OIDS; ++i)
- git__free(oids[i]);
-
- git_oid_shorten_free(os);
-
-#undef MAX_OIDS
-END_TEST
-
-static char *hello_id = "22596363b3de40b06f981fb85d82312e8c0ed511";
-static char *hello_text = "hello world\n";
-
-static char *bye_id = "ce08fe4884650f067bd5703b6a59a8b3b3c99a09";
-static char *bye_text = "bye world\n";
-
-BEGIN_TEST(hash0, "normal hash by blocks")
- git_hash_ctx *ctx;
- git_oid id1, id2;
-
- must_be_true((ctx = git_hash_new_ctx()) != NULL);
-
- /* should already be init'd */
- git_hash_update(ctx, hello_text, strlen(hello_text));
- git_hash_final(&id2, ctx);
- must_pass(git_oid_fromstr(&id1, hello_id));
- must_be_true(git_oid_cmp(&id1, &id2) == 0);
-
- /* reinit should permit reuse */
- git_hash_init(ctx);
- git_hash_update(ctx, bye_text, strlen(bye_text));
- git_hash_final(&id2, ctx);
- must_pass(git_oid_fromstr(&id1, bye_id));
- must_be_true(git_oid_cmp(&id1, &id2) == 0);
-
- git_hash_free_ctx(ctx);
-END_TEST
-
-BEGIN_TEST(hash1, "hash whole buffer in a single call")
- git_oid id1, id2;
-
- must_pass(git_oid_fromstr(&id1, hello_id));
-
- git_hash_buf(&id2, hello_text, strlen(hello_text));
-
- must_be_true(git_oid_cmp(&id1, &id2) == 0);
-END_TEST
-
-BEGIN_TEST(hash2, "hash a vector")
- git_oid id1, id2;
- git_buf_vec vec[2];
-
- must_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);
-
- must_be_true(git_oid_cmp(&id1, &id2) == 0);
-END_TEST
-
-BEGIN_TEST(objtype0, "convert type to string")
- must_be_true(!strcmp(git_object_type2string(GIT_OBJ_BAD), ""));
- must_be_true(!strcmp(git_object_type2string(GIT_OBJ__EXT1), ""));
- must_be_true(!strcmp(git_object_type2string(GIT_OBJ_COMMIT), "commit"));
- must_be_true(!strcmp(git_object_type2string(GIT_OBJ_TREE), "tree"));
- must_be_true(!strcmp(git_object_type2string(GIT_OBJ_BLOB), "blob"));
- must_be_true(!strcmp(git_object_type2string(GIT_OBJ_TAG), "tag"));
- must_be_true(!strcmp(git_object_type2string(GIT_OBJ__EXT2), ""));
- must_be_true(!strcmp(git_object_type2string(GIT_OBJ_OFS_DELTA), "OFS_DELTA"));
- must_be_true(!strcmp(git_object_type2string(GIT_OBJ_REF_DELTA), "REF_DELTA"));
-
- must_be_true(!strcmp(git_object_type2string(-2), ""));
- must_be_true(!strcmp(git_object_type2string(8), ""));
- must_be_true(!strcmp(git_object_type2string(1234), ""));
-END_TEST
-
-BEGIN_TEST(objtype1, "convert string to type")
- must_be_true(git_object_string2type(NULL) == GIT_OBJ_BAD);
- must_be_true(git_object_string2type("") == GIT_OBJ_BAD);
- must_be_true(git_object_string2type("commit") == GIT_OBJ_COMMIT);
- must_be_true(git_object_string2type("tree") == GIT_OBJ_TREE);
- must_be_true(git_object_string2type("blob") == GIT_OBJ_BLOB);
- must_be_true(git_object_string2type("tag") == GIT_OBJ_TAG);
- must_be_true(git_object_string2type("OFS_DELTA") == GIT_OBJ_OFS_DELTA);
- must_be_true(git_object_string2type("REF_DELTA") == GIT_OBJ_REF_DELTA);
-
- must_be_true(git_object_string2type("CoMmIt") == GIT_OBJ_BAD);
- must_be_true(git_object_string2type("hohoho") == GIT_OBJ_BAD);
-END_TEST
-
-BEGIN_TEST(objtype2, "check if an object type is loose")
- must_be_true(git_object_typeisloose(GIT_OBJ_BAD) == 0);
- must_be_true(git_object_typeisloose(GIT_OBJ__EXT1) == 0);
- must_be_true(git_object_typeisloose(GIT_OBJ_COMMIT) == 1);
- must_be_true(git_object_typeisloose(GIT_OBJ_TREE) == 1);
- must_be_true(git_object_typeisloose(GIT_OBJ_BLOB) == 1);
- must_be_true(git_object_typeisloose(GIT_OBJ_TAG) == 1);
- must_be_true(git_object_typeisloose(GIT_OBJ__EXT2) == 0);
- must_be_true(git_object_typeisloose(GIT_OBJ_OFS_DELTA) == 0);
- must_be_true(git_object_typeisloose(GIT_OBJ_REF_DELTA) == 0);
-
- must_be_true(git_object_typeisloose(-2) == 0);
- must_be_true(git_object_typeisloose(8) == 0);
- must_be_true(git_object_typeisloose(1234) == 0);
-END_TEST
-
-BEGIN_TEST(objhash0, "hash junk data")
- git_oid id, id_zero;
-
- must_pass(git_oid_fromstr(&id_zero, zero_id));
-
- /* invalid types: */
- junk_obj.data = some_data;
- must_fail(hash_object(&id, &junk_obj));
-
- junk_obj.type = GIT_OBJ__EXT1;
- must_fail(hash_object(&id, &junk_obj));
-
- junk_obj.type = GIT_OBJ__EXT2;
- must_fail(hash_object(&id, &junk_obj));
-
- junk_obj.type = GIT_OBJ_OFS_DELTA;
- must_fail(hash_object(&id, &junk_obj));
-
- junk_obj.type = GIT_OBJ_REF_DELTA;
- must_fail(hash_object(&id, &junk_obj));
-
- /* data can be NULL only if len is zero: */
- junk_obj.type = GIT_OBJ_BLOB;
- junk_obj.data = NULL;
- must_pass(hash_object(&id, &junk_obj));
- must_be_true(git_oid_cmp(&id, &id_zero) == 0);
-
- junk_obj.len = 1;
- must_fail(hash_object(&id, &junk_obj));
-END_TEST
-
-BEGIN_TEST(objhash1, "hash a commit object")
- git_oid id1, id2;
-
- must_pass(git_oid_fromstr(&id1, commit_id));
-
- must_pass(hash_object(&id2, &commit_obj));
-
- must_be_true(git_oid_cmp(&id1, &id2) == 0);
-END_TEST
-
-BEGIN_TEST(objhash2, "hash a tree object")
- git_oid id1, id2;
-
- must_pass(git_oid_fromstr(&id1, tree_id));
-
- must_pass(hash_object(&id2, &tree_obj));
-
- must_be_true(git_oid_cmp(&id1, &id2) == 0);
-END_TEST
-
-BEGIN_TEST(objhash3, "hash a tag object")
- git_oid id1, id2;
-
- must_pass(git_oid_fromstr(&id1, tag_id));
-
- must_pass(hash_object(&id2, &tag_obj));
-
- must_be_true(git_oid_cmp(&id1, &id2) == 0);
-END_TEST
-
-BEGIN_TEST(objhash4, "hash a zero-length object")
- git_oid id1, id2;
-
- must_pass(git_oid_fromstr(&id1, zero_id));
-
- must_pass(hash_object(&id2, &zero_obj));
-
- must_be_true(git_oid_cmp(&id1, &id2) == 0);
-END_TEST
-
-BEGIN_TEST(objhash5, "hash an one-byte long object")
- git_oid id1, id2;
-
- must_pass(git_oid_fromstr(&id1, one_id));
-
- must_pass(hash_object(&id2, &one_obj));
-
- must_be_true(git_oid_cmp(&id1, &id2) == 0);
-END_TEST
-
-BEGIN_TEST(objhash6, "hash a two-byte long object")
- git_oid id1, id2;
-
- must_pass(git_oid_fromstr(&id1, two_id));
-
- must_pass(hash_object(&id2, &two_obj));
-
- must_be_true(git_oid_cmp(&id1, &id2) == 0);
-END_TEST
-
-BEGIN_TEST(objhash7, "hash an object several bytes long")
- git_oid id1, id2;
-
- must_pass(git_oid_fromstr(&id1, some_id));
-
- must_pass(hash_object(&id2, &some_obj));
-
- must_be_true(git_oid_cmp(&id1, &id2) == 0);
-END_TEST
-
-BEGIN_SUITE(rawobjects)
- ADD_TEST(oid0);
- ADD_TEST(oid1);
- ADD_TEST(oid2);
- ADD_TEST(oid3);
- ADD_TEST(oid4);
- ADD_TEST(oid5);
- ADD_TEST(oid6);
- ADD_TEST(oid7);
- ADD_TEST(oid8);
- ADD_TEST(oid9);
- ADD_TEST(oid10);
- ADD_TEST(oid11);
- ADD_TEST(oid12);
- ADD_TEST(oid13);
- ADD_TEST(oid14);
- ADD_TEST(oid15);
- ADD_TEST(oid16);
- ADD_TEST(oid17);
-
- ADD_TEST(hash0);
- ADD_TEST(hash1);
- ADD_TEST(hash2);
-
- ADD_TEST(objtype0);
- ADD_TEST(objtype1);
- ADD_TEST(objtype2);
-
- ADD_TEST(objhash0);
- ADD_TEST(objhash1);
- ADD_TEST(objhash2);
- ADD_TEST(objhash3);
- ADD_TEST(objhash4);
- ADD_TEST(objhash5);
- ADD_TEST(objhash6);
- ADD_TEST(objhash7);
-END_SUITE
-
diff --git a/tests/t03-data.h b/tests/t03-data.h
deleted file mode 100644
index a4b73fec3..000000000
--- a/tests/t03-data.h
+++ /dev/null
@@ -1,344 +0,0 @@
-
-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 object_data commit = {
- "3d7f8a6af076c8c3f20071a8935cdbe8228594d1",
- "test-objects/3d",
- "test-objects/3d/7f8a6af076c8c3f20071a8935cdbe8228594d1",
-};
-
-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 git_rawobj commit_obj = {
- commit_data,
- sizeof(commit_data),
- GIT_OBJ_COMMIT
-};
-
-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
-};
-
-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
-};
-
-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
-};
-
-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
-};
-
-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
-};
-
-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
-};
diff --git a/tests/t03-objwrite.c b/tests/t03-objwrite.c
deleted file mode 100644
index 1650b8060..000000000
--- a/tests/t03-objwrite.c
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
- * This file is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2,
- * as published by the Free Software Foundation.
- *
- * In addition to the permissions in the GNU General Public License,
- * the authors give you unlimited permission to link the compiled
- * version of this file into combinations with other programs,
- * and to distribute those combinations without any restriction
- * coming from the use of this file. (The General Public License
- * restrictions do apply in other respects; for example, they cover
- * modification of the file, and distribution when not linked into
- * a combined executable.)
- *
- * This file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-#include "test_lib.h"
-#include "fileops.h"
-#include "odb.h"
-
-static char *odb_dir = "test-objects";
-#include "t03-data.h"
-
-static int make_odb_dir(void)
-{
- if (p_mkdir(odb_dir, GIT_OBJECT_DIR_MODE) < 0) {
- int err = errno;
- fprintf(stderr, "can't make directory \"%s\"", odb_dir);
- if (err == EEXIST)
- fprintf(stderr, " (already exists)");
- fprintf(stderr, "\n");
- return -1;
- }
- return 0;
-}
-
-static int check_object_files(object_data *d)
-{
- if (git_path_exists(d->dir) < 0)
- return -1;
- if (git_path_exists(d->file) < 0)
- return -1;
- return 0;
-}
-
-static int cmp_objects(git_rawobj *o1, git_rawobj *o2)
-{
- if (o1->type != o2->type)
- return -1;
- if (o1->len != o2->len)
- return -1;
- if ((o1->len > 0) && (memcmp(o1->data, o2->data, o1->len) != 0))
- return -1;
- return 0;
-}
-
-static int remove_object_files(object_data *d)
-{
- if (p_unlink(d->file) < 0) {
- fprintf(stderr, "can't delete object file \"%s\"\n", d->file);
- return -1;
- }
- if ((p_rmdir(d->dir) < 0) && (errno != ENOTEMPTY)) {
- fprintf(stderr, "can't remove directory \"%s\"\n", d->dir);
- return -1;
- }
-
- if (p_rmdir(odb_dir) < 0) {
- fprintf(stderr, "can't remove directory \"%s\"\n", odb_dir);
- return -1;
- }
-
- return 0;
-}
-
-static int streaming_write(git_oid *oid, git_odb *odb, git_rawobj *raw)
-{
- git_odb_stream *stream;
- int error;
-
- if ((error = git_odb_open_wstream(&stream, odb, raw->len, raw->type)) < GIT_SUCCESS)
- return error;
-
- stream->write(stream, raw->data, raw->len);
-
- error = stream->finalize_write(oid, stream);
- stream->free(stream);
-
- return error;
-}
-
-BEGIN_TEST(write0, "write loose commit object")
- git_odb *db;
- git_oid id1, id2;
- git_odb_object *obj;
-
- must_pass(make_odb_dir());
- must_pass(git_odb_open(&db, odb_dir));
- must_pass(git_oid_fromstr(&id1, commit.id));
-
- must_pass(streaming_write(&id2, db, &commit_obj));
- must_be_true(git_oid_cmp(&id1, &id2) == 0);
- must_pass(check_object_files(&commit));
-
- must_pass(git_odb_read(&obj, db, &id1));
- must_pass(cmp_objects(&obj->raw, &commit_obj));
-
- git_odb_object_free(obj);
- git_odb_free(db);
- must_pass(remove_object_files(&commit));
-END_TEST
-
-BEGIN_TEST(write1, "write loose tree object")
- git_odb *db;
- git_oid id1, id2;
- git_odb_object *obj;
-
- must_pass(make_odb_dir());
- must_pass(git_odb_open(&db, odb_dir));
- must_pass(git_oid_fromstr(&id1, tree.id));
-
- must_pass(streaming_write(&id2, db, &tree_obj));
- must_be_true(git_oid_cmp(&id1, &id2) == 0);
- must_pass(check_object_files(&tree));
-
- must_pass(git_odb_read(&obj, db, &id1));
- must_pass(cmp_objects(&obj->raw, &tree_obj));
-
- git_odb_object_free(obj);
- git_odb_free(db);
- must_pass(remove_object_files(&tree));
-END_TEST
-
-BEGIN_TEST(write2, "write loose tag object")
- git_odb *db;
- git_oid id1, id2;
- git_odb_object *obj;
-
- must_pass(make_odb_dir());
- must_pass(git_odb_open(&db, odb_dir));
- must_pass(git_oid_fromstr(&id1, tag.id));
-
- must_pass(streaming_write(&id2, db, &tag_obj));
- must_be_true(git_oid_cmp(&id1, &id2) == 0);
- must_pass(check_object_files(&tag));
-
- must_pass(git_odb_read(&obj, db, &id1));
- must_pass(cmp_objects(&obj->raw, &tag_obj));
-
- git_odb_object_free(obj);
- git_odb_free(db);
- must_pass(remove_object_files(&tag));
-END_TEST
-
-BEGIN_TEST(write3, "write zero-length object")
- git_odb *db;
- git_oid id1, id2;
- git_odb_object *obj;
-
- must_pass(make_odb_dir());
- must_pass(git_odb_open(&db, odb_dir));
- must_pass(git_oid_fromstr(&id1, zero.id));
-
- must_pass(streaming_write(&id2, db, &zero_obj));
- must_be_true(git_oid_cmp(&id1, &id2) == 0);
- must_pass(check_object_files(&zero));
-
- must_pass(git_odb_read(&obj, db, &id1));
- must_pass(cmp_objects(&obj->raw, &zero_obj));
-
- git_odb_object_free(obj);
- git_odb_free(db);
- must_pass(remove_object_files(&zero));
-END_TEST
-
-BEGIN_TEST(write4, "write one-byte long object")
- git_odb *db;
- git_oid id1, id2;
- git_odb_object *obj;
-
- must_pass(make_odb_dir());
- must_pass(git_odb_open(&db, odb_dir));
- must_pass(git_oid_fromstr(&id1, one.id));
-
- must_pass(streaming_write(&id2, db, &one_obj));
- must_be_true(git_oid_cmp(&id1, &id2) == 0);
- must_pass(check_object_files(&one));
-
- must_pass(git_odb_read(&obj, db, &id1));
- must_pass(cmp_objects(&obj->raw, &one_obj));
-
- git_odb_object_free(obj);
- git_odb_free(db);
- must_pass(remove_object_files(&one));
-END_TEST
-
-BEGIN_TEST(write5, "write two-byte long object")
- git_odb *db;
- git_oid id1, id2;
- git_odb_object *obj;
-
- must_pass(make_odb_dir());
- must_pass(git_odb_open(&db, odb_dir));
- must_pass(git_oid_fromstr(&id1, two.id));
-
- must_pass(streaming_write(&id2, db, &two_obj));
- must_be_true(git_oid_cmp(&id1, &id2) == 0);
- must_pass(check_object_files(&two));
-
- must_pass(git_odb_read(&obj, db, &id1));
- must_pass(cmp_objects(&obj->raw, &two_obj));
-
- git_odb_object_free(obj);
- git_odb_free(db);
- must_pass(remove_object_files(&two));
-END_TEST
-
-BEGIN_TEST(write6, "write an object which is several bytes long")
- git_odb *db;
- git_oid id1, id2;
- git_odb_object *obj;
-
- must_pass(make_odb_dir());
- must_pass(git_odb_open(&db, odb_dir));
- must_pass(git_oid_fromstr(&id1, some.id));
-
- must_pass(streaming_write(&id2, db, &some_obj));
- must_be_true(git_oid_cmp(&id1, &id2) == 0);
- must_pass(check_object_files(&some));
-
- must_pass(git_odb_read(&obj, db, &id1));
- must_pass(cmp_objects(&obj->raw, &some_obj));
-
- git_odb_object_free(obj);
- git_odb_free(db);
- must_pass(remove_object_files(&some));
-END_TEST
-
-BEGIN_SUITE(objwrite)
- ADD_TEST(write0);
- ADD_TEST(write1);
- ADD_TEST(write2);
- ADD_TEST(write3);
- ADD_TEST(write4);
- ADD_TEST(write5);
- ADD_TEST(write6);
-END_SUITE
diff --git a/tests/t04-commit.c b/tests/t04-commit.c
deleted file mode 100644
index 82eb983ed..000000000
--- a/tests/t04-commit.c
+++ /dev/null
@@ -1,789 +0,0 @@
-/*
- * This file is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2,
- * as published by the Free Software Foundation.
- *
- * In addition to the permissions in the GNU General Public License,
- * the authors give you unlimited permission to link the compiled
- * version of this file into combinations with other programs,
- * and to distribute those combinations without any restriction
- * coming from the use of this file. (The General Public License
- * restrictions do apply in other respects; for example, they cover
- * modification of the file, and distribution when not linked into
- * a combined executable.)
- *
- * This file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-#include "test_lib.h"
-#include "test_helpers.h"
-
-#include "commit.h"
-#include "signature.h"
-
-static char *test_commits_broken[] = {
-
-/* 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 *test_commits_working[] = {
-/* 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",
-};
-
-BEGIN_TEST(parse0, "parse the OID line in a commit")
-
- git_oid oid;
-
-#define TEST_OID_PASS(string, header) { \
- const char *ptr = string;\
- const char *ptr_original = ptr;\
- size_t len = strlen(ptr);\
- must_pass(git_oid__parse(&oid, &ptr, ptr + len, header));\
- must_be_true(ptr == ptr_original + len);\
-}
-
-#define TEST_OID_FAIL(string, header) { \
- const char *ptr = string;\
- size_t len = strlen(ptr);\
- must_fail(git_oid__parse(&oid, &ptr, ptr + len, header));\
-}
-
- TEST_OID_PASS("parent 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "parent ");
- TEST_OID_PASS("tree 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree ");
- TEST_OID_PASS("random_heading 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "random_heading ");
- TEST_OID_PASS("stuck_heading05452d6349abcd67aa396dfb28660d765d8b2a36\n", "stuck_heading");
- TEST_OID_PASS("tree 5F4BEFFC0759261D015AA63A3A85613FF2F235DE\n", "tree ");
- TEST_OID_PASS("tree 1A669B8AB81B5EB7D9DB69562D34952A38A9B504\n", "tree ");
- TEST_OID_PASS("tree 5B20DCC6110FCC75D31C6CEDEBD7F43ECA65B503\n", "tree ");
- TEST_OID_PASS("tree 173E7BF00EA5C33447E99E6C1255954A13026BE4\n", "tree ");
-
- TEST_OID_FAIL("parent 05452d6349abcd67aa396dfb28660d765d8b2a36", "parent ");
- TEST_OID_FAIL("05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree ");
- TEST_OID_FAIL("parent05452d6349abcd67aa396dfb28660d765d8b2a6a\n", "parent ");
- TEST_OID_FAIL("parent 05452d6349abcd67aa396dfb280d765d8b2a6\n", "parent ");
- TEST_OID_FAIL("tree 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree ");
- TEST_OID_FAIL("parent 0545xd6349abcd67aa396dfb28660d765d8b2a36\n", "parent ");
- TEST_OID_FAIL("parent 0545xd6349abcd67aa396dfb28660d765d8b2a36FF\n", "parent ");
- TEST_OID_FAIL("", "tree ");
- TEST_OID_FAIL("", "");
-
-#undef TEST_OID_PASS
-#undef TEST_OID_FAIL
-
-END_TEST
-
-BEGIN_TEST(parse1, "parse the signature line in a commit")
-
-#define TEST_SIGNATURE_PASS(_string, _header, _name, _email, _time, _offset) { \
- const char *ptr = _string; \
- size_t len = strlen(_string);\
- git_signature person = {NULL, NULL, {0, 0}}; \
- must_pass(git_signature__parse(&person, &ptr, ptr + len, _header, '\n'));\
- must_be_true(strcmp(_name, person.name) == 0);\
- must_be_true(strcmp(_email, person.email) == 0);\
- must_be_true(_time == person.when.time);\
- must_be_true(_offset == person.when.offset);\
- git__free(person.name); git__free(person.email);\
-}
-
-#define TEST_SIGNATURE_FAIL(_string, _header) { \
- const char *ptr = _string; \
- size_t len = strlen(_string);\
- git_signature person = {NULL, NULL, {0, 0}}; \
- must_fail(git_signature__parse(&person, &ptr, ptr + len, _header, '\n'));\
- git__free(person.name); git__free(person.email);\
-}
-
- TEST_SIGNATURE_PASS(
- "author Vicent Marti <tanoku@gmail.com> 12345 \n",
- "author ",
- "Vicent Marti",
- "tanoku@gmail.com",
- 12345,
- 0);
-
- TEST_SIGNATURE_PASS(
- "author Vicent Marti <> 12345 \n",
- "author ",
- "Vicent Marti",
- "",
- 12345,
- 0);
-
- TEST_SIGNATURE_PASS(
- "author Vicent Marti <tanoku@gmail.com> 231301 +1020\n",
- "author ",
- "Vicent Marti",
- "tanoku@gmail.com",
- 231301,
- 620);
-
- TEST_SIGNATURE_PASS(
- "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);
-
- TEST_SIGNATURE_PASS(
- "author Vicent Marti <tanokuwithaveryveryverylongemail\
- whichwillprobablyvoverflowtheemailbuffer@gmail.com> 12345 \n",
- "author ",
- "Vicent Marti",
- "tanokuwithaveryveryverylongemail\
- whichwillprobablyvoverflowtheemailbuffer@gmail.com",
- 12345,
- 0);
-
- TEST_SIGNATURE_PASS(
- "committer Vicent Marti <tanoku@gmail.com> 123456 +0000 \n",
- "committer ",
- "Vicent Marti",
- "tanoku@gmail.com",
- 123456,
- 0);
-
- TEST_SIGNATURE_PASS(
- "committer Vicent Marti <tanoku@gmail.com> 123456 +0100 \n",
- "committer ",
- "Vicent Marti",
- "tanoku@gmail.com",
- 123456,
- 60);
-
- TEST_SIGNATURE_PASS(
- "committer Vicent Marti <tanoku@gmail.com> 123456 -0100 \n",
- "committer ",
- "Vicent Marti",
- "tanoku@gmail.com",
- 123456,
- -60);
-
- /* Parse a signature without an author field */
- TEST_SIGNATURE_PASS(
- "committer <tanoku@gmail.com> 123456 -0100 \n",
- "committer ",
- "",
- "tanoku@gmail.com",
- 123456,
- -60);
-
- /* Parse a signature without an author field */
- TEST_SIGNATURE_PASS(
- "committer <tanoku@gmail.com> 123456 -0100 \n",
- "committer ",
- "",
- "tanoku@gmail.com",
- 123456,
- -60);
-
- /* Parse a signature with an empty author field */
- TEST_SIGNATURE_PASS(
- "committer <tanoku@gmail.com> 123456 -0100 \n",
- "committer ",
- "",
- "tanoku@gmail.com",
- 123456,
- -60);
-
- /* Parse a signature with an empty email field */
- TEST_SIGNATURE_PASS(
- "committer Vicent Marti <> 123456 -0100 \n",
- "committer ",
- "Vicent Marti",
- "",
- 123456,
- -60);
-
- /* Parse a signature with an empty email field */
- TEST_SIGNATURE_PASS(
- "committer Vicent Marti < > 123456 -0100 \n",
- "committer ",
- "Vicent Marti",
- "",
- 123456,
- -60);
-
- /* Parse a signature with empty name and email */
- TEST_SIGNATURE_PASS(
- "committer <> 123456 -0100 \n",
- "committer ",
- "",
- "",
- 123456,
- -60);
-
- /* Parse a signature with empty name and email */
- TEST_SIGNATURE_PASS(
- "committer <> 123456 -0100 \n",
- "committer ",
- "",
- "",
- 123456,
- -60);
-
- /* Parse a signature with empty name and email */
- TEST_SIGNATURE_PASS(
- "committer < > 123456 -0100 \n",
- "committer ",
- "",
- "",
- 123456,
- -60);
-
- /* Parse an obviously invalid signature */
- TEST_SIGNATURE_PASS(
- "committer foo<@bar> 123456 -0100 \n",
- "committer ",
- "foo",
- "@bar",
- 123456,
- -60);
-
- /* Parse an obviously invalid signature */
- TEST_SIGNATURE_PASS(
- "committer foo<@bar>123456 -0100 \n",
- "committer ",
- "foo",
- "@bar",
- 123456,
- -60);
-
- /* Parse an obviously invalid signature */
- TEST_SIGNATURE_PASS(
- "committer <>\n",
- "committer ",
- "",
- "",
- 0,
- 0);
-
- TEST_SIGNATURE_PASS(
- "committer Vicent Marti <tanoku@gmail.com> 123456 -1500 \n",
- "committer ",
- "Vicent Marti",
- "tanoku@gmail.com",
- 0,
- 0);
-
- TEST_SIGNATURE_PASS(
- "committer Vicent Marti <tanoku@gmail.com> 123456 +0163 \n",
- "committer ",
- "Vicent Marti",
- "tanoku@gmail.com",
- 0,
- 0);
-
- TEST_SIGNATURE_PASS(
- "author Vicent Marti <tanoku@gmail.com> notime \n",
- "author ",
- "Vicent Marti",
- "tanoku@gmail.com",
- 0,
- 0);
-
- TEST_SIGNATURE_PASS(
- "author Vicent Marti <tanoku@gmail.com> 123456 notimezone \n",
- "author ",
- "Vicent Marti",
- "tanoku@gmail.com",
- 0,
- 0);
-
- TEST_SIGNATURE_PASS(
- "author Vicent Marti <tanoku@gmail.com> notime +0100\n",
- "author ",
- "Vicent Marti",
- "tanoku@gmail.com",
- 0,
- 0);
-
- TEST_SIGNATURE_PASS(
- "author Vicent Marti <tanoku@gmail.com>\n",
- "author ",
- "Vicent Marti",
- "tanoku@gmail.com",
- 0,
- 0);
-
- TEST_SIGNATURE_PASS(
- "author A U Thor <author@example.com>, C O. Miter <comiter@example.com> 1234567890 -0700\n",
- "author ",
- "A U Thor",
- "author@example.com",
- 1234567890,
- -420);
-
- TEST_SIGNATURE_PASS(
- "author A U Thor <author@example.com> and others 1234567890 -0700\n",
- "author ",
- "A U Thor",
- "author@example.com",
- 1234567890,
- -420);
-
- TEST_SIGNATURE_PASS(
- "author A U Thor <author@example.com> and others 1234567890\n",
- "author ",
- "A U Thor",
- "author@example.com",
- 1234567890,
- 0);
-
- TEST_SIGNATURE_PASS(
- "author A U Thor> <author@example.com> and others 1234567890\n",
- "author ",
- "A U Thor>",
- "author@example.com",
- 1234567890,
- 0);
-
- TEST_SIGNATURE_FAIL(
- "committer Vicent Marti tanoku@gmail.com> 123456 -0100 \n",
- "committer ");
-
- TEST_SIGNATURE_FAIL(
- "author Vicent Marti <tanoku@gmail.com> 12345 \n",
- "author ");
-
- TEST_SIGNATURE_FAIL(
- "author Vicent Marti <tanoku@gmail.com> 12345 \n",
- "committer ");
-
- TEST_SIGNATURE_FAIL(
- "author Vicent Marti 12345 \n",
- "author ");
-
- TEST_SIGNATURE_FAIL(
- "author Vicent Marti <broken@email 12345 \n",
- "author ");
-
- TEST_SIGNATURE_FAIL(
- "committer Vicent Marti ><\n",
- "committer ");
-
- TEST_SIGNATURE_FAIL(
- "author ",
- "author ");
-
-#undef TEST_SIGNATURE_PASS
-#undef TEST_SIGNATURE_FAIL
-
-END_TEST
-
-static int try_build_signature(const char *name, const char *email, git_time_t time, int offset)
-{
- git_signature *sign;
- int error = GIT_SUCCESS;
-
- if ((error = git_signature_new(&sign, name, email, time, offset)) < GIT_SUCCESS)
- return error;
-
- git_signature_free((git_signature *)sign);
-
- return error;
-}
-
-BEGIN_TEST(signature0, "creating a signature trims leading and trailing spaces")
- git_signature *sign;
- must_pass(git_signature_new(&sign, " nulltoken ", " emeric.fermas@gmail.com ", 1234567890, 60));
- must_be_true(strcmp(sign->name, "nulltoken") == 0);
- must_be_true(strcmp(sign->email, "emeric.fermas@gmail.com") == 0);
- git_signature_free((git_signature *)sign);
-END_TEST
-
-BEGIN_TEST(signature1, "can not create a signature with empty name or email")
- must_pass(try_build_signature("nulltoken", "emeric.fermas@gmail.com", 1234567890, 60));
-
- must_fail(try_build_signature("", "emeric.fermas@gmail.com", 1234567890, 60));
- must_fail(try_build_signature(" ", "emeric.fermas@gmail.com", 1234567890, 60));
- must_fail(try_build_signature("nulltoken", "", 1234567890, 60));
- must_fail(try_build_signature("nulltoken", " ", 1234567890, 60));
-END_TEST
-
-BEGIN_TEST(signature2, "creating a one character signature")
- git_signature *sign;
- must_pass(git_signature_new(&sign, "x", "foo@bar.baz", 1234567890, 60));
- must_be_true(strcmp(sign->name, "x") == 0);
- must_be_true(strcmp(sign->email, "foo@bar.baz") == 0);
- git_signature_free((git_signature *)sign);
-END_TEST
-
-BEGIN_TEST(signature3, "creating a two character signature")
- git_signature *sign;
- must_pass(git_signature_new(&sign, "xx", "x@y.z", 1234567890, 60));
- must_be_true(strcmp(sign->name, "xx") == 0);
- must_be_true(strcmp(sign->email, "x@y.z") == 0);
- git_signature_free((git_signature *)sign);
-END_TEST
-
-BEGIN_TEST(signature4, "creating a zero character signature")
- git_signature *sign;
- must_fail(git_signature_new(&sign, "", "x@y.z", 1234567890, 60));
- must_be_true(sign == NULL);
-END_TEST
-
-
-BEGIN_TEST(parse2, "parse a whole commit buffer")
- const int broken_commit_count = sizeof(test_commits_broken) / sizeof(*test_commits_broken);
- const int working_commit_count = sizeof(test_commits_working) / sizeof(*test_commits_working);
-
- int i;
- git_repository *repo;
-
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
-
- for (i = 0; i < broken_commit_count; ++i) {
- git_commit *commit;
- commit = git__malloc(sizeof(git_commit));
- memset(commit, 0x0, sizeof(git_commit));
- commit->object.repo = repo;
-
- must_fail(git_commit__parse_buffer(
- commit,
- test_commits_broken[i],
- strlen(test_commits_broken[i]))
- );
-
- git_commit__free(commit);
- }
-
- for (i = 0; i < working_commit_count; ++i) {
- git_commit *commit;
-
- commit = git__malloc(sizeof(git_commit));
- memset(commit, 0x0, sizeof(git_commit));
- commit->object.repo = repo;
-
- must_pass(git_commit__parse_buffer(
- commit,
- test_commits_working[i],
- strlen(test_commits_working[i]))
- );
-
- git_commit__free(commit);
-
- commit = git__malloc(sizeof(git_commit));
- memset(commit, 0x0, sizeof(git_commit));
- commit->object.repo = repo;
-
- must_pass(git_commit__parse_buffer(
- commit,
- test_commits_working[i],
- strlen(test_commits_working[i]))
- );
-
- git_commit__free(commit);
- }
-
- git_repository_free(repo);
-END_TEST
-
-static const char *commit_ids[] = {
- "a4a7dce85cf63874e984719f4fdd239f5145052f", /* 0 */
- "9fd738e8f7967c078dceed8190330fc8648ee56a", /* 1 */
- "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", /* 2 */
- "c47800c7266a2be04c571c04d5a6614691ea99bd", /* 3 */
- "8496071c1b46c854b31185ea97743be6a8774479", /* 4 */
- "5b5b025afb0b4c913b4c338a42934a3863bf3644", /* 5 */
- "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", /* 6 */
-};
-
-BEGIN_TEST(details0, "query the details on a parsed commit")
- const size_t commit_count = sizeof(commit_ids) / sizeof(const char *);
-
- unsigned int i;
- git_repository *repo;
-
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
-
- 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]);
-
- must_pass(git_commit_lookup(&commit, 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);
-
- must_be_true(strcmp(author->name, "Scott Chacon") == 0);
- must_be_true(strcmp(author->email, "schacon@gmail.com") == 0);
- must_be_true(strcmp(committer->name, "Scott Chacon") == 0);
- must_be_true(strcmp(committer->email, "schacon@gmail.com") == 0);
- must_be_true(message != NULL);
- must_be_true(strchr(message, '\n') != NULL);
- must_be_true(commit_time > 0);
- must_be_true(parents <= 2);
- for (p = 0;p < parents;p++) {
- if (old_parent != NULL)
- git_commit_free(old_parent);
-
- old_parent = parent;
- must_pass(git_commit_parent(&parent, commit, p));
- must_be_true(parent != NULL);
- must_be_true(git_commit_author(parent) != NULL); // is it really a commit?
- }
- git_commit_free(old_parent);
- git_commit_free(parent);
-
- must_fail(git_commit_parent(&parent, commit, parents));
- git_commit_free(commit);
- }
-
- git_repository_free(repo);
-END_TEST
-
-#define COMMITTER_NAME "Vicent Marti"
-#define COMMITTER_EMAIL "vicent@github.com"
-#define 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";
-
-BEGIN_TEST(write0, "write a new commit object from memory to disk")
- git_repository *repo;
- git_commit *commit;
- git_oid tree_id, parent_id, commit_id;
- git_signature *author, *committer;
- const git_signature *author1, *committer1;
- git_commit *parent;
- git_tree *tree;
-
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
-
- git_oid_fromstr(&tree_id, tree_oid);
- must_pass(git_tree_lookup(&tree, repo, &tree_id));
-
- git_oid_fromstr(&parent_id, commit_ids[4]);
- must_pass(git_commit_lookup(&parent, repo, &parent_id));
-
- /* create signatures */
- must_pass(git_signature_new(&committer, COMMITTER_NAME, COMMITTER_EMAIL, 123456789, 60));
- must_pass(git_signature_new(&author, COMMITTER_NAME, COMMITTER_EMAIL, 987654321, 90));
-
- must_pass(git_commit_create_v(
- &commit_id, /* out id */
- 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);
-
- must_pass(git_commit_lookup(&commit, repo, &commit_id));
-
- /* Check attributes were set correctly */
- author1 = git_commit_author(commit);
- must_be_true(author1 != NULL);
- must_be_true(strcmp(author1->name, COMMITTER_NAME) == 0);
- must_be_true(strcmp(author1->email, COMMITTER_EMAIL) == 0);
- must_be_true(author1->when.time == 987654321);
- must_be_true(author1->when.offset == 90);
-
- committer1 = git_commit_committer(commit);
- must_be_true(committer1 != NULL);
- must_be_true(strcmp(committer1->name, COMMITTER_NAME) == 0);
- must_be_true(strcmp(committer1->email, COMMITTER_EMAIL) == 0);
- must_be_true(committer1->when.time == 123456789);
- must_be_true(committer1->when.offset == 60);
-
- must_be_true(strcmp(git_commit_message(commit), COMMIT_MESSAGE) == 0);
-
-#ifndef GIT_WIN32
- must_be_true((loose_object_mode(REPOSITORY_FOLDER, (git_object *)commit) & 0777) == GIT_OBJECT_FILE_MODE);
-#endif
-
- must_pass(remove_loose_object(REPOSITORY_FOLDER, (git_object *)commit));
-
- git_commit_free(commit);
- git_repository_free(repo);
-END_TEST
-
-#define ROOT_COMMIT_MESSAGE "This is a root commit\n\
-This is a root commit and should be the only one in this branch\n"
-
-BEGIN_TEST(root0, "create a root commit")
- git_repository *repo;
- git_commit *commit;
- 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_reference *head, *branch;
- char *head_old;
- git_tree *tree;
-
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
-
- git_oid_fromstr(&tree_id, tree_oid);
- must_pass(git_tree_lookup(&tree, repo, &tree_id));
-
- /* create signatures */
- must_pass(git_signature_new(&committer, COMMITTER_NAME, COMMITTER_EMAIL, 123456789, 60));
- must_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 */
- must_pass(git_reference_lookup(&head, repo, "HEAD"));
- must_be_true(git_reference_type(head) == GIT_REF_SYMBOLIC);
- head_old = git__strdup(git_reference_target(head));
- must_be_true(head_old != NULL);
-
- must_pass(git_reference_set_target(head, branch_name));
-
- must_pass(git_commit_create_v(
- &commit_id, /* out id */
- 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.
- */
- must_pass(git_commit_lookup(&commit, repo, &commit_id));
- must_be_true(git_commit_parentcount(commit) == 0);
- must_pass(git_reference_lookup(&branch, repo, branch_name));
- branch_oid = git_reference_oid(branch);
- must_pass(git_oid_cmp(branch_oid, &commit_id));
- must_be_true(!strcmp(git_commit_message(commit), ROOT_COMMIT_MESSAGE));
-
- /* Remove the data we just added to the repo */
- git_reference_free(head);
- must_pass(git_reference_lookup(&head, repo, "HEAD"));
- must_pass(git_reference_set_target(head, head_old));
- must_pass(git_reference_delete(branch));
- must_pass(remove_loose_object(REPOSITORY_FOLDER, (git_object *)commit));
- git__free(head_old);
- git_commit_free(commit);
- git_repository_free(repo);
-
- git_reference_free(head);
-END_TEST
-
-BEGIN_SUITE(commit)
- ADD_TEST(parse0);
- ADD_TEST(parse1);
- ADD_TEST(parse2);
- ADD_TEST(details0);
-
- ADD_TEST(write0);
-
- ADD_TEST(root0);
-
- ADD_TEST(signature0);
- ADD_TEST(signature1);
- ADD_TEST(signature2);
- ADD_TEST(signature3);
- ADD_TEST(signature4);
-END_SUITE
diff --git a/tests/t06-index.c b/tests/t06-index.c
deleted file mode 100644
index 7b0f05129..000000000
--- a/tests/t06-index.c
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * This file is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2,
- * as published by the Free Software Foundation.
- *
- * In addition to the permissions in the GNU General Public License,
- * the authors give you unlimited permission to link the compiled
- * version of this file into combinations with other programs,
- * and to distribute those combinations without any restriction
- * coming from the use of this file. (The General Public License
- * restrictions do apply in other respects; for example, they cover
- * modification of the file, and distribution when not linked into
- * a combined executable.)
- *
- * This file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-#include "test_lib.h"
-#include "test_helpers.h"
-
-#include "index.h"
-
-#define TEST_INDEX_ENTRY_COUNT 109
-#define TEST_INDEX2_ENTRY_COUNT 1437
-
-struct test_entry {
- unsigned int index;
- char path[128];
- git_off_t file_size;
- git_time_t mtime;
-};
-
-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}
-};
-
-BEGIN_TEST(read0, "load an empty index")
- git_index *index;
-
- must_pass(git_index_open(&index, "in-memory-index"));
- must_be_true(index->on_disk == 0);
-
- must_be_true(git_index_entrycount(index) == 0);
- must_be_true(index->entries.sorted);
-
- git_index_free(index);
-END_TEST
-
-BEGIN_TEST(read1, "load a standard index (default test index)")
- git_index *index;
- unsigned int i;
- git_index_entry **entries;
-
- must_pass(git_index_open(&index, TEST_INDEX_PATH));
- must_be_true(index->on_disk);
-
- must_be_true(git_index_entrycount(index) == TEST_INDEX_ENTRY_COUNT);
- must_be_true(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];
-
- must_be_true(strcmp(e->path, TEST_ENTRIES[i].path) == 0);
- must_be_true(e->mtime.seconds == TEST_ENTRIES[i].mtime);
- must_be_true(e->file_size == TEST_ENTRIES[i].file_size);
- }
-
- git_index_free(index);
-END_TEST
-
-BEGIN_TEST(read2, "load a standard index (git.git index)")
- git_index *index;
-
- must_pass(git_index_open(&index, TEST_INDEX2_PATH));
- must_be_true(index->on_disk);
-
- must_be_true(git_index_entrycount(index) == TEST_INDEX2_ENTRY_COUNT);
- must_be_true(index->entries.sorted);
- must_be_true(index->tree != NULL);
-
- git_index_free(index);
-END_TEST
-
-BEGIN_TEST(find0, "find an entry on an index")
- git_index *index;
- unsigned int i;
-
- must_pass(git_index_open(&index, TEST_INDEX_PATH));
-
- for (i = 0; i < ARRAY_SIZE(TEST_ENTRIES); ++i) {
- int idx = git_index_find(index, TEST_ENTRIES[i].path);
- must_be_true((unsigned int)idx == TEST_ENTRIES[i].index);
- }
-
- git_index_free(index);
-END_TEST
-
-BEGIN_TEST(find1, "find an entry in an empty index")
- git_index *index;
- unsigned int i;
-
- must_pass(git_index_open(&index, "fake-index"));
-
- for (i = 0; i < ARRAY_SIZE(TEST_ENTRIES); ++i) {
- int idx = git_index_find(index, TEST_ENTRIES[i].path);
- must_be_true(idx == GIT_ENOTFOUND);
- }
-
- git_index_free(index);
-END_TEST
-
-BEGIN_TEST(write0, "write an index back to disk")
- git_index *index;
-
- must_pass(copy_file(TEST_INDEXBIG_PATH, "index_rewrite"));
-
- must_pass(git_index_open(&index, "index_rewrite"));
- must_be_true(index->on_disk);
-
- must_pass(git_index_write(index));
- must_pass(cmp_files(TEST_INDEXBIG_PATH, "index_rewrite"));
-
- git_index_free(index);
-
- p_unlink("index_rewrite");
-END_TEST
-
-BEGIN_TEST(sort0, "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
- */
-END_TEST
-
-BEGIN_TEST(sort1, "sort the entires in an empty index")
- git_index *index;
-
- must_pass(git_index_open(&index, "fake-index"));
-
- /* FIXME: this test is slightly dumb */
- must_be_true(index->entries.sorted);
-
- git_index_free(index);
-END_TEST
-
-BEGIN_TEST(add0, "add a new file to the index")
- git_index *index;
- git_filebuf file = GIT_FILEBUF_INIT;
- git_repository *repo;
- git_index_entry *entry;
- git_oid id1;
-
- /* Intialize a new repository */
- must_pass(git_repository_init(&repo, TEMP_REPO_FOLDER "myrepo", 0));
-
- /* Ensure we're the only guy in the room */
- must_pass(git_repository_index(&index, repo));
- must_pass(git_index_entrycount(index) == 0);
-
- /* Create a new file in the working directory */
- must_pass(git_futils_mkpath2file(TEMP_REPO_FOLDER "myrepo/test.txt", 0777));
- must_pass(git_filebuf_open(&file, TEMP_REPO_FOLDER "myrepo/test.txt", 0));
- must_pass(git_filebuf_write(&file, "hey there\n", 10));
- must_pass(git_filebuf_commit(&file, 0666));
-
- /* Store the expected hash of the file/blob
- * This has been generated by executing the following
- * $ echo "hey there" | git hash-object --stdin
- */
- must_pass(git_oid_fromstr(&id1, "a8233120f6ad708f843d861ce2b7228ec4e3dec6"));
-
- /* Add the new file to the index */
- must_pass(git_index_add(index, "test.txt", 0));
-
- /* Wow... it worked! */
- must_pass(git_index_entrycount(index) == 1);
- entry = git_index_get(index, 0);
-
- /* And the built-in hashing mechanism worked as expected */
- must_be_true(git_oid_cmp(&id1, &entry->oid) == 0);
-
- git_index_free(index);
- git_repository_free(repo);
- must_pass(git_futils_rmdir_r(TEMP_REPO_FOLDER, 1));
-END_TEST
-
-BEGIN_SUITE(index)
- ADD_TEST(read0);
- ADD_TEST(read1);
- ADD_TEST(read2);
-
- ADD_TEST(find0);
- ADD_TEST(find1);
-
- ADD_TEST(write0);
-
- ADD_TEST(sort0);
- ADD_TEST(sort1);
-
- ADD_TEST(add0);
-END_SUITE
diff --git a/tests/t07-hashtable.c b/tests/t07-hashtable.c
deleted file mode 100644
index 41d52af19..000000000
--- a/tests/t07-hashtable.c
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * This file is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2,
- * as published by the Free Software Foundation.
- *
- * In addition to the permissions in the GNU General Public License,
- * the authors give you unlimited permission to link the compiled
- * version of this file into combinations with other programs,
- * and to distribute those combinations without any restriction
- * coming from the use of this file. (The General Public License
- * restrictions do apply in other respects; for example, they cover
- * modification of the file, and distribution when not linked into
- * a combined executable.)
- *
- * This file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-#include "test_lib.h"
-#include "test_helpers.h"
-
-#include "hashtable.h"
-#include "hash.h"
-
-typedef struct _aux_object {
- int __bulk;
- git_oid id;
- int visited;
-} table_item;
-
-static uint32_t hash_func(const void *key, int hash_id)
-{
- uint32_t r;
- const git_oid *id = key;
-
- memcpy(&r, id->id + (hash_id * sizeof(uint32_t)), sizeof(r));
- return r;
-}
-
-static int hash_cmpkey(const void *a, const void *b)
-{
- return git_oid_cmp(a, b);
-}
-
-BEGIN_TEST(table0, "create a new hashtable")
-
- git_hashtable *table = NULL;
-
- table = git_hashtable_alloc(55, hash_func, hash_cmpkey);
- must_be_true(table != NULL);
- must_be_true(table->size_mask + 1 == 64);
-
- git_hashtable_free(table);
-
-END_TEST
-
-BEGIN_TEST(table1, "fill the hashtable with random entries")
-
- const int objects_n = 32;
- int i;
-
- table_item *objects;
- git_hashtable *table = NULL;
-
- table = git_hashtable_alloc(objects_n * 2, hash_func, hash_cmpkey);
- must_be_true(table != NULL);
-
- objects = git__malloc(objects_n * sizeof(table_item));
- memset(objects, 0x0, objects_n * sizeof(table_item));
-
- /* populate the hash table */
- for (i = 0; i < objects_n; ++i) {
- git_hash_buf(&(objects[i].id), &i, sizeof(int));
- must_pass(git_hashtable_insert(table, &(objects[i].id), &(objects[i])));
- }
-
- /* make sure all the inserted objects can be found */
- for (i = 0; i < objects_n; ++i) {
- git_oid id;
- table_item *ob;
-
- git_hash_buf(&id, &i, sizeof(int));
- ob = (table_item *)git_hashtable_lookup(table, &id);
-
- must_be_true(ob != NULL);
- must_be_true(ob == &(objects[i]));
- }
-
- /* make sure we cannot find inexisting objects */
- for (i = 0; i < 50; ++i) {
- int hash_id;
- git_oid id;
-
- hash_id = (rand() % 50000) + objects_n;
- git_hash_buf(&id, &hash_id, sizeof(int));
- must_be_true(git_hashtable_lookup(table, &id) == NULL);
- }
-
- git_hashtable_free(table);
- git__free(objects);
-
-END_TEST
-
-
-BEGIN_TEST(table2, "make sure the table resizes automatically")
-
- const int objects_n = 64;
- int i;
- unsigned int old_size;
- table_item *objects;
- git_hashtable *table = NULL;
-
- table = git_hashtable_alloc(objects_n, hash_func, hash_cmpkey);
- must_be_true(table != NULL);
-
- objects = git__malloc(objects_n * sizeof(table_item));
- memset(objects, 0x0, objects_n * sizeof(table_item));
-
- old_size = table->size_mask + 1;
-
- /* populate the hash table -- should be automatically resized */
- for (i = 0; i < objects_n; ++i) {
- git_hash_buf(&(objects[i].id), &i, sizeof(int));
- must_pass(git_hashtable_insert(table, &(objects[i].id), &(objects[i])));
- }
-
- must_be_true(table->size_mask > old_size);
-
- /* make sure all the inserted objects can be found */
- for (i = 0; i < objects_n; ++i) {
- git_oid id;
- table_item *ob;
-
- git_hash_buf(&id, &i, sizeof(int));
- ob = (table_item *)git_hashtable_lookup(table, &id);
-
- must_be_true(ob != NULL);
- must_be_true(ob == &(objects[i]));
- }
-
- git_hashtable_free(table);
- git__free(objects);
-
-END_TEST
-
-BEGIN_TEST(tableit0, "iterate through all the contents of the table")
-
- const int objects_n = 32;
- int i;
- table_item *objects, *ob;
- const void *GIT_UNUSED(_unused);
-
- git_hashtable *table = NULL;
-
- table = git_hashtable_alloc(objects_n * 2, hash_func, hash_cmpkey);
- must_be_true(table != NULL);
-
- objects = git__malloc(objects_n * sizeof(table_item));
- memset(objects, 0x0, objects_n * sizeof(table_item));
-
- /* populate the hash table */
- for (i = 0; i < objects_n; ++i) {
- git_hash_buf(&(objects[i].id), &i, sizeof(int));
- must_pass(git_hashtable_insert(table, &(objects[i].id), &(objects[i])));
- }
-
- GIT_HASHTABLE_FOREACH(table, _unused, ob,
- ob->visited = 1;
- );
-
- /* make sure all nodes have been visited */
- for (i = 0; i < objects_n; ++i)
- must_be_true(objects[i].visited);
-
- git_hashtable_free(table);
- git__free(objects);
-END_TEST
-
-
-BEGIN_SUITE(hashtable)
- ADD_TEST(table0);
- ADD_TEST(table1);
- ADD_TEST(table2);
- ADD_TEST(tableit0);
-END_SUITE
-
diff --git a/tests/t08-tag.c b/tests/t08-tag.c
deleted file mode 100644
index eacbb3ae1..000000000
--- a/tests/t08-tag.c
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * This file is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2,
- * as published by the Free Software Foundation.
- *
- * In addition to the permissions in the GNU General Public License,
- * the authors give you unlimited permission to link the compiled
- * version of this file into combinations with other programs,
- * and to distribute those combinations without any restriction
- * coming from the use of this file. (The General Public License
- * restrictions do apply in other respects; for example, they cover
- * modification of the file, and distribution when not linked into
- * a combined executable.)
- *
- * This file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-#include "test_lib.h"
-#include "test_helpers.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";
-
-BEGIN_TEST(read0, "read and parse a tag from the repository")
- git_repository *repo;
- git_tag *tag1, *tag2;
- git_commit *commit;
- git_oid id1, id2, id_commit;
-
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
-
- git_oid_fromstr(&id1, tag1_id);
- git_oid_fromstr(&id2, tag2_id);
- git_oid_fromstr(&id_commit, tagged_commit);
-
- must_pass(git_tag_lookup(&tag1, repo, &id1));
-
- must_be_true(strcmp(git_tag_name(tag1), "test") == 0);
- must_be_true(git_tag_type(tag1) == GIT_OBJ_TAG);
-
- must_pass(git_tag_target((git_object **)&tag2, tag1));
- must_be_true(tag2 != NULL);
-
- must_be_true(git_oid_cmp(&id2, git_tag_id(tag2)) == 0);
-
- must_pass(git_tag_target((git_object **)&commit, tag2));
- must_be_true(commit != NULL);
-
- must_be_true(git_oid_cmp(&id_commit, git_commit_id(commit)) == 0);
-
- git_tag_free(tag1);
- git_tag_free(tag2);
- git_commit_free(commit);
- git_repository_free(repo);
-END_TEST
-
-BEGIN_TEST(read1, "list all tag names from the repository")
- git_repository *repo;
- git_strarray tag_list;
-
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
- must_pass(git_tag_list(&tag_list, repo));
-
- must_be_true(tag_list.count == 3);
-
- git_strarray_free(&tag_list);
- git_repository_free(repo);
-END_TEST
-
-static int ensure_tag_pattern_match(git_repository *repo, const char *pattern, const size_t expected_matches)
-{
- git_strarray tag_list;
- int error = GIT_SUCCESS;
-
- if ((error = git_tag_list_match(&tag_list, pattern, repo)) < GIT_SUCCESS)
- goto exit;
-
- if (tag_list.count != expected_matches)
- error = GIT_ERROR;
-
-exit:
- git_strarray_free(&tag_list);
- return error;
-}
-
-BEGIN_TEST(read2, "list all tag names from the repository matching a specified pattern")
- git_repository *repo;
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
- must_pass(ensure_tag_pattern_match(repo, "", 3));
- must_pass(ensure_tag_pattern_match(repo, "*", 3));
- must_pass(ensure_tag_pattern_match(repo, "t*", 1));
- must_pass(ensure_tag_pattern_match(repo, "*b", 2));
- must_pass(ensure_tag_pattern_match(repo, "e", 0));
- must_pass(ensure_tag_pattern_match(repo, "e90810b", 1));
- must_pass(ensure_tag_pattern_match(repo, "e90810[ab]", 1));
- git_repository_free(repo);
-END_TEST
-
-#define BAD_TAG_REPOSITORY_FOLDER TEST_RESOURCES "/bad_tag.git/"
-
-BEGIN_TEST(read3, "read and parse a tag without a tagger field")
- git_repository *repo;
- git_tag *bad_tag;
- git_commit *commit;
- git_oid id, id_commit;
-
- must_pass(git_repository_open(&repo, BAD_TAG_REPOSITORY_FOLDER));
-
- git_oid_fromstr(&id, bad_tag_id);
- git_oid_fromstr(&id_commit, badly_tagged_commit);
-
- must_pass(git_tag_lookup(&bad_tag, repo, &id));
- must_be_true(bad_tag != NULL);
-
- must_be_true(strcmp(git_tag_name(bad_tag), "e90810b") == 0);
- must_be_true(git_oid_cmp(&id, git_tag_id(bad_tag)) == 0);
- must_be_true(bad_tag->tagger == NULL);
-
- must_pass(git_tag_target((git_object **)&commit, bad_tag));
- must_be_true(commit != NULL);
-
- must_be_true(git_oid_cmp(&id_commit, git_commit_id(commit)) == 0);
-
- git_tag_free(bad_tag);
- git_commit_free(commit);
-
- git_repository_free(repo);
-END_TEST
-
-
-#define TAGGER_NAME "Vicent Marti"
-#define TAGGER_EMAIL "vicent@github.com"
-#define TAGGER_MESSAGE "This is my tag.\n\nThere are many tags, but this one is mine\n"
-
-BEGIN_TEST(write0, "write a tag to the repository and read it again")
- git_repository *repo;
- git_tag *tag;
- git_oid target_id, tag_id;
- git_signature *tagger;
- const git_signature *tagger1;
- git_reference *ref_tag;
- git_object *target;
-
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
-
- git_oid_fromstr(&target_id, tagged_commit);
- must_pass(git_object_lookup(&target, repo, &target_id, GIT_OBJ_COMMIT));
-
- /* create signature */
- must_pass(git_signature_new(&tagger, TAGGER_NAME, TAGGER_EMAIL, 123456789, 60));
-
- must_pass(git_tag_create(
- &tag_id, /* out id */
- repo,
- "the-tag",
- target,
- tagger,
- TAGGER_MESSAGE,
- 0));
-
- git_object_free(target);
- git_signature_free(tagger);
-
- must_pass(git_tag_lookup(&tag, repo, &tag_id));
- must_be_true(git_oid_cmp(git_tag_target_oid(tag), &target_id) == 0);
-
- /* Check attributes were set correctly */
- tagger1 = git_tag_tagger(tag);
- must_be_true(tagger1 != NULL);
- must_be_true(strcmp(tagger1->name, TAGGER_NAME) == 0);
- must_be_true(strcmp(tagger1->email, TAGGER_EMAIL) == 0);
- must_be_true(tagger1->when.time == 123456789);
- must_be_true(tagger1->when.offset == 60);
-
- must_be_true(strcmp(git_tag_message(tag), TAGGER_MESSAGE) == 0);
-
- must_pass(git_reference_lookup(&ref_tag, repo, "refs/tags/the-tag"));
- must_be_true(git_oid_cmp(git_reference_oid(ref_tag), &tag_id) == 0);
- must_pass(git_reference_delete(ref_tag));
-#ifndef GIT_WIN32
- must_be_true((loose_object_mode(REPOSITORY_FOLDER, (git_object *)tag) & 0777) == GIT_OBJECT_FILE_MODE);
-#endif
-
- must_pass(remove_loose_object(REPOSITORY_FOLDER, (git_object *)tag));
-
- git_tag_free(tag);
- git_repository_free(repo);
-END_TEST
-
-BEGIN_TEST(write2, "Attempt to write a tag bearing the same name than an already existing tag")
- git_repository *repo;
- git_oid target_id, tag_id;
- git_signature *tagger;
- git_object *target;
-
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
-
- git_oid_fromstr(&target_id, tagged_commit);
- must_pass(git_object_lookup(&target, repo, &target_id, GIT_OBJ_COMMIT));
-
- /* create signature */
- must_pass(git_signature_new(&tagger, TAGGER_NAME, TAGGER_EMAIL, 123456789, 60));
-
- must_fail(git_tag_create(
- &tag_id, /* out id */
- repo,
- "e90810b",
- target,
- tagger,
- TAGGER_MESSAGE,
- 0));
-
- git_object_free(target);
- git_signature_free(tagger);
-
- git_repository_free(repo);
-
-END_TEST
-
-BEGIN_TEST(write3, "Replace an already existing tag")
- git_repository *repo;
- git_oid target_id, tag_id, old_tag_id;
- git_signature *tagger;
- git_reference *ref_tag;
- git_object *target;
-
- must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
-
- git_oid_fromstr(&target_id, tagged_commit);
- must_pass(git_object_lookup(&target, repo, &target_id, GIT_OBJ_COMMIT));
-
- must_pass(git_reference_lookup(&ref_tag, repo, "refs/tags/e90810b"));
- git_oid_cpy(&old_tag_id, git_reference_oid(ref_tag));
- git_reference_free(ref_tag);
-
- /* create signature */
- must_pass(git_signature_new(&tagger, TAGGER_NAME, TAGGER_EMAIL, 123456789, 60));
-
- must_pass(git_tag_create(
- &tag_id, /* out id */
- repo,
- "e90810b",
- target,
- tagger,
- TAGGER_MESSAGE,
- 1));
-
- git_object_free(target);
- git_signature_free(tagger);
-
- must_pass(git_reference_lookup(&ref_tag, repo, "refs/tags/e90810b"));
- must_be_true(git_oid_cmp(git_reference_oid(ref_tag), &tag_id) == 0);
- must_be_true(git_oid_cmp(git_reference_oid(ref_tag), &old_tag_id) != 0);
-
- close_temp_repo(repo);
-
- git_reference_free(ref_tag);
-END_TEST
-
-BEGIN_TEST(write4, "write a lightweight tag to the repository and read it again")
- git_repository *repo;
- git_oid target_id, object_id;
- git_reference *ref_tag;
- git_object *target;
-
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
-
- git_oid_fromstr(&target_id, tagged_commit);
- must_pass(git_object_lookup(&target, repo, &target_id, GIT_OBJ_COMMIT));
-
- must_pass(git_tag_create_lightweight(
- &object_id,
- repo,
- "light-tag",
- target,
- 0));
-
- git_object_free(target);
-
- must_be_true(git_oid_cmp(&object_id, &target_id) == 0);
-
- must_pass(git_reference_lookup(&ref_tag, repo, "refs/tags/light-tag"));
- must_be_true(git_oid_cmp(git_reference_oid(ref_tag), &target_id) == 0);
-
- must_pass(git_tag_delete(repo, "light-tag"));
-
- git_repository_free(repo);
-
- git_reference_free(ref_tag);
-END_TEST
-
-BEGIN_TEST(write5, "Attempt to write a lightweight tag bearing the same name than an already existing tag")
- git_repository *repo;
- git_oid target_id, object_id, existing_object_id;
- git_object *target;
-
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
-
- git_oid_fromstr(&target_id, tagged_commit);
- must_pass(git_object_lookup(&target, repo, &target_id, GIT_OBJ_COMMIT));
-
- must_fail(git_tag_create_lightweight(
- &object_id,
- repo,
- "e90810b",
- target,
- 0));
-
- git_oid_fromstr(&existing_object_id, tag2_id);
- must_be_true(git_oid_cmp(&object_id, &existing_object_id) == 0);
-
- git_object_free(target);
-
- git_repository_free(repo);
-END_TEST
-
-BEGIN_TEST(delete0, "Delete an already existing tag")
- git_repository *repo;
- git_reference *ref_tag;
-
- must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
-
- must_pass(git_tag_delete(repo, "e90810b"));
-
- must_fail(git_reference_lookup(&ref_tag, repo, "refs/tags/e90810b"));
-
- close_temp_repo(repo);
-
- git_reference_free(ref_tag);
-END_TEST
-
-BEGIN_SUITE(tag)
- ADD_TEST(read0);
- ADD_TEST(read1);
- ADD_TEST(read2);
- ADD_TEST(read3);
-
- ADD_TEST(write0);
- ADD_TEST(write2);
- ADD_TEST(write3);
- ADD_TEST(write4);
- ADD_TEST(write5);
-
- ADD_TEST(delete0);
-
-END_SUITE
diff --git a/tests/t09-tree.c b/tests/t09-tree.c
deleted file mode 100644
index 8995b45ef..000000000
--- a/tests/t09-tree.c
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * This file is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2,
- * as published by the Free Software Foundation.
- *
- * In addition to the permissions in the GNU General Public License,
- * the authors give you unlimited permission to link the compiled
- * version of this file into combinations with other programs,
- * and to distribute those combinations without any restriction
- * coming from the use of this file. (The General Public License
- * restrictions do apply in other respects; for example, they cover
- * modification of the file, and distribution when not linked into
- * a combined executable.)
- *
- * This file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-#include "test_lib.h"
-#include "test_helpers.h"
-
-#include "tree.h"
-
-static const char *tree_oid = "1810dff58d8a660512d4832e740f692884338ccd";
-
-static const char *blob_oid = "fa49b077972391ad58037050f2a75f74e3671e92";
-static const char *first_tree = "181037049a54a1eb5fab404658a3a250b44335d7";
-static const char *second_tree = "f60079018b664e4e79329a7ef9559c8d9e0378d1";
-static const char *third_tree = "eb86d8b81d6adbd5290a935d6c9976882de98488";
-
-#if 0
-static int print_tree(git_repository *repo, const git_oid *tree_oid, int depth)
-{
- static const char *indent = " ";
- git_tree *tree;
- unsigned int i;
-
- if (git_tree_lookup(&tree, repo, tree_oid) < GIT_SUCCESS)
- return GIT_ERROR;
-
- for (i = 0; i < git_tree_entrycount(tree); ++i) {
- const git_tree_entry *entry = git_tree_entry_byindex(tree, i);
- char entry_oid[40];
-
- git_oid_fmt(entry_oid, &entry->oid);
- printf("%.*s%o [%.*s] %s\n", depth*2, indent, entry->attr, 40, entry_oid, entry->filename);
-
- if (entry->attr == S_IFDIR) {
- if (print_tree(repo, &entry->oid, depth + 1) < GIT_SUCCESS) {
- git_tree_free(tree);
- return GIT_ERROR;
- }
- }
- }
-
- git_tree_free(tree);
- return GIT_SUCCESS;
-}
-#endif
-
-BEGIN_TEST(read0, "acces randomly the entries on a loaded tree")
- git_oid id;
- git_repository *repo;
- git_tree *tree;
-
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
-
- git_oid_fromstr(&id, tree_oid);
-
- must_pass(git_tree_lookup(&tree, repo, &id));
-
- must_be_true(git_tree_entry_byname(tree, "README") != NULL);
- must_be_true(git_tree_entry_byname(tree, "NOTEXISTS") == NULL);
- must_be_true(git_tree_entry_byname(tree, "") == NULL);
- must_be_true(git_tree_entry_byindex(tree, 0) != NULL);
- must_be_true(git_tree_entry_byindex(tree, 2) != NULL);
- must_be_true(git_tree_entry_byindex(tree, 3) == NULL);
- must_be_true(git_tree_entry_byindex(tree, (unsigned int)-1) == NULL);
-
- git_tree_free(tree);
- git_repository_free(repo);
-END_TEST
-
-BEGIN_TEST(read1, "read a tree from the repository")
- git_oid id;
- git_repository *repo;
- git_tree *tree;
- const git_tree_entry *entry;
- git_object *obj;
-
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
-
- git_oid_fromstr(&id, tree_oid);
-
- must_pass(git_tree_lookup(&tree, repo, &id));
-
- must_be_true(git_tree_entrycount(tree) == 3);
-
- /* GH-86: git_object_lookup() should also check the type if the object comes from the cache */
- must_be_true(git_object_lookup(&obj, repo, &id, GIT_OBJ_TREE) == 0);
- must_be_true(obj != NULL);
- git_object_free(obj);
- obj = NULL;
- must_be_true(git_object_lookup(&obj, repo, &id, GIT_OBJ_BLOB) == GIT_EINVALIDTYPE);
- must_be_true(obj == NULL);
-
- entry = git_tree_entry_byname(tree, "README");
- must_be_true(entry != NULL);
-
- must_be_true(strcmp(git_tree_entry_name(entry), "README") == 0);
-
- must_pass(git_tree_entry_2object(&obj, repo, entry));
- must_be_true(obj != NULL);
-
- git_object_free(obj);
- git_tree_free(tree);
- git_repository_free(repo);
-END_TEST
-
-#if 0
-BEGIN_TEST(write0, "write a tree from an index")
- git_repository *repo;
- git_index *index;
- git_oid tree_oid;
-
- must_pass(git_repository_open(&repo, "/tmp/redtmp/.git"));
- must_pass(git_repository_index(&index, repo));
-
- must_pass(git_tree_create_fromindex(&tree_oid, index));
- must_pass(print_tree(repo, &tree_oid, 0));
-
- git_repository_free(repo);
-END_TEST
-#endif
-
-BEGIN_TEST(write2, "write a tree from a memory")
- git_repository *repo;
- git_treebuilder *builder;
- git_tree *tree;
- git_oid id, bid, rid, id2;
-
- must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
- 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.
- must_pass(git_tree_lookup(&tree, repo, &id));
- must_pass(git_treebuilder_create(&builder, tree));
-
- must_fail(git_treebuilder_insert(NULL, builder, "", &bid, 0100644));
- must_fail(git_treebuilder_insert(NULL, builder, "/", &bid, 0100644));
- must_fail(git_treebuilder_insert(NULL, builder, "folder/new.txt", &bid, 0100644));
-
- must_pass(git_treebuilder_insert(NULL,builder,"new.txt",&bid,0100644));
- must_pass(git_treebuilder_write(&rid,repo,builder));
-
- must_be_true(git_oid_cmp(&rid, &id2) == 0);
-
- git_treebuilder_free(builder);
- git_tree_free(tree);
- close_temp_repo(repo);
-END_TEST
-
-BEGIN_TEST(write3, "write a hierarchical tree from a memory")
- git_repository *repo;
- git_treebuilder *builder;
- git_tree *tree;
- git_oid id, bid, subtree_id, id2, id3;
- git_oid id_hiearar;
-
- must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
- 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
- must_pass(git_treebuilder_create(&builder, NULL));
- must_pass(git_treebuilder_insert(NULL,builder,"new.txt",&bid,0100644));
- must_pass(git_treebuilder_write(&subtree_id,repo,builder));
- git_treebuilder_free(builder);
-
- // create parent tree
- must_pass(git_tree_lookup(&tree, repo, &id));
- must_pass(git_treebuilder_create(&builder, tree));
- must_pass(git_treebuilder_insert(NULL,builder,"new",&subtree_id,040000));
- must_pass(git_treebuilder_write(&id_hiearar,repo,builder));
- git_treebuilder_free(builder);
- git_tree_free(tree);
-
- must_be_true(git_oid_cmp(&id_hiearar, &id3) == 0);
-
- // check data is correct
- must_pass(git_tree_lookup(&tree, repo, &id_hiearar));
- must_be_true(2 == git_tree_entrycount(tree));
-#ifndef GIT_WIN32
- must_be_true((loose_object_dir_mode(TEMP_REPO_FOLDER, (git_object *)tree) & 0777) == GIT_OBJECT_DIR_MODE);
- must_be_true((loose_object_mode(TEMP_REPO_FOLDER, (git_object *)tree) & 0777) == GIT_OBJECT_FILE_MODE);
-#endif
- git_tree_free(tree);
-
- close_temp_repo(repo);
-
-END_TEST
-
-BEGIN_SUITE(tree)
- //ADD_TEST(print0);
- ADD_TEST(read0);
- ADD_TEST(read1);
- //ADD_TEST(write0);
- //ADD_TEST(write1);
- ADD_TEST(write2);
- ADD_TEST(write3);
-END_SUITE
diff --git a/tests/t10-refs.c b/tests/t10-refs.c
deleted file mode 100644
index 63d1cb7d1..000000000
--- a/tests/t10-refs.c
+++ /dev/null
@@ -1,1338 +0,0 @@
-/*
- * This file is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2,
- * as published by the Free Software Foundation.
- *
- * In addition to the permissions in the GNU General Public License,
- * the authors give you unlimited permission to link the compiled
- * version of this file into combinations with other programs,
- * and to distribute those combinations without any restriction
- * coming from the use of this file. (The General Public License
- * restrictions do apply in other respects; for example, they cover
- * modification of the file, and distribution when not linked into
- * a combined executable.)
- *
- * This file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-#include "test_lib.h"
-#include "test_helpers.h"
-
-#include "repository.h"
-
-#include "git2/reflog.h"
-#include "reflog.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";
-
-BEGIN_TEST(readtag0, "lookup a loose tag reference")
- git_repository *repo;
- git_reference *reference;
- git_object *object;
- git_buf ref_name_from_tag_name = GIT_BUF_INIT;
-
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
-
- must_pass(git_reference_lookup(&reference, repo, loose_tag_ref_name));
- must_be_true(git_reference_type(reference) & GIT_REF_OID);
- must_be_true(git_reference_is_packed(reference) == 0);
- must_be_true(strcmp(reference->name, loose_tag_ref_name) == 0);
-
- must_pass(git_object_lookup(&object, repo, git_reference_oid(reference), GIT_OBJ_ANY));
- must_be_true(object != NULL);
- must_be_true(git_object_type(object) == GIT_OBJ_TAG);
-
- /* Ensure the name of the tag matches the name of the reference */
- must_pass(git_buf_joinpath(&ref_name_from_tag_name, GIT_REFS_TAGS_DIR, git_tag_name((git_tag *)object)));
- must_be_true(strcmp(ref_name_from_tag_name.ptr, loose_tag_ref_name) == 0);
- git_buf_free(&ref_name_from_tag_name);
-
- git_object_free(object);
- git_repository_free(repo);
-
- git_reference_free(reference);
-END_TEST
-
-BEGIN_TEST(readtag1, "lookup a loose tag reference that doesn't exist")
- git_repository *repo;
- git_reference *reference;
-
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
- must_fail(git_reference_lookup(&reference, repo, non_existing_tag_ref_name));
-
- git_repository_free(repo);
-
- git_reference_free(reference);
-END_TEST
-
-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";
-
-BEGIN_TEST(readsym0, "lookup a symbolic reference")
- git_repository *repo;
- git_reference *reference, *resolved_ref;
- git_object *object;
- git_oid id;
-
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
-
- must_pass(git_reference_lookup(&reference, repo, GIT_HEAD_FILE));
- must_be_true(git_reference_type(reference) & GIT_REF_SYMBOLIC);
- must_be_true(git_reference_is_packed(reference) == 0);
- must_be_true(strcmp(reference->name, GIT_HEAD_FILE) == 0);
-
- must_pass(git_reference_resolve(&resolved_ref, reference));
- must_be_true(git_reference_type(resolved_ref) == GIT_REF_OID);
-
- must_pass(git_object_lookup(&object, repo, git_reference_oid(resolved_ref), GIT_OBJ_ANY));
- must_be_true(object != NULL);
- must_be_true(git_object_type(object) == GIT_OBJ_COMMIT);
-
- git_oid_fromstr(&id, current_master_tip);
- must_be_true(git_oid_cmp(&id, git_object_id(object)) == 0);
-
- git_object_free(object);
- git_repository_free(repo);
-
- git_reference_free(reference);
- git_reference_free(resolved_ref);
-END_TEST
-
-BEGIN_TEST(readsym1, "lookup a nested symbolic reference")
- git_repository *repo;
- git_reference *reference, *resolved_ref;
- git_object *object;
- git_oid id;
-
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
-
- must_pass(git_reference_lookup(&reference, repo, head_tracker_sym_ref_name));
- must_be_true(git_reference_type(reference) & GIT_REF_SYMBOLIC);
- must_be_true(git_reference_is_packed(reference) == 0);
- must_be_true(strcmp(reference->name, head_tracker_sym_ref_name) == 0);
-
- must_pass(git_reference_resolve(&resolved_ref, reference));
- must_be_true(git_reference_type(resolved_ref) == GIT_REF_OID);
-
- must_pass(git_object_lookup(&object, repo, git_reference_oid(resolved_ref), GIT_OBJ_ANY));
- must_be_true(object != NULL);
- must_be_true(git_object_type(object) == GIT_OBJ_COMMIT);
-
- git_oid_fromstr(&id, current_master_tip);
- must_be_true(git_oid_cmp(&id, git_object_id(object)) == 0);
-
- git_object_free(object);
- git_repository_free(repo);
-
- git_reference_free(reference);
- git_reference_free(resolved_ref);
-END_TEST
-
-BEGIN_TEST(readsym2, "lookup the HEAD and resolve the master branch")
- git_repository *repo;
- git_reference *reference, *resolved_ref, *comp_base_ref;
-
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
-
- must_pass(git_reference_lookup(&reference, repo, head_tracker_sym_ref_name));
- must_pass(git_reference_resolve(&comp_base_ref, reference));
- git_reference_free(reference);
-
- must_pass(git_reference_lookup(&reference, repo, GIT_HEAD_FILE));
- must_pass(git_reference_resolve(&resolved_ref, reference));
- must_pass(git_oid_cmp(git_reference_oid(comp_base_ref), git_reference_oid(resolved_ref)));
- git_reference_free(reference);
- git_reference_free(resolved_ref);
-
- must_pass(git_reference_lookup(&reference, repo, current_head_target));
- must_pass(git_reference_resolve(&resolved_ref, reference));
- must_pass(git_oid_cmp(git_reference_oid(comp_base_ref), git_reference_oid(resolved_ref)));
- git_reference_free(reference);
- git_reference_free(resolved_ref);
-
- git_reference_free(comp_base_ref);
- git_repository_free(repo);
-END_TEST
-
-BEGIN_TEST(readsym3, "lookup the master branch and then the HEAD")
- git_repository *repo;
- git_reference *reference, *master_ref, *resolved_ref;
-
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
-
- must_pass(git_reference_lookup(&master_ref, repo, current_head_target));
- must_pass(git_reference_lookup(&reference, repo, GIT_HEAD_FILE));
-
- must_pass(git_reference_resolve(&resolved_ref, reference));
- must_pass(git_oid_cmp(git_reference_oid(master_ref), git_reference_oid(resolved_ref)));
-
- git_repository_free(repo);
-
- git_reference_free(reference);
- git_reference_free(resolved_ref);
- git_reference_free(master_ref);
-END_TEST
-
-static const char *packed_head_name = "refs/heads/packed";
-static const char *packed_test_head_name = "refs/heads/packed-test";
-
-BEGIN_TEST(readpacked0, "lookup a packed reference")
- git_repository *repo;
- git_reference *reference;
- git_object *object;
-
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
-
- must_pass(git_reference_lookup(&reference, repo, packed_head_name));
- must_be_true(git_reference_type(reference) & GIT_REF_OID);
- must_be_true(git_reference_is_packed(reference));
- must_be_true(strcmp(reference->name, packed_head_name) == 0);
-
- must_pass(git_object_lookup(&object, repo, git_reference_oid(reference), GIT_OBJ_ANY));
- must_be_true(object != NULL);
- must_be_true(git_object_type(object) == GIT_OBJ_COMMIT);
-
- git_object_free(object);
- git_repository_free(repo);
-
- git_reference_free(reference);
-END_TEST
-
-BEGIN_TEST(readpacked1, "assure that a loose reference is looked up before a packed reference")
- git_repository *repo;
- git_reference *reference;
-
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
- must_pass(git_reference_lookup(&reference, repo, packed_head_name));
- git_reference_free(reference);
- must_pass(git_reference_lookup(&reference, repo, packed_test_head_name));
- must_be_true(git_reference_type(reference) & GIT_REF_OID);
- must_be_true(git_reference_is_packed(reference) == 0);
- must_be_true(strcmp(reference->name, packed_test_head_name) == 0);
-
- git_repository_free(repo);
-
- git_reference_free(reference);
-END_TEST
-
-BEGIN_TEST(create0, "create a new symbolic reference")
- git_reference *new_reference, *looked_up_ref, *resolved_ref;
- git_repository *repo, *repo2;
- git_oid id;
- git_buf ref_path = GIT_BUF_INIT;
-
- const char *new_head_tracker = "another-head-tracker";
-
- git_oid_fromstr(&id, current_master_tip);
-
- must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
-
- /* Retrieve the physical path to the symbolic ref for further cleaning */
- must_pass(git_buf_joinpath(&ref_path, repo->path_repository, new_head_tracker));
- git_buf_free(&ref_path);
-
- /* Create and write the new symbolic reference */
- must_pass(git_reference_create_symbolic(&new_reference, repo, new_head_tracker, current_head_target, 0));
-
- /* Ensure the reference can be looked-up... */
- must_pass(git_reference_lookup(&looked_up_ref, repo, new_head_tracker));
- must_be_true(git_reference_type(looked_up_ref) & GIT_REF_SYMBOLIC);
- must_be_true(git_reference_is_packed(looked_up_ref) == 0);
- must_be_true(strcmp(looked_up_ref->name, new_head_tracker) == 0);
-
- /* ...peeled.. */
- must_pass(git_reference_resolve(&resolved_ref, looked_up_ref));
- must_be_true(git_reference_type(resolved_ref) == GIT_REF_OID);
-
- /* ...and that it points to the current master tip */
- must_be_true(git_oid_cmp(&id, git_reference_oid(resolved_ref)) == 0);
- git_reference_free(looked_up_ref);
- git_reference_free(resolved_ref);
-
- git_repository_free(repo);
-
- /* Similar test with a fresh new repository */
- must_pass(git_repository_open(&repo2, TEMP_REPO_FOLDER));
-
- must_pass(git_reference_lookup(&looked_up_ref, repo2, new_head_tracker));
- must_pass(git_reference_resolve(&resolved_ref, looked_up_ref));
- must_be_true(git_oid_cmp(&id, git_reference_oid(resolved_ref)) == 0);
-
- close_temp_repo(repo2);
-
- git_reference_free(new_reference);
- git_reference_free(looked_up_ref);
- git_reference_free(resolved_ref);
-END_TEST
-
-BEGIN_TEST(create1, "create a deep symbolic reference")
- git_reference *new_reference, *looked_up_ref, *resolved_ref;
- git_repository *repo;
- git_oid id;
- git_buf ref_path = GIT_BUF_INIT;
-
- const char *new_head_tracker = "deep/rooted/tracker";
-
- git_oid_fromstr(&id, current_master_tip);
-
- must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
-
- must_pass(git_buf_joinpath(&ref_path, repo->path_repository, new_head_tracker));
- must_pass(git_reference_create_symbolic(&new_reference, repo, new_head_tracker, current_head_target, 0));
- must_pass(git_reference_lookup(&looked_up_ref, repo, new_head_tracker));
- must_pass(git_reference_resolve(&resolved_ref, looked_up_ref));
- must_be_true(git_oid_cmp(&id, git_reference_oid(resolved_ref)) == 0);
-
- close_temp_repo(repo);
-
- git_reference_free(new_reference);
- git_reference_free(looked_up_ref);
- git_reference_free(resolved_ref);
- git_buf_free(&ref_path);
-END_TEST
-
-BEGIN_TEST(create2, "create a new OID reference")
- git_reference *new_reference, *looked_up_ref;
- git_repository *repo, *repo2;
- git_oid id;
- git_buf ref_path = GIT_BUF_INIT;
-
- const char *new_head = "refs/heads/new-head";
-
- git_oid_fromstr(&id, current_master_tip);
-
- must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
-
- /* Retrieve the physical path to the symbolic ref for further cleaning */
- must_pass(git_buf_joinpath(&ref_path, repo->path_repository, new_head));
-
- /* Create and write the new object id reference */
- must_pass(git_reference_create_oid(&new_reference, repo, new_head, &id, 0));
-
- /* Ensure the reference can be looked-up... */
- must_pass(git_reference_lookup(&looked_up_ref, repo, new_head));
- must_be_true(git_reference_type(looked_up_ref) & GIT_REF_OID);
- must_be_true(git_reference_is_packed(looked_up_ref) == 0);
- must_be_true(strcmp(looked_up_ref->name, new_head) == 0);
-
- /* ...and that it points to the current master tip */
- must_be_true(git_oid_cmp(&id, git_reference_oid(looked_up_ref)) == 0);
- git_reference_free(looked_up_ref);
-
- git_repository_free(repo);
-
- /* Similar test with a fresh new repository */
- must_pass(git_repository_open(&repo2, TEMP_REPO_FOLDER));
-
- must_pass(git_reference_lookup(&looked_up_ref, repo2, new_head));
- must_be_true(git_oid_cmp(&id, git_reference_oid(looked_up_ref)) == 0);
-
- close_temp_repo(repo2);
-
- git_reference_free(new_reference);
- git_reference_free(looked_up_ref);
- git_buf_free(&ref_path);
-END_TEST
-
-BEGIN_TEST(create3, "Can not create a new OID reference which targets at an unknown id")
- git_reference *new_reference, *looked_up_ref;
- git_repository *repo;
- git_oid id;
-
- const char *new_head = "refs/heads/new-head";
-
- git_oid_fromstr(&id, "deadbeef3f795b2b4353bcce3a527ad0a4f7f644");
-
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
-
- /* Create and write the new object id reference */
- must_fail(git_reference_create_oid(&new_reference, repo, new_head, &id, 0));
-
- /* Ensure the reference can't be looked-up... */
- must_fail(git_reference_lookup(&looked_up_ref, repo, new_head));
-
- git_repository_free(repo);
-END_TEST
-
-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";
-BEGIN_TEST(overwrite0, "Overwrite an existing symbolic reference")
- git_reference *ref, *branch_ref;
- git_repository *repo;
-
- must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
-
- /* The target needds to exist and we need to check the name has changed */
- must_pass(git_reference_create_symbolic(&branch_ref, repo, ref_branch_name, ref_master_name, 0));
- must_pass(git_reference_create_symbolic(&ref, repo, ref_name, ref_branch_name, 0));
- git_reference_free(ref);
-
- /* Ensure it points to the right place*/
- must_pass(git_reference_lookup(&ref, repo, ref_name));
- must_be_true(git_reference_type(ref) & GIT_REF_SYMBOLIC);
- must_be_true(!strcmp(git_reference_target(ref), ref_branch_name));
- git_reference_free(ref);
-
- /* Ensure we can't create it unless we force it to */
- must_fail(git_reference_create_symbolic(&ref, repo, ref_name, ref_master_name, 0));
- must_pass(git_reference_create_symbolic(&ref, repo, ref_name, ref_master_name, 1));
- git_reference_free(ref);
-
- /* Ensure it points to the right place */
- must_pass(git_reference_lookup(&ref, repo, ref_name));
- must_be_true(git_reference_type(ref) & GIT_REF_SYMBOLIC);
- must_be_true(!strcmp(git_reference_target(ref), ref_master_name));
-
- close_temp_repo(repo);
-
- git_reference_free(ref);
- git_reference_free(branch_ref);
-END_TEST
-
-BEGIN_TEST(overwrite1, "Overwrite an existing object id reference")
- git_reference *ref;
- git_repository *repo;
- git_oid id;
-
- must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
-
- must_pass(git_reference_lookup(&ref, repo, ref_master_name));
- must_be_true(git_reference_type(ref) & GIT_REF_OID);
- git_oid_cpy(&id, git_reference_oid(ref));
- git_reference_free(ref);
-
- /* Create it */
- must_pass(git_reference_create_oid(&ref, repo, ref_name, &id, 0));
- git_reference_free(ref);
-
- must_pass(git_reference_lookup(&ref, repo, ref_test_name));
- must_be_true(git_reference_type(ref) & GIT_REF_OID);
- git_oid_cpy(&id, git_reference_oid(ref));
- git_reference_free(ref);
-
- /* Ensure we can't overwrite unless we force it */
- must_fail(git_reference_create_oid(&ref, repo, ref_name, &id, 0));
- must_pass(git_reference_create_oid(&ref, repo, ref_name, &id, 1));
- git_reference_free(ref);
-
- /* Ensure it has been overwritten */
- must_pass(git_reference_lookup(&ref, repo, ref_name));
- must_be_true(!git_oid_cmp(&id, git_reference_oid(ref)));
-
- close_temp_repo(repo);
-
- git_reference_free(ref);
-END_TEST
-
-BEGIN_TEST(overwrite2, "Overwrite an existing object id reference with a symbolic one")
- git_reference *ref;
- git_repository *repo;
- git_oid id;
-
- must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
-
- must_pass(git_reference_lookup(&ref, repo, ref_master_name));
- must_be_true(git_reference_type(ref) & GIT_REF_OID);
- git_oid_cpy(&id, git_reference_oid(ref));
- git_reference_free(ref);
-
- must_pass(git_reference_create_oid(&ref, repo, ref_name, &id, 0));
- git_reference_free(ref);
- must_fail(git_reference_create_symbolic(&ref, repo, ref_name, ref_master_name, 0));
- must_pass(git_reference_create_symbolic(&ref, repo, ref_name, ref_master_name, 1));
- git_reference_free(ref);
-
- /* Ensure it points to the right place */
- must_pass(git_reference_lookup(&ref, repo, ref_name));
- must_be_true(git_reference_type(ref) & GIT_REF_SYMBOLIC);
- must_be_true(!strcmp(git_reference_target(ref), ref_master_name));
-
- close_temp_repo(repo);
-
- git_reference_free(ref);
-END_TEST
-
-BEGIN_TEST(overwrite3, "Overwrite an existing symbolic reference with an object id one")
- git_reference *ref;
- git_repository *repo;
- git_oid id;
-
- must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
-
- must_pass(git_reference_lookup(&ref, repo, ref_master_name));
- must_be_true(git_reference_type(ref) & GIT_REF_OID);
- git_oid_cpy(&id, git_reference_oid(ref));
- git_reference_free(ref);
-
- /* Create the symbolic ref */
- must_pass(git_reference_create_symbolic(&ref, repo, ref_name, ref_master_name, 0));
- git_reference_free(ref);
- /* It shouldn't overwrite unless we tell it to */
- must_fail(git_reference_create_oid(&ref, repo, ref_name, &id, 0));
- must_pass(git_reference_create_oid(&ref, repo, ref_name, &id, 1));
- git_reference_free(ref);
-
- /* Ensure it points to the right place */
- must_pass(git_reference_lookup(&ref, repo, ref_name));
- must_be_true(git_reference_type(ref) & GIT_REF_OID);
- must_be_true(!git_oid_cmp(git_reference_oid(ref), &id));
-
- close_temp_repo(repo);
-
- git_reference_free(ref);
-END_TEST
-
-BEGIN_TEST(pack0, "create a packfile for an empty folder")
- git_repository *repo;
- git_buf temp_path = GIT_BUF_INIT;
-
- must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
-
- must_pass(git_buf_join_n(&temp_path, '/', 3, repo->path_repository, GIT_REFS_HEADS_DIR, "empty_dir"));
- must_pass(git_futils_mkdir_r(temp_path.ptr, NULL, GIT_REFS_DIR_MODE));
- git_buf_free(&temp_path);
-
- must_pass(git_reference_packall(repo));
-
- close_temp_repo(repo);
-END_TEST
-
-BEGIN_TEST(pack1, "create a packfile from all the loose rn a repo")
- git_repository *repo;
- git_reference *reference;
- git_buf temp_path = GIT_BUF_INIT;
-
- must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
-
- /* Ensure a known loose ref can be looked up */
- must_pass(git_reference_lookup(&reference, repo, loose_tag_ref_name));
- must_be_true(git_reference_is_packed(reference) == 0);
- must_be_true(strcmp(reference->name, loose_tag_ref_name) == 0);
- 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
- */
- must_pass(git_reference_packall(repo));
-
- /* Ensure the packed-refs file exists */
- must_pass(git_buf_joinpath(&temp_path, repo->path_repository, GIT_PACKEDREFS_FILE));
- must_pass(git_path_exists(temp_path.ptr));
-
- /* Ensure the known ref can still be looked up but is now packed */
- must_pass(git_reference_lookup(&reference, repo, loose_tag_ref_name));
- must_be_true(git_reference_is_packed(reference));
- must_be_true(strcmp(reference->name, loose_tag_ref_name) == 0);
-
- /* Ensure the known ref has been removed from the loose folder structure */
- must_pass(git_buf_joinpath(&temp_path, repo->path_repository, loose_tag_ref_name));
- must_pass(!git_path_exists(temp_path.ptr));
-
- close_temp_repo(repo);
-
- git_reference_free(reference);
- git_buf_free(&temp_path);
-END_TEST
-
-BEGIN_TEST(rename0, "rename a loose reference")
- git_reference *looked_up_ref, *another_looked_up_ref;
- git_repository *repo;
- git_buf temp_path = GIT_BUF_INIT;
- const char *new_name = "refs/tags/Nemo/knows/refs.kung-fu";
-
- must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
-
- /* Ensure the ref doesn't exist on the file system */
- must_pass(git_buf_joinpath(&temp_path, repo->path_repository, new_name));
- must_pass(!git_path_exists(temp_path.ptr));
-
- /* Retrieval of the reference to rename */
- must_pass(git_reference_lookup(&looked_up_ref, repo, loose_tag_ref_name));
-
- /* ... which is indeed loose */
- must_be_true(git_reference_is_packed(looked_up_ref) == 0);
-
- /* Now that the reference is renamed... */
- must_pass(git_reference_rename(looked_up_ref, new_name, 0));
- must_be_true(!strcmp(looked_up_ref->name, new_name));
-
- /* ...It can't be looked-up with the old name... */
- must_fail(git_reference_lookup(&another_looked_up_ref, repo, loose_tag_ref_name));
-
- /* ...but the new name works ok... */
- must_pass(git_reference_lookup(&another_looked_up_ref, repo, new_name));
- must_be_true(!strcmp(another_looked_up_ref->name, new_name));
-
- /* .. the ref is still loose... */
- must_be_true(git_reference_is_packed(another_looked_up_ref) == 0);
- must_be_true(git_reference_is_packed(looked_up_ref) == 0);
-
- /* ...and the ref can be found in the file system */
- must_pass(git_buf_joinpath(&temp_path, repo->path_repository, new_name));
- must_pass(git_path_exists(temp_path.ptr));
-
- close_temp_repo(repo);
-
- git_reference_free(looked_up_ref);
- git_reference_free(another_looked_up_ref);
- git_buf_free(&temp_path);
-END_TEST
-
-BEGIN_TEST(rename1, "rename a packed reference (should make it loose)")
- git_reference *looked_up_ref, *another_looked_up_ref;
- git_repository *repo;
- git_buf temp_path = GIT_BUF_INIT;
- const char *brand_new_name = "refs/heads/brand_new_name";
-
- must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
-
- /* Ensure the ref doesn't exist on the file system */
- must_pass(git_buf_joinpath(&temp_path, repo->path_repository, packed_head_name));
- must_pass(!git_path_exists(temp_path.ptr));
-
- /* The reference can however be looked-up... */
- must_pass(git_reference_lookup(&looked_up_ref, repo, packed_head_name));
-
- /* .. and it's packed */
- must_be_true(git_reference_is_packed(looked_up_ref) != 0);
-
- /* Now that the reference is renamed... */
- must_pass(git_reference_rename(looked_up_ref, brand_new_name, 0));
- must_be_true(!strcmp(looked_up_ref->name, brand_new_name));
-
- /* ...It can't be looked-up with the old name... */
- must_fail(git_reference_lookup(&another_looked_up_ref, repo, packed_head_name));
-
- /* ...but the new name works ok... */
- must_pass(git_reference_lookup(&another_looked_up_ref, repo, brand_new_name));
- must_be_true(!strcmp(another_looked_up_ref->name, brand_new_name));
-
- /* .. the ref is no longer packed... */
- must_be_true(git_reference_is_packed(another_looked_up_ref) == 0);
- must_be_true(git_reference_is_packed(looked_up_ref) == 0);
-
- /* ...and the ref now happily lives in the file system */
- must_pass(git_buf_joinpath(&temp_path, repo->path_repository, brand_new_name));
- must_pass(git_path_exists(temp_path.ptr));
-
- close_temp_repo(repo);
-
- git_reference_free(looked_up_ref);
- git_reference_free(another_looked_up_ref);
- git_buf_free(&temp_path);
-END_TEST
-
-BEGIN_TEST(rename2, "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;
- git_repository *repo;
- git_buf temp_path = GIT_BUF_INIT;
- const char *brand_new_name = "refs/heads/brand_new_name";
-
- must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
-
- /* Ensure the other reference exists on the file system */
- must_pass(git_buf_joinpath(&temp_path, repo->path_repository, packed_test_head_name));
- must_pass(git_path_exists(temp_path.ptr));
-
- /* Lookup the other reference */
- must_pass(git_reference_lookup(&another_looked_up_ref, repo, packed_test_head_name));
-
- /* Ensure it's loose */
- must_be_true(git_reference_is_packed(another_looked_up_ref) == 0);
- git_reference_free(another_looked_up_ref);
-
- /* Lookup the reference to rename */
- must_pass(git_reference_lookup(&looked_up_ref, repo, packed_head_name));
-
- /* Ensure it's packed */
- must_be_true(git_reference_is_packed(looked_up_ref) != 0);
-
- /* Now that the reference is renamed... */
- must_pass(git_reference_rename(looked_up_ref, brand_new_name, 0));
-
- /* Lookup the other reference */
- must_pass(git_reference_lookup(&another_looked_up_ref, repo, packed_test_head_name));
-
- /* Ensure it's loose */
- must_be_true(git_reference_is_packed(another_looked_up_ref) == 0);
-
- /* Ensure the other ref still exists on the file system */
- must_pass(git_path_exists(temp_path.ptr));
-
- close_temp_repo(repo);
-
- git_reference_free(looked_up_ref);
- git_reference_free(another_looked_up_ref);
- git_buf_free(&temp_path);
-END_TEST
-
-BEGIN_TEST(rename3, "can not rename a reference with the name of an existing reference")
- git_reference *looked_up_ref;
- git_repository *repo;
-
- must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
-
- /* An existing reference... */
- must_pass(git_reference_lookup(&looked_up_ref, repo, packed_head_name));
-
- /* Can not be renamed to the name of another existing reference. */
- must_fail(git_reference_rename(looked_up_ref, packed_test_head_name, 0));
- git_reference_free(looked_up_ref);
-
- /* Failure to rename it hasn't corrupted its state */
- must_pass(git_reference_lookup(&looked_up_ref, repo, packed_head_name));
- must_be_true(!strcmp(looked_up_ref->name, packed_head_name));
-
- close_temp_repo(repo);
-
- git_reference_free(looked_up_ref);
-END_TEST
-
-BEGIN_TEST(rename4, "can not rename a reference with an invalid name")
- git_reference *looked_up_ref;
- git_repository *repo;
-
- must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
-
- /* An existing oid reference... */
- must_pass(git_reference_lookup(&looked_up_ref, repo, packed_test_head_name));
-
- /* Can not be renamed with an invalid name. */
- must_fail(git_reference_rename(looked_up_ref, "Hello! I'm a very invalid name.", 0));
-
- /* Can not be renamed outside of the refs hierarchy. */
- must_fail(git_reference_rename(looked_up_ref, "i-will-sudo-you", 0));
-
- /* Failure to rename it hasn't corrupted its state */
- git_reference_free(looked_up_ref);
- must_pass(git_reference_lookup(&looked_up_ref, repo, packed_test_head_name));
- must_be_true(!strcmp(looked_up_ref->name, packed_test_head_name));
-
- close_temp_repo(repo);
-
- git_reference_free(looked_up_ref);
-END_TEST
-
-BEGIN_TEST(rename5, "can force-rename a packed reference with the name of an existing loose and packed reference")
- git_reference *looked_up_ref;
- git_repository *repo;
- git_oid oid;
-
- must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
-
- /* An existing reference... */
- must_pass(git_reference_lookup(&looked_up_ref, repo, packed_head_name));
- git_oid_cpy(&oid, git_reference_oid(looked_up_ref));
-
- /* Can be force-renamed to the name of another existing reference. */
- must_pass(git_reference_rename(looked_up_ref, packed_test_head_name, 1));
- git_reference_free(looked_up_ref);
-
- /* Check we actually renamed it */
- must_pass(git_reference_lookup(&looked_up_ref, repo, packed_test_head_name));
- must_be_true(!strcmp(looked_up_ref->name, packed_test_head_name));
- must_be_true(!git_oid_cmp(&oid, git_reference_oid(looked_up_ref)));
- git_reference_free(looked_up_ref);
-
- /* And that the previous one doesn't exist any longer */
- must_fail(git_reference_lookup(&looked_up_ref, repo, packed_head_name));
-
- close_temp_repo(repo);
-END_TEST
-
-BEGIN_TEST(rename6, "can force-rename a loose reference with the name of an existing loose reference")
- git_reference *looked_up_ref;
- git_repository *repo;
- git_oid oid;
-
- must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
-
- /* An existing reference... */
- must_pass(git_reference_lookup(&looked_up_ref, repo, "refs/heads/br2"));
- git_oid_cpy(&oid, git_reference_oid(looked_up_ref));
-
- /* Can be force-renamed to the name of another existing reference. */
-must_pass(git_reference_rename(looked_up_ref, "refs/heads/test", 1));
- git_reference_free(looked_up_ref);
-
- /* Check we actually renamed it */
- must_pass(git_reference_lookup(&looked_up_ref, repo, "refs/heads/test"));
- must_be_true(!strcmp(looked_up_ref->name, "refs/heads/test"));
- must_be_true(!git_oid_cmp(&oid, git_reference_oid(looked_up_ref)));
- git_reference_free(looked_up_ref);
-
- /* And that the previous one doesn't exist any longer */
- must_fail(git_reference_lookup(&looked_up_ref, repo, "refs/heads/br2"));
-
- close_temp_repo(repo);
-
- git_reference_free(looked_up_ref);
-END_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";
-
-BEGIN_TEST(rename7, "can not overwrite name of existing reference")
- git_reference *ref, *ref_one, *ref_one_new, *ref_two;
- git_repository *repo;
- git_oid id;
-
- must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
-
- must_pass(git_reference_lookup(&ref, repo, ref_master_name));
- must_be_true(git_reference_type(ref) & GIT_REF_OID);
-
- git_oid_cpy(&id, git_reference_oid(ref));
-
- /* Create loose references */
- must_pass(git_reference_create_oid(&ref_one, repo, ref_one_name, &id, 0));
- must_pass(git_reference_create_oid(&ref_two, repo, ref_two_name, &id, 0));
-
- /* Pack everything */
- must_pass(git_reference_packall(repo));
-
- /* Attempt to create illegal reference */
- must_fail(git_reference_create_oid(&ref_one_new, repo, ref_one_name_new, &id, 0));
-
- /* Illegal reference couldn't be created so this is supposed to fail */
- must_fail(git_reference_lookup(&ref_one_new, repo, ref_one_name_new));
-
- close_temp_repo(repo);
-
- git_reference_free(ref);
- git_reference_free(ref_one);
- git_reference_free(ref_one_new);
- git_reference_free(ref_two);
-END_TEST
-
-static const char *ref_two_name_new = "refs/heads/two/two";
-
-BEGIN_TEST(rename8, "can be renamed to a new name prefixed with the old name")
- git_reference *ref, *ref_two, *looked_up_ref;
- git_repository *repo;
- git_oid id;
-
- must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
-
- must_pass(git_reference_lookup(&ref, repo, ref_master_name));
- must_be_true(git_reference_type(ref) & GIT_REF_OID);
-
- git_oid_cpy(&id, git_reference_oid(ref));
-
- /* Create loose references */
- must_pass(git_reference_create_oid(&ref_two, repo, ref_two_name, &id, 0));
-
- /* An existing reference... */
- must_pass(git_reference_lookup(&looked_up_ref, repo, ref_two_name));
-
- /* Can be rename to a new name starting with the old name. */
- must_pass(git_reference_rename(looked_up_ref, ref_two_name_new, 0));
- git_reference_free(looked_up_ref);
-
- /* Check we actually renamed it */
- must_pass(git_reference_lookup(&looked_up_ref, repo, ref_two_name_new));
- must_be_true(!strcmp(looked_up_ref->name, ref_two_name_new));
- git_reference_free(looked_up_ref);
- must_fail(git_reference_lookup(&looked_up_ref, repo, ref_two_name));
-
- close_temp_repo(repo);
-
- git_reference_free(ref);
- git_reference_free(ref_two);
- git_reference_free(looked_up_ref);
-END_TEST
-
-BEGIN_TEST(rename9, "can move a reference to a upper reference hierarchy")
- git_reference *ref, *ref_two, *looked_up_ref;
- git_repository *repo;
- git_oid id;
-
- must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
-
- must_pass(git_reference_lookup(&ref, repo, ref_master_name));
- must_be_true(git_reference_type(ref) & GIT_REF_OID);
-
- git_oid_cpy(&id, git_reference_oid(ref));
-
- /* Create loose references */
- must_pass(git_reference_create_oid(&ref_two, repo, ref_two_name_new, &id, 0));
- git_reference_free(ref_two);
-
- /* An existing reference... */
- must_pass(git_reference_lookup(&looked_up_ref, repo, ref_two_name_new));
-
- /* Can be renamed upward the reference tree. */
- must_pass(git_reference_rename(looked_up_ref, ref_two_name, 0));
- git_reference_free(looked_up_ref);
-
- /* Check we actually renamed it */
- must_pass(git_reference_lookup(&looked_up_ref, repo, ref_two_name));
- must_be_true(!strcmp(looked_up_ref->name, ref_two_name));
- git_reference_free(looked_up_ref);
- must_fail(git_reference_lookup(&looked_up_ref, repo, ref_two_name_new));
- git_reference_free(ref);
- git_reference_free(looked_up_ref);
-
- close_temp_repo(repo);
-END_TEST
-
-BEGIN_TEST(delete0, "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_repository *repo;
- git_buf temp_path = GIT_BUF_INIT;
-
- must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
-
- /* Ensure the loose reference exists on the file system */
- must_pass(git_buf_joinpath(&temp_path, repo->path_repository, packed_test_head_name));
- must_pass(git_path_exists(temp_path.ptr));
-
- /* Lookup the reference */
- must_pass(git_reference_lookup(&looked_up_ref, repo, packed_test_head_name));
-
- /* Ensure it's the loose version that has been found */
- must_be_true(git_reference_is_packed(looked_up_ref) == 0);
-
- /* Now that the reference is deleted... */
- must_pass(git_reference_delete(looked_up_ref));
-
- /* Looking up the reference once again should not retrieve it */
- must_fail(git_reference_lookup(&another_looked_up_ref, repo, packed_test_head_name));
-
- /* Ensure the loose reference doesn't exist any longer on the file system */
- must_pass(!git_path_exists(temp_path.ptr));
-
- close_temp_repo(repo);
-
- git_reference_free(another_looked_up_ref);
- git_buf_free(&temp_path);
-END_TEST
-
-BEGIN_TEST(delete1, "can delete a just packed reference")
- git_reference *ref;
- git_repository *repo;
- git_oid id;
- const char *new_ref = "refs/heads/new_ref";
-
- git_oid_fromstr(&id, current_master_tip);
-
- must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
-
- /* Create and write the new object id reference */
- must_pass(git_reference_create_oid(&ref, repo, new_ref, &id, 0));
- git_reference_free(ref);
-
- /* Lookup the reference */
- must_pass(git_reference_lookup(&ref, repo, new_ref));
-
- /* Ensure it's a loose reference */
- must_be_true(git_reference_is_packed(ref) == 0);
-
- /* Pack all existing references */
- must_pass(git_reference_packall(repo));
-
- /* Reload the reference from disk */
- must_pass(git_reference_reload(ref));
-
- /* Ensure it's a packed reference */
- must_be_true(git_reference_is_packed(ref) == 1);
-
- /* This should pass */
- must_pass(git_reference_delete(ref));
-
- close_temp_repo(repo);
-END_TEST
-
-static int ensure_refname_normalized(int is_oid_ref, const char *input_refname, const char *expected_refname)
-{
- int error = GIT_SUCCESS;
- char buffer_out[GIT_REFNAME_MAX];
-
- if (is_oid_ref)
- error = git_reference__normalize_name_oid(buffer_out, sizeof(buffer_out), input_refname);
- else
- error = git_reference__normalize_name(buffer_out, sizeof(buffer_out), input_refname);
-
- if (error < GIT_SUCCESS)
- return error;
-
- if (expected_refname == NULL)
- return error;
-
- if (strcmp(buffer_out, expected_refname))
- error = GIT_ERROR;
-
- return error;
-}
-
-#define OID_REF 1
-#define SYM_REF 0
-
-BEGIN_TEST(normalize0, "normalize a direct (OID) reference name")
- must_fail(ensure_refname_normalized(OID_REF, "a", NULL));
- must_fail(ensure_refname_normalized(OID_REF, "", NULL));
- must_fail(ensure_refname_normalized(OID_REF, "refs/heads/a/", NULL));
- must_fail(ensure_refname_normalized(OID_REF, "refs/heads/a.", NULL));
- must_fail(ensure_refname_normalized(OID_REF, "refs/heads/a.lock", NULL));
- must_pass(ensure_refname_normalized(OID_REF, "refs/dummy/a", NULL));
- must_pass(ensure_refname_normalized(OID_REF, "refs/stash", NULL));
- must_pass(ensure_refname_normalized(OID_REF, "refs/tags/a", "refs/tags/a"));
- must_pass(ensure_refname_normalized(OID_REF, "refs/heads/a/b", "refs/heads/a/b"));
- must_pass(ensure_refname_normalized(OID_REF, "refs/heads/a./b", "refs/heads/a./b"));
- must_fail(ensure_refname_normalized(OID_REF, "refs/heads/foo?bar", NULL));
- must_fail(ensure_refname_normalized(OID_REF, "refs/heads\foo", NULL));
- must_pass(ensure_refname_normalized(OID_REF, "refs/heads/v@ation", "refs/heads/v@ation"));
- must_pass(ensure_refname_normalized(OID_REF, "refs///heads///a", "refs/heads/a"));
- must_fail(ensure_refname_normalized(OID_REF, "refs/heads/.a/b", NULL));
- must_fail(ensure_refname_normalized(OID_REF, "refs/heads/foo/../bar", NULL));
- must_fail(ensure_refname_normalized(OID_REF, "refs/heads/foo..bar", NULL));
- must_fail(ensure_refname_normalized(OID_REF, "refs/heads/./foo", NULL));
- must_fail(ensure_refname_normalized(OID_REF, "refs/heads/v@{ation", NULL));
-END_TEST
-
-BEGIN_TEST(normalize1, "normalize a symbolic reference name")
- must_pass(ensure_refname_normalized(SYM_REF, "a", "a"));
- must_pass(ensure_refname_normalized(SYM_REF, "a/b", "a/b"));
- must_pass(ensure_refname_normalized(SYM_REF, "refs///heads///a", "refs/heads/a"));
- must_fail(ensure_refname_normalized(SYM_REF, "", NULL));
- must_fail(ensure_refname_normalized(SYM_REF, "heads\foo", NULL));
-END_TEST
-
-/* Ported from JGit, BSD licence.
- * See https://github.com/spearce/JGit/commit/e4bf8f6957bbb29362575d641d1e77a02d906739 */
-BEGIN_TEST(normalize2, "tests borrowed from JGit")
-
-/* EmptyString */
- must_fail(ensure_refname_normalized(SYM_REF, "", NULL));
- must_fail(ensure_refname_normalized(SYM_REF, "/", NULL));
-
-/* MustHaveTwoComponents */
- must_fail(ensure_refname_normalized(OID_REF, "master", NULL));
- must_pass(ensure_refname_normalized(SYM_REF, "heads/master", "heads/master"));
-
-/* ValidHead */
-
- must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/master", "refs/heads/master"));
- must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/pu", "refs/heads/pu"));
- must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/z", "refs/heads/z"));
- must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/FoO", "refs/heads/FoO"));
-
-/* ValidTag */
- must_pass(ensure_refname_normalized(SYM_REF, "refs/tags/v1.0", "refs/tags/v1.0"));
-
-/* NoLockSuffix */
- must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master.lock", NULL));
-
-/* NoDirectorySuffix */
- must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master/", NULL));
-
-/* NoSpace */
- must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/i haz space", NULL));
-
-/* 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';
- must_fail(ensure_refname_normalized(SYM_REF, buffer, NULL));
- }
- }
-
-/* NoBareDot */
- must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/.", NULL));
- must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/..", NULL));
- must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/./master", NULL));
- must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/../master", NULL));
-
-/* NoLeadingOrTrailingDot */
- must_fail(ensure_refname_normalized(SYM_REF, ".", NULL));
- must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/.bar", NULL));
- must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/..bar", NULL));
- must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/bar.", NULL));
-
-/* ContainsDot */
- must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/m.a.s.t.e.r", "refs/heads/m.a.s.t.e.r"));
- must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master..pu", NULL));
-
-/* NoMagicRefCharacters */
- must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master^", NULL));
- must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/^master", NULL));
- must_fail(ensure_refname_normalized(SYM_REF, "^refs/heads/master", NULL));
-
- must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master~", NULL));
- must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/~master", NULL));
- must_fail(ensure_refname_normalized(SYM_REF, "~refs/heads/master", NULL));
-
- must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master:", NULL));
- must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/:master", NULL));
- must_fail(ensure_refname_normalized(SYM_REF, ":refs/heads/master", NULL));
-
-/* ShellGlob */
- must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master?", NULL));
- must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/?master", NULL));
- must_fail(ensure_refname_normalized(SYM_REF, "?refs/heads/master", NULL));
-
- must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master[", NULL));
- must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/[master", NULL));
- must_fail(ensure_refname_normalized(SYM_REF, "[refs/heads/master", NULL));
-
- must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master*", NULL));
- must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/*master", NULL));
- must_fail(ensure_refname_normalized(SYM_REF, "*refs/heads/master", NULL));
-
-/* ValidSpecialCharacters */
- must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/!", "refs/heads/!"));
- must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/\"", "refs/heads/\""));
- must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/#", "refs/heads/#"));
- must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/$", "refs/heads/$"));
- must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/%", "refs/heads/%"));
- must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/&", "refs/heads/&"));
- must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/'", "refs/heads/'"));
- must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/(", "refs/heads/("));
- must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/)", "refs/heads/)"));
- must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/+", "refs/heads/+"));
- must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/,", "refs/heads/,"));
- must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/-", "refs/heads/-"));
- must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/;", "refs/heads/;"));
- must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/<", "refs/heads/<"));
- must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/=", "refs/heads/="));
- must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/>", "refs/heads/>"));
- must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/@", "refs/heads/@"));
- must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/]", "refs/heads/]"));
- must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/_", "refs/heads/_"));
- must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/`", "refs/heads/`"));
- must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/{", "refs/heads/{"));
- must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/|", "refs/heads/|"));
- must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/}", "refs/heads/}"));
-
- // This is valid on UNIX, but not on Windows
- // hence we make in invalid due to non-portability
- //
- must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/\\", NULL));
-
-/* UnicodeNames */
- /*
- * Currently this fails.
- * must_pass(ensure_refname_normalized(SYM_REF, "refs/heads/\u00e5ngstr\u00f6m", "refs/heads/\u00e5ngstr\u00f6m"));
- */
-
-/* RefLogQueryIsValidRef */
- must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master@{1}", NULL));
- must_fail(ensure_refname_normalized(SYM_REF, "refs/heads/master@{1.hour.ago}", NULL));
-END_TEST
-
-BEGIN_TEST(list0, "try to list all the references in our test repo")
- git_repository *repo;
- git_strarray ref_list;
-
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
- must_pass(git_reference_listall(&ref_list, repo, GIT_REF_LISTALL));
-
- /*{
- unsigned short i;
- for (i = 0; i < ref_list.count; ++i)
- printf("# %s\n", ref_list.strings[i]);
- }*/
-
- /* We have exactly 9 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 */
- must_be_true(ref_list.count == 9);
-
- git_strarray_free(&ref_list);
- git_repository_free(repo);
-END_TEST
-
-BEGIN_TEST(list1, "try to list only the symbolic references")
- git_repository *repo;
- git_strarray ref_list;
-
- must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
- must_pass(git_reference_listall(&ref_list, repo, GIT_REF_SYMBOLIC));
- must_be_true(ref_list.count == 0); /* no symrefs in the test repo */
-
- git_strarray_free(&ref_list);
- git_repository_free(repo);
-END_TEST
-
-static const char *new_ref = "refs/heads/test-reflog";
-#define commit_msg "commit: bla bla"
-
-static int assert_signature(git_signature *expected, git_signature *actual)
-{
- if (actual == NULL)
- return GIT_ERROR;
-
- if (strcmp(expected->name, actual->name) != 0)
- return GIT_ERROR;
-
- if (strcmp(expected->email, actual->email) != 0)
- return GIT_ERROR;
-
- if (expected->when.offset != actual->when.offset)
- return GIT_ERROR;
-
- if (expected->when.time != actual->when.time)
- return GIT_ERROR;
-
- return GIT_SUCCESS;
-}
-
-BEGIN_TEST(reflog0, "write a reflog for a given reference and ensure it can be read back")
- git_repository *repo, *repo2;
- git_reference *ref, *lookedup_ref;
- git_oid oid;
- git_signature *committer;
- git_reflog *reflog;
- git_reflog_entry *entry;
- char oid_str[GIT_OID_HEXSZ+1];
-
- must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
-
- /* Create a new branch pointing at the HEAD */
- git_oid_fromstr(&oid, current_master_tip);
- must_pass(git_reference_create_oid(&ref, repo, new_ref, &oid, 0));
- git_reference_free(ref);
- must_pass(git_reference_lookup(&ref, repo, new_ref));
-
- must_pass(git_signature_now(&committer, "foo", "foo@bar"));
-
- must_pass(git_reflog_write(ref, NULL, committer, NULL));
- must_fail(git_reflog_write(ref, NULL, committer, "no ancestor NULL for an existing reflog"));
- must_fail(git_reflog_write(ref, NULL, committer, "no\nnewline"));
- must_pass(git_reflog_write(ref, &oid, committer, commit_msg));
-
- git_repository_free(repo);
-
- /* Reopen a new instance of the repository */
- must_pass(git_repository_open(&repo2, TEMP_REPO_FOLDER));
-
- /* Lookup the preivously created branch */
- must_pass(git_reference_lookup(&lookedup_ref, repo2, new_ref));
-
- /* Read and parse the reflog for this branch */
- must_pass(git_reflog_read(&reflog, lookedup_ref));
- must_be_true(reflog->entries.length == 2);
-
- entry = (git_reflog_entry *)git_vector_get(&reflog->entries, 0);
- must_pass(assert_signature(committer, entry->committer));
- git_oid_to_string(oid_str, GIT_OID_HEXSZ+1, &entry->oid_old);
- must_be_true(strcmp("0000000000000000000000000000000000000000", oid_str) == 0);
- git_oid_to_string(oid_str, GIT_OID_HEXSZ+1, &entry->oid_cur);
- must_be_true(strcmp(current_master_tip, oid_str) == 0);
- must_be_true(entry->msg == NULL);
-
- entry = (git_reflog_entry *)git_vector_get(&reflog->entries, 1);
- must_pass(assert_signature(committer, entry->committer));
- git_oid_to_string(oid_str, GIT_OID_HEXSZ+1, &entry->oid_old);
- must_be_true(strcmp(current_master_tip, oid_str) == 0);
- git_oid_to_string(oid_str, GIT_OID_HEXSZ+1, &entry->oid_cur);
- must_be_true(strcmp(current_master_tip, oid_str) == 0);
- must_be_true(strcmp(commit_msg, entry->msg) == 0);
-
- git_signature_free(committer);
- git_reflog_free(reflog);
- close_temp_repo(repo2);
-
- git_reference_free(ref);
- git_reference_free(lookedup_ref);
-END_TEST
-
-BEGIN_TEST(reflog1, "avoid writing an obviously wrong reflog")
- git_repository *repo;
- git_reference *ref;
- git_oid oid;
- git_signature *committer;
-
- must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
-
- /* Create a new branch pointing at the HEAD */
- git_oid_fromstr(&oid, current_master_tip);
- must_pass(git_reference_create_oid(&ref, repo, new_ref, &oid, 0));
- git_reference_free(ref);
- must_pass(git_reference_lookup(&ref, repo, new_ref));
-
- must_pass(git_signature_now(&committer, "foo", "foo@bar"));
-
- /* Write the reflog for the new branch */
- must_pass(git_reflog_write(ref, NULL, committer, NULL));
-
- /* Try to update the reflog with wrong information:
- * It's no new reference, so the ancestor OID cannot
- * be NULL. */
- must_fail(git_reflog_write(ref, NULL, committer, NULL));
-
- git_signature_free(committer);
-
- close_temp_repo(repo);
-
- git_reference_free(ref);
-END_TEST
-
-BEGIN_SUITE(refs)
- ADD_TEST(readtag0);
- ADD_TEST(readtag1);
-
- ADD_TEST(readsym0);
- ADD_TEST(readsym1);
- ADD_TEST(readsym2);
- ADD_TEST(readsym3);
-
- ADD_TEST(readpacked0);
- ADD_TEST(readpacked1);
-
- ADD_TEST(create0);
- ADD_TEST(create1);
- ADD_TEST(create2);
- ADD_TEST(create3);
-
- ADD_TEST(overwrite0);
- ADD_TEST(overwrite1);
- ADD_TEST(overwrite2);
- ADD_TEST(overwrite3);
-
- ADD_TEST(normalize0);
- ADD_TEST(normalize1);
- ADD_TEST(normalize2);
-
- ADD_TEST(pack0);
- ADD_TEST(pack1);
-
- ADD_TEST(rename0);
- ADD_TEST(rename1);
- ADD_TEST(rename2);
- ADD_TEST(rename3);
- ADD_TEST(rename4);
- ADD_TEST(rename5);
- ADD_TEST(rename6);
- ADD_TEST(rename7);
- ADD_TEST(rename8);
- ADD_TEST(rename9);
-
- ADD_TEST(delete0);
- ADD_TEST(delete1);
-
- ADD_TEST(list0);
- ADD_TEST(list1);
-
- ADD_TEST(reflog0);
- ADD_TEST(reflog1);
-END_SUITE
diff --git a/tests/t12-repo.c b/tests/t12-repo.c
deleted file mode 100644
index 6a080ecb3..000000000
--- a/tests/t12-repo.c
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * This file is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2,
- * as published by the Free Software Foundation.
- *
- * In addition to the permissions in the GNU General Public License,
- * the authors give you unlimited permission to link the compiled
- * version of this file into combinations with other programs,
- * and to distribute those combinations without any restriction
- * coming from the use of this file. (The General Public License
- * restrictions do apply in other respects; for example, they cover
- * modification of the file, and distribution when not linked into
- * a combined executable.)
- *
- * This file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-#include "test_lib.h"
-#include "test_helpers.h"
-
-#include "odb.h"
-#include "git2/odb_backend.h"
-#include "repository.h"
-
-#define EMPTY_BARE_REPOSITORY_FOLDER TEST_RESOURCES "/empty_bare.git/"
-
-#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 int ensure_repository_discover(const char *start_path, const char *ceiling_dirs, const char *expected_path)
-{
- int error;
- char found_path[GIT_PATH_MAX];
-
- error = 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
-
- if (error < GIT_SUCCESS)
- return error;
-
- return strcmp(found_path, expected_path) ? GIT_ERROR : GIT_SUCCESS;
-}
-
-static int write_file(const char *path, const char *content)
-{
- int error;
- git_file file;
-
- if (git_path_exists(path) == GIT_SUCCESS) {
- error = p_unlink(path);
-
- if (error < GIT_SUCCESS)
- return error;
- }
-
- file = git_futils_creat_withpath(path, 0777, 0666);
- if (file < GIT_SUCCESS)
- return file;
-
- error = p_write(file, content, strlen(content) * sizeof(char));
-
- p_close(file);
-
- return error;
-}
-
-//no check is performed on ceiling_dirs length, so be sure it's long enough
-static int append_ceiling_dir(git_buf *ceiling_dirs, const char *path)
-{
- git_buf pretty_path = GIT_BUF_INIT;
- int error;
- char ceiling_separator[2] = { GIT_PATH_LIST_SEPARATOR, '\0' };
-
- error = git_path_prettify_dir(&pretty_path, path, NULL);
- if (error < GIT_SUCCESS)
- return git__rethrow(error, "Failed to append ceiling directory.");
-
- 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);
-
- return git_buf_lasterror(ceiling_dirs);
-}
-
-BEGIN_TEST(discover0, "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);
- must_pass(append_ceiling_dir(&ceiling_dirs_buf, TEMP_REPO_FOLDER));
- ceiling_dirs = git_buf_cstr(&ceiling_dirs_buf);
-
- must_be_true(git_repository_discover(repository_path, sizeof(repository_path), DISCOVER_FOLDER, 0, ceiling_dirs) == GIT_ENOTAREPO);
-
- must_pass(git_repository_init(&repo, DISCOVER_FOLDER, 1));
- must_pass(git_repository_discover(repository_path, sizeof(repository_path), DISCOVER_FOLDER, 0, ceiling_dirs));
- git_repository_free(repo);
-
- must_pass(git_repository_init(&repo, SUB_REPOSITORY_FOLDER, 0));
- must_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, NULL, mode));
- must_pass(git_repository_discover(sub_repository_path, sizeof(sub_repository_path), SUB_REPOSITORY_FOLDER, 0, ceiling_dirs));
-
- must_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, NULL, mode));
- must_pass(ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB, ceiling_dirs, sub_repository_path));
- must_pass(ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB_SUB, ceiling_dirs, sub_repository_path));
- must_pass(ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, ceiling_dirs, sub_repository_path));
-
- must_pass(git_futils_mkdir_r(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, NULL, mode));
- must_pass(write_file(REPOSITORY_ALTERNATE_FOLDER "/" DOT_GIT, "gitdir: ../" SUB_REPOSITORY_FOLDER_NAME "/" DOT_GIT));
- must_pass(write_file(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB "/" DOT_GIT, "gitdir: ../../../" SUB_REPOSITORY_FOLDER_NAME "/" DOT_GIT));
- must_pass(write_file(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB "/" DOT_GIT, "gitdir: ../../../../"));
- must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER, ceiling_dirs, sub_repository_path));
- must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB, ceiling_dirs, sub_repository_path));
- must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB, ceiling_dirs, sub_repository_path));
- must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, ceiling_dirs, repository_path));
-
- must_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER1, NULL, mode));
- must_pass(write_file(ALTERNATE_MALFORMED_FOLDER1 "/" DOT_GIT, "Anything but not gitdir:"));
- must_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER2, NULL, mode));
- must_pass(write_file(ALTERNATE_MALFORMED_FOLDER2 "/" DOT_GIT, "gitdir:"));
- must_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER3, NULL, mode));
- must_pass(write_file(ALTERNATE_MALFORMED_FOLDER3 "/" DOT_GIT, "gitdir: \n\n\n"));
- must_pass(git_futils_mkdir_r(ALTERNATE_NOT_FOUND_FOLDER, NULL, mode));
- must_pass(write_file(ALTERNATE_NOT_FOUND_FOLDER "/" DOT_GIT, "gitdir: a_repository_that_surely_does_not_exist"));
- must_fail(git_repository_discover(found_path, sizeof(found_path), ALTERNATE_MALFORMED_FOLDER1, 0, ceiling_dirs));
- must_fail(git_repository_discover(found_path, sizeof(found_path), ALTERNATE_MALFORMED_FOLDER2, 0, ceiling_dirs));
- must_fail(git_repository_discover(found_path, sizeof(found_path), ALTERNATE_MALFORMED_FOLDER3, 0, ceiling_dirs));
- must_fail(git_repository_discover(found_path, sizeof(found_path), ALTERNATE_NOT_FOUND_FOLDER, 0, ceiling_dirs));
-
- must_pass(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
- must_pass(git_repository_discover(found_path, sizeof(found_path), SUB_REPOSITORY_FOLDER, 0, ceiling_dirs));
- must_fail(git_repository_discover(found_path, sizeof(found_path), SUB_REPOSITORY_FOLDER_SUB, 0, ceiling_dirs));
- must_fail(git_repository_discover(found_path, sizeof(found_path), SUB_REPOSITORY_FOLDER_SUB_SUB, 0, ceiling_dirs));
- must_fail(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
- must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER, ceiling_dirs, sub_repository_path));
- must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB, ceiling_dirs, sub_repository_path));
- must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB, ceiling_dirs, sub_repository_path));
- must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, ceiling_dirs, repository_path));
-
- must_pass(git_futils_rmdir_r(TEMP_REPO_FOLDER, 1));
- git_repository_free(repo);
- git_buf_free(&ceiling_dirs_buf);
-END_TEST
-
-BEGIN_SUITE(repository)
- ADD_TEST(discover0);
-END_SUITE
-
diff --git a/tests/t13-threads.c b/tests/t13-threads.c
deleted file mode 100644
index 3888b70ce..000000000
--- a/tests/t13-threads.c
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * This file is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2,
- * as published by the Free Software Foundation.
- *
- * In addition to the permissions in the GNU General Public License,
- * the authors give you unlimited permission to link the compiled
- * version of this file into combinations with other programs,
- * and to distribute those combinations without any restriction
- * coming from the use of this file. (The General Public License
- * restrictions do apply in other respects; for example, they cover
- * modification of the file, and distribution when not linked into
- * a combined executable.)
- *
- * This file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-#include "test_lib.h"
-#include "test_helpers.h"
-#include "cache.h"
-
-
-typedef struct {
- git_cached_obj cached;
- unsigned int __dummy;
-} ttest_obj;
-
-BEGIN_TEST(cache0, "run several threads polling the cache at the same time")
-
-END_TEST
-
-BEGIN_SUITE(threads)
- ADD_TEST(cache0);
-END_SUITE
diff --git a/tests/t17-bufs.c b/tests/t17-bufs.c
deleted file mode 100644
index 2cbd8c87a..000000000
--- a/tests/t17-bufs.c
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * This file is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2,
- * as published by the Free Software Foundation.
- *
- * In addition to the permissions in the GNU General Public License,
- * the authors give you unlimited permission to link the compiled
- * version of this file into combinations with other programs,
- * and to distribute those combinations without any restriction
- * coming from the use of this file. (The General Public License
- * restrictions do apply in other respects; for example, they cover
- * modification of the file, and distribution when not linked into
- * a combined executable.)
- *
- * This file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-#include "test_lib.h"
-#include "test_helpers.h"
-
-#include <git2.h>
-#include "buffer.h"
-
-const char *test_string = "Have you seen that? Have you seeeen that??";
-
-BEGIN_TEST(buf0, "check that resizing works properly")
- git_buf buf = GIT_BUF_INIT;
- git_buf_puts(&buf, test_string);
-
- must_be_true(git_buf_oom(&buf) == 0);
- must_be_true(strcmp(git_buf_cstr(&buf), test_string) == 0);
-
- git_buf_puts(&buf, test_string);
- must_be_true(strlen(git_buf_cstr(&buf)) == strlen(test_string) * 2);
- git_buf_free(&buf);
-END_TEST
-
-BEGIN_TEST(buf1, "check that printf works properly")
- git_buf buf = GIT_BUF_INIT;
-
- git_buf_printf(&buf, "%s %s %d ", "shoop", "da", 23);
- must_be_true(git_buf_oom(&buf) == 0);
- must_be_true(strcmp(git_buf_cstr(&buf), "shoop da 23 ") == 0);
-
- git_buf_printf(&buf, "%s %d", "woop", 42);
- must_be_true(git_buf_oom(&buf) == 0);
- must_be_true(strcmp(git_buf_cstr(&buf), "shoop da 23 woop 42") == 0);
- git_buf_free(&buf);
-END_TEST
-
-BEGIN_SUITE(buffers)
- ADD_TEST(buf0)
- ADD_TEST(buf1)
-END_SUITE
diff --git a/tests/t18-status.c b/tests/t18-status.c
deleted file mode 100644
index 270aa7b46..000000000
--- a/tests/t18-status.c
+++ /dev/null
@@ -1,455 +0,0 @@
-/*
- * This file is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2,
- * as published by the Free Software Foundation.
- *
- * In addition to the permissions in the GNU General Public License,
- * the authors give you unlimited permission to link the compiled
- * version of this file into combinations with other programs,
- * and to distribute those combinations without any restriction
- * coming from the use of this file. (The General Public License
- * restrictions do apply in other respects; for example, they cover
- * modification of the file, and distribution when not linked into
- * a combined executable.)
- *
- * This file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-#include "test_lib.h"
-#include "test_helpers.h"
-#include "fileops.h"
-#include "git2/status.h"
-
-static const char *test_blob_oid = "d4fa8600b4f37d7516bef4816ae2c64dbf029e3a";
-
-#define STATUS_WORKDIR_FOLDER TEST_RESOURCES "/status/"
-#define STATUS_REPOSITORY_TEMP_FOLDER TEMP_REPO_FOLDER ".gitted/"
-
-static int file_create(const char *filename, const char *content)
-{
- int fd;
-
- fd = p_creat(filename, 0666);
- if (fd == 0)
- return GIT_ERROR;
- if (p_write(fd, content, strlen(content)) != 0)
- return GIT_ERROR;
- if (p_close(fd) != 0)
- return GIT_ERROR;
-
- return GIT_SUCCESS;
-}
-
-BEGIN_TEST(file0, "test retrieving OID from a file apart from the ODB")
- git_oid expected_id, actual_id;
- char filename[] = "new_file";
-
- must_pass(file_create(filename, "new_file\n\0"));
-
- must_pass(git_odb_hashfile(&actual_id, filename, GIT_OBJ_BLOB));
-
- must_pass(git_oid_fromstr(&expected_id, test_blob_oid));
- must_be_true(git_oid_cmp(&expected_id, &actual_id) == 0);
-
- must_pass(p_unlink(filename));
-END_TEST
-
-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",
-};
-
-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,
-};
-
-#define ENTRY_COUNT0 15
-
-struct status_entry_counts {
- 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;
-};
-
-static int status_cb(const char *path, unsigned int status_flags, void *payload)
-{
- struct status_entry_counts *counts = (struct status_entry_counts *)payload;
-
- 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 GIT_SUCCESS;
-}
-
-BEGIN_TEST(statuscb0, "test retrieving status for worktree of repository")
- git_repository *repo;
- struct status_entry_counts counts;
-
- must_pass(copydir_recurs(STATUS_WORKDIR_FOLDER, TEMP_REPO_FOLDER));
- must_pass(p_rename(STATUS_REPOSITORY_TEMP_FOLDER, TEST_STD_REPO_FOLDER));
- must_pass(git_repository_open(&repo, TEST_STD_REPO_FOLDER));
-
- memset(&counts, 0x0, sizeof(struct status_entry_counts));
- counts.expected_entry_count = ENTRY_COUNT0;
- counts.expected_paths = entry_paths0;
- counts.expected_statuses = entry_statuses0;
-
- must_pass(git_status_foreach(repo, status_cb, &counts));
- must_be_true(counts.entry_count == counts.expected_entry_count);
- must_be_true(counts.wrong_status_flags_count == 0);
- must_be_true(counts.wrong_sorted_path == 0);
-
- git_repository_free(repo);
-
- git_futils_rmdir_r(TEMP_REPO_FOLDER, 1);
-END_TEST
-
-static int status_cb1(const char *GIT_UNUSED(path), unsigned int GIT_UNUSED(status_flags), void *payload)
-{
- int *count = (int *)payload;;
-
- GIT_UNUSED_ARG(path);
- GIT_UNUSED_ARG(status_flags);
-
- (void) *count++;
-
- return GIT_SUCCESS;
-}
-
-BEGIN_TEST(statuscb1, "test retrieving status for a worktree of an empty repository")
- git_repository *repo;
- int count = 0;
-
- must_pass(copydir_recurs(EMPTY_REPOSITORY_FOLDER, TEST_STD_REPO_FOLDER));
- must_pass(remove_placeholders(TEST_STD_REPO_FOLDER, "dummy-marker.txt"));
- must_pass(git_repository_open(&repo, TEST_STD_REPO_FOLDER));
-
- must_pass(git_status_foreach(repo, status_cb1, &count));
- must_be_true(count == 0);
-
- git_repository_free(repo);
-
- git_futils_rmdir_r(TEMP_REPO_FOLDER, 1);
-END_TEST
-
-static const char *entry_paths2[] = {
- "current_file",
- "file_deleted",
- "ignored_file",
- "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/current_file",
- "subdir/deleted_file",
- "subdir/modified_file",
-};
-
-static const unsigned int entry_statuses2[] = {
- GIT_STATUS_WT_DELETED,
- GIT_STATUS_WT_DELETED,
- GIT_STATUS_IGNORED,
- 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,
-};
-
-#define ENTRY_COUNT2 15
-
-BEGIN_TEST(statuscb2, "test retrieving status for a purged worktree of an valid repository")
- git_repository *repo;
- struct status_entry_counts counts;
-
- must_pass(copydir_recurs(STATUS_WORKDIR_FOLDER, TEMP_REPO_FOLDER));
- must_pass(p_rename(STATUS_REPOSITORY_TEMP_FOLDER, TEST_STD_REPO_FOLDER));
- must_pass(git_repository_open(&repo, TEST_STD_REPO_FOLDER));
-
- /* Purging the working */
- must_pass(p_unlink(TEMP_REPO_FOLDER "current_file"));
- must_pass(p_unlink(TEMP_REPO_FOLDER "modified_file"));
- must_pass(p_unlink(TEMP_REPO_FOLDER "new_file"));
- must_pass(p_unlink(TEMP_REPO_FOLDER "staged_changes"));
- must_pass(p_unlink(TEMP_REPO_FOLDER "staged_changes_modified_file"));
- must_pass(p_unlink(TEMP_REPO_FOLDER "staged_delete_modified_file"));
- must_pass(p_unlink(TEMP_REPO_FOLDER "staged_new_file"));
- must_pass(p_unlink(TEMP_REPO_FOLDER "staged_new_file_modified_file"));
- must_pass(git_futils_rmdir_r(TEMP_REPO_FOLDER "subdir", 1));
-
- memset(&counts, 0x0, sizeof(struct status_entry_counts));
- counts.expected_entry_count = ENTRY_COUNT2;
- counts.expected_paths = entry_paths2;
- counts.expected_statuses = entry_statuses2;
-
- must_pass(git_status_foreach(repo, status_cb, &counts));
- must_be_true(counts.entry_count == counts.expected_entry_count);
- must_be_true(counts.wrong_status_flags_count == 0);
- must_be_true(counts.wrong_sorted_path == 0);
-
- git_repository_free(repo);
-
- git_futils_rmdir_r(TEMP_REPO_FOLDER, 1);
-END_TEST
-
-static const char *entry_paths3[] = {
- ".HEADER",
- "42-is-not-prime.sigh",
- "README.md",
- "current_file",
- "current_file/current_file",
- "current_file/modified_file",
- "current_file/new_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",
-};
-
-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_NEW,
- 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,
-};
-
-#define ENTRY_COUNT3 23
-
-BEGIN_TEST(statuscb3, "test retrieving status for a worktree where a file and a subdir have been renamed and some files have been added")
- git_repository *repo;
- struct status_entry_counts counts;
-
- must_pass(copydir_recurs(STATUS_WORKDIR_FOLDER, TEMP_REPO_FOLDER));
- must_pass(p_rename(STATUS_REPOSITORY_TEMP_FOLDER, TEST_STD_REPO_FOLDER));
- must_pass(git_repository_open(&repo, TEST_STD_REPO_FOLDER));
-
- must_pass(p_rename(TEMP_REPO_FOLDER "current_file", TEMP_REPO_FOLDER "swap"));
- must_pass(p_rename(TEMP_REPO_FOLDER "subdir", TEMP_REPO_FOLDER "current_file"));
- must_pass(p_rename(TEMP_REPO_FOLDER "swap", TEMP_REPO_FOLDER "subdir"));
-
- must_pass(file_create(TEMP_REPO_FOLDER ".HEADER", "dummy"));
- must_pass(file_create(TEMP_REPO_FOLDER "42-is-not-prime.sigh", "dummy"));
- must_pass(file_create(TEMP_REPO_FOLDER "README.md", "dummy"));
-
- memset(&counts, 0x0, sizeof(struct status_entry_counts));
- counts.expected_entry_count = ENTRY_COUNT3;
- counts.expected_paths = entry_paths3;
- counts.expected_statuses = entry_statuses3;
-
- must_pass(git_status_foreach(repo, status_cb, &counts));
- must_be_true(counts.entry_count == counts.expected_entry_count);
- must_be_true(counts.wrong_status_flags_count == 0);
- must_be_true(counts.wrong_sorted_path == 0);
-
- git_repository_free(repo);
-
- git_futils_rmdir_r(TEMP_REPO_FOLDER, 1);
-END_TEST
-
-BEGIN_TEST(singlestatus0, "test retrieving status for single file")
- git_repository *repo;
- unsigned int status_flags;
- int i;
-
- must_pass(copydir_recurs(STATUS_WORKDIR_FOLDER, TEMP_REPO_FOLDER));
- must_pass(p_rename(STATUS_REPOSITORY_TEMP_FOLDER, TEST_STD_REPO_FOLDER));
- must_pass(git_repository_open(&repo, TEST_STD_REPO_FOLDER));
-
- for (i = 0; i < ENTRY_COUNT0; ++i) {
- must_pass(git_status_file(&status_flags, repo, entry_paths0[i]));
- must_be_true(status_flags == entry_statuses0[i]);
- }
-
- git_repository_free(repo);
-
- git_futils_rmdir_r(TEMP_REPO_FOLDER, 1);
-END_TEST
-
-BEGIN_TEST(singlestatus1, "test retrieving status for nonexistent file")
- git_repository *repo;
- unsigned int status_flags;
- int error;
-
- must_pass(copydir_recurs(STATUS_WORKDIR_FOLDER, TEMP_REPO_FOLDER));
- must_pass(p_rename(STATUS_REPOSITORY_TEMP_FOLDER, TEST_STD_REPO_FOLDER));
- must_pass(git_repository_open(&repo, TEST_STD_REPO_FOLDER));
-
- // "nonexistent" does not exist in HEAD, Index or the worktree
- error = git_status_file(&status_flags, repo, "nonexistent");
- must_be_true(error == GIT_ENOTFOUND);
-
- git_repository_free(repo);
-
- git_futils_rmdir_r(TEMP_REPO_FOLDER, 1);
-END_TEST
-
-BEGIN_TEST(singlestatus2, "test retrieving status for a non existent file in an empty repository")
- git_repository *repo;
- unsigned int status_flags;
- int error;
-
- must_pass(copydir_recurs(EMPTY_REPOSITORY_FOLDER, TEST_STD_REPO_FOLDER));
- must_pass(remove_placeholders(TEST_STD_REPO_FOLDER, "dummy-marker.txt"));
- must_pass(git_repository_open(&repo, TEST_STD_REPO_FOLDER));
-
- error = git_status_file(&status_flags, repo, "nonexistent");
- must_be_true(error == GIT_ENOTFOUND);
-
- git_repository_free(repo);
-
- git_futils_rmdir_r(TEMP_REPO_FOLDER, 1);
-END_TEST
-
-BEGIN_TEST(singlestatus3, "test retrieving status for a new file in an empty repository")
- git_repository *repo;
- unsigned int status_flags;
- git_buf file_path = GIT_BUF_INIT;
- char filename[] = "new_file";
- int fd;
-
- must_pass(copydir_recurs(EMPTY_REPOSITORY_FOLDER, TEST_STD_REPO_FOLDER));
- must_pass(remove_placeholders(TEST_STD_REPO_FOLDER, "dummy-marker.txt"));
-
- must_pass(git_buf_joinpath(&file_path, TEMP_REPO_FOLDER, filename));
- fd = p_creat(file_path.ptr, 0666);
- must_pass(fd);
- must_pass(p_write(fd, "new_file\n", 9));
- must_pass(p_close(fd));
-
- must_pass(git_repository_open(&repo, TEST_STD_REPO_FOLDER));
-
- must_pass(git_status_file(&status_flags, repo, filename));
- must_be_true(status_flags == GIT_STATUS_WT_NEW);
-
- git_repository_free(repo);
- git_buf_free(&file_path);
-
- git_futils_rmdir_r(TEMP_REPO_FOLDER, 1);
-END_TEST
-
-BEGIN_TEST(singlestatus4, "can't determine the status for a folder")
- git_repository *repo;
- unsigned int status_flags;
- int error;
-
- must_pass(copydir_recurs(STATUS_WORKDIR_FOLDER, TEMP_REPO_FOLDER));
- must_pass(p_rename(STATUS_REPOSITORY_TEMP_FOLDER, TEST_STD_REPO_FOLDER));
- must_pass(git_repository_open(&repo, TEST_STD_REPO_FOLDER));
-
- error = git_status_file(&status_flags, repo, "subdir");
- must_be_true(error == GIT_EINVALIDPATH);
-
- git_repository_free(repo);
-
- git_futils_rmdir_r(TEMP_REPO_FOLDER, 1);
-END_TEST
-
-BEGIN_SUITE(status)
- ADD_TEST(file0);
-
- ADD_TEST(statuscb0);
- ADD_TEST(statuscb1);
- ADD_TEST(statuscb2);
- ADD_TEST(statuscb3);
-
- ADD_TEST(singlestatus0);
- ADD_TEST(singlestatus1);
- ADD_TEST(singlestatus2);
- ADD_TEST(singlestatus3);
- ADD_TEST(singlestatus4);
-END_SUITE
diff --git a/tests/test_helpers.c b/tests/test_helpers.c
deleted file mode 100644
index 42c8031cd..000000000
--- a/tests/test_helpers.c
+++ /dev/null
@@ -1,338 +0,0 @@
-/*
- * This file is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2,
- * as published by the Free Software Foundation.
- *
- * In addition to the permissions in the GNU General Public License,
- * the authors give you unlimited permission to link the compiled
- * version of this file into combinations with other programs,
- * and to distribute those combinations without any restriction
- * coming from the use of this file. (The General Public License
- * restrictions do apply in other respects; for example, they cover
- * modification of the file, and distribution when not linked into
- * a combined executable.)
- *
- * This file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-#include "common.h"
-#include "test_helpers.h"
-#include "fileops.h"
-
-int write_object_data(char *file, void *data, size_t len)
-{
- git_file fd;
- int ret;
-
- if ((fd = p_creat(file, S_IREAD | S_IWRITE)) < 0)
- return -1;
- ret = p_write(fd, data, len);
- p_close(fd);
-
- return ret;
-}
-
-int write_object_files(const char *odb_dir, object_data *d)
-{
- if (p_mkdir(odb_dir, GIT_OBJECT_DIR_MODE) < 0) {
- int err = errno;
- fprintf(stderr, "can't make directory \"%s\"", odb_dir);
- if (err == EEXIST)
- fprintf(stderr, " (already exists)");
- fprintf(stderr, "\n");
- return -1;
- }
-
- if ((p_mkdir(d->dir, GIT_OBJECT_DIR_MODE) < 0) && (errno != EEXIST)) {
- fprintf(stderr, "can't make object directory \"%s\"\n", d->dir);
- return -1;
- }
- if (write_object_data(d->file, d->bytes, d->blen) < 0) {
- fprintf(stderr, "can't write object file \"%s\"\n", d->file);
- return -1;
- }
-
- return 0;
-}
-
-int remove_object_files(const char *odb_dir, object_data *d)
-{
- if (p_unlink(d->file) < 0) {
- fprintf(stderr, "can't delete object file \"%s\"\n", d->file);
- return -1;
- }
- if ((p_rmdir(d->dir) < 0) && (errno != ENOTEMPTY)) {
- fprintf(stderr, "can't remove object directory \"%s\"\n", d->dir);
- return -1;
- }
-
- if (p_rmdir(odb_dir) < 0) {
- fprintf(stderr, "can't remove directory \"%s\"\n", odb_dir);
- return -1;
- }
-
- return 0;
-}
-
-void locate_loose_object(const char *repository_folder, git_object *object, char **out, char **out_folder)
-{
- static const char *objects_folder = "objects/";
-
- char *ptr, *full_path, *top_folder;
- int path_length, objects_length;
-
- assert(repository_folder && object);
-
- objects_length = strlen(objects_folder);
- path_length = strlen(repository_folder);
- ptr = full_path = git__malloc(path_length + objects_length + GIT_OID_HEXSZ + 3);
-
- strcpy(ptr, repository_folder);
- strcpy(ptr + path_length, objects_folder);
-
- ptr = top_folder = ptr + path_length + objects_length;
- *ptr++ = '/';
- git_oid_pathfmt(ptr, git_object_id(object));
- ptr += GIT_OID_HEXSZ + 1;
- *ptr = 0;
-
- *out = full_path;
-
- if (out_folder)
- *out_folder = top_folder;
-}
-
-int loose_object_mode(const char *repository_folder, git_object *object)
-{
- char *object_path;
- struct stat st;
-
- locate_loose_object(repository_folder, object, &object_path, NULL);
- if (p_stat(object_path, &st) < 0)
- return 0;
- free(object_path);
-
- return st.st_mode;
-}
-
-int loose_object_dir_mode(const char *repository_folder, git_object *object)
-{
- char *object_path;
- size_t pos;
- struct stat st;
-
- locate_loose_object(repository_folder, object, &object_path, NULL);
-
- pos = strlen(object_path);
- while (pos--) {
- if (object_path[pos] == '/') {
- object_path[pos] = 0;
- break;
- }
- }
-
- if (p_stat(object_path, &st) < 0)
- return 0;
- free(object_path);
-
- return st.st_mode;
-}
-
-int remove_loose_object(const char *repository_folder, git_object *object)
-{
- char *full_path, *top_folder;
-
- locate_loose_object(repository_folder, object, &full_path, &top_folder);
-
- if (p_unlink(full_path) < 0) {
- fprintf(stderr, "can't delete object file \"%s\"\n", full_path);
- return -1;
- }
-
- *top_folder = 0;
-
- if ((p_rmdir(full_path) < 0) && (errno != ENOTEMPTY)) {
- fprintf(stderr, "can't remove object directory \"%s\"\n", full_path);
- return -1;
- }
-
- git__free(full_path);
-
- return GIT_SUCCESS;
-}
-
-int cmp_objects(git_rawobj *o, object_data *d)
-{
- if (o->type != git_object_string2type(d->type))
- return -1;
- if (o->len != d->dlen)
- return -1;
- if ((o->len > 0) && (memcmp(o->data, d->data, o->len) != 0))
- return -1;
- return 0;
-}
-
-int copy_file(const char *src, const char *dst)
-{
- git_fbuffer source_buf;
- git_file dst_fd;
- int error = GIT_ERROR;
-
- if (git_futils_readbuffer(&source_buf, src) < GIT_SUCCESS)
- return GIT_ENOTFOUND;
-
- dst_fd = git_futils_creat_withpath(dst, 0777, 0666);
- if (dst_fd < 0)
- goto cleanup;
-
- error = p_write(dst_fd, source_buf.data, source_buf.len);
-
-cleanup:
- git_futils_freebuffer(&source_buf);
- p_close(dst_fd);
-
- return error;
-}
-
-int cmp_files(const char *a, const char *b)
-{
- git_fbuffer buf_a, buf_b;
- int error = GIT_ERROR;
-
- if (git_futils_readbuffer(&buf_a, a) < GIT_SUCCESS)
- return GIT_ERROR;
-
- if (git_futils_readbuffer(&buf_b, b) < GIT_SUCCESS) {
- git_futils_freebuffer(&buf_a);
- return GIT_ERROR;
- }
-
- if (buf_a.len == buf_b.len && !memcmp(buf_a.data, buf_b.data, buf_a.len))
- error = GIT_SUCCESS;
-
- git_futils_freebuffer(&buf_a);
- git_futils_freebuffer(&buf_b);
-
- return error;
-}
-
-typedef struct {
- git_buf src;
- size_t src_baselen;
- git_buf dst;
- size_t dst_baselen;
-} copydir_data;
-
-static int copy_filesystem_element_recurs(void *_data, git_buf *source)
-{
- copydir_data *data = (copydir_data *)_data;
-
- git_buf_truncate(&data->dst, data->dst_baselen);
- git_buf_puts(&data->dst, source->ptr + data->src_baselen);
-
- if (git_path_isdir(source->ptr) == GIT_SUCCESS)
- return git_path_direach(source, copy_filesystem_element_recurs, _data);
- else
- return copy_file(source->ptr, data->dst.ptr);
-}
-
-int copydir_recurs(
- const char *source_directory_path,
- const char *destination_directory_path)
-{
- int error;
- copydir_data data = { GIT_BUF_INIT, 0, GIT_BUF_INIT, 0 };
-
- /* Source has to exist, Destination hast to _not_ exist */
- if (git_path_isdir(source_directory_path) != GIT_SUCCESS ||
- git_path_isdir(destination_directory_path) == GIT_SUCCESS)
- return GIT_EINVALIDPATH;
-
- git_buf_joinpath(&data.src, source_directory_path, "");
- data.src_baselen = data.src.size;
-
- git_buf_joinpath(&data.dst, destination_directory_path, "");
- data.dst_baselen = data.dst.size;
-
- error = copy_filesystem_element_recurs(&data, &data.src);
-
- git_buf_free(&data.src);
- git_buf_free(&data.dst);
-
- return error;
-}
-
-int open_temp_repo(git_repository **repo, const char *path)
-{
- int error;
- if ((error = copydir_recurs(path, TEMP_REPO_FOLDER)) < GIT_SUCCESS)
- return error;
-
- return git_repository_open(repo, TEMP_REPO_FOLDER);
-}
-
-void close_temp_repo(git_repository *repo)
-{
- git_repository_free(repo);
- if (git_futils_rmdir_r(TEMP_REPO_FOLDER, 1) < GIT_SUCCESS) {
- printf("\nFailed to remove temporary folder. Aborting test suite.\n");
- exit(-1);
- }
-}
-
-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))
- return git_path_direach(path, remove_placeholders_recurs, data);
-
- pathlen = path->size;
-
- if (pathlen < data->filename_len)
- return GIT_SUCCESS;
-
- /* 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 GIT_SUCCESS;
-}
-
-int 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))
- return GIT_EINVALIDPATH;
-
- if ((error = git_buf_sets(&buffer, directory_path)) < GIT_SUCCESS)
- return error;
-
- data.filename = filename;
- data.filename_len = strlen(filename);
-
- error = remove_placeholders_recurs(&data, &buffer);
-
- git_buf_free(&buffer);
-
- return error;
-}
diff --git a/tests/test_helpers.h b/tests/test_helpers.h
deleted file mode 100644
index a475f66f3..000000000
--- a/tests/test_helpers.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * This file is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2,
- * as published by the Free Software Foundation.
- *
- * In addition to the permissions in the GNU General Public License,
- * the authors give you unlimited permission to link the compiled
- * version of this file into combinations with other programs,
- * and to distribute those combinations without any restriction
- * coming from the use of this file. (The General Public License
- * restrictions do apply in other respects; for example, they cover
- * modification of the file, and distribution when not linked into
- * a combined executable.)
- *
- * This file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-#ifndef INCLUDE_test_helpers_h__
-#define INCLUDE_test_helpers_h__
-
-#include "test_lib.h"
-#include <git2.h>
-
-#include "odb.h"
-
-#define TEST_REPOSITORY_NAME "testrepo.git"
-#define REPOSITORY_FOLDER TEST_RESOURCES "/" TEST_REPOSITORY_NAME "/"
-#define ODB_FOLDER (REPOSITORY_FOLDER "objects/")
-#define TEST_INDEX_PATH (REPOSITORY_FOLDER "index")
-#define TEST_INDEX2_PATH (TEST_RESOURCES "/gitgit.index")
-#define TEST_INDEXBIG_PATH (TEST_RESOURCES "/big.index")
-#define EMPTY_REPOSITORY_FOLDER TEST_RESOURCES "/empty_standard_repo/.gitted/"
-
-#define TEMP_FOLDER ""
-#define TEMP_REPO_FOLDER TEMP_FOLDER TEST_REPOSITORY_NAME "/"
-#define TEMP_REPO_FOLDER_NS TEMP_FOLDER TEST_REPOSITORY_NAME
-#define TEST_STD_REPO_FOLDER TEMP_REPO_FOLDER ".git/"
-
-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;
-
-extern int write_object_data(char *file, void *data, size_t len);
-
-extern int write_object_files(const char *odb_dir, object_data *d);
-
-extern int remove_object_files(const char *odb_dir, object_data *d);
-
-extern int cmp_objects(git_rawobj *o, object_data *d);
-
-extern void locate_loose_object(const char *odb_dir, git_object *object, char **out, char **out_folder);
-
-extern int loose_object_mode(const char *odb_dir, git_object *object);
-extern int loose_object_dir_mode(const char *odb_dir, git_object *object);
-
-extern int remove_loose_object(const char *odb_dir, git_object *object);
-
-extern int cmp_files(const char *a, const char *b);
-extern int copy_file(const char *source, const char *dest);
-extern int rmdir_recurs(const char *directory_path);
-extern int copydir_recurs(const char *source_directory_path, const char *destination_directory_path);
-extern int remove_placeholders(const char *directory_path, const char *filename);
-
-extern int open_temp_repo(git_repository **repo, const char *path);
-extern void close_temp_repo(git_repository *repo);
-
-#endif
-/* INCLUDE_test_helpers_h__ */
diff --git a/tests/test_lib.c b/tests/test_lib.c
deleted file mode 100755
index a4c39dfde..000000000
--- a/tests/test_lib.c
+++ /dev/null
@@ -1,198 +0,0 @@
-#include <assert.h>
-#include <setjmp.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <math.h>
-
-#include "test_lib.h"
-
-#define DO_ALLOC(TYPE) ((TYPE*) malloc(sizeof(TYPE)))
-#define GIT_MAX_TEST_CASES 64
-
-struct git_test {
- char *name;
- char *message;
- char *failed_pos;
- char *description;
- char *error_message;
-
- git_testfunc function;
- unsigned failed:1, ran:1;
- jmp_buf *jump;
-};
-
-struct git_testsuite {
- char *name;
- int count, fail_count;
- git_test *list[GIT_MAX_TEST_CASES];
-};
-
-static void test_free(git_test *t)
-{
- if (t) {
- free(t->name);
- free(t->description);
- free(t->failed_pos);
- free(t->message);
- free(t->error_message);
- free(t);
- }
-}
-
-static void test_run(git_test *tc)
-{
- jmp_buf buf;
- tc->jump = &buf;
-
- if (setjmp(buf) == 0) {
- tc->ran = 1;
- (tc->function)(tc);
- }
-
- tc->jump = 0;
-}
-
-static git_test *create_test(git_testfunc function)
-{
- git_test *t = DO_ALLOC(git_test);
-
- memset(t, 0x0, sizeof(git_test));
- t->function = function;
-
- return t;
-}
-
-void git_test__init(git_test *t, const char *name, const char *description)
-{
- t->name = strdup(name);
- t->description = strdup(description);
-}
-
-
-/*-------------------------------------------------------------------------*
- * Public assert methods
- *-------------------------------------------------------------------------*/
-
-static void fail_test(git_test *tc, const char *file, int line, const char *message)
-{
- char buf[1024];
- const char *last_error = git_lasterror();
-
- snprintf(buf, 1024, "%s:%d", file, line);
-
- tc->failed = 1;
- tc->message = strdup(message);
- tc->failed_pos = strdup(buf);
-
- if (last_error)
- tc->error_message = strdup(last_error);
-
- if (tc->jump != 0)
- longjmp(*(tc->jump), 0);
-}
-
-void git_test__fail(git_test *tc, const char *file, int line, const char *message)
-{
- fail_test(tc, file, line, message);
-}
-
-void git_test__assert(git_test *tc, const char *file, int line, const char *message, int condition)
-{
- if (condition == 0)
- fail_test(tc, file, line, message);
-}
-
-void git_test__assert_pass(git_test *tc, const char *file, int line, const char *message, int ret_value)
-{
- if (ret_value < 0)
- fail_test(tc, file, line, message);
-}
-
-/*-------------------------------------------------------------------------*
- * Test Suite
- *-------------------------------------------------------------------------*/
-
-static void testsuite_init(git_testsuite *ts)
-{
- ts->count = 0;
- ts->fail_count = 0;
- memset(ts->list, 0, sizeof(ts->list));
-}
-
-git_testsuite *git_testsuite_new(const char *name)
-{
- git_testsuite *ts = DO_ALLOC(git_testsuite);
- testsuite_init(ts);
- ts->name = strdup(name);
- return ts;
-}
-
-static void free_suite(git_testsuite *ts)
-{
- unsigned int n;
-
- for (n = 0; n < GIT_MAX_TEST_CASES; n++)
- if (ts->list[n])
- test_free(ts->list[n]);
-
- free(ts->name);
- free(ts);
-}
-
-void git_testsuite_add(git_testsuite *ts, git_testfunc test)
-{
- assert(ts->count < GIT_MAX_TEST_CASES);
- ts->list[ts->count++] = create_test(test);
-}
-
-static void print_details(git_testsuite *ts)
-{
- int i;
- int failCount = 0;
-
- if (ts->fail_count == 0) {
- const char *testWord = ts->count == 1 ? "test" : "tests";
- printf("OK (%d %s)\n", ts->count, testWord);
- } else {
- printf("Failed (%d failures):\n", ts->fail_count);
-
- for (i = 0 ; i < ts->count ; ++i) {
- git_test *tc = ts->list[i];
- if (tc->failed) {
- failCount++;
- printf(" %d) \"%s\" [test %s @ %s]\n\t%s\n",
- failCount, tc->description, tc->name, tc->failed_pos, tc->message);
- if (tc->error_message)
- printf("\tError: %s\n", tc->error_message);
- }
- }
- }
-}
-
-int git_testsuite_run(git_testsuite *ts)
-{
- int i, fail_count;
-
- printf("Suite \"%s\": ", ts->name);
-
- for (i = 0 ; i < ts->count ; ++i) {
- git_test *tc = ts->list[i];
-
- test_run(tc);
- if (tc->failed) {
- ts->fail_count++;
- putchar('F');
- } else
- putchar('.');
-
- fflush(stdout);
- }
- printf("\n ");
- print_details(ts);
- fail_count = ts->fail_count;
-
- free_suite(ts);
- return fail_count;
-}
-
diff --git a/tests/test_lib.h b/tests/test_lib.h
deleted file mode 100755
index 9d90e4847..000000000
--- a/tests/test_lib.h
+++ /dev/null
@@ -1,54 +0,0 @@
-#ifndef __LIBGIT2_TEST_H__
-#define __LIBGIT2_TEST_H__
-
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "common.h"
-#include <git2.h>
-
-#define DECLARE_SUITE(SNAME) extern git_testsuite *libgit2_suite_##SNAME(void)
-#define SUITE_NAME(SNAME) libgit2_suite_##SNAME
-
-#define BEGIN_SUITE(SNAME) \
- git_testsuite *libgit2_suite_##SNAME(void);\
- git_testsuite *libgit2_suite_##SNAME(void) {\
- git_testsuite *_gitsuite = git_testsuite_new(#SNAME);
-
-#define ADD_TEST(TNAME) \
- git_testsuite_add(_gitsuite, _gittest__##TNAME);
-
-#define END_SUITE \
- return _gitsuite;\
- }
-
-#define BEGIN_TEST(TNAME, DESC) \
- static void _gittest__##TNAME(git_test *_gittest) { \
- git_test__init(_gittest, #TNAME, DESC); \
- git_clearerror();\
- {\
-
-#define END_TEST }}
-
-typedef struct git_test git_test;
-typedef struct git_testsuite git_testsuite;
-typedef void (*git_testfunc)(git_test *);
-typedef git_testsuite *(*libgit2_suite)(void);
-
-void git_test__init(git_test *t, const char *name, const char *description);
-void git_test__fail(git_test *tc, const char *file, int line, const char *message);
-void git_test__assert(git_test *tc, const char *file, int line, const char *message, int condition);
-void git_test__assert_pass(git_test *tc, const char *file, int line, const char *message, int ret_value);
-
-#define must_pass(expr) git_test__assert_pass(_gittest, __FILE__, __LINE__, "Method failed: " #expr, (expr))
-#define must_fail(expr) git_test__assert(_gittest, __FILE__, __LINE__, "Expected method to fail: " #expr, (expr) < 0)
-#define must_be_true(expr) git_test__assert(_gittest, __FILE__, __LINE__, "Expression is not true: " #expr, !!(expr))
-
-git_testsuite *git_testsuite_new(const char *name);
-void git_testsuite_add(git_testsuite *ts, git_testfunc test);
-int git_testsuite_run(git_testsuite *ts);
-
-#endif
-
diff --git a/tests/test_main.c b/tests/test_main.c
deleted file mode 100644
index 732d25a9d..000000000
--- a/tests/test_main.c
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * This file is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2,
- * as published by the Free Software Foundation.
- *
- * In addition to the permissions in the GNU General Public License,
- * the authors give you unlimited permission to link the compiled
- * version of this file into combinations with other programs,
- * and to distribute those combinations without any restriction
- * coming from the use of this file. (The General Public License
- * restrictions do apply in other respects; for example, they cover
- * modification of the file, and distribution when not linked into
- * a combined executable.)
- *
- * This file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING. If not, write to
- * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-#include <string.h>
-#include <git2.h>
-
-#include "posix.h"
-
-#include "test_lib.h"
-#include "test_helpers.h"
-
-DECLARE_SUITE(core);
-DECLARE_SUITE(rawobjects);
-DECLARE_SUITE(objwrite);
-DECLARE_SUITE(commit);
-DECLARE_SUITE(revwalk);
-DECLARE_SUITE(index);
-DECLARE_SUITE(hashtable);
-DECLARE_SUITE(tag);
-DECLARE_SUITE(tree);
-DECLARE_SUITE(refs);
-DECLARE_SUITE(repository);
-DECLARE_SUITE(threads);
-DECLARE_SUITE(buffers);
-DECLARE_SUITE(status);
-
-static libgit2_suite suite_methods[]= {
- SUITE_NAME(core),
- SUITE_NAME(rawobjects),
- SUITE_NAME(objwrite),
- SUITE_NAME(commit),
- SUITE_NAME(revwalk),
- SUITE_NAME(index),
- SUITE_NAME(hashtable),
- SUITE_NAME(tag),
- SUITE_NAME(tree),
- SUITE_NAME(refs),
- SUITE_NAME(repository),
- SUITE_NAME(threads),
- SUITE_NAME(buffers),
- SUITE_NAME(status),
-};
-
-#define GIT_SUITE_COUNT (ARRAY_SIZE(suite_methods))
-
-#ifdef GIT_WIN32
-int __cdecl
-#else
-int
-#endif
-main(int GIT_UNUSED(argc), char *GIT_UNUSED(argv[]))
-{
- unsigned int i, failures;
-
- GIT_UNUSED_ARG(argc);
- GIT_UNUSED_ARG(argv);
-
- git_threads_init();
-
- p_umask(0);
-
- failures = 0;
-
- for (i = 0; i < GIT_SUITE_COUNT; ++i)
- failures += git_testsuite_run(suite_methods[i]());
-
- git_threads_shutdown();
-
- return failures ? -1 : 0;
-}
-
diff --git a/tests/tests.supp b/tests/tests.supp
deleted file mode 100644
index fe9d965dc..000000000
--- a/tests/tests.supp
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- ignore-zlib-cond
- Memcheck:Cond
- obj:*libz.so*
-}
-